Reland of Better encapsulate oval/rrect batchs. (patchset #1 id:1 of https://codereview.chromium.org/2121313004/ )
Reason for revert:
Workaround for compiler/stdlib bug landed: https://codereview.chromium.org/2135453002
Original issue's description:
> Revert of Better encapsulate oval/rrect batchs. (patchset #5 id:80001 of https://codereview.chromium.org/2104423003/ )
>
> Reason for revert:
> This CL is suspected to break compilation on arm64.
> https://build.chromium.org/p/chromium.android/builders/Android%20arm64%20Builder%20%28dbg%29/builds/5851/steps/compile/logs/stdio
>
> Original issue's description:
> > Better encapsulate oval/rrect batchs.
> > GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2104423003
> >
> > Committed: https://skia.googlesource.com/skia/+/5fd209e8ee477c703bc5c11b008f247d515fc0fc
> > Committed: https://skia.googlesource.com/skia/+/7f06c6947a3bef78dc57b9252779567c33604c90
> > Committed: https://skia.googlesource.com/skia/+/11bf8b2eae7d1780cb969146422a2ab3b933047a
>
> TBR=robertphillips@google.com,bsalomon@google.com
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
>
> Committed: https://skia.googlesource.com/skia/+/c79fc33d3e83664ffbd3651df3131c3e9e0b635c
TBR=robertphillips@google.com,hush@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
Review-Url: https://codereview.chromium.org/2134693002
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 3f73c6b..f5dcfcb 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -524,49 +524,58 @@
///////////////////////////////////////////////////////////////////////////////
-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
- struct Geometry {
- SkRect fDevBounds;
- SkScalar fInnerRadius;
- SkScalar fOuterRadius;
- GrColor fColor;
- };
+ 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());
- CircleBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
- : INHERITED(ClassID())
- , fStroked(stroked)
- , fViewMatrixIfUsingLocalCoords(viewMatrix) {
- fGeoData.push_back(geometry);
- this->setBounds(geometry.fDevBounds);
+ 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;
}
+
const char* name() const override { return "CircleBatch"; }
SkString dumpInfo() const override {
@@ -680,6 +689,13 @@
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 @@
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());
- struct Geometry {
- SkRect fDevBounds;
- SkScalar fXRadius;
- SkScalar fYRadius;
- SkScalar fInnerXRadius;
- SkScalar fInnerYRadius;
- GrColor fColor;
- };
+ // 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);
- EllipseBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
- : INHERITED(ClassID())
- , fStroked(stroked)
- , fViewMatrixIfUsingLocalCoords(viewMatrix) {
- fGeoData.push_back(geometry);
- this->setBounds(geometry.fDevBounds);
+ // 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;
+ }
+
+ 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 @@
}
private:
+ EllipseBatch() : INHERITED(ClassID()) {}
+
void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
// Handle any overrides that affect our GP.
if (!overrides.readsCoverage()) {
@@ -874,6 +898,14 @@
return true;
}
+ struct Geometry {
+ GrColor fColor;
+ SkScalar fXRadius;
+ SkScalar fYRadius;
+ SkScalar fInnerXRadius;
+ SkScalar fInnerYRadius;
+ SkRect fDevBounds;
+ };
bool fStroked;
SkMatrix fViewMatrixIfUsingLocalCoords;
@@ -882,110 +914,89 @@
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;
- }
-
- 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(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());
- static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
- return new DIEllipseBatch(geometry, bounds);
+ 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;
}
const char* name() const override { return "DIEllipseBatch"; }
@@ -1000,6 +1011,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
+ }
- RRectEllipseRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
- : INHERITED(ClassID())
- , fStroked(stroked)
- , fViewMatrixIfUsingLocalCoords(viewMatrix) {
- fGeoData.push_back(geometry);
- this->setBounds(geometry.fDevBounds);
+ // 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;
}
const char* name() const override { return "RRectEllipseRendererBatch"; }
@@ -1405,6 +1417,8 @@
}
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 @@
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 @@
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 @@
// 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);
- }
+ return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
+ scaledStroke, isStrokeOnly);
- // 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);
}
}
@@ -1692,7 +1637,32 @@
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 @@
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) {