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#include shared,ellipse 6 7// For edges, the colors are the same. For corners, these 8// are the colors of each edge making up the corner. 9flat varying vec4 vColor00; 10flat varying vec4 vColor01; 11flat varying vec4 vColor10; 12flat varying vec4 vColor11; 13 14// A point + tangent defining the line where the edge 15// transition occurs. Used for corners only. 16flat varying vec4 vColorLine; 17 18// x = segment, y = styles, z = edge axes, w = clip mode 19// Since by default in GLES the vertex shader uses highp 20// and the fragment shader uses mediump, we explicitely 21// use mediump precision so we align with the default 22// mediump precision in the fragment shader. 23flat varying mediump ivec4 vConfig; 24 25// xy = Local space position of the clip center. 26// zw = Scale the rect origin by this to get the outer 27// corner from the segment rectangle. 28flat varying vec4 vClipCenter_Sign; 29 30// An outer and inner elliptical radii for border 31// corner clipping. 32flat varying vec4 vClipRadii; 33 34// Reference point for determine edge clip lines. 35flat varying vec4 vEdgeReference; 36 37// Stores widths/2 and widths/3 to save doing this in FS. 38flat varying vec4 vPartialWidths; 39 40// Clipping parameters for dot or dash. 41flat varying vec4 vClipParams1; 42flat varying vec4 vClipParams2; 43 44// Local space position 45varying vec2 vPos; 46 47#define SEGMENT_TOP_LEFT 0 48#define SEGMENT_TOP_RIGHT 1 49#define SEGMENT_BOTTOM_RIGHT 2 50#define SEGMENT_BOTTOM_LEFT 3 51#define SEGMENT_LEFT 4 52#define SEGMENT_TOP 5 53#define SEGMENT_RIGHT 6 54#define SEGMENT_BOTTOM 7 55 56// Border styles as defined in webrender_api/types.rs 57#define BORDER_STYLE_NONE 0 58#define BORDER_STYLE_SOLID 1 59#define BORDER_STYLE_DOUBLE 2 60#define BORDER_STYLE_DOTTED 3 61#define BORDER_STYLE_DASHED 4 62#define BORDER_STYLE_HIDDEN 5 63#define BORDER_STYLE_GROOVE 6 64#define BORDER_STYLE_RIDGE 7 65#define BORDER_STYLE_INSET 8 66#define BORDER_STYLE_OUTSET 9 67 68#define CLIP_NONE 0 69#define CLIP_DASH_CORNER 1 70#define CLIP_DASH_EDGE 2 71#define CLIP_DOT 3 72 73#ifdef WR_VERTEX_SHADER 74 75PER_INSTANCE in vec2 aTaskOrigin; 76PER_INSTANCE in vec4 aRect; 77PER_INSTANCE in vec4 aColor0; 78PER_INSTANCE in vec4 aColor1; 79PER_INSTANCE in int aFlags; 80PER_INSTANCE in vec2 aWidths; 81PER_INSTANCE in vec2 aRadii; 82PER_INSTANCE in vec4 aClipParams1; 83PER_INSTANCE in vec4 aClipParams2; 84 85vec2 get_outer_corner_scale(int segment) { 86 vec2 p; 87 88 switch (segment) { 89 case SEGMENT_TOP_LEFT: 90 p = vec2(0.0, 0.0); 91 break; 92 case SEGMENT_TOP_RIGHT: 93 p = vec2(1.0, 0.0); 94 break; 95 case SEGMENT_BOTTOM_RIGHT: 96 p = vec2(1.0, 1.0); 97 break; 98 case SEGMENT_BOTTOM_LEFT: 99 p = vec2(0.0, 1.0); 100 break; 101 default: 102 // The result is only used for non-default segment cases 103 p = vec2(0.0); 104 break; 105 } 106 107 return p; 108} 109 110// NOTE(emilio): If you change this algorithm, do the same change 111// in border.rs 112vec4 mod_color(vec4 color, bool is_black, bool lighter) { 113 const float light_black = 0.7; 114 const float dark_black = 0.3; 115 116 const float dark_scale = 0.66666666; 117 const float light_scale = 1.0; 118 119 if (is_black) { 120 if (lighter) { 121 return vec4(vec3(light_black), color.a); 122 } 123 return vec4(vec3(dark_black), color.a); 124 } 125 126 if (lighter) { 127 return vec4(color.rgb * light_scale, color.a); 128 } 129 return vec4(color.rgb * dark_scale, color.a); 130} 131 132vec4[2] get_colors_for_side(vec4 color, int style) { 133 vec4 result[2]; 134 135 bool is_black = color.rgb == vec3(0.0, 0.0, 0.0); 136 137 switch (style) { 138 case BORDER_STYLE_GROOVE: 139 result[0] = mod_color(color, is_black, true); 140 result[1] = mod_color(color, is_black, false); 141 break; 142 case BORDER_STYLE_RIDGE: 143 result[0] = mod_color(color, is_black, false); 144 result[1] = mod_color(color, is_black, true); 145 break; 146 default: 147 result[0] = color; 148 result[1] = color; 149 break; 150 } 151 152 return result; 153} 154 155void main(void) { 156 int segment = aFlags & 0xff; 157 int style0 = (aFlags >> 8) & 0xff; 158 int style1 = (aFlags >> 16) & 0xff; 159 int clip_mode = (aFlags >> 24) & 0x0f; 160 161 vec2 outer_scale = get_outer_corner_scale(segment); 162 vec2 outer = outer_scale * aRect.zw; 163 vec2 clip_sign = 1.0 - 2.0 * outer_scale; 164 165 // Set some flags used by the FS to determine the 166 // orientation of the two edges in this corner. 167 ivec2 edge_axis = ivec2(0, 0); 168 // Derive the positions for the edge clips, which must be handled 169 // differently between corners and edges. 170 vec2 edge_reference = vec2(0.0); 171 switch (segment) { 172 case SEGMENT_TOP_LEFT: 173 edge_axis = ivec2(0, 1); 174 edge_reference = outer; 175 break; 176 case SEGMENT_TOP_RIGHT: 177 edge_axis = ivec2(1, 0); 178 edge_reference = vec2(outer.x - aWidths.x, outer.y); 179 break; 180 case SEGMENT_BOTTOM_RIGHT: 181 edge_axis = ivec2(0, 1); 182 edge_reference = outer - aWidths; 183 break; 184 case SEGMENT_BOTTOM_LEFT: 185 edge_axis = ivec2(1, 0); 186 edge_reference = vec2(outer.x, outer.y - aWidths.y); 187 break; 188 case SEGMENT_TOP: 189 case SEGMENT_BOTTOM: 190 edge_axis = ivec2(1, 1); 191 break; 192 case SEGMENT_LEFT: 193 case SEGMENT_RIGHT: 194 default: 195 break; 196 } 197 198 vConfig = ivec4( 199 segment, 200 style0 | (style1 << 8), 201 edge_axis.x | (edge_axis.y << 8), 202 clip_mode 203 ); 204 vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0); 205 vPos = aRect.zw * aPosition.xy; 206 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; 219 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; 224 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; 229 230 vPos = vClipParams1.xy + radius * (2.0 * aPosition.xy - 1.0); 231 vPos = clamp(vPos, vec2(0.0), aRect.zw); 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 } 244 245 gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0); 246} 247#endif 248 249#ifdef WR_FRAGMENT_SHADER 250vec4 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 aa_range 270 ); 271 float d_radii_b = distance_to_ellipse( 272 clip_relative_pos, 273 clip_radii.xy - 2.0 * vPartialWidths.xy, 274 aa_range 275 ); 276 float d = min(-d_radii_a, d_radii_b); 277 color0 *= distance_aa(aa_range, d); 278 break; 279 } 280 case BORDER_STYLE_GROOVE: 281 case BORDER_STYLE_RIDGE: { 282 float d = distance_to_ellipse( 283 clip_relative_pos, 284 clip_radii.xy - vPartialWidths.zw, 285 aa_range 286 ); 287 float alpha = distance_aa(aa_range, d); 288 float swizzled_factor; 289 switch (segment) { 290 case SEGMENT_TOP_LEFT: swizzled_factor = 0.0; break; 291 case SEGMENT_TOP_RIGHT: swizzled_factor = mix_factor; break; 292 case SEGMENT_BOTTOM_RIGHT: swizzled_factor = 1.0; break; 293 case SEGMENT_BOTTOM_LEFT: swizzled_factor = 1.0 - mix_factor; break; 294 default: swizzled_factor = 0.0; break; 295 }; 296 vec4 c0 = mix(color1, color0, swizzled_factor); 297 vec4 c1 = mix(color0, color1, swizzled_factor); 298 color0 = mix(c0, c1, alpha); 299 break; 300 } 301 default: 302 break; 303 } 304 305 return color0; 306} 307 308vec4 evaluate_color_for_style_in_edge( 309 vec2 pos_vec, 310 int style, 311 vec4 color0, 312 vec4 color1, 313 float aa_range, 314 int edge_axis_id 315) { 316 vec2 edge_axis = edge_axis_id != 0 ? vec2(0.0, 1.0) : vec2(1.0, 0.0); 317 float pos = dot(pos_vec, edge_axis); 318 switch (style) { 319 case BORDER_STYLE_DOUBLE: { 320 float d = -1.0; 321 float partial_width = dot(vPartialWidths.xy, edge_axis); 322 if (partial_width >= 1.0) { 323 vec2 ref = vec2( 324 dot(vEdgeReference.xy, edge_axis) + partial_width, 325 dot(vEdgeReference.zw, edge_axis) - partial_width 326 ); 327 d = min(pos - ref.x, ref.y - pos); 328 } 329 color0 *= distance_aa(aa_range, d); 330 break; 331 } 332 case BORDER_STYLE_GROOVE: 333 case BORDER_STYLE_RIDGE: { 334 float ref = dot(vEdgeReference.xy + vPartialWidths.zw, edge_axis); 335 float d = pos - ref; 336 float alpha = distance_aa(aa_range, d); 337 color0 = mix(color0, color1, alpha); 338 break; 339 } 340 default: 341 break; 342 } 343 344 return color0; 345} 346 347void main(void) { 348 float aa_range = compute_aa_range(vPos); 349 vec4 color0, color1; 350 351 int segment = vConfig.x; 352 ivec2 style = ivec2(vConfig.y & 0xff, vConfig.y >> 8); 353 ivec2 edge_axis = ivec2(vConfig.z & 0xff, vConfig.z >> 8); 354 int clip_mode = vConfig.w; 355 356 float mix_factor = 0.0; 357 if (edge_axis.x != edge_axis.y) { 358 float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos); 359 mix_factor = distance_aa(aa_range, -d_line); 360 } 361 362 // Check if inside corner clip-region 363 vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy; 364 bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0))); 365 float d = -1.0; 366 367 switch (clip_mode) { 368 case CLIP_DOT: { 369 // Set clip distance based or dot position and radius. 370 d = distance(vClipParams1.xy, vPos) - vClipParams1.z; 371 break; 372 } 373 case CLIP_DASH_EDGE: { 374 bool is_vertical = vClipParams1.x == 0.; 375 float half_dash = is_vertical ? vClipParams1.y : vClipParams1.x; 376 // We want to draw something like: 377 // +---+---+---+---+ 378 // |xxx| | |xxx| 379 // +---+---+---+---+ 380 float pos = is_vertical ? vPos.y : vPos.x; 381 bool in_dash = pos < half_dash || pos > 3.0 * half_dash; 382 if (!in_dash) { 383 d = 1.; 384 } 385 break; 386 } 387 case CLIP_DASH_CORNER: { 388 // Get SDF for the two line/tangent clip lines, 389 // do SDF subtract to get clip distance. 390 float d0 = distance_to_line(vClipParams1.xy, 391 vClipParams1.zw, 392 vPos); 393 float d1 = distance_to_line(vClipParams2.xy, 394 vClipParams2.zw, 395 vPos); 396 d = max(d0, -d1); 397 break; 398 } 399 case CLIP_NONE: 400 default: 401 break; 402 } 403 404 if (in_clip_region) { 405 float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy, aa_range); 406 float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw, aa_range); 407 float d_radii = max(d_radii_a, -d_radii_b); 408 d = max(d, d_radii); 409 410 color0 = evaluate_color_for_style_in_corner( 411 clip_relative_pos, 412 style.x, 413 vColor00, 414 vColor01, 415 vClipRadii, 416 mix_factor, 417 segment, 418 aa_range 419 ); 420 color1 = evaluate_color_for_style_in_corner( 421 clip_relative_pos, 422 style.y, 423 vColor10, 424 vColor11, 425 vClipRadii, 426 mix_factor, 427 segment, 428 aa_range 429 ); 430 } else { 431 color0 = evaluate_color_for_style_in_edge( 432 vPos, 433 style.x, 434 vColor00, 435 vColor01, 436 aa_range, 437 edge_axis.x 438 ); 439 color1 = evaluate_color_for_style_in_edge( 440 vPos, 441 style.y, 442 vColor10, 443 vColor11, 444 aa_range, 445 edge_axis.y 446 ); 447 } 448 449 float alpha = distance_aa(aa_range, d); 450 vec4 color = mix(color0, color1, mix_factor); 451 oFragColor = color * alpha; 452} 453#endif 454