use std::collections::HashMap;
use std::sync::Arc;
use wayland_server::backend::ObjectId;
use super::imp::{PixelData, Surface};
struct Layer {
x: i32,
y: i32,
logical_w: u32,
logical_h: u32,
pixel_w: u32,
scale: i32,
is_opaque: bool,
rgba: Vec<u8>,
}
#[inline]
pub(crate) fn to_physical(logical: u32, scale_120: u32) -> u32 {
(logical * scale_120).div_ceil(120)
}
pub(crate) fn cpu_composite_from_cache(
root_surface_id: &ObjectId,
surfaces: &HashMap<ObjectId, Surface>,
cache: &HashMap<ObjectId, (u32, u32, i32, bool, PixelData)>,
output_scale_120: u16,
) -> Option<(u32, u32, PixelData)> {
let s120 = (output_scale_120 as u32).max(120);
let mut layers: Vec<Layer> = Vec::new();
collect_layers(root_surface_id, surfaces, cache, 0, 0, &mut layers);
if layers.is_empty() {
return None;
}
let (crop_x, crop_y, log_w, log_h) = surfaces
.get(root_surface_id)
.and_then(|s| s.xdg_geometry)
.filter(|&(_, _, w, h)| w > 0 && h > 0)
.map(|(x, y, w, h)| (x, y, w as u32, h as u32))
.unwrap_or_else(|| {
let (mut mw, mut mh) = (0i32, 0i32);
for l in &layers {
mw = mw.max(l.x + l.logical_w as i32);
mh = mh.max(l.y + l.logical_h as i32);
}
(0, 0, mw.max(0) as u32, mh.max(0) as u32)
});
if log_w == 0 || log_h == 0 {
return None;
}
let phys_w = to_physical(log_w, s120) as usize;
let phys_h = to_physical(log_h, s120) as usize;
let stride = phys_w * 4;
let mut out = vec![0u8; stride * phys_h];
for layer in &layers {
let (lx, ly) = if layer.logical_w == log_w && layer.logical_h == log_h {
(layer.x, layer.y)
} else {
(layer.x - crop_x, layer.y - crop_y)
};
let buf_s = layer.scale.max(1) as usize;
let pw = layer.pixel_w as usize;
let layer_phys_w = to_physical(layer.logical_w, s120) as usize;
let layer_phys_h = to_physical(layer.logical_h, s120) as usize;
for prow in 0..layer_phys_h {
let dy = (ly as i64) * (s120 as i64) / 120 + prow as i64;
if dy < 0 || dy >= phys_h as i64 {
continue;
}
for pcol in 0..layer_phys_w {
let dx = (lx as i64) * (s120 as i64) / 120 + pcol as i64;
if dx < 0 || dx >= phys_w as i64 {
continue;
}
let src_row = prow * buf_s * 120 / (s120 as usize);
let src_col = pcol * buf_s * 120 / (s120 as usize);
let src_off = (src_row * pw + src_col) * 4;
let dst_off = dy as usize * stride + dx as usize * 4;
if src_off + 3 >= layer.rgba.len() || dst_off + 3 >= out.len() {
continue;
}
let (sr, sg, sb) = (
layer.rgba[src_off],
layer.rgba[src_off + 1],
layer.rgba[src_off + 2],
);
let sa = if layer.is_opaque {
255u32
} else {
layer.rgba[src_off + 3] as u32
};
if sa == 0 {
continue;
}
if sa == 255 {
out[dst_off] = sr;
out[dst_off + 1] = sg;
out[dst_off + 2] = sb;
out[dst_off + 3] = 255;
} else {
let inv = 255 - sa;
out[dst_off] = ((sr as u32 * sa + out[dst_off] as u32 * inv) / 255) as u8;
out[dst_off + 1] =
((sg as u32 * sa + out[dst_off + 1] as u32 * inv) / 255) as u8;
out[dst_off + 2] =
((sb as u32 * sa + out[dst_off + 2] as u32 * inv) / 255) as u8;
out[dst_off + 3] = 255;
}
}
}
}
Some((phys_w as u32, phys_h as u32, PixelData::Rgba(Arc::new(out))))
}
fn collect_layers(
surface_id: &ObjectId,
surfaces: &HashMap<ObjectId, Surface>,
cache: &HashMap<ObjectId, (u32, u32, i32, bool, PixelData)>,
parent_x: i32,
parent_y: i32,
layers: &mut Vec<Layer>,
) {
let Some(surf) = surfaces.get(surface_id) else {
return;
};
let (x, y) = (
parent_x + surf.subsurface_position.0,
parent_y + surf.subsurface_position.1,
);
if let Some((w, h, scale, is_opaque, pixels)) = cache.get(surface_id) {
let rgba = pixels.to_rgba(*w, *h);
if !rgba.is_empty() {
let s = (*scale).max(1) as u32;
let (lw, lh) = surf
.viewport_destination
.filter(|&(dw, dh)| dw > 0 && dh > 0)
.map(|(dw, dh)| (dw as u32, dh as u32))
.unwrap_or((*w / s, *h / s));
layers.push(Layer {
x,
y,
logical_w: lw,
logical_h: lh,
pixel_w: *w,
scale: *scale,
is_opaque: *is_opaque,
rgba,
});
}
}
for child_id in &surf.children {
collect_layers(child_id, surfaces, cache, x, y, layers);
}
}
pub(crate) struct GpuLayer<'a> {
pub x: i32,
pub y: i32,
pub logical_w: u32,
pub logical_h: u32,
pub pixel_w: u32,
pub pixel_h: u32,
pub pixels: &'a PixelData,
}
pub(crate) fn collect_gpu_layers<'a>(
surface_id: &ObjectId,
surfaces: &HashMap<ObjectId, Surface>,
cache: &'a HashMap<ObjectId, (u32, u32, i32, bool, PixelData)>,
parent_x: i32,
parent_y: i32,
layers: &mut Vec<GpuLayer<'a>>,
) {
let Some(surf) = surfaces.get(surface_id) else {
return;
};
let (x, y) = (
parent_x + surf.subsurface_position.0,
parent_y + surf.subsurface_position.1,
);
if let Some((w, h, scale, _is_opaque, pixels)) = cache.get(surface_id)
&& !pixels.is_empty()
{
let s = (*scale).max(1) as u32;
let (lw, lh) = surf
.viewport_destination
.filter(|&(dw, dh)| dw > 0 && dh > 0)
.map(|(dw, dh)| (dw as u32, dh as u32))
.unwrap_or((*w / s, *h / s));
static DBG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let n = DBG.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if n < 20 || n.is_multiple_of(1000) {
eprintln!(
"[gpu-layer #{n}] sid={surface_id:?} pos=({x},{y}) pixel={}x{} scale={} viewport={:?} logical={}x{}",
w, h, scale, surf.viewport_destination, lw, lh,
);
}
layers.push(GpuLayer {
x,
y,
logical_w: lw,
logical_h: lh,
pixel_w: *w,
pixel_h: *h,
pixels,
});
}
for child_id in &surf.children {
collect_gpu_layers(child_id, surfaces, cache, x, y, layers);
}
}