Home | History | Annotate | Download | only in gl
      1 
      2 /*
      3  * Copyright 2012 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "GrGLPath.h"
     10 #include "GrGLPathRendering.h"
     11 #include "GrGLGpu.h"
     12 
     13 namespace {
     14 inline GrGLubyte verb_to_gl_path_cmd(SkPath::Verb verb) {
     15     static const GrGLubyte gTable[] = {
     16         GR_GL_MOVE_TO,
     17         GR_GL_LINE_TO,
     18         GR_GL_QUADRATIC_CURVE_TO,
     19         GR_GL_CONIC_CURVE_TO,
     20         GR_GL_CUBIC_CURVE_TO,
     21         GR_GL_CLOSE_PATH,
     22     };
     23     GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
     24     GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
     25     GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
     26     GR_STATIC_ASSERT(3 == SkPath::kConic_Verb);
     27     GR_STATIC_ASSERT(4 == SkPath::kCubic_Verb);
     28     GR_STATIC_ASSERT(5 == SkPath::kClose_Verb);
     29 
     30     SkASSERT(verb >= 0 && (size_t)verb < SK_ARRAY_COUNT(gTable));
     31     return gTable[verb];
     32 }
     33 
     34 #ifdef SK_DEBUG
     35 inline int num_coords(SkPath::Verb verb) {
     36     static const int gTable[] = {
     37         2, // move
     38         2, // line
     39         4, // quad
     40         5, // conic
     41         6, // cubic
     42         0, // close
     43     };
     44     GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
     45     GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
     46     GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
     47     GR_STATIC_ASSERT(3 == SkPath::kConic_Verb);
     48     GR_STATIC_ASSERT(4 == SkPath::kCubic_Verb);
     49     GR_STATIC_ASSERT(5 == SkPath::kClose_Verb);
     50 
     51     SkASSERT(verb >= 0 && (size_t)verb < SK_ARRAY_COUNT(gTable));
     52     return gTable[verb];
     53 }
     54 #endif
     55 
     56 inline GrGLenum join_to_gl_join(SkPaint::Join join) {
     57     static GrGLenum gSkJoinsToGrGLJoins[] = {
     58         GR_GL_MITER_REVERT,
     59         GR_GL_ROUND,
     60         GR_GL_BEVEL
     61     };
     62     return gSkJoinsToGrGLJoins[join];
     63     GR_STATIC_ASSERT(0 == SkPaint::kMiter_Join);
     64     GR_STATIC_ASSERT(1 == SkPaint::kRound_Join);
     65     GR_STATIC_ASSERT(2 == SkPaint::kBevel_Join);
     66     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gSkJoinsToGrGLJoins) == SkPaint::kJoinCount);
     67 }
     68 
     69 inline GrGLenum cap_to_gl_cap(SkPaint::Cap cap) {
     70     static GrGLenum gSkCapsToGrGLCaps[] = {
     71         GR_GL_FLAT,
     72         GR_GL_ROUND,
     73         GR_GL_SQUARE
     74     };
     75     return gSkCapsToGrGLCaps[cap];
     76     GR_STATIC_ASSERT(0 == SkPaint::kButt_Cap);
     77     GR_STATIC_ASSERT(1 == SkPaint::kRound_Cap);
     78     GR_STATIC_ASSERT(2 == SkPaint::kSquare_Cap);
     79     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gSkCapsToGrGLCaps) == SkPaint::kCapCount);
     80 }
     81 
     82 inline void points_to_coords(const SkPoint points[], size_t first_point, size_t amount,
     83                              GrGLfloat coords[]) {
     84     for (size_t i = 0;  i < amount; ++i) {
     85         coords[i * 2] =  SkScalarToFloat(points[first_point + i].fX);
     86         coords[i * 2 + 1] = SkScalarToFloat(points[first_point + i].fY);
     87     }
     88 }
     89 
     90 template<bool checkForDegenerates>
     91 inline bool init_path_object_for_general_path(GrGLGpu* gpu, GrGLuint pathID,
     92                                               const SkPath& skPath) {
     93     SkDEBUGCODE(int numCoords = 0);
     94     int verbCnt = skPath.countVerbs();
     95     int pointCnt = skPath.countPoints();
     96     int minCoordCnt = pointCnt * 2;
     97 
     98     SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt);
     99     SkSTArray<16, GrGLfloat, true> pathCoords(minCoordCnt);
    100     bool lastVerbWasMove = true; // A path with just "close;" means "moveto(0,0); close;"
    101     SkPoint points[4];
    102     SkPath::RawIter iter(skPath);
    103     SkPath::Verb verb;
    104     while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
    105         pathCommands.push_back(verb_to_gl_path_cmd(verb));
    106         GrGLfloat coords[6];
    107         int coordsForVerb;
    108         switch (verb) {
    109             case SkPath::kMove_Verb:
    110                 if (checkForDegenerates) {
    111                     lastVerbWasMove = true;
    112                 }
    113                 points_to_coords(points, 0, 1, coords);
    114                 coordsForVerb = 2;
    115                 break;
    116             case SkPath::kLine_Verb:
    117                 if (checkForDegenerates) {
    118                     if (SkPath::IsLineDegenerate(points[0], points[1], true)) {
    119                         return false;
    120                     }
    121                     lastVerbWasMove = false;
    122                 }
    123 
    124                 points_to_coords(points, 1, 1, coords);
    125                 coordsForVerb = 2;
    126                 break;
    127             case SkPath::kConic_Verb:
    128                 if (checkForDegenerates) {
    129                     if (SkPath::IsQuadDegenerate(points[0], points[1], points[2], true)) {
    130                         return false;
    131                     }
    132                     lastVerbWasMove = false;
    133                 }
    134                 points_to_coords(points, 1, 2, coords);
    135                 coords[4] = SkScalarToFloat(iter.conicWeight());
    136                 coordsForVerb = 5;
    137                 break;
    138             case SkPath::kQuad_Verb:
    139                 if (checkForDegenerates) {
    140                     if (SkPath::IsQuadDegenerate(points[0], points[1], points[2], true)) {
    141                         return false;
    142                     }
    143                     lastVerbWasMove = false;
    144                 }
    145                 points_to_coords(points, 1, 2, coords);
    146                 coordsForVerb = 4;
    147                 break;
    148             case SkPath::kCubic_Verb:
    149                 if (checkForDegenerates) {
    150                     if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3],
    151                                                   true)) {
    152                         return false;
    153                     }
    154                     lastVerbWasMove = false;
    155                 }
    156                 points_to_coords(points, 1, 3, coords);
    157                 coordsForVerb = 6;
    158                 break;
    159             case SkPath::kClose_Verb:
    160                 if (checkForDegenerates) {
    161                     if (lastVerbWasMove) {
    162                         // Interpret "move(x,y);close;" as "move(x,y);lineto(x,y);close;".
    163                         // which produces a degenerate segment.
    164                         return false;
    165                     }
    166                 }
    167                 continue;
    168             default:
    169                 SkASSERT(false);  // Not reached.
    170                 continue;
    171         }
    172         SkDEBUGCODE(numCoords += num_coords(verb));
    173         pathCoords.push_back_n(coordsForVerb, coords);
    174     }
    175     SkASSERT(verbCnt == pathCommands.count());
    176     SkASSERT(numCoords == pathCoords.count());
    177 
    178     GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0],
    179                                                 pathCoords.count(), GR_GL_FLOAT, &pathCoords[0]));
    180     return true;
    181 }
    182 
    183 /*
    184  * For now paths only natively support winding and even odd fill types
    185  */
    186 static GrPathRendering::FillType convert_skpath_filltype(SkPath::FillType fill) {
    187     switch (fill) {
    188         default:
    189             SkFAIL("Incomplete Switch\n");
    190         case SkPath::kWinding_FillType:
    191         case SkPath::kInverseWinding_FillType:
    192             return GrPathRendering::kWinding_FillType;
    193         case SkPath::kEvenOdd_FillType:
    194         case SkPath::kInverseEvenOdd_FillType:
    195             return GrPathRendering::kEvenOdd_FillType;
    196     }
    197 }
    198 
    199 } // namespace
    200 
    201 bool GrGLPath::InitPathObjectPathDataCheckingDegenerates(GrGLGpu* gpu, GrGLuint pathID,
    202                                                          const SkPath& skPath) {
    203     return init_path_object_for_general_path<true>(gpu, pathID, skPath);
    204 }
    205 
    206 void GrGLPath::InitPathObjectPathData(GrGLGpu* gpu,
    207                                       GrGLuint pathID,
    208                                       const SkPath& skPath) {
    209     SkASSERT(!skPath.isEmpty());
    210 
    211 #ifdef SK_SCALAR_IS_FLOAT
    212     // This branch does type punning, converting SkPoint* to GrGLfloat*.
    213     if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) {
    214         int verbCnt = skPath.countVerbs();
    215         int pointCnt = skPath.countPoints();
    216         int coordCnt = pointCnt * 2;
    217         SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt);
    218         SkSTArray<16, GrGLfloat, true> pathCoords(coordCnt);
    219 
    220         static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_two_floats");
    221 
    222         pathCommands.resize_back(verbCnt);
    223         pathCoords.resize_back(coordCnt);
    224         skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt);
    225         skPath.getVerbs(&pathCommands[0], verbCnt);
    226 
    227         SkDEBUGCODE(int verbCoordCnt = 0);
    228         for (int i = 0; i < verbCnt; ++i) {
    229             SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]);
    230             pathCommands[i] = verb_to_gl_path_cmd(v);
    231             SkDEBUGCODE(verbCoordCnt += num_coords(v));
    232         }
    233         SkASSERT(verbCnt == pathCommands.count());
    234         SkASSERT(verbCoordCnt == pathCoords.count());
    235         GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0],
    236                                                     pathCoords.count(), GR_GL_FLOAT,
    237                                                     &pathCoords[0]));
    238         return;
    239     }
    240 #endif
    241     SkAssertResult(init_path_object_for_general_path<false>(gpu, pathID, skPath));
    242 }
    243 
    244 void GrGLPath::InitPathObjectStroke(GrGLGpu* gpu, GrGLuint pathID, const GrStrokeInfo& stroke) {
    245     SkASSERT(stroke.needToApply());
    246     SkASSERT(!stroke.isDashed());
    247     SkASSERT(!stroke.isHairlineStyle());
    248     GR_GL_CALL(gpu->glInterface(),
    249                PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth())));
    250     GR_GL_CALL(gpu->glInterface(),
    251                PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter())));
    252     GrGLenum join = join_to_gl_join(stroke.getJoin());
    253     GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join));
    254     GrGLenum cap = cap_to_gl_cap(stroke.getCap());
    255     GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, cap));
    256     GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUND, 0.02f));
    257 }
    258 
    259 void GrGLPath::InitPathObjectEmptyPath(GrGLGpu* gpu, GrGLuint pathID) {
    260     GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLOAT, nullptr));
    261 }
    262 
    263 GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& origStroke)
    264     : INHERITED(gpu, origSkPath, origStroke),
    265       fPathID(gpu->glPathRendering()->genPaths(1)) {
    266 
    267     if (origSkPath.isEmpty()) {
    268         InitPathObjectEmptyPath(gpu, fPathID);
    269         fShouldStroke = false;
    270         fShouldFill = false;
    271     } else {
    272         const SkPath* skPath = &origSkPath;
    273         SkTLazy<SkPath> tmpPath;
    274         const GrStrokeInfo* stroke = &origStroke;
    275         GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle);
    276 
    277         if (stroke->isDashed()) {
    278             // Skia stroking and NVPR stroking differ with respect to dashing
    279             // pattern.
    280             // Convert a dashing to either a stroke or a fill.
    281             if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
    282                 skPath = tmpPath.get();
    283                 stroke = &tmpStroke;
    284             }
    285         }
    286 
    287         bool didInit = false;
    288         if (stroke->needToApply() && stroke->getCap() != SkPaint::kButt_Cap) {
    289             // Skia stroking and NVPR stroking differ with respect to stroking
    290             // end caps of empty subpaths.
    291             // Convert stroke to fill if path contains empty subpaths.
    292             didInit = InitPathObjectPathDataCheckingDegenerates(gpu, fPathID, *skPath);
    293             if (!didInit) {
    294                 if (!tmpPath.isValid()) {
    295                     tmpPath.init();
    296                 }
    297                 SkAssertResult(stroke->applyToPath(tmpPath.get(), *skPath));
    298                 skPath = tmpPath.get();
    299                 tmpStroke.setFillStyle();
    300                 stroke = &tmpStroke;
    301             }
    302         }
    303 
    304         if (!didInit) {
    305             InitPathObjectPathData(gpu, fPathID, *skPath);
    306         }
    307 
    308         fShouldStroke = stroke->needToApply();
    309         fShouldFill = stroke->isFillStyle() ||
    310                 stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style;
    311 
    312         fFillType = convert_skpath_filltype(skPath->getFillType());
    313         fBounds = skPath->getBounds();
    314 
    315         if (fShouldStroke) {
    316             InitPathObjectStroke(gpu, fPathID, *stroke);
    317 
    318             // FIXME: try to account for stroking, without rasterizing the stroke.
    319             fBounds.outset(stroke->getWidth(), stroke->getWidth());
    320         }
    321     }
    322 
    323     this->registerWithCache();
    324 }
    325 
    326 void GrGLPath::onRelease() {
    327     if (0 != fPathID && this->shouldFreeResources()) {
    328         static_cast<GrGLGpu*>(this->getGpu())->glPathRendering()->deletePaths(fPathID, 1);
    329         fPathID = 0;
    330     }
    331 
    332     INHERITED::onRelease();
    333 }
    334 
    335 void GrGLPath::onAbandon() {
    336     fPathID = 0;
    337 
    338     INHERITED::onAbandon();
    339 }
    340