1use crate::basics::CoverType;
9use crate::color::Rgba8;
10use crate::rendering_buffer::RowAccessor;
11
12pub trait PixelFormat {
22 type ColorType;
23
24 fn width(&self) -> u32;
25 fn height(&self) -> u32;
26
27 fn blend_pixel(&mut self, x: i32, y: i32, c: &Self::ColorType, cover: CoverType);
29
30 fn blend_hline(&mut self, x: i32, y: i32, len: u32, c: &Self::ColorType, cover: CoverType);
32
33 fn blend_solid_hspan(
35 &mut self,
36 x: i32,
37 y: i32,
38 len: u32,
39 c: &Self::ColorType,
40 covers: &[CoverType],
41 );
42
43 fn copy_hline(&mut self, x: i32, y: i32, len: u32, c: &Self::ColorType);
45
46 fn copy_pixel(&mut self, x: i32, y: i32, c: &Self::ColorType);
48
49 fn blend_color_hspan(
54 &mut self,
55 x: i32,
56 y: i32,
57 len: u32,
58 colors: &[Self::ColorType],
59 covers: &[CoverType],
60 cover: CoverType,
61 );
62
63 fn pixel(&self, x: i32, y: i32) -> Self::ColorType;
65}
66
67pub struct PixfmtRgba32<'a> {
79 rbuf: &'a mut RowAccessor,
80}
81
82const BPP: usize = 4; impl<'a> PixfmtRgba32<'a> {
85 pub fn new(rbuf: &'a mut RowAccessor) -> Self {
86 Self { rbuf }
87 }
88
89 pub fn clear(&mut self, c: &Rgba8) {
91 let w = self.rbuf.width();
92 let h = self.rbuf.height();
93 for y in 0..h {
94 let row = unsafe {
95 let ptr = self.rbuf.row_ptr(y as i32);
96 std::slice::from_raw_parts_mut(ptr, (w as usize) * BPP)
97 };
98 for x in 0..w as usize {
99 let off = x * BPP;
100 row[off] = c.r;
101 row[off + 1] = c.g;
102 row[off + 2] = c.b;
103 row[off + 3] = c.a;
104 }
105 }
106 }
107
108 #[inline]
110 fn blend_pix(p: &mut [u8], cr: u8, cg: u8, cb: u8, alpha: u8) {
111 p[0] = Rgba8::lerp(p[0], cr, alpha);
112 p[1] = Rgba8::lerp(p[1], cg, alpha);
113 p[2] = Rgba8::lerp(p[2], cb, alpha);
114 p[3] = Rgba8::lerp(p[3], 255, alpha);
115 }
116
117 pub fn apply_gamma_inv(&mut self, gamma: &crate::gamma::GammaLut) {
123 let w = self.rbuf.width();
124 let h = self.rbuf.height();
125 for y in 0..h {
126 let row = unsafe {
127 let ptr = self.rbuf.row_ptr(y as i32);
128 std::slice::from_raw_parts_mut(ptr, (w as usize) * BPP)
129 };
130 for x in 0..w as usize {
131 let off = x * BPP;
132 row[off] = gamma.inv(row[off]);
133 row[off + 1] = gamma.inv(row[off + 1]);
134 row[off + 2] = gamma.inv(row[off + 2]);
135 }
137 }
138 }
139
140 pub fn apply_gamma_dir(&mut self, gamma: &crate::gamma::GammaLut) {
145 let w = self.rbuf.width();
146 let h = self.rbuf.height();
147 for y in 0..h {
148 let row = unsafe {
149 let ptr = self.rbuf.row_ptr(y as i32);
150 std::slice::from_raw_parts_mut(ptr, (w as usize) * BPP)
151 };
152 for x in 0..w as usize {
153 let off = x * BPP;
154 row[off] = gamma.dir(row[off]);
155 row[off + 1] = gamma.dir(row[off + 1]);
156 row[off + 2] = gamma.dir(row[off + 2]);
157 }
158 }
159 }
160}
161
162impl<'a> PixelFormat for PixfmtRgba32<'a> {
163 type ColorType = Rgba8;
164
165 fn width(&self) -> u32 {
166 self.rbuf.width()
167 }
168
169 fn height(&self) -> u32 {
170 self.rbuf.height()
171 }
172
173 fn pixel(&self, x: i32, y: i32) -> Rgba8 {
174 let row = unsafe {
175 let ptr = self.rbuf.row_ptr(y);
176 std::slice::from_raw_parts(ptr, (self.rbuf.width() as usize) * BPP)
177 };
178 let off = x as usize * BPP;
179 Rgba8::new(
180 row[off] as u32,
181 row[off + 1] as u32,
182 row[off + 2] as u32,
183 row[off + 3] as u32,
184 )
185 }
186
187 fn copy_pixel(&mut self, x: i32, y: i32, c: &Rgba8) {
188 let row = unsafe {
189 let ptr = self.rbuf.row_ptr(y);
190 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
191 };
192 let off = x as usize * BPP;
193 row[off] = c.r;
194 row[off + 1] = c.g;
195 row[off + 2] = c.b;
196 row[off + 3] = c.a;
197 }
198
199 fn copy_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8) {
200 let row = unsafe {
201 let ptr = self.rbuf.row_ptr(y);
202 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
203 };
204 for i in 0..len as usize {
205 let off = (x as usize + i) * BPP;
206 row[off] = c.r;
207 row[off + 1] = c.g;
208 row[off + 2] = c.b;
209 row[off + 3] = c.a;
210 }
211 }
212
213 fn blend_pixel(&mut self, x: i32, y: i32, c: &Rgba8, cover: CoverType) {
214 let row = unsafe {
215 let ptr = self.rbuf.row_ptr(y);
216 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
217 };
218 let off = x as usize * BPP;
219 let alpha = Rgba8::mult_cover(c.a, cover);
220 if alpha == 255 {
221 row[off] = c.r;
222 row[off + 1] = c.g;
223 row[off + 2] = c.b;
224 row[off + 3] = 255;
225 } else {
226 Self::blend_pix(&mut row[off..off + BPP], c.r, c.g, c.b, alpha);
227 }
228 }
229
230 fn blend_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8, cover: CoverType) {
231 let row = unsafe {
232 let ptr = self.rbuf.row_ptr(y);
233 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
234 };
235 let alpha = Rgba8::mult_cover(c.a, cover);
236 if alpha == 255 {
237 for i in 0..len as usize {
238 let off = (x as usize + i) * BPP;
239 row[off] = c.r;
240 row[off + 1] = c.g;
241 row[off + 2] = c.b;
242 row[off + 3] = 255;
243 }
244 } else {
245 for i in 0..len as usize {
246 let off = (x as usize + i) * BPP;
247 Self::blend_pix(&mut row[off..off + BPP], c.r, c.g, c.b, alpha);
248 }
249 }
250 }
251
252 fn blend_solid_hspan(&mut self, x: i32, y: i32, len: u32, c: &Rgba8, covers: &[CoverType]) {
253 let row = unsafe {
254 let ptr = self.rbuf.row_ptr(y);
255 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
256 };
257 for (i, &cov) in covers.iter().enumerate().take(len as usize) {
258 let off = (x as usize + i) * BPP;
259 let alpha = Rgba8::mult_cover(c.a, cov);
260 if alpha == 255 {
261 row[off] = c.r;
262 row[off + 1] = c.g;
263 row[off + 2] = c.b;
264 row[off + 3] = 255;
265 } else if alpha > 0 {
266 Self::blend_pix(&mut row[off..off + BPP], c.r, c.g, c.b, alpha);
267 }
268 }
269 }
270
271 fn blend_color_hspan(
272 &mut self,
273 x: i32,
274 y: i32,
275 len: u32,
276 colors: &[Rgba8],
277 covers: &[CoverType],
278 cover: CoverType,
279 ) {
280 let row = unsafe {
281 let ptr = self.rbuf.row_ptr(y);
282 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
283 };
284 if !covers.is_empty() {
285 for i in 0..len as usize {
287 let off = (x as usize + i) * BPP;
288 let c = &colors[i];
289 let alpha = Rgba8::mult_cover(c.a, covers[i]);
290 if alpha == 255 {
291 row[off] = c.r;
292 row[off + 1] = c.g;
293 row[off + 2] = c.b;
294 row[off + 3] = 255;
295 } else if alpha > 0 {
296 Self::blend_pix(&mut row[off..off + BPP], c.r, c.g, c.b, alpha);
297 }
298 }
299 } else if cover == 255 {
300 for (i, c) in colors.iter().enumerate().take(len as usize) {
302 let off = (x as usize + i) * BPP;
303 if c.a == 255 {
304 row[off] = c.r;
305 row[off + 1] = c.g;
306 row[off + 2] = c.b;
307 row[off + 3] = 255;
308 } else if c.a > 0 {
309 Self::blend_pix(&mut row[off..off + BPP], c.r, c.g, c.b, c.a);
310 }
311 }
312 } else {
313 for (i, c) in colors.iter().enumerate().take(len as usize) {
315 let off = (x as usize + i) * BPP;
316 let alpha = Rgba8::mult_cover(c.a, cover);
317 if alpha == 255 {
318 row[off] = c.r;
319 row[off + 1] = c.g;
320 row[off + 2] = c.b;
321 row[off + 3] = 255;
322 } else if alpha > 0 {
323 Self::blend_pix(&mut row[off..off + BPP], c.r, c.g, c.b, alpha);
324 }
325 }
326 }
327 }
328}
329
330#[cfg(test)]
335mod tests {
336 use super::*;
337
338 fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
339 let stride = (w * BPP as u32) as i32;
340 let buf = vec![0u8; (h * w * BPP as u32) as usize];
341 let mut ra = RowAccessor::new();
342 unsafe {
343 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
344 }
345 (buf, ra)
346 }
347
348 #[test]
349 fn test_new() {
350 let (_buf, mut ra) = make_buffer(100, 100);
351 let pf = PixfmtRgba32::new(&mut ra);
352 assert_eq!(pf.width(), 100);
353 assert_eq!(pf.height(), 100);
354 }
355
356 #[test]
357 fn test_copy_pixel() {
358 let (_buf, mut ra) = make_buffer(10, 10);
359 let mut pf = PixfmtRgba32::new(&mut ra);
360 let red = Rgba8::new(255, 0, 0, 255);
361 pf.copy_pixel(5, 5, &red);
362 let p = pf.pixel(5, 5);
363 assert_eq!(p.r, 255);
364 assert_eq!(p.g, 0);
365 assert_eq!(p.b, 0);
366 assert_eq!(p.a, 255);
367 }
368
369 #[test]
370 fn test_copy_hline() {
371 let (_buf, mut ra) = make_buffer(20, 10);
372 let mut pf = PixfmtRgba32::new(&mut ra);
373 let green = Rgba8::new(0, 255, 0, 255);
374 pf.copy_hline(5, 3, 10, &green);
375 for x in 5..15 {
376 let p = pf.pixel(x, 3);
377 assert_eq!(p.g, 255);
378 }
379 let p = pf.pixel(4, 3);
381 assert_eq!(p.g, 0);
382 }
383
384 #[test]
385 fn test_blend_pixel_opaque() {
386 let (_buf, mut ra) = make_buffer(10, 10);
387 let mut pf = PixfmtRgba32::new(&mut ra);
388 let blue = Rgba8::new(0, 0, 255, 255);
389 pf.blend_pixel(3, 3, &blue, 255);
390 let p = pf.pixel(3, 3);
391 assert_eq!(p.b, 255);
392 assert_eq!(p.a, 255);
393 }
394
395 #[test]
396 fn test_blend_pixel_semi_transparent() {
397 let (_buf, mut ra) = make_buffer(10, 10);
398 let mut pf = PixfmtRgba32::new(&mut ra);
399 let white = Rgba8::new(255, 255, 255, 255);
401 pf.copy_pixel(3, 3, &white);
402 let red = Rgba8::new(255, 0, 0, 255);
404 pf.blend_pixel(3, 3, &red, 128);
405 let p = pf.pixel(3, 3);
406 assert!(p.r > 128);
408 assert!(p.g < 200);
409 assert!(p.b < 200);
410 }
411
412 #[test]
413 fn test_blend_hline() {
414 let (_buf, mut ra) = make_buffer(20, 10);
415 let mut pf = PixfmtRgba32::new(&mut ra);
416 let red = Rgba8::new(255, 0, 0, 255);
417 pf.blend_hline(5, 3, 5, &red, 255);
418 for x in 5..10 {
419 let p = pf.pixel(x, 3);
420 assert_eq!(p.r, 255);
421 }
422 }
423
424 #[test]
425 fn test_blend_solid_hspan() {
426 let (_buf, mut ra) = make_buffer(20, 10);
427 let mut pf = PixfmtRgba32::new(&mut ra);
428 let green = Rgba8::new(0, 255, 0, 255);
429 let covers = [255u8, 128, 64, 0, 255];
430 pf.blend_solid_hspan(5, 3, 5, &green, &covers);
431 let p0 = pf.pixel(5, 3);
433 assert_eq!(p0.g, 255);
434 let p3 = pf.pixel(8, 3);
436 assert_eq!(p3.g, 0);
437 }
438
439 #[test]
440 fn test_clear() {
441 let (_buf, mut ra) = make_buffer(10, 10);
442 let mut pf = PixfmtRgba32::new(&mut ra);
443 let white = Rgba8::new(255, 255, 255, 255);
444 pf.clear(&white);
445 for y in 0..10 {
446 for x in 0..10 {
447 let p = pf.pixel(x, y);
448 assert_eq!(p.r, 255);
449 assert_eq!(p.g, 255);
450 assert_eq!(p.b, 255);
451 assert_eq!(p.a, 255);
452 }
453 }
454 }
455
456 #[test]
457 fn test_blend_on_black_background() {
458 let (_buf, mut ra) = make_buffer(10, 10);
459 let mut pf = PixfmtRgba32::new(&mut ra);
460 let white = Rgba8::new(255, 255, 255, 255);
462 pf.blend_pixel(0, 0, &white, 128);
463 let p = pf.pixel(0, 0);
464 assert!((p.r as i32 - 128).abs() <= 2);
466 }
467
468 #[test]
469 fn test_blend_preserves_adjacent_pixels() {
470 let (_buf, mut ra) = make_buffer(10, 10);
471 let mut pf = PixfmtRgba32::new(&mut ra);
472 let red = Rgba8::new(255, 0, 0, 255);
473 pf.blend_pixel(5, 5, &red, 255);
474 let p = pf.pixel(4, 5);
476 assert_eq!(p.r, 0);
477 assert_eq!(p.g, 0);
478 assert_eq!(p.b, 0);
479 assert_eq!(p.a, 0);
480 }
481
482 #[test]
483 fn test_pixel_read_write_roundtrip() {
484 let (_buf, mut ra) = make_buffer(10, 10);
485 let mut pf = PixfmtRgba32::new(&mut ra);
486 let c = Rgba8::new(42, 128, 200, 180);
487 pf.copy_pixel(7, 3, &c);
488 let p = pf.pixel(7, 3);
489 assert_eq!(p.r, 42);
490 assert_eq!(p.g, 128);
491 assert_eq!(p.b, 200);
492 assert_eq!(p.a, 180);
493 }
494
495 #[test]
496 fn test_blend_color_hspan_per_pixel_covers() {
497 let (_buf, mut ra) = make_buffer(20, 10);
498 let mut pf = PixfmtRgba32::new(&mut ra);
499 let colors = [
500 Rgba8::new(255, 0, 0, 255),
501 Rgba8::new(0, 255, 0, 255),
502 Rgba8::new(0, 0, 255, 255),
503 ];
504 let covers = [255u8, 128, 0];
505 pf.blend_color_hspan(5, 3, 3, &colors, &covers, 0);
506 let p0 = pf.pixel(5, 3);
508 assert_eq!(p0.r, 255);
509 assert_eq!(p0.g, 0);
510 let p1 = pf.pixel(6, 3);
512 assert!(p1.g > 64);
513 let p2 = pf.pixel(7, 3);
515 assert_eq!(p2.b, 0);
516 }
517
518 #[test]
519 fn test_blend_color_hspan_uniform_cover() {
520 let (_buf, mut ra) = make_buffer(20, 10);
521 let mut pf = PixfmtRgba32::new(&mut ra);
522 let colors = [Rgba8::new(255, 0, 0, 255), Rgba8::new(0, 255, 0, 255)];
523 pf.blend_color_hspan(3, 3, 2, &colors, &[], 255);
525 let p0 = pf.pixel(3, 3);
526 assert_eq!(p0.r, 255);
527 let p1 = pf.pixel(4, 3);
528 assert_eq!(p1.g, 255);
529 }
530
531 #[test]
532 fn test_blend_color_hspan_full_cover_opaque() {
533 let (_buf, mut ra) = make_buffer(10, 10);
534 let mut pf = PixfmtRgba32::new(&mut ra);
535 let white = Rgba8::new(255, 255, 255, 255);
536 pf.clear(&white);
537 let colors = [Rgba8::new(100, 150, 200, 255)];
538 pf.blend_color_hspan(0, 0, 1, &colors, &[], 255);
539 let p = pf.pixel(0, 0);
540 assert_eq!(p.r, 100);
541 assert_eq!(p.g, 150);
542 assert_eq!(p.b, 200);
543 }
544}