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