diff options
-rw-r--r-- | include/gpu/GrDrawContext.h | 24 | ||||
-rw-r--r-- | src/gpu/GrDrawContext.cpp | 38 | ||||
-rw-r--r-- | src/gpu/GrOvalRenderer.cpp | 444 | ||||
-rw-r--r-- | src/gpu/GrOvalRenderer.h | 15 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 19 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.h | 2 |
6 files changed, 445 insertions, 97 deletions
diff --git a/include/gpu/GrDrawContext.h b/include/gpu/GrDrawContext.h index 77a3443f9e..993bb56f9e 100644 --- a/include/gpu/GrDrawContext.h +++ b/include/gpu/GrDrawContext.h @@ -230,9 +230,31 @@ public: const SkMatrix& viewMatrix, const SkRect& oval, const GrStyle& style); + /** + * Draws a partial arc of an oval. + * + * @param paint describes how to color pixels. + * @param viewMatrix transformation matrix. + * @param oval the bounding rect of the oval. + * @param startAngle starting angle in degrees. + * @param sweepAngle angle to sweep in degrees. Must be in (-360, 360) + * @param useCenter true means that the implied path begins at the oval center, connects as a + * line to the point indicated by the start contains the arc indicated by + * the sweep angle. If false the line beginning at the center point is + * omitted. + * @param style style to apply to the oval. + */ + void drawArc(const GrClip&, + const GrPaint& paint, + const SkMatrix& viewMatrix, + const SkRect& oval, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const GrStyle& style); /** - * Draw the image as a set of rects, specified by |iter|. + * Draw the image as a set of rects, specified by |iter|. */ void drawImageLattice(const GrClip&, const GrPaint& paint, diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp index 7044b499fa..3b346b6efd 100644 --- a/src/gpu/GrDrawContext.cpp +++ b/src/gpu/GrDrawContext.cpp @@ -997,6 +997,44 @@ void GrDrawContext::drawOval(const GrClip& clip, this->internalDrawPath(clip, paint, viewMatrix, path, style); } +void GrDrawContext::drawArc(const GrClip& clip, + const GrPaint& paint, + const SkMatrix& viewMatrix, + const SkRect& oval, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const GrStyle& style) { + bool useHWAA; + if (should_apply_coverage_aa(paint, fRenderTarget.get(), &useHWAA)) { + GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps(); + SkAutoTUnref<GrDrawBatch> batch(GrOvalRenderer::CreateArcBatch(paint.getColor(), + viewMatrix, + oval, + startAngle, + sweepAngle, + useCenter, + style, + shaderCaps)); + if (batch) { + GrPipelineBuilder pipelineBuilder(paint, useHWAA); + this->getDrawTarget()->drawBatch(pipelineBuilder, this, clip, batch); + return; + } + } + SkPath path; + path.setIsVolatile(true); + if (useCenter) { + path.moveTo(oval.centerX(), oval.centerY()); + } + path.arcTo(oval, startAngle, sweepAngle, !useCenter); + if (useCenter) { + path.close(); + } + this->internalDrawPath(clip, paint, viewMatrix, path, style); + return; +} + void GrDrawContext::drawImageLattice(const GrClip& clip, const GrPaint& paint, const SkMatrix& viewMatrix, diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index 079c261a66..c17388dca3 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) { diff --git a/src/gpu/GrOvalRenderer.h b/src/gpu/GrOvalRenderer.h index 66182b24e8..c4ea4968d7 100644 --- a/src/gpu/GrOvalRenderer.h +++ b/src/gpu/GrOvalRenderer.h @@ -12,6 +12,7 @@ class GrDrawBatch; class GrShaderCaps; +class GrStyle; class SkMatrix; struct SkRect; class SkRRect; @@ -26,15 +27,21 @@ public: const SkMatrix& viewMatrix, const SkRect& oval, const SkStrokeRec& stroke, - GrShaderCaps* shaderCaps); + const GrShaderCaps* shaderCaps); static GrDrawBatch* CreateRRectBatch(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect, const SkStrokeRec& stroke, - GrShaderCaps* shaderCaps); + const GrShaderCaps* shaderCaps); -private: - GrOvalRenderer(); + static GrDrawBatch* CreateArcBatch(GrColor, + const SkMatrix& viewMatrix, + const SkRect& oval, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const GrStyle&, + const GrShaderCaps* shaderCaps); }; #endif // GrOvalRenderer_DEFINED diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 258355e497..a6c8a1fc74 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -554,6 +554,25 @@ void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint fDrawContext->drawOval(fClip, grPaint, *draw.fMatrix, oval, GrStyle(paint)); } +void SkGpuDevice::drawArc(const SkDraw& draw, const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawArc", fContext); + CHECK_SHOULD_DRAW(draw); + + if (paint.getMaskFilter()) { + this->INHERITED::drawArc(draw, oval, startAngle, sweepAngle, useCenter, paint); + return; + } + GrPaint grPaint; + if (!SkPaintToGrPaint(this->context(), fDrawContext.get(), paint, *draw.fMatrix, &grPaint)) { + return; + } + + fDrawContext->drawArc(fClip, grPaint, *draw.fMatrix, oval, startAngle, sweepAngle, useCenter, + GrStyle(paint)); +} + #include "SkMaskFilter.h" /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index 4de0a50f4b..bbf15b5fbd 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -77,6 +77,8 @@ public: const SkRRect& inner, const SkPaint& paint) override; virtual void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override; + virtual void drawArc(const SkDraw&, const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle, bool useCenter, const SkPaint& paint) override; virtual void drawPath(const SkDraw&, const SkPath& path, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) override; |