1
0
Fork 0
mirror of synced 2024-04-25 08:12:07 +12:00

Heic or heif support, don't know (#750)

This commit is contained in:
Rafał Mikrut 2022-06-08 21:42:51 +02:00 committed by GitHub
parent 56fc29fa4f
commit db09dc9363
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 373 additions and 161 deletions

View file

@ -32,17 +32,10 @@ jobs:
linux-cli-${{github.ref}}-${{github.sha}}
- name: Install basic libraries
run: sudo apt-get update; sudo apt install libgtk-4-dev libasound2-dev -y
- name: Build CLI Debug
run: cargo build --bin czkawka_cli
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
if: ${{ matrix.type == 'debug'}}
run: sudo apt-get update; sudo apt install libgtk-4-dev libheif-dev -y
- name: Build CLI Release
run: cargo build --release --bin czkawka_cli
run: cargo build --release --bin czkawka_cli --features heif
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
@ -150,21 +143,26 @@ jobs:
linux-gui-${{github.ref}}-${{github.sha}}
- name: Install Gtk, Mingw, unzip, zip and wget
run: sudo apt-get update; sudo apt install libgtk-4-dev libasound2-dev fuse libfuse2 -y
run: sudo apt-get update; sudo apt install libgtk-4-dev fuse libfuse2
if: ${{ matrix.toolchain == '1.60.0' }}
- name: Build GUI Debug
run: cargo build --bin czkawka_gui
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
if: ${{ matrix.type == 'debug'}}
- name: Install Gtk, Mingw, unzip, zip and wget and libheif
run: sudo apt-get update; sudo apt install libgtk-4-dev fuse libfuse2 libheif-dev -y
if: ${{ matrix.toolchain == 'stable' }}
- name: Build GUI Release
run: cargo build --release --bin czkawka_gui
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
if: ${{ matrix.type == 'release'}}
if: ${{ (matrix.type == 'release') && (matrix.toolchain == '1.60.0') }}
- name: Build GUI Release
run: cargo build --release --bin czkawka_gui --features heif
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
if: ${{ (matrix.type == 'release') && (matrix.toolchain == 'stable') }}
- name: Store Linux GUI
uses: actions/upload-artifact@v2
@ -214,7 +212,7 @@ jobs:
linux-appimage-gui-${{github.ref}}-${{github.sha}}
- name: Install Gtk,
run: sudo apt-get update; sudo apt install libgtk-4-dev libasound2-dev librsvg2-dev wget fuse libfuse2 -y
run: sudo apt-get update; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y
- name: Build GUI Release
run: cargo build --release --bin czkawka_gui

View file

@ -31,15 +31,8 @@ jobs:
restore-keys: |
mac-cli-${{github.ref}}-${{github.sha}}
- name: Build CLI Debug
run: cargo build --bin czkawka_cli
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0 -D warnings"
if: ${{ matrix.type == 'debug'}}
- name: Build CLI Release
run: cargo build --release --bin czkawka_cli
run: cargo build --release --bin czkawka_cli --features heif
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0 -D warnings"
@ -73,17 +66,10 @@ jobs:
run: rm '/usr/local/bin/2to3'
- name: Install GTK4
run: brew install rust gtk4
- name: Build GUI Debug
run: cargo build --bin czkawka_gui
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0 -D warnings"
if: ${{ matrix.type == 'debug'}}
run: brew install rust gtk4 libheif
- name: Build GUI Release
run: cargo build --release --bin czkawka_gui
run: cargo build --release --bin czkawka_gui --features heif
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0 -D warnings"

View file

@ -28,8 +28,8 @@ jobs:
components: rustfmt, clippy
override: true
- name: Install Gtk
run: sudo apt-get update; sudo apt install -y libgtk-4-dev libasound2-dev
- name: Install Gtk 4
run: sudo apt-get update; sudo apt install -y libgtk-4-dev libheif-dev -y
- name: Check the format
run: cargo fmt --all -- --check

79
Cargo.lock generated
View file

