1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#define VECS_PER_SPECIFIC_BRUSH 3
6#define WR_FEATURE_TEXTURE_2D
7
8#include shared,prim_shared,brush
9
10// UV and bounds for the source image
11varying vec2 v_src_uv;
12flat varying vec4 v_src_uv_sample_bounds;
13
14// UV and bounds for the backdrop image
15varying vec2 v_backdrop_uv;
16flat varying vec4 v_backdrop_uv_sample_bounds;
17
18#if defined(PLATFORM_ANDROID) && !defined(SWGL)
19// Work around Adreno 3xx driver bug. See the v_perspective comment in
20// brush_image or bug 1630356 for details.
21flat varying vec2 v_perspective_vec;
22#define v_perspective v_perspective_vec.x
23#else
24// Flag to allow perspective interpolation of UV.
25flat varying float v_perspective;
26#endif
27
28// mix-blend op
29flat varying int v_op;
30
31#ifdef WR_VERTEX_SHADER
32
33void get_uv(
34    int res_address,
35    vec2 f,
36    ivec2 texture_size,
37    float perspective_f,
38    out vec2 out_uv,
39    out vec4 out_uv_sample_bounds
40) {
41    ImageSource res = fetch_image_source(res_address);
42    vec2 uv0 = res.uv_rect.p0;
43    vec2 uv1 = res.uv_rect.p1;
44
45    vec2 inv_texture_size = vec2(1.0) / vec2(texture_size);
46    f = get_image_quad_uv(res_address, f);
47    vec2 uv = mix(uv0, uv1, f);
48
49    out_uv = uv * inv_texture_size * perspective_f;
50    out_uv_sample_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) * inv_texture_size.xyxy;
51}
52
53void brush_vs(
54    VertexInfo vi,
55    int prim_address,
56    RectWithEndpoint local_rect,
57    RectWithEndpoint segment_rect,
58    ivec4 prim_user_data,
59    int specific_resource_address,
60    mat4 transform,
61    PictureTask pic_task,
62    int brush_flags,
63    vec4 unused
64) {
65    vec2 f = (vi.local_pos - local_rect.p0) / rect_size(local_rect);
66    float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
67    float perspective_f = mix(vi.world_pos.w, 1.0, perspective_interpolate);
68    v_perspective = perspective_interpolate;
69    v_op = prim_user_data.x;
70
71    get_uv(
72        prim_user_data.y,
73        f,
74        TEX_SIZE(sColor0).xy,
75        1.0,
76        v_backdrop_uv,
77        v_backdrop_uv_sample_bounds
78    );
79
80    get_uv(
81        prim_user_data.z,
82        f,
83        TEX_SIZE(sColor1).xy,
84        perspective_f,
85        v_src_uv,
86        v_src_uv_sample_bounds
87    );
88}
89#endif
90
91#ifdef WR_FRAGMENT_SHADER
92vec3 Multiply(vec3 Cb, vec3 Cs) {
93    return Cb * Cs;
94}
95
96vec3 Screen(vec3 Cb, vec3 Cs) {
97    return Cb + Cs - (Cb * Cs);
98}
99
100vec3 HardLight(vec3 Cb, vec3 Cs) {
101    vec3 m = Multiply(Cb, 2.0 * Cs);
102    vec3 s = Screen(Cb, 2.0 * Cs - 1.0);
103    vec3 edge = vec3(0.5, 0.5, 0.5);
104    return mix(m, s, step(edge, Cs));
105}
106
107// TODO: Worth doing with mix/step? Check GLSL output.
108float ColorDodge(float Cb, float Cs) {
109    if (Cb == 0.0)
110        return 0.0;
111    else if (Cs == 1.0)
112        return 1.0;
113    else
114        return min(1.0, Cb / (1.0 - Cs));
115}
116
117// TODO: Worth doing with mix/step? Check GLSL output.
118float ColorBurn(float Cb, float Cs) {
119    if (Cb == 1.0)
120        return 1.0;
121    else if (Cs == 0.0)
122        return 0.0;
123    else
124        return 1.0 - min(1.0, (1.0 - Cb) / Cs);
125}
126
127float SoftLight(float Cb, float Cs) {
128    if (Cs <= 0.5) {
129        return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb);
130    } else {
131        float D;
132
133        if (Cb <= 0.25)
134            D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb;
135        else
136            D = sqrt(Cb);
137
138        return Cb + (2.0 * Cs - 1.0) * (D - Cb);
139    }
140}
141
142vec3 Difference(vec3 Cb, vec3 Cs) {
143    return abs(Cb - Cs);
144}
145
146vec3 Exclusion(vec3 Cb, vec3 Cs) {
147    return Cb + Cs - 2.0 * Cb * Cs;
148}
149
150// These functions below are taken from the spec.
151// There's probably a much quicker way to implement
152// them in GLSL...
153float Sat(vec3 c) {
154    return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));
155}
156
157float Lum(vec3 c) {
158    vec3 f = vec3(0.3, 0.59, 0.11);
159    return dot(c, f);
160}
161
162vec3 ClipColor(vec3 C) {
163    float L = Lum(C);
164    float n = min(C.r, min(C.g, C.b));
165    float x = max(C.r, max(C.g, C.b));
166
167    if (n < 0.0)
168        C = L + (((C - L) * L) / (L - n));
169
170    if (x > 1.0)
171        C = L + (((C - L) * (1.0 - L)) / (x - L));
172
173    return C;
174}
175
176vec3 SetLum(vec3 C, float l) {
177    float d = l - Lum(C);
178    return ClipColor(C + d);
179}
180
181void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) {
182    if (Cmax > Cmin) {
183        Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin));
184        Cmax = s;
185    } else {
186        Cmid = 0.0;
187        Cmax = 0.0;
188    }
189    Cmin = 0.0;
190}
191
192vec3 SetSat(vec3 C, float s) {
193    if (C.r <= C.g) {
194        if (C.g <= C.b) {
195            SetSatInner(C.r, C.g, C.b, s);
196        } else {
197            if (C.r <= C.b) {
198                SetSatInner(C.r, C.b, C.g, s);
199            } else {
200                SetSatInner(C.b, C.r, C.g, s);
201            }
202        }
203    } else {
204        if (C.r <= C.b) {
205            SetSatInner(C.g, C.r, C.b, s);
206        } else {
207            if (C.g <= C.b) {
208                SetSatInner(C.g, C.b, C.r, s);
209            } else {
210                SetSatInner(C.b, C.g, C.r, s);
211            }
212        }
213    }
214    return C;
215}
216
217vec3 Hue(vec3 Cb, vec3 Cs) {
218    return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb));
219}
220
221vec3 Saturation(vec3 Cb, vec3 Cs) {
222    return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb));
223}
224
225vec3 Color(vec3 Cb, vec3 Cs) {
226    return SetLum(Cs, Lum(Cb));
227}
228
229vec3 Luminosity(vec3 Cb, vec3 Cs) {
230    return SetLum(Cb, Lum(Cs));
231}
232
233const int MixBlendMode_Multiply    = 1;
234const int MixBlendMode_Screen      = 2;
235const int MixBlendMode_Overlay     = 3;
236const int MixBlendMode_Darken      = 4;
237const int MixBlendMode_Lighten     = 5;
238const int MixBlendMode_ColorDodge  = 6;
239const int MixBlendMode_ColorBurn   = 7;
240const int MixBlendMode_HardLight   = 8;
241const int MixBlendMode_SoftLight   = 9;
242const int MixBlendMode_Difference  = 10;
243const int MixBlendMode_Exclusion   = 11;
244const int MixBlendMode_Hue         = 12;
245const int MixBlendMode_Saturation  = 13;
246const int MixBlendMode_Color       = 14;
247const int MixBlendMode_Luminosity  = 15;
248
249Fragment brush_fs() {
250    float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective);
251
252    vec2 src_uv = v_src_uv * perspective_divisor;
253    src_uv = clamp(src_uv, v_src_uv_sample_bounds.xy, v_src_uv_sample_bounds.zw);
254
255    vec2 backdrop_uv = clamp(v_backdrop_uv, v_backdrop_uv_sample_bounds.xy, v_backdrop_uv_sample_bounds.zw);
256
257    vec4 Cb = texture(sColor0, backdrop_uv);
258    vec4 Cs = texture(sColor1, src_uv);
259
260    // The mix-blend-mode functions assume no premultiplied alpha
261    if (Cb.a != 0.0) {
262        Cb.rgb /= Cb.a;
263    }
264
265    if (Cs.a != 0.0) {
266        Cs.rgb /= Cs.a;
267    }
268
269    // Return yellow if none of the branches match (shouldn't happen).
270    vec4 result = vec4(1.0, 1.0, 0.0, 1.0);
271
272    switch (v_op) {
273        case MixBlendMode_Multiply:
274            result.rgb = Multiply(Cb.rgb, Cs.rgb);
275            break;
276        case MixBlendMode_Screen:
277            result.rgb = Screen(Cb.rgb, Cs.rgb);
278            break;
279        case MixBlendMode_Overlay:
280            // Overlay is inverse of Hardlight
281            result.rgb = HardLight(Cs.rgb, Cb.rgb);
282            break;
283        case MixBlendMode_Darken:
284            result.rgb = min(Cs.rgb, Cb.rgb);
285            break;
286        case MixBlendMode_Lighten:
287            result.rgb = max(Cs.rgb, Cb.rgb);
288            break;
289        case MixBlendMode_ColorDodge:
290            result.r = ColorDodge(Cb.r, Cs.r);
291            result.g = ColorDodge(Cb.g, Cs.g);
292            result.b = ColorDodge(Cb.b, Cs.b);
293            break;
294        case MixBlendMode_ColorBurn:
295            result.r = ColorBurn(Cb.r, Cs.r);
296            result.g = ColorBurn(Cb.g, Cs.g);
297            result.b = ColorBurn(Cb.b, Cs.b);
298            break;
299        case MixBlendMode_HardLight:
300            result.rgb = HardLight(Cb.rgb, Cs.rgb);
301            break;
302        case MixBlendMode_SoftLight:
303            result.r = SoftLight(Cb.r, Cs.r);
304            result.g = SoftLight(Cb.g, Cs.g);
305            result.b = SoftLight(Cb.b, Cs.b);
306            break;
307        case MixBlendMode_Difference:
308            result.rgb = Difference(Cb.rgb, Cs.rgb);
309            break;
310        case MixBlendMode_Exclusion:
311            result.rgb = Exclusion(Cb.rgb, Cs.rgb);
312            break;
313        case MixBlendMode_Hue:
314            result.rgb = Hue(Cb.rgb, Cs.rgb);
315            break;
316        case MixBlendMode_Saturation:
317            result.rgb = Saturation(Cb.rgb, Cs.rgb);
318            break;
319        case MixBlendMode_Color:
320            result.rgb = Color(Cb.rgb, Cs.rgb);
321            break;
322        case MixBlendMode_Luminosity:
323            result.rgb = Luminosity(Cb.rgb, Cs.rgb);
324            break;
325        default: break;
326    }
327
328    result.rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb;
329    result.a = Cs.a;
330
331    result.rgb *= result.a;
332
333    return Fragment(result);
334}
335#endif
336