Index: src/gpu/GrOvalRenderer.cpp |
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
index 3f73c6bf89fc0fd6deee24bb0f4a96b20ea6d6bb..f5dcfcb0f5ad51c10b81ddfa5e52254b8e5627d7 100644 |
--- a/src/gpu/GrOvalRenderer.cpp |
+++ b/src/gpu/GrOvalRenderer.cpp |
@@ -524,49 +524,58 @@ sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTes |
/////////////////////////////////////////////////////////////////////////////// |
-GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& oval, |
- const SkStrokeRec& stroke, |
- GrShaderCaps* shaderCaps) { |
- // we can draw circles |
- if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) { |
- return CreateCircleBatch(color, viewMatrix, oval, stroke); |
- } |
- |
- // if we have shader derivative support, render as device-independent |
- if (shaderCaps->shaderDerivativeSupport()) { |
- return CreateDIEllipseBatch(color, viewMatrix, oval, stroke); |
- } |
+class CircleBatch : public GrVertexBatch { |
+public: |
+ DEFINE_BATCH_CLASS_ID |
- // otherwise axis-aligned ellipses only |
- if (viewMatrix.rectStaysRect()) { |
- return CreateEllipseBatch(color, viewMatrix, oval, stroke); |
- } |
+ CircleBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& circle, |
+ const SkStrokeRec& stroke) |
+ : INHERITED(ClassID()) |
+ , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
+ SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY()); |
+ viewMatrix.mapPoints(¢er, 1); |
+ SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width())); |
+ SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); |
- return nullptr; |
-} |
+ SkStrokeRec::Style style = stroke.getStyle(); |
+ bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
+ SkStrokeRec::kHairline_Style == style; |
+ bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
-/////////////////////////////////////////////////////////////////////////////// |
+ SkScalar innerRadius = 0.0f; |
+ SkScalar outerRadius = radius; |
+ SkScalar halfWidth = 0; |
+ if (hasStroke) { |
+ if (SkScalarNearlyZero(strokeWidth)) { |
+ halfWidth = SK_ScalarHalf; |
+ } else { |
+ halfWidth = SkScalarHalf(strokeWidth); |
+ } |
-class CircleBatch : public GrVertexBatch { |
-public: |
- DEFINE_BATCH_CLASS_ID |
+ outerRadius += halfWidth; |
+ if (isStrokeOnly) { |
+ innerRadius = radius - halfWidth; |
+ } |
+ } |
- struct Geometry { |
- SkRect fDevBounds; |
- SkScalar fInnerRadius; |
- SkScalar fOuterRadius; |
- GrColor fColor; |
- }; |
+ // The radii are outset for two reasons. First, it allows the shader to simply perform |
+ // simpler computation because the computed alpha is zero, rather than 50%, at the radius. |
+ // Second, the outer radius is used to compute the verts of the bounding box that is |
+ // rendered and the outset ensures the box will cover all partially covered by the circle. |
+ outerRadius += SK_ScalarHalf; |
+ innerRadius -= SK_ScalarHalf; |
- CircleBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
- : INHERITED(ClassID()) |
- , fStroked(stroked) |
- , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
- fGeoData.push_back(geometry); |
- this->setBounds(geometry.fDevBounds); |
+ fGeoData.emplace_back(Geometry { |
+ color, |
+ innerRadius, |
+ outerRadius, |
+ SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, |
+ center.fX + outerRadius, center.fY + outerRadius) |
+ }); |
+ this->setBounds(fGeoData.back().fDevBounds); |
+ fStroked = isStrokeOnly && innerRadius > 0; |
} |
+ |
const char* name() const override { return "CircleBatch"; } |
SkString dumpInfo() const override { |
@@ -680,6 +689,13 @@ private: |
return true; |
} |
+ struct Geometry { |
+ GrColor fColor; |
+ SkScalar fInnerRadius; |
+ SkScalar fOuterRadius; |
+ SkRect fDevBounds; |
+ }; |
+ |
bool fStroked; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
@@ -687,81 +703,87 @@ private: |
typedef GrVertexBatch INHERITED; |
}; |
-static GrDrawBatch* create_circle_batch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& circle, |
- const SkStrokeRec& stroke) { |
- SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY()); |
- viewMatrix.mapPoints(¢er, 1); |
- SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width())); |
- 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; |
- |
- SkScalar innerRadius = 0.0f; |
- SkScalar outerRadius = radius; |
- SkScalar halfWidth = 0; |
- if (hasStroke) { |
- if (SkScalarNearlyZero(strokeWidth)) { |
- halfWidth = SK_ScalarHalf; |
- } else { |
- halfWidth = SkScalarHalf(strokeWidth); |
- } |
- |
- outerRadius += halfWidth; |
- if (isStrokeOnly) { |
- innerRadius = radius - halfWidth; |
- } |
- } |
- |
- // The radii are outset for two reasons. First, it allows the shader to simply perform simpler |
- // computation because the computed alpha is zero, rather than 50%, at the radius. |
- // Second, the outer radius is used to compute the verts of the bounding box that is rendered |
- // and the outset ensures the box will cover all partially covered by the circle. |
- outerRadius += SK_ScalarHalf; |
- innerRadius -= SK_ScalarHalf; |
- |
- CircleBatch::Geometry geometry; |
- geometry.fColor = color; |
- geometry.fInnerRadius = innerRadius; |
- geometry.fOuterRadius = outerRadius; |
- geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, |
- center.fX + outerRadius, center.fY + outerRadius); |
- |
- return new CircleBatch(geometry, viewMatrix, isStrokeOnly && innerRadius > 0); |
-} |
- |
-GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& circle, |
- const SkStrokeRec& stroke) { |
- return create_circle_batch(color, viewMatrix, circle, stroke); |
-} |
- |
/////////////////////////////////////////////////////////////////////////////// |
class EllipseBatch : public GrVertexBatch { |
public: |
DEFINE_BATCH_CLASS_ID |
+ static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse, |
+ const SkStrokeRec& stroke) { |
+ SkASSERT(viewMatrix.rectStaysRect()); |
+ |
+ // do any matrix crunching before we reset the draw state for device coords |
+ SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); |
+ viewMatrix.mapPoints(¢er, 1); |
+ SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); |
+ SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); |
+ SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius + |
+ viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius); |
+ SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius + |
+ viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius); |
+ |
+ // do (potentially) anisotropic mapping of stroke |
+ SkVector scaledStroke; |
+ SkScalar strokeWidth = stroke.getWidth(); |
+ scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] + |
+ viewMatrix[SkMatrix::kMSkewY])); |
+ scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] + |
+ viewMatrix[SkMatrix::kMScaleY])); |
+ |
+ SkStrokeRec::Style style = stroke.getStyle(); |
+ bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
+ SkStrokeRec::kHairline_Style == style; |
+ bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
+ |
+ SkScalar innerXRadius = 0; |
+ SkScalar innerYRadius = 0; |
+ if (hasStroke) { |
+ if (SkScalarNearlyZero(scaledStroke.length())) { |
+ scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
+ } else { |
+ scaledStroke.scale(SK_ScalarHalf); |
+ } |
- struct Geometry { |
- SkRect fDevBounds; |
- SkScalar fXRadius; |
- SkScalar fYRadius; |
- SkScalar fInnerXRadius; |
- SkScalar fInnerYRadius; |
- GrColor fColor; |
- }; |
+ // we only handle thick strokes for near-circular ellipses |
+ if (scaledStroke.length() > SK_ScalarHalf && |
+ (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
+ return nullptr; |
+ } |
- EllipseBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
- : INHERITED(ClassID()) |
- , fStroked(stroked) |
- , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
- fGeoData.push_back(geometry); |
- this->setBounds(geometry.fDevBounds); |
+ // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
+ if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || |
+ scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { |
+ return nullptr; |
+ } |
+ |
+ // this is legit only if scale & translation (which should be the case at the moment) |
+ if (isStrokeOnly) { |
+ innerXRadius = xRadius - scaledStroke.fX; |
+ innerYRadius = yRadius - scaledStroke.fY; |
+ } |
+ |
+ xRadius += scaledStroke.fX; |
+ yRadius += scaledStroke.fY; |
+ } |
+ |
+ EllipseBatch* batch = new EllipseBatch(); |
+ batch->fGeoData.emplace_back(Geometry { |
+ color, |
+ xRadius, |
+ yRadius, |
+ innerXRadius, |
+ innerYRadius, |
+ SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius, |
+ center.fX + xRadius, center.fY + yRadius) |
+ }); |
+ |
+ // Outset bounds to include half-pixel width antialiasing. |
+ batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
+ |
+ batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0; |
+ batch->fViewMatrixIfUsingLocalCoords = viewMatrix; |
+ batch->setBounds(batch->fGeoData.back().fDevBounds); |
+ return batch; |
} |
const char* name() const override { return "EllipseBatch"; } |
@@ -775,6 +797,8 @@ public: |
} |
private: |
+ EllipseBatch() : INHERITED(ClassID()) {} |
+ |
void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
// Handle any overrides that affect our GP. |
if (!overrides.readsCoverage()) { |
@@ -874,6 +898,14 @@ private: |
return true; |
} |
+ struct Geometry { |
+ GrColor fColor; |
+ SkScalar fXRadius; |
+ SkScalar fYRadius; |
+ SkScalar fInnerXRadius; |
+ SkScalar fInnerYRadius; |
+ SkRect fDevBounds; |
+ }; |
bool fStroked; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
@@ -882,110 +914,89 @@ private: |
typedef GrVertexBatch INHERITED; |
}; |
-static GrDrawBatch* create_ellipse_batch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& ellipse, |
- const SkStrokeRec& stroke) { |
- SkASSERT(viewMatrix.rectStaysRect()); |
+///////////////////////////////////////////////////////////////////////////////////////////////// |
- // do any matrix crunching before we reset the draw state for device coords |
- SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); |
- viewMatrix.mapPoints(¢er, 1); |
- SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); |
- SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); |
- SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius + |
- viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius); |
- SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius + |
- viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius); |
- |
- // do (potentially) anisotropic mapping of stroke |
- SkVector scaledStroke; |
- SkScalar strokeWidth = stroke.getWidth(); |
- scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] + |
- viewMatrix[SkMatrix::kMSkewY])); |
- scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] + |
- viewMatrix[SkMatrix::kMScaleY])); |
+class DIEllipseBatch : public GrVertexBatch { |
+public: |
+ DEFINE_BATCH_CLASS_ID |
- SkStrokeRec::Style style = stroke.getStyle(); |
- bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
- SkStrokeRec::kHairline_Style == style; |
- bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
+ static GrDrawBatch* Create(GrColor color, |
+ const SkMatrix& viewMatrix, |
+ const SkRect& ellipse, |
+ const SkStrokeRec& stroke) { |
+ SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); |
+ SkScalar xRadius = SkScalarHalf(ellipse.width()); |
+ SkScalar yRadius = SkScalarHalf(ellipse.height()); |
+ |
+ SkStrokeRec::Style style = stroke.getStyle(); |
+ DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ? |
+ DIEllipseStyle::kStroke : |
+ (SkStrokeRec::kHairline_Style == style) ? |
+ DIEllipseStyle::kHairline : DIEllipseStyle::kFill; |
+ |
+ SkScalar innerXRadius = 0; |
+ SkScalar innerYRadius = 0; |
+ if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { |
+ SkScalar strokeWidth = stroke.getWidth(); |
+ |
+ if (SkScalarNearlyZero(strokeWidth)) { |
+ strokeWidth = SK_ScalarHalf; |
+ } else { |
+ strokeWidth *= SK_ScalarHalf; |
+ } |
- SkScalar innerXRadius = 0; |
- SkScalar innerYRadius = 0; |
- if (hasStroke) { |
- if (SkScalarNearlyZero(scaledStroke.length())) { |
- scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
- } else { |
- scaledStroke.scale(SK_ScalarHalf); |
- } |
+ // we only handle thick strokes for near-circular ellipses |
+ if (strokeWidth > SK_ScalarHalf && |
+ (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
+ return nullptr; |
+ } |
- // we only handle thick strokes for near-circular ellipses |
- if (scaledStroke.length() > SK_ScalarHalf && |
- (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
- return nullptr; |
- } |
+ // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
+ if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius || |
+ strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) { |
+ return nullptr; |
+ } |
- // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
- if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || |
- scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { |
- return nullptr; |
- } |
+ // set inner radius (if needed) |
+ if (SkStrokeRec::kStroke_Style == style) { |
+ innerXRadius = xRadius - strokeWidth; |
+ innerYRadius = yRadius - strokeWidth; |
+ } |
- // this is legit only if scale & translation (which should be the case at the moment) |
- if (isStrokeOnly) { |
- innerXRadius = xRadius - scaledStroke.fX; |
- innerYRadius = yRadius - scaledStroke.fY; |
+ xRadius += strokeWidth; |
+ yRadius += strokeWidth; |
+ } |
+ if (DIEllipseStyle::kStroke == dieStyle) { |
+ dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke : |
+ DIEllipseStyle ::kFill; |
} |
- xRadius += scaledStroke.fX; |
- yRadius += scaledStroke.fY; |
- } |
- |
- EllipseBatch::Geometry geometry; |
- geometry.fColor = color; |
- geometry.fXRadius = xRadius; |
- geometry.fYRadius = yRadius; |
- geometry.fInnerXRadius = innerXRadius; |
- geometry.fInnerYRadius = innerYRadius; |
- geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius, |
- center.fX + xRadius, center.fY + yRadius); |
- |
- // outset bounds to include half-pixel width antialiasing. |
- geometry.fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
- |
- return new EllipseBatch(geometry, viewMatrix, |
- isStrokeOnly && innerXRadius > 0 && innerYRadius > 0); |
-} |
- |
-GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& ellipse, |
- const SkStrokeRec& stroke) { |
- return create_ellipse_batch(color, viewMatrix, ellipse, stroke); |
-} |
- |
-///////////////////////////////////////////////////////////////////////////////////////////////// |
- |
-class DIEllipseBatch : public GrVertexBatch { |
-public: |
- DEFINE_BATCH_CLASS_ID |
- |
- struct Geometry { |
- SkMatrix fViewMatrix; |
- SkRect fBounds; |
- SkScalar fXRadius; |
- SkScalar fYRadius; |
- SkScalar fInnerXRadius; |
- SkScalar fInnerYRadius; |
- SkScalar fGeoDx; |
- SkScalar fGeoDy; |
- GrColor fColor; |
- DIEllipseStyle fStyle; |
- }; |
- |
- static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) { |
- return new DIEllipseBatch(geometry, bounds); |
+ // This expands the outer rect so that after CTM we end up with a half-pixel border |
+ SkScalar a = viewMatrix[SkMatrix::kMScaleX]; |
+ SkScalar b = viewMatrix[SkMatrix::kMSkewX]; |
+ SkScalar c = viewMatrix[SkMatrix::kMSkewY]; |
+ SkScalar d = viewMatrix[SkMatrix::kMScaleY]; |
+ SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c); |
+ SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d); |
+ |
+ DIEllipseBatch* batch = new DIEllipseBatch(); |
+ batch->fGeoData.emplace_back(Geometry { |
+ viewMatrix, |
+ color, |
+ xRadius, |
+ yRadius, |
+ innerXRadius, |
+ innerYRadius, |
+ geoDx, |
+ geoDy, |
+ dieStyle, |
+ SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy, |
+ center.fX + xRadius + geoDx, center.fY + yRadius + geoDy) |
+ }); |
+ SkRect devBounds = batch->fGeoData.back().fBounds; |
+ viewMatrix.mapRect(&devBounds); |
+ batch->setBounds(devBounds); |
+ return batch; |
} |
const char* name() const override { return "DIEllipseBatch"; } |
@@ -1000,6 +1011,8 @@ public: |
private: |
+ DIEllipseBatch() : INHERITED(ClassID()) {} |
+ |
void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
// Handle any overrides that affect our GP. |
overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
@@ -1062,12 +1075,6 @@ private: |
helper.recordDraw(target, gp); |
} |
- DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) { |
- fGeoData.push_back(geometry); |
- |
- this->setBounds(bounds); |
- } |
- |
bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { |
DIEllipseBatch* that = t->cast<DIEllipseBatch>(); |
if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), |
@@ -1092,96 +1099,25 @@ private: |
const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } |
DIEllipseStyle style() const { return fGeoData[0].fStyle; } |
+ struct Geometry { |
+ SkMatrix fViewMatrix; |
+ GrColor fColor; |
+ SkScalar fXRadius; |
+ SkScalar fYRadius; |
+ SkScalar fInnerXRadius; |
+ SkScalar fInnerYRadius; |
+ SkScalar fGeoDx; |
+ SkScalar fGeoDy; |
+ DIEllipseStyle fStyle; |
+ SkRect fBounds; |
+ }; |
+ |
bool fUsesLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
typedef GrVertexBatch INHERITED; |
}; |
-static GrDrawBatch* create_diellipse_batch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& ellipse, |
- const SkStrokeRec& stroke) { |
- SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); |
- SkScalar xRadius = SkScalarHalf(ellipse.width()); |
- SkScalar yRadius = SkScalarHalf(ellipse.height()); |
- |
- SkStrokeRec::Style style = stroke.getStyle(); |
- DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ? |
- DIEllipseStyle::kStroke : |
- (SkStrokeRec::kHairline_Style == style) ? |
- DIEllipseStyle::kHairline : DIEllipseStyle::kFill; |
- |
- SkScalar innerXRadius = 0; |
- SkScalar innerYRadius = 0; |
- if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { |
- SkScalar strokeWidth = stroke.getWidth(); |
- |
- if (SkScalarNearlyZero(strokeWidth)) { |
- strokeWidth = SK_ScalarHalf; |
- } else { |
- strokeWidth *= SK_ScalarHalf; |
- } |
- |
- // we only handle thick strokes for near-circular ellipses |
- if (strokeWidth > SK_ScalarHalf && |
- (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
- return nullptr; |
- } |
- |
- // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
- if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius || |
- strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) { |
- return nullptr; |
- } |
- |
- // set inner radius (if needed) |
- if (SkStrokeRec::kStroke_Style == style) { |
- innerXRadius = xRadius - strokeWidth; |
- innerYRadius = yRadius - strokeWidth; |
- } |
- |
- xRadius += strokeWidth; |
- yRadius += strokeWidth; |
- } |
- if (DIEllipseStyle::kStroke == dieStyle) { |
- dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke : |
- DIEllipseStyle ::kFill; |
- } |
- |
- // This expands the outer rect so that after CTM we end up with a half-pixel border |
- SkScalar a = viewMatrix[SkMatrix::kMScaleX]; |
- SkScalar b = viewMatrix[SkMatrix::kMSkewX]; |
- SkScalar c = viewMatrix[SkMatrix::kMSkewY]; |
- SkScalar d = viewMatrix[SkMatrix::kMScaleY]; |
- SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c); |
- SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d); |
- |
- DIEllipseBatch::Geometry geometry; |
- geometry.fViewMatrix = viewMatrix; |
- geometry.fColor = color; |
- geometry.fXRadius = xRadius; |
- geometry.fYRadius = yRadius; |
- geometry.fInnerXRadius = innerXRadius; |
- geometry.fInnerYRadius = innerYRadius; |
- geometry.fGeoDx = geoDx; |
- geometry.fGeoDy = geoDy; |
- geometry.fStyle = dieStyle; |
- geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy, |
- center.fX + xRadius + geoDx, center.fY + yRadius + geoDy); |
- |
- SkRect devBounds = geometry.fBounds; |
- viewMatrix.mapRect(&devBounds); |
- return DIEllipseBatch::Create(geometry, devBounds); |
-} |
- |
-GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color, |
- const SkMatrix& viewMatrix, |
- const SkRect& ellipse, |
- const SkStrokeRec& stroke) { |
- return create_diellipse_batch(color, viewMatrix, ellipse, stroke); |
-} |
- |
/////////////////////////////////////////////////////////////////////////////// |
static const uint16_t gRRectIndices[] = { |
@@ -1231,20 +1167,46 @@ class RRectCircleRendererBatch : public GrVertexBatch { |
public: |
DEFINE_BATCH_CLASS_ID |
- struct Geometry { |
- SkRect fDevBounds; |
- SkScalar fInnerRadius; |
- SkScalar fOuterRadius; |
- GrColor fColor; |
- }; |
+ // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates |
+ // whether the rrect is only stroked or stroked and filled. |
+ RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect, |
+ float devRadius, float devStrokeWidth, bool strokeOnly) |
+ : INHERITED(ClassID()) |
+ , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
+ SkRect bounds = devRect; |
+ SkASSERT(!(devStrokeWidth <= 0 && strokeOnly)); |
+ SkScalar innerRadius = 0.0f; |
+ SkScalar outerRadius = devRadius; |
+ SkScalar halfWidth = 0; |
+ fStroked = false; |
+ if (devStrokeWidth > 0) { |
+ if (SkScalarNearlyZero(devStrokeWidth)) { |
+ halfWidth = SK_ScalarHalf; |
+ } else { |
+ halfWidth = SkScalarHalf(devStrokeWidth); |
+ } |
- RRectCircleRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
- : INHERITED(ClassID()) |
- , fStroked(stroked) |
- , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
- fGeoData.push_back(geometry); |
+ if (strokeOnly) { |
+ innerRadius = devRadius - halfWidth; |
+ fStroked = innerRadius >= 0; |
+ } |
+ outerRadius += halfWidth; |
+ bounds.outset(halfWidth, halfWidth); |
+ } |
- this->setBounds(geometry.fDevBounds); |
+ // The radii are outset for two reasons. First, it allows the shader to simply perform |
+ // simpler computation because the computed alpha is zero, rather than 50%, at the radius. |
+ // Second, the outer radius is used to compute the verts of the bounding box that is |
+ // rendered and the outset ensures the box will cover all partially covered by the rrect |
+ // corners. |
+ outerRadius += SK_ScalarHalf; |
+ innerRadius -= SK_ScalarHalf; |
+ |
+ // Expand the rect so all the pixels will be captured. |
+ bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
+ |
+ fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds }); |
+ this->setBounds(bounds); |
} |
const char* name() const override { return "RRectCircleBatch"; } |
@@ -1366,6 +1328,13 @@ private: |
return true; |
} |
+ struct Geometry { |
+ GrColor fColor; |
+ SkScalar fInnerRadius; |
+ SkScalar fOuterRadius; |
+ SkRect fDevBounds; |
+ }; |
+ |
bool fStroked; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
@@ -1377,21 +1346,64 @@ class RRectEllipseRendererBatch : public GrVertexBatch { |
public: |
DEFINE_BATCH_CLASS_ID |
- struct Geometry { |
- SkRect fDevBounds; |
- SkScalar fXRadius; |
- SkScalar fYRadius; |
- SkScalar fInnerXRadius; |
- SkScalar fInnerYRadius; |
- GrColor fColor; |
- }; |
+ // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates |
+ // whether the rrect is only stroked or stroked and filled. |
+ static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect, |
+ float devXRadius, float devYRadius, SkVector devStrokeWidths, |
+ bool strokeOnly) { |
+ SkASSERT(devXRadius > 0.5); |
+ SkASSERT(devYRadius > 0.5); |
+ SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0)); |
+ SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0)); |
+ SkScalar innerXRadius = 0.0f; |
+ SkScalar innerYRadius = 0.0f; |
+ SkRect bounds = devRect; |
+ bool stroked = false; |
+ if (devStrokeWidths.fX > 0) { |
+ if (SkScalarNearlyZero(devStrokeWidths.length())) { |
+ devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf); |
+ } else { |
+ devStrokeWidths.scale(SK_ScalarHalf); |
+ } |
+ |
+ // we only handle thick strokes for near-circular ellipses |
+ if (devStrokeWidths.length() > SK_ScalarHalf && |
+ (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) { |
+ return nullptr; |
+ } |
+ |
+ // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
+ if (devStrokeWidths.fX*(devYRadius*devYRadius) < |
+ (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) { |
+ return nullptr; |
+ } |
+ if (devStrokeWidths.fY*(devXRadius*devXRadius) < |
+ (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) { |
+ return nullptr; |
+ } |
- RRectEllipseRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
- : INHERITED(ClassID()) |
- , fStroked(stroked) |
- , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
- fGeoData.push_back(geometry); |
- this->setBounds(geometry.fDevBounds); |
+ // this is legit only if scale & translation (which should be the case at the moment) |
+ if (strokeOnly) { |
+ innerXRadius = devXRadius - devStrokeWidths.fX; |
+ innerYRadius = devYRadius - devStrokeWidths.fY; |
+ stroked = (innerXRadius >= 0 && innerYRadius >= 0); |
+ } |
+ |
+ devXRadius += devStrokeWidths.fX; |
+ devYRadius += devStrokeWidths.fY; |
+ bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY); |
+ } |
+ |
+ // Expand the rect so all the pixels will be captured. |
+ bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
+ |
+ RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch(); |
+ batch->fStroked = stroked; |
+ batch->fViewMatrixIfUsingLocalCoords = viewMatrix; |
+ batch->fGeoData.emplace_back( |
+ Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds}); |
+ batch->setBounds(bounds); |
+ return batch; |
} |
const char* name() const override { return "RRectEllipseRendererBatch"; } |
@@ -1405,6 +1417,8 @@ public: |
} |
private: |
+ RRectEllipseRendererBatch() : INHERITED(ClassID()) {} |
+ |
void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
// Handle overrides that affect our GP. |
overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
@@ -1524,6 +1538,15 @@ private: |
return true; |
} |
+ struct Geometry { |
+ GrColor fColor; |
+ SkScalar fXRadius; |
+ SkScalar fYRadius; |
+ SkScalar fInnerXRadius; |
+ SkScalar fInnerYRadius; |
+ SkRect fDevBounds; |
+ }; |
+ |
bool fStroked; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
@@ -1553,8 +1576,8 @@ static GrDrawBatch* create_rrect_batch(GrColor color, |
SkStrokeRec::Style style = stroke.getStyle(); |
- // do (potentially) anisotropic mapping of stroke |
- SkVector scaledStroke; |
+ // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws. |
+ SkVector scaledStroke = {-1, -1}; |
SkScalar strokeWidth = stroke.getWidth(); |
bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
@@ -1588,91 +1611,13 @@ static GrDrawBatch* create_rrect_batch(GrColor color, |
// if the corners are circles, use the circle renderer |
if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { |
- SkScalar innerRadius = 0.0f; |
- SkScalar outerRadius = xRadius; |
- SkScalar halfWidth = 0; |
- if (hasStroke) { |
- if (SkScalarNearlyZero(scaledStroke.fX)) { |
- halfWidth = SK_ScalarHalf; |
- } else { |
- halfWidth = SkScalarHalf(scaledStroke.fX); |
- } |
- |
- if (isStrokeOnly) { |
- innerRadius = xRadius - halfWidth; |
- } |
- outerRadius += halfWidth; |
- bounds.outset(halfWidth, halfWidth); |
- } |
- |
- isStrokeOnly = (isStrokeOnly && innerRadius >= 0); |
- |
- // The radii are outset for two reasons. First, it allows the shader to simply perform |
- // simpler computation because the computed alpha is zero, rather than 50%, at the radius. |
- // Second, the outer radius is used to compute the verts of the bounding box that is |
- // rendered and the outset ensures the box will cover all partially covered by the rrect |
- // corners. |
- outerRadius += SK_ScalarHalf; |
- innerRadius -= SK_ScalarHalf; |
- |
- // Expand the rect so all the pixels will be captured. |
- bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
- |
- RRectCircleRendererBatch::Geometry geometry; |
- geometry.fColor = color; |
- geometry.fInnerRadius = innerRadius; |
- geometry.fOuterRadius = outerRadius; |
- geometry.fDevBounds = bounds; |
- |
- return new RRectCircleRendererBatch(geometry, viewMatrix, isStrokeOnly); |
+ return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX, |
+ isStrokeOnly); |
// otherwise we use the ellipse renderer |
} else { |
- SkScalar innerXRadius = 0.0f; |
- SkScalar innerYRadius = 0.0f; |
- if (hasStroke) { |
- if (SkScalarNearlyZero(scaledStroke.length())) { |
- scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
- } else { |
- scaledStroke.scale(SK_ScalarHalf); |
- } |
- |
- // we only handle thick strokes for near-circular ellipses |
- if (scaledStroke.length() > SK_ScalarHalf && |
- (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { |
- return nullptr; |
- } |
- |
- // we don't handle it if curvature of the stroke is less than curvature of the ellipse |
- if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || |
- scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { |
- return nullptr; |
- } |
+ return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius, |
+ scaledStroke, isStrokeOnly); |
- // this is legit only if scale & translation (which should be the case at the moment) |
- if (isStrokeOnly) { |
- innerXRadius = xRadius - scaledStroke.fX; |
- innerYRadius = yRadius - scaledStroke.fY; |
- } |
- |
- xRadius += scaledStroke.fX; |
- yRadius += scaledStroke.fY; |
- bounds.outset(scaledStroke.fX, scaledStroke.fY); |
- } |
- |
- isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0); |
- |
- // Expand the rect so all the pixels will be captured. |
- bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
- |
- RRectEllipseRendererBatch::Geometry geometry; |
- geometry.fColor = color; |
- geometry.fXRadius = xRadius; |
- geometry.fYRadius = yRadius; |
- geometry.fInnerXRadius = innerXRadius; |
- geometry.fInnerYRadius = innerYRadius; |
- geometry.fDevBounds = bounds; |
- |
- return new RRectEllipseRendererBatch(geometry, viewMatrix, isStrokeOnly); |
} |
} |
@@ -1692,7 +1637,32 @@ GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color, |
return create_rrect_batch(color, viewMatrix, rrect, stroke); |
} |
-/////////////////////////////////////////////////////////////////////////////////////////////////// |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color, |
+ const SkMatrix& viewMatrix, |
+ const SkRect& oval, |
+ const SkStrokeRec& stroke, |
+ GrShaderCaps* shaderCaps) { |
+ // we can draw circles |
+ if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) { |
+ return new CircleBatch(color, viewMatrix, oval, stroke); |
+ } |
+ |
+ // if we have shader derivative support, render as device-independent |
+ if (shaderCaps->shaderDerivativeSupport()) { |
+ return DIEllipseBatch::Create(color, viewMatrix, oval, stroke); |
+ } |
+ |
+ // otherwise axis-aligned ellipses only |
+ if (viewMatrix.rectStaysRect()) { |
+ return EllipseBatch::Create(color, viewMatrix, oval, stroke); |
+ } |
+ |
+ return nullptr; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
#ifdef GR_TEST_UTILS |
@@ -1700,21 +1670,21 @@ DRAW_BATCH_TEST_DEFINE(CircleBatch) { |
SkMatrix viewMatrix = GrTest::TestMatrix(random); |
GrColor color = GrRandomColor(random); |
SkRect circle = GrTest::TestSquare(random); |
- return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(random)); |
+ return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(random)); |
} |
DRAW_BATCH_TEST_DEFINE(EllipseBatch) { |
SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); |
GrColor color = GrRandomColor(random); |
SkRect ellipse = GrTest::TestSquare(random); |
- return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
+ return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
} |
DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) { |
SkMatrix viewMatrix = GrTest::TestMatrix(random); |
GrColor color = GrRandomColor(random); |
SkRect ellipse = GrTest::TestSquare(random); |
- return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
+ return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
} |
DRAW_BATCH_TEST_DEFINE(RRectBatch) { |