Index: src/core/SkGlyphCache.cpp |
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp |
index 1512dbf0e5dd0a627369e74d32ce804945dd6bfc..93d48a3d1511795fec61fe38ddc2f02e40ec92e5 100644 |
--- a/src/core/SkGlyphCache.cpp |
+++ b/src/core/SkGlyphCache.cpp |
@@ -53,7 +53,10 @@ SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca |
} |
SkGlyphCache::~SkGlyphCache() { |
- fGlyphMap.foreach ([](SkGlyph* g) { delete g->fPath; }); |
+ fGlyphMap.foreach ([](SkGlyph* g) { |
+ if (g->fPathData) { |
+ delete g->fPathData->fPath; |
+ } } ); |
SkDescriptor::Free(fDesc); |
delete fScalerContext; |
this->invokeAndRemoveAuxProcs(); |
@@ -214,14 +217,185 @@ const void* SkGlyphCache::findImage(const SkGlyph& glyph) { |
const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { |
if (glyph.fWidth) { |
- if (glyph.fPath == nullptr) { |
- const_cast<SkGlyph&>(glyph).fPath = new SkPath; |
- fScalerContext->getPath(glyph, glyph.fPath); |
- fMemoryUsed += sizeof(SkPath) + |
- glyph.fPath->countPoints() * sizeof(SkPoint); |
+ if (glyph.fPathData == nullptr) { |
+ SkGlyph::PathData* pathData = |
+ (SkGlyph::PathData* ) fGlyphAlloc.allocThrow(sizeof(SkGlyph::PathData)); |
+ const_cast<SkGlyph&>(glyph).fPathData = pathData; |
+ pathData->fIntercept = nullptr; |
+ SkPath* path = pathData->fPath = new SkPath; |
+ fScalerContext->getPath(glyph, path); |
+ fMemoryUsed += sizeof(SkPath) + path->countPoints() * sizeof(SkPoint); |
} |
} |
- return glyph.fPath; |
+ return glyph.fPathData ? glyph.fPathData->fPath : nullptr; |
+} |
+ |
+#include "../pathops/SkPathOpsCubic.h" |
+#include "../pathops/SkPathOpsQuad.h" |
+ |
+static bool quad_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { |
+ SkScalar min = SkTMin(SkTMin(pts[0], pts[2]), pts[4]); |
+ if (bounds[1] < min) { |
+ return false; |
+ } |
+ SkScalar max = SkTMax(SkTMax(pts[0], pts[2]), pts[4]); |
+ return bounds[0] < max; |
+} |
+ |
+static bool cubic_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { |
+ SkScalar min = SkTMin(SkTMin(SkTMin(pts[0], pts[2]), pts[4]), pts[6]); |
+ if (bounds[1] < min) { |
+ return false; |
+ } |
+ SkScalar max = SkTMax(SkTMax(SkTMax(pts[0], pts[2]), pts[4]), pts[6]); |
+ return bounds[0] < max; |
+} |
+ |
+void SkGlyphCache::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, |
+ SkScalar xPos, SkScalar* array, int* count) { |
+ if (array) { |
+ array += *count; |
+ for (int index = 0; index < 2; index++) { |
+ *array++ = intercept->fInterval[index] * scale + xPos; |
+ } |
+ } |
+ *count += 2; |
+} |
+ |
+void SkGlyphCache::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) { |
+ intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val); |
+ intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val); |
+} |
+ |
+void SkGlyphCache::AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2], |
+ bool yAxis, SkGlyph::Intercept* intercept) { |
+ for (int i = 0; i < ptCount; ++i) { |
+ SkScalar val = *(&pts[i].fY - yAxis); |
+ if (bounds[0] < val && val < bounds[1]) { |
+ AddInterval(*(&pts[i].fX + yAxis), intercept); |
+ } |
+ } |
+} |
+ |
+void SkGlyphCache::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, |
+ SkGlyph::Intercept* intercept) { |
+ SkScalar t = yAxis ? (axis - pts[0].fX) / (pts[1].fX - pts[0].fX) |
+ : (axis - pts[0].fY) / (pts[1].fY - pts[0].fY); |
+ if (0 <= t && t < 1) { // this handles divide by zero above |
+ AddInterval(yAxis ? pts[0].fY + t * (pts[1].fY - pts[0].fY) |
+ : pts[0].fX + t * (pts[1].fX - pts[0].fX), intercept); |
+ } |
+} |
+ |
+void SkGlyphCache::AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis, |
+ SkGlyph::Intercept* intercept) { |
+ SkDQuad quad; |
+ quad.set(pts); |
+ double roots[2]; |
+ int count = yAxis ? quad.verticalIntersect(axis, roots) |
+ : quad.horizontalIntersect(axis, roots); |
+ while (--count >= 0) { |
+ SkPoint pt = quad.ptAtT(roots[count]).asSkPoint(); |
+ AddInterval(*(&pt.fX + yAxis), intercept); |
+ } |
+} |
+ |
+void SkGlyphCache::AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis, |
+ SkGlyph::Intercept* intercept) { |
+ SkDCubic cubic; |
+ cubic.set(pts); |
+ double roots[3]; |
+ int count = yAxis ? cubic.verticalIntersect(axis, roots) |
+ : cubic.horizontalIntersect(axis, roots); |
+ while (--count >= 0) { |
+ SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint(); |
+ AddInterval(*(&pt.fX + yAxis), intercept); |
+ } |
+} |
+ |
+const SkGlyph::Intercept* SkGlyphCache::MatchBounds(const SkGlyph* glyph, |
+ const SkScalar bounds[2]) { |
+ if (!glyph->fPathData) { |
+ return nullptr; |
+ } |
+ const SkGlyph::Intercept* intercept = glyph->fPathData->fIntercept; |
+ while (intercept) { |
+ if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) { |
+ return intercept; |
+ } |
+ intercept = intercept->fNext; |
+ } |
+ return nullptr; |
+} |
+ |
+void SkGlyphCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, |
+ bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) { |
+ const SkGlyph::Intercept* match = MatchBounds(glyph, bounds); |
+ |
+ if (match) { |
+ if (match->fInterval[0] < match->fInterval[1]) { |
+ OffsetResults(match, scale, xPos, array, count); |
+ } |
+ return; |
+ } |
+ |
+ SkGlyph::Intercept* intercept = |
+ (SkGlyph::Intercept* ) fGlyphAlloc.allocThrow(sizeof(SkGlyph::Intercept)); |
+ intercept->fNext = glyph->fPathData->fIntercept; |
+ intercept->fBounds[0] = bounds[0]; |
+ intercept->fBounds[1] = bounds[1]; |
+ intercept->fInterval[0] = SK_ScalarMax; |
+ intercept->fInterval[1] = SK_ScalarMin; |
+ glyph->fPathData->fIntercept = intercept; |
+ const SkPath* path = glyph->fPathData->fPath; |
+ const SkRect& pathBounds = path->getBounds(); |
+ if (*(&pathBounds.fBottom - yAxis) < bounds[0] || bounds[1] < *(&pathBounds.fTop - yAxis)) { |
+ return; |
+ } |
+ SkPath::Iter iter(*path, false); |
+ SkPoint pts[4]; |
+ SkPath::Verb verb; |
+ while (SkPath::kDone_Verb != (verb = iter.next(pts))) { |
+ switch (verb) { |
+ case SkPath::kMove_Verb: |
+ break; |
+ case SkPath::kLine_Verb: |
+ AddLine(pts, bounds[0], yAxis, intercept); |
+ AddLine(pts, bounds[1], yAxis, intercept); |
+ AddPoints(pts, 2, bounds, yAxis, intercept); |
+ break; |
+ case SkPath::kQuad_Verb: |
+ if (!quad_in_bounds(&pts[0].fY - yAxis, bounds)) { |
+ break; |
+ } |
+ AddQuad(pts, bounds[0], yAxis, intercept); |
+ AddQuad(pts, bounds[1], yAxis, intercept); |
+ AddPoints(pts, 3, bounds, yAxis, intercept); |
+ break; |
+ case SkPath::kConic_Verb: |
+ SkASSERT(0); // no support for text composed of conics |
+ break; |
+ case SkPath::kCubic_Verb: |
+ if (!cubic_in_bounds(&pts[0].fY - yAxis, bounds)) { |
+ break; |
+ } |
+ AddCubic(pts, bounds[0], yAxis, intercept); |
+ AddCubic(pts, bounds[1], yAxis, intercept); |
+ AddPoints(pts, 4, bounds, yAxis, intercept); |
+ break; |
+ case SkPath::kClose_Verb: |
+ break; |
+ default: |
+ SkASSERT(0); |
+ break; |
+ } |
+ } |
+ if (intercept->fInterval[0] >= intercept->fInterval[1]) { |
+ intercept->fInterval[0] = SK_ScalarMax; |
+ intercept->fInterval[1] = SK_ScalarMin; |
+ return; |
+ } |
+ OffsetResults(intercept, scale, xPos, array, count); |
} |
void SkGlyphCache::dump() const { |