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 12in fragmentProcessor inputFP; 13layout(key) in GrClipEdgeType edgeType; 14in float2 center; 15in float2 radii; 16 17float2 prevCenter; 18float2 prevRadii = float2(-1); 19// The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2) 20// The last two terms can underflow when float != fp32, so we also provide a workaround. 21uniform float4 ellipse; 22 23layout(when=!sk_Caps.floatIs32Bits) uniform float2 scale; 24 25@make { 26 static GrFPResult Make(std::unique_ptr<GrFragmentProcessor> inputFP, GrClipEdgeType edgeType, 27 SkPoint center, 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 GrFPFailure(std::move(inputFP)); 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 GrFPFailure(std::move(inputFP)); 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 GrFPFailure(std::move(inputFP)); 39 } 40 return GrFPSuccess(std::unique_ptr<GrFragmentProcessor>( 41 new GrEllipseEffect(std::move(inputFP), edgeType, center, radii))); 42 } 43} 44 45@optimizationFlags { 46 ProcessorOptimizationFlags(inputFP.get()) & kCompatibleWithCoverageAsAlpha_OptimizationFlag 47} 48 49@setData(pdman) { 50 if (radii != prevRadii || center != prevCenter) { 51 float invRXSqd; 52 float invRYSqd; 53 // If we're using a scale factor to work around precision issues, choose the larger 54 // radius as the scale factor. The inv radii need to be pre-adjusted by the scale 55 // factor. 56 if (scale.isValid()) { 57 if (radii.fX > radii.fY) { 58 invRXSqd = 1.f; 59 invRYSqd = (radii.fX * radii.fX) / (radii.fY * radii.fY); 60 pdman.set2f(scale, radii.fX, 1.f / radii.fX); 61 } else { 62 invRXSqd = (radii.fY * radii.fY) / (radii.fX * radii.fX); 63 invRYSqd = 1.f; 64 pdman.set2f(scale, radii.fY, 1.f / radii.fY); 65 } 66 } else { 67 invRXSqd = 1.f / (radii.fX * radii.fX); 68 invRYSqd = 1.f / (radii.fY * radii.fY); 69 } 70 pdman.set4f(ellipse, center.fX, center.fY, invRXSqd, invRYSqd); 71 prevCenter = center; 72 prevRadii = radii; 73 } 74} 75 76half4 main() { 77 // d is the offset to the ellipse center 78 float2 d = sk_FragCoord.xy - ellipse.xy; 79 // If we're on a device with a "real" mediump then we'll do the distance computation in a space 80 // that is normalized by the larger radius or 128, whichever is smaller. The scale uniform will 81 // be scale, 1/scale. The inverse squared radii uniform values are already in this normalized space. 82 // The center is not. 83 const bool medPrecision = !sk_Caps.floatIs32Bits; 84 @if (medPrecision) { 85 d *= scale.y; 86 } 87 float2 Z = d * ellipse.zw; 88 // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1. 89 float implicit = dot(Z, d) - 1; 90 // grad_dot is the squared length of the gradient of the implicit. 91 float grad_dot = 4 * dot(Z, Z); 92 // Avoid calling inversesqrt on zero. 93 @if (medPrecision) { 94 grad_dot = max(grad_dot, 6.1036e-5); 95 } else { 96 grad_dot = max(grad_dot, 1.1755e-38); 97 } 98 float approx_dist = implicit * inversesqrt(grad_dot); 99 @if (medPrecision) { 100 approx_dist *= scale.x; 101 } 102 103 half alpha; 104 @switch (edgeType) { 105 case GrClipEdgeType::kFillBW: 106 alpha = approx_dist > 0.0 ? 0.0 : 1.0; 107 break; 108 case GrClipEdgeType::kFillAA: 109 alpha = saturate(0.5 - half(approx_dist)); 110 break; 111 case GrClipEdgeType::kInverseFillBW: 112 alpha = approx_dist > 0.0 ? 1.0 : 0.0; 113 break; 114 case GrClipEdgeType::kInverseFillAA: 115 alpha = saturate(0.5 + half(approx_dist)); 116 break; 117 default: 118 // hairline not supported 119 discard; 120 } 121 return sample(inputFP) * alpha; 122} 123 124@test(testData) { 125 SkPoint center; 126 center.fX = testData->fRandom->nextRangeScalar(0.f, 1000.f); 127 center.fY = testData->fRandom->nextRangeScalar(0.f, 1000.f); 128 SkScalar rx = testData->fRandom->nextRangeF(0.f, 1000.f); 129 SkScalar ry = testData->fRandom->nextRangeF(0.f, 1000.f); 130 bool success; 131 std::unique_ptr<GrFragmentProcessor> fp = testData->inputFP(); 132 do { 133 GrClipEdgeType et = (GrClipEdgeType)testData->fRandom->nextULessThan(kGrClipEdgeTypeCnt); 134 std::tie(success, fp) = GrEllipseEffect::Make(std::move(fp), et, center, 135 SkPoint::Make(rx, ry), 136 *testData->caps()->shaderCaps()); 137 } while (!success); 138 return fp; 139} 140