feat(wallpaper): apply EXIF orientation to wallpapers (#2075)
Same fix as pop-os/cosmic-bg#142, which was asked for there. `open_image` has two decoding paths, and neither applies the Exif orientation: `ImageReader::decode` for the common formats, and `DynamicImage::from_decoder` in `decode_jpegxl`. Cameras store portrait photos with the pixels laid out landscape and record the rotation in the Exif metadata, so such a photo is shown rotated in the wallpaper page, both in the preview and in the thumbnails. Both paths now read the orientation from the decoder and apply it to the decoded image. `into_decoder` does not check the allocation limit against the size of the decoded image the way `decode` does, so that check is kept explicitly. Tested with the 8 Exif orientation values, with images that carry no Exif metadata (unchanged), and with the JPEG XL wallpapers from gnome-backgrounds, which decode identically. cosmic-greeter needs a different change: it does not decode images itself. It passes the raw file bytes to `widget::image::Handle::from_bytes` (src/common.rs), and those are decoded by `iced_graphics` with `image::load_from_memory`, which does not apply the orientation either. So the orientation has to be applied before the handle is built, or in the toolkit. - [x] I have disclosed use of any AI generated code in my commit messages. - [x] I understand these changes in full and will be able to respond to review comments. - [x] My change is accurately described in the commit message. - [x] My contribution is tested and working as described. - [x] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions. Signed-off-by: Leandro Pérez G. <leandropatodo@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
044fc11b2f
commit
65d0f0c7c8
1 changed files with 31 additions and 5 deletions
|
|
@ -4,7 +4,7 @@ use fast_image_resize::SrcCropping;
|
|||
use futures_lite::Stream;
|
||||
use futures_util::StreamExt;
|
||||
use image::imageops::FilterType;
|
||||
use image::{DynamicImage, ImageBuffer, Rgba, RgbaImage};
|
||||
use image::{DynamicImage, ImageBuffer, ImageDecoder, ImageResult, Limits, Rgba, RgbaImage};
|
||||
use jxl_oxide::integration::JxlDecoder;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -304,7 +304,7 @@ fn open_image(input_buffer: &mut Vec<u8>, path: &Path) -> Option<DynamicImage> {
|
|||
return None;
|
||||
};
|
||||
|
||||
match image_decoder.decode() {
|
||||
match decode(image_decoder) {
|
||||
Ok(image) => Some(image),
|
||||
Err(why) => {
|
||||
tracing::error!(?path, ?why, "image decode failed");
|
||||
|
|
@ -313,6 +313,24 @@ fn open_image(input_buffer: &mut Vec<u8>, path: &Path) -> Option<DynamicImage> {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode(
|
||||
mut reader: image::ImageReader<std::io::Cursor<&mut Vec<u8>>>,
|
||||
) -> ImageResult<DynamicImage> {
|
||||
let mut limits = Limits::default();
|
||||
reader.limits(limits.clone());
|
||||
|
||||
let mut decoder = reader.into_decoder()?;
|
||||
let orientation = decoder.orientation()?;
|
||||
|
||||
limits.reserve(decoder.total_bytes())?;
|
||||
decoder.set_limits(limits)?;
|
||||
|
||||
let mut image = DynamicImage::from_decoder(decoder)?;
|
||||
image.apply_orientation(orientation);
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
// https://users.rust-lang.org/t/how-to-trim-image-to-circle-image-without-jaggy/70374/2
|
||||
fn round(img: &mut image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, radius: [u32; 4]) {
|
||||
let (width, height) = img.dimensions();
|
||||
|
|
@ -434,11 +452,19 @@ fn border_radius(
|
|||
pub fn decode_jpegxl(path: &std::path::Path) -> eyre::Result<DynamicImage> {
|
||||
let file = File::open(path).map_err(|why| eyre!("failed to open jxl image file: {why}"))?;
|
||||
|
||||
let decoder =
|
||||
let mut decoder =
|
||||
JxlDecoder::new(file).map_err(|why| eyre!("failed to read jxl image header: {why}"))?;
|
||||
|
||||
image::DynamicImage::from_decoder(decoder)
|
||||
.map_err(|why| eyre!("failed to decode jxl image: {why}"))
|
||||
let orientation = decoder
|
||||
.orientation()
|
||||
.map_err(|why| eyre!("failed to read jxl image orientation: {why}"))?;
|
||||
|
||||
let mut image = image::DynamicImage::from_decoder(decoder)
|
||||
.map_err(|why| eyre!("failed to decode jxl image: {why}"))?;
|
||||
|
||||
image.apply_orientation(orientation);
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
/// Use `fast-image-resize` crate for faster thumbnail generation.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue