| /* | |
| * 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 "GrShadowRRectOp.h" | |
| #include "GrDrawOpTest.h" | |
| #include "GrMemoryPool.h" | |
| #include "GrOpFlushState.h" | |
| #include "GrRecordingContext.h" | |
| #include "GrRecordingContextPriv.h" | |
| #include "SkRRectPriv.h" | |
| #include "effects/GrShadowGeoProc.h" | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // Circle Data | |
| // | |
| // 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 | |
| // clang-format off | |
| 0, 1, 8, 1, 2, 8, | |
| 2, 3, 8, 3, 4, 8, | |
| 4, 5, 8, 5, 6, 8, | |
| 6, 7, 8, 7, 0, 8, | |
| // clang-format on | |
| }; | |
| // For stroked circles, we use two nested octagons. | |
| static const uint16_t gStrokeCircleIndices[] = { | |
| // enter the octagon | |
| // clang-format off | |
| 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, | |
| // clang-format on | |
| }; | |
| 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; | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // RoundRect Data | |
| // | |
| // The geometry for a shadow roundrect is similar to a 9-patch: | |
| // ____________ | |
| // |_|________|_| | |
| // | | | | | |
| // | | | | | |
| // | | | | | |
| // |_|________|_| | |
| // |_|________|_| | |
| // | |
| // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram | |
| // shows the upper part of the upper left corner. The bottom triangle would similarly be split | |
| // into two triangles.) | |
| // ________ | |
| // |\ \ | | |
| // | \ \ | | |
| // | \\ | | |
| // | \| | |
| // -------- | |
| // | |
| // The center of the fan handles the curve of the corner. For roundrects where the stroke width | |
| // is greater than the corner radius, the outer triangles blend from the curve to the straight | |
| // sides. Otherwise these triangles will be degenerate. | |
| // | |
| // In the case where the stroke width is greater than the corner radius and the | |
| // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center. | |
| // This rectangle extends the coverage values of the center edges of the 9-patch. | |
| // ____________ | |
| // |_|________|_| | |
| // | |\ ____ /| | | |
| // | | | | | | | |
| // | | |____| | | | |
| // |_|/______\|_| | |
| // |_|________|_| | |
| // | |
| // For filled rrects we reuse the stroke geometry but add an additional quad to the center. | |
| static const uint16_t gRRectIndices[] = { | |
| // clang-format off | |
| // overstroke quads | |
| // we place this at the beginning so that we can skip these indices when rendering as filled | |
| 0, 6, 25, 0, 25, 24, | |
| 6, 18, 27, 6, 27, 25, | |
| 18, 12, 26, 18, 26, 27, | |
| 12, 0, 24, 12, 24, 26, | |
| // corners | |
| 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, | |
| 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7, | |
| 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13, | |
| 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23, | |
| // edges | |
| 0, 5, 11, 0, 11, 6, | |
| 6, 7, 19, 6, 19, 18, | |
| 18, 23, 17, 18, 17, 12, | |
| 12, 13, 1, 12, 1, 0, | |
| // fill quad | |
| // we place this at the end so that we can skip these indices when rendering as stroked | |
| 0, 6, 18, 0, 18, 12, | |
| // clang-format on | |
| }; | |
| // overstroke count | |
| static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; | |
| // simple stroke count skips overstroke indices | |
| static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4; | |
| // fill count adds final quad to stroke count | |
| static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6; | |
| static const int kVertsPerStrokeRRect = 24; | |
| static const int kVertsPerOverstrokeRRect = 28; | |
| static const int kVertsPerFillRRect = 24; | |
| enum RRectType { | |
| kFill_RRectType, | |
| kStroke_RRectType, | |
| kOverstroke_RRectType, | |
| }; | |
| static int rrect_type_to_vert_count(RRectType type) { | |
| switch (type) { | |
| case kFill_RRectType: | |
| return kVertsPerFillRRect; | |
| case kStroke_RRectType: | |
| return kVertsPerStrokeRRect; | |
| case kOverstroke_RRectType: | |
| return kVertsPerOverstrokeRRect; | |
| } | |
| SK_ABORT("Invalid type"); | |
| return 0; | |
| } | |
| static int rrect_type_to_index_count(RRectType type) { | |
| switch (type) { | |
| case kFill_RRectType: | |
| return kIndicesPerFillRRect; | |
| case kStroke_RRectType: | |
| return kIndicesPerStrokeRRect; | |
| case kOverstroke_RRectType: | |
| return kIndicesPerOverstrokeRRect; | |
| } | |
| SK_ABORT("Invalid type"); | |
| return 0; | |
| } | |
| static const uint16_t* rrect_type_to_indices(RRectType type) { | |
| switch (type) { | |
| case kFill_RRectType: | |
| case kStroke_RRectType: | |
| return gRRectIndices + 6*4; | |
| case kOverstroke_RRectType: | |
| return gRRectIndices; | |
| } | |
| SK_ABORT("Invalid type"); | |
| return nullptr; | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| namespace { | |
| class ShadowCircularRRectOp final : public GrMeshDrawOp { | |
| public: | |
| DEFINE_OP_CLASS_ID | |
| // An insetWidth > 1/2 rect width or height indicates a simple fill. | |
| ShadowCircularRRectOp(GrColor color, const SkRect& devRect, | |
| float devRadius, bool isCircle, float blurRadius, float insetWidth) | |
| : INHERITED(ClassID()) { | |
| SkRect bounds = devRect; | |
| SkASSERT(insetWidth > 0); | |
| SkScalar innerRadius = 0.0f; | |
| SkScalar outerRadius = devRadius; | |
| SkScalar umbraInset; | |
| RRectType type = kFill_RRectType; | |
| if (isCircle) { | |
| umbraInset = 0; | |
| } else { | |
| umbraInset = SkTMax(outerRadius, blurRadius); | |
| } | |
| // If stroke is greater than width or height, this is still a fill, | |
| // otherwise we compute stroke params. | |
| if (isCircle) { | |
| innerRadius = devRadius - insetWidth; | |
| type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType; | |
| } else { | |
| if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) { | |
| // We don't worry about a real inner radius, we just need to know if we | |
| // need to create overstroke vertices. | |
| innerRadius = SkTMax(insetWidth - umbraInset, 0.0f); | |
| type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType; | |
| } | |
| } | |
| this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); | |
| fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius, | |
| blurRadius, bounds, type, isCircle}); | |
| if (isCircle) { | |
| fVertCount = circle_type_to_vert_count(kStroke_RRectType == type); | |
| fIndexCount = circle_type_to_index_count(kStroke_RRectType == type); | |
| } else { | |
| fVertCount = rrect_type_to_vert_count(type); | |
| fIndexCount = rrect_type_to_index_count(type); | |
| } | |
| } | |
| const char* name() const override { return "ShadowCircularRRectOp"; } | |
| #ifdef SK_DEBUG | |
| 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, Umbra: %.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].fUmbraInset, | |
| fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius); | |
| } | |
| string.append(INHERITED::dumpInfo()); | |
| return string; | |
| } | |
| #endif | |
| FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } | |
| GrProcessorSet::Analysis finalize( | |
| const GrCaps&, const GrAppliedClip*, GrFSAAType, GrClampType) override { | |
| return GrProcessorSet::EmptySetAnalysis(); | |
| } | |
| private: | |
| struct Geometry { | |
| GrColor fColor; | |
| SkScalar fOuterRadius; | |
| SkScalar fUmbraInset; | |
| SkScalar fInnerRadius; | |
| SkScalar fBlurRadius; | |
| SkRect fDevBounds; | |
| RRectType fType; | |
| bool fIsCircle; | |
| }; | |
| struct CircleVertex { | |
| SkPoint fPos; | |
| GrColor fColor; | |
| SkPoint fOffset; | |
| SkScalar fDistanceCorrection; | |
| }; | |
| void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const { | |
| GrColor color = args.fColor; | |
| SkScalar outerRadius = args.fOuterRadius; | |
| SkScalar innerRadius = args.fInnerRadius; | |
| SkScalar blurRadius = args.fBlurRadius; | |
| SkScalar distanceCorrection = outerRadius / blurRadius; | |
| const SkRect& bounds = args.fDevBounds; | |
| // 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 | |
| (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-octOffset, -1); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(octOffset, -1); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(1, -octOffset); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(1, octOffset); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(octOffset, 1); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-octOffset, 1); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-1, octOffset); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-1, -octOffset); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| if (isStroked) { | |
| // compute the inner ring | |
| // cosine and sine of pi/8 | |
| SkScalar c = 0.923579533f; | |
| SkScalar s = 0.382683432f; | |
| SkScalar r = args.fInnerRadius; | |
| (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(s * r, -c * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(c * r, -s * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(c * r, s * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(s * r, c * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(-s * r, c * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(-c * r, s * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| } else { | |
| // filled | |
| (*verts)->fPos = center; | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(0, 0); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| } | |
| } | |
| void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const { | |
| GrColor color = args.fColor; | |
| SkScalar outerRadius = args.fOuterRadius; | |
| const SkRect& bounds = args.fDevBounds; | |
| SkScalar umbraInset = args.fUmbraInset; | |
| SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height()); | |
| if (umbraInset > minDim) { | |
| umbraInset = minDim; | |
| } | |
| SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset, | |
| bounds.fLeft + umbraInset, bounds.fRight - umbraInset }; | |
| SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius, | |
| bounds.fLeft + outerRadius, bounds.fRight - outerRadius }; | |
| SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight, | |
| bounds.fLeft, bounds.fRight }; | |
| SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset, | |
| bounds.fBottom - umbraInset, bounds.fBottom - umbraInset }; | |
| SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius, | |
| bounds.fBottom - outerRadius, bounds.fBottom - outerRadius }; | |
| SkScalar yOuter[4] = { bounds.fTop, bounds.fTop, | |
| bounds.fBottom, bounds.fBottom }; | |
| SkScalar blurRadius = args.fBlurRadius; | |
| // In the case where we have to inset more for the umbra, our two triangles in the | |
| // corner get skewed to a diamond rather than a square. To correct for that, | |
| // we also skew the vectors we send to the shader that help define the circle. | |
| // By doing so, we end up with a quarter circle in the corner rather than the | |
| // elliptical curve. | |
| // This is a bit magical, but it gives us the correct results at extrema: | |
| // a) umbraInset == outerRadius produces an orthogonal vector | |
| // b) outerRadius == 0 produces a diagonal vector | |
| // And visually the corner looks correct. | |
| SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset); | |
| outerVec.normalize(); | |
| // We want the circle edge to fall fractionally along the diagonal at | |
| // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset | |
| // | |
| // Setting the components of the diagonal offset to the following value will give us that. | |
| SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius); | |
| SkVector diagVec = SkVector::Make(diagVal, diagVal); | |
| SkScalar distanceCorrection = umbraInset / blurRadius; | |
| // build corner by corner | |
| for (int i = 0; i < 4; ++i) { | |
| // inner point | |
| (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkVector::Make(0, 0); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| // outer points | |
| (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkVector::Make(0, -1); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = outerVec; | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = diagVec; | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = outerVec; | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkVector::Make(0, -1); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| } | |
| // Add the additional vertices for overstroked rrects. | |
| // Effectively this is an additional stroked rrect, with its | |
| // parameters equal to those in the center of the 9-patch. This will | |
| // give constant values across this inner ring. | |
| if (kOverstroke_RRectType == args.fType) { | |
| SkASSERT(args.fInnerRadius > 0.0f); | |
| SkScalar inset = umbraInset + args.fInnerRadius; | |
| // TL | |
| (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(0, 0); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| // TR | |
| (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(0, 0); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| // BL | |
| (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(0, 0); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| // BR | |
| (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset); | |
| (*verts)->fColor = color; | |
| (*verts)->fOffset = SkPoint::Make(0, 0); | |
| (*verts)->fDistanceCorrection = distanceCorrection; | |
| (*verts)++; | |
| } | |
| } | |
| void onPrepareDraws(Target* target) override { | |
| // Setup geometry processor | |
| sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make(); | |
| int instanceCount = fGeoData.count(); | |
| SkASSERT(sizeof(CircleVertex) == gp->vertexStride()); | |
| sk_sp<const GrBuffer> vertexBuffer; | |
| int firstVertex; | |
| CircleVertex* verts = (CircleVertex*)target->makeVertexSpace( | |
| sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex); | |
| if (!verts) { | |
| SkDebugf("Could not allocate vertices\n"); | |
| return; | |
| } | |
| sk_sp<const GrBuffer> indexBuffer; | |
| 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]; | |
| if (args.fIsCircle) { | |
| bool isStroked = SkToBool(kStroke_RRectType == args.fType); | |
| this->fillInCircleVerts(args, isStroked, &verts); | |
| const uint16_t* primIndices = circle_type_to_indices(isStroked); | |
| const int primIndexCount = circle_type_to_index_count(isStroked); | |
| for (int i = 0; i < primIndexCount; ++i) { | |
| *indices++ = primIndices[i] + currStartVertex; | |
| } | |
| currStartVertex += circle_type_to_vert_count(isStroked); | |
| } else { | |
| this->fillInRRectVerts(args, &verts); | |
| 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 = target->allocMesh(GrPrimitiveType::kTriangles); | |
| mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1, | |
| GrPrimitiveRestart::kNo); | |
| mesh->setVertexData(std::move(vertexBuffer), firstVertex); | |
| target->recordDraw(std::move(gp), mesh); | |
| } | |
| void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { | |
| flushState->executeDrawsAndUploadsForMeshDrawOp( | |
| this, chainBounds, GrProcessorSet::MakeEmptySet()); | |
| } | |
| CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override { | |
| ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>(); | |
| fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); | |
| fVertCount += that->fVertCount; | |
| fIndexCount += that->fIndexCount; | |
| return CombineResult::kMerged; | |
| } | |
| SkSTArray<1, Geometry, true> fGeoData; | |
| int fVertCount; | |
| int fIndexCount; | |
| typedef GrMeshDrawOp INHERITED; | |
| }; | |
| } // anonymous namespace | |
| /////////////////////////////////////////////////////////////////////////////// | |
| namespace GrShadowRRectOp { | |
| std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context, | |
| GrColor color, | |
| const SkMatrix& viewMatrix, | |
| const SkRRect& rrect, | |
| SkScalar blurWidth, | |
| SkScalar insetWidth) { | |
| // Shadow rrect ops only handle simple circular rrects. | |
| SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect)); | |
| // Do any matrix crunching before we reset the draw state for device coords. | |
| const SkRect& rrectBounds = rrect.getBounds(); | |
| SkRect bounds; | |
| viewMatrix.mapRect(&bounds, rrectBounds); | |
| // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic. | |
| SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX; | |
| SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]; | |
| SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor); | |
| SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor); | |
| GrOpMemoryPool* pool = context->priv().opMemoryPool(); | |
| return pool->allocate<ShadowCircularRRectOp>(color, bounds, | |
| scaledRadius, | |
| rrect.isOval(), | |
| blurWidth, | |
| scaledInsetWidth); | |
| } | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| #if GR_TEST_UTILS | |
| GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) { | |
| // create a similarity matrix | |
| SkScalar rotate = random->nextSScalar1() * 360.f; | |
| SkScalar translateX = random->nextSScalar1() * 1000.f; | |
| SkScalar translateY = random->nextSScalar1() * 1000.f; | |
| SkScalar scale; | |
| do { | |
| scale = random->nextSScalar1() * 100.f; | |
| } while (scale == 0); | |
| SkMatrix viewMatrix; | |
| viewMatrix.setRotate(rotate); | |
| viewMatrix.postTranslate(translateX, translateY); | |
| viewMatrix.postScale(scale, scale); | |
| SkScalar insetWidth = random->nextSScalar1() * 72.f; | |
| SkScalar blurWidth = random->nextSScalar1() * 72.f; | |
| bool isCircle = random->nextBool(); | |
| // This op doesn't use a full GrPaint, just a color. | |
| GrColor color = paint.getColor4f().toBytes_RGBA(); | |
| if (isCircle) { | |
| SkRect circle = GrTest::TestSquare(random); | |
| SkRRect rrect = SkRRect::MakeOval(circle); | |
| return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth); | |
| } else { | |
| SkRRect rrect; | |
| do { | |
| // This may return a rrect with elliptical corners, which we don't support. | |
| rrect = GrTest::TestRRectSimple(random); | |
| } while (!SkRRectPriv::IsSimpleCircular(rrect)); | |
| return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth); | |
| } | |
| } | |
| #endif |