cs_border_segment.glsl (14521B)
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,rect,ellipse 6 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; 13 14 // A point + tangent defining the line where the edge 15 // transition occurs. Used for corners only. 16 flat varying highp vec4 vColorLine; 17 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; 26 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; 31 32 // An outer and inner elliptical radii for border 33 // corner clipping. 34 flat varying highp vec4 vClipRadii; 35 36 // Reference point for determine edge clip lines. 37 flat varying highp vec4 vEdgeReference; 38 39 // Stores widths/2 and widths/3 to save doing this in FS. 40 flat varying highp vec4 vPartialWidths; 41 42 // Clipping parameters for dot or dash. 43 flat varying mediump vec4 vClipParams1; 44 flat varying mediump vec4 vClipParams2; 45 46 // Local space position 47 varying highp vec2 vPos; 48 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 57 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 69 70 #define CLIP_NONE 0 71 #define CLIP_DASH_CORNER 1 72 #define CLIP_DASH_EDGE 2 73 #define CLIP_DOT 3 74 75 #ifdef WR_VERTEX_SHADER 76 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; 86 87 vec2 get_outer_corner_scale(int segment) { 88 vec2 p; 89 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 } 108 109 return p; 110 } 111 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; 117 118 const float dark_scale = 0.66666666; 119 const float light_scale = 1.0; 120 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 } 127 128 if (lighter) { 129 return vec4(color.rgb * light_scale, color.a); 130 } 131 return vec4(color.rgb * dark_scale, color.a); 132 } 133 134 vec4[2] get_colors_for_side(vec4 color, int style) { 135 vec4 result[2]; 136 137 bool is_black = color.rgb == vec3(0.0, 0.0, 0.0); 138 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 } 153 154 return result; 155 } 156 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; 162 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; 167 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 } 200 201 vSegmentClipMode = vec2(float(segment), float(clip_mode)); 202 vStyleEdgeAxis = vec4(float(style0), float(style1), float(edge_axis.x), float(edge_axis.y)); 203 204 vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0); 205 vPos = size * 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), 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 } 244 245 gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0); 246 } 247 #endif 248 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 } 301 302 return color0; 303 } 304 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 } 340 341 return color0; 342 } 343 344 void main(void) { 345 float aa_range = compute_aa_range(vPos); 346 vec4 color0, color1; 347 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)); 352 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 } 358 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; 363 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 } 400 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); 406 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 } 445 446 float alpha = distance_aa(aa_range, d); 447 vec4 color = mix(color0, color1, mix_factor); 448 oFragColor = color * alpha; 449 } 450 #endif