@ -224,9 +224,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "bumpalo"
version = "3.9.1"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "bytemuck"
@ -461,6 +461,7 @@ dependencies = [
name = "czkawka_core"
version = "4.1.0"
dependencies = [
"anyhow",
"audio_checker",
"bincode",
"bitflags",
@ -479,6 +480,7 @@ dependencies = [
"image_hasher",
"imagepipe",
"infer",
"libheif-rs",
"lofty",
"mime_guess",
"once_cell",
@ -718,7 +720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
"miniz_oxide 0.5.1",
"miniz_oxide 0.5.3",
]
[[package]]
@ -917,9 +919,9 @@ dependencies = [
[[package]]
name = "gdk4"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a2fc0bd03d59383fc10b71a8cb731a1fac2998732a36a0c03e9b1de1513218"
checksum = "4fabb7cf843c26b085a5d68abb95d0c0bf27a9ae2eeff9c4adb503a1eb580876"
dependencies = [
"bitflags",
"cairo-rs",
@ -933,9 +935,9 @@ dependencies = [
[[package]]
name = "gdk4-sys"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34a7e60e0c0103cffeaf7baf9dc05358dca74fd350a0c4d61c6c3083ca9fdf1"
checksum = "efe7dcb44f5c00aeabff3f69abfc5673de46559070f89bd3fbb7b66485d9cef2"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -1098,9 +1100,9 @@ dependencies = [
[[package]]
name = "gsk4"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14d5a47a78c682bb67496b562495ed84972c0512ba0654888c4dc92b80a85bd3"
checksum = "05e9020d333280b3aa38d496495bfa9b50712eebf1ad63f0ec5bcddb5eb61be4"
dependencies = [
"bitflags",
"cairo-rs",
@ -1114,9 +1116,9 @@ dependencies = [
[[package]]
name = "gsk4-sys"
version = "0.4.2"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31d21d7ce02ba261bb24c50c4ab238a10b41a2c97c32afffae29471b7cca69b"
checksum = "7add39ccf60078508c838643a2dcc91f045c46ed63b5ea6ab701b2e25bda3fea"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
@ -1130,9 +1132,9 @@ dependencies = [
[[package]]
name = "gtk4"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb5d40303dabe4608fc260de2bd7563da6f85bc90af956323f0cd8ae0abcfe03"
checksum = "c64f0c2a3d80e899dc3febddad5bac193ffcf74a0fd7e31037f30dd34d6f7396"
dependencies = [
"bitflags",
"cairo-rs",
@ -1153,9 +1155,9 @@ dependencies = [
[[package]]
name = "gtk4-macros"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3c4aa605fb3d78205c7aef0eeaa6db61d8cc4dd05a465dc6ffdfdaee84f825"
checksum = "fafbcc920af4eb677d7d164853e7040b9de5a22379c596f570190c675d45f7a7"
dependencies = [
"anyhow",
"proc-macro-crate",
@ -1168,9 +1170,9 @@ dependencies = [
[[package]]
name = "gtk4-sys"
version = "0.4.5"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c47c075e8f795c38f6e9a47b51a73eab77b325f83c0154979ed4d4245c36490d"
checksum = "5bc8006eea634b7c72da3ff79e24606e45f21b3b832a3c5a1f543f5f97eb0f63"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -1517,6 +1519,25 @@ version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libheif-rs"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a28e98f7e0b934f2d240bbd438b02d82d2bbce34a97d5cf25767e2c2ff249f7"
dependencies = [
"enumn",
"libheif-sys",
]
[[package]]
name = "libheif-sys"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9426b3c799fe53b9636aac80ddc60efa5060c1959faee2e9d3edf0da6b8536a"
dependencies = [
"libc",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
@ -1643,9 +1664,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.5.1"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
dependencies = [
"adler",
]
@ -1800,12 +1821,12 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "open"
version = "2.1.2"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0524af9508f9b5c4eb41dce095860456727748f63b478d625f119a70e0d764a"
checksum = "f2423ffbf445b82e58c3b1543655968923dd06f85432f10be2bb4f1b7122f98c"
dependencies = [
"pathdiff",
"winapi",
"windows-sys",
]
[[package]]
@ -1841,9 +1862,9 @@ dependencies = [
[[package]]
name = "parking_lot"
version = "0.12.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
@ -2010,7 +2031,7 @@ dependencies = [
"bitflags",
"crc32fast",
"deflate 1.0.0",
"miniz_oxide 0.5.1",
"miniz_oxide 0.5.3",
]
[[package]]
@ -2716,9 +2737,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.95"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
dependencies = [
"proc-macro2",
"quote",
@ -2999,9 +3020,9 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93bbc61e655a4833cf400d0d15bf3649313422fa7572886ad6dab16d79886365"
checksum = "c6d5d669b51467dcf7b2f1a796ce0f955f05f01cafda6c19d6e95f730df29238"
[[package]]
name = "vec_map"

View file

@ -9,8 +9,16 @@ homepage = "https://github.com/qarmin/czkawka"
repository = "https://github.com/qarmin/czkawka"
[dependencies]
czkawka_core = { path = "../czkawka_core", version = "4.1.0" }
structopt = "0.3.26"
# For enum types
image_hasher = "1.0.0"
image_hasher = "1.0.0"
[dependencies.czkawka_core]
path = "../czkawka_core"
version = "4.1.0"
features = []
[features]
default = []
heif = ["czkawka_core/heif"]

View file

@ -63,4 +63,11 @@ imagepipe = "0.5.0"
# Checking for invalid extensions
mime_guess = "2.0.4"
infer = "0.8.0"
infer = "0.8.0"
libheif-rs = { version = "0.15.0", optional = true }
anyhow = { version = "1.0.57", optional = true }
[features]
default = []
heif = ["dep:libheif-rs", "dep:anyhow"]

View file

@ -9,20 +9,26 @@ use directories_next::ProjectDirs;
use image::{DynamicImage, ImageBuffer, Rgb};
use imagepipe::{ImageSource, Pipeline};
#[cfg(feature = "heif")]
use anyhow::Result;
#[cfg(feature = "heif")]
use libheif_rs::{Channel, ColorSpace, HeifContext, RgbChroma};
/// Class for common functions used across other class/functions
pub const RAW_IMAGE_EXTENSIONS: &[&str] = &[
".mrw", ".arw", ".srf", ".sr2", ".mef", ".orf", ".srw", ".erf", ".kdc", ".kdc", ".dcs", ".rw2", ".raf", ".dcr", ".dng", ".pef", ".crw", ".iiq", ".3fr", ".nrw", ".nef", ".mos",
".cr2", ".ari",
];
pub const IMAGE_RS_EXTENSIONS: &[&str] = &[
".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".tga", ".ff", ".jif", ".jfi", ".webp", ".gif", ".ico", ".exr", ".hdr",
".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".tga", ".ff", ".jif", ".jfi", ".webp", ".gif", ".ico", ".exr",
];
pub const IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS: &[&str] = &[".jpg", ".jpeg", ".png", ".tiff", ".tif", ".tga", ".ff", ".jif", ".jfi", ".bmp", ".webp", ".exr", ".hdr"];
pub const IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS: &[&str] = &[".jpg", ".jpeg", ".png", ".tiff", ".tif", ".tga", ".ff", ".jif", ".jfi", ".bmp", ".webp", ".exr"];
pub const IMAGE_RS_BROKEN_FILES_EXTENSIONS: &[&str] = &[
".jpg", ".jpeg", ".png", ".tiff", ".tif", ".tga", ".ff", ".jif", ".jfi", ".gif", ".bmp", ".ico", ".jfif", ".jpe", ".pnz", ".dib", ".webp", ".exr", ".hdr",
".jpg", ".jpeg", ".png", ".tiff", ".tif", ".tga", ".ff", ".jif", ".jfi", ".gif", ".bmp", ".ico", ".jfif", ".jpe", ".pnz", ".dib", ".webp", ".exr",
];
pub const HEIC_EXTENSIONS: &[&str] = &[".heif", ".heifs", ".heic", ".heics", ".avci", ".avcs", ".avif", ".avifs"];
pub const ZIP_FILES_EXTENSIONS: &[&str] = &[".zip"];
@ -96,6 +102,20 @@ pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, use_json: b
None
}
#[cfg(feature = "heif")]
pub fn get_dynamic_image_from_heic(path: &str) -> Result<DynamicImage> {
let im = HeifContext::read_from_file(path)?;
let handle = im.primary_image_handle()?;
let image = handle.decode(ColorSpace::Rgb(RgbChroma::Rgb), false)?;
let width = image.width(Channel::Interleaved).map_err(|e| anyhow::anyhow!("{}", e))?;
let height = image.height(Channel::Interleaved).map_err(|e| anyhow::anyhow!("{}", e))?;
let planes = image.planes();
let interleaved_plane = planes.interleaved.unwrap();
ImageBuffer::from_raw(width, height, interleaved_plane.data.to_owned())
.map(DynamicImage::ImageRgb8)
.ok_or_else(|| anyhow::anyhow!("Failed to create image buffer"))
}
pub fn get_dynamic_image_from_raw_image(path: impl AsRef<Path> + std::fmt::Debug) -> Option<DynamicImage> {
let file_handler = match OpenOptions::new().read(true).open(&path) {
Ok(t) => t,

View file

@ -18,7 +18,10 @@ use image_hasher::{FilterType, HashAlg, HasherConfig};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use crate::common::{get_dynamic_image_from_raw_image, open_cache_folder, Common, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, LOOP_DURATION, RAW_IMAGE_EXTENSIONS};
#[cfg(feature = "heif")]
use crate::common::get_dynamic_image_from_heic;
use crate::common::{get_dynamic_image_from_raw_image, open_cache_folder, Common, HEIC_EXTENSIONS, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, LOOP_DURATION, RAW_IMAGE_EXTENSIONS};
use crate::common_directory::Directories;
use crate::common_extensions::Extensions;
use crate::common_items::ExcludedItems;
@ -279,9 +282,11 @@ impl SimilarImages {
if !self.allowed_extensions.using_custom_extensions() {
self.allowed_extensions.extend_allowed_extensions(IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS);
self.allowed_extensions.extend_allowed_extensions(RAW_IMAGE_EXTENSIONS);
#[cfg(feature = "heif")]
self.allowed_extensions.extend_allowed_extensions(HEIC_EXTENSIONS);
} else {
self.allowed_extensions
.validate_allowed_extensions(&[IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS].concat());
.validate_allowed_extensions(&[IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS, HEIC_EXTENSIONS].concat());
if !self.allowed_extensions.using_custom_extensions() {
return true;
}
@ -562,13 +567,29 @@ impl SimilarImages {
let image;
if !IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS.iter().any(|e| file_name_lowercase.ends_with(e)) {
image = match get_dynamic_image_from_raw_image(&file_entry.path) {
Some(t) => t,
None =>
return Some(Some((file_entry, Vec::new())))
};
} else {
#[allow(clippy::never_loop)] // Required to implement nice if/else
'krztyna: loop {
if RAW_IMAGE_EXTENSIONS.iter().any(|e| file_name_lowercase.ends_with(e)) {
image = match get_dynamic_image_from_raw_image(&file_entry.path) {
Some(t) => t,
None =>
return Some(Some((file_entry, Vec::new())))
};
break 'krztyna;
}
# [cfg(feature = "heif")]
if HEIC_EXTENSIONS.iter().any(|e| file_name_lowercase.ends_with(e)) {
image = match get_dynamic_image_from_heic(&file_entry.path.to_string_lossy().to_string()) {
Ok(t) => t,
Err(_) => {
return Some(Some((file_entry, Vec::new())));
}
};
break 'krztyna;
}
// Normal image extension, when any other fail, not using if/else
let result = panic::catch_unwind(|| {
match image::open(file_entry.path.clone()) {
Ok(t) => Ok(t),
@ -590,8 +611,9 @@ impl SimilarImages {
println!("Image-rs library crashed when opening \"{:?}\" image, please check if problem happens with latest image-rs version(this can be checked via https://github.com/qarmin/ImageOpening tool) and if it is not reported, please report bug here - https://github.com/image-rs/image/issues", file_entry.path);
return Some(Some((file_entry, Vec::new())));
}
}
break 'krztyna;
}
let dimensions = image.dimensions();

View file

@ -9,7 +9,6 @@ homepage = "https://github.com/qarmin/czkawka"
repository = "https://github.com/qarmin/czkawka"
[dependencies]
czkawka_core = { path = "../czkawka_core", version = "4.1.0"}
gdk4 = "0.4.7"
glib = "0.15.11"
@ -57,3 +56,11 @@ version = "0.4.7"
default-features = false # just in case
features = ["v4_6"]
[dependencies.czkawka_core]
path = "../czkawka_core"
version = "4.1.0"
features = []
[features]
default = []
heif = ["czkawka_core/heif"]

View file

@ -1,17 +1,24 @@
use std::cell::RefCell;
use std::rc::Rc;
#[cfg(feature = "heif")]
use czkawka_core::common::get_dynamic_image_from_heic;
use czkawka_core::common::HEIC_EXTENSIONS;
use gdk4::gdk_pixbuf::{InterpType, Pixbuf};
use gtk4::prelude::*;
use gtk4::{CheckButton, Image, ListStore, Orientation, ScrolledWindow, TreeIter, TreeModel, TreePath, TreeSelection, Widget};
use gtk4::{Align, CheckButton, Image, ListStore, Orientation, ScrolledWindow, TreeIter, TreeModel, TreePath, TreeSelection, Widget};
use image::DynamicImage;
use crate::flg;
use crate::gui_structs::gui_data::GuiData;
use crate::help_functions::{count_number_of_groups, get_all_children, get_full_name_from_path_name, get_max_file_name, resize_pixbuf_dimension, NotebookObject, NOTEBOOKS_INFOS};
use crate::help_functions::{
count_number_of_groups, get_all_children, get_full_name_from_path_name, get_max_file_name, get_pixbuf_from_dynamic_image, resize_pixbuf_dimension, NotebookObject,
NOTEBOOKS_INFOS,
};
use crate::localizer_core::generate_translation_hashmap;
const BIG_PREVIEW_SIZE: i32 = 600;
const SMALL_PREVIEW_SIZE: i32 = 100;
const SMALL_PREVIEW_SIZE: i32 = 130;
pub fn connect_button_compare(gui_data: &GuiData) {
let button_compare = gui_data.bottom_buttons.buttons_compare.clone();
@ -88,7 +95,6 @@ pub fn connect_button_compare(gui_data: &GuiData) {
let image_compare_left = gui_data.compare_images.image_compare_left.clone();
let image_compare_right = gui_data.compare_images.image_compare_right.clone();
window_compare.connect_close_request(move |window_compare| {
// TODO GTK4
window_compare.hide();
*shared_image_cache.borrow_mut() = Vec::new();
*shared_current_path.borrow_mut() = None;
@ -347,35 +353,85 @@ fn generate_cache_for_results(vector_with_path: Vec<(String, String, TreePath)>)
let small_img = Image::new();
let big_img = Image::new();
match Pixbuf::from_file(&full_path) {
Ok(pixbuf) =>
{
#[allow(clippy::never_loop)]
loop {
let pixbuf_big = match resize_pixbuf_dimension(pixbuf, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), InterpType::Nearest) {
None => {
println!("Failed to resize image {}.", full_path);
break;
}
Some(pixbuf) => pixbuf,
};
let pixbuf_small = match resize_pixbuf_dimension(pixbuf_big.clone(), (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), InterpType::Nearest) {
None => {
println!("Failed to resize image {}.", full_path);
break;
}
Some(pixbuf) => pixbuf,
};
let mut pixbuf = get_pixbuf_from_dynamic_image(&DynamicImage::new_rgb8(1, 1)).unwrap();
let name_lowercase = name.to_lowercase();
let is_heic = HEIC_EXTENSIONS.iter().any(|extension| name_lowercase.ends_with(extension));
let is_webp = name.to_lowercase().ends_with(".webp");
big_img.set_from_pixbuf(Some(&pixbuf_big));
small_img.set_from_pixbuf(Some(&pixbuf_small));
if is_heic || is_webp {
#[allow(clippy::never_loop)]
'czystka: loop {
#[cfg(feature = "heif")]
if is_heic {
match get_dynamic_image_from_heic(&full_path) {
Ok(t) => {
match get_pixbuf_from_dynamic_image(&t) {
Ok(t) => {
pixbuf = t;
}
Err(e) => {
println!("Failed to open image {}, reason {}", full_path, e);
}
};
}
Err(e) => {
println!("Failed to open image {}, reason {}", full_path, e);
}
};
break 'czystka;
}
if is_webp {
match image::open(&full_path) {
Ok(t) => {
match get_pixbuf_from_dynamic_image(&t) {
Ok(t) => {
pixbuf = t;
}
Err(e) => {
println!("Failed to open image {}, reason {}", full_path, e);
}
};
}
Err(e) => {
println!("Failed to open image {}, reason {}", full_path, e);
}
};
break 'czystka;
}
break 'czystka;
}
} else {
match Pixbuf::from_file(&full_path) {
Ok(t) => {
pixbuf = t;
}
Err(e) => {
println!("Failed to open image {}, reason {}", full_path, e);
}
};
}
#[allow(clippy::never_loop)]
loop {
let pixbuf_big = match resize_pixbuf_dimension(pixbuf, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), InterpType::Nearest) {
None => {
println!("Failed to resize image {}.", full_path);
break;
}
}
Err(e) => {
println!("Failed to open image {}, reason {}", full_path, e);
}
};
Some(pixbuf) => pixbuf,
};
let pixbuf_small = match resize_pixbuf_dimension(pixbuf_big.clone(), (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), InterpType::Nearest) {
None => {
println!("Failed to resize image {}.", full_path);
break;
}
Some(pixbuf) => pixbuf,
};
big_img.set_from_pixbuf(Some(&pixbuf_big));
small_img.set_from_pixbuf(Some(&pixbuf_small));
break;
}
cache_all_images.push((full_path, name, big_img, small_img, tree_path));
}
@ -474,10 +530,11 @@ fn populate_similar_scrolled_view(
column_selection: i32,
) {
scrolled_window.set_child(None::<&Widget>);
scrolled_window.set_propagate_natural_height(true);
let all_gtk_box = gtk4::Box::new(Orientation::Horizontal, 5);
all_gtk_box.set_widget_name("all_box");
all_gtk_box.set_halign(Align::Fill);
all_gtk_box.set_valign(Align::Fill);
for (number, (path, _name, big_thumbnail, small_thumbnail, tree_path)) in image_cache.iter().enumerate() {
let small_box = gtk4::Box::new(Orientation::Vertical, 3);
@ -534,6 +591,17 @@ fn populate_similar_scrolled_view(
smaller_box.append(&button_right);
small_box.append(&smaller_box);
small_box.set_halign(Align::Fill);
small_box.set_valign(Align::Fill);
small_box.set_hexpand_set(true);
small_box.set_vexpand_set(true);
small_thumbnail.set_halign(Align::Fill);
small_thumbnail.set_valign(Align::Fill);
small_thumbnail.set_hexpand(true);
small_thumbnail.set_hexpand_set(true);
small_thumbnail.set_vexpand(true);
small_thumbnail.set_vexpand_set(true);
small_box.append(small_thumbnail);
all_gtk_box.append(&small_box);

View file

@ -3,8 +3,12 @@ use std::collections::HashMap;
use std::path::PathBuf;
use gdk4::gdk_pixbuf::{InterpType, Pixbuf};
use glib::Error;
use gtk4::prelude::*;
use gtk4::{ListStore, TextView, TreeView, Widget};
use image::codecs::jpeg::JpegEncoder;
use image::{DynamicImage, EncodableLayout};
use once_cell::sync::OnceCell;
use czkawka_core::bad_extensions::BadExtensions;
use czkawka_core::big_file::BigFile;
@ -857,3 +861,16 @@ pub fn set_icon_of_button<P: IsA<Widget>>(button: &P, data: &'static [u8]) {
let pixbuf = pixbuf.scale_simple(SIZE_OF_ICON, SIZE_OF_ICON, TYPE_OF_INTERPOLATION).unwrap();
image.set_from_pixbuf(Some(&pixbuf));
}
static mut IMAGE_PREVIEW_ARRAY: OnceCell<Vec<u8>> = OnceCell::new();
pub fn get_pixbuf_from_dynamic_image(dynamic_image: &DynamicImage) -> Result<Pixbuf, Error> {
let mut output = Vec::new();
JpegEncoder::new(&mut output).encode_image(dynamic_image).unwrap();
let arra;
unsafe {
IMAGE_PREVIEW_ARRAY.take();
IMAGE_PREVIEW_ARRAY.set(output).unwrap();
arra = IMAGE_PREVIEW_ARRAY.get().unwrap().as_bytes();
}
Pixbuf::from_read(arra)
}

View file

@ -8,7 +8,9 @@ use gtk4::gdk_pixbuf::InterpType;
use gtk4::prelude::*;
use gtk4::{CheckButton, Image, SelectionMode, TextView, TreeView};
use czkawka_core::common::{IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS};
#[cfg(feature = "heif")]
use czkawka_core::common::get_dynamic_image_from_heic;
use czkawka_core::common::{HEIC_EXTENSIONS, IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS};
use czkawka_core::similar_images::SIMILAR_VALUES;
use czkawka_core::similar_videos::MAX_TOLERANCE;
@ -699,27 +701,85 @@ fn show_preview(
}
}
let is_heic;
let is_webp;
if let Some(extension) = Path::new(&name).extension() {
let extension = format!(".{}", extension.to_string_lossy().to_lowercase());
if !RAW_IMAGE_EXTENSIONS.contains(&extension.as_str()) && !IMAGE_RS_EXTENSIONS.contains(&extension.as_str()) {
is_heic = HEIC_EXTENSIONS.contains(&extension.as_str());
is_webp = ".webp" == extension;
if !RAW_IMAGE_EXTENSIONS.contains(&extension.as_str()) && !IMAGE_RS_EXTENSIONS.contains(&extension.as_str()) && !is_heic {
break 'dir;
}
} else {
break 'dir;
}
let mut pixbuf = if is_heic || is_webp {
let image = if is_heic {
#[cfg(feature = "heif")]
match get_dynamic_image_from_heic(file_name) {
Ok(t) => t,
Err(e) => {
add_text_to_text_view(
text_view_errors,
flg!(
"preview_image_opening_failure",
generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())])
)
.as_str(),
);
break 'dir;
}
}
let mut pixbuf = match Pixbuf::from_file(file_name) {
Ok(pixbuf) => pixbuf,
Err(e) => {
add_text_to_text_view(
text_view_errors,
flg!(
"preview_image_opening_failure",
generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())])
)
.as_str(),
);
break 'dir;
#[cfg(not(feature = "heif"))]
panic!("")
} else if is_webp {
match image::open(file_name) {
Ok(t) => t,
Err(e) => {
add_text_to_text_view(
text_view_errors,
flg!(
"preview_image_opening_failure",
generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())])
)
.as_str(),
);
break 'dir;
}
}
} else {
panic!("");
};
match get_pixbuf_from_dynamic_image(&image) {
Ok(t) => t,
Err(e) => {
add_text_to_text_view(
text_view_errors,
flg!(
"preview_image_opening_failure",
generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())])
)
.as_str(),
);
break 'dir;
}
}
} else {
match Pixbuf::from_file(file_name) {
Ok(pixbuf) => pixbuf,
Err(e) => {
add_text_to_text_view(
text_view_errors,
flg!(
"preview_image_opening_failure",
generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())])
)
.as_str(),
);
break 'dir;
}
}
};

View file

@ -11,14 +11,7 @@
<child>
<object class="GtkBox">
<child>
<object class="GtkLabel" id="label_group_info">
<property name="halign">center</property>
<property name="hexpand">1</property>
<property name="label" translatable="yes">Group XD/PER XD (99 images in current group)</property>
</object>
</child>
<child>
<object class="GtkButton" id="button_go_next_compare_group">
<object class="GtkButton" id="button_go_previous_compare_group">
<property name="focusable">1</property>
<property name="receives-default">1</property>
<child>
@ -29,7 +22,14 @@
</object>
</child>
<child>
<object class="GtkButton" id="button_go_previous_compare_group">
<object class="GtkLabel" id="label_group_info">
<property name="halign">center</property>
<property name="hexpand">1</property>
<property name="label" translatable="yes">Group XD/PER XD (99 images in current group)</property>
</object>
</child>
<child>
<object class="GtkButton" id="button_go_next_compare_group">
<property name="focusable">1</property>
<property name="receives-default">1</property>
<child>
@ -63,16 +63,22 @@
<property name="homogeneous">1</property>
<property name="vexpand">1</property>
<child>
<object class="GtkImage" id="image_compare_left"/>
<object class="GtkImage" id="image_compare_left">
<property name="height-request">100</property>
</object>
</child>
<child>
<object class="GtkImage" id="image_compare_right"/>
<object class="GtkImage" id="image_compare_right">
<property name="height-request">100</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolled_window_compare_choose_images">
<property name="focusable">1</property>
<property name="max-content-height">150</property>
<property name="min-content-height">150</property>
</object>
</child>
</object>

View file

@ -24,10 +24,10 @@
(4,1,"GtkDialog","window_compare",None,None,None,None,None),
(4,2,"GtkBox",None,1,None,None,None,None),
(4,3,"GtkBox",None,2,None,None,None,None),
(4,4,"GtkLabel","label_group_info",3,None,None,None,None),
(4,5,"GtkButton","button_go_next_compare_group",3,None,None,None,1),
(4,4,"GtkLabel","label_group_info",3,None,None,None,1),
(4,5,"GtkButton","button_go_next_compare_group",3,None,None,None,2),
(4,6,"GtkImage",None,5,None,None,None,None),
(4,7,"GtkButton","button_go_previous_compare_group",3,None,None,None,2),
(4,7,"GtkButton","button_go_previous_compare_group",3,None,None,None,None),
(4,8,"GtkImage",None,7,None,None,None,None),
(4,9,"GtkBox",None,2,None,None,None,1),
(4,10,"GtkCheckButton","check_button_left_preview_text",9,None,None,None,None),
@ -260,8 +260,6 @@
(8,26,"GtkLabel",None,24,None,None,None,1),
(9,1,"GtkDialog","window_settings",None,None,None,None,None),
(9,3,"GtkBox","potatoo",1,None,None,None,None),
(9,4,"GtkBox",None,3,None,None,None,None),
(9,5,"GtkComboBoxText",None,4,None,None,None,None),
(9,6,"GtkNotebook","notebook_settings",3,None,None,None,1),
(9,7,"GtkNotebookPage",None,6,None,None,None,None),
(9,8,"GtkBox",None,7,None,None,None,None),
@ -336,6 +334,10 @@
(4,11,"GtkWidget","focusable","1",None,None,None,None,None),
(4,12,"GtkBox","homogeneous","1",None,None,None,None,None),
(4,12,"GtkWidget","vexpand","1",None,None,None,None,None),
(4,13,"GtkWidget","height-request","100",None,None,None,None,None),
(4,14,"GtkWidget","height-request","100",None,None,None,None,None),
(4,15,"GtkScrolledWindow","max-content-height","150",None,None,None,None,None),
(4,15,"GtkScrolledWindow","min-content-height","150",None,None,None,None,None),
(4,15,"GtkWidget","focusable","1",None,None,None,None,None),
(5,1,"GtkAdjustment","page-increment","10",None,None,None,None,None),
(5,1,"GtkAdjustment","step-increment","1",None,None,None,None,None),
@ -803,8 +805,6 @@
(9,1,"GtkWindow","title","Czkawka Options",1,None,None,None,None),
(9,3,"GtkOrientable","orientation","vertical",None,None,None,None,None),
(9,3,"GtkWidget","vexpand","1",None,None,None,None,None),
(9,4,"GtkOrientable","orientation","vertical",None,None,None,None,None),
(9,5,"GtkWidget","valign","center",None,None,None,None,None),
(9,6,"GtkNotebook","tab-pos","left",None,None,None,None,None),
(9,6,"GtkWidget","focusable","1",None,None,None,None,None),
(9,6,"GtkWidget","vexpand","1",None,None,None,None,None),

View file

@ -10,16 +10,6 @@
<object class="GtkBox" id="potatoo">
<property name="orientation">vertical</property>
<property name="vexpand">1</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkComboBoxText">
<property name="valign">center</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkNotebook" id="notebook_settings">
<property name="focusable">1</property>

View file

@ -6,6 +6,8 @@ If you only want the terminal version without a GUI, just skip all the packages
FFmpeg is not included here because it is not needed to build - it is dynamically loaded.
Support for heif images is optional and require to install libheif library.
| Program | Min | What for |
|---------|------|-------------------------------------------------------------------------------|
@ -26,12 +28,12 @@ sudo yum install gtk3-devel glib2-devel
```
#### macOS
You need to install Rust via Homebrew and GTK Libraries
You need to install Rust via Homebrew, GTK Libraries and optionally heif library(to have support for heic files, which are quite popular on mac)
```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install rustup
rustup-init
brew install gtk+3 adwaita-icon-theme librsvg
brew install gtk+3 adwaita-icon-theme librsvg libheif
```
### Windows

View file

@ -29,7 +29,7 @@ One very straight-forward way to do this is by using [Homebrew](https://brew.sh/
Installation in the terminal:
```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install gtk+3 adwaita-icon-theme ffmpeg librsvg
brew install gtk+3 adwaita-icon-theme ffmpeg librsvg libheif
```
After that, go to the location where you downloaded Czkawka and add the `executable` permission to this file.
```shell
@ -43,7 +43,7 @@ At the end execute it:
**Warning**
Prebuilt binaries are available only for x86_64, so if you use ARM machine like e.g. Mac M1, you need to compile manually app or install special version of required libraries which can be done via this:
```shell
arch -x86_64 /usr/local/bin/brew install gtk+3 adwaita-icon-theme ffmpeg librsvg
arch -x86_64 /usr/local/bin/brew install gtk+3 adwaita-icon-theme ffmpeg librsvg libheif
```
### Windows

View file

@ -1,5 +1,5 @@
name: czkawka # you probably want to 'snapcraft register <name>'
base: core20 # the base snap is the execution environment for this snap
base: core22 # the base snap is the execution environment for this snap
version: '4.1.0' # just for humans, typically '1.2+git' or '1.3.2'
summary: Czkawka - fast data cleaner written in Rust # 79 char long summary
description: |