Index: src/gpu/gl/GrGLPath.cpp |
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp |
index 1dfeaee7b3f8e0f7144cae87b7cb49da2cde5c9f..d1fc39dffcc554e22035ced711275323b2baa9fb 100644 |
--- a/src/gpu/gl/GrGLPath.cpp |
+++ b/src/gpu/gl/GrGLPath.cpp |
@@ -86,128 +86,218 @@ inline void points_to_coords(const SkPoint points[], size_t first_point, size_t |
coords[i * 2 + 1] = SkScalarToFloat(points[first_point + i].fY); |
} |
} |
+ |
+template<bool checkForDegenerates> |
+inline bool init_path_object_for_general_path(GrGLGpu* gpu, GrGLuint pathID, |
+ const SkPath& skPath) { |
+ SkDEBUGCODE(int numCoords = 0); |
+ int verbCnt = skPath.countVerbs(); |
+ int pointCnt = skPath.countPoints(); |
+ int minCoordCnt = pointCnt * 2; |
+ |
+ SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt); |
+ SkSTArray<16, GrGLfloat, true> pathCoords(minCoordCnt); |
+ bool lastVerbWasMove = true; // A path with just "close;" means "moveto(0,0); close;" |
+ SkPoint points[4]; |
+ SkPath::RawIter iter(skPath); |
+ SkPath::Verb verb; |
+ while ((verb = iter.next(points)) != SkPath::kDone_Verb) { |
+ pathCommands.push_back(verb_to_gl_path_cmd(verb)); |
+ GrGLfloat coords[6]; |
+ int coordsForVerb; |
+ switch (verb) { |
+ case SkPath::kMove_Verb: |
+ if (checkForDegenerates) { |
+ lastVerbWasMove = true; |
+ } |
+ points_to_coords(points, 0, 1, coords); |
+ coordsForVerb = 2; |
+ break; |
+ case SkPath::kLine_Verb: |
+ if (checkForDegenerates) { |
+ if (SkPath::IsLineDegenerate(points[0], points[1], true)) { |
+ return false; |
+ } |
+ lastVerbWasMove = false; |
+ } |
+ |
+ points_to_coords(points, 1, 1, coords); |
+ coordsForVerb = 2; |
+ break; |
+ case SkPath::kConic_Verb: |
+ if (checkForDegenerates) { |
+ if (SkPath::IsQuadDegenerate(points[0], points[1], points[2], true)) { |
+ return false; |
+ } |
+ lastVerbWasMove = false; |
+ } |
+ points_to_coords(points, 1, 2, coords); |
+ coords[4] = SkScalarToFloat(iter.conicWeight()); |
+ coordsForVerb = 5; |
+ break; |
+ case SkPath::kQuad_Verb: |
+ if (checkForDegenerates) { |
+ if (SkPath::IsQuadDegenerate(points[0], points[1], points[2], true)) { |
+ return false; |
+ } |
+ lastVerbWasMove = false; |
+ } |
+ points_to_coords(points, 1, 2, coords); |
+ coordsForVerb = 4; |
+ break; |
+ case SkPath::kCubic_Verb: |
+ if (checkForDegenerates) { |
+ if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3], |
+ true)) { |
+ return false; |
+ } |
+ lastVerbWasMove = false; |
+ } |
+ points_to_coords(points, 1, 3, coords); |
+ coordsForVerb = 6; |
+ break; |
+ case SkPath::kClose_Verb: |
+ if (checkForDegenerates) { |
+ if (lastVerbWasMove) { |
+ // Interpret "move(x,y);close;" as "move(x,y);lineto(x,y);close;". |
+ // which produces a degenerate segment. |
+ return false; |
+ } |
+ } |
+ continue; |
+ default: |
+ SkASSERT(false); // Not reached. |
+ continue; |
+ } |
+ SkDEBUGCODE(numCoords += num_coords(verb)); |
+ pathCoords.push_back_n(coordsForVerb, coords); |
+ } |
+ SkASSERT(verbCnt == pathCommands.count()); |
+ SkASSERT(numCoords == pathCoords.count()); |
+ |
+ GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0], |
+ pathCoords.count(), GR_GL_FLOAT, &pathCoords[0])); |
+ return true; |
} |
+} // namespace |
-void GrGLPath::InitPathObject(GrGLGpu* gpu, |
- GrGLuint pathID, |
- const SkPath& skPath, |
- const GrStrokeInfo& stroke) { |
- SkASSERT(!stroke.isDashed()); |
- if (!skPath.isEmpty()) { |
+bool GrGLPath::InitPathObjectPathDataCheckingDegenerates(GrGLGpu* gpu, GrGLuint pathID, |
+ const SkPath& skPath) { |
+ return init_path_object_for_general_path<true>(gpu, pathID, skPath); |
+} |
+ |
+void GrGLPath::InitPathObjectPathData(GrGLGpu* gpu, |
+ GrGLuint pathID, |
+ const SkPath& skPath) { |
+ SkASSERT(!skPath.isEmpty()); |
+ |
+#ifdef SK_SCALAR_IS_FLOAT |
+ // This branch does type punning, converting SkPoint* to GrGLfloat*. |
+ if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) { |
int verbCnt = skPath.countVerbs(); |
int pointCnt = skPath.countPoints(); |
- int minCoordCnt = pointCnt * 2; |
- |
+ int coordCnt = pointCnt * 2; |
SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt); |
- SkSTArray<16, GrGLfloat, true> pathCoords(minCoordCnt); |
+ SkSTArray<16, GrGLfloat, true> pathCoords(coordCnt); |
- SkDEBUGCODE(int numCoords = 0); |
+ static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_two_floats"); |
- if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) { |
- // This branch does type punning, converting SkPoint* to GrGLfloat*. |
- static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_two_floats"); |
- // This branch does not convert with SkScalarToFloat. |
-#ifndef SK_SCALAR_IS_FLOAT |
-#error Need SK_SCALAR_IS_FLOAT. |
-#endif |
- pathCommands.resize_back(verbCnt); |
- pathCoords.resize_back(minCoordCnt); |
- skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt); |
- skPath.getVerbs(&pathCommands[0], verbCnt); |
- for (int i = 0; i < verbCnt; ++i) { |
- SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); |
- pathCommands[i] = verb_to_gl_path_cmd(v); |
- SkDEBUGCODE(numCoords += num_coords(v)); |
- } |
- } else { |
- SkPoint points[4]; |
- SkPath::RawIter iter(skPath); |
- SkPath::Verb verb; |
- while ((verb = iter.next(points)) != SkPath::kDone_Verb) { |
- pathCommands.push_back(verb_to_gl_path_cmd(verb)); |
- GrGLfloat coords[6]; |
- int coordsForVerb; |
- switch (verb) { |
- case SkPath::kMove_Verb: |
- points_to_coords(points, 0, 1, coords); |
- coordsForVerb = 2; |
- break; |
- case SkPath::kLine_Verb: |
- points_to_coords(points, 1, 1, coords); |
- coordsForVerb = 2; |
- break; |
- case SkPath::kConic_Verb: |
- points_to_coords(points, 1, 2, coords); |
- coords[4] = SkScalarToFloat(iter.conicWeight()); |
- coordsForVerb = 5; |
- break; |
- case SkPath::kQuad_Verb: |
- points_to_coords(points, 1, 2, coords); |
- coordsForVerb = 4; |
- break; |
- case SkPath::kCubic_Verb: |
- points_to_coords(points, 1, 3, coords); |
- coordsForVerb = 6; |
- break; |
- case SkPath::kClose_Verb: |
- continue; |
- default: |
- SkASSERT(false); // Not reached. |
- continue; |
- } |
- SkDEBUGCODE(numCoords += num_coords(verb)); |
- pathCoords.push_back_n(coordsForVerb, coords); |
- } |
- } |
+ pathCommands.resize_back(verbCnt); |
+ pathCoords.resize_back(coordCnt); |
+ skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt); |
+ skPath.getVerbs(&pathCommands[0], verbCnt); |
+ SkDEBUGCODE(int verbCoordCnt = 0); |
+ for (int i = 0; i < verbCnt; ++i) { |
+ SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); |
+ pathCommands[i] = verb_to_gl_path_cmd(v); |
+ SkDEBUGCODE(verbCoordCnt += num_coords(v)); |
+ } |
SkASSERT(verbCnt == pathCommands.count()); |
- SkASSERT(numCoords == pathCoords.count()); |
- |
+ SkASSERT(verbCoordCnt == pathCoords.count()); |
GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0], |
- pathCoords.count(), GR_GL_FLOAT, &pathCoords[0])); |
- } else { |
- GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLOAT, nullptr)); |
+ pathCoords.count(), GR_GL_FLOAT, |
+ &pathCoords[0])); |
+ return; |
} |
+#endif |
+ SkAssertResult(init_path_object_for_general_path<false>(gpu, pathID, skPath)); |
+} |
- if (stroke.needToApply()) { |
- SkASSERT(!stroke.isHairlineStyle()); |
- GR_GL_CALL(gpu->glInterface(), |
- PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth()))); |
- GR_GL_CALL(gpu->glInterface(), |
- PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter()))); |
- GrGLenum join = join_to_gl_join(stroke.getJoin()); |
- GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join)); |
- GrGLenum cap = cap_to_gl_cap(stroke.getCap()); |
- GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, cap)); |
- GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUND, 0.02f)); |
- } |
+void GrGLPath::InitPathObjectStroke(GrGLGpu* gpu, GrGLuint pathID, const GrStrokeInfo& stroke) { |
+ SkASSERT(stroke.needToApply()); |
+ SkASSERT(!stroke.isDashed()); |
+ SkASSERT(!stroke.isHairlineStyle()); |
+ GR_GL_CALL(gpu->glInterface(), |
+ PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth()))); |
+ GR_GL_CALL(gpu->glInterface(), |
+ PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter()))); |
+ GrGLenum join = join_to_gl_join(stroke.getJoin()); |
+ GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join)); |
+ GrGLenum cap = cap_to_gl_cap(stroke.getCap()); |
+ GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, cap)); |
+ GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUND, 0.02f)); |
+} |
+ |
+void GrGLPath::InitPathObjectEmptyPath(GrGLGpu* gpu, GrGLuint pathID) { |
+ GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, nullptr, 0, GR_GL_FLOAT, nullptr)); |
} |
GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& origStroke) |
: INHERITED(gpu, origSkPath, origStroke), |
fPathID(gpu->glPathRendering()->genPaths(1)) { |
- // Convert a dashing to either a stroke or a fill. |
- const SkPath* skPath = &origSkPath; |
- SkTLazy<SkPath> tmpPath; |
- const GrStrokeInfo* stroke = &origStroke; |
- GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle); |
- |
- if (stroke->isDashed()) { |
- if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) { |
- skPath = tmpPath.get(); |
- stroke = &tmpStroke; |
+ |
+ if (origSkPath.isEmpty()) { |
+ InitPathObjectEmptyPath(gpu, fPathID); |
+ fShouldStroke = false; |
+ fShouldFill = false; |
+ } else { |
+ const SkPath* skPath = &origSkPath; |
+ SkTLazy<SkPath> tmpPath; |
+ const GrStrokeInfo* stroke = &origStroke; |
+ GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle); |
+ |
+ if (stroke->isDashed()) { |
+ // Skia stroking and NVPR stroking differ with respect to dashing |
+ // pattern. |
+ // Convert a dashing to either a stroke or a fill. |
+ if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) { |
+ skPath = tmpPath.get(); |
+ stroke = &tmpStroke; |
+ } |
+ } |
+ |
+ bool didInit = false; |
+ if (stroke->needToApply() && stroke->getCap() != SkPaint::kButt_Cap) { |
+ // Skia stroking and NVPR stroking differ with respect to stroking |
+ // end caps of empty subpaths. |
+ // Convert stroke to fill if path contains empty subpaths. |
+ didInit = InitPathObjectPathDataCheckingDegenerates(gpu, fPathID, *skPath); |
+ if (!didInit) { |
+ if (!tmpPath.isValid()) { |
+ tmpPath.init(); |
+ } |
+ SkAssertResult(stroke->applyToPath(tmpPath.get(), *skPath)); |
+ skPath = tmpPath.get(); |
+ tmpStroke.setFillStyle(); |
+ stroke = &tmpStroke; |
+ } |
} |
- } |
- InitPathObject(gpu, fPathID, *skPath, *stroke); |
+ if (!didInit) { |
+ InitPathObjectPathData(gpu, fPathID, *skPath); |
+ } |
- fShouldStroke = stroke->needToApply(); |
- fShouldFill = stroke->isFillStyle() || |
- stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style; |
+ fShouldStroke = stroke->needToApply(); |
+ fShouldFill = stroke->isFillStyle() || |
+ stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style; |
- if (fShouldStroke) { |
- // FIXME: try to account for stroking, without rasterizing the stroke. |
- fBounds.outset(stroke->getWidth(), stroke->getWidth()); |
+ if (fShouldStroke) { |
+ InitPathObjectStroke(gpu, fPathID, *stroke); |
+ |
+ // FIXME: try to account for stroking, without rasterizing the stroke. |
+ fBounds.outset(stroke->getWidth(), stroke->getWidth()); |
+ } |
} |
this->registerWithCache(); |