From da99cfc765aa251e1007453f4f3bc7f14960060c Mon Sep 17 00:00:00 2001 From: yeqing Date: Thu, 26 Feb 2026 19:38:34 +0800 Subject: [PATCH] refactor: Extract canvas feature --- src/main.rs | 1 + src/mscanvas.rs | 408 ++++++++++++++++++++++++++++++++++++++ src/paint.rs | 513 ++++++------------------------------------------ 3 files changed, 464 insertions(+), 458 deletions(-) create mode 100644 src/mscanvas.rs diff --git a/src/main.rs b/src/main.rs index 49606c1..1ea3e64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod image_button; mod mouse_area; +mod mscanvas; mod paint; pub fn main() -> iced::Result { diff --git a/src/mscanvas.rs b/src/mscanvas.rs new file mode 100644 index 0000000..24951a9 --- /dev/null +++ b/src/mscanvas.rs @@ -0,0 +1,408 @@ +use iced::Point; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct MSColor { + r: u8, + g: u8, + b: u8, + a: u8, +} + +#[allow(unused)] +impl MSColor { + fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + Self { r, g, b, a } + } + + pub const ZERO: MSColor = MSColor { + r: 0, + g: 0, + b: 0, + a: 0, + }; + + pub const BLACK: MSColor = MSColor { + r: 0, + g: 0, + b: 0, + a: 255, + }; + + pub const WHITE: MSColor = MSColor { + r: 255, + g: 255, + b: 255, + a: 255, + }; +} + +pub struct MSCanvas { + width: i32, + height: i32, + + // 原始像素数据:RGBA 格式 + // 长度 = width * height * 4 + pixels: Vec, + pixels_bak: Vec, + + // 当前笔画颜色 + color: MSColor, + + // brush 大小 + brush_radius: f32, +} + +#[allow(unused)] +impl MSCanvas { + pub fn new(width: i32, height: i32) -> Self { + Self { + width, + height, + pixels: vec![255; (width * height * 4) as usize], + pixels_bak: Vec::new(), + color: MSColor::BLACK, + brush_radius: 0.5, + } + } + + pub fn set_color(&mut self, color: MSColor) { + self.color = color; + } + + pub fn set_brush_radius(&mut self, brush_radius: f32) { + self.brush_radius = brush_radius; + } +} + +#[allow(unused)] +impl MSCanvas { + pub fn get_pixels(&self) -> Vec { + self.pixels.clone() + } + + pub fn pixel_at(&self, x: i32, y: i32) -> MSColor { + // 边界检查 + if x < 0 || x >= self.width || y < 0 || y >= self.height as i32 { + return MSColor::ZERO; + } + + let index = ((y * self.width + x) * 4) as usize; + + MSColor::new( + self.pixels[index], + self.pixels[index + 1], + self.pixels[index + 2], + self.pixels[index + 3], + ) + } + + pub fn draw_pixel_at(&mut self, point: Point) { + let Point { x, y } = point; + let x = x as i32; + let y = y as i32; + // 边界检查 + if x < 0 || x >= self.width || y < 0 || y >= self.height { + return; + } + // 计算索引:(y * width + x) * 4 + let index = ((y * self.width + x) * 4) as usize; + + // 写入 RGBA 数据 + // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 + self.pixels[index] = self.color.r; // R + self.pixels[index + 1] = self.color.g; // G + self.pixels[index + 2] = self.color.b; // B + self.pixels[index + 3] = self.color.a; // A + } + + fn draw_pixel_at1(&mut self, x: i32, y: i32) { + // 边界检查 + if x < 0 || x >= self.width || y < 0 || y >= self.height { + return; + } + // 计算索引:(y * width + x) * 4 + let index = ((y * self.width + x) * 4) as usize; + + // 写入 RGBA 数据 + // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 + self.pixels[index] = self.color.r; // R + self.pixels[index + 1] = self.color.g; // G + self.pixels[index + 2] = self.color.b; // B + self.pixels[index + 3] = self.color.a; // A + } + + pub fn draw_pixel_row(&mut self, xs: i32, xe: i32, y: i32) { + if y < 0 || y >= self.height { + return; + } + + let xs = xs.clamp(0, self.width - 1); + let xe = xe.clamp(0, self.width as i32); + let y = y; + for x in xs..xe { + let index = ((y * self.width + x) * 4) as usize; + + // 写入 RGBA 数据 + // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 + self.pixels[index] = self.color.r; // R + self.pixels[index + 1] = self.color.g; // G + self.pixels[index + 2] = self.color.b; // B + self.pixels[index + 3] = self.color.a; // A + } + } + + pub fn draw_pixels(&mut self, points: Vec) { + for point in points { + self.draw_pixel_at(point); + } + } + + pub fn draw_brush_at(&mut self, center: Point) { + if self.brush_radius < 1.0 { + self.draw_pixel_at(center); + return; + } + let square = self.brush_radius * self.brush_radius; + let r = self.brush_radius.floor() as i32; + for dy in -r..=r { + for dx in -r..=r { + if (dx * dx + dy * dy) as f32 <= square { + self.draw_pixel_at(Point::new(center.x + dx as f32, center.y + dy as f32)); + } + } + } + } + + fn bresenham_line(&mut self, begin: Point, end: Point) -> Vec { + let x1 = begin.x; + let y1 = begin.y; + let x2 = end.x; + let y2 = end.y; + + let dx = (x2 - x1); + let dy = (y2 - y1); + let dx1 = dx.abs(); + let dy1 = dy.abs(); + let mut px = 2.0 * dy1 - dx1; + let mut py = 2.0 * dx1 - dy1; + + let mut x; + let mut y; + let xe; + let ye; + + let mut points = Vec::new(); + if dy1 <= dx1 { + if dx >= 0.0 { + x = x1; + y = y1; + xe = x2; + ye = y2; + } else { + x = x2; + y = y2; + xe = x1; + ye = y1; + } + points.push(Point::new(x, y)); + while x < xe { + x += 1.0; + if px < 0.0 { + px = px + 2.0 * dy1; + } else { + if (dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0) { + y = y + 1.0; + } else { + y = y - 1.0; + } + px = px + 2.0 * (dy1 - dx1); + } + points.push(Point::new(x, y)); + } + } else { + if dy >= 0.0 { + x = x1; + y = y1; + ye = y2; + xe = x2; + } else { + x = x2; + y = y2; + ye = y1; + xe = x1; + } + points.push(Point::new(x, y)); + while y < ye { + y = y + 1.0; + if py <= 0.0 { + py = py + 2.0 * dx1; + } else { + if (dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0) { + x = x + 1.0; + } else { + x = x - 1.0; + } + py = py + 2.0 * (dx1 - dy1); + } + points.push(Point::new(x, y)); + } + } + points.push(Point::new(xe, ye)); + + points + } + + pub fn draw_line(&mut self, begin: Point, end: Point) { + let points = self.bresenham_line(begin, end); + for point in points { + self.draw_pixel_at(point); + } + } + + pub fn draw_line_thick(&mut self, begin: Point, end: Point) { + let points = self.bresenham_line(begin, end); + for point in points { + self.draw_brush_at(point); + } + } + + pub fn save_pixels(&mut self) { + self.pixels_bak = self.pixels.clone(); + } + + pub fn restore_pixels(&mut self) { + self.pixels = self.pixels_bak.clone(); + } + + pub fn fill_slow(&mut self, begin: Point) -> (i32, i32) { + let start_x = begin.x as i32; + let start_y = begin.y as i32; + let target_color = self.pixel_at(start_x, start_y); + if target_color == self.color { + return (0, 0); + } + + let mut scan_points = vec![(start_x, start_y)]; + let width = self.width; + let height = self.height; + let mut iter_count = 0; + let mut fill_count = 0; + while let Some((x, y)) = scan_points.pop() { + iter_count += 1; + + if x < 0 || x >= self.width || y < 0 || y >= self.height { + continue; + } + if self.pixel_at(x, y) == target_color { + self.draw_pixel_at1(x, y); + fill_count += 1; + + let p1 = (x - 1, y); + let p2 = (x + 1, y); + let p3 = (x, y - 1); + let p4 = (x, y + 1); + scan_points.push(p1); + scan_points.push(p2); + scan_points.push(p3); + scan_points.push(p4); + } + } + (iter_count, fill_count) + } + + pub fn fill_scanline(&mut self, begin: Point) -> (i32, i32) { + let start_x = begin.x as i32; + let start_y = begin.y as i32; + let width = self.width; + let height = self.height; + + // 边界检查 + if start_x < 0 || start_x >= width || start_y < 0 || start_y >= height { + return (0, 0); + } + + let target_color = self.pixel_at(start_x, start_y); + if target_color == (self.color) { + return (0, 0); + } + + // 栈中存储 (y, x1, x2):表示第 y 行从 x1 到 x2(含)需要向上/下扫描 + let mut stack = vec![(start_y, start_x, start_x)]; + let mut iter_count = 0; + let mut fill_count = 0; + + while let Some((y, mut lx, mut rx)) = stack.pop() { + iter_count += 1; + + // 向左扩展 lx + while lx - 1 >= 0 && self.pixel_at(lx - 1, y) == target_color { + lx -= 1; + } + // 向右扩展 rx + while rx + 1 < width && self.pixel_at(rx + 1, y) == target_color { + rx += 1; + } + + // 填充当前行 [lx, rx] + for x in lx..=rx { + self.draw_pixel_at1(x, y); + fill_count += 1; + } + + // 检查上一行 (y - 1) + if y - 1 >= 0 { + let mut x = lx; + while x <= rx { + if self.pixel_at(x, y - 1) == target_color { + let span_start = x; + // 跳过连续的目标色块 + while x <= rx && self.pixel_at(x, y - 1) == target_color { + x += 1; + } + // 将这个 span 入栈(用于后续处理上一行的上一行) + stack.push((y - 1, span_start, x - 1)); + } else { + x += 1; + } + } + } + + // 检查下一行 (y + 1) + if y + 1 < height { + let mut x = lx; + while x <= rx { + if self.pixel_at(x, y + 1) == target_color { + let span_start = x; + while x <= rx && self.pixel_at(x, y + 1) == target_color { + x += 1; + } + stack.push((y + 1, span_start, x - 1)); + } else { + x += 1; + } + } + } + } + + (iter_count, fill_count) + } + + pub fn stroke_rect(&mut self, x: f32, y: f32, width: f32, height: f32) { + self.draw_line_thick(Point::new(x, y), Point::new(x, y + height)); + self.draw_line_thick(Point::new(x, y + height), Point::new(x + width, y + height)); + self.draw_line_thick(Point::new(x + width, y + height), Point::new(x + width, y)); + self.draw_line_thick(Point::new(x, y), Point::new(x + width, y)); + } + + pub fn stroke_rect1(&mut self, left_top: Point, right_bottom: Point) { + let x = left_top.x; + let y = left_top.y; + let width = (right_bottom.x - left_top.x); + let height = (right_bottom.y - left_top.y); + self.stroke_rect(x, y, width, height); + } + + pub fn clear(&mut self) { + self.pixels.fill(255); + } +} diff --git a/src/paint.rs b/src/paint.rs index cd4fe2c..1584a46 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -3,10 +3,12 @@ use crate::mouse_area::mouse_area; use iced::Theme; use iced::padding; use iced::widget::container; -use iced::widget::{Column, button, column, image, row}; +use iced::widget::{Column, button, column, image, row, text}; use iced::{Border, Color, Element, Length, Point, Renderer, Task}; use iced_core::color; +use crate::mscanvas::MSCanvas; + const WIDTH: u32 = 800; const HEIGHT: u32 = 600; @@ -20,6 +22,9 @@ pub enum Message { RefreshImage, ClickTool(Tool), + + Increment, + Decrement, } #[derive(Clone, Copy, Debug)] @@ -92,43 +97,11 @@ impl From for usize { } } -#[derive(Debug, Clone, Copy, PartialEq)] -struct ColorU8 { - r: u8, - g: u8, - b: u8, - a: u8, -} - -impl ColorU8 { - fn new(r: u8, g: u8, b: u8, a: u8) -> Self { - Self { r, g, b, a } - } - - fn is_same(&self, color: Color) -> bool { - self.r == (color.r * 255.0) as u8 - && self.g == (color.g * 255.0) as u8 - && self.b == (color.b * 255.0) as u8 - && self.a == (color.a * 255.0) as u8 - } -} - - -pub fn point_to_i32(point: Point) -> Point { - Point::new(point.x as i32, point.y as i32) -} - struct PaintApp { tool_states: [bool; Tool::Count as usize], tool_selected: Tool, - // 原始像素数据:RGBA 格式 - // 长度 = WIDTH * HEIGHT * 4 - pixels: Vec, - pixels_bak: Vec, - - // 当前画笔颜色 - color: Color, + canvas: MSCanvas, // 是否正在绘制 is_drawing: bool, @@ -139,32 +112,28 @@ struct PaintApp { // 每次像素变化后需要重新生成 image_handle: image::Handle, - // brush 大小 - brush_radius: i32, - // 标记像素是否被修改,用于优化图像句柄的生成 dirty: bool, + + value: i32, } impl PaintApp { // region iced application (new view update) pub fn new() -> Self { - // 初始化全白背景 (R=255, G=255, B=255, A=255) - let pixels = vec![255u8; (WIDTH * HEIGHT * 4) as usize]; - let data = pixels.clone(); - + let canvas = MSCanvas::new(WIDTH as i32, HEIGHT as i32); + let pixels = canvas.get_pixels(); Self { tool_states: [false; Tool::Count as usize], tool_selected: Tool::Count, - pixels, - pixels_bak: Vec::new(), - color: Color::BLACK, + canvas, is_drawing: false, begin_point: Point::ORIGIN, - image_handle: image::Handle::from_rgba(WIDTH, HEIGHT, data), - brush_radius: 1, + image_handle: image::Handle::from_rgba(WIDTH, HEIGHT, pixels), dirty: false, + + value: 1, } } @@ -220,10 +189,10 @@ impl PaintApp { }); let tool_config_area = container(tool_config_area).padding(padding::top(5).left(5)); columns.push(tool_config_area.into()); - let grid = Column::from_vec(columns).height(Length::Fill); + let grid = Column::from_vec(columns); let tool_area = container(grid) - .padding(padding::top(5).left(5).right(5).bottom(100)) + .padding(padding::top(5).left(5).right(5).bottom(10)) .style(|theme: &Theme| { let palette = theme.extended_palette(); @@ -238,10 +207,19 @@ impl PaintApp { } }); - // We use a column: a simple vertical layout + let debug_area = column![ + text("brush"), + row![ + button("+").on_press(Message::Increment), + text(self.value), + button("-").on_press(Message::Decrement), + ] + ]; + column![ button("CLEAR").on_press(Message::Clear), row![tool_area, canvas_area], + debug_area, ] } @@ -265,7 +243,7 @@ impl PaintApp { match message { Message::Clear => { // 重置为白色 - self.pixels.fill(255); + self.canvas.clear(); self.dirty = true; } Message::RefreshImage => { @@ -277,6 +255,17 @@ impl PaintApp { Message::ClickTool(tool) => { self.update_tool_states(tool); } + Message::Increment => { + self.value = self.value + 1; + self.canvas.set_brush_radius(self.value as f32 / 2.0); + } + Message::Decrement => { + self.value = self.value - 1; + if self.value < 1 { + self.value = 1; + } + self.canvas.set_brush_radius(self.value as f32 / 2.0); + } _ => {} } @@ -298,7 +287,8 @@ impl PaintApp { pub fn update_with_fill_with_color(&mut self, message: Message) { match message { Message::MousePressed(pos) => { - self.fill_scanline(pos); + self.canvas.fill_scanline(pos); + self.dirty = true; } _ => {} } @@ -308,7 +298,7 @@ impl PaintApp { match message { Message::MousePressed(pos) => { self.is_drawing = true; - self.draw_pixel_atp(pos); + self.canvas.draw_pixel_at(pos); self.begin_point = pos; } Message::MouseReleased(pos) => { @@ -317,8 +307,9 @@ impl PaintApp { } Message::MouseMoved(pos) => { if self.is_drawing { - self.draw_line(self.begin_point, pos); + self.canvas.draw_line(self.begin_point, pos); self.begin_point = pos; + self.dirty = true; } } _ => {} @@ -329,8 +320,10 @@ impl PaintApp { match message { Message::MousePressed(pos) => { self.is_drawing = true; - self.save_pixels(); + self.canvas.draw_brush_at(pos); + self.canvas.save_pixels(); self.begin_point = pos; + self.dirty = true; } Message::MouseReleased(pos) => { self.is_drawing = false; @@ -338,8 +331,9 @@ impl PaintApp { } Message::MouseMoved(pos) => { if self.is_drawing { - self.restore_pixels(); - self.draw_line(self.begin_point, pos); + self.canvas.restore_pixels(); + self.canvas.draw_line_thick(self.begin_point, pos); + self.dirty = true; } } _ => {} @@ -350,7 +344,7 @@ impl PaintApp { match message { Message::MousePressed(pos) => { self.is_drawing = true; - self.save_pixels(); + self.canvas.save_pixels(); self.begin_point = pos; } Message::MouseReleased(pos) => { @@ -359,8 +353,9 @@ impl PaintApp { } Message::MouseMoved(pos) => { if self.is_drawing { - self.restore_pixels(); - self.stroke_rect1(self.begin_point, pos, self.brush_radius * 2); + self.canvas.restore_pixels(); + self.canvas.stroke_rect1(self.begin_point, pos); + self.dirty = true; } } _ => {} @@ -386,410 +381,12 @@ impl PaintApp { fn update_image_handle(&mut self) { // 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大 // 这里为了简单直接 clone,对于 800x600 (约 2MB) 来说很快 - let data = self.pixels.clone(); + let data = self.canvas.get_pixels(); self.image_handle = image::Handle::from_rgba(WIDTH, HEIGHT, data); } } -/// draw method -#[allow(unused)] -impl PaintApp { - fn pixel_at(&self, x: i32, y: i32) -> ColorU8 { - // 边界检查 - if x < 0 || x >= WIDTH as i32 || y < 0 || y >= HEIGHT as i32 { - return ColorU8::new(0, 0, 0, 0); - } - - let x = x as u32; - let y = y as u32; - - let index = ((y * WIDTH + x) * 4) as usize; - - ColorU8::new( - self.pixels[index], - self.pixels[index + 1], - self.pixels[index + 2], - self.pixels[index + 3], - ) - } - - fn pixel_at_raw(&self, x: i32, y: i32) -> ColorU8 { - let x = x as u32; - let y = y as u32; - let index = ((y * WIDTH + x) * 4) as usize; - ColorU8::new( - self.pixels[index], - self.pixels[index + 1], - self.pixels[index + 2], - self.pixels[index + 3], - ) - } - - fn pixel_at1(&self, point: Point) -> ColorU8 { - self.pixel_at(point.x as i32, point.y as i32) - } - - fn draw_brush_at(&mut self, center_x: i32, center_y: i32) { - let r = self.brush_radius; - for dy in -r..=r { - for dx in -r..=r { - if dx * dx + dy * dy <= r * r { - self.draw_pixel_at(center_x + dx, center_y + dy); - } - } - } - } - - #[inline] - fn draw_pixel_at_raw(&mut self, x: u32, y: u32) { - // 计算索引:(y * width + x) * 4 - let index = ((y * WIDTH + x) * 4) as usize; - - // 写入 RGBA 数据 - // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 - self.pixels[index] = (self.color.r * 255.0) as u8; // R - self.pixels[index + 1] = (self.color.g * 255.0) as u8; // G - self.pixels[index + 2] = (self.color.b * 255.0) as u8; // B - self.pixels[index + 3] = (self.color.a * 255.0) as u8; // A - - self.dirty = true; - } - - #[inline] - fn draw_pixel_at(&mut self, x: i32, y: i32) { - // 边界检查 - if x < 0 || x >= WIDTH as i32 || y < 0 || y >= HEIGHT as i32 { - return; - } - self.draw_pixel_at_raw(x as u32, y as u32); - } - - fn draw_pixel_row(&mut self, xs: i32, xe: i32, y: i32) { - if y < 0 || y >= HEIGHT as i32 { - return; - } - - let xs = xs.clamp(0, WIDTH as i32 - 1) as u32; - let xe = xe.clamp(0, WIDTH as i32) as u32; - let y = y as u32; - for x in xs..xe { - let index = ((y * WIDTH + x) * 4) as usize; - - // 写入 RGBA 数据 - // 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255 - self.pixels[index] = (self.color.r * 255.0) as u8; // R - self.pixels[index + 1] = (self.color.g * 255.0) as u8; // G - self.pixels[index + 2] = (self.color.b * 255.0) as u8; // B - self.pixels[index + 3] = (self.color.a * 255.0) as u8; // A - } - - self.dirty = true; - } - - fn draw_pixel_atp(&mut self, pos: Point) { - self.draw_pixel_at(pos.x as i32, pos.y as i32) - } - - fn draw_pixels(&mut self, points: Vec>) { - for point in &points { - self.draw_pixel_at(point.x, point.y); - } - } - - fn bresenham_line(&mut self, begin: Point, end: Point) -> Vec> { - let x1 = begin.x ; - let y1 = begin.y; - let x2 = end.x ; - let y2 = end.y; - - let dx = (x2 - x1); - let dy = (y2 - y1); - let dx1 = dx.abs(); - let dy1 = dy.abs(); - let mut px = 2 * dy1 - dx1; - let mut py = 2 * dx1 - dy1; - - let mut x; - let mut y; - let xe; - let ye; - - let mut points = Vec::new(); - if dy1 <= dx1 { - if dx >= 0 { - x = x1; - y = y1; - xe = x2; - ye = y2; - } else { - x = x2; - y = y2; - xe = x1; - ye = y1; - } - points.push(Point::new(x, y)); - while x < xe { - x += 1; - if px < 0 { - px = px + 2 * dy1; - } else { - if (dx < 0 && dy < 0) || (dx > 0 && dy > 0) { - y = y + 1; - } else { - y = y - 1; - } - px = px + 2 * (dy1 - dx1); - } - points.push(Point::new(x, y)); - } - } else { - if dy >= 0 { - x = x1; - y = y1; - ye = y2; - xe = x2; - } else { - x = x2; - y = y2; - ye = y1; - xe = x1; - } - points.push(Point::new(x, y)); - while y < ye { - y = y + 1; - if py <= 0 { - py = py + 2 * dx1; - } else { - if (dx < 0 && dy < 0) || (dx > 0 && dy > 0) { - x = x + 1; - } else { - x = x - 1; - } - py = py + 2 * (dx1 - dy1); - } - points.push(Point::new(x, y)); - } - } - points.push(Point::new(xe, ye)); - - points - } - - /// Bresenham's line drawing algorithm - fn draw_line(&mut self, begin: Point, end: Point) { - let points = self.bresenham_line(point_to_i32(begin), point_to_i32(end)); - self.draw_pixels(points); - } - - fn draw_line_thick(&mut self, begin: Point, end: Point, line_width: i32) { - if line_width <= 0 { - return; - } - - let half = (line_width) / 2; - let points = self.bresenham_line(begin, end); - if line_width == 1 { - self.draw_pixels(points); - return; - } - for point in &points { - for dx in -half..=(line_width - 1 - half) { - for dy in -half..=(line_width - 1 - half) { - self.draw_pixel_at((point.x + dx), (point.y + dy)); - } - } - } - } - - fn save_pixels(&mut self) { - self.pixels_bak = self.pixels.clone(); - } - - fn restore_pixels(&mut self) { - self.pixels = self.pixels_bak.clone(); - } - - fn fill_slow(&mut self, begin: Point) -> (i32, i32) { - let start_x = begin.x as i32; - let start_y = begin.y as i32; - let target_color = self.pixel_at(start_x, start_y); - if target_color.is_same(self.color) { - return (0, 0); - } - - let mut scan_points = vec![(start_x, start_y)]; - let width = WIDTH as i32; - let height = HEIGHT as i32; - let mut iter_count = 0; - let mut fill_count = 0; - while let Some((x, y)) = scan_points.pop() { - iter_count += 1; - - if x < 0 || x >= width || y < 0 || y >= height { - continue; - } - if self.pixel_at_raw(x, y) == target_color { - self.draw_pixel_at_raw(x as u32, y as u32); - fill_count += 1; - - let p1 = (x - 1, y); - let p2 = (x + 1, y); - let p3 = (x, y - 1); - let p4 = (x, y + 1); - scan_points.push(p1); - scan_points.push(p2); - scan_points.push(p3); - scan_points.push(p4); - } - } - (iter_count, fill_count) - } - - fn fill_less_slow(&mut self, begin: Point) -> (i32, i32) { - let start_x = begin.x as i32; - let start_y = begin.y as i32; - let width = WIDTH as i32; - let height = HEIGHT as i32; - - if start_x < 0 || start_x >= width || start_y < 0 || start_y >= height { - return (0, 0); - } - - let target_color = self.pixel_at(start_x, start_y); - if target_color.is_same(self.color) { - return (0, 0); - } - - let mut stack = vec![(start_x, start_y)]; - let mut iter_count = 0; - let mut fill_count = 0; - - while let Some((x, y)) = stack.pop() { - iter_count += 1; - - if x < 0 || x >= width || y < 0 || y >= height { - continue; - } - - if self.pixel_at_raw(x, y) != target_color { - continue; - } - - self.draw_pixel_at_raw(x as u32, y as u32); - fill_count += 1; - - // 提前检查边界再入栈 - if x > 0 { - stack.push((x - 1, y)); - } - if x + 1 < width { - stack.push((x + 1, y)); - } - if y > 0 { - stack.push((x, y - 1)); - } - if y + 1 < height { - stack.push((x, y + 1)); - } - } - - (iter_count, fill_count) - } - - fn fill_scanline(&mut self, begin: Point) -> (i32, i32) { - let start_x = begin.x as i32; - let start_y = begin.y as i32; - let width = WIDTH as i32; - let height = HEIGHT as i32; - - // 边界检查 - if start_x < 0 || start_x >= width || start_y < 0 || start_y >= height { - return (0, 0); - } - - let target_color = self.pixel_at(start_x, start_y); - if target_color.is_same(self.color) { - return (0, 0); - } - - // 栈中存储 (y, x1, x2):表示第 y 行从 x1 到 x2(含)需要向上/下扫描 - let mut stack = vec![(start_y, start_x, start_x)]; - let mut iter_count = 0; - let mut fill_count = 0; - - while let Some((y, mut lx, mut rx)) = stack.pop() { - iter_count += 1; - - // 向左扩展 lx - while lx - 1 >= 0 && self.pixel_at_raw(lx - 1, y) == target_color { - lx -= 1; - } - // 向右扩展 rx - while rx + 1 < width && self.pixel_at_raw(rx + 1, y) == target_color { - rx += 1; - } - - // 填充当前行 [lx, rx] - for x in lx..=rx { - self.draw_pixel_at_raw(x as u32, y as u32); - fill_count += 1; - } - - // 检查上一行 (y - 1) - if y - 1 >= 0 { - let mut x = lx; - while x <= rx { - if self.pixel_at_raw(x, y - 1) == target_color { - let span_start = x; - // 跳过连续的目标色块 - while x <= rx && self.pixel_at_raw(x, y - 1) == target_color { - x += 1; - } - // 将这个 span 入栈(用于后续处理上一行的上一行) - stack.push((y - 1, span_start, x - 1)); - } else { - x += 1; - } - } - } - - // 检查下一行 (y + 1) - if y + 1 < height { - let mut x = lx; - while x <= rx { - if self.pixel_at_raw(x, y + 1) == target_color { - let span_start = x; - while x <= rx && self.pixel_at_raw(x, y + 1) == target_color { - x += 1; - } - stack.push((y + 1, span_start, x - 1)); - } else { - x += 1; - } - } - } - } - - (iter_count, fill_count) - } - - fn stroke_rect(&mut self, x: i32, y: i32, width: i32, height: i32, line_width: i32) { - self.draw_line_thick(Point::new(x, y), Point::new(x, y + height), line_width); - self.draw_line_thick(Point::new(x, y + height), Point::new(x + width, y + height), line_width); - self.draw_line_thick(Point::new(x + width, y + height), Point::new(x + width, y), line_width); - self.draw_line_thick(Point::new(x, y), Point::new(x + width, y), line_width); - } - - fn stroke_rect1(&mut self, left_top: Point, right_bottom: Point, line_width: i32) { - let x = left_top.x as i32; - let y = left_top.y as i32; - let width = (right_bottom.x - left_top.x) as i32; - let height = (right_bottom.y - left_top.y) as i32; - self.stroke_rect(x, y, width, height, line_width); - } -} - pub fn main() -> iced::Result { iced::application(PaintApp::new, PaintApp::update, PaintApp::view) .theme(Theme::Dark)