diff options
Diffstat (limited to 'gfx/wr/webrender/res/cs_border_segment.glsl')
-rw-r--r-- | gfx/wr/webrender/res/cs_border_segment.glsl | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/gfx/wr/webrender/res/cs_border_segment.glsl b/gfx/wr/webrender/res/cs_border_segment.glsl new file mode 100644 index 0000000000..68553074fa --- /dev/null +++ b/gfx/wr/webrender/res/cs_border_segment.glsl @@ -0,0 +1,450 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include shared,ellipse + +// For edges, the colors are the same. For corners, these +// are the colors of each edge making up the corner. +flat varying vec4 vColor00; +flat varying vec4 vColor01; +flat varying vec4 vColor10; +flat varying vec4 vColor11; + +// A point + tangent defining the line where the edge +// transition occurs. Used for corners only. +flat varying vec4 vColorLine; + +// x = segment, y = styles, z = edge axes, w = clip mode +// Since by default in GLES the vertex shader uses highp +// and the fragment shader uses mediump, we explicitely +// use mediump precision so we align with the default +// mediump precision in the fragment shader. +flat varying mediump ivec4 vConfig; + +// xy = Local space position of the clip center. +// zw = Scale the rect origin by this to get the outer +// corner from the segment rectangle. +flat varying vec4 vClipCenter_Sign; + +// An outer and inner elliptical radii for border +// corner clipping. +flat varying vec4 vClipRadii; + +// Reference point for determine edge clip lines. +flat varying vec4 vEdgeReference; + +// Stores widths/2 and widths/3 to save doing this in FS. +flat varying vec4 vPartialWidths; + +// Clipping parameters for dot or dash. +flat varying vec4 vClipParams1; +flat varying vec4 vClipParams2; + +// Local space position +varying vec2 vPos; + +#define SEGMENT_TOP_LEFT 0 +#define SEGMENT_TOP_RIGHT 1 +#define SEGMENT_BOTTOM_RIGHT 2 +#define SEGMENT_BOTTOM_LEFT 3 +#define SEGMENT_LEFT 4 +#define SEGMENT_TOP 5 +#define SEGMENT_RIGHT 6 +#define SEGMENT_BOTTOM 7 + +// Border styles as defined in webrender_api/types.rs +#define BORDER_STYLE_NONE 0 +#define BORDER_STYLE_SOLID 1 +#define BORDER_STYLE_DOUBLE 2 +#define BORDER_STYLE_DOTTED 3 +#define BORDER_STYLE_DASHED 4 +#define BORDER_STYLE_HIDDEN 5 +#define BORDER_STYLE_GROOVE 6 +#define BORDER_STYLE_RIDGE 7 +#define BORDER_STYLE_INSET 8 +#define BORDER_STYLE_OUTSET 9 + +#define CLIP_NONE 0 +#define CLIP_DASH_CORNER 1 +#define CLIP_DASH_EDGE 2 +#define CLIP_DOT 3 + +#ifdef WR_VERTEX_SHADER + +PER_INSTANCE in vec2 aTaskOrigin; +PER_INSTANCE in vec4 aRect; +PER_INSTANCE in vec4 aColor0; +PER_INSTANCE in vec4 aColor1; +PER_INSTANCE in int aFlags; +PER_INSTANCE in vec2 aWidths; +PER_INSTANCE in vec2 aRadii; +PER_INSTANCE in vec4 aClipParams1; +PER_INSTANCE in vec4 aClipParams2; + +vec2 get_outer_corner_scale(int segment) { + vec2 p; + + switch (segment) { + case SEGMENT_TOP_LEFT: + p = vec2(0.0, 0.0); + break; + case SEGMENT_TOP_RIGHT: + p = vec2(1.0, 0.0); + break; + case SEGMENT_BOTTOM_RIGHT: + p = vec2(1.0, 1.0); + break; + case SEGMENT_BOTTOM_LEFT: + p = vec2(0.0, 1.0); + break; + default: + // The result is only used for non-default segment cases + p = vec2(0.0); + break; + } + + return p; +} + +// NOTE(emilio): If you change this algorithm, do the same change +// in border.rs +vec4 mod_color(vec4 color, bool is_black, bool lighter) { + const float light_black = 0.7; + const float dark_black = 0.3; + + const float dark_scale = 0.66666666; + const float light_scale = 1.0; + + if (is_black) { + if (lighter) { + return vec4(vec3(light_black), color.a); + } + return vec4(vec3(dark_black), color.a); + } + + if (lighter) { + return vec4(color.rgb * light_scale, color.a); + } + return vec4(color.rgb * dark_scale, color.a); +} + +vec4[2] get_colors_for_side(vec4 color, int style) { + vec4 result[2]; + + bool is_black = color.rgb == vec3(0.0, 0.0, 0.0); + + switch (style) { + case BORDER_STYLE_GROOVE: + result[0] = mod_color(color, is_black, true); + result[1] = mod_color(color, is_black, false); + break; + case BORDER_STYLE_RIDGE: + result[0] = mod_color(color, is_black, false); + result[1] = mod_color(color, is_black, true); + break; + default: + result[0] = color; + result[1] = color; + break; + } + + return result; +} + +void main(void) { + int segment = aFlags & 0xff; + int style0 = (aFlags >> 8) & 0xff; + int style1 = (aFlags >> 16) & 0xff; + int clip_mode = (aFlags >> 24) & 0x0f; + + vec2 outer_scale = get_outer_corner_scale(segment); + vec2 outer = outer_scale * aRect.zw; + vec2 clip_sign = 1.0 - 2.0 * outer_scale; + + // Set some flags used by the FS to determine the + // orientation of the two edges in this corner. + ivec2 edge_axis = ivec2(0, 0); + // Derive the positions for the edge clips, which must be handled + // differently between corners and edges. + vec2 edge_reference = vec2(0.0); + switch (segment) { + case SEGMENT_TOP_LEFT: + edge_axis = ivec2(0, 1); + edge_reference = outer; + break; + case SEGMENT_TOP_RIGHT: + edge_axis = ivec2(1, 0); + edge_reference = vec2(outer.x - aWidths.x, outer.y); + break; + case SEGMENT_BOTTOM_RIGHT: + edge_axis = ivec2(0, 1); + edge_reference = outer - aWidths; + break; + case SEGMENT_BOTTOM_LEFT: + edge_axis = ivec2(1, 0); + edge_reference = vec2(outer.x, outer.y - aWidths.y); + break; + case SEGMENT_TOP: + case SEGMENT_BOTTOM: + edge_axis = ivec2(1, 1); + break; + case SEGMENT_LEFT: + case SEGMENT_RIGHT: + default: + break; + } + + vConfig = ivec4( + segment, + style0 | (style1 << 8), + edge_axis.x | (edge_axis.y << 8), + clip_mode + ); + vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0); + vPos = aRect.zw * aPosition.xy; + + vec4[2] color0 = get_colors_for_side(aColor0, style0); + vColor00 = color0[0]; + vColor01 = color0[1]; + vec4[2] color1 = get_colors_for_side(aColor1, style1); + vColor10 = color1[0]; + vColor11 = color1[1]; + vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign); + vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0)); + vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x); + vEdgeReference = vec4(edge_reference, edge_reference + aWidths); + vClipParams1 = aClipParams1; + vClipParams2 = aClipParams2; + + // For the case of dot and dash clips, optimize the number of pixels that + // are hit to just include the dot itself. + if (clip_mode == CLIP_DOT) { + float radius = aClipParams1.z; + + // Expand by a small amount to allow room for AA around + // the dot if it's big enough. + if (radius > 0.5) + radius += 2.0; + + vPos = vClipParams1.xy + radius * (2.0 * aPosition.xy - 1.0); + vPos = clamp(vPos, vec2(0.0), aRect.zw); + } else if (clip_mode == CLIP_DASH_CORNER) { + vec2 center = (aClipParams1.xy + aClipParams2.xy) * 0.5; + // This is a gross approximation which works out because dashes don't have + // a strong curvature and we will overshoot by inflating the geometry by + // this amount on each side (sqrt(2) * length(dash) would be enough and we + // compute 2 * approx_length(dash)). + float dash_length = length(aClipParams1.xy - aClipParams2.xy); + float width = max(aWidths.x, aWidths.y); + // expand by a small amout for AA just like we do for dots. + vec2 r = vec2(max(dash_length, width)) + 2.0; + vPos = clamp(vPos, center - r, center + r); + } + + gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +vec4 evaluate_color_for_style_in_corner( + vec2 clip_relative_pos, + int style, + vec4 color0, + vec4 color1, + vec4 clip_radii, + float mix_factor, + int segment, + float aa_range +) { + switch (style) { + case BORDER_STYLE_DOUBLE: { + // Get the distances from 0.33 of the radii, and + // also 0.67 of the radii. Use these to form a + // SDF subtraction which will clip out the inside + // third of the rounded edge. + float d_radii_a = distance_to_ellipse( + clip_relative_pos, + clip_radii.xy - vPartialWidths.xy + ); + float d_radii_b = distance_to_ellipse( + clip_relative_pos, + clip_radii.xy - 2.0 * vPartialWidths.xy + ); + float d = min(-d_radii_a, d_radii_b); + color0 *= distance_aa(aa_range, d); + break; + } + case BORDER_STYLE_GROOVE: + case BORDER_STYLE_RIDGE: { + float d = distance_to_ellipse( + clip_relative_pos, + clip_radii.xy - vPartialWidths.zw + ); + float alpha = distance_aa(aa_range, d); + float swizzled_factor; + switch (segment) { + case SEGMENT_TOP_LEFT: swizzled_factor = 0.0; break; + case SEGMENT_TOP_RIGHT: swizzled_factor = mix_factor; break; + case SEGMENT_BOTTOM_RIGHT: swizzled_factor = 1.0; break; + case SEGMENT_BOTTOM_LEFT: swizzled_factor = 1.0 - mix_factor; break; + default: swizzled_factor = 0.0; break; + }; + vec4 c0 = mix(color1, color0, swizzled_factor); + vec4 c1 = mix(color0, color1, swizzled_factor); + color0 = mix(c0, c1, alpha); + break; + } + default: + break; + } + + return color0; +} + +vec4 evaluate_color_for_style_in_edge( + vec2 pos_vec, + int style, + vec4 color0, + vec4 color1, + float aa_range, + int edge_axis_id +) { + vec2 edge_axis = edge_axis_id != 0 ? vec2(0.0, 1.0) : vec2(1.0, 0.0); + float pos = dot(pos_vec, edge_axis); + switch (style) { + case BORDER_STYLE_DOUBLE: { + float d = -1.0; + float partial_width = dot(vPartialWidths.xy, edge_axis); + if (partial_width >= 1.0) { + vec2 ref = vec2( + dot(vEdgeReference.xy, edge_axis) + partial_width, + dot(vEdgeReference.zw, edge_axis) - partial_width + ); + d = min(pos - ref.x, ref.y - pos); + } + color0 *= distance_aa(aa_range, d); + break; + } + case BORDER_STYLE_GROOVE: + case BORDER_STYLE_RIDGE: { + float ref = dot(vEdgeReference.xy + vPartialWidths.zw, edge_axis); + float d = pos - ref; + float alpha = distance_aa(aa_range, d); + color0 = mix(color0, color1, alpha); + break; + } + default: + break; + } + + return color0; +} + +void main(void) { + float aa_range = compute_aa_range(vPos); + vec4 color0, color1; + + int segment = vConfig.x; + ivec2 style = ivec2(vConfig.y & 0xff, vConfig.y >> 8); + ivec2 edge_axis = ivec2(vConfig.z & 0xff, vConfig.z >> 8); + int clip_mode = vConfig.w; + + float mix_factor = 0.0; + if (edge_axis.x != edge_axis.y) { + float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos); + mix_factor = distance_aa(aa_range, -d_line); + } + + // Check if inside corner clip-region + vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy; + bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0))); + float d = -1.0; + + switch (clip_mode) { + case CLIP_DOT: { + // Set clip distance based or dot position and radius. + d = distance(vClipParams1.xy, vPos) - vClipParams1.z; + break; + } + case CLIP_DASH_EDGE: { + bool is_vertical = vClipParams1.x == 0.; + float half_dash = is_vertical ? vClipParams1.y : vClipParams1.x; + // We want to draw something like: + // +---+---+---+---+ + // |xxx| | |xxx| + // +---+---+---+---+ + float pos = is_vertical ? vPos.y : vPos.x; + bool in_dash = pos < half_dash || pos > 3.0 * half_dash; + if (!in_dash) { + d = 1.; + } + break; + } + case CLIP_DASH_CORNER: { + // Get SDF for the two line/tangent clip lines, + // do SDF subtract to get clip distance. + float d0 = distance_to_line(vClipParams1.xy, + vClipParams1.zw, + vPos); + float d1 = distance_to_line(vClipParams2.xy, + vClipParams2.zw, + vPos); + d = max(d0, -d1); + break; + } + case CLIP_NONE: + default: + break; + } + + if (in_clip_region) { + float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy); + float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw); + float d_radii = max(d_radii_a, -d_radii_b); + d = max(d, d_radii); + + color0 = evaluate_color_for_style_in_corner( + clip_relative_pos, + style.x, + vColor00, + vColor01, + vClipRadii, + mix_factor, + segment, + aa_range + ); + color1 = evaluate_color_for_style_in_corner( + clip_relative_pos, + style.y, + vColor10, + vColor11, + vClipRadii, + mix_factor, + segment, + aa_range + ); + } else { + color0 = evaluate_color_for_style_in_edge( + vPos, + style.x, + vColor00, + vColor01, + aa_range, + edge_axis.x + ); + color1 = evaluate_color_for_style_in_edge( + vPos, + style.y, + vColor10, + vColor11, + aa_range, + edge_axis.y + ); + } + + float alpha = distance_aa(aa_range, d); + vec4 color = mix(color0, color1, mix_factor); + oFragColor = color * alpha; +} +#endif |