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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2012 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 "GrAAConvexPathRenderer.h"
       9             : 
      10             : #include "GrAAConvexTessellator.h"
      11             : #include "GrCaps.h"
      12             : #include "GrContext.h"
      13             : #include "GrDefaultGeoProcFactory.h"
      14             : #include "GrDrawOpTest.h"
      15             : #include "GrGeometryProcessor.h"
      16             : #include "GrOpFlushState.h"
      17             : #include "GrPathUtils.h"
      18             : #include "GrPipelineBuilder.h"
      19             : #include "GrProcessor.h"
      20             : #include "SkGeometry.h"
      21             : #include "SkPathPriv.h"
      22             : #include "SkString.h"
      23             : #include "SkTraceEvent.h"
      24             : #include "glsl/GrGLSLFragmentShaderBuilder.h"
      25             : #include "glsl/GrGLSLGeometryProcessor.h"
      26             : #include "glsl/GrGLSLProgramDataManager.h"
      27             : #include "glsl/GrGLSLUniformHandler.h"
      28             : #include "glsl/GrGLSLVarying.h"
      29             : #include "glsl/GrGLSLVertexShaderBuilder.h"
      30             : #include "ops/GrMeshDrawOp.h"
      31             : 
      32           0 : GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
      33           0 : }
      34             : 
      35             : struct Segment {
      36             :     enum {
      37             :         // These enum values are assumed in member functions below.
      38             :         kLine = 0,
      39             :         kQuad = 1,
      40             :     } fType;
      41             : 
      42             :     // line uses one pt, quad uses 2 pts
      43             :     SkPoint fPts[2];
      44             :     // normal to edge ending at each pt
      45             :     SkVector fNorms[2];
      46             :     // is the corner where the previous segment meets this segment
      47             :     // sharp. If so, fMid is a normalized bisector facing outward.
      48             :     SkVector fMid;
      49             : 
      50           0 :     int countPoints() {
      51             :         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
      52           0 :         return fType + 1;
      53             :     }
      54           0 :     const SkPoint& endPt() const {
      55             :         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
      56           0 :         return fPts[fType];
      57             :     }
      58           0 :     const SkPoint& endNorm() const {
      59             :         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
      60           0 :         return fNorms[fType];
      61             :     }
      62             : };
      63             : 
      64             : typedef SkTArray<Segment, true> SegmentArray;
      65             : 
      66           0 : static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
      67           0 :     SkScalar area = 0;
      68           0 :     SkPoint center = {0, 0};
      69           0 :     int count = segments.count();
      70           0 :     SkPoint p0 = {0, 0};
      71           0 :     if (count > 2) {
      72             :         // We translate the polygon so that the first point is at the origin.
      73             :         // This avoids some precision issues with small area polygons far away
      74             :         // from the origin.
      75           0 :         p0 = segments[0].endPt();
      76             :         SkPoint pi;
      77             :         SkPoint pj;
      78             :         // the first and last iteration of the below loop would compute
      79             :         // zeros since the starting / ending point is (0,0). So instead we start
      80             :         // at i=1 and make the last iteration i=count-2.
      81           0 :         pj = segments[1].endPt() - p0;
      82           0 :         for (int i = 1; i < count - 1; ++i) {
      83           0 :             pi = pj;
      84           0 :             pj = segments[i + 1].endPt() - p0;
      85             : 
      86           0 :             SkScalar t = SkPoint::CrossProduct(pi, pj);
      87           0 :             area += t;
      88           0 :             center.fX += (pi.fX + pj.fX) * t;
      89           0 :             center.fY += (pi.fY + pj.fY) * t;
      90             :         }
      91             :     }
      92             : 
      93             :     // If the poly has no area then we instead return the average of
      94             :     // its points.
      95           0 :     if (SkScalarNearlyZero(area)) {
      96             :         SkPoint avg;
      97           0 :         avg.set(0, 0);
      98           0 :         for (int i = 0; i < count; ++i) {
      99           0 :             const SkPoint& pt = segments[i].endPt();
     100           0 :             avg.fX += pt.fX;
     101           0 :             avg.fY += pt.fY;
     102             :         }
     103           0 :         SkScalar denom = SK_Scalar1 / count;
     104           0 :         avg.scale(denom);
     105           0 :         *c = avg;
     106             :     } else {
     107           0 :         area *= 3;
     108           0 :         area = SkScalarInvert(area);
     109           0 :         center.scale(area);
     110             :         // undo the translate of p0 to the origin.
     111           0 :         *c = center + p0;
     112             :     }
     113           0 :     SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
     114           0 : }
     115             : 
     116           0 : static void compute_vectors(SegmentArray* segments,
     117             :                             SkPoint* fanPt,
     118             :                             SkPathPriv::FirstDirection dir,
     119             :                             int* vCount,
     120             :                             int* iCount) {
     121           0 :     center_of_mass(*segments, fanPt);
     122           0 :     int count = segments->count();
     123             : 
     124             :     // Make the normals point towards the outside
     125             :     SkPoint::Side normSide;
     126           0 :     if (dir == SkPathPriv::kCCW_FirstDirection) {
     127           0 :         normSide = SkPoint::kRight_Side;
     128             :     } else {
     129           0 :         normSide = SkPoint::kLeft_Side;
     130             :     }
     131             : 
     132           0 :     *vCount = 0;
     133           0 :     *iCount = 0;
     134             :     // compute normals at all points
     135           0 :     for (int a = 0; a < count; ++a) {
     136           0 :         Segment& sega = (*segments)[a];
     137           0 :         int b = (a + 1) % count;
     138           0 :         Segment& segb = (*segments)[b];
     139             : 
     140           0 :         const SkPoint* prevPt = &sega.endPt();
     141           0 :         int n = segb.countPoints();
     142           0 :         for (int p = 0; p < n; ++p) {
     143           0 :             segb.fNorms[p] = segb.fPts[p] - *prevPt;
     144           0 :             segb.fNorms[p].normalize();
     145           0 :             segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
     146           0 :             prevPt = &segb.fPts[p];
     147             :         }
     148           0 :         if (Segment::kLine == segb.fType) {
     149           0 :             *vCount += 5;
     150           0 :             *iCount += 9;
     151             :         } else {
     152           0 :             *vCount += 6;
     153           0 :             *iCount += 12;
     154             :         }
     155             :     }
     156             : 
     157             :     // compute mid-vectors where segments meet. TODO: Detect shallow corners
     158             :     // and leave out the wedges and close gaps by stitching segments together.
     159           0 :     for (int a = 0; a < count; ++a) {
     160           0 :         const Segment& sega = (*segments)[a];
     161           0 :         int b = (a + 1) % count;
     162           0 :         Segment& segb = (*segments)[b];
     163           0 :         segb.fMid = segb.fNorms[0] + sega.endNorm();
     164           0 :         segb.fMid.normalize();
     165             :         // corner wedges
     166           0 :         *vCount += 4;
     167           0 :         *iCount += 6;
     168             :     }
     169           0 : }
     170             : 
     171             : struct DegenerateTestData {
     172           0 :     DegenerateTestData() { fStage = kInitial; }
     173           0 :     bool isDegenerate() const { return kNonDegenerate != fStage; }
     174             :     enum {
     175             :         kInitial,
     176             :         kPoint,
     177             :         kLine,
     178             :         kNonDegenerate
     179             :     }           fStage;
     180             :     SkPoint     fFirstPoint;
     181             :     SkVector    fLineNormal;
     182             :     SkScalar    fLineC;
     183             : };
     184             : 
     185             : static const SkScalar kClose = (SK_Scalar1 / 16);
     186             : static const SkScalar kCloseSqd = kClose * kClose;
     187             : 
     188           0 : static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
     189           0 :     switch (data->fStage) {
     190             :         case DegenerateTestData::kInitial:
     191           0 :             data->fFirstPoint = pt;
     192           0 :             data->fStage = DegenerateTestData::kPoint;
     193           0 :             break;
     194             :         case DegenerateTestData::kPoint:
     195           0 :             if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
     196           0 :                 data->fLineNormal = pt - data->fFirstPoint;
     197           0 :                 data->fLineNormal.normalize();
     198           0 :                 data->fLineNormal.setOrthog(data->fLineNormal);
     199           0 :                 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
     200           0 :                 data->fStage = DegenerateTestData::kLine;
     201             :             }
     202           0 :             break;
     203             :         case DegenerateTestData::kLine:
     204           0 :             if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
     205           0 :                 data->fStage = DegenerateTestData::kNonDegenerate;
     206             :             }
     207             :         case DegenerateTestData::kNonDegenerate:
     208           0 :             break;
     209             :         default:
     210           0 :             SkFAIL("Unexpected degenerate test stage.");
     211             :     }
     212           0 : }
     213             : 
     214           0 : static inline bool get_direction(const SkPath& path, const SkMatrix& m,
     215             :                                  SkPathPriv::FirstDirection* dir) {
     216           0 :     if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
     217           0 :         return false;
     218             :     }
     219             :     // check whether m reverses the orientation
     220           0 :     SkASSERT(!m.hasPerspective());
     221           0 :     SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
     222           0 :                       m.get(SkMatrix::kMSkewX)  * m.get(SkMatrix::kMSkewY);
     223           0 :     if (det2x2 < 0) {
     224           0 :         *dir = SkPathPriv::OppositeFirstDirection(*dir);
     225             :     }
     226           0 :     return true;
     227             : }
     228             : 
     229           0 : static inline void add_line_to_segment(const SkPoint& pt,
     230             :                                        SegmentArray* segments) {
     231           0 :     segments->push_back();
     232           0 :     segments->back().fType = Segment::kLine;
     233           0 :     segments->back().fPts[0] = pt;
     234           0 : }
     235             : 
     236           0 : static inline void add_quad_segment(const SkPoint pts[3],
     237             :                                     SegmentArray* segments) {
     238           0 :     if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
     239           0 :         if (pts[0] != pts[2]) {
     240           0 :             add_line_to_segment(pts[2], segments);
     241             :         }
     242             :     } else {
     243           0 :         segments->push_back();
     244           0 :         segments->back().fType = Segment::kQuad;
     245           0 :         segments->back().fPts[0] = pts[1];
     246           0 :         segments->back().fPts[1] = pts[2];
     247             :     }
     248           0 : }
     249             : 
     250           0 : static inline void add_cubic_segments(const SkPoint pts[4],
     251             :                                       SkPathPriv::FirstDirection dir,
     252             :                                       SegmentArray* segments) {
     253           0 :     SkSTArray<15, SkPoint, true> quads;
     254           0 :     GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
     255           0 :     int count = quads.count();
     256           0 :     for (int q = 0; q < count; q += 3) {
     257           0 :         add_quad_segment(&quads[q], segments);
     258             :     }
     259           0 : }
     260             : 
     261           0 : static bool get_segments(const SkPath& path,
     262             :                          const SkMatrix& m,
     263             :                          SegmentArray* segments,
     264             :                          SkPoint* fanPt,
     265             :                          int* vCount,
     266             :                          int* iCount) {
     267           0 :     SkPath::Iter iter(path, true);
     268             :     // This renderer over-emphasizes very thin path regions. We use the distance
     269             :     // to the path from the sample to compute coverage. Every pixel intersected
     270             :     // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
     271             :     // notice that the sample may be close to a very thin area of the path and
     272             :     // thus should be very light. This is particularly egregious for degenerate
     273             :     // line paths. We detect paths that are very close to a line (zero area) and
     274             :     // draw nothing.
     275           0 :     DegenerateTestData degenerateData;
     276             :     SkPathPriv::FirstDirection dir;
     277             :     // get_direction can fail for some degenerate paths.
     278           0 :     if (!get_direction(path, m, &dir)) {
     279           0 :         return false;
     280             :     }
     281             : 
     282             :     for (;;) {
     283             :         SkPoint pts[4];
     284           0 :         SkPath::Verb verb = iter.next(pts, true, true);
     285           0 :         switch (verb) {
     286             :             case SkPath::kMove_Verb:
     287           0 :                 m.mapPoints(pts, 1);
     288           0 :                 update_degenerate_test(&degenerateData, pts[0]);
     289           0 :                 break;
     290             :             case SkPath::kLine_Verb: {
     291           0 :                 m.mapPoints(&pts[1], 1);
     292           0 :                 update_degenerate_test(&degenerateData, pts[1]);
     293           0 :                 add_line_to_segment(pts[1], segments);
     294           0 :                 break;
     295             :             }
     296             :             case SkPath::kQuad_Verb:
     297           0 :                 m.mapPoints(pts, 3);
     298           0 :                 update_degenerate_test(&degenerateData, pts[1]);
     299           0 :                 update_degenerate_test(&degenerateData, pts[2]);
     300           0 :                 add_quad_segment(pts, segments);
     301           0 :                 break;
     302             :             case SkPath::kConic_Verb: {
     303           0 :                 m.mapPoints(pts, 3);
     304           0 :                 SkScalar weight = iter.conicWeight();
     305           0 :                 SkAutoConicToQuads converter;
     306           0 :                 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
     307           0 :                 for (int i = 0; i < converter.countQuads(); ++i) {
     308           0 :                     update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
     309           0 :                     update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
     310           0 :                     add_quad_segment(quadPts + 2*i, segments);
     311             :                 }
     312           0 :                 break;
     313             :             }
     314             :             case SkPath::kCubic_Verb: {
     315           0 :                 m.mapPoints(pts, 4);
     316           0 :                 update_degenerate_test(&degenerateData, pts[1]);
     317           0 :                 update_degenerate_test(&degenerateData, pts[2]);
     318           0 :                 update_degenerate_test(&degenerateData, pts[3]);
     319           0 :                 add_cubic_segments(pts, dir, segments);
     320           0 :                 break;
     321             :             };
     322             :             case SkPath::kDone_Verb:
     323           0 :                 if (degenerateData.isDegenerate()) {
     324           0 :                     return false;
     325             :                 } else {
     326           0 :                     compute_vectors(segments, fanPt, dir, vCount, iCount);
     327           0 :                     return true;
     328             :                 }
     329             :             default:
     330           0 :                 break;
     331             :         }
     332           0 :     }
     333             : }
     334             : 
     335             : struct QuadVertex {
     336             :     SkPoint  fPos;
     337             :     SkPoint  fUV;
     338             :     SkScalar fD0;
     339             :     SkScalar fD1;
     340             : };
     341             : 
     342             : struct Draw {
     343           0 :     Draw() : fVertexCnt(0), fIndexCnt(0) {}
     344             :     int fVertexCnt;
     345             :     int fIndexCnt;
     346             : };
     347             : 
     348             : typedef SkTArray<Draw, true> DrawArray;
     349             : 
     350           0 : static void create_vertices(const SegmentArray&  segments,
     351             :                             const SkPoint& fanPt,
     352             :                             DrawArray*     draws,
     353             :                             QuadVertex*    verts,
     354             :                             uint16_t*      idxs) {
     355           0 :     Draw* draw = &draws->push_back();
     356             :     // alias just to make vert/index assignments easier to read.
     357           0 :     int* v = &draw->fVertexCnt;
     358           0 :     int* i = &draw->fIndexCnt;
     359             : 
     360           0 :     int count = segments.count();
     361           0 :     for (int a = 0; a < count; ++a) {
     362           0 :         const Segment& sega = segments[a];
     363           0 :         int b = (a + 1) % count;
     364           0 :         const Segment& segb = segments[b];
     365             : 
     366             :         // Check whether adding the verts for this segment to the current draw would cause index
     367             :         // values to overflow.
     368           0 :         int vCount = 4;
     369           0 :         if (Segment::kLine == segb.fType) {
     370           0 :             vCount += 5;
     371             :         } else {
     372           0 :             vCount += 6;
     373             :         }
     374           0 :         if (draw->fVertexCnt + vCount > (1 << 16)) {
     375           0 :             verts += *v;
     376           0 :             idxs += *i;
     377           0 :             draw = &draws->push_back();
     378           0 :             v = &draw->fVertexCnt;
     379           0 :             i = &draw->fIndexCnt;
     380             :         }
     381             : 
     382             :         // FIXME: These tris are inset in the 1 unit arc around the corner
     383           0 :         verts[*v + 0].fPos = sega.endPt();
     384           0 :         verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
     385           0 :         verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
     386           0 :         verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
     387           0 :         verts[*v + 0].fUV.set(0,0);
     388           0 :         verts[*v + 1].fUV.set(0,-SK_Scalar1);
     389           0 :         verts[*v + 2].fUV.set(0,-SK_Scalar1);
     390           0 :         verts[*v + 3].fUV.set(0,-SK_Scalar1);
     391           0 :         verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
     392           0 :         verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
     393           0 :         verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
     394           0 :         verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
     395             : 
     396           0 :         idxs[*i + 0] = *v + 0;
     397           0 :         idxs[*i + 1] = *v + 2;
     398           0 :         idxs[*i + 2] = *v + 1;
     399           0 :         idxs[*i + 3] = *v + 0;
     400           0 :         idxs[*i + 4] = *v + 3;
     401           0 :         idxs[*i + 5] = *v + 2;
     402             : 
     403           0 :         *v += 4;
     404           0 :         *i += 6;
     405             : 
     406           0 :         if (Segment::kLine == segb.fType) {
     407           0 :             verts[*v + 0].fPos = fanPt;
     408           0 :             verts[*v + 1].fPos = sega.endPt();
     409           0 :             verts[*v + 2].fPos = segb.fPts[0];
     410             : 
     411           0 :             verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
     412           0 :             verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
     413             : 
     414             :             // we draw the line edge as a degenerate quad (u is 0, v is the
     415             :             // signed distance to the edge)
     416           0 :             SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
     417           0 :                                                         verts[*v + 2].fPos);
     418           0 :             verts[*v + 0].fUV.set(0, dist);
     419           0 :             verts[*v + 1].fUV.set(0, 0);
     420           0 :             verts[*v + 2].fUV.set(0, 0);
     421           0 :             verts[*v + 3].fUV.set(0, -SK_Scalar1);
     422           0 :             verts[*v + 4].fUV.set(0, -SK_Scalar1);
     423             : 
     424           0 :             verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
     425           0 :             verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
     426           0 :             verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
     427           0 :             verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
     428           0 :             verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
     429             : 
     430           0 :             idxs[*i + 0] = *v + 3;
     431           0 :             idxs[*i + 1] = *v + 1;
     432           0 :             idxs[*i + 2] = *v + 2;
     433             : 
     434           0 :             idxs[*i + 3] = *v + 4;
     435           0 :             idxs[*i + 4] = *v + 3;
     436           0 :             idxs[*i + 5] = *v + 2;
     437             : 
     438           0 :             *i += 6;
     439             : 
     440             :             // Draw the interior fan if it exists.
     441             :             // TODO: Detect and combine colinear segments. This will ensure we catch every case
     442             :             // with no interior, and that the resulting shared edge uses the same endpoints.
     443           0 :             if (count >= 3) {
     444           0 :                 idxs[*i + 0] = *v + 0;
     445           0 :                 idxs[*i + 1] = *v + 2;
     446           0 :                 idxs[*i + 2] = *v + 1;
     447             : 
     448           0 :                 *i += 3;
     449             :             }
     450             : 
     451           0 :             *v += 5;
     452             :         } else {
     453           0 :             SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
     454             : 
     455           0 :             SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
     456           0 :             midVec.normalize();
     457             : 
     458           0 :             verts[*v + 0].fPos = fanPt;
     459           0 :             verts[*v + 1].fPos = qpts[0];
     460           0 :             verts[*v + 2].fPos = qpts[2];
     461           0 :             verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
     462           0 :             verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
     463           0 :             verts[*v + 5].fPos = qpts[1] + midVec;
     464             : 
     465           0 :             SkScalar c = segb.fNorms[0].dot(qpts[0]);
     466           0 :             verts[*v + 0].fD0 =  -segb.fNorms[0].dot(fanPt) + c;
     467           0 :             verts[*v + 1].fD0 =  0.f;
     468           0 :             verts[*v + 2].fD0 =  -segb.fNorms[0].dot(qpts[2]) + c;
     469           0 :             verts[*v + 3].fD0 = -SK_ScalarMax/100;
     470           0 :             verts[*v + 4].fD0 = -SK_ScalarMax/100;
     471           0 :             verts[*v + 5].fD0 = -SK_ScalarMax/100;
     472             : 
     473           0 :             c = segb.fNorms[1].dot(qpts[2]);
     474           0 :             verts[*v + 0].fD1 =  -segb.fNorms[1].dot(fanPt) + c;
     475           0 :             verts[*v + 1].fD1 =  -segb.fNorms[1].dot(qpts[0]) + c;
     476           0 :             verts[*v + 2].fD1 =  0.f;
     477           0 :             verts[*v + 3].fD1 = -SK_ScalarMax/100;
     478           0 :             verts[*v + 4].fD1 = -SK_ScalarMax/100;
     479           0 :             verts[*v + 5].fD1 = -SK_ScalarMax/100;
     480             : 
     481           0 :             GrPathUtils::QuadUVMatrix toUV(qpts);
     482           0 :             toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
     483             : 
     484           0 :             idxs[*i + 0] = *v + 3;
     485           0 :             idxs[*i + 1] = *v + 1;
     486           0 :             idxs[*i + 2] = *v + 2;
     487           0 :             idxs[*i + 3] = *v + 4;
     488           0 :             idxs[*i + 4] = *v + 3;
     489           0 :             idxs[*i + 5] = *v + 2;
     490             : 
     491           0 :             idxs[*i + 6] = *v + 5;
     492           0 :             idxs[*i + 7] = *v + 3;
     493           0 :             idxs[*i + 8] = *v + 4;
     494             : 
     495           0 :             *i += 9;
     496             : 
     497             :             // Draw the interior fan if it exists.
     498             :             // TODO: Detect and combine colinear segments. This will ensure we catch every case
     499             :             // with no interior, and that the resulting shared edge uses the same endpoints.
     500           0 :             if (count >= 3) {
     501           0 :                 idxs[*i + 0] = *v + 0;
     502           0 :                 idxs[*i + 1] = *v + 2;
     503           0 :                 idxs[*i + 2] = *v + 1;
     504             : 
     505           0 :                 *i += 3;
     506             :             }
     507             : 
     508           0 :             *v += 6;
     509             :         }
     510             :     }
     511           0 : }
     512             : 
     513             : ///////////////////////////////////////////////////////////////////////////////
     514             : 
     515             : /*
     516             :  * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
     517             :  * two components of the vertex attribute. Coverage is based on signed
     518             :  * distance with negative being inside, positive outside. The edge is specified in
     519             :  * window space (y-down). If either the third or fourth component of the interpolated
     520             :  * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
     521             :  * attempt to trim to a portion of the infinite quad.
     522             :  * Requires shader derivative instruction support.
     523             :  */
     524             : 
     525             : class QuadEdgeEffect : public GrGeometryProcessor {
     526             : public:
     527             : 
     528           0 :     static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& localMatrix,
     529             :                                            bool usesLocalCoords) {
     530           0 :         return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(color, localMatrix, usesLocalCoords));
     531             :     }
     532             : 
     533           0 :     ~QuadEdgeEffect() override {}
     534             : 
     535           0 :     const char* name() const override { return "QuadEdge"; }
     536             : 
     537           0 :     const Attribute* inPosition() const { return fInPosition; }
     538           0 :     const Attribute* inQuadEdge() const { return fInQuadEdge; }
     539           0 :     GrColor color() const { return fColor; }
     540           0 :     const SkMatrix& localMatrix() const { return fLocalMatrix; }
     541           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     542             : 
     543           0 :     class GLSLProcessor : public GrGLSLGeometryProcessor {
     544             :     public:
     545           0 :         GLSLProcessor() : fColor(GrColor_ILLEGAL) {}
     546             : 
     547           0 :         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
     548           0 :             const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
     549           0 :             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
     550           0 :             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
     551           0 :             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
     552             : 
     553             :             // emit attributes
     554           0 :             varyingHandler->emitAttributes(qe);
     555             : 
     556           0 :             GrGLSLVertToFrag v(kVec4f_GrSLType);
     557           0 :             varyingHandler->addVarying("QuadEdge", &v);
     558           0 :             vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
     559             : 
     560           0 :             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
     561             :             // Setup pass through color
     562           0 :             this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
     563           0 :                                     &fColorUniform);
     564             : 
     565             :             // Setup position
     566           0 :             this->setupPosition(vertBuilder, gpArgs, qe.inPosition()->fName);
     567             : 
     568             :             // emit transforms
     569           0 :             this->emitTransforms(vertBuilder,
     570             :                                  varyingHandler,
     571             :                                  uniformHandler,
     572             :                                  gpArgs->fPositionVar,
     573           0 :                                  qe.inPosition()->fName,
     574             :                                  qe.localMatrix(),
     575           0 :                                  args.fFPCoordTransformHandler);
     576             : 
     577           0 :             fragBuilder->codeAppendf("float edgeAlpha;");
     578             : 
     579             :             // keep the derivative instructions outside the conditional
     580           0 :             fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
     581           0 :             fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
     582           0 :             fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
     583             :             // today we know z and w are in device space. We could use derivatives
     584           0 :             fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
     585           0 :                                      v.fsIn());
     586           0 :             fragBuilder->codeAppendf ("} else {");
     587           0 :             fragBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
     588             :                                      "               2.0*%s.x*duvdy.x - duvdy.y);",
     589           0 :                                      v.fsIn(), v.fsIn());
     590           0 :             fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
     591           0 :                                      v.fsIn());
     592           0 :             fragBuilder->codeAppendf("edgeAlpha = "
     593           0 :                                      "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
     594             : 
     595           0 :             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
     596           0 :         }
     597             : 
     598           0 :         static inline void GenKey(const GrGeometryProcessor& gp,
     599             :                                   const GrShaderCaps&,
     600             :                                   GrProcessorKeyBuilder* b) {
     601           0 :             const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
     602           0 :             b->add32(SkToBool(qee.usesLocalCoords() && qee.localMatrix().hasPerspective()));
     603           0 :         }
     604             : 
     605           0 :         void setData(const GrGLSLProgramDataManager& pdman,
     606             :                      const GrPrimitiveProcessor& gp,
     607             :                      FPCoordTransformIter&& transformIter) override {
     608           0 :             const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
     609           0 :             if (qe.color() != fColor) {
     610             :                 float c[4];
     611           0 :                 GrColorToRGBAFloat(qe.color(), c);
     612           0 :                 pdman.set4fv(fColorUniform, 1, c);
     613           0 :                 fColor = qe.color();
     614             :             }
     615           0 :             this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
     616           0 :         }
     617             : 
     618             :     private:
     619             :         GrColor fColor;
     620             :         UniformHandle fColorUniform;
     621             : 
     622             :         typedef GrGLSLGeometryProcessor INHERITED;
     623             :     };
     624             : 
     625           0 :     void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
     626           0 :         GLSLProcessor::GenKey(*this, caps, b);
     627           0 :     }
     628             : 
     629           0 :     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
     630           0 :         return new GLSLProcessor();
     631             :     }
     632             : 
     633             : private:
     634           0 :     QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoords)
     635           0 :         : fColor(color)
     636             :         , fLocalMatrix(localMatrix)
     637           0 :         , fUsesLocalCoords(usesLocalCoords) {
     638           0 :         this->initClassID<QuadEdgeEffect>();
     639           0 :         fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
     640           0 :         fInQuadEdge = &this->addVertexAttrib("inQuadEdge", kVec4f_GrVertexAttribType);
     641           0 :     }
     642             : 
     643             :     const Attribute* fInPosition;
     644             :     const Attribute* fInQuadEdge;
     645             :     GrColor          fColor;
     646             :     SkMatrix         fLocalMatrix;
     647             :     bool             fUsesLocalCoords;
     648             : 
     649             :     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
     650             : 
     651             :     typedef GrGeometryProcessor INHERITED;
     652             : };
     653             : 
     654             : GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
     655             : 
     656             : #if GR_TEST_UTILS
     657           0 : sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
     658             :     // Doesn't work without derivative instructions.
     659           0 :     return d->caps()->shaderCaps()->shaderDerivativeSupport()
     660             :                    ? QuadEdgeEffect::Make(GrRandomColor(d->fRandom),
     661             :                                           GrTest::TestMatrix(d->fRandom),
     662           0 :                                           d->fRandom->nextBool())
     663           0 :                    : nullptr;
     664             : }
     665             : #endif
     666             : 
     667             : ///////////////////////////////////////////////////////////////////////////////
     668             : 
     669           0 : bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
     670           0 :     return (args.fShaderCaps->shaderDerivativeSupport() && (GrAAType::kCoverage == args.fAAType) &&
     671           0 :             args.fShape->style().isSimpleFill() && !args.fShape->inverseFilled() &&
     672           0 :             args.fShape->knownToBeConvex());
     673             : }
     674             : 
     675             : // extract the result vertices and indices from the GrAAConvexTessellator
     676           0 : static void extract_verts(const GrAAConvexTessellator& tess,
     677             :                           void* vertices,
     678             :                           size_t vertexStride,
     679             :                           GrColor color,
     680             :                           uint16_t* idxs,
     681             :                           bool tweakAlphaForCoverage) {
     682           0 :     intptr_t verts = reinterpret_cast<intptr_t>(vertices);
     683             : 
     684           0 :     for (int i = 0; i < tess.numPts(); ++i) {
     685           0 :         *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
     686             :     }
     687             : 
     688             :     // Make 'verts' point to the colors
     689           0 :     verts += sizeof(SkPoint);
     690           0 :     for (int i = 0; i < tess.numPts(); ++i) {
     691           0 :         if (tweakAlphaForCoverage) {
     692           0 :             SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
     693           0 :             unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
     694           0 :             GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
     695           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
     696             :         } else {
     697           0 :             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
     698           0 :             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
     699           0 :                     tess.coverage(i);
     700             :         }
     701             :     }
     702             : 
     703           0 :     for (int i = 0; i < tess.numIndices(); ++i) {
     704           0 :         idxs[i] = tess.index(i);
     705             :     }
     706           0 : }
     707             : 
     708           0 : static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage,
     709             :                                                  const SkMatrix& viewMatrix,
     710             :                                                  bool usesLocalCoords) {
     711             :     using namespace GrDefaultGeoProcFactory;
     712             : 
     713             :     Coverage::Type coverageType;
     714           0 :     if (tweakAlphaForCoverage) {
     715           0 :         coverageType = Coverage::kSolid_Type;
     716             :     } else {
     717           0 :         coverageType = Coverage::kAttribute_Type;
     718             :     }
     719             :     LocalCoords::Type localCoordsType =
     720           0 :             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
     721             :     return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
     722           0 :                               viewMatrix);
     723             : }
     724             : 
     725           0 : class AAConvexPathOp final : public GrLegacyMeshDrawOp {
     726             : public:
     727           0 :     DEFINE_OP_CLASS_ID
     728           0 :     static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
     729             :                                                     const SkPath& path) {
     730           0 :         return std::unique_ptr<GrLegacyMeshDrawOp>(new AAConvexPathOp(color, viewMatrix, path));
     731             :     }
     732             : 
     733           0 :     const char* name() const override { return "AAConvexPathOp"; }
     734             : 
     735           0 :     SkString dumpInfo() const override {
     736           0 :         SkString string;
     737           0 :         string.appendf("Color: 0x%08x, Count: %d\n", fColor, fPaths.count());
     738           0 :         string.append(DumpPipelineInfo(*this->pipeline()));
     739           0 :         string.append(INHERITED::dumpInfo());
     740           0 :         return string;
     741             :     }
     742             : 
     743             : private:
     744           0 :     AAConvexPathOp(GrColor color, const SkMatrix& viewMatrix, const SkPath& path)
     745           0 :             : INHERITED(ClassID()), fColor(color) {
     746           0 :         fPaths.emplace_back(PathData{viewMatrix, path});
     747           0 :         this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
     748           0 :     }
     749             : 
     750           0 :     void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
     751             :                                     GrProcessorAnalysisCoverage* coverage) const override {
     752           0 :         color->setToConstant(fColor);
     753           0 :         *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     754           0 :     }
     755             : 
     756           0 :     void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
     757           0 :         optimizations.getOverrideColorIfSet(&fColor);
     758             : 
     759           0 :         fUsesLocalCoords = optimizations.readsLocalCoords();
     760           0 :         fLinesOnly = SkPath::kLine_SegmentMask == fPaths[0].fPath.getSegmentMasks();
     761           0 :         fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
     762           0 :     }
     763             : 
     764           0 :     void prepareLinesOnlyDraws(Target* target) const {
     765           0 :         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
     766             : 
     767             :         // Setup GrGeometryProcessor
     768             :         sk_sp<GrGeometryProcessor> gp(create_fill_gp(
     769           0 :                 canTweakAlphaForCoverage, this->viewMatrix(), this->usesLocalCoords()));
     770           0 :         if (!gp) {
     771           0 :             SkDebugf("Could not create GrGeometryProcessor\n");
     772           0 :             return;
     773             :         }
     774             : 
     775           0 :         size_t vertexStride = gp->getVertexStride();
     776             : 
     777           0 :         SkASSERT(canTweakAlphaForCoverage ?
     778             :                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
     779             :                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
     780             : 
     781           0 :         GrAAConvexTessellator tess;
     782             : 
     783           0 :         int instanceCount = fPaths.count();
     784             : 
     785           0 :         for (int i = 0; i < instanceCount; i++) {
     786           0 :             tess.rewind();
     787             : 
     788           0 :             const PathData& args = fPaths[i];
     789             : 
     790           0 :             if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
     791           0 :                 continue;
     792             :             }
     793             : 
     794             :             const GrBuffer* vertexBuffer;
     795             :             int firstVertex;
     796             : 
     797           0 :             void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
     798           0 :                                                   &firstVertex);
     799           0 :             if (!verts) {
     800           0 :                 SkDebugf("Could not allocate vertices\n");
     801           0 :                 return;
     802             :             }
     803             : 
     804             :             const GrBuffer* indexBuffer;
     805             :             int firstIndex;
     806             : 
     807           0 :             uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
     808           0 :             if (!idxs) {
     809           0 :                 SkDebugf("Could not allocate indices\n");
     810           0 :                 return;
     811             :             }
     812             : 
     813           0 :             extract_verts(tess, verts, vertexStride, fColor, idxs, canTweakAlphaForCoverage);
     814             : 
     815           0 :             GrMesh mesh;
     816           0 :             mesh.initIndexed(kTriangles_GrPrimitiveType,
     817             :                              vertexBuffer, indexBuffer,
     818             :                              firstVertex, firstIndex,
     819           0 :                              tess.numPts(), tess.numIndices());
     820           0 :             target->draw(gp.get(), this->pipeline(), mesh);
     821             :         }
     822             :     }
     823             : 
     824           0 :     void onPrepareDraws(Target* target) const override {
     825             : #ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
     826           0 :         if (this->linesOnly()) {
     827           0 :             this->prepareLinesOnlyDraws(target);
     828           0 :             return;
     829             :         }
     830             : #endif
     831             : 
     832           0 :         int instanceCount = fPaths.count();
     833             : 
     834             :         SkMatrix invert;
     835           0 :         if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
     836           0 :             SkDebugf("Could not invert viewmatrix\n");
     837           0 :             return;
     838             :         }
     839             : 
     840             :         // Setup GrGeometryProcessor
     841             :         sk_sp<GrGeometryProcessor> quadProcessor(
     842           0 :                 QuadEdgeEffect::Make(this->color(), invert, this->usesLocalCoords()));
     843             : 
     844             :         // TODO generate all segments for all paths and use one vertex buffer
     845           0 :         for (int i = 0; i < instanceCount; i++) {
     846           0 :             const PathData& args = fPaths[i];
     847             : 
     848             :             // We use the fact that SkPath::transform path does subdivision based on
     849             :             // perspective. Otherwise, we apply the view matrix when copying to the
     850             :             // segment representation.
     851           0 :             const SkMatrix* viewMatrix = &args.fViewMatrix;
     852             : 
     853             :             // We avoid initializing the path unless we have to
     854           0 :             const SkPath* pathPtr = &args.fPath;
     855           0 :             SkTLazy<SkPath> tmpPath;
     856           0 :             if (viewMatrix->hasPerspective()) {
     857           0 :                 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
     858           0 :                 tmpPathPtr->setIsVolatile(true);
     859           0 :                 tmpPathPtr->transform(*viewMatrix);
     860           0 :                 viewMatrix = &SkMatrix::I();
     861           0 :                 pathPtr = tmpPathPtr;
     862             :             }
     863             : 
     864             :             int vertexCount;
     865             :             int indexCount;
     866             :             enum {
     867             :                 kPreallocSegmentCnt = 512 / sizeof(Segment),
     868             :                 kPreallocDrawCnt = 4,
     869             :             };
     870           0 :             SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
     871             :             SkPoint fanPt;
     872             : 
     873           0 :             if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
     874             :                               &indexCount)) {
     875           0 :                 continue;
     876             :             }
     877             : 
     878             :             const GrBuffer* vertexBuffer;
     879             :             int firstVertex;
     880             : 
     881           0 :             size_t vertexStride = quadProcessor->getVertexStride();
     882           0 :             QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
     883           0 :                 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
     884             : 
     885           0 :             if (!verts) {
     886           0 :                 SkDebugf("Could not allocate vertices\n");
     887           0 :                 return;
     888             :             }
     889             : 
     890             :             const GrBuffer* indexBuffer;
     891             :             int firstIndex;
     892             : 
     893           0 :             uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
     894           0 :             if (!idxs) {
     895           0 :                 SkDebugf("Could not allocate indices\n");
     896           0 :                 return;
     897             :             }
     898             : 
     899           0 :             SkSTArray<kPreallocDrawCnt, Draw, true> draws;
     900           0 :             create_vertices(segments, fanPt, &draws, verts, idxs);
     901             : 
     902           0 :             GrMesh mesh;
     903             : 
     904           0 :             for (int j = 0; j < draws.count(); ++j) {
     905           0 :                 const Draw& draw = draws[j];
     906             :                 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
     907           0 :                                  firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
     908           0 :                 target->draw(quadProcessor.get(), this->pipeline(), mesh);
     909           0 :                 firstVertex += draw.fVertexCnt;
     910           0 :                 firstIndex += draw.fIndexCnt;
     911             :             }
     912             :         }
     913             :     }
     914             : 
     915           0 :     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
     916           0 :         AAConvexPathOp* that = t->cast<AAConvexPathOp>();
     917           0 :         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
     918             :                                     that->bounds(), caps)) {
     919           0 :             return false;
     920             :         }
     921             : 
     922           0 :         if (this->color() != that->color()) {
     923           0 :             return false;
     924             :         }
     925             : 
     926           0 :         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
     927           0 :         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
     928           0 :             return false;
     929             :         }
     930             : 
     931           0 :         if (this->linesOnly() != that->linesOnly()) {
     932           0 :             return false;
     933             :         }
     934             : 
     935             :         // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
     936             :         // tweaking
     937           0 :         if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
     938           0 :             fCanTweakAlphaForCoverage = false;
     939             :         }
     940             : 
     941           0 :         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
     942           0 :         this->joinBounds(*that);
     943           0 :         return true;
     944             :     }
     945             : 
     946           0 :     GrColor color() const { return fColor; }
     947           0 :     bool linesOnly() const { return fLinesOnly; }
     948           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     949           0 :     bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
     950           0 :     const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
     951             : 
     952             :     GrColor fColor;
     953             :     bool fUsesLocalCoords;
     954             :     bool fLinesOnly;
     955             :     bool fCanTweakAlphaForCoverage;
     956             : 
     957           0 :     struct PathData {
     958             :         SkMatrix fViewMatrix;
     959             :         SkPath fPath;
     960             :     };
     961             : 
     962             :     SkSTArray<1, PathData, true> fPaths;
     963             : 
     964             :     typedef GrLegacyMeshDrawOp INHERITED;
     965             : };
     966             : 
     967           0 : bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
     968           0 :     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
     969             :                               "GrAAConvexPathRenderer::onDrawPath");
     970           0 :     SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
     971           0 :     SkASSERT(!args.fShape->isEmpty());
     972             : 
     973           0 :     SkPath path;
     974           0 :     args.fShape->asPath(&path);
     975             : 
     976             :     std::unique_ptr<GrLegacyMeshDrawOp> op =
     977           0 :             AAConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path);
     978             : 
     979           0 :     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     980           0 :     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
     981             : 
     982           0 :     args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
     983           0 :                                                    std::move(op));
     984             : 
     985           0 :     return true;
     986             : 
     987             : }
     988             : 
     989             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     990             : 
     991             : #if GR_TEST_UTILS
     992             : 
     993           0 : DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
     994           0 :     GrColor color = GrRandomColor(random);
     995           0 :     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     996           0 :     SkPath path = GrTest::TestPathConvex(random);
     997             : 
     998           0 :     return AAConvexPathOp::Make(color, viewMatrix, path);
     999             : }
    1000             : 
    1001             : #endif

Generated by: LCOV version 1.13