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 {
verb_to_gl_path_cmd(SkPath::Verb verb)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
num_coords(SkPath::Verb verb)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 
join_to_gl_join(SkPaint::Join join)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 
cap_to_gl_cap(SkPaint::Cap cap)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 
points_to_coords(const SkPoint points[],size_t first_point,size_t amount,GrGLfloat coords[])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>
init_path_object_for_general_path(GrGLGpu * gpu,GrGLuint pathID,const SkPath & skPath)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  */
convert_skpath_filltype(SkPath::FillType fill)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 
InitPathObjectPathDataCheckingDegenerates(GrGLGpu * gpu,GrGLuint pathID,const SkPath & skPath)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 
InitPathObjectPathData(GrGLGpu * gpu,GrGLuint pathID,const SkPath & skPath)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 
InitPathObjectStroke(GrGLGpu * gpu,GrGLuint pathID,const GrStrokeInfo & stroke)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 
InitPathObjectEmptyPath(GrGLGpu * gpu,GrGLuint pathID)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 
GrGLPath(GrGLGpu * gpu,const SkPath & origSkPath,const GrStrokeInfo & origStroke)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 
onRelease()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 
onAbandon()335 void GrGLPath::onAbandon() {
336     fPathID = 0;
337 
338     INHERITED::onAbandon();
339 }
340