• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
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
8@header {
9    #include "src/gpu/GrShaderCaps.h"
10}
11
12layout(key) in GrClipEdgeType edgeType;
13in float2 center;
14in float2 radii;
15
16float2 prevCenter;
17float2 prevRadii = float2(-1);
18// The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2)
19// The last two terms can underflow when float != fp32, so we also provide a workaround.
20uniform float4 ellipse;
21
22bool medPrecision = !sk_Caps.floatIs32Bits;
23layout(when=medPrecision) uniform float2 scale;
24
25@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        }
32        // 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        }
40        return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii));
41    }
42}
43
44@optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
45
46@setData(pdman) {
47    if (radii != prevRadii || center != prevCenter) {
48        float invRXSqd;
49        float invRYSqd;
50        // 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.
53        if (scale.isValid()) {
54            if (radii.fX > radii.fY) {
55                invRXSqd = 1.f;
56                invRYSqd = (radii.fX * radii.fX) / (radii.fY * radii.fY);
57                pdman.set2f(scale, radii.fX, 1.f / radii.fX);
58            } else {
59                invRXSqd = (radii.fY * radii.fY) / (radii.fX * radii.fX);
60                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
75    float2 d = sk_FragCoord.xy - ellipse.xy;
76    // If we're on a device with a "real" mediump then we'll do the distance computation in a space
77    // 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) {
81        d *= scale.y;
82    }
83    float2 Z = d * ellipse.zw;
84    // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
85    float implicit = dot(Z, d) - 1;
86    // grad_dot is the squared length of the gradient of the implicit.
87    float grad_dot = 4 * dot(Z, Z);
88    // Avoid calling inversesqrt on zero.
89    @if (medPrecision) {
90        grad_dot = max(grad_dot, 6.1036e-5);
91    } else {
92        grad_dot = max(grad_dot, 1.1755e-38);
93    }
94    float approx_dist = implicit * inversesqrt(grad_dot);
95    @if (medPrecision) {
96        approx_dist *= scale.x;
97    }
98
99    half alpha;
100    @switch (edgeType) {
101        case GrClipEdgeType::kFillBW:
102            alpha = approx_dist > 0.0 ? 0.0 : 1.0;
103            break;
104        case GrClipEdgeType::kFillAA:
105            alpha = saturate(0.5 - half(approx_dist));
106            break;
107        case GrClipEdgeType::kInverseFillBW:
108            alpha = approx_dist > 0.0 ? 1.0 : 0.0;
109            break;
110        case GrClipEdgeType::kInverseFillAA:
111            alpha = saturate(0.5 + half(approx_dist));
112            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);
126    GrClipEdgeType et;
127    do {
128        et = (GrClipEdgeType) testData->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
129    } while (GrClipEdgeType::kHairlineAA == et);
130    return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry),
131                                 *testData->caps()->shaderCaps());
132}
133