use crate::framebuffer::{pack_rgba, unpack};
#[inline]
pub fn fill_solid(pixels: &mut [u32], color: u32) {
#[cfg(feature = "simd")]
{
use wide::u32x8;
const LANES: usize = 8;
let v = u32x8::splat(color);
let (chunks, tail) = pixels.split_at_mut((pixels.len() / LANES) * LANES);
for chunk in chunks.chunks_exact_mut(LANES) {
let _stored: [u32; LANES] = v.into();
chunk.copy_from_slice(&_stored);
}
for px in tail.iter_mut() {
*px = color;
}
}
#[cfg(not(feature = "simd"))]
{
for px in pixels.iter_mut() {
*px = color;
}
}
}
#[inline]
pub fn alpha_blend_row(src: &[u8], dst: &mut [u32]) {
debug_assert_eq!(src.len(), dst.len() * 4);
use crate::blend::blend_over_premult_u8;
for (i, dst_px) in dst.iter_mut().enumerate() {
let si = i * 4;
let (sr, sg, sb, sa) = (src[si], src[si + 1], src[si + 2], src[si + 3]);
if sa == 0 {
continue;
}
let (dr, dg, db, da) = unpack(*dst_px);
if sa == 255 {
*dst_px = pack_rgba(sr, sg, sb, 255);
} else {
let (or_, og, ob, oa) = blend_over_premult_u8(sr, sg, sb, sa, dr, dg, db, da);
*dst_px = pack_rgba(or_, og, ob, oa);
}
}
}
#[inline]
pub fn gradient_row_horizontal(
dst: &mut [u32],
x_start: u32,
total_width: u32,
color_left: u32,
color_right: u32,
) {
if total_width == 0 {
return;
}
let (lr, lg, lb, la) = unpack(color_left);
let (rr, rg, rb, ra) = unpack(color_right);
#[cfg(feature = "simd")]
{
use wide::f32x8;
let tw = total_width as f32;
let (lr_f, lg_f, lb_f, la_f) = (lr as f32, lg as f32, lb as f32, la as f32);
let (rr_f, rg_f, rb_f, ra_f) = (rr as f32, rg as f32, rb as f32, ra as f32);
const LANE_OFFSETS: [f32; 8] = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
let lane_offset = f32x8::from(LANE_OFFSETS);
let width = dst.len();
let mut i = 0usize;
while i + 8 <= width {
let xs = f32x8::splat((x_start as usize + i) as f32);
let t = (xs + lane_offset) / f32x8::splat(tw - 1.0).max(f32x8::splat(1.0));
let t = t.max(f32x8::ZERO).min(f32x8::ONE);
let make_ch = |l: f32, r: f32| -> [f32; 8] {
let lv = f32x8::splat(l);
let rv = f32x8::splat(r);
let ch = lv + t * (rv - lv);
ch.to_array()
};
let r_vals = make_ch(lr_f, rr_f);
let g_vals = make_ch(lg_f, rg_f);
let b_vals = make_ch(lb_f, rb_f);
let a_vals = make_ch(la_f, ra_f);
for k in 0..8 {
dst[i + k] = pack_rgba(
r_vals[k].round() as u8,
g_vals[k].round() as u8,
b_vals[k].round() as u8,
a_vals[k].round() as u8,
);
}
i += 8;
}
for (j, px) in dst[i..].iter_mut().enumerate() {
let x = (x_start as usize + i + j) as f32;
let denom = (total_width as f32 - 1.0).max(1.0);
let t = (x / denom).clamp(0.0, 1.0);
let lerp = |l: f32, r: f32| (l + t * (r - l)).round() as u8;
*px = pack_rgba(
lerp(lr_f, rr_f),
lerp(lg_f, rg_f),
lerp(lb_f, rb_f),
lerp(la_f, ra_f),
);
}
}
#[cfg(not(feature = "simd"))]
{
let denom = (total_width as f32 - 1.0).max(1.0);
for (j, px) in dst.iter_mut().enumerate() {
let x = (x_start as usize + j) as f32;
let t = (x / denom).clamp(0.0, 1.0);
let lerp = |l: u8, r: u8| (l as f32 + t * (r as f32 - l as f32)).round() as u8;
*px = pack_rgba(lerp(lr, rr), lerp(lg, rg), lerp(lb, rb), lerp(la, ra));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::framebuffer::pack;
use oxiui_core::Color;
#[test]
fn fill_solid_all_pixels() {
let color = pack(&Color(255, 0, 128, 200));
let mut buf = vec![0u32; 20];
fill_solid(&mut buf, color);
for &px in &buf {
assert_eq!(px, color, "all pixels should equal the fill colour");
}
}
#[test]
fn fill_solid_empty_slice() {
let mut buf: Vec<u32> = Vec::new();
fill_solid(&mut buf, 0xFF_FF_FF_FF); }
#[test]
fn fill_solid_single_pixel() {
let mut buf = vec![0u32; 1];
fill_solid(&mut buf, 0xDEAD_BEEF);
assert_eq!(buf[0], 0xDEAD_BEEF);
}
#[test]
fn alpha_blend_row_opaque_overwrites() {
let mut dst = vec![0xFF_00_00_00u32]; let src = vec![255u8, 255, 255, 255]; alpha_blend_row(&src, &mut dst);
let (r, g, b, a) = unpack(dst[0]);
assert_eq!((r, g, b, a), (255, 255, 255, 255));
}
#[test]
fn alpha_blend_row_transparent_noop() {
let mut dst = vec![pack(&Color(10, 20, 30, 255))];
let src = vec![255u8, 0, 0, 0]; alpha_blend_row(&src, &mut dst);
let (r, g, b, _) = unpack(dst[0]);
assert_eq!((r, g, b), (10, 20, 30));
}
#[test]
fn gradient_row_horizontal_endpoints() {
let mut dst = vec![0u32; 10];
let left = pack(&Color(255, 0, 0, 255));
let right = pack(&Color(0, 0, 255, 255));
gradient_row_horizontal(&mut dst, 0, 10, left, right);
let (r0, _, b0, _) = unpack(dst[0]);
assert!(
r0 > 200 && b0 < 55,
"left end should be mostly red: r={r0} b={b0}"
);
let (r9, _, b9, _) = unpack(dst[9]);
assert!(
b9 > 200 && r9 < 55,
"right end should be mostly blue: r={r9} b={b9}"
);
}
#[test]
fn gradient_row_horizontal_single_pixel() {
let mut dst = vec![0u32; 1];
let left = pack(&Color(100, 200, 50, 255));
let right = pack(&Color(0, 0, 0, 255));
gradient_row_horizontal(&mut dst, 0, 1, left, right);
let _ = dst[0];
}
#[test]
fn gradient_row_horizontal_zero_total_width_noop() {
let mut dst = vec![0xDEAD_BEEFu32; 4];
gradient_row_horizontal(&mut dst, 0, 0, 0, 0xFFFF_FFFF);
for &px in &dst {
assert_eq!(px, 0xDEAD_BEEF);
}
}
}