Compare commits

..

14 Commits

Author SHA1 Message Date
53a0a227ee feat: 实现自由选择框功能 2026-03-07 17:53:54 +08:00
aaa1714f0f feat: 选择功能支持透明模式 2026-03-07 16:23:56 +08:00
d409c11f50 feat: 优化 overlay_pixels 2026-03-07 15:51:13 +08:00
2631fec53c feat: 优化 overlay_pixels 2026-03-06 16:37:20 +08:00
6db8941b67 fix: 矩形框移动出界 2026-03-06 15:39:57 +08:00
d694a69723 feat: 实现取色功能 2026-03-05 14:20:43 +08:00
51055b8ea8 feat: 实现矩形选择功能 2026-03-04 17:36:27 +08:00
de76574654 feat: 实现选择虚线框效果 2026-03-04 00:58:35 +08:00
09d29473bb feat: 椭圆支持线宽和一些重构 2026-03-03 21:06:41 +08:00
849cfb247d feat: 实现 ShapeStyle 配置 2026-03-03 20:55:02 +08:00
a4adcd42c8 feat: 增加 ShapeStyle 配置 2026-03-03 15:57:00 +08:00
71c4de08a6 refactor: 重构 MSCanvas 颜色配置 2026-03-03 15:40:43 +08:00
8ca7c52cb1 feat: 实现椭圆填充功能 2026-03-03 01:28:48 +08:00
1174019eff fix: 画好的多边形颜色改变 2026-03-03 01:00:06 +08:00
2 changed files with 1134 additions and 293 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,19 @@
use std::thread;
use ::image::{ImageBuffer, ImageError, Rgba}; use ::image::{ImageBuffer, ImageError, Rgba};
use iced::Theme; use iced::Theme;
use iced::padding; use iced::padding;
use iced::time::{self, Instant, milliseconds}; use iced::time::{self, Instant, milliseconds};
use iced::widget::canvas::Canvas; use iced::widget::canvas::Canvas;
use iced::widget::container; use iced::widget::container;
use iced::widget::{Column, button, column, image, pick_list, row, text}; use iced::widget::{Column, button, checkbox, column, image, pick_list, row, text};
use iced::{Border, Element, Length, Point, Renderer, Subscription, Task}; use iced::{Border, Element, Length, Point, Renderer, Subscription, Task};
use iced::{color, event, mouse}; use iced::{color, event, mouse};
use std::thread;
// use std::time::Duration;
use crate::color_box::{ColorSelectionBox, CurrentColorBox}; use crate::color_box::{ColorSelectionBox, CurrentColorBox};
use crate::image_button::image_button; use crate::image_button::image_button;
use crate::mouse_area::mouse_area; use crate::mouse_area::mouse_area;
use crate::mscanvas::{MSCanvas, MSColor}; use crate::mscanvas::{MSCanvas, MSColor, RectSelection};
const WIDTH: u32 = 800; const WIDTH: u32 = 800;
const HEIGHT: u32 = 600; const HEIGHT: u32 = 600;
@@ -51,12 +51,44 @@ impl std::fmt::Display for BrushKind {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq)]
enum ShapeStyle {
Stroke,
Fill,
FillAndStroke,
}
impl ShapeStyle {
const ALL: [ShapeStyle; 3] = [
ShapeStyle::Stroke,
ShapeStyle::Fill,
ShapeStyle::FillAndStroke,
];
}
impl std::fmt::Display for ShapeStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ShapeStyle::Stroke => "Stroke",
ShapeStyle::Fill => "Fill",
ShapeStyle::FillAndStroke => "FillAndStroke",
}
)
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum Message { enum Message {
MousePressed(Point), MousePressed(Point),
MouseReleased(Point), MouseReleased(Point),
MouseMoved(Point), MouseMoved(Point),
MouseDoubleClick(Point), MouseDoubleClick(Point),
MouseRightPressed(Point),
MouseRightReleased(Point),
Clear, Clear,
SavePNG, SavePNG,
@@ -69,6 +101,7 @@ enum Message {
Decrement(ConfigOption), Decrement(ConfigOption),
BrushSelected(BrushKind), BrushSelected(BrushKind),
ShapeStyleSelected(ShapeStyle),
/// 全局鼠标释放 /// 全局鼠标释放
WindowMouseRelease, WindowMouseRelease,
@@ -78,6 +111,8 @@ enum Message {
ClickForegroundColor(MSColor), ClickForegroundColor(MSColor),
ClickBackgroundColor(MSColor), ClickBackgroundColor(MSColor),
SwapForeBackColor, SwapForeBackColor,
CheckboxToggled(bool),
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -194,6 +229,9 @@ struct Config {
airbrush_radius: i32, airbrush_radius: i32,
airbrush_density: i32, airbrush_density: i32,
rounded_radius: i32, rounded_radius: i32,
brush_kind: Option<BrushKind>,
shape_style: Option<ShapeStyle>,
is_transparent_white: bool,
} }
impl Default for Config { impl Default for Config {
@@ -209,7 +247,11 @@ impl Config {
airbrush_radius: 4, airbrush_radius: 4,
airbrush_density: 16, airbrush_density: 16,
rounded_radius: 3, rounded_radius: 3,
brush_kind: None,
shape_style: None,
is_transparent_white: false,
}; };
fn incr(&mut self, option: ConfigOption, step: i32) { fn incr(&mut self, option: ConfigOption, step: i32) {
match option { match option {
ConfigOption::EraserWidth => { ConfigOption::EraserWidth => {
@@ -244,6 +286,14 @@ impl Config {
} }
} }
} }
fn set_brush_kind(&mut self, kind: BrushKind) {
self.brush_kind = Some(kind);
}
fn set_shape_style(&mut self, shape_style: ShapeStyle) {
self.shape_style = Some(shape_style);
}
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
@@ -257,8 +307,12 @@ enum ControlState {
struct PaintApp { struct PaintApp {
tool_states: [bool; Tool::Count as usize], tool_states: [bool; Tool::Count as usize],
tool_selected: Tool, tool_selected: Tool,
prev_tool_selected: Tool,
/// 画图层
canvas: MSCanvas, canvas: MSCanvas,
/// 显示层
view_canvas: MSCanvas,
/// 是否正在绘制 /// 是否正在绘制
is_drawing: bool, is_drawing: bool,
@@ -281,25 +335,35 @@ struct PaintApp {
config: Config, config: Config,
brush_selected: Option<BrushKind>,
foreground_color: MSColor, foreground_color: MSColor,
background_color: MSColor, background_color: MSColor,
rect_selection: Option<RectSelection>,
select_points: Vec<Point>,
} }
impl PaintApp { impl PaintApp {
// region iced application // region iced application
pub fn new() -> Self { pub fn new() -> Self {
let mut canvas = MSCanvas::new(WIDTH as i32, HEIGHT as i32); let canvas = MSCanvas::new(WIDTH as i32, HEIGHT as i32);
let (width, height) = canvas.size(); let (width, height) = canvas.size();
let pixels = canvas.get_pixels(); let pixels = canvas.get_pixels();
let config = Config::default();
canvas.set_line_width(config.line_width); let mut view_canvas = MSCanvas::new(width, height);
Self { view_canvas.clear_zero();
view_canvas
.fill_color(MSColor::WHITE)
.stroke_color(MSColor::BLACK)
.line_width(1);
let mut ins = Self {
tool_states: [false; Tool::Count as usize], tool_states: [false; Tool::Count as usize],
tool_selected: Tool::Count, tool_selected: Tool::Count,
prev_tool_selected: Tool::Count,
canvas, canvas,
view_canvas,
is_drawing: false, is_drawing: false,
begin_point: Point::ORIGIN, begin_point: Point::ORIGIN,
end_point: Point::ORIGIN, end_point: Point::ORIGIN,
@@ -307,11 +371,26 @@ impl PaintApp {
control_points: Vec::with_capacity(2), control_points: Vec::with_capacity(2),
image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels), image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels),
dirty: false, dirty: false,
config, config: Config::default(),
brush_selected: None,
foreground_color: MSColor::BLACK, foreground_color: MSColor::BLACK,
background_color: MSColor::WHITE, background_color: MSColor::WHITE,
} rect_selection: None,
select_points: Vec::new(),
};
ins.canvas
.fill_color(ins.foreground_color)
.stroke_color(ins.foreground_color)
.line_width(ins.config.line_width);
ins.view_canvas
.fill_color(MSColor::BLACK)
.stroke_color(MSColor::BLACK)
.line_width(1);
ins.update_tool_states(Tool::Pencil);
ins
} }
pub fn view(&self) -> Column<'_, Message> { pub fn view(&self) -> Column<'_, Message> {
@@ -322,10 +401,12 @@ impl PaintApp {
.height(Length::Fixed(height as f32)); .height(Length::Fixed(height as f32));
let canvas_area = mouse_area(image_widget) let canvas_area = mouse_area(image_widget)
.on_press(|pos| Message::MousePressed(pos)) // 占位,实际逻辑在 on_drag 或自定义 .on_press(|pos| Message::MousePressed(pos))
.on_release(|pos| Message::MouseReleased(pos)) .on_release(|pos| Message::MouseReleased(pos))
.on_move(|pos| Message::MouseMoved(pos)) .on_move(|pos| Message::MouseMoved(pos))
.on_double_click(|pos| Message::MouseDoubleClick(pos)); .on_double_click(|pos| Message::MouseDoubleClick(pos))
.on_right_press(|pos| Message::MouseRightPressed(pos))
.on_right_release(|pos| Message::MouseRightReleased(pos));
// 注意mouse_area 的 on_move 给出的坐标通常是相对于 widget 左上角的,这正是我们需要的! // 注意mouse_area 的 on_move 给出的坐标通常是相对于 widget 左上角的,这正是我们需要的!
let canvas_area = container(canvas_area) let canvas_area = container(canvas_area)
.width(Length::Fill) .width(Length::Fill)
@@ -470,7 +551,7 @@ impl PaintApp {
.padding(padding::right(5)), .padding(padding::right(5)),
pick_list( pick_list(
&BrushKind::ALL[..], &BrushKind::ALL[..],
self.brush_selected, self.config.brush_kind,
Message::BrushSelected, Message::BrushSelected,
) )
.placeholder("Brush..."), .placeholder("Brush..."),
@@ -501,6 +582,15 @@ impl PaintApp {
], ],
] ]
.padding(padding::right(5)), .padding(padding::right(5)),
pick_list(
&ShapeStyle::ALL[..],
self.config.shape_style,
Message::ShapeStyleSelected,
)
.placeholder("Shape Style..."),
checkbox(self.config.is_transparent_white)
.label("Transparent White")
.on_toggle(Message::CheckboxToggled),
]; ];
debug_area = debug_area.padding(padding::top(10).left(5).bottom(10)); debug_area = debug_area.padding(padding::top(10).left(5).bottom(10));
@@ -516,13 +606,24 @@ impl PaintApp {
} }
pub fn update(&mut self, message: Message) -> Task<Message> { pub fn update(&mut self, message: Message) -> Task<Message> {
// let now = Instant::now();
match self.tool_selected { match self.tool_selected {
Tool::FreeFormSelect => {
self.update_with_free_form_select(message);
}
Tool::Select => {
self.update_with_select(message);
}
Tool::Eraser => { Tool::Eraser => {
self.update_with_eraser(message); self.update_with_eraser(message);
} }
Tool::FillWithColor => { Tool::FillWithColor => {
self.update_with_fill_with_color(message); self.update_with_fill_with_color(message);
} }
Tool::PickColor => {
self.update_with_pick_color(message);
}
Tool::Pencil => { Tool::Pencil => {
self.update_with_pencil(message); self.update_with_pencil(message);
} }
@@ -577,17 +678,20 @@ impl PaintApp {
Message::Increment(opt) => { Message::Increment(opt) => {
self.config.incr(opt, 1); self.config.incr(opt, 1);
if opt == ConfigOption::LineWidth { if opt == ConfigOption::LineWidth {
self.canvas.set_line_width(self.config.line_width); self.canvas.line_width(self.config.line_width);
} }
} }
Message::Decrement(opt) => { Message::Decrement(opt) => {
self.config.incr(opt, -1); self.config.incr(opt, -1);
if opt == ConfigOption::LineWidth { if opt == ConfigOption::LineWidth {
self.canvas.set_line_width(self.config.line_width); self.canvas.line_width(self.config.line_width);
} }
} }
Message::BrushSelected(kind) => { Message::BrushSelected(kind) => {
self.brush_selected = Some(kind); self.config.set_brush_kind(kind);
}
Message::ShapeStyleSelected(kind) => {
self.config.set_shape_style(kind);
} }
Message::WindowMouseRelease => { Message::WindowMouseRelease => {
// 处理鼠标在 canvas_area 外面释放 // 处理鼠标在 canvas_area 外面释放
@@ -605,18 +709,21 @@ impl PaintApp {
} }
Message::ClickForegroundColor(color) => { Message::ClickForegroundColor(color) => {
self.foreground_color = color; self.foreground_color = color;
self.canvas.set_foreground_color(color); self.canvas.stroke_color(color);
self.canvas.fill_color(color);
} }
Message::ClickBackgroundColor(color) => { Message::ClickBackgroundColor(color) => {
self.background_color = color; self.background_color = color;
self.canvas.set_background_color(color);
} }
Message::SwapForeBackColor => { Message::SwapForeBackColor => {
let tmp = self.foreground_color; let tmp = self.foreground_color;
self.foreground_color = self.background_color; self.foreground_color = self.background_color;
self.background_color = tmp; self.background_color = tmp;
self.canvas.set_foreground_color(self.foreground_color); self.canvas.stroke_color(self.foreground_color);
self.canvas.set_background_color(self.background_color); self.canvas.fill_color(self.foreground_color);
}
Message::CheckboxToggled(is_checked) => {
self.config.is_transparent_white = is_checked;
} }
_ => {} _ => {}
} }
@@ -629,6 +736,11 @@ impl PaintApp {
return Task::perform(async { Message::RefreshImage }, |msg| msg); return Task::perform(async { Message::RefreshImage }, |msg| msg);
} }
// let elapsed = now.elapsed();
// if elapsed.gt(&Duration::from_millis(10)) {
// println!("update used: {:?}", elapsed);
// }
Task::none() Task::none()
} }
@@ -648,6 +760,258 @@ impl PaintApp {
// region tool update // region tool update
fn draw_rect_selection(&mut self, x: i32, y: i32) {
if self.rect_selection.is_none() {
return;
}
let rect_selection = self.rect_selection.as_ref().unwrap();
let (width, height) = rect_selection.size();
self.view_canvas.draw_rect_selection(x, y, rect_selection);
self.view_canvas
.stroke_dashed_rect(x, y, width, height, 1, 1);
// 画矩形四条边上的三个点
{
let sw = 3;
let half_sw = sw / 2;
self.view_canvas.fill_rect(x - half_sw, y - half_sw, sw, sw);
self.view_canvas
.fill_rect((x + width / 2) - half_sw, y - half_sw, sw, sw);
self.view_canvas
.fill_rect((x + width - 1) - half_sw, y - half_sw, sw, sw);
self.view_canvas
.fill_rect(x - half_sw, (y + height / 2) - half_sw, sw, sw);
self.view_canvas.fill_rect(
(x + width - 1) - half_sw,
(y + height / 2) - half_sw,
sw,
sw,
);
self.view_canvas
.fill_rect(x - half_sw, (y + height - 1) - half_sw, sw, sw);
self.view_canvas.fill_rect(
(x + width / 2) - half_sw,
(y + height - 1) - half_sw,
sw,
sw,
);
self.view_canvas.fill_rect(
(x + width - 1) - half_sw,
(y + height - 1) - half_sw,
sw,
sw,
);
}
}
fn release_rect_selection(&mut self) {
if self.rect_selection.is_none() {
return;
}
let rect_selection = self.rect_selection.as_ref().unwrap();
let (x, y) = rect_selection.position();
self.view_canvas.restore_pixels();
self.canvas.restore_pixels();
self.canvas.draw_rect_selection(x, y, rect_selection);
self.dirty = true;
self.rect_selection = None;
self.select_points.clear();
}
pub fn update_with_free_form_select(&mut self, message: Message) {
match message {
Message::MousePressed(pos) => match self.control_state {
ControlState::Zero => {
self.is_drawing = true;
self.begin_point = pos;
self.control_state = ControlState::One;
self.select_points.push(pos);
self.view_canvas.save_pixels();
}
ControlState::One => {
if let Some(rect_selection) = self.rect_selection.as_ref() {
if rect_selection.contains(pos) {
self.is_drawing = true;
self.begin_point = pos;
self.control_state = ControlState::Two;
} else {
self.release_rect_selection();
self.is_drawing = true;
self.view_canvas.save_pixels();
self.begin_point = pos;
self.control_state = ControlState::One;
}
}
}
_ => {}
},
Message::MouseReleased(pos) => {
self.is_drawing = false;
match self.control_state {
ControlState::One => {
if self.select_points.len() < 3 {
self.select_points.clear();
self.control_state = ControlState::Zero;
return;
}
self.select_points.push(self.select_points[0]);
self.view_canvas.restore_pixels();
let rect_selection = self.canvas.select_and_clear_polygon(
&self.select_points,
self.config.is_transparent_white,
);
self.canvas.save_pixels();
let (x, y) = rect_selection.position();
self.rect_selection = Some(rect_selection);
self.draw_rect_selection(x, y);
self.begin_point = pos;
self.dirty = true
}
ControlState::Two => {
self.control_state = ControlState::One;
if let Some(rect_selection) = self.rect_selection.as_mut() {
let offset_x = pos.x - self.begin_point.x;
let offset_y = pos.y - self.begin_point.y;
rect_selection.move_offset(offset_x as i32, offset_y as i32);
}
}
_ => {}
}
self.begin_point = pos;
}
Message::MouseMoved(pos) => {
if self.is_drawing {
match self.control_state {
ControlState::One => {
self.select_points.push(pos);
self.view_canvas.draw_line(self.begin_point, pos);
self.begin_point = pos;
self.dirty = true;
}
ControlState::Two => {
if let Some(rect_selection) = self.rect_selection.as_ref() {
let offset_x = pos.x - self.begin_point.x;
let offset_y = pos.y - self.begin_point.y;
let (x, y) = rect_selection.position();
self.view_canvas.restore_pixels();
self.draw_rect_selection(x + offset_x as i32, y + offset_y as i32);
self.dirty = true;
}
}
_ => {}
}
}
}
_ => {}
}
}
pub fn update_with_select(&mut self, message: Message) {
match message {
Message::MousePressed(pos) => match self.control_state {
ControlState::Zero => {
self.is_drawing = true;
self.view_canvas.save_pixels();
self.begin_point = pos;
self.control_state = ControlState::One;
}
ControlState::One => {
if let Some(rect_selection) = self.rect_selection.as_ref() {
if rect_selection.contains(pos) {
self.is_drawing = true;
self.begin_point = pos;
self.control_state = ControlState::Two;
} else {
self.release_rect_selection();
self.is_drawing = true;
self.view_canvas.save_pixels();
self.begin_point = pos;
self.control_state = ControlState::One;
}
}
}
_ => {}
},
Message::MouseReleased(pos) => {
self.is_drawing = false;
match self.control_state {
ControlState::One => {
self.view_canvas.restore_pixels();
let p1 = self.begin_point;
let p2 = pos;
let mut x = p1.x;
let mut y = p1.y;
let mut width = p2.x - p1.x;
let mut height = p2.y - p1.y;
if width < 1.0 || height < 1.0 {
self.control_state = ControlState::Zero;
return;
}
if width < 0.0 && height < 0.0 {
x = p2.x;
y = p2.y;
width = p1.x - p2.x;
height = p1.y - p2.y;
} else if width < 0.0 {
x += width;
width = -width;
} else if height < 0.0 {
y += height;
height = -height;
}
self.rect_selection = Some(self.canvas.select_rect(
x as i32,
y as i32,
width as i32,
height as i32,
self.config.is_transparent_white,
));
self.canvas
.clear_rect(x as i32, y as i32, width as i32, height as i32);
self.canvas.save_pixels();
self.draw_rect_selection(x as i32, y as i32);
self.begin_point = pos;
self.dirty = true;
}
ControlState::Two => {
self.control_state = ControlState::One;
if let Some(rect_selection) = self.rect_selection.as_mut() {
let offset_x = pos.x - self.begin_point.x;
let offset_y = pos.y - self.begin_point.y;
rect_selection.move_offset(offset_x as i32, offset_y as i32);
}
}
_ => {}
}
}
Message::MouseMoved(pos) => {
if self.is_drawing {
match self.control_state {
ControlState::One => {
self.view_canvas.restore_pixels();
self.view_canvas
.stroke_dashed_rect1(self.begin_point, pos, 3, 1);
self.dirty = true;
}
ControlState::Two => {
if let Some(rect_selection) = self.rect_selection.as_ref() {
let offset_x = pos.x - self.begin_point.x;
let offset_y = pos.y - self.begin_point.y;
let (x, y) = rect_selection.position();
self.view_canvas.restore_pixels();
self.draw_rect_selection(x + offset_x as i32, y + offset_y as i32);
self.dirty = true;
}
}
_ => {}
}
}
}
_ => {}
}
}
pub fn update_with_eraser(&mut self, message: Message) { pub fn update_with_eraser(&mut self, message: Message) {
match message { match message {
Message::MousePressed(pos) => { Message::MousePressed(pos) => {
@@ -673,6 +1037,20 @@ impl PaintApp {
); );
self.dirty = true; self.dirty = true;
} }
self.view_canvas.clear_zero();
self.view_canvas.clear_rect(
(pos.x as i32) - self.config.eraser_width / 2,
(pos.y as i32) - self.config.eraser_width / 2,
self.config.eraser_width,
self.config.eraser_width,
);
self.view_canvas.stroke_rect(
(pos.x as i32) - self.config.eraser_width / 2,
(pos.y as i32) - self.config.eraser_width / 2,
self.config.eraser_width,
self.config.eraser_width,
);
self.dirty = true;
} }
_ => {} _ => {}
} }
@@ -688,11 +1066,35 @@ impl PaintApp {
} }
} }
pub fn update_with_pick_color(&mut self, message: Message) {
match message {
Message::MousePressed(pos) => {
let color_option = self.canvas.pixel_at(pos.x as i32, pos.y as i32);
if let Some(color) = color_option {
self.foreground_color = color;
self.canvas.stroke_color(color);
self.canvas.fill_color(color);
}
}
Message::MouseRightPressed(pos) => {
let color_option = self.canvas.pixel_at(pos.x as i32, pos.y as i32);
if let Some(color) = color_option {
self.background_color = color;
}
}
Message::MouseReleased(_) | Message::MouseRightReleased(_) => {
self.update_tool_states(self.prev_tool_selected);
}
_ => {}
}
}
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) => {
self.is_drawing = true; self.is_drawing = true;
self.canvas.draw_pixel_at(pos); self.canvas.fill_pixel_at(pos);
self.begin_point = pos; self.begin_point = pos;
self.dirty = true; self.dirty = true;
} }
@@ -712,15 +1114,12 @@ impl PaintApp {
} }
pub fn update_with_brush(&mut self, message: Message) { pub fn update_with_brush(&mut self, message: Message) {
let mut brush_fn: fn(&mut MSCanvas, Point) = MSCanvas::brush_circle; let brush_fn = match self.get_brush_kind() {
if let Some(kind) = self.brush_selected {
brush_fn = match kind {
BrushKind::Circle => MSCanvas::brush_circle, BrushKind::Circle => MSCanvas::brush_circle,
BrushKind::Square => MSCanvas::brush_square, BrushKind::Square => MSCanvas::brush_square,
BrushKind::Slash => MSCanvas::brush_slash, BrushKind::Slash => MSCanvas::brush_slash,
BrushKind::Backslash => MSCanvas::brush_backslash, BrushKind::Backslash => MSCanvas::brush_backslash,
} };
}
match message { match message {
Message::MousePressed(pos) => { Message::MousePressed(pos) => {
self.is_drawing = true; self.is_drawing = true;
@@ -894,7 +1293,20 @@ impl PaintApp {
Message::MouseMoved(pos) => { Message::MouseMoved(pos) => {
if self.is_drawing { if self.is_drawing {
self.canvas.restore_pixels(); self.canvas.restore_pixels();
self.canvas.rect1(self.begin_point, pos); match self.get_shape_style() {
ShapeStyle::Stroke => {
self.canvas.stroke_rect1(self.begin_point, pos);
}
ShapeStyle::Fill => {
self.canvas.fill_rect1(self.begin_point, pos);
}
ShapeStyle::FillAndStroke => {
self.canvas.fill_color(self.background_color);
self.canvas.fill_rect1(self.begin_point, pos);
self.canvas.fill_color(self.foreground_color);
self.canvas.stroke_rect1(self.begin_point, pos);
}
}
self.dirty = true; self.dirty = true;
} }
} }
@@ -902,6 +1314,25 @@ impl PaintApp {
} }
} }
fn close_polygon(&mut self) {
self.canvas.close_path();
match self.get_shape_style() {
ShapeStyle::Stroke => {
self.canvas.stroke();
}
ShapeStyle::Fill => {
self.canvas.fill();
}
ShapeStyle::FillAndStroke => {
self.canvas.fill_color(self.background_color);
self.canvas.fill();
self.canvas.fill_color(self.foreground_color);
self.canvas.stroke();
}
}
self.dirty = true;
}
pub fn update_with_polygon(&mut self, message: Message) { pub fn update_with_polygon(&mut self, message: Message) {
match message { match message {
Message::MousePressed(pos) => match self.control_state { Message::MousePressed(pos) => match self.control_state {
@@ -938,11 +1369,9 @@ impl PaintApp {
} }
} }
Message::MouseDoubleClick(_pos) => { Message::MouseDoubleClick(_pos) => {
self.canvas.close_path();
self.canvas.stroke();
self.is_drawing = false; self.is_drawing = false;
self.control_state = ControlState::Zero; self.control_state = ControlState::Zero;
self.dirty = true; self.close_polygon();
} }
_ => {} _ => {}
} }
@@ -962,7 +1391,20 @@ impl PaintApp {
Message::MouseMoved(pos) => { Message::MouseMoved(pos) => {
if self.is_drawing { if self.is_drawing {
self.canvas.restore_pixels(); self.canvas.restore_pixels();
self.canvas.ellipse1(self.begin_point, pos); match self.get_shape_style() {
ShapeStyle::Stroke => {
self.canvas.stroke_ellipse1(self.begin_point, pos);
}
ShapeStyle::Fill => {
self.canvas.fill_ellipse1(self.begin_point, pos);
}
ShapeStyle::FillAndStroke => {
self.canvas.fill_color(self.background_color);
self.canvas.fill_ellipse1(self.begin_point, pos);
self.canvas.fill_color(self.foreground_color);
self.canvas.stroke_ellipse1(self.begin_point, pos);
}
}
self.dirty = true; self.dirty = true;
} }
} }
@@ -984,11 +1426,36 @@ impl PaintApp {
Message::MouseMoved(pos) => { Message::MouseMoved(pos) => {
if self.is_drawing { if self.is_drawing {
self.canvas.restore_pixels(); self.canvas.restore_pixels();
self.canvas.round_rect1( match self.get_shape_style() {
ShapeStyle::Stroke => {
self.canvas.stroke_round_rect1(
self.begin_point, self.begin_point,
pos, pos,
self.config.rounded_radius as f32, self.config.rounded_radius as f32,
); );
}
ShapeStyle::Fill => {
self.canvas.fill_round_rect1(
self.begin_point,
pos,
self.config.rounded_radius as f32,
);
}
ShapeStyle::FillAndStroke => {
self.canvas.fill_color(self.background_color);
self.canvas.fill_round_rect1(
self.begin_point,
pos,
self.config.rounded_radius as f32,
);
self.canvas.fill_color(self.foreground_color);
self.canvas.stroke_round_rect1(
self.begin_point,
pos,
self.config.rounded_radius as f32,
);
}
}
self.dirty = true; self.dirty = true;
} }
} }
@@ -998,16 +1465,35 @@ impl PaintApp {
// endregion // endregion
fn get_shape_style(&self) -> ShapeStyle {
self.config.shape_style.unwrap_or(ShapeStyle::Stroke)
}
fn get_brush_kind(&self) -> BrushKind {
self.config.brush_kind.unwrap_or(BrushKind::Circle)
}
pub fn update_tool_states(&mut self, tool: Tool) { pub fn update_tool_states(&mut self, tool: Tool) {
let idx = tool as usize; let idx = tool as usize;
if idx >= self.tool_states.len() { if idx >= self.tool_states.len() {
return; return;
} }
if self.tool_selected == Tool::Polygon { if self.tool_selected == Tool::Polygon && self.control_state != ControlState::Zero {
// 切换到其他工具,闭合路径 // 切换到其他工具,闭合路径
self.canvas.close_path(); self.close_polygon();
self.canvas.stroke(); }
if self.tool_selected == Tool::Select && self.control_state != ControlState::Zero {
// 切换到其他工具,释放选择框
self.release_rect_selection();
}
self.is_drawing = false;
self.control_state = ControlState::Zero;
self.view_canvas.clear_zero();
self.dirty = true; self.dirty = true;
if self.tool_selected == idx.into() {
return;
} }
let old_value = self.tool_states[idx]; let old_value = self.tool_states[idx];
@@ -1015,19 +1501,23 @@ impl PaintApp {
self.tool_states[i] = false; self.tool_states[i] = false;
} }
self.tool_states[idx] = !old_value; self.tool_states[idx] = !old_value;
self.prev_tool_selected = self.tool_selected;
self.tool_selected = idx.into(); self.tool_selected = idx.into();
self.is_drawing = false;
self.control_state = ControlState::Zero;
} }
/// 将原始字节转换为 Iced 的图像句柄 /// 将原始字节转换为 Iced 的图像句柄
fn update_image_handle(&mut self) { fn update_image_handle(&mut self) {
// let now = Instant::now();
// 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大 // 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大
// 这里为了简单直接 clone对于 800x600 (约 2MB) 来说很快 // 这里为了简单直接 clone对于 800x600 (约 2MB) 来说很快
let data = self.canvas.get_pixels(); // let data = self.canvas.get_pixels();
let data = self.view_canvas.overlay_pixels(&self.canvas);
// println!("overlay_pixels used: {:?}", now.elapsed());
let (width, height) = self.canvas.size();
self.image_handle = image::Handle::from_rgba(WIDTH, HEIGHT, data); self.image_handle = image::Handle::from_rgba(width as u32, height as u32, data);
// println!("update_image_handle used: {:?}", now.elapsed());
} }
} }