use crate::basics::CoverType;
use crate::color::Gray8;
use crate::pixfmt_rgba::PixelFormat;
use crate::rendering_buffer::RowAccessor;
const BPP: usize = 1;
pub struct PixfmtGray8<'a> {
rbuf: &'a mut RowAccessor,
}
impl<'a> PixfmtGray8<'a> {
pub fn new(rbuf: &'a mut RowAccessor) -> Self {
Self { rbuf }
}
pub fn clear(&mut self, c: &Gray8) {
let w = self.rbuf.width();
let h = self.rbuf.height();
for y in 0..h {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y as i32);
std::slice::from_raw_parts_mut(ptr, w as usize * BPP)
};
for x in 0..w as usize {
row[x] = c.v;
}
}
}
#[inline]
fn blend_pix(p: &mut u8, cv: u8, alpha: u8) {
*p = Gray8::lerp(*p, cv, alpha);
}
}
impl<'a> PixelFormat for PixfmtGray8<'a> {
type ColorType = Gray8;
fn width(&self) -> u32 {
self.rbuf.width()
}
fn height(&self) -> u32 {
self.rbuf.height()
}
fn pixel(&self, x: i32, y: i32) -> Gray8 {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts(ptr, self.rbuf.width() as usize * BPP)
};
Gray8::new(row[x as usize] as u32, 255)
}
fn copy_pixel(&mut self, x: i32, y: i32, c: &Gray8) {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
};
row[x as usize] = c.v;
}
fn copy_hline(&mut self, x: i32, y: i32, len: u32, c: &Gray8) {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
};
for i in 0..len as usize {
row[x as usize + i] = c.v;
}
}
fn blend_pixel(&mut self, x: i32, y: i32, c: &Gray8, cover: CoverType) {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
};
let alpha = Gray8::mult_cover(c.a, cover);
if alpha == 255 {
row[x as usize] = c.v;
} else if alpha > 0 {
Self::blend_pix(&mut row[x as usize], c.v, alpha);
}
}
fn blend_hline(&mut self, x: i32, y: i32, len: u32, c: &Gray8, cover: CoverType) {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
};
let alpha = Gray8::mult_cover(c.a, cover);
if alpha == 255 {
for i in 0..len as usize {
row[x as usize + i] = c.v;
}
} else if alpha > 0 {
for i in 0..len as usize {
Self::blend_pix(&mut row[x as usize + i], c.v, alpha);
}
}
}
fn blend_solid_hspan(&mut self, x: i32, y: i32, len: u32, c: &Gray8, covers: &[CoverType]) {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
};
for (i, &cov) in covers.iter().enumerate().take(len as usize) {
let alpha = Gray8::mult_cover(c.a, cov);
if alpha == 255 {
row[x as usize + i] = c.v;
} else if alpha > 0 {
Self::blend_pix(&mut row[x as usize + i], c.v, alpha);
}
}
}
fn blend_color_hspan(
&mut self,
x: i32,
y: i32,
len: u32,
colors: &[Gray8],
covers: &[CoverType],
cover: CoverType,
) {
let row = unsafe {
let ptr = self.rbuf.row_ptr(y);
std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
};
if !covers.is_empty() {
for i in 0..len as usize {
let c = &colors[i];
let alpha = Gray8::mult_cover(c.a, covers[i]);
if alpha == 255 {
row[x as usize + i] = c.v;
} else if alpha > 0 {
Self::blend_pix(&mut row[x as usize + i], c.v, alpha);
}
}
} else if cover == 255 {
for (i, c) in colors.iter().enumerate().take(len as usize) {
if c.a == 255 {
row[x as usize + i] = c.v;
} else if c.a > 0 {
Self::blend_pix(&mut row[x as usize + i], c.v, c.a);
}
}
} else {
for (i, c) in colors.iter().enumerate().take(len as usize) {
let alpha = Gray8::mult_cover(c.a, cover);
if alpha == 255 {
row[x as usize + i] = c.v;
} else if alpha > 0 {
Self::blend_pix(&mut row[x as usize + i], c.v, alpha);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
let stride = w as i32;
let buf = vec![0u8; (h * w) as usize];
let mut ra = RowAccessor::new();
unsafe {
ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
}
(buf, ra)
}
#[test]
fn test_new() {
let (_buf, mut ra) = make_buffer(100, 100);
let pf = PixfmtGray8::new(&mut ra);
assert_eq!(pf.width(), 100);
assert_eq!(pf.height(), 100);
}
#[test]
fn test_copy_pixel() {
let (_buf, mut ra) = make_buffer(10, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let white = Gray8::new(255, 255);
pf.copy_pixel(5, 5, &white);
let p = pf.pixel(5, 5);
assert_eq!(p.v, 255);
assert_eq!(p.a, 255);
}
#[test]
fn test_copy_hline() {
let (_buf, mut ra) = make_buffer(20, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let mid = Gray8::new(128, 255);
pf.copy_hline(5, 3, 10, &mid);
for x in 5..15 {
let p = pf.pixel(x, 3);
assert_eq!(p.v, 128);
}
let p = pf.pixel(4, 3);
assert_eq!(p.v, 0);
}
#[test]
fn test_blend_pixel_opaque() {
let (_buf, mut ra) = make_buffer(10, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let c = Gray8::new(200, 255);
pf.blend_pixel(3, 3, &c, 255);
let p = pf.pixel(3, 3);
assert_eq!(p.v, 200);
}
#[test]
fn test_blend_pixel_semitransparent() {
let (_buf, mut ra) = make_buffer(10, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let white = Gray8::new(255, 255);
pf.copy_hline(0, 0, 10, &white);
let black_50 = Gray8::new(0, 128);
pf.blend_pixel(5, 0, &black_50, 255);
let p = pf.pixel(5, 0);
assert!(p.v > 120 && p.v < 140, "v={}", p.v);
}
#[test]
fn test_blend_hline() {
let (_buf, mut ra) = make_buffer(20, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let c = Gray8::new(100, 255);
pf.blend_hline(2, 2, 5, &c, 255);
for x in 2..7 {
let p = pf.pixel(x, 2);
assert_eq!(p.v, 100);
}
}
#[test]
fn test_blend_solid_hspan() {
let (_buf, mut ra) = make_buffer(20, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let c = Gray8::new(200, 255);
let covers = [255u8, 128, 64, 0];
pf.blend_solid_hspan(0, 0, 4, &c, &covers);
let p0 = pf.pixel(0, 0);
assert_eq!(p0.v, 200);
let p3 = pf.pixel(3, 0);
assert_eq!(p3.v, 0); }
#[test]
fn test_clear() {
let (_buf, mut ra) = make_buffer(10, 10);
let mut pf = PixfmtGray8::new(&mut ra);
pf.clear(&Gray8::new(128, 255));
let p = pf.pixel(5, 5);
assert_eq!(p.v, 128);
}
#[test]
fn test_pixel_always_opaque() {
let (_buf, mut ra) = make_buffer(10, 10);
let pf = PixfmtGray8::new(&mut ra);
let p = pf.pixel(0, 0);
assert_eq!(p.a, 255);
}
#[test]
fn test_blend_color_hspan_with_covers() {
let (_buf, mut ra) = make_buffer(20, 10);
let mut pf = PixfmtGray8::new(&mut ra);
let colors = [
Gray8::new(100, 255),
Gray8::new(200, 255),
Gray8::new(50, 128),
];
let covers = [255u8, 255, 255];
pf.blend_color_hspan(0, 0, 3, &colors, &covers, 255);
assert_eq!(pf.pixel(0, 0).v, 100);
assert_eq!(pf.pixel(1, 0).v, 200);
let p2 = pf.pixel(2, 0);
assert!(p2.v > 20 && p2.v < 35, "v={}", p2.v);
}
}