blob: 256d0f416ff5a8bb2bd53adf02b314a3785fbbcc [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
Brian Salomon14471772017-12-05 10:35:15 -05008@header {
9 #include "GrShaderCaps.h"
10}
11
Ethan Nicholasaae47c82017-11-10 15:34:03 -050012layout(key) in GrClipEdgeType edgeType;
Chris Dalton47c8ed32017-11-15 18:27:09 -070013in float2 center;
14in float2 radii;
Ethan Nicholas420f1562017-07-14 13:11:38 -040015
Chris Dalton47c8ed32017-11-15 18:27:09 -070016float2 prevCenter;
17float2 prevRadii = float2(-1);
Ethan Nicholas420f1562017-07-14 13:11:38 -040018// The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2)
Chris Dalton47c8ed32017-11-15 18:27:09 -070019// The last two terms can underflow when float != fp32, so we also provide a workaround.
Ethan Nicholas8aa45692017-09-20 11:24:15 -040020uniform float4 ellipse;
Ethan Nicholas420f1562017-07-14 13:11:38 -040021
Jim Van Verth20ae25c2019-03-29 08:50:41 -040022bool medPrecision = !sk_Caps.floatIs32Bits;
23layout(when=medPrecision) uniform float2 scale;
Ethan Nicholas420f1562017-07-14 13:11:38 -040024
Brian Salomon14471772017-12-05 10:35:15 -050025@make {
26 static std::unique_ptr<GrFragmentProcessor> Make(GrClipEdgeType edgeType, SkPoint center,
27 SkPoint radii, const GrShaderCaps& caps) {
28 // Small radii produce bad results on devices without full float.
29 if (!caps.floatIs32Bits() && (radii.fX < 0.5f || radii.fY < 0.5f)) {
30 return nullptr;
31 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -040032 // Very narrow ellipses produce bad results on devices without full float
33 if (!caps.floatIs32Bits() && (radii.fX > 255*radii.fY || radii.fY > 255*radii.fX)) {
34 return nullptr;
35 }
36 // Very large ellipses produce bad results on devices without full float
37 if (!caps.floatIs32Bits() && (radii.fX > 16384 || radii.fY > 16384)) {
38 return nullptr;
39 }
Brian Salomon14471772017-12-05 10:35:15 -050040 return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii));
41 }
42}
43
Ethan Nicholas420f1562017-07-14 13:11:38 -040044@optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
45
46@setData(pdman) {
47 if (radii != prevRadii || center != prevCenter) {
48 float invRXSqd;
49 float invRYSqd;
Jim Van Verth20ae25c2019-03-29 08:50:41 -040050 // If we're using a scale factor to work around precision issues, choose the larger
51 // radius as the scale factor. The inv radii need to be pre-adjusted by the scale
52 // factor.
Ethan Nicholas420f1562017-07-14 13:11:38 -040053 if (scale.isValid()) {
54 if (radii.fX > radii.fY) {
55 invRXSqd = 1.f;
Jim Van Verth20ae25c2019-03-29 08:50:41 -040056 invRYSqd = (radii.fX * radii.fX) / (radii.fY * radii.fY);
Ethan Nicholas420f1562017-07-14 13:11:38 -040057 pdman.set2f(scale, radii.fX, 1.f / radii.fX);
58 } else {
Jim Van Verth20ae25c2019-03-29 08:50:41 -040059 invRXSqd = (radii.fY * radii.fY) / (radii.fX * radii.fX);
Ethan Nicholas420f1562017-07-14 13:11:38 -040060 invRYSqd = 1.f;
61 pdman.set2f(scale, radii.fY, 1.f / radii.fY);
62 }
63 } else {
64 invRXSqd = 1.f / (radii.fX * radii.fX);
65 invRYSqd = 1.f / (radii.fY * radii.fY);
66 }
67 pdman.set4f(ellipse, center.fX, center.fY, invRXSqd, invRYSqd);
68 prevCenter = center;
69 prevRadii = radii;
70 }
71}
72
73void main() {
74 // d is the offset to the ellipse center
Chris Dalton47c8ed32017-11-15 18:27:09 -070075 float2 d = sk_FragCoord.xy - ellipse.xy;
Ethan Nicholas420f1562017-07-14 13:11:38 -040076 // If we're on a device with a "real" mediump then we'll do the distance computation in a space
Jim Van Verth20ae25c2019-03-29 08:50:41 -040077 // that is normalized by the larger radius or 128, whichever is smaller. The scale uniform will
78 // be scale, 1/scale. The inverse squared radii uniform values are already in this normalized space.
79 // The center is not.
80 @if (medPrecision) {
Ethan Nicholas420f1562017-07-14 13:11:38 -040081 d *= scale.y;
82 }
Chris Dalton47c8ed32017-11-15 18:27:09 -070083 float2 Z = d * ellipse.zw;
Ethan Nicholas420f1562017-07-14 13:11:38 -040084 // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
Chris Dalton47c8ed32017-11-15 18:27:09 -070085 float implicit = dot(Z, d) - 1;
Ethan Nicholas420f1562017-07-14 13:11:38 -040086 // grad_dot is the squared length of the gradient of the implicit.
Chris Dalton47c8ed32017-11-15 18:27:09 -070087 float grad_dot = 4 * dot(Z, Z);
Ethan Nicholas420f1562017-07-14 13:11:38 -040088 // Avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -040089 @if (medPrecision) {
90 grad_dot = max(grad_dot, 6.1036e-5);
91 } else {
92 grad_dot = max(grad_dot, 1.1755e-38);
93 }
Chris Dalton47c8ed32017-11-15 18:27:09 -070094 float approx_dist = implicit * inversesqrt(grad_dot);
Jim Van Verth20ae25c2019-03-29 08:50:41 -040095 @if (medPrecision) {
Ethan Nicholas420f1562017-07-14 13:11:38 -040096 approx_dist *= scale.x;
97 }
98
Ethan Nicholasf7b88202017-09-18 14:10:39 -040099 half alpha;
Ethan Nicholas420f1562017-07-14 13:11:38 -0400100 @switch (edgeType) {
Ethan Nicholasaae47c82017-11-10 15:34:03 -0500101 case GrClipEdgeType::kFillBW:
Ethan Nicholas420f1562017-07-14 13:11:38 -0400102 alpha = approx_dist > 0.0 ? 0.0 : 1.0;
103 break;
Ethan Nicholasaae47c82017-11-10 15:34:03 -0500104 case GrClipEdgeType::kFillAA:
Ethan Nicholase1f55022019-02-05 17:17:40 -0500105 alpha = saturate(0.5 - half(approx_dist));
Ethan Nicholas420f1562017-07-14 13:11:38 -0400106 break;
Ethan Nicholasaae47c82017-11-10 15:34:03 -0500107 case GrClipEdgeType::kInverseFillBW:
Ethan Nicholas420f1562017-07-14 13:11:38 -0400108 alpha = approx_dist > 0.0 ? 1.0 : 0.0;
109 break;
Ethan Nicholasaae47c82017-11-10 15:34:03 -0500110 case GrClipEdgeType::kInverseFillAA:
Ethan Nicholase1f55022019-02-05 17:17:40 -0500111 alpha = saturate(0.5 + half(approx_dist));
Ethan Nicholas420f1562017-07-14 13:11:38 -0400112 break;
113 default:
114 // hairline not supported
115 discard;
116 }
117 sk_OutColor = sk_InColor * alpha;
118}
119
120@test(testData) {
121 SkPoint center;
122 center.fX = testData->fRandom->nextRangeScalar(0.f, 1000.f);
123 center.fY = testData->fRandom->nextRangeScalar(0.f, 1000.f);
124 SkScalar rx = testData->fRandom->nextRangeF(0.f, 1000.f);
125 SkScalar ry = testData->fRandom->nextRangeF(0.f, 1000.f);
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500126 GrClipEdgeType et;
Ethan Nicholas420f1562017-07-14 13:11:38 -0400127 do {
Ethan Nicholas1706f842017-11-10 11:58:19 -0500128 et = (GrClipEdgeType) testData->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
129 } while (GrClipEdgeType::kHairlineAA == et);
Brian Salomon14471772017-12-05 10:35:15 -0500130 return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry),
131 *testData->caps()->shaderCaps());
Chris Dalton47c8ed32017-11-15 18:27:09 -0700132}