use drmkit_scene::{CompositeRect, CompositeSrc, blend_into, clear_into, convert_row};
use crate::{DamageRect, intersect_rect};
#[derive(Debug, Clone, Copy)]
#[allow(
clippy::exhaustive_structs,
reason = "built by callers, with no default to fill a new field from"
)]
pub struct BlitItem<'a> {
pub pixels: &'a [u8],
pub stride: u32,
pub width: u32,
pub height: u32,
pub fourcc: u32,
pub x: i32,
pub y: i32,
}
#[must_use]
pub const fn fb_fourcc_for(
bpp: u32,
red_offset: u32,
blue_offset: u32,
transparency_length: u32,
) -> Option<u32> {
match bpp {
32 => {
let has_alpha = transparency_length != 0;
Some(if red_offset > blue_offset {
if has_alpha {
drmkit_fmt::fourcc::ARGB8888
} else {
drmkit_fmt::fourcc::XRGB8888
}
} else if has_alpha {
drmkit_fmt::fourcc::ABGR8888
} else {
drmkit_fmt::fourcc::XBGR8888
})
}
16 => Some(if red_offset > blue_offset {
drmkit_fmt::fourcc::RGB565
} else {
drmkit_fmt::fourcc::BGR565
}),
_ => None,
}
}
#[derive(Debug)]
#[allow(
clippy::exhaustive_structs,
reason = "built by callers, with no default to fill a new field from"
)]
pub struct FbTarget<'a> {
pub pixels: &'a mut [u8],
pub stride: u32,
pub width: u32,
pub height: u32,
pub fourcc: u32,
}
pub fn compose_into_framebuffer(
target: &mut FbTarget<'_>,
shadow: &mut [u8],
items: &[BlitItem<'_>],
damage: DamageRect,
) {
let (fb_width, fb_height, fb_stride, fb_fourcc) =
(target.width, target.height, target.stride, target.fourcc);
let shadow_stride = fb_width * 4;
if damage.is_empty()
|| shadow.len() < shadow_stride as usize * fb_height as usize
|| fb_width == 0
|| fb_height == 0
{
return;
}
let rect = |region: DamageRect| CompositeRect {
x: region.x,
y: region.y,
w: region.w,
h: region.h,
};
clear_into(shadow, shadow_stride, fb_width, fb_height, rect(damage));
for item in items {
let overlap = intersect_rect(item.x, item.y, item.width, item.height, damage);
if overlap.is_empty() {
continue;
}
let src = CompositeSrc {
pixels: item.pixels,
src_stride_bytes: item.stride,
src_width: item.width,
src_height: item.height,
drm_fourcc: item.fourcc,
plane_alpha: u16::MAX,
};
blend_into(
shadow,
shadow_stride,
fb_width,
fb_height,
&src,
CompositeRect {
x: overlap.x - item.x,
y: overlap.y - item.y,
w: overlap.w,
h: overlap.h,
},
rect(overlap),
);
}
let first = damage.y.max(0).unsigned_abs();
let last = (first + damage.h).min(fb_height);
for row in first..last {
let to = row as usize * fb_stride as usize;
let from = row as usize * shadow_stride as usize;
let (Some(src), Some(dst)) = (
shadow.get(from..from + shadow_stride as usize),
target.pixels.get_mut(to..to + fb_stride as usize),
) else {
break;
};
convert_row(dst, src, fb_width as usize, fb_fourcc);
}
}