diff options
author | jvanverth <jvanverth@google.com> | 2016-08-25 08:12:35 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-08-25 08:12:35 -0700 |
commit | c3d0e422761702c52a974411eaf0b06d223fe98e (patch) | |
tree | a6a698b9e037c7294c779fa9734e0531d06f33e1 /src | |
parent | 48b2461ee0e18f05d6c47b54564130d84fa0dbf3 (diff) |
Add Ganesh support for circular roundrects with strokes > 2*radii.
Overstroked rrects are supported by adding additional geometry to the
center of the nine patch, and setting constant offset vectors to fake
out the distance calculation.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2272703004
Review-Url: https://codereview.chromium.org/2272703004
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/GrOvalRenderer.cpp | 192 |
1 files changed, 160 insertions, 32 deletions
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index f32168e945..fe35869e2f 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 |