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 "src/gpu/GrStyle.h" |
9 | #include "src/gpu/gl/GrGLGpu.h" |
10 | #include "src/gpu/gl/GrGLPath.h" |
11 | #include "src/gpu/gl/GrGLPathRendering.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 | static_assert(0 == SkPath::kMove_Verb); |
24 | static_assert(1 == SkPath::kLine_Verb); |
25 | static_assert(2 == SkPath::kQuad_Verb); |
26 | static_assert(3 == SkPath::kConic_Verb); |
27 | static_assert(4 == SkPath::kCubic_Verb); |
28 | 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 | static_assert(0 == SkPath::kMove_Verb); |
45 | static_assert(1 == SkPath::kLine_Verb); |
46 | static_assert(2 == SkPath::kQuad_Verb); |
47 | static_assert(3 == SkPath::kConic_Verb); |
48 | static_assert(4 == SkPath::kCubic_Verb); |
49 | 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 | static_assert(0 == SkPaint::kMiter_Join); |
64 | static_assert(1 == SkPaint::kRound_Join); |
65 | static_assert(2 == SkPaint::kBevel_Join); |
66 | 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 | static_assert(0 == SkPaint::kButt_Cap); |
77 | static_assert(1 == SkPaint::kRound_Cap); |
78 | static_assert(2 == SkPaint::kSquare_Cap); |
79 | static_assert(SK_ARRAY_COUNT(gSkCapsToGrGLCaps) == SkPaint::kCapCount); |
80 | } |
81 | |
82 | #ifdef SK_DEBUG |
83 | inline void verify_floats(const float* floats, int count) { |
84 | for (int i = 0; i < count; ++i) { |
85 | SkASSERT(!SkScalarIsNaN(SkFloatToScalar(floats[i]))); |
86 | } |
87 | } |
88 | #endif |
89 | |
90 | inline void points_to_coords(const SkPoint points[], size_t first_point, size_t amount, |
91 | GrGLfloat coords[]) { |
92 | for (size_t i = 0; i < amount; ++i) { |
93 | coords[i * 2] = SkScalarToFloat(points[first_point + i].fX); |
94 | coords[i * 2 + 1] = SkScalarToFloat(points[first_point + i].fY); |
95 | } |
96 | } |
97 | |
98 | template<bool checkForDegenerates> |
99 | inline bool init_path_object_for_general_path(GrGLGpu* gpu, GrGLuint pathID, |
100 | const SkPath& skPath) { |
101 | SkDEBUGCODE(int numCoords = 0); |
102 | int verbCnt = skPath.countVerbs(); |
103 | int pointCnt = skPath.countPoints(); |
104 | int minCoordCnt = pointCnt * 2; |
105 | |
106 | SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt); |
107 | SkSTArray<16, GrGLfloat, true> pathCoords(minCoordCnt); |
108 | bool lastVerbWasMove = true; // A path with just "close;" means "moveto(0,0); close;" |
109 | SkPoint points[4]; |
110 | SkPath::RawIter iter(skPath); |
111 | SkPath::Verb verb; |
112 | while ((verb = iter.next(points)) != SkPath::kDone_Verb) { |
113 | pathCommands.push_back(verb_to_gl_path_cmd(verb)); |
114 | GrGLfloat coords[6]; |
115 | int coordsForVerb; |
116 | switch (verb) { |
117 | case SkPath::kMove_Verb: |
118 | if (checkForDegenerates) { |
119 | lastVerbWasMove = true; |
120 | } |
121 | points_to_coords(points, 0, 1, coords); |
122 | coordsForVerb = 2; |
123 | break; |
124 | case SkPath::kLine_Verb: |
125 | if (checkForDegenerates) { |
126 | if (SkPath::IsLineDegenerate(points[0], points[1], true)) { |
127 | return false; |
128 | } |
129 | lastVerbWasMove = false; |
130 | } |
131 | |
132 | points_to_coords(points, 1, 1, coords); |
133 | coordsForVerb = 2; |
134 | break; |
135 | case SkPath::kConic_Verb: |
136 | if (checkForDegenerates) { |
137 | if (SkPath::IsQuadDegenerate(points[0], points[1], points[2], true)) { |
138 | return false; |
139 | } |
140 | lastVerbWasMove = false; |
141 | } |
142 | points_to_coords(points, 1, 2, coords); |
143 | coords[4] = SkScalarToFloat(iter.conicWeight()); |
144 | coordsForVerb = 5; |
145 | break; |
146 | case SkPath::kQuad_Verb: |
147 | if (checkForDegenerates) { |
148 | if (SkPath::IsQuadDegenerate(points[0], points[1], points[2], true)) { |
149 | return false; |
150 | } |
151 | lastVerbWasMove = false; |
152 | } |
153 | points_to_coords(points, 1, 2, coords); |
154 | coordsForVerb = 4; |
155 | break; |
156 | case SkPath::kCubic_Verb: |
157 | if (checkForDegenerates) { |
158 | if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3], |
159 | true)) { |
160 | return false; |
161 | } |
162 | lastVerbWasMove = false; |
163 | } |
164 | points_to_coords(points, 1, 3, coords); |
165 | coordsForVerb = 6; |
166 | break; |
167 | case SkPath::kClose_Verb: |
168 | if (checkForDegenerates) { |
169 | if (lastVerbWasMove) { |
170 | // Interpret "move(x,y);close;" as "move(x,y);lineto(x,y);close;". |
171 | // which produces a degenerate segment. |
172 | return false; |
173 | } |
174 | } |
175 | continue; |
176 | default: |
177 | SkASSERT(false); // Not reached. |
178 | continue; |
179 | } |
180 | SkDEBUGCODE(numCoords += num_coords(verb)); |
181 | SkDEBUGCODE(verify_floats(coords, coordsForVerb)); |
182 | pathCoords.push_back_n(coordsForVerb, coords); |
183 | } |
184 | SkASSERT(verbCnt == pathCommands.count()); |
185 | SkASSERT(numCoords == pathCoords.count()); |
186 | |
187 | GR_GL_CALL(gpu->glInterface(), |
188 | PathCommands(pathID, pathCommands.count(), pathCommands.begin(), |
189 | pathCoords.count(), GR_GL_FLOAT, pathCoords.begin())); |
190 | return true; |
191 | } |
192 | |
193 | /* |
194 | * For now paths only natively support winding and even odd fill types |
195 | */ |
196 | static GrPathRendering::FillType convert_skpath_filltype(SkPathFillType fill) { |
197 | switch (fill) { |
198 | default: |
199 | SK_ABORT("Incomplete Switch\n" ); |
200 | case SkPathFillType::kWinding: |
201 | case SkPathFillType::kInverseWinding: |
202 | return GrPathRendering::kWinding_FillType; |
203 | case SkPathFillType::kEvenOdd: |
204 | case SkPathFillType::kInverseEvenOdd: |
205 | return GrPathRendering::kEvenOdd_FillType; |
206 | } |
207 | } |
208 | |
209 | } // namespace |
210 | |
211 | bool GrGLPath::InitPathObjectPathDataCheckingDegenerates(GrGLGpu* gpu, GrGLuint pathID, |
212 | const SkPath& skPath) { |
213 | return init_path_object_for_general_path<true>(gpu, pathID, skPath); |
214 | } |
215 | |
216 | void GrGLPath::InitPathObjectPathData(GrGLGpu* gpu, |
217 | GrGLuint pathID, |
218 | const SkPath& skPath) { |
219 | SkASSERT(!skPath.isEmpty()); |
220 | |
221 | #if 1 // SK_SCALAR_IS_FLOAT |
222 | // This branch does type punning, converting SkPoint* to GrGLfloat*. |
223 | if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) { |
224 | int verbCnt = skPath.countVerbs(); |
225 | int pointCnt = skPath.countPoints(); |
226 | int coordCnt = pointCnt * 2; |
227 | SkAutoSTArray<16, GrGLubyte> pathCommands(verbCnt); |
228 | SkAutoSTArray<16, GrGLfloat> pathCoords(coordCnt); |
229 | |
230 | static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_two_floats" ); |
231 | |
232 | skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt); |
233 | skPath.getVerbs(&pathCommands[0], verbCnt); |
234 | |
235 | SkDEBUGCODE(int verbCoordCnt = 0); |
236 | for (int i = 0; i < verbCnt; ++i) { |
237 | SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); |
238 | pathCommands[i] = verb_to_gl_path_cmd(v); |
239 | SkDEBUGCODE(verbCoordCnt += num_coords(v)); |
240 | } |
241 | SkASSERT(verbCnt == pathCommands.count()); |
242 | SkASSERT(verbCoordCnt == pathCoords.count()); |
243 | SkDEBUGCODE(verify_floats(&pathCoords[0], pathCoords.count())); |
244 | GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0], |
245 | pathCoords.count(), GR_GL_FLOAT, |
246 | &pathCoords[0])); |
247 | return; |
248 | } |
249 | #endif |
250 | SkAssertResult(init_path_object_for_general_path<false>(gpu, pathID, skPath)); |
251 | } |
252 | |
253 | void GrGLPath::InitPathObjectStroke(GrGLGpu* gpu, GrGLuint pathID, const SkStrokeRec& stroke) { |
254 | SkASSERT(!stroke.isHairlineStyle()); |
255 | GR_GL_CALL(gpu->glInterface(), |
256 | PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth()))); |
257 | GR_GL_CALL(gpu->glInterface(), |
258 | PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter()))); |
259 | GrGLenum join = join_to_gl_join(stroke.getJoin()); |
260 | GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join)); |
261 | GrGLenum cap = cap_to_gl_cap(stroke.getCap()); |
262 | GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, cap)); |
263 | GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUND, 0.02f)); |
264 | } |
265 | |
266 | void GrGLPath::InitPathObjectEmptyPath(GrGLGpu* gpu, GrGLuint pathID) { |
267 | GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLOAT, nullptr)); |
268 | } |
269 | |
270 | GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStyle& style) |
271 | : INHERITED(gpu, origSkPath, style), |
272 | fPathID(gpu->glPathRendering()->genPaths(1)) { |
273 | |
274 | if (origSkPath.isEmpty()) { |
275 | InitPathObjectEmptyPath(gpu, fPathID); |
276 | fShouldStroke = false; |
277 | fShouldFill = false; |
278 | } else { |
279 | const SkPath* skPath = &origSkPath; |
280 | SkTLazy<SkPath> tmpPath; |
281 | SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
282 | |
283 | if (style.pathEffect()) { |
284 | // Skia stroking and NVPR stroking differ with respect to dashing |
285 | // pattern. |
286 | // Convert a dashing (or other path effect) to either a stroke or a fill. |
287 | if (style.applyPathEffectToPath(tmpPath.init(), &stroke, *skPath, SK_Scalar1)) { |
288 | skPath = tmpPath.get(); |
289 | } |
290 | } else { |
291 | stroke = style.strokeRec(); |
292 | } |
293 | |
294 | bool didInit = false; |
295 | if (stroke.needToApply() && stroke.getCap() != SkPaint::kButt_Cap) { |
296 | // Skia stroking and NVPR stroking differ with respect to stroking |
297 | // end caps of empty subpaths. |
298 | // Convert stroke to fill if path contains empty subpaths. |
299 | didInit = InitPathObjectPathDataCheckingDegenerates(gpu, fPathID, *skPath); |
300 | if (!didInit) { |
301 | if (!tmpPath.isValid()) { |
302 | tmpPath.init(); |
303 | } |
304 | SkAssertResult(stroke.applyToPath(tmpPath.get(), *skPath)); |
305 | skPath = tmpPath.get(); |
306 | stroke.setFillStyle(); |
307 | } |
308 | } |
309 | |
310 | if (!didInit) { |
311 | InitPathObjectPathData(gpu, fPathID, *skPath); |
312 | } |
313 | |
314 | fShouldStroke = stroke.needToApply(); |
315 | fShouldFill = stroke.isFillStyle() || |
316 | stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style; |
317 | |
318 | fFillType = convert_skpath_filltype(skPath->getFillType()); |
319 | fBounds = skPath->getBounds(); |
320 | SkScalar radius = stroke.getInflationRadius(); |
321 | fBounds.outset(radius, radius); |
322 | if (fShouldStroke) { |
323 | InitPathObjectStroke(gpu, fPathID, stroke); |
324 | } |
325 | } |
326 | |
327 | this->registerWithCache(SkBudgeted::kYes); |
328 | } |
329 | |
330 | void GrGLPath::onRelease() { |
331 | TRACE_EVENT0("skia.gpu" , TRACE_FUNC); |
332 | |
333 | if (0 != fPathID) { |
334 | static_cast<GrGLGpu*>(this->getGpu())->glPathRendering()->deletePaths(fPathID, 1); |
335 | fPathID = 0; |
336 | } |
337 | |
338 | INHERITED::onRelease(); |
339 | } |
340 | |
341 | void GrGLPath::onAbandon() { |
342 | fPathID = 0; |
343 | |
344 | INHERITED::onAbandon(); |
345 | } |
346 | |