diff --git a/src/paint.rs b/src/paint.rs index 387c976..f8b34bc 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -4,7 +4,8 @@ use iced::padding; use iced::widget::container; use iced::widget::{Column, button, column, image, pick_list, row, text}; use iced::{Border, Color, Element, Length, Point, Renderer, Task}; -use iced_core::color; +use iced::{Subscription, color, event, mouse}; +use std::thread; use crate::image_button::image_button; use crate::mouse_area::mouse_area; @@ -54,7 +55,7 @@ enum Message { Clear, SavePNG, - // 内部消息:请求刷新图像 + /// 内部消息:请求刷新图像 RefreshImage, ClickTool(Tool), @@ -63,6 +64,9 @@ enum Message { Decrement(ConfigOption), BrushSelected(BrushKind), + + /// 全局鼠标释放 + WindowMouseRelease, } #[derive(Clone, Copy, Debug)] @@ -200,7 +204,7 @@ struct PaintApp { } impl PaintApp { - // region iced application (new view update) + // region iced application pub fn new() -> Self { let mut canvas = MSCanvas::new(WIDTH as i32, HEIGHT as i32); @@ -357,17 +361,10 @@ impl PaintApp { self.dirty = true; } Message::SavePNG => { - let scale = 4; let (width, height) = self.canvas.size(); - let pixels = self.canvas.get_pixels_scale(4); - save_rgba_to_png( - pixels, - (width * scale) as u32, - (height * scale) as u32, - "mspaint.png", - ) - .unwrap(); - println!("save png"); + let pixels = self.canvas.get_pixels(); + let path = "mspaint.png"; + save_pixels_async(pixels, width as u32, height as u32, 4, path.to_string()); } Message::RefreshImage => { if self.dirty { @@ -393,6 +390,10 @@ impl PaintApp { Message::BrushSelected(kind) => { self.brush_selected = Some(kind); } + Message::WindowMouseRelease => { + // 处理鼠标在 canvas_area 外面释放 + self.is_drawing = false; + } _ => {} } @@ -407,6 +408,16 @@ impl PaintApp { Task::none() } + pub fn subscription(&self) -> Subscription { + event::listen().filter_map(|event| match event { + // 只关心鼠标释放事件 + iced::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => { + Some(Message::WindowMouseRelease) + } + _ => None, + }) + } + // endregion // region tool update @@ -580,6 +591,7 @@ impl PaintApp { pub fn main() -> iced::Result { iced::application(PaintApp::new, PaintApp::update, PaintApp::view) .theme(Theme::Dark) + .subscription(PaintApp::subscription) .run() } @@ -596,3 +608,52 @@ fn save_rgba_to_png( img.save(path)?; Ok(()) } + +fn scale_pixels(pixels: Vec, width: u32, height: u32, scale: u32) -> Vec { + if scale <= 1 { + return pixels; + } + let dst_width = width * scale; + let dst_height = height * scale; + let mut dst = vec![0; (dst_width * dst_height * 4) as usize]; // RGBA + + for y in 0..height { + for x in 0..width { + // 源像素索引 + let src_idx = ((y * width + x) * 4) as usize; + + // 源像素颜色 + let r = pixels[src_idx]; + let g = pixels[src_idx + 1]; + let b = pixels[src_idx + 2]; + let a = pixels[src_idx + 3]; + + // 在目标图像中填充 scale×scale 区域 + for dy in 0..scale { + for dx in 0..scale { + let dst_x = x * scale + dx; + let dst_y = y * scale + dy; + let dst_idx = ((dst_y * dst_width + dst_x) * 4) as usize; + + dst[dst_idx] = r; + dst[dst_idx + 1] = g; + dst[dst_idx + 2] = b; + dst[dst_idx + 3] = a; + } + } + } + } + + dst +} + +fn save_pixels_async(pixels: Vec, width: u32, height: u32, scale: u32, path: String) { + // 用 Arc 共享数据(或直接 move 进去) + thread::spawn(move || { + let pixels = scale_pixels(pixels, width, height, scale); + match save_rgba_to_png(pixels, width, height, &path) { + Ok(()) => println!("✅ Image saved to {}", path), + Err(e) => eprintln!("❌ Failed to save image: {}", e), + } + }); +}