use tracing::debug;
use crate::geometry::rectangle::Rectangle;
use super::delta_buffer::{DeltaBuffer, PixelDelta};
#[derive(Debug, Clone)]
pub struct Layer<T: PixelProvider> {
pub content: T,
pub position: (f64, f64),
pub z_index: i32,
pub opacity: f32,
pub blend_mode: BlendMode,
pub transform: Transform,
pub visible: bool,
pub clip_mask: Option<ClipMask>,
pub last_render_hash: u64,
}
#[derive(Debug, Clone, Copy)]
pub enum BlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
}
impl<T: PixelProvider> Layer<T> {
pub fn collect_changes(&self, prev_state: Option<&LayerState>) -> DeltaBuffer {
debug!(
"Collecting changes for layer at position ({}, {}), visible: {}",
self.position.0, self.position.1, self.visible
);
let mut delta = DeltaBuffer::default();
if !self.visible || (prev_state.is_some() && self.unchanged_since(prev_state.unwrap())) {
debug!("Layer is either invisible or unchanged since the previous state.");
return delta;
}
let bounds = self.get_affected_bounds(prev_state);
debug!(
"Affected bounds: ({}, {}, {}, {})",
bounds.x1, bounds.y1, bounds.width, bounds.height
);
for y in bounds.y1..bounds.y1 + bounds.height {
for x in bounds.x1..bounds.x1 + bounds.width {
let new_color = self.compute_pixel_at(x, y);
if prev_state.is_none() || prev_state.unwrap().pixel_at(x, y) != new_color {
delta.changes.push(PixelDelta {
x,
y,
color: new_color.into(),
});
}
}
}
debug!(
"Collected {} pixel changes for the layer.",
delta.changes.len()
);
if !delta.changes.is_empty() {
delta
.dirty_regions
.get_or_insert_with(Vec::new)
.push(bounds);
}
delta
}
pub fn unchanged_since(&self, prev_state: &LayerState) -> bool {
self.last_render_hash == prev_state.hash
}
pub fn compute_pixel_at(&self, x: u32, y: u32) -> rgb::Rgba<u8> {
let transformed_x = ((x as f64 - self.position.0) / self.transform.scale_x) as u32;
let transformed_y = ((y as f64 - self.position.1) / self.transform.scale_y) as u32;
if transformed_x >= self.width() || transformed_y >= self.height() {
return rgb::Rgba {
r: 0,
g: 0,
b: 0,
a: 0,
}; }
let base_color = self.content.pixel_at(transformed_x, transformed_y);
let alpha = (base_color.a as f32 * self.opacity) as u8;
rgb::Rgba {
r: base_color.r,
g: base_color.g,
b: base_color.b,
a: alpha,
}
}
pub fn get_affected_bounds(&self, prev_state: Option<&LayerState>) -> Rectangle {
if let Some(prev) = prev_state {
let x1 = self.position.0.min(prev.x as f64) as u32;
let y1 = self.position.1.min(prev.y as f64) as u32;
let width = (self.transform.scale_x * self.width() as f64) as u32;
let height = (self.transform.scale_y * self.height() as f64) as u32;
Rectangle {
x1,
y1,
width: (self.position.0.max(prev.x as f64)
+ self.transform.scale_x * self.width() as f64) as u32,
height: (self.position.1.max(prev.y as f64)
+ self.transform.scale_y * self.height() as f64) as u32,
x2: x1 + width,
y2: y1 + height,
border: None,
filled: false,
fill_color: None,
}
} else {
let x1 = self.position.0 as u32;
let y1 = self.position.1 as u32;
let width = (self.transform.scale_x * self.width() as f64) as u32;
let height = (self.transform.scale_y * self.height() as f64) as u32;
Rectangle {
x1,
y1,
width,
height,
x2: x1 + width,
y2: y1 + height,
border: None,
filled: false,
fill_color: None,
}
}
}
fn width(&self) -> u32 {
self.content.width()
}
fn height(&self) -> u32 {
self.content.height()
}
}
pub struct LayerState {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
pub color: rgb::Rgba<u8>,
pub opacity: f32,
pub z_index: i32,
pub blend_mode: BlendMode,
pub hash: u64,
}
impl LayerState {
pub fn pixel_at(&self, x: u32, y: u32) -> rgb::Rgba<u8> {
if x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height {
self.color
} else {
rgb::Rgba {
r: 0,
g: 0,
b: 0,
a: 0,
} }
}
}
#[derive(Debug, Clone)]
pub struct ClipMask {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
pub mask_data: Vec<u8>,
}
impl ClipMask {
pub fn contains(&self, x: u32, y: u32) -> bool {
x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
}
pub fn value_at(&self, x: u32, y: u32) -> Option<u8> {
if self.contains(x, y) {
let index = ((y - self.y) * self.width + (x - self.x)) as usize;
Some(self.mask_data[index])
} else {
None
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Transform {
pub translate_x: f64,
pub translate_y: f64,
pub scale_x: f64,
pub scale_y: f64,
pub rotation: f64,
}
impl Transform {
pub fn identity() -> Self {
Self {
translate_x: 0.0,
translate_y: 0.0,
scale_x: 1.0,
scale_y: 1.0,
rotation: 0.0,
}
}
}
pub trait PixelProvider {
fn pixel_at(&self, x: u32, y: u32) -> rgb::Rgba<u8>;
fn width(&self) -> u32;
fn height(&self) -> u32;
}