Skip to content

Commit

Permalink
feat(operations): added "apply icc profile" operation
Browse files Browse the repository at this point in the history
  • Loading branch information
SalOne22 committed May 3, 2024
1 parent 1a6bf92 commit e12b7c4
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 1 deletion.
51 changes: 51 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ oxipng = ["dep:oxipng"]
webp = ["dep:webp"]
# Enables avif codec
avif = ["dep:ravif", "dep:libavif", "dep:rgb"]
icc = ["dep:lcms2"]

[dependencies]
zune-core = "0.5.0-rc0"
Expand All @@ -60,6 +61,7 @@ oxipng = { version = "9.0", default-features = false, features = ["zopfli", "fil
webp = { version = "0.2.6", default-features = false, optional = true }
ravif = { version = "0.11.4", optional = true }
libavif = { version = "0.13.0", default-features = false, features = ["codec-aom"], optional = true }
lcms2 = { version = "6.1.0", optional = true }

# cli
anyhow = { version = "1.0.80", optional = true }
Expand Down
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use cli::{
utils::paths::{collect_files, get_paths},
};
use rayon::prelude::*;
use rimage::operations::icc::ApplySRGB;
use zune_core::colorspace::ColorSpace;
use zune_image::{core_filters::colorspace::ColorspaceConv, image::Image, pipelines::Pipeline};
use zune_imageprocs::auto_orient::AutoOrient;
Expand Down Expand Up @@ -82,6 +83,7 @@ fn main() {
output.set_extension(available_encoder.to_extension());

pipeline.chain_operations(Box::new(AutoOrient));
pipeline.chain_operations(Box::new(ApplySRGB));

operations(matches, &img)
.into_iter()
Expand All @@ -96,7 +98,7 @@ fn main() {
}
});

pipeline.chain_image(img);
pipeline.chain_decoder(img);

handle_error!(input, pipeline.advance_to_end());

Expand Down
143 changes: 143 additions & 0 deletions src/operations/icc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use lcms2::*;
use zune_core::{bit_depth::BitType, colorspace::ColorSpace};
use zune_image::{
errors::{ImageErrors, ImageOperationsErrors},
frame::Frame,
image::Image,
traits::OperationsTrait,
};

/// Apply icc profile
pub struct ApplyICC {
profile: Profile,
}

impl ApplyICC {
/// Create a new icc apply operation
///
/// # Arguments
/// - chunk: ICC profile chunk
#[must_use]
pub fn new(profile: Profile) -> Self {
Self { profile }
}
}

impl OperationsTrait for ApplyICC {
fn name(&self) -> &'static str {
"apply icc profile"
}

fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
let icc_chunk = match image.metadata().icc_chunk() {
Some(icc) => icc,
None => {
log::warn!("No icc profile in the image, skipping");
return Ok(());
}
};

let src_profile = Profile::new_icc(icc_chunk)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

let colorspace = image.colorspace();

let format = match (colorspace, image.depth().bit_size()) {
(ColorSpace::RGB, 8) => PixelFormat::RGB_8,
(ColorSpace::RGB, 16) => PixelFormat::RGB_16,
(ColorSpace::RGBA, 8) => PixelFormat::RGBA_8,
(ColorSpace::RGBA, 16) => PixelFormat::RGBA_16,
(ColorSpace::YCbCr, 8) => PixelFormat::YCbCr_8,
(ColorSpace::YCbCr, 16) => PixelFormat::YCbCr_16,
(ColorSpace::Luma, 8) => PixelFormat::GRAY_8,
(ColorSpace::Luma, 16) => PixelFormat::GRAY_16,
(ColorSpace::LumaA, 8) => PixelFormat::GRAYA_8,
(ColorSpace::LumaA, 16) => PixelFormat::GRAYA_16,
(ColorSpace::CMYK, 8) => PixelFormat::CMYK_8,
(ColorSpace::CMYK, 16) => PixelFormat::CMYK_16,
(ColorSpace::BGR, 8) => PixelFormat::BGR_8,
(ColorSpace::BGR, 16) => PixelFormat::BGR_16,
(ColorSpace::BGRA, 8) => PixelFormat::BGRA_8,
(ColorSpace::BGRA, 16) => PixelFormat::BGRA_16,
(ColorSpace::ARGB, 8) => PixelFormat::ARGB_8,
(ColorSpace::ARGB, 16) => PixelFormat::ARGB_16,
(ColorSpace::HSV, 8) => PixelFormat::HSV_8,
(ColorSpace::HSV, 16) => PixelFormat::HSV_16,
_ => unreachable!("This should be handled in supported_colorspaces"),
};

let t = Transform::new(
&src_profile,
format,
&self.profile,
format,
Intent::Perceptual,
)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

for frame in image.frames_mut() {
let mut buffer = frame.flatten::<u8>(colorspace);
t.transform_in_place(&mut buffer);
let _ = std::mem::replace(frame, Frame::from_u8(&buffer, colorspace, 0, 0));
}

image.metadata_mut().set_icc_chunk(
self.profile
.icc()
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?,
);

Ok(())
}

fn supported_types(&self) -> &'static [BitType] {
&[BitType::U8, BitType::U16]
}

fn supported_colorspaces(&self) -> &'static [ColorSpace] {
&[
ColorSpace::RGB,
ColorSpace::RGBA,
ColorSpace::YCbCr,
ColorSpace::Luma,
ColorSpace::LumaA,
ColorSpace::CMYK,
ColorSpace::BGR,
ColorSpace::BGRA,
ColorSpace::ARGB,
ColorSpace::HSV,
]
}
}

/// Apply srgb icc profile
pub struct ApplySRGB;

impl OperationsTrait for ApplySRGB {
fn name(&self) -> &'static str {
"apply srgb profile"
}

fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
ApplyICC::new(Profile::new_srgb()).execute_impl(image)
}

fn supported_types(&self) -> &'static [BitType] {
&[BitType::U8, BitType::U16]
}

fn supported_colorspaces(&self) -> &'static [ColorSpace] {
&[
ColorSpace::RGB,
ColorSpace::RGBA,
ColorSpace::YCbCr,
ColorSpace::Luma,
ColorSpace::LumaA,
ColorSpace::CMYK,
ColorSpace::BGR,
ColorSpace::BGRA,
ColorSpace::ARGB,
ColorSpace::HSV,
]
}
}
4 changes: 4 additions & 0 deletions src/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ pub mod quantize;
/// Resize an image to a new dimensions
#[cfg(feature = "resize")]
pub mod resize;

/// Operations to apply icc profiles
#[cfg(feature = "icc")]
pub mod icc;

0 comments on commit e12b7c4

Please sign in to comment.