blob: 8effaeb561fdf0d303dff1c798911d297ad1cf5a [file] [log] [blame]
Ethan Nicholas420f1562017-07-14 13:11:38 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8layout(key) in int edgeType;
Ethan Nicholasf7b88202017-09-18 14:10:39 -04009in half2 center;
10in half2 radii;
Ethan Nicholas420f1562017-07-14 13:11:38 -040011
Ethan Nicholasf7b88202017-09-18 14:10:39 -040012half2 prevCenter;
13half2 prevRadii = half2(-1);
Ethan Nicholas420f1562017-07-14 13:11:38 -040014// The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2)
Ethan Nicholasf7b88202017-09-18 14:10:39 -040015// The last two terms can underflow with halfs, so we use floats.
Ethan Nicholas8aa45692017-09-20 11:24:15 -040016uniform float4 ellipse;
Ethan Nicholas420f1562017-07-14 13:11:38 -040017
18bool useScale = sk_Caps.floatPrecisionVaries;
Ethan Nicholasf7b88202017-09-18 14:10:39 -040019layout(when=useScale) uniform half2 scale;
Ethan Nicholas420f1562017-07-14 13:11:38 -040020
21@optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
22
23@setData(pdman) {
24 if (radii != prevRadii || center != prevCenter) {
25 float invRXSqd;
26 float invRYSqd;
27 // If we're using a scale factor to work around precision issues, choose the larger radius
28 // as the scale factor. The inv radii need to be pre-adjusted by the scale factor.
29 if (scale.isValid()) {
30 if (radii.fX > radii.fY) {
31 invRXSqd = 1.f;
32 invRYSqd = (radii.fX * radii.fX) /
33 (radii.fY * radii.fY);
34 pdman.set2f(scale, radii.fX, 1.f / radii.fX);
35 } else {
36 invRXSqd = (radii.fY * radii.fY) /
37 (radii.fX * radii.fX);
38 invRYSqd = 1.f;
39 pdman.set2f(scale, radii.fY, 1.f / radii.fY);
40 }
41 } else {
42 invRXSqd = 1.f / (radii.fX * radii.fX);
43 invRYSqd = 1.f / (radii.fY * radii.fY);
44 }
45 pdman.set4f(ellipse, center.fX, center.fY, invRXSqd, invRYSqd);
46 prevCenter = center;
47 prevRadii = radii;
48 }
49}
50
51void main() {
52 // d is the offset to the ellipse center
Ethan Nicholasf7b88202017-09-18 14:10:39 -040053 half2 d = sk_FragCoord.xy - ellipse.xy;
Ethan Nicholas420f1562017-07-14 13:11:38 -040054 // If we're on a device with a "real" mediump then we'll do the distance computation in a space
55 // that is normalized by the larger radius. The scale uniform will be scale, 1/scale. The
56 // inverse squared radii uniform values are already in this normalized space. The center is
57 // not.
58 @if (useScale) {
59 d *= scale.y;
60 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -040061 half2 Z = d * ellipse.zw;
Ethan Nicholas420f1562017-07-14 13:11:38 -040062 // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
Ethan Nicholasf7b88202017-09-18 14:10:39 -040063 half implicit = dot(Z, d) - 1;
Ethan Nicholas420f1562017-07-14 13:11:38 -040064 // grad_dot is the squared length of the gradient of the implicit.
Ethan Nicholasf7b88202017-09-18 14:10:39 -040065 half grad_dot = 4 * dot(Z, Z);
Ethan Nicholas420f1562017-07-14 13:11:38 -040066 // Avoid calling inversesqrt on zero.
67 grad_dot = max(grad_dot, 1e-4);
Ethan Nicholasf7b88202017-09-18 14:10:39 -040068 half approx_dist = implicit * inversesqrt(grad_dot);
Ethan Nicholas420f1562017-07-14 13:11:38 -040069 @if (useScale) {
70 approx_dist *= scale.x;
71 }
72
Ethan Nicholasf7b88202017-09-18 14:10:39 -040073 half alpha;
Ethan Nicholas420f1562017-07-14 13:11:38 -040074 @switch (edgeType) {
Ethan Nicholas0f3c7322017-11-09 14:51:17 -050075 case 0 /* kFillBW_GrClipEdgeType */:
Ethan Nicholas420f1562017-07-14 13:11:38 -040076 alpha = approx_dist > 0.0 ? 0.0 : 1.0;
77 break;
Ethan Nicholas0f3c7322017-11-09 14:51:17 -050078 case 1 /* kFillAA_GrClipEdgeType */:
Ethan Nicholas420f1562017-07-14 13:11:38 -040079 alpha = clamp(0.5 - approx_dist, 0.0, 1.0);
80 break;
Ethan Nicholas0f3c7322017-11-09 14:51:17 -050081 case 2 /* kInverseFillBW_GrClipEdgeType */:
Ethan Nicholas420f1562017-07-14 13:11:38 -040082 alpha = approx_dist > 0.0 ? 1.0 : 0.0;
83 break;
Ethan Nicholas0f3c7322017-11-09 14:51:17 -050084 case 3 /* kInverseFillAA_GrClipEdgeType */:
Ethan Nicholas420f1562017-07-14 13:11:38 -040085 alpha = clamp(0.5 + approx_dist, 0.0, 1.0);
86 break;
87 default:
88 // hairline not supported
89 discard;
90 }
91 sk_OutColor = sk_InColor * alpha;
92}
93
94@test(testData) {
95 SkPoint center;
96 center.fX = testData->fRandom->nextRangeScalar(0.f, 1000.f);
97 center.fY = testData->fRandom->nextRangeScalar(0.f, 1000.f);
98 SkScalar rx = testData->fRandom->nextRangeF(0.f, 1000.f);
99 SkScalar ry = testData->fRandom->nextRangeF(0.f, 1000.f);
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500100 GrClipEdgeType et;
Ethan Nicholas420f1562017-07-14 13:11:38 -0400101 do {
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500102 et = (GrClipEdgeType) testData->fRandom->nextULessThan(kGrProcessorEdgeTypeCnt);
103 } while (kHairlineAA_GrClipEdgeType == et);
Ethan Nicholas420f1562017-07-14 13:11:38 -0400104 return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry));
105}