feat: Implement rectangle
This commit is contained in:
153
src/paint.rs
153
src/paint.rs
@@ -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();
|
||||||
@@ -193,13 +196,13 @@ impl Paint {
|
|||||||
format!("image/selected/selected_{:02}.jpg", i + 1),
|
format!("image/selected/selected_{:02}.jpg", i + 1),
|
||||||
self.tool_states[i],
|
self.tool_states[i],
|
||||||
)
|
)
|
||||||
.on_press(Message::ClickTool(Tool::from(i)));
|
.on_press(Message::ClickTool(Tool::from(i)));
|
||||||
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());
|
||||||
}
|
}
|
||||||
let tool_config_area = container("").width(90).height(200).style(|theme: &Theme| {
|
let tool_config_area = container("").width(90).height(200).style(|theme: &Theme| {
|
||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user