Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / gfx / wr / webrender / res / cs_border_segment.glsl
blob7deb62f6efd2815baaa89546c1dc53b20ccd8876
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/. */
5 #include shared,rect,ellipse
7 // For edges, the colors are the same. For corners, these
8 // are the colors of each edge making up the corner.
9 flat varying mediump vec4 vColor00;
10 flat varying mediump vec4 vColor01;
11 flat varying mediump vec4 vColor10;
12 flat varying mediump vec4 vColor11;
14 // A point + tangent defining the line where the edge
15 // transition occurs. Used for corners only.
16 flat varying highp vec4 vColorLine;
18 // x: segment, y: clip mode
19 // We cast these to/from floats rather than using an ivec due to a driver bug
20 // on Adreno 3xx. See bug 1730458.
21 flat varying mediump vec2 vSegmentClipMode;
22 // x, y: styles, z, w: edge axes
23 // We cast these to/from floats rather than using an ivec (and bitshifting)
24 // due to a driver bug on Adreno 3xx. See bug 1730458.
25 flat varying mediump vec4 vStyleEdgeAxis;
27 // xy = Local space position of the clip center.
28 // zw = Scale the rect origin by this to get the outer
29 // corner from the segment rectangle.
30 flat varying highp vec4 vClipCenter_Sign;
32 // An outer and inner elliptical radii for border
33 // corner clipping.
34 flat varying highp vec4 vClipRadii;
36 // Reference point for determine edge clip lines.
37 flat varying highp vec4 vEdgeReference;
39 // Stores widths/2 and widths/3 to save doing this in FS.
40 flat varying highp vec4 vPartialWidths;
42 // Clipping parameters for dot or dash.
43 flat varying mediump vec4 vClipParams1;
44 flat varying mediump vec4 vClipParams2;
46 // Local space position
47 varying highp vec2 vPos;
49 #define SEGMENT_TOP_LEFT        0
50 #define SEGMENT_TOP_RIGHT       1
51 #define SEGMENT_BOTTOM_RIGHT    2
52 #define SEGMENT_BOTTOM_LEFT     3
53 #define SEGMENT_LEFT            4
54 #define SEGMENT_TOP             5
55 #define SEGMENT_RIGHT           6
56 #define SEGMENT_BOTTOM          7
58 // Border styles as defined in webrender_api/types.rs
59 #define BORDER_STYLE_NONE         0
60 #define BORDER_STYLE_SOLID        1
61 #define BORDER_STYLE_DOUBLE       2
62 #define BORDER_STYLE_DOTTED       3
63 #define BORDER_STYLE_DASHED       4
64 #define BORDER_STYLE_HIDDEN       5
65 #define BORDER_STYLE_GROOVE       6
66 #define BORDER_STYLE_RIDGE        7
67 #define BORDER_STYLE_INSET        8
68 #define BORDER_STYLE_OUTSET       9
70 #define CLIP_NONE        0
71 #define CLIP_DASH_CORNER 1
72 #define CLIP_DASH_EDGE   2
73 #define CLIP_DOT         3
75 #ifdef WR_VERTEX_SHADER
77 PER_INSTANCE in vec2 aTaskOrigin;
78 PER_INSTANCE in vec4 aRect;
79 PER_INSTANCE in vec4 aColor0;
80 PER_INSTANCE in vec4 aColor1;
81 PER_INSTANCE in int aFlags;
82 PER_INSTANCE in vec2 aWidths;
83 PER_INSTANCE in vec2 aRadii;
84 PER_INSTANCE in vec4 aClipParams1;
85 PER_INSTANCE in vec4 aClipParams2;
87 vec2 get_outer_corner_scale(int segment) {
88     vec2 p;
90     switch (segment) {
91         case SEGMENT_TOP_LEFT:
92             p = vec2(0.0, 0.0);
93             break;
94         case SEGMENT_TOP_RIGHT:
95             p = vec2(1.0, 0.0);
96             break;
97         case SEGMENT_BOTTOM_RIGHT:
98             p = vec2(1.0, 1.0);
99             break;
100         case SEGMENT_BOTTOM_LEFT:
101             p = vec2(0.0, 1.0);
102             break;
103         default:
104             // The result is only used for non-default segment cases
105             p = vec2(0.0);
106             break;
107     }
109     return p;
112 // NOTE(emilio): If you change this algorithm, do the same change
113 // in border.rs
114 vec4 mod_color(vec4 color, bool is_black, bool lighter) {
115     const float light_black = 0.7;
116     const float dark_black = 0.3;
118     const float dark_scale = 0.66666666;
119     const float light_scale = 1.0;
121     if (is_black) {
122         if (lighter) {
123             return vec4(vec3(light_black), color.a);
124         }
125         return vec4(vec3(dark_black), color.a);
126     }
128     if (lighter) {
129         return vec4(color.rgb * light_scale, color.a);
130     }
131     return vec4(color.rgb * dark_scale, color.a);
134 vec4[2] get_colors_for_side(vec4 color, int style) {
135     vec4 result[2];
137     bool is_black = color.rgb == vec3(0.0, 0.0, 0.0);
139     switch (style) {
140         case BORDER_STYLE_GROOVE:
141             result[0] = mod_color(color, is_black, true);
142             result[1] = mod_color(color, is_black, false);
143             break;
144         case BORDER_STYLE_RIDGE:
145             result[0] = mod_color(color, is_black, false);
146             result[1] = mod_color(color, is_black, true);
147             break;
148         default:
149             result[0] = color;
150             result[1] = color;
151             break;
152     }
154     return result;
157 void main(void) {
158     int segment = aFlags & 0xff;
159     int style0 = (aFlags >> 8) & 0xff;
160     int style1 = (aFlags >> 16) & 0xff;
161     int clip_mode = (aFlags >> 24) & 0x0f;
163     vec2 size = aRect.zw - aRect.xy;
164     vec2 outer_scale = get_outer_corner_scale(segment);
165     vec2 outer = outer_scale * size;
166     vec2 clip_sign = 1.0 - 2.0 * outer_scale;
168     // Set some flags used by the FS to determine the
169     // orientation of the two edges in this corner.
170     ivec2 edge_axis = ivec2(0, 0);
171     // Derive the positions for the edge clips, which must be handled
172     // differently between corners and edges.
173     vec2 edge_reference = vec2(0.0);
174     switch (segment) {
175         case SEGMENT_TOP_LEFT:
176             edge_axis = ivec2(0, 1);
177             edge_reference = outer;
178             break;
179         case SEGMENT_TOP_RIGHT:
180             edge_axis = ivec2(1, 0);
181             edge_reference = vec2(outer.x - aWidths.x, outer.y);
182             break;
183         case SEGMENT_BOTTOM_RIGHT:
184             edge_axis = ivec2(0, 1);
185             edge_reference = outer - aWidths;
186             break;
187         case SEGMENT_BOTTOM_LEFT:
188             edge_axis = ivec2(1, 0);
189             edge_reference = vec2(outer.x, outer.y - aWidths.y);
190             break;
191         case SEGMENT_TOP:
192         case SEGMENT_BOTTOM:
193             edge_axis = ivec2(1, 1);
194             break;
195         case SEGMENT_LEFT:
196         case SEGMENT_RIGHT:
197         default:
198             break;
199     }
201     vSegmentClipMode = vec2(float(segment), float(clip_mode));
202     vStyleEdgeAxis = vec4(float(style0), float(style1), float(edge_axis.x), float(edge_axis.y));
204     vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0);
205     vPos = size * aPosition.xy;
207     vec4[2] color0 = get_colors_for_side(aColor0, style0);
208     vColor00 = color0[0];
209     vColor01 = color0[1];
210     vec4[2] color1 = get_colors_for_side(aColor1, style1);
211     vColor10 = color1[0];
212     vColor11 = color1[1];
213     vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
214     vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
215     vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
216     vEdgeReference = vec4(edge_reference, edge_reference + aWidths);
217     vClipParams1 = aClipParams1;
218     vClipParams2 = aClipParams2;
220     // For the case of dot and dash clips, optimize the number of pixels that
221     // are hit to just include the dot itself.
222     if (clip_mode == CLIP_DOT) {
223         float radius = aClipParams1.z;
225         // Expand by a small amount to allow room for AA around
226         // the dot if it's big enough.
227         if (radius > 0.5)
228             radius += 2.0;
230         vPos = vClipParams1.xy + radius * (2.0 * aPosition.xy - 1.0);
231         vPos = clamp(vPos, vec2(0.0), size);
232     } else if (clip_mode == CLIP_DASH_CORNER) {
233         vec2 center = (aClipParams1.xy + aClipParams2.xy) * 0.5;
234         // This is a gross approximation which works out because dashes don't have
235         // a strong curvature and we will overshoot by inflating the geometry by
236         // this amount on each side (sqrt(2) * length(dash) would be enough and we
237         // compute 2 * approx_length(dash)).
238         float dash_length = length(aClipParams1.xy - aClipParams2.xy);
239         float width = max(aWidths.x, aWidths.y);
240         // expand by a small amout for AA just like we do for dots.
241         vec2 r = vec2(max(dash_length, width)) + 2.0;
242         vPos = clamp(vPos, center - r, center + r);
243     }
245     gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
247 #endif
249 #ifdef WR_FRAGMENT_SHADER
250 vec4 evaluate_color_for_style_in_corner(
251     vec2 clip_relative_pos,
252     int style,
253     vec4 color0,
254     vec4 color1,
255     vec4 clip_radii,
256     float mix_factor,
257     int segment,
258     float aa_range
259 ) {
260     switch (style) {
261         case BORDER_STYLE_DOUBLE: {
262             // Get the distances from 0.33 of the radii, and
263             // also 0.67 of the radii. Use these to form a
264             // SDF subtraction which will clip out the inside
265             // third of the rounded edge.
266             float d_radii_a = distance_to_ellipse(
267                 clip_relative_pos,
268                 clip_radii.xy - vPartialWidths.xy
269             );
270             float d_radii_b = distance_to_ellipse(
271                 clip_relative_pos,
272                 clip_radii.xy - 2.0 * vPartialWidths.xy
273             );
274             float d = min(-d_radii_a, d_radii_b);
275             color0 *= distance_aa(aa_range, d);
276             break;
277         }
278         case BORDER_STYLE_GROOVE:
279         case BORDER_STYLE_RIDGE: {
280             float d = distance_to_ellipse(
281                 clip_relative_pos,
282                 clip_radii.xy - vPartialWidths.zw
283             );
284             float alpha = distance_aa(aa_range, d);
285             float swizzled_factor;
286             switch (segment) {
287                 case SEGMENT_TOP_LEFT: swizzled_factor = 0.0; break;
288                 case SEGMENT_TOP_RIGHT: swizzled_factor = mix_factor; break;
289                 case SEGMENT_BOTTOM_RIGHT: swizzled_factor = 1.0; break;
290                 case SEGMENT_BOTTOM_LEFT: swizzled_factor = 1.0 - mix_factor; break;
291                 default: swizzled_factor = 0.0; break;
292             };
293             vec4 c0 = mix(color1, color0, swizzled_factor);
294             vec4 c1 = mix(color0, color1, swizzled_factor);
295             color0 = mix(c0, c1, alpha);
296             break;
297         }
298         default:
299             break;
300     }
302     return color0;
305 vec4 evaluate_color_for_style_in_edge(
306     vec2 pos_vec,
307     int style,
308     vec4 color0,
309     vec4 color1,
310     float aa_range,
311     int edge_axis_id
312 ) {
313     vec2 edge_axis = edge_axis_id != 0 ? vec2(0.0, 1.0) : vec2(1.0, 0.0);
314     float pos = dot(pos_vec, edge_axis);
315     switch (style) {
316         case BORDER_STYLE_DOUBLE: {
317             float d = -1.0;
318             float partial_width = dot(vPartialWidths.xy, edge_axis);
319             if (partial_width >= 1.0) {
320                 vec2 ref = vec2(
321                     dot(vEdgeReference.xy, edge_axis) + partial_width,
322                     dot(vEdgeReference.zw, edge_axis) - partial_width
323                 );
324                 d = min(pos - ref.x, ref.y - pos);
325             }
326             color0 *= distance_aa(aa_range, d);
327             break;
328         }
329         case BORDER_STYLE_GROOVE:
330         case BORDER_STYLE_RIDGE: {
331             float ref = dot(vEdgeReference.xy + vPartialWidths.zw, edge_axis);
332             float d = pos - ref;
333             float alpha = distance_aa(aa_range, d);
334             color0 = mix(color0, color1, alpha);
335             break;
336         }
337         default:
338             break;
339     }
341     return color0;
344 void main(void) {
345     float aa_range = compute_aa_range(vPos);
346     vec4 color0, color1;
348     int segment = int(vSegmentClipMode.x);
349     int clip_mode = int(vSegmentClipMode.y);
350     ivec2 style = ivec2(int(vStyleEdgeAxis.x), int(vStyleEdgeAxis.y));
351     ivec2 edge_axis = ivec2(int(vStyleEdgeAxis.z), int(vStyleEdgeAxis.w));
353     float mix_factor = 0.0;
354     if (edge_axis.x != edge_axis.y) {
355         float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
356         mix_factor = distance_aa(aa_range, -d_line);
357     }
359     // Check if inside corner clip-region
360     vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy;
361     bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
362     float d = -1.0;
364     switch (clip_mode) {
365         case CLIP_DOT: {
366             // Set clip distance based or dot position and radius.
367             d = distance(vClipParams1.xy, vPos) - vClipParams1.z;
368             break;
369         }
370         case CLIP_DASH_EDGE: {
371             bool is_vertical = vClipParams1.x == 0.;
372             float half_dash = is_vertical ? vClipParams1.y : vClipParams1.x;
373             // We want to draw something like:
374             // +---+---+---+---+
375             // |xxx|   |   |xxx|
376             // +---+---+---+---+
377             float pos = is_vertical ? vPos.y : vPos.x;
378             bool in_dash = pos < half_dash || pos > 3.0 * half_dash;
379             if (!in_dash) {
380                 d = 1.;
381             }
382             break;
383         }
384         case CLIP_DASH_CORNER: {
385             // Get SDF for the two line/tangent clip lines,
386             // do SDF subtract to get clip distance.
387             float d0 = distance_to_line(vClipParams1.xy,
388                                         vClipParams1.zw,
389                                         vPos);
390             float d1 = distance_to_line(vClipParams2.xy,
391                                         vClipParams2.zw,
392                                         vPos);
393             d = max(d0, -d1);
394             break;
395         }
396         case CLIP_NONE:
397         default:
398             break;
399     }
401     if (in_clip_region) {
402         float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy);
403         float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw);
404         float d_radii = max(d_radii_a, -d_radii_b);
405         d = max(d, d_radii);
407         color0 = evaluate_color_for_style_in_corner(
408             clip_relative_pos,
409             style.x,
410             vColor00,
411             vColor01,
412             vClipRadii,
413             mix_factor,
414             segment,
415             aa_range
416         );
417         color1 = evaluate_color_for_style_in_corner(
418             clip_relative_pos,
419             style.y,
420             vColor10,
421             vColor11,
422             vClipRadii,
423             mix_factor,
424             segment,
425             aa_range
426         );
427     } else {
428         color0 = evaluate_color_for_style_in_edge(
429             vPos,
430             style.x,
431             vColor00,
432             vColor01,
433             aa_range,
434             edge_axis.x
435         );
436         color1 = evaluate_color_for_style_in_edge(
437             vPos,
438             style.y,
439             vColor10,
440             vColor11,
441             aa_range,
442             edge_axis.y
443         );
444     }
446     float alpha = distance_aa(aa_range, d);
447     vec4 color = mix(color0, color1, mix_factor);
448     oFragColor = color * alpha;
450 #endif