diff options
Diffstat (limited to 'src/gpu/batches/GrShadowRRectBatch.cpp')
-rwxr-xr-x | src/gpu/batches/GrShadowRRectBatch.cpp | 966 |
1 files changed, 0 insertions, 966 deletions
diff --git a/src/gpu/batches/GrShadowRRectBatch.cpp b/src/gpu/batches/GrShadowRRectBatch.cpp deleted file mode 100755 index 35a3da7fdb..0000000000 --- a/src/gpu/batches/GrShadowRRectBatch.cpp +++ /dev/null @@ -1,966 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrShadowRRectBatch.h" - -#include "GrBatchTest.h" -#include "GrOpFlushState.h" -#include "GrResourceProvider.h" -#include "GrStyle.h" - -#include "effects/GrShadowGeoProc.h" - -/////////////////////////////////////////////////////////////////////////////// - -// We have two possible cases for geometry for a circle: - -// In the case of a normal fill, we draw geometry for the circle as an octagon. -static const uint16_t gFillCircleIndices[] = { - // enter the octagon - 0, 1, 8, 1, 2, 8, - 2, 3, 8, 3, 4, 8, - 4, 5, 8, 5, 6, 8, - 6, 7, 8, 7, 0, 8, -}; - -// For stroked circles, we use two nested octagons. -static const uint16_t gStrokeCircleIndices[] = { - // enter the octagon - 0, 1, 9, 0, 9, 8, - 1, 2, 10, 1, 10, 9, - 2, 3, 11, 2, 11, 10, - 3, 4, 12, 3, 12, 11, - 4, 5, 13, 4, 13, 12, - 5, 6, 14, 5, 14, 13, - 6, 7, 15, 6, 15, 14, - 7, 0, 8, 7, 8, 15, -}; - -static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); -static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); -static const int kVertsPerStrokeCircle = 16; -static const int kVertsPerFillCircle = 9; - -static int circle_type_to_vert_count(bool stroked) { - return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; -} - -static int circle_type_to_index_count(bool stroked) { - return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; -} - -static const uint16_t* circle_type_to_indices(bool stroked) { - return stroked ? gStrokeCircleIndices : gFillCircleIndices; -} - -/////////////////////////////////////////////////////////////////////////////// - -class ShadowCircleBatch : public GrMeshDrawOp { -public: - DEFINE_OP_CLASS_ID - - static GrDrawOp* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center, - SkScalar radius, SkScalar blurRadius, const GrStyle& style) { - SkASSERT(viewMatrix.isSimilarity()); - const SkStrokeRec& stroke = style.strokeRec(); - if (style.hasPathEffect()) { - return nullptr; - } - SkStrokeRec::Style recStyle = stroke.getStyle(); - - viewMatrix.mapPoints(¢er, 1); - radius = viewMatrix.mapRadius(radius); - SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); - - bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle || - SkStrokeRec::kHairline_Style == recStyle; - bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle; - - SkScalar innerRadius = -SK_ScalarHalf; - 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; - } - } - - // TODO: still needed? - // 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; - bool stroked = isStrokeOnly && innerRadius > 0.0f; - ShadowCircleBatch* batch = new ShadowCircleBatch(); - batch->fViewMatrixIfUsingLocalCoords = viewMatrix; - - SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, - center.fX + outerRadius, center.fY + outerRadius); - - batch->fGeoData.emplace_back(Geometry{ - color, - outerRadius, - innerRadius, - blurRadius, - devBounds, - stroked - }); - - // Use the original radius and stroke radius for the bounds so that it does not include the - // AA bloat. - radius += halfWidth; - batch->setBounds({ center.fX - radius, center.fY - radius, - center.fX + radius, center.fY + radius }, - HasAABloat::kNo, IsZeroArea::kNo); - batch->fVertCount = circle_type_to_vert_count(stroked); - batch->fIndexCount = circle_type_to_index_count(stroked); - return batch; - } - - const char* name() const override { return "ShadowCircleBatch"; } - - 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], " - "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n", - fGeoData[i].fColor, - fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop, - fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom, - fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, - fGeoData[i].fBlurRadius); - } - string.append(DumpPipelineInfo(*this->pipeline())); - string.append(INHERITED::dumpInfo()); - return string; - } - - void computePipelineOptimizations(GrInitInvariantOutput* color, - GrInitInvariantOutput* coverage, - GrBatchToXPOverrides* overrides) const override { - // When this is called on a batch, there is only one geometry bundle - color->setKnownFourComponents(fGeoData[0].fColor); - coverage->setUnknownSingleComponent(); - } - -private: - ShadowCircleBatch() : INHERITED(ClassID()) {} - void initBatchTracker(const GrXPOverridesForBatch& overrides) override { - // Handle any overrides that affect our GP. - overrides.getOverrideColorIfSet(&fGeoData[0].fColor); - if (!overrides.readsLocalCoords()) { - fViewMatrixIfUsingLocalCoords.reset(); - } - } - - void onPrepareDraws(Target* target) const override { - SkMatrix localMatrix; - if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { - return; - } - - // Setup geometry processor - sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix)); - - struct CircleVertex { - SkPoint fPos; - GrColor fColor; - SkPoint fOffset; - SkScalar fOuterRadius; - SkScalar fBlurRadius; - }; - - int instanceCount = fGeoData.count(); - size_t vertexStride = gp->getVertexStride(); - SkASSERT(vertexStride == sizeof(CircleVertex)); - - const GrBuffer* vertexBuffer; - int firstVertex; - char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, - &vertexBuffer, &firstVertex); - if (!vertices) { - SkDebugf("Could not allocate vertices\n"); - return; - } - - const GrBuffer* indexBuffer = nullptr; - int firstIndex = 0; - uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); - if (!indices) { - SkDebugf("Could not allocate indices\n"); - return; - } - - int currStartVertex = 0; - for (int i = 0; i < instanceCount; i++) { - const Geometry& geom = fGeoData[i]; - - GrColor color = geom.fColor; - SkScalar outerRadius = geom.fOuterRadius; - SkScalar innerRadius = geom.fInnerRadius; - SkScalar blurRadius = geom.fBlurRadius; - - const SkRect& bounds = geom.fDevBounds; - CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride); - CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride); - CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride); - CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride); - CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride); - CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride); - CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride); - CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride); - - // The inner radius in the vertex data must be specified in normalized space. - innerRadius = innerRadius / outerRadius; - - SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); - SkScalar halfWidth = 0.5f*bounds.width(); - SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 - - ov0->fPos = center + SkPoint::Make(-octOffset*halfWidth, -halfWidth); - ov0->fColor = color; - ov0->fOffset = SkPoint::Make(-octOffset, -1); - ov0->fOuterRadius = outerRadius; - ov0->fBlurRadius = blurRadius; - - ov1->fPos = center + SkPoint::Make(octOffset*halfWidth, -halfWidth); - ov1->fColor = color; - ov1->fOffset = SkPoint::Make(octOffset, -1); - ov1->fOuterRadius = outerRadius; - ov1->fBlurRadius = blurRadius; - - ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset*halfWidth); - ov2->fColor = color; - ov2->fOffset = SkPoint::Make(1, -octOffset); - ov2->fOuterRadius = outerRadius; - ov2->fBlurRadius = blurRadius; - - ov3->fPos = center + SkPoint::Make(halfWidth, octOffset*halfWidth); - ov3->fColor = color; - ov3->fOffset = SkPoint::Make(1, octOffset); - ov3->fOuterRadius = outerRadius; - ov3->fBlurRadius = blurRadius; - - ov4->fPos = center + SkPoint::Make(octOffset*halfWidth, halfWidth); - ov4->fColor = color; - ov4->fOffset = SkPoint::Make(octOffset, 1); - ov4->fOuterRadius = outerRadius; - ov4->fBlurRadius = blurRadius; - - ov5->fPos = center + SkPoint::Make(-octOffset*halfWidth, halfWidth); - ov5->fColor = color; - ov5->fOffset = SkPoint::Make(-octOffset, 1); - ov5->fOuterRadius = outerRadius; - ov5->fBlurRadius = blurRadius; - - ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset*halfWidth); - ov6->fColor = color; - ov6->fOffset = SkPoint::Make(-1, octOffset); - ov6->fOuterRadius = outerRadius; - ov6->fBlurRadius = blurRadius; - - ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset*halfWidth); - ov7->fColor = color; - ov7->fOffset = SkPoint::Make(-1, -octOffset); - ov7->fOuterRadius = outerRadius; - ov7->fBlurRadius = blurRadius; - - if (geom.fStroked) { - // compute the inner ring - CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); - CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride); - CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride); - CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride); - CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride); - CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride); - CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride); - CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride); - - // cosine and sine of pi/8 - SkScalar c = 0.923579533f; - SkScalar s = 0.382683432f; - SkScalar r = geom.fInnerRadius; - - iv0->fPos = center + SkPoint::Make(-s*r, -c*r); - iv0->fColor = color; - iv0->fOffset = SkPoint::Make(-s*innerRadius, -c*innerRadius); - iv0->fOuterRadius = outerRadius; - iv0->fBlurRadius = blurRadius; - - iv1->fPos = center + SkPoint::Make(s*r, -c*r); - iv1->fColor = color; - iv1->fOffset = SkPoint::Make(s*innerRadius, -c*innerRadius); - iv1->fOuterRadius = outerRadius; - iv1->fBlurRadius = blurRadius; - - iv2->fPos = center + SkPoint::Make(c*r, -s*r); - iv2->fColor = color; - iv2->fOffset = SkPoint::Make(c*innerRadius, -s*innerRadius); - iv2->fOuterRadius = outerRadius; - iv2->fBlurRadius = blurRadius; - - iv3->fPos = center + SkPoint::Make(c*r, s*r); - iv3->fColor = color; - iv3->fOffset = SkPoint::Make(c*innerRadius, s*innerRadius); - iv3->fOuterRadius = outerRadius; - iv3->fBlurRadius = blurRadius; - - iv4->fPos = center + SkPoint::Make(s*r, c*r); - iv4->fColor = color; - iv4->fOffset = SkPoint::Make(s*innerRadius, c*innerRadius); - iv4->fOuterRadius = outerRadius; - iv4->fBlurRadius = blurRadius; - - iv5->fPos = center + SkPoint::Make(-s*r, c*r); - iv5->fColor = color; - iv5->fOffset = SkPoint::Make(-s*innerRadius, c*innerRadius); - iv5->fOuterRadius = outerRadius; - iv5->fBlurRadius = blurRadius; - - iv6->fPos = center + SkPoint::Make(-c*r, s*r); - iv6->fColor = color; - iv6->fOffset = SkPoint::Make(-c*innerRadius, s*innerRadius); - iv6->fOuterRadius = outerRadius; - iv6->fBlurRadius = blurRadius; - - iv7->fPos = center + SkPoint::Make(-c*r, -s*r); - iv7->fColor = color; - iv7->fOffset = SkPoint::Make(-c*innerRadius, -s*innerRadius); - iv7->fOuterRadius = outerRadius; - iv7->fBlurRadius = blurRadius; - } else { - // filled - CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); - iv->fPos = center; - iv->fColor = color; - iv->fOffset = SkPoint::Make(0, 0); - iv->fOuterRadius = outerRadius; - iv->fBlurRadius = blurRadius; - } - - const uint16_t* primIndices = circle_type_to_indices(geom.fStroked); - const int primIndexCount = circle_type_to_index_count(geom.fStroked); - for (int i = 0; i < primIndexCount; ++i) { - *indices++ = primIndices[i] + currStartVertex; - } - - currStartVertex += circle_type_to_vert_count(geom.fStroked); - vertices += circle_type_to_vert_count(geom.fStroked)*vertexStride; - } - - GrMesh mesh; - mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex, - firstIndex, fVertCount, fIndexCount); - target->draw(gp.get(), mesh); - } - - bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { - ShadowCircleBatch* that = t->cast<ShadowCircleBatch>(); - if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), - that->bounds(), caps)) { - return false; - } - - if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { - return false; - } - - fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); - this->joinBounds(*that); - fVertCount += that->fVertCount; - fIndexCount += that->fIndexCount; - return true; - } - - struct Geometry { - GrColor fColor; - SkScalar fOuterRadius; - SkScalar fInnerRadius; - SkScalar fBlurRadius; - SkRect fDevBounds; - bool fStroked; - }; - - SkSTArray<1, Geometry, true> fGeoData; - SkMatrix fViewMatrixIfUsingLocalCoords; - int fVertCount; - int fIndexCount; - - typedef GrMeshDrawOp INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -// We have two possible cases for geometry for a shadow roundrect. -// -// In the case of a normal stroke, we draw the roundrect as a 9-patch without the center quad. -// ____________ -// |_|________|_| -// | | | | -// | | | | -// | | | | -// |_|________|_| -// |_|________|_| -// -// 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. The shared vertices -// are duplicated so we can set a different outer radius for the fill calculation. -// ____________ -// |_|________|_| -// | |\ ____ /| | -// | | | | | | -// | | |____| | | -// |_|/______\|_| -// |_|________|_| -// -// For filled rrects we reuse the overstroke geometry but make the inner rect degenerate -// (either a point or a horizontal or vertical line). - -static const uint16_t gOverstrokeRRectIndices[] = { - // corners - 0, 1, 5, 0, 5, 4, - 2, 3, 7, 2, 7, 6, - 8, 9, 13, 8, 13, 12, - 10, 11, 15, 10, 15, 14, - - // edges - 1, 2, 6, 1, 6, 5, - 4, 5, 9, 4, 9, 8, - 6, 7, 11, 6, 11, 10, - 9, 10, 14, 9, 14, 13, - - // overstroke quads - // we place this at the end so that we can skip these indices when rendering as stroked - 16, 17, 19, 16, 19, 18, - 19, 17, 23, 19, 23, 21, - 21, 23, 22, 21, 22, 20, - 22, 16, 18, 22, 18, 20, -}; -// standard stroke indices start at the same place, but will skip the overstroke "ring" -static const uint16_t* gStrokeRRectIndices = gOverstrokeRRectIndices; - -// overstroke count -static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices); -// simple stroke count skips overstroke indices -static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6; -static const int kVertsPerStrokeRRect = 16; -static const int kVertsPerOverstrokeRRect = 24; - -enum RRectType { - kFill_RRectType, - kStroke_RRectType, - kOverstroke_RRectType, -}; - -static int rrect_type_to_vert_count(RRectType type) { - static const int kTypeToVertCount[] = { - kVertsPerOverstrokeRRect, - kVertsPerStrokeRRect, - kVertsPerOverstrokeRRect, - }; - - return kTypeToVertCount[type]; -} - -static int rrect_type_to_index_count(RRectType type) { - static const int kTypeToIndexCount[] = { - kIndicesPerOverstrokeRRect, - kIndicesPerStrokeRRect, - kIndicesPerOverstrokeRRect, - }; - - return kTypeToIndexCount[type]; -} - -static const uint16_t* rrect_type_to_indices(RRectType type) { - static const uint16_t* kTypeToIndices[] = { - gOverstrokeRRectIndices, - gStrokeRRectIndices, - gOverstrokeRRectIndices, - }; - - return kTypeToIndices[type]; -} - -// For distance computations in the interior of filled rrects we: -// -// add a interior degenerate (point or line) rect -// each vertex of that rect gets -outerRad as its radius -// this makes the computation of the distance to the outer edge be negative -// negative values are caught and then handled differently in the GP's onEmitCode -// each vertex is also given the normalized x & y distance from the interior rect's edge -// the GP takes the min of those depths +1 to get the normalized distance to the outer edge - -class ShadowCircularRRectBatch final : public GrMeshDrawOp { -public: - DEFINE_OP_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. - ShadowCircularRRectBatch(GrColor color, const SkMatrix& viewMatrix, - const SkRect& devRect, float devRadius, float blurRadius, - 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; - RRectType type = kFill_RRectType; - if (devStrokeWidth > 0) { - if (SkScalarNearlyZero(devStrokeWidth)) { - halfWidth = SK_ScalarHalf; - } else { - halfWidth = SkScalarHalf(devStrokeWidth); - } - - if (strokeOnly) { - // 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; - type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; - } - } - outerRadius += halfWidth; - bounds.outset(halfWidth, halfWidth); - } - - // TODO: still needed? - // 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; - - this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); - - // Expand the rect for aa to generate correct vertices. - bounds.outset(SK_ScalarHalf, SK_ScalarHalf); - - fGeoData.emplace_back(Geometry{color, outerRadius, innerRadius, blurRadius, bounds, type}); - fVertCount = rrect_type_to_vert_count(type); - fIndexCount = rrect_type_to_index_count(type); - } - - const char* name() const override { return "ShadowCircularRRectBatch"; } - - 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]," - "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n", - fGeoData[i].fColor, - fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop, - fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom, - fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, - fGeoData[i].fBlurRadius); - } - string.append(DumpPipelineInfo(*this->pipeline())); - string.append(INHERITED::dumpInfo()); - return string; - } - - void computePipelineOptimizations(GrInitInvariantOutput* color, - GrInitInvariantOutput* coverage, - GrBatchToXPOverrides* overrides) const override { - // When this is called on a batch, there is only one geometry bundle - color->setKnownFourComponents(fGeoData[0].fColor); - coverage->setUnknownSingleComponent(); - } - -private: - void initBatchTracker(const GrXPOverridesForBatch& overrides) override { - // Handle any overrides that affect our GP. - overrides.getOverrideColorIfSet(&fGeoData[0].fColor); - if (!overrides.readsLocalCoords()) { - fViewMatrixIfUsingLocalCoords.reset(); - } - } - - struct CircleVertex { - SkPoint fPos; - GrColor fColor; - SkPoint fOffset; - SkScalar fOuterRadius; - SkScalar fBlurRadius; - }; - - static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, - SkScalar smInset, SkScalar bigInset, SkScalar xOffset, - SkScalar outerRadius, GrColor color, SkScalar blurRadius) { - SkASSERT(smInset < bigInset); - - // TL - (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(xOffset, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - // TR - (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(xOffset, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(0, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(0, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(0, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(0, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - // BL - (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(xOffset, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - - // BR - (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset); - (*verts)->fColor = color; - (*verts)->fOffset = SkPoint::Make(xOffset, 0); - (*verts)->fOuterRadius = outerRadius; - (*verts)->fBlurRadius = blurRadius; - (*verts)++; - } - - void onPrepareDraws(Target* target) const override { - // Invert the view matrix as a local matrix (if any other processors require coords). - SkMatrix localMatrix; - if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { - return; - } - - // Setup geometry processor - sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix)); - - int instanceCount = fGeoData.count(); - size_t vertexStride = gp->getVertexStride(); - SkASSERT(sizeof(CircleVertex) == vertexStride); - - const GrBuffer* vertexBuffer; - int firstVertex; - - CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount, - &vertexBuffer, &firstVertex); - if (!verts) { - SkDebugf("Could not allocate vertices\n"); - return; - } - - const GrBuffer* indexBuffer = nullptr; - int firstIndex = 0; - uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); - if (!indices) { - SkDebugf("Could not allocate indices\n"); - return; - } - - int currStartVertex = 0; - for (int i = 0; i < instanceCount; i++) { - const Geometry& args = fGeoData[i]; - - GrColor color = args.fColor; - SkScalar outerRadius = args.fOuterRadius; - - const SkRect& bounds = args.fDevBounds; - - SkScalar yCoords[4] = { - bounds.fTop, - bounds.fTop + outerRadius, - bounds.fBottom - outerRadius, - bounds.fBottom - }; - - SkScalar yOuterRadii[4] = { -1, 0, 0, 1 }; - // The inner radius in the vertex data must be specified in normalized space. - // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius. - SkScalar blurRadius = args.fBlurRadius; - for (int i = 0; i < 4; ++i) { - verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); - verts->fColor = color; - verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]); - verts->fOuterRadius = outerRadius; - verts->fBlurRadius = blurRadius; - verts++; - - verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); - verts->fColor = color; - verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); - verts->fOuterRadius = outerRadius; - verts->fBlurRadius = blurRadius; - verts++; - - verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); - verts->fColor = color; - verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); - verts->fOuterRadius = outerRadius; - verts->fBlurRadius = blurRadius; - verts++; - - verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); - verts->fColor = color; - verts->fOffset = SkPoint::Make(1, yOuterRadii[i]); - verts->fOuterRadius = outerRadius; - verts->fBlurRadius = blurRadius; - verts++; - } - // Add the additional vertices for overstroked rrects. - // Effectively this is an additional stroked rrect, with its - // outer radius = outerRadius - innerRadius, and inner radius = 0. - // This will give us correct AA in the center and the correct - // distance to the outer edge. - // - // Also, the outer offset is a constant vector pointing to the right, which - // guarantees that the distance value along the outer rectangle is constant. - if (kOverstroke_RRectType == args.fType) { - SkASSERT(args.fInnerRadius <= 0.0f); - - SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius; - // this is the normalized distance from the outer rectangle of this - // geometry to the outer edge - SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius; - - FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, - maxOffset, overstrokeOuterRadius, color, blurRadius); - } - - if (kFill_RRectType == args.fType) { - SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height()); - - SkScalar xOffset = 1.0f - outerRadius / halfMinDim; - - FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, - xOffset, halfMinDim, color, blurRadius); - } - - const uint16_t* primIndices = rrect_type_to_indices(args.fType); - const int primIndexCount = rrect_type_to_index_count(args.fType); - for (int i = 0; i < primIndexCount; ++i) { - *indices++ = primIndices[i] + currStartVertex; - } - - currStartVertex += rrect_type_to_vert_count(args.fType); - } - - GrMesh mesh; - mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex, - firstIndex, fVertCount, fIndexCount); - target->draw(gp.get(), mesh); - } - - bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { - ShadowCircularRRectBatch* that = t->cast<ShadowCircularRRectBatch>(); - if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), - that->bounds(), caps)) { - return false; - } - - if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { - return false; - } - - fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); - this->joinBounds(*that); - fVertCount += that->fVertCount; - fIndexCount += that->fIndexCount; - return true; - } - - struct Geometry { - GrColor fColor; - SkScalar fOuterRadius; - SkScalar fInnerRadius; - SkScalar fBlurRadius; - SkRect fDevBounds; - RRectType fType; - }; - - SkSTArray<1, Geometry, true> fGeoData; - SkMatrix fViewMatrixIfUsingLocalCoords; - int fVertCount; - int fIndexCount; - - typedef GrMeshDrawOp INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// - -static GrDrawOp* create_shadow_circle_batch(GrColor color, - const SkMatrix& viewMatrix, - const SkRect& oval, - SkScalar blurRadius, - const SkStrokeRec& stroke, - const GrShaderCaps* shaderCaps) { - // we can only draw circles - SkScalar width = oval.width(); - SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity()); - SkPoint center = { oval.centerX(), oval.centerY() }; - return ShadowCircleBatch::Create(color, viewMatrix, center, width / 2.f, - blurRadius, GrStyle(stroke, nullptr)); -} - -static GrDrawOp* create_shadow_rrect_batch(GrColor color, - const SkMatrix& viewMatrix, - const SkRRect& rrect, - SkScalar blurRadius, - const SkStrokeRec& stroke) { - SkASSERT(viewMatrix.rectStaysRect()); - SkASSERT(rrect.isSimple()); - SkASSERT(!rrect.isOval()); - - // Shadow rrect batchs only handle simple circular rrects - // do any matrix crunching before we reset the draw state for device coords - const SkRect& rrectBounds = rrect.getBounds(); - SkRect bounds; - viewMatrix.mapRect(&bounds, rrectBounds); - - SkVector radii = rrect.getSimpleRadii(); - SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX + - viewMatrix[SkMatrix::kMSkewY] * radii.fY); - SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX + - viewMatrix[SkMatrix::kMScaleY] * radii.fY); - SkASSERT(SkScalarNearlyEqual(xRadius, yRadius)); - - SkStrokeRec::Style style = stroke.getStyle(); - - // 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 || - SkStrokeRec::kHairline_Style == style; - bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; - - if (hasStroke) { - if (SkStrokeRec::kHairline_Style == style) { - scaledStroke.set(1, 1); - } else { - scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] + - viewMatrix[SkMatrix::kMSkewY])); - scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] + - viewMatrix[SkMatrix::kMScaleY])); - } - - // we don't handle anisotropic strokes - if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) { - return nullptr; - } - } - - // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on - // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- - // patch will have fractional coverage. This only matters when the interior is actually filled. - // We could consider falling back to rect rendering here, since a tiny radius is - // indistinguishable from a square corner. - if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { - return nullptr; - } - - return new ShadowCircularRRectBatch(color, viewMatrix, bounds, xRadius, - blurRadius, scaledStroke.fX, isStrokeOnly); -} - -GrDrawOp* CreateShadowRRectBatch(GrColor color, - const SkMatrix& viewMatrix, - const SkRRect& rrect, - const SkScalar blurRadius, - const SkStrokeRec& stroke, - const GrShaderCaps* shaderCaps) { - if (rrect.isOval()) { - return create_shadow_circle_batch(color, viewMatrix, rrect.getBounds(), - blurRadius, stroke, shaderCaps); - } - - if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { - return nullptr; - } - - return create_shadow_rrect_batch(color, viewMatrix, rrect, blurRadius, stroke); -} - -/////////////////////////////////////////////////////////////////////////////// - -#ifdef GR_TEST_UTILS - -DRAW_BATCH_TEST_DEFINE(ShadowCircleBatch) { - do { - SkScalar rotate = random->nextSScalar1() * 360.f; - SkScalar translateX = random->nextSScalar1() * 1000.f; - SkScalar translateY = random->nextSScalar1() * 1000.f; - SkScalar scale = random->nextSScalar1() * 100.f; - SkMatrix viewMatrix; - viewMatrix.setRotate(rotate); - viewMatrix.postTranslate(translateX, translateY); - viewMatrix.postScale(scale, scale); - GrColor color = GrRandomColor(random); - SkRect circle = GrTest::TestSquare(random); - SkPoint center = { circle.centerX(), circle.centerY() }; - SkScalar radius = circle.width() / 2.f; - SkStrokeRec stroke = GrTest::TestStrokeRec(random); - SkScalar blurRadius = random->nextSScalar1() * 72.f; - GrDrawOp* batch = ShadowCircleBatch::Create(color, viewMatrix, center, radius, - blurRadius, GrStyle(stroke, nullptr)); - if (batch) { - return batch; - } - } while (true); -} - -DRAW_BATCH_TEST_DEFINE(ShadowRRectBatch) { - SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); - GrColor color = GrRandomColor(random); - const SkRRect& rrect = GrTest::TestRRectSimple(random); - SkScalar blurRadius = random->nextSScalar1() * 72.f; - return create_shadow_rrect_batch(color, viewMatrix, rrect, - blurRadius, GrTest::TestStrokeRec(random)); -} - -#endif |