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