Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 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 | |
Brian Salomon | 1447177 | 2017-12-05 10:35:15 -0500 | [diff] [blame] | 8 | @header { |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 9 | #include "src/gpu/GrShaderCaps.h" |
Brian Salomon | 1447177 | 2017-12-05 10:35:15 -0500 | [diff] [blame] | 10 | } |
| 11 | |
Ethan Nicholas | aae47c8 | 2017-11-10 15:34:03 -0500 | [diff] [blame] | 12 | layout(key) in GrClipEdgeType edgeType; |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 13 | in float2 center; |
| 14 | in float2 radii; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 15 | |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 16 | float2 prevCenter; |
| 17 | float2 prevRadii = float2(-1); |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 18 | // The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2) |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 19 | // The last two terms can underflow when float != fp32, so we also provide a workaround. |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 20 | uniform float4 ellipse; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 21 | |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 22 | bool medPrecision = !sk_Caps.floatIs32Bits; |
| 23 | layout(when=medPrecision) uniform float2 scale; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 24 | |
Brian Salomon | 1447177 | 2017-12-05 10:35:15 -0500 | [diff] [blame] | 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 | } |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 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 | } |
Brian Salomon | 1447177 | 2017-12-05 10:35:15 -0500 | [diff] [blame] | 40 | return std::unique_ptr<GrFragmentProcessor>(new GrEllipseEffect(edgeType, center, radii)); |
| 41 | } |
| 42 | } |
| 43 | |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 44 | @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag } |
| 45 | |
| 46 | @setData(pdman) { |
| 47 | if (radii != prevRadii || center != prevCenter) { |
| 48 | float invRXSqd; |
| 49 | float invRYSqd; |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 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. |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 53 | if (scale.isValid()) { |
| 54 | if (radii.fX > radii.fY) { |
| 55 | invRXSqd = 1.f; |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 56 | invRYSqd = (radii.fX * radii.fX) / (radii.fY * radii.fY); |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 57 | pdman.set2f(scale, radii.fX, 1.f / radii.fX); |
| 58 | } else { |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 59 | invRXSqd = (radii.fY * radii.fY) / (radii.fX * radii.fX); |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 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 | |
| 73 | void main() { |
| 74 | // d is the offset to the ellipse center |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 75 | float2 d = sk_FragCoord.xy - ellipse.xy; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 76 | // If we're on a device with a "real" mediump then we'll do the distance computation in a space |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 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) { |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 81 | d *= scale.y; |
| 82 | } |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 83 | float2 Z = d * ellipse.zw; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 84 | // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1. |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 85 | float implicit = dot(Z, d) - 1; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 86 | // grad_dot is the squared length of the gradient of the implicit. |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 87 | float grad_dot = 4 * dot(Z, Z); |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 88 | // Avoid calling inversesqrt on zero. |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 89 | @if (medPrecision) { |
| 90 | grad_dot = max(grad_dot, 6.1036e-5); |
| 91 | } else { |
| 92 | grad_dot = max(grad_dot, 1.1755e-38); |
| 93 | } |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 94 | float approx_dist = implicit * inversesqrt(grad_dot); |
Jim Van Verth | 20ae25c | 2019-03-29 08:50:41 -0400 | [diff] [blame] | 95 | @if (medPrecision) { |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 96 | approx_dist *= scale.x; |
| 97 | } |
| 98 | |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 99 | half alpha; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 100 | @switch (edgeType) { |
Ethan Nicholas | aae47c8 | 2017-11-10 15:34:03 -0500 | [diff] [blame] | 101 | case GrClipEdgeType::kFillBW: |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 102 | alpha = approx_dist > 0.0 ? 0.0 : 1.0; |
| 103 | break; |
Ethan Nicholas | aae47c8 | 2017-11-10 15:34:03 -0500 | [diff] [blame] | 104 | case GrClipEdgeType::kFillAA: |
Ethan Nicholas | e1f5502 | 2019-02-05 17:17:40 -0500 | [diff] [blame] | 105 | alpha = saturate(0.5 - half(approx_dist)); |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 106 | break; |
Ethan Nicholas | aae47c8 | 2017-11-10 15:34:03 -0500 | [diff] [blame] | 107 | case GrClipEdgeType::kInverseFillBW: |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 108 | alpha = approx_dist > 0.0 ? 1.0 : 0.0; |
| 109 | break; |
Ethan Nicholas | aae47c8 | 2017-11-10 15:34:03 -0500 | [diff] [blame] | 110 | case GrClipEdgeType::kInverseFillAA: |
Ethan Nicholas | e1f5502 | 2019-02-05 17:17:40 -0500 | [diff] [blame] | 111 | alpha = saturate(0.5 + half(approx_dist)); |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 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); |
Ethan Nicholas | 0f3c732 | 2017-11-09 14:51:17 -0500 | [diff] [blame] | 126 | GrClipEdgeType et; |
Ethan Nicholas | 420f156 | 2017-07-14 13:11:38 -0400 | [diff] [blame] | 127 | do { |
Ethan Nicholas | 1706f84 | 2017-11-10 11:58:19 -0500 | [diff] [blame] | 128 | et = (GrClipEdgeType) testData->fRandom->nextULessThan(kGrClipEdgeTypeCnt); |
| 129 | } while (GrClipEdgeType::kHairlineAA == et); |
Brian Salomon | 1447177 | 2017-12-05 10:35:15 -0500 | [diff] [blame] | 130 | return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry), |
| 131 | *testData->caps()->shaderCaps()); |
Chris Dalton | 47c8ed3 | 2017-11-15 18:27:09 -0700 | [diff] [blame] | 132 | } |