1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
4 * Copyright (C) 2013-2018 INRIA
5 *
6 * openfx-supportext is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * openfx-supportext is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with openfx-supportext. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
18 * ***** END LICENSE BLOCK ***** */
19
20 /*
21 * OFX Merge helpers
22 */
23
24 #ifndef Misc_Merging_helper_h
25 #define Misc_Merging_helper_h
26
27 #include <cmath>
28 #include <cfloat>
29 #include <algorithm>
30
31 #include "ofxsImageEffect.h"
32
33 #ifndef M_PI
34 #define M_PI 3.14159265358979323846264338327950288 /* pi */
35 #endif
36
37 namespace OFX {
38 // References:
39 //
40 // SVG Compositing Specification:
41 // http://www.w3.org/TR/SVGCompositing/
42 // PDF Reference v1.7:
43 // http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
44 // http://www.adobe.com/devnet/pdf/pdf_reference_archive.html
45 // Adobe photoshop blending modes:
46 // http://helpx.adobe.com/en/photoshop/using/blending-modes.html
47 // http://www.deepskycolors.com/archive/2010/04/21/formulas-for-Photoshop-blending-modes.html
48 // ImageMagick:
49 // http://www.imagemagick.org/Usage/compose/
50 //
51 // Note about the Soft-Light operation:
52 // Soft-light as implemented in Nuke comes from the SVG 2004 specification, which is wrong.
53 // In SVG 2004, 'Soft_Light' did not work as expected, producing a brightening for any non-gray shade
54 // image overlay.
55 // It was fixed in the March 2009 SVG specification, which was used for this implementation.
56
57 namespace MergeImages2D {
58 // please keep this long list sorted alphabetically
59 enum MergingFunctionEnum
60 {
61 eMergeATop = 0,
62 eMergeAverage,
63 eMergeColor,
64 eMergeColorBurn,
65 eMergeColorDodge,
66 eMergeConjointOver,
67 eMergeCopy,
68 eMergeDifference,
69 eMergeDisjointOver,
70 eMergeDivide,
71 eMergeExclusion,
72 eMergeFreeze,
73 eMergeFrom,
74 eMergeGeometric,
75 eMergeGrainExtract,
76 eMergeGrainMerge,
77 eMergeHardLight,
78 eMergeHue,
79 eMergeHypot,
80 eMergeIn,
81 //eMergeInterpolated,
82 eMergeLuminosity,
83 eMergeMask,
84 eMergeMatte,
85 eMergeMax,
86 eMergeMin,
87 eMergeMinus,
88 eMergeMultiply,
89 eMergeOut,
90 eMergeOver,
91 eMergeOverlay,
92 eMergePinLight,
93 eMergePlus,
94 eMergeReflect,
95 eMergeSaturation,
96 eMergeScreen,
97 eMergeSoftLight,
98 eMergeStencil,
99 eMergeUnder,
100 eMergeXOR,
101 };
102
103 inline bool
isMaskable(MergingFunctionEnum operation)104 isMaskable(MergingFunctionEnum operation)
105 {
106 switch (operation) {
107 case eMergeAverage:
108 case eMergeColorBurn:
109 case eMergeColorDodge:
110 case eMergeDifference:
111 case eMergeDivide:
112 case eMergeExclusion:
113 case eMergeFrom:
114 case eMergeFreeze:
115 case eMergeGeometric:
116 case eMergeGrainExtract:
117 case eMergeGrainMerge:
118 case eMergeHardLight:
119 case eMergeHypot:
120 //case eMergeInterpolated:
121 case eMergeMax:
122 case eMergeMin:
123 case eMergeMinus:
124 case eMergeMultiply:
125 case eMergeOverlay:
126 case eMergePinLight:
127 case eMergePlus:
128 case eMergeReflect:
129 case eMergeSoftLight:
130
131 return true;
132 case eMergeATop:
133 case eMergeConjointOver:
134 case eMergeCopy:
135 case eMergeDisjointOver:
136 case eMergeIn:
137 case eMergeMask:
138 case eMergeMatte:
139 case eMergeOut:
140 case eMergeOver:
141 case eMergeScreen:
142 case eMergeStencil:
143 case eMergeUnder:
144 case eMergeXOR:
145 case eMergeHue:
146 case eMergeSaturation:
147 case eMergeColor:
148 case eMergeLuminosity:
149
150 return false;
151 }
152
153 return true;
154 } // isMaskable
155
156 // if Aa is black and transparent, does the operator give Bb?
157 inline bool
isIdentityForBOnly(MergingFunctionEnum operation)158 isIdentityForBOnly(MergingFunctionEnum operation)
159 {
160 switch (operation) {
161 case eMergeATop: //"Ab + B(1 - a) (a.k.a. src-atop)";
162 case eMergeExclusion: //"A+B-2AB";
163 case eMergeMatte: //"Aa + B(1-a) (unpremultiplied over)";
164 case eMergeOver: //"A+B(1-a) (a.k.a. src-over)";
165 case eMergePlus: //"A+B (a.k.a. add)";
166 case eMergeScreen: //"A+B-AB if A or B <= 1, otherwise max(A, B)";
167 case eMergeStencil: //"B(1-a) (a.k.a. dst-out)";
168 case eMergeUnder: //"A(1-b)+B (a.k.a. dst-over)";
169 case eMergeXOR: //"A(1-b)+B(1-a)";
170
171 return true;
172
173 default:
174
175 return false;
176 } // switch
177 } // isIdentityForBOnly
178
179 // is the operator separable for R,G,B components, or do they have to be processed simultaneously?
180 inline bool
isSeparable(MergingFunctionEnum operation)181 isSeparable(MergingFunctionEnum operation)
182 {
183 switch (operation) {
184 case eMergeHue:
185 case eMergeSaturation:
186 case eMergeColor:
187 case eMergeLuminosity:
188
189 return false;
190
191 default:
192
193 return true;
194 }
195 }
196
197 inline std::string
getOperationString(MergingFunctionEnum operation)198 getOperationString(MergingFunctionEnum operation)
199 {
200 switch (operation) {
201 case eMergeATop:
202
203 return "atop";
204
205 case eMergeAverage:
206
207 return "average";
208
209 case eMergeColor:
210
211 return "color";
212
213 case eMergeColorBurn:
214
215 return "color-burn";
216
217 case eMergeColorDodge:
218
219 return "color-dodge";
220
221 case eMergeConjointOver:
222
223 return "conjoint-over";
224
225 case eMergeCopy:
226
227 return "copy";
228
229 case eMergeDifference:
230
231 return "difference";
232
233 case eMergeDisjointOver:
234
235 return "disjoint-over";
236
237 case eMergeDivide:
238
239 return "divide";
240
241 case eMergeExclusion:
242
243 return "exclusion";
244
245 case eMergeFreeze:
246
247 return "freeze";
248
249 case eMergeFrom:
250
251 return "from";
252
253 case eMergeGeometric:
254
255 return "geometric";
256
257 case eMergeGrainExtract:
258
259 return "grain-extract";
260
261 case eMergeGrainMerge:
262
263 return "grain-merge";
264
265 case eMergeHardLight:
266
267 return "hard-light";
268
269 case eMergeHue:
270
271 return "hue";
272
273 case eMergeHypot:
274
275 return "hypot";
276
277 case eMergeIn:
278
279 return "in";
280
281 //case eMergeInterpolated:
282 // return "interpolated";
283
284 case eMergeLuminosity:
285
286 return "luminosity";
287
288 case eMergeMask:
289
290 return "mask";
291
292 case eMergeMatte:
293
294 return "matte";
295
296 case eMergeMax:
297
298 return "max";
299
300 case eMergeMin:
301
302 return "min";
303
304 case eMergeMinus:
305
306 return "minus";
307
308 case eMergeMultiply:
309
310 return "multiply";
311
312 case eMergeOut:
313
314 return "out";
315
316 case eMergeOver:
317
318 return "over";
319
320 case eMergeOverlay:
321
322 return "overlay";
323
324 case eMergePinLight:
325
326 return "pinlight";
327
328 case eMergePlus:
329
330 return "plus";
331
332 case eMergeReflect:
333
334 return "reflect";
335
336 case eMergeSaturation:
337
338 return "saturation";
339
340 case eMergeScreen:
341
342 return "screen";
343
344 case eMergeSoftLight:
345
346 return "soft-light";
347
348 case eMergeStencil:
349
350 return "stencil";
351
352 case eMergeUnder:
353
354 return "under";
355
356 case eMergeXOR:
357
358 return "xor";
359 } // switch
360
361 return "unknown";
362 } // getOperationString
363
364 inline std::string
getOperationDescription(MergingFunctionEnum operation)365 getOperationDescription(MergingFunctionEnum operation)
366 {
367 switch (operation) {
368 case eMergeATop:
369
370 return "Ab + B(1 - a) (a.k.a. src-atop)";
371
372 case eMergeAverage:
373
374 return "(A + B) / 2";
375
376 case eMergeColor:
377
378 return "SetLum(A, Lum(B))";
379
380 case eMergeColorBurn:
381
382 return "darken B towards A";
383
384 case eMergeColorDodge:
385
386 return "brighten B towards A";
387
388 case eMergeConjointOver:
389
390 return "A + B(1-a)/b, A if a > b";
391
392 case eMergeCopy:
393
394 return "A (a.k.a. src)";
395
396 case eMergeDifference:
397
398 return "abs(A-B) (a.k.a. absminus)";
399
400 case eMergeDisjointOver:
401
402 return "A+B(1-a)/b, A+B if a+b < 1";
403
404 case eMergeDivide:
405
406 return "A/B, 0 if A < 0 and B < 0";
407
408 case eMergeExclusion:
409
410 return "A+B-2AB";
411
412 case eMergeFreeze:
413
414 return "1-sqrt(1-A)/B";
415
416 case eMergeFrom:
417
418 return "B-A (a.k.a. subtract)";
419
420 case eMergeGeometric:
421
422 return "2AB/(A+B)";
423
424 case eMergeGrainExtract:
425
426 return "B - A + 0.5";
427
428 case eMergeGrainMerge:
429
430 return "B + A - 0.5";
431
432 case eMergeHardLight:
433
434 return "multiply if A < 0.5, screen if A > 0.5";
435
436 case eMergeHue:
437
438 return "SetLum(SetSat(A, Sat(B)), Lum(B))";
439
440 case eMergeHypot:
441
442 return "sqrt(A*A+B*B)";
443
444 case eMergeIn:
445
446 return "Ab (a.k.a. src-in)";
447
448 //case eMergeInterpolated:
449 // return "(like average but better and slower)";
450
451 case eMergeLuminosity:
452
453 return "SetLum(B, Lum(A))";
454
455 case eMergeMask:
456
457 return "Ba (a.k.a dst-in)";
458
459 case eMergeMatte:
460
461 return "Aa + B(1-a) (unpremultiplied over)";
462
463 case eMergeMax:
464
465 return "max(A, B) (a.k.a. lighten only)";
466
467 case eMergeMin:
468
469 return "min(A, B) (a.k.a. darken only)";
470
471 case eMergeMinus:
472
473 return "A-B";
474
475 case eMergeMultiply:
476
477 return "AB, 0 if A < 0 and B < 0";
478
479 case eMergeOut:
480
481 return "A(1-b) (a.k.a. src-out)";
482
483 case eMergeOver:
484
485 return "A+B(1-a) (a.k.a. src-over)";
486
487 case eMergeOverlay:
488
489 return "multiply if B < 0.5, screen if B > 0.5";
490
491 case eMergePinLight:
492
493 return "if B >= 0.5 then max(A, 2*B - 1), min(A, B * 2.0 ) else";
494
495 case eMergePlus:
496
497 return "A+B (a.k.a. add)";
498
499 case eMergeReflect:
500
501 return "A*A / (1 - B)";
502
503 case eMergeSaturation:
504
505 return "SetLum(SetSat(B, Sat(A)), Lum(B))";
506
507 case eMergeScreen:
508
509 return "A+B-AB if A or B <= 1, otherwise max(A, B)";
510
511 case eMergeSoftLight:
512
513 return "burn-in if A < 0.5, lighten if A > 0.5";
514
515 case eMergeStencil:
516
517 return "B(1-a) (a.k.a. dst-out)";
518
519 case eMergeUnder:
520
521 return "A(1-b)+B (a.k.a. dst-over)";
522
523 case eMergeXOR:
524
525 return "A(1-b)+B(1-a)";
526 } // switch
527
528 return "unknown";
529 } // getOperationString
530
531 inline std::string
getOperationHelp(MergingFunctionEnum operation,bool markdown)532 getOperationHelp(MergingFunctionEnum operation, bool markdown)
533 {
534 if (!markdown) {
535 return getOperationString(operation) + ": " + getOperationDescription(operation);
536 }
537 std::string escaped = getOperationString(operation) + ": ";
538 std::string plain = getOperationDescription(operation);
539 // the following chars must be backslash-escaped in markdown:
540 // \ backslash
541 // ` backtick
542 // * asterisk
543 // _ underscore
544 // {} curly braces
545 // [] square brackets
546 // () parentheses
547 // # hash mark
548 // + plus sign
549 // - minus sign (hyphen)
550 // . dot
551 // ! exclamation mark
552 for (unsigned i = 0; i < plain.size(); ++i) {
553 if (plain[i] == '\\' ||
554 plain[i] == '`' ||
555 plain[i] == '*' ||
556 plain[i] == '_' ||
557 plain[i] == '{' ||
558 plain[i] == '}' ||
559 plain[i] == '[' ||
560 plain[i] == ']' ||
561 plain[i] == '(' ||
562 plain[i] == ')' ||
563 plain[i] == '#' ||
564 plain[i] == '+' ||
565 plain[i] == '-' ||
566 plain[i] == '.' ||
567 plain[i] == '!') {
568 escaped += '\\';
569 }
570 escaped += plain[i];
571 }
572 return escaped;
573 }
574
575 inline std::string
getOperationGroupString(MergingFunctionEnum operation)576 getOperationGroupString(MergingFunctionEnum operation)
577 {
578 switch (operation) {
579 // Porter Duff Compositing Operators
580 // missing: clear
581 case eMergeCopy: // src
582 // missing: dst
583 case eMergeOver: // src-over
584 case eMergeUnder: // dst-over
585 case eMergeIn: // src-in
586 case eMergeMask: // dst-in
587 case eMergeOut: // src-out
588 case eMergeStencil: // dst-out
589 case eMergeATop: // src-atop
590 case eMergeXOR: // xor
591 return "Operator";
592
593 // Blend modes, see https://en.wikipedia.org/wiki/Blend_modes
594
595 // Multiply and screen
596 case eMergeMultiply:
597 case eMergeScreen:
598 case eMergeOverlay:
599 case eMergeHardLight:
600 case eMergeSoftLight:
601
602 return "Multiply and Screen";
603
604 // Dodge and burn
605 case eMergeColorDodge:
606 case eMergeColorBurn:
607 case eMergePinLight:
608 //case eMergeDifference:
609 case eMergeExclusion:
610
611 //case eMergeDivide:
612 return "Dodge and Burn";
613
614 // Simple arithmetic blend modes
615 case eMergeDivide:
616 case eMergePlus:
617 case eMergeFrom:
618 case eMergeMinus:
619 case eMergeDifference:
620 case eMergeMin:
621 case eMergeMax:
622
623 return "HSL";
624
625 // Hue, saturation, luminosity
626 case eMergeHue:
627 case eMergeSaturation:
628 case eMergeColor:
629 case eMergeLuminosity:
630
631 return "HSL";
632
633 case eMergeAverage:
634 case eMergeConjointOver:
635 case eMergeDisjointOver:
636 case eMergeFreeze:
637 case eMergeGeometric:
638 case eMergeGrainExtract:
639 case eMergeGrainMerge:
640 case eMergeHypot:
641 //case eMergeInterpolated:
642 case eMergeMatte:
643 case eMergeReflect:
644
645 return "Other";
646 } // switch
647
648 return "unknown";
649 } // getOperationGroupString
650
651 template <typename PIX>
652 PIX
averageFunctor(PIX A,PIX B)653 averageFunctor(PIX A,
654 PIX B)
655 {
656 return (A + B) / 2;
657 }
658
659 template <typename PIX>
660 PIX
copyFunctor(PIX A,PIX)661 copyFunctor(PIX A,
662 PIX /*B*/)
663 {
664 return A;
665 }
666
667 template <typename PIX>
668 PIX
plusFunctor(PIX A,PIX B)669 plusFunctor(PIX A,
670 PIX B)
671 {
672 return A + B;
673 }
674
675 template <typename PIX, int maxValue>
676 PIX
grainExtractFunctor(PIX A,PIX B)677 grainExtractFunctor(PIX A,
678 PIX B)
679 {
680 return (B - A + (PIX)maxValue / 2);
681 }
682
683 template <typename PIX, int maxValue>
684 PIX
grainMergeFunctor(PIX A,PIX B)685 grainMergeFunctor(PIX A,
686 PIX B)
687 {
688 return (B + A - (PIX)maxValue / 2);
689 }
690
691 template <typename PIX>
692 PIX
differenceFunctor(PIX A,PIX B)693 differenceFunctor(PIX A,
694 PIX B)
695 {
696 return std::abs(A - B);
697 }
698
699 template <typename PIX>
700 PIX
divideFunctor(PIX A,PIX B)701 divideFunctor(PIX A,
702 PIX B)
703 {
704 if (B <= 0) {
705 return 0;
706 }
707
708 return A / B;
709 }
710
711 template <typename PIX, int maxValue>
712 PIX
exclusionFunctor(PIX A,PIX B)713 exclusionFunctor(PIX A,
714 PIX B)
715 {
716 return PIX(A + B - 2 * A * B / (double)maxValue);
717 }
718
719 template <typename PIX>
720 PIX
fromFunctor(PIX A,PIX B)721 fromFunctor(PIX A,
722 PIX B)
723 {
724 return B - A;
725 }
726
727 template <typename PIX>
728 PIX
geometricFunctor(PIX A,PIX B)729 geometricFunctor(PIX A,
730 PIX B)
731 {
732 double sum = (double)A + (double)B;
733
734 if (sum == 0) {
735 return 0;
736 } else {
737 return 2 * A * B / sum;
738 }
739 }
740
741 template <typename PIX, int maxValue>
742 PIX
multiplyFunctor(PIX A,PIX B)743 multiplyFunctor(PIX A,
744 PIX B)
745 {
746 return PIX(A * B / (double)maxValue);
747 }
748
749 template <typename PIX, int maxValue>
750 PIX
screenFunctor(PIX A,PIX B)751 screenFunctor(PIX A,
752 PIX B)
753 {
754 if ( (A <= maxValue) || (B <= maxValue) ) {
755 return PIX( (double)A + B - (double)A * B );
756 } else {
757 return (std::max)(A, B);
758 }
759 }
760
761 template <typename PIX, int maxValue>
762 PIX
hardLightFunctor(PIX A,PIX B)763 hardLightFunctor(PIX A,
764 PIX B)
765 {
766 if ( A < ( (double)maxValue / 2. ) ) {
767 return PIX(2 * A * B / (double)maxValue);
768 } else {
769 return PIX( maxValue * ( 1. - 2 * (1. - A / (double)maxValue) * (1. - B / (double)maxValue) ) );
770 }
771 }
772
773 template <typename PIX, int maxValue>
774 PIX
softLightFunctor(PIX A,PIX B)775 softLightFunctor(PIX A,
776 PIX B)
777 {
778 double An = A / (double)maxValue;
779 double Bn = B / (double)maxValue;
780
781 if (2 * An <= 1) {
782 return PIX( maxValue * ( Bn - (1 - 2 * An) * Bn * (1 - Bn) ) );
783 } else if (4 * Bn <= 1) {
784 return PIX( maxValue * ( Bn + (2 * An - 1) * (4 * Bn * (4 * Bn + 1) * (Bn - 1) + 7 * Bn) ) );
785 } else {
786 return PIX( maxValue * ( Bn + (2 * An - 1) * (sqrt(Bn) - Bn) ) );
787 }
788 }
789
790 template <typename PIX>
791 PIX
hypotFunctor(PIX A,PIX B)792 hypotFunctor(PIX A,
793 PIX B)
794 {
795 return PIX( std::sqrt( (double)(A * A + B * B) ) );
796 }
797
798 template <typename PIX>
799 PIX
minusFunctor(PIX A,PIX B)800 minusFunctor(PIX A,
801 PIX B)
802 {
803 return A - B;
804 }
805
806 template <typename PIX>
807 PIX
darkenFunctor(PIX A,PIX B)808 darkenFunctor(PIX A,
809 PIX B)
810 {
811 return (std::min)(A, B);
812 }
813
814 template <typename PIX>
815 PIX
lightenFunctor(PIX A,PIX B)816 lightenFunctor(PIX A,
817 PIX B)
818 {
819 return (std::max)(A, B);
820 }
821
822 template <typename PIX, int maxValue>
823 PIX
overlayFunctor(PIX A,PIX B)824 overlayFunctor(PIX A,
825 PIX B)
826 {
827 double An = A / (double)maxValue;
828 double Bn = B / (double)maxValue;
829
830 if (2 * Bn <= 1.) {
831 // multiply
832 return PIX( maxValue * (2 * An * Bn) );
833 } else {
834 // screen
835 return PIX( maxValue * ( 1 - 2 * (1 - Bn) * (1 - An) ) );
836 }
837 }
838
839 template <typename PIX, int maxValue>
840 PIX
colorDodgeFunctor(PIX A,PIX B)841 colorDodgeFunctor(PIX A,
842 PIX B)
843 {
844 if (A >= maxValue) {
845 return A;
846 } else {
847 return PIX( maxValue * (std::min)( 1., B / (maxValue - (double)A) ) );
848 }
849 }
850
851 template <typename PIX, int maxValue>
852 PIX
colorBurnFunctor(PIX A,PIX B)853 colorBurnFunctor(PIX A,
854 PIX B)
855 {
856 if (A <= 0) {
857 return A;
858 } else {
859 return PIX( maxValue * ( 1. - (std::min)(1., (maxValue - B) / (double)A) ) );
860 }
861 }
862
863 template <typename PIX, int maxValue>
864 PIX
pinLightFunctor(PIX A,PIX B)865 pinLightFunctor(PIX A,
866 PIX B)
867 {
868 PIX max2 = PIX( (double)maxValue / 2. );
869
870 return A >= max2 ? (std::max)(B, (A - max2) * 2) : (std::min)(B, A * 2);
871 }
872
873 template <typename PIX, int maxValue>
874 PIX
reflectFunctor(PIX A,PIX B)875 reflectFunctor(PIX A,
876 PIX B)
877 {
878 if (B >= maxValue) {
879 return maxValue;
880 } else {
881 return PIX( (std::min)( (double)maxValue, A * A / (double)(maxValue - B) ) );
882 }
883 }
884
885 template <typename PIX, int maxValue>
886 PIX
freezeFunctor(PIX A,PIX B)887 freezeFunctor(PIX A,
888 PIX B)
889 {
890 if (B <= 0) {
891 return 0;
892 } else {
893 double An = A / (double)maxValue;
894 double Bn = B / (double)maxValue;
895
896 return PIX( (std::max)( 0., maxValue * (1 - std::sqrt( (std::max)(0., 1. - An) ) / Bn) ) );
897 }
898 }
899
900 // This functions seems wrong. Is it a confusion with cosine interpolation?
901 // see http://paulbourke.net/miscellaneous/interpolation/
902 template <typename PIX, int maxValue>
903 PIX
interpolatedFunctor(PIX A,PIX B)904 interpolatedFunctor(PIX A,
905 PIX B)
906 {
907 double An = A / (double)maxValue;
908 double Bn = B / (double)maxValue;
909
910 return PIX( maxValue * ( 0.5 - 0.25 * ( std::cos(M_PI * An) - std::cos(M_PI * Bn) ) ) );
911 }
912
913 template <typename PIX, int maxValue>
914 PIX
atopFunctor(PIX A,PIX B,PIX alphaA,PIX alphaB)915 atopFunctor(PIX A,
916 PIX B,
917 PIX alphaA,
918 PIX alphaB)
919 {
920 return PIX( A * alphaB / (double)maxValue + B * (1. - alphaA / (double)maxValue) );
921 }
922
923 template <typename PIX, int maxValue>
924 PIX
conjointOverFunctor(PIX A,PIX B,PIX alphaA,PIX alphaB)925 conjointOverFunctor(PIX A,
926 PIX B,
927 PIX alphaA,
928 PIX alphaB)
929 {
930 if (alphaA > alphaB) {
931 return A;
932 } else if (alphaB <= 0) {
933 return A + B;
934 } else {
935 return A + B * ( 1. - (alphaA / (double)alphaB) );
936 }
937 }
938
939 template <typename PIX, int maxValue>
940 PIX
disjointOverFunctor(PIX A,PIX B,PIX alphaA,PIX alphaB)941 disjointOverFunctor(PIX A,
942 PIX B,
943 PIX alphaA,
944 PIX alphaB)
945 {
946 if (alphaA >= maxValue) {
947 return A;
948 } else if ( (alphaA + alphaB) < maxValue ) {
949 return A + B;
950 } else if (alphaB <= 0) {
951 return A + B * (1 - alphaA / (double)maxValue);
952 } else {
953 return A + B * (maxValue - alphaA) / alphaB;
954 }
955 }
956
957 template <typename PIX, int maxValue>
958 PIX
inFunctor(PIX A,PIX,PIX,PIX alphaB)959 inFunctor(PIX A,
960 PIX /*B*/,
961 PIX /*alphaA*/,
962 PIX alphaB)
963 {
964 return PIX(A * alphaB / (double)maxValue);
965 }
966
967 template <typename PIX, int maxValue>
968 PIX
matteFunctor(PIX A,PIX B,PIX alphaA,PIX)969 matteFunctor(PIX A,
970 PIX B,
971 PIX alphaA,
972 PIX /*alphaB*/)
973 {
974 return PIX( A * alphaA / (double)maxValue + B * (1. - alphaA / (double)maxValue) );
975 }
976
977 template <typename PIX, int maxValue>
978 PIX
maskFunctor(PIX,PIX B,PIX alphaA,PIX)979 maskFunctor(PIX /*A*/,
980 PIX B,
981 PIX alphaA,
982 PIX /*alphaB*/)
983 {
984 return PIX(B * alphaA / (double)maxValue);
985 }
986
987 template <typename PIX, int maxValue>
988 PIX
outFunctor(PIX A,PIX,PIX,PIX alphaB)989 outFunctor(PIX A,
990 PIX /*B*/,
991 PIX /*alphaA*/,
992 PIX alphaB)
993 {
994 return PIX( A * (1. - alphaB / (double)maxValue) );
995 }
996
997 template <typename PIX, int maxValue>
998 PIX
overFunctor(PIX A,PIX B,PIX alphaA,PIX)999 overFunctor(PIX A,
1000 PIX B,
1001 PIX alphaA,
1002 PIX /*alphaB*/)
1003 {
1004 return PIX( A + B * (1 - alphaA / (double)maxValue) );
1005 }
1006
1007 template <typename PIX, int maxValue>
1008 PIX
stencilFunctor(PIX,PIX B,PIX alphaA,PIX)1009 stencilFunctor(PIX /*A*/,
1010 PIX B,
1011 PIX alphaA,
1012 PIX /*alphaB*/)
1013 {
1014 return PIX( B * (1 - alphaA / (double)maxValue) );
1015 }
1016
1017 template <typename PIX, int maxValue>
1018 PIX
underFunctor(PIX A,PIX B,PIX,PIX alphaB)1019 underFunctor(PIX A,
1020 PIX B,
1021 PIX /*alphaA*/,
1022 PIX alphaB)
1023 {
1024 return PIX(A * (1 - alphaB / (double)maxValue) + B);
1025 }
1026
1027 template <typename PIX, int maxValue>
1028 PIX
xorFunctor(PIX A,PIX B,PIX alphaA,PIX alphaB)1029 xorFunctor(PIX A,
1030 PIX B,
1031 PIX alphaA,
1032 PIX alphaB)
1033 {
1034 return PIX( A * (1 - alphaB / (double)maxValue) + B * (1 - alphaA / (double)maxValue) );
1035 }
1036
1037 ///////////////////////////////////////////////////////////////////////////////
1038 //
1039 // Code from pixman-combine-float.c
1040 #define OFX_PIXMAN_USE_DOUBLE
1041 #ifdef OFX_PIXMAN_USE_DOUBLE
1042 typedef double pixman_float_t;
1043 #define PIXMAN_FLT_MIN DBL_MIN
1044 #else
1045 typedef float pixman_float_t;
1046 #define PIXMAN_FLT_MIN FLT_MIN
1047 #endif
1048
1049 // START
1050 /*
1051 * Copyright © 2010, 2012 Soren Sandmann Pedersen
1052 * Copyright © 2010, 2012 Red Hat, Inc.
1053 *
1054 * Permission is hereby granted, free of charge, to any person obtaining a
1055 * copy of this software and associated documentation files (the "Software"),
1056 * to deal in the Software without restriction, including without limitation
1057 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1058 * and/or sell copies of the Software, and to permit persons to whom the
1059 * Software is furnished to do so, subject to the following conditions:
1060 *
1061 * The above copyright notice and this permission notice (including the next
1062 * paragraph) shall be included in all copies or substantial portions of the
1063 * Software.
1064 *
1065 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1066 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1067 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1068 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1069 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1070 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1071 * DEALINGS IN THE SOFTWARE.
1072 *
1073 * Author: Soren Sandmann Pedersen (sandmann@cs.au.dk)
1074 */
1075 /*
1076 * PDF nonseperable blend modes are implemented using the following functions
1077 * to operate in Hsl space, with Cmax, Cmid, Cmin referring to the max, mid
1078 * and min value of the red, green and blue components.
1079 *
1080 * LUM (C) = 0.3 × Cred + 0.59 × Cgreen + 0.11 × Cblue
1081 *
1082 * clip_color (C):
1083 * l = LUM (C)
1084 * min = Cmin
1085 * max = Cmax
1086 * if n < 0.0
1087 * C = l + (((C – l) × l) ⁄ (l – min))
1088 * if x > 1.0
1089 * C = l + (((C – l) × (1 – l) ) ⁄ (max – l))
1090 * return C
1091 *
1092 * set_lum (C, l):
1093 * d = l – LUM (C)
1094 * C += d
1095 * return clip_color (C)
1096 *
1097 * SAT (C) = CH_MAX (C) - CH_MIN (C)
1098 *
1099 * set_sat (C, s):
1100 * if Cmax > Cmin
1101 * Cmid = ( ( ( Cmid – Cmin ) × s ) ⁄ ( Cmax – Cmin ) )
1102 * Cmax = s
1103 * else
1104 * Cmid = Cmax = 0.0
1105 * Cmin = 0.0
1106 * return C
1107 */
1108
1109 /* For premultiplied colors, we need to know what happens when C is
1110 * multiplied by a real number. LUM and SAT are linear:
1111 *
1112 * LUM (r × C) = r × LUM (C) SAT (r * C) = r * SAT (C)
1113 *
1114 * If we extend clip_color with an extra argument a and change
1115 *
1116 * if x >= 1.0
1117 *
1118 * into
1119 *
1120 * if x >= a
1121 *
1122 * then clip_color is also linear:
1123 *
1124 * r * clip_color (C, a) = clip_color (r * C, r * a);
1125 *
1126 * for positive r.
1127 *
1128 * Similarly, we can extend set_lum with an extra argument that is just passed
1129 * on to clip_color:
1130 *
1131 * r * set_lum (C, l, a)
1132 *
1133 * = r × clip_color (C + l - LUM (C), a)
1134 *
1135 * = clip_color (r * C + r × l - r * LUM (C), r * a)
1136 *
1137 * = set_lum (r * C, r * l, r * a)
1138 *
1139 * Finally, set_sat:
1140 *
1141 * r * set_sat (C, s) = set_sat (x * C, r * s)
1142 *
1143 * The above holds for all non-zero x, because the x'es in the fraction for
1144 * C_mid cancel out. Specifically, it holds for x = r:
1145 *
1146 * r * set_sat (C, s) = set_sat (r * C, r * s)
1147 *
1148 */
1149 typedef struct
1150 {
1151 pixman_float_t r;
1152 pixman_float_t g;
1153 pixman_float_t b;
1154 } pixman_rgb_t;
1155
1156 /*
1157 https://cgit.freedesktop.org/pixman/commit/pixman/pixman-combine-float.c?id=4dfda2adfe2eb1130fc27b1da35df778284afd91
1158 float-combiner.c: Change tests for x == 0.0 tests to - FLT_MIN < x < FLT_MIN
1159
1160 pixman-float-combiner.c currently uses checks like these:
1161
1162 if (x == 0.0f)
1163 ...
1164 else
1165 ... / x;
1166
1167 to prevent division by 0. In theory this is correct: a division-by-zero
1168 exception is only supposed to happen when the floating point numerator is
1169 exactly equal to a positive or negative zero.
1170
1171 However, in practice, the combination of x87 and gcc optimizations
1172 causes issues. The x87 registers are 80 bits wide, which means the
1173 initial test:
1174
1175 if (x == 0.0f)
1176
1177 may be false when x is an 80 bit floating point number, but when x is
1178 rounded to a 32 bit single precision number, it becomes equal to
1179 0.0. In principle, gcc should compensate for this quirk of x87, and
1180 there are some options such as -ffloat-store, -fexcess-precision=standard,
1181 and -std=c99 that will make it do so, but these all have a performance
1182 cost. It is also possible to set the FPU to a mode that makes it do
1183 all computation with single or double precision, but that would
1184 require pixman to save the existing mode before doing anything with
1185 floating point and restore it afterwards.
1186
1187 Instead, this patch side-steps the issue by replacing exact checks for
1188 equality with zero with a new macro that checkes whether the value is
1189 between -FLT_MIN and FLT_MIN.
1190
1191 There is extensive reading material about this issue linked off the
1192 infamous gcc bug 323:
1193
1194 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
1195 */
1196 #define PIXMAN_IS_ZERO(f) (-PIXMAN_FLT_MIN < (f) && (f) < PIXMAN_FLT_MIN)
1197
1198 inline pixman_float_t
channel_min(const pixman_rgb_t * c)1199 channel_min (const pixman_rgb_t *c)
1200 {
1201 return (std::min)((std::min)(c->r, c->g), c->b);
1202 }
1203
1204 inline pixman_float_t
channel_max(const pixman_rgb_t * c)1205 channel_max (const pixman_rgb_t *c)
1206 {
1207 return (std::max)((std::max)(c->r, c->g), c->b);
1208 }
1209
1210 inline pixman_float_t
get_lum(const pixman_rgb_t * c)1211 get_lum (const pixman_rgb_t *c)
1212 {
1213 return c->r * 0.3f + c->g * 0.59f + c->b * 0.11f;
1214 }
1215
1216 inline pixman_float_t
get_sat(const pixman_rgb_t * c)1217 get_sat (const pixman_rgb_t *c)
1218 {
1219 return channel_max(c) - channel_min(c);
1220 }
1221
1222 inline void
clip_color(pixman_rgb_t * color,pixman_float_t a)1223 clip_color (pixman_rgb_t *color,
1224 pixman_float_t a)
1225 {
1226 pixman_float_t l = get_lum(color);
1227 pixman_float_t n = channel_min(color);
1228 pixman_float_t x = channel_max(color);
1229 pixman_float_t t;
1230
1231 if (n < 0.0f) {
1232 t = l - n;
1233 if ( PIXMAN_IS_ZERO(t) ) {
1234 color->r = 0.0f;
1235 color->g = 0.0f;
1236 color->b = 0.0f;
1237 } else {
1238 color->r = l + ( ( (color->r - l) * l ) / t );
1239 color->g = l + ( ( (color->g - l) * l ) / t );
1240 color->b = l + ( ( (color->b - l) * l ) / t );
1241 }
1242 }
1243 if (x > a) {
1244 t = x - l;
1245 if ( PIXMAN_IS_ZERO(t) ) {
1246 color->r = a;
1247 color->g = a;
1248 color->b = a;
1249 } else {
1250 color->r = l + ( ( (color->r - l) * (a - l) / t ) );
1251 color->g = l + ( ( (color->g - l) * (a - l) / t ) );
1252 color->b = l + ( ( (color->b - l) * (a - l) / t ) );
1253 }
1254 }
1255 }
1256
1257 static void
set_lum(pixman_rgb_t * color,pixman_float_t sa,pixman_float_t l)1258 set_lum (pixman_rgb_t *color,
1259 pixman_float_t sa,
1260 pixman_float_t l)
1261 {
1262 pixman_float_t d = l - get_lum(color);
1263
1264 color->r = color->r + d;
1265 color->g = color->g + d;
1266 color->b = color->b + d;
1267
1268 clip_color(color, sa);
1269 }
1270
1271 inline void
set_sat(pixman_rgb_t * src,pixman_float_t sat)1272 set_sat (pixman_rgb_t *src,
1273 pixman_float_t sat)
1274 {
1275 pixman_float_t *max, *mid, *min;
1276 pixman_float_t t;
1277
1278 if (src->r > src->g) {
1279 if (src->r > src->b) {
1280 max = &(src->r);
1281
1282 if (src->g > src->b) {
1283 mid = &(src->g);
1284 min = &(src->b);
1285 } else {
1286 mid = &(src->b);
1287 min = &(src->g);
1288 }
1289 } else {
1290 max = &(src->b);
1291 mid = &(src->r);
1292 min = &(src->g);
1293 }
1294 } else {
1295 if (src->r > src->b) {
1296 max = &(src->g);
1297 mid = &(src->r);
1298 min = &(src->b);
1299 } else {
1300 min = &(src->r);
1301
1302 if (src->g > src->b) {
1303 max = &(src->g);
1304 mid = &(src->b);
1305 } else {
1306 max = &(src->b);
1307 mid = &(src->g);
1308 }
1309 }
1310 }
1311
1312 t = *max - *min;
1313
1314 if ( PIXMAN_IS_ZERO(t) ) {
1315 *mid = *max = 0.0f;
1316 } else {
1317 *mid = ( (*mid - *min) * sat ) / t;
1318 *max = sat;
1319 }
1320
1321 *min = 0.0f;
1322 } // set_sat
1323
1324 /* Hue:
1325 *
1326 * as * ad * B(s/as, d/as)
1327 * = as * ad * set_lum (set_sat (s/as, SAT (d/ad)), LUM (d/ad), 1)
1328 * = set_lum (set_sat (ad * s, as * SAT (d)), as * LUM (d), as * ad)
1329 *
1330 */
1331 inline void
blend_hsl_hue(pixman_rgb_t * res,const pixman_rgb_t * dest,pixman_float_t da,const pixman_rgb_t * src,pixman_float_t sa)1332 blend_hsl_hue (pixman_rgb_t *res,
1333 const pixman_rgb_t *dest,
1334 pixman_float_t da,
1335 const pixman_rgb_t *src,
1336 pixman_float_t sa)
1337 {
1338 res->r = src->r * da;
1339 res->g = src->g * da;
1340 res->b = src->b * da;
1341
1342 set_sat(res, get_sat(dest) * sa);
1343 set_lum(res, sa * da, get_lum(dest) * sa);
1344 }
1345
1346 /*
1347 * Saturation
1348 *
1349 * as * ad * B(s/as, d/ad)
1350 * = as * ad * set_lum (set_sat (d/ad, SAT (s/as)), LUM (d/ad), 1)
1351 * = set_lum (as * ad * set_sat (d/ad, SAT (s/as)),
1352 * as * LUM (d), as * ad)
1353 * = set_lum (set_sat (as * d, ad * SAT (s), as * LUM (d), as * ad))
1354 */
1355 inline void
blend_hsl_saturation(pixman_rgb_t * res,const pixman_rgb_t * dest,pixman_float_t da,const pixman_rgb_t * src,pixman_float_t sa)1356 blend_hsl_saturation (pixman_rgb_t *res,
1357 const pixman_rgb_t *dest,
1358 pixman_float_t da,
1359 const pixman_rgb_t *src,
1360 pixman_float_t sa)
1361 {
1362 res->r = dest->r * sa;
1363 res->g = dest->g * sa;
1364 res->b = dest->b * sa;
1365
1366 set_sat(res, get_sat(src) * da);
1367 set_lum(res, sa * da, get_lum(dest) * sa);
1368 }
1369
1370 /*
1371 * Color
1372 *
1373 * as * ad * B(s/as, d/as)
1374 * = as * ad * set_lum (s/as, LUM (d/ad), 1)
1375 * = set_lum (s * ad, as * LUM (d), as * ad)
1376 */
1377 inline void
blend_hsl_color(pixman_rgb_t * res,const pixman_rgb_t * dest,pixman_float_t da,const pixman_rgb_t * src,pixman_float_t sa)1378 blend_hsl_color (pixman_rgb_t *res,
1379 const pixman_rgb_t *dest,
1380 pixman_float_t da,
1381 const pixman_rgb_t *src,
1382 pixman_float_t sa)
1383 {
1384 res->r = src->r * da;
1385 res->g = src->g * da;
1386 res->b = src->b * da;
1387
1388 set_lum(res, sa * da, get_lum(dest) * sa);
1389 }
1390
1391 /*
1392 * Luminosity
1393 *
1394 * as * ad * B(s/as, d/ad)
1395 * = as * ad * set_lum (d/ad, LUM (s/as), 1)
1396 * = set_lum (as * d, ad * LUM (s), as * ad)
1397 */
1398 inline void
blend_hsl_luminosity(pixman_rgb_t * res,const pixman_rgb_t * dest,pixman_float_t da,const pixman_rgb_t * src,pixman_float_t sa)1399 blend_hsl_luminosity (pixman_rgb_t *res,
1400 const pixman_rgb_t *dest,
1401 pixman_float_t da,
1402 const pixman_rgb_t *src,
1403 pixman_float_t sa)
1404 {
1405 res->r = dest->r * sa;
1406 res->g = dest->g * sa;
1407 res->b = dest->b * sa;
1408
1409 set_lum (res, sa * da, get_lum (src) * da);
1410 }
1411
1412 // END
1413 // Code from pixman-combine-float.c
1414 ///////////////////////////////////////////////////////////////////////////////
1415
1416 /**
1417 * @brief Global wrapper templated by the blending operator.
1418 * A and B are respectively the color of the image A and B and is assumed to of size nComponents,
1419 * nComponents being at most 4
1420 **/
1421 template <MergingFunctionEnum f, typename PIX, int nComponents, int maxValue>
1422 void
mergePixel(bool doAlphaMasking,const PIX * A,PIX a,const PIX * B,PIX b,PIX * dst)1423 mergePixel(bool doAlphaMasking,
1424 const PIX *A,
1425 PIX a,
1426 const PIX *B,
1427 PIX b,
1428 PIX* dst)
1429 {
1430 doAlphaMasking = (f == eMergeMatte) || (doAlphaMasking && isMaskable(f));
1431
1432 ///When doAlphaMasking is enabled and we're in RGBA the output alpha is set to alphaA+alphaB-alphaA*alphaB
1433 int maxComp = nComponents;
1434 if ( !isSeparable(f) ) {
1435 // HSL modes
1436 pixman_rgb_t src, dest, res;
1437 if (PIXMAN_IS_ZERO(a) || nComponents < 3) {
1438 src.r = src.g = src.b = 0;
1439 } else {
1440 src.r = A[0] / (pixman_float_t)a;
1441 src.g = A[1] / (pixman_float_t)a;
1442 src.b = A[2] / (pixman_float_t)a;
1443 }
1444 if (PIXMAN_IS_ZERO(b) || nComponents < 3) {
1445 dest.r = dest.g = dest.b = 0;
1446 } else {
1447 dest.r = B[0] / (pixman_float_t)b;
1448 dest.g = B[1] / (pixman_float_t)b;
1449 dest.b = B[2] / (pixman_float_t)b;
1450 }
1451 pixman_float_t sa = a / (pixman_float_t)maxValue;
1452 pixman_float_t da = b / (pixman_float_t)maxValue;
1453
1454 switch (f) {
1455 case eMergeHue:
1456 blend_hsl_hue(&res, &dest, da, &src, sa);
1457 break;
1458
1459 case eMergeSaturation:
1460 blend_hsl_saturation(&res, &dest, da, &src, sa);
1461 break;
1462
1463 case eMergeColor:
1464 blend_hsl_color(&res, &dest, da, &src, sa);
1465 break;
1466
1467 case eMergeLuminosity:
1468 blend_hsl_luminosity(&res, &dest, da, &src, sa);
1469 break;
1470
1471 default:
1472 res.r = res.g = res.b = 0;
1473 assert(false);
1474 break;
1475 }
1476 pixman_float_t R[3] = { res.r, res.g, res.b };
1477 for (int i = 0; i < (std::min)(nComponents, 3); ++i) {
1478 dst[i] = PIX( (1 - sa) * B[i] + (1 - da) * A[i] + R[i] * maxValue );
1479 }
1480 if (nComponents == 4) {
1481 dst[3] = PIX(a + b - a * b / (double)maxValue);
1482 }
1483
1484 return;
1485 }
1486
1487 // separable modes
1488 if ( doAlphaMasking && (nComponents == 4) ) {
1489 maxComp = 3;
1490 dst[3] = PIX(a + b - a * b / (double)maxValue);
1491 }
1492 for (int i = 0; i < maxComp; ++i) {
1493 switch (f) {
1494 case eMergeATop:
1495 dst[i] = atopFunctor<PIX, maxValue>(A[i], B[i], a, b);
1496 break;
1497 case eMergeAverage:
1498 dst[i] = averageFunctor(A[i], B[i]);
1499 break;
1500 case eMergeColorBurn:
1501 dst[i] = colorBurnFunctor<PIX, maxValue>(A[i], B[i]);
1502 break;
1503 case eMergeColorDodge:
1504 dst[i] = colorDodgeFunctor<PIX, maxValue>(A[i], B[i]);
1505 break;
1506 case eMergeConjointOver:
1507 dst[i] = conjointOverFunctor<PIX, maxValue>(A[i], B[i], a, b);
1508 break;
1509 case eMergeCopy:
1510 dst[i] = copyFunctor(A[i], B[i]);
1511 break;
1512 case eMergeDifference:
1513 dst[i] = differenceFunctor(A[i], B[i]);
1514 break;
1515 case eMergeDisjointOver:
1516 dst[i] = disjointOverFunctor<PIX, maxValue>(A[i], B[i], a, b);
1517 break;
1518 case eMergeDivide:
1519 dst[i] = divideFunctor(A[i], B[i]);
1520 break;
1521 case eMergeExclusion:
1522 dst[i] = exclusionFunctor<PIX, maxValue>(A[i], B[i]);
1523 break;
1524 case eMergeFreeze:
1525 dst[i] = freezeFunctor<PIX, maxValue>(A[i], B[i]);
1526 break;
1527 case eMergeFrom:
1528 dst[i] = fromFunctor(A[i], B[i]);
1529 break;
1530 case eMergeGeometric:
1531 dst[i] = geometricFunctor(A[i], B[i]);
1532 break;
1533 case eMergeGrainExtract:
1534 dst[i] = grainExtractFunctor<PIX, maxValue>(A[i], B[i]);
1535 break;
1536 case eMergeGrainMerge:
1537 dst[i] = grainMergeFunctor<PIX, maxValue>(A[i], B[i]);
1538 break;
1539 case eMergeHardLight:
1540 dst[i] = hardLightFunctor<PIX, maxValue>(A[i], B[i]);
1541 break;
1542 case eMergeHypot:
1543 dst[i] = hypotFunctor(A[i], B[i]);
1544 break;
1545 case eMergeIn:
1546 dst[i] = inFunctor<PIX, maxValue>(A[i], B[i], a, b);
1547 break;
1548 //case eMergeInterpolated:
1549 // dst[i] = interpolatedFunctor<PIX, maxValue>(A[i], B[i]);
1550 // break;
1551 case eMergeMask:
1552 dst[i] = maskFunctor<PIX, maxValue>(A[i], B[i], a, b);
1553 break;
1554 case eMergeMatte:
1555 dst[i] = matteFunctor<PIX, maxValue>(A[i], B[i], a, b);
1556 break;
1557 case eMergeMax:
1558 dst[i] = lightenFunctor(A[i], B[i]);
1559 break;
1560 case eMergeMin:
1561 dst[i] = darkenFunctor(A[i], B[i]);
1562 break;
1563 case eMergeMinus:
1564 dst[i] = minusFunctor(A[i], B[i]);
1565 break;
1566 case eMergeMultiply:
1567 dst[i] = multiplyFunctor<PIX, maxValue>(A[i], B[i]);
1568 break;
1569 case eMergeOut:
1570 dst[i] = outFunctor<PIX, maxValue>(A[i], B[i], a, b);
1571 break;
1572 case eMergeOver:
1573 dst[i] = overFunctor<PIX, maxValue>(A[i], B[i], a, b);
1574 break;
1575 case eMergeOverlay:
1576 dst[i] = overlayFunctor<PIX, maxValue>(A[i], B[i]);
1577 break;
1578 case eMergePinLight:
1579 dst[i] = pinLightFunctor<PIX, maxValue>(A[i], B[i]);
1580 break;
1581 case eMergePlus:
1582 dst[i] = plusFunctor(A[i], B[i]);
1583 break;
1584 case eMergeReflect:
1585 dst[i] = reflectFunctor<PIX, maxValue>(A[i], B[i]);
1586 break;
1587 case eMergeScreen:
1588 dst[i] = screenFunctor<PIX, maxValue>(A[i], B[i]);
1589 break;
1590 case eMergeSoftLight:
1591 dst[i] = softLightFunctor<PIX, maxValue>(A[i], B[i]);
1592 break;
1593 case eMergeStencil:
1594 dst[i] = stencilFunctor<PIX, maxValue>(A[i], B[i], a, b);
1595 break;
1596 case eMergeUnder:
1597 dst[i] = underFunctor<PIX, maxValue>(A[i], B[i], a, b);
1598 break;
1599 case eMergeXOR:
1600 dst[i] = xorFunctor<PIX, maxValue>(A[i], B[i], a, b);
1601 break;
1602 default:
1603 dst[i] = 0;
1604 assert(false);
1605 break;
1606 } // switch
1607 }
1608 } // mergePixel
1609 } // MergeImages2D
1610 } // OFX
1611
1612
1613 #endif // Misc_Merging_helper_h
1614