1 /*
2     Inochi2D Common Data
3 
4     Copyright © 2020, Inochi2D Project
5     Distributed under the 2-Clause BSD License, see LICENSE file.
6     
7     Authors: Luna Nielsen
8 */
9 module inochi2d.core.nodes.common;
10 import bindbc.opengl;
11 import inochi2d.fmt.serialize;
12 import bindbc.opengl.context;
13 import std.string;
14 
15 private {
16     bool inAdvancedBlending;
17     bool inAdvancedBlendingCoherent;
18     bool inAdvancedBlendingNV;
19     extern(C) void function() glBlendBarrierKHR;
20 }
21 
22 void inInitBlending() {
23     GLint extensionCount;
24     glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
25     foreach(i; 0..extensionCount) {
26         string ext = cast(string)glGetStringi(GL_EXTENSIONS, i).fromStringz;
27 
28         // KHR blending extension
29         if (ext == "GL_KHR_blend_equation_advanced") inAdvancedBlending = true;
30         if (ext == "GL_KHR_blend_equation_advanced_coherent") inAdvancedBlendingCoherent = true;
31 
32         // NVIDIA blending extension
33         if (ext == "GL_NV_blend_equation_advanced") {
34             inAdvancedBlending = true;
35             inAdvancedBlendingNV = true;
36         }
37         if (ext == "GL_NV_blend_equation_advanced_coherent") inAdvancedBlendingCoherent = true;
38 
39         if (inAdvancedBlending && inAdvancedBlendingCoherent) break;
40     }
41 
42     if (inAdvancedBlendingCoherent) glEnable(0x9285); // BLEND_ADVANCED_COHERENT_KHR && BLEND_ADVANCED_COHERENT_NV
43     else {
44         inAdvancedBlendingCoherent = false;
45         
46         debug {
47             import std.stdio : writeln;
48             if (inAdvancedBlending) writeln("Advanced blending is supported, but non-coherent blending is not available.");
49         }
50         inAdvancedBlending = false;
51     }
52     
53 }
54 
55 /*
56     INFORMATION ABOUT BLENDING MODES
57     Blending is a complicated topic, especially once we get to mobile devices and games consoles.
58 
59     The following blending modes are supported in Standard mode:
60         Normal
61         Multiply
62         Screen
63         Overlay
64         Darken
65         Lighten
66         Color Dodge
67         Linear Dodge
68         Color Burn
69         Hard Light
70         Soft Light
71         Difference
72         Exclusion
73         Subtract
74         Inverse
75         Destination In
76         Clip To Lower
77         Slice from Lower
78     Some of these blending modes behave better on Tiling GPUs.
79 
80     The following blending modes are supported in Core mode:
81         Normal
82         Multiply
83         Screen
84         Lighten
85         Color Dodge
86         Linear Dodge
87         Inverse
88         Destination In
89         Clip to Lower
90         Slice from Lower
91     Tiling GPUs on older mobile devices don't have great drivers, we shouldn't tempt fate.
92 */
93 
94 /**
95     Blending modes
96 */
97 enum BlendMode {
98     // Normal blending mode
99     Normal,
100 
101     // Multiply blending mode
102     Multiply,
103 
104     // Screen
105     Screen,
106 
107     // Overlay
108     Overlay,
109 
110     // Darken
111     Darken,
112 
113     // Lighten
114     Lighten,
115     
116     // Color Dodge
117     ColorDodge,
118 
119     // Linear Dodge
120     LinearDodge,
121 
122     // Color Burn
123     ColorBurn,
124 
125     // Hard Light
126     HardLight,
127 
128     // Soft Light
129     SoftLight,
130 
131     // Difference
132     Difference,
133 
134     // Exclusion
135     Exclusion,
136 
137     // Subtract
138     Subtract,
139 
140     // Inverse
141     Inverse,
142 
143     // Destination In
144     DestinationIn,
145 
146     // Clip to Lower
147     // Special blending mode that clips the drawable
148     // to a lower rendered area.
149     ClipToLower,
150 
151     // Slice from Lower
152     // Special blending mode that slices the drawable
153     // via a lower rendered area.
154     // Basically inverse ClipToLower
155     SliceFromLower
156 }
157 
158 // KHR blending
159 enum GL_MULTIPLY_KHR = 0x9294;
160 enum GL_SCREEN_KHR = 0x9295;
161 enum GL_OVERLAY_KHR = 0x9296;
162 enum GL_DARKEN_KHR = 0x9297;
163 enum GL_LIGHTEN_KHR = 0x9298;
164 enum GL_COLORDODGE_KHR = 0x9299;
165 enum GL_COLORBURN_KHR = 0x929A;
166 enum GL_HARDLIGHT_KHR = 0x929B;
167 enum GL_SOFTLIGHT_KHR = 0x929C;
168 enum GL_DIFFERENCE_KHR = 0x929E;
169 enum GL_EXCLUSION_KHR = 0x92A0;
170 
171 // NVIDIA blending
172 enum GL_SRC_NV = 0x9286;
173 enum GL_DST_NV = 0x9287;
174 enum GL_SRC_OVER_NV = 0x9288;
175 enum GL_DST_OVER_NV = 0x9289;
176 enum GL_SRC_IN_NV = 0x928A;
177 enum GL_DST_IN_NV = 0x928B;
178 enum GL_SRC_OUT_NV = 0x928C;
179 enum GL_DST_OUT_NV = 0x928D;
180 enum GL_SRC_ATOP_NV = 0x928E;
181 enum GL_DST_ATOP_NV = 0x928F;
182 enum GL_XOR_NV = 0x1506;
183 enum GL_MULTIPLY_NV = 0x9294;
184 enum GL_SCREEN_NV = 0x9295;
185 enum GL_OVERLAY_NV = 0x9296;
186 enum GL_DARKEN_NV = 0x9297;
187 enum GL_LIGHTEN_NV = 0x9298;
188 enum GL_COLORDODGE_NV = 0x9299;
189 enum GL_COLORBURN_NV = 0x929A;
190 enum GL_HARDLIGHT_NV = 0x929B;
191 enum GL_SOFTLIGHT_NV = 0x929C;
192 enum GL_DIFFERENCE_NV = 0x929E;
193 enum GL_EXCLUSION_NV = 0x92A0;
194 enum GL_INVERT_RGB_NV = 0x92A3;
195 enum GL_LINEARDODGE_NV = 0x92A4;
196 enum GL_LINEARBURN_NV = 0x92A5;
197 enum GL_VIVIDLIGHT_NV = 0x92A6;
198 enum GL_LINEARLIGHT_NV = 0x92A7;
199 enum GL_PINLIGHT_NV = 0x92A8;
200 enum GL_MINUS_NV = 0x929F;
201 enum MINUS_CLAMPED_NV = 0x92B3;
202 
203 void inSetBlendMode(BlendMode blendingMode) {
204     if (!inAdvancedBlending) {
205         switch(blendingMode) {
206             
207             // If the advanced blending extension is not supported, force to Normal blending
208             default:
209                 glBlendEquation(GL_FUNC_ADD);
210                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
211 
212             case BlendMode.Normal: 
213                 glBlendEquation(GL_FUNC_ADD);
214                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
215 
216             case BlendMode.Multiply: 
217                 glBlendEquation(GL_FUNC_ADD);
218                 glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break;
219 
220             case BlendMode.Screen:
221                 glBlendEquation(GL_FUNC_ADD);
222                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); break;
223 
224             case BlendMode.Lighten:
225                 glBlendEquation(GL_MAX);
226                 glBlendFunc(GL_ONE, GL_ONE); break;
227 
228             case BlendMode.ColorDodge:
229                 glBlendEquation(GL_FUNC_ADD);
230                 glBlendFunc(GL_DST_COLOR, GL_ONE); break;
231 
232             case BlendMode.LinearDodge:
233                 glBlendEquation(GL_FUNC_ADD);
234                 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE); break;
235 
236             case BlendMode.Subtract:
237                 glBlendEquationSeparate(GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD);
238                 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE); break;
239 
240             case BlendMode.Exclusion:
241                 glBlendEquation(GL_FUNC_ADD);
242                 glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE); break;
243 
244             case BlendMode.Inverse:
245                 glBlendEquation(GL_FUNC_ADD);
246                 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break;
247             
248             case BlendMode.DestinationIn:
249                 glBlendEquation(GL_FUNC_ADD);
250                 glBlendFunc(GL_ZERO, GL_SRC_ALPHA); break;
251 
252             case BlendMode.ClipToLower:
253                 glBlendEquation(GL_FUNC_ADD);
254                 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break;
255 
256             case BlendMode.SliceFromLower:
257                 glBlendEquation(GL_FUNC_ADD);
258                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); break;
259         }
260     } else {
261         switch(blendingMode) {
262             case BlendMode.Normal: 
263                 glBlendEquation(GL_FUNC_ADD);
264                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
265 
266             case BlendMode.Multiply: glBlendEquation(GL_MULTIPLY_KHR); break;
267             case BlendMode.Screen: glBlendEquation(GL_SCREEN_KHR); break;
268             case BlendMode.Overlay: glBlendEquation(GL_OVERLAY_KHR); break;
269             case BlendMode.Darken: glBlendEquation(GL_DARKEN_KHR); break;
270             case BlendMode.Lighten: glBlendEquation(GL_LIGHTEN_KHR); break;
271             case BlendMode.ColorDodge: glBlendEquation(GL_COLORDODGE_KHR); break;
272             case BlendMode.LinearDodge:
273                 if (inAdvancedBlendingNV) {
274                     glBlendEquation(GL_LINEARDODGE_NV);
275                     glBlendFunc(GL_ONE, GL_ONE); break;
276                 } else {
277                     glBlendEquation(GL_FUNC_ADD);
278                     glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE); break;
279                 }
280 
281             case BlendMode.ColorBurn: glBlendEquation(GL_COLORBURN_KHR); break;
282             case BlendMode.HardLight: glBlendEquation(GL_HARDLIGHT_KHR); break;
283             case BlendMode.SoftLight: glBlendEquation(GL_SOFTLIGHT_KHR); break;
284             case BlendMode.Difference: glBlendEquation(GL_DIFFERENCE_KHR); break;
285             case BlendMode.Exclusion: glBlendEquation(GL_EXCLUSION_KHR); break;
286 
287             case BlendMode.Subtract:
288                 if (inAdvancedBlendingNV) {
289                     glBlendEquation(MINUS_CLAMPED_NV);
290                     glBlendFunc(GL_ONE, GL_ONE); break;
291                 } else {
292                     glBlendEquationSeparate(GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD);
293                     glBlendFunc(GL_ONE, GL_ONE); break;
294                 }
295 
296             case BlendMode.Inverse:
297                 if (inAdvancedBlendingNV) {
298                     glBlendEquation(GL_INVERT);
299                     glBlendFunc(GL_ONE, GL_ONE); break;
300                 } else {
301                     glBlendEquation(GL_FUNC_ADD);
302                     glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break;
303                 }
304                         
305             case BlendMode.DestinationIn:
306                 glBlendEquation(GL_FUNC_ADD);
307                 glBlendFunc(GL_ZERO, GL_SRC_ALPHA); break;
308 
309             case BlendMode.ClipToLower:
310                 glBlendEquation(GL_FUNC_ADD);
311                 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break;
312             
313             case BlendMode.SliceFromLower:
314                 glBlendEquation(GL_FUNC_ADD);
315                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); break;
316             
317             default: assert(0);
318         }
319     }
320 }
321 
322 void inBlendModeBarrier() {
323     
324     // TODO: bindbc-opengl needs to support KHR_blend_equation_advanced
325     // Before we can have non-coherent blending
326     // if (inAdvancedBlending && !inAdvancedBlendingCoherent) glBlendBarrierKHR();
327 }
328 
329 /**
330     Masking mode
331 */
332 enum MaskingMode {
333 
334     /**
335         The part should be masked by the drawables specified
336     */
337     Mask,
338 
339     /**
340         The path should be dodge masked by the drawables specified
341     */
342     DodgeMask
343 }
344 
345 /**
346     A binding between a mask and a mode
347 */
348 struct MaskBinding {
349 public:
350     import inochi2d.core.nodes.drawable : Drawable;
351     @Name("source")
352     uint maskSrcUUID;
353 
354     @Name("mode")
355     MaskingMode mode;
356     
357     @Ignore
358     Drawable maskSrc;
359 }