Compare commits
11 Commits
02c18c7387
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2631fec53c | |||
| 6db8941b67 | |||
| d694a69723 | |||
| 51055b8ea8 | |||
| de76574654 | |||
| 09d29473bb | |||
| 849cfb247d | |||
| a4adcd42c8 | |||
| 71c4de08a6 | |||
| 8ca7c52cb1 | |||
| 1174019eff |
740
src/mscanvas.rs
740
src/mscanvas.rs
File diff suppressed because it is too large
Load Diff
470
src/paint.rs
470
src/paint.rs
@@ -13,7 +13,7 @@ use iced::{color, event, mouse};
|
|||||||
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,
|
||||||
@@ -194,6 +227,8 @@ 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -209,7 +244,10 @@ 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
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 +282,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 +303,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 +331,33 @@ 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +365,25 @@ 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 +394,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 +544,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 +575,12 @@ impl PaintApp {
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
.padding(padding::right(5)),
|
.padding(padding::right(5)),
|
||||||
|
pick_list(
|
||||||
|
&ShapeStyle::ALL[..],
|
||||||
|
self.config.shape_style,
|
||||||
|
Message::ShapeStyleSelected,
|
||||||
|
)
|
||||||
|
.placeholder("Shape Style..."),
|
||||||
];
|
];
|
||||||
debug_area = debug_area.padding(padding::top(10).left(5).bottom(10));
|
debug_area = debug_area.padding(padding::top(10).left(5).bottom(10));
|
||||||
|
|
||||||
@@ -517,12 +597,18 @@ impl PaintApp {
|
|||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
match self.tool_selected {
|
match self.tool_selected {
|
||||||
|
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 +663,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 +694,18 @@ 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);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -648,6 +737,167 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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 +923,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 +952,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 +1000,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 {
|
BrushKind::Circle => MSCanvas::brush_circle,
|
||||||
brush_fn = match kind {
|
BrushKind::Square => MSCanvas::brush_square,
|
||||||
BrushKind::Circle => MSCanvas::brush_circle,
|
BrushKind::Slash => MSCanvas::brush_slash,
|
||||||
BrushKind::Square => MSCanvas::brush_square,
|
BrushKind::Backslash => MSCanvas::brush_backslash,
|
||||||
BrushKind::Slash => MSCanvas::brush_slash,
|
};
|
||||||
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 +1179,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 +1200,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 +1255,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 +1277,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 +1312,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() {
|
||||||
self.begin_point,
|
ShapeStyle::Stroke => {
|
||||||
pos,
|
self.canvas.stroke_round_rect1(
|
||||||
self.config.rounded_radius as f32,
|
self.begin_point,
|
||||||
);
|
pos,
|
||||||
|
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 +1351,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();
|
}
|
||||||
self.dirty = true;
|
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;
|
||||||
|
|
||||||
|
if self.tool_selected == idx.into() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let old_value = self.tool_states[idx];
|
let old_value = self.tool_states[idx];
|
||||||
@@ -1015,19 +1387,19 @@ 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) {
|
||||||
// 克隆数据以避免所有权问题,或者使用 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);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user