README: update mention need for ldconfig
[mypaint:maxy-experimental.git] / lib / composite_rgbx.hpp
1 /* This file is part of MyPaint.
2  * Copyright (C) 2012 by Andrew Chadwick <a.t.chadwick@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9
10
11 /*
12 Where possible, work in premultiplied-alpha and use the terminology from
13 the W3C specifications for compositing modes available at
14
15   http://www.w3.org/TR/SVGCompositing/
16   http://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html
17
18 Namely,
19
20   Sc - source (top) layer channel value, non-premultiplied
21   Sa - source (top) layer alpha: src_p[3] x opac
22   Sca - source (top) layer channel value, premult by Da: src_p[c] x opac
23   Dc - destination layer channel value, non-premultiplied
24   Da - destination layer alpha: dst_p[3]
25   Dca - destination layer channel value, premult by Da: dst_p[c]
26
27 Values from src_p[] arguments are almost(?) always multiplied by the layer
28 opacity, opac, before use as above.
29
30 For now, this file is included twice, once with COMPOSITE_MODE_RGBA defined
31 and once without it. In the latter case, assume Da=fix15_one, and don't write
32 to the destination alpha.
33
34 */
35
36
37 // It's always better to use inline functions rather than than fumble around
38 // with scaling factors: the code will be much cleaner. It's OK to define
39 // optimal funcs for special cases like a sum of two products.
40 #include "fix15.hpp"
41
42
43
44
45 inline void
46 #ifdef COMPOSITE_MODE_RGBA
47 rgba_composite_src_over_rgba
48 #else
49 rgba_composite_src_over_rgbu
50 #endif
51     (const fix15_short_t src_p[],
52      fix15_short_t dst_p[],
53      const fix15_short_t opac)
54 {
55     // http://www.w3.org/TR/SVGCompositing/#comp-op-src-over
56     const fix15_t Sa = fix15_mul(src_p[3], opac);
57     dst_p[0] = fix15_sumprods( src_p[0], opac,   fix15_one-Sa, dst_p[0] );
58     dst_p[1] = fix15_sumprods( src_p[1], opac,   fix15_one-Sa, dst_p[1] );
59     dst_p[2] = fix15_sumprods( src_p[2], opac,   fix15_one-Sa, dst_p[2] );
60 #ifdef COMPOSITE_MODE_RGBA
61     const fix15_t Da = dst_p[3];
62     dst_p[3] = Sa + Da - fix15_mul(Sa, Da);
63 #endif
64 }
65
66
67 inline void
68 #ifdef COMPOSITE_MODE_RGBA
69 rgba_composite_multiply_rgba
70 #else
71 rgba_composite_multiply_rgbu
72 #endif
73     (const fix15_short_t src_p[],
74      fix15_short_t dst_p[],
75      const fix15_short_t opac)
76 {
77     // http://www.w3.org/TR/SVGCompositing/#comp-op-multiply
78     const fix15_t Sa = fix15_mul(src_p[3], opac);
79     // Dca' = Sca*Dca + Sca*(1 - Da) + Dca*(1 - Sa)
80     //      = Dca * (1 + Sca - Sa)                   ; if Da == 1
81     //      = Dca * (1 + Sca - Sa) + Sca*(1 - Da)    ; otherwise
82     const fix15_t Sca0 = fix15_mul(src_p[0], opac);
83     const fix15_t Sca1 = fix15_mul(src_p[1], opac);
84     const fix15_t Sca2 = fix15_mul(src_p[2], opac);
85     dst_p[0] = fix15_mul(dst_p[0], fix15_one + Sca0 - Sa);
86     dst_p[1] = fix15_mul(dst_p[1], fix15_one + Sca1 - Sa);
87     dst_p[2] = fix15_mul(dst_p[2], fix15_one + Sca2 - Sa);
88 #ifdef COMPOSITE_MODE_RGBA
89     const fix15_t Da = dst_p[3];
90     dst_p[0] += fix15_mul(Sca0, fix15_one - Da);
91     dst_p[1] += fix15_mul(Sca1, fix15_one - Da);
92     dst_p[2] += fix15_mul(Sca2, fix15_one - Da);
93     dst_p[3] = Sa + Da - fix15_mul(Sa, Da);
94 #endif
95 }
96
97
98 inline void
99 #ifdef COMPOSITE_MODE_RGBA
100 rgba_composite_screen_rgba
101 #else
102 rgba_composite_screen_rgbu
103 #endif
104     (const fix15_short_t src_p[],
105      fix15_short_t dst_p[],
106      const fix15_short_t opac)
107 {
108     // http://www.w3.org/TR/SVGCompositing/#comp-op-multiply
109     // Dca' = (Sca*Da + Dca*Sa - Sca*Dca) + Sca*(1 - Da) + Dca*(1 - Sa)
110     //      = Sca + Dca - Sca*Dca
111
112     const fix15_t Sca0 = fix15_mul(src_p[0], opac);
113     const fix15_t Sca1 = fix15_mul(src_p[1], opac);
114     const fix15_t Sca2 = fix15_mul(src_p[2], opac);
115     dst_p[0] += Sca0 - fix15_mul(Sca0, dst_p[0]);
116     dst_p[1] += Sca1 - fix15_mul(Sca1, dst_p[1]);
117     dst_p[2] += Sca2 - fix15_mul(Sca2, dst_p[2]);
118 #ifdef COMPOSITE_MODE_RGBA
119     const fix15_t Sa = fix15_mul(src_p[3], opac);
120     const fix15_t Da = fix15_mul(dst_p[3], opac);
121     dst_p[3] = Sa + Da - fix15_mul(Sa, Da);
122 #endif
123 }
124
125
126 inline void
127 #ifdef COMPOSITE_MODE_RGBA
128 rgba_composite_overlay_rgba
129 #else
130 rgba_composite_overlay_rgbu
131 #endif
132     (const fix15_short_t src_p[],
133      fix15_short_t dst_p[],
134      const fix15_short_t opac)
135 {
136     const fix15_t Sa = fix15_mul(src_p[3], opac);
137     const fix15_t one_minus_Sa = fix15_one - Sa;
138 #ifdef COMPOSITE_MODE_RGBA
139     const fix15_t Da = dst_p[3];
140     const fix15_t one_minus_Da = fix15_one - Da;
141     const fix15_t SaDa = fix15_mul(Sa, Da);
142 #else
143     const fix15_t Da = fix15_one;
144 #endif
145     // http://www.w3.org/TR/SVGCompositing/#comp-op-overlay
146     // if 2 * Dca <= Da
147     //   Dca' = 2*Sca*Dca + Sca*(1 - Da) + Dca*(1 - Sa)
148     // otherwise
149     //   Dca' = Sa*Da - 2*(Da - Dca)*(Sa - Sca) + Sca*(1 - Da) + Dca*(1 - Sa)
150     //        = Sca*(1 + Da) + Dca*(1 + Sa) - 2*Dca*Sca - Da*Sa
151     for (int c=0; c<3; c++) {
152         const fix15_t Dca = dst_p[c];
153         const fix15_t twoDca = Dca * 2;
154         const fix15_t Sca = fix15_mul(src_p[c], opac);
155         fix15_t Dca_out = 0;
156         if (twoDca <= Da) {
157             Dca_out = fix15_sumprods( twoDca, Sca,  Dca, one_minus_Sa );
158 #ifdef COMPOSITE_MODE_RGBA
159             // (1-Da) != 0
160             Dca_out += fix15_mul(Sca, one_minus_Da);
161 #endif //COMPOSITE_MODE_RGBA
162         }
163         else {
164 #ifdef COMPOSITE_MODE_RGBA
165             Dca_out = fix15_sumprods(Sca, fix15_one+Da,  Dca, fix15_one+Sa)
166                     - fix15_mul(twoDca, Sca)
167                     - SaDa;
168 #else
169             // Da == 1
170             Dca_out = Sca * 2
171                     + fix15_mul(Dca, fix15_one+Sa)
172                     - fix15_mul(twoDca, Sca)
173                     - Sa;
174 #endif //COMPOSITE_MODE_RGBA
175         }
176         dst_p[c] = fix15_short_clamp(Dca_out);
177 #ifdef HEAVY_DEBUG
178         assert(dst_p[c] <= (1<<15));
179         assert(src_p[c] <= (1<<15));
180 #endif //HEAVY_DEBUG
181     }
182
183 #ifdef COMPOSITE_MODE_RGBA
184     dst_p[3] = Sa + Da - SaDa;
185 #ifdef HEAVY_DEBUG
186     assert(src_p[0] <= src_p[3]);
187     assert(dst_p[0] <= dst_p[3]);
188     assert(src_p[1] <= src_p[3]);
189     assert(dst_p[1] <= dst_p[3]);
190     assert(src_p[2] <= src_p[3]);
191     assert(dst_p[2] <= dst_p[3]);
192 #endif //HEAVY_DEBUG
193 #endif //COMPOSITE_MODE_RGBA
194 }
195
196
197 inline void
198 #ifdef COMPOSITE_MODE_RGBA
199 rgba_composite_hard_light_rgba
200 #else
201 rgba_composite_hard_light_rgbu
202 #endif
203     (const fix15_short_t src_p[],
204      fix15_short_t dst_p[],
205      const fix15_short_t opac)
206 {
207     const fix15_t Sa = fix15_mul(src_p[3], opac);
208     const fix15_t one_minus_Sa = fix15_one - Sa;
209 #ifdef COMPOSITE_MODE_RGBA
210     const fix15_t Da = dst_p[3];
211     const fix15_t one_minus_Da = fix15_one - Da;
212     const fix15_t SaDa = fix15_mul(Sa, Da);
213 #endif
214     // From http://www.w3.org/TR/SVGCompositing/#comp-op-hard-light --
215     // if 2 * Sca <= Da
216     //   Dca' = 2*Sca*Dca + Sca*(1 - Da) + Dca*(1 - Sa)
217     // otherwise
218     //   Dca' = Sa*Da - 2*(Da - Dca)*(Sa - Sca) + Sca*(1 - Da) + Dca*(1 - Sa)
219     //        = Sca*(1 + Da) + Dca*(1 + Sa) - 2*Dca*Sca - Da*Sa
220     //
221     // Identical to Overlay, but with a different test.
222     for (int c=0; c<3; c++) {
223         const fix15_t Dca = dst_p[c];
224         const fix15_t twoDca = Dca * 2;
225         const fix15_t Sca = fix15_mul(src_p[c], opac);
226         const fix15_t twoSca = Sca * 2;
227         fix15_t Dca_out = 0;
228         if (twoSca <= Sa) {
229             Dca_out = fix15_mul(twoDca, Sca)
230                     + fix15_mul(Dca, one_minus_Sa);
231 #ifdef COMPOSITE_MODE_RGBA
232             // (1-Da) != 0
233             Dca_out += fix15_mul(Sca, one_minus_Da);
234 #endif //COMPOSITE_MODE_RGBA
235         }
236         else {
237             // Dca' = Sca*(1 + Da) + Dca*(1 + Sa) - 2*Dca*Sca - Da*Sa
238 #ifdef COMPOSITE_MODE_RGBA
239             Dca_out = fix15_mul(Sca, fix15_one + Da)
240                     + fix15_mul(Dca, fix15_one + Sa)
241                     - fix15_mul(twoDca, Sca)
242                     - SaDa;
243 #else
244             // Da == 1
245             Dca_out = twoSca
246                     + fix15_mul(Dca, fix15_one + Sa)
247                     - fix15_mul(twoDca, Sca)
248                     - Sa;
249 #endif //COMPOSITE_MODE_RGBA
250         }
251         dst_p[c] = fix15_short_clamp(Dca_out);
252 #ifdef HEAVY_DEBUG
253         assert(dst_p[c] <= fix15_one);
254         assert(src_p[c] <= fix15_one);
255 #endif //HEAVY_DEBUG
256     }
257
258 #ifdef COMPOSITE_MODE_RGBA
259     dst_p[3] = Sa + Da - SaDa;
260 #ifdef HEAVY_DEBUG
261     assert(src_p[0] <= src_p[3]);
262     assert(dst_p[0] <= dst_p[3]);
263     assert(src_p[1] <= src_p[3]);
264     assert(dst_p[1] <= dst_p[3]);
265     assert(src_p[2] <= src_p[3]);
266     assert(dst_p[2] <= dst_p[3]);
267 #endif //HEAVY_DEBUG
268 #endif //COMPOSITE_MODE_RGBA
269 }
270
271
272 inline void
273 #ifdef COMPOSITE_MODE_RGBA
274 rgba_composite_soft_light_rgba
275 #else
276 rgba_composite_soft_light_rgbu
277 #endif
278     (const fix15_short_t src_p[],
279      fix15_short_t dst_p[],
280      const fix15_short_t opac)
281 {
282     /* <URI:https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html
283             #blendingsoftlight > */
284
285     // Leave the backdrop alone if the source is fully transparent
286     const fix15_t a_s = fix15_mul(src_p[3], opac);
287     if (a_s == 0) {
288         return;
289     }
290
291     // If the backdrop (dst) is fully transparent, it becomes the
292     // source times opacity.
293 #ifdef COMPOSITE_MODE_RGBA
294     const fix15_t a_b = dst_p[3];
295     if (a_b == 0) {
296         dst_p[0] = fix15_short_clamp(fix15_mul(opac, src_p[0]));
297         dst_p[1] = fix15_short_clamp(fix15_mul(opac, src_p[1]));
298         dst_p[2] = fix15_short_clamp(fix15_mul(opac, src_p[2]));
299         dst_p[3] = a_s;
300         return;
301     }
302 #else
303     const fix15_t a_b = fix15_one;
304 #endif //COMPOSITE_MODE_RGBA
305
306 #ifdef HEAVY_DEBUG
307     // Underflow is a possibility here
308     assert(a_b <= fix15_one);
309     assert(a_s <= fix15_one);
310     // Confirm we guarded against divisions by zero below
311     assert(a_b > 0);
312     assert(a_s > 0);
313 #endif
314
315     for (int i=0; i<3; ++i) {
316         // De-premultiplied input components from premultiplied
317         const fix15_t aC_s = fix15_mul(opac, src_p[i]);
318         const fix15_t aC_b = dst_p[i];
319         const fix15_t C_s = fix15_div(aC_s, a_s);
320         const fix15_t C_b = fix15_div(aC_b, a_b);
321
322         // The guts of it, a blending function B(C_b, C_s) whose output is
323         // used as the input to a regular src-over operation.
324         fix15_t B = 0;
325         const fix15_t two_C_s = C_s << 1;
326         if (two_C_s <= fix15_one) {  // i.e. C_s < 0.5
327             B = fix15_one - fix15_mul(fix15_one - two_C_s,
328                                       fix15_one - C_b);
329             B = fix15_mul(B, C_b);
330         }
331         else {
332             fix15_t D = 0;
333             const fix15_t four_C_b = C_b << 2;
334             if (four_C_b <= fix15_one) {
335                 const fix15_t C_b_squared = fix15_mul(C_b, C_b);
336                 D = four_C_b; /* which is always greater than... */
337                 D += 16 * fix15_mul(C_b_squared, C_b);
338                 D -= 12 * C_b_squared;
339                 /* ... in the range 0 <= C_b <= 0.25 */
340             }
341             else {
342                 D = fix15_sqrt(C_b);
343             }
344 #ifdef HEAVY_DEBUG
345             /* Guard against underflows */
346             assert(2*C_s > fix15_one);
347             assert(D >= C_b);
348 #endif
349             B = C_b + fix15_mul(2*C_s - fix15_one /* 2*C_s > 1 */,
350                                 D - C_b           /* D >= C_b */  );
351         }
352
353         // Composite a premultiplied output component as src-over.
354         fix15_t aC_o = fix15_mul(fix15_one - a_b, aC_s)
355                      + fix15_mul(fix15_one - a_s, aC_b)
356                      + fix15_mul(B, fix15_mul(a_s, a_b));
357         dst_p[i] = fix15_short_clamp(aC_o);
358     }
359
360 #ifdef COMPOSITE_MODE_RGBA
361     dst_p[3] = fix15_short_clamp(a_s + a_b - fix15_mul(a_s, a_b));
362 #ifdef HEAVY_DEBUG
363     assert(src_p[0] <= src_p[3]);
364     assert(dst_p[0] <= dst_p[3]);
365     assert(src_p[1] <= src_p[3]);
366     assert(dst_p[1] <= dst_p[3]);
367     assert(src_p[2] <= src_p[3]);
368     assert(dst_p[2] <= dst_p[3]);
369 #endif //HEAVY_DEBUG
370 #endif
371 }
372
373
374 inline void
375 #ifdef COMPOSITE_MODE_RGBA
376 rgba_composite_color_dodge_rgba
377 #else
378 rgba_composite_color_dodge_rgbu
379 #endif
380     (const fix15_short_t src_p[],
381      fix15_short_t dst_p[],
382      const fix15_short_t opac)
383 {
384     // http://www.w3.org/TR/SVGCompositing/#comp-op-color-dodge
385     const fix15_t Sa = fix15_mul(src_p[3], opac);
386     const fix15_t one_minus_Sa = fix15_one - Sa;
387 #ifdef COMPOSITE_MODE_RGBA
388     const fix15_t Da = dst_p[3];
389     const fix15_t one_minus_Da = fix15_one - Da;
390     const fix15_t SaDa = fix15_mul(Sa, Da);
391 #else
392     const fix15_t SaDa = Sa;
393 #endif
394
395     for (int c=0; c<3; c++) {
396         const fix15_t Sca = fix15_mul(src_p[c], opac);
397         const fix15_t Dca = dst_p[c];
398         fix15_t Dca_out = 0;
399         if (Sca >= Sa) {
400             if (Dca == 0) {
401                 // Sca == Sa and Dca == 0
402                 //  Dca' = Sca*(1 - Da) + Dca*(1 - Sa)
403                 //       = Sca*(1 - Da)
404 #ifdef COMPOSITE_MODE_RGBA
405                 Dca_out = fix15_mul(Sca, one_minus_Da);
406 #else
407                 Dca_out = 0;
408 #endif //COMPOSITE_MODE_RGBA
409             }
410             else {
411                 // otherwise if Sca == Sa and Dca > 0
412                 //  Dca' = Sa*Da + Sca*(1 - Da) + Dca*(1 - Sa)
413                 //       = Sca*Da + Sca*(1 - Da) + Dca*(1 - Sa)
414                 //       = Sca*(Da + 1 - Da) + Dca*(1 - Sa)
415                 //       = Sca + Dca*(1 - Sa)
416                 Dca_out = Sca + fix15_mul(Dca, one_minus_Sa);
417             }
418         }
419         else {
420             // Sca < Sa
421             //    Dca' = Sa*Da * min(1, m) + Sca*(1 - Da) + Dca*(1 - Sa)
422             // Where
423             //       m = Dca/Da * Sa/(Sa - Sca)
424             //         = (Dca*Sa) / (Da*(Sa - Sca))
425             fix15_t m = 0;
426 #ifdef COMPOSITE_MODE_RGBA
427             if (Da != 0) {
428                 m = fix15_div(fix15_mul(Dca, Sa), fix15_mul(Da, Sa - Sca));
429             }
430             Dca_out = fix15_sumprods(Sca, one_minus_Da, Dca, one_minus_Sa);
431 #else
432             // Da == 1
433             m = fix15_div(fix15_mul(Dca, Sa), Sa - Sca);
434             Dca_out = fix15_mul(Dca, one_minus_Sa);
435 #endif
436             if (m < fix15_one) {
437                 Dca_out += fix15_mul(SaDa, m);
438             }
439             else {
440                 Dca_out += SaDa;
441             }
442         }
443         dst_p[c] = fix15_short_clamp(Dca_out);
444 #ifdef HEAVY_DEBUG
445         assert(dst_p[c] <= fix15_one);
446         assert(src_p[c] <= fix15_one);
447 #endif
448     }
449
450 #ifdef COMPOSITE_MODE_RGBA
451     // Da'  = Sa + Da - Sa*Da
452     dst_p[3] = fix15_short_clamp(Sa + Da - SaDa);
453 #ifdef HEAVY_DEBUG
454     assert(src_p[0] <= src_p[3]);
455     assert(dst_p[0] <= dst_p[3]);
456     assert(src_p[1] <= src_p[3]);
457     assert(dst_p[1] <= dst_p[3]);
458     assert(src_p[2] <= src_p[3]);
459     assert(dst_p[2] <= dst_p[3]);
460 #endif //HEAVY_DEBUG
461 #endif //COMPOSITE_MODE_RGBA
462
463 #ifdef HEAVY_DEBUG
464     assert(dst_p[3] <= fix15_one);
465     assert(src_p[3] <= fix15_one);
466 #endif
467 }
468
469
470
471 inline void
472 #ifdef COMPOSITE_MODE_RGBA
473 rgba_composite_color_burn_rgba
474 #else
475 rgba_composite_color_burn_rgbu
476 #endif
477     (const fix15_short_t src_p[],
478      fix15_short_t dst_p[],
479      const fix15_short_t opac)
480 {
481     // http://www.w3.org/TR/SVGCompositing/#comp-op-color-burn
482     const fix15_t Sa = fix15_mul(src_p[3], opac);
483     const fix15_t one_minus_Sa = fix15_one - Sa;
484 #ifdef COMPOSITE_MODE_RGBA
485     const fix15_t Da = dst_p[3];
486     const fix15_t one_minus_Da = fix15_one - Da;
487 #else
488     const fix15_t Da = fix15_one;
489 #endif
490     for (int c=0; c<3; c++) {
491         const fix15_t Sca = fix15_mul(src_p[c], opac);
492         const fix15_t Dca = dst_p[c];
493         if (Sca == 0) {
494             //if Sca == 0 and Dca == Da
495             //  Dca' = Sa*Da + Sca*(1 - Da) + Dca*(1 - Sa)
496             //       = Sa*Dca + Dca*(1 - Sa)
497             //       = Sa*Dca + Dca - Sa*Dca
498             //       = Dca
499             if (Dca != Da) {
500                 //otherwise (when Sca == 0)
501                 //  Dca' = Sca*(1 - Da) + Dca*(1 - Sa)
502                 //       = Dca*(1 - Sa)
503                 dst_p[c] = fix15_short_clamp(fix15_mul(Dca, one_minus_Sa));
504             }
505         }
506         else {
507 #ifdef HEAVY_DEBUG
508             assert(Sca <= fix15_one);
509             assert(Sca > 0);
510 #endif
511             //otherwise if Sca > 0
512             //  let i = Sca*(1 - Da) + Dca*(1 - Sa)
513             //  let m = (1 - Dca/Da) * Sa/Sca
514             //
515             //  Dca' = Sa*Da - Sa*Da * min(1, (1 - Dca/Da) * Sa/Sca) + i
516             //       = Sa*Da * (1 - min(1, (1 - Dca/Da) * Sa/Sca)) + i
517
518 #ifdef COMPOSITE_MODE_RGBA
519             fix15_t res = fix15_sumprods(Sca, one_minus_Da, Dca, one_minus_Sa);
520             if (Da > 0) {
521                 const fix15_t m = fix15_div(fix15_mul(
522                                               fix15_one - fix15_div(Dca, Da),
523                                               Sa),
524                                             Sca);
525                 if (m < fix15_one) {
526                     res += fix15_mul(fix15_mul(Sa, Da), fix15_one - m);
527                 }
528             }
529 #else
530             fix15_t res = fix15_mul(Dca, one_minus_Sa);
531             const fix15_t m = fix15_div(fix15_mul(
532                                           fix15_one - Dca,
533                                           Sa),
534                                         Sca);
535             if (m < fix15_one) {
536                 res += fix15_mul(Sa, fix15_one - m);
537             }
538 #endif
539             dst_p[c] = fix15_short_clamp(res);
540         }
541 #ifdef HEAVY_DEBUG
542         assert(dst_p[c] <= fix15_one);
543         assert(src_p[c] <= fix15_one);
544 #endif
545     }
546
547 #ifdef COMPOSITE_MODE_RGBA
548     // Da'  = Sa + Da - Sa*Da
549     dst_p[3] = fix15_short_clamp(Sa + Da
550                                  - fix15_mul(Sa, Da));
551 #ifdef HEAVY_DEBUG
552     assert(src_p[0] <= src_p[3]);
553     assert(dst_p[0] <= dst_p[3]);
554     assert(src_p[1] <= src_p[3]);
555     assert(dst_p[1] <= dst_p[3]);
556     assert(src_p[2] <= src_p[3]);
557     assert(dst_p[2] <= dst_p[3]);
558 #endif //HEAVY_DEBUG
559 #endif //COMPOSITE_MODE_RGBA
560
561 #ifdef HEAVY_DEBUG
562     assert(dst_p[3] <= fix15_one);
563     assert(src_p[3] <= fix15_one);
564 #endif
565 }
566
567
568
569 // Non-separable blend modes.
570 // http://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html
571 // Same as the ones in Cairo, and in the PDF specs.
572
573
574 #ifndef __HAVE_NONSEP_MAPFUNC
575 #define __HAVE_NONSEP_MAPFUNC
576 typedef void (*_nonseparable_mapfunc) (const uint16_t /* src red in */,
577                                        const uint16_t /* src green in */,
578                                        const uint16_t /* src blue in */,
579                                        uint16_t * /* dst red in/out */,
580                                        uint16_t * /* dst green in/out */,
581                                        uint16_t * /* dst blue in/out */);
582 #endif // __HAVE_NONSEP_MAPFUNC
583
584
585
586 static inline void
587 #ifdef COMPOSITE_MODE_RGBA
588 _rgba_composite_nonseparable_over_rgba
589 #else
590 _rgba_composite_nonseparable_over_rgbu
591 #endif
592     (const uint16_t src_p[],
593      uint16_t dst_p[],
594      const uint16_t opac,
595      const _nonseparable_mapfunc mapfunc)
596 {
597     uint16_t src_r, src_g, src_b;
598     src_r = src_g = src_b = 0;
599     const uint16_t src_a = src_p[3];
600     if (src_a == 0)
601         return;
602
603     // De-premult
604     src_r = ((1<<15)*((uint32_t)src_p[0])) / src_a;
605     src_g = ((1<<15)*((uint32_t)src_p[1])) / src_a;
606     src_b = ((1<<15)*((uint32_t)src_p[2])) / src_a;
607
608     // Create a temporary "source" colour based on dst_p, colorized in
609     // the desired way by src_p.
610
611     uint16_t tmp_p[4] = { dst_p[0], dst_p[1], dst_p[2], src_a };
612     mapfunc ( src_r, src_g, src_b,
613               &tmp_p[0], &tmp_p[1], &tmp_p[2] );
614
615     // Re-premult
616     tmp_p[0] = ((uint32_t) tmp_p[0]) * src_a / (1<<15);
617     tmp_p[1] = ((uint32_t) tmp_p[1]) * src_a / (1<<15);
618     tmp_p[2] = ((uint32_t) tmp_p[2]) * src_a / (1<<15);
619
620     // Combine it in the normal way with the destination layer.
621 #ifdef COMPOSITE_MODE_RGBA
622     rgba_composite_src_over_rgba (tmp_p, dst_p, opac);
623 #else
624     rgba_composite_src_over_rgbu (tmp_p, dst_p, opac);
625 #endif
626 }
627
628
629
630 #ifndef __HAVE_SVGFX_BLENDS
631 #define __HAVE_SVGFX_BLENDS
632
633 // Luma/luminance coefficients, from the spec linked above as of Mercurial
634 // revision fc58b9389b07, dated Thu Jul 26 07:27:58 2012. They're similar, but
635 // not identical to, the ones defined in Rec. ITU-R BT.601-7 Section 2.5.1.
636
637 static const float SVGFX_LUM_R_COEFF = 0.3;
638 static const float SVGFX_LUM_G_COEFF = 0.59;
639 static const float SVGFX_LUM_B_COEFF = 0.11;
640
641
642 // Returns the luma/luminance of an RGB triple, expressed as scaled ints.
643
644 #define svgfx_lum(r,g,b) \
645    (  (r) * (uint16_t)(SVGFX_LUM_R_COEFF * (1<<15)) \
646     + (g) * (uint16_t)(SVGFX_LUM_G_COEFF * (1<<15)) \
647     + (b) * (uint16_t)(SVGFX_LUM_B_COEFF * (1<<15))  )
648
649
650 #ifndef MIN3
651 #define MIN3(a,b,c) ( (a)<(b) ? MIN((a), (c)) : MIN((b), (c)) )
652 #endif
653 #ifndef MAX3
654 #define MAX3(a,b,c) ( (a)>(b) ? MAX((a), (c)) : MAX((b), (c)) )
655 #endif
656
657
658
659 // Sets the target's luma/luminance to that of the input, retaining its hue
660 // angle and clipping the saturation if necessary.
661 //
662 //
663 // All params are scaled ints having factor 2**-15, and must not store
664 // premultiplied alpha.
665
666
667 inline void
668 svgfx_blend_color(const uint16_t r0,
669                   const uint16_t g0,
670                   const uint16_t b0,
671                   uint16_t *r1,
672                   uint16_t *g1,
673                   uint16_t *b1)
674 {
675     // Spec: SetLum()
676     // Colours potentially can go out of band to both sides, hence the
677     // temporary representation inflation.
678     const uint16_t lum1 = svgfx_lum(*r1, *g1, *b1) / (1<<15);
679     const uint16_t lum0 = svgfx_lum(r0, g0, b0) / (1<<15);
680     const int16_t diff = lum1 - lum0;
681     int32_t r = r0 + diff;
682     int32_t g = g0 + diff;
683     int32_t b = b0 + diff;
684
685     // Spec: ClipColor()
686     // Trim out of band values, retaining lum.
687     int32_t lum = svgfx_lum(r, g, b) / (1<<15);
688     int32_t cmin = MIN3(r, g, b);
689     int32_t cmax = MAX3(r, g, b);
690
691     if (cmin < 0) {
692         r = lum + (((r - lum) * lum) / (lum - cmin));
693         g = lum + (((g - lum) * lum) / (lum - cmin));
694         b = lum + (((b - lum) * lum) / (lum - cmin));
695     }
696     if (cmax > (1<<15)) {
697         r = lum + (((r - lum) * ((1<<15)-lum)) / (cmax - lum));
698         g = lum + (((g - lum) * ((1<<15)-lum)) / (cmax - lum));
699         b = lum + (((b - lum) * ((1<<15)-lum)) / (cmax - lum));
700     }
701 #ifdef HEAVY_DEBUG
702     assert((0 <= r) && (r <= (1<<15)));
703     assert((0 <= g) && (g <= (1<<15)));
704     assert((0 <= b) && (b <= (1<<15)));
705 #endif
706
707     *r1 = r;
708     *g1 = g;
709     *b1 = b;
710 }
711
712
713
714 // The two modes are defined to be symmetrical (whether or not that's really a
715 // good idea artistically). The Luminosity blend mode suffers from contrast
716 // issues, but it's likely to be used less by artists.
717
718 inline void
719 svgfx_blend_luminosity(const uint16_t r0,
720                        const uint16_t g0,
721                        const uint16_t b0,
722                        uint16_t *r1,
723                        uint16_t *g1,
724                        uint16_t *b1)
725 {
726     uint16_t r = r0;
727     uint16_t g = g0;
728     uint16_t b = b0;
729     svgfx_blend_color(*r1, *g1, *b1, &r, &g, &b);
730     *r1 = r;
731     *g1 = g;
732     *b1 = b;
733 }
734
735
736 #endif // __HAVE_SVGFX_BLENDS
737
738
739
740 static inline void
741 #ifdef COMPOSITE_MODE_RGBA
742 rgba_composite_color_rgba
743 #else
744 rgba_composite_color_rgbu
745 #endif
746     (const uint16_t src_p[],
747      uint16_t dst_p[],
748      const uint16_t opac)
749 {
750 #ifdef COMPOSITE_MODE_RGBA
751 _rgba_composite_nonseparable_over_rgba
752 #else
753 _rgba_composite_nonseparable_over_rgbu
754 #endif
755                                       (src_p, dst_p, opac,
756                                        svgfx_blend_color);
757 }
758
759
760 static inline void
761 #ifdef COMPOSITE_MODE_RGBA
762 rgba_composite_luminosity_rgba
763 #else
764 rgba_composite_luminosity_rgbu
765 #endif
766     (const uint16_t src_p[],
767      uint16_t dst_p[],
768      const uint16_t opac)
769 {
770 #ifdef COMPOSITE_MODE_RGBA
771 _rgba_composite_nonseparable_over_rgba
772 #else
773 _rgba_composite_nonseparable_over_rgbu
774 #endif
775                                       (src_p, dst_p, opac,
776                                        svgfx_blend_luminosity);
777 }
778
779