use alloc::vec::Vec;
use enough::Stop;
use crate::error::BitmapError;
use crate::pixel::PixelLayout;
pub(crate) fn encode_farbfeld(
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(),
});
}
let pixel_bytes = w
.checked_mul(h)
.and_then(|wh| wh.checked_mul(8))
.ok_or(BitmapError::DimensionsTooLarge { width, height })?;
let total = pixel_bytes
.checked_add(16)
.ok_or(BitmapError::DimensionsTooLarge { width, height })?;
let mut out = Vec::with_capacity(total);
out.extend_from_slice(b"farbfeld");
out.extend_from_slice(&width.to_be_bytes());
out.extend_from_slice(&height.to_be_bytes());
stop.check()?;
match layout {
PixelLayout::Rgba16 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w * 8).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for pair in row.chunks_exact(2) {
let val = u16::from_ne_bytes([pair[0], pair[1]]);
out.extend_from_slice(&val.to_be_bytes());
}
}
}
PixelLayout::Rgba8 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w * 4).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for &byte in row {
let val: u16 = byte as u16 * 257;
out.extend_from_slice(&val.to_be_bytes());
}
}
}
PixelLayout::Rgb8 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w * 3).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for pixel in row.chunks_exact(3) {
let r: u16 = pixel[0] as u16 * 257;
let g: u16 = pixel[1] as u16 * 257;
let b: u16 = pixel[2] as u16 * 257;
out.extend_from_slice(&r.to_be_bytes());
out.extend_from_slice(&g.to_be_bytes());
out.extend_from_slice(&b.to_be_bytes());
out.extend_from_slice(&65535u16.to_be_bytes());
}
}
}
PixelLayout::Bgra8 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w * 4).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for pixel in row.chunks_exact(4) {
let r: u16 = pixel[2] as u16 * 257;
let g: u16 = pixel[1] as u16 * 257;
let b: u16 = pixel[0] as u16 * 257;
let a: u16 = pixel[3] as u16 * 257;
out.extend_from_slice(&r.to_be_bytes());
out.extend_from_slice(&g.to_be_bytes());
out.extend_from_slice(&b.to_be_bytes());
out.extend_from_slice(&a.to_be_bytes());
}
}
}
PixelLayout::Bgrx8 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w * 4).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for pixel in row.chunks_exact(4) {
let r: u16 = pixel[2] as u16 * 257;
let g: u16 = pixel[1] as u16 * 257;
let b: u16 = pixel[0] as u16 * 257;
out.extend_from_slice(&r.to_be_bytes());
out.extend_from_slice(&g.to_be_bytes());
out.extend_from_slice(&b.to_be_bytes());
out.extend_from_slice(&65535u16.to_be_bytes());
}
}
}
PixelLayout::Bgr8 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w * 3).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for pixel in row.chunks_exact(3) {
let r: u16 = pixel[2] as u16 * 257;
let g: u16 = pixel[1] as u16 * 257;
let b: u16 = pixel[0] as u16 * 257;
out.extend_from_slice(&r.to_be_bytes());
out.extend_from_slice(&g.to_be_bytes());
out.extend_from_slice(&b.to_be_bytes());
out.extend_from_slice(&65535u16.to_be_bytes());
}
}
}
PixelLayout::Gray8 => {
for (row_idx, row) in pixels[..expected].chunks_exact(w).enumerate() {
if row_idx % 16 == 0 {
stop.check()?;
}
for &byte in row {
let val: u16 = byte as u16 * 257;
out.extend_from_slice(&val.to_be_bytes());
out.extend_from_slice(&val.to_be_bytes());
out.extend_from_slice(&val.to_be_bytes());
out.extend_from_slice(&65535u16.to_be_bytes());
}
}
}
_ => {
return Err(BitmapError::UnsupportedVariant(alloc::format!(
"cannot encode {:?} as farbfeld (supported: Rgba16, Rgba8, Rgb8, Gray8)",
layout
)));
}
}
Ok(out)
}