Compare commits
4 Commits
de76574654
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2631fec53c | |||
| 6db8941b67 | |||
| d694a69723 | |||
| 51055b8ea8 |
182
src/mscanvas.rs
182
src/mscanvas.rs
@@ -68,6 +68,10 @@ impl MSColor {
|
|||||||
let b = n as u8;
|
let b = n as u8;
|
||||||
Self { r, g, b, a: 0xff }
|
Self { r, g, b, a: 0xff }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn invert(&self) -> Self {
|
||||||
|
Self::new(255 - self.r, 255 - self.g, 255 - self.b, self.a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
@@ -113,6 +117,40 @@ impl Path2D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RectSelection {
|
||||||
|
/// 左上角坐标
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
w: i32,
|
||||||
|
h: i32,
|
||||||
|
pixels: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectSelection {
|
||||||
|
pub fn new(x: i32, y: i32, w: i32, h: i32, pixels: Vec<u8>) -> RectSelection {
|
||||||
|
Self { x, y, w, h, pixels }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, p: Point) -> bool {
|
||||||
|
let x = p.x as i32;
|
||||||
|
let y = p.y as i32;
|
||||||
|
!(x < self.x || y < self.y || x >= self.x + self.w || y >= self.y + self.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_offset(&mut self, offset_x: i32, offset_y: i32) {
|
||||||
|
self.x += offset_x;
|
||||||
|
self.y += offset_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> (i32, i32) {
|
||||||
|
(self.w, self.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> (i32, i32) {
|
||||||
|
(self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct MSCanvas {
|
pub struct MSCanvas {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
@@ -209,41 +247,71 @@ impl MSCanvas {
|
|||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pixel_at(&self, x: i32, y: i32) -> MSColor {
|
pub fn pixel_at(&self, x: i32, y: i32) -> Option<MSColor> {
|
||||||
// 边界检查
|
// 边界检查
|
||||||
if x < 0 || x >= self.width || y < 0 || y >= self.height {
|
if x < 0 || x >= self.width || y < 0 || y >= self.height {
|
||||||
return MSColor::ZERO;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = ((y * self.width + x) * 4) as usize;
|
let index = ((y * self.width + x) * 4) as usize;
|
||||||
|
|
||||||
MSColor::new(
|
Some(MSColor::new(
|
||||||
self.pixels[index],
|
self.pixels[index],
|
||||||
self.pixels[index + 1],
|
self.pixels[index + 1],
|
||||||
self.pixels[index + 2],
|
self.pixels[index + 2],
|
||||||
self.pixels[index + 3],
|
self.pixels[index + 3],
|
||||||
)
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pixel_is(&self, x: i32, y: i32, color: MSColor) -> bool {
|
||||||
|
match self.pixel_at(x, y) {
|
||||||
|
Some(c) => c == color,
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_rect(&self, x: i32, y: i32, w: i32, h: i32) -> RectSelection {
|
||||||
|
let mut result = vec![255; (w * h * 4) as usize];
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
for yi in y..(y + h) {
|
||||||
|
for xi in x..(x + w) {
|
||||||
|
let color = self.pixel_at(xi, yi).unwrap_or(MSColor::WHITE);
|
||||||
|
result[index] = color.r;
|
||||||
|
result[index + 1] = color.g;
|
||||||
|
result[index + 2] = color.b;
|
||||||
|
result[index + 3] = color.a;
|
||||||
|
index += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RectSelection::new(x, y, w, h, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overlay_pixels(&mut self, canvas: &MSCanvas) -> Vec<u8> {
|
pub fn overlay_pixels(&mut self, canvas: &MSCanvas) -> Vec<u8> {
|
||||||
let mut pixels = canvas.get_pixels();
|
let mut pixels = canvas.get_pixels();
|
||||||
|
let width = canvas.width.min(self.width) as usize;
|
||||||
|
let height = canvas.height.min(self.height) as usize;
|
||||||
|
|
||||||
let width = canvas.width.min(self.width);
|
// 预计算步长,避免重复计算
|
||||||
let height = canvas.height.min(self.height);
|
let src_stride = self.width as usize * 4;
|
||||||
|
let dst_stride = width * 4;
|
||||||
|
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
|
let src_base = y * src_stride;
|
||||||
|
let dst_base = y * dst_stride;
|
||||||
|
|
||||||
|
// 使用显式循环,可能被编译器自动向量化
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
let index = ((y * width + x) * 4) as usize;
|
let src_idx = src_base + x * 4;
|
||||||
|
let dst_idx = dst_base + x * 4;
|
||||||
|
|
||||||
// 跳过透明像素
|
// 只在非透明时复制
|
||||||
if self.pixels[index + 3] == 0 {
|
if self.pixels[src_idx + 3] != 0 {
|
||||||
continue;
|
// 一次复制4个字节(可能被优化为单个u32操作)
|
||||||
|
pixels[dst_idx..dst_idx + 4]
|
||||||
|
.copy_from_slice(&self.pixels[src_idx..src_idx + 4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pixels[index] = self.pixels[index];
|
|
||||||
pixels[index + 1] = self.pixels[index + 1];
|
|
||||||
pixels[index + 2] = self.pixels[index + 2];
|
|
||||||
pixels[index + 3] = self.pixels[index + 3];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,6 +423,12 @@ impl MSCanvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fill_pixels_i32(&mut self, points: Vec<Point<i32>>) {
|
||||||
|
for point in points {
|
||||||
|
self.fill_pixel_at1(point.x, point.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stroke_cross(&mut self, point: Point) {
|
pub fn stroke_cross(&mut self, point: Point) {
|
||||||
let Point { x, y } = point;
|
let Point { x, y } = point;
|
||||||
let r = 10;
|
let r = 10;
|
||||||
@@ -540,7 +614,7 @@ impl MSCanvas {
|
|||||||
pub fn fill_slow(&mut self, begin: Point) {
|
pub fn fill_slow(&mut self, begin: Point) {
|
||||||
let start_x = begin.x as i32;
|
let start_x = begin.x as i32;
|
||||||
let start_y = begin.y as i32;
|
let start_y = begin.y as i32;
|
||||||
let target_color = self.pixel_at(start_x, start_y);
|
let target_color = self.pixel_at(start_x, start_y).unwrap_or(MSColor::ZERO);
|
||||||
if target_color == self.fill_color {
|
if target_color == self.fill_color {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -552,7 +626,7 @@ impl MSCanvas {
|
|||||||
if x < 0 || x >= self.width || y < 0 || y >= self.height {
|
if x < 0 || x >= self.width || y < 0 || y >= self.height {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.pixel_at(x, y) == target_color {
|
if self.pixel_is(x, y, target_color) {
|
||||||
self.fill_pixel_at1(x, y);
|
self.fill_pixel_at1(x, y);
|
||||||
|
|
||||||
let p1 = (x - 1, y);
|
let p1 = (x - 1, y);
|
||||||
@@ -578,7 +652,7 @@ impl MSCanvas {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_color = self.pixel_at(start_x, start_y);
|
let target_color = self.pixel_at(start_x, start_y).unwrap_or(MSColor::ZERO);
|
||||||
if target_color == self.fill_color {
|
if target_color == self.fill_color {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -588,11 +662,11 @@ impl MSCanvas {
|
|||||||
|
|
||||||
while let Some((y, mut lx, mut rx)) = stack.pop() {
|
while let Some((y, mut lx, mut rx)) = stack.pop() {
|
||||||
// 向左扩展 lx
|
// 向左扩展 lx
|
||||||
while lx - 1 >= 0 && self.pixel_at(lx - 1, y) == target_color {
|
while lx - 1 >= 0 && self.pixel_is(lx - 1, y, target_color) {
|
||||||
lx -= 1;
|
lx -= 1;
|
||||||
}
|
}
|
||||||
// 向右扩展 rx
|
// 向右扩展 rx
|
||||||
while rx + 1 < width && self.pixel_at(rx + 1, y) == target_color {
|
while rx + 1 < width && self.pixel_is(rx + 1, y, target_color) {
|
||||||
rx += 1;
|
rx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,10 +679,10 @@ impl MSCanvas {
|
|||||||
if y - 1 >= 0 {
|
if y - 1 >= 0 {
|
||||||
let mut x = lx;
|
let mut x = lx;
|
||||||
while x <= rx {
|
while x <= rx {
|
||||||
if self.pixel_at(x, y - 1) == target_color {
|
if self.pixel_is(x, y - 1, target_color) {
|
||||||
let span_start = x;
|
let span_start = x;
|
||||||
// 跳过连续的目标色块
|
// 跳过连续的目标色块
|
||||||
while x <= rx && self.pixel_at(x, y - 1) == target_color {
|
while x <= rx && self.pixel_is(x, y - 1, target_color) {
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
// 将这个 span 入栈(用于后续处理上一行的上一行)
|
// 将这个 span 入栈(用于后续处理上一行的上一行)
|
||||||
@@ -623,9 +697,9 @@ impl MSCanvas {
|
|||||||
if y + 1 < height {
|
if y + 1 < height {
|
||||||
let mut x = lx;
|
let mut x = lx;
|
||||||
while x <= rx {
|
while x <= rx {
|
||||||
if self.pixel_at(x, y + 1) == target_color {
|
if self.pixel_is(x, y + 1, target_color) {
|
||||||
let span_start = x;
|
let span_start = x;
|
||||||
while x <= rx && self.pixel_at(x, y + 1) == target_color {
|
while x <= rx && self.pixel_is(x, y + 1, target_color) {
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
stack.push((y + 1, span_start, x - 1));
|
stack.push((y + 1, span_start, x - 1));
|
||||||
@@ -1217,10 +1291,10 @@ impl MSCanvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 填充一个简单多边形(支持凹多边形)
|
fn rasterize_polygon(&mut self, points: &[Point]) -> Vec<Point<i32>> {
|
||||||
pub fn fill_polygon(&mut self, points: &[Point]) {
|
let mut result = Vec::new();
|
||||||
if points.len() < 3 {
|
if points.len() < 3 {
|
||||||
return;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算 y 范围
|
// 计算 y 范围
|
||||||
@@ -1267,7 +1341,7 @@ impl MSCanvas {
|
|||||||
let x_start = active_edges[i].x.ceil() as i32;
|
let x_start = active_edges[i].x.ceil() as i32;
|
||||||
let x_end = active_edges[i + 1].x.floor() as i32;
|
let x_end = active_edges[i + 1].x.floor() as i32;
|
||||||
for x in x_start..=x_end {
|
for x in x_start..=x_end {
|
||||||
self.fill_pixel_at1(x, y);
|
result.push(Point::new(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1277,6 +1351,14 @@ impl MSCanvas {
|
|||||||
edge.x += edge.dx_dy;
|
edge.x += edge.dx_dy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 填充一个简单多边形(支持凹多边形)
|
||||||
|
pub fn fill_polygon(&mut self, points: &[Point]) {
|
||||||
|
let filled_points = self.rasterize_polygon(points);
|
||||||
|
self.fill_pixels_i32(filled_points);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_path(&mut self) {
|
pub fn begin_path(&mut self) {
|
||||||
@@ -1314,6 +1396,50 @@ impl MSCanvas {
|
|||||||
let points = self.path2d.points.clone();
|
let points = self.path2d.points.clone();
|
||||||
self.fill_polygon(&points[..]);
|
self.fill_polygon(&points[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_rect_selection(&mut self, x: i32, y: i32, rect: &RectSelection) {
|
||||||
|
// 边界检查
|
||||||
|
if x >= self.width || y >= self.height {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 考虑矩形四边出界的情况
|
||||||
|
let w = (rect.w).min(self.width - x).min(rect.w + x);
|
||||||
|
let h = (rect.h).min(self.height - y).min(rect.h + y);
|
||||||
|
|
||||||
|
if w == 0 || h == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut src_offset_x = 0usize;
|
||||||
|
let mut src_offset_y = 0usize;
|
||||||
|
if x < 0 {
|
||||||
|
src_offset_x = (-x) as usize;
|
||||||
|
}
|
||||||
|
if y < 0 {
|
||||||
|
src_offset_y = (-y) as usize;
|
||||||
|
}
|
||||||
|
let x = x.max(0) as usize;
|
||||||
|
let y = y.max(0) as usize;
|
||||||
|
let w = w as usize;
|
||||||
|
let h = h as usize;
|
||||||
|
let width = self.width as usize;
|
||||||
|
|
||||||
|
for dy in 0..h {
|
||||||
|
for dx in 0..w {
|
||||||
|
let src_index = ((dy + src_offset_y) * rect.w as usize + dx + src_offset_x) * 4;
|
||||||
|
if rect.pixels[src_index + 3] == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let dst_index = ((y + dy) * width + x + dx) * 4;
|
||||||
|
|
||||||
|
self.pixels[dst_index] = rect.pixels[src_index];
|
||||||
|
self.pixels[dst_index + 1] = rect.pixels[src_index + 1];
|
||||||
|
self.pixels[dst_index + 2] = rect.pixels[src_index + 2];
|
||||||
|
self.pixels[dst_index + 3] = rect.pixels[src_index + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point_muln(point: Point, t: f32) -> Point {
|
fn point_muln(point: Point, t: f32) -> Point {
|
||||||
|
|||||||
241
src/paint.rs
241
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;
|
||||||
@@ -80,12 +80,15 @@ impl std::fmt::Display for ShapeStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
@@ -300,6 +303,7 @@ 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,
|
||||||
@@ -329,6 +333,8 @@ struct PaintApp {
|
|||||||
|
|
||||||
foreground_color: MSColor,
|
foreground_color: MSColor,
|
||||||
background_color: MSColor,
|
background_color: MSColor,
|
||||||
|
|
||||||
|
rect_selection: Option<RectSelection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintApp {
|
impl PaintApp {
|
||||||
@@ -349,6 +355,7 @@ impl PaintApp {
|
|||||||
let mut ins = Self {
|
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,
|
view_canvas,
|
||||||
is_drawing: false,
|
is_drawing: false,
|
||||||
@@ -361,11 +368,20 @@ impl PaintApp {
|
|||||||
config: Config::default(),
|
config: Config::default(),
|
||||||
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);
|
ins.canvas
|
||||||
ins.canvas.stroke_color(ins.foreground_color);
|
.fill_color(ins.foreground_color)
|
||||||
ins.canvas.line_width(ins.config.line_width);
|
.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
|
ins
|
||||||
}
|
}
|
||||||
@@ -378,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)
|
||||||
@@ -588,6 +606,9 @@ impl PaintApp {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -716,27 +737,161 @@ 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) {
|
pub fn update_with_select(&mut self, message: Message) {
|
||||||
match message {
|
match message {
|
||||||
Message::MousePressed(pos) => {
|
Message::MousePressed(pos) => match self.control_state {
|
||||||
self.is_drawing = true;
|
ControlState::Zero => {
|
||||||
self.view_canvas.save_pixels();
|
self.is_drawing = true;
|
||||||
self.begin_point = pos;
|
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) => {
|
Message::MouseReleased(pos) => {
|
||||||
self.is_drawing = false;
|
self.is_drawing = false;
|
||||||
self.view_canvas.restore_pixels();
|
match self.control_state {
|
||||||
self.view_canvas
|
ControlState::One => {
|
||||||
.stroke_dashed_rect1(self.begin_point, pos, 1, 1);
|
self.view_canvas.restore_pixels();
|
||||||
self.begin_point = pos;
|
let p1 = self.begin_point;
|
||||||
self.dirty = true;
|
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) => {
|
Message::MouseMoved(pos) => {
|
||||||
if self.is_drawing {
|
if self.is_drawing {
|
||||||
self.view_canvas.restore_pixels();
|
match self.control_state {
|
||||||
self.view_canvas
|
ControlState::One => {
|
||||||
.stroke_dashed_rect1(self.begin_point, pos, 3, 1);
|
self.view_canvas.restore_pixels();
|
||||||
self.dirty = true;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -797,6 +952,30 @@ 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) => {
|
||||||
@@ -1037,6 +1216,7 @@ impl PaintApp {
|
|||||||
self.canvas.stroke();
|
self.canvas.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_with_polygon(&mut self, message: Message) {
|
pub fn update_with_polygon(&mut self, message: Message) {
|
||||||
@@ -1078,7 +1258,6 @@ impl PaintApp {
|
|||||||
self.is_drawing = false;
|
self.is_drawing = false;
|
||||||
self.control_state = ControlState::Zero;
|
self.control_state = ControlState::Zero;
|
||||||
self.close_polygon();
|
self.close_polygon();
|
||||||
self.dirty = true;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -1188,7 +1367,19 @@ impl PaintApp {
|
|||||||
if self.tool_selected == Tool::Polygon && self.control_state != ControlState::Zero {
|
if self.tool_selected == Tool::Polygon && self.control_state != ControlState::Zero {
|
||||||
// 切换到其他工具,闭合路径
|
// 切换到其他工具,闭合路径
|
||||||
self.close_polygon();
|
self.close_polygon();
|
||||||
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];
|
||||||
@@ -1196,12 +1387,8 @@ 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;
|
|
||||||
self.view_canvas.clear_zero();
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 将原始字节转换为 Iced 的图像句柄
|
/// 将原始字节转换为 Iced 的图像句柄
|
||||||
|
|||||||
Reference in New Issue
Block a user