Chromium Code Reviews| Index: src/gpu/GrOvalRenderer.cpp |
| diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
| index f32168e945883333e6e838ed5d4fea9368ac0372..1485316b2d5a98f8d1899cd5942c9fb52690fbb3 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; |
| @@ -1403,7 +1454,8 @@ public: |
| if (strokeOnly) { |
| innerRadius = devRadius - halfWidth; |
| - fStroked = innerRadius >= 0; |
| + // TODO: if radius is really large this might just be a fill |
|
robertphillips
2016/08/25 12:55:42
I think, once GrShape is complete, the stroke->fil
jvanverth1
2016/08/25 14:02:52
Fixed until then.
|
| + fType = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; |
| } |
| outerRadius += halfWidth; |
| bounds.outset(halfWidth, halfWidth); |
| @@ -1427,6 +1479,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 +1519,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 +1537,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 +1604,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 +1658,7 @@ private: |
| return false; |
| } |
| - if (fStroked != that->fStroked) { |
| + if (fType != that->fType) { |
| return false; |
| } |
| @@ -1561,7 +1678,7 @@ private: |
| SkRect fDevBounds; |
| }; |
| - bool fStroked; |
| + RRectType fType; |
| SkMatrix fViewMatrixIfUsingLocalCoords; |
| SkSTArray<1, Geometry, true> fGeoData; |
| @@ -1666,14 +1783,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 +1927,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 +1938,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 +1957,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 |