aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/gpu/GrDrawContext.h24
-rw-r--r--src/gpu/GrDrawContext.cpp38
-rw-r--r--src/gpu/GrOvalRenderer.cpp444
-rw-r--r--src/gpu/GrOvalRenderer.h15
-rw-r--r--src/gpu/SkGpuDevice.cpp19
-rw-r--r--src/gpu/SkGpuDevice.h2
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(&center, 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;