|
3 | 3 | * crates/resvg/src/filter: mod.rs, box_blur.rs (itself based on https://github.com/fschutt/fastblur), iir_blur.rs, |
4 | 4 | * color_matrix.rs, component_transfer.rs, composite.rs, morphology.rs, convolve_matrix.rs, turbulence.rs (itself from |
5 | 5 | * the reference implementation in the SVG 1.1 specification), lighting.rs, displacement_map.rs; Copyright 2017 the Resvg |
6 | | - * Authors, MIT License) and the blend modes of tiny-skia (https://github.com/linebender/tiny-skia, |
7 | | - * src/pipeline/highp.rs; Copyright 2011 Google Inc., 2020 Yevhenii Reizner, BSD-3-Clause License, |
8 | | - * itself from Skia). The IIR blur |
| 6 | + * Authors, MIT License). The IIR blur |
9 | 7 | * follows Pascal Getreuer's gaussianiir (http://www.getreuer.info/home/gaussianiir; Simplified BSD License), an |
10 | 8 | * implementation of Alvarez and Mazorra's fast Gaussian convolution. See THIRD_PARTY_NOTICES.md |
11 | 9 | */ |
12 | 10 |
|
| 11 | +import { blendPixel } from './blend.ts'; |
| 12 | + |
13 | 13 | /** premultiplied RGBA pixels */ |
14 | 14 | export interface Pixmap { |
15 | 15 | width: number; |
@@ -643,80 +643,13 @@ function transfer(f: Exclude<Transfer, null>, value: number): number { |
643 | 643 | return u8(clamp(0, v, 1) * 255); |
644 | 644 | } |
645 | 645 |
|
646 | | -// feBlend's modes, like tiny-skia's (premultiplied, the source over the destination) |
| 646 | +// feBlend's modes (the source over the destination) |
647 | 647 | function blend(dst: Pixmap, src: Pixmap, mode: string): void { |
648 | 648 | const s = src.data, d = dst.data; |
649 | | - const inv = (v: number): number => 1 - v; |
650 | | - const lum = (r: number, g: number, b: number): number => r * 0.3 + g * 0.59 + b * 0.11; |
651 | | - const sat = (r: number, g: number, b: number): number => Math.max(r, g, b) - Math.min(r, g, b); |
652 | | - const setSat = (c: number[], value: number): void => { |
653 | | - const mn = Math.min(c[0], c[1], c[2]), mx = Math.max(c[0], c[1], c[2]), range = mx - mn; |
654 | | - for (let k = 0; k < 3; k++) c[k] = range === 0 ? 0 : (c[k] - mn) * value / range; |
655 | | - }; |
656 | | - const setLum = (c: number[], l: number): void => { |
657 | | - const diff = l - lum(c[0], c[1], c[2]); |
658 | | - for (let k = 0; k < 3; k++) c[k] += diff; |
659 | | - }; |
660 | | - const clipColor = (c: number[], a: number): void => { |
661 | | - const mn = Math.min(c[0], c[1], c[2]), mx = Math.max(c[0], c[1], c[2]), l = lum(c[0], c[1], c[2]); |
662 | | - for (let k = 0; k < 3; k++) { |
663 | | - let v = c[k]; |
664 | | - if (!(mx >= 0)) v = l + (v - l) * l / (l - mn); |
665 | | - if (mx > a) v = l + (v - l) * (a - l) / (mx - l); |
666 | | - c[k] = Math.max(v, 0); |
667 | | - } |
668 | | - }; |
669 | | - const separable = (sc: number, dc: number, sa: number, da: number): number => { |
670 | | - switch (mode) { |
671 | | - case 'multiply': return sc * inv(da) + dc * inv(sa) + sc * dc; |
672 | | - case 'screen': return sc + dc - sc * dc; |
673 | | - case 'darken': return sc + dc - Math.max(sc * da, dc * sa); |
674 | | - case 'lighten': return sc + dc - Math.min(sc * da, dc * sa); |
675 | | - case 'difference': return sc + dc - 2 * Math.min(sc * da, dc * sa); |
676 | | - case 'exclusion': return sc + dc - 2 * sc * dc; |
677 | | - case 'color-burn': |
678 | | - return dc === da ? dc + sc * inv(da) : sc === 0 ? dc * inv(sa) : sa * (da - Math.min(da, (da - dc) * sa / sc)) + sc * inv(da) + dc * inv(sa); |
679 | | - case 'color-dodge': |
680 | | - return dc === 0 ? sc * inv(da) : sc === sa ? sc + dc * inv(sa) : sa * Math.min(da, dc * sa / (sa - sc)) + sc * inv(da) + dc * inv(sa); |
681 | | - case 'hard-light': |
682 | | - return sc * inv(da) + dc * inv(sa) + (2 * sc <= sa ? 2 * sc * dc : sa * da - 2 * (da - dc) * (sa - sc)); |
683 | | - case 'overlay': |
684 | | - return sc * inv(da) + dc * inv(sa) + (2 * dc <= da ? 2 * sc * dc : sa * da - 2 * (da - dc) * (sa - sc)); |
685 | | - case 'soft-light': { |
686 | | - const m = da > 0 ? dc / da : 0, s2 = 2 * sc, m4 = 4 * m; |
687 | | - const darkSrc = dc * (sa + (s2 - sa) * (1 - m)); |
688 | | - const darkDst = (m4 * m4 + m4) * (m - 1) + 7 * m; |
689 | | - const liteDst = Math.sqrt(m) - m; |
690 | | - const liteSrc = dc * sa + da * (s2 - sa) * (4 * dc <= da ? darkDst : liteDst); |
691 | | - return sc * inv(da) + dc * inv(sa) + (s2 <= sa ? darkSrc : liteSrc); |
692 | | - } |
693 | | - default: return sc + dc * inv(sa); |
694 | | - } |
695 | | - }; |
696 | | - const c = [0, 0, 0]; |
| 649 | + const out = [0, 0, 0, 0]; |
697 | 650 | for (let i = 0; i < d.length; i += 4) { |
698 | | - const r = s[i] / 255, g = s[i + 1] / 255, b = s[i + 2] / 255, a = s[i + 3] / 255; |
699 | | - const dr = d[i] / 255, dg = d[i + 1] / 255, db = d[i + 2] / 255, da = d[i + 3] / 255; |
700 | | - let out: number[]; |
701 | | - if (mode === 'hue' || mode === 'saturation' || mode === 'color' || mode === 'luminosity') { |
702 | | - if (mode === 'hue') { |
703 | | - c[0] = r * a; c[1] = g * a; c[2] = b * a; |
704 | | - setSat(c, sat(dr, dg, db) * a); |
705 | | - setLum(c, lum(dr, dg, db) * a); |
706 | | - } else if (mode === 'saturation') { |
707 | | - c[0] = dr * a; c[1] = dg * a; c[2] = db * a; |
708 | | - setSat(c, sat(r, g, b) * da); |
709 | | - setLum(c, lum(dr, dg, db) * a); |
710 | | - } else if (mode === 'color') { |
711 | | - c[0] = r * da; c[1] = g * da; c[2] = b * da; |
712 | | - setLum(c, lum(dr, dg, db) * a); |
713 | | - } else { |
714 | | - c[0] = dr * a; c[1] = dg * a; c[2] = db * a; |
715 | | - setLum(c, lum(r, g, b) * da); |
716 | | - } |
717 | | - clipColor(c, a * da); |
718 | | - out = [r * inv(da) + dr * inv(a) + c[0], g * inv(da) + dg * inv(a) + c[1], b * inv(da) + db * inv(a) + c[2], a + da - a * da]; |
719 | | - } else out = [separable(r, dr, a, da), separable(g, dg, a, da), separable(b, db, a, da), da * inv(a) + a]; |
| 651 | + for (let k = 0; k < 4; k++) out[k] = d[i + k] / 255; |
| 652 | + blendPixel(mode, s[i] / 255, s[i + 1] / 255, s[i + 2] / 255, s[i + 3] / 255, out, 0); |
720 | 653 | for (let k = 0; k < 4; k++) d[i + k] = u8(clamp(0, out[k], 1) * 255 + 0.5); |
721 | 654 | } |
722 | 655 | } |
|
0 commit comments