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 
19     void inSetBlendModeLegacy(BlendMode blendingMode) {
20         switch(blendingMode) {
21             
22             // If the advanced blending extension is not supported, force to Normal blending
23             default:
24                 glBlendEquation(GL_FUNC_ADD);
25                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
26 
27             case BlendMode.Normal: 
28                 glBlendEquation(GL_FUNC_ADD);
29                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
30 
31             case BlendMode.Multiply: 
32                 glBlendEquation(GL_FUNC_ADD);
33                 glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break;
34 
35             case BlendMode.Screen:
36                 glBlendEquation(GL_FUNC_ADD);
37                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); break;
38 
39             case BlendMode.Lighten:
40                 glBlendEquation(GL_MAX);
41                 glBlendFunc(GL_ONE, GL_ONE); break;
42 
43             case BlendMode.ColorDodge:
44                 glBlendEquation(GL_FUNC_ADD);
45                 glBlendFunc(GL_DST_COLOR, GL_ONE); break;
46 
47             case BlendMode.LinearDodge:
48                 glBlendEquation(GL_FUNC_ADD);
49                 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
50                 
51             case BlendMode.AddGlow:
52                 glBlendEquation(GL_FUNC_ADD);
53                 glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break;
54 
55             case BlendMode.Subtract:
56                 glBlendEquationSeparate(GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD);
57                 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE); break;
58 
59             case BlendMode.Exclusion:
60                 glBlendEquation(GL_FUNC_ADD);
61                 glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE); break;
62 
63             case BlendMode.Inverse:
64                 glBlendEquation(GL_FUNC_ADD);
65                 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break;
66             
67             case BlendMode.DestinationIn:
68                 glBlendEquation(GL_FUNC_ADD);
69                 glBlendFunc(GL_ZERO, GL_SRC_ALPHA); break;
70 
71             case BlendMode.ClipToLower:
72                 glBlendEquation(GL_FUNC_ADD);
73                 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break;
74 
75             case BlendMode.SliceFromLower:
76                 glBlendEquation(GL_FUNC_ADD);
77                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); break;
78         }
79     }
80 }
81 
82 /**
83     Whether a multi-stage rendering pass should be used for blending
84 */
85 bool inUseMultistageBlending(BlendMode blendingMode) {
86     switch(blendingMode) {
87         case BlendMode.Normal,
88              BlendMode.LinearDodge,
89              BlendMode.AddGlow,
90              BlendMode.Subtract,
91              BlendMode.Inverse,
92              BlendMode.DestinationIn,
93              BlendMode.ClipToLower,
94              BlendMode.SliceFromLower:
95                  return false;
96         default: return hasKHRBlendEquationAdvanced;
97     }
98 }
99 
100 void inInitBlending() {
101     
102     if (hasKHRBlendEquationAdvanced) inAdvancedBlending = true;
103     if (hasKHRBlendEquationAdvancedCoherent) inAdvancedBlendingCoherent = true;
104     if (inAdvancedBlendingCoherent) glEnable(GL_BLEND_ADVANCED_COHERENT_KHR);
105 }
106 
107 /*
108     INFORMATION ABOUT BLENDING MODES
109     Blending is a complicated topic, especially once we get to mobile devices and games consoles.
110 
111     The following blending modes are supported in Standard mode:
112         Normal
113         Multiply
114         Screen
115         Overlay
116         Darken
117         Lighten
118         Color Dodge
119         Linear Dodge
120         Add (Glow)
121         Color Burn
122         Hard Light
123         Soft Light
124         Difference
125         Exclusion
126         Subtract
127         Inverse
128         Destination In
129         Clip To Lower
130         Slice from Lower
131     Some of these blending modes behave better on Tiling GPUs.
132 
133     The following blending modes are supported in Core mode:
134         Normal
135         Multiply
136         Screen
137         Lighten
138         Color Dodge
139         Linear Dodge
140         Add (Glow)
141         Inverse
142         Destination In
143         Clip to Lower
144         Slice from Lower
145     Tiling GPUs on older mobile devices don't have great drivers, we shouldn't tempt fate.
146 */
147 
148 /**
149     Blending modes
150 */
151 enum BlendMode {
152     // Normal blending mode
153     Normal,
154 
155     // Multiply blending mode
156     Multiply,
157 
158     // Screen
159     Screen,
160 
161     // Overlay
162     Overlay,
163 
164     // Darken
165     Darken,
166 
167     // Lighten
168     Lighten,
169     
170     // Color Dodge
171     ColorDodge,
172 
173     // Linear Dodge
174     LinearDodge,
175 
176     // Add (Glow)
177     AddGlow,
178 
179     // Color Burn
180     ColorBurn,
181 
182     // Hard Light
183     HardLight,
184 
185     // Soft Light
186     SoftLight,
187 
188     // Difference
189     Difference,
190 
191     // Exclusion
192     Exclusion,
193 
194     // Subtract
195     Subtract,
196 
197     // Inverse
198     Inverse,
199 
200     // Destination In
201     DestinationIn,
202 
203     // Clip to Lower
204     // Special blending mode that clips the drawable
205     // to a lower rendered area.
206     ClipToLower,
207 
208     // Slice from Lower
209     // Special blending mode that slices the drawable
210     // via a lower rendered area.
211     // Basically inverse ClipToLower
212     SliceFromLower
213 }
214 
215 bool inIsAdvancedBlendMode(BlendMode mode) {
216     if (!inAdvancedBlending) return false;
217     switch(mode) {
218         case BlendMode.Multiply:
219         case BlendMode.Screen: 
220         case BlendMode.Overlay: 
221         case BlendMode.Darken: 
222         case BlendMode.Lighten: 
223         case BlendMode.ColorDodge: 
224         case BlendMode.ColorBurn: 
225         case BlendMode.HardLight: 
226         case BlendMode.SoftLight: 
227         case BlendMode.Difference: 
228         case BlendMode.Exclusion: 
229             return true;
230         
231         // Fallback to legacy
232         default: 
233             return false;
234     }
235 }
236 
237 void inSetBlendMode(BlendMode blendingMode, bool legacyOnly=false) {
238     if (!inAdvancedBlending || legacyOnly) inSetBlendModeLegacy(blendingMode);
239     else switch(blendingMode) {
240         case BlendMode.Multiply: glBlendEquation(GL_MULTIPLY_KHR); break;
241         case BlendMode.Screen: glBlendEquation(GL_SCREEN_KHR); break;
242         case BlendMode.Overlay: glBlendEquation(GL_OVERLAY_KHR); break;
243         case BlendMode.Darken: glBlendEquation(GL_DARKEN_KHR); break;
244         case BlendMode.Lighten: glBlendEquation(GL_LIGHTEN_KHR); break;
245         case BlendMode.ColorDodge: glBlendEquation(GL_COLORDODGE_KHR); break;
246         case BlendMode.ColorBurn: glBlendEquation(GL_COLORBURN_KHR); break;
247         case BlendMode.HardLight: glBlendEquation(GL_HARDLIGHT_KHR); break;
248         case BlendMode.SoftLight: glBlendEquation(GL_SOFTLIGHT_KHR); break;
249         case BlendMode.Difference: glBlendEquation(GL_DIFFERENCE_KHR); break;
250         case BlendMode.Exclusion: glBlendEquation(GL_EXCLUSION_KHR); break;
251         
252         // Fallback to legacy
253         default: inSetBlendModeLegacy(blendingMode); break;
254     }
255 }
256 
257 void inBlendModeBarrier(BlendMode mode) {
258     if (inAdvancedBlending && !inAdvancedBlendingCoherent && inIsAdvancedBlendMode(mode)) 
259         glBlendBarrierKHR();
260 }
261 
262 /**
263     Masking mode
264 */
265 enum MaskingMode {
266 
267     /**
268         The part should be masked by the drawables specified
269     */
270     Mask,
271 
272     /**
273         The path should be dodge masked by the drawables specified
274     */
275     DodgeMask
276 }
277 
278 /**
279     A binding between a mask and a mode
280 */
281 struct MaskBinding {
282 public:
283     import inochi2d.core.nodes.drawable : Drawable;
284     @Name("source")
285     uint maskSrcUUID;
286 
287     @Name("mode")
288     MaskingMode mode;
289     
290     @Ignore
291     Drawable maskSrc;
292 }