From 6a07591a5577b63893dc4018a7c54fc4bb28dbb2 Mon Sep 17 00:00:00 2001 From: Herb Derby <herb@google.com> Date: Wed, 29 May 2019 17:02:49 -0400 Subject: [PATCH] Simplify calculation of the path gap. Simplify the gap calculation code. Mainly avoid passing the interval all over the place. Change code from handling both horizontal and vertical to just horizantal. Change-Id: Idb364da1e489e6a95d9b6d6dd97c4f6e85900c42 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/216691 Reviewed-by: Mike Klein <mtklein@google.com> Commit-Queue: Herb Derby <herb@google.com> --- src/core/SkStrike.cpp | 216 ++++++++++++++++++++-------------------- src/core/SkStrike.h | 11 +- src/core/SkTextBlob.cpp | 3 +- 3 files changed, 109 insertions(+), 121 deletions(-) diff --git a/src/core/SkStrike.cpp b/src/core/SkStrike.cpp index a9161aea69..41381dbe85 100644 --- a/src/core/SkStrike.cpp +++ b/src/core/SkStrike.cpp @@ -277,24 +277,6 @@ SkSpan<const SkGlyphPos> SkStrike::prepareForDrawing(const SkGlyphID glyphIDs[], #include "src/pathops/SkPathOpsCubic.h" #include "src/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 SkStrike::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, SkScalar xPos, SkScalar* array, int* count) { if (array) { @@ -306,57 +288,6 @@ void SkStrike::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale *count += 2; } -void SkStrike::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) { - intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val); - intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val); -} - -void SkStrike::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 SkStrike::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, - SkGlyph::Intercept* intercept) { - SkScalar t = yAxis ? sk_ieee_float_divide(axis - pts[0].fX, pts[1].fX - pts[0].fX) - : sk_ieee_float_divide(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 SkStrike::AddQuad(const SkPoint pts[3], 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 SkStrike::AddCubic(const SkPoint pts[4], 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* SkStrike::MatchBounds(const SkGlyph* glyph, const SkScalar bounds[2]) { if (!glyph->fPathData) { @@ -372,8 +303,109 @@ const SkGlyph::Intercept* SkStrike::MatchBounds(const SkGlyph* glyph, return nullptr; } +static std::tuple<SkScalar, SkScalar> calculate_path_gap( + SkScalar topOffset, SkScalar bottomOffset, const SkPath& path) { + + // Left and Right of an ever expanding gap around the path. + SkScalar left = SK_ScalarMax, + right = SK_ScalarMin; + auto expandGap = [&left, &right](SkScalar v) { + left = SkTMin(left, v); + right = SkTMax(right, v); + }; + + // Handle all the different verbs for the path. + SkPoint pts[4]; + auto addLine = [&expandGap, &pts](SkScalar offset) { + SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY); + if (0 <= t && t < 1) { // this handles divide by zero above + expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX)); + } + }; + + auto addQuad = [&expandGap, &pts](SkScalar offset) { + SkDQuad quad; + quad.set(pts); + double roots[2]; + int count = quad.horizontalIntersect(offset, roots); + while (--count >= 0) { + expandGap(quad.ptAtT(roots[count]).asSkPoint().fX); + } + }; + + auto addCubic = [&expandGap, &pts](SkScalar offset) { + SkDCubic cubic; + cubic.set(pts); + double roots[3]; + int count = cubic.horizontalIntersect(offset, roots); + while (--count >= 0) { + expandGap(cubic.ptAtT(roots[count]).asSkPoint().fX); + } + }; + + // Handle when a verb's points are in the gap between top and bottom. + auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) { + for (int i = 0; i < ptCount; ++i) { + if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) { + expandGap(pts[i].fX); + } + } + }; + + SkPath::Iter iter(path, false); + SkPath::Verb verb; + while (SkPath::kDone_Verb != (verb = iter.next(pts))) { + switch (verb) { + case SkPath::kMove_Verb: { + break; + } + case SkPath::kLine_Verb: { + addLine(topOffset); + addLine(bottomOffset); + addPts(2); + break; + } + case SkPath::kQuad_Verb: { + SkScalar quadTop = SkTMin(SkTMin(pts[0].fY, pts[1].fY), pts[2].fY); + if (bottomOffset < quadTop) { break; } + SkScalar quadBottom = SkTMax(SkTMax(pts[0].fY, pts[1].fY), pts[2].fY); + if (topOffset > quadBottom) { break; } + addQuad(topOffset); + addQuad(bottomOffset); + addPts(3); + break; + } + case SkPath::kConic_Verb: { + SkASSERT(0); // no support for text composed of conics + break; + } + case SkPath::kCubic_Verb: { + SkScalar quadTop = + SkTMin(SkTMin(SkTMin(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY); + if (bottomOffset < quadTop) { break; } + SkScalar quadBottom = + SkTMax(SkTMax(SkTMax(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY); + if (topOffset > quadBottom) { break; } + addCubic(topOffset); + addCubic(bottomOffset); + addPts(4); + break; + } + case SkPath::kClose_Verb: { + break; + } + default: { + SkASSERT(0); + break; + } + } + } + + return std::tie(left, right); +} + void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, - bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) { + SkGlyph* glyph, SkScalar* array, int* count) { const SkGlyph::Intercept* match = MatchBounds(glyph, bounds); if (match) { @@ -392,47 +424,13 @@ void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar 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)) { + if (pathBounds.fBottom < bounds[0] || bounds[1] < pathBounds.fTop) { 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; - } - } + + std::tie(intercept->fInterval[0], intercept->fInterval[1]) + = calculate_path_gap(bounds[0], bounds[1], *path); + if (intercept->fInterval[0] >= intercept->fInterval[1]) { intercept->fInterval[0] = SK_ScalarMax; intercept->fInterval[1] = SK_ScalarMin; diff --git a/src/core/SkStrike.h b/src/core/SkStrike.h index 9c098e652a..c5f25509aa 100644 --- a/src/core/SkStrike.h +++ b/src/core/SkStrike.h @@ -83,7 +83,7 @@ public: to the array (if non-null), and set the count to the updated array length. */ void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, - bool yAxis, SkGlyph* , SkScalar* array, int* count); + SkGlyph* , SkScalar* array, int* count); /** Return the Path associated with the glyph. If it has not been generated this will trigger that. @@ -188,15 +188,6 @@ private: static void OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, SkScalar xPos, SkScalar* array, int* count); - static void AddInterval(SkScalar val, SkGlyph::Intercept* intercept); - static void AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2], - bool yAxis, SkGlyph::Intercept* intercept); - static void AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, - SkGlyph::Intercept* intercept); - static void AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis, - SkGlyph::Intercept* intercept); - static void AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis, - SkGlyph::Intercept* intercept); static const SkGlyph::Intercept* MatchBounds(const SkGlyph* glyph, const SkScalar bounds[2]); diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp index be61c66067..b893349797 100644 --- a/src/core/SkTextBlob.cpp +++ b/src/core/SkTextBlob.cpp @@ -943,8 +943,7 @@ bool TextInterceptsIter::next(SkScalar* array, int* count) { fXPos += fPrevAdvance * fScale; fPrevAdvance = SkFloatToScalar(glyph.fAdvanceX); if (fCache->findPath(glyph)) { - fCache->findIntercepts(fBounds, fScale, fXPos, false, - const_cast<SkGlyph*>(&glyph), array, count); + fCache->findIntercepts(fBounds, fScale, fXPos, const_cast<SkGlyph*>(&glyph), array, count); } return fGlyphs < fStop; }