zune-core-0.4.12/.cargo_vcs_info.json0000644000000001560000000000100130310ustar { "git": { "sha1": "f8fbb123d5ed04441e8324a555bfcda0cb1bd28f" }, "path_in_vcs": "crates/zune-core" }zune-core-0.4.12/CHANGELOG.md000064400000000000000000000006071046102023000134330ustar 00000000000000## 0.2.14 - Fixed building with no-std - Add `peek_at` and `pos` for writer - Make serde non default - Add option to make PNG add an alpha channel ## 0.2.12 - Add endianness conversion - Hide exposed values for EncoderOptions - Add Float32 bit depth - Remove support for BitDepth 10 and 12 - Add bit_size method ## 0.2.1 Improve documentation on various parts ## 0.2.0 Initial versionzune-core-0.4.12/Cargo.toml0000644000000017370000000000100110350ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "zune-core" version = "0.4.12" exclude = ["tests/"] description = "Core utilities for image processing in the zune family of crates" homepage = "https://github.com/etemesi254/zune-image/tree/dev/zune-core" readme = "README.md" keywords = ["image"] categories = [ "multimedia::images", "multimedia::encoding", ] license = "MIT OR Apache-2.0 OR Zlib" [dependencies.log] version = "0.4.17" optional = true [dependencies.serde] version = "1.0.52" optional = true [features] std = [] zune-core-0.4.12/Cargo.toml.orig000064400000000000000000000014031046102023000145040ustar 00000000000000[package] name = "zune-core" version = "0.4.12" edition = "2021" description = "Core utilities for image processing in the zune family of crates" exclude = ["tests/"] homepage = "https://github.com/etemesi254/zune-image/tree/dev/zune-core" keywords = ["image"] categories = ["multimedia::images", "multimedia::encoding"] license = "MIT OR Apache-2.0 OR Zlib" [features] # When present, we can use std facilities to detect # if a specific feature exists # Not enabled by default. Other zune crates can enable dep:zune-core/std by default. # But if we enable it here, they can't disable it anymore. # See: https://github.com/rust-lang/cargo/issues/8366 std = [] [dependencies] log = { version = "0.4.17", optional = true } serde = { version = "1.0.52", optional = true } zune-core-0.4.12/README.md000064400000000000000000000005211046102023000130740ustar 00000000000000## Zune core Core primitives necessary for image manipulations This crate contains small set of primitives necessary for image manipulations which are shared among most `zune-` family of decoders and encoders. ### Items present Currently,it contains. - Colorspace definitions - Bit depth definitions. - Decoder and encoder optionszune-core-0.4.12/src/bit_depth.rs000064400000000000000000000117401046102023000147210ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! Image bit depth, information and manipulations /// The image bit depth. /// /// The library successfully supports depths up to /// 16 bits, as the underlying storage is usually a `u16`. /// /// This allows us to comfortably support a wide variety of images /// e.g 10 bit av1, 16 bit png and ppm. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub enum BitDepth { /// U8 bit depth. /// /// Images with such bit depth use [`u8`] to store /// pixels and use the whole range from 0-255. /// /// It is currently the smallest supported bit depth /// by the library. /// /// For images with bit depths lower than this, they will be scaled /// to this bit depth Eight, /// U16 bit depth /// /// Images with such bit depths use [`u16`] to store values and use the whole range /// i.e 0-65535 /// /// Data is stored and processed in native endian. Sixteen, /// Floating point 32 bit data, range is 0.0 to 1.0 /// /// Uses f32 to store data Float32, /// Bit depth information is unknown Unknown } /// The underlying bit representation of the image /// /// This represents the minimum rust type that /// can be used to represent image data, required /// by `Channel` struct in zune-image #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub enum BitType { /// Images represented using a [`u8`] as their /// underlying pixel storage U8, /// Images represented using a [`u16`] as their /// underlying pixel storage. U16, /// Images represented using a [`f32`] as their /// underlying pixel storage F32 } impl BitType { /// Return the equivalent of the image bit type's depth pub fn to_depth(self) -> BitDepth { match self { BitType::U8 => BitDepth::Eight, BitType::U16 => BitDepth::Sixteen, BitType::F32 => BitDepth::Float32 } } } impl Default for BitDepth { fn default() -> Self { Self::Unknown } } impl BitDepth { /// Get the max value supported by the bit depth /// /// During conversion from one bit depth to another /// /// larger values should be clamped to this bit depth #[rustfmt::skip] #[allow(clippy::zero_prefixed_literal)] pub const fn max_value(self) -> u16 { match self { Self::Eight => (1 << 08) - 1, Self::Sixteen => u16::MAX, Self::Float32 => 1, Self::Unknown => 0, } } /// Return the minimum number of bits that can be used to represent /// each pixel in the image /// /// All bit depths below 8 return a bit type of `BitType::U8`. /// and all those above 8 and below 16 return a bit type of `BitType::SixTeen` /// /// # Returns /// An enum whose variants represent the minimum size for an unsigned integer /// which can store the image pixels without overflow /// /// # Example /// /// ``` /// use zune_core::bit_depth::{BitDepth, BitType}; /// assert_eq!(BitDepth::Eight.bit_type(),BitType::U8); /// /// assert_eq!(BitDepth::Sixteen.bit_type(),BitType::U16); /// ``` /// /// See also [size_of](BitDepth::size_of) pub const fn bit_type(self) -> BitType { match self { Self::Eight => BitType::U8, Self::Sixteen => BitType::U16, Self::Float32 => BitType::F32, Self::Unknown => panic!("Unknown bit type") } } /// Get the number of bytes needed to store a specific bit depth /// /// /// # Example /// For images less than or equal to 8 bits(1 byte), we can use a [`u8`] to store /// the pixels, and a size_of [`u8`] is 1 /// /// For images greater than 8 bits and less than 16 bits(2 bytes), we can use a [`u16`] to /// store the pixels, a size_of [`u16`] is 2. /// ``` /// use zune_core::bit_depth::BitDepth; /// let depth = BitDepth::Sixteen; /// // greater 12 bits is greater than 8 and less than 16 /// assert_eq!(depth.size_of(),2); /// ``` pub const fn size_of(self) -> usize { match self { Self::Eight => core::mem::size_of::(), Self::Sixteen => core::mem::size_of::(), Self::Float32 => core::mem::size_of::(), Self::Unknown => panic!("Unknown bit type") } } pub const fn bit_size(&self) -> usize { self.size_of() * 8 } } /// Byte endianness of returned samples /// this is useful when the decoder returns samples which span more /// than one byte yet the type returned is `&[u8]` /// /// This helps you interpret how those bytes should be reconstructed /// to a higher order type #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ByteEndian { /// Little Endian byte-order LE, /// Big Endian byte-order BE } zune-core-0.4.12/src/bytestream/reader.rs000064400000000000000000000362621046102023000164060ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ use core::cmp::min; use crate::bytestream::traits::ZReaderTrait; const ERROR_MSG: &str = "No more bytes"; /// An encapsulation of a byte stream reader /// /// This provides an interface similar to [std::io::Cursor] but /// it provides fine grained options for reading different integer data types from /// the underlying buffer. /// /// There are two variants mainly error and non error variants, /// the error variants are useful for cases where you need bytes /// from the underlying stream, and cannot do with zero result. /// the non error variants are useful when you may have proved data already exists /// eg by using [`has`] method or you are okay with returning zero if the underlying /// buffer has been completely read. /// /// [std::io::Cursor]: https://doc.rust-lang.org/std/io/struct.Cursor.html /// [`has`]: Self::has pub struct ZByteReader { /// Data stream stream: T, position: usize } enum Mode { // Big endian BE, // Little Endian LE } #[cfg(feature = "std")] impl std::io::Read for ZByteReader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { Ok(self.read(buf).unwrap()) } } impl ZByteReader { /// Create a new instance of the byte stream /// /// Bytes will be read from the start of `buf`. /// /// `buf` is expected to live as long as this and /// all references to it live /// /// # Returns /// A byte reader which will pull bits from bye pub const fn new(buf: T) -> ZByteReader { ZByteReader { stream: buf, position: 0 } } /// Destroy this reader returning /// the underlying source of the bytes /// from which we were decoding pub fn consume(self) -> T { self.stream } /// Skip `num` bytes ahead of the stream. /// /// This bumps up the internal cursor wit a wrapping addition /// The bytes between current position and `num` will be skipped /// /// # Arguments /// `num`: How many bytes to skip /// /// # Note /// This does not consider length of the buffer, so skipping more bytes /// than possible and then reading bytes will return an error if using error variants /// or zero if using non-error variants /// /// # Example /// ``` /// use zune_core::bytestream::ZByteReader; /// let zero_to_hundred:Vec = (0..100).collect(); /// let mut stream = ZByteReader::new(&zero_to_hundred); /// // skip 37 bytes /// stream.skip(37); /// /// assert_eq!(stream.get_u8(),37); /// ``` /// /// See [`rewind`](ZByteReader::rewind) for moving the internal cursor back pub fn skip(&mut self, num: usize) { // Can this overflow ?? self.position = self.position.wrapping_add(num); } /// Undo a buffer read by moving the position pointer `num` /// bytes behind. /// /// This operation will saturate at zero pub fn rewind(&mut self, num: usize) { self.position = self.position.saturating_sub(num); } /// Return whether the underlying buffer /// has `num` bytes available for reading /// /// # Example /// /// ``` /// use zune_core::bytestream::ZByteReader; /// let data = [0_u8;120]; /// let reader = ZByteReader::new(data.as_slice()); /// assert!(reader.has(3)); /// assert!(!reader.has(121)); /// ``` #[inline] pub fn has(&self, num: usize) -> bool { self.position.saturating_add(num) <= self.stream.get_len() } /// Get number of bytes available in the stream #[inline] pub fn get_bytes_left(&self) -> usize { // Must be saturating to prevent underflow self.stream.get_len().saturating_sub(self.position) } /// Get length of the underlying buffer. /// /// To get the number of bytes left in the buffer, /// use [remaining] method /// /// [remaining]: Self::remaining #[inline] pub fn len(&self) -> usize { self.stream.get_len() } /// Return true if the underlying buffer stream is empty #[inline] pub fn is_empty(&self) -> bool { self.stream.get_len() == 0 } /// Get current position of the buffer. #[inline] pub const fn get_position(&self) -> usize { self.position } /// Return true whether or not we read to the end of the /// buffer and have no more bytes left. #[inline] pub fn eof(&self) -> bool { self.position >= self.len() } /// Get number of bytes unread inside this /// stream. /// /// To get the length of the underlying stream, /// use [len] method /// /// [len]: Self::len() #[inline] pub fn remaining(&self) -> usize { self.stream.get_len().saturating_sub(self.position) } /// Get a part of the bytestream as a reference. /// /// This increments the position to point past the bytestream /// if position+num is in bounds pub fn get(&mut self, num: usize) -> Result<&[u8], &'static str> { match self.stream.get_slice(self.position..self.position + num) { Some(bytes) => { self.position += num; Ok(bytes) } None => Err(ERROR_MSG) } } /// Look ahead position bytes and return a reference /// to num_bytes from that position, or an error if the /// peek would be out of bounds. /// /// This doesn't increment the position, bytes would have to be discarded /// at a later point. #[inline] pub fn peek_at(&self, position: usize, num_bytes: usize) -> Result<&[u8], &'static str> { let start = self.position + position; let end = self.position + position + num_bytes; match self.stream.get_slice(start..end) { Some(bytes) => Ok(bytes), None => Err(ERROR_MSG) } } /// Get a fixed amount of bytes or return an error if we cant /// satisfy the read /// /// This should be combined with [`has`] since if there are no /// more bytes you get an error. /// /// But it's useful for cases where you expect bytes but they are not present /// /// For the zero variant see, [`get_fixed_bytes_or_zero`] /// /// # Example /// ```rust /// use zune_core::bytestream::ZByteReader; /// let mut stream = ZByteReader::new([0x0,0x5,0x3,0x2].as_slice()); /// let first_bytes = stream.get_fixed_bytes_or_err::<10>(); // not enough bytes /// assert!(first_bytes.is_err()); /// ``` /// /// [`has`]:Self::has /// [`get_fixed_bytes_or_zero`]: Self::get_fixed_bytes_or_zero #[inline] pub fn get_fixed_bytes_or_err(&mut self) -> Result<[u8; N], &'static str> { let mut byte_store: [u8; N] = [0; N]; match self.stream.get_slice(self.position..self.position + N) { Some(bytes) => { self.position += N; byte_store.copy_from_slice(bytes); Ok(byte_store) } None => Err(ERROR_MSG) } } /// Get a fixed amount of bytes or return a zero array size /// if we can't satisfy the read /// /// This should be combined with [`has`] since if there are no /// more bytes you get a zero initialized array /// /// For the error variant see, [`get_fixed_bytes_or_err`] /// /// # Example /// ```rust /// use zune_core::bytestream::ZByteReader; /// let mut stream = ZByteReader::new([0x0,0x5,0x3,0x2].as_slice()); /// let first_bytes = stream.get_fixed_bytes_or_zero::<2>(); /// assert_eq!(first_bytes,[0x0,0x5]); /// ``` /// /// [`has`]:Self::has /// [`get_fixed_bytes_or_err`]: Self::get_fixed_bytes_or_err #[inline] pub fn get_fixed_bytes_or_zero(&mut self) -> [u8; N] { let mut byte_store: [u8; N] = [0; N]; match self.stream.get_slice(self.position..self.position + N) { Some(bytes) => { self.position += N; byte_store.copy_from_slice(bytes); byte_store } None => byte_store } } #[inline] /// Skip bytes until a condition becomes false or the stream runs out of bytes /// /// # Example /// /// ```rust /// use zune_core::bytestream::ZByteReader; /// let mut stream = ZByteReader::new([0;10].as_slice()); /// stream.skip_until_false(|x| x.is_ascii()) // skip until we meet a non ascii character /// ``` pub fn skip_until_false bool>(&mut self, func: F) { // iterate until we have no more bytes while !self.eof() { // get a byte from stream let byte = self.get_u8(); if !(func)(byte) { // function returned false meaning we stop skipping self.rewind(1); break; } } } /// Return the remaining unread bytes in this byte reader pub fn remaining_bytes(&self) -> &[u8] { debug_assert!(self.position <= self.len()); self.stream.get_slice(self.position..self.len()).unwrap() } pub fn read(&mut self, buf: &mut [u8]) -> Result { let buf_length = buf.len(); let start = self.position; let end = min(self.len(), self.position + buf_length); let diff = end - start; buf[0..diff].copy_from_slice(self.stream.get_slice(start..end).unwrap()); self.skip(diff); Ok(diff) } /// Read enough bytes to fill in pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), &'static str> { let size = self.read(buf)?; if size != buf.len() { return Err("Could not read into the whole buffer"); } Ok(()) } /// Set the cursor position /// /// After this, all reads will proceed from the position as an anchor /// point pub fn set_position(&mut self, position: usize) { self.position = position; } } macro_rules! get_single_type { ($name:tt,$name2:tt,$name3:tt,$name4:tt,$name5:tt,$name6:tt,$int_type:tt) => { impl ZByteReader { #[inline(always)] fn $name(&mut self, mode: Mode) -> $int_type { const SIZE_OF_VAL: usize = core::mem::size_of::<$int_type>(); let mut space = [0; SIZE_OF_VAL]; match self.stream.get_slice(self.position..self.position + SIZE_OF_VAL) { Some(position) => { space.copy_from_slice(position); self.position += SIZE_OF_VAL; match mode { Mode::LE => $int_type::from_le_bytes(space), Mode::BE => $int_type::from_be_bytes(space), } } None => 0, } } #[inline(always)] fn $name2(&mut self, mode: Mode) -> Result<$int_type, &'static str> { const SIZE_OF_VAL: usize = core::mem::size_of::<$int_type>(); let mut space = [0; SIZE_OF_VAL]; match self.stream.get_slice(self.position..self.position + SIZE_OF_VAL) { Some(position) => { space.copy_from_slice(position); self.position += SIZE_OF_VAL; match mode { Mode::LE => Ok($int_type::from_le_bytes(space)), Mode::BE => Ok($int_type::from_be_bytes(space)), } } None => Err(ERROR_MSG), } } #[doc=concat!("Read ",stringify!($int_type)," as a big endian integer")] #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," read.")] #[inline] pub fn $name3(&mut self) -> Result<$int_type, &'static str> { self.$name2(Mode::BE) } #[doc=concat!("Read ",stringify!($int_type)," as a little endian integer")] #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," read.")] #[inline] pub fn $name4(&mut self) -> Result<$int_type, &'static str> { self.$name2(Mode::LE) } #[doc=concat!("Read ",stringify!($int_type)," as a big endian integer")] #[doc=concat!("Returning 0 if the underlying buffer does not have enough bytes for a ",stringify!($int_type)," read.")] #[inline(always)] pub fn $name5(&mut self) -> $int_type { self.$name(Mode::BE) } #[doc=concat!("Read ",stringify!($int_type)," as a little endian integer")] #[doc=concat!("Returning 0 if the underlying buffer does not have enough bytes for a ",stringify!($int_type)," read.")] #[inline(always)] pub fn $name6(&mut self) -> $int_type { self.$name(Mode::LE) } } }; } // U8 implementation // The benefit of our own unrolled u8 impl instead of macros is that this is sometimes used in some // impls and is called multiple times, e.g jpeg during huffman decoding. // we can make some functions leaner like get_u8 is branchless impl ZByteReader where T: ZReaderTrait { /// Retrieve a byte from the underlying stream /// returning 0 if there are no more bytes available /// /// This means 0 might indicate a bit or an end of stream, but /// this is useful for some scenarios where one needs a byte. /// /// For the panicking one, see [`get_u8_err`] /// /// [`get_u8_err`]: Self::get_u8_err #[inline(always)] pub fn get_u8(&mut self) -> u8 { let byte = *self.stream.get_byte(self.position).unwrap_or(&0); self.position += usize::from(self.position < self.len()); byte } /// Retrieve a byte from the underlying stream /// returning an error if there are no more bytes available /// /// For the non panicking one, see [`get_u8`] /// /// [`get_u8`]: Self::get_u8 #[inline(always)] pub fn get_u8_err(&mut self) -> Result { match self.stream.get_byte(self.position) { Some(byte) => { self.position += 1; Ok(*byte) } None => Err(ERROR_MSG) } } } // u16,u32,u64 -> macros get_single_type!( get_u16_inner_or_default, get_u16_inner_or_die, get_u16_be_err, get_u16_le_err, get_u16_be, get_u16_le, u16 ); get_single_type!( get_u32_inner_or_default, get_u32_inner_or_die, get_u32_be_err, get_u32_le_err, get_u32_be, get_u32_le, u32 ); get_single_type!( get_u64_inner_or_default, get_u64_inner_or_die, get_u64_be_err, get_u64_le_err, get_u64_be, get_u64_le, u64 ); zune-core-0.4.12/src/bytestream/traits.rs000064400000000000000000000074111046102023000164440ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! Traits for reading and writing images in zune //! //! //! This exposes the traits and implementations for readers //! and writers in the zune family of decoders and encoders. use alloc::vec::Vec; use core::ops::Range; /// The underlying reader trait /// /// # Considerations /// ///- When implementing this for a type, it is recommended to implement methods with /// `#inline[(always)]` directive to allow the functions to get inlined in call sites, /// this may make it faster on some situations since the call sites may be in hot loop. /// /// - If you are reading from a file and it's small , it is preferable to read it into memory /// instead of using a file reader. pub trait ZReaderTrait { /// Get a single byte which is at position `index` /// /// # Arguments /// - `index`: The position of the bytes fn get_byte(&self, index: usize) -> Option<&u8>; /// Get a slice of bytes from a range of start..end /// /// # Arguments /// /// * `index`: The range of the bytes to read /// /// returns: `Option<&[u8]>` /// /// # Examples /// /// - Read 10 bytes from /// ``` /// extern crate alloc; /// use alloc::vec::Vec; /// use zune_core::bytestream::ZReaderTrait; /// /// let bytes = vec![0_u8;100]; /// /// // get ten bytes from 0..10 /// let re = bytes.get_slice(0..10).unwrap(); /// assert_eq!(10,re.len()) /// /// ``` fn get_slice(&self, index: Range) -> Option<&[u8]>; /// Get total length of the underlying buffer. /// /// This should be the total bytes that are present in /// the buffer. /// /// For files, this includes the file length. /// For buffers this includes the internal buffer length fn get_len(&self) -> usize; } impl ZReaderTrait for &[u8] { #[inline(always)] fn get_byte(&self, index: usize) -> Option<&u8> { self.get(index) } #[inline(always)] fn get_slice(&self, index: Range) -> Option<&[u8]> { self.get(index) } #[inline(always)] fn get_len(&self) -> usize { self.len() } } impl ZReaderTrait for Vec { #[inline(always)] fn get_byte(&self, index: usize) -> Option<&u8> { self.get(index) } #[inline(always)] fn get_slice(&self, index: Range) -> Option<&[u8]> { self.get(index) } #[inline(always)] fn get_len(&self) -> usize { self.len() } } impl ZReaderTrait for &Vec { #[inline(always)] fn get_byte(&self, index: usize) -> Option<&u8> { self.get(index) } #[inline(always)] fn get_slice(&self, index: Range) -> Option<&[u8]> { self.get(index) } #[inline(always)] fn get_len(&self) -> usize { self.len() } } impl ZReaderTrait for &[u8; N] { fn get_byte(&self, index: usize) -> Option<&u8> { self.get(index) } fn get_slice(&self, index: Range) -> Option<&[u8]> { self.get(index) } fn get_len(&self) -> usize { N } } impl ZReaderTrait for [u8; N] { fn get_byte(&self, index: usize) -> Option<&u8> { self.get(index) } fn get_slice(&self, index: Range) -> Option<&[u8]> { self.get(index) } fn get_len(&self) -> usize { N } } impl ZReaderTrait for dyn AsRef<&[u8]> { fn get_byte(&self, index: usize) -> Option<&u8> { self.as_ref().get(index) } fn get_slice(&self, index: Range) -> Option<&[u8]> { self.as_ref().get(index) } fn get_len(&self) -> usize { self.as_ref().len() } } zune-core-0.4.12/src/bytestream/writer.rs000064400000000000000000000260741046102023000164600ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ use core::mem::size_of; enum Mode { // Big endian BE, // Little Endian LE } static ERROR_MSG: &str = "No more space"; /// Encapsulates a simple Byte writer with /// support for Endian aware writes pub struct ZByteWriter<'a> { buffer: &'a mut [u8], position: usize } impl<'a> ZByteWriter<'a> { /// Write bytes from the buf into the bytestream /// and return how many bytes were written /// /// # Arguments /// - `buf`: The bytes to be written to the bytestream /// /// # Returns /// - `Ok(usize)` - Number of bytes written /// This number may be less than `buf.len()` if the length of the buffer is greater /// than the internal bytestream length /// /// If you want to be sure that all bytes were written, see [`write_all`](Self::write_all) /// #[inline] pub fn write(&mut self, buf: &[u8]) -> Result { let min = buf.len().min(self.bytes_left()); // write self.buffer[self.position..self.position + min].copy_from_slice(&buf[0..min]); self.position += min; Ok(min) } /// Write all bytes from `buf` into the bytestream and return /// and panic if not all bytes were written to the bytestream /// /// # Arguments /// - `buf`: The bytes to be written into the bytestream /// ///# Returns /// - `Ok(())`: Indicates all bytes were written into the bytestream /// - `Err(&static str)`: In case all the bytes could not be written /// to the stream pub fn write_all(&mut self, buf: &[u8]) -> Result<(), &'static str> { let size = self.write(buf)?; if size != buf.len() { return Err("Could not write the whole buffer"); } Ok(()) } /// Create a new bytestream writer /// Bytes are written from the start to the end and not assumptions /// are made of the nature of the underlying stream /// /// # Arguments pub fn new(data: &'a mut [u8]) -> ZByteWriter<'a> { ZByteWriter { buffer: data, position: 0 } } /// Return number of unwritten bytes in this stream /// /// # Example /// ``` /// use zune_core::bytestream::ZByteWriter; /// let mut storage = [0;10]; /// /// let writer = ZByteWriter::new(&mut storage); /// assert_eq!(writer.bytes_left(),10); // no bytes were written /// ``` pub const fn bytes_left(&self) -> usize { self.buffer.len().saturating_sub(self.position) } /// Return the number of bytes the writer has written /// /// ``` /// use zune_core::bytestream::ZByteWriter; /// let mut stream = ZByteWriter::new(&mut []); /// assert_eq!(stream.position(),0); /// ``` pub const fn position(&self) -> usize { self.position } /// Write a single byte into the bytestream or error out /// if there is not enough space /// /// # Example /// ``` /// use zune_core::bytestream::ZByteWriter; /// let mut buf = [0;10]; /// let mut stream = ZByteWriter::new(&mut buf); /// assert!(stream.write_u8_err(34).is_ok()); /// ``` /// No space /// ``` /// use zune_core::bytestream::ZByteWriter; /// let mut stream = ZByteWriter::new(&mut []); /// assert!(stream.write_u8_err(32).is_err()); /// ``` /// pub fn write_u8_err(&mut self, byte: u8) -> Result<(), &'static str> { match self.buffer.get_mut(self.position) { Some(m_byte) => { self.position += 1; *m_byte = byte; Ok(()) } None => Err(ERROR_MSG) } } /// Write a single byte in the stream or don't write /// anything if the buffer is full and cannot support the byte read /// /// Should be combined with [`has`](Self::has) pub fn write_u8(&mut self, byte: u8) { if let Some(m_byte) = self.buffer.get_mut(self.position) { self.position += 1; *m_byte = byte; } } /// Check if the byte writer can support /// the following write /// /// # Example /// ``` /// use zune_core::bytestream::ZByteWriter; /// let mut data = [0;10]; /// let mut stream = ZByteWriter::new(&mut data); /// assert!(stream.has(5)); /// assert!(!stream.has(100)); /// ``` pub const fn has(&self, bytes: usize) -> bool { self.position.saturating_add(bytes) <= self.buffer.len() } /// Get length of the underlying buffer. #[inline] pub const fn len(&self) -> usize { self.buffer.len() } /// Return true if the underlying buffer stream is empty #[inline] pub const fn is_empty(&self) -> bool { self.len() == 0 } /// Return true whether or not we read to the end of the /// buffer and have no more bytes left. /// /// If this is true, all non error variants will silently discard the /// byte and all error variants will return an error on writing a byte /// if any write occurs /// /// #[inline] pub const fn eof(&self) -> bool { self.position >= self.len() } /// Rewind the position of the internal cursor back by `by` bytes /// /// The position saturates at zero /// /// # Example /// ``` /// use zune_core::bytestream::ZByteWriter; /// let bytes = &mut [1,2,4]; /// let mut stream = ZByteWriter::new(bytes); /// stream.write_u16_be(23); /// // now internal cursor is at position 2. /// // lets rewind it /// stream.rewind(usize::MAX); /// assert_eq!(stream.position(),0); /// ``` #[inline] pub fn rewind(&mut self, by: usize) { self.position = self.position.saturating_sub(by); } /// Move the internal cursor forward some bytes /// /// /// This saturates at maximum value of usize in your platform. #[inline] pub fn skip(&mut self, by: usize) { self.position = self.position.saturating_add(by); } /// Look ahead position bytes and return a reference /// to num_bytes from that position, or an error if the /// peek would be out of bounds. /// /// This doesn't increment the position, bytes would have to be discarded /// at a later point. #[inline] pub fn peek_at(&'a self, position: usize, num_bytes: usize) -> Result<&'a [u8], &'static str> { let start = self.position + position; let end = self.position + position + num_bytes; match self.buffer.get(start..end) { Some(bytes) => Ok(bytes), None => Err(ERROR_MSG) } } /// Set position for the internal cursor /// /// Further calls to write bytes will proceed from the /// position set pub fn set_position(&mut self, position: usize) { self.position = position; } } macro_rules! write_single_type { ($name:tt,$name2:tt,$name3:tt,$name4:tt,$name5:tt,$name6:tt,$int_type:tt) => { impl<'a> ZByteWriter<'a> { #[inline(always)] fn $name(&mut self, byte: $int_type, mode: Mode) -> Result<(), &'static str> { const SIZE: usize = size_of::<$int_type>(); match self.buffer.get_mut(self.position..self.position + SIZE) { Some(m_byte) => { self.position += SIZE; // get bits, depending on mode. // This should be inlined and not visible in // the generated binary since mode is a compile // time constant. let bytes = match mode { Mode::BE => byte.to_be_bytes(), Mode::LE => byte.to_le_bytes() }; m_byte.copy_from_slice(&bytes); Ok(()) } None => Err(ERROR_MSG) } } #[inline(always)] fn $name2(&mut self, byte: $int_type, mode: Mode) { const SIZE: usize = size_of::<$int_type>(); if let Some(m_byte) = self.buffer.get_mut(self.position..self.position + SIZE) { self.position += SIZE; // get bits, depending on mode. // This should be inlined and not visible in // the generated binary since mode is a compile // time constant. let bytes = match mode { Mode::BE => byte.to_be_bytes(), Mode::LE => byte.to_le_bytes() }; m_byte.copy_from_slice(&bytes); } } #[doc=concat!("Write ",stringify!($int_type)," as a big endian integer")] #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," write.")] #[inline] pub fn $name3(&mut self, byte: $int_type) -> Result<(), &'static str> { self.$name(byte, Mode::BE) } #[doc=concat!("Write ",stringify!($int_type)," as a little endian integer")] #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," write.")] #[inline] pub fn $name4(&mut self, byte: $int_type) -> Result<(), &'static str> { self.$name(byte, Mode::LE) } #[doc=concat!("Write ",stringify!($int_type)," as a big endian integer")] #[doc=concat!("Or don't write anything if the reader cannot support a ",stringify!($int_type)," write.")] #[doc=concat!("\nShould be combined with the [`has`](Self::has) method to ensure a write succeeds")] #[inline] pub fn $name5(&mut self, byte: $int_type) { self.$name2(byte, Mode::BE) } #[doc=concat!("Write ",stringify!($int_type)," as a little endian integer")] #[doc=concat!("Or don't write anything if the reader cannot support a ",stringify!($int_type)," write.")] #[doc=concat!("Should be combined with the [`has`](Self::has) method to ensure a write succeeds")] #[inline] pub fn $name6(&mut self, byte: $int_type) { self.$name2(byte, Mode::LE) } } }; } write_single_type!( write_u64_inner_or_die, write_u64_inner_or_none, write_u64_be_err, write_u64_le_err, write_u64_be, write_u64_le, u64 ); write_single_type!( write_u32_inner_or_die, write_u32_inner_or_none, write_u32_be_err, write_u32_le_err, write_u32_be, write_u32_le, u32 ); write_single_type!( write_u16_inner_or_die, write_u16_inner_or_none, write_u16_be_err, write_u16_le_err, write_u16_be, write_u16_le, u16 ); zune-core-0.4.12/src/bytestream.rs000064400000000000000000000010311046102023000151260ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! A simple implementation of a bytestream reader //! and writer. //! //! This module contains two main structs that help in //! byte reading and byte writing //! //! Useful for a lot of image readers and writers, it's put //! here to minimize code reuse pub use reader::ZByteReader; pub use traits::*; pub use writer::ZByteWriter; mod reader; mod traits; mod writer; zune-core-0.4.12/src/colorspace.rs000064400000000000000000000072741046102023000151200ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! Image Colorspace information and manipulation utilities. /// All possible image colorspaces /// Some of them aren't yet supported exist here. #[allow(clippy::upper_case_acronyms)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub enum ColorSpace { /// Red, Green , Blue RGB, /// Red, Green, Blue, Alpha RGBA, /// YUV colorspace YCbCr, /// Grayscale colorspace Luma, /// Grayscale with alpha colorspace LumaA, YCCK, /// Cyan , Magenta, Yellow, Black CMYK, /// Blue, Green, Red BGR, /// Blue, Green, Red, Alpha BGRA, /// The colorspace is unknown Unknown, /// Alpha Red Green Blue ARGB, /// Hue,Saturation,Lightness /// Conversion from RGB to HSL and back matches that of Python [colorsys](https://docs.python.org/3/library/colorsys.html) module /// Color type is expected to be in floating point HSL, /// Hue, Saturation,Value /// /// Conversion from RGB to HSV and back matches that of Python [colorsys](https://docs.python.org/3/library/colorsys.html) module /// Color type is expected to be in floating point HSV } impl ColorSpace { /// Number of color channels present for a certain colorspace /// /// E.g. RGB returns 3 since it contains R,G and B colors to make up a pixel pub const fn num_components(&self) -> usize { match self { Self::RGB | Self::YCbCr | Self::BGR | Self::HSV | Self::HSL => 3, Self::RGBA | Self::YCCK | Self::CMYK | Self::BGRA | Self::ARGB => 4, Self::Luma => 1, Self::LumaA => 2, Self::Unknown => 0 } } pub const fn has_alpha(&self) -> bool { matches!(self, Self::RGBA | Self::LumaA | Self::BGRA | Self::ARGB) } pub const fn is_grayscale(&self) -> bool { matches!(self, Self::LumaA | Self::Luma) } /// Returns the position of the alpha pixel in a pixel /// /// /// That is for an array of color components say `[0,1,2,3]` if the image has an alpha channel /// and is in RGBA format, this will return `Some(3)`, indicating alpha is found in the third index /// but if the image is in `ARGB` format, it will return `Some(0)` indicating alpha is found in /// index 0 /// /// If an image doesn't have an alpha channel returns `None` /// pub const fn alpha_position(&self) -> Option { match self { ColorSpace::RGBA => Some(3), ColorSpace::LumaA => Some(1), ColorSpace::BGRA => Some(3), ColorSpace::ARGB => Some(0), _ => None } } } /// Encapsulates all colorspaces supported by /// the library pub static ALL_COLORSPACES: [ColorSpace; 12] = [ ColorSpace::RGB, ColorSpace::RGBA, ColorSpace::LumaA, ColorSpace::Luma, ColorSpace::CMYK, ColorSpace::BGRA, ColorSpace::BGR, ColorSpace::YCCK, ColorSpace::YCbCr, ColorSpace::ARGB, ColorSpace::HSL, ColorSpace::HSV ]; /// Color characteristics /// /// Gives more information about values in a certain /// colorspace #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, PartialEq)] pub enum ColorCharacteristics { /// Normal default gamma setting /// The float contains gamma present /// /// The default gamma value is 2.2 but for /// decoders that allow specifying gamma values,e.g PNG, /// the gamma value becomes the specified value by the decoder sRGB, /// Linear transfer characteristics /// The image is in linear colorspace Linear } zune-core-0.4.12/src/lib.rs000064400000000000000000000022031046102023000135170ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! Core routines shared by all libraries //! //! This crate provides a set of core routines shared //! by the decoders and encoders under `zune` umbrella //! //! It currently contains //! //! - A bytestream reader and writer with endian aware reads and writes //! - Colorspace and bit depth information shared by images //! - Image decoder and encoder options //! - A simple enum type to hold image decoding results. //! //! This library is `#[no_std]` with `alloc` feature needed for defining `Vec` //! which we need for storing decoded bytes. //! //! //! # Features //! - `no_std`: Enables `#[no_std]` compilation support. //! //! - `serde`: Enables serializing of some of the data structures //! present in the crate //! #![cfg_attr(not(feature = "std"), no_std)] #![macro_use] extern crate alloc; #[cfg(not(feature = "log"))] pub mod log; #[cfg(feature = "log")] pub use log; pub mod bit_depth; pub mod bytestream; pub mod colorspace; pub mod options; pub mod result; mod serde; zune-core-0.4.12/src/log.rs000064400000000000000000000025151046102023000135400ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ // #[macro_export] is required to make macros works across crates // but it always put the macro in the crate root. // #[doc(hidden)] + "pub use" is a workaround to namespace a macro. pub use crate::{ __debug as debug, __error as error, __info as info, __log_enabled as log_enabled, __trace as trace, __warn as warn }; #[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum Level { Error = 1, Warn, Info, Debug, Trace } #[doc(hidden)] #[macro_export] macro_rules! __log_enabled { ($lvl:expr) => {{ let _ = $lvl; false }}; } #[doc(hidden)] #[macro_export] macro_rules! __error { ($($arg:tt)+) => { #[cfg(feature = "std")] { //eprintln!($($arg)+); } }; } #[doc(hidden)] #[macro_export] macro_rules! __warn { ($($arg:tt)+) => { #[cfg(feature = "std")] { //eprintln!($($arg)+); } }; } #[doc(hidden)] #[macro_export] macro_rules! __info { ($($arg:tt)+) => {}; } #[doc(hidden)] #[macro_export] macro_rules! __debug { ($($arg:tt)+) => {}; } #[doc(hidden)] #[macro_export] macro_rules! __trace { ($($arg:tt)+) => {}; } zune-core-0.4.12/src/options/decoder.rs000064400000000000000000000516411046102023000160630ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! Global Decoder options #![allow(clippy::zero_prefixed_literal)] use crate::bit_depth::ByteEndian; use crate::colorspace::ColorSpace; fn decoder_strict_mode() -> DecoderFlags { DecoderFlags { inflate_confirm_adler: true, png_confirm_crc: true, jpg_error_on_non_conformance: true, zune_use_unsafe: true, zune_use_neon: true, zune_use_avx: true, zune_use_avx2: true, zune_use_sse2: true, zune_use_sse3: true, zune_use_sse41: true, png_add_alpha_channel: false, png_strip_16_bit_to_8_bit: false, png_decode_animated: true, jxl_decode_animated: true } } /// Fast decoder options /// /// Enables all intrinsics + unsafe routines /// /// Disables png adler and crc checking. fn fast_options() -> DecoderFlags { DecoderFlags { inflate_confirm_adler: false, png_confirm_crc: false, jpg_error_on_non_conformance: false, zune_use_unsafe: true, zune_use_neon: true, zune_use_avx: true, zune_use_avx2: true, zune_use_sse2: true, zune_use_sse3: true, zune_use_sse41: true, png_add_alpha_channel: false, png_strip_16_bit_to_8_bit: false, png_decode_animated: true, jxl_decode_animated: true } } /// Command line options error resilient and fast /// /// Features /// - Ignore CRC and Adler in png /// - Do not error out on non-conformance in jpg /// - Use unsafe paths fn cmd_options() -> DecoderFlags { DecoderFlags { inflate_confirm_adler: false, png_confirm_crc: false, jpg_error_on_non_conformance: false, zune_use_unsafe: true, zune_use_neon: true, zune_use_avx: true, zune_use_avx2: true, zune_use_sse2: true, zune_use_sse3: true, zune_use_sse41: true, png_add_alpha_channel: false, png_strip_16_bit_to_8_bit: false, png_decode_animated: true, jxl_decode_animated: true } } /// Decoder options that are flags /// /// NOTE: When you extend this, add true or false to /// all options above that return a `DecoderFlag` #[derive(Copy, Debug, Clone, Default)] pub struct DecoderFlags { /// Whether the decoder should confirm and report adler mismatch inflate_confirm_adler: bool, /// Whether the PNG decoder should confirm crc png_confirm_crc: bool, /// Whether the png decoder should error out on image non-conformance jpg_error_on_non_conformance: bool, /// Whether the decoder should use unsafe platform specific intrinsics /// /// This will also shut down platform specific intrinsics `(ZUNE_USE_{EXT})` value zune_use_unsafe: bool, /// Whether we should use SSE2. /// /// This should be enabled for all x64 platforms but can be turned off if /// `ZUNE_USE_UNSAFE` is false zune_use_sse2: bool, /// Whether we should use SSE3 instructions where possible. zune_use_sse3: bool, /// Whether we should use sse4.1 instructions where possible. zune_use_sse41: bool, /// Whether we should use avx instructions where possible. zune_use_avx: bool, /// Whether we should use avx2 instructions where possible. zune_use_avx2: bool, /// Whether the png decoder should add alpha channel where possible. png_add_alpha_channel: bool, /// Whether we should use neon instructions where possible. zune_use_neon: bool, /// Whether the png decoder should strip 16 bit to 8 bit png_strip_16_bit_to_8_bit: bool, /// Decode all frames for an animated images png_decode_animated: bool, jxl_decode_animated: bool } /// Decoder options /// /// Not all options are respected by decoders all decoders #[derive(Debug, Copy, Clone)] pub struct DecoderOptions { /// Maximum width for which decoders will /// not try to decode images larger than /// the specified width. /// /// - Default value: 16384 /// - Respected by: `all decoders` max_width: usize, /// Maximum height for which decoders will not /// try to decode images larger than the /// specified height /// /// - Default value: 16384 /// - Respected by: `all decoders` max_height: usize, /// Output colorspace /// /// The jpeg decoder allows conversion to a separate colorspace /// than the input. /// /// I.e you can convert a RGB jpeg image to grayscale without /// first decoding it to RGB to get /// /// - Default value: `ColorSpace::RGB` /// - Respected by: `jpeg` out_colorspace: ColorSpace, /// Maximum number of scans allowed /// for progressive jpeg images /// /// Progressive jpegs have scans /// /// - Default value:100 /// - Respected by: `jpeg` max_scans: usize, /// Maximum size for deflate. /// Respected by all decoders that use inflate/deflate deflate_limit: usize, /// Boolean flags that influence decoding flags: DecoderFlags, /// The byte endian of the returned bytes will be stored in /// in case a single pixel spans more than a byte endianness: ByteEndian } /// Initializers impl DecoderOptions { /// Create the decoder with options setting most configurable /// options to be their safe counterparts /// /// This is the same as `default` option as default initializes /// options to the safe variant. /// /// Note, decoders running on this will be slower as it disables /// platform specific intrinsics pub fn new_safe() -> DecoderOptions { DecoderOptions::default() } /// Create the decoder with options setting the configurable options /// to the fast counterparts /// /// This enables platform specific code paths and enable use of unsafe pub fn new_fast() -> DecoderOptions { let flag = fast_options(); DecoderOptions::default().set_decoder_flags(flag) } /// Create the decoder options with the following characteristics /// /// - Use unsafe paths. /// - Ignore error checksuming, e.g in png we do not confirm adler and crc in this mode /// - Enable fast intrinsics paths pub fn new_cmd() -> DecoderOptions { let flag = cmd_options(); DecoderOptions::default().set_decoder_flags(flag) } } /// Global options respected by all decoders impl DecoderOptions { /// Get maximum width configured for which the decoder /// should not try to decode images greater than this width pub const fn get_max_width(&self) -> usize { self.max_width } /// Get maximum height configured for which the decoder should /// not try to decode images greater than this height pub const fn get_max_height(&self) -> usize { self.max_height } /// Return true whether the decoder should be in strict mode /// And reject most errors pub fn get_strict_mode(&self) -> bool { self.flags.jpg_error_on_non_conformance | self.flags.png_confirm_crc | self.flags.inflate_confirm_adler } /// Return true if the decoder should use unsafe /// routines where possible pub const fn get_use_unsafe(&self) -> bool { self.flags.zune_use_unsafe } /// Set maximum width for which the decoder should not try /// decoding images greater than that width /// /// # Arguments /// /// * `width`: The maximum width allowed /// /// returns: DecoderOptions pub fn set_max_width(mut self, width: usize) -> Self { self.max_width = width; self } /// Set maximum height for which the decoder should not try /// decoding images greater than that height /// # Arguments /// /// * `height`: The maximum height allowed /// /// returns: DecoderOptions /// pub fn set_max_height(mut self, height: usize) -> Self { self.max_height = height; self } /// Whether the routines can use unsafe platform specific /// intrinsics when necessary /// /// Platform intrinsics are implemented for operations which /// the compiler can't auto-vectorize, or we can do a marginably /// better job at it /// /// All decoders with unsafe routines respect it. /// /// Treat this with caution, disabling it will cause slowdowns but /// it's provided for mainly for debugging use. /// /// - Respected by: `png` and `jpeg`(decoders with unsafe routines) pub fn set_use_unsafe(mut self, yes: bool) -> Self { // first clear the flag self.flags.zune_use_unsafe = yes; self } fn set_decoder_flags(mut self, flags: DecoderFlags) -> Self { self.flags = flags; self } /// Set whether the decoder should be in standards conforming/ /// strict mode /// /// This reduces the error tolerance level for the decoders and invalid /// samples will be rejected by the decoder /// /// # Arguments /// /// * `yes`: /// /// returns: DecoderOptions /// pub fn set_strict_mode(mut self, yes: bool) -> Self { self.flags.jpg_error_on_non_conformance = yes; self.flags.png_confirm_crc = yes; self.flags.inflate_confirm_adler = yes; self } /// Set the byte endian for which raw samples will be stored in /// in case a single pixel sample spans more than a byte. /// /// The default is usually native endian hence big endian values /// will be converted to little endian on little endian systems, /// /// and little endian values will be converted to big endian on big endian systems /// /// # Arguments /// /// * `endian`: The endianness to which to set the bytes to /// /// returns: DecoderOptions pub fn set_byte_endian(mut self, endian: ByteEndian) -> Self { self.endianness = endian; self } /// Get the byte endian for which samples that span more than one byte will /// be treated pub const fn get_byte_endian(&self) -> ByteEndian { self.endianness } } /// PNG specific options impl DecoderOptions { /// Whether the inflate decoder should confirm /// adler checksums pub const fn inflate_get_confirm_adler(&self) -> bool { self.flags.inflate_confirm_adler } /// Set whether the inflate decoder should confirm /// adler checksums pub fn inflate_set_confirm_adler(mut self, yes: bool) -> Self { self.flags.inflate_confirm_adler = yes; self } /// Get default inflate limit for which the decoder /// will not try to decompress further pub const fn inflate_get_limit(&self) -> usize { self.deflate_limit } /// Set the default inflate limit for which decompressors /// relying on inflate won't surpass this limit #[must_use] pub fn inflate_set_limit(mut self, limit: usize) -> Self { self.deflate_limit = limit; self } /// Whether the inflate decoder should confirm /// crc 32 checksums pub const fn png_get_confirm_crc(&self) -> bool { self.flags.png_confirm_crc } /// Set whether the png decoder should confirm /// CRC 32 checksums #[must_use] pub fn png_set_confirm_crc(mut self, yes: bool) -> Self { self.flags.png_confirm_crc = yes; self } /// Set whether the png decoder should add an alpha channel to /// images where possible. /// /// For Luma images, it converts it to Luma+Alpha /// /// For RGB images it converts it to RGB+Alpha pub fn png_set_add_alpha_channel(mut self, yes: bool) -> Self { self.flags.png_add_alpha_channel = yes; self } /// Return true whether the png decoder should add an alpha /// channel to images where possible pub const fn png_get_add_alpha_channel(&self) -> bool { self.flags.png_add_alpha_channel } /// Whether the png decoder should reduce 16 bit images to 8 bit /// images implicitly. /// /// Equivalent to [png::Transformations::STRIP_16](https://docs.rs/png/latest/png/struct.Transformations.html#associatedconstant.STRIP_16) pub fn png_set_strip_to_8bit(mut self, yes: bool) -> Self { self.flags.png_strip_16_bit_to_8_bit = yes; self } /// Return a boolean indicating whether the png decoder should reduce /// 16 bit images to 8 bit images implicitly pub const fn png_get_strip_to_8bit(&self) -> bool { self.flags.png_strip_16_bit_to_8_bit } /// Return whether `zune-image` should decode animated images or /// whether we should just decode the first frame only pub const fn png_decode_animated(&self) -> bool { self.flags.png_decode_animated } /// Set whether `zune-image` should decode animated images or /// whether we should just decode the first frame only pub const fn png_set_decode_animated(mut self, yes: bool) -> Self { self.flags.png_decode_animated = yes; self } } /// JPEG specific options impl DecoderOptions { /// Get maximum scans for which the jpeg decoder /// should not go above for progressive images pub const fn jpeg_get_max_scans(&self) -> usize { self.max_scans } /// Set maximum scans for which the jpeg decoder should /// not exceed when reconstructing images. pub fn jpeg_set_max_scans(mut self, max_scans: usize) -> Self { self.max_scans = max_scans; self } /// Get expected output colorspace set by the user for which the image /// is expected to be reconstructed into. /// /// This may be different from the pub const fn jpeg_get_out_colorspace(&self) -> ColorSpace { self.out_colorspace } /// Set expected colorspace for which the jpeg output is expected to be in /// /// This is mainly provided as is, we do not guarantee the decoder can convert to all colorspaces /// and the decoder can change it internally when it sees fit. #[must_use] pub fn jpeg_set_out_colorspace(mut self, colorspace: ColorSpace) -> Self { self.out_colorspace = colorspace; self } } /// Intrinsics support /// /// These routines are compiled depending /// on the platform they are used, if compiled for a platform /// it doesn't support,(e.g avx2 on Arm), it will always return `false` impl DecoderOptions { /// Use SSE 2 code paths where possible /// /// This checks for existence of SSE2 first and returns /// false if it's not present #[allow(unreachable_code)] pub fn use_sse2(&self) -> bool { let opt = self.flags.zune_use_sse2 | self.flags.zune_use_unsafe; // options says no if !opt { return false; } #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { // where we can do runtime check if feature is present #[cfg(feature = "std")] { if is_x86_feature_detected!("sse2") { return true; } } // where we can't do runtime check if feature is present // check if the compile feature had it enabled #[cfg(all(not(feature = "std"), target_feature = "sse2"))] { return true; } } // everything failed return false false } /// Use SSE 3 paths where possible /// /// /// This also checks for SSE3 support and returns false if /// it's not present #[allow(unreachable_code)] pub fn use_sse3(&self) -> bool { let opt = self.flags.zune_use_sse3 | self.flags.zune_use_unsafe; // options says no if !opt { return false; } #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { // where we can do runtime check if feature is present #[cfg(feature = "std")] { if is_x86_feature_detected!("sse3") { return true; } } // where we can't do runtime check if feature is present // check if the compile feature had it enabled #[cfg(all(not(feature = "std"), target_feature = "sse3"))] { return true; } } // everything failed return false false } /// Use SSE4 paths where possible /// /// This also checks for sse 4.1 support and returns false if it /// is not present #[allow(unreachable_code)] pub fn use_sse41(&self) -> bool { let opt = self.flags.zune_use_sse41 | self.flags.zune_use_unsafe; // options says no if !opt { return false; } #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { // where we can do runtime check if feature is present #[cfg(feature = "std")] { if is_x86_feature_detected!("sse4.1") { return true; } } // where we can't do runtime check if feature is present // check if the compile feature had it enabled #[cfg(all(not(feature = "std"), target_feature = "sse4.1"))] { return true; } } // everything failed return false false } /// Use AVX paths where possible /// /// This also checks for AVX support and returns false if it's /// not present #[allow(unreachable_code)] pub fn use_avx(&self) -> bool { let opt = self.flags.zune_use_avx | self.flags.zune_use_unsafe; // options says no if !opt { return false; } #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { // where we can do runtime check if feature is present #[cfg(feature = "std")] { if is_x86_feature_detected!("avx") { return true; } } // where we can't do runitme check if feature is present // check if the compile feature had it enabled #[cfg(all(not(feature = "std"), target_feature = "avx"))] { return true; } } // everything failed return false false } /// Use avx2 paths where possible /// /// This also checks for AVX2 support and returns false if it's not /// present #[allow(unreachable_code)] pub fn use_avx2(&self) -> bool { let opt = self.flags.zune_use_avx2 | self.flags.zune_use_unsafe; // options says no if !opt { return false; } #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { // where we can do runtime check if feature is present #[cfg(feature = "std")] { if is_x86_feature_detected!("avx2") { return true; } } // where we can't do runitme check if feature is present // check if the compile feature had it enabled #[cfg(all(not(feature = "std"), target_feature = "avx2"))] { return true; } } // everything failed return false false } #[allow(unreachable_code)] pub fn use_neon(&self) -> bool { let opt = self.flags.zune_use_neon | self.flags.zune_use_unsafe; // options says no if !opt { return false; } #[cfg(target_arch = "aarch64")] { // aarch64 implies neon on a compliant cpu // but for real prod should do something better here return true; } // everything failed return false false } } /// JPEG_XL specific options impl DecoderOptions { /// Return whether `zune-image` should decode animated images or /// whether we should just decode the first frame only pub const fn jxl_decode_animated(&self) -> bool { self.flags.jxl_decode_animated } /// Set whether `zune-image` should decode animated images or /// whether we should just decode the first frame only pub const fn jxl_set_decode_animated(mut self, yes: bool) -> Self { self.flags.jxl_decode_animated = yes; self } } impl Default for DecoderOptions { fn default() -> Self { Self { out_colorspace: ColorSpace::RGB, max_width: 1 << 14, max_height: 1 << 14, max_scans: 100, deflate_limit: 1 << 30, flags: decoder_strict_mode(), endianness: ByteEndian::BE } } } zune-core-0.4.12/src/options/encoder.rs000064400000000000000000000143441046102023000160740ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ use crate::bit_depth::BitDepth; use crate::colorspace::ColorSpace; /// Encoder options that are flags #[derive(Copy, Debug, Clone, Default)] struct EncoderFlags { /// Whether JPEG images should be encoded as progressive images jpeg_encode_progressive: bool, /// Whether JPEG images should use optimized huffman tables jpeg_optimize_huffman: bool, /// Whether to not preserve metadata across image transformations image_strip_metadata: bool } /// Options shared by some of the encoders in /// the `zune-` family of image crates #[derive(Debug, Copy, Clone)] pub struct EncoderOptions { width: usize, height: usize, colorspace: ColorSpace, quality: u8, depth: BitDepth, num_threads: u8, effort: u8, flags: EncoderFlags } impl Default for EncoderOptions { fn default() -> Self { Self { width: 0, height: 0, colorspace: ColorSpace::RGB, quality: 80, depth: BitDepth::Eight, num_threads: 4, effort: 4, flags: EncoderFlags::default() } } } impl EncoderOptions { /// Create new encode options /// /// # Arguments /// /// * `width`: Image width /// * `height`: Image height /// * `colorspace`: Image colorspaces /// * `depth`: Image depth /// /// returns: EncoderOptions /// pub fn new( width: usize, height: usize, colorspace: ColorSpace, depth: BitDepth ) -> EncoderOptions { EncoderOptions { width, height, colorspace, depth, ..Default::default() } } /// Get the width for which the image will be encoded in pub const fn get_width(&self) -> usize { self.width } /// Get height for which the image will be encoded in /// /// returns: usize /// /// # Panics /// If height is zero pub fn get_height(&self) -> usize { assert_ne!(self.height, 0); self.height } /// Get the depth for which the image will be encoded in pub const fn get_depth(&self) -> BitDepth { self.depth } /// Get the quality for which the image will be encoded with /// /// # Lossy /// - Higher quality means some images take longer to write and /// are big but they look good /// /// - Lower quality means small images and low quality. /// /// # Lossless /// - High quality indicates more time is spent in making the file /// smaller /// /// - Low quality indicates less time is spent in making the file bigger pub const fn get_quality(&self) -> u8 { self.quality } /// Get the colorspace for which the image will be encoded in pub const fn get_colorspace(&self) -> ColorSpace { self.colorspace } pub const fn get_effort(&self) -> u8 { self.effort } /// Set width for the image to be encoded pub fn set_width(mut self, width: usize) -> Self { self.width = width; self } /// Set height for the image to be encoded pub fn set_height(mut self, height: usize) -> Self { self.height = height; self } /// Set depth for the image to be encoded pub fn set_depth(mut self, depth: BitDepth) -> Self { self.depth = depth; self } /// Set quality of the image to be encoded /// /// Quality is clamped from 0..100 /// /// Quality means different options depending on the encoder, see /// [get_quality](Self::get_quality) pub fn set_quality(mut self, quality: u8) -> Self { self.quality = quality.clamp(0, 100); self } /// Set colorspace for the image to be encoded pub fn set_colorspace(mut self, colorspace: ColorSpace) -> Self { self.colorspace = colorspace; self } /// Set the number of threads allowed for multithreaded encoding /// where supported /// /// Zero means use a single thread pub fn set_num_threads(mut self, threads: u8) -> Self { self.num_threads = threads; self } pub fn set_effort(mut self, effort: u8) -> Self { self.effort = effort; self } /// Return number of threads configured for multithreading /// where possible /// /// This is used for multi-threaded encoders, /// currently only jpeg-xl pub const fn get_num_threads(&self) -> u8 { self.num_threads } /// Set whether the encoder should remove metadata from the image /// /// When set to `true`, supported encoders will strip away metadata /// from the resulting image. If set to false, where supported, encoders /// will not remove metadata from images pub fn set_strip_metadata(mut self, yes: bool) -> Self { self.flags.image_strip_metadata = yes; self } /// Whether or not the encoder should remove metadata from the image /// /// The default value is false, and encoders that respect this try to preserve as much /// data as possible from one image to another pub const fn strip_metadata(&self) -> bool { !self.flags.image_strip_metadata } } /// JPEG options impl EncoderOptions { /// Whether the jpeg encoder should encode the image in progressive mode /// /// Default is `false`. /// /// This may be used to create slightly smaller images at the cost of more processing /// time pub const fn jpeg_encode_progressive(&self) -> bool { self.flags.jpeg_encode_progressive } /// Whether the jpeg encoder should optimize huffman tables to create smaller files /// at the cost of processing time /// /// Default is `false`. pub const fn jpeg_optimized_huffman_tables(&self) -> bool { self.flags.jpeg_optimize_huffman } /// Set whether the jpeg encoder should encode the imagei in progressive mode /// /// Default is `false` pub fn set_jpeg_encode_progressive(mut self, yes: bool) -> Self { self.flags.jpeg_optimize_huffman = yes; self } } zune-core-0.4.12/src/options.rs000064400000000000000000000006271046102023000144540ustar 00000000000000//! Decoder and Encoder Options //! //! This module exposes a struct for which all implemented //! decoders get shared options for decoding //! //! All supported options are put into one _Options to allow for global configurations //! options e.g the same `DecoderOption` can be reused for all other decoders //! pub use decoder::DecoderOptions; pub use encoder::EncoderOptions; mod decoder; mod encoder; zune-core-0.4.12/src/result.rs000064400000000000000000000036431046102023000143000ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ //! Decoding results for images use alloc::vec::Vec; /// A simple enum that can hold decode /// results of most images #[non_exhaustive] pub enum DecodingResult { U8(Vec), U16(Vec), F32(Vec) } impl DecodingResult { /// Return the contents if the enum stores `Vec` or otherwise /// return `None`. /// /// Useful for de-sugaring the result of a decoding operation /// into raw bytes /// /// # Example /// ``` /// use zune_core::result::DecodingResult; /// let data = DecodingResult::U8(vec![0;100]); /// // we know this won't fail because we created it with u8 /// assert!(data.u8().is_some()); /// /// let data = DecodingResult::U16(vec![0;100]); /// // it should now return nothing since the type is u18 /// assert!(data.u8().is_none()); /// /// ``` pub fn u8(self) -> Option> { match self { DecodingResult::U8(data) => Some(data), _ => None } } /// Return the contents if the enum stores `Vec` or otherwise /// return `None`. /// /// Useful for de-sugaring the result of a decoding operation /// into raw bytes /// /// # Example /// ``` /// use zune_core::result::DecodingResult; /// let data = DecodingResult::U8(vec![0;100]); /// // we know this will fail because we created it with u16 /// assert!(data.u16().is_none()); /// /// /// let data = DecodingResult::U16(vec![0;100]); /// // it should now return something since the type is u16 /// assert!(data.u16().is_some()); /// /// ``` pub fn u16(self) -> Option> { match self { DecodingResult::U16(data) => Some(data), _ => None } } } zune-core-0.4.12/src/serde.rs000064400000000000000000000024211046102023000140550ustar 00000000000000/* * Copyright (c) 2023. * * This software is free software; * * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license */ #![cfg(feature = "serde")] //! Serde support for serializing //! crate datastructures //! //! Implements serialize for //! - ColorSpace //! - BitDepth //! - ColorCharacteristics use alloc::format; use serde::ser::*; use crate::bit_depth::BitDepth; use crate::colorspace::{ColorCharacteristics, ColorSpace}; impl Serialize for ColorSpace { #[allow(clippy::uninlined_format_args)] fn serialize(&self, serializer: S) -> Result where S: Serializer { // colorspace serialization is simply it's debug value serializer.serialize_str(&format!("{:?}", self)) } } impl Serialize for BitDepth { #[allow(clippy::uninlined_format_args)] fn serialize(&self, serializer: S) -> Result where S: Serializer { serializer.serialize_str(&format!("{:?}", self)) } } impl Serialize for ColorCharacteristics { #[allow(clippy::uninlined_format_args)] fn serialize(&self, serializer: S) -> Result where S: Serializer { serializer.serialize_str(&format!("{:?}", self)) } }