OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2012 Google Inc. | 3 * Copyright 2012 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 #include "GrGLPath.h" | 9 #include "GrGLPath.h" |
10 #include "GrGLPathRendering.h" | 10 #include "GrGLPathRendering.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gSkCapsToGrGLCaps) == SkPaint::kCapCount); | 79 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gSkCapsToGrGLCaps) == SkPaint::kCapCount); |
80 } | 80 } |
81 | 81 |
82 inline void points_to_coords(const SkPoint points[], size_t first_point, size_t
amount, | 82 inline void points_to_coords(const SkPoint points[], size_t first_point, size_t
amount, |
83 GrGLfloat coords[]) { | 83 GrGLfloat coords[]) { |
84 for (size_t i = 0; i < amount; ++i) { | 84 for (size_t i = 0; i < amount; ++i) { |
85 coords[i * 2] = SkScalarToFloat(points[first_point + i].fX); | 85 coords[i * 2] = SkScalarToFloat(points[first_point + i].fX); |
86 coords[i * 2 + 1] = SkScalarToFloat(points[first_point + i].fY); | 86 coords[i * 2 + 1] = SkScalarToFloat(points[first_point + i].fY); |
87 } | 87 } |
88 } | 88 } |
89 } | 89 |
90 | 90 template<bool checkForDegenerates> |
91 void GrGLPath::InitPathObject(GrGLGpu* gpu, | 91 inline bool init_path_object_for_general_path(GrGLGpu* gpu, GrGLuint pathID, |
92 GrGLuint pathID, | 92 const SkPath& skPath) { |
93 const SkPath& skPath, | 93 SkDEBUGCODE(int numCoords = 0); |
94 const GrStrokeInfo& stroke) { | 94 int verbCnt = skPath.countVerbs(); |
95 SkASSERT(!stroke.isDashed()); | 95 int pointCnt = skPath.countPoints(); |
96 if (!skPath.isEmpty()) { | 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(), &p
athCommands[0], |
| 179 pathCoords.count(), GR_GL_FLOAT,
&pathCoords[0])); |
| 180 return true; |
| 181 } |
| 182 } // namespace |
| 183 |
| 184 bool GrGLPath::InitPathObjectPathDataCheckingDegenerates(GrGLGpu* gpu, GrGLuint
pathID, |
| 185 const SkPath& skPath) { |
| 186 return init_path_object_for_general_path<true>(gpu, pathID, skPath); |
| 187 } |
| 188 |
| 189 void GrGLPath::InitPathObjectPathData(GrGLGpu* gpu, |
| 190 GrGLuint pathID, |
| 191 const SkPath& skPath) { |
| 192 SkASSERT(!skPath.isEmpty()); |
| 193 |
| 194 #ifdef SK_SCALAR_IS_FLOAT |
| 195 // This branch does type punning, converting SkPoint* to GrGLfloat*. |
| 196 if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) { |
97 int verbCnt = skPath.countVerbs(); | 197 int verbCnt = skPath.countVerbs(); |
98 int pointCnt = skPath.countPoints(); | 198 int pointCnt = skPath.countPoints(); |
99 int minCoordCnt = pointCnt * 2; | 199 int coordCnt = pointCnt * 2; |
100 | |
101 SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt); | 200 SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt); |
102 SkSTArray<16, GrGLfloat, true> pathCoords(minCoordCnt); | 201 SkSTArray<16, GrGLfloat, true> pathCoords(coordCnt); |
103 | 202 |
104 SkDEBUGCODE(int numCoords = 0); | 203 static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_tw
o_floats"); |
105 | 204 |
106 if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) { | 205 pathCommands.resize_back(verbCnt); |
107 // This branch does type punning, converting SkPoint* to GrGLfloat*. | 206 pathCoords.resize_back(coordCnt); |
108 static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_no
t_two_floats"); | 207 skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt); |
109 // This branch does not convert with SkScalarToFloat. | 208 skPath.getVerbs(&pathCommands[0], verbCnt); |
110 #ifndef SK_SCALAR_IS_FLOAT | 209 |
111 #error Need SK_SCALAR_IS_FLOAT. | 210 SkDEBUGCODE(int verbCoordCnt = 0); |
| 211 for (int i = 0; i < verbCnt; ++i) { |
| 212 SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); |
| 213 pathCommands[i] = verb_to_gl_path_cmd(v); |
| 214 SkDEBUGCODE(verbCoordCnt += num_coords(v)); |
| 215 } |
| 216 SkASSERT(verbCnt == pathCommands.count()); |
| 217 SkASSERT(verbCoordCnt == pathCoords.count()); |
| 218 GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count()
, &pathCommands[0], |
| 219 pathCoords.count(), GR_GL_FL
OAT, |
| 220 &pathCoords[0])); |
| 221 return; |
| 222 } |
112 #endif | 223 #endif |
113 pathCommands.resize_back(verbCnt); | 224 SkAssertResult(init_path_object_for_general_path<false>(gpu, pathID, skPath)
); |
114 pathCoords.resize_back(minCoordCnt); | 225 } |
115 skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCn
t); | 226 |
116 skPath.getVerbs(&pathCommands[0], verbCnt); | 227 void GrGLPath::InitPathObjectStroke(GrGLGpu* gpu, GrGLuint pathID, const GrStrok
eInfo& stroke) { |
117 for (int i = 0; i < verbCnt; ++i) { | 228 SkASSERT(stroke.needToApply()); |
118 SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); | 229 SkASSERT(!stroke.isDashed()); |
119 pathCommands[i] = verb_to_gl_path_cmd(v); | 230 SkASSERT(!stroke.isHairlineStyle()); |
120 SkDEBUGCODE(numCoords += num_coords(v)); | 231 GR_GL_CALL(gpu->glInterface(), |
121 } | 232 PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(s
troke.getWidth()))); |
122 } else { | 233 GR_GL_CALL(gpu->glInterface(), |
123 SkPoint points[4]; | 234 PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(st
roke.getMiter()))); |
124 SkPath::RawIter iter(skPath); | 235 GrGLenum join = join_to_gl_join(stroke.getJoin()); |
125 SkPath::Verb verb; | 236 GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE,
join)); |
126 while ((verb = iter.next(points)) != SkPath::kDone_Verb) { | 237 GrGLenum cap = cap_to_gl_cap(stroke.getCap()); |
127 pathCommands.push_back(verb_to_gl_path_cmd(verb)); | 238 GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, c
ap)); |
128 GrGLfloat coords[6]; | 239 GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUN
D, 0.02f)); |
129 int coordsForVerb; | 240 } |
130 switch (verb) { | 241 |
131 case SkPath::kMove_Verb: | 242 void GrGLPath::InitPathObjectEmptyPath(GrGLGpu* gpu, GrGLuint pathID) { |
132 points_to_coords(points, 0, 1, coords); | 243 GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLO
AT, nullptr)); |
133 coordsForVerb = 2; | |
134 break; | |
135 case SkPath::kLine_Verb: | |
136 points_to_coords(points, 1, 1, coords); | |
137 coordsForVerb = 2; | |
138 break; | |
139 case SkPath::kConic_Verb: | |
140 points_to_coords(points, 1, 2, coords); | |
141 coords[4] = SkScalarToFloat(iter.conicWeight()); | |
142 coordsForVerb = 5; | |
143 break; | |
144 case SkPath::kQuad_Verb: | |
145 points_to_coords(points, 1, 2, coords); | |
146 coordsForVerb = 4; | |
147 break; | |
148 case SkPath::kCubic_Verb: | |
149 points_to_coords(points, 1, 3, coords); | |
150 coordsForVerb = 6; | |
151 break; | |
152 case SkPath::kClose_Verb: | |
153 continue; | |
154 default: | |
155 SkASSERT(false); // Not reached. | |
156 continue; | |
157 } | |
158 SkDEBUGCODE(numCoords += num_coords(verb)); | |
159 pathCoords.push_back_n(coordsForVerb, coords); | |
160 } | |
161 } | |
162 | |
163 SkASSERT(verbCnt == pathCommands.count()); | |
164 SkASSERT(numCoords == pathCoords.count()); | |
165 | |
166 GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count()
, &pathCommands[0], | |
167 pathCoords.count(), GR_GL_FLOAT, &pathCoords[0])); | |
168 } else { | |
169 GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL
_FLOAT, nullptr)); | |
170 } | |
171 | |
172 if (stroke.needToApply()) { | |
173 SkASSERT(!stroke.isHairlineStyle()); | |
174 GR_GL_CALL(gpu->glInterface(), | |
175 PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stro
ke.getWidth()))); | |
176 GR_GL_CALL(gpu->glInterface(), | |
177 PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(strok
e.getMiter()))); | |
178 GrGLenum join = join_to_gl_join(stroke.getJoin()); | |
179 GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_ST
YLE, join)); | |
180 GrGLenum cap = cap_to_gl_cap(stroke.getCap()); | |
181 GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAP
S, cap)); | |
182 GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_
BOUND, 0.02f)); | |
183 } | |
184 } | 244 } |
185 | 245 |
186 GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& o
rigStroke) | 246 GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& o
rigStroke) |
187 : INHERITED(gpu, origSkPath, origStroke), | 247 : INHERITED(gpu, origSkPath, origStroke), |
188 fPathID(gpu->glPathRendering()->genPaths(1)) { | 248 fPathID(gpu->glPathRendering()->genPaths(1)) { |
189 // Convert a dashing to either a stroke or a fill. | 249 |
190 const SkPath* skPath = &origSkPath; | 250 if (origSkPath.isEmpty()) { |
191 SkTLazy<SkPath> tmpPath; | 251 InitPathObjectEmptyPath(gpu, fPathID); |
192 const GrStrokeInfo* stroke = &origStroke; | 252 fShouldStroke = false; |
193 GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle); | 253 fShouldFill = false; |
194 | 254 } else { |
195 if (stroke->isDashed()) { | 255 const SkPath* skPath = &origSkPath; |
196 if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) { | 256 SkTLazy<SkPath> tmpPath; |
197 skPath = tmpPath.get(); | 257 const GrStrokeInfo* stroke = &origStroke; |
198 stroke = &tmpStroke; | 258 GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle); |
199 } | 259 |
200 } | 260 if (stroke->isDashed()) { |
201 | 261 // Skia stroking and NVPR stroking differ with respect to dashing |
202 InitPathObject(gpu, fPathID, *skPath, *stroke); | 262 // pattern. |
203 | 263 // Convert a dashing to either a stroke or a fill. |
204 fShouldStroke = stroke->needToApply(); | 264 if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) { |
205 fShouldFill = stroke->isFillStyle() || | 265 skPath = tmpPath.get(); |
206 stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style; | 266 stroke = &tmpStroke; |
207 | 267 } |
208 if (fShouldStroke) { | 268 } |
209 // FIXME: try to account for stroking, without rasterizing the stroke. | 269 |
210 fBounds.outset(stroke->getWidth(), stroke->getWidth()); | 270 bool didInit = false; |
| 271 if (stroke->needToApply() && stroke->getCap() != SkPaint::kButt_Cap) { |
| 272 // Skia stroking and NVPR stroking differ with respect to stroking |
| 273 // end caps of empty subpaths. |
| 274 // Convert stroke to fill if path contains empty subpaths. |
| 275 didInit = InitPathObjectPathDataCheckingDegenerates(gpu, fPathID, *s
kPath); |
| 276 if (!didInit) { |
| 277 if (!tmpPath.isValid()) { |
| 278 tmpPath.init(); |
| 279 } |
| 280 SkAssertResult(stroke->applyToPath(tmpPath.get(), *skPath)); |
| 281 skPath = tmpPath.get(); |
| 282 tmpStroke.setFillStyle(); |
| 283 stroke = &tmpStroke; |
| 284 } |
| 285 } |
| 286 |
| 287 if (!didInit) { |
| 288 InitPathObjectPathData(gpu, fPathID, *skPath); |
| 289 } |
| 290 |
| 291 fShouldStroke = stroke->needToApply(); |
| 292 fShouldFill = stroke->isFillStyle() || |
| 293 stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style; |
| 294 |
| 295 if (fShouldStroke) { |
| 296 InitPathObjectStroke(gpu, fPathID, *stroke); |
| 297 |
| 298 // FIXME: try to account for stroking, without rasterizing the strok
e. |
| 299 fBounds.outset(stroke->getWidth(), stroke->getWidth()); |
| 300 } |
211 } | 301 } |
212 | 302 |
213 this->registerWithCache(); | 303 this->registerWithCache(); |
214 } | 304 } |
215 | 305 |
216 void GrGLPath::onRelease() { | 306 void GrGLPath::onRelease() { |
217 if (0 != fPathID && this->shouldFreeResources()) { | 307 if (0 != fPathID && this->shouldFreeResources()) { |
218 static_cast<GrGLGpu*>(this->getGpu())->glPathRendering()->deletePaths(fP
athID, 1); | 308 static_cast<GrGLGpu*>(this->getGpu())->glPathRendering()->deletePaths(fP
athID, 1); |
219 fPathID = 0; | 309 fPathID = 0; |
220 } | 310 } |
221 | 311 |
222 INHERITED::onRelease(); | 312 INHERITED::onRelease(); |
223 } | 313 } |
224 | 314 |
225 void GrGLPath::onAbandon() { | 315 void GrGLPath::onAbandon() { |
226 fPathID = 0; | 316 fPathID = 0; |
227 | 317 |
228 INHERITED::onAbandon(); | 318 INHERITED::onAbandon(); |
229 } | 319 } |
OLD | NEW |