1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
/* 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/. */
// Preprocess the radii for computing the distance approximation. This should
// be used in the vertex shader if possible to avoid doing expensive division
// in the fragment shader. When dealing with a point (zero radii), approximate
// it as an ellipse with very small radii so that we don't need to branch.
vec2 inverse_radii_squared(vec2 radii) {
return any(lessThanEqual(radii, vec2(0.0)))
? vec2(1.0 / (1.0e-3 * 1.0e-3))
: 1.0 / (radii * radii);
}
#ifdef WR_FRAGMENT_SHADER
// One iteration of Newton's method on the 2D equation of an ellipse:
//
// E(x, y) = x^2/a^2 + y^2/b^2 - 1
//
// The Jacobian of this equation is:
//
// J(E(x, y)) = [ 2*x/a^2 2*y/b^2 ]
//
// We approximate the distance with:
//
// E(x, y) / ||J(E(x, y))||
//
// See G. Taubin, "Distance Approximations for Rasterizing Implicit
// Curves", section 3.
float distance_to_ellipse_approx(vec2 p, vec2 inv_radii_sq) {
float g = dot(p * p, inv_radii_sq) - 1.0;
vec2 dG = 2.0 * p * inv_radii_sq;
return g * inversesqrt(dot(dG, dG));
}
// Slower but more accurate version that uses the exact distance when dealing
// with a 0-radius point distance and otherwise uses the faster approximation
// when dealing with non-zero radii.
float distance_to_ellipse(vec2 p, vec2 radii) {
return any(lessThanEqual(radii, vec2(0.0)))
? length(p)
: distance_to_ellipse_approx(p, 1.0 / (radii * radii));
}
float clip_against_ellipse_if_needed(
vec2 pos,
vec4 ellipse_center_radius,
vec2 sign_modifier
) {
vec2 p = pos - ellipse_center_radius.xy;
// If we're not within the distance bounds of both of the radii (in the
// corner), then return a large magnitude negative value to cause us to
// clamp off the anti-aliasing.
return all(lessThan(sign_modifier * p, vec2(0.0)))
? distance_to_ellipse_approx(p, ellipse_center_radius.zw)
: -1.0e6;
}
float rounded_rect(vec2 pos,
vec4 clip_center_radius_tl,
vec4 clip_center_radius_tr,
vec4 clip_center_radius_br,
vec4 clip_center_radius_bl,
float aa_range) {
// Clip against each ellipse. If the fragment is in a corner, one of the
// clip_against_ellipse_if_needed calls below will update it. If outside
// any ellipse, the clip routine will return a negative value so that max()
// will choose the greatest amount of applicable anti-aliasing.
float current_distance =
max(max(clip_against_ellipse_if_needed(pos, clip_center_radius_tl, vec2(1.0)),
clip_against_ellipse_if_needed(pos, clip_center_radius_tr, vec2(-1.0, 1.0))),
max(clip_against_ellipse_if_needed(pos, clip_center_radius_br, vec2(-1.0)),
clip_against_ellipse_if_needed(pos, clip_center_radius_bl, vec2(1.0, -1.0))));
// Apply AA
// See comment in ps_border_corner about the choice of constants.
return distance_aa(aa_range, current_distance);
}
#endif
|