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 WR_FEATURE_TEXTURE_2D
6
7#include shared,prim_shared
8
9varying vec2 vInput1Uv;
10varying vec2 vInput2Uv;
11flat varying vec4 vInput1UvRect;
12flat varying vec4 vInput2UvRect;
13flat varying ivec4 vData;
14flat varying vec4 vFilterData0;
15flat varying vec4 vFilterData1;
16
17// x: Filter input count, y: Filter kind.
18// Packed in to a vector to work around bug 1630356.
19flat varying ivec2 vFilterInputCountFilterKindVec;
20#define vFilterInputCount vFilterInputCountFilterKindVec.x
21#define vFilterKind vFilterInputCountFilterKindVec.y
22// Packed in to a vector to work around bug 1630356.
23flat varying vec2 vFloat0;
24
25flat varying mat4 vColorMat;
26flat varying ivec4 vFuncs;
27
28#define FILTER_BLEND                0
29#define FILTER_FLOOD                1
30#define FILTER_LINEAR_TO_SRGB       2
31#define FILTER_SRGB_TO_LINEAR       3
32#define FILTER_OPACITY              4
33#define FILTER_COLOR_MATRIX         5
34#define FILTER_DROP_SHADOW          6
35#define FILTER_OFFSET               7
36#define FILTER_COMPONENT_TRANSFER   8
37#define FILTER_IDENTITY             9
38#define FILTER_COMPOSITE            10
39
40#define COMPOSITE_OVER       0
41#define COMPOSITE_IN         1
42#define COMPOSITE_OUT        2
43#define COMPOSITE_ATOP       3
44#define COMPOSITE_XOR        4
45#define COMPOSITE_LIGHTER    5
46#define COMPOSITE_ARITHMETIC 6
47
48#ifdef WR_VERTEX_SHADER
49
50PER_INSTANCE in int aFilterRenderTaskAddress;
51PER_INSTANCE in int aFilterInput1TaskAddress;
52PER_INSTANCE in int aFilterInput2TaskAddress;
53PER_INSTANCE in int aFilterKind;
54PER_INSTANCE in int aFilterInputCount;
55PER_INSTANCE in int aFilterGenericInt;
56PER_INSTANCE in ivec2 aFilterExtraDataAddress;
57
58struct FilterTask {
59    RectWithEndpoint task_rect;
60    vec3 user_data;
61};
62
63FilterTask fetch_filter_task(int address) {
64    RenderTaskData task_data = fetch_render_task_data(address);
65
66    FilterTask task = FilterTask(
67        task_data.task_rect,
68        task_data.user_data.xyz
69    );
70
71    return task;
72}
73
74vec4 compute_uv_rect(RectWithEndpoint task_rect, vec2 texture_size) {
75    vec4 uvRect = vec4(task_rect.p0 + vec2(0.5),
76                       task_rect.p1 - vec2(0.5));
77    uvRect /= texture_size.xyxy;
78    return uvRect;
79}
80
81vec2 compute_uv(RectWithEndpoint task_rect, vec2 texture_size) {
82    vec2 uv0 = task_rect.p0 / texture_size;
83    vec2 uv1 = floor(task_rect.p1) / texture_size;
84    return mix(uv0, uv1, aPosition.xy);
85}
86
87void main(void) {
88    FilterTask filter_task = fetch_filter_task(aFilterRenderTaskAddress);
89    RectWithEndpoint target_rect = filter_task.task_rect;
90
91    vec2 pos = mix(target_rect.p0, target_rect.p1, aPosition.xy);
92
93    RectWithEndpoint input_1_task;
94    if (aFilterInputCount > 0) {
95        vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
96        input_1_task = fetch_render_task_rect(aFilterInput1TaskAddress);
97        vInput1UvRect = compute_uv_rect(input_1_task, texture_size);
98        vInput1Uv = compute_uv(input_1_task, texture_size);
99    }
100
101    RectWithEndpoint input_2_task;
102    if (aFilterInputCount > 1) {
103        vec2 texture_size = vec2(TEX_SIZE(sColor1).xy);
104        input_2_task = fetch_render_task_rect(aFilterInput2TaskAddress);
105        vInput2UvRect = compute_uv_rect(input_2_task, texture_size);
106        vInput2Uv = compute_uv(input_2_task, texture_size);
107    }
108
109    vFilterInputCount = aFilterInputCount;
110    vFilterKind = aFilterKind;
111
112    // This assignment is only used for component transfer filters but this
113    // assignment has to be done here and not in the component transfer case
114    // below because it doesn't get executed on Windows because of a suspected
115    // miscompile of this shader on Windows. See
116    // https://github.com/servo/webrender/wiki/Driver-issues#bug-1505871---assignment-to-varying-flat-arrays-inside-switch-statement-of-vertex-shader-suspected-miscompile-on-windows
117    // default: just to satisfy angle_shader_validation.rs which needs one
118    // default: for every switch, even in comments.
119    vFuncs.r = (aFilterGenericInt >> 12) & 0xf; // R
120    vFuncs.g = (aFilterGenericInt >> 8)  & 0xf; // G
121    vFuncs.b = (aFilterGenericInt >> 4)  & 0xf; // B
122    vFuncs.a = (aFilterGenericInt)       & 0xf; // A
123
124    switch (aFilterKind) {
125        case FILTER_BLEND:
126            vData = ivec4(aFilterGenericInt, 0, 0, 0);
127            break;
128        case FILTER_FLOOD:
129            vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
130            break;
131        case FILTER_OPACITY:
132            vFloat0.x = filter_task.user_data.x;
133            break;
134        case FILTER_COLOR_MATRIX:
135            vec4 mat_data[4] = fetch_from_gpu_cache_4_direct(aFilterExtraDataAddress);
136            vColorMat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]);
137            vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress + ivec2(4, 0));
138            break;
139        case FILTER_DROP_SHADOW:
140            vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
141            break;
142        case FILTER_OFFSET:
143            vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
144            vFilterData0 = vec4(-filter_task.user_data.xy / texture_size, vec2(0.0));
145
146            RectWithEndpoint task_rect = input_1_task;
147            vec4 clipRect = vec4(task_rect.p0, task_rect.p1);
148            clipRect /= texture_size.xyxy;
149            vFilterData1 = clipRect;
150            break;
151        case FILTER_COMPONENT_TRANSFER:
152            vData = ivec4(aFilterExtraDataAddress, 0, 0);
153            break;
154        case FILTER_COMPOSITE:
155            vData = ivec4(aFilterGenericInt, 0, 0, 0);
156            if (aFilterGenericInt == COMPOSITE_ARITHMETIC) {
157              vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
158            }
159            break;
160        default:
161            break;
162    }
163
164    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
165}
166#endif
167
168#ifdef WR_FRAGMENT_SHADER
169
170#define COMPONENT_TRANSFER_IDENTITY 0
171#define COMPONENT_TRANSFER_TABLE 1
172#define COMPONENT_TRANSFER_DISCRETE 2
173#define COMPONENT_TRANSFER_LINEAR 3
174#define COMPONENT_TRANSFER_GAMMA 4
175
176vec3 Multiply(vec3 Cb, vec3 Cs) {
177    return Cb * Cs;
178}
179
180vec3 Screen(vec3 Cb, vec3 Cs) {
181    return Cb + Cs - (Cb * Cs);
182}
183
184vec3 HardLight(vec3 Cb, vec3 Cs) {
185    vec3 m = Multiply(Cb, 2.0 * Cs);
186    vec3 s = Screen(Cb, 2.0 * Cs - 1.0);
187    vec3 edge = vec3(0.5, 0.5, 0.5);
188    return mix(m, s, step(edge, Cs));
189}
190
191// TODO: Worth doing with mix/step? Check GLSL output.
192float ColorDodge(float Cb, float Cs) {
193    if (Cb == 0.0)
194        return 0.0;
195    else if (Cs == 1.0)
196        return 1.0;
197    else
198        return min(1.0, Cb / (1.0 - Cs));
199}
200
201// TODO: Worth doing with mix/step? Check GLSL output.
202float ColorBurn(float Cb, float Cs) {
203    if (Cb == 1.0)
204        return 1.0;
205    else if (Cs == 0.0)
206        return 0.0;
207    else
208        return 1.0 - min(1.0, (1.0 - Cb) / Cs);
209}
210
211float SoftLight(float Cb, float Cs) {
212    if (Cs <= 0.5) {
213        return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb);
214    } else {
215        float D;
216
217        if (Cb <= 0.25)
218            D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb;
219        else
220            D = sqrt(Cb);
221
222        return Cb + (2.0 * Cs - 1.0) * (D - Cb);
223    }
224}
225
226vec3 Difference(vec3 Cb, vec3 Cs) {
227    return abs(Cb - Cs);
228}
229
230vec3 Exclusion(vec3 Cb, vec3 Cs) {
231    return Cb + Cs - 2.0 * Cb * Cs;
232}
233
234// These functions below are taken from the spec.
235// There's probably a much quicker way to implement
236// them in GLSL...
237float Sat(vec3 c) {
238    return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));
239}
240
241float Lum(vec3 c) {
242    vec3 f = vec3(0.3, 0.59, 0.11);
243    return dot(c, f);
244}
245
246vec3 ClipColor(vec3 C) {
247    float L = Lum(C);
248    float n = min(C.r, min(C.g, C.b));
249    float x = max(C.r, max(C.g, C.b));
250
251    if (n < 0.0)
252        C = L + (((C - L) * L) / (L - n));
253
254    if (x > 1.0)
255        C = L + (((C - L) * (1.0 - L)) / (x - L));
256
257    return C;
258}
259
260vec3 SetLum(vec3 C, float l) {
261    float d = l - Lum(C);
262    return ClipColor(C + d);
263}
264
265void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) {
266    if (Cmax > Cmin) {
267        Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin));
268        Cmax = s;
269    } else {
270        Cmid = 0.0;
271        Cmax = 0.0;
272    }
273    Cmin = 0.0;
274}
275
276vec3 SetSat(vec3 C, float s) {
277    if (C.r <= C.g) {
278        if (C.g <= C.b) {
279            SetSatInner(C.r, C.g, C.b, s);
280        } else {
281            if (C.r <= C.b) {
282                SetSatInner(C.r, C.b, C.g, s);
283            } else {
284                SetSatInner(C.b, C.r, C.g, s);
285            }
286        }
287    } else {
288        if (C.r <= C.b) {
289            SetSatInner(C.g, C.r, C.b, s);
290        } else {
291            if (C.g <= C.b) {
292                SetSatInner(C.g, C.b, C.r, s);
293            } else {
294                SetSatInner(C.b, C.g, C.r, s);
295            }
296        }
297    }
298    return C;
299}
300
301vec3 Hue(vec3 Cb, vec3 Cs) {
302    return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb));
303}
304
305vec3 Saturation(vec3 Cb, vec3 Cs) {
306    return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb));
307}
308
309vec3 Color(vec3 Cb, vec3 Cs) {
310    return SetLum(Cs, Lum(Cb));
311}
312
313vec3 Luminosity(vec3 Cb, vec3 Cs) {
314    return SetLum(Cb, Lum(Cs));
315}
316
317const int BlendMode_Normal      = 0;
318const int BlendMode_Multiply    = 1;
319const int BlendMode_Screen      = 2;
320const int BlendMode_Overlay     = 3;
321const int BlendMode_Darken      = 4;
322const int BlendMode_Lighten     = 5;
323const int BlendMode_ColorDodge  = 6;
324const int BlendMode_ColorBurn   = 7;
325const int BlendMode_HardLight   = 8;
326const int BlendMode_SoftLight   = 9;
327const int BlendMode_Difference  = 10;
328const int BlendMode_Exclusion   = 11;
329const int BlendMode_Hue         = 12;
330const int BlendMode_Saturation  = 13;
331const int BlendMode_Color       = 14;
332const int BlendMode_Luminosity  = 15;
333
334vec4 blend(vec4 Cs, vec4 Cb, int mode) {
335    vec4 result = vec4(1.0, 0.0, 0.0, 1.0);
336
337    switch (mode) {
338        case BlendMode_Normal:
339            result.rgb = Cs.rgb;
340            break;
341        case BlendMode_Multiply:
342            result.rgb = Multiply(Cb.rgb, Cs.rgb);
343            break;
344        case BlendMode_Screen:
345            result.rgb = Screen(Cb.rgb, Cs.rgb);
346            break;
347        case BlendMode_Overlay:
348            // Overlay is inverse of Hardlight
349            result.rgb = HardLight(Cs.rgb, Cb.rgb);
350            break;
351        case BlendMode_Darken:
352            result.rgb = min(Cs.rgb, Cb.rgb);
353            break;
354        case BlendMode_Lighten:
355            result.rgb = max(Cs.rgb, Cb.rgb);
356            break;
357        case BlendMode_ColorDodge:
358            result.r = ColorDodge(Cb.r, Cs.r);
359            result.g = ColorDodge(Cb.g, Cs.g);
360            result.b = ColorDodge(Cb.b, Cs.b);
361            break;
362        case BlendMode_ColorBurn:
363            result.r = ColorBurn(Cb.r, Cs.r);
364            result.g = ColorBurn(Cb.g, Cs.g);
365            result.b = ColorBurn(Cb.b, Cs.b);
366            break;
367        case BlendMode_HardLight:
368            result.rgb = HardLight(Cb.rgb, Cs.rgb);
369            break;
370        case BlendMode_SoftLight:
371            result.r = SoftLight(Cb.r, Cs.r);
372            result.g = SoftLight(Cb.g, Cs.g);
373            result.b = SoftLight(Cb.b, Cs.b);
374            break;
375        case BlendMode_Difference:
376            result.rgb = Difference(Cb.rgb, Cs.rgb);
377            break;
378        case BlendMode_Exclusion:
379            result.rgb = Exclusion(Cb.rgb, Cs.rgb);
380            break;
381        case BlendMode_Hue:
382            result.rgb = Hue(Cb.rgb, Cs.rgb);
383            break;
384        case BlendMode_Saturation:
385            result.rgb = Saturation(Cb.rgb, Cs.rgb);
386            break;
387        case BlendMode_Color:
388            result.rgb = Color(Cb.rgb, Cs.rgb);
389            break;
390        case BlendMode_Luminosity:
391            result.rgb = Luminosity(Cb.rgb, Cs.rgb);
392            break;
393        default: break;
394    }
395    vec3 rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb;
396    result = mix(vec4(Cb.rgb * Cb.a, Cb.a), vec4(rgb, 1.0), Cs.a);
397    return result;
398}
399
400// Based on the Gecko's implementation in
401// https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24
402// These could be made faster by sampling a lookup table stored in a float texture
403// with linear interpolation.
404
405vec3 SrgbToLinear(vec3 color) {
406    vec3 c1 = color / 12.92;
407    vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4));
408    return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2);
409}
410
411vec3 LinearToSrgb(vec3 color) {
412    vec3 c1 = color * 12.92;
413    vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055);
414    return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2);
415}
416
417// This function has to be factored out due to the following issue:
418// https://github.com/servo/webrender/wiki/Driver-issues#bug-1532245---switch-statement-inside-control-flow-inside-switch-statement-fails-to-compile-on-some-android-phones
419// (and now the words "default: default:" so angle_shader_validation.rs passes)
420vec4 ComponentTransfer(vec4 colora) {
421    // We push a different amount of data to the gpu cache depending on the
422    // function type.
423    // Identity => 0 blocks
424    // Table/Discrete => 64 blocks (256 values)
425    // Linear => 1 block (2 values)
426    // Gamma => 1 block (3 values)
427    // We loop through the color components and increment the offset (for the
428    // next color component) into the gpu cache based on how many blocks that
429    // function type put into the gpu cache.
430    // Table/Discrete use a 256 entry look up table.
431    // Linear/Gamma are a simple calculation.
432    int offset = 0;
433    vec4 texel;
434    int k;
435
436    // Dynamically indexing a vector is buggy on some devices, so use a temporary array.
437    int[4] funcs = int[4](vFuncs.r, vFuncs.g, vFuncs.b, vFuncs.a);
438    for (int i = 0; i < 4; i++) {
439        switch (funcs[i]) {
440            case COMPONENT_TRANSFER_IDENTITY:
441                break;
442            case COMPONENT_TRANSFER_TABLE:
443            case COMPONENT_TRANSFER_DISCRETE:
444                // fetch value from lookup table
445                k = int(floor(colora[i]*255.0));
446                texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset + k/4, 0));
447                colora[i] = clamp(texel[k % 4], 0.0, 1.0);
448                // offset plus 256/4 blocks
449                offset = offset + 64;
450                break;
451            case COMPONENT_TRANSFER_LINEAR:
452                // fetch the two values for use in the linear equation
453                texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset, 0));
454                colora[i] = clamp(texel[0] * colora[i] + texel[1], 0.0, 1.0);
455                // offset plus 1 block
456                offset = offset + 1;
457                break;
458            case COMPONENT_TRANSFER_GAMMA:
459                // fetch the three values for use in the gamma equation
460                texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset, 0));
461                colora[i] = clamp(texel[0] * pow(colora[i], texel[1]) + texel[2], 0.0, 1.0);
462                // offset plus 1 block
463                offset = offset + 1;
464                break;
465            default:
466                // shouldn't happen
467                break;
468        }
469    }
470    return colora;
471}
472
473// Composite Filter
474
475vec4 composite(vec4 Cs, vec4 Cb, int mode) {
476    vec4 Cr = vec4(0.0, 1.0, 0.0, 1.0);
477    switch (mode) {
478        case COMPOSITE_OVER:
479            Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb * (1.0 - Cs.a);
480            Cr.a = Cs.a + Cb.a * (1.0 - Cs.a);
481            break;
482        case COMPOSITE_IN:
483            Cr.rgb = Cs.a * Cs.rgb * Cb.a;
484            Cr.a = Cs.a * Cb.a;
485            break;
486        case COMPOSITE_OUT:
487            Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a);
488            Cr.a = Cs.a * (1.0 - Cb.a);
489            break;
490        case COMPOSITE_ATOP:
491            Cr.rgb = Cs.a * Cs.rgb * Cb.a + Cb.a * Cb.rgb * (1.0 - Cs.a);
492            Cr.a = Cs.a * Cb.a + Cb.a * (1.0 - Cs.a);
493            break;
494        case COMPOSITE_XOR:
495            Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a) + Cb.a * Cb.rgb * (1.0 - Cs.a);
496            Cr.a = Cs.a * (1.0 - Cb.a) + Cb.a * (1.0 - Cs.a);
497            break;
498        case COMPOSITE_LIGHTER:
499            Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb;
500            Cr.a = Cs.a + Cb.a;
501            Cr = clamp(Cr, vec4(0.0), vec4(1.0));
502            break;
503        case COMPOSITE_ARITHMETIC:
504            Cr = vec4(vFilterData0.x) * Cs * Cb + vec4(vFilterData0.y) * Cs + vec4(vFilterData0.z) * Cb + vec4(vFilterData0.w);
505            Cr = clamp(Cr, vec4(0.0), vec4(1.0));
506            break;
507        default:
508            break;
509    }
510    return Cr;
511}
512
513vec4 sampleInUvRect(sampler2D sampler, vec2 uv, vec4 uvRect) {
514    vec2 clamped = clamp(uv.xy, uvRect.xy, uvRect.zw);
515    return texture(sampler, clamped);
516}
517
518void main(void) {
519    vec4 Ca = vec4(0.0, 0.0, 0.0, 0.0);
520    vec4 Cb = vec4(0.0, 0.0, 0.0, 0.0);
521    if (vFilterInputCount > 0) {
522        Ca = sampleInUvRect(sColor0, vInput1Uv, vInput1UvRect);
523        if (Ca.a != 0.0) {
524            Ca.rgb /= Ca.a;
525        }
526    }
527    if (vFilterInputCount > 1) {
528        Cb = sampleInUvRect(sColor1, vInput2Uv, vInput2UvRect);
529        if (Cb.a != 0.0) {
530            Cb.rgb /= Cb.a;
531        }
532    }
533
534    vec4 result = vec4(1.0, 0.0, 0.0, 1.0);
535
536    bool needsPremul = true;
537
538    switch (vFilterKind) {
539        case FILTER_BLEND:
540            result = blend(Ca, Cb, vData.x);
541            needsPremul = false;
542            break;
543        case FILTER_FLOOD:
544            result = vFilterData0;
545            needsPremul = false;
546            break;
547        case FILTER_LINEAR_TO_SRGB:
548            result.rgb = LinearToSrgb(Ca.rgb);
549            result.a = Ca.a;
550            break;
551        case FILTER_SRGB_TO_LINEAR:
552            result.rgb = SrgbToLinear(Ca.rgb);
553            result.a = Ca.a;
554            break;
555        case FILTER_OPACITY:
556            result.rgb = Ca.rgb;
557            result.a = Ca.a * vFloat0.x;
558            break;
559        case FILTER_COLOR_MATRIX:
560            result = vColorMat * Ca + vFilterData0;
561            result = clamp(result, vec4(0.0), vec4(1.0));
562            break;
563        case FILTER_DROP_SHADOW:
564            vec4 shadow = vec4(vFilterData0.rgb, Cb.a * vFilterData0.a);
565            // Normal blend + source-over coposite
566            result = blend(Ca, shadow, BlendMode_Normal);
567            needsPremul = false;
568            break;
569        case FILTER_OFFSET:
570            vec2 offsetUv = vInput1Uv + vFilterData0.xy;
571            result = sampleInUvRect(sColor0, offsetUv, vInput1UvRect);
572            result *= point_inside_rect(offsetUv, vFilterData1.xy, vFilterData1.zw);
573            needsPremul = false;
574            break;
575        case FILTER_COMPONENT_TRANSFER:
576            result = ComponentTransfer(Ca);
577            break;
578        case FILTER_IDENTITY:
579            result = Ca;
580            break;
581        case FILTER_COMPOSITE:
582            result = composite(Ca, Cb, vData.x);
583            needsPremul = false;
584        default:
585            break;
586    }
587
588    if (needsPremul) {
589        result.rgb *= result.a;
590    }
591
592    oFragColor = result;
593}
594#endif
595