From 44779721d4bd82d4e39f86bfc391923ed0b456a4 Mon Sep 17 00:00:00 2001
From: Inflation <2375962+inflation@users.noreply.github.com>
Date: Sat, 4 May 2024 17:52:11 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20metadata=20when=20encoding?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
jpegxl-rs/Cargo.toml | 4 +-
jpegxl-rs/src/encode.rs | 189 ++++++-------------------------
jpegxl-rs/src/encode/frame.rs | 97 ++++++++++++++++
jpegxl-rs/src/encode/metadata.rs | 24 ++++
jpegxl-rs/src/encode/options.rs | 69 +++++++++++
jpegxl-rs/src/memory.rs | 4 +-
jpegxl-sys/Cargo.toml | 2 +-
jpegxl-sys/src/encode.rs | 2 +-
jpegxl-sys/src/types.rs | 3 +-
9 files changed, 233 insertions(+), 161 deletions(-)
create mode 100644 jpegxl-rs/src/encode/frame.rs
create mode 100644 jpegxl-rs/src/encode/metadata.rs
create mode 100644 jpegxl-rs/src/encode/options.rs
diff --git a/jpegxl-rs/Cargo.toml b/jpegxl-rs/Cargo.toml
index 0402a8a..ff8f2be 100644
--- a/jpegxl-rs/Cargo.toml
+++ b/jpegxl-rs/Cargo.toml
@@ -8,7 +8,7 @@ license = "GPL-3.0-or-later"
name = "jpegxl-rs"
readme = "README.md"
repository = "https://github.com/inflation/jpegxl-rs"
-version = "0.10.2+libjxl-0.10.2"
+version = "0.10.3+libjxl-0.10.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -34,7 +34,7 @@ half = "2.4.0"
byteorder = "1.5.0"
[dependencies.jpegxl-sys]
-version = "0.10.2"
+version = "0.10.3"
path = "../jpegxl-sys"
[dev-dependencies]
diff --git a/jpegxl-rs/src/encode.rs b/jpegxl-rs/src/encode.rs
index dd10b8f..b1d86c4 100644
--- a/jpegxl-rs/src/encode.rs
+++ b/jpegxl-rs/src/encode.rs
@@ -20,134 +20,22 @@ along with jpegxl-rs. If not, see .
use std::{marker::PhantomData, mem::MaybeUninit, ops::Deref, ptr::null};
#[allow(clippy::wildcard_imports)]
-use jpegxl_sys::{
- color_encoding::JxlColorEncoding,
- encode::*,
- types::{JxlEndianness, JxlPixelFormat},
-};
+use jpegxl_sys::encode::*;
use crate::{
common::PixelType, errors::EncodeError, memory::MemoryManager, parallel::JxlParallelRunner,
};
-// MARK: Utility types
-
-/// Encoding speed
-#[derive(Debug, Clone, Copy)]
-pub enum EncoderSpeed {
- /// Fastest, 1
- Lightning = 1,
- /// 2
- Thunder = 2,
- /// 3
- Falcon = 3,
- /// 4
- Cheetah,
- /// 5
- Hare,
- /// 6
- Wombat,
- /// 7, default
- Squirrel,
- /// 8
- Kitten,
- /// Slowest, 9
- Tortoise,
-}
-
-impl std::default::Default for EncoderSpeed {
- fn default() -> Self {
- Self::Squirrel
- }
-}
-
-/// Encoding color profile
-#[derive(Debug, Clone, Copy)]
-pub enum ColorEncoding {
- /// SRGB, default for uint pixel types
- Srgb,
- /// Linear SRGB, default for float pixel types
- LinearSrgb,
- /// SRGB, images with only luma channel
- SrgbLuma,
- /// Linear SRGB with only luma channel
- LinearSrgbLuma,
-}
-
-impl From for JxlColorEncoding {
- fn from(val: ColorEncoding) -> Self {
- use ColorEncoding::{LinearSrgb, LinearSrgbLuma, Srgb, SrgbLuma};
-
- let mut color_encoding = MaybeUninit::uninit();
-
- unsafe {
- match val {
- Srgb => JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), false),
- LinearSrgb => JxlColorEncodingSetToLinearSRGB(color_encoding.as_mut_ptr(), false),
- SrgbLuma => JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), true),
- LinearSrgbLuma => {
- JxlColorEncodingSetToLinearSRGB(color_encoding.as_mut_ptr(), true);
- }
- }
- color_encoding.assume_init()
- }
- }
-}
-
-/// A frame for the encoder, consisting of the pixels and its options
-pub struct EncoderFrame<'data, T: PixelType> {
- data: &'data [T],
- num_channels: Option,
- endianness: Option,
- align: Option,
-}
+mod options;
+pub use options::*;
-impl<'data, T: PixelType> EncoderFrame<'data, T> {
- /// Create a default frame from the data.
- ///
- /// Use RGB(3) channels, native endianness and no alignment.
- pub fn new(data: &'data [T]) -> Self {
- Self {
- data,
- num_channels: None,
- endianness: None,
- align: None,
- }
- }
-
- /// Set the number of channels of the source.
- ///
- /// _Note_: If you want to use alpha channel, add here
- #[must_use]
- pub fn num_channels(mut self, value: u32) -> Self {
- self.num_channels = Some(value);
- self
- }
-
- /// Set the endianness of the source.
- #[must_use]
- pub fn endianness(mut self, value: JxlEndianness) -> Self {
- self.endianness = Some(value);
- self
- }
+mod metadata;
+pub use metadata::*;
- /// Set the align of the source.
- /// Align scanlines to a multiple of align bytes, or 0 to require no alignment at all
- #[must_use]
- pub fn align(mut self, value: usize) -> Self {
- self.align = Some(value);
- self
- }
+mod frame;
+pub use frame::*;
- fn pixel_format(&self) -> JxlPixelFormat {
- JxlPixelFormat {
- num_channels: self.num_channels.unwrap_or(3),
- data_type: T::pixel_type(),
- endianness: self.endianness.unwrap_or(JxlEndianness::Native),
- align: self.align.unwrap_or(0),
- }
- }
-}
+// MARK: Utility types
/// Encoder result
pub struct EncoderResult {
@@ -490,7 +378,29 @@ impl<'prl, 'mm> JxlEncoder<'prl, 'mm> {
height: u32,
) -> Result, EncodeError> {
self.setup_encoder(width, height, U::bits_per_sample(), self.has_alpha)?;
- Ok(MultiFrames::<'enc, 'prl, '_, U>(self, PhantomData))
+ Ok(MultiFrames::<'enc, 'prl, 'mm, U>(self, PhantomData))
+ }
+
+ /// Add a metadata box to the encoder
+ ///
+ /// # Errors
+ /// Return [`EncodeError`] if it fails to add metadata
+ pub fn add_metadata(&mut self, metadata: &Metadata, compress: bool) -> Result<(), EncodeError> {
+ let (&t, &data) = match metadata {
+ Metadata::Exif(data) => (b"Exif", data),
+ Metadata::Xmp(data) => (b"xml ", data),
+ Metadata::Jumb(data) => (b"jumb", data),
+ Metadata::Custom(t, data) => (t, data),
+ };
+ self.check_enc_status(unsafe {
+ JxlEncoderAddBox(
+ self.enc,
+ Metadata::box_type(t),
+ data.as_ptr().cast(),
+ data.len(),
+ compress.into(),
+ )
+ })
}
/// Encode a JPEG XL image from existing raw JPEG data
@@ -521,7 +431,8 @@ impl<'prl, 'mm> JxlEncoder<'prl, 'mm> {
/// Encode a JPEG XL image from pixels
///
- /// Note: Use RGB(3) channels, native endianness and no alignment. Ignore alpha channel settings
+ /// Note: Use RGB(3) channels, native endianness and no alignment.
+ /// Ignore alpha channel settings
///
/// # Errors
/// Return [`EncodeError`] if the internal encoder fails to encode
@@ -536,7 +447,8 @@ impl<'prl, 'mm> JxlEncoder<'prl, 'mm> {
self.start_encoding::()
}
- /// Encode a JPEG XL image from a frame. See [`EncoderFrame`] for custom options of the original pixels.
+ /// Encode a JPEG XL image from a frame.
+ /// See [`EncoderFrame`] for custom options of the original pixels.
///
/// # Errors
/// Return [`EncodeError`] if the internal encoder fails to encode
@@ -558,37 +470,6 @@ impl Drop for JxlEncoder<'_, '_> {
}
}
-/// A wrapper type for encoding multiple frames
-pub struct MultiFrames<'enc, 'prl, 'mm, U>(&'enc mut JxlEncoder<'prl, 'mm>, PhantomData)
-where
- 'prl: 'enc,
- 'mm: 'enc;
-
-impl MultiFrames<'_, '_, '_, U> {
- /// Add a frame to the encoder
- /// # Errors
- /// Return [`EncodeError`] if the internal encoder fails to add a frame
- pub fn add_frame(self, frame: &EncoderFrame) -> Result {
- self.0.add_frame(frame)?;
- Ok(self)
- }
-
- /// Add a JPEG raw frame to the encoder
- /// # Errors
- /// Return [`EncodeError`] if the internal encoder fails to add a jpeg frame
- pub fn add_jpeg_frame(self, data: &[u8]) -> Result {
- self.0.add_jpeg_frame(data)?;
- Ok(self)
- }
-
- /// Encode a JPEG XL image from the frames
- /// # Errors
- /// Return [`EncodeError`] if the internal encoder fails to encode
- pub fn encode(self) -> Result, EncodeError> {
- self.0.start_encoding()
- }
-}
-
/// Return a [`JxlEncoderBuilder`] with default settings
#[must_use]
pub fn encoder_builder<'prl, 'mm>() -> JxlEncoderBuilder<'prl, 'mm> {
diff --git a/jpegxl-rs/src/encode/frame.rs b/jpegxl-rs/src/encode/frame.rs
new file mode 100644
index 0000000..9f3b912
--- /dev/null
+++ b/jpegxl-rs/src/encode/frame.rs
@@ -0,0 +1,97 @@
+use std::marker::PhantomData;
+
+use jpegxl_sys::types::{JxlEndianness, JxlPixelFormat};
+
+use crate::{common::PixelType, EncodeError};
+
+use super::{EncoderResult, JxlEncoder};
+
+/// A frame for the encoder, consisting of the pixels and its options
+#[allow(clippy::module_name_repetitions)]
+pub struct EncoderFrame<'data, T: PixelType> {
+ pub(crate) data: &'data [T],
+ num_channels: Option,
+ endianness: Option,
+ align: Option,
+}
+
+impl<'data, T: PixelType> EncoderFrame<'data, T> {
+ /// Create a default frame from the data.
+ ///
+ /// Use RGB(3) channels, native endianness and no alignment.
+ pub fn new(data: &'data [T]) -> Self {
+ Self {
+ data,
+ num_channels: None,
+ endianness: None,
+ align: None,
+ }
+ }
+
+ /// Set the number of channels of the source.
+ ///
+ /// _Note_: If you want to use alpha channel, add here
+ #[must_use]
+ pub fn num_channels(mut self, value: u32) -> Self {
+ self.num_channels = Some(value);
+ self
+ }
+
+ /// Set the endianness of the source.
+ #[must_use]
+ pub fn endianness(mut self, value: JxlEndianness) -> Self {
+ self.endianness = Some(value);
+ self
+ }
+
+ /// Set the align of the source.
+ /// Align scanlines to a multiple of align bytes, or 0 to require no alignment at all
+ #[must_use]
+ pub fn align(mut self, value: usize) -> Self {
+ self.align = Some(value);
+ self
+ }
+
+ pub(crate) fn pixel_format(&self) -> JxlPixelFormat {
+ JxlPixelFormat {
+ num_channels: self.num_channels.unwrap_or(3),
+ data_type: T::pixel_type(),
+ endianness: self.endianness.unwrap_or(JxlEndianness::Native),
+ align: self.align.unwrap_or(0),
+ }
+ }
+}
+
+/// A wrapper type for encoding multiple frames
+pub struct MultiFrames<'enc, 'prl, 'mm, U>(
+ pub(crate) &'enc mut JxlEncoder<'prl, 'mm>,
+ pub(crate) PhantomData,
+)
+where
+ 'prl: 'enc,
+ 'mm: 'enc;
+
+impl MultiFrames<'_, '_, '_, U> {
+ /// Add a frame to the encoder
+ /// # Errors
+ /// Return [`EncodeError`] if the internal encoder fails to add a frame
+ pub fn add_frame(self, frame: &EncoderFrame) -> Result {
+ self.0.add_frame(frame)?;
+ Ok(self)
+ }
+
+ /// Add a JPEG raw frame to the encoder
+ /// # Errors
+ /// Return [`EncodeError`] if the internal encoder fails to add a jpeg frame
+ pub fn add_jpeg_frame(self, data: &[u8]) -> Result {
+ self.0.add_jpeg_frame(data)?;
+ Ok(self)
+ }
+
+ /// Encode a JPEG XL image from the frames
+ /// # Errors
+ /// Return [`EncodeError`] if the internal encoder fails to encode
+ pub fn encode(self) -> Result, EncodeError> {
+ self.0.start_encoding()
+ }
+}
diff --git a/jpegxl-rs/src/encode/metadata.rs b/jpegxl-rs/src/encode/metadata.rs
new file mode 100644
index 0000000..69670ef
--- /dev/null
+++ b/jpegxl-rs/src/encode/metadata.rs
@@ -0,0 +1,24 @@
+use jpegxl_sys::types::JxlBoxType;
+
+/// Metadata box
+pub enum Metadata<'d> {
+ /// EXIF
+ /// The contents of this box must be prepended by a 4-byte tiff header offset,
+ /// which may be 4 zero bytes in case the tiff header follows immediately.
+ Exif(&'d [u8]),
+ /// XMP/IPTC metadata
+ Xmp(&'d [u8]),
+ /// JUMBF superbox
+ Jumb(&'d [u8]),
+ /// Custom Metadata.
+ /// Type should not start with `jxl`, `JXL`, or conflict with other box type,
+ /// and should be registered with MP4RA (mp4ra.org).
+ Custom([u8; 4], &'d [u8]),
+}
+
+impl Metadata<'_> {
+ #[must_use]
+ pub(crate) fn box_type(t: [u8; 4]) -> JxlBoxType {
+ JxlBoxType(unsafe { std::mem::transmute(t) })
+ }
+}
diff --git a/jpegxl-rs/src/encode/options.rs b/jpegxl-rs/src/encode/options.rs
new file mode 100644
index 0000000..5bac696
--- /dev/null
+++ b/jpegxl-rs/src/encode/options.rs
@@ -0,0 +1,69 @@
+use std::mem::MaybeUninit;
+
+use jpegxl_sys::{color_encoding::JxlColorEncoding, encode as api};
+
+/// Encoding speed
+#[derive(Debug, Clone, Copy)]
+pub enum EncoderSpeed {
+ /// Fastest, 1
+ Lightning = 1,
+ /// 2
+ Thunder = 2,
+ /// 3
+ Falcon = 3,
+ /// 4
+ Cheetah,
+ /// 5
+ Hare,
+ /// 6
+ Wombat,
+ /// 7, default
+ Squirrel,
+ /// 8
+ Kitten,
+ /// 9
+ Tortoise,
+ /// Slowest, 10
+ Glacier,
+}
+
+impl std::default::Default for EncoderSpeed {
+ fn default() -> Self {
+ Self::Squirrel
+ }
+}
+
+/// Encoding color profile
+#[derive(Debug, Clone, Copy)]
+pub enum ColorEncoding {
+ /// SRGB, default for uint pixel types
+ Srgb,
+ /// Linear SRGB, default for float pixel types
+ LinearSrgb,
+ /// SRGB, images with only luma channel
+ SrgbLuma,
+ /// Linear SRGB with only luma channel
+ LinearSrgbLuma,
+}
+
+impl From for JxlColorEncoding {
+ fn from(val: ColorEncoding) -> Self {
+ use ColorEncoding::{LinearSrgb, LinearSrgbLuma, Srgb, SrgbLuma};
+
+ let mut color_encoding = MaybeUninit::uninit();
+
+ unsafe {
+ match val {
+ Srgb => api::JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), false),
+ LinearSrgb => {
+ api::JxlColorEncodingSetToLinearSRGB(color_encoding.as_mut_ptr(), false);
+ }
+ SrgbLuma => api::JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), true),
+ LinearSrgbLuma => {
+ api::JxlColorEncodingSetToLinearSRGB(color_encoding.as_mut_ptr(), true);
+ }
+ }
+ color_encoding.assume_init()
+ }
+ }
+}
diff --git a/jpegxl-rs/src/memory.rs b/jpegxl-rs/src/memory.rs
index c281f86..1b76ada 100644
--- a/jpegxl-rs/src/memory.rs
+++ b/jpegxl-rs/src/memory.rs
@@ -39,7 +39,7 @@ pub trait MemoryManager {
#[must_use]
fn manager(&self) -> JxlMemoryManager {
JxlMemoryManager {
- opaque: (self as *const Self).cast_mut().cast(),
+ opaque: std::ptr::from_ref::(self).cast_mut().cast(),
alloc: self.alloc(),
free: self.free(),
}
@@ -118,7 +118,7 @@ pub(crate) mod tests {
new = s + size;
} else {
let addr = mm.arena.get_unchecked_mut(footer);
- break (addr as *mut u8).cast();
+ break std::ptr::from_mut::(addr).cast();
}
}
}
diff --git a/jpegxl-sys/Cargo.toml b/jpegxl-sys/Cargo.toml
index 65c2145..4f4e366 100644
--- a/jpegxl-sys/Cargo.toml
+++ b/jpegxl-sys/Cargo.toml
@@ -9,7 +9,7 @@ links = "jxl"
name = "jpegxl-sys"
readme = "README.md"
repository = "https://github.com/inflation/jpegxl-rs"
-version = "0.10.2+libjxl-0.10.2"
+version = "0.10.3+libjxl-0.10.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/jpegxl-sys/src/encode.rs b/jpegxl-sys/src/encode.rs
index c6529f2..5d71d39 100644
--- a/jpegxl-sys/src/encode.rs
+++ b/jpegxl-sys/src/encode.rs
@@ -242,7 +242,7 @@ extern "C" {
pub fn JxlEncoderAddBox(
enc: *mut JxlEncoder,
- box_type: &mut JxlBoxType,
+ box_type: JxlBoxType,
contents: *const u8,
size: usize,
compress_box: JxlBool,
diff --git a/jpegxl-sys/src/types.rs b/jpegxl-sys/src/types.rs
index 1f0c08a..32dbfdd 100644
--- a/jpegxl-sys/src/types.rs
+++ b/jpegxl-sys/src/types.rs
@@ -76,4 +76,5 @@ pub struct JxlBitDepth {
exponent_bits_per_sample: u32,
}
-pub type JxlBoxType = [c_char; 4];
+#[repr(transparent)]
+pub struct JxlBoxType(pub [c_char; 4]);