2022-01-02 21:47:13 +00:00
|
|
|
use std::convert::TryInto;
|
|
|
|
|
2022-01-02 20:30:28 +00:00
|
|
|
use bytemuck::cast_slice;
|
|
|
|
|
2021-12-29 13:00:16 +00:00
|
|
|
use crate::consts::{QOI_HEADER_SIZE, QOI_MAGIC, QOI_PIXELS_MAX};
|
2022-01-02 21:47:13 +00:00
|
|
|
use crate::encoded_size_limit;
|
2021-12-01 17:07:21 +00:00
|
|
|
use crate::error::{Error, Result};
|
2022-01-02 21:47:13 +00:00
|
|
|
use crate::types::{Channels, ColorSpace};
|
2021-12-01 17:07:21 +00:00
|
|
|
use crate::utils::unlikely;
|
2021-11-28 12:36:12 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct Header {
|
|
|
|
pub width: u32,
|
|
|
|
pub height: u32,
|
2022-01-02 21:47:13 +00:00
|
|
|
pub channels: Channels,
|
2021-11-29 22:30:29 +00:00
|
|
|
pub colorspace: ColorSpace,
|
2021-11-28 12:36:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Header {
|
2021-12-01 17:07:32 +00:00
|
|
|
#[inline]
|
2021-11-28 12:36:12 +00:00
|
|
|
fn default() -> Self {
|
2022-01-02 21:47:13 +00:00
|
|
|
Self {
|
|
|
|
width: 1,
|
|
|
|
height: 1,
|
|
|
|
channels: Channels::default(),
|
|
|
|
colorspace: ColorSpace::default(),
|
|
|
|
}
|
2021-11-28 12:36:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Header {
|
2021-12-02 15:57:57 +00:00
|
|
|
#[inline]
|
2022-01-02 21:47:13 +00:00
|
|
|
pub const fn try_new(
|
|
|
|
width: u32, height: u32, channels: Channels, colorspace: ColorSpace,
|
|
|
|
) -> Result<Self> {
|
|
|
|
if unlikely(height == 0 || width == 0) {
|
|
|
|
return Err(Error::EmptyImage { width, height });
|
|
|
|
} else if unlikely((width as usize).saturating_mul(height as usize) > QOI_PIXELS_MAX) {
|
|
|
|
return Err(Error::ImageTooLarge { width, height });
|
|
|
|
}
|
|
|
|
Ok(Self { width, height, channels, colorspace })
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub const fn with_channels(mut self, channels: Channels) -> Self {
|
|
|
|
self.channels = channels;
|
|
|
|
self
|
2021-12-02 15:57:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2021-12-31 10:37:56 +00:00
|
|
|
pub const fn with_colorspace(mut self, colorspace: ColorSpace) -> Self {
|
2021-12-31 10:34:27 +00:00
|
|
|
self.colorspace = colorspace;
|
2021-12-02 15:57:57 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2021-12-02 15:57:03 +00:00
|
|
|
#[inline]
|
|
|
|
pub const fn encoded_size() -> usize {
|
|
|
|
QOI_HEADER_SIZE
|
|
|
|
}
|
2021-11-28 12:36:12 +00:00
|
|
|
|
2021-12-01 17:07:32 +00:00
|
|
|
#[inline]
|
2021-12-31 10:34:27 +00:00
|
|
|
pub(crate) fn encode(&self) -> [u8; QOI_HEADER_SIZE] {
|
2021-11-28 12:36:12 +00:00
|
|
|
let mut out = [0; QOI_HEADER_SIZE];
|
2022-01-02 20:30:28 +00:00
|
|
|
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());
|
2022-01-02 21:47:13 +00:00
|
|
|
out[12] = self.channels.into();
|
2021-11-29 22:30:29 +00:00
|
|
|
out[13] = self.colorspace.into();
|
2021-11-28 12:36:12 +00:00
|
|
|
out
|
|
|
|
}
|
|
|
|
|
2021-12-01 17:07:32 +00:00
|
|
|
#[inline]
|
2021-12-31 10:34:27 +00:00
|
|
|
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::InputBufferTooSmall { size: data.len(), required: QOI_HEADER_SIZE });
|
|
|
|
}
|
2022-01-02 20:30:28 +00:00
|
|
|
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]);
|
2022-01-02 21:47:13 +00:00
|
|
|
let channels = data[12].try_into()?;
|
|
|
|
let colorspace = data[13].try_into()?;
|
2021-12-31 10:34:27 +00:00
|
|
|
if unlikely(magic != QOI_MAGIC) {
|
|
|
|
return Err(Error::InvalidMagic { magic });
|
2021-12-02 15:55:56 +00:00
|
|
|
}
|
2022-01-02 21:47:13 +00:00
|
|
|
Self::try_new(width, height, channels, colorspace)
|
2021-11-28 12:36:12 +00:00
|
|
|
}
|
2021-12-01 16:04:04 +00:00
|
|
|
|
2021-12-01 17:07:32 +00:00
|
|
|
#[inline]
|
2021-12-01 16:04:04 +00:00
|
|
|
pub const fn n_pixels(&self) -> usize {
|
|
|
|
(self.width as usize).saturating_mul(self.height as usize)
|
|
|
|
}
|
2022-01-02 21:47:13 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub const fn n_bytes(&self) -> usize {
|
|
|
|
self.n_pixels() * self.channels.as_u8() as usize
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn encoded_size_limit(&self) -> usize {
|
|
|
|
encoded_size_limit(self.width, self.height, self.channels)
|
|
|
|
}
|
2021-11-28 12:36:12 +00:00
|
|
|
}
|