diff options
author | caryclark <caryclark@google.com> | 2016-02-09 13:25:45 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-09 13:25:45 -0800 |
commit | 0449bcfb2fa1dd33cb3a4c0c8b17960d17edf01a (patch) | |
tree | 8c9e8f7e7b02e5a337636a5f41e981ba4e363d59 /gm/texteffects.cpp | |
parent | 3cb954245cecf262d740a83913681b9fe4b41555 (diff) |
add helper to create fancy underlines
Add a couple of utility functions to SkPaint that return the bounds
of glyphs between a pair of lines.
The common use case envisioned generates the edges of descenders
between the top and bottom bounds of an underline to allow computing
a stroke that skips those descenders.
The implementation stores a linked list in each glyph containing
the bounds of the lines parallel to the advance and the outermost
intersections within those bounds.
When the glyph cache is constructed, the glyph path is intersected
with the bounds and the extreme min and max values within the bounds
is added to an intercept.
Share the text to path iter to construct the data.
Make a half-hearted attempt to support vertical text; while the
vertical implementation is complete; surrounding code (e.g. paint
align) has short-comings with vertical.
R=fmalita@chromium.org, reed@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1654883003
Review URL: https://codereview.chromium.org/1654883003
Diffstat (limited to 'gm/texteffects.cpp')
-rw-r--r-- | gm/texteffects.cpp | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp index 2eb76efa19..6ad3d23bf2 100644 --- a/gm/texteffects.cpp +++ b/gm/texteffects.cpp @@ -228,3 +228,163 @@ DEF_SIMPLE_GM(textunderstrike, canvas, 460, 680) { paint.setStyle(SkPaint::kFill_Style); canvas->drawText("Hello", 5, 100, 50, paint); } + +static SkPath create_underline(const SkTDArray<SkScalar>& intersections, + SkScalar last, SkScalar finalPos, + SkScalar uPos, SkScalar uWidth, SkScalar textSize) { + SkPath underline; + SkScalar end = last; + for (int index = 0; index < intersections.count(); index += 2) { + SkScalar start = intersections[index] - uWidth;; + end = intersections[index + 1] + uWidth; + if (start > last && last + textSize / 12 < start) { + underline.moveTo(last, uPos); + underline.lineTo(start, uPos); + } + last = end; + } + if (end < finalPos) { + underline.moveTo(end, uPos); + underline.lineTo(finalPos, uPos); + } + return underline; +} + +static void find_intercepts(const char* test, size_t len, SkScalar x, SkScalar y, + const SkPaint& paint, SkScalar uWidth, SkTDArray<SkScalar>* intersections) { + SkScalar uPos = y + uWidth; + SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 }; + int count = paint.getTextIntercepts(test, len, x, y, bounds, nullptr); + SkASSERT(!(count % 2)); + if (count) { + intersections->setCount(count); + paint.getTextIntercepts(test, len, x, y, bounds, intersections->begin()); + } +} + +DEF_SIMPLE_GM(fancyunderline, canvas, 900, 1350) { + SkPaint paint; + paint.setAntiAlias(true); + const char* fam[] = { "sans-serif", "serif", "monospace" }; + const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ"; + SkPoint textPt = { 10, 80 }; + for (int font = 0; font < 3; ++font) { + sk_tool_utils::set_portable_typeface(&paint, fam[font], SkTypeface::kNormal); + for (SkScalar textSize = 100; textSize > 10; textSize -= 20) { + paint.setTextSize(textSize); + const SkScalar uWidth = textSize / 15; + paint.setStrokeWidth(uWidth); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawText(test, sizeof(test) - 1, textPt.fX, textPt.fY, paint); + + SkTDArray<SkScalar> intersections; + find_intercepts(test, sizeof(test) - 1, textPt.fX, textPt.fY, paint, uWidth, + &intersections); + + SkScalar start = textPt.fX; + SkScalar end = paint.measureText(test, sizeof(test) - 1) + textPt.fX; + SkScalar uPos = textPt.fY + uWidth; + SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(underline, paint); + + canvas->translate(0, textSize * 1.3f); + } + canvas->translate(0, 60); + } +} + +static void find_intercepts(const char* test, size_t len, const SkPoint* pos, const SkPaint& paint, + SkScalar uWidth, SkTDArray<SkScalar>* intersections) { + SkScalar uPos = pos[0].fY + uWidth; + SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 }; + int count = paint.getPosTextIntercepts(test, len, pos, bounds, nullptr); + SkASSERT(!(count % 2)); + if (count) { + intersections->setCount(count); + paint.getPosTextIntercepts(test, len, pos, bounds, intersections->begin()); + } +} + +DEF_SIMPLE_GM(fancyposunderline, canvas, 900, 1350) { + SkPaint paint; + paint.setAntiAlias(true); + const char* fam[] = { "sans-serif", "serif", "monospace" }; + const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ"; + SkPoint textPt = { 10, 80 }; + for (int font = 0; font < 3; ++font) { + sk_tool_utils::set_portable_typeface(&paint, fam[font], SkTypeface::kNormal); + for (SkScalar textSize = 100; textSize > 10; textSize -= 20) { + paint.setTextSize(textSize); + const SkScalar uWidth = textSize / 15; + paint.setStrokeWidth(uWidth); + paint.setStyle(SkPaint::kFill_Style); + int widthCount = paint.getTextWidths(test, sizeof(test) - 1, nullptr); + SkTDArray<SkScalar> widths; + widths.setCount(widthCount); + (void) paint.getTextWidths(test, sizeof(test) - 1, widths.begin()); + SkTDArray<SkPoint> pos; + pos.setCount(widthCount); + SkScalar posX = textPt.fX; + for (int index = 0; index < widthCount; ++index) { + pos[index].fX = posX; + posX += widths[index]; + pos[index].fY = textPt.fY + (textSize / 25) * (index % 4); + } + canvas->drawPosText(test, sizeof(test) - 1, pos.begin(), paint); + + SkTDArray<SkScalar> intersections; + find_intercepts(test, sizeof(test) - 1, pos.begin(), paint, uWidth, &intersections); + + SkScalar start = textPt.fX; + SkScalar end = posX; + SkScalar uPos = textPt.fY + uWidth; + SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(underline, paint); + + canvas->translate(0, textSize * 1.3f); + } + canvas->translate(0, 60); + } +} + +DEF_SIMPLE_GM(fancyunderlinebars, canvas, 1500, 460) { + SkPaint paint; + paint.setAntiAlias(true); + const char test[] = " .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_"; + SkPoint textPt = { 10, 80 }; + sk_tool_utils::set_portable_typeface(&paint, "serif"); + for (SkScalar textSize = 100; textSize > 10; textSize -= 20) { + paint.setTextSize(textSize); + SkScalar uWidth = textSize / 15; + paint.setStrokeWidth(uWidth); + paint.setStyle(SkPaint::kFill_Style); + int widthCount = paint.getTextWidths(test, sizeof(test) - 1, nullptr); + SkTDArray<SkScalar> widths; + widths.setCount(widthCount); + (void) paint.getTextWidths(test, sizeof(test) - 1, widths.begin()); + SkTDArray<SkPoint> pos; + pos.setCount(widthCount); + SkScalar posX = textPt.fX; + pos[0] = textPt; + posX += widths[0]; + for (int index = 1; index < widthCount; ++index) { + pos[index].fX = posX; + posX += widths[index]; + pos[index].fY = textPt.fY - (textSize / 50) * (index / 5) + textSize / 50 * 4; + } + canvas->drawPosText(test, sizeof(test) - 1, pos.begin(), paint); + + SkTDArray<SkScalar> intersections; + find_intercepts(test, sizeof(test) - 1, pos.begin(), paint, uWidth, &intersections); + + SkScalar start = textPt.fX; + SkScalar end = posX; + SkScalar uPos = pos[0].fY + uWidth; + SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(underline, paint); + canvas->translate(0, textSize * 1.3f); + } +} |