1use crate::blit::{BlitCaps, Blitter, PixelFmt, Rect, Surface};
7
8pub struct CpuBlitter;
10
11impl CpuBlitter {
12 fn pixel_size(fmt: PixelFmt) -> usize {
13 match fmt {
14 PixelFmt::Argb8888 => 4,
15 PixelFmt::Rgb565 => 2,
16 PixelFmt::L8 | PixelFmt::A8 => 1,
17 PixelFmt::A4 => 1,
18 }
19 }
20
21 fn argb8888_to_rgb565(c: u32) -> u16 {
22 let r = ((c >> 16) & 0xff) as u16;
23 let g = ((c >> 8) & 0xff) as u16;
24 let b = (c & 0xff) as u16;
25 ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
26 }
27
28 fn rgb565_to_argb8888(c: u16) -> u32 {
29 let r = ((c >> 11) & 0x1f) as u32;
30 let g = ((c >> 5) & 0x3f) as u32;
31 let b = (c & 0x1f) as u32;
32 0xff00_0000 | ((r << 3) << 16) | ((g << 2) << 8) | (b << 3)
33 }
34
35 fn read_pixel(surf: &Surface, x: i32, y: i32) -> u32 {
36 let bpp = Self::pixel_size(surf.format);
37 let offset = match surf.format {
38 PixelFmt::A4 => (y as usize * surf.stride) + (x as usize / 2),
39 _ => (y as usize * surf.stride) + (x as usize * bpp),
40 };
41 match surf.format {
42 PixelFmt::Argb8888 => {
43 let bytes: [u8; 4] = surf.buf[offset..offset + 4].try_into().unwrap();
44 u32::from_le_bytes(bytes)
45 }
46 PixelFmt::Rgb565 => {
47 let bytes: [u8; 2] = surf.buf[offset..offset + 2].try_into().unwrap();
48 Self::rgb565_to_argb8888(u16::from_le_bytes(bytes))
49 }
50 PixelFmt::L8 => {
51 let v = surf.buf[offset] as u32;
52 0xff00_0000 | (v << 16) | (v << 8) | v
53 }
54 PixelFmt::A8 => {
55 let a = surf.buf[offset] as u32;
56 a << 24
57 }
58 PixelFmt::A4 => {
59 let byte = surf.buf[offset];
60 let nib = if x & 1 == 0 { byte >> 4 } else { byte & 0x0f } as u32;
61 let a = (nib << 4) | nib;
62 a << 24
63 }
64 }
65 }
66
67 fn write_pixel(surf: &mut Surface, x: i32, y: i32, color: u32) {
68 let bpp = Self::pixel_size(surf.format);
69 let offset = match surf.format {
70 PixelFmt::A4 => (y as usize * surf.stride) + (x as usize / 2),
71 _ => (y as usize * surf.stride) + (x as usize * bpp),
72 };
73 match surf.format {
74 PixelFmt::Argb8888 => {
75 surf.buf[offset..offset + 4].copy_from_slice(&color.to_le_bytes());
76 }
77 PixelFmt::Rgb565 => {
78 let c = Self::argb8888_to_rgb565(color);
79 surf.buf[offset..offset + 2].copy_from_slice(&c.to_le_bytes());
80 }
81 PixelFmt::L8 => {
82 let r = (color >> 16) & 0xff;
83 let g = (color >> 8) & 0xff;
84 let b = color & 0xff;
85 surf.buf[offset] = ((r + g + b) / 3) as u8;
86 }
87 PixelFmt::A8 => {
88 surf.buf[offset] = (color >> 24) as u8;
89 }
90 PixelFmt::A4 => {
91 let a = (color >> 24) as u8 >> 4;
92 let byte = &mut surf.buf[offset];
93 if x & 1 == 0 {
94 *byte = (*byte & 0x0f) | (a << 4);
95 } else {
96 *byte = (*byte & 0xf0) | a;
97 }
98 }
99 }
100 }
101
102 fn blend_pixel(src: u32, dst: u32) -> u32 {
103 let sa = (src >> 24) & 0xff;
104 let inv = 255 - sa;
105 let sr = (src >> 16) & 0xff;
106 let sg = (src >> 8) & 0xff;
107 let sb = src & 0xff;
108 let dr = (dst >> 16) & 0xff;
109 let dg = (dst >> 8) & 0xff;
110 let db = dst & 0xff;
111 let r = (sr * sa + dr * inv) / 255;
112 let g = (sg * sa + dg * inv) / 255;
113 let b = (sb * sa + db * inv) / 255;
114 0xff00_0000 | (r << 16) | (g << 8) | b
115 }
116}
117
118impl Blitter for CpuBlitter {
119 fn caps(&self) -> BlitCaps {
120 BlitCaps::FILL | BlitCaps::BLIT | BlitCaps::BLEND | BlitCaps::PFC
121 }
122
123 fn fill(&mut self, dst: &mut Surface, area: Rect, color: u32) {
124 let sx = if area.x < 0 { 0i32 } else { area.x };
126 let sy = if area.y < 0 { 0i32 } else { area.y };
127 let ex = (area.x + area.w as i32).min(dst.width as i32);
128 let ey = (area.y + area.h as i32).min(dst.height as i32);
129 if sx >= ex || sy >= ey {
130 return;
131 }
132 let cw = (ex - sx) as u32;
133
134 match dst.format {
135 PixelFmt::Argb8888 => {
136 let bpp = 4usize;
148 for row in sy..ey {
149 let start = row as usize * dst.stride + sx as usize * bpp;
150 let end = start + cw as usize * bpp;
151 if end > dst.buf.len() {
152 break;
153 }
154 let line = &mut dst.buf[start..end];
165 let line_u32 = unsafe {
166 core::slice::from_raw_parts_mut(
167 line.as_mut_ptr().cast::<u32>(),
168 cw as usize,
169 )
170 };
171 line_u32.fill(color.to_le());
172 }
173 }
174 PixelFmt::Rgb565 => {
175 let c = Self::argb8888_to_rgb565(color);
176 let bpp = 2usize;
177 for row in sy..ey {
178 let start = row as usize * dst.stride + sx as usize * bpp;
179 let end = start + cw as usize * bpp;
180 if end > dst.buf.len() {
181 break;
182 }
183 let line = &mut dst.buf[start..end];
188 let line_u16 = unsafe {
189 core::slice::from_raw_parts_mut(
190 line.as_mut_ptr().cast::<u16>(),
191 cw as usize,
192 )
193 };
194 line_u16.fill(c.to_le());
195 }
196 }
197 _ => {
198 for y in sy..ey {
199 for x in sx..ex {
200 Self::write_pixel(dst, x, y, color);
201 }
202 }
203 }
204 }
205 }
206
207 fn blit(&mut self, src: &Surface, src_area: Rect, dst: &mut Surface, dst_pos: (i32, i32)) {
208 let clip_x = if dst_pos.0 < 0 { -dst_pos.0 } else { 0 };
210 let clip_y = if dst_pos.1 < 0 { -dst_pos.1 } else { 0 };
211 let dst_x = dst_pos.0.max(0);
212 let dst_y = dst_pos.1.max(0);
213 let w = (src_area.w as i32 - clip_x).min(dst.width as i32 - dst_x);
214 let h = (src_area.h as i32 - clip_y).min(dst.height as i32 - dst_y);
215 if w <= 0 || h <= 0 {
216 return;
217 }
218
219 let src_x0 = src_area.x + clip_x;
220 let src_y0 = src_area.y + clip_y;
221
222 if src.format == dst.format {
223 let bpp = Self::pixel_size(src.format);
224 for row in 0..h {
225 let src_start = ((src_y0 + row) as usize * src.stride) + (src_x0 as usize * bpp);
226 let dst_start = ((dst_y + row) as usize * dst.stride) + (dst_x as usize * bpp);
227 let len = w as usize * bpp;
228 dst.buf[dst_start..dst_start + len]
229 .copy_from_slice(&src.buf[src_start..src_start + len]);
230 }
231 return;
232 }
233
234 for row in 0..h {
235 for col in 0..w {
236 let px = Self::read_pixel(src, src_x0 + col, src_y0 + row);
237 Self::write_pixel(dst, dst_x + col, dst_y + row, px);
238 }
239 }
240 }
241
242 fn blend(&mut self, src: &Surface, src_area: Rect, dst: &mut Surface, dst_pos: (i32, i32)) {
243 let clip_x = if dst_pos.0 < 0 { -dst_pos.0 } else { 0 };
245 let clip_y = if dst_pos.1 < 0 { -dst_pos.1 } else { 0 };
246 let dst_x = dst_pos.0.max(0);
247 let dst_y = dst_pos.1.max(0);
248 let w = (src_area.w as i32 - clip_x).min(dst.width as i32 - dst_x);
249 let h = (src_area.h as i32 - clip_y).min(dst.height as i32 - dst_y);
250 if w <= 0 || h <= 0 {
251 return;
252 }
253
254 let src_x0 = src_area.x + clip_x;
255 let src_y0 = src_area.y + clip_y;
256
257 for row in 0..h {
258 for col in 0..w {
259 let s = Self::read_pixel(src, src_x0 + col, src_y0 + row);
260 let d = Self::read_pixel(dst, dst_x + col, dst_y + row);
261 let out = Self::blend_pixel(s, d);
262 Self::write_pixel(dst, dst_x + col, dst_y + row, out);
263 }
264 }
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use heapless::Vec;
272
273 #[test]
274 fn fill_argb8888() {
275 let mut buf = [0u8; 64];
276 let mut surf = Surface::new(&mut buf, 16, PixelFmt::Argb8888, 4, 4);
277 let mut blit = CpuBlitter;
278 blit.fill(
279 &mut surf,
280 Rect {
281 x: 0,
282 y: 0,
283 w: 4,
284 h: 4,
285 },
286 0x11223344,
287 );
288 for px in buf.chunks_exact(4) {
289 assert_eq!(u32::from_le_bytes(px.try_into().unwrap()), 0x11223344);
290 }
291 }
292
293 #[test]
294 fn blit_argb8888_to_rgb565() {
295 let mut src_buf = [0u8; 16];
296 let mut dst_buf = [0u8; 32];
297 let src_colors = [0xff0000ffu32, 0xff00ff00, 0xffff0000, 0xffffffff];
298 for (i, chunk) in src_buf.chunks_exact_mut(4).enumerate() {
299 chunk.copy_from_slice(&src_colors[i].to_le_bytes());
300 }
301 let src = Surface::new(&mut src_buf, 8, PixelFmt::Argb8888, 2, 2);
302 let mut dst = Surface::new(&mut dst_buf, 8, PixelFmt::Rgb565, 4, 4);
303 let mut blit = CpuBlitter;
304 blit.blit(
305 &src,
306 Rect {
307 x: 0,
308 y: 0,
309 w: 2,
310 h: 2,
311 },
312 &mut dst,
313 (1, 1),
314 );
315 let expected: Vec<u16, 4> = Vec::from_slice(&[
316 CpuBlitter::argb8888_to_rgb565(0xff0000ff),
317 CpuBlitter::argb8888_to_rgb565(0xff00ff00),
318 CpuBlitter::argb8888_to_rgb565(0xffff0000),
319 CpuBlitter::argb8888_to_rgb565(0xffffffff),
320 ])
321 .unwrap();
322 for (i, row) in (1..3).enumerate() {
323 for (j, col) in (1..3).enumerate() {
324 let idx = row * 8 + col * 2;
325 let val = u16::from_le_bytes([dst_buf[idx], dst_buf[idx + 1]]);
326 assert_eq!(val, expected[i * 2 + j]);
327 }
328 }
329 }
330
331 #[test]
332 fn blend_argb8888() {
333 let mut src_buf = [0u8; 16];
334 let mut dst_buf = [0u8; 16];
335 for chunk in src_buf.chunks_exact_mut(4) {
336 chunk.copy_from_slice(&0x80ff0000u32.to_le_bytes());
337 }
338 for chunk in dst_buf.chunks_exact_mut(4) {
339 chunk.copy_from_slice(&0xff000000u32.to_le_bytes());
340 }
341 let src = Surface::new(&mut src_buf, 8, PixelFmt::Argb8888, 2, 2);
342 let mut dst = Surface::new(&mut dst_buf, 8, PixelFmt::Argb8888, 2, 2);
343 let mut blit = CpuBlitter;
344 blit.blend(
345 &src,
346 Rect {
347 x: 0,
348 y: 0,
349 w: 2,
350 h: 2,
351 },
352 &mut dst,
353 (0, 0),
354 );
355 for chunk in dst_buf.chunks_exact(4) {
356 assert_eq!(u32::from_le_bytes(chunk.try_into().unwrap()), 0xff800000);
357 }
358 }
359}