feat: Implement rectangle

This commit is contained in:
2026-02-26 13:18:05 +08:00
parent 17cb31841f
commit 4216a6f507

View File

@@ -1,11 +1,11 @@
use crate::image_button::image_button;
use crate::mouse_area::mouse_area;
use iced::Theme; use iced::Theme;
use iced::padding; use iced::padding;
use iced::widget::container; use iced::widget::container;
use iced::widget::{Column, button, column, image, row}; 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 iced_core::color;
use crate::image_button::image_button;
use crate::mouse_area::mouse_area;
const WIDTH: u32 = 800; const WIDTH: u32 = 800;
const HEIGHT: u32 = 600; const HEIGHT: u32 = 600;
@@ -113,7 +113,12 @@ impl ColorU8 {
} }
} }
struct Paint {
pub fn point_to_i32(point: Point) -> Point<i32> {
Point::new(point.x as i32, point.y as i32)
}
struct PaintApp {
tool_states: [bool; Tool::Count as usize], tool_states: [bool; Tool::Count as usize],
tool_selected: Tool, tool_selected: Tool,
@@ -141,7 +146,7 @@ struct Paint {
dirty: bool, dirty: bool,
} }
impl Paint { impl PaintApp {
// region iced application (new view update) // region iced application (new view update)
pub fn new() -> Self { pub fn new() -> Self {
@@ -179,11 +184,9 @@ impl Paint {
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
.padding(padding::left(5).top(5)) .padding(padding::left(5).top(5))
.style(|_| { .style(|_| container::Style {
container::Style {
background: Some(color!(0x808080).into()), background: Some(color!(0x808080).into()),
..container::Style::default() ..container::Style::default()
}
}); });
let mut columns: Vec<Element<'_, Message, Theme, Renderer>> = Vec::new(); let mut columns: Vec<Element<'_, Message, Theme, Renderer>> = Vec::new();
@@ -197,7 +200,7 @@ impl Paint {
let btn2 = image_button( let btn2 = image_button(
format!("image/normal/normal_{:02}.jpg", i + 2), format!("image/normal/normal_{:02}.jpg", i + 2),
format!("image/selected/selected_{: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()); columns.push(row![btn1, btn2].into());
@@ -253,6 +256,9 @@ impl Paint {
Tool::Line => { Tool::Line => {
self.update_with_line(message); 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) { pub fn update_with_pencil(&mut self, message: Message) {
match message { match message {
Message::MousePressed(pos) => { Message::MousePressed(pos) => {
println!("pressed: {:?}", pos);
self.is_drawing = true; self.is_drawing = true;
self.draw_pixel_at1(pos); self.draw_pixel_atp(pos);
self.begin_point = pos; self.begin_point = pos;
} }
Message::MouseReleased(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 // endregion
pub fn update_tool_states(&mut self, tool: Tool) { pub fn update_tool_states(&mut self, tool: Tool) {
@@ -368,7 +394,7 @@ impl Paint {
/// draw method /// draw method
#[allow(unused)] #[allow(unused)]
impl Paint { impl PaintApp {
fn pixel_at(&self, x: i32, y: i32) -> ColorU8 { fn pixel_at(&self, x: i32, y: i32) -> ColorU8 {
// 边界检查 // 边界检查
if x < 0 || x >= WIDTH as i32 || y < 0 || y >= HEIGHT as i32 { 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) { fn draw_pixel_at_raw(&mut self, x: u32, y: u32) {
// 计算索引:(y * width + x) * 4 // 计算索引:(y * width + x) * 4
let index = ((y * WIDTH + x) * 4) as usize; let index = ((y * WIDTH + x) * 4) as usize;
@@ -429,7 +456,7 @@ impl Paint {
self.dirty = true; self.dirty = true;
} }
/// 核心绘图逻辑:直接在字节数组上操作 #[inline]
fn draw_pixel_at(&mut self, x: i32, y: i32) { fn draw_pixel_at(&mut self, x: i32, y: i32) {
// 边界检查 // 边界检查
if x < 0 || x >= WIDTH as i32 || y < 0 || y >= HEIGHT as i32 { if x < 0 || x >= WIDTH as i32 || y < 0 || y >= HEIGHT as i32 {
@@ -460,36 +487,21 @@ impl Paint {
self.dirty = true; 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) self.draw_pixel_at(pos.x as i32, pos.y as i32)
} }
fn draw_lines(&mut self, points: &[Point]) { fn draw_pixels(&mut self, points: Vec<Point<i32>>) {
if points.is_empty() { for point in &points {
return; self.draw_pixel_at(point.x, point.y);
}
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();
} }
} }
/// Bresenham's line drawing algorithm fn bresenham_line(&mut self, begin: Point<i32>, end: Point<i32>) -> Vec<Point<i32>> {
fn draw_line(&mut self, begin: Point, end: Point) { let x1 = begin.x ;
let x1 = begin.x as i32; let y1 = begin.y;
let y1 = begin.y as i32; let x2 = end.x ;
let x2 = end.x as i32; let y2 = end.y;
let y2 = end.y as i32;
// draw start end point, 防止多条线段在连接点出现断开(比如 ab bc
self.draw_pixel_at(x1, y1);
self.draw_pixel_at(x2, y2);
let dx = (x2 - x1); let dx = (x2 - x1);
let dy = (y2 - y1); let dy = (y2 - y1);
@@ -503,17 +515,20 @@ impl Paint {
let xe; let xe;
let ye; let ye;
let mut points = Vec::new();
if dy1 <= dx1 { if dy1 <= dx1 {
if dx >= 0 { if dx >= 0 {
x = x1; x = x1;
y = y1; y = y1;
xe = x2; xe = x2;
ye = y2;
} else { } else {
x = x2; x = x2;
y = y2; y = y2;
xe = x1; xe = x1;
ye = y1;
} }
self.draw_pixel_at(x, y); points.push(Point::new(x, y));
while x < xe { while x < xe {
x += 1; x += 1;
if px < 0 { if px < 0 {
@@ -526,19 +541,21 @@ impl Paint {
} }
px = px + 2 * (dy1 - dx1); px = px + 2 * (dy1 - dx1);
} }
self.draw_pixel_at(x, y); points.push(Point::new(x, y));
} }
} else { } else {
if dy >= 0 { if dy >= 0 {
x = x1; x = x1;
y = y1; y = y1;
ye = y2; ye = y2;
xe = x2;
} else { } else {
x = x2; x = x2;
y = y2; y = y2;
ye = y1; ye = y1;
xe = x1;
} }
self.draw_pixel_at(x, y); points.push(Point::new(x, y));
while y < ye { while y < ye {
y = y + 1; y = y + 1;
if py <= 0 { if py <= 0 {
@@ -551,7 +568,36 @@ impl Paint {
} }
py = py + 2 * (dx1 - dy1); 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<i32>, end: Point<i32>, 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) (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 { pub fn main() -> iced::Result {
iced::application(Paint::new, Paint::update, Paint::view) iced::application(PaintApp::new, PaintApp::update, PaintApp::view)
.theme(Theme::Dark) .theme(Theme::Dark)
.run() .run()
} }