Chromium Code Reviews| Index: src/gpu/GrOvalRenderer.cpp |
| diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
| index 079c261a66e7a6692983b38c01ef91f9d7394525..5621b9b83dd41e7995835c7be4f196a0e8a0d59f 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: |
|
robertphillips
2016/08/22 19:43:34
results -> result ?
bsalomon
2016/08/22 19:46:40
Done.
|
| + // This produces a strange results that this batch doesn't implement. |
| + return nullptr; |
| + case SkStrokeRec::kFill_Style: |
| + // This supports all fills. |
| + break; |
|
robertphillips
2016/08/22 19:43:34
// fall through ?
bsalomon
2016/08/22 19:46:40
Done.
|
| + case SkStrokeRec::kStroke_Style: |
| + 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,113 @@ 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}; |
| + |
| + 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; |
|
robertphillips
2016/08/22 19:02:52
space before '>' ?
bsalomon
2016/08/22 19:18:27
Done.
|
| + 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}, |
|
robertphillips
2016/08/22 19:02:52
I'm not sure if it would be clearer to create a si
bsalomon
2016/08/22 19:18:27
Done.
|
| + SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, |
| + center.fX + outerRadius, center.fY + outerRadius) |
| + }); |
| + 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]}, |
| + SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, |
| + center.fX + outerRadius, center.fY + outerRadius) |
| + }); |
| + 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]}, |
| + SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, |
| + center.fX + outerRadius, center.fY + outerRadius) |
| + }); |
| + 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]}, |
| + SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, |
| + center.fX + outerRadius, center.fY + outerRadius) |
| + }); |
| + 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; |
|
robertphillips
2016/08/22 19:02:52
Are these bounds correct? It seems like we should
bsalomon
2016/08/22 19:18:27
See comment above, using original values since the
|
| - 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 +769,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 +785,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 +819,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 +885,10 @@ private: |
| return false; |
| } |
|
robertphillips
2016/08/22 19:02:52
// Because we've set up the batches that don't use
bsalomon
2016/08/22 19:18:27
Done.
|
| + 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 - .01; |
| + 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) { |