feat: 实现自由选择框功能
This commit is contained in:
@@ -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;
|
||||
|
||||
98
src/paint.rs
98
src/paint.rs
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user