2021-11-29 22:30:29 +00:00
|
|
|
use crate::colorspace::ColorSpace;
|
2021-12-29 13:00:16 +00:00
|
|
|
use crate::consts::{QOI_HEADER_SIZE, QOI_MAGIC, QOI_PIXELS_MAX};
|
2021-12-01 17:07:21 +00:00
|
|
|
use crate::error::{Error, Result};
|
|
|
|
use crate::utils::unlikely;
|
2021-11-28 12:36:12 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct Header {
|
2021-11-29 22:38:09 +00:00
|
|
|
pub magic: u32,
|
2021-11-28 12:36:12 +00:00
|
|
|
pub width: u32,
|
|
|
|
pub height: u32,
|
|
|
|
pub channels: u8,
|
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 {
|
2021-11-29 22:30:29 +00:00
|
|
|
Self {
|
|
|
|
magic: QOI_MAGIC,
|
2021-11-29 22:30:53 +00:00
|
|
|
width: 1,
|
|
|
|
height: 1,
|
2021-11-29 22:30:29 +00:00
|
|
|
channels: 3,
|
|
|
|
colorspace: ColorSpace::default(),
|
|
|
|
}
|
2021-11-28 12:36:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-01 17:07:32 +00:00
|
|
|
#[inline(always)]
|
2021-11-28 12:36:12 +00:00
|
|
|
const fn u32_to_be(v: u32) -> [u8; 4] {
|
|
|
|
[
|
2021-12-02 15:55:56 +00:00
|
|
|
((0xff00_0000 & v) >> 24) as u8,
|
|
|
|
((0x00ff_0000 & v) >> 16) as u8,
|
2021-11-28 12:36:12 +00:00
|
|
|
((0xff00 & v) >> 8) as u8,
|
2021-12-02 15:55:56 +00:00
|
|
|
(0x00ff & v) as u8,
|
2021-11-28 12:36:12 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2021-12-01 17:07:32 +00:00
|
|
|
#[inline(always)]
|
2021-11-28 12:36:12 +00:00
|
|
|
const fn u32_from_be(v: &[u8]) -> u32 {
|
|
|
|
((v[0] as u32) << 24) | ((v[1] as u32) << 16) | ((v[2] as u32) << 8) | (v[3] as u32)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Header {
|
2021-12-02 15:57:57 +00:00
|
|
|
#[inline]
|
|
|
|
pub const fn new(width: u32, height: u32, channels: u8) -> Self {
|
|
|
|
Self { magic: QOI_MAGIC, width, height, channels, colorspace: ColorSpace::from_u8(0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn with_colorspace(mut self, colorspace: impl Into<ColorSpace>) -> Self {
|
|
|
|
self.colorspace = colorspace.into();
|
|
|
|
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-11-28 12:36:12 +00:00
|
|
|
pub(crate) fn to_bytes(&self) -> [u8; QOI_HEADER_SIZE] {
|
|
|
|
let mut out = [0; QOI_HEADER_SIZE];
|
2021-11-29 22:38:09 +00:00
|
|
|
out[..4].copy_from_slice(&u32_to_be(self.magic));
|
2021-11-28 12:36:12 +00:00
|
|
|
out[4..8].copy_from_slice(&u32_to_be(self.width));
|
|
|
|
out[8..12].copy_from_slice(&u32_to_be(self.height));
|
|
|
|
out[12] = self.channels;
|
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-11-28 12:36:12 +00:00
|
|
|
pub(crate) fn from_bytes(v: [u8; QOI_HEADER_SIZE]) -> Self {
|
2021-12-02 15:55:56 +00:00
|
|
|
Self {
|
|
|
|
magic: u32_from_be(&v[..4]),
|
|
|
|
width: u32_from_be(&v[4..8]),
|
|
|
|
height: u32_from_be(&v[8..12]),
|
|
|
|
channels: v[12],
|
|
|
|
colorspace: v[13].into(),
|
|
|
|
}
|
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)
|
|
|
|
}
|
2021-12-01 17:07:21 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-12-02 15:55:56 +00:00
|
|
|
pub const fn validate(&self) -> Result<()> {
|
2021-12-01 17:07:21 +00:00
|
|
|
if unlikely(self.magic != QOI_MAGIC) {
|
|
|
|
return Err(Error::InvalidMagic { magic: self.magic });
|
|
|
|
} else if unlikely(self.height == 0 || self.width == 0) {
|
|
|
|
return Err(Error::EmptyImage { width: self.width, height: self.height });
|
2021-12-29 13:00:16 +00:00
|
|
|
} else if unlikely((self.height as usize) * (self.width as usize) > QOI_PIXELS_MAX) {
|
|
|
|
return Err(Error::ImageTooLarge { width: self.width, height: self.height });
|
2021-12-01 17:07:21 +00:00
|
|
|
} else if unlikely(self.channels < 3 || self.channels > 4) {
|
|
|
|
return Err(Error::InvalidChannels { channels: self.channels });
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-11-28 12:36:12 +00:00
|
|
|
}
|