Home | History | Annotate | Download | only in effects
      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 "GrShaderCaps.h"
     10 }
     11 
     12 layout(key) in GrClipEdgeType edgeType;
     13 in float2 center;
     14 in float2 radii;
     15 
     16 float2 prevCenter;
     17 float2 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.
     20 uniform float4 ellipse;
     21 
     22 bool medPrecision = !sk_Caps.floatIs32Bits;
     23 layout(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 
     73 void 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