| Index: src/gpu/GrOvalRenderer.cpp
|
| diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
|
| index 079c261a66e7a6692983b38c01ef91f9d7394525..c17388dca342cd862a8a11e7aee8eb013dc97601 100644
|
| --- a/src/gpu/GrOvalRenderer.cpp
|
| +++ b/src/gpu/GrOvalRenderer.cpp
|
| @@ -13,6 +13,7 @@
|
| #include "GrInvariantOutput.h"
|
| #include "GrProcessor.h"
|
| #include "GrResourceProvider.h"
|
| +#include "GrStyle.h"
|
| #include "SkRRect.h"
|
| #include "SkStrokeRec.h"
|
| #include "batches/GrVertexBatch.h"
|
| @@ -28,14 +29,6 @@
|
|
|
| namespace {
|
|
|
| -struct CircleVertex {
|
| - SkPoint fPos;
|
| - GrColor fColor;
|
| - SkPoint fOffset;
|
| - SkScalar fOuterRadius;
|
| - SkScalar fInnerRadius;
|
| -};
|
| -
|
| struct EllipseVertex {
|
| SkPoint fPos;
|
| GrColor fColor;
|
| @@ -75,20 +68,42 @@ inline bool circle_stays_circle(const SkMatrix& m) {
|
| * v is a normalized vector pointing to the outer edge
|
| * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
|
| * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
|
| + * Additional clip planes are supported for rendering circular arcs. The additional planes are
|
| + * either intersected or unioned together. Up to three planes are supported (an initial plane,
|
| + * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
|
| + * are useful for any given arc, but having all three in one instance allows batching different
|
| + * types of arcs.
|
| */
|
|
|
| class CircleGeometryProcessor : public GrGeometryProcessor {
|
| public:
|
| - CircleGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix){
|
| + CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
|
| + const SkMatrix& localMatrix)
|
| + : fLocalMatrix(localMatrix) {
|
| this->initClassID<CircleGeometryProcessor>();
|
| fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
|
| kHigh_GrSLPrecision);
|
| fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
|
| fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
|
| + if (clipPlane) {
|
| + fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
|
| + } else {
|
| + fInClipPlane = nullptr;
|
| + }
|
| + if (isectPlane) {
|
| + fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
|
| + } else {
|
| + fInIsectPlane = nullptr;
|
| + }
|
| + if (unionPlane) {
|
| + fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
|
| + } else {
|
| + fInUnionPlane = nullptr;
|
| + }
|
| fStroke = stroke;
|
| }
|
|
|
| - bool implementsDistanceVector() const override { return true; };
|
| + bool implementsDistanceVector() const override { return !fInClipPlane; };
|
|
|
| virtual ~CircleGeometryProcessor() {}
|
|
|
| @@ -112,15 +127,27 @@ private:
|
| GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
|
| GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
| GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
|
| + GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
|
|
| // emit attributes
|
| varyingHandler->emitAttributes(cgp);
|
| + fragBuilder->codeAppend("vec4 circleEdge;");
|
| + varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
|
| + if (cgp.fInClipPlane) {
|
| + fragBuilder->codeAppend("vec3 clipPlane;");
|
| + varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
|
| + }
|
| + if (cgp.fInIsectPlane) {
|
| + SkASSERT(cgp.fInClipPlane);
|
| + fragBuilder->codeAppend("vec3 isectPlane;");
|
| + varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
|
| + }
|
| + if (cgp.fInUnionPlane) {
|
| + SkASSERT(cgp.fInClipPlane);
|
| + fragBuilder->codeAppend("vec3 unionPlane;");
|
| + varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
|
| + }
|
|
|
| - GrGLSLVertToFrag v(kVec4f_GrSLType);
|
| - varyingHandler->addVarying("CircleEdge", &v);
|
| - vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cgp.fInCircleEdge->fName);
|
| -
|
| - GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
| // setup pass through color
|
| varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
|
|
|
| @@ -137,12 +164,11 @@ private:
|
| args.fTransformsIn,
|
| args.fTransformsOut);
|
|
|
| - fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
|
| - fragBuilder->codeAppendf("float distanceToOuterEdge = %s.z * (1.0 - d);", v.fsIn());
|
| - fragBuilder->codeAppendf("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
|
| + fragBuilder->codeAppend("float d = length(circleEdge.xy);");
|
| + fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
|
| + fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
|
| if (cgp.fStroke) {
|
| - fragBuilder->codeAppendf("float distanceToInnerEdge = %s.z * (d - %s.w);",
|
| - v.fsIn(), v.fsIn());
|
| + fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
|
| fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
|
| fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
|
| }
|
| @@ -154,12 +180,20 @@ private:
|
| "%s);", // no normalize
|
| args.fDistanceVectorName, innerEdgeDistance);
|
| fragBuilder->codeAppend ("} else {");
|
| - fragBuilder->codeAppendf(" %s = vec4(normalize(%s.xy), distanceToOuterEdge, "
|
| - "%s);",
|
| - args.fDistanceVectorName, v.fsIn(), innerEdgeDistance);
|
| + fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
|
| + args.fDistanceVectorName, innerEdgeDistance);
|
| fragBuilder->codeAppend ("}");
|
| }
|
| -
|
| + if (cgp.fInClipPlane) {
|
| + fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
|
| + if (cgp.fInIsectPlane) {
|
| + fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
|
| + }
|
| + if (cgp.fInUnionPlane) {
|
| + fragBuilder->codeAppend("clip += (1-clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
|
| + }
|
| + fragBuilder->codeAppend("edgeAlpha *= clip;");
|
| + }
|
| fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
|
| }
|
|
|
| @@ -167,14 +201,16 @@ private:
|
| const GrGLSLCaps&,
|
| GrProcessorKeyBuilder* b) {
|
| const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
|
| - uint16_t key = cgp.fStroke ? 0x1 : 0x0;
|
| - key |= cgp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
|
| + uint16_t key;
|
| + key = cgp.fStroke ? 0x01 : 0x0;
|
| + key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
|
| + key |= cgp.fInClipPlane ? 0x04 : 0x0;
|
| + key |= cgp.fInIsectPlane ? 0x08 : 0x0;
|
| + key |= cgp.fInUnionPlane ? 0x10 : 0x0;
|
| b->add32(key);
|
| }
|
|
|
| - void setData(const GrGLSLProgramDataManager& pdman,
|
| - const GrPrimitiveProcessor& gp) override {
|
| - }
|
| + void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
|
|
|
| void setTransformData(const GrPrimitiveProcessor& primProc,
|
| const GrGLSLProgramDataManager& pdman,
|
| @@ -192,6 +228,9 @@ private:
|
| const Attribute* fInPosition;
|
| const Attribute* fInColor;
|
| const Attribute* fInCircleEdge;
|
| + const Attribute* fInClipPlane;
|
| + const Attribute* fInIsectPlane;
|
| + const Attribute* fInUnionPlane;
|
| bool fStroke;
|
|
|
| GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
|
| @@ -203,7 +242,9 @@ GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
|
|
|
| sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
|
| return sk_sp<GrGeometryProcessor>(
|
| - new CircleGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
|
| + new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
|
| + d->fRandom->nextBool(), d->fRandom->nextBool(),
|
| + GrTest::TestMatrix(d->fRandom)));
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| @@ -529,19 +570,47 @@ class CircleBatch : public GrVertexBatch {
|
| public:
|
| DEFINE_BATCH_CLASS_ID
|
|
|
| - CircleBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& circle,
|
| - const SkStrokeRec& stroke)
|
| - : INHERITED(ClassID())
|
| - , fViewMatrixIfUsingLocalCoords(viewMatrix) {
|
| - SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
|
| + /** Optional extra params to render a partial arc rather than a full circle. */
|
| + struct ArcParams {
|
| + SkScalar fStartAngleRadians;
|
| + SkScalar fSweepAngleRadians;
|
| + bool fUseCenter;
|
| + };
|
| + static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
|
| + SkScalar radius, const GrStyle& style,
|
| + const ArcParams* arcParams = nullptr) {
|
| + SkASSERT(circle_stays_circle(viewMatrix));
|
| + const SkStrokeRec& stroke = style.strokeRec();
|
| + if (style.hasPathEffect()) {
|
| + return nullptr;
|
| + }
|
| + SkStrokeRec::Style recStyle = stroke.getStyle();
|
| + if (arcParams) {
|
| + // Arc support depends on the style.
|
| + switch (recStyle) {
|
| + case SkStrokeRec::kStrokeAndFill_Style:
|
| + // This produces a strange result that this batch doesn't implement.
|
| + return nullptr;
|
| + case SkStrokeRec::kFill_Style:
|
| + // This supports all fills.
|
| + break;
|
| + case SkStrokeRec::kStroke_Style: // fall through
|
| + case SkStrokeRec::kHairline_Style:
|
| + // Strokes that don't use the center point are supported with butt cap.
|
| + if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
|
| + return nullptr;
|
| + }
|
| + break;
|
| + }
|
| + }
|
| +
|
| viewMatrix.mapPoints(¢er, 1);
|
| - SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
|
| + radius = viewMatrix.mapRadius(radius);
|
| SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
|
|
|
| - SkStrokeRec::Style style = stroke.getStyle();
|
| - bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
|
| - SkStrokeRec::kHairline_Style == style;
|
| - bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
|
| + bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
|
| + SkStrokeRec::kHairline_Style == recStyle;
|
| + bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
|
|
|
| SkScalar innerRadius = 0.0f;
|
| SkScalar outerRadius = radius;
|
| @@ -565,21 +634,111 @@ public:
|
| // rendered and the outset ensures the box will cover all partially covered by the circle.
|
| outerRadius += SK_ScalarHalf;
|
| innerRadius -= SK_ScalarHalf;
|
| + CircleBatch* batch = new CircleBatch();
|
| + batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
|
|
|
| - fGeoData.emplace_back(Geometry {
|
| - color,
|
| - innerRadius,
|
| - outerRadius,
|
| - SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
|
| - center.fX + outerRadius, center.fY + outerRadius)
|
| - });
|
| + // This makes every point fully inside the intersection plane.
|
| + static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
|
| + // This makes every point fully outside the union plane.
|
| + static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
|
| + SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
|
| + center.fX + outerRadius, center.fY + outerRadius);
|
| +
|
| + if (arcParams) {
|
| + // The shader operates in a space where the circle is translated to be centered at the
|
| + // origin. Here we compute points on the unit circle at the starting and ending angles.
|
| + SkPoint startPoint, stopPoint;
|
| + startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
|
| + SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
|
| + stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
|
| + // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
|
| + // radial lines. However, in both cases we have to be careful about the half-circle.
|
| + // case. In that case the two radial lines are equal and so that edge gets clipped
|
| + // twice. Since the shared edge goes through the center we fall back on the useCenter
|
| + // case.
|
| + bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
|
| + !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
|
| + SK_ScalarPI);
|
| + if (useCenter) {
|
| + SkVector norm0 = {startPoint.fY, -startPoint.fX};
|
| + SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
|
| + if (arcParams->fSweepAngleRadians > 0) {
|
| + norm0.negate();
|
| + } else {
|
| + norm1.negate();
|
| + }
|
| + batch->fClipPlane = true;
|
| + if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
|
| + batch->fGeoData.emplace_back(Geometry {
|
| + color,
|
| + innerRadius,
|
| + outerRadius,
|
| + {norm0.fX, norm0.fY, 0.5f},
|
| + {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
| + {norm1.fX, norm1.fY, 0.5f},
|
| + devBounds
|
| + });
|
| + batch->fClipPlaneIsect = false;
|
| + batch->fClipPlaneUnion = true;
|
| + } else {
|
| + batch->fGeoData.emplace_back(Geometry {
|
| + color,
|
| + innerRadius,
|
| + outerRadius,
|
| + {norm0.fX, norm0.fY, 0.5f},
|
| + {norm1.fX, norm1.fY, 0.5f},
|
| + {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
|
| + devBounds
|
| + });
|
| + batch->fClipPlaneIsect = true;
|
| + batch->fClipPlaneUnion = false;
|
| + }
|
| + } else {
|
| + // We clip to a secant of the original circle.
|
| + startPoint.scale(radius);
|
| + stopPoint.scale(radius);
|
| + SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
|
| + norm.normalize();
|
| + if (arcParams->fSweepAngleRadians > 0) {
|
| + norm.negate();
|
| + }
|
| + SkScalar d = -norm.dot(startPoint) + 0.5f;
|
| +
|
| + batch->fGeoData.emplace_back(Geometry {
|
| + color,
|
| + innerRadius,
|
| + outerRadius,
|
| + {norm.fX, norm.fY, d},
|
| + {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
| + {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
|
| + devBounds
|
| + });
|
| + batch->fClipPlane = true;
|
| + batch->fClipPlaneIsect = false;
|
| + batch->fClipPlaneUnion = false;
|
| + }
|
| + } else {
|
| + batch->fGeoData.emplace_back(Geometry {
|
| + color,
|
| + innerRadius,
|
| + outerRadius,
|
| + {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
| + {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
| + {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
|
| + devBounds
|
| + });
|
| + batch->fClipPlane = false;
|
| + batch->fClipPlaneIsect = false;
|
| + batch->fClipPlaneUnion = false;
|
| + }
|
| // Use the original radius and stroke radius for the bounds so that it does not include the
|
| // AA bloat.
|
| radius += halfWidth;
|
| - this->setBounds({center.fX - radius, center.fY - radius,
|
| - center.fX + radius, center.fY + radius},
|
| - HasAABloat::kYes, IsZeroArea::kNo);
|
| - fStroked = isStrokeOnly && innerRadius > 0;
|
| + batch->setBounds({center.fX - radius, center.fY - radius,
|
| + center.fX + radius, center.fY + radius},
|
| + HasAABloat::kYes, IsZeroArea::kNo);
|
| + batch->fStroked = isStrokeOnly && innerRadius > 0;
|
| + return batch;
|
| }
|
|
|
| const char* name() const override { return "CircleBatch"; }
|
| @@ -608,6 +767,7 @@ public:
|
| }
|
|
|
| private:
|
| + CircleBatch() : INHERITED(ClassID()) {}
|
| void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
|
| // Handle any overrides that affect our GP.
|
| overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
|
| @@ -623,15 +783,29 @@ private:
|
| }
|
|
|
| // Setup geometry processor
|
| - SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
|
| + SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, fClipPlane,
|
| + fClipPlaneIsect,
|
| + fClipPlaneUnion,
|
| + localMatrix));
|
| +
|
| + struct CircleVertex {
|
| + SkPoint fPos;
|
| + GrColor fColor;
|
| + SkPoint fOffset;
|
| + SkScalar fOuterRadius;
|
| + SkScalar fInnerRadius;
|
| + // These planes may or may not be present in the vertex buffer.
|
| + SkScalar fHalfPlanes[3][3];
|
| + };
|
|
|
| int instanceCount = fGeoData.count();
|
| size_t vertexStride = gp->getVertexStride();
|
| - SkASSERT(vertexStride == sizeof(CircleVertex));
|
| + SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
|
| + - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
|
| + - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
|
| QuadHelper helper;
|
| - CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
|
| - instanceCount));
|
| - if (!verts) {
|
| + char* vertices = reinterpret_cast<char*>(helper.init(target, vertexStride, instanceCount));
|
| + if (!vertices) {
|
| return;
|
| }
|
|
|
| @@ -643,34 +817,57 @@ private:
|
| SkScalar outerRadius = geom.fOuterRadius;
|
|
|
| const SkRect& bounds = geom.fDevBounds;
|
| + CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 0)*vertexStride);
|
| + CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 1)*vertexStride);
|
| + CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 2)*vertexStride);
|
| + CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 3)*vertexStride);
|
|
|
| // The inner radius in the vertex data must be specified in normalized space.
|
| innerRadius = innerRadius / outerRadius;
|
| - verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
|
| - verts[0].fColor = color;
|
| - verts[0].fOffset = SkPoint::Make(-1, -1);
|
| - verts[0].fOuterRadius = outerRadius;
|
| - verts[0].fInnerRadius = innerRadius;
|
| -
|
| - verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
|
| - verts[1].fColor = color;
|
| - verts[1].fOffset = SkPoint::Make(-1, 1);
|
| - verts[1].fOuterRadius = outerRadius;
|
| - verts[1].fInnerRadius = innerRadius;
|
| -
|
| - verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
|
| - verts[2].fColor = color;
|
| - verts[2].fOffset = SkPoint::Make(1, 1);
|
| - verts[2].fOuterRadius = outerRadius;
|
| - verts[2].fInnerRadius = innerRadius;
|
| -
|
| - verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
|
| - verts[3].fColor = color;
|
| - verts[3].fOffset = SkPoint::Make(1, -1);
|
| - verts[3].fOuterRadius = outerRadius;
|
| - verts[3].fInnerRadius = innerRadius;
|
| -
|
| - verts += kVerticesPerQuad;
|
| + v0->fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
|
| + v0->fColor = color;
|
| + v0->fOffset = SkPoint::Make(-1, -1);
|
| + v0->fOuterRadius = outerRadius;
|
| + v0->fInnerRadius = innerRadius;
|
| +
|
| + v1->fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
|
| + v1->fColor = color;
|
| + v1->fOffset = SkPoint::Make(-1, 1);
|
| + v1->fOuterRadius = outerRadius;
|
| + v1->fInnerRadius = innerRadius;
|
| +
|
| + v2->fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
|
| + v2->fColor = color;
|
| + v2->fOffset = SkPoint::Make(1, 1);
|
| + v2->fOuterRadius = outerRadius;
|
| + v2->fInnerRadius = innerRadius;
|
| +
|
| + v3->fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
|
| + v3->fColor = color;
|
| + v3->fOffset = SkPoint::Make(1, -1);
|
| + v3->fOuterRadius = outerRadius;
|
| + v3->fInnerRadius = innerRadius;
|
| +
|
| + if (fClipPlane) {
|
| + memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
| + }
|
| + int unionIdx = 1;
|
| + if (fClipPlaneIsect) {
|
| + memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
| + unionIdx = 2;
|
| + }
|
| + if (fClipPlaneUnion) {
|
| + memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
| + memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
| + }
|
| }
|
| helper.recordDraw(target, gp);
|
| }
|
| @@ -686,6 +883,12 @@ private:
|
| return false;
|
| }
|
|
|
| + // Because we've set up the batches that don't use the planes with noop values
|
| + // we can just accumulate used planes by later batches.
|
| + fClipPlane |= that->fClipPlane;
|
| + fClipPlaneIsect |= that->fClipPlaneIsect;
|
| + fClipPlaneUnion |= that->fClipPlaneUnion;
|
| +
|
| if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
|
| return false;
|
| }
|
| @@ -696,13 +899,19 @@ private:
|
| }
|
|
|
| struct Geometry {
|
| - GrColor fColor;
|
| + GrColor fColor;
|
| SkScalar fInnerRadius;
|
| SkScalar fOuterRadius;
|
| - SkRect fDevBounds;
|
| + SkScalar fClipPlane[3];
|
| + SkScalar fIsectPlane[3];
|
| + SkScalar fUnionPlane[3];
|
| + SkRect fDevBounds;
|
| };
|
|
|
| bool fStroked;
|
| + bool fClipPlane;
|
| + bool fClipPlaneIsect;
|
| + bool fClipPlaneUnion;
|
| SkMatrix fViewMatrixIfUsingLocalCoords;
|
| SkSTArray<1, Geometry, true> fGeoData;
|
|
|
| @@ -1243,7 +1452,17 @@ private:
|
| }
|
|
|
| // Setup geometry processor
|
| - SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
|
| + SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, false, false,
|
| + false, localMatrix));
|
| +
|
| + struct CircleVertex {
|
| + SkPoint fPos;
|
| + GrColor fColor;
|
| + SkPoint fOffset;
|
| + SkScalar fOuterRadius;
|
| + SkScalar fInnerRadius;
|
| + // No half plane, we don't use it here.
|
| + };
|
|
|
| int instanceCount = fGeoData.count();
|
| size_t vertexStride = gp->getVertexStride();
|
| @@ -1631,7 +1850,7 @@ GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
|
| const SkMatrix& viewMatrix,
|
| const SkRRect& rrect,
|
| const SkStrokeRec& stroke,
|
| - GrShaderCaps* shaderCaps) {
|
| + const GrShaderCaps* shaderCaps) {
|
| if (rrect.isOval()) {
|
| return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
|
| }
|
| @@ -1649,10 +1868,13 @@ GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
|
| const SkMatrix& viewMatrix,
|
| const SkRect& oval,
|
| const SkStrokeRec& stroke,
|
| - GrShaderCaps* shaderCaps) {
|
| + const GrShaderCaps* shaderCaps) {
|
| // we can draw circles
|
| - if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
|
| - return new CircleBatch(color, viewMatrix, oval, stroke);
|
| + SkScalar width = oval.width();
|
| + if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
|
| + SkPoint center = {oval.centerX(), oval.centerY()};
|
| + return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
|
| + GrStyle(stroke, nullptr));
|
| }
|
|
|
| // if we have shader derivative support, render as device-independent
|
| @@ -1670,13 +1892,51 @@ GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| +GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
|
| + const SkMatrix& viewMatrix,
|
| + const SkRect& oval,
|
| + SkScalar startAngle, SkScalar sweepAngle,
|
| + bool useCenter,
|
| + const GrStyle& style,
|
| + const GrShaderCaps* shaderCaps) {
|
| + SkScalar width = oval.width();
|
| + if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
|
| + return nullptr;
|
| + }
|
| + SkPoint center = {oval.centerX(), oval.centerY()};
|
| + CircleBatch::ArcParams arcParams = {
|
| + SkDegreesToRadians(startAngle),
|
| + SkDegreesToRadians(sweepAngle),
|
| + useCenter
|
| + };
|
| + return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +
|
| #ifdef GR_TEST_UTILS
|
|
|
| DRAW_BATCH_TEST_DEFINE(CircleBatch) {
|
| - SkMatrix viewMatrix = GrTest::TestMatrix(random);
|
| - GrColor color = GrRandomColor(random);
|
| - SkRect circle = GrTest::TestSquare(random);
|
| - return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
|
| + do {
|
| + SkMatrix viewMatrix = GrTest::TestMatrix(random);
|
| + GrColor color = GrRandomColor(random);
|
| + SkRect circle = GrTest::TestSquare(random);
|
| + SkPoint center = {circle.centerX(), circle.centerY()};
|
| + SkScalar radius = circle.width() / 2.f;
|
| + SkStrokeRec stroke = GrTest::TestStrokeRec(random);
|
| + CircleBatch::ArcParams arcParamsTmp;
|
| + const CircleBatch::ArcParams* arcParams = nullptr;
|
| + if (random->nextBool()) {
|
| + arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
|
| + arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
|
| + arcParams = &arcParamsTmp;
|
| + }
|
| + GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
|
| + GrStyle(stroke, nullptr), arcParams);
|
| + if (batch) {
|
| + return batch;
|
| + }
|
| + } while (true);
|
| }
|
|
|
| DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
|
|
|