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
7#include shared,prim_shared,brush
8
9// Interpolated UV coordinates to sample.
10varying vec2 vUv;
11
12// X = layer index to sample, Y = flag to allow perspective interpolation of UV.
13flat varying vec2 vLayerAndPerspective;
14flat varying float vAmount;
15flat varying int vOp;
16flat varying mat3 vColorMat;
17flat varying vec3 vColorOffset;
18flat varying vec4 vUvClipBounds;
19
20#ifdef WR_VERTEX_SHADER
21
22void brush_vs(
23    VertexInfo vi,
24    int prim_address,
25    RectWithSize local_rect,
26    RectWithSize segment_rect,
27    ivec4 user_data,
28    mat4 transform,
29    PictureTask pic_task,
30    int brush_flags,
31    vec4 unused
32) {
33    ImageResource res = fetch_image_resource(user_data.x);
34    vec2 uv0 = res.uv_rect.p0;
35    vec2 uv1 = res.uv_rect.p1;
36
37    // PictureTask src_task = fetch_picture_task(user_data.x);
38    vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
39    vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
40    f = get_image_quad_uv(user_data.x, f);
41    vec2 uv = mix(uv0, uv1, f);
42    float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
43
44    vUv = uv / texture_size * mix(vi.world_pos.w, 1.0, perspective_interpolate);
45    vLayerAndPerspective = vec2(res.layer, perspective_interpolate);
46    vUvClipBounds = vec4(uv0, uv1) / texture_size.xyxy;
47
48    float lumR = 0.2126;
49    float lumG = 0.7152;
50    float lumB = 0.0722;
51    float oneMinusLumR = 1.0 - lumR;
52    float oneMinusLumG = 1.0 - lumG;
53    float oneMinusLumB = 1.0 - lumB;
54
55    float amount = float(user_data.z) / 65536.0;
56    float invAmount = 1.0 - amount;
57
58    vOp = user_data.y;
59    vAmount = amount;
60
61    switch (vOp) {
62        case 2: {
63            // Grayscale
64            vColorMat = mat3(
65                vec3(lumR + oneMinusLumR * invAmount, lumR - lumR * invAmount, lumR - lumR * invAmount),
66                vec3(lumG - lumG * invAmount, lumG + oneMinusLumG * invAmount, lumG - lumG * invAmount),
67                vec3(lumB - lumB * invAmount, lumB - lumB * invAmount, lumB + oneMinusLumB * invAmount)
68            );
69            vColorOffset = vec3(0.0);
70            break;
71        }
72        case 3: {
73            // HueRotate
74            float c = cos(amount);
75            float s = sin(amount);
76            vColorMat = mat3(
77                vec3(lumR + oneMinusLumR * c - lumR * s, lumR - lumR * c + 0.143 * s, lumR - lumR * c - oneMinusLumR * s),
78                vec3(lumG - lumG * c - lumG * s, lumG + oneMinusLumG * c + 0.140 * s, lumG - lumG * c + lumG * s),
79                vec3(lumB - lumB * c + oneMinusLumB * s, lumB - lumB * c - 0.283 * s, lumB + oneMinusLumB * c + lumB * s)
80            );
81            vColorOffset = vec3(0.0);
82            break;
83        }
84        case 5: {
85            // Saturate
86            vColorMat = mat3(
87                vec3(invAmount * lumR + amount, invAmount * lumR, invAmount * lumR),
88                vec3(invAmount * lumG, invAmount * lumG + amount, invAmount * lumG),
89                vec3(invAmount * lumB, invAmount * lumB, invAmount * lumB + amount)
90            );
91            vColorOffset = vec3(0.0);
92            break;
93        }
94        case 6: {
95            // Sepia
96            vColorMat = mat3(
97                vec3(0.393 + 0.607 * invAmount, 0.349 - 0.349 * invAmount, 0.272 - 0.272 * invAmount),
98                vec3(0.769 - 0.769 * invAmount, 0.686 + 0.314 * invAmount, 0.534 - 0.534 * invAmount),
99                vec3(0.189 - 0.189 * invAmount, 0.168 - 0.168 * invAmount, 0.131 + 0.869 * invAmount)
100            );
101            vColorOffset = vec3(0.0);
102            break;
103        }
104        case 10: {
105            // Color Matrix
106            vec4 mat_data[3] = fetch_from_gpu_cache_3(user_data.z);
107            vec4 offset_data = fetch_from_gpu_cache_1(user_data.z + 4);
108            vColorMat = mat3(mat_data[0].xyz, mat_data[1].xyz, mat_data[2].xyz);
109            vColorOffset = offset_data.rgb;
110            break;
111        }
112        default: break;
113    }
114}
115#endif
116
117#ifdef WR_FRAGMENT_SHADER
118vec3 Contrast(vec3 Cs, float amount) {
119    return Cs.rgb * amount - 0.5 * amount + 0.5;
120}
121
122vec3 Invert(vec3 Cs, float amount) {
123    return mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount);
124}
125
126vec3 Brightness(vec3 Cs, float amount) {
127    // Apply the brightness factor.
128    // Resulting color needs to be clamped to output range
129    // since we are pre-multiplying alpha in the shader.
130    return clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0));
131}
132
133// Based on the Gecko's implementation in
134// https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24
135// These could be made faster by sampling a lookup table stored in a float texture
136// with linear interpolation.
137
138vec3 SrgbToLinear(vec3 color) {
139    vec3 c1 = color / 12.92;
140    vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4));
141    return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2);
142}
143
144vec3 LinearToSrgb(vec3 color) {
145    vec3 c1 = color * 12.92;
146    vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055);
147    return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2);
148}
149
150Fragment brush_fs() {
151    float perspective_divisor = mix(gl_FragCoord.w, 1.0, vLayerAndPerspective.y);
152    vec2 uv = vUv * perspective_divisor;
153    vec4 Cs = texture(sColor0, vec3(uv, vLayerAndPerspective.x));
154
155    // Un-premultiply the input.
156    float alpha = Cs.a;
157    vec3 color = alpha != 0.0 ? Cs.rgb / alpha : Cs.rgb;
158
159    switch (vOp) {
160        case 0:
161            break;
162        case 1:
163            color = Contrast(color, vAmount);
164            break;
165        case 4:
166            color = Invert(color, vAmount);
167            break;
168        case 7:
169            color = Brightness(color, vAmount);
170            break;
171        case 8: // Opacity
172            alpha *= vAmount;
173            break;
174        case 11:
175            color = SrgbToLinear(color);
176            break;
177        case 12:
178            color = LinearToSrgb(color);
179            break;
180        default:
181            color = vColorMat * color + vColorOffset;
182    }
183
184    // Fail-safe to ensure that we don't sample outside the rendered
185    // portion of a blend source.
186    alpha *= point_inside_rect(uv, vUvClipBounds.xy, vUvClipBounds.zw);
187
188    // Pre-multiply the alpha into the output value.
189    return Fragment(alpha * vec4(color, 1.0));
190}
191#endif
192