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
13namespace {
14inline 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
35inline 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
56inline 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
69inline 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
82inline 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
90template<bool checkForDegenerates>
91inline 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 */
186static 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
201bool GrGLPath::InitPathObjectPathDataCheckingDegenerates(GrGLGpu* gpu, GrGLuint pathID,
202                                                         const SkPath& skPath) {
203    return init_path_object_for_general_path<true>(gpu, pathID, skPath);
204}
205
206void 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
244void 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
259void GrGLPath::InitPathObjectEmptyPath(GrGLGpu* gpu, GrGLuint pathID) {
260    GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLOAT, nullptr));
261}
262
263GrGLPath::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
326void 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
335void GrGLPath::onAbandon() {
336    fPathID = 0;
337
338    INHERITED::onAbandon();
339}
340