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 }