Tons of docstrings + some renames
This commit is contained in:
parent
86d5462958
commit
f3120d5df3
6 changed files with 96 additions and 15 deletions
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "qoi-fast"
|
name = "qoi-fast"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
description = "VERY fast encoder/decoder for QOI (Quite Okay Image) format"
|
description = "Fast encoder/decoder for QOI (Quite Okay Image) format"
|
||||||
authors = ["Ivan Smirnov <rust@ivan.smirnov.ie>"]
|
authors = ["Ivan Smirnov <rust@ivan.smirnov.ie>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -106,6 +106,10 @@ fn decode_impl_slice_all(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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]
|
#[inline]
|
||||||
pub fn decode_to_buf(buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>) -> Result<Header> {
|
pub fn decode_to_buf(buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>) -> Result<Header> {
|
||||||
let mut decoder = Decoder::new(&data)?;
|
let mut decoder = Decoder::new(&data)?;
|
||||||
|
@ -113,6 +117,10 @@ pub fn decode_to_buf(buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>) -> Result<He
|
||||||
Ok(*decoder.header())
|
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"))]
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> {
|
pub fn decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> {
|
||||||
|
@ -121,6 +129,7 @@ pub fn decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> {
|
||||||
Ok((*decoder.header(), out))
|
Ok((*decoder.header(), out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode the image header from a slice of bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn decode_header(data: impl AsRef<[u8]>) -> Result<Header> {
|
pub fn decode_header(data: impl AsRef<[u8]>) -> Result<Header> {
|
||||||
Header::decode(data)
|
Header::decode(data)
|
||||||
|
@ -264,6 +273,7 @@ impl<R: Read> Reader for R {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode QOI images from slices or from streams.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Decoder<R> {
|
pub struct Decoder<R> {
|
||||||
reader: R,
|
reader: R,
|
||||||
|
@ -272,11 +282,19 @@ pub struct Decoder<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Decoder<Bytes<'a>> {
|
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]
|
#[inline]
|
||||||
pub fn new(data: &'a (impl AsRef<[u8]> + ?Sized)) -> Result<Self> {
|
pub fn new(data: &'a (impl AsRef<[u8]> + ?Sized)) -> Result<Self> {
|
||||||
Self::new_impl(Bytes::new(data.as_ref()))
|
Self::new_impl(Bytes::new(data.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the undecoded tail of the input slice of bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn data(&self) -> &[u8] {
|
pub const fn data(&self) -> &[u8] {
|
||||||
self.reader.as_slice()
|
self.reader.as_slice()
|
||||||
|
@ -285,16 +303,24 @@ impl<'a> Decoder<Bytes<'a>> {
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl<R: Read> Decoder<R> {
|
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]
|
#[inline]
|
||||||
pub fn from_stream(reader: R) -> Result<Self> {
|
pub fn from_stream(reader: R) -> Result<Self> {
|
||||||
Self::new_impl(reader)
|
Self::new_impl(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an immutable reference to the underlying reader.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reader(&self) -> &R {
|
pub fn reader(&self) -> &R {
|
||||||
&self.reader
|
&self.reader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes the decoder and returns the underlying reader back.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_reader(self) -> R {
|
pub fn into_reader(self) -> R {
|
||||||
self.reader
|
self.reader
|
||||||
|
@ -308,25 +334,47 @@ impl<R: Reader> Decoder<R> {
|
||||||
Ok(Self { reader, header, channels: header.channels })
|
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 fn with_channels(mut self, channels: Channels) -> Self {
|
pub fn with_channels(mut self, channels: Channels) -> Self {
|
||||||
self.channels = channels;
|
self.channels = channels;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of channels in the decoded image.
|
||||||
|
///
|
||||||
|
/// Note: this may differ from the number of channels specified in the header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn channels(&self) -> Channels {
|
pub fn channels(&self) -> Channels {
|
||||||
self.channels
|
self.channels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the decoded image header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn header(&self) -> &Header {
|
pub fn header(&self) -> &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 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]
|
#[inline]
|
||||||
pub fn decode_to_buf(&mut self, mut buf: impl AsMut<[u8]>) -> Result<usize> {
|
pub fn decode_to_buf(&mut self, mut buf: impl AsMut<[u8]>) -> Result<usize> {
|
||||||
let buf = buf.as_mut();
|
let buf = buf.as_mut();
|
||||||
let size = self.header.n_pixels() * self.channels.as_u8() as usize;
|
let size = self.required_buf_len();
|
||||||
if unlikely(buf.len() < size) {
|
if unlikely(buf.len() < size) {
|
||||||
return Err(Error::OutputBufferTooSmall { size: buf.len(), required: size });
|
return Err(Error::OutputBufferTooSmall { size: buf.len(), required: size });
|
||||||
}
|
}
|
||||||
|
@ -334,6 +382,7 @@ impl<R: Reader> Decoder<R> {
|
||||||
Ok(size)
|
Ok(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the image into a newly allocated vector of bytes and returns it.
|
||||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn decode_to_vec(&mut self) -> Result<Vec<u8>> {
|
pub fn decode_to_vec(&mut self) -> Result<Vec<u8>> {
|
||||||
|
|
|
@ -43,6 +43,7 @@ where
|
||||||
if run != 0 {
|
if run != 0 {
|
||||||
#[cfg(not(feature = "reference"))]
|
#[cfg(not(feature = "reference"))]
|
||||||
{
|
{
|
||||||
|
// credits for the original idea: @zakarumych
|
||||||
buf = buf.write_one(if run == 1 && i != 1 {
|
buf = buf.write_one(if run == 1 && i != 1 {
|
||||||
QOI_OP_INDEX | (hash_prev as u8)
|
QOI_OP_INDEX | (hash_prev as u8)
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,8 +81,11 @@ fn encode_impl_all<W: Writer>(out: W, data: &[u8], channels: Channels) -> Result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The maximum number of bytes the encoded image will take.
|
||||||
|
///
|
||||||
|
/// Can be used to pre-allocate the buffer to encode the image into.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn encode_size_limit(width: u32, height: u32, channels: impl Into<u8>) -> usize {
|
pub fn encode_max_len(width: u32, height: u32, channels: impl Into<u8>) -> usize {
|
||||||
let (width, height) = (width as usize, height as usize);
|
let (width, height) = (width as usize, height as usize);
|
||||||
let n_pixels = width.saturating_mul(height);
|
let n_pixels = width.saturating_mul(height);
|
||||||
QOI_HEADER_SIZE
|
QOI_HEADER_SIZE
|
||||||
|
@ -90,6 +94,9 @@ pub fn encode_size_limit(width: u32, height: u32, channels: impl Into<u8>) -> us
|
||||||
+ QOI_PADDING_SIZE
|
+ QOI_PADDING_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encode the image into a pre-allocated buffer.
|
||||||
|
///
|
||||||
|
/// Returns the total number of bytes written.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn encode_to_buf(
|
pub fn encode_to_buf(
|
||||||
buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>, width: u32, height: u32,
|
buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>, width: u32, height: u32,
|
||||||
|
@ -97,18 +104,24 @@ pub fn encode_to_buf(
|
||||||
Encoder::new(&data, width, height)?.encode_to_buf(buf)
|
Encoder::new(&data, width, height)?.encode_to_buf(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encode the image into a newly allocated vector.
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn encode_to_vec(data: impl AsRef<[u8]>, width: u32, height: u32) -> Result<Vec<u8>> {
|
pub fn encode_to_vec(data: impl AsRef<[u8]>, width: u32, height: u32) -> Result<Vec<u8>> {
|
||||||
Encoder::new(&data, width, height)?.encode_to_vec()
|
Encoder::new(&data, width, height)?.encode_to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encode QOI images into buffers or into streams.
|
||||||
pub struct Encoder<'a> {
|
pub struct Encoder<'a> {
|
||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
header: Header,
|
header: Header,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Encoder<'a> {
|
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]
|
#[inline]
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
pub fn new(data: &'a (impl AsRef<[u8]> + ?Sized), width: u32, height: u32) -> Result<Self> {
|
pub fn new(data: &'a (impl AsRef<[u8]> + ?Sized), width: u32, height: u32) -> Result<Self> {
|
||||||
|
@ -124,31 +137,43 @@ impl<'a> Encoder<'a> {
|
||||||
Ok(Self { data, header })
|
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]
|
#[inline]
|
||||||
pub const fn with_colorspace(mut self, colorspace: ColorSpace) -> Self {
|
pub const fn with_colorspace(mut self, colorspace: ColorSpace) -> Self {
|
||||||
self.header = self.header.with_colorspace(colorspace);
|
self.header = self.header.with_colorspace(colorspace);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the inferred number of channels.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn channels(&self) -> Channels {
|
pub const fn channels(&self) -> Channels {
|
||||||
self.header.channels
|
self.header.channels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the header that will be stored in the encoded image.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn header(&self) -> &Header {
|
pub const fn header(&self) -> &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]
|
#[inline]
|
||||||
pub fn encode_size_limit(&self) -> usize {
|
pub fn required_buf_len(&self) -> usize {
|
||||||
self.header.encode_size_limit()
|
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]
|
#[inline]
|
||||||
pub fn encode_to_buf(&self, mut buf: impl AsMut<[u8]>) -> Result<usize> {
|
pub fn encode_to_buf(&self, mut buf: impl AsMut<[u8]>) -> Result<usize> {
|
||||||
let buf = buf.as_mut();
|
let buf = buf.as_mut();
|
||||||
let size_required = self.encode_size_limit();
|
let size_required = self.required_buf_len();
|
||||||
if unlikely(buf.len() < size_required) {
|
if unlikely(buf.len() < size_required) {
|
||||||
return Err(Error::OutputBufferTooSmall { size: buf.len(), required: size_required });
|
return Err(Error::OutputBufferTooSmall { size: buf.len(), required: size_required });
|
||||||
}
|
}
|
||||||
|
@ -158,15 +183,20 @@ impl<'a> Encoder<'a> {
|
||||||
Ok(QOI_HEADER_SIZE + n_written)
|
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"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn encode_to_vec(&self) -> Result<Vec<u8>> {
|
pub fn encode_to_vec(&self) -> Result<Vec<u8>> {
|
||||||
let mut out = vec![0_u8; self.encode_size_limit()];
|
let mut out = vec![0_u8; self.required_buf_len()];
|
||||||
let size = self.encode_to_buf(&mut out)?;
|
let size = self.encode_to_buf(&mut out)?;
|
||||||
out.truncate(size);
|
out.truncate(size);
|
||||||
Ok(out)
|
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")]
|
#[cfg(feature = "std")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn encode_to_stream<W: Write>(&self, writer: &mut W) -> Result<usize> {
|
pub fn encode_to_stream<W: Write>(&self, writer: &mut W) -> Result<usize> {
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub enum Error {
|
||||||
IoError(std::io::Error),
|
IoError(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alias for `Result` with the error type `qoi_fast::Error`.
|
/// Alias for [`Result`](std::result::Result) with the error type of [`Error`].
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use core::convert::TryInto;
|
||||||
use bytemuck::cast_slice;
|
use bytemuck::cast_slice;
|
||||||
|
|
||||||
use crate::consts::{QOI_HEADER_SIZE, QOI_MAGIC, QOI_PIXELS_MAX};
|
use crate::consts::{QOI_HEADER_SIZE, QOI_MAGIC, QOI_PIXELS_MAX};
|
||||||
use crate::encode_size_limit;
|
use crate::encode_max_len;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::types::{Channels, ColorSpace};
|
use crate::types::{Channels, ColorSpace};
|
||||||
use crate::utils::unlikely;
|
use crate::utils::unlikely;
|
||||||
|
@ -102,17 +102,19 @@ impl Header {
|
||||||
(self.width as usize).saturating_mul(self.height as usize)
|
(self.width as usize).saturating_mul(self.height as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total number of bytes in the image.
|
/// 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]
|
#[inline]
|
||||||
pub const fn n_bytes(&self) -> usize {
|
pub const fn n_bytes(&self) -> usize {
|
||||||
self.n_pixels() * self.channels.as_u8() as usize
|
self.n_pixels() * self.channels.as_u8() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the maximum number of bytes the image can possibly occupy when QOI-encoded.
|
/// The maximum number of bytes the encoded image will take.
|
||||||
///
|
///
|
||||||
/// This comes useful when pre-allocating a buffer to encode the image into.
|
/// Can be used to pre-allocate the buffer to encode the image into.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn encode_size_limit(&self) -> usize {
|
pub fn encode_max_len(&self) -> usize {
|
||||||
encode_size_limit(self.width, self.height, self.channels)
|
encode_max_len(self.width, self.height, self.channels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub use crate::decode::{decode_header, decode_to_buf, Decoder};
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
pub use crate::encode::encode_to_vec;
|
pub use crate::encode::encode_to_vec;
|
||||||
pub use crate::encode::{encode_size_limit, encode_to_buf, Encoder};
|
pub use crate::encode::{encode_max_len, encode_to_buf, Encoder};
|
||||||
|
|
||||||
pub use crate::error::{Error, Result};
|
pub use crate::error::{Error, Result};
|
||||||
pub use crate::header::Header;
|
pub use crate::header::Header;
|
||||||
|
|
Loading…
Reference in a new issue