1use crate::basics::CoverType;
9use crate::color::Rgba8;
10use crate::pixfmt_rgba::PixelFormat;
11use crate::rendering_buffer::RowAccessor;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[repr(u8)]
23pub enum CompOp {
24 Clear = 0,
25 Src = 1,
26 Dst = 2,
27 SrcOver = 3,
28 DstOver = 4,
29 SrcIn = 5,
30 DstIn = 6,
31 SrcOut = 7,
32 DstOut = 8,
33 SrcAtop = 9,
34 DstAtop = 10,
35 Xor = 11,
36 Plus = 12,
37 Minus = 13,
38 Multiply = 14,
39 Screen = 15,
40 Overlay = 16,
41 Darken = 17,
42 Lighten = 18,
43 ColorDodge = 19,
44 ColorBurn = 20,
45 HardLight = 21,
46 SoftLight = 22,
47 Difference = 23,
48 Exclusion = 24,
49}
50
51impl Default for CompOp {
52 fn default() -> Self {
53 CompOp::SrcOver
54 }
55}
56
57#[derive(Debug, Clone, Copy)]
63struct PremulRgba {
64 r: f64,
65 g: f64,
66 b: f64,
67 a: f64,
68}
69
70impl PremulRgba {
71 #[inline]
73 fn get(r: u8, g: u8, b: u8, a: u8, cover: u8) -> Self {
74 if cover == 0 {
75 return Self {
76 r: 0.0,
77 g: 0.0,
78 b: 0.0,
79 a: 0.0,
80 };
81 }
82 let mut c = Self {
83 r: Rgba8::to_double(r),
84 g: Rgba8::to_double(g),
85 b: Rgba8::to_double(b),
86 a: Rgba8::to_double(a),
87 };
88 if cover < 255 {
89 let x = cover as f64 / 255.0;
90 c.r *= x;
91 c.g *= x;
92 c.b *= x;
93 c.a *= x;
94 }
95 c
96 }
97
98 #[inline]
100 fn get_pix(p: &[u8], cover: u8) -> Self {
101 Self::get(p[0], p[1], p[2], p[3], cover)
102 }
103
104 #[inline]
106 fn set(p: &mut [u8], c: &PremulRgba) {
107 p[0] = Rgba8::from_double(c.r);
108 p[1] = Rgba8::from_double(c.g);
109 p[2] = Rgba8::from_double(c.b);
110 p[3] = Rgba8::from_double(c.a);
111 }
112
113 #[inline]
115 fn set_rgba(p: &mut [u8], r: u8, g: u8, b: u8, a: u8) {
116 p[0] = r;
117 p[1] = g;
118 p[2] = b;
119 p[3] = a;
120 }
121
122 #[inline]
124 fn clip(c: &mut PremulRgba) {
125 c.r = c.r.clamp(0.0, 1.0);
126 c.g = c.g.clamp(0.0, 1.0);
127 c.b = c.b.clamp(0.0, 1.0);
128 c.a = c.a.clamp(0.0, 1.0);
129 }
130}
131
132#[inline]
141fn comp_op_blend(op: CompOp, p: &mut [u8], sr: u8, sg: u8, sb: u8, sa: u8, cover: u8) {
142 let r = Rgba8::multiply(sr, sa);
144 let g = Rgba8::multiply(sg, sa);
145 let b = Rgba8::multiply(sb, sa);
146 let a = sa;
147
148 match op {
149 CompOp::Clear => blend_clear(p, cover),
150 CompOp::Src => blend_src(p, r, g, b, a, cover),
151 CompOp::Dst => {} CompOp::SrcOver => blend_src_over(p, r, g, b, a, cover),
153 CompOp::DstOver => blend_dst_over(p, r, g, b, a, cover),
154 CompOp::SrcIn => blend_src_in(p, r, g, b, a, cover),
155 CompOp::DstIn => blend_dst_in(p, a, cover),
156 CompOp::SrcOut => blend_src_out(p, r, g, b, a, cover),
157 CompOp::DstOut => blend_dst_out(p, a, cover),
158 CompOp::SrcAtop => blend_src_atop(p, r, g, b, a, cover),
159 CompOp::DstAtop => blend_dst_atop(p, r, g, b, a, cover),
160 CompOp::Xor => blend_xor(p, r, g, b, a, cover),
161 CompOp::Plus => blend_plus(p, r, g, b, a, cover),
162 CompOp::Minus => blend_minus(p, r, g, b, a, cover),
163 CompOp::Multiply => blend_multiply(p, r, g, b, a, cover),
164 CompOp::Screen => blend_screen(p, r, g, b, a, cover),
165 CompOp::Overlay => blend_overlay(p, r, g, b, a, cover),
166 CompOp::Darken => blend_darken(p, r, g, b, a, cover),
167 CompOp::Lighten => blend_lighten(p, r, g, b, a, cover),
168 CompOp::ColorDodge => blend_color_dodge(p, r, g, b, a, cover),
169 CompOp::ColorBurn => blend_color_burn(p, r, g, b, a, cover),
170 CompOp::HardLight => blend_hard_light(p, r, g, b, a, cover),
171 CompOp::SoftLight => blend_soft_light(p, r, g, b, a, cover),
172 CompOp::Difference => blend_difference(p, r, g, b, a, cover),
173 CompOp::Exclusion => blend_exclusion(p, r, g, b, a, cover),
174 }
175}
176
177#[inline]
179fn blend_clear(p: &mut [u8], cover: u8) {
180 if cover >= 255 {
181 p[0] = 0;
182 p[1] = 0;
183 p[2] = 0;
184 p[3] = 0;
185 } else if cover > 0 {
186 let d = PremulRgba::get_pix(p, 255 - cover);
187 PremulRgba::set(p, &d);
188 }
189}
190
191#[inline]
193fn blend_src(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
194 if cover >= 255 {
195 PremulRgba::set_rgba(p, r, g, b, a);
196 } else {
197 let s = PremulRgba::get(r, g, b, a, cover);
198 let d = PremulRgba::get_pix(p, 255 - cover);
199 let out = PremulRgba {
200 r: d.r + s.r,
201 g: d.g + s.g,
202 b: d.b + s.b,
203 a: d.a + s.a,
204 };
205 PremulRgba::set(p, &out);
206 }
207}
208
209#[inline]
211fn blend_src_over(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
212 let s = PremulRgba::get(r, g, b, a, cover);
213 let d = PremulRgba::get_pix(p, 255);
214 let out = PremulRgba {
215 r: d.r + s.r - d.r * s.a,
216 g: d.g + s.g - d.g * s.a,
217 b: d.b + s.b - d.b * s.a,
218 a: d.a + s.a - d.a * s.a,
219 };
220 PremulRgba::set(p, &out);
221}
222
223#[inline]
225fn blend_dst_over(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
226 let s = PremulRgba::get(r, g, b, a, cover);
227 let mut d = PremulRgba::get_pix(p, 255);
228 let d1a = 1.0 - d.a;
229 d.r += s.r * d1a;
230 d.g += s.g * d1a;
231 d.b += s.b * d1a;
232 d.a += s.a * d1a;
233 PremulRgba::set(p, &d);
234}
235
236#[inline]
238fn blend_src_in(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
239 let da = Rgba8::to_double(p[3]);
240 if da > 0.0 {
241 let s = PremulRgba::get(r, g, b, a, cover);
242 let mut d = PremulRgba::get_pix(p, 255 - cover);
243 d.r += s.r * da;
244 d.g += s.g * da;
245 d.b += s.b * da;
246 d.a += s.a * da;
247 PremulRgba::set(p, &d);
248 }
249}
250
251#[inline]
253fn blend_dst_in(p: &mut [u8], a: u8, cover: u8) {
254 let sa = Rgba8::to_double(a);
255 let mut d = PremulRgba::get_pix(p, 255 - cover);
256 let d2 = PremulRgba::get_pix(p, cover);
257 d.r += d2.r * sa;
258 d.g += d2.g * sa;
259 d.b += d2.b * sa;
260 d.a += d2.a * sa;
261 PremulRgba::set(p, &d);
262}
263
264#[inline]
266fn blend_src_out(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
267 let s = PremulRgba::get(r, g, b, a, cover);
268 let mut d = PremulRgba::get_pix(p, 255 - cover);
269 let d1a = 1.0 - Rgba8::to_double(p[3]);
270 d.r += s.r * d1a;
271 d.g += s.g * d1a;
272 d.b += s.b * d1a;
273 d.a += s.a * d1a;
274 PremulRgba::set(p, &d);
275}
276
277#[inline]
279fn blend_dst_out(p: &mut [u8], a: u8, cover: u8) {
280 let mut d = PremulRgba::get_pix(p, 255 - cover);
281 let dc = PremulRgba::get_pix(p, cover);
282 let s1a = 1.0 - Rgba8::to_double(a);
283 d.r += dc.r * s1a;
284 d.g += dc.g * s1a;
285 d.b += dc.b * s1a;
286 d.a += dc.a * s1a;
287 PremulRgba::set(p, &d);
288}
289
290#[inline]
292fn blend_src_atop(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
293 let s = PremulRgba::get(r, g, b, a, cover);
294 let mut d = PremulRgba::get_pix(p, 255);
295 let s1a = 1.0 - s.a;
296 d.r = s.r * d.a + d.r * s1a;
297 d.g = s.g * d.a + d.g * s1a;
298 d.b = s.b * d.a + d.b * s1a;
299 PremulRgba::set(p, &d);
301}
302
303#[inline]
305fn blend_dst_atop(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
306 let sc = PremulRgba::get(r, g, b, a, cover);
307 let dc = PremulRgba::get_pix(p, cover);
308 let mut d = PremulRgba::get_pix(p, 255 - cover);
309 let sa = Rgba8::to_double(a);
310 let d1a = 1.0 - Rgba8::to_double(p[3]);
311 d.r += dc.r * sa + sc.r * d1a;
312 d.g += dc.g * sa + sc.g * d1a;
313 d.b += dc.b * sa + sc.b * d1a;
314 d.a += sc.a;
315 PremulRgba::set(p, &d);
316}
317
318#[inline]
320fn blend_xor(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
321 let s = PremulRgba::get(r, g, b, a, cover);
322 let mut d = PremulRgba::get_pix(p, 255);
323 let s1a = 1.0 - s.a;
324 let d1a = 1.0 - Rgba8::to_double(p[3]);
325 d.r = s.r * d1a + d.r * s1a;
326 d.g = s.g * d1a + d.g * s1a;
327 d.b = s.b * d1a + d.b * s1a;
328 d.a = s.a + d.a - 2.0 * s.a * d.a;
329 PremulRgba::set(p, &d);
330}
331
332#[inline]
334fn blend_plus(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
335 let s = PremulRgba::get(r, g, b, a, cover);
336 if s.a > 0.0 {
337 let mut d = PremulRgba::get_pix(p, 255);
338 d.a = (d.a + s.a).min(1.0);
339 d.r = (d.r + s.r).min(d.a);
340 d.g = (d.g + s.g).min(d.a);
341 d.b = (d.b + s.b).min(d.a);
342 PremulRgba::clip(&mut d);
343 PremulRgba::set(p, &d);
344 }
345}
346
347#[inline]
349fn blend_minus(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
350 let s = PremulRgba::get(r, g, b, a, cover);
351 if s.a > 0.0 {
352 let mut d = PremulRgba::get_pix(p, 255);
353 d.a += s.a - s.a * d.a;
354 d.r = (d.r - s.r).max(0.0);
355 d.g = (d.g - s.g).max(0.0);
356 d.b = (d.b - s.b).max(0.0);
357 PremulRgba::clip(&mut d);
358 PremulRgba::set(p, &d);
359 }
360}
361
362#[inline]
364fn blend_multiply(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
365 let s = PremulRgba::get(r, g, b, a, cover);
366 if s.a > 0.0 {
367 let mut d = PremulRgba::get_pix(p, 255);
368 let s1a = 1.0 - s.a;
369 let d1a = 1.0 - d.a;
370 d.r = s.r * d.r + s.r * d1a + d.r * s1a;
371 d.g = s.g * d.g + s.g * d1a + d.g * s1a;
372 d.b = s.b * d.b + s.b * d1a + d.b * s1a;
373 d.a += s.a - s.a * d.a;
374 PremulRgba::clip(&mut d);
375 PremulRgba::set(p, &d);
376 }
377}
378
379#[inline]
381fn blend_screen(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
382 let s = PremulRgba::get(r, g, b, a, cover);
383 if s.a > 0.0 {
384 let mut d = PremulRgba::get_pix(p, 255);
385 d.r += s.r - s.r * d.r;
386 d.g += s.g - s.g * d.g;
387 d.b += s.b - s.b * d.b;
388 d.a += s.a - s.a * d.a;
389 PremulRgba::clip(&mut d);
390 PremulRgba::set(p, &d);
391 }
392}
393
394#[inline]
396fn overlay_calc(dca: f64, sca: f64, da: f64, sa: f64, sada: f64, d1a: f64, s1a: f64) -> f64 {
397 if 2.0 * dca <= da {
398 2.0 * sca * dca + sca * d1a + dca * s1a
399 } else {
400 sada - 2.0 * (da - dca) * (sa - sca) + sca * d1a + dca * s1a
401 }
402}
403
404#[inline]
405fn blend_overlay(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
406 let s = PremulRgba::get(r, g, b, a, cover);
407 if s.a > 0.0 {
408 let mut d = PremulRgba::get_pix(p, 255);
409 let d1a = 1.0 - d.a;
410 let s1a = 1.0 - s.a;
411 let sada = s.a * d.a;
412 d.r = overlay_calc(d.r, s.r, d.a, s.a, sada, d1a, s1a);
413 d.g = overlay_calc(d.g, s.g, d.a, s.a, sada, d1a, s1a);
414 d.b = overlay_calc(d.b, s.b, d.a, s.a, sada, d1a, s1a);
415 d.a += s.a - s.a * d.a;
416 PremulRgba::clip(&mut d);
417 PremulRgba::set(p, &d);
418 }
419}
420
421#[inline]
423fn blend_darken(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
424 let s = PremulRgba::get(r, g, b, a, cover);
425 if s.a > 0.0 {
426 let mut d = PremulRgba::get_pix(p, 255);
427 let d1a = 1.0 - d.a;
428 let s1a = 1.0 - s.a;
429 d.r = (s.r * d.a).min(d.r * s.a) + s.r * d1a + d.r * s1a;
430 d.g = (s.g * d.a).min(d.g * s.a) + s.g * d1a + d.g * s1a;
431 d.b = (s.b * d.a).min(d.b * s.a) + s.b * d1a + d.b * s1a;
432 d.a += s.a - s.a * d.a;
433 PremulRgba::clip(&mut d);
434 PremulRgba::set(p, &d);
435 }
436}
437
438#[inline]
440fn blend_lighten(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
441 let s = PremulRgba::get(r, g, b, a, cover);
442 if s.a > 0.0 {
443 let mut d = PremulRgba::get_pix(p, 255);
444 let d1a = 1.0 - d.a;
445 let s1a = 1.0 - s.a;
446 d.r = (s.r * d.a).max(d.r * s.a) + s.r * d1a + d.r * s1a;
447 d.g = (s.g * d.a).max(d.g * s.a) + s.g * d1a + d.g * s1a;
448 d.b = (s.b * d.a).max(d.b * s.a) + s.b * d1a + d.b * s1a;
449 d.a += s.a - s.a * d.a;
450 PremulRgba::clip(&mut d);
451 PremulRgba::set(p, &d);
452 }
453}
454
455#[inline]
457fn color_dodge_calc(dca: f64, sca: f64, da: f64, sa: f64, sada: f64, d1a: f64, s1a: f64) -> f64 {
458 if sca < sa {
459 sada * (1.0f64).min((dca / da) * sa / (sa - sca)) + sca * d1a + dca * s1a
460 } else if dca > 0.0 {
461 sada + sca * d1a + dca * s1a
462 } else {
463 sca * d1a
464 }
465}
466
467#[inline]
468fn blend_color_dodge(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
469 let s = PremulRgba::get(r, g, b, a, cover);
470 if s.a > 0.0 {
471 let mut d = PremulRgba::get_pix(p, 255);
472 if d.a > 0.0 {
473 let sada = s.a * d.a;
474 let s1a = 1.0 - s.a;
475 let d1a = 1.0 - d.a;
476 d.r = color_dodge_calc(d.r, s.r, d.a, s.a, sada, d1a, s1a);
477 d.g = color_dodge_calc(d.g, s.g, d.a, s.a, sada, d1a, s1a);
478 d.b = color_dodge_calc(d.b, s.b, d.a, s.a, sada, d1a, s1a);
479 d.a += s.a - s.a * d.a;
480 PremulRgba::clip(&mut d);
481 PremulRgba::set(p, &d);
482 } else {
483 PremulRgba::set(p, &s);
484 }
485 }
486}
487
488#[inline]
490fn color_burn_calc(dca: f64, sca: f64, da: f64, sa: f64, sada: f64, d1a: f64, s1a: f64) -> f64 {
491 if sca > 0.0 {
492 sada * (1.0 - (1.0f64).min((1.0 - dca / da) * sa / sca)) + sca * d1a + dca * s1a
493 } else if dca > da {
494 sada + dca * s1a
495 } else {
496 dca * s1a
497 }
498}
499
500#[inline]
501fn blend_color_burn(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
502 let s = PremulRgba::get(r, g, b, a, cover);
503 if s.a > 0.0 {
504 let mut d = PremulRgba::get_pix(p, 255);
505 if d.a > 0.0 {
506 let sada = s.a * d.a;
507 let s1a = 1.0 - s.a;
508 let d1a = 1.0 - d.a;
509 d.r = color_burn_calc(d.r, s.r, d.a, s.a, sada, d1a, s1a);
510 d.g = color_burn_calc(d.g, s.g, d.a, s.a, sada, d1a, s1a);
511 d.b = color_burn_calc(d.b, s.b, d.a, s.a, sada, d1a, s1a);
512 d.a += s.a - sada;
513 PremulRgba::clip(&mut d);
514 PremulRgba::set(p, &d);
515 } else {
516 PremulRgba::set(p, &s);
517 }
518 }
519}
520
521#[inline]
523fn hard_light_calc(dca: f64, sca: f64, da: f64, sa: f64, sada: f64, d1a: f64, s1a: f64) -> f64 {
524 if 2.0 * sca < sa {
525 2.0 * sca * dca + sca * d1a + dca * s1a
526 } else {
527 sada - 2.0 * (da - dca) * (sa - sca) + sca * d1a + dca * s1a
528 }
529}
530
531#[inline]
532fn blend_hard_light(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
533 let s = PremulRgba::get(r, g, b, a, cover);
534 if s.a > 0.0 {
535 let mut d = PremulRgba::get_pix(p, 255);
536 let d1a = 1.0 - d.a;
537 let s1a = 1.0 - s.a;
538 let sada = s.a * d.a;
539 d.r = hard_light_calc(d.r, s.r, d.a, s.a, sada, d1a, s1a);
540 d.g = hard_light_calc(d.g, s.g, d.a, s.a, sada, d1a, s1a);
541 d.b = hard_light_calc(d.b, s.b, d.a, s.a, sada, d1a, s1a);
542 d.a += s.a - sada;
543 PremulRgba::clip(&mut d);
544 PremulRgba::set(p, &d);
545 }
546}
547
548#[inline]
550fn soft_light_calc(dca: f64, sca: f64, da: f64, sa: f64, sada: f64, d1a: f64, s1a: f64) -> f64 {
551 let dcasa = dca * sa;
552 if 2.0 * sca <= sa {
553 dcasa - (sada - 2.0 * sca * da) * dcasa * (sada - dcasa) + sca * d1a + dca * s1a
554 } else if 4.0 * dca <= da {
555 dcasa
556 + (2.0 * sca * da - sada)
557 * ((((16.0 * dcasa - 12.0) * dcasa + 4.0) * dca * da) - dca * da)
558 + sca * d1a
559 + dca * s1a
560 } else {
561 dcasa + (2.0 * sca * da - sada) * (dcasa.sqrt() - dcasa) + sca * d1a + dca * s1a
562 }
563}
564
565#[inline]
566fn blend_soft_light(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
567 let s = PremulRgba::get(r, g, b, a, cover);
568 if s.a > 0.0 {
569 let mut d = PremulRgba::get_pix(p, 255);
570 if d.a > 0.0 {
571 let sada = s.a * d.a;
572 let s1a = 1.0 - s.a;
573 let d1a = 1.0 - d.a;
574 d.r = soft_light_calc(d.r, s.r, d.a, s.a, sada, d1a, s1a);
575 d.g = soft_light_calc(d.g, s.g, d.a, s.a, sada, d1a, s1a);
576 d.b = soft_light_calc(d.b, s.b, d.a, s.a, sada, d1a, s1a);
577 d.a += s.a - sada;
578 PremulRgba::clip(&mut d);
579 PremulRgba::set(p, &d);
580 } else {
581 PremulRgba::set(p, &s);
582 }
583 }
584}
585
586#[inline]
588fn blend_difference(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
589 let s = PremulRgba::get(r, g, b, a, cover);
590 if s.a > 0.0 {
591 let mut d = PremulRgba::get_pix(p, 255);
592 d.r += s.r - 2.0 * (s.r * d.a).min(d.r * s.a);
593 d.g += s.g - 2.0 * (s.g * d.a).min(d.g * s.a);
594 d.b += s.b - 2.0 * (s.b * d.a).min(d.b * s.a);
595 d.a += s.a - s.a * d.a;
596 PremulRgba::clip(&mut d);
597 PremulRgba::set(p, &d);
598 }
599}
600
601#[inline]
603fn blend_exclusion(p: &mut [u8], r: u8, g: u8, b: u8, a: u8, cover: u8) {
604 let s = PremulRgba::get(r, g, b, a, cover);
605 if s.a > 0.0 {
606 let mut d = PremulRgba::get_pix(p, 255);
607 let d1a = 1.0 - d.a;
608 let s1a = 1.0 - s.a;
609 d.r = (s.r * d.a + d.r * s.a - 2.0 * s.r * d.r) + s.r * d1a + d.r * s1a;
610 d.g = (s.g * d.a + d.g * s.a - 2.0 * s.g * d.g) + s.g * d1a + d.g * s1a;
611 d.b = (s.b * d.a + d.b * s.a - 2.0 * s.b * d.b) + s.b * d1a + d.b * s1a;
612 d.a += s.a - s.a * d.a;
613 PremulRgba::clip(&mut d);
614 PremulRgba::set(p, &d);
615 }
616}
617
618const BPP: usize = 4;
623
624pub struct PixfmtRgba32CompOp<'a> {
630 rbuf: &'a mut RowAccessor,
631 comp_op: CompOp,
632}
633
634impl<'a> PixfmtRgba32CompOp<'a> {
635 pub fn new(rbuf: &'a mut RowAccessor) -> Self {
636 Self {
637 rbuf,
638 comp_op: CompOp::SrcOver,
639 }
640 }
641
642 pub fn new_with_op(rbuf: &'a mut RowAccessor, op: CompOp) -> Self {
643 Self { rbuf, comp_op: op }
644 }
645
646 pub fn comp_op(&self) -> CompOp {
647 self.comp_op
648 }
649
650 pub fn set_comp_op(&mut self, op: CompOp) {
651 self.comp_op = op;
652 }
653
654 pub fn clear(&mut self, c: &Rgba8) {
656 let w = self.rbuf.width();
657 let h = self.rbuf.height();
658 for y in 0..h {
659 let row = unsafe {
660 let ptr = self.rbuf.row_ptr(y as i32);
661 std::slice::from_raw_parts_mut(ptr, (w as usize) * BPP)
662 };
663 for x in 0..w as usize {
664 let off = x * BPP;
665 row[off] = c.r;
666 row[off + 1] = c.g;
667 row[off + 2] = c.b;
668 row[off + 3] = c.a;
669 }
670 }
671 }
672}
673
674impl<'a> PixelFormat for PixfmtRgba32CompOp<'a> {
675 type ColorType = Rgba8;
676
677 fn width(&self) -> u32 {
678 self.rbuf.width()
679 }
680
681 fn height(&self) -> u32 {
682 self.rbuf.height()
683 }
684
685 fn pixel(&self, x: i32, y: i32) -> Rgba8 {
686 let row = unsafe {
687 let ptr = self.rbuf.row_ptr(y);
688 std::slice::from_raw_parts(ptr, (self.rbuf.width() as usize) * BPP)
689 };
690 let off = x as usize * BPP;
691 Rgba8::new(
692 row[off] as u32,
693 row[off + 1] as u32,
694 row[off + 2] as u32,
695 row[off + 3] as u32,
696 )
697 }
698
699 fn copy_pixel(&mut self, x: i32, y: i32, c: &Rgba8) {
700 let row = unsafe {
701 let ptr = self.rbuf.row_ptr(y);
702 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
703 };
704 let off = x as usize * BPP;
705 row[off] = c.r;
706 row[off + 1] = c.g;
707 row[off + 2] = c.b;
708 row[off + 3] = c.a;
709 }
710
711 fn copy_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8) {
712 let row = unsafe {
713 let ptr = self.rbuf.row_ptr(y);
714 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
715 };
716 for i in 0..len as usize {
717 let off = (x as usize + i) * BPP;
718 row[off] = c.r;
719 row[off + 1] = c.g;
720 row[off + 2] = c.b;
721 row[off + 3] = c.a;
722 }
723 }
724
725 fn blend_pixel(&mut self, x: i32, y: i32, c: &Rgba8, cover: CoverType) {
726 let row = unsafe {
727 let ptr = self.rbuf.row_ptr(y);
728 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
729 };
730 let off = x as usize * BPP;
731 comp_op_blend(self.comp_op, &mut row[off..off + BPP], c.r, c.g, c.b, c.a, cover);
732 }
733
734 fn blend_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8, cover: CoverType) {
735 let row = unsafe {
736 let ptr = self.rbuf.row_ptr(y);
737 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
738 };
739 for i in 0..len as usize {
740 let off = (x as usize + i) * BPP;
741 comp_op_blend(self.comp_op, &mut row[off..off + BPP], c.r, c.g, c.b, c.a, cover);
742 }
743 }
744
745 fn blend_solid_hspan(&mut self, x: i32, y: i32, len: u32, c: &Rgba8, covers: &[CoverType]) {
746 let row = unsafe {
747 let ptr = self.rbuf.row_ptr(y);
748 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
749 };
750 for (i, &cov) in covers.iter().enumerate().take(len as usize) {
751 let off = (x as usize + i) * BPP;
752 comp_op_blend(self.comp_op, &mut row[off..off + BPP], c.r, c.g, c.b, c.a, cov);
753 }
754 }
755
756 fn blend_color_hspan(
757 &mut self,
758 x: i32,
759 y: i32,
760 len: u32,
761 colors: &[Rgba8],
762 covers: &[CoverType],
763 cover: CoverType,
764 ) {
765 let row = unsafe {
766 let ptr = self.rbuf.row_ptr(y);
767 std::slice::from_raw_parts_mut(ptr, (self.rbuf.width() as usize) * BPP)
768 };
769 for i in 0..len as usize {
770 let c = &colors[i];
771 let cov = if !covers.is_empty() { covers[i] } else { cover };
772 let off = (x as usize + i) * BPP;
773 comp_op_blend(self.comp_op, &mut row[off..off + BPP], c.r, c.g, c.b, c.a, cov);
774 }
775 }
776}
777
778#[cfg(test)]
783mod tests {
784 use super::*;
785 use crate::rendering_buffer::RowAccessor;
786
787 fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
788 let stride = (w * 4) as i32;
789 let buf = vec![0u8; (h * w * 4) as usize];
790 let mut ra = RowAccessor::new();
791 unsafe {
792 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
793 }
794 (buf, ra)
795 }
796
797 fn get_pixel(buf: &[u8], w: u32, x: u32, y: u32) -> (u8, u8, u8, u8) {
798 let off = (y * w * 4 + x * 4) as usize;
799 (buf[off], buf[off + 1], buf[off + 2], buf[off + 3])
800 }
801
802 #[test]
803 fn test_comp_op_default() {
804 assert_eq!(CompOp::default(), CompOp::SrcOver);
805 }
806
807 #[test]
808 fn test_clear() {
809 let (_buf, mut ra) = make_buffer(10, 10);
810 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
811 pf.copy_pixel(5, 5, &Rgba8::new(255, 0, 0, 255));
813 let p = pf.pixel(5, 5);
814 assert_eq!(p.r, 255);
815
816 pf.set_comp_op(CompOp::Clear);
818 pf.blend_pixel(5, 5, &Rgba8::new(0, 0, 0, 255), 255);
819 let p = pf.pixel(5, 5);
820 assert_eq!(p.r, 0);
821 assert_eq!(p.a, 0);
822 }
823
824 #[test]
825 fn test_src() {
826 let (_buf, mut ra) = make_buffer(10, 10);
827 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
828 pf.copy_pixel(0, 0, &Rgba8::new(100, 100, 100, 255));
829
830 pf.set_comp_op(CompOp::Src);
831 pf.blend_pixel(0, 0, &Rgba8::new(200, 50, 0, 255), 255);
832 let p = pf.pixel(0, 0);
833 assert_eq!(p.r, 200);
835 assert_eq!(p.g, 50);
836 assert_eq!(p.b, 0);
837 assert_eq!(p.a, 255);
838 }
839
840 #[test]
841 fn test_dst_is_noop() {
842 let (_buf, mut ra) = make_buffer(10, 10);
843 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
844 pf.copy_pixel(0, 0, &Rgba8::new(42, 43, 44, 200));
845
846 pf.set_comp_op(CompOp::Dst);
847 pf.blend_pixel(0, 0, &Rgba8::new(255, 255, 255, 255), 255);
848 let p = pf.pixel(0, 0);
849 assert_eq!(p.r, 42);
850 assert_eq!(p.g, 43);
851 assert_eq!(p.b, 44);
852 assert_eq!(p.a, 200);
853 }
854
855 #[test]
856 fn test_src_over_opaque() {
857 let (_buf, mut ra) = make_buffer(10, 10);
858 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
859 pf.copy_pixel(0, 0, &Rgba8::new(100, 100, 100, 255));
860
861 pf.set_comp_op(CompOp::SrcOver);
862 pf.blend_pixel(0, 0, &Rgba8::new(200, 50, 25, 255), 255);
863 let p = pf.pixel(0, 0);
864 assert_eq!(p.r, 200);
866 assert_eq!(p.g, 50);
867 assert_eq!(p.b, 25);
868 assert_eq!(p.a, 255);
869 }
870
871 #[test]
872 fn test_src_over_semitransparent() {
873 let (_buf, mut ra) = make_buffer(10, 10);
874 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
875 pf.copy_pixel(0, 0, &Rgba8::new(0, 0, 0, 255));
876
877 pf.set_comp_op(CompOp::SrcOver);
878 pf.blend_pixel(0, 0, &Rgba8::new(255, 0, 0, 128), 255);
880 let p = pf.pixel(0, 0);
881 assert!(p.r > 100 && p.r < 140, "r={}", p.r);
883 assert_eq!(p.a, 255);
884 }
885
886 #[test]
887 fn test_multiply_mode() {
888 let (_buf, mut ra) = make_buffer(10, 10);
889 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
890 pf.copy_pixel(0, 0, &Rgba8::new(255, 255, 255, 255));
892
893 pf.set_comp_op(CompOp::Multiply);
894 pf.blend_pixel(0, 0, &Rgba8::new(128, 128, 128, 255), 255);
895 let p = pf.pixel(0, 0);
896 assert!(p.r > 120 && p.r < 140, "r={}", p.r);
898 }
899
900 #[test]
901 fn test_screen_mode() {
902 let (_buf, mut ra) = make_buffer(10, 10);
903 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
904 pf.copy_pixel(0, 0, &Rgba8::new(0, 0, 0, 255));
906
907 pf.set_comp_op(CompOp::Screen);
908 pf.blend_pixel(0, 0, &Rgba8::new(128, 128, 128, 255), 255);
909 let p = pf.pixel(0, 0);
910 assert!(p.r > 120 && p.r < 140, "r={}", p.r);
912 }
913
914 #[test]
915 fn test_xor_mode() {
916 let (_buf, mut ra) = make_buffer(10, 10);
917 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
918 pf.copy_pixel(0, 0, &Rgba8::new(255, 0, 0, 255));
920
921 pf.set_comp_op(CompOp::Xor);
922 pf.blend_pixel(0, 0, &Rgba8::new(0, 0, 255, 255), 255);
924 let p = pf.pixel(0, 0);
925 assert_eq!(p.a, 0);
927 }
928
929 #[test]
930 fn test_blend_hline() {
931 let (_buf, mut ra) = make_buffer(20, 1);
932 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
933 pf.set_comp_op(CompOp::SrcOver);
934 let red = Rgba8::new(255, 0, 0, 255);
935 pf.blend_hline(5, 0, 10, &red, 255);
936 let p = pf.pixel(10, 0);
938 assert_eq!(p.r, 255);
939 let p = pf.pixel(0, 0);
941 assert_eq!(p.a, 0);
942 }
943
944 #[test]
945 fn test_blend_solid_hspan() {
946 let (_buf, mut ra) = make_buffer(10, 1);
947 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
948 pf.set_comp_op(CompOp::SrcOver);
949 let green = Rgba8::new(0, 255, 0, 255);
950 let covers = [255u8, 128, 64, 0];
951 pf.blend_solid_hspan(0, 0, 4, &green, &covers);
952 let p = pf.pixel(0, 0);
954 assert_eq!(p.g, 255);
955 let p = pf.pixel(3, 0);
957 assert_eq!(p.g, 0);
958 }
959
960 #[test]
961 fn test_all_ops_no_panic() {
962 let ops = [
963 CompOp::Clear,
964 CompOp::Src,
965 CompOp::Dst,
966 CompOp::SrcOver,
967 CompOp::DstOver,
968 CompOp::SrcIn,
969 CompOp::DstIn,
970 CompOp::SrcOut,
971 CompOp::DstOut,
972 CompOp::SrcAtop,
973 CompOp::DstAtop,
974 CompOp::Xor,
975 CompOp::Plus,
976 CompOp::Minus,
977 CompOp::Multiply,
978 CompOp::Screen,
979 CompOp::Overlay,
980 CompOp::Darken,
981 CompOp::Lighten,
982 CompOp::ColorDodge,
983 CompOp::ColorBurn,
984 CompOp::HardLight,
985 CompOp::SoftLight,
986 CompOp::Difference,
987 CompOp::Exclusion,
988 ];
989 let (_buf, mut ra) = make_buffer(10, 10);
990 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
991 let c = Rgba8::new(128, 64, 32, 200);
992 pf.copy_pixel(0, 0, &Rgba8::new(100, 150, 200, 180));
994 for &op in &ops {
995 pf.set_comp_op(op);
996 pf.blend_pixel(0, 0, &c, 128);
997 }
998 }
999
1000 #[test]
1001 fn test_difference_mode() {
1002 let (_buf, mut ra) = make_buffer(10, 10);
1003 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
1004 pf.copy_pixel(0, 0, &Rgba8::new(255, 255, 255, 255));
1006
1007 pf.set_comp_op(CompOp::Difference);
1008 pf.blend_pixel(0, 0, &Rgba8::new(255, 255, 255, 255), 255);
1009 let p = pf.pixel(0, 0);
1010 assert!(p.r < 5, "r={}", p.r);
1012 assert!(p.g < 5, "g={}", p.g);
1013 assert!(p.b < 5, "b={}", p.b);
1014 }
1015
1016 #[test]
1017 fn test_plus_saturates() {
1018 let (_buf, mut ra) = make_buffer(10, 10);
1019 let mut pf = PixfmtRgba32CompOp::new(&mut ra);
1020 pf.copy_pixel(0, 0, &Rgba8::new(200, 200, 200, 255));
1021
1022 pf.set_comp_op(CompOp::Plus);
1023 pf.blend_pixel(0, 0, &Rgba8::new(200, 200, 200, 255), 255);
1024 let p = pf.pixel(0, 0);
1025 assert_eq!(p.r, 255);
1027 assert_eq!(p.g, 255);
1028 assert_eq!(p.b, 255);
1029 }
1030}