Line data Source code
1 : /* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
2 : /*
3 : * Copyright © 2010, 2012 Soren Sandmann Pedersen
4 : * Copyright © 2010, 2012 Red Hat, Inc.
5 : *
6 : * Permission is hereby granted, free of charge, to any person obtaining a
7 : * copy of this software and associated documentation files (the "Software"),
8 : * to deal in the Software without restriction, including without limitation
9 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 : * and/or sell copies of the Software, and to permit persons to whom the
11 : * Software is furnished to do so, subject to the following conditions:
12 : *
13 : * The above copyright notice and this permission notice (including the next
14 : * paragraph) shall be included in all copies or substantial portions of the
15 : * Software.
16 : *
17 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 : * DEALINGS IN THE SOFTWARE.
24 : *
25 : * Author: Soren Sandmann Pedersen (sandmann@cs.au.dk)
26 : */
27 :
28 : #ifdef HAVE_CONFIG_H
29 : #include <config.h>
30 : #endif
31 :
32 : #include <math.h>
33 : #include <string.h>
34 : #include <float.h>
35 :
36 : #include "pixman-private.h"
37 :
38 : /* Workaround for http://gcc.gnu.org/PR54965 */
39 : /* GCC 4.6 has problems with force_inline, so just use normal inline instead */
40 : #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 6)
41 : #undef force_inline
42 : #define force_inline __inline__
43 : #endif
44 :
45 : #define IS_ZERO(f) (-FLT_MIN < (f) && (f) < FLT_MIN)
46 :
47 : typedef float (* combine_channel_t) (float sa, float s, float da, float d);
48 :
49 : static force_inline void
50 : combine_inner (pixman_bool_t component,
51 : float *dest, const float *src, const float *mask, int n_pixels,
52 : combine_channel_t combine_a, combine_channel_t combine_c)
53 : {
54 : int i;
55 :
56 0 : if (!mask)
57 : {
58 0 : for (i = 0; i < 4 * n_pixels; i += 4)
59 : {
60 0 : float sa = src[i + 0];
61 0 : float sr = src[i + 1];
62 0 : float sg = src[i + 2];
63 0 : float sb = src[i + 3];
64 :
65 0 : float da = dest[i + 0];
66 0 : float dr = dest[i + 1];
67 0 : float dg = dest[i + 2];
68 0 : float db = dest[i + 3];
69 :
70 0 : dest[i + 0] = combine_a (sa, sa, da, da);
71 0 : dest[i + 1] = combine_c (sa, sr, da, dr);
72 0 : dest[i + 2] = combine_c (sa, sg, da, dg);
73 0 : dest[i + 3] = combine_c (sa, sb, da, db);
74 : }
75 : }
76 : else
77 : {
78 0 : for (i = 0; i < 4 * n_pixels; i += 4)
79 : {
80 : float sa, sr, sg, sb;
81 : float ma, mr, mg, mb;
82 : float da, dr, dg, db;
83 :
84 0 : sa = src[i + 0];
85 0 : sr = src[i + 1];
86 0 : sg = src[i + 2];
87 0 : sb = src[i + 3];
88 :
89 0 : if (component)
90 : {
91 0 : ma = mask[i + 0];
92 0 : mr = mask[i + 1];
93 0 : mg = mask[i + 2];
94 0 : mb = mask[i + 3];
95 :
96 0 : sr *= mr;
97 0 : sg *= mg;
98 0 : sb *= mb;
99 :
100 0 : ma *= sa;
101 0 : mr *= sa;
102 0 : mg *= sa;
103 0 : mb *= sa;
104 :
105 0 : sa = ma;
106 : }
107 : else
108 : {
109 0 : ma = mask[i + 0];
110 :
111 0 : sa *= ma;
112 0 : sr *= ma;
113 0 : sg *= ma;
114 0 : sb *= ma;
115 :
116 0 : ma = mr = mg = mb = sa;
117 : }
118 :
119 0 : da = dest[i + 0];
120 0 : dr = dest[i + 1];
121 0 : dg = dest[i + 2];
122 0 : db = dest[i + 3];
123 :
124 0 : dest[i + 0] = combine_a (ma, sa, da, da);
125 0 : dest[i + 1] = combine_c (mr, sr, da, dr);
126 0 : dest[i + 2] = combine_c (mg, sg, da, dg);
127 0 : dest[i + 3] = combine_c (mb, sb, da, db);
128 : }
129 : }
130 : }
131 :
132 : #define MAKE_COMBINER(name, component, combine_a, combine_c) \
133 : static void \
134 : combine_ ## name ## _float (pixman_implementation_t *imp, \
135 : pixman_op_t op, \
136 : float *dest, \
137 : const float *src, \
138 : const float *mask, \
139 : int n_pixels) \
140 : { \
141 : combine_inner (component, dest, src, mask, n_pixels, \
142 : combine_a, combine_c); \
143 : }
144 :
145 : #define MAKE_COMBINERS(name, combine_a, combine_c) \
146 : MAKE_COMBINER(name ## _ca, TRUE, combine_a, combine_c) \
147 : MAKE_COMBINER(name ## _u, FALSE, combine_a, combine_c)
148 :
149 :
150 : /*
151 : * Porter/Duff operators
152 : */
153 : typedef enum
154 : {
155 : ZERO,
156 : ONE,
157 : SRC_ALPHA,
158 : DEST_ALPHA,
159 : INV_SA,
160 : INV_DA,
161 : SA_OVER_DA,
162 : DA_OVER_SA,
163 : INV_SA_OVER_DA,
164 : INV_DA_OVER_SA,
165 : ONE_MINUS_SA_OVER_DA,
166 : ONE_MINUS_DA_OVER_SA,
167 : ONE_MINUS_INV_DA_OVER_SA,
168 : ONE_MINUS_INV_SA_OVER_DA
169 : } combine_factor_t;
170 :
171 : #define CLAMP(f) \
172 : (((f) < 0)? 0 : (((f) > 1.0) ? 1.0 : (f)))
173 :
174 : static force_inline float
175 : get_factor (combine_factor_t factor, float sa, float da)
176 : {
177 0 : float f = -1;
178 :
179 0 : switch (factor)
180 : {
181 : case ZERO:
182 0 : f = 0.0f;
183 : break;
184 :
185 : case ONE:
186 0 : f = 1.0f;
187 : break;
188 :
189 : case SRC_ALPHA:
190 0 : f = sa;
191 : break;
192 :
193 : case DEST_ALPHA:
194 0 : f = da;
195 : break;
196 :
197 : case INV_SA:
198 0 : f = 1 - sa;
199 : break;
200 :
201 : case INV_DA:
202 0 : f = 1 - da;
203 : break;
204 :
205 : case SA_OVER_DA:
206 0 : if (IS_ZERO (da))
207 0 : f = 1.0f;
208 : else
209 0 : f = CLAMP (sa / da);
210 : break;
211 :
212 : case DA_OVER_SA:
213 0 : if (IS_ZERO (sa))
214 0 : f = 1.0f;
215 : else
216 0 : f = CLAMP (da / sa);
217 : break;
218 :
219 : case INV_SA_OVER_DA:
220 0 : if (IS_ZERO (da))
221 0 : f = 1.0f;
222 : else
223 0 : f = CLAMP ((1.0f - sa) / da);
224 : break;
225 :
226 : case INV_DA_OVER_SA:
227 0 : if (IS_ZERO (sa))
228 0 : f = 1.0f;
229 : else
230 0 : f = CLAMP ((1.0f - da) / sa);
231 : break;
232 :
233 : case ONE_MINUS_SA_OVER_DA:
234 0 : if (IS_ZERO (da))
235 0 : f = 0.0f;
236 : else
237 0 : f = CLAMP (1.0f - sa / da);
238 : break;
239 :
240 : case ONE_MINUS_DA_OVER_SA:
241 0 : if (IS_ZERO (sa))
242 0 : f = 0.0f;
243 : else
244 0 : f = CLAMP (1.0f - da / sa);
245 : break;
246 :
247 : case ONE_MINUS_INV_DA_OVER_SA:
248 0 : if (IS_ZERO (sa))
249 0 : f = 0.0f;
250 : else
251 0 : f = CLAMP (1.0f - (1.0f - da) / sa);
252 : break;
253 :
254 : case ONE_MINUS_INV_SA_OVER_DA:
255 0 : if (IS_ZERO (da))
256 0 : f = 0.0f;
257 : else
258 0 : f = CLAMP (1.0f - (1.0f - sa) / da);
259 : break;
260 : }
261 :
262 0 : return f;
263 : }
264 :
265 : #define MAKE_PD_COMBINERS(name, a, b) \
266 : static float force_inline \
267 : pd_combine_ ## name (float sa, float s, float da, float d) \
268 : { \
269 : const float fa = get_factor (a, sa, da); \
270 : const float fb = get_factor (b, sa, da); \
271 : \
272 : return MIN (1.0f, s * fa + d * fb); \
273 : } \
274 : \
275 : MAKE_COMBINERS(name, pd_combine_ ## name, pd_combine_ ## name)
276 :
277 0 : MAKE_PD_COMBINERS (clear, ZERO, ZERO)
278 0 : MAKE_PD_COMBINERS (src, ONE, ZERO)
279 0 : MAKE_PD_COMBINERS (dst, ZERO, ONE)
280 0 : MAKE_PD_COMBINERS (over, ONE, INV_SA)
281 0 : MAKE_PD_COMBINERS (over_reverse, INV_DA, ONE)
282 0 : MAKE_PD_COMBINERS (in, DEST_ALPHA, ZERO)
283 0 : MAKE_PD_COMBINERS (in_reverse, ZERO, SRC_ALPHA)
284 0 : MAKE_PD_COMBINERS (out, INV_DA, ZERO)
285 0 : MAKE_PD_COMBINERS (out_reverse, ZERO, INV_SA)
286 0 : MAKE_PD_COMBINERS (atop, DEST_ALPHA, INV_SA)
287 0 : MAKE_PD_COMBINERS (atop_reverse, INV_DA, SRC_ALPHA)
288 0 : MAKE_PD_COMBINERS (xor, INV_DA, INV_SA)
289 0 : MAKE_PD_COMBINERS (add, ONE, ONE)
290 :
291 0 : MAKE_PD_COMBINERS (saturate, INV_DA_OVER_SA, ONE)
292 :
293 0 : MAKE_PD_COMBINERS (disjoint_clear, ZERO, ZERO)
294 0 : MAKE_PD_COMBINERS (disjoint_src, ONE, ZERO)
295 0 : MAKE_PD_COMBINERS (disjoint_dst, ZERO, ONE)
296 0 : MAKE_PD_COMBINERS (disjoint_over, ONE, INV_SA_OVER_DA)
297 0 : MAKE_PD_COMBINERS (disjoint_over_reverse, INV_DA_OVER_SA, ONE)
298 0 : MAKE_PD_COMBINERS (disjoint_in, ONE_MINUS_INV_DA_OVER_SA, ZERO)
299 0 : MAKE_PD_COMBINERS (disjoint_in_reverse, ZERO, ONE_MINUS_INV_SA_OVER_DA)
300 0 : MAKE_PD_COMBINERS (disjoint_out, INV_DA_OVER_SA, ZERO)
301 0 : MAKE_PD_COMBINERS (disjoint_out_reverse, ZERO, INV_SA_OVER_DA)
302 0 : MAKE_PD_COMBINERS (disjoint_atop, ONE_MINUS_INV_DA_OVER_SA, INV_SA_OVER_DA)
303 0 : MAKE_PD_COMBINERS (disjoint_atop_reverse, INV_DA_OVER_SA, ONE_MINUS_INV_SA_OVER_DA)
304 0 : MAKE_PD_COMBINERS (disjoint_xor, INV_DA_OVER_SA, INV_SA_OVER_DA)
305 :
306 0 : MAKE_PD_COMBINERS (conjoint_clear, ZERO, ZERO)
307 0 : MAKE_PD_COMBINERS (conjoint_src, ONE, ZERO)
308 0 : MAKE_PD_COMBINERS (conjoint_dst, ZERO, ONE)
309 0 : MAKE_PD_COMBINERS (conjoint_over, ONE, ONE_MINUS_SA_OVER_DA)
310 0 : MAKE_PD_COMBINERS (conjoint_over_reverse, ONE_MINUS_DA_OVER_SA, ONE)
311 0 : MAKE_PD_COMBINERS (conjoint_in, DA_OVER_SA, ZERO)
312 0 : MAKE_PD_COMBINERS (conjoint_in_reverse, ZERO, SA_OVER_DA)
313 0 : MAKE_PD_COMBINERS (conjoint_out, ONE_MINUS_DA_OVER_SA, ZERO)
314 0 : MAKE_PD_COMBINERS (conjoint_out_reverse, ZERO, ONE_MINUS_SA_OVER_DA)
315 0 : MAKE_PD_COMBINERS (conjoint_atop, DA_OVER_SA, ONE_MINUS_SA_OVER_DA)
316 0 : MAKE_PD_COMBINERS (conjoint_atop_reverse, ONE_MINUS_DA_OVER_SA, SA_OVER_DA)
317 0 : MAKE_PD_COMBINERS (conjoint_xor, ONE_MINUS_DA_OVER_SA, ONE_MINUS_SA_OVER_DA)
318 :
319 : /*
320 : * PDF blend modes:
321 : *
322 : * The following blend modes have been taken from the PDF ISO 32000
323 : * specification, which at this point in time is available from
324 : * http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf
325 : * The relevant chapters are 11.3.5 and 11.3.6.
326 : * The formula for computing the final pixel color given in 11.3.6 is:
327 : * αr × Cr = (1 – αs) × αb × Cb + (1 – αb) × αs × Cs + αb × αs × B(Cb, Cs)
328 : * with B() being the blend function.
329 : * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs
330 : *
331 : * These blend modes should match the SVG filter draft specification, as
332 : * it has been designed to mirror ISO 32000. Note that at the current point
333 : * no released draft exists that shows this, as the formulas have not been
334 : * updated yet after the release of ISO 32000.
335 : *
336 : * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and
337 : * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an
338 : * argument. Note that this implementation operates on premultiplied colors,
339 : * while the PDF specification does not. Therefore the code uses the formula
340 : * ar.Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as)
341 : */
342 :
343 : #define MAKE_SEPARABLE_PDF_COMBINERS(name) \
344 : static force_inline float \
345 : combine_ ## name ## _a (float sa, float s, float da, float d) \
346 : { \
347 : return da + sa - da * sa; \
348 : } \
349 : \
350 : static force_inline float \
351 : combine_ ## name ## _c (float sa, float s, float da, float d) \
352 : { \
353 : float f = (1 - sa) * d + (1 - da) * s; \
354 : \
355 : return f + blend_ ## name (sa, s, da, d); \
356 : } \
357 : \
358 : MAKE_COMBINERS (name, combine_ ## name ## _a, combine_ ## name ## _c)
359 :
360 : static force_inline float
361 : blend_multiply (float sa, float s, float da, float d)
362 : {
363 0 : return d * s;
364 : }
365 :
366 : static force_inline float
367 : blend_screen (float sa, float s, float da, float d)
368 : {
369 0 : return d * sa + s * da - s * d;
370 : }
371 :
372 : static force_inline float
373 : blend_overlay (float sa, float s, float da, float d)
374 : {
375 0 : if (2 * d < da)
376 0 : return 2 * s * d;
377 : else
378 0 : return sa * da - 2 * (da - d) * (sa - s);
379 : }
380 :
381 : static force_inline float
382 : blend_darken (float sa, float s, float da, float d)
383 : {
384 0 : s = s * da;
385 0 : d = d * sa;
386 :
387 0 : if (s > d)
388 0 : return d;
389 : else
390 0 : return s;
391 : }
392 :
393 : static force_inline float
394 : blend_lighten (float sa, float s, float da, float d)
395 : {
396 0 : s = s * da;
397 0 : d = d * sa;
398 :
399 0 : if (s > d)
400 0 : return s;
401 : else
402 0 : return d;
403 : }
404 :
405 : static force_inline float
406 : blend_color_dodge (float sa, float s, float da, float d)
407 : {
408 0 : if (IS_ZERO (d))
409 0 : return 0.0f;
410 0 : else if (d * sa >= sa * da - s * da)
411 0 : return sa * da;
412 0 : else if (IS_ZERO (sa - s))
413 0 : return sa * da;
414 : else
415 0 : return sa * sa * d / (sa - s);
416 : }
417 :
418 : static force_inline float
419 : blend_color_burn (float sa, float s, float da, float d)
420 : {
421 0 : if (d >= da)
422 0 : return sa * da;
423 0 : else if (sa * (da - d) >= s * da)
424 0 : return 0.0f;
425 0 : else if (IS_ZERO (s))
426 0 : return 0.0f;
427 : else
428 0 : return sa * (da - sa * (da - d) / s);
429 : }
430 :
431 : static force_inline float
432 : blend_hard_light (float sa, float s, float da, float d)
433 : {
434 0 : if (2 * s < sa)
435 0 : return 2 * s * d;
436 : else
437 0 : return sa * da - 2 * (da - d) * (sa - s);
438 : }
439 :
440 : static force_inline float
441 : blend_soft_light (float sa, float s, float da, float d)
442 : {
443 0 : if (2 * s < sa)
444 : {
445 0 : if (IS_ZERO (da))
446 0 : return d * sa;
447 : else
448 0 : return d * sa - d * (da - d) * (sa - 2 * s) / da;
449 : }
450 : else
451 : {
452 0 : if (IS_ZERO (da))
453 : {
454 0 : return 0.0f;
455 : }
456 : else
457 : {
458 0 : if (4 * d <= da)
459 0 : return d * sa + (2 * s - sa) * d * ((16 * d / da - 12) * d / da + 3);
460 : else
461 0 : return d * sa + (sqrtf (d * da) - d) * (2 * s - sa);
462 : }
463 : }
464 : }
465 :
466 : static force_inline float
467 : blend_difference (float sa, float s, float da, float d)
468 : {
469 0 : float dsa = d * sa;
470 0 : float sda = s * da;
471 :
472 0 : if (sda < dsa)
473 0 : return dsa - sda;
474 : else
475 0 : return sda - dsa;
476 : }
477 :
478 : static force_inline float
479 : blend_exclusion (float sa, float s, float da, float d)
480 : {
481 0 : return s * da + d * sa - 2 * d * s;
482 : }
483 :
484 0 : MAKE_SEPARABLE_PDF_COMBINERS (multiply)
485 0 : MAKE_SEPARABLE_PDF_COMBINERS (screen)
486 0 : MAKE_SEPARABLE_PDF_COMBINERS (overlay)
487 0 : MAKE_SEPARABLE_PDF_COMBINERS (darken)
488 0 : MAKE_SEPARABLE_PDF_COMBINERS (lighten)
489 0 : MAKE_SEPARABLE_PDF_COMBINERS (color_dodge)
490 0 : MAKE_SEPARABLE_PDF_COMBINERS (color_burn)
491 0 : MAKE_SEPARABLE_PDF_COMBINERS (hard_light)
492 0 : MAKE_SEPARABLE_PDF_COMBINERS (soft_light)
493 0 : MAKE_SEPARABLE_PDF_COMBINERS (difference)
494 0 : MAKE_SEPARABLE_PDF_COMBINERS (exclusion)
495 :
496 : /*
497 : * PDF nonseperable blend modes.
498 : *
499 : * These are implemented using the following functions to operate in Hsl
500 : * space, with Cmax, Cmid, Cmin referring to the max, mid and min value
501 : * of the red, green and blue components.
502 : *
503 : * LUM (C) = 0.3 × Cred + 0.59 × Cgreen + 0.11 × Cblue
504 : *
505 : * clip_color (C):
506 : * l = LUM (C)
507 : * min = Cmin
508 : * max = Cmax
509 : * if n < 0.0
510 : * C = l + (((C – l) × l) ⁄ (l – min))
511 : * if x > 1.0
512 : * C = l + (((C – l) × (1 – l)) (max – l))
513 : * return C
514 : *
515 : * set_lum (C, l):
516 : * d = l – LUM (C)
517 : * C += d
518 : * return clip_color (C)
519 : *
520 : * SAT (C) = CH_MAX (C) - CH_MIN (C)
521 : *
522 : * set_sat (C, s):
523 : * if Cmax > Cmin
524 : * Cmid = ( ( ( Cmid – Cmin ) × s ) ⁄ ( Cmax – Cmin ) )
525 : * Cmax = s
526 : * else
527 : * Cmid = Cmax = 0.0
528 : * Cmin = 0.0
529 : * return C
530 : */
531 :
532 : /* For premultiplied colors, we need to know what happens when C is
533 : * multiplied by a real number. LUM and SAT are linear:
534 : *
535 : * LUM (r × C) = r × LUM (C) SAT (r × C) = r × SAT (C)
536 : *
537 : * If we extend clip_color with an extra argument a and change
538 : *
539 : * if x >= 1.0
540 : *
541 : * into
542 : *
543 : * if x >= a
544 : *
545 : * then clip_color is also linear:
546 : *
547 : * r * clip_color (C, a) = clip_color (r_c, ra);
548 : *
549 : * for positive r.
550 : *
551 : * Similarly, we can extend set_lum with an extra argument that is just passed
552 : * on to clip_color:
553 : *
554 : * r × set_lum ( C, l, a)
555 : *
556 : * = r × clip_color ( C + l - LUM (C), a)
557 : *
558 : * = clip_color ( r * C + r × l - LUM (r × C), r * a)
559 : *
560 : * = set_lum ( r * C, r * l, r * a)
561 : *
562 : * Finally, set_sat:
563 : *
564 : * r * set_sat (C, s) = set_sat (x * C, r * s)
565 : *
566 : * The above holds for all non-zero x because they x'es in the fraction for
567 : * C_mid cancel out. Specifically, it holds for x = r:
568 : *
569 : * r * set_sat (C, s) = set_sat (r_c, rs)
570 : *
571 : *
572 : *
573 : *
574 : * So, for the non-separable PDF blend modes, we have (using s, d for
575 : * non-premultiplied colors, and S, D for premultiplied:
576 : *
577 : * Color:
578 : *
579 : * a_s * a_d * B(s, d)
580 : * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1)
581 : * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d)
582 : *
583 : *
584 : * Luminosity:
585 : *
586 : * a_s * a_d * B(s, d)
587 : * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1)
588 : * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d)
589 : *
590 : *
591 : * Saturation:
592 : *
593 : * a_s * a_d * B(s, d)
594 : * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1)
595 : * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)),
596 : * a_s * LUM (D), a_s * a_d)
597 : * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d))
598 : *
599 : * Hue:
600 : *
601 : * a_s * a_d * B(s, d)
602 : * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1)
603 : * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d)
604 : *
605 : */
606 :
607 : typedef struct
608 : {
609 : float r;
610 : float g;
611 : float b;
612 : } rgb_t;
613 :
614 : static force_inline float
615 : minf (float a, float b)
616 : {
617 0 : return a < b? a : b;
618 : }
619 :
620 : static force_inline float
621 : maxf (float a, float b)
622 : {
623 0 : return a > b? a : b;
624 : }
625 :
626 : static force_inline float
627 : channel_min (const rgb_t *c)
628 : {
629 0 : return minf (minf (c->r, c->g), c->b);
630 : }
631 :
632 : static force_inline float
633 : channel_max (const rgb_t *c)
634 : {
635 0 : return maxf (maxf (c->r, c->g), c->b);
636 : }
637 :
638 : static force_inline float
639 : get_lum (const rgb_t *c)
640 : {
641 0 : return c->r * 0.3f + c->g * 0.59f + c->b * 0.11f;
642 : }
643 :
644 : static force_inline float
645 : get_sat (const rgb_t *c)
646 : {
647 0 : return channel_max (c) - channel_min (c);
648 : }
649 :
650 : static void
651 0 : clip_color (rgb_t *color, float a)
652 : {
653 0 : float l = get_lum (color);
654 0 : float n = channel_min (color);
655 0 : float x = channel_max (color);
656 : float t;
657 :
658 0 : if (n < 0.0f)
659 : {
660 0 : t = l - n;
661 0 : if (IS_ZERO (t))
662 : {
663 0 : color->r = 0.0f;
664 0 : color->g = 0.0f;
665 0 : color->b = 0.0f;
666 : }
667 : else
668 : {
669 0 : color->r = l + (((color->r - l) * l) / t);
670 0 : color->g = l + (((color->g - l) * l) / t);
671 0 : color->b = l + (((color->b - l) * l) / t);
672 : }
673 : }
674 0 : if (x > a)
675 : {
676 0 : t = x - l;
677 0 : if (IS_ZERO (t))
678 : {
679 0 : color->r = a;
680 0 : color->g = a;
681 0 : color->b = a;
682 : }
683 : else
684 : {
685 0 : color->r = l + (((color->r - l) * (a - l) / t));
686 0 : color->g = l + (((color->g - l) * (a - l) / t));
687 0 : color->b = l + (((color->b - l) * (a - l) / t));
688 : }
689 : }
690 0 : }
691 :
692 : static void
693 0 : set_lum (rgb_t *color, float sa, float l)
694 : {
695 0 : float d = l - get_lum (color);
696 :
697 0 : color->r = color->r + d;
698 0 : color->g = color->g + d;
699 0 : color->b = color->b + d;
700 :
701 0 : clip_color (color, sa);
702 0 : }
703 :
704 : static void
705 0 : set_sat (rgb_t *src, float sat)
706 : {
707 : float *max, *mid, *min;
708 : float t;
709 :
710 0 : if (src->r > src->g)
711 : {
712 0 : if (src->r > src->b)
713 : {
714 0 : max = &(src->r);
715 :
716 0 : if (src->g > src->b)
717 : {
718 0 : mid = &(src->g);
719 0 : min = &(src->b);
720 : }
721 : else
722 : {
723 0 : mid = &(src->b);
724 0 : min = &(src->g);
725 : }
726 : }
727 : else
728 : {
729 0 : max = &(src->b);
730 0 : mid = &(src->r);
731 0 : min = &(src->g);
732 : }
733 : }
734 : else
735 : {
736 0 : if (src->r > src->b)
737 : {
738 0 : max = &(src->g);
739 0 : mid = &(src->r);
740 0 : min = &(src->b);
741 : }
742 : else
743 : {
744 0 : min = &(src->r);
745 :
746 0 : if (src->g > src->b)
747 : {
748 0 : max = &(src->g);
749 0 : mid = &(src->b);
750 : }
751 : else
752 : {
753 0 : max = &(src->b);
754 0 : mid = &(src->g);
755 : }
756 : }
757 : }
758 :
759 0 : t = *max - *min;
760 :
761 0 : if (IS_ZERO (t))
762 : {
763 0 : *mid = *max = 0.0f;
764 : }
765 : else
766 : {
767 0 : *mid = ((*mid - *min) * sat) / t;
768 0 : *max = sat;
769 : }
770 :
771 0 : *min = 0.0f;
772 0 : }
773 :
774 : /*
775 : * Hue:
776 : * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb))
777 : */
778 : static force_inline void
779 : blend_hsl_hue (rgb_t *res,
780 : const rgb_t *dest, float da,
781 : const rgb_t *src, float sa)
782 : {
783 0 : res->r = src->r * da;
784 0 : res->g = src->g * da;
785 0 : res->b = src->b * da;
786 :
787 0 : set_sat (res, get_sat (dest) * sa);
788 0 : set_lum (res, sa * da, get_lum (dest) * sa);
789 : }
790 :
791 : /*
792 : * Saturation:
793 : * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb))
794 : */
795 : static force_inline void
796 : blend_hsl_saturation (rgb_t *res,
797 : const rgb_t *dest, float da,
798 : const rgb_t *src, float sa)
799 : {
800 0 : res->r = dest->r * sa;
801 0 : res->g = dest->g * sa;
802 0 : res->b = dest->b * sa;
803 :
804 0 : set_sat (res, get_sat (src) * da);
805 0 : set_lum (res, sa * da, get_lum (dest) * sa);
806 : }
807 :
808 : /*
809 : * Color:
810 : * B(Cb, Cs) = set_lum (Cs, LUM (Cb))
811 : */
812 : static force_inline void
813 : blend_hsl_color (rgb_t *res,
814 : const rgb_t *dest, float da,
815 : const rgb_t *src, float sa)
816 : {
817 0 : res->r = src->r * da;
818 0 : res->g = src->g * da;
819 0 : res->b = src->b * da;
820 :
821 0 : set_lum (res, sa * da, get_lum (dest) * sa);
822 : }
823 :
824 : /*
825 : * Luminosity:
826 : * B(Cb, Cs) = set_lum (Cb, LUM (Cs))
827 : */
828 : static force_inline void
829 : blend_hsl_luminosity (rgb_t *res,
830 : const rgb_t *dest, float da,
831 : const rgb_t *src, float sa)
832 : {
833 0 : res->r = dest->r * sa;
834 0 : res->g = dest->g * sa;
835 0 : res->b = dest->b * sa;
836 :
837 0 : set_lum (res, sa * da, get_lum (src) * da);
838 : }
839 :
840 : #define MAKE_NON_SEPARABLE_PDF_COMBINERS(name) \
841 : static void \
842 : combine_ ## name ## _u_float (pixman_implementation_t *imp, \
843 : pixman_op_t op, \
844 : float *dest, \
845 : const float *src, \
846 : const float *mask, \
847 : int n_pixels) \
848 : { \
849 : int i; \
850 : \
851 : for (i = 0; i < 4 * n_pixels; i += 4) \
852 : { \
853 : float sa, da; \
854 : rgb_t sc, dc, rc; \
855 : \
856 : sa = src[i + 0]; \
857 : sc.r = src[i + 1]; \
858 : sc.g = src[i + 2]; \
859 : sc.b = src[i + 3]; \
860 : \
861 : da = dest[i + 0]; \
862 : dc.r = dest[i + 1]; \
863 : dc.g = dest[i + 2]; \
864 : dc.b = dest[i + 3]; \
865 : \
866 : if (mask) \
867 : { \
868 : float ma = mask[i + 0]; \
869 : \
870 : /* Component alpha is not supported for HSL modes */ \
871 : sa *= ma; \
872 : sc.r *= ma; \
873 : sc.g *= ma; \
874 : sc.g *= ma; \
875 : } \
876 : \
877 : blend_ ## name (&rc, &dc, da, &sc, sa); \
878 : \
879 : dest[i + 0] = sa + da - sa * da; \
880 : dest[i + 1] = (1 - sa) * dc.r + (1 - da) * sc.r + rc.r; \
881 : dest[i + 2] = (1 - sa) * dc.g + (1 - da) * sc.g + rc.g; \
882 : dest[i + 3] = (1 - sa) * dc.b + (1 - da) * sc.b + rc.b; \
883 : } \
884 : }
885 :
886 0 : MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_hue)
887 0 : MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_saturation)
888 0 : MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_color)
889 0 : MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_luminosity)
890 :
891 : void
892 1 : _pixman_setup_combiner_functions_float (pixman_implementation_t *imp)
893 : {
894 : /* Unified alpha */
895 1 : imp->combine_float[PIXMAN_OP_CLEAR] = combine_clear_u_float;
896 1 : imp->combine_float[PIXMAN_OP_SRC] = combine_src_u_float;
897 1 : imp->combine_float[PIXMAN_OP_DST] = combine_dst_u_float;
898 1 : imp->combine_float[PIXMAN_OP_OVER] = combine_over_u_float;
899 1 : imp->combine_float[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u_float;
900 1 : imp->combine_float[PIXMAN_OP_IN] = combine_in_u_float;
901 1 : imp->combine_float[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u_float;
902 1 : imp->combine_float[PIXMAN_OP_OUT] = combine_out_u_float;
903 1 : imp->combine_float[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u_float;
904 1 : imp->combine_float[PIXMAN_OP_ATOP] = combine_atop_u_float;
905 1 : imp->combine_float[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u_float;
906 1 : imp->combine_float[PIXMAN_OP_XOR] = combine_xor_u_float;
907 1 : imp->combine_float[PIXMAN_OP_ADD] = combine_add_u_float;
908 1 : imp->combine_float[PIXMAN_OP_SATURATE] = combine_saturate_u_float;
909 :
910 : /* Disjoint, unified */
911 1 : imp->combine_float[PIXMAN_OP_DISJOINT_CLEAR] = combine_disjoint_clear_u_float;
912 1 : imp->combine_float[PIXMAN_OP_DISJOINT_SRC] = combine_disjoint_src_u_float;
913 1 : imp->combine_float[PIXMAN_OP_DISJOINT_DST] = combine_disjoint_dst_u_float;
914 1 : imp->combine_float[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u_float;
915 1 : imp->combine_float[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_disjoint_over_reverse_u_float;
916 1 : imp->combine_float[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u_float;
917 1 : imp->combine_float[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u_float;
918 1 : imp->combine_float[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u_float;
919 1 : imp->combine_float[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u_float;
920 1 : imp->combine_float[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u_float;
921 1 : imp->combine_float[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u_float;
922 1 : imp->combine_float[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u_float;
923 :
924 : /* Conjoint, unified */
925 1 : imp->combine_float[PIXMAN_OP_CONJOINT_CLEAR] = combine_conjoint_clear_u_float;
926 1 : imp->combine_float[PIXMAN_OP_CONJOINT_SRC] = combine_conjoint_src_u_float;
927 1 : imp->combine_float[PIXMAN_OP_CONJOINT_DST] = combine_conjoint_dst_u_float;
928 1 : imp->combine_float[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u_float;
929 1 : imp->combine_float[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u_float;
930 1 : imp->combine_float[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u_float;
931 1 : imp->combine_float[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u_float;
932 1 : imp->combine_float[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u_float;
933 1 : imp->combine_float[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u_float;
934 1 : imp->combine_float[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u_float;
935 1 : imp->combine_float[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u_float;
936 1 : imp->combine_float[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_u_float;
937 :
938 : /* PDF operators, unified */
939 1 : imp->combine_float[PIXMAN_OP_MULTIPLY] = combine_multiply_u_float;
940 1 : imp->combine_float[PIXMAN_OP_SCREEN] = combine_screen_u_float;
941 1 : imp->combine_float[PIXMAN_OP_OVERLAY] = combine_overlay_u_float;
942 1 : imp->combine_float[PIXMAN_OP_DARKEN] = combine_darken_u_float;
943 1 : imp->combine_float[PIXMAN_OP_LIGHTEN] = combine_lighten_u_float;
944 1 : imp->combine_float[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_u_float;
945 1 : imp->combine_float[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u_float;
946 1 : imp->combine_float[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u_float;
947 1 : imp->combine_float[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u_float;
948 1 : imp->combine_float[PIXMAN_OP_DIFFERENCE] = combine_difference_u_float;
949 1 : imp->combine_float[PIXMAN_OP_EXCLUSION] = combine_exclusion_u_float;
950 :
951 1 : imp->combine_float[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u_float;
952 1 : imp->combine_float[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u_float;
953 1 : imp->combine_float[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u_float;
954 1 : imp->combine_float[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_u_float;
955 :
956 : /* Component alpha combiners */
957 1 : imp->combine_float_ca[PIXMAN_OP_CLEAR] = combine_clear_ca_float;
958 1 : imp->combine_float_ca[PIXMAN_OP_SRC] = combine_src_ca_float;
959 1 : imp->combine_float_ca[PIXMAN_OP_DST] = combine_dst_ca_float;
960 1 : imp->combine_float_ca[PIXMAN_OP_OVER] = combine_over_ca_float;
961 1 : imp->combine_float_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca_float;
962 1 : imp->combine_float_ca[PIXMAN_OP_IN] = combine_in_ca_float;
963 1 : imp->combine_float_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca_float;
964 1 : imp->combine_float_ca[PIXMAN_OP_OUT] = combine_out_ca_float;
965 1 : imp->combine_float_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca_float;
966 1 : imp->combine_float_ca[PIXMAN_OP_ATOP] = combine_atop_ca_float;
967 1 : imp->combine_float_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca_float;
968 1 : imp->combine_float_ca[PIXMAN_OP_XOR] = combine_xor_ca_float;
969 1 : imp->combine_float_ca[PIXMAN_OP_ADD] = combine_add_ca_float;
970 1 : imp->combine_float_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca_float;
971 :
972 : /* Disjoint CA */
973 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_disjoint_clear_ca_float;
974 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_SRC] = combine_disjoint_src_ca_float;
975 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_DST] = combine_disjoint_dst_ca_float;
976 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca_float;
977 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_disjoint_over_reverse_ca_float;
978 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca_float;
979 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca_float;
980 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca_float;
981 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca_float;
982 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca_float;
983 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca_float;
984 1 : imp->combine_float_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca_float;
985 :
986 : /* Conjoint CA */
987 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_conjoint_clear_ca_float;
988 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_SRC] = combine_conjoint_src_ca_float;
989 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_DST] = combine_conjoint_dst_ca_float;
990 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca_float;
991 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca_float;
992 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca_float;
993 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca_float;
994 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca_float;
995 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca_float;
996 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca_float;
997 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca_float;
998 1 : imp->combine_float_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_ca_float;
999 :
1000 : /* PDF operators CA */
1001 1 : imp->combine_float_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca_float;
1002 1 : imp->combine_float_ca[PIXMAN_OP_SCREEN] = combine_screen_ca_float;
1003 1 : imp->combine_float_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca_float;
1004 1 : imp->combine_float_ca[PIXMAN_OP_DARKEN] = combine_darken_ca_float;
1005 1 : imp->combine_float_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca_float;
1006 1 : imp->combine_float_ca[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_ca_float;
1007 1 : imp->combine_float_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca_float;
1008 1 : imp->combine_float_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca_float;
1009 1 : imp->combine_float_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca_float;
1010 1 : imp->combine_float_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca_float;
1011 1 : imp->combine_float_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca_float;
1012 :
1013 : /* It is not clear that these make sense, so make them noops for now */
1014 1 : imp->combine_float_ca[PIXMAN_OP_HSL_HUE] = combine_dst_u_float;
1015 1 : imp->combine_float_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst_u_float;
1016 1 : imp->combine_float_ca[PIXMAN_OP_HSL_COLOR] = combine_dst_u_float;
1017 1 : imp->combine_float_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst_u_float;
1018 1 : }
|