From 4216a6f507af61237c69dfd89a3897552de7e009 Mon Sep 17 00:00:00 2001 From: yeqing Date: Thu, 26 Feb 2026 13:18:05 +0800 Subject: [PATCH] feat: Implement rectangle --- src/paint.rs | 153 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/src/paint.rs b/src/paint.rs index 77152f4..cd4fe2c 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -1,11 +1,11 @@ +use crate::image_button::image_button; +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::{Border, Color, Length, Point, Task, Element, Renderer}; +use iced::{Border, Color, Element, Length, Point, Renderer, Task}; use iced_core::color; -use crate::image_button::image_button; -use crate::mouse_area::mouse_area; const WIDTH: u32 = 800; const HEIGHT: u32 = 600; @@ -113,7 +113,12 @@ impl ColorU8 { } } -struct Paint { + +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, @@ -141,7 +146,7 @@ struct Paint { dirty: bool, } -impl Paint { +impl PaintApp { // region iced application (new view update) pub fn new() -> Self { @@ -179,11 +184,9 @@ impl Paint { .width(Length::Fill) .height(Length::Fill) .padding(padding::left(5).top(5)) - .style(|_| { - container::Style { - background: Some(color!(0x808080).into()), - ..container::Style::default() - } + .style(|_| container::Style { + background: Some(color!(0x808080).into()), + ..container::Style::default() }); let mut columns: Vec> = Vec::new(); @@ -193,13 +196,13 @@ impl Paint { format!("image/selected/selected_{:02}.jpg", i + 1), self.tool_states[i], ) - .on_press(Message::ClickTool(Tool::from(i))); + .on_press(Message::ClickTool(Tool::from(i))); let btn2 = image_button( format!("image/normal/normal_{:02}.jpg", i + 2), format!("image/selected/selected_{:02}.jpg", i + 2), - self.tool_states[i+1], + self.tool_states[i + 1], ) - .on_press(Message::ClickTool(Tool::from(i + 1))); + .on_press(Message::ClickTool(Tool::from(i + 1))); columns.push(row![btn1, btn2].into()); } let tool_config_area = container("").width(90).height(200).style(|theme: &Theme| { @@ -253,6 +256,9 @@ impl Paint { Tool::Line => { self.update_with_line(message); } + Tool::Rectangle => { + self.update_with_rectangle(message); + } _ => {} } @@ -301,9 +307,8 @@ impl Paint { pub fn update_with_pencil(&mut self, message: Message) { match message { Message::MousePressed(pos) => { - println!("pressed: {:?}", pos); self.is_drawing = true; - self.draw_pixel_at1(pos); + self.draw_pixel_atp(pos); self.begin_point = pos; } Message::MouseReleased(pos) => { @@ -341,6 +346,27 @@ impl Paint { } } + pub fn update_with_rectangle(&mut self, message: Message) { + match message { + Message::MousePressed(pos) => { + self.is_drawing = true; + self.save_pixels(); + self.begin_point = pos; + } + Message::MouseReleased(pos) => { + self.is_drawing = false; + self.begin_point = pos; + } + Message::MouseMoved(pos) => { + if self.is_drawing { + self.restore_pixels(); + self.stroke_rect1(self.begin_point, pos, self.brush_radius * 2); + } + } + _ => {} + } + } + // endregion pub fn update_tool_states(&mut self, tool: Tool) { @@ -368,7 +394,7 @@ impl Paint { /// draw method #[allow(unused)] -impl Paint { +impl PaintApp { fn pixel_at(&self, x: i32, y: i32) -> ColorU8 { // 边界检查 if x < 0 || x >= WIDTH as i32 || y < 0 || y >= HEIGHT as i32 { @@ -415,6 +441,7 @@ impl Paint { } } + #[inline] fn draw_pixel_at_raw(&mut self, x: u32, y: u32) { // 计算索引:(y * width + x) * 4 let index = ((y * WIDTH + x) * 4) as usize; @@ -429,7 +456,7 @@ impl Paint { 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 { @@ -460,36 +487,21 @@ impl Paint { self.dirty = true; } - fn draw_pixel_at1(&mut self, pos: Point) { + fn draw_pixel_atp(&mut self, pos: Point) { self.draw_pixel_at(pos.x as i32, pos.y as i32) } - fn draw_lines(&mut self, points: &[Point]) { - if points.is_empty() { - return; - } - if points.len() == 1 { - self.draw_pixel_at1(points[0]); - return; - } - - let mut begin = points[0]; - for point in points.iter().skip(1) { - self.draw_line(begin, point.clone()); - begin = point.clone(); + fn draw_pixels(&mut self, points: Vec>) { + for point in &points { + self.draw_pixel_at(point.x, point.y); } } - /// Bresenham's line drawing algorithm - fn draw_line(&mut self, begin: Point, end: Point) { - let x1 = begin.x as i32; - let y1 = begin.y as i32; - let x2 = end.x as i32; - let y2 = end.y as i32; - - // draw start end point, 防止多条线段在连接点出现断开(比如 ab bc) - self.draw_pixel_at(x1, y1); - self.draw_pixel_at(x2, y2); + 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); @@ -503,17 +515,20 @@ impl Paint { 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; } - self.draw_pixel_at(x, y); + points.push(Point::new(x, y)); while x < xe { x += 1; if px < 0 { @@ -526,19 +541,21 @@ impl Paint { } px = px + 2 * (dy1 - dx1); } - self.draw_pixel_at(x, y); + 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; } - self.draw_pixel_at(x, y); + points.push(Point::new(x, y)); while y < ye { y = y + 1; if py <= 0 { @@ -551,7 +568,36 @@ impl Paint { } py = py + 2 * (dx1 - dy1); } - self.draw_pixel_at(x, y); + 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)); + } } } } @@ -727,10 +773,25 @@ impl Paint { (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(Paint::new, Paint::update, Paint::view) + iced::application(PaintApp::new, PaintApp::update, PaintApp::view) .theme(Theme::Dark) .run() }