Index: src/gpu/GrOvalRenderer.cpp |
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
index 37dc8311ba8cdfa2277af26499247a74eaaa10d1..cef08f431e3a4f047737d6d845c66e58b8f2cf3a 100644 |
--- a/src/gpu/GrOvalRenderer.cpp |
+++ b/src/gpu/GrOvalRenderer.cpp |
@@ -524,58 +524,49 @@ |
/////////////////////////////////////////////////////////////////////////////// |
+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); |
+ } |
+ |
+ // otherwise axis-aligned ellipses only |
+ if (viewMatrix.rectStaysRect()) { |
+ return CreateEllipseBatch(color, viewMatrix, oval, stroke); |
+ } |
+ |
+ return nullptr; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
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()); |
- 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; |
- |
- 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; |
- } |
- |
+ struct Geometry { |
+ SkRect fDevBounds; |
+ SkScalar fInnerRadius; |
+ SkScalar fOuterRadius; |
+ GrColor fColor; |
+ }; |
+ |
+ CircleBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
+ : INHERITED(ClassID()) |
+ , fStroked(stroked) |
+ , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
+ fGeoData.push_back(geometry); |
+ this->setBounds(geometry.fDevBounds); |
+ } |
const char* name() const override { return "CircleBatch"; } |
SkString dumpInfo() const override { |
@@ -689,13 +680,6 @@ |
return true; |
} |
- struct Geometry { |
- GrColor fColor; |
- SkScalar fInnerRadius; |
- SkScalar fOuterRadius; |
- SkRect fDevBounds; |
- }; |
- |
bool fStroked; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
@@ -703,89 +687,81 @@ |
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); |
- } |
- |
- // 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; |
- } |
- |
- // 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; |
- } |
- |
- // We've extended the outer x radius out half a pixel to antialias. |
- // This will also expand the rect so all the pixels will be captured. |
- // TODO: Consider if we should use sqrt(2)/2 instead |
- xRadius += SK_ScalarHalf; |
- yRadius += SK_ScalarHalf; |
- |
- 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) |
- }); |
- batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0; |
- batch->fViewMatrixIfUsingLocalCoords = viewMatrix; |
- batch->setBounds(batch->fGeoData.back().fDevBounds); |
- return batch; |
+ |
+ struct Geometry { |
+ SkRect fDevBounds; |
+ SkScalar fXRadius; |
+ SkScalar fYRadius; |
+ SkScalar fInnerXRadius; |
+ SkScalar fInnerYRadius; |
+ GrColor fColor; |
+ }; |
+ |
+ EllipseBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
+ : INHERITED(ClassID()) |
+ , fStroked(stroked) |
+ , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
+ fGeoData.push_back(geometry); |
+ this->setBounds(geometry.fDevBounds); |
} |
const char* name() const override { return "EllipseBatch"; } |
@@ -799,8 +775,6 @@ |
} |
private: |
- EllipseBatch() : INHERITED(ClassID()) {} |
- |
void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
// Handle any overrides that affect our GP. |
if (!overrides.readsCoverage()) { |
@@ -896,105 +870,121 @@ |
return true; |
} |
+ |
+ bool fStroked; |
+ SkMatrix fViewMatrixIfUsingLocalCoords; |
+ SkSTArray<1, Geometry, true> fGeoData; |
+ |
+ 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])); |
+ |
+ 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); |
+ } |
+ |
+ // 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; |
+ } |
+ |
+ // 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; |
+ } |
+ |
+ // We've extended the outer x radius out half a pixel to antialias. |
+ // This will also expand the rect so all the pixels will be captured. |
+ // TODO: Consider if we should use sqrt(2)/2 instead |
+ xRadius += SK_ScalarHalf; |
+ yRadius += SK_ScalarHalf; |
+ |
+ 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); |
+ |
+ 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 { |
- GrColor fColor; |
+ SkMatrix fViewMatrix; |
+ SkRect fBounds; |
SkScalar fXRadius; |
SkScalar fYRadius; |
SkScalar fInnerXRadius; |
SkScalar fInnerYRadius; |
- SkRect fDevBounds; |
+ SkScalar fGeoDx; |
+ SkScalar fGeoDy; |
+ GrColor fColor; |
+ DIEllipseStyle fStyle; |
}; |
- bool fStroked; |
- SkMatrix fViewMatrixIfUsingLocalCoords; |
- SkSTArray<1, Geometry, true> fGeoData; |
- |
- typedef GrVertexBatch INHERITED; |
-}; |
- |
-///////////////////////////////////////////////////////////////////////////////////////////////// |
- |
-class DIEllipseBatch : public GrVertexBatch { |
-public: |
- DEFINE_BATCH_CLASS_ID |
- |
- 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; |
- } |
- |
- // 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* 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; |
+ static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) { |
+ return new DIEllipseBatch(geometry, bounds); |
} |
const char* name() const override { return "DIEllipseBatch"; } |
@@ -1008,8 +998,6 @@ |
} |
private: |
- |
- DIEllipseBatch() : INHERITED(ClassID()) {} |
void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
// Handle any overrides that affect our GP. |
@@ -1073,6 +1061,12 @@ |
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(), |
@@ -1097,24 +1091,95 @@ |
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); |
+} |
/////////////////////////////////////////////////////////////////////////////// |
@@ -1165,46 +1230,20 @@ |
public: |
DEFINE_BATCH_CLASS_ID |
- // 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); |
- } |
- |
- if (strokeOnly) { |
- innerRadius = devRadius - halfWidth; |
- fStroked = innerRadius >= 0; |
- } |
- outerRadius += halfWidth; |
- bounds.outset(halfWidth, 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 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); |
+ struct Geometry { |
+ SkRect fDevBounds; |
+ SkScalar fInnerRadius; |
+ SkScalar fOuterRadius; |
+ GrColor fColor; |
+ }; |
+ |
+ RRectCircleRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
+ : INHERITED(ClassID()) |
+ , fStroked(stroked) |
+ , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
+ fGeoData.push_back(geometry); |
+ |
+ this->setBounds(geometry.fDevBounds); |
} |
const char* name() const override { return "RRectCircleBatch"; } |
@@ -1326,13 +1365,6 @@ |
return true; |
} |
- struct Geometry { |
- GrColor fColor; |
- SkScalar fInnerRadius; |
- SkScalar fOuterRadius; |
- SkRect fDevBounds; |
- }; |
- |
bool fStroked; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
@@ -1344,64 +1376,21 @@ |
public: |
DEFINE_BATCH_CLASS_ID |
- // 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; |
- } |
- |
- // 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; |
+ struct Geometry { |
+ SkRect fDevBounds; |
+ SkScalar fXRadius; |
+ SkScalar fYRadius; |
+ SkScalar fInnerXRadius; |
+ SkScalar fInnerYRadius; |
+ GrColor fColor; |
+ }; |
+ |
+ RRectEllipseRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked) |
+ : INHERITED(ClassID()) |
+ , fStroked(stroked) |
+ , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
+ fGeoData.push_back(geometry); |
+ this->setBounds(geometry.fDevBounds); |
} |
const char* name() const override { return "RRectEllipseRendererBatch"; } |
@@ -1415,8 +1404,6 @@ |
} |
private: |
- RRectEllipseRendererBatch() : INHERITED(ClassID()) {} |
- |
void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
// Handle overrides that affect our GP. |
overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
@@ -1536,15 +1523,6 @@ |
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; |
@@ -1574,8 +1552,8 @@ |
SkStrokeRec::Style style = stroke.getStyle(); |
- // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws. |
- SkVector scaledStroke = {-1, -1}; |
+ // do (potentially) anisotropic mapping of stroke |
+ SkVector scaledStroke; |
SkScalar strokeWidth = stroke.getWidth(); |
bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
@@ -1609,13 +1587,91 @@ |
// if the corners are circles, use the circle renderer |
if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { |
- return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX, |
- isStrokeOnly); |
+ 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); |
// otherwise we use the ellipse renderer |
} else { |
- return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius, |
- scaledStroke, isStrokeOnly); |
- |
+ 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; |
+ } |
+ |
+ // 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); |
} |
} |
@@ -1635,32 +1691,7 @@ |
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 |
@@ -1668,21 +1699,21 @@ |
SkMatrix viewMatrix = GrTest::TestMatrix(random); |
GrColor color = GrRandomColor(random); |
SkRect circle = GrTest::TestSquare(random); |
- return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(random)); |
+ return create_circle_batch(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 EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
+ return create_ellipse_batch(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 DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
+ return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)); |
} |
DRAW_BATCH_TEST_DEFINE(RRectBatch) { |