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 {
|
pub fn select_rect(
|
||||||
let mut result = vec![255; (w * h * 4) as usize];
|
&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 mut index = 0;
|
||||||
let default_color = if is_transparent_white {
|
let default_color = if is_transparent_white {
|
||||||
@@ -281,9 +288,10 @@ impl MSCanvas {
|
|||||||
};
|
};
|
||||||
for yi in y..(y + h) {
|
for yi in y..(y + h) {
|
||||||
for xi in x..(x + w) {
|
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 {
|
if is_transparent_white && color == MSColor::WHITE {
|
||||||
color = MSColor::ZERO;
|
index += 4;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
result[index] = color.r;
|
result[index] = color.r;
|
||||||
result[index + 1] = color.g;
|
result[index + 1] = color.g;
|
||||||
@@ -296,6 +304,47 @@ impl MSCanvas {
|
|||||||
RectSelection::new(x, y, w, h, result)
|
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> {
|
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 width = canvas.width.min(self.width) as usize;
|
||||||
@@ -353,6 +402,18 @@ impl MSCanvas {
|
|||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
impl MSCanvas {
|
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) {
|
pub fn fill_pixel_at(&mut self, point: Point) {
|
||||||
let Point { x, y } = point;
|
let Point { x, y } = point;
|
||||||
self.fill_pixel_at1(x as i32, y as i32);
|
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();
|
let mut result = Vec::new();
|
||||||
if points.len() < 3 {
|
if points.len() < 3 {
|
||||||
return result;
|
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::time::{self, Instant, milliseconds};
|
||||||
use iced::widget::canvas::Canvas;
|
use iced::widget::canvas::Canvas;
|
||||||
use iced::widget::container;
|
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::{Border, Element, Length, Point, Renderer, Subscription, Task};
|
||||||
use iced::{color, event, mouse};
|
use iced::{color, event, mouse};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@@ -339,6 +339,8 @@ struct PaintApp {
|
|||||||
background_color: MSColor,
|
background_color: MSColor,
|
||||||
|
|
||||||
rect_selection: Option<RectSelection>,
|
rect_selection: Option<RectSelection>,
|
||||||
|
|
||||||
|
select_points: Vec<Point>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintApp {
|
impl PaintApp {
|
||||||
@@ -373,6 +375,7 @@ impl PaintApp {
|
|||||||
foreground_color: MSColor::BLACK,
|
foreground_color: MSColor::BLACK,
|
||||||
background_color: MSColor::WHITE,
|
background_color: MSColor::WHITE,
|
||||||
rect_selection: None,
|
rect_selection: None,
|
||||||
|
select_points: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
ins.canvas
|
ins.canvas
|
||||||
@@ -606,6 +609,9 @@ impl PaintApp {
|
|||||||
// let now = Instant::now();
|
// let now = Instant::now();
|
||||||
|
|
||||||
match self.tool_selected {
|
match self.tool_selected {
|
||||||
|
Tool::FreeFormSelect => {
|
||||||
|
self.update_with_free_form_select(message);
|
||||||
|
}
|
||||||
Tool::Select => {
|
Tool::Select => {
|
||||||
self.update_with_select(message);
|
self.update_with_select(message);
|
||||||
}
|
}
|
||||||
@@ -809,6 +815,96 @@ impl PaintApp {
|
|||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
self.rect_selection = None;
|
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) {
|
pub fn update_with_select(&mut self, message: Message) {
|
||||||
|
|||||||
Reference in New Issue
Block a user