feat: 实现自由选择框功能

This commit is contained in:
2026-03-07 17:53:54 +08:00
parent aaa1714f0f
commit 53a0a227ee
2 changed files with 163 additions and 6 deletions

View File

@@ -270,8 +270,15 @@ impl MSCanvas {
}
}
pub fn select_rect(&self, x: i32, y: i32, w: i32, h: i32, is_transparent_white: bool) -> RectSelection {
let mut result = vec![255; (w * h * 4) as usize];
pub fn select_rect(
&self,
x: i32,
y: i32,
w: i32,
h: i32,
is_transparent_white: bool,
) -> RectSelection {
let mut result = vec![0; (w * h * 4) as usize];
let mut index = 0;
let default_color = if is_transparent_white {
@@ -281,9 +288,10 @@ impl MSCanvas {
};
for yi in y..(y + h) {
for xi in x..(x + w) {
let mut color = self.pixel_at(xi, yi).unwrap_or(default_color);
let color = self.pixel_at(xi, yi).unwrap_or(default_color);
if is_transparent_white && color == MSColor::WHITE {
color = MSColor::ZERO;
index += 4;
continue;
}
result[index] = color.r;
result[index + 1] = color.g;
@@ -296,6 +304,47 @@ impl MSCanvas {
RectSelection::new(x, y, w, h, result)
}
pub fn select_and_clear_polygon(
&mut self,
points: &[Point],
is_transparent_white: bool,
) -> RectSelection {
let min_x = points.iter().map(|p| p.x as i32).min().unwrap_or(0);
let max_x = points.iter().map(|p| p.x as i32).max().unwrap_or(0);
let min_y = points.iter().map(|p| p.y as i32).min().unwrap_or(0);
let max_y = points.iter().map(|p| p.y as i32).max().unwrap_or(0);
let w = max_x - min_x + 1;
let h = max_y - min_y + 1;
let mut result = vec![0; (w * h * 4) as usize];
let default_color = if is_transparent_white {
MSColor::ZERO
} else {
MSColor::WHITE
};
let filled_points = self.rasterize_polygon(points);
for point in &filled_points {
let dx = point.x - min_x;
let dy = point.y - min_y;
let index = ((dy * w + dx) * 4) as usize;
let color = self.pixel_at(point.x, point.y).unwrap_or(default_color);
if is_transparent_white && color == MSColor::WHITE {
continue;
}
result[index] = color.r;
result[index + 1] = color.g;
result[index + 2] = color.b;
result[index + 3] = color.a;
self.draw_pixel_at_uncheck(point.x, point.y, MSColor::WHITE);
}
RectSelection::new(min_x, min_y, w, h, result)
}
pub fn overlay_pixels(&mut self, canvas: &MSCanvas) -> Vec<u8> {
let mut pixels = canvas.get_pixels();
let width = canvas.width.min(self.width) as usize;
@@ -353,6 +402,18 @@ impl MSCanvas {
#[allow(unused)]
impl MSCanvas {
fn draw_pixel_at_uncheck(&mut self, x: i32, y: i32, color: MSColor) {
// 计算索引:(y * width + x) * 4
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
}
pub fn fill_pixel_at(&mut self, point: Point) {
let Point { x, y } = point;
self.fill_pixel_at1(x as i32, y as i32);
@@ -1323,7 +1384,7 @@ impl MSCanvas {
}
}
fn rasterize_polygon(&mut self, points: &[Point]) -> Vec<Point<i32>> {
fn rasterize_polygon(&self, points: &[Point]) -> Vec<Point<i32>> {
let mut result = Vec::new();
if points.len() < 3 {
return result;

View File

@@ -4,7 +4,7 @@ use iced::padding;
use iced::time::{self, Instant, milliseconds};
use iced::widget::canvas::Canvas;
use iced::widget::container;
use iced::widget::{Column, button, column, image, pick_list, row, text, checkbox};
use iced::widget::{Column, button, checkbox, column, image, pick_list, row, text};
use iced::{Border, Element, Length, Point, Renderer, Subscription, Task};
use iced::{color, event, mouse};
use std::thread;
@@ -339,6 +339,8 @@ struct PaintApp {
background_color: MSColor,
rect_selection: Option<RectSelection>,
select_points: Vec<Point>,
}
impl PaintApp {
@@ -373,6 +375,7 @@ impl PaintApp {
foreground_color: MSColor::BLACK,
background_color: MSColor::WHITE,
rect_selection: None,
select_points: Vec::new(),
};
ins.canvas
@@ -606,6 +609,9 @@ impl PaintApp {
// let now = Instant::now();
match self.tool_selected {
Tool::FreeFormSelect => {
self.update_with_free_form_select(message);
}
Tool::Select => {
self.update_with_select(message);
}
@@ -809,6 +815,96 @@ impl PaintApp {
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) {