Index: src/gpu/GrOvalRenderer.cpp |
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
index f32168e945883333e6e838ed5d4fea9368ac0372..fe35869e2fdd86c75702458899100bac83260d63 100644 |
--- a/src/gpu/GrOvalRenderer.cpp |
+++ b/src/gpu/GrOvalRenderer.cpp |
@@ -1335,7 +1335,40 @@ private: |
/////////////////////////////////////////////////////////////////////////////// |
-static const uint16_t gRRectIndices[] = { |
+// We have three possible cases for geometry for a roundrect. |
+// |
+// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch: |
+// ____________ |
+// |_|________|_| |
+// | | | | |
+// | | | | |
+// | | | | |
+// |_|________|_| |
+// |_|________|_| |
+// |
+// For strokes, we don't draw the center quad. |
+// |
+// For circular roundrects, in the case where the stroke width is greater than twice |
+// the corner radius (overstroke), we add additional geometry to mark out the rectangle |
+// in the center: |
+// ____________ |
+// |_|________|_| |
+// | |\ ____ /| | |
+// | | | | | | |
+// | | |____| | | |
+// |_|/______\|_| |
+// |_|________|_| |
+// |
+// We don't draw the center quad from the fill rect in this case. |
+ |
+static const uint16_t gRRectOverstrokeIndices[] = { |
+ // overstroke quads |
+ // we place this at the beginning so that we can skip these indices when rendering normally |
+ 5, 6, 17, 5, 17, 16, |
+ 17, 6, 10, 17, 10, 19, |
+ 10, 9, 18, 10, 18, 19, |
+ 18, 9, 5, 18, 5, 16, |
+ |
// corners |
0, 1, 5, 0, 5, 4, |
2, 3, 7, 2, 7, 6, |
@@ -1349,31 +1382,49 @@ static const uint16_t gRRectIndices[] = { |
9, 10, 14, 9, 14, 13, |
// center |
- // we place this at the end so that we can ignore these indices when rendering stroke-only |
- 5, 6, 10, 5, 10, 9 |
+ // we place this at the end so that we can ignore these indices when not rendering as filled |
+ 5, 6, 10, 5, 10, 9, |
}; |
-static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; |
-static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices); |
-static const int kVertsPerRRect = 16; |
+static const uint16_t* gRRectIndices = gRRectOverstrokeIndices + 6*4; |
+ |
+// overstroke count is arraysize |
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectOverstrokeIndices) - 6; |
+static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6; |
+static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6; |
+static const int kVertsPerStandardRRect = 16; |
+static const int kVertsPerOverstrokeRRect = 20; |
static const int kNumRRectsInIndexBuffer = 256; |
+enum RRectType { |
+ kFill_RRectType, |
+ kStroke_RRectType, |
+ kOverstroke_RRectType |
+}; |
+ |
GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); |
GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); |
-static const GrBuffer* ref_rrect_index_buffer(bool strokeOnly, |
+GR_DECLARE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey); |
+static const GrBuffer* ref_rrect_index_buffer(RRectType type, |
GrResourceProvider* resourceProvider) { |
GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); |
GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); |
- if (strokeOnly) { |
- return resourceProvider->findOrCreateInstancedIndexBuffer( |
- gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect, |
- gStrokeRRectOnlyIndexBufferKey); |
- } else { |
- return resourceProvider->findOrCreateInstancedIndexBuffer( |
- gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect, |
- gRRectOnlyIndexBufferKey); |
- |
- } |
+ GR_DEFINE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey); |
+ switch (type) { |
+ case kFill_RRectType: |
+ default: |
+ return resourceProvider->findOrCreateInstancedIndexBuffer( |
+ gRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer, |
+ kVertsPerStandardRRect, gRRectOnlyIndexBufferKey); |
+ case kStroke_RRectType: |
+ return resourceProvider->findOrCreateInstancedIndexBuffer( |
+ gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, |
+ kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey); |
+ case kOverstroke_RRectType: |
+ return resourceProvider->findOrCreateInstancedIndexBuffer( |
+ gRRectOverstrokeIndices, kIndicesPerOverstrokeRRect, kNumRRectsInIndexBuffer, |
+ kVertsPerOverstrokeRRect, gOverstrokeRRectOnlyIndexBufferKey); |
+ }; |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
@@ -1393,7 +1444,7 @@ public: |
SkScalar innerRadius = 0.0f; |
SkScalar outerRadius = devRadius; |
SkScalar halfWidth = 0; |
- fStroked = false; |
+ fType = kFill_RRectType; |
if (devStrokeWidth > 0) { |
if (SkScalarNearlyZero(devStrokeWidth)) { |
halfWidth = SK_ScalarHalf; |
@@ -1402,8 +1453,15 @@ public: |
} |
if (strokeOnly) { |
- innerRadius = devRadius - halfWidth; |
- fStroked = innerRadius >= 0; |
+ // Outset stroke by 1/4 pixel |
+ devStrokeWidth += 0.25f; |
+ // If stroke is greater than width or height, this is still a fill |
+ // Otherwise we compute stroke params |
+ if (devStrokeWidth <= devRect.width() && |
+ devStrokeWidth <= devRect.height()) { |
+ innerRadius = devRadius - halfWidth; |
+ fType = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; |
+ } |
} |
outerRadius += halfWidth; |
bounds.outset(halfWidth, halfWidth); |
@@ -1427,6 +1485,21 @@ public: |
const char* name() const override { return "RRectCircleBatch"; } |
+ SkString dumpInfo() const override { |
+ SkString string; |
+ for (int i = 0; i < fGeoData.count(); ++i) { |
+ string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," |
+ "InnerRad: %.2f, OuterRad: %.2f\n", |
+ fGeoData[i].fColor, |
+ fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop, |
+ fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom, |
+ fGeoData[i].fInnerRadius, |
+ fGeoData[i].fOuterRadius); |
+ } |
+ string.append(INHERITED::dumpInfo()); |
+ return string; |
+ } |
+ |
void computePipelineOptimizations(GrInitInvariantOutput* color, |
GrInitInvariantOutput* coverage, |
GrBatchToXPOverrides* overrides) const override { |
@@ -1452,7 +1525,8 @@ private: |
} |
// Setup geometry processor |
- SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, false, false, |
+ SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kStroke_RRectType == fType, |
+ false, false, |
false, localMatrix)); |
struct CircleVertex { |
@@ -1469,13 +1543,20 @@ private: |
SkASSERT(vertexStride == sizeof(CircleVertex)); |
// drop out the middle quad if we're stroked |
- int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect; |
+ int indicesPerInstance = kIndicesPerFillRRect; |
+ if (kStroke_RRectType == fType) { |
+ indicesPerInstance = kIndicesPerStrokeRRect; |
+ } else if (kOverstroke_RRectType == fType) { |
+ indicesPerInstance = kIndicesPerOverstrokeRRect; |
+ } |
SkAutoTUnref<const GrBuffer> indexBuffer( |
- ref_rrect_index_buffer(fStroked, target->resourceProvider())); |
+ ref_rrect_index_buffer(fType, target->resourceProvider())); |
InstancedHelper helper; |
+ int vertexCount = (kOverstroke_RRectType == fType) ? kVertsPerOverstrokeRRect |
+ : kVertsPerStandardRRect; |
CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, |
- kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect, |
+ kTriangles_GrPrimitiveType, vertexStride, indexBuffer, vertexCount, |
indicesPerInstance, instanceCount)); |
if (!verts || !indexBuffer) { |
SkDebugf("Could not allocate vertices\n"); |
@@ -1529,6 +1610,48 @@ private: |
verts->fInnerRadius = innerRadius; |
verts++; |
} |
+ // Add the additional vertices for overstroked rrects. |
+ // |
+ // Note that args.fInnerRadius is negative in this case. |
+ // Also, the offset is a constant vector pointing to the right, which guarantees |
+ // that the distance value along the inner rectangle is constant, which |
+ // is what we want to get nice anti-aliasing. |
+ if (kOverstroke_RRectType == fType) { |
+ // outerRadius = originalOuter + 0.5, and innerRadius = originalInner - 0.5. |
+ // What we want is originalOuter - originalInner + 0.5, so we subtract 0.5. |
+ SkScalar inset = outerRadius - args.fInnerRadius - SK_ScalarHalf; |
+ verts->fPos = SkPoint::Make(bounds.fLeft + inset, |
+ bounds.fTop + inset); |
+ verts->fColor = color; |
+ verts->fOffset = SkPoint::Make(1, 0); |
+ verts->fOuterRadius = outerRadius; |
+ verts->fInnerRadius = innerRadius; |
+ verts++; |
+ |
+ verts->fPos = SkPoint::Make(bounds.fRight - inset, |
+ bounds.fTop + inset); |
+ verts->fColor = color; |
+ verts->fOffset = SkPoint::Make(1, 0); |
+ verts->fOuterRadius = outerRadius; |
+ verts->fInnerRadius = innerRadius; |
+ verts++; |
+ |
+ verts->fPos = SkPoint::Make(bounds.fLeft + inset, |
+ bounds.fBottom - inset); |
+ verts->fColor = color; |
+ verts->fOffset = SkPoint::Make(1, 0); |
+ verts->fOuterRadius = outerRadius; |
+ verts->fInnerRadius = innerRadius; |
+ verts++; |
+ |
+ verts->fPos = SkPoint::Make(bounds.fRight - inset, |
+ bounds.fBottom - inset); |
+ verts->fColor = color; |
+ verts->fOffset = SkPoint::Make(1, 0); |
+ verts->fOuterRadius = outerRadius; |
+ verts->fInnerRadius = innerRadius; |
+ verts++; |
+ } |
} |
helper.recordDraw(target, gp); |
@@ -1541,7 +1664,7 @@ private: |
return false; |
} |
- if (fStroked != that->fStroked) { |
+ if (fType != that->fType) { |
return false; |
} |
@@ -1561,7 +1684,7 @@ private: |
SkRect fDevBounds; |
}; |
- bool fStroked; |
+ RRectType fType; |
SkMatrix fViewMatrixIfUsingLocalCoords; |
SkSTArray<1, Geometry, true> fGeoData; |
@@ -1666,14 +1789,15 @@ private: |
SkASSERT(vertexStride == sizeof(EllipseVertex)); |
// drop out the middle quad if we're stroked |
- int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect; |
+ int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect; |
SkAutoTUnref<const GrBuffer> indexBuffer( |
- ref_rrect_index_buffer(fStroked, target->resourceProvider())); |
+ ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType, |
+ target->resourceProvider())); |
InstancedHelper helper; |
EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( |
helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer, |
- kVertsPerRRect, indicesPerInstance, instanceCount)); |
+ kVertsPerStandardRRect, indicesPerInstance, instanceCount)); |
if (!verts || !indexBuffer) { |
SkDebugf("Could not allocate vertices\n"); |
return; |
@@ -1809,6 +1933,7 @@ static GrDrawBatch* create_rrect_batch(GrColor color, |
SkStrokeRec::kHairline_Style == style; |
bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
+ bool isCircular = (xRadius == yRadius); |
if (hasStroke) { |
if (SkStrokeRec::kHairline_Style == style) { |
scaledStroke.set(1, 1); |
@@ -1819,8 +1944,11 @@ static GrDrawBatch* create_rrect_batch(GrColor color, |
viewMatrix[SkMatrix::kMScaleY])); |
} |
- // if half of strokewidth is greater than radius, we don't handle that right now |
- if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) { |
+ isCircular = isCircular && scaledStroke.fX == scaledStroke.fY; |
+ // for non-circular rrects, if half of strokewidth is greater than radius, |
+ // we don't handle that right now |
+ if (!isCircular && |
+ (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) { |
return nullptr; |
} |
} |
@@ -1835,7 +1963,7 @@ static GrDrawBatch* create_rrect_batch(GrColor color, |
} |
// if the corners are circles, use the circle renderer |
- if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { |
+ if (isCircular) { |
return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX, |
isStrokeOnly); |
// otherwise we use the ellipse renderer |