pub struct Framebuffer {
pixels: Vec<u8>, width: u32,
height: u32,
}
impl Framebuffer {
pub fn new(width: u32, height: u32) -> Self {
Self {
pixels: vec![0u8; (width * height * 4) as usize],
width,
height,
}
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn pixels(&self) -> &[u8] {
&self.pixels
}
pub fn pixels_mut(&mut self) -> &mut [u8] {
&mut self.pixels
}
pub fn into_pixels(self) -> Vec<u8> {
self.pixels
}
pub fn resize(&mut self, width: u32, height: u32) {
if self.width != width || self.height != height {
self.width = width;
self.height = height;
self.pixels = vec![0u8; (width * height * 4) as usize];
}
}
pub fn pixels_flipped(&self) -> Vec<u8> {
let row_bytes = (self.width * 4) as usize;
let mut flipped = vec![0u8; self.pixels.len()];
for y in 0..self.height as usize {
let src = (self.height as usize - 1 - y) * row_bytes;
let dst = y * row_bytes;
flipped[dst..dst + row_bytes].copy_from_slice(&self.pixels[src..src + row_bytes]);
}
flipped
}
}
pub fn unpremultiply_rgba_inplace(data: &mut [u8]) {
for px in data.chunks_exact_mut(4) {
let a = px[3];
if a == 0 {
px[0] = 0;
px[1] = 0;
px[2] = 0;
} else if a < 255 {
let af = a as u32;
px[0] = (((px[0] as u32) * 255 + af / 2) / af).min(255) as u8;
px[1] = (((px[1] as u32) * 255 + af / 2) / af).min(255) as u8;
px[2] = (((px[2] as u32) * 255 + af / 2) / af).min(255) as u8;
}
}
}
pub fn premultiply_rgba_inplace(data: &mut [u8]) {
for px in data.chunks_exact_mut(4) {
let a = px[3] as u32;
if a < 255 {
px[0] = (((px[0] as u32) * a + 127) / 255) as u8;
px[1] = (((px[1] as u32) * a + 127) / 255) as u8;
px[2] = (((px[2] as u32) * a + 127) / 255) as u8;
}
}
}
#[cfg(test)]
mod alpha_tests {
use super::*;
#[test]
fn test_premul_roundtrip_half_alpha_white() {
let mut px = [255, 255, 255, 128];
premultiply_rgba_inplace(&mut px);
assert_eq!(px, [128, 128, 128, 128]);
unpremultiply_rgba_inplace(&mut px);
assert_eq!(px, [255, 255, 255, 128]);
}
#[test]
fn test_premul_opaque_unchanged() {
let mut px = [60, 120, 240, 255];
premultiply_rgba_inplace(&mut px);
assert_eq!(px, [60, 120, 240, 255]);
unpremultiply_rgba_inplace(&mut px);
assert_eq!(px, [60, 120, 240, 255]);
}
#[test]
fn test_unpremul_zero_alpha_zeros_colour() {
let mut px = [10, 20, 30, 0];
unpremultiply_rgba_inplace(&mut px);
assert_eq!(px, [0, 0, 0, 0]);
}
}