feat: allow brush strokes to extend beyond canvas edges in viewport and shader calculations
This commit is contained in:
@@ -270,10 +270,6 @@ impl Engine {
|
||||
let w = self.document.canvas_width as f32;
|
||||
let h = self.document.canvas_height as f32;
|
||||
let inside = x >= 0.0 && y >= 0.0 && x < w && y < h;
|
||||
if !inside {
|
||||
self.last_stroke_pos = None;
|
||||
return;
|
||||
}
|
||||
let tip = self.brush_tip_with_time_dynamics(x, y);
|
||||
let color = self.apply_cyclic_color(self.current_color);
|
||||
|
||||
@@ -287,10 +283,14 @@ impl Engine {
|
||||
let is_eraser = self.is_eraser;
|
||||
|
||||
let dirty_r = Self::brush_dirty_radius(&tip);
|
||||
if lx >= 0.0 && ly >= 0.0 && lx < w && ly < h {
|
||||
self.document.expand_dirty(lx as u32, ly as u32, dirty_r);
|
||||
self.document.expand_dirty(x as u32, y as u32, dirty_r);
|
||||
self.expand_stroke_bounds(lx as u32, ly as u32, dirty_r);
|
||||
}
|
||||
if inside {
|
||||
self.document.expand_dirty(x as u32, y as u32, dirty_r);
|
||||
self.expand_stroke_bounds(x as u32, y as u32, dirty_r);
|
||||
}
|
||||
|
||||
self.draw_target_pixels_or_mask(
|
||||
layer_id,
|
||||
@@ -348,13 +348,15 @@ impl Engine {
|
||||
|
||||
let dirty_r = tip.size.max(2.0);
|
||||
if let Some((lx, ly, _)) = self.last_stroke_pos {
|
||||
if lx >= 0.0 && ly >= 0.0 && lx < w && ly < h {
|
||||
self.document.expand_dirty(lx as u32, ly as u32, dirty_r);
|
||||
}
|
||||
self.document.expand_dirty(x as u32, y as u32, dirty_r);
|
||||
if let Some((lx, ly, _)) = self.last_stroke_pos {
|
||||
self.expand_stroke_bounds(lx as u32, ly as u32, dirty_r);
|
||||
}
|
||||
}
|
||||
if inside {
|
||||
self.document.expand_dirty(x as u32, y as u32, dirty_r);
|
||||
self.expand_stroke_bounds(x as u32, y as u32, dirty_r);
|
||||
}
|
||||
|
||||
if self.editing_mask {
|
||||
self.draw_target_pixels_or_mask(
|
||||
|
||||
@@ -1024,8 +1024,9 @@ pub struct CanvasShaderProgram {
|
||||
/// Convert a viewport-local point to canvas-space coordinates.
|
||||
///
|
||||
/// `local_pos` is relative to the shader widget's top-left corner.
|
||||
/// `viewport_size` is the widget's size. Returns `(Some(x), Some(y))`
|
||||
/// when inside the canvas image bounds, otherwise `(None, None)`.
|
||||
/// `viewport_size` is the widget's size. Returns the full signed canvas
|
||||
/// coordinates even when the cursor is outside the visible canvas image,
|
||||
/// so brush strokes can continue mathematically beyond the canvas edges.
|
||||
fn screen_to_canvas_local(
|
||||
local_pos: Point,
|
||||
viewport_w: f32,
|
||||
@@ -1053,17 +1054,7 @@ fn screen_to_canvas_local(
|
||||
let canvas_x = (local_pos.x - origin_x) / zoom;
|
||||
let canvas_y = (local_pos.y - origin_y) / zoom;
|
||||
|
||||
let cx = if canvas_x >= 0.0 && canvas_x < engine_w {
|
||||
Some(canvas_x)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cy = if canvas_y >= 0.0 && canvas_y < engine_h {
|
||||
Some(canvas_y)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(cx, cy)
|
||||
(Some(canvas_x), Some(canvas_y))
|
||||
}
|
||||
|
||||
impl shader::Program<Message> for CanvasShaderProgram {
|
||||
@@ -1106,8 +1097,8 @@ impl shader::Program<Message> for CanvasShaderProgram {
|
||||
|
||||
// Left mouse drag (drawing)
|
||||
if state.left_pressed {
|
||||
let cx = canvas_x.unwrap_or(0.0).clamp(0.0, self.engine_w as f32);
|
||||
let cy = canvas_y.unwrap_or(0.0).clamp(0.0, self.engine_h as f32);
|
||||
let cx = canvas_x.unwrap_or(0.0);
|
||||
let cy = canvas_y.unwrap_or(0.0);
|
||||
return (
|
||||
iced::event::Status::Captured,
|
||||
Some(Message::CanvasPointerMoved {
|
||||
|
||||
@@ -200,6 +200,9 @@ impl Program<Message> for CanvasViewportProgram {
|
||||
}
|
||||
|
||||
/// Convert viewport-space coordinates to canvas-space coordinates.
|
||||
///
|
||||
/// Returns signed canvas coordinates even when the cursor is outside the canvas
|
||||
/// image so brush strokes can start or continue beyond the visible edges.
|
||||
fn viewport_to_canvas(viewport_pos: Point, bounds: Rectangle, config: &CanvasViewportConfig) -> Option<(f32, f32)> {
|
||||
let display_w = config.engine_w as f32 * config.zoom;
|
||||
let display_h = config.engine_h as f32 * config.zoom;
|
||||
@@ -210,13 +213,7 @@ fn viewport_to_canvas(viewport_pos: Point, bounds: Rectangle, config: &CanvasVie
|
||||
let canvas_x = (viewport_pos.x - origin_x) / config.zoom;
|
||||
let canvas_y = (viewport_pos.y - origin_y) / config.zoom;
|
||||
|
||||
if canvas_x >= 0.0 && canvas_x < config.engine_w as f32
|
||||
&& canvas_y >= 0.0 && canvas_y < config.engine_h as f32
|
||||
{
|
||||
Some((canvas_x, canvas_y))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the canvas viewport widget.
|
||||
|
||||
Reference in New Issue
Block a user