| 1 | // Aseprite Document Library |
| 2 | // Copyright (c) 2019-2020 Igara Studio S.A. |
| 3 | // Copyright (c) 2001-2017 David Capello |
| 4 | // |
| 5 | // This file is released under the terms of the MIT license. |
| 6 | // Read LICENSE.txt for more information. |
| 7 | // |
| 8 | // -- |
| 9 | // |
| 10 | // Some references about alpha compositing and blend modes: |
| 11 | // |
| 12 | // http://dev.w3.org/fxtf/compositing-1/ |
| 13 | // http://www.adobe.com/devnet/pdf/pdf_reference.html |
| 14 | // |
| 15 | |
| 16 | #ifdef HAVE_CONFIG_H |
| 17 | #include "config.h" |
| 18 | #endif |
| 19 | |
| 20 | #include "doc/blend_funcs.h" |
| 21 | |
| 22 | #include "base/debug.h" |
| 23 | #include "doc/blend_internals.h" |
| 24 | |
| 25 | #include <algorithm> |
| 26 | #include <cmath> |
| 27 | |
| 28 | namespace { |
| 29 | |
| 30 | #define blend_multiply(b, s, t) (MUL_UN8((b), (s), (t))) |
| 31 | #define blend_screen(b, s, t) ((b) + (s) - MUL_UN8((b), (s), (t))) |
| 32 | #define blend_overlay(b, s, t) (blend_hard_light(s, b, t)) |
| 33 | #define blend_darken(b, s) (std::min((b), (s))) |
| 34 | #define blend_lighten(b, s) (std::max((b), (s))) |
| 35 | #define blend_hard_light(b, s, t) ((s) < 128 ? \ |
| 36 | blend_multiply((b), (s)<<1, (t)): \ |
| 37 | blend_screen((b), ((s)<<1)-255, (t))) |
| 38 | #define blend_difference(b, s) (ABS((b) - (s))) |
| 39 | #define blend_exclusion(b, s, t) ((t) = MUL_UN8((b), (s), (t)), ((b) + (s) - 2*(t))) |
| 40 | |
| 41 | // New Blender Method macros |
| 42 | #define RGBA_BLENDER_N(name) \ |
| 43 | color_t rgba_blender_##name##_n(color_t backdrop, color_t src, int opacity) { \ |
| 44 | if (backdrop & rgba_a_mask) { \ |
| 45 | color_t normal = rgba_blender_normal(backdrop, src, opacity); \ |
| 46 | color_t blend = rgba_blender_##name(backdrop, src, opacity); \ |
| 47 | int Ba = rgba_geta(backdrop); \ |
| 48 | color_t normalToBlendMerge = rgba_blender_merge(normal, blend, Ba); \ |
| 49 | int t; \ |
| 50 | int srcTotalAlpha = MUL_UN8(rgba_geta(src), opacity, t); \ |
| 51 | int compositeAlpha = MUL_UN8(Ba, srcTotalAlpha, t); \ |
| 52 | return rgba_blender_merge(normalToBlendMerge, blend, compositeAlpha); \ |
| 53 | } \ |
| 54 | else \ |
| 55 | return rgba_blender_normal(backdrop, src, opacity); \ |
| 56 | } |
| 57 | |
| 58 | #define GRAYA_BLENDER_N(name) \ |
| 59 | color_t graya_blender_##name##_n(color_t backdrop, color_t src, int opacity) { \ |
| 60 | if (backdrop & graya_a_mask) { \ |
| 61 | color_t normal = graya_blender_normal(backdrop, src, opacity); \ |
| 62 | color_t blend = graya_blender_##name(backdrop, src, opacity); \ |
| 63 | int Ba = graya_geta(backdrop); \ |
| 64 | color_t normalToBlendMerge = graya_blender_merge(normal, blend, Ba); \ |
| 65 | int t; \ |
| 66 | int srcTotalAlpha = MUL_UN8(graya_geta(src), opacity, t); \ |
| 67 | int compositeAlpha = MUL_UN8(Ba, srcTotalAlpha, t); \ |
| 68 | return graya_blender_merge(normalToBlendMerge, blend, compositeAlpha); \ |
| 69 | } \ |
| 70 | else \ |
| 71 | return graya_blender_normal(backdrop, src, opacity); \ |
| 72 | } |
| 73 | |
| 74 | inline uint32_t blend_divide(uint32_t b, uint32_t s) |
| 75 | { |
| 76 | if (b == 0) |
| 77 | return 0; |
| 78 | else if (b >= s) |
| 79 | return 255; |
| 80 | else |
| 81 | return DIV_UN8(b, s); // return b / s |
| 82 | } |
| 83 | |
| 84 | inline uint32_t blend_color_dodge(uint32_t b, uint32_t s) |
| 85 | { |
| 86 | if (b == 0) |
| 87 | return 0; |
| 88 | |
| 89 | s = (255 - s); |
| 90 | if (b >= s) |
| 91 | return 255; |
| 92 | else |
| 93 | return DIV_UN8(b, s); // return b / (1-s) |
| 94 | } |
| 95 | |
| 96 | inline uint32_t blend_color_burn(uint32_t b, uint32_t s) |
| 97 | { |
| 98 | if (b == 255) |
| 99 | return 255; |
| 100 | |
| 101 | b = (255 - b); |
| 102 | if (b >= s) |
| 103 | return 0; |
| 104 | else |
| 105 | return 255 - DIV_UN8(b, s); // return 1 - ((1-b)/s) |
| 106 | } |
| 107 | |
| 108 | inline uint32_t blend_soft_light(uint32_t _b, uint32_t _s) |
| 109 | { |
| 110 | double b = _b / 255.0; |
| 111 | double s = _s / 255.0; |
| 112 | double r, d; |
| 113 | |
| 114 | if (b <= 0.25) |
| 115 | d = ((16*b-12)*b+4)*b; |
| 116 | else |
| 117 | d = std::sqrt(b); |
| 118 | |
| 119 | if (s <= 0.5) |
| 120 | r = b - (1.0 - 2.0 * s) * b * (1.0 - b); |
| 121 | else |
| 122 | r = b + (2.0 * s - 1.0) * (d - b); |
| 123 | |
| 124 | return (uint32_t)(r * 255 + 0.5); |
| 125 | } |
| 126 | |
| 127 | } // annonymous namespace |
| 128 | |
| 129 | namespace doc { |
| 130 | |
| 131 | ////////////////////////////////////////////////////////////////////// |
| 132 | // RGB blenders |
| 133 | |
| 134 | color_t rgba_blender_src(color_t backdrop, color_t src, int opacity) |
| 135 | { |
| 136 | return src; |
| 137 | } |
| 138 | |
| 139 | color_t rgba_blender_merge(color_t backdrop, color_t src, int opacity) |
| 140 | { |
| 141 | int Br, Bg, Bb, Ba; |
| 142 | int Sr, Sg, Sb, Sa; |
| 143 | int Rr, Rg, Rb, Ra; |
| 144 | int t; |
| 145 | |
| 146 | Br = rgba_getr(backdrop); |
| 147 | Bg = rgba_getg(backdrop); |
| 148 | Bb = rgba_getb(backdrop); |
| 149 | Ba = rgba_geta(backdrop); |
| 150 | |
| 151 | Sr = rgba_getr(src); |
| 152 | Sg = rgba_getg(src); |
| 153 | Sb = rgba_getb(src); |
| 154 | Sa = rgba_geta(src); |
| 155 | |
| 156 | if (Ba == 0) { |
| 157 | Rr = Sr; |
| 158 | Rg = Sg; |
| 159 | Rb = Sb; |
| 160 | } |
| 161 | else if (Sa == 0) { |
| 162 | Rr = Br; |
| 163 | Rg = Bg; |
| 164 | Rb = Bb; |
| 165 | } |
| 166 | else { |
| 167 | Rr = Br + MUL_UN8((Sr - Br), opacity, t); |
| 168 | Rg = Bg + MUL_UN8((Sg - Bg), opacity, t); |
| 169 | Rb = Bb + MUL_UN8((Sb - Bb), opacity, t); |
| 170 | } |
| 171 | Ra = Ba + MUL_UN8((Sa - Ba), opacity, t); |
| 172 | if (Ra == 0) |
| 173 | Rr = Rg = Rb = 0; |
| 174 | |
| 175 | return rgba(Rr, Rg, Rb, Ra); |
| 176 | } |
| 177 | |
| 178 | color_t rgba_blender_neg_bw(color_t backdrop, color_t src, int opacity) |
| 179 | { |
| 180 | if (!(backdrop & rgba_a_mask)) |
| 181 | return rgba(0, 0, 0, 255); |
| 182 | else if (rgba_luma(backdrop) < 128) |
| 183 | return rgba(255, 255, 255, 255); |
| 184 | else |
| 185 | return rgba(0, 0, 0, 255); |
| 186 | } |
| 187 | |
| 188 | color_t rgba_blender_red_tint(color_t backdrop, color_t src, int opacity) |
| 189 | { |
| 190 | int v = rgba_luma(src); |
| 191 | src = rgba((255+v)/2, v/2, v/2, rgba_geta(src)); |
| 192 | return rgba_blender_normal(backdrop, src, opacity); |
| 193 | } |
| 194 | |
| 195 | color_t rgba_blender_blue_tint(color_t backdrop, color_t src, int opacity) |
| 196 | { |
| 197 | int v = rgba_luma(src); |
| 198 | src = rgba(v/2, v/2, (255+v)/2, rgba_geta(src)); |
| 199 | return rgba_blender_normal(backdrop, src, opacity); |
| 200 | } |
| 201 | |
| 202 | color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity) |
| 203 | { |
| 204 | int t; |
| 205 | |
| 206 | if (!(backdrop & rgba_a_mask)) { |
| 207 | int a = rgba_geta(src); |
| 208 | a = MUL_UN8(a, opacity, t); |
| 209 | a <<= rgba_a_shift; |
| 210 | return (src & rgba_rgb_mask) | a; |
| 211 | } |
| 212 | else if (!(src & rgba_a_mask)) { |
| 213 | return backdrop; |
| 214 | } |
| 215 | |
| 216 | const int Br = rgba_getr(backdrop); |
| 217 | const int Bg = rgba_getg(backdrop); |
| 218 | const int Bb = rgba_getb(backdrop); |
| 219 | const int Ba = rgba_geta(backdrop); |
| 220 | |
| 221 | const int Sr = rgba_getr(src); |
| 222 | const int Sg = rgba_getg(src); |
| 223 | const int Sb = rgba_getb(src); |
| 224 | int Sa = rgba_geta(src); |
| 225 | Sa = MUL_UN8(Sa, opacity, t); |
| 226 | |
| 227 | // Ra = Sa + Ba*(1-Sa) |
| 228 | // = Sa + Ba - Ba*Sa |
| 229 | const int Ra = Sa + Ba - MUL_UN8(Ba, Sa, t); |
| 230 | |
| 231 | // Ra = Sa + Ba*(1-Sa) |
| 232 | // Ba = (Ra-Sa) / (1-Sa) |
| 233 | // Rc = (Sc*Sa + Bc*Ba*(1-Sa)) / Ra Replacing Ba with (Ra-Sa) / (1-Sa)... |
| 234 | // = (Sc*Sa + Bc*(Ra-Sa)/(1-Sa)*(1-Sa)) / Ra |
| 235 | // = (Sc*Sa + Bc*(Ra-Sa)) / Ra |
| 236 | // = Sc*Sa/Ra + Bc*Ra/Ra - Bc*Sa/Ra |
| 237 | // = Sc*Sa/Ra + Bc - Bc*Sa/Ra |
| 238 | // = Bc + (Sc-Bc)*Sa/Ra |
| 239 | const int Rr = Br + (Sr-Br) * Sa / Ra; |
| 240 | const int Rg = Bg + (Sg-Bg) * Sa / Ra; |
| 241 | const int Rb = Bb + (Sb-Bb) * Sa / Ra; |
| 242 | |
| 243 | return rgba(Rr, Rg, Rb, Ra); |
| 244 | } |
| 245 | |
| 246 | color_t rgba_blender_normal_dst_over(color_t backdrop, color_t src, int opacity) |
| 247 | { |
| 248 | int t; |
| 249 | int Sa = MUL_UN8(rgba_geta(src), opacity, t); |
| 250 | src = (src & rgba_rgb_mask) | (Sa << rgba_a_shift); |
| 251 | return rgba_blender_normal(src, backdrop); |
| 252 | } |
| 253 | |
| 254 | color_t rgba_blender_multiply(color_t backdrop, color_t src, int opacity) |
| 255 | { |
| 256 | int t; |
| 257 | int r = blend_multiply(rgba_getr(backdrop), rgba_getr(src), t); |
| 258 | int g = blend_multiply(rgba_getg(backdrop), rgba_getg(src), t); |
| 259 | int b = blend_multiply(rgba_getb(backdrop), rgba_getb(src), t); |
| 260 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 261 | return rgba_blender_normal(backdrop, src, opacity); |
| 262 | } |
| 263 | |
| 264 | color_t rgba_blender_screen(color_t backdrop, color_t src, int opacity) |
| 265 | { |
| 266 | int t; |
| 267 | int r = blend_screen(rgba_getr(backdrop), rgba_getr(src), t); |
| 268 | int g = blend_screen(rgba_getg(backdrop), rgba_getg(src), t); |
| 269 | int b = blend_screen(rgba_getb(backdrop), rgba_getb(src), t); |
| 270 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 271 | return rgba_blender_normal(backdrop, src, opacity); |
| 272 | } |
| 273 | |
| 274 | color_t rgba_blender_overlay(color_t backdrop, color_t src, int opacity) |
| 275 | { |
| 276 | int t; |
| 277 | int r = blend_overlay(rgba_getr(backdrop), rgba_getr(src), t); |
| 278 | int g = blend_overlay(rgba_getg(backdrop), rgba_getg(src), t); |
| 279 | int b = blend_overlay(rgba_getb(backdrop), rgba_getb(src), t); |
| 280 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 281 | return rgba_blender_normal(backdrop, src, opacity); |
| 282 | } |
| 283 | |
| 284 | color_t rgba_blender_darken(color_t backdrop, color_t src, int opacity) |
| 285 | { |
| 286 | int r = blend_darken(rgba_getr(backdrop), rgba_getr(src)); |
| 287 | int g = blend_darken(rgba_getg(backdrop), rgba_getg(src)); |
| 288 | int b = blend_darken(rgba_getb(backdrop), rgba_getb(src)); |
| 289 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 290 | return rgba_blender_normal(backdrop, src, opacity); |
| 291 | } |
| 292 | |
| 293 | color_t rgba_blender_lighten(color_t backdrop, color_t src, int opacity) |
| 294 | { |
| 295 | int r = blend_lighten(rgba_getr(backdrop), rgba_getr(src)); |
| 296 | int g = blend_lighten(rgba_getg(backdrop), rgba_getg(src)); |
| 297 | int b = blend_lighten(rgba_getb(backdrop), rgba_getb(src)); |
| 298 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 299 | return rgba_blender_normal(backdrop, src, opacity); |
| 300 | } |
| 301 | |
| 302 | color_t rgba_blender_color_dodge(color_t backdrop, color_t src, int opacity) |
| 303 | { |
| 304 | int r = blend_color_dodge(rgba_getr(backdrop), rgba_getr(src)); |
| 305 | int g = blend_color_dodge(rgba_getg(backdrop), rgba_getg(src)); |
| 306 | int b = blend_color_dodge(rgba_getb(backdrop), rgba_getb(src)); |
| 307 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 308 | return rgba_blender_normal(backdrop, src, opacity); |
| 309 | } |
| 310 | |
| 311 | color_t rgba_blender_color_burn(color_t backdrop, color_t src, int opacity) |
| 312 | { |
| 313 | int r = blend_color_burn(rgba_getr(backdrop), rgba_getr(src)); |
| 314 | int g = blend_color_burn(rgba_getg(backdrop), rgba_getg(src)); |
| 315 | int b = blend_color_burn(rgba_getb(backdrop), rgba_getb(src)); |
| 316 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 317 | return rgba_blender_normal(backdrop, src, opacity); |
| 318 | } |
| 319 | |
| 320 | color_t rgba_blender_hard_light(color_t backdrop, color_t src, int opacity) |
| 321 | { |
| 322 | int t; |
| 323 | int r = blend_hard_light(rgba_getr(backdrop), rgba_getr(src), t); |
| 324 | int g = blend_hard_light(rgba_getg(backdrop), rgba_getg(src), t); |
| 325 | int b = blend_hard_light(rgba_getb(backdrop), rgba_getb(src), t); |
| 326 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 327 | return rgba_blender_normal(backdrop, src, opacity); |
| 328 | } |
| 329 | |
| 330 | color_t rgba_blender_soft_light(color_t backdrop, color_t src, int opacity) |
| 331 | { |
| 332 | int r = blend_soft_light(rgba_getr(backdrop), rgba_getr(src)); |
| 333 | int g = blend_soft_light(rgba_getg(backdrop), rgba_getg(src)); |
| 334 | int b = blend_soft_light(rgba_getb(backdrop), rgba_getb(src)); |
| 335 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 336 | return rgba_blender_normal(backdrop, src, opacity); |
| 337 | } |
| 338 | |
| 339 | color_t rgba_blender_difference(color_t backdrop, color_t src, int opacity) |
| 340 | { |
| 341 | int r = blend_difference(rgba_getr(backdrop), rgba_getr(src)); |
| 342 | int g = blend_difference(rgba_getg(backdrop), rgba_getg(src)); |
| 343 | int b = blend_difference(rgba_getb(backdrop), rgba_getb(src)); |
| 344 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 345 | return rgba_blender_normal(backdrop, src, opacity); |
| 346 | } |
| 347 | |
| 348 | color_t rgba_blender_exclusion(color_t backdrop, color_t src, int opacity) |
| 349 | { |
| 350 | int t; |
| 351 | int r = blend_exclusion(rgba_getr(backdrop), rgba_getr(src), t); |
| 352 | int g = blend_exclusion(rgba_getg(backdrop), rgba_getg(src), t); |
| 353 | int b = blend_exclusion(rgba_getb(backdrop), rgba_getb(src), t); |
| 354 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 355 | return rgba_blender_normal(backdrop, src, opacity); |
| 356 | } |
| 357 | |
| 358 | ////////////////////////////////////////////////////////////////////// |
| 359 | // HSV blenders |
| 360 | |
| 361 | static double lum(double r, double g, double b) |
| 362 | { |
| 363 | return 0.3*r + 0.59*g + 0.11*b; |
| 364 | } |
| 365 | |
| 366 | static double sat(double r, double g, double b) |
| 367 | { |
| 368 | return std::max(r, std::max(g, b)) - std::min(r, std::min(g, b)); |
| 369 | } |
| 370 | |
| 371 | static void clip_color(double& r, double& g, double& b) |
| 372 | { |
| 373 | double l = lum(r, g, b); |
| 374 | double n = std::min(r, std::min(g, b)); |
| 375 | double x = std::max(r, std::max(g, b)); |
| 376 | |
| 377 | if (n < 0) { |
| 378 | r = l + (((r - l) * l) / (l - n)); |
| 379 | g = l + (((g - l) * l) / (l - n)); |
| 380 | b = l + (((b - l) * l) / (l - n)); |
| 381 | } |
| 382 | |
| 383 | if (x > 1) { |
| 384 | r = l + (((r - l) * (1 - l)) / (x - l)); |
| 385 | g = l + (((g - l) * (1 - l)) / (x - l)); |
| 386 | b = l + (((b - l) * (1 - l)) / (x - l)); |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | static void set_lum(double& r, double& g, double& b, double l) |
| 391 | { |
| 392 | double d = l - lum(r, g, b); |
| 393 | r += d; |
| 394 | g += d; |
| 395 | b += d; |
| 396 | clip_color(r, g, b); |
| 397 | } |
| 398 | |
| 399 | // TODO replace this with a better impl (and test this, not sure if it's correct) |
| 400 | static void set_sat(double& r, double& g, double& b, double s) |
| 401 | { |
| 402 | #undef MIN |
| 403 | #undef MAX |
| 404 | #undef MID |
| 405 | #define MIN(x,y) (((x) < (y)) ? (x) : (y)) |
| 406 | #define MAX(x,y) (((x) > (y)) ? (x) : (y)) |
| 407 | #define MID(x,y,z) ((x) > (y) ? ((y) > (z) ? (y) : ((x) > (z) ? \ |
| 408 | (z) : (x))) : ((y) > (z) ? ((z) > (x) ? (z) : \ |
| 409 | (x)): (y))) |
| 410 | |
| 411 | double& min = MIN(r, MIN(g, b)); |
| 412 | double& mid = MID(r, g, b); |
| 413 | double& max = MAX(r, MAX(g, b)); |
| 414 | |
| 415 | if (max > min) { |
| 416 | mid = ((mid - min)*s) / (max - min); |
| 417 | max = s; |
| 418 | } |
| 419 | else |
| 420 | mid = max = 0; |
| 421 | |
| 422 | min = 0; |
| 423 | } |
| 424 | |
| 425 | color_t rgba_blender_hsl_hue(color_t backdrop, color_t src, int opacity) |
| 426 | { |
| 427 | double r = rgba_getr(backdrop)/255.0; |
| 428 | double g = rgba_getg(backdrop)/255.0; |
| 429 | double b = rgba_getb(backdrop)/255.0; |
| 430 | double s = sat(r, g, b); |
| 431 | double l = lum(r, g, b); |
| 432 | |
| 433 | r = rgba_getr(src)/255.0; |
| 434 | g = rgba_getg(src)/255.0; |
| 435 | b = rgba_getb(src)/255.0; |
| 436 | |
| 437 | set_sat(r, g, b, s); |
| 438 | set_lum(r, g, b, l); |
| 439 | |
| 440 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
| 441 | return rgba_blender_normal(backdrop, src, opacity); |
| 442 | } |
| 443 | |
| 444 | color_t rgba_blender_hsl_saturation(color_t backdrop, color_t src, int opacity) |
| 445 | { |
| 446 | double r = rgba_getr(src)/255.0; |
| 447 | double g = rgba_getg(src)/255.0; |
| 448 | double b = rgba_getb(src)/255.0; |
| 449 | double s = sat(r, g, b); |
| 450 | |
| 451 | r = rgba_getr(backdrop)/255.0; |
| 452 | g = rgba_getg(backdrop)/255.0; |
| 453 | b = rgba_getb(backdrop)/255.0; |
| 454 | double l = lum(r, g, b); |
| 455 | |
| 456 | set_sat(r, g, b, s); |
| 457 | set_lum(r, g, b, l); |
| 458 | |
| 459 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
| 460 | return rgba_blender_normal(backdrop, src, opacity); |
| 461 | } |
| 462 | |
| 463 | color_t rgba_blender_hsl_color(color_t backdrop, color_t src, int opacity) |
| 464 | { |
| 465 | double r = rgba_getr(backdrop)/255.0; |
| 466 | double g = rgba_getg(backdrop)/255.0; |
| 467 | double b = rgba_getb(backdrop)/255.0; |
| 468 | double l = lum(r, g, b); |
| 469 | |
| 470 | r = rgba_getr(src)/255.0; |
| 471 | g = rgba_getg(src)/255.0; |
| 472 | b = rgba_getb(src)/255.0; |
| 473 | |
| 474 | set_lum(r, g, b, l); |
| 475 | |
| 476 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
| 477 | return rgba_blender_normal(backdrop, src, opacity); |
| 478 | } |
| 479 | |
| 480 | color_t rgba_blender_hsl_luminosity(color_t backdrop, color_t src, int opacity) |
| 481 | { |
| 482 | double r = rgba_getr(src)/255.0; |
| 483 | double g = rgba_getg(src)/255.0; |
| 484 | double b = rgba_getb(src)/255.0; |
| 485 | double l = lum(r, g, b); |
| 486 | |
| 487 | r = rgba_getr(backdrop)/255.0; |
| 488 | g = rgba_getg(backdrop)/255.0; |
| 489 | b = rgba_getb(backdrop)/255.0; |
| 490 | |
| 491 | set_lum(r, g, b, l); |
| 492 | |
| 493 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
| 494 | return rgba_blender_normal(backdrop, src, opacity); |
| 495 | } |
| 496 | |
| 497 | color_t rgba_blender_addition(color_t backdrop, color_t src, int opacity) |
| 498 | { |
| 499 | int r = rgba_getr(backdrop) + rgba_getr(src); |
| 500 | int g = rgba_getg(backdrop) + rgba_getg(src); |
| 501 | int b = rgba_getb(backdrop) + rgba_getb(src); |
| 502 | src = rgba(std::min(r, 255), |
| 503 | std::min(g, 255), |
| 504 | std::min(b, 255), 0) | (src & rgba_a_mask); |
| 505 | return rgba_blender_normal(backdrop, src, opacity); |
| 506 | } |
| 507 | |
| 508 | color_t rgba_blender_subtract(color_t backdrop, color_t src, int opacity) |
| 509 | { |
| 510 | int r = rgba_getr(backdrop) - rgba_getr(src); |
| 511 | int g = rgba_getg(backdrop) - rgba_getg(src); |
| 512 | int b = rgba_getb(backdrop) - rgba_getb(src); |
| 513 | src = rgba(MAX(r, 0), MAX(g, 0), MAX(b, 0), 0) | (src & rgba_a_mask); |
| 514 | return rgba_blender_normal(backdrop, src, opacity); |
| 515 | } |
| 516 | |
| 517 | color_t rgba_blender_divide(color_t backdrop, color_t src, int opacity) |
| 518 | { |
| 519 | int r = blend_divide(rgba_getr(backdrop), rgba_getr(src)); |
| 520 | int g = blend_divide(rgba_getg(backdrop), rgba_getg(src)); |
| 521 | int b = blend_divide(rgba_getb(backdrop), rgba_getb(src)); |
| 522 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
| 523 | return rgba_blender_normal(backdrop, src, opacity); |
| 524 | } |
| 525 | |
| 526 | // New Blender Methods: |
| 527 | RGBA_BLENDER_N(multiply) |
| 528 | RGBA_BLENDER_N(screen) |
| 529 | RGBA_BLENDER_N(overlay) |
| 530 | RGBA_BLENDER_N(darken) |
| 531 | RGBA_BLENDER_N(lighten) |
| 532 | RGBA_BLENDER_N(color_dodge) |
| 533 | RGBA_BLENDER_N(color_burn) |
| 534 | RGBA_BLENDER_N(hard_light) |
| 535 | RGBA_BLENDER_N(soft_light) |
| 536 | RGBA_BLENDER_N(difference) |
| 537 | RGBA_BLENDER_N(exclusion) |
| 538 | RGBA_BLENDER_N(hsl_color) |
| 539 | RGBA_BLENDER_N(hsl_hue) |
| 540 | RGBA_BLENDER_N(hsl_saturation) |
| 541 | RGBA_BLENDER_N(hsl_luminosity) |
| 542 | RGBA_BLENDER_N(addition) |
| 543 | RGBA_BLENDER_N(subtract) |
| 544 | RGBA_BLENDER_N(divide) |
| 545 | |
| 546 | ////////////////////////////////////////////////////////////////////// |
| 547 | // GRAY blenders |
| 548 | |
| 549 | color_t graya_blender_src(color_t backdrop, color_t src, int opacity) |
| 550 | { |
| 551 | return src; |
| 552 | } |
| 553 | |
| 554 | color_t graya_blender_merge(color_t backdrop, color_t src, int opacity) |
| 555 | { |
| 556 | int Bk, Ba; |
| 557 | int Sk, Sa; |
| 558 | int Rk, Ra; |
| 559 | int t; |
| 560 | |
| 561 | Bk = graya_getv(backdrop); |
| 562 | Ba = graya_geta(backdrop); |
| 563 | |
| 564 | Sk = graya_getv(src); |
| 565 | Sa = graya_geta(src); |
| 566 | |
| 567 | if (Ba == 0) { |
| 568 | Rk = Sk; |
| 569 | } |
| 570 | else if (Sa == 0) { |
| 571 | Rk = Bk; |
| 572 | } |
| 573 | else { |
| 574 | Rk = Bk + MUL_UN8((Sk-Bk), opacity, t); |
| 575 | } |
| 576 | Ra = Ba + MUL_UN8((Sa-Ba), opacity, t); |
| 577 | if (Ra == 0) |
| 578 | Rk = 0; |
| 579 | |
| 580 | return graya(Rk, Ra); |
| 581 | } |
| 582 | |
| 583 | color_t graya_blender_neg_bw(color_t backdrop, color_t src, int opacity) |
| 584 | { |
| 585 | if ((backdrop & graya_a_mask) == 0) |
| 586 | return src; |
| 587 | else if (graya_getv(backdrop) < 128) |
| 588 | return graya(255, 255); |
| 589 | else |
| 590 | return graya(0, 255); |
| 591 | } |
| 592 | |
| 593 | color_t graya_blender_normal(color_t backdrop, color_t src, int opacity) |
| 594 | { |
| 595 | int t; |
| 596 | |
| 597 | if (!(backdrop & graya_a_mask)) { |
| 598 | int a = graya_geta(src); |
| 599 | a = MUL_UN8(a, opacity, t); |
| 600 | a <<= graya_a_shift; |
| 601 | return (src & 0xff) | a; |
| 602 | } |
| 603 | else if (!(src & graya_a_mask)) |
| 604 | return backdrop; |
| 605 | |
| 606 | int Bg, Ba; |
| 607 | int Sg, Sa; |
| 608 | int Rg, Ra; |
| 609 | |
| 610 | Bg = graya_getv(backdrop); |
| 611 | Ba = graya_geta(backdrop); |
| 612 | |
| 613 | Sg = graya_getv(src); |
| 614 | Sa = graya_geta(src); |
| 615 | Sa = MUL_UN8(Sa, opacity, t); |
| 616 | |
| 617 | Ra = Ba + Sa - MUL_UN8(Ba, Sa, t); |
| 618 | Rg = Bg + (Sg-Bg) * Sa / Ra; |
| 619 | |
| 620 | return graya(Rg, Ra); |
| 621 | } |
| 622 | |
| 623 | color_t graya_blender_normal_dst_over(color_t backdrop, color_t src, int opacity) |
| 624 | { |
| 625 | int t; |
| 626 | int Sa = MUL_UN8(graya_geta(src), opacity, t); |
| 627 | src = (src & graya_v_mask) | (Sa << graya_a_shift); |
| 628 | return graya_blender_normal(src, backdrop); |
| 629 | } |
| 630 | |
| 631 | color_t graya_blender_multiply(color_t backdrop, color_t src, int opacity) |
| 632 | { |
| 633 | int t; |
| 634 | int v = blend_multiply(graya_getv(backdrop), graya_getv(src), t); |
| 635 | src = graya(v, 0) | (src & graya_a_mask); |
| 636 | return graya_blender_normal(backdrop, src, opacity); |
| 637 | } |
| 638 | |
| 639 | color_t graya_blender_screen(color_t backdrop, color_t src, int opacity) |
| 640 | { |
| 641 | int t; |
| 642 | int v = blend_screen(graya_getv(backdrop), graya_getv(src), t); |
| 643 | src = graya(v, 0) | (src & graya_a_mask); |
| 644 | return graya_blender_normal(backdrop, src, opacity); |
| 645 | } |
| 646 | |
| 647 | color_t graya_blender_overlay(color_t backdrop, color_t src, int opacity) |
| 648 | { |
| 649 | int t; |
| 650 | int v = blend_overlay(graya_getv(backdrop), graya_getv(src), t); |
| 651 | src = graya(v, 0) | (src & graya_a_mask); |
| 652 | return graya_blender_normal(backdrop, src, opacity); |
| 653 | } |
| 654 | |
| 655 | color_t graya_blender_darken(color_t backdrop, color_t src, int opacity) |
| 656 | { |
| 657 | int v = blend_darken(graya_getv(backdrop), graya_getv(src)); |
| 658 | src = graya(v, 0) | (src & graya_a_mask); |
| 659 | return graya_blender_normal(backdrop, src, opacity); |
| 660 | } |
| 661 | |
| 662 | color_t graya_blender_lighten(color_t backdrop, color_t src, int opacity) |
| 663 | { |
| 664 | int v = blend_lighten(graya_getv(backdrop), graya_getv(src)); |
| 665 | src = graya(v, 0) | (src & graya_a_mask); |
| 666 | return graya_blender_normal(backdrop, src, opacity); |
| 667 | } |
| 668 | |
| 669 | color_t graya_blender_color_dodge(color_t backdrop, color_t src, int opacity) |
| 670 | { |
| 671 | int v = blend_color_dodge(graya_getv(backdrop), graya_getv(src)); |
| 672 | src = graya(v, 0) | (src & graya_a_mask); |
| 673 | return graya_blender_normal(backdrop, src, opacity); |
| 674 | } |
| 675 | |
| 676 | color_t graya_blender_color_burn(color_t backdrop, color_t src, int opacity) |
| 677 | { |
| 678 | int v = blend_color_burn(graya_getv(backdrop), graya_getv(src)); |
| 679 | src = graya(v, 0) | (src & graya_a_mask); |
| 680 | return graya_blender_normal(backdrop, src, opacity); |
| 681 | } |
| 682 | |
| 683 | color_t graya_blender_hard_light(color_t backdrop, color_t src, int opacity) |
| 684 | { |
| 685 | int t; |
| 686 | int v = blend_hard_light(graya_getv(backdrop), graya_getv(src), t); |
| 687 | src = graya(v, 0) | (src & graya_a_mask); |
| 688 | return graya_blender_normal(backdrop, src, opacity); |
| 689 | } |
| 690 | |
| 691 | color_t graya_blender_soft_light(color_t backdrop, color_t src, int opacity) |
| 692 | { |
| 693 | int v = blend_soft_light(graya_getv(backdrop), graya_getv(src)); |
| 694 | src = graya(v, 0) | (src & graya_a_mask); |
| 695 | return graya_blender_normal(backdrop, src, opacity); |
| 696 | } |
| 697 | |
| 698 | color_t graya_blender_difference(color_t backdrop, color_t src, int opacity) |
| 699 | { |
| 700 | int v = blend_difference(graya_getv(backdrop), graya_getv(src)); |
| 701 | src = graya(v, 0) | (src & graya_a_mask); |
| 702 | return graya_blender_normal(backdrop, src, opacity); |
| 703 | } |
| 704 | |
| 705 | color_t graya_blender_exclusion(color_t backdrop, color_t src, int opacity) |
| 706 | { |
| 707 | int t; |
| 708 | int v = blend_exclusion(graya_getv(backdrop), graya_getv(src), t); |
| 709 | src = graya(v, 0) | (src & graya_a_mask); |
| 710 | return graya_blender_normal(backdrop, src, opacity); |
| 711 | } |
| 712 | |
| 713 | color_t graya_blender_addition(color_t backdrop, color_t src, int opacity) |
| 714 | { |
| 715 | int v = graya_getv(backdrop) + graya_getv(src); |
| 716 | src = graya(std::min(v, 255), 0) | (src & graya_a_mask); |
| 717 | return graya_blender_normal(backdrop, src, opacity); |
| 718 | } |
| 719 | |
| 720 | color_t graya_blender_subtract(color_t backdrop, color_t src, int opacity) |
| 721 | { |
| 722 | int v = graya_getv(backdrop) - graya_getv(src); |
| 723 | src = graya(std::max(v, 0), 0) | (src & graya_a_mask); |
| 724 | return graya_blender_normal(backdrop, src, opacity); |
| 725 | } |
| 726 | |
| 727 | color_t graya_blender_divide(color_t backdrop, color_t src, int opacity) |
| 728 | { |
| 729 | int v = blend_divide(graya_getv(backdrop), graya_getv(src)); |
| 730 | src = graya(v, 0) | (src & graya_a_mask); |
| 731 | return graya_blender_normal(backdrop, src, opacity); |
| 732 | } |
| 733 | |
| 734 | GRAYA_BLENDER_N(multiply) |
| 735 | GRAYA_BLENDER_N(screen) |
| 736 | GRAYA_BLENDER_N(overlay) |
| 737 | GRAYA_BLENDER_N(darken) |
| 738 | GRAYA_BLENDER_N(lighten) |
| 739 | GRAYA_BLENDER_N(color_dodge) |
| 740 | GRAYA_BLENDER_N(color_burn) |
| 741 | GRAYA_BLENDER_N(hard_light) |
| 742 | GRAYA_BLENDER_N(soft_light) |
| 743 | GRAYA_BLENDER_N(difference) |
| 744 | GRAYA_BLENDER_N(exclusion) |
| 745 | GRAYA_BLENDER_N(addition) |
| 746 | GRAYA_BLENDER_N(subtract) |
| 747 | GRAYA_BLENDER_N(divide) |
| 748 | |
| 749 | ////////////////////////////////////////////////////////////////////// |
| 750 | // indexed |
| 751 | |
| 752 | color_t indexed_blender_src(color_t dst, color_t src, int opacity) |
| 753 | { |
| 754 | return src; |
| 755 | } |
| 756 | |
| 757 | ////////////////////////////////////////////////////////////////////// |
| 758 | // getters |
| 759 | |
| 760 | BlendFunc get_rgba_blender(BlendMode blendmode, const bool newBlend) |
| 761 | { |
| 762 | switch (blendmode) { |
| 763 | case BlendMode::SRC: return rgba_blender_src; |
| 764 | case BlendMode::MERGE: return rgba_blender_merge; |
| 765 | case BlendMode::NEG_BW: return rgba_blender_neg_bw; |
| 766 | case BlendMode::RED_TINT: return rgba_blender_red_tint; |
| 767 | case BlendMode::BLUE_TINT: return rgba_blender_blue_tint; |
| 768 | case BlendMode::DST_OVER: return rgba_blender_normal_dst_over; |
| 769 | |
| 770 | case BlendMode::NORMAL: return rgba_blender_normal; |
| 771 | case BlendMode::MULTIPLY: return newBlend? rgba_blender_multiply_n: rgba_blender_multiply; |
| 772 | case BlendMode::SCREEN: return newBlend? rgba_blender_screen_n: rgba_blender_screen; |
| 773 | case BlendMode::OVERLAY: return newBlend? rgba_blender_overlay_n: rgba_blender_overlay; |
| 774 | case BlendMode::DARKEN: return newBlend? rgba_blender_darken_n: rgba_blender_darken; |
| 775 | case BlendMode::LIGHTEN: return newBlend? rgba_blender_lighten_n: rgba_blender_lighten; |
| 776 | case BlendMode::COLOR_DODGE: return newBlend? rgba_blender_color_dodge_n: rgba_blender_color_dodge; |
| 777 | case BlendMode::COLOR_BURN: return newBlend? rgba_blender_color_burn_n: rgba_blender_color_burn; |
| 778 | case BlendMode::HARD_LIGHT: return newBlend? rgba_blender_hard_light_n: rgba_blender_hard_light; |
| 779 | case BlendMode::SOFT_LIGHT: return newBlend? rgba_blender_soft_light_n: rgba_blender_soft_light; |
| 780 | case BlendMode::DIFFERENCE: return newBlend? rgba_blender_difference_n: rgba_blender_difference; |
| 781 | case BlendMode::EXCLUSION: return newBlend? rgba_blender_exclusion_n: rgba_blender_exclusion; |
| 782 | case BlendMode::HSL_HUE: return newBlend? rgba_blender_hsl_hue_n: rgba_blender_hsl_hue; |
| 783 | case BlendMode::HSL_SATURATION: return newBlend? rgba_blender_hsl_saturation_n: rgba_blender_hsl_saturation; |
| 784 | case BlendMode::HSL_COLOR: return newBlend? rgba_blender_hsl_color_n: rgba_blender_hsl_color; |
| 785 | case BlendMode::HSL_LUMINOSITY: return newBlend? rgba_blender_hsl_luminosity_n: rgba_blender_hsl_luminosity; |
| 786 | case BlendMode::ADDITION: return newBlend? rgba_blender_addition_n: rgba_blender_addition; |
| 787 | case BlendMode::SUBTRACT: return newBlend? rgba_blender_subtract_n: rgba_blender_subtract; |
| 788 | case BlendMode::DIVIDE: return newBlend? rgba_blender_divide_n: rgba_blender_divide; |
| 789 | } |
| 790 | ASSERT(false); |
| 791 | return rgba_blender_src; |
| 792 | } |
| 793 | |
| 794 | BlendFunc get_graya_blender(BlendMode blendmode, const bool newBlend) |
| 795 | { |
| 796 | switch (blendmode) { |
| 797 | case BlendMode::SRC: return graya_blender_src; |
| 798 | case BlendMode::MERGE: return graya_blender_merge; |
| 799 | case BlendMode::NEG_BW: return graya_blender_neg_bw; |
| 800 | case BlendMode::RED_TINT: return graya_blender_normal; |
| 801 | case BlendMode::BLUE_TINT: return graya_blender_normal; |
| 802 | case BlendMode::DST_OVER: return graya_blender_normal_dst_over; |
| 803 | |
| 804 | case BlendMode::NORMAL: return graya_blender_normal; |
| 805 | case BlendMode::MULTIPLY: return newBlend? graya_blender_multiply_n: graya_blender_multiply; |
| 806 | case BlendMode::SCREEN: return newBlend? graya_blender_screen_n: graya_blender_screen; |
| 807 | case BlendMode::OVERLAY: return newBlend? graya_blender_overlay_n: graya_blender_overlay; |
| 808 | case BlendMode::DARKEN: return newBlend? graya_blender_darken_n: graya_blender_darken; |
| 809 | case BlendMode::LIGHTEN: return newBlend? graya_blender_lighten_n: graya_blender_lighten; |
| 810 | case BlendMode::COLOR_DODGE: return newBlend? graya_blender_color_dodge_n: graya_blender_color_dodge; |
| 811 | case BlendMode::COLOR_BURN: return newBlend? graya_blender_color_burn_n: graya_blender_color_burn; |
| 812 | case BlendMode::HARD_LIGHT: return newBlend? graya_blender_hard_light_n: graya_blender_hard_light; |
| 813 | case BlendMode::SOFT_LIGHT: return newBlend? graya_blender_soft_light_n: graya_blender_soft_light; |
| 814 | case BlendMode::DIFFERENCE: return newBlend? graya_blender_difference_n: graya_blender_difference; |
| 815 | case BlendMode::EXCLUSION: return newBlend? graya_blender_exclusion_n: graya_blender_exclusion; |
| 816 | case BlendMode::HSL_HUE: return graya_blender_normal; |
| 817 | case BlendMode::HSL_SATURATION: return graya_blender_normal; |
| 818 | case BlendMode::HSL_COLOR: return graya_blender_normal; |
| 819 | case BlendMode::HSL_LUMINOSITY: return graya_blender_normal; |
| 820 | case BlendMode::ADDITION: return newBlend? graya_blender_exclusion_n: graya_blender_addition; |
| 821 | case BlendMode::SUBTRACT: return newBlend? graya_blender_subtract_n: graya_blender_subtract; |
| 822 | case BlendMode::DIVIDE: return newBlend? graya_blender_divide_n: graya_blender_divide; |
| 823 | } |
| 824 | ASSERT(false); |
| 825 | return graya_blender_src; |
| 826 | } |
| 827 | |
| 828 | BlendFunc get_indexed_blender(BlendMode blendmode, const bool newBlend) |
| 829 | { |
| 830 | return indexed_blender_src; |
| 831 | } |
| 832 | |
| 833 | } // namespace doc |
| 834 | |