feat: 实现选择虚线框效果
This commit is contained in:
142
src/mscanvas.rs
142
src/mscanvas.rs
@@ -145,16 +145,19 @@ impl MSCanvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_width(&mut self, line_width: i32) {
|
pub fn line_width(&mut self, line_width: i32) -> &mut Self {
|
||||||
self.line_width = line_width;
|
self.line_width = line_width;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_color(&mut self, color: MSColor) {
|
pub fn fill_color(&mut self, color: MSColor) -> &mut Self {
|
||||||
self.fill_color = color;
|
self.fill_color = color;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stroke_color(&mut self, color: MSColor) {
|
pub fn stroke_color(&mut self, color: MSColor) -> &mut Self {
|
||||||
self.stroke_color = color;
|
self.stroke_color = color;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
pub fn size(&self) -> (i32, i32) {
|
pub fn size(&self) -> (i32, i32) {
|
||||||
(self.width, self.height)
|
(self.width, self.height)
|
||||||
@@ -221,6 +224,31 @@ impl MSCanvas {
|
|||||||
self.pixels[index + 3],
|
self.pixels[index + 3],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn overlay_pixels(&mut self, canvas: &MSCanvas) -> Vec<u8> {
|
||||||
|
let mut pixels = canvas.get_pixels();
|
||||||
|
|
||||||
|
let width = canvas.width.min(self.width);
|
||||||
|
let height = canvas.height.min(self.height);
|
||||||
|
|
||||||
|
for y in 0..height {
|
||||||
|
for x in 0..width {
|
||||||
|
let index = ((y * width + x) * 4) as usize;
|
||||||
|
|
||||||
|
// 跳过透明像素
|
||||||
|
if self.pixels[index + 3] == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -267,7 +295,26 @@ impl MSCanvas {
|
|||||||
self.pixels[index + 3] = self.stroke_color.a; // A
|
self.pixels[index + 3] = self.stroke_color.a; // A
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_row(&mut self, xs: i32, xe: i32, y: i32) {
|
fn draw_column_with_color(&mut self, ys: i32, ye: i32, x: i32, color: MSColor) {
|
||||||
|
if x < 0 || x >= self.width {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ys = ys.clamp(0, self.height - 1);
|
||||||
|
let ye = ye.clamp(0, self.height);
|
||||||
|
for y in ys..ye {
|
||||||
|
let index = ((y * self.width + x) * 4) as usize;
|
||||||
|
|
||||||
|
// 写入 RGBA 数据
|
||||||
|
// 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
|
||||||
|
self.pixels[index] = color.r; // R
|
||||||
|
self.pixels[index + 1] = color.g; // G
|
||||||
|
self.pixels[index + 2] = color.b; // B
|
||||||
|
self.pixels[index + 3] = color.a; // A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_row_with_color(&mut self, xs: i32, xe: i32, y: i32, color: MSColor) {
|
||||||
if y < 0 || y >= self.height {
|
if y < 0 || y >= self.height {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -279,20 +326,36 @@ impl MSCanvas {
|
|||||||
|
|
||||||
// 写入 RGBA 数据
|
// 写入 RGBA 数据
|
||||||
// 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
|
// 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
|
||||||
self.pixels[index] = self.fill_color.r; // R
|
self.pixels[index] = color.r; // R
|
||||||
self.pixels[index + 1] = self.fill_color.g; // G
|
self.pixels[index + 1] = color.g; // G
|
||||||
self.pixels[index + 2] = self.fill_color.b; // B
|
self.pixels[index + 2] = color.b; // B
|
||||||
self.pixels[index + 3] = self.fill_color.a; // A
|
self.pixels[index + 3] = color.a; // A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fill_column(&mut self, ys: i32, ye: i32, x: i32) {
|
||||||
|
self.draw_column_with_color(ys, ye, x, self.fill_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_row(&mut self, xs: i32, xe: i32, y: i32) {
|
||||||
|
self.draw_row_with_color(xs, xe, y, self.fill_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stroke_column(&mut self, ys: i32, ye: i32, x: i32) {
|
||||||
|
self.draw_column_with_color(ys, ye, x, self.stroke_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stroke_row(&mut self, xs: i32, xe: i32, y: i32) {
|
||||||
|
self.draw_row_with_color(xs, xe, y, self.stroke_color);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fill_pixels(&mut self, points: Vec<Point>) {
|
pub fn fill_pixels(&mut self, points: Vec<Point>) {
|
||||||
for point in points {
|
for point in points {
|
||||||
self.fill_pixel_at(point);
|
self.fill_pixel_at(point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stroke_cross_color(&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;
|
||||||
for dy in -r..=r {
|
for dy in -r..=r {
|
||||||
@@ -875,10 +938,71 @@ impl MSCanvas {
|
|||||||
self.stroke_rect(x as i32, y as i32, width as i32, height as i32);
|
self.stroke_rect(x as i32, y as i32, width as i32, height as i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stroke_dashed_rect(
|
||||||
|
&mut self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
dash_length: i32,
|
||||||
|
gap_length: i32,
|
||||||
|
) {
|
||||||
|
let mut is_dash = true;
|
||||||
|
|
||||||
|
// 上下两边边框
|
||||||
|
for dx in (x..x + width).step_by((dash_length + gap_length) as usize) {
|
||||||
|
if is_dash {
|
||||||
|
self.stroke_row(dx, dx + dash_length, y);
|
||||||
|
self.stroke_row(dx, dx + dash_length, y + height - 1);
|
||||||
|
}
|
||||||
|
is_dash = !is_dash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左右两边边框
|
||||||
|
for dy in (y..y + height).step_by((dash_length + gap_length) as usize) {
|
||||||
|
if is_dash {
|
||||||
|
self.stroke_column(dy, dy + dash_length, x);
|
||||||
|
self.stroke_column(dy, dy + dash_length, x + width - 1);
|
||||||
|
}
|
||||||
|
is_dash = !is_dash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stroke_dashed_rect1(&mut self, p1: Point, p2: Point, dash_length: i32, gap_length: i32) {
|
||||||
|
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 < 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.stroke_dashed_rect(
|
||||||
|
x as i32,
|
||||||
|
y as i32,
|
||||||
|
width as i32,
|
||||||
|
height as i32,
|
||||||
|
dash_length,
|
||||||
|
gap_length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.pixels.fill(255);
|
self.pixels.fill(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear_zero(&mut self) {
|
||||||
|
self.pixels.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear_row(&mut self, xs: i32, xe: i32, y: i32) {
|
pub fn clear_row(&mut self, xs: i32, xe: i32, y: i32) {
|
||||||
if y < 0 || y >= self.height {
|
if y < 0 || y >= self.height {
|
||||||
return;
|
return;
|
||||||
|
|||||||
63
src/paint.rs
63
src/paint.rs
@@ -301,7 +301,10 @@ struct PaintApp {
|
|||||||
tool_states: [bool; Tool::Count as usize],
|
tool_states: [bool; Tool::Count as usize],
|
||||||
tool_selected: Tool,
|
tool_selected: Tool,
|
||||||
|
|
||||||
|
/// 画图层
|
||||||
canvas: MSCanvas,
|
canvas: MSCanvas,
|
||||||
|
/// 显示层
|
||||||
|
view_canvas: MSCanvas,
|
||||||
|
|
||||||
/// 是否正在绘制
|
/// 是否正在绘制
|
||||||
is_drawing: bool,
|
is_drawing: bool,
|
||||||
@@ -336,10 +339,18 @@ impl PaintApp {
|
|||||||
let (width, height) = canvas.size();
|
let (width, height) = canvas.size();
|
||||||
let pixels = canvas.get_pixels();
|
let pixels = canvas.get_pixels();
|
||||||
|
|
||||||
|
let mut view_canvas = MSCanvas::new(width, height);
|
||||||
|
view_canvas.clear_zero();
|
||||||
|
view_canvas
|
||||||
|
.fill_color(MSColor::WHITE)
|
||||||
|
.stroke_color(MSColor::BLACK)
|
||||||
|
.line_width(1);
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
@@ -568,6 +579,9 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -702,6 +716,33 @@ impl PaintApp {
|
|||||||
|
|
||||||
// region tool update
|
// region tool update
|
||||||
|
|
||||||
|
pub fn update_with_select(&mut self, message: Message) {
|
||||||
|
match message {
|
||||||
|
Message::MousePressed(pos) => {
|
||||||
|
self.is_drawing = true;
|
||||||
|
self.view_canvas.save_pixels();
|
||||||
|
self.begin_point = pos;
|
||||||
|
}
|
||||||
|
Message::MouseReleased(pos) => {
|
||||||
|
self.is_drawing = false;
|
||||||
|
self.view_canvas.restore_pixels();
|
||||||
|
self.view_canvas
|
||||||
|
.stroke_dashed_rect1(self.begin_point, pos, 1, 1);
|
||||||
|
self.begin_point = pos;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
Message::MouseMoved(pos) => {
|
||||||
|
if self.is_drawing {
|
||||||
|
self.view_canvas.restore_pixels();
|
||||||
|
self.view_canvas
|
||||||
|
.stroke_dashed_rect1(self.begin_point, pos, 3, 1);
|
||||||
|
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) => {
|
||||||
@@ -727,6 +768,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;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -1145,15 +1200,19 @@ impl PaintApp {
|
|||||||
|
|
||||||
self.is_drawing = false;
|
self.is_drawing = false;
|
||||||
self.control_state = ControlState::Zero;
|
self.control_state = ControlState::Zero;
|
||||||
|
self.view_canvas.clear_zero();
|
||||||
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 将原始字节转换为 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