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
|
||||
}
|
||||
|
||||
pub fn fill_color(&mut self, color: MSColor) {
|
||||
pub fn fill_color(&mut self, color: MSColor) -> &mut Self {
|
||||
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
|
||||
}
|
||||
pub fn size(&self) -> (i32, i32) {
|
||||
(self.width, self.height)
|
||||
@@ -221,6 +224,31 @@ impl MSCanvas {
|
||||
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)]
|
||||
@@ -267,7 +295,26 @@ impl MSCanvas {
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
@@ -279,20 +326,36 @@ impl MSCanvas {
|
||||
|
||||
// 写入 RGBA 数据
|
||||
// 注意:Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
|
||||
self.pixels[index] = self.fill_color.r; // R
|
||||
self.pixels[index + 1] = self.fill_color.g; // G
|
||||
self.pixels[index + 2] = self.fill_color.b; // B
|
||||
self.pixels[index + 3] = self.fill_color.a; // A
|
||||
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 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>) {
|
||||
for point in points {
|
||||
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 r = 10;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
if y < 0 || y >= self.height {
|
||||
return;
|
||||
|
||||
63
src/paint.rs
63
src/paint.rs
@@ -301,7 +301,10 @@ struct PaintApp {
|
||||
tool_states: [bool; Tool::Count as usize],
|
||||
tool_selected: Tool,
|
||||
|
||||
/// 画图层
|
||||
canvas: MSCanvas,
|
||||
/// 显示层
|
||||
view_canvas: MSCanvas,
|
||||
|
||||
/// 是否正在绘制
|
||||
is_drawing: bool,
|
||||
@@ -336,10 +339,18 @@ impl PaintApp {
|
||||
let (width, height) = canvas.size();
|
||||
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 {
|
||||
tool_states: [false; Tool::Count as usize],
|
||||
tool_selected: Tool::Count,
|
||||
canvas,
|
||||
view_canvas,
|
||||
is_drawing: false,
|
||||
begin_point: Point::ORIGIN,
|
||||
end_point: Point::ORIGIN,
|
||||
@@ -568,6 +579,9 @@ impl PaintApp {
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match self.tool_selected {
|
||||
Tool::Select => {
|
||||
self.update_with_select(message);
|
||||
}
|
||||
Tool::Eraser => {
|
||||
self.update_with_eraser(message);
|
||||
}
|
||||
@@ -702,6 +716,33 @@ impl PaintApp {
|
||||
|
||||
// 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) {
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
@@ -727,6 +768,20 @@ impl PaintApp {
|
||||
);
|
||||
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.control_state = ControlState::Zero;
|
||||
self.view_canvas.clear_zero();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// 将原始字节转换为 Iced 的图像句柄
|
||||
fn update_image_handle(&mut self) {
|
||||
// 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大
|
||||
// 这里为了简单直接 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