feat: 优化 overlay_pixels

This commit is contained in:
2026-03-07 15:51:13 +08:00
parent 2631fec53c
commit d409c11f50
2 changed files with 50 additions and 15 deletions

View File

@@ -293,24 +293,48 @@ impl MSCanvas {
let width = canvas.width.min(self.width) as usize; let width = canvas.width.min(self.width) as usize;
let height = canvas.height.min(self.height) as usize; let height = canvas.height.min(self.height) as usize;
// 预计算步长,避免重复计算
let src_stride = self.width as usize * 4; let src_stride = self.width as usize * 4;
let dst_stride = width * 4; let dst_stride = canvas.width as usize * 4; // ⚠️ Bug fix: use canvas.width, not `width`
for y in 0..height { for y in 0..height {
let src_base = y * src_stride; let src_row = &self.pixels[y * src_stride..y * src_stride + width * 4];
let dst_base = y * dst_stride; let dst_row = &mut pixels[y * dst_stride..y * dst_stride + width * 4];
// 使用显式循环,可能被编译器自动向量化 // Process 4 pixels at a time (16 bytes) for better auto-vectorization
for x in 0..width { let chunks = width / 4;
let src_idx = src_base + x * 4; let remainder = width % 4;
let dst_idx = dst_base + x * 4;
// 只在非透明时复制 for chunk in 0..chunks {
if self.pixels[src_idx + 3] != 0 { let base = chunk * 16;
// 一次复制4个字节可能被优化为单个u32操作 // Check alpha bytes for 4 pixels at once
pixels[dst_idx..dst_idx + 4] let a0 = src_row[base + 3];
.copy_from_slice(&self.pixels[src_idx..src_idx + 4]); let a1 = src_row[base + 7];
let a2 = src_row[base + 11];
let a3 = src_row[base + 15];
if a0 | a1 | a2 | a3 != 0 {
// At least one pixel is non-transparent
if a0 != 0 && a1 != 0 && a2 != 0 && a3 != 0 {
// All 4 pixels are non-transparent — bulk copy 16 bytes
dst_row[base..base + 16].copy_from_slice(&src_row[base..base + 16]);
} else {
// Mixed — copy individually
for i in 0..4 {
let off = base + i * 4;
if src_row[off + 3] != 0 {
dst_row[off..off + 4].copy_from_slice(&src_row[off..off + 4]);
}
}
}
}
// else: all 4 transparent — skip entirely
}
// Handle remaining pixels
for x in (chunks * 4)..width {
let off = x * 4;
if src_row[off + 3] != 0 {
dst_row[off..off + 4].copy_from_slice(&src_row[off..off + 4]);
} }
} }
} }

View File

@@ -1,5 +1,3 @@
use std::thread;
use ::image::{ImageBuffer, ImageError, Rgba}; use ::image::{ImageBuffer, ImageError, Rgba};
use iced::Theme; use iced::Theme;
use iced::padding; use iced::padding;
@@ -9,6 +7,8 @@ use iced::widget::container;
use iced::widget::{Column, button, column, image, pick_list, row, text}; use iced::widget::{Column, button, 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::time::Duration;
use crate::color_box::{ColorSelectionBox, CurrentColorBox}; use crate::color_box::{ColorSelectionBox, CurrentColorBox};
use crate::image_button::image_button; use crate::image_button::image_button;
@@ -596,6 +596,8 @@ impl PaintApp {
} }
pub fn update(&mut self, message: Message) -> Task<Message> { pub fn update(&mut self, message: Message) -> Task<Message> {
// let now = Instant::now();
match self.tool_selected { match self.tool_selected {
Tool::Select => { Tool::Select => {
self.update_with_select(message); self.update_with_select(message);
@@ -718,6 +720,11 @@ impl PaintApp {
return Task::perform(async { Message::RefreshImage }, |msg| msg); return Task::perform(async { Message::RefreshImage }, |msg| msg);
} }
// let elapsed = now.elapsed();
// if elapsed.gt(&Duration::from_millis(10)) {
// println!("update used: {:?}", elapsed);
// }
Task::none() Task::none()
} }
@@ -1393,13 +1400,17 @@ impl PaintApp {
/// 将原始字节转换为 Iced 的图像句柄 /// 将原始字节转换为 Iced 的图像句柄
fn update_image_handle(&mut self) { fn update_image_handle(&mut self) {
// let now = Instant::now();
// 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大 // 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大
// 这里为了简单直接 clone对于 800x600 (约 2MB) 来说很快 // 这里为了简单直接 clone对于 800x600 (约 2MB) 来说很快
// let data = self.canvas.get_pixels(); // let data = self.canvas.get_pixels();
let data = self.view_canvas.overlay_pixels(&self.canvas); let data = self.view_canvas.overlay_pixels(&self.canvas);
// println!("overlay_pixels used: {:?}", now.elapsed());
let (width, height) = self.canvas.size(); let (width, height) = self.canvas.size();
self.image_handle = image::Handle::from_rgba(width as u32, height as u32, data); self.image_handle = image::Handle::from_rgba(width as u32, height as u32, data);
// println!("update_image_handle used: {:?}", now.elapsed());
} }
} }