LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/ops - GrAAStrokeRectOp.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 242 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 26 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015 Google Inc.
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : 
       8             : #include "GrAAStrokeRectOp.h"
       9             : 
      10             : #include "GrDefaultGeoProcFactory.h"
      11             : #include "GrOpFlushState.h"
      12             : #include "GrResourceKey.h"
      13             : #include "GrResourceProvider.h"
      14             : #include "SkStrokeRec.h"
      15             : 
      16             : GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
      17             : GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
      18             : 
      19           0 : static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
      20           0 :     pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
      21           0 : }
      22             : 
      23             : // We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
      24             : // limit makes a miter join effectively beveled.
      25           0 : inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
      26           0 :     SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
      27             :              stroke.getStyle() == SkStrokeRec::kHairline_Style);
      28             :     // For hairlines, make bevel and round joins appear the same as mitered ones.
      29           0 :     if (!stroke.getWidth()) {
      30           0 :         *isMiter = true;
      31           0 :         return true;
      32             :     }
      33           0 :     if (stroke.getJoin() == SkPaint::kBevel_Join) {
      34           0 :         *isMiter = false;
      35           0 :         return true;
      36             :     }
      37           0 :     if (stroke.getJoin() == SkPaint::kMiter_Join) {
      38           0 :         *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
      39           0 :         return true;
      40             :     }
      41           0 :     return false;
      42             : }
      43             : 
      44           0 : static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
      45             :                           bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
      46             :                           SkScalar strokeWidth, bool miterStroke) {
      47             :     SkRect devRect;
      48           0 :     viewMatrix.mapRect(&devRect, rect);
      49             : 
      50             :     SkVector devStrokeSize;
      51           0 :     if (strokeWidth > 0) {
      52           0 :         devStrokeSize.set(strokeWidth, strokeWidth);
      53           0 :         viewMatrix.mapVectors(&devStrokeSize, 1);
      54           0 :         devStrokeSize.setAbs(devStrokeSize);
      55             :     } else {
      56           0 :         devStrokeSize.set(SK_Scalar1, SK_Scalar1);
      57             :     }
      58             : 
      59           0 :     const SkScalar dx = devStrokeSize.fX;
      60           0 :     const SkScalar dy = devStrokeSize.fY;
      61           0 :     const SkScalar rx = SkScalarHalf(dx);
      62           0 :     const SkScalar ry = SkScalarHalf(dy);
      63             : 
      64           0 :     *devOutside = devRect;
      65           0 :     *devOutsideAssist = devRect;
      66           0 :     *devInside = devRect;
      67             : 
      68           0 :     devOutside->outset(rx, ry);
      69           0 :     devInside->inset(rx, ry);
      70             : 
      71             :     // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
      72             :     // make a degenerate inside rect to avoid double hitting.  We will also jam all of the points
      73             :     // together when we render these rects.
      74             :     SkScalar spare;
      75             :     {
      76           0 :         SkScalar w = devRect.width() - dx;
      77           0 :         SkScalar h = devRect.height() - dy;
      78           0 :         spare = SkTMin(w, h);
      79             :     }
      80             : 
      81           0 :     *isDegenerate = spare <= 0;
      82           0 :     if (*isDegenerate) {
      83           0 :         devInside->fLeft = devInside->fRight = devRect.centerX();
      84           0 :         devInside->fTop = devInside->fBottom = devRect.centerY();
      85             :     }
      86             : 
      87             :     // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
      88             :     // to draw the outside of the octagon. Because there are 8 vertices on the outer
      89             :     // edge, while vertex number of inner edge is 4, the same as miter-stroke.
      90           0 :     if (!miterStroke) {
      91           0 :         devOutside->inset(0, ry);
      92           0 :         devOutsideAssist->outset(0, ry);
      93             :     }
      94           0 : }
      95             : 
      96           0 : static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
      97             :                                                         const SkMatrix& viewMatrix,
      98             :                                                         bool usesLocalCoords) {
      99             :     using namespace GrDefaultGeoProcFactory;
     100             : 
     101             :     Coverage::Type coverageType;
     102           0 :     if (tweakAlphaForCoverage) {
     103           0 :         coverageType = Coverage::kSolid_Type;
     104             :     } else {
     105           0 :         coverageType = Coverage::kAttribute_Type;
     106             :     }
     107             :     LocalCoords::Type localCoordsType =
     108           0 :             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
     109             :     return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
     110           0 :                               viewMatrix);
     111             : }
     112             : 
     113           0 : class AAStrokeRectOp final : public GrLegacyMeshDrawOp {
     114             : public:
     115           0 :     DEFINE_OP_CLASS_ID
     116             : 
     117           0 :     AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
     118             :                    const SkRect& devInside)
     119           0 :             : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
     120           0 :         SkASSERT(!devOutside.isEmpty());
     121           0 :         SkASSERT(!devInside.isEmpty());
     122             : 
     123           0 :         fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
     124           0 :         this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
     125           0 :         fMiterStroke = true;
     126           0 :     }
     127             : 
     128           0 :     static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
     129             :                                                     const SkRect& rect, const SkStrokeRec& stroke) {
     130             :         bool isMiter;
     131           0 :         if (!allowed_stroke(stroke, &isMiter)) {
     132           0 :             return nullptr;
     133             :         }
     134             : 
     135           0 :         AAStrokeRectOp* op = new AAStrokeRectOp();
     136           0 :         op->fMiterStroke = isMiter;
     137           0 :         RectInfo& info = op->fRects.push_back();
     138           0 :         compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
     139           0 :                       &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
     140           0 :         info.fColor = color;
     141           0 :         if (isMiter) {
     142           0 :             op->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
     143             :         } else {
     144             :             // The outer polygon of the bevel stroke is an octagon specified by the points of a
     145             :             // pair of overlapping rectangles where one is wide and the other is narrow.
     146           0 :             SkRect bounds = info.fDevOutside;
     147           0 :             bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
     148           0 :             op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
     149             :         }
     150           0 :         op->fViewMatrix = viewMatrix;
     151           0 :         return std::unique_ptr<GrLegacyMeshDrawOp>(op);
     152             :     }
     153             : 
     154           0 :     const char* name() const override { return "AAStrokeRect"; }
     155             : 
     156           0 :     SkString dumpInfo() const override {
     157           0 :         SkString string;
     158           0 :         for (const auto& info : fRects) {
     159           0 :             string.appendf(
     160             :                     "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
     161             :                     "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
     162             :                     "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
     163           0 :                     info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop,
     164           0 :                     info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
     165           0 :                     info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
     166           0 :                     info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
     167           0 :                     info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
     168             :         }
     169           0 :         string.append(DumpPipelineInfo(*this->pipeline()));
     170           0 :         string.append(INHERITED::dumpInfo());
     171           0 :         return string;
     172             :     }
     173             : 
     174             : private:
     175           0 :     AAStrokeRectOp() : INHERITED(ClassID()) {}
     176             : 
     177           0 :     void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
     178             :                                     GrProcessorAnalysisCoverage* coverage) const override {
     179           0 :         color->setToConstant(fRects[0].fColor);
     180           0 :         *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     181           0 :     }
     182             :     void applyPipelineOptimizations(const PipelineOptimizations&) override;
     183             :     void onPrepareDraws(Target*) const override;
     184             : 
     185             :     static const int kMiterIndexCnt = 3 * 24;
     186             :     static const int kMiterVertexCnt = 16;
     187             :     static const int kNumMiterRectsInIndexBuffer = 256;
     188             : 
     189             :     static const int kBevelIndexCnt = 48 + 36 + 24;
     190             :     static const int kBevelVertexCnt = 24;
     191             :     static const int kNumBevelRectsInIndexBuffer = 256;
     192             : 
     193             :     static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
     194             : 
     195           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     196           0 :     bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
     197           0 :     const SkMatrix& viewMatrix() const { return fViewMatrix; }
     198           0 :     bool miterStroke() const { return fMiterStroke; }
     199             : 
     200             :     bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
     201             : 
     202             :     void generateAAStrokeRectGeometry(void* vertices,
     203             :                                       size_t offset,
     204             :                                       size_t vertexStride,
     205             :                                       int outerVertexNum,
     206             :                                       int innerVertexNum,
     207             :                                       GrColor color,
     208             :                                       const SkRect& devOutside,
     209             :                                       const SkRect& devOutsideAssist,
     210             :                                       const SkRect& devInside,
     211             :                                       bool miterStroke,
     212             :                                       bool degenerate,
     213             :                                       bool tweakAlphaForCoverage) const;
     214             : 
     215             :     // TODO support AA rotated stroke rects by copying around view matrices
     216             :     struct RectInfo {
     217             :         GrColor fColor;
     218             :         SkRect fDevOutside;
     219             :         SkRect fDevOutsideAssist;
     220             :         SkRect fDevInside;
     221             :         bool fDegenerate;
     222             :     };
     223             : 
     224             :     SkSTArray<1, RectInfo, true> fRects;
     225             :     bool fUsesLocalCoords;
     226             :     bool fCanTweakAlphaForCoverage;
     227             :     SkMatrix fViewMatrix;
     228             :     bool fMiterStroke;
     229             : 
     230             :     typedef GrLegacyMeshDrawOp INHERITED;
     231             : };
     232             : 
     233           0 : void AAStrokeRectOp::applyPipelineOptimizations(const PipelineOptimizations& optimizations) {
     234           0 :     optimizations.getOverrideColorIfSet(&fRects[0].fColor);
     235             : 
     236           0 :     fUsesLocalCoords = optimizations.readsLocalCoords();
     237           0 :     fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
     238           0 :     fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
     239           0 : }
     240             : 
     241           0 : void AAStrokeRectOp::onPrepareDraws(Target* target) const {
     242           0 :     bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
     243             : 
     244             :     sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
     245             :                                                         this->viewMatrix(),
     246           0 :                                                         this->usesLocalCoords()));
     247           0 :     if (!gp) {
     248           0 :         SkDebugf("Couldn't create GrGeometryProcessor\n");
     249           0 :         return;
     250             :     }
     251             : 
     252           0 :     size_t vertexStride = gp->getVertexStride();
     253             : 
     254           0 :     SkASSERT(canTweakAlphaForCoverage
     255             :                      ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
     256             :                      : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
     257           0 :     int innerVertexNum = 4;
     258           0 :     int outerVertexNum = this->miterStroke() ? 4 : 8;
     259           0 :     int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
     260           0 :     int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
     261           0 :     int instanceCount = fRects.count();
     262             : 
     263             :     const sk_sp<const GrBuffer> indexBuffer(
     264           0 :             GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
     265           0 :     InstancedHelper helper;
     266             :     void* vertices =
     267           0 :             helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
     268           0 :                         verticesPerInstance, indicesPerInstance, instanceCount);
     269           0 :     if (!vertices || !indexBuffer) {
     270           0 :         SkDebugf("Could not allocate vertices\n");
     271           0 :         return;
     272             :     }
     273             : 
     274           0 :     for (int i = 0; i < instanceCount; i++) {
     275           0 :         const RectInfo& info = fRects[i];
     276           0 :         this->generateAAStrokeRectGeometry(vertices,
     277           0 :                                            i * verticesPerInstance * vertexStride,
     278             :                                            vertexStride,
     279             :                                            outerVertexNum,
     280             :                                            innerVertexNum,
     281           0 :                                            info.fColor,
     282             :                                            info.fDevOutside,
     283             :                                            info.fDevOutsideAssist,
     284             :                                            info.fDevInside,
     285           0 :                                            fMiterStroke,
     286           0 :                                            info.fDegenerate,
     287           0 :                                            canTweakAlphaForCoverage);
     288             :     }
     289           0 :     helper.recordDraw(target, gp.get(), this->pipeline());
     290             : }
     291             : 
     292           0 : const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
     293             :                                                bool miterStroke) {
     294           0 :     if (miterStroke) {
     295             :         // clang-format off
     296             :         static const uint16_t gMiterIndices[] = {
     297             :             0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
     298             :             1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
     299             :             2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
     300             :             3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
     301             : 
     302             :             0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
     303             :             1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
     304             :             2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
     305             :             3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
     306             : 
     307             :             0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
     308             :             1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
     309             :             2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
     310             :             3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
     311             :         };
     312             :         // clang-format on
     313             :         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
     314           0 :         GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
     315           0 :         return resourceProvider->findOrCreateInstancedIndexBuffer(
     316             :                 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
     317           0 :                 gMiterIndexBufferKey);
     318             :     } else {
     319             :         /**
     320             :          * As in miter-stroke, index = a + b, and a is the current index, b is the shift
     321             :          * from the first index. The index layout:
     322             :          * outer AA line: 0~3, 4~7
     323             :          * outer edge:    8~11, 12~15
     324             :          * inner edge:    16~19
     325             :          * inner AA line: 20~23
     326             :          * Following comes a bevel-stroke rect and its indices:
     327             :          *
     328             :          *           4                                 7
     329             :          *            *********************************
     330             :          *          *   ______________________________  *
     331             :          *         *  / 12                          15 \  *
     332             :          *        *  /                                  \  *
     333             :          *     0 *  |8     16_____________________19  11 |  * 3
     334             :          *       *  |       |                    |       |  *
     335             :          *       *  |       |  ****************  |       |  *
     336             :          *       *  |       |  * 20        23 *  |       |  *
     337             :          *       *  |       |  *              *  |       |  *
     338             :          *       *  |       |  * 21        22 *  |       |  *
     339             :          *       *  |       |  ****************  |       |  *
     340             :          *       *  |       |____________________|       |  *
     341             :          *     1 *  |9    17                      18   10|  * 2
     342             :          *        *  \                                  /  *
     343             :          *         *  \13 __________________________14/  *
     344             :          *          *                                   *
     345             :          *           **********************************
     346             :          *          5                                  6
     347             :          */
     348             :         // clang-format off
     349             :         static const uint16_t gBevelIndices[] = {
     350             :             // Draw outer AA, from outer AA line to outer edge, shift is 0.
     351             :             0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
     352             :             1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
     353             :             5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
     354             :             6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
     355             :             2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
     356             :             3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
     357             :             7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
     358             :             4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
     359             : 
     360             :             // Draw the stroke, from outer edge to inner edge, shift is 8.
     361             :             0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
     362             :             1 + 8, 5 + 8, 9 + 8,
     363             :             5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
     364             :             6 + 8, 2 + 8, 10 + 8,
     365             :             2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
     366             :             3 + 8, 7 + 8, 11 + 8,
     367             :             7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
     368             :             4 + 8, 0 + 8, 8 + 8,
     369             : 
     370             :             // Draw the inner AA, from inner edge to inner AA line, shift is 16.
     371             :             0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
     372             :             1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
     373             :             2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
     374             :             3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
     375             :         };
     376             :         // clang-format on
     377             :         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
     378             : 
     379           0 :         GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
     380           0 :         return resourceProvider->findOrCreateInstancedIndexBuffer(
     381             :                 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
     382           0 :                 gBevelIndexBufferKey);
     383             :     }
     384             : }
     385             : 
     386           0 : bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
     387           0 :     AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
     388             : 
     389           0 :     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
     390             :                                 that->bounds(), caps)) {
     391           0 :         return false;
     392             :     }
     393             : 
     394             :     // TODO combine across miterstroke changes
     395           0 :     if (this->miterStroke() != that->miterStroke()) {
     396           0 :         return false;
     397             :     }
     398             : 
     399             :     // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
     400             :     // local coords then we won't be able to combine.  We could actually upload the viewmatrix
     401             :     // using vertex attributes in these cases, but haven't investigated that
     402           0 :     if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
     403           0 :         return false;
     404             :     }
     405             : 
     406             :     // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
     407             :     // tweaking.
     408           0 :     if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
     409           0 :         fCanTweakAlphaForCoverage = false;
     410             :     }
     411             : 
     412           0 :     fRects.push_back_n(that->fRects.count(), that->fRects.begin());
     413           0 :     this->joinBounds(*that);
     414           0 :     return true;
     415             : }
     416             : 
     417           0 : static void setup_scale(int* scale, SkScalar inset) {
     418           0 :     if (inset < SK_ScalarHalf) {
     419           0 :         *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
     420           0 :         SkASSERT(*scale >= 0 && *scale <= 255);
     421             :     } else {
     422           0 :         *scale = 0xff;
     423             :     }
     424           0 : }
     425             : 
     426           0 : void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
     427             :                                                   size_t offset,
     428             :                                                   size_t vertexStride,
     429             :                                                   int outerVertexNum,
     430             :                                                   int innerVertexNum,
     431             :                                                   GrColor color,
     432             :                                                   const SkRect& devOutside,
     433             :                                                   const SkRect& devOutsideAssist,
     434             :                                                   const SkRect& devInside,
     435             :                                                   bool miterStroke,
     436             :                                                   bool degenerate,
     437             :                                                   bool tweakAlphaForCoverage) const {
     438           0 :     intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
     439             : 
     440             :     // We create vertices for four nested rectangles. There are two ramps from 0 to full
     441             :     // coverage, one on the exterior of the stroke and the other on the interior.
     442             :     // The following pointers refer to the four rects, from outermost to innermost.
     443           0 :     SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
     444           0 :     SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
     445           0 :     SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
     446             :     SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
     447           0 :             verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
     448             : 
     449             : #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
     450             :     // TODO: this only really works if the X & Y margins are the same all around
     451             :     // the rect (or if they are all >= 1.0).
     452             :     SkScalar inset;
     453           0 :     if (!degenerate) {
     454           0 :         inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
     455           0 :         inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
     456           0 :         inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
     457           0 :         if (miterStroke) {
     458           0 :             inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
     459             :         } else {
     460           0 :             inset = SK_ScalarHalf *
     461           0 :                     SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
     462             :         }
     463           0 :         SkASSERT(inset >= 0);
     464             :     } else {
     465             :         // TODO use real devRect here
     466           0 :         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
     467           0 :         inset = SK_ScalarHalf *
     468           0 :                 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
     469             :     }
     470             : #else
     471             :     SkScalar inset;
     472             :     if (!degenerate) {
     473             :         inset = SK_ScalarHalf;
     474             :     } else {
     475             :         // TODO use real devRect here
     476             :         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
     477             :         inset = SK_ScalarHalf *
     478             :                 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
     479             :     }
     480             : #endif
     481             : 
     482           0 :     if (miterStroke) {
     483             :         // outermost
     484           0 :         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
     485             :         // inner two
     486           0 :         set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
     487           0 :         if (!degenerate) {
     488           0 :             set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
     489             :             // innermost
     490           0 :             set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
     491             :         } else {
     492             :             // When the interior rect has become degenerate we smoosh to a single point
     493           0 :             SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
     494           0 :             fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
     495           0 :                                 devInside.fBottom, vertexStride);
     496           0 :             fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
     497           0 :                                 devInside.fBottom, vertexStride);
     498             :         }
     499             :     } else {
     500           0 :         SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
     501             :         SkPoint* fan1AssistPos =
     502           0 :                 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
     503             :         // outermost
     504           0 :         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
     505             :         set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
     506           0 :                       -SK_ScalarHalf);
     507             :         // outer one of the inner two
     508           0 :         set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
     509           0 :         set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
     510           0 :         if (!degenerate) {
     511             :             // inner one of the inner two
     512           0 :             set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
     513             :             // innermost
     514           0 :             set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
     515             :         } else {
     516             :             // When the interior rect has become degenerate we smoosh to a single point
     517           0 :             SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
     518           0 :             fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
     519           0 :                                 devInside.fBottom, vertexStride);
     520           0 :             fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
     521           0 :                                 devInside.fBottom, vertexStride);
     522             :         }
     523             :     }
     524             : 
     525             :     // Make verts point to vertex color and then set all the color and coverage vertex attrs
     526             :     // values. The outermost rect has 0 coverage
     527           0 :     verts += sizeof(SkPoint);
     528           0 :     for (int i = 0; i < outerVertexNum; ++i) {
     529           0 :         if (tweakAlphaForCoverage) {
     530           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
     531             :         } else {
     532           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
     533           0 :             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
     534             :         }
     535             :     }
     536             : 
     537             :     // scale is the coverage for the the inner two rects.
     538             :     int scale;
     539           0 :     setup_scale(&scale, inset);
     540             : 
     541           0 :     float innerCoverage = GrNormalizeByteToFloat(scale);
     542           0 :     GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
     543             : 
     544           0 :     verts += outerVertexNum * vertexStride;
     545           0 :     for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
     546           0 :         if (tweakAlphaForCoverage) {
     547           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
     548             :         } else {
     549           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
     550           0 :             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
     551             :         }
     552             :     }
     553             : 
     554             :     // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
     555             :     // scaled coverage
     556           0 :     verts += (outerVertexNum + innerVertexNum) * vertexStride;
     557           0 :     if (!degenerate) {
     558           0 :         innerCoverage = 0;
     559           0 :         scaledColor = 0;
     560             :     }
     561             : 
     562           0 :     for (int i = 0; i < innerVertexNum; ++i) {
     563           0 :         if (tweakAlphaForCoverage) {
     564           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
     565             :         } else {
     566           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
     567           0 :             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
     568             :         }
     569             :     }
     570           0 : }
     571             : 
     572             : namespace GrAAStrokeRectOp {
     573             : 
     574           0 : std::unique_ptr<GrLegacyMeshDrawOp> MakeFillBetweenRects(GrColor color,
     575             :                                                          const SkMatrix& viewMatrix,
     576             :                                                          const SkRect& devOutside,
     577             :                                                          const SkRect& devInside) {
     578             :     return std::unique_ptr<GrLegacyMeshDrawOp>(
     579           0 :             new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
     580             : }
     581             : 
     582           0 : std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
     583             :                                          const SkMatrix& viewMatrix,
     584             :                                          const SkRect& rect,
     585             :                                          const SkStrokeRec& stroke) {
     586           0 :     return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
     587             : }
     588             : }
     589             : 
     590             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     591             : 
     592             : #if GR_TEST_UTILS
     593             : 
     594             : #include "GrDrawOpTest.h"
     595             : 
     596           0 : DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
     597           0 :     bool miterStroke = random->nextBool();
     598             : 
     599             :     // Create either a empty rect or a non-empty rect.
     600             :     SkRect rect =
     601           0 :             random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
     602           0 :     SkScalar minDim = SkMinScalar(rect.width(), rect.height());
     603           0 :     SkScalar strokeWidth = random->nextUScalar1() * minDim;
     604             : 
     605           0 :     GrColor color = GrRandomColor(random);
     606             : 
     607           0 :     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
     608           0 :     rec.setStrokeStyle(strokeWidth);
     609           0 :     rec.setStrokeParams(SkPaint::kButt_Cap,
     610           0 :                         miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
     611           0 :     SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
     612           0 :     return GrAAStrokeRectOp::Make(color, matrix, rect, rec);
     613             : }
     614             : 
     615             : #endif

Generated by: LCOV version 1.13