aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/batches/GrShadowRRectBatch.cpp
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2016-12-14 21:07:01 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2016-12-15 14:40:59 +0000
commitfc527d27641bb693a0a7703ba9d35100f7500fd7 (patch)
tree5cb9eb7c5fd6304ca7f98cfe9301b4c36f2fa5f6 /src/gpu/batches/GrShadowRRectBatch.cpp
parentebccb82680fc13cc92d9051e8520de6b460df074 (diff)
Even more batch->op and sk_sp'ification.
Change-Id: I9930381465ebad690206e2251171004f9579fbcd Reviewed-on: https://skia-review.googlesource.com/6100 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
Diffstat (limited to 'src/gpu/batches/GrShadowRRectBatch.cpp')
-rwxr-xr-xsrc/gpu/batches/GrShadowRRectBatch.cpp966
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(&center, 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