use alloc::vec::Vec;
use enough::Stop;
use crate::error::BitmapError;
use crate::pixel::PixelLayout;
pub(crate) fn encode_qoi(
pixels: &[u8],
width: u32,
height: u32,
layout: PixelLayout,
stop: &dyn Stop,
) -> Result<Vec<u8>, BitmapError> {
let w = width as usize;
let h = height as usize;
let bpp = layout.bytes_per_pixel();
let expected = w
.checked_mul(h)
.and_then(|wh| wh.checked_mul(bpp))
.ok_or(BitmapError::DimensionsTooLarge { width, height })?;
if pixels.len() < expected {
return Err(BitmapError::BufferTooSmall {
needed: expected,
actual: pixels.len(),
});
}
stop.check()?;
let (qoi_pixels, colors) = match layout {
PixelLayout::Rgb8 => (None, rapid_qoi::Colors::Srgb),
PixelLayout::Rgba8 => (None, rapid_qoi::Colors::SrgbLinA),
PixelLayout::Bgr8 => {
stop.check()?;
let mut rgb = pixels[..expected].to_vec();
#[cfg(feature = "simd")]
{
let _ = garb::bytes::rgb_to_bgr_inplace(&mut rgb);
}
#[cfg(not(feature = "simd"))]
for pixel in rgb.chunks_exact_mut(3) {
pixel.swap(0, 2);
}
(Some(rgb), rapid_qoi::Colors::Srgb)
}
PixelLayout::Bgra8 => {
stop.check()?;
let mut rgba = pixels[..expected].to_vec();
#[cfg(feature = "simd")]
{
let _ = garb::bytes::rgba_to_bgra_inplace(&mut rgba);
}
#[cfg(not(feature = "simd"))]
for pixel in rgba.chunks_exact_mut(4) {
pixel.swap(0, 2);
}
(Some(rgba), rapid_qoi::Colors::SrgbLinA)
}
PixelLayout::Bgrx8 => {
stop.check()?;
let mut rgba = pixels[..expected].to_vec();
#[cfg(feature = "simd")]
{
let _ = garb::bytes::rgba_to_bgra_inplace(&mut rgba);
let _ = garb::bytes::fill_alpha_rgba(&mut rgba);
}
#[cfg(not(feature = "simd"))]
for pixel in rgba.chunks_exact_mut(4) {
pixel.swap(0, 2);
pixel[3] = 255;
}
(Some(rgba), rapid_qoi::Colors::SrgbLinA)
}
_ => {
return Err(BitmapError::UnsupportedVariant(alloc::format!(
"cannot encode {layout:?} as QOI (supported: Rgb8, Rgba8, Bgr8, Bgra8)"
)));
}
};
let qoi = rapid_qoi::Qoi {
width,
height,
colors,
};
let encode_data = qoi_pixels.as_deref().unwrap_or(&pixels[..expected]);
let encoded = qoi
.encode_alloc(encode_data)
.map_err(|e| BitmapError::InvalidData(alloc::format!("{e:?}")))?;
Ok(encoded)
}