blend modes: add missing ones, redo as templates
[mypaint:maxy-experimental.git] / lib / compositing.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 // Template functions for compositing buffers
11
12 #ifndef __HAVE_COMPOSITING
13 #define __HAVE_COMPOSITING
14
15 #include "fix15.hpp"
16
17
18 /* Specifies how to treat output buffers when compositing into them. */
19
20 typedef enum {
21     BufferCompOutputRGBA,   // Expect and write full RGBA
22     BufferCompOutputRGBX,   // RGB with ignored (opaque) alpha
23 } BufferCompOutputType;
24
25
26
27 // Generic buffer compositor. Templated by output type specifier, stateless
28 // color blend mode functor, and the sizes of the buffers.
29 //
30 // Need to template this at the class level to permit partial template
31 // specialization for more optimized forms. The C++ spec does not permit plain
32 // functions to be partially specialized.
33
34 template <BufferCompOutputType OUTBUFUSAGE,
35           unsigned int BUFSIZE,
36           typename BLENDFUNC>
37 class BufferComp
38 {
39   private:
40     static BLENDFUNC blendfunc;
41   public:
42     static inline void composite_src_over
43             (const fix15_short_t * const src,
44              fix15_short_t * const dst,
45              const fix15_short_t opac)
46     {
47         if (opac == 0) {
48             return;
49         }
50         for (unsigned int i=0; i<BUFSIZE; i+=4) {
51             // Leave the backdrop alone if the source is fully transparent
52             const fix15_t a_s = fix15_mul(src[i+3], opac);
53             if (a_s == 0) {
54                 continue;
55             }
56             const fix15_t src_a0 = fix15_mul(opac, src[i]);
57             const fix15_t src_a1 = fix15_mul(opac, src[i+1]);
58             const fix15_t src_a2 = fix15_mul(opac, src[i+2]);
59             const fix15_t a_b = (OUTBUFUSAGE == BufferCompOutputRGBA)
60                                 ? dst[i+3]
61                                 : fix15_one ;
62             // If the backdrop is empty, the source contributes fully
63             if (OUTBUFUSAGE == BufferCompOutputRGBA) {
64                 if (a_b == 0) {
65                     dst[i] = fix15_short_clamp(src_a0);
66                     dst[i+1] = fix15_short_clamp(src_a1);
67                     dst[i+2] = fix15_short_clamp(src_a2);
68                     dst[i+3] = a_s;
69                     continue;
70                 }
71             }
72             // De-premultiplied version of dst
73             fix15_t tmp0 = ((OUTBUFUSAGE == BufferCompOutputRGBA)
74                             ? fix15_div(dst[i], a_b)
75                             : dst[i]);
76             fix15_t tmp1 = ((OUTBUFUSAGE == BufferCompOutputRGBA)
77                             ? fix15_div(dst[i+1], a_b)
78                             : dst[i+1]);
79             fix15_t tmp2 = ((OUTBUFUSAGE == BufferCompOutputRGBA)
80                             ? fix15_div(dst[i+2], a_b)
81                             : dst[i+2]);
82             // Combine using the blend function
83             blendfunc(fix15_div(src_a0, a_s),
84                       fix15_div(src_a1, a_s),  //de-premult srcs
85                       fix15_div(src_a2, a_s),
86                       tmp0, tmp1, tmp2);
87             // Composite the result using src-over
88             const fix15_t asab = (OUTBUFUSAGE == BufferCompOutputRGBA)
89                                     ? fix15_mul(a_s, a_b)
90                                     : a_s;
91             const fix15_t one_minus_a_s = fix15_one - a_s;
92             dst[i+0] = fix15_sumprods(one_minus_a_s, dst[i+0],
93                                       fix15_short_clamp(tmp0), asab);
94             dst[i+1] = fix15_sumprods(one_minus_a_s, dst[i+1],
95                                       fix15_short_clamp(tmp1), asab);
96             dst[i+2] = fix15_sumprods(one_minus_a_s, dst[i+2],
97                                       fix15_short_clamp(tmp2), asab);
98             if (OUTBUFUSAGE == BufferCompOutputRGBA) {
99                 const fix15_t one_minus_a_b = fix15_one - a_b;
100                 dst[i+0] += fix15_mul(one_minus_a_b, src_a0);
101                 dst[i+1] += fix15_mul(one_minus_a_b, src_a1);
102                 dst[i+2] += fix15_mul(one_minus_a_b, src_a2);
103                 dst[i+3] = fix15_short_clamp(a_s + a_b - asab);
104             }
105         }
106     }
107 };
108
109
110 #endif //__HAVE_COMPOSITING