use std::marker::PhantomData;
use crate::types::{AA_SIZE, Pixel};
#[inline]
const fn u32_to_usize(v: u32) -> usize {
const {
assert!(
u32::BITS <= usize::BITS,
"platform has a narrower usize than u32"
);
}
v as usize
}
pub struct Bitmap<P: Pixel> {
pub width: u32,
pub height: u32,
pub stride: usize,
data: Vec<u8>,
alpha: Option<Vec<u8>>,
_marker: PhantomData<P>,
}
impl<P: Pixel> Bitmap<P> {
#[must_use]
pub fn new(width: u32, height: u32, row_pad: usize, with_alpha: bool) -> Self {
assert!(row_pad >= 1, "row_pad must be ≥ 1");
let raw_stride = u32_to_usize(width) * P::BYTES;
let stride = if row_pad <= 1 {
raw_stride
} else {
raw_stride.div_ceil(row_pad) * row_pad
};
let data = vec![0u8; stride * u32_to_usize(height)];
let alpha = if with_alpha {
Some(vec![0u8; u32_to_usize(width) * u32_to_usize(height)])
} else {
None
};
Self {
width,
height,
stride,
data,
alpha,
_marker: PhantomData,
}
}
#[must_use]
pub fn row(&self, y: u32) -> &[P] {
let bytes = self.row_bytes(y);
bytemuck::cast_slice(&bytes[..u32_to_usize(self.width) * P::BYTES])
}
pub fn row_mut(&mut self, y: u32) -> &mut [P] {
let w = u32_to_usize(self.width) * P::BYTES;
let bytes = self.row_bytes_mut(y);
bytemuck::cast_slice_mut(&mut bytes[..w])
}
#[inline]
fn assert_row_in_bounds(&self, y: u32) {
assert!(
y < self.height,
"row index {y} is out of bounds for bitmap height {}",
self.height
);
}
#[must_use]
pub fn row_bytes(&self, y: u32) -> &[u8] {
self.assert_row_in_bounds(y);
let off = u32_to_usize(y) * self.stride;
&self.data[off..off + self.stride]
}
pub fn row_bytes_mut(&mut self, y: u32) -> &mut [u8] {
self.assert_row_in_bounds(y);
let off = u32_to_usize(y) * self.stride;
&mut self.data[off..off + self.stride]
}
#[must_use]
pub fn alpha_row(&self, y: u32) -> Option<&[u8]> {
self.assert_row_in_bounds(y);
self.alpha.as_ref().map(|a| {
let w = u32_to_usize(self.width);
let off = u32_to_usize(y) * w;
&a[off..off + w]
})
}
#[must_use]
pub const fn has_alpha(&self) -> bool {
self.alpha.is_some()
}
#[must_use]
pub fn alpha_plane(&self) -> Option<&[u8]> {
self.alpha.as_deref()
}
pub fn alpha_plane_mut(&mut self) -> Option<&mut [u8]> {
self.alpha.as_deref_mut()
}
pub fn row_and_alpha_mut(&mut self, y: u32) -> (&mut [u8], Option<&mut [u8]>) {
self.assert_row_in_bounds(y);
let stride = self.stride;
let w = u32_to_usize(self.width);
let off = u32_to_usize(y) * stride;
let pixels = &mut self.data[off..off + stride];
let alpha = self.alpha.as_mut().map(|a| {
let aoff = u32_to_usize(y) * w;
&mut a[aoff..aoff + w]
});
(pixels, alpha)
}
pub fn clear(&mut self, color: P, alpha: u8) {
let pixel_bytes: &[u8] = bytemuck::bytes_of(&color);
if P::BYTES == 1 {
self.data.fill(pixel_bytes[0]);
} else {
debug_assert!(
P::BYTES > 0,
"clear: P::BYTES must be non-zero for chunked fill"
);
let w = u32_to_usize(self.width);
let n = w * P::BYTES;
let stride = self.stride;
for row in self.data.chunks_exact_mut(stride) {
for dst in row[..n].chunks_exact_mut(P::BYTES) {
dst.copy_from_slice(pixel_bytes);
}
row[n..].fill(0);
}
}
if let Some(ref mut a) = self.alpha {
a.fill(alpha);
}
}
#[must_use]
pub const fn data_len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.data
}
}
pub struct BitmapBand<'bmp, P: Pixel> {
pub width: u32,
pub height: u32,
pub y_start: u32,
pub stride: usize,
data: &'bmp mut [u8],
alpha: Option<&'bmp mut [u8]>,
_marker: PhantomData<P>,
}
impl<P: Pixel> BitmapBand<'_, P> {
pub fn row_and_alpha_mut(&mut self, y: u32) -> (&mut [u8], Option<&mut [u8]>) {
let y_local = self.local_y(y);
let stride = self.stride;
let w = u32_to_usize(self.width);
let off = y_local * stride;
let pixels = &mut self.data[off..off + stride];
let alpha = self.alpha.as_mut().map(|a| {
let aoff = y_local * w;
&mut a[aoff..aoff + w]
});
(pixels, alpha)
}
pub fn row_bytes_mut(&mut self, y: u32) -> &mut [u8] {
let y_local = self.local_y(y);
let stride = self.stride;
let off = y_local * stride;
&mut self.data[off..off + stride]
}
#[inline]
fn local_y(&self, y: u32) -> usize {
assert!(
y >= self.y_start && y < self.y_start + self.height,
"row index {y} is out of range for band [y_start={}, height={}]",
self.y_start,
self.height,
);
u32_to_usize(y - self.y_start)
}
}
impl<P: Pixel> Bitmap<P> {
pub fn bands_mut(&mut self, n_bands: usize) -> Vec<BitmapBand<'_, P>> {
assert!(n_bands >= 1, "n_bands must be ≥ 1");
let h = u32_to_usize(self.height);
let n = n_bands.min(h.max(1));
let width = self.width;
let stride = self.stride;
let mut remaining_data: &mut [u8] = &mut self.data;
let mut remaining_alpha: Option<&mut [u8]> = self.alpha.as_deref_mut();
let mut bands = Vec::with_capacity(n);
for band_idx in 0..n {
let rows_before = (h / n) * band_idx + band_idx.min(h % n);
let rows_in_band = h / n + usize::from(band_idx < h % n);
let _ = rows_before;
let data_bytes = rows_in_band * stride;
let alpha_bytes = rows_in_band * u32_to_usize(width);
let (band_data, rest_data) = remaining_data.split_at_mut(data_bytes);
remaining_data = rest_data;
let (band_alpha_opt, rest_alpha_opt) = remaining_alpha.map_or((None, None), |a| {
let (ba, ra) = a.split_at_mut(alpha_bytes);
(Some(ba), Some(ra))
});
remaining_alpha = rest_alpha_opt;
let y_start = (h / n) * band_idx + band_idx.min(h % n);
bands.push(BitmapBand {
width,
height: u32::try_from(rows_in_band).expect("rows_in_band fits in u32"),
y_start: u32::try_from(y_start).expect("y_start fits in u32"),
stride,
data: band_data,
alpha: band_alpha_opt,
_marker: PhantomData,
});
}
bands
}
}
pub enum AnyBitmap {
Rgb8(Bitmap<crate::types::Rgb8>),
Rgba8(Bitmap<crate::types::Rgba8>),
Gray8(Bitmap<crate::types::Gray8>),
Cmyk8(Bitmap<crate::types::Cmyk8>),
DeviceN8(Bitmap<crate::types::DeviceN8>),
}
impl AnyBitmap {
#[must_use]
pub const fn width(&self) -> u32 {
match self {
Self::Rgb8(b) => b.width,
Self::Rgba8(b) => b.width,
Self::Gray8(b) => b.width,
Self::Cmyk8(b) => b.width,
Self::DeviceN8(b) => b.width,
}
}
#[must_use]
pub const fn height(&self) -> u32 {
match self {
Self::Rgb8(b) => b.height,
Self::Rgba8(b) => b.height,
Self::Gray8(b) => b.height,
Self::Cmyk8(b) => b.height,
Self::DeviceN8(b) => b.height,
}
}
#[must_use]
pub const fn mode(&self) -> crate::types::PixelMode {
match self {
Self::Rgb8(_) => crate::types::PixelMode::Rgb8,
Self::Rgba8(_) => crate::types::PixelMode::Xbgr8,
Self::Gray8(_) => crate::types::PixelMode::Mono8,
Self::Cmyk8(_) => crate::types::PixelMode::Cmyk8,
Self::DeviceN8(_) => crate::types::PixelMode::DeviceN8,
}
}
}
pub struct AaBuf {
pub width: usize,
pub height: usize,
data: Vec<u8>, }
const AA_SIZE_USIZE: usize = {
assert!(AA_SIZE >= 0, "AA_SIZE must be non-negative");
AA_SIZE as usize
};
impl AaBuf {
#[must_use]
pub fn new(bitmap_width: usize) -> Self {
let width = bitmap_width * AA_SIZE_USIZE;
let height = AA_SIZE_USIZE;
let row_bytes = width.div_ceil(8);
Self {
width,
height,
data: vec![0u8; row_bytes * height],
}
}
#[inline]
#[must_use]
pub const fn row_bytes(&self) -> usize {
self.width.div_ceil(8)
}
#[inline]
fn assert_row_in_bounds(&self, row: usize) {
assert!(
row < self.height,
"row index {row} is out of bounds for AaBuf height {}",
self.height
);
}
#[inline]
pub fn clear(&mut self) {
self.data.fill(0);
}
pub fn set_span(&mut self, row: usize, x0: usize, x1: usize) {
self.assert_row_in_bounds(row);
let x0 = x0.min(self.width);
let x1 = x1.min(self.width);
if x0 >= x1 {
return;
}
let rb = self.row_bytes();
let base = row * rb;
let b0 = x0 >> 3;
let b1 = (x1 - 1) >> 3;
if b0 == b1 {
let left_mask = 0xff_u8 >> (x0 & 7);
let right_shift = x1 & 7;
let right_mask = if right_shift == 0 {
0xff_u8
} else {
!(0xff_u8 >> right_shift)
};
self.data[base + b0] |= left_mask & right_mask;
} else {
self.data[base + b0] |= 0xff_u8 >> (x0 & 7);
for b in (b0 + 1)..b1 {
self.data[base + b] = 0xff;
}
let right_shift = x1 & 7;
let right_mask = if right_shift == 0 {
0xff_u8
} else {
!(0xff_u8 >> right_shift)
};
self.data[base + b1] |= right_mask;
}
}
#[inline]
#[must_use]
pub fn get_byte(&self, row: usize, byte_idx: usize) -> u8 {
self.assert_row_in_bounds(row);
let rb = self.row_bytes();
assert!(
byte_idx < rb,
"byte_idx {byte_idx} is out of bounds for row_bytes {rb}"
);
self.data[row * rb + byte_idx]
}
#[must_use]
pub fn row_slice(&self, row: usize) -> &[u8] {
self.assert_row_in_bounds(row);
let rb = self.row_bytes();
&self.data[row * rb..(row + 1) * rb]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Rgb8;
#[test]
fn bitmap_stride_alignment() {
let bm = Bitmap::<Rgb8>::new(7, 3, 4, false);
assert_eq!(bm.stride, 24);
}
#[test]
fn bitmap_row_len() {
let bm = Bitmap::<Rgb8>::new(5, 2, 1, false);
assert_eq!(bm.row(0).len(), 5);
assert_eq!(bm.row(1).len(), 5);
}
#[test]
fn bitmap_clear() {
let mut bm = Bitmap::<Rgb8>::new(4, 2, 1, true);
bm.clear(
Rgb8 {
r: 255,
g: 0,
b: 128,
},
200,
);
for y in 0..2u32 {
for px in bm.row(y) {
assert_eq!(
*px,
Rgb8 {
r: 255,
g: 0,
b: 128
}
);
}
for &a in bm.alpha_row(y).unwrap() {
assert_eq!(a, 200);
}
}
}
#[test]
fn aabuf_set_span_single_byte() {
let mut buf = AaBuf::new(4); buf.set_span(0, 2, 6);
assert_eq!(buf.get_byte(0, 0), 0x3c);
assert_eq!(buf.get_byte(0, 1), 0x00);
}
#[test]
fn aabuf_set_span_cross_byte() {
let mut buf = AaBuf::new(4); buf.set_span(0, 6, 10);
assert_eq!(buf.get_byte(0, 0), 0x03);
assert_eq!(buf.get_byte(0, 1), 0xc0);
}
#[test]
fn aabuf_clear() {
let mut buf = AaBuf::new(4);
buf.set_span(0, 0, 16);
buf.clear();
for i in 0..buf.row_bytes() {
assert_eq!(buf.get_byte(0, i), 0);
}
}
}