diff --git a/src/mscanvas.rs b/src/mscanvas.rs index c587d95..85b8195 100644 --- a/src/mscanvas.rs +++ b/src/mscanvas.rs @@ -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 { 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> { + fn rasterize_polygon(&self, points: &[Point]) -> Vec> { let mut result = Vec::new(); if points.len() < 3 { return result; diff --git a/src/paint.rs b/src/paint.rs index 743fab7..ba3dd4d 100644 --- a/src/paint.rs +++ b/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, + + select_points: Vec, } 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) {