blob: 85c4c96f7564f356a55b7ea407752a9d7077ded2 [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;
Brian Salomon1d816b92017-08-17 11:07:59 -04009in float2 center;
10in float2 radii;
Ethan Nicholas420f1562017-07-14 13:11:38 -040011
Brian Salomon1d816b92017-08-17 11:07:59 -040012float2 prevCenter;
13float2 prevRadii = float2(-1);
Ethan Nicholas420f1562017-07-14 13:11:38 -040014// The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2)
Brian Salomon1d816b92017-08-17 11:07:59 -040015// The last two terms can underflow on mediump, so we use highp.
16uniform highp float4 ellipse;
Ethan Nicholas420f1562017-07-14 13:11:38 -040017
18bool useScale = sk_Caps.floatPrecisionVaries;
Brian Salomon1d816b92017-08-17 11:07:59 -040019layout(when=useScale) uniform float2 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
Brian Salomon1d816b92017-08-17 11:07:59 -040053 float2 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 }
Brian Salomon1d816b92017-08-17 11:07:59 -040061 float2 Z = d * ellipse.zw;
Ethan Nicholas420f1562017-07-14 13:11:38 -040062 // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
Brian Salomon1d816b92017-08-17 11:07:59 -040063 float 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.
Brian Salomon1d816b92017-08-17 11:07:59 -040065 float 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);
Brian Salomon1d816b92017-08-17 11:07:59 -040068 float approx_dist = implicit * inversesqrt(grad_dot);
Ethan Nicholas420f1562017-07-14 13:11:38 -040069 @if (useScale) {
70 approx_dist *= scale.x;
71 }
72
Brian Salomon1d816b92017-08-17 11:07:59 -040073 float alpha;
Ethan Nicholas420f1562017-07-14 13:11:38 -040074 @switch (edgeType) {
75 case 0 /* kFillBW_GrProcessorEdgeType */:
76 alpha = approx_dist > 0.0 ? 0.0 : 1.0;
77 break;
78 case 1 /* kFillAA_GrProcessorEdgeType */:
79 alpha = clamp(0.5 - approx_dist, 0.0, 1.0);
80 break;
81 case 2 /* kInverseFillBW_GrProcessorEdgeType */:
82 alpha = approx_dist > 0.0 ? 1.0 : 0.0;
83 break;
84 case 3 /* kInverseFillAA_GrProcessorEdgeType */:
85 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);
100 GrPrimitiveEdgeType et;
101 do {
102 et = (GrPrimitiveEdgeType) testData->fRandom->nextULessThan(kGrProcessorEdgeTypeCnt);
103 } while (kHairlineAA_GrProcessorEdgeType == et);
104 return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry));
105}