aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/effects/GrEllipseEffect.fp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/effects/GrEllipseEffect.fp')
-rw-r--r--src/gpu/effects/GrEllipseEffect.fp105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/gpu/effects/GrEllipseEffect.fp b/src/gpu/effects/GrEllipseEffect.fp
new file mode 100644
index 0000000000..7bf32206fb
--- /dev/null
+++ b/src/gpu/effects/GrEllipseEffect.fp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+layout(key) in int edgeType;
+in vec2 center;
+in vec2 radii;
+
+vec2 prevCenter;
+vec2 prevRadii = vec2(-1);
+// The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2)
+// The last two terms can underflow on mediump, so we use highp.
+uniform highp vec4 ellipse;
+
+bool useScale = sk_Caps.floatPrecisionVaries;
+layout(when=useScale) uniform vec2 scale;
+
+@optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
+
+@setData(pdman) {
+ if (radii != prevRadii || center != prevCenter) {
+ float invRXSqd;
+ float invRYSqd;
+ // If we're using a scale factor to work around precision issues, choose the larger radius
+ // as the scale factor. The inv radii need to be pre-adjusted by the scale factor.
+ if (scale.isValid()) {
+ if (radii.fX > radii.fY) {
+ invRXSqd = 1.f;
+ invRYSqd = (radii.fX * radii.fX) /
+ (radii.fY * radii.fY);
+ pdman.set2f(scale, radii.fX, 1.f / radii.fX);
+ } else {
+ invRXSqd = (radii.fY * radii.fY) /
+ (radii.fX * radii.fX);
+ invRYSqd = 1.f;
+ pdman.set2f(scale, radii.fY, 1.f / radii.fY);
+ }
+ } else {
+ invRXSqd = 1.f / (radii.fX * radii.fX);
+ invRYSqd = 1.f / (radii.fY * radii.fY);
+ }
+ pdman.set4f(ellipse, center.fX, center.fY, invRXSqd, invRYSqd);
+ prevCenter = center;
+ prevRadii = radii;
+ }
+}
+
+void main() {
+ // d is the offset to the ellipse center
+ vec2 d = sk_FragCoord.xy - ellipse.xy;
+ // If we're on a device with a "real" mediump then we'll do the distance computation in a space
+ // that is normalized by the larger radius. The scale uniform will be scale, 1/scale. The
+ // inverse squared radii uniform values are already in this normalized space. The center is
+ // not.
+ @if (useScale) {
+ d *= scale.y;
+ }
+ vec2 Z = d * ellipse.zw;
+ // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
+ float implicit = dot(Z, d) - 1;
+ // grad_dot is the squared length of the gradient of the implicit.
+ float grad_dot = 4 * dot(Z, Z);
+ // Avoid calling inversesqrt on zero.
+ grad_dot = max(grad_dot, 1e-4);
+ float approx_dist = implicit * inversesqrt(grad_dot);
+ @if (useScale) {
+ approx_dist *= scale.x;
+ }
+
+ float alpha;
+ @switch (edgeType) {
+ case 0 /* kFillBW_GrProcessorEdgeType */:
+ alpha = approx_dist > 0.0 ? 0.0 : 1.0;
+ break;
+ case 1 /* kFillAA_GrProcessorEdgeType */:
+ alpha = clamp(0.5 - approx_dist, 0.0, 1.0);
+ break;
+ case 2 /* kInverseFillBW_GrProcessorEdgeType */:
+ alpha = approx_dist > 0.0 ? 1.0 : 0.0;
+ break;
+ case 3 /* kInverseFillAA_GrProcessorEdgeType */:
+ alpha = clamp(0.5 + approx_dist, 0.0, 1.0);
+ break;
+ default:
+ // hairline not supported
+ discard;
+ }
+ sk_OutColor = sk_InColor * alpha;
+}
+
+@test(testData) {
+ SkPoint center;
+ center.fX = testData->fRandom->nextRangeScalar(0.f, 1000.f);
+ center.fY = testData->fRandom->nextRangeScalar(0.f, 1000.f);
+ SkScalar rx = testData->fRandom->nextRangeF(0.f, 1000.f);
+ SkScalar ry = testData->fRandom->nextRangeF(0.f, 1000.f);
+ GrPrimitiveEdgeType et;
+ do {
+ et = (GrPrimitiveEdgeType) testData->fRandom->nextULessThan(kGrProcessorEdgeTypeCnt);
+ } while (kHairlineAA_GrProcessorEdgeType == et);
+ return GrEllipseEffect::Make(et, center, SkPoint::Make(rx, ry));
+} \ No newline at end of file