Source code
Revision control
Copy as Markdown
Other Tools
/* 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
#include shared,rect,ellipse
// For edges, the colors are the same. For corners, these
// are the colors of each edge making up the corner.
flat varying mediump vec4 vColor00;
flat varying mediump vec4 vColor01;
flat varying mediump vec4 vColor10;
flat varying mediump vec4 vColor11;
// A point + tangent defining the line where the edge
// transition occurs. Used for corners only.
flat varying highp vec4 vColorLine;
// x: segment, y: clip mode
// We cast these to/from floats rather than using an ivec due to a driver bug
flat varying mediump vec2 vSegmentClipMode;
// x, y: styles, z, w: edge axes
// We cast these to/from floats rather than using an ivec (and bitshifting)
flat varying mediump vec4 vStyleEdgeAxis;
// 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 highp vec4 vClipCenter_Sign;
// An outer and inner elliptical radii for border
// corner clipping.
flat varying highp vec4 vClipRadii;
// Reference point for determine edge clip lines.
flat varying highp vec4 vEdgeReference;
// Stores widths/2 and widths/3 to save doing this in FS.
flat varying highp vec4 vPartialWidths;
// Clipping parameters for dot or dash.
flat varying mediump vec4 vClipParams1;
flat varying mediump vec4 vClipParams2;
// Local space position
varying highp 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 size = aRect.zw - aRect.xy;
vec2 outer_scale = get_outer_corner_scale(segment);
vec2 outer = outer_scale * size;
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;
}
vSegmentClipMode = vec2(float(segment), float(clip_mode));
vStyleEdgeAxis = vec4(float(style0), float(style1), float(edge_axis.x), float(edge_axis.y));
vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0);
vPos = size * 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), size);
} 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 = int(vSegmentClipMode.x);
int clip_mode = int(vSegmentClipMode.y);
ivec2 style = ivec2(int(vStyleEdgeAxis.x), int(vStyleEdgeAxis.y));
ivec2 edge_axis = ivec2(int(vStyleEdgeAxis.z), int(vStyleEdgeAxis.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