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 "GrGLPath.h" 9 #include "GrGLPathRendering.h" 10 #include "GrGLGpu.h" 11 #include "GrStyle.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 #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(SkPath::FillType fill) { 197 switch (fill) { 198 default: 199 SK_ABORT("Incomplete Switch\n"); 200 case SkPath::kWinding_FillType: 201 case SkPath::kInverseWinding_FillType: 202 return GrPathRendering::kWinding_FillType; 203 case SkPath::kEvenOdd_FillType: 204 case SkPath::kInverseEvenOdd_FillType: 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 SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt); 228 SkSTArray<16, GrGLfloat, true> pathCoords(coordCnt); 229 230 static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_two_floats"); 231 232 pathCommands.resize_back(verbCnt); 233 pathCoords.resize_back(coordCnt); 234 skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt); 235 skPath.getVerbs(&pathCommands[0], verbCnt); 236 237 SkDEBUGCODE(int verbCoordCnt = 0); 238 for (int i = 0; i < verbCnt; ++i) { 239 SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); 240 pathCommands[i] = verb_to_gl_path_cmd(v); 241 SkDEBUGCODE(verbCoordCnt += num_coords(v)); 242 } 243 SkASSERT(verbCnt == pathCommands.count()); 244 SkASSERT(verbCoordCnt == pathCoords.count()); 245 SkDEBUGCODE(verify_floats(&pathCoords[0], pathCoords.count())); 246 GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0], 247 pathCoords.count(), GR_GL_FLOAT, 248 &pathCoords[0])); 249 return; 250 } 251 #endif 252 SkAssertResult(init_path_object_for_general_path<false>(gpu, pathID, skPath)); 253 } 254 255 void GrGLPath::InitPathObjectStroke(GrGLGpu* gpu, GrGLuint pathID, const SkStrokeRec& stroke) { 256 SkASSERT(!stroke.isHairlineStyle()); 257 GR_GL_CALL(gpu->glInterface(), 258 PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth()))); 259 GR_GL_CALL(gpu->glInterface(), 260 PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter()))); 261 GrGLenum join = join_to_gl_join(stroke.getJoin()); 262 GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join)); 263 GrGLenum cap = cap_to_gl_cap(stroke.getCap()); 264 GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, cap)); 265 GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUND, 0.02f)); 266 } 267 268 void GrGLPath::InitPathObjectEmptyPath(GrGLGpu* gpu, GrGLuint pathID) { 269 GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLOAT, nullptr)); 270 } 271 272 GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStyle& style) 273 : INHERITED(gpu, origSkPath, style), 274 fPathID(gpu->glPathRendering()->genPaths(1)) { 275 276 if (origSkPath.isEmpty()) { 277 InitPathObjectEmptyPath(gpu, fPathID); 278 fShouldStroke = false; 279 fShouldFill = false; 280 } else { 281 const SkPath* skPath = &origSkPath; 282 SkTLazy<SkPath> tmpPath; 283 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 284 285 if (style.pathEffect()) { 286 // Skia stroking and NVPR stroking differ with respect to dashing 287 // pattern. 288 // Convert a dashing (or other path effect) to either a stroke or a fill. 289 if (style.applyPathEffectToPath(tmpPath.init(), &stroke, *skPath, SK_Scalar1)) { 290 skPath = tmpPath.get(); 291 } 292 } else { 293 stroke = style.strokeRec(); 294 } 295 296 bool didInit = false; 297 if (stroke.needToApply() && stroke.getCap() != SkPaint::kButt_Cap) { 298 // Skia stroking and NVPR stroking differ with respect to stroking 299 // end caps of empty subpaths. 300 // Convert stroke to fill if path contains empty subpaths. 301 didInit = InitPathObjectPathDataCheckingDegenerates(gpu, fPathID, *skPath); 302 if (!didInit) { 303 if (!tmpPath.isValid()) { 304 tmpPath.init(); 305 } 306 SkAssertResult(stroke.applyToPath(tmpPath.get(), *skPath)); 307 skPath = tmpPath.get(); 308 stroke.setFillStyle(); 309 } 310 } 311 312 if (!didInit) { 313 InitPathObjectPathData(gpu, fPathID, *skPath); 314 } 315 316 fShouldStroke = stroke.needToApply(); 317 fShouldFill = stroke.isFillStyle() || 318 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style; 319 320 fFillType = convert_skpath_filltype(skPath->getFillType()); 321 fBounds = skPath->getBounds(); 322 SkScalar radius = stroke.getInflationRadius(); 323 fBounds.outset(radius, radius); 324 if (fShouldStroke) { 325 InitPathObjectStroke(gpu, fPathID, stroke); 326 } 327 } 328 329 this->registerWithCache(SkBudgeted::kYes); 330 } 331 332 void GrGLPath::onRelease() { 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