diff options
Diffstat (limited to 'vendor/qoi/src')
-rw-r--r-- | vendor/qoi/src/consts.rs | 17 | ||||
-rw-r--r-- | vendor/qoi/src/decode.rs | 396 | ||||
-rw-r--r-- | vendor/qoi/src/encode.rs | 210 | ||||
-rw-r--r-- | vendor/qoi/src/error.rs | 82 | ||||
-rw-r--r-- | vendor/qoi/src/header.rs | 120 | ||||
-rw-r--r-- | vendor/qoi/src/lib.rs | 95 | ||||
-rw-r--r-- | vendor/qoi/src/pixel.rs | 183 | ||||
-rw-r--r-- | vendor/qoi/src/types.rs | 113 | ||||
-rw-r--r-- | vendor/qoi/src/utils.rs | 107 |
9 files changed, 0 insertions, 1323 deletions
diff --git a/vendor/qoi/src/consts.rs b/vendor/qoi/src/consts.rs deleted file mode 100644 index 2281c24..0000000 --- a/vendor/qoi/src/consts.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub const QOI_OP_INDEX: u8 = 0x00; // 00xxxxxx -pub const QOI_OP_DIFF: u8 = 0x40; // 01xxxxxx -pub const QOI_OP_LUMA: u8 = 0x80; // 10xxxxxx -pub const QOI_OP_RUN: u8 = 0xc0; // 11xxxxxx -pub const QOI_OP_RGB: u8 = 0xfe; // 11111110 -pub const QOI_OP_RGBA: u8 = 0xff; // 11111111 - -pub const QOI_MASK_2: u8 = 0xc0; // (11)000000 - -pub const QOI_HEADER_SIZE: usize = 14; - -pub const QOI_PADDING: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0x01]; // 7 zeros and one 0x01 marker -pub const QOI_PADDING_SIZE: usize = 8; - -pub const QOI_MAGIC: u32 = u32::from_be_bytes(*b"qoif"); - -pub const QOI_PIXELS_MAX: usize = 400_000_000; diff --git a/vendor/qoi/src/decode.rs b/vendor/qoi/src/decode.rs deleted file mode 100644 index 019dac2..0000000 --- a/vendor/qoi/src/decode.rs +++ /dev/null @@ -1,396 +0,0 @@ -#[cfg(any(feature = "std", feature = "alloc"))] -use alloc::{vec, vec::Vec}; -#[cfg(feature = "std")] -use std::io::Read; - -// TODO: can be removed once https://github.com/rust-lang/rust/issues/74985 is stable -use bytemuck::{cast_slice_mut, Pod}; - -use crate::consts::{ - QOI_HEADER_SIZE, QOI_OP_DIFF, QOI_OP_INDEX, QOI_OP_LUMA, QOI_OP_RGB, QOI_OP_RGBA, QOI_OP_RUN, - QOI_PADDING, QOI_PADDING_SIZE, -}; -use crate::error::{Error, Result}; -use crate::header::Header; -use crate::pixel::{Pixel, SupportedChannels}; -use crate::types::Channels; -use crate::utils::{cold, unlikely}; - -const QOI_OP_INDEX_END: u8 = QOI_OP_INDEX | 0x3f; -const QOI_OP_RUN_END: u8 = QOI_OP_RUN | 0x3d; // <- note, 0x3d (not 0x3f) -const QOI_OP_DIFF_END: u8 = QOI_OP_DIFF | 0x3f; -const QOI_OP_LUMA_END: u8 = QOI_OP_LUMA | 0x3f; - -#[inline] -fn decode_impl_slice<const N: usize, const RGBA: bool>(data: &[u8], out: &mut [u8]) -> Result<usize> -where - Pixel<N>: SupportedChannels, - [u8; N]: Pod, -{ - let mut pixels = cast_slice_mut::<_, [u8; N]>(out); - let data_len = data.len(); - let mut data = data; - - let mut index = [Pixel::<4>::new(); 256]; - let mut px = Pixel::<N>::new().with_a(0xff); - let mut px_rgba: Pixel<4>; - - while let [px_out, ptail @ ..] = pixels { - pixels = ptail; - match data { - [b1 @ QOI_OP_INDEX..=QOI_OP_INDEX_END, dtail @ ..] => { - px_rgba = index[*b1 as usize]; - px.update(px_rgba); - *px_out = px.into(); - data = dtail; - continue; - } - [QOI_OP_RGB, r, g, b, dtail @ ..] => { - px.update_rgb(*r, *g, *b); - data = dtail; - } - [QOI_OP_RGBA, r, g, b, a, dtail @ ..] if RGBA => { - px.update_rgba(*r, *g, *b, *a); - data = dtail; - } - [b1 @ QOI_OP_RUN..=QOI_OP_RUN_END, dtail @ ..] => { - *px_out = px.into(); - let run = ((b1 & 0x3f) as usize).min(pixels.len()); - let (phead, ptail) = pixels.split_at_mut(run); // can't panic - phead.fill(px.into()); - pixels = ptail; - data = dtail; - continue; - } - [b1 @ QOI_OP_DIFF..=QOI_OP_DIFF_END, dtail @ ..] => { - px.update_diff(*b1); - data = dtail; - } - [b1 @ QOI_OP_LUMA..=QOI_OP_LUMA_END, b2, dtail @ ..] => { - px.update_luma(*b1, *b2); - data = dtail; - } - _ => { - cold(); - if unlikely(data.len() < QOI_PADDING_SIZE) { - return Err(Error::UnexpectedBufferEnd); - } - } - } - - px_rgba = px.as_rgba(0xff); - index[px_rgba.hash_index() as usize] = px_rgba; - *px_out = px.into(); - } - - if unlikely(data.len() < QOI_PADDING_SIZE) { - return Err(Error::UnexpectedBufferEnd); - } else if unlikely(data[..QOI_PADDING_SIZE] != QOI_PADDING) { - return Err(Error::InvalidPadding); - } - - Ok(data_len.saturating_sub(data.len()).saturating_sub(QOI_PADDING_SIZE)) -} - -#[inline] -fn decode_impl_slice_all( - data: &[u8], out: &mut [u8], channels: u8, src_channels: u8, -) -> Result<usize> { - match (channels, src_channels) { - (3, 3) => decode_impl_slice::<3, false>(data, out), - (3, 4) => decode_impl_slice::<3, true>(data, out), - (4, 3) => decode_impl_slice::<4, false>(data, out), - (4, 4) => decode_impl_slice::<4, true>(data, out), - _ => { - cold(); - Err(Error::InvalidChannels { channels }) - } - } -} - -/// Decode the image into a pre-allocated buffer. -/// -/// Note: the resulting number of channels will match the header. In order to change -/// the number of channels, use [`Decoder::with_channels`]. -#[inline] -pub fn decode_to_buf(buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>) -> Result<Header> { - let mut decoder = Decoder::new(&data)?; - decoder.decode_to_buf(buf)?; - Ok(*decoder.header()) -} - -/// Decode the image into a newly allocated vector. -/// -/// Note: the resulting number of channels will match the header. In order to change -/// the number of channels, use [`Decoder::with_channels`]. -#[cfg(any(feature = "std", feature = "alloc"))] -#[inline] -pub fn decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> { - let mut decoder = Decoder::new(&data)?; - let out = decoder.decode_to_vec()?; - Ok((*decoder.header(), out)) -} - -/// Decode the image header from a slice of bytes. -#[inline] -pub fn decode_header(data: impl AsRef<[u8]>) -> Result<Header> { - Header::decode(data) -} - -#[cfg(any(feature = "std"))] -#[inline] -fn decode_impl_stream<R: Read, const N: usize, const RGBA: bool>( - data: &mut R, out: &mut [u8], -) -> Result<()> -where - Pixel<N>: SupportedChannels, - [u8; N]: Pod, -{ - let mut pixels = cast_slice_mut::<_, [u8; N]>(out); - - let mut index = [Pixel::<N>::new(); 256]; - let mut px = Pixel::<N>::new().with_a(0xff); - - while let [px_out, ptail @ ..] = pixels { - pixels = ptail; - let mut p = [0]; - data.read_exact(&mut p)?; - let [b1] = p; - match b1 { - QOI_OP_INDEX..=QOI_OP_INDEX_END => { - px = index[b1 as usize]; - *px_out = px.into(); - continue; - } - QOI_OP_RGB => { - let mut p = [0; 3]; - data.read_exact(&mut p)?; - px.update_rgb(p[0], p[1], p[2]); - } - QOI_OP_RGBA if RGBA => { - let mut p = [0; 4]; - data.read_exact(&mut p)?; - px.update_rgba(p[0], p[1], p[2], p[3]); - } - QOI_OP_RUN..=QOI_OP_RUN_END => { - *px_out = px.into(); - let run = ((b1 & 0x3f) as usize).min(pixels.len()); - let (phead, ptail) = pixels.split_at_mut(run); // can't panic - phead.fill(px.into()); - pixels = ptail; - continue; - } - QOI_OP_DIFF..=QOI_OP_DIFF_END => { - px.update_diff(b1); - } - QOI_OP_LUMA..=QOI_OP_LUMA_END => { - let mut p = [0]; - data.read_exact(&mut p)?; - let [b2] = p; - px.update_luma(b1, b2); - } - _ => { - cold(); - } - } - - index[px.hash_index() as usize] = px; - *px_out = px.into(); - } - - let mut p = [0_u8; QOI_PADDING_SIZE]; - data.read_exact(&mut p)?; - if unlikely(p != QOI_PADDING) { - return Err(Error::InvalidPadding); - } - - Ok(()) -} - -#[cfg(feature = "std")] -#[inline] -fn decode_impl_stream_all<R: Read>( - data: &mut R, out: &mut [u8], channels: u8, src_channels: u8, -) -> Result<()> { - match (channels, src_channels) { - (3, 3) => decode_impl_stream::<_, 3, false>(data, out), - (3, 4) => decode_impl_stream::<_, 3, true>(data, out), - (4, 3) => decode_impl_stream::<_, 4, false>(data, out), - (4, 4) => decode_impl_stream::<_, 4, true>(data, out), - _ => { - cold(); - Err(Error::InvalidChannels { channels }) - } - } -} - -#[doc(hidden)] -pub trait Reader: Sized { - fn decode_header(&mut self) -> Result<Header>; - fn decode_image(&mut self, out: &mut [u8], channels: u8, src_channels: u8) -> Result<()>; -} - -pub struct Bytes<'a>(&'a [u8]); - -impl<'a> Bytes<'a> { - #[inline] - pub const fn new(buf: &'a [u8]) -> Self { - Self(buf) - } - - #[inline] - pub const fn as_slice(&self) -> &[u8] { - self.0 - } -} - -impl<'a> Reader for Bytes<'a> { - #[inline] - fn decode_header(&mut self) -> Result<Header> { - let header = Header::decode(self.0)?; - self.0 = &self.0[QOI_HEADER_SIZE..]; // can't panic - Ok(header) - } - - #[inline] - fn decode_image(&mut self, out: &mut [u8], channels: u8, src_channels: u8) -> Result<()> { - let n_read = decode_impl_slice_all(self.0, out, channels, src_channels)?; - self.0 = &self.0[n_read..]; - Ok(()) - } -} - -#[cfg(feature = "std")] -impl<R: Read> Reader for R { - #[inline] - fn decode_header(&mut self) -> Result<Header> { - let mut b = [0; QOI_HEADER_SIZE]; - self.read_exact(&mut b)?; - Header::decode(b) - } - - #[inline] - fn decode_image(&mut self, out: &mut [u8], channels: u8, src_channels: u8) -> Result<()> { - decode_impl_stream_all(self, out, channels, src_channels) - } -} - -/// Decode QOI images from slices or from streams. -#[derive(Clone)] -pub struct Decoder<R> { - reader: R, - header: Header, - channels: Channels, -} - -impl<'a> Decoder<Bytes<'a>> { - /// Creates a new decoder from a slice of bytes. - /// - /// The header will be decoded immediately upon construction. - /// - /// Note: this provides the most efficient decoding, but requires the source data to - /// be loaded in memory in order to decode it. In order to decode from a generic - /// stream, use [`Decoder::from_stream`] instead. - #[inline] - pub fn new(data: &'a (impl AsRef<[u8]> + ?Sized)) -> Result<Self> { - Self::new_impl(Bytes::new(data.as_ref())) - } - - /// Returns the undecoded tail of the input slice of bytes. - #[inline] - pub const fn data(&self) -> &[u8] { - self.reader.as_slice() - } -} - -#[cfg(feature = "std")] -impl<R: Read> Decoder<R> { - /// Creates a new decoder from a generic reader that implements [`Read`](std::io::Read). - /// - /// The header will be decoded immediately upon construction. - /// - /// Note: while it's possible to pass a `&[u8]` slice here since it implements `Read`, it - /// would be more efficient to use a specialized constructor instead: [`Decoder::new`]. - #[inline] - pub fn from_stream(reader: R) -> Result<Self> { - Self::new_impl(reader) - } - - /// Returns an immutable reference to the underlying reader. - #[inline] - pub const fn reader(&self) -> &R { - &self.reader - } - - /// Consumes the decoder and returns the underlying reader back. - #[inline] - #[allow(clippy::missing_const_for_fn)] - pub fn into_reader(self) -> R { - self.reader - } -} - -impl<R: Reader> Decoder<R> { - #[inline] - fn new_impl(mut reader: R) -> Result<Self> { - let header = reader.decode_header()?; - Ok(Self { reader, header, channels: header.channels }) - } - - /// Returns a new decoder with modified number of channels. - /// - /// By default, the number of channels in the decoded image will be equal - /// to whatever is specified in the header. However, it is also possible - /// to decode RGB into RGBA (in which case the alpha channel will be set - /// to 255), and vice versa (in which case the alpha channel will be ignored). - #[inline] - pub const fn with_channels(mut self, channels: Channels) -> Self { - self.channels = channels; - self - } - - /// Returns the number of channels in the decoded image. - /// - /// Note: this may differ from the number of channels specified in the header. - #[inline] - pub const fn channels(&self) -> Channels { - self.channels - } - - /// Returns the decoded image header. - #[inline] - pub const fn header(&self) -> &Header { - &self.header - } - - /// The number of bytes the decoded image will take. - /// - /// Can be used to pre-allocate the buffer to decode the image into. - #[inline] - pub const fn required_buf_len(&self) -> usize { - self.header.n_pixels().saturating_mul(self.channels.as_u8() as usize) - } - - /// Decodes the image to a pre-allocated buffer and returns the number of bytes written. - /// - /// The minimum size of the buffer can be found via [`Decoder::required_buf_len`]. - #[inline] - pub fn decode_to_buf(&mut self, mut buf: impl AsMut<[u8]>) -> Result<usize> { - let buf = buf.as_mut(); - let size = self.required_buf_len(); - if unlikely(buf.len() < size) { - return Err(Error::OutputBufferTooSmall { size: buf.len(), required: size }); - } - self.reader.decode_image(buf, self.channels.as_u8(), self.header.channels.as_u8())?; - Ok(size) - } - - /// Decodes the image into a newly allocated vector of bytes and returns it. - #[cfg(any(feature = "std", feature = "alloc"))] - #[inline] - pub fn decode_to_vec(&mut self) -> Result<Vec<u8>> { - let mut out = vec![0; self.header.n_pixels() * self.channels.as_u8() as usize]; - let _ = self.decode_to_buf(&mut out)?; - Ok(out) - } -} diff --git a/vendor/qoi/src/encode.rs b/vendor/qoi/src/encode.rs deleted file mode 100644 index 0ed8476..0000000 --- a/vendor/qoi/src/encode.rs +++ /dev/null @@ -1,210 +0,0 @@ -#[cfg(any(feature = "std", feature = "alloc"))] -use alloc::{vec, vec::Vec}; -use core::convert::TryFrom; -#[cfg(feature = "std")] -use std::io::Write; - -use bytemuck::Pod; - -use crate::consts::{QOI_HEADER_SIZE, QOI_OP_INDEX, QOI_OP_RUN, QOI_PADDING, QOI_PADDING_SIZE}; -use crate::error::{Error, Result}; -use crate::header::Header; -use crate::pixel::{Pixel, SupportedChannels}; -use crate::types::{Channels, ColorSpace}; -#[cfg(feature = "std")] -use crate::utils::GenericWriter; -use crate::utils::{unlikely, BytesMut, Writer}; - -#[allow(clippy::cast_possible_truncation, unused_assignments, unused_variables)] -fn encode_impl<W: Writer, const N: usize>(mut buf: W, data: &[u8]) -> Result<usize> -where - Pixel<N>: SupportedChannels, - [u8; N]: Pod, -{ - let cap = buf.capacity(); - - let mut index = [Pixel::new(); 256]; - let mut px_prev = Pixel::new().with_a(0xff); - let mut hash_prev = px_prev.hash_index(); - let mut run = 0_u8; - let mut px = Pixel::<N>::new().with_a(0xff); - let mut index_allowed = false; - - let n_pixels = data.len() / N; - - for (i, chunk) in data.chunks_exact(N).enumerate() { - px.read(chunk); - if px == px_prev { - run += 1; - if run == 62 || unlikely(i == n_pixels - 1) { - buf = buf.write_one(QOI_OP_RUN | (run - 1))?; - run = 0; - } - } else { - if run != 0 { - #[cfg(not(feature = "reference"))] - { - // credits for the original idea: @zakarumych (had to be fixed though) - buf = buf.write_one(if run == 1 && index_allowed { - QOI_OP_INDEX | hash_prev - } else { - QOI_OP_RUN | (run - 1) - })?; - } - #[cfg(feature = "reference")] - { - buf = buf.write_one(QOI_OP_RUN | (run - 1))?; - } - run = 0; - } - index_allowed = true; - let px_rgba = px.as_rgba(0xff); - hash_prev = px_rgba.hash_index(); - let index_px = &mut index[hash_prev as usize]; - if *index_px == px_rgba { - buf = buf.write_one(QOI_OP_INDEX | hash_prev)?; - } else { - *index_px = px_rgba; - buf = px.encode_into(px_prev, buf)?; - } - px_prev = px; - } - } - - buf = buf.write_many(&QOI_PADDING)?; - Ok(cap.saturating_sub(buf.capacity())) -} - -#[inline] -fn encode_impl_all<W: Writer>(out: W, data: &[u8], channels: Channels) -> Result<usize> { - match channels { - Channels::Rgb => encode_impl::<_, 3>(out, data), - Channels::Rgba => encode_impl::<_, 4>(out, data), - } -} - -/// The maximum number of bytes the encoded image will take. -/// -/// Can be used to pre-allocate the buffer to encode the image into. -#[inline] -pub fn encode_max_len(width: u32, height: u32, channels: impl Into<u8>) -> usize { - let (width, height) = (width as usize, height as usize); - let n_pixels = width.saturating_mul(height); - QOI_HEADER_SIZE - + n_pixels.saturating_mul(channels.into() as usize) - + n_pixels - + QOI_PADDING_SIZE -} - -/// Encode the image into a pre-allocated buffer. -/// -/// Returns the total number of bytes written. -#[inline] -pub fn encode_to_buf( - buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>, width: u32, height: u32, -) -> Result<usize> { - Encoder::new(&data, width, height)?.encode_to_buf(buf) -} - -/// Encode the image into a newly allocated vector. -#[cfg(any(feature = "alloc", feature = "std"))] -#[inline] -pub fn encode_to_vec(data: impl AsRef<[u8]>, width: u32, height: u32) -> Result<Vec<u8>> { - Encoder::new(&data, width, height)?.encode_to_vec() -} - -/// Encode QOI images into buffers or into streams. -pub struct Encoder<'a> { - data: &'a [u8], - header: Header, -} - -impl<'a> Encoder<'a> { - /// Creates a new encoder from a given array of pixel data and image dimensions. - /// - /// The number of channels will be inferred automatically (the valid values - /// are 3 or 4). The color space will be set to sRGB by default. - #[inline] - #[allow(clippy::cast_possible_truncation)] - pub fn new(data: &'a (impl AsRef<[u8]> + ?Sized), width: u32, height: u32) -> Result<Self> { - let data = data.as_ref(); - let mut header = - Header::try_new(width, height, Channels::default(), ColorSpace::default())?; - let size = data.len(); - let n_channels = size / header.n_pixels(); - if header.n_pixels() * n_channels != size { - return Err(Error::InvalidImageLength { size, width, height }); - } - header.channels = Channels::try_from(n_channels.min(0xff) as u8)?; - Ok(Self { data, header }) - } - - /// Returns a new encoder with modified color space. - /// - /// Note: the color space doesn't affect encoding or decoding in any way, it's - /// a purely informative field that's stored in the image header. - #[inline] - pub const fn with_colorspace(mut self, colorspace: ColorSpace) -> Self { - self.header = self.header.with_colorspace(colorspace); - self - } - - /// Returns the inferred number of channels. - #[inline] - pub const fn channels(&self) -> Channels { - self.header.channels - } - - /// Returns the header that will be stored in the encoded image. - #[inline] - pub const fn header(&self) -> &Header { - &self.header - } - - /// The maximum number of bytes the encoded image will take. - /// - /// Can be used to pre-allocate the buffer to encode the image into. - #[inline] - pub fn required_buf_len(&self) -> usize { - self.header.encode_max_len() - } - - /// Encodes the image to a pre-allocated buffer and returns the number of bytes written. - /// - /// The minimum size of the buffer can be found via [`Encoder::required_buf_len`]. - #[inline] - pub fn encode_to_buf(&self, mut buf: impl AsMut<[u8]>) -> Result<usize> { - let buf = buf.as_mut(); - let size_required = self.required_buf_len(); - if unlikely(buf.len() < size_required) { - return Err(Error::OutputBufferTooSmall { size: buf.len(), required: size_required }); - } - let (head, tail) = buf.split_at_mut(QOI_HEADER_SIZE); // can't panic - head.copy_from_slice(&self.header.encode()); - let n_written = encode_impl_all(BytesMut::new(tail), self.data, self.header.channels)?; - Ok(QOI_HEADER_SIZE + n_written) - } - - /// Encodes the image into a newly allocated vector of bytes and returns it. - #[cfg(any(feature = "alloc", feature = "std"))] - #[inline] - pub fn encode_to_vec(&self) -> Result<Vec<u8>> { - let mut out = vec![0_u8; self.required_buf_len()]; - let size = self.encode_to_buf(&mut out)?; - out.truncate(size); - Ok(out) - } - - /// Encodes the image directly to a generic writer that implements [`Write`](std::io::Write). - /// - /// Note: while it's possible to pass a `&mut [u8]` slice here since it implements `Write`, - /// it would more effficient to use a specialized method instead: [`Encoder::encode_to_buf`]. - #[cfg(feature = "std")] - #[inline] - pub fn encode_to_stream<W: Write>(&self, writer: &mut W) -> Result<usize> { - writer.write_all(&self.header.encode())?; - let n_written = - encode_impl_all(GenericWriter::new(writer), self.data, self.header.channels)?; - Ok(n_written + QOI_HEADER_SIZE) - } -} diff --git a/vendor/qoi/src/error.rs b/vendor/qoi/src/error.rs deleted file mode 100644 index 2b90636..0000000 --- a/vendor/qoi/src/error.rs +++ /dev/null @@ -1,82 +0,0 @@ -use core::convert::Infallible; -use core::fmt::{self, Display}; - -use crate::consts::QOI_MAGIC; - -/// Errors that can occur during encoding or decoding. -#[derive(Debug)] -pub enum Error { - /// Leading 4 magic bytes don't match when decoding - InvalidMagic { magic: u32 }, - /// Invalid number of channels: expected 3 or 4 - InvalidChannels { channels: u8 }, - /// Invalid color space: expected 0 or 1 - InvalidColorSpace { colorspace: u8 }, - /// Invalid image dimensions: can't be empty or larger than 400Mp - InvalidImageDimensions { width: u32, height: u32 }, - /// Image dimensions are inconsistent with image buffer length - InvalidImageLength { size: usize, width: u32, height: u32 }, - /// Output buffer is too small to fit encoded/decoded image - OutputBufferTooSmall { size: usize, required: usize }, - /// Input buffer ended unexpectedly before decoding was finished - UnexpectedBufferEnd, - /// Invalid stream end marker encountered when decoding - InvalidPadding, - #[cfg(feature = "std")] - /// Generic I/O error from the wrapped reader/writer - IoError(std::io::Error), -} - -/// Alias for [`Result`](std::result::Result) with the error type of [`Error`]. -pub type Result<T> = core::result::Result<T, Error>; - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Self::InvalidMagic { magic } => { - write!(f, "invalid magic: expected {:?}, got {:?}", QOI_MAGIC, magic.to_be_bytes()) - } - Self::InvalidChannels { channels } => { - write!(f, "invalid number of channels: {}", channels) - } - Self::InvalidColorSpace { colorspace } => { - write!(f, "invalid color space: {} (expected 0 or 1)", colorspace) - } - Self::InvalidImageDimensions { width, height } => { - write!(f, "invalid image dimensions: {}x{}", width, height) - } - Self::InvalidImageLength { size, width, height } => { - write!(f, "invalid image length: {} bytes for {}x{}", size, width, height) - } - Self::OutputBufferTooSmall { size, required } => { - write!(f, "output buffer size too small: {} (required: {})", size, required) - } - Self::UnexpectedBufferEnd => { - write!(f, "unexpected input buffer end while decoding") - } - Self::InvalidPadding => { - write!(f, "invalid padding (stream end marker mismatch)") - } - #[cfg(feature = "std")] - Self::IoError(ref err) => { - write!(f, "i/o error: {}", err) - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} - -impl From<Infallible> for Error { - fn from(_: Infallible) -> Self { - unreachable!() - } -} - -#[cfg(feature = "std")] -impl From<std::io::Error> for Error { - fn from(err: std::io::Error) -> Self { - Self::IoError(err) - } -} diff --git a/vendor/qoi/src/header.rs b/vendor/qoi/src/header.rs deleted file mode 100644 index ccdb1cc..0000000 --- a/vendor/qoi/src/header.rs +++ /dev/null @@ -1,120 +0,0 @@ -use core::convert::TryInto; - -use bytemuck::cast_slice; - -use crate::consts::{QOI_HEADER_SIZE, QOI_MAGIC, QOI_PIXELS_MAX}; -use crate::encode_max_len; -use crate::error::{Error, Result}; -use crate::types::{Channels, ColorSpace}; -use crate::utils::unlikely; - -/// Image header: dimensions, channels, color space. -/// -/// ### Notes -/// A valid image header must satisfy the following conditions: -/// * Both width and height must be non-zero. -/// * Maximum number of pixels is 400Mp (=4e8 pixels). -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct Header { - /// Image width in pixels - pub width: u32, - /// Image height in pixels - pub height: u32, - /// Number of 8-bit channels per pixel - pub channels: Channels, - /// Color space (informative field, doesn't affect encoding) - pub colorspace: ColorSpace, -} - -impl Default for Header { - #[inline] - fn default() -> Self { - Self { - width: 1, - height: 1, - channels: Channels::default(), - colorspace: ColorSpace::default(), - } - } -} - -impl Header { - /// Creates a new header and validates image dimensions. - #[inline] - pub const fn try_new( - width: u32, height: u32, channels: Channels, colorspace: ColorSpace, - ) -> Result<Self> { - let n_pixels = (width as usize).saturating_mul(height as usize); - if unlikely(n_pixels == 0 || n_pixels > QOI_PIXELS_MAX) { - return Err(Error::InvalidImageDimensions { width, height }); - } - Ok(Self { width, height, channels, colorspace }) - } - - /// Creates a new header with modified channels. - #[inline] - pub const fn with_channels(mut self, channels: Channels) -> Self { - self.channels = channels; - self - } - - /// Creates a new header with modified color space. - #[inline] - pub const fn with_colorspace(mut self, colorspace: ColorSpace) -> Self { - self.colorspace = colorspace; - self - } - - /// Serializes the header into a bytes array. - #[inline] - pub(crate) fn encode(&self) -> [u8; QOI_HEADER_SIZE] { - let mut out = [0; QOI_HEADER_SIZE]; - out[..4].copy_from_slice(&QOI_MAGIC.to_be_bytes()); - out[4..8].copy_from_slice(&self.width.to_be_bytes()); - out[8..12].copy_from_slice(&self.height.to_be_bytes()); - out[12] = self.channels.into(); - out[13] = self.colorspace.into(); - out - } - - /// Deserializes the header from a byte array. - #[inline] - pub(crate) fn decode(data: impl AsRef<[u8]>) -> Result<Self> { - let data = data.as_ref(); - if unlikely(data.len() < QOI_HEADER_SIZE) { - return Err(Error::UnexpectedBufferEnd); - } - let v = cast_slice::<_, [u8; 4]>(&data[..12]); - let magic = u32::from_be_bytes(v[0]); - let width = u32::from_be_bytes(v[1]); - let height = u32::from_be_bytes(v[2]); - let channels = data[12].try_into()?; - let colorspace = data[13].try_into()?; - if unlikely(magic != QOI_MAGIC) { - return Err(Error::InvalidMagic { magic }); - } - Self::try_new(width, height, channels, colorspace) - } - - /// Returns a number of pixels in the image. - #[inline] - pub const fn n_pixels(&self) -> usize { - (self.width as usize).saturating_mul(self.height as usize) - } - - /// Returns the total number of bytes in the raw pixel array. - /// - /// This may come useful when pre-allocating a buffer to decode the image into. - #[inline] - pub const fn n_bytes(&self) -> usize { - self.n_pixels() * self.channels.as_u8() as usize - } - - /// The maximum number of bytes the encoded image will take. - /// - /// Can be used to pre-allocate the buffer to encode the image into. - #[inline] - pub fn encode_max_len(&self) -> usize { - encode_max_len(self.width, self.height, self.channels) - } -} diff --git a/vendor/qoi/src/lib.rs b/vendor/qoi/src/lib.rs deleted file mode 100644 index f77506b..0000000 --- a/vendor/qoi/src/lib.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! Fast encoder/decoder for [QOI image format](https://qoiformat.org/), implemented in pure and safe Rust. -//! -//! - One of the [fastest](#benchmarks) QOI encoders/decoders out there. -//! - Compliant with the [latest](https://qoiformat.org/qoi-specification.pdf) QOI format specification. -//! - Zero unsafe code. -//! - Supports decoding from / encoding to `std::io` streams directly. -//! - `no_std` support. -//! - Roundtrip-tested vs the reference C implementation; fuzz-tested. -//! -//! ### Examples -//! -//! ```rust -//! use qoi::{encode_to_vec, decode_to_vec}; -//! -//! let encoded = encode_to_vec(&pixels, width, height)?; -//! let (header, decoded) = decode_to_vec(&encoded)?; -//! -//! assert_eq!(header.width, width); -//! assert_eq!(header.height, height); -//! assert_eq!(decoded, pixels); -//! ``` -//! -//! ### Benchmarks -//! -//! ``` -//! decode:Mp/s encode:Mp/s decode:MB/s encode:MB/s -//! qoi.h 282.9 225.3 978.3 778.9 -//! qoi-rust 427.4 290.0 1477.7 1002.9 -//! ``` -//! -//! - Reference C implementation: -//! [phoboslab/qoi@00e34217](https://github.com/phoboslab/qoi/commit/00e34217). -//! - Benchmark timings were collected on an Apple M1 laptop. -//! - 2846 images from the suite provided upstream -//! ([tarball](https://phoboslab.org/files/qoibench/qoi_benchmark_suite.tar)): -//! all pngs except two with broken checksums. -//! - 1.32 GPixels in total with 4.46 GB of raw pixel data. -//! -//! Benchmarks have also been run for all of the other Rust implementations -//! of QOI for comparison purposes and, at the time of writing this document, -//! this library proved to be the fastest one by a noticeable margin. -//! -//! ### Rust version -//! -//! The minimum supported Rust version is 1.51.0 (any changes to this would be -//! considered to be a breaking change). -//! -//! ### `no_std` -//! -//! This crate supports `no_std` mode. By default, std is enabled via the `std` -//! feature. You can deactivate the `default-features` to target core instead. -//! In that case anything related to `std::io`, `std::error::Error` and heap -//! allocations is disabled. There is an additional `alloc` feature that can -//! be activated to bring back the support for heap allocations. - -#![forbid(unsafe_code)] -#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] -#![allow( - clippy::inline_always, - clippy::similar_names, - clippy::missing_errors_doc, - clippy::must_use_candidate, - clippy::module_name_repetitions, - clippy::cargo_common_metadata, - clippy::doc_markdown, - clippy::return_self_not_must_use, -)] -#![cfg_attr(not(any(feature = "std", test)), no_std)] -#[cfg(all(feature = "alloc", not(any(feature = "std", test))))] -extern crate alloc; -#[cfg(any(feature = "std", test))] -extern crate std as alloc; - -mod decode; -mod encode; -mod error; -mod header; -mod pixel; -mod types; -mod utils; - -#[doc(hidden)] -pub mod consts; - -#[cfg(any(feature = "alloc", feature = "std"))] -pub use crate::decode::decode_to_vec; -pub use crate::decode::{decode_header, decode_to_buf, Decoder}; - -#[cfg(any(feature = "alloc", feature = "std"))] -pub use crate::encode::encode_to_vec; -pub use crate::encode::{encode_max_len, encode_to_buf, Encoder}; - -pub use crate::error::{Error, Result}; -pub use crate::header::Header; -pub use crate::types::{Channels, ColorSpace}; diff --git a/vendor/qoi/src/pixel.rs b/vendor/qoi/src/pixel.rs deleted file mode 100644 index d103494..0000000 --- a/vendor/qoi/src/pixel.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::consts::{QOI_OP_DIFF, QOI_OP_LUMA, QOI_OP_RGB, QOI_OP_RGBA}; -use crate::error::Result; -use crate::utils::Writer; -use bytemuck::{cast, Pod}; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[repr(transparent)] -pub struct Pixel<const N: usize>([u8; N]); - -impl<const N: usize> Pixel<N> { - #[inline] - pub const fn new() -> Self { - Self([0; N]) - } - - #[inline] - pub fn read(&mut self, s: &[u8]) { - if s.len() == N { - let mut i = 0; - while i < N { - self.0[i] = s[i]; - i += 1; - } - } else { - unreachable!(); - } - } - - #[inline] - pub fn update<const M: usize>(&mut self, px: Pixel<M>) { - let mut i = 0; - while i < M && i < N { - self.0[i] = px.0[i]; - i += 1; - } - } - - #[inline] - pub fn update_rgb(&mut self, r: u8, g: u8, b: u8) { - self.0[0] = r; - self.0[1] = g; - self.0[2] = b; - } - - #[inline] - pub fn update_rgba(&mut self, r: u8, g: u8, b: u8, a: u8) { - self.0[0] = r; - self.0[1] = g; - self.0[2] = b; - if N >= 4 { - self.0[3] = a; - } - } - - #[inline] - pub fn update_diff(&mut self, b1: u8) { - self.0[0] = self.0[0].wrapping_add((b1 >> 4) & 0x03).wrapping_sub(2); - self.0[1] = self.0[1].wrapping_add((b1 >> 2) & 0x03).wrapping_sub(2); - self.0[2] = self.0[2].wrapping_add(b1 & 0x03).wrapping_sub(2); - } - - #[inline] - pub fn update_luma(&mut self, b1: u8, b2: u8) { - let vg = (b1 & 0x3f).wrapping_sub(32); - let vg_8 = vg.wrapping_sub(8); - let vr = vg_8.wrapping_add((b2 >> 4) & 0x0f); - let vb = vg_8.wrapping_add(b2 & 0x0f); - self.0[0] = self.0[0].wrapping_add(vr); - self.0[1] = self.0[1].wrapping_add(vg); - self.0[2] = self.0[2].wrapping_add(vb); - } - - #[inline] - pub const fn as_rgba(self, with_a: u8) -> Pixel<4> { - let mut i = 0; - let mut out = Pixel::new(); - while i < N { - out.0[i] = self.0[i]; - i += 1; - } - if N < 4 { - out.0[3] = with_a; - } - out - } - - #[inline] - pub const fn r(self) -> u8 { - self.0[0] - } - - #[inline] - pub const fn g(self) -> u8 { - self.0[1] - } - - #[inline] - pub const fn b(self) -> u8 { - self.0[2] - } - - #[inline] - pub const fn with_a(mut self, value: u8) -> Self { - if N >= 4 { - self.0[3] = value; - } - self - } - - #[inline] - pub const fn a_or(self, value: u8) -> u8 { - if N < 4 { - value - } else { - self.0[3] - } - } - - #[inline] - #[allow(clippy::cast_lossless, clippy::cast_possible_truncation)] - pub fn hash_index(self) -> u8 - where - [u8; N]: Pod, - { - // credits for the initial idea: @zakarumych - let v = if N == 4 { - u32::from_ne_bytes(cast(self.0)) - } else { - u32::from_ne_bytes([self.0[0], self.0[1], self.0[2], 0xff]) - } as u64; - let s = ((v & 0xff00_ff00) << 32) | (v & 0x00ff_00ff); - s.wrapping_mul(0x0300_0700_0005_000b_u64).to_le().swap_bytes() as u8 & 63 - } - - #[inline] - pub fn rgb_add(&mut self, r: u8, g: u8, b: u8) { - self.0[0] = self.0[0].wrapping_add(r); - self.0[1] = self.0[1].wrapping_add(g); - self.0[2] = self.0[2].wrapping_add(b); - } - - #[inline] - pub fn encode_into<W: Writer>(&self, px_prev: Self, buf: W) -> Result<W> { - if N == 3 || self.a_or(0) == px_prev.a_or(0) { - let vg = self.g().wrapping_sub(px_prev.g()); - let vg_32 = vg.wrapping_add(32); - if vg_32 | 63 == 63 { - let vr = self.r().wrapping_sub(px_prev.r()); - let vb = self.b().wrapping_sub(px_prev.b()); - let vg_r = vr.wrapping_sub(vg); - let vg_b = vb.wrapping_sub(vg); - let (vr_2, vg_2, vb_2) = - (vr.wrapping_add(2), vg.wrapping_add(2), vb.wrapping_add(2)); - if vr_2 | vg_2 | vb_2 | 3 == 3 { - buf.write_one(QOI_OP_DIFF | vr_2 << 4 | vg_2 << 2 | vb_2) - } else { - let (vg_r_8, vg_b_8) = (vg_r.wrapping_add(8), vg_b.wrapping_add(8)); - if vg_r_8 | vg_b_8 | 15 == 15 { - buf.write_many(&[QOI_OP_LUMA | vg_32, vg_r_8 << 4 | vg_b_8]) - } else { - buf.write_many(&[QOI_OP_RGB, self.r(), self.g(), self.b()]) - } - } - } else { - buf.write_many(&[QOI_OP_RGB, self.r(), self.g(), self.b()]) - } - } else { - buf.write_many(&[QOI_OP_RGBA, self.r(), self.g(), self.b(), self.a_or(0xff)]) - } - } -} - -impl<const N: usize> From<Pixel<N>> for [u8; N] { - #[inline(always)] - fn from(px: Pixel<N>) -> Self { - px.0 - } -} - -pub trait SupportedChannels {} - -impl SupportedChannels for Pixel<3> {} -impl SupportedChannels for Pixel<4> {} diff --git a/vendor/qoi/src/types.rs b/vendor/qoi/src/types.rs deleted file mode 100644 index 81229d4..0000000 --- a/vendor/qoi/src/types.rs +++ /dev/null @@ -1,113 +0,0 @@ -use core::convert::TryFrom; - -use crate::error::{Error, Result}; -use crate::utils::unlikely; - -/// Image color space. -/// -/// Note: the color space is purely informative. Although it is saved to the -/// file header, it does not affect encoding/decoding in any way. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] -#[repr(u8)] -pub enum ColorSpace { - /// sRGB with linear alpha - Srgb = 0, - /// All channels are linear - Linear = 1, -} - -impl ColorSpace { - /// Returns true if the color space is sRGB with linear alpha. - pub const fn is_srgb(self) -> bool { - matches!(self, Self::Srgb) - } - - /// Returns true is all channels are linear. - pub const fn is_linear(self) -> bool { - matches!(self, Self::Linear) - } - - /// Converts to an integer (0 if sRGB, 1 if all linear). - pub const fn as_u8(self) -> u8 { - self as u8 - } -} - -impl Default for ColorSpace { - fn default() -> Self { - Self::Srgb - } -} - -impl From<ColorSpace> for u8 { - #[inline] - fn from(colorspace: ColorSpace) -> Self { - colorspace as Self - } -} - -impl TryFrom<u8> for ColorSpace { - type Error = Error; - - #[inline] - fn try_from(colorspace: u8) -> Result<Self> { - if unlikely(colorspace | 1 != 1) { - Err(Error::InvalidColorSpace { colorspace }) - } else { - Ok(if colorspace == 0 { Self::Srgb } else { Self::Linear }) - } - } -} - -/// Number of 8-bit channels in a pixel. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] -#[repr(u8)] -pub enum Channels { - /// Three 8-bit channels (RGB) - Rgb = 3, - /// Four 8-bit channels (RGBA) - Rgba = 4, -} - -impl Channels { - /// Returns true if there are 3 channels (RGB). - pub const fn is_rgb(self) -> bool { - matches!(self, Self::Rgb) - } - - /// Returns true if there are 4 channels (RGBA). - pub const fn is_rgba(self) -> bool { - matches!(self, Self::Rgba) - } - - /// Converts to an integer (3 if RGB, 4 if RGBA). - pub const fn as_u8(self) -> u8 { - self as u8 - } -} - -impl Default for Channels { - fn default() -> Self { - Self::Rgb - } -} - -impl From<Channels> for u8 { - #[inline] - fn from(channels: Channels) -> Self { - channels as Self - } -} - -impl TryFrom<u8> for Channels { - type Error = Error; - - #[inline] - fn try_from(channels: u8) -> Result<Self> { - if unlikely(channels != 3 && channels != 4) { - Err(Error::InvalidChannels { channels }) - } else { - Ok(if channels == 3 { Self::Rgb } else { Self::Rgba }) - } - } -} diff --git a/vendor/qoi/src/utils.rs b/vendor/qoi/src/utils.rs deleted file mode 100644 index d0c37a6..0000000 --- a/vendor/qoi/src/utils.rs +++ /dev/null @@ -1,107 +0,0 @@ -#[cfg(feature = "std")] -use std::io::Write; - -use crate::error::Result; - -#[inline(always)] -#[cold] -pub const fn cold() {} - -#[inline(always)] -#[allow(unused)] -pub const fn likely(b: bool) -> bool { - if !b { - cold(); - } - b -} - -#[inline(always)] -pub const fn unlikely(b: bool) -> bool { - if b { - cold(); - } - b -} - -pub trait Writer: Sized { - fn write_one(self, v: u8) -> Result<Self>; - fn write_many(self, v: &[u8]) -> Result<Self>; - fn capacity(&self) -> usize; -} - -pub struct BytesMut<'a>(&'a mut [u8]); - -impl<'a> BytesMut<'a> { - pub fn new(buf: &'a mut [u8]) -> Self { - Self(buf) - } - - #[inline] - pub fn write_one(self, v: u8) -> Self { - if let Some((first, tail)) = self.0.split_first_mut() { - *first = v; - Self(tail) - } else { - unreachable!() - } - } - - #[inline] - pub fn write_many(self, v: &[u8]) -> Self { - if v.len() <= self.0.len() { - let (head, tail) = self.0.split_at_mut(v.len()); - head.copy_from_slice(v); - Self(tail) - } else { - unreachable!() - } - } -} - -impl<'a> Writer for BytesMut<'a> { - #[inline] - fn write_one(self, v: u8) -> Result<Self> { - Ok(BytesMut::write_one(self, v)) - } - - #[inline] - fn write_many(self, v: &[u8]) -> Result<Self> { - Ok(BytesMut::write_many(self, v)) - } - - #[inline] - fn capacity(&self) -> usize { - self.0.len() - } -} - -#[cfg(feature = "std")] -pub struct GenericWriter<W> { - writer: W, - n_written: usize, -} - -#[cfg(feature = "std")] -impl<W: Write> GenericWriter<W> { - pub const fn new(writer: W) -> Self { - Self { writer, n_written: 0 } - } -} - -#[cfg(feature = "std")] -impl<W: Write> Writer for GenericWriter<W> { - fn write_one(mut self, v: u8) -> Result<Self> { - self.n_written += 1; - self.writer.write_all(&[v]).map(|_| self).map_err(Into::into) - } - - fn write_many(mut self, v: &[u8]) -> Result<Self> { - self.n_written += v.len(); - self.writer.write_all(v).map(|_| self).map_err(Into::into) - } - - fn capacity(&self) -> usize { - usize::MAX - self.n_written - } -} |