Giới thiệu về ShaderBlendMaskBlitter
Trong quy trình render đồ họa 2D của Raqote, ShaderBlendMaskBlitter đóng vai trò trung gian quan trọng để xử lý việc tô màu các pixel. Cấu trúc này tích hợp ba thành phần chính: bộ tạo màu (Shader), mặt nạ độ trong suốt (Mask) và hàm trộn màu (Blend Function). Dưới đây là phân tích chi tiết về cách thức hoạt động và cấu trúc nội bộ của nó.
Định nghĩa Cấu trúc Dữ liệu
Để quản lý trạng thái render, cấu trúc này lưu trữ các tham số cần thiết cho vùng đích và các công cụ xử lý màu. Các trường dữ liệu chính bao gồm:
pub struct ShaderBlendMaskBlitter<'a> {
pub origin_x: i32, // Tọa độ X bắt đầu của vùng đích
pub origin_y: i32, // Tọa độ Y bắt đầu của vùng đích
pub color_shader: &'a dyn Shader, // Đối tượng shader tạo màu pixel
pub pixel_cache: Vec<u32>, // Bộ đệm tạm chứa màu từ shader
pub target_buffer: &'a mut [u32], // Con trỏ tới buffer ảnh đích
pub row_stride: i32, // Số lượng pixel trên mỗi hàng (stride)
pub compose_func: fn(&[u32], &[u8], &mut [u32]), // Hàm trộn màu tùy chỉnh
}
Triển khai Trait Blitter
Phương thức blit_span chịu trách nhiệm xử lý một dòng pixel cụ thể. Logic này tính toán vị trí bộ nhớ, gọi shader để lấy màu nguồn, sau đó áp dụng hàm trộn cùng với dữ liệu mask.
impl<'a> Blitter for ShaderBlendMaskBlitter<'a> {
fn blit_span(&mut self, y: i32, x_start: i32, x_end: i32, mask_data: &[u8]) {
// Tính toán vị trí bắt đầu của dòng hiện tại trong buffer đích
let row_offset = (y - self.origin_y) as usize * self.row_stride as usize;
// Xác định số lượng pixel cần xử lý trong đoạn này
let pixel_count = (x_end - x_start) as usize;
// Yêu cầu shader tạo màu cho đoạn pixel và lưu vào cache
self.color_shader.shade_span(x_start, y, &mut self.pixel_cache[..], pixel_count);
// Thực thi hàm trộn màu với đầu vào là màu nguồn, mask và buffer đích
(self.compose_func)(
&self.pixel_cache[..], // Dữ liệu màu nguồn
mask_data, // Dữ liệu mặt nạ alpha
&mut self.target_buffer[(row_offset + x_start - self.origin_x) as usize..] // Vùng nhớ đích
);
}
}
Đặc điểm Kỹ thuật Chính
- Tích hợp Shader linh hoạt: Màu sắc pixel không cố định mà được sinh động bởi
color_shaderthông qua phương thứcshade_span, kết quả được lưu trữ tạm thời trước khi ghi. - Chiến lược Trộn màu (Blending): Hàm
compose_funcđược truyền vào từ bên ngoài, cho phép thay đổi thuật toán hòa trộn (như alpha blending, additive, v.v.) mà không cần sửa đổi logic của Blitter. - Hỗ trợ Mặt nạ (Masking): Dữ liệu mask được truyền vào mỗi lần gọi
blit_span, giúp kiểm soát độ trong suốt từng pixel dựa trên hình dạng đường viền hoặc vùng phủ. - Tối ưu truy xuất bộ nhớ: Sử dụng
row_strideđể hỗ trợ các buffer có bộ nhớ không liên tục, đồng thời xử lý theo từng đoạn (span) để tăng hiệu suất.
So sánh với Phiên bản Clip
Phiên bản này được tối giản hóa so với ShaderClipBlendMaskBlitter. Sự khác biệt chính nằm ở việc loại bỏ các trường liên quan đến clip region. Hàm trộn màu chỉ nhận mask làm tham số điều khiển, giúp giảm độ phức tạp tính toán trong các trường hợp không yêu cầu cắt xén hình học phức tạp.
Quy trình Thực thi
- Xác định tọa độ dòng scanline cần vẽ (
y) và đoạn ngang (x_startđếnx_end). - Gọi shader để điền dữ liệu màu thô vào bộ đệm tạm
pixel_cache. - Thực thi hàm
compose_funcđể kết hợp màu nguồn, giá trị mask và màu hiện có trongtarget_buffer.
Ví dụ về Hàm Trộn Màu
Một hàm trộn alpha cơ bản có thể được triển khai như sau, sử dụng vòng lặp để tính toán tỷ lệ hòa trộn dựa trên giá trị mask:
fn standard_alpha_blend(source: &[u32], alpha_map: &[u8], output: &mut [u32]) {
for index in 0..source.len() {
// Chuẩn hóa giá trị alpha từ 0-255 về 0.0-1.0
let factor = alpha_map[index] as f32 * (1.0 / 255.0);
// Thực hiện pha trộn màu giữa nguồn và đích
output[index] = composite_pixels(source[index], output[index], factor);
}
}
Cách tiếp cận này phù hợp cho các hệ thống yêu cầu khả năng tùy biến cao trong quá trình render, ví dụ như giao diện người động hoặc các hiệu ứng hạt nơi thuật toán blending cần thay đổi theo ngữ cảnh.