decoder: Add basic color profile support
This only covers 8bit RGB(A) JPEG, PNG, and HEIF. Should only be a temporary solution until GTK supports color profiles. Closes #36 Part-of: <https://gitlab.gnome.org/Incubator/loupe/-/merge_requests/172>
This commit is contained in:
parent
3b6cc8c404
commit
e710419a22
6 changed files with 186 additions and 23 deletions
|
|
@ -2,14 +2,16 @@
|
|||
use super::*;
|
||||
use crate::decoder::tiling::{self, FrameBufferExt};
|
||||
use crate::deps::*;
|
||||
use crate::util;
|
||||
use crate::util::gettext::*;
|
||||
use crate::util::ToBufRead;
|
||||
|
||||
use anyhow::Context;
|
||||
use arc_swap::ArcSwap;
|
||||
use gtk::prelude::*;
|
||||
use libheif_rs::{ColorSpace, HeifContext, LibHeif, Plane, RgbChroma};
|
||||
use libheif_rs::{ColorProfile, ColorSpace, HeifContext, LibHeif, Plane, RgbChroma};
|
||||
use once_cell::sync::Lazy;
|
||||
use rgb::AsPixels;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -73,6 +75,21 @@ impl Heif {
|
|||
.decode(&handle, ColorSpace::Rgb(rgb_chroma), None)
|
||||
.context(gettext("Failed to decode image"))?;
|
||||
|
||||
let icc_profile = if let Some(profile) = handle.color_profile_raw() {
|
||||
if [
|
||||
libheif_rs::color_profile_types::R_ICC,
|
||||
libheif_rs::color_profile_types::PROF,
|
||||
]
|
||||
.contains(&profile.profile_type())
|
||||
{
|
||||
Some(profile.data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let plane = image
|
||||
.planes_mut()
|
||||
.interleaved
|
||||
|
|
@ -83,6 +100,7 @@ impl Heif {
|
|||
has_alpha_channel: handle.has_alpha_channel(),
|
||||
pre_multiplied_alpha: handle.is_premultiplied_alpha(),
|
||||
rgb_chroma,
|
||||
icc_profile,
|
||||
};
|
||||
|
||||
let tile = tiling::Tile {
|
||||
|
|
@ -106,6 +124,7 @@ pub struct Decoded<'a> {
|
|||
has_alpha_channel: bool,
|
||||
pre_multiplied_alpha: bool,
|
||||
rgb_chroma: RgbChroma,
|
||||
icc_profile: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'a> Decoded<'a> {
|
||||
|
|
@ -150,7 +169,19 @@ impl<'a> Decoded<'a> {
|
|||
RgbChroma::C444 => unreachable!(),
|
||||
};
|
||||
|
||||
let bytes = glib::Bytes::from_owned(self.plane.data.to_vec());
|
||||
let mut buffer = self.plane.data.to_vec();
|
||||
|
||||
if let Some(icc_profile) = &self.icc_profile {
|
||||
if memory_format == gdk::MemoryFormat::R8g8b8 {
|
||||
util::new_trafo::<rgb::RGB8>(icc_profile, lcms2::PixelFormat::RGB_8)?
|
||||
.transform_in_place(buffer.as_pixels_mut());
|
||||
} else if memory_format == gdk::MemoryFormat::R8g8b8a8 {
|
||||
util::new_trafo::<rgb::RGBA8>(icc_profile, lcms2::PixelFormat::RGBA_8)?
|
||||
.transform_in_place(buffer.as_pixels_mut());
|
||||
}
|
||||
}
|
||||
|
||||
let bytes = glib::Bytes::from_owned(buffer);
|
||||
|
||||
let tex = gdk::MemoryTexture::new(
|
||||
self.plane.width as i32,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
use super::*;
|
||||
use crate::decoder::tiling::{self, FrameBufferExt};
|
||||
use crate::deps::*;
|
||||
use crate::util;
|
||||
use crate::util::gettext::*;
|
||||
use crate::util::{BufReadSeek, ToBufRead};
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use arc_swap::ArcSwap;
|
||||
use gtk::prelude::*;
|
||||
use image_rs::AnimationDecoder;
|
||||
use image_rs::{AnimationDecoder, ImageDecoder};
|
||||
use rgb::AsPixels;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -87,13 +89,38 @@ impl ImageRsOther {
|
|||
|
||||
tiles.set_original_dimensions(dimensions);
|
||||
|
||||
let mut reader = Self::reader(&file, format)?;
|
||||
let (icc_profile, dynamic_image) = match format {
|
||||
image_rs::ImageFormat::Jpeg => {
|
||||
let reader = file.to_buf_read()?;
|
||||
let mut decoder = image_rs::codecs::jpeg::JpegDecoder::new(reader)?;
|
||||
let _ = decoder.set_limits(image_rs::io::Limits::no_limits());
|
||||
let icc_profile = decoder.icc_profile();
|
||||
let dynamic_image = image_rs::DynamicImage::from_decoder(decoder)
|
||||
.context(gettext("Failed to decode image"))?;
|
||||
(icc_profile, dynamic_image)
|
||||
}
|
||||
image_rs::ImageFormat::Png => {
|
||||
let reader = file.to_buf_read()?;
|
||||
let mut decoder = image_rs::codecs::png::PngDecoder::new(reader)?;
|
||||
let _ = decoder.set_limits(image_rs::io::Limits::no_limits());
|
||||
let icc_profile = decoder.icc_profile();
|
||||
let dynamic_image = image_rs::DynamicImage::from_decoder(decoder)
|
||||
.context(gettext("Failed to decode image"))?;
|
||||
(icc_profile, dynamic_image)
|
||||
}
|
||||
_ => {
|
||||
let mut reader = Self::reader(&file, format)?;
|
||||
reader.limits(image_rs::io::Limits::no_limits());
|
||||
let dynamic_image =
|
||||
reader.decode().context(gettext("Failed to decode image"))?;
|
||||
(None, dynamic_image)
|
||||
}
|
||||
};
|
||||
|
||||
reader.limits(image_rs::io::Limits::no_limits());
|
||||
|
||||
let dynamic_image = reader.decode().context(gettext("Failed to decode image"))?;
|
||||
|
||||
let decoded_image = Decoded { dynamic_image };
|
||||
let decoded_image = Decoded {
|
||||
dynamic_image,
|
||||
icc_profile,
|
||||
};
|
||||
|
||||
let tile = tiling::Tile {
|
||||
position: (0, 0),
|
||||
|
|
@ -177,7 +204,10 @@ impl ImageRsOther {
|
|||
|
||||
let dynamic_image = image_rs::DynamicImage::from(rgb_image);
|
||||
|
||||
let decoded_image = Decoded { dynamic_image };
|
||||
let decoded_image = Decoded {
|
||||
dynamic_image,
|
||||
icc_profile: None,
|
||||
};
|
||||
|
||||
let tile = tiling::Tile {
|
||||
position,
|
||||
|
|
@ -224,6 +254,7 @@ impl ImageRsOther {
|
|||
|
||||
pub struct Decoded {
|
||||
dynamic_image: image_rs::DynamicImage,
|
||||
icc_profile: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Decoded {
|
||||
|
|
@ -247,18 +278,30 @@ impl Decoded {
|
|||
buffer.into_raw(),
|
||||
)
|
||||
}
|
||||
image_rs::DynamicImage::ImageRgb8(buffer) => (
|
||||
1,
|
||||
gdk::MemoryFormat::R8g8b8,
|
||||
buffer.sample_layout(),
|
||||
buffer.into_raw(),
|
||||
),
|
||||
image_rs::DynamicImage::ImageRgba8(buffer) => (
|
||||
1,
|
||||
gdk::MemoryFormat::R8g8b8a8,
|
||||
buffer.sample_layout(),
|
||||
buffer.into_raw(),
|
||||
),
|
||||
image_rs::DynamicImage::ImageRgb8(mut buffer) => {
|
||||
if let Some(icc_profile) = &self.icc_profile {
|
||||
util::new_trafo::<rgb::RGB8>(icc_profile, lcms2::PixelFormat::RGB_8)?
|
||||
.transform_in_place(buffer.as_pixels_mut());
|
||||
}
|
||||
(
|
||||
1,
|
||||
gdk::MemoryFormat::R8g8b8,
|
||||
buffer.sample_layout(),
|
||||
buffer.into_raw(),
|
||||
)
|
||||
}
|
||||
image_rs::DynamicImage::ImageRgba8(mut buffer) => {
|
||||
if let Some(icc_profile) = &self.icc_profile {
|
||||
util::new_trafo::<rgb::RGBA8>(icc_profile, lcms2::PixelFormat::RGBA_8)?
|
||||
.transform_in_place(buffer.as_pixels_mut());
|
||||
}
|
||||
(
|
||||
1,
|
||||
gdk::MemoryFormat::R8g8b8a8,
|
||||
buffer.sample_layout(),
|
||||
buffer.into_raw(),
|
||||
)
|
||||
}
|
||||
img @ image_rs::DynamicImage::ImageLuma16(_) => {
|
||||
let buffer = img.to_rgb16();
|
||||
(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue