use alloc::vec;
use alloc::vec::Vec;
use crate::decode::CombinationOperator;
#[derive(Debug, Clone)]
pub(crate) struct Bitmap {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) stride: u32,
pub(crate) data: Vec<u32>,
pub(crate) x_location: u32,
pub(crate) y_location: u32,
}
impl Bitmap {
pub(crate) fn new(width: u32, height: u32) -> Self {
Self::new_with(width, height, 0, 0, false)
}
pub(crate) fn new_with(
width: u32,
height: u32,
x_location: u32,
y_location: u32,
default_pixel: bool,
) -> Self {
let stride = width.div_ceil(32);
let default_word = if default_pixel { !0_u32 } else { 0_u32 };
let data = vec![default_word; (stride * height) as usize];
Self {
width,
height,
stride,
data,
x_location,
y_location,
}
}
#[inline]
pub(crate) fn get_pixel(&self, x: u32, y: u32) -> bool {
if x >= self.width || y >= self.height {
return false;
}
let word_idx = (y * self.stride + x / 32) as usize;
let bit_pos = 31 - (x % 32);
(self.data[word_idx] >> bit_pos) & 1 != 0
}
#[inline]
pub(crate) fn set_pixel(&mut self, x: u32, y: u32, value: bool) {
if x >= self.width || y >= self.height {
return;
}
let word_idx = (y * self.stride + x / 32) as usize;
let bit_pos = 31 - (x % 32);
self.data[word_idx] |= (value as u32) << bit_pos;
}
pub(crate) fn combine(&mut self, other: &Self, x: i32, y: i32, operator: CombinationOperator) {
for src_y in 0..other.height {
let dest_y = y + src_y as i32;
if dest_y < 0 || dest_y >= self.height as i32 {
continue;
}
let dest_x_start = x.max(0);
let dest_x_end = (x + other.width as i32).min(self.width as i32);
if dest_x_start >= dest_x_end {
continue;
}
let src_x_start = (dest_x_start - x) as u32;
let dest_y = dest_y as u32;
let dest_x_start = dest_x_start as u32;
let dest_x_end = dest_x_end as u32;
let first_word = dest_x_start / 32;
let last_word = (dest_x_end - 1) / 32;
for word_idx in first_word..=last_word {
let word_start_x = word_idx * 32;
let word_end_x = word_start_x + 32;
let px_start = dest_x_start.max(word_start_x);
let px_end = dest_x_end.min(word_end_x);
let bit_start = px_start - word_start_x;
let bit_end = px_end - word_start_x;
let mask = if bit_end == 32 {
!0_u32 >> bit_start
} else {
(!0_u32 >> bit_start) & !(!0_u32 >> bit_end)
};
let src_x_for_range = src_x_start + (px_start - dest_x_start);
let src_word_idx = src_x_for_range / 32;
let src_bit_offset = src_x_for_range % 32;
let src_word1 = other.get_word(src_y, src_word_idx);
let src_word2 = other.get_word(src_y, src_word_idx + 1);
let src_raw = if src_bit_offset == 0 {
src_word1
} else {
(src_word1 << src_bit_offset) | (src_word2 >> (32 - src_bit_offset))
};
let src_aligned = src_raw >> bit_start;
let dest_idx = (dest_y * self.stride + word_idx) as usize;
let dest_word = self.data[dest_idx];
let result = match operator {
CombinationOperator::Or => dest_word | src_aligned,
CombinationOperator::And => dest_word & src_aligned,
CombinationOperator::Xor => dest_word ^ src_aligned,
CombinationOperator::Xnor => !(dest_word ^ src_aligned),
CombinationOperator::Replace => src_aligned,
};
self.data[dest_idx] = (dest_word & !mask) | (result & mask);
}
}
}
#[inline]
pub(crate) fn get_word(&self, row: u32, word_idx: u32) -> u32 {
if row >= self.height || word_idx >= self.stride {
return 0;
}
self.data[(row * self.stride + word_idx) as usize]
}
}