From fe43bc3a4c75347584072ebc59c57a3961dc30b3 Mon Sep 17 00:00:00 2001 From: Sophie Herold Date: Mon, 1 Jan 2024 22:41:56 +0100 Subject: [PATCH 1/2] metadata: Centralize many loose ends --- Cargo.lock | 1 + Cargo.toml | 1 + src/about.rs | 5 +- src/decoder/formats/glycin_proxy.rs | 23 +++---- src/decoder/formats/mod.rs | 7 --- src/decoder/formats/svg.rs | 25 +++----- src/decoder/mod.rs | 16 ++--- src/decoder/tiling.rs | 14 +---- src/metadata/format.rs | 51 --------------- src/metadata/mod.rs | 90 ++++++++++++++++++++------- src/util/gettext.rs | 8 +-- src/widgets/image.rs | 2 - src/widgets/image/background_color.rs | 8 +-- src/widgets/image/input_handling.rs | 51 ++++++++------- src/widgets/image/loading.rs | 11 +--- src/widgets/image/metadata.rs | 4 -- src/widgets/image/rendering.rs | 4 +- src/widgets/image/zoom.rs | 4 +- src/widgets/print.rs | 11 ++-- src/widgets/properties_view.rs | 36 +++++++++-- src/window.rs | 4 +- 21 files changed, 180 insertions(+), 196 deletions(-) delete mode 100644 src/metadata/format.rs diff --git a/Cargo.lock b/Cargo.lock index 8d70b867..a62cf1b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1488,6 +1488,7 @@ dependencies = [ "gettext-sys", "gio", "glycin", + "glycin-utils", "gtk4", "gvdb-macros", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index decb8dcb..d38519fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ env_logger = "0.10.0" futures-channel = "0.3.25" futures-lite = "2.1.0" glycin = "1.0.0-alpha" +glycin-utils = "1.0.0-alpha" gvdb-macros = "0.1.6" indexmap = "2.0.0" kamadak-exif = "0.5.5" diff --git a/src/about.rs b/src/about.rs index 69bc68cd..6365ac1c 100644 --- a/src/about.rs +++ b/src/about.rs @@ -1,5 +1,6 @@ +// Copyright (c) 2023-2024 Sophie Herold +// Copyright (c) 2023 Automeris naranja // Copyright (c) 2023 Christopher Davis -// Copyright (c) 2023 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -38,7 +39,7 @@ pub async fn window() -> adw::AboutWindow { .designers(["Allan Day", "Jakub Steiner", "Tobias Bernard"]) // Translators: Replace "translator-credits" with your names, one name per line .translator_credits(gettext("translator-credits")) - .copyright(gettext("Copyright © 2020–2023 Christopher Davis et al.")) + .copyright(gettext("Copyright © 2020–2024 Christopher Davis et al.")) .license_type(gtk::License::Gpl30) .debug_info(debug_info().await) .build() diff --git a/src/decoder/formats/glycin_proxy.rs b/src/decoder/formats/glycin_proxy.rs index ca6e8f1f..95f4da7a 100644 --- a/src/decoder/formats/glycin_proxy.rs +++ b/src/decoder/formats/glycin_proxy.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ use gtk::prelude::*; use super::*; use crate::decoder::tiling::{self, SharedFrameBuffer}; use crate::deps::*; -use crate::metadata::{ImageFormat, Metadata}; +use crate::metadata::Metadata; pub const FRAME_BUFFER: usize = 3; @@ -57,23 +57,20 @@ impl Glycin { let image = image_request.request().await?; - if let Some(exif_raw) = image.info().details.exif.clone() { - let mut metadata = Metadata::default(); - metadata.set_exif_bytes(exif_raw); - metadata.set_transformations_applied(image.info().details.transformations_applied); - updater.send(DecoderUpdate::Metadata(metadata)); - } + let mut metadata: Metadata = Metadata::default(); + metadata.set_image_info(image.info().details.clone()); + metadata.set_mime_type(image.mime_type()); + updater.send(DecoderUpdate::Metadata(Box::new(metadata))); let dimensions = (image.info().width, image.info().height); tiles.set_original_dimensions(dimensions); - updater.send(DecoderUpdate::Format(ImageFormat::new( - image.mime_type(), - image.format_name(), - ))); - let frame = image.next_frame().await?; + let mut metadata: Metadata = Metadata::default(); + metadata.set_frame_info(frame.details); + updater.send(DecoderUpdate::Metadata(Box::new(metadata))); + if let Some(delay) = frame.delay { updater.send(DecoderUpdate::Animated); diff --git a/src/decoder/formats/mod.rs b/src/decoder/formats/mod.rs index 3541848f..1a7c2ea9 100644 --- a/src/decoder/formats/mod.rs +++ b/src/decoder/formats/mod.rs @@ -22,10 +22,3 @@ pub use glycin_proxy::Glycin; pub use svg::{Svg, RSVG_MAX_SIZE}; use super::{DecoderUpdate, UpdateSender}; - -#[derive(Clone, Debug, Default)] -pub enum ImageDimensionDetails { - Svg(String, Option<(f64, f64)>), - #[default] - None, -} diff --git a/src/decoder/formats/svg.rs b/src/decoder/formats/svg.rs index 4539652e..1674e244 100644 --- a/src/decoder/formats/svg.rs +++ b/src/decoder/formats/svg.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ use super::*; use crate::decoder::tiling::SharedFrameBuffer; use crate::decoder::TileRequest; use crate::deps::*; -use crate::metadata::ImageFormat; +use crate::metadata::Metadata; /// Current librsvg limit on maximum dimensions. See /// @@ -78,21 +78,12 @@ impl Svg { let image = image_request.request().await?; - let dimensions = if let Some(string) = image.info().details.dimensions_text.as_ref() { - ImageDimensionDetails::Svg(string.to_string(), image.info().details.dimensions_inch) - } else { - ImageDimensionDetails::None - }; - - tiles.set_original_dimensions_full( - (image.info().width, image.info().height), - dimensions, - ); - - updater.send(DecoderUpdate::Format(ImageFormat::new( - image.mime_type(), - image.format_name(), - ))); + let mut metadata: Metadata = Metadata::default(); + metadata.set_image_info(image.info().details.clone()); + metadata.set_mime_type(image.mime_type()); + updater.send(DecoderUpdate::Metadata(Box::new(metadata))); + + tiles.set_original_dimensions((image.info().width, image.info().height)); loop { let tile_request = { diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index b41fa479..740bf6a0 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // Copyright (c) 2023 Julian Hofer // // This program is free software: you can redistribute it and/or modify @@ -24,13 +24,13 @@ pub mod tiling; use std::sync::Arc; use anyhow::Result; +pub use formats::RSVG_MAX_SIZE; use formats::*; -pub use formats::{ImageDimensionDetails, RSVG_MAX_SIZE}; use futures_channel::mpsc; use self::tiling::SharedFrameBuffer; use crate::deps::*; -use crate::metadata::{ImageFormat, Metadata}; +use crate::metadata::Metadata; #[derive(Clone, Copy, Debug)] /// Renderer requests new tiles @@ -52,11 +52,9 @@ pub struct UpdateSender { /// Signals for renderer (LpImage) pub enum DecoderUpdate { /// Dimensions of image in `TilingSore` available/updated - Dimensions(ImageDimensionDetails), + Dimensions, /// Metadata available - Metadata(Metadata), - /// Image format determined - Format(ImageFormat), + Metadata(Box), /// Image format not supported or unknown UnsupportedFormat, /// New image data available, redraw @@ -144,10 +142,6 @@ impl Decoder { // - image/svg+xml // - image/svg+xml-compressed if mime_type.split('+').next() == Some("image/svg") { - update_sender.send(DecoderUpdate::Format(ImageFormat::new( - mime_type, - Some("SVG".into()), - ))); return FormatDecoder::Svg(Svg::new(file, update_sender, tiles)); } } diff --git a/src/decoder/tiling.rs b/src/decoder/tiling.rs index ee310635..9656268b 100644 --- a/src/decoder/tiling.rs +++ b/src/decoder/tiling.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // Copyright (c) 2023 FineFindus // // This program is free software: you can redistribute it and/or modify @@ -33,7 +33,7 @@ use std::time::Duration; use arc_swap::ArcSwap; use gtk::prelude::*; -use super::{DecoderUpdate, ImageDimensionDetails, UpdateSender}; +use super::{DecoderUpdate, UpdateSender}; use crate::deps::*; const ZOOM_SIGNIFICANT_DIGITS: i32 = 6; @@ -676,14 +676,6 @@ impl SharedFrameBuffer { } pub fn set_original_dimensions(&self, size: Coordinates) { - self.set_original_dimensions_full(size, Default::default()); - } - - pub fn set_original_dimensions_full( - &self, - size: Coordinates, - dimension_details: ImageDimensionDetails, - ) { self.buffer.rcu(|tiling_store| { let mut new_store = (**tiling_store).clone(); new_store.current().original_dimensions = Some(size); @@ -691,7 +683,7 @@ impl SharedFrameBuffer { }); if let Some(updater) = &self.buffer.load().update_sender { - updater.send(DecoderUpdate::Dimensions(dimension_details)); + updater.send(DecoderUpdate::Dimensions); } } diff --git a/src/metadata/format.rs b/src/metadata/format.rs deleted file mode 100644 index 79e0349f..00000000 --- a/src/metadata/format.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2023 Sophie Herold -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// SPDX-License-Identifier: GPL-3.0-or-later - -use crate::util::gettext::*; - -#[derive(Clone, Debug)] -pub struct ImageFormat { - mime_type: glycin::MimeType, - name: Option, -} - -impl ImageFormat { - pub fn new(mime_type: glycin::MimeType, name: Option) -> Self { - Self { mime_type, name } - } - - pub fn is_svg(&self) -> bool { - matches!( - self.mime_type.as_str(), - "image/svg+xml" | "image/svg+xml-compressed" - ) - } - - pub fn is_potentially_transparent(&self) -> bool { - !matches!(self.mime_type.as_str(), "image/bmp" | "image/jpeg") - } -} - -impl std::fmt::Display for ImageFormat { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!( - f, - "{}", - self.name.clone().unwrap_or_else(|| gettext("Unknown")) - ) - } -} diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index 6eb323b2..913bd933 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Sophie Herold +// Copyright (c) 2022-2024 Sophie Herold // Copyright (c) 2023 FineFindus // // This program is free software: you can redistribute it and/or modify @@ -22,11 +22,10 @@ */ mod file; -mod format; mod gps; pub use file::FileInfo; -pub use format::ImageFormat; +use glycin_utils::{FrameDetails, ImageInfoDetails}; pub use gps::GPSLocation; use crate::deps::*; @@ -35,10 +34,12 @@ use crate::util::gettext::*; #[derive(Default)] pub struct Metadata { + mime_type: Option, exif: Option, file_info: Option, - format: Option, - transformations_applied: Option, + // TODO: Replace with glycin in newer glycin version + image_info: Option, + frame_info: Option, } impl std::fmt::Debug for Metadata { @@ -62,7 +63,7 @@ impl std::fmt::Debug for Metadata { } impl Metadata { - pub fn set_exif_bytes(&mut self, bytes: Vec) { + fn set_exif_bytes(&mut self, bytes: Vec) { let reader = exif::Reader::new(); let exif = reader.read_raw(bytes); @@ -73,16 +74,23 @@ impl Metadata { self.exif = exif.ok(); } - pub fn set_transformations_applied(&mut self, applied: bool) { - self.transformations_applied = Some(applied); + pub fn set_image_info(&mut self, image_info: ImageInfoDetails) { + if let Some(exif_raw) = image_info.exif.as_ref() { + self.set_exif_bytes(exif_raw.clone()); + } + self.image_info = Some(image_info); + } + + pub fn set_frame_info(&mut self, frame_info: FrameDetails) { + self.frame_info = Some(frame_info); } pub fn set_file_info(&mut self, file_info: FileInfo) { self.file_info = Some(file_info); } - pub fn set_format(&mut self, format: ImageFormat) { - self.format = Some(format); + pub fn set_mime_type(&mut self, mime_type: String) { + self.mime_type = Some(mime_type); } pub fn merge(&mut self, other: Self) { @@ -92,8 +100,14 @@ impl Metadata { if self.file_info.is_none() { self.file_info = other.file_info; } - if self.transformations_applied.is_none() { - self.transformations_applied = other.transformations_applied; + if self.image_info.is_none() { + self.image_info = other.image_info; + } + if self.mime_type.is_none() { + self.mime_type = other.mime_type; + } + if self.frame_info.is_none() { + self.frame_info = other.frame_info; } } @@ -104,18 +118,42 @@ impl Metadata { .map(|x| x.to_string()) } - pub fn format(&self) -> Option<&ImageFormat> { - self.format.as_ref() - } - pub fn format_name(&self) -> Option { - self.format() - .map(|x| x.to_string()) + self.image_info + .as_ref() + .and_then(|x| x.format_name.clone()) .or_else(|| self.mime_type()) } + pub fn alpha_channel(&self) -> Option { + self.frame_info.as_ref().and_then(|x| x.alpha_channel) + } + + pub fn grayscale(&self) -> Option { + self.frame_info.as_ref().and_then(|x| x.grayscale) + } + + pub fn bit_depth(&self) -> Option { + self.frame_info.as_ref().and_then(|x| x.bit_depth) + } + + pub fn is_svg(&self) -> bool { + matches!( + self.mime_type.as_deref(), + Some("image/svg+xml") | Some("image/svg+xml-compressed") + ) + } + + pub fn is_potentially_transparent(&self) -> bool { + // TODO: Implement again + true + } + pub fn transformations_applied(&self) -> bool { - self.transformations_applied.unwrap_or(false) + self.image_info + .as_ref() + .map(|x| x.transformations_applied) + .unwrap_or(false) } pub fn orientation(&self) -> Orientation { @@ -136,6 +174,16 @@ impl Metadata { } } + pub fn dimensions_inch(&self) -> Option<(f64, f64)> { + self.image_info.as_ref().and_then(|x| x.dimensions_inch) + } + + pub fn dimensions_text(&self) -> Option { + self.image_info + .as_ref() + .and_then(|x| x.dimensions_text.clone()) + } + pub fn file_name(&self) -> Option { self.file_info.as_ref().map(|x| x.display_name.to_string()) } @@ -210,7 +258,7 @@ impl Metadata { let exposure = format!("{:.0}", 1. / rational.first()?.to_f32()); // Translators: Unit for exposure time in seconds - return Some(gettext_f("1\u{2215}{}\u{202F}s", &[&exposure])); + return Some(gettext_f("1\u{2215}{}\u{202F}s", [exposure])); } } @@ -235,7 +283,7 @@ impl Metadata { if let exif::Value::Rational(rational) = &field.value { let length = format!("{:.0}", rational.first()?.to_f32()); // Translators: Unit for focal length in millimeters - return Some(gettext_f("{}\u{202F}mm", &[&length])); + return Some(gettext_f("{}\u{202F}mm", [length])); } } diff --git a/src/util/gettext.rs b/src/util/gettext.rs index a040e3db..110a25ce 100644 --- a/src/util/gettext.rs +++ b/src/util/gettext.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,15 +17,15 @@ pub use gettextrs::gettext; -fn freplace(mut s: String, args: &[&str]) -> String { +fn freplace(mut s: String, args: impl IntoIterator>) -> String { for arg in args { - s = s.replacen("{}", arg, 1); + s = s.replacen("{}", arg.as_ref(), 1); } s } -pub fn gettext_f(format: &str, args: &[&str]) -> String { +pub fn gettext_f(format: &str, args: impl IntoIterator>) -> String { let s = gettextrs::gettext(format); freplace(s, args) } diff --git a/src/widgets/image.rs b/src/widgets/image.rs index 9ee928d4..3b2b6239 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -198,8 +198,6 @@ mod imp { /// Currently EXIF data pub(super) metadata: RefCell, - /// Image dimension details for SVGs - pub(super) dimension_details: RefCell, #[property(get)] _image_size_available: bool, diff --git a/src/widgets/image/background_color.rs b/src/widgets/image/background_color.rs index d645b85b..18e6e3be 100644 --- a/src/widgets/image/background_color.rs +++ b/src/widgets/image/background_color.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -62,11 +62,7 @@ impl imp::LpImage { } // Shortcut for formats that don't support transparency - if !obj - .metadata() - .format() - .map_or(true, |x| x.is_potentially_transparent()) - { + if !obj.metadata().is_potentially_transparent() { log::debug!("This format does not support transparency"); return Some(Self::default_background_color()); } diff --git a/src/widgets/image/input_handling.rs b/src/widgets/image/input_handling.rs index 5d8094a8..bde5588f 100644 --- a/src/widgets/image/input_handling.rs +++ b/src/widgets/image/input_handling.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -45,41 +45,48 @@ impl imp::LpImage { gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL); scroll_controller.connect_scroll(glib::clone!(@weak obj => @default-return glib::Propagation::Proceed, move |event, _, y| { - // use Ctrl key as modifier for vertical scrolling - if event.current_event_state().contains(gdk::ModifierType::CONTROL_MASK) - || event.current_event_state().contains(gdk::ModifierType::SHIFT_MASK) - { - // propagate event to scrolled window - return glib::Propagation::Proceed; - } + let state = event.current_event_state(); - // touchpads do zoom via gestures only - if event.current_event_device().map(|x| x.source()) - == Some(gdk::InputSource::Touchpad) + if event.current_event_device().map(|x| x.source()) == Some(gdk::InputSource::Touchpad) { - // propagate event to scrolled window - return glib::Propagation::Proceed; + // Touchpads do zoom via gestures, expect when Ctrl key is pressed + if !state.contains(gdk::ModifierType::CONTROL_MASK) { + // propagate event to scrolled window + return glib::Propagation::Proceed; + } + } else { + // use Ctrl key as modifier for vertical scrolling + if state.contains(gdk::ModifierType::CONTROL_MASK) + || state.contains(gdk::ModifierType::SHIFT_MASK) + { + // propagate event to scrolled window + return glib::Propagation::Proceed; + } } // Use exponential scaling since zoom is always multiplicative with the existing value // This is the right thing since `exp(n/2)^2 == exp(n)` (two small steps are the same as one larger step) let (zoom_factor, animated) = match event.unit() { - gdk::ScrollUnit::Wheel => (f64::exp( - y * f64::ln(ZOOM_FACTOR_SCROLL_WHEEL)), y.abs() >= 1.), - gdk::ScrollUnit::Surface => (f64::exp( - y * f64::ln(ZOOM_FACTOR_SCROLL_SURFACE)), false), + gdk::ScrollUnit::Wheel => ( + f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_WHEEL)), + y.abs() >= 1., + ), + gdk::ScrollUnit::Surface => { + (f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_SURFACE)), false) + } unknown_unit => { log::warn!("Ignoring unknown scroll unit: {unknown_unit}"); (1., false) } }; - let zoom = - obj.imp().zoom_target.get() * zoom_factor; + let zoom = obj.imp().zoom_target.get() * zoom_factor; - if animated { - obj.zoom_to(zoom); - } else { - obj.imp().zoom_to_full(zoom, false, false); - } + if animated { + obj.zoom_to(zoom); + } else { + obj.imp().zoom_to_full(zoom, false, false); + } // do not propagate event to scrolled window glib::Propagation::Stop diff --git a/src/widgets/image/loading.rs b/src/widgets/image/loading.rs index e7abd28e..0a73dcf1 100644 --- a/src/widgets/image/loading.rs +++ b/src/widgets/image/loading.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -68,14 +68,13 @@ impl imp::LpImage { match update { DecoderUpdate::Metadata(metadata) => { log::debug!("Received metadata"); - self.metadata.borrow_mut().merge(metadata); + self.metadata.borrow_mut().merge(*metadata); self.emmit_metadata_changed(); obj.reset_rotation(); } - DecoderUpdate::Dimensions(dimension_details) => { + DecoderUpdate::Dimensions => { log::debug!("Received dimensions: {:?}", self.original_dimensions()); - self.dimension_details.replace(dimension_details); obj.notify_image_size_available(); self.configure_best_fit(); self.request_tiles(); @@ -105,10 +104,6 @@ impl imp::LpImage { DecoderUpdate::Error(err) => { self.set_error(Some(err)); } - DecoderUpdate::Format(format) => { - self.metadata.borrow_mut().set_format(format); - self.emmit_metadata_changed(); - } DecoderUpdate::Animated => { if self.still.get() { // Just load the first frame diff --git a/src/widgets/image/metadata.rs b/src/widgets/image/metadata.rs index 6c9afc4d..f1ecd32b 100644 --- a/src/widgets/image/metadata.rs +++ b/src/widgets/image/metadata.rs @@ -83,10 +83,6 @@ impl imp::LpImage { } impl LpImage { - pub fn dimension_details(&self) -> decoder::ImageDimensionDetails { - self.imp().dimension_details.borrow().clone() - } - pub fn file(&self) -> Option { self.imp().file.borrow().clone() } diff --git a/src/widgets/image/rendering.rs b/src/widgets/image/rendering.rs index 0bef767a..ac3d8a1a 100644 --- a/src/widgets/image/rendering.rs +++ b/src/widgets/image/rendering.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -65,7 +65,7 @@ impl WidgetImpl for imp::LpImage { let applicable_zoom = self.applicable_zoom(); - let scaling_filter = if obj.metadata().format().map_or(false, |x| x.is_svg()) { + let scaling_filter = if obj.metadata().is_svg() { // Looks better in SVG animations and avoids rendering issues gsk::ScalingFilter::Linear } else if applicable_zoom < 1. { diff --git a/src/widgets/image/zoom.rs b/src/widgets/image/zoom.rs index 77e98a3c..b1a254ec 100644 --- a/src/widgets/image/zoom.rs +++ b/src/widgets/image/zoom.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -88,7 +88,7 @@ impl imp::LpImage { pub(super) fn max_zoom(&self) -> f64 { let obj = self.obj(); - if obj.metadata().format().map_or(false, |x| x.is_svg()) { + if obj.metadata().is_svg() { let (width, height) = self.original_dimensions(); // Avoid division by 0 let long_side = f64::max(1., i32::max(width, height) as f64); diff --git a/src/widgets/print.rs b/src/widgets/print.rs index 6d3ddf66..67ee7b7b 100644 --- a/src/widgets/print.rs +++ b/src/widgets/print.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Sophie Herold +// Copyright (c) 2023-2024 Sophie Herold // Copyright (c) 2023 Julian Hofer // // This program is free software: you can redistribute it and/or modify @@ -27,7 +27,7 @@ use glib::Properties; use gtk::CompositeTemplate; use once_cell::sync::OnceCell; -use crate::decoder::{self, ImageDimensionDetails}; +use crate::decoder::{self}; use crate::deps::*; use crate::util::gettext::*; use crate::widgets::{LpImage, LpPrintPreview}; @@ -370,7 +370,7 @@ mod imp { imp.title.set_title(&gettext_f( // Translators: {} is a placeholder for the filename "Print “{}”", - &[&basename], + [basename], )); if let Some(printer) = operation.print_settings().and_then(|x| x.printer()) { imp.title.set_subtitle(&printer); @@ -514,7 +514,8 @@ impl LpPrint { pub fn original_size(&self) -> (f64, f64) { let image = self.image(); - if let ImageDimensionDetails::Svg(_, Some((w, h))) = image.dimension_details() { + let metadata = image.metadata(); + if let Some((w, h)) = metadata.dimensions_inch() { let dpi = self.dpi(); ((w * dpi).round(), (h * dpi).round()) @@ -897,7 +898,7 @@ impl LpPrint { let texture_scale = self.user_width() / orig_width; let cairo_scale = cairo_dpi / self.dpi(); - let texture = if image.metadata().format().map_or(false, |x| x.is_svg()) { + let texture = if image.metadata().is_svg() { // Render SVG to exact needed sizes // TODO: This should be async decoder::formats::Svg::render_print( diff --git a/src/widgets/properties_view.rs b/src/widgets/properties_view.rs index ff3ced2e..48076971 100644 --- a/src/widgets/properties_view.rs +++ b/src/widgets/properties_view.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Sophie Herold +// Copyright (c) 2022-2024 Sophie Herold // Copyright (c) 2022 Christopher Davis // // This program is free software: you can redistribute it and/or modify @@ -23,9 +23,9 @@ use adw::subclass::prelude::*; use glib::Properties; use gtk::CompositeTemplate; -use crate::decoder::ImageDimensionDetails; use crate::deps::*; -use crate::util; +use crate::util::gettext::*; +use crate::util::{self}; use crate::widgets::image::LpImage; const FALLBACK: &str = "–"; @@ -177,7 +177,7 @@ mod imp { // Image info Self::update_row(&self.image_size, self.image_size()); - Self::update_row(&self.image_format, metadata.format_name()); + Self::update_row(&self.image_format, self.format_name()); Self::update_row(&self.file_size, metadata.file_size()); // Dates @@ -222,12 +222,36 @@ mod imp { .and_then(|p| util::get_file_display_name(&p)) } + fn format_name(&self) -> Option { + let image = self.image.borrow(); + let metadata = image.as_ref()?.metadata(); + + let mut format = metadata.format_name().unwrap_or(gettext("Unknown")); + + if metadata.alpha_channel().unwrap_or(false) { + // Translators: Addition of image being transparent to format name + format.push_str(&gettext(", transparent")); + } + + if metadata.grayscale().unwrap_or(false) { + // Translators: Addition of image being grayscale to format name + format.push_str(&gettext(", grayscale")); + } + + if let Some(bit_depth) = metadata.bit_depth() { + // Translators: Addition of bit size to format name + format.push_str(&gettext_f(", {}\u{202F}bit", [bit_depth.to_string()])); + } + + Some(format) + } + fn image_size(&self) -> Option { let obj = self.obj(); if let Some(image) = obj.image() { - match image.dimension_details() { - ImageDimensionDetails::Svg(string, _) => Some(string), + match image.metadata().dimensions_text() { + Some(string) => Some(string.to_string()), _ => { let (width, height) = image.image_size(); if width > 0 && height > 0 { diff --git a/src/window.rs b/src/window.rs index 8c5fda74..74c33261 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,5 +1,5 @@ // Copyright (c) 2020-2023 Christopher Davis -// Copyright (c) 2022-2023 Sophie Herold +// Copyright (c) 2022-2024 Sophie Herold // Copyright (c) 2022 Elton A Rodrigues // Copyright (c) 2022 Maximiliano Sandoval R // Copyright (c) 2023 Matteo Nardi @@ -684,7 +684,7 @@ impl LpWindow { .heading(gettext("Permanently Delete Image?")) .body(gettext_f( "The image “{}” can only be deleted permanently.", - &[&PathBuf::from(&path.file_name().unwrap_or_default()) + [PathBuf::from(&path.file_name().unwrap_or_default()) .display() .to_string()], )) -- GitLab From bee10be168639a082576e8a6eea34d40a364c680 Mon Sep 17 00:00:00 2001 From: Sophie Herold Date: Mon, 1 Jan 2024 23:06:52 +0100 Subject: [PATCH 2/2] flatpak: Update glycin loaders --- build-aux/flatpak/org.gnome.Loupe.Devel.json | 2 +- po/POTFILES.in | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build-aux/flatpak/org.gnome.Loupe.Devel.json b/build-aux/flatpak/org.gnome.Loupe.Devel.json index c04dfb5c..463ce700 100644 --- a/build-aux/flatpak/org.gnome.Loupe.Devel.json +++ b/build-aux/flatpak/org.gnome.Loupe.Devel.json @@ -156,7 +156,7 @@ { "type": "git", "url": "https://gitlab.gnome.org/sophie-h/glycin.git", - "commit": "2775373f373056ba79f5c3e0afd256815ad0dc32" + "commit": "5abe5c2970806885ccf082d8a7d01ed99c563d70" } ] }, diff --git a/po/POTFILES.in b/po/POTFILES.in index 27fe3054..93c8704f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,7 +12,6 @@ src/decoder/tiling.rs src/file_model.rs src/main.rs src/metadata/file.rs -src/metadata/format.rs src/metadata/gps.rs src/metadata/mod.rs src/util/gettext.rs -- GitLab