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