feat(canvas): enhance composite shader with selection overlay and marching ants
- Updated the canvas composite shader to include a selection mask fill and animated marching ants border. - Added new uniforms for animation time, selection mask presence, and quick mask mode. - Modified the fragment shader to handle selection rendering, including color tinting for selected areas and a dash pattern for the marching ants effect. - Refactored the OverlayProgram to remove the old marching ants rendering logic, now handled by the GPU shader. - Introduced new data structures in the shader pipeline for managing selection mask textures and samplers. - Implemented functionality to upload selection mask data to the GPU. - Updated selection state handling to ensure correct bounds calculations. - Enhanced sidebar tool button rendering with fallback for missing icons.
This commit is contained in:
@@ -751,6 +751,9 @@ impl Engine {
|
||||
// ── Filter Ops ───────────────────────────────────────────────────────
|
||||
|
||||
pub fn apply_filter(&mut self, filter_id: &str, params: serde_json::Value) {
|
||||
// Clone the selection mask before borrowing the layer mutably, to avoid
|
||||
// borrow-checker conflict between `active_layer_mut()` and `selection_mask`.
|
||||
let mask = self.document.selection_mask.clone();
|
||||
if let Some(layer) = self.document.active_layer_mut() {
|
||||
let before_pixels = layer.pixels.clone();
|
||||
let _ = apply_filter(
|
||||
@@ -760,6 +763,30 @@ impl Engine {
|
||||
layer.width,
|
||||
layer.height,
|
||||
);
|
||||
// When a selection is active, restore original pixels for unselected
|
||||
// areas so the filter is confined to the selected region.
|
||||
if let Some(ref mask) = mask {
|
||||
let w = layer.width as usize;
|
||||
let h = layer.height as usize;
|
||||
if mask.len() == w * h {
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let idx = y * w + x;
|
||||
if mask[idx] == 0 {
|
||||
let pidx = idx * 4;
|
||||
if pidx + 3 < layer.pixels.len()
|
||||
&& pidx + 3 < before_pixels.len()
|
||||
{
|
||||
layer.pixels[pidx] = before_pixels[pidx];
|
||||
layer.pixels[pidx + 1] = before_pixels[pidx + 1];
|
||||
layer.pixels[pidx + 2] = before_pixels[pidx + 2];
|
||||
layer.pixels[pidx + 3] = before_pixels[pidx + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
layer.dirty = true;
|
||||
self.document.push_draw_snapshot(
|
||||
self.document.active_layer,
|
||||
@@ -773,7 +800,11 @@ impl Engine {
|
||||
}
|
||||
|
||||
pub fn apply_filter_preview(&mut self, filter_id: &str, params: serde_json::Value) {
|
||||
let mask = self.document.selection_mask.clone();
|
||||
if let Some(layer) = self.document.active_layer_mut() {
|
||||
// Save the current pixels before applying the filter, so we can
|
||||
// restore unselected areas afterwards.
|
||||
let saved_before = layer.pixels.clone();
|
||||
let _ = apply_filter(
|
||||
filter_id,
|
||||
¶ms,
|
||||
@@ -781,6 +812,30 @@ impl Engine {
|
||||
layer.width,
|
||||
layer.height,
|
||||
);
|
||||
// When a selection is active, restore original pixels for unselected
|
||||
// areas so the filter preview is confined to the selected region.
|
||||
if let Some(ref mask) = mask {
|
||||
let w = layer.width as usize;
|
||||
let h = layer.height as usize;
|
||||
if mask.len() == w * h {
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let idx = y * w + x;
|
||||
if mask[idx] == 0 {
|
||||
let pidx = idx * 4;
|
||||
if pidx + 3 < layer.pixels.len()
|
||||
&& pidx + 3 < saved_before.len()
|
||||
{
|
||||
layer.pixels[pidx] = saved_before[pidx];
|
||||
layer.pixels[pidx + 1] = saved_before[pidx + 1];
|
||||
layer.pixels[pidx + 2] = saved_before[pidx + 2];
|
||||
layer.pixels[pidx + 3] = saved_before[pidx + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
layer.dirty = true;
|
||||
self.document.composite_dirty = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user