1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc.
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
78a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
83d1cb97240c568253b657b8a1e50258792457166mike@reedtribe.org#include "gm.h"
933d2055e594177b27360f84e0631b26d74a55a9bMike Klein#include "sk_tool_utils.h"
10b7061176c7f414616fe2e79e832b3e0abe326af6robertphillips@google.com#include "SkBlurMask.h"
11b7061176c7f414616fe2e79e832b3e0abe326af6robertphillips@google.com#include "SkBlurMaskFilter.h"
128b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkReadBuffer.h"
13eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita#include "SkTextBlob.h"
148b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkWriteBuffer.h"
158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "Sk2DPathEffect.h"
178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
180449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclarkstatic SkPath create_underline(const SkTDArray<SkScalar>& intersections,
190449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar last, SkScalar finalPos,
200449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar uPos, SkScalar uWidth, SkScalar textSize) {
210449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPath underline;
220449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkScalar end = last;
230449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    for (int index = 0; index < intersections.count(); index += 2) {
240449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar start = intersections[index] - uWidth;;
250449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        end = intersections[index + 1] + uWidth;
260449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        if (start > last && last + textSize / 12 < start) {
270449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            underline.moveTo(last, uPos);
280449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            underline.lineTo(start, uPos);
290449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        }
300449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        last = end;
310449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
320449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    if (end < finalPos) {
330449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        underline.moveTo(end, uPos);
340449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        underline.lineTo(finalPos, uPos);
350449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
360449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    return underline;
370449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark}
380449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
390449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclarkstatic void find_intercepts(const char* test, size_t len, SkScalar x, SkScalar y,
400449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        const SkPaint& paint, SkScalar uWidth, SkTDArray<SkScalar>* intersections) {
410449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkScalar uPos = y + uWidth;
420449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
430449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    int count = paint.getTextIntercepts(test, len, x, y, bounds, nullptr);
440449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkASSERT(!(count % 2));
450449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    if (count) {
460449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        intersections->setCount(count);
470449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        paint.getTextIntercepts(test, len, x, y, bounds, intersections->begin());
480449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
490449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark}
500449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
510449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclarkDEF_SIMPLE_GM(fancyunderline, canvas, 900, 1350) {
520449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPaint paint;
530449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    paint.setAntiAlias(true);
540449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    const char* fam[] = { "sans-serif", "serif", "monospace" };
550449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
560449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPoint textPt = { 10, 80 };
57eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    for (size_t font = 0; font < SK_ARRAY_COUNT(fam); ++font) {
58ee6a9919a362e16c1d84a870ce867d1ad7b8a141mboc        sk_tool_utils::set_portable_typeface(&paint, fam[font]);
590449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
600449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setTextSize(textSize);
610449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            const SkScalar uWidth = textSize / 15;
620449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setStrokeWidth(uWidth);
630449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setStyle(SkPaint::kFill_Style);
640449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            canvas->drawText(test, sizeof(test) - 1, textPt.fX, textPt.fY, paint);
650449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
660449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkTDArray<SkScalar> intersections;
670449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            find_intercepts(test, sizeof(test) - 1, textPt.fX, textPt.fY, paint, uWidth,
680449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark                &intersections);
690449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
700449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar start = textPt.fX;
710449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar end = paint.measureText(test, sizeof(test) - 1) + textPt.fX;
720449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar uPos = textPt.fY + uWidth;
730449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize);
740449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setStyle(SkPaint::kStroke_Style);
750449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            canvas->drawPath(underline, paint);
760449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
770449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            canvas->translate(0, textSize * 1.3f);
780449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        }
790449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        canvas->translate(0, 60);
800449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
810449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark}
820449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
830449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclarkstatic void find_intercepts(const char* test, size_t len, const SkPoint* pos, const SkPaint& paint,
840449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar uWidth, SkTDArray<SkScalar>* intersections) {
850449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkScalar uPos = pos[0].fY + uWidth;
860449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
870449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    int count = paint.getPosTextIntercepts(test, len, pos, bounds, nullptr);
880449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkASSERT(!(count % 2));
890449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    if (count) {
900449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        intersections->setCount(count);
910449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        paint.getPosTextIntercepts(test, len, pos, bounds, intersections->begin());
920449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
930449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark}
940449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
950449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclarkDEF_SIMPLE_GM(fancyposunderline, canvas, 900, 1350) {
960449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPaint paint;
970449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    paint.setAntiAlias(true);
980449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    const char* fam[] = { "sans-serif", "serif", "monospace" };
990449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
1000449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPoint textPt = { 10, 80 };
101eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    for (size_t font = 0; font < SK_ARRAY_COUNT(fam); ++font) {
102ee6a9919a362e16c1d84a870ce867d1ad7b8a141mboc        sk_tool_utils::set_portable_typeface(&paint, fam[font]);
1030449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
1040449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setTextSize(textSize);
1050449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            const SkScalar uWidth = textSize / 15;
1060449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setStrokeWidth(uWidth);
1070449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setStyle(SkPaint::kFill_Style);
1080449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            int widthCount = paint.getTextWidths(test, sizeof(test) - 1, nullptr);
1090449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkTDArray<SkScalar> widths;
1100449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            widths.setCount(widthCount);
1110449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            (void) paint.getTextWidths(test, sizeof(test) - 1, widths.begin());
1120449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkTDArray<SkPoint> pos;
1130449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            pos.setCount(widthCount);
1140449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar posX = textPt.fX;
1150449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            for (int index = 0; index < widthCount; ++index) {
1160449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark                pos[index].fX = posX;
1170449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark                posX += widths[index];
1180449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark                pos[index].fY = textPt.fY + (textSize / 25) * (index % 4);
1190449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            }
1200449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            canvas->drawPosText(test, sizeof(test) - 1, pos.begin(), paint);
1210449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
1220449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkTDArray<SkScalar> intersections;
1230449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            find_intercepts(test, sizeof(test) - 1, pos.begin(), paint, uWidth, &intersections);
1240449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
1250449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar start = textPt.fX;
1260449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar end = posX;
1270449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkScalar uPos = textPt.fY + uWidth;
1280449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize);
1290449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            paint.setStyle(SkPaint::kStroke_Style);
1300449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            canvas->drawPath(underline, paint);
1310449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
1320449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            canvas->translate(0, textSize * 1.3f);
1330449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        }
1340449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        canvas->translate(0, 60);
1350449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
1360449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark}
1370449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
138eae6a9127707b9391546012d11bcc7d9920dfa6dfmalitanamespace {
139eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
14037283c28aa5bea2204c18956e74f83b238d7a891fmalitask_sp<SkTextBlob> MakeFancyBlob(const SkPaint& paint, const char* text) {
141eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkPaint blobPaint(paint);
142eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
143eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const size_t textLen = strlen(text);
144eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const int glyphCount = blobPaint.textToGlyphs(text, textLen, nullptr);
145eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkAutoTArray<SkGlyphID> glyphs(glyphCount);
146eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    blobPaint.textToGlyphs(text, textLen, glyphs.get());
147eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
148eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    blobPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
149eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const size_t glyphTextBytes = SkTo<uint32_t>(glyphCount) * sizeof(SkGlyphID);
150eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const int widthCount = blobPaint.getTextWidths(glyphs.get(), glyphTextBytes, nullptr);
151eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkAssertResult(widthCount == glyphCount);
152eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
153eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkAutoTArray<SkScalar> widths(glyphCount);
154eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    blobPaint.getTextWidths(glyphs.get(), glyphTextBytes, widths.get());
155eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
156eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkTextBlobBuilder blobBuilder;
157eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    int glyphIndex = 0;
158eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkScalar advance = 0;
159eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
160eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    // Default-positioned run.
161eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    {
162eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        const int defaultRunLen = glyphCount / 3;
163eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRun(blobPaint,
164eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita                                                                       defaultRunLen,
165eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita                                                                       advance, 0);
166eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        memcpy(buf.glyphs, glyphs.get(), SkTo<uint32_t>(defaultRunLen) * sizeof(SkGlyphID));
167eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
168eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        for (int i = 0; i < defaultRunLen; ++i) {
169eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            advance += widths[glyphIndex++];
170eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        }
171eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    }
172eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
173eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    // Horizontal-positioned run.
174eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    {
175eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        const int horizontalRunLen = glyphCount / 3;
176eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRunPosH(blobPaint,
177eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita                                                                           horizontalRunLen,
178eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita                                                                           0);
179eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        memcpy(buf.glyphs, glyphs.get() + glyphIndex,
180eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita               SkTo<uint32_t>(horizontalRunLen) * sizeof(SkGlyphID));
181eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        for (int i = 0; i < horizontalRunLen; ++i) {
182eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            buf.pos[i] = advance;
183eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            advance += widths[glyphIndex++];
184eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        }
185eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    }
186eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
187eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    // Full-positioned run.
188eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    {
189eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        const int fullRunLen = glyphCount - glyphIndex;
190eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRunPos(blobPaint, fullRunLen);
191eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        memcpy(buf.glyphs, glyphs.get() + glyphIndex,
192eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita               SkTo<uint32_t>(fullRunLen) * sizeof(SkGlyphID));
193eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        for (int i = 0; i < fullRunLen; ++i) {
194eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            buf.pos[i * 2 + 0] = advance; // x offset
195eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            buf.pos[i * 2 + 1] = 0;       // y offset
196eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            advance += widths[glyphIndex++];
197eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        }
198eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    }
199eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
20037283c28aa5bea2204c18956e74f83b238d7a891fmalita    return blobBuilder.make();
201eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita}
202eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
203eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita} // anonymous ns
204eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
205eae6a9127707b9391546012d11bcc7d9920dfa6dfmalitaDEF_SIMPLE_GM(fancyblobunderline, canvas, 1480, 1380) {
206eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    SkPaint paint;
207eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    paint.setAntiAlias(true);
208eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const char* fam[] = { "sans-serif", "serif", "monospace" };
209eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
210eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    const SkPoint blobOffset = { 10, 80 };
211eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
212eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    for (size_t font = 0; font < SK_ARRAY_COUNT(fam); ++font) {
213eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        sk_tool_utils::set_portable_typeface(&paint, fam[font]);
214eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
215eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            paint.setTextSize(textSize);
216eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            const SkScalar uWidth = textSize / 15;
217eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            paint.setStrokeWidth(uWidth);
218eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            paint.setStyle(SkPaint::kFill_Style);
219eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
22037283c28aa5bea2204c18956e74f83b238d7a891fmalita            sk_sp<SkTextBlob> blob = MakeFancyBlob(paint, test);
22137283c28aa5bea2204c18956e74f83b238d7a891fmalita            canvas->drawTextBlob(blob, blobOffset.x(), blobOffset.y(), paint);
222eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
223eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            const SkScalar uPos = uWidth;
224eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            const SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
225eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            const int interceptCount = paint.getTextBlobIntercepts(blob.get(), bounds, nullptr);
226eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            SkASSERT(!(interceptCount % 2));
227eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
228eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            SkTDArray<SkScalar> intercepts;
229eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            intercepts.setCount(interceptCount);
230eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            paint.getTextBlobIntercepts(blob.get(), bounds, intercepts.begin());
231eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
232eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            const SkScalar start = blob->bounds().left();
233eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            const SkScalar end = blob->bounds().right();
234eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            SkPath underline = create_underline(intercepts, start, end, uPos, uWidth, textSize);
235eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            underline.offset(blobOffset.x(), blobOffset.y());
236eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            paint.setStyle(SkPaint::kStroke_Style);
237eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            canvas->drawPath(underline, paint);
238eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
239eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita            canvas->translate(0, textSize * 1.3f);
240eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        }
241eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
242eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita        canvas->translate(0, 60);
243eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita    }
244eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita}
245eae6a9127707b9391546012d11bcc7d9920dfa6dfmalita
2460449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclarkDEF_SIMPLE_GM(fancyunderlinebars, canvas, 1500, 460) {
2470449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPaint paint;
2480449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    paint.setAntiAlias(true);
2490449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    const char test[] = " .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_";
2500449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    SkPoint textPt = { 10, 80 };
2510449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    sk_tool_utils::set_portable_typeface(&paint, "serif");
2520449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
2530449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        paint.setTextSize(textSize);
2540449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar uWidth = textSize / 15;
2550449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        paint.setStrokeWidth(uWidth);
2560449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        paint.setStyle(SkPaint::kFill_Style);
2570449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        int widthCount = paint.getTextWidths(test, sizeof(test) - 1, nullptr);
2580449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkTDArray<SkScalar> widths;
2590449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        widths.setCount(widthCount);
2600449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        (void) paint.getTextWidths(test, sizeof(test) - 1, widths.begin());
2610449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkTDArray<SkPoint> pos;
2620449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        pos.setCount(widthCount);
2630449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar posX = textPt.fX;
2640449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        pos[0] = textPt;
2650449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        posX += widths[0];
2660449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        for (int index = 1; index < widthCount; ++index) {
2670449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            pos[index].fX = posX;
2680449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            posX += widths[index];
2690449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark            pos[index].fY = textPt.fY - (textSize / 50) * (index / 5) + textSize / 50 * 4;
2700449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        }
2710449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        canvas->drawPosText(test, sizeof(test) - 1, pos.begin(), paint);
2720449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
2730449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkTDArray<SkScalar> intersections;
2740449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        find_intercepts(test, sizeof(test) - 1, pos.begin(), paint, uWidth, &intersections);
2750449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark
2760449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar start = textPt.fX;
2770449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar end = posX;
2780449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkScalar uPos = pos[0].fY + uWidth;
2790449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize);
2800449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        paint.setStyle(SkPaint::kStroke_Style);
2810449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        canvas->drawPath(underline, paint);
2820449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark        canvas->translate(0, textSize * 1.3f);
2830449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark    }
2840449bcfb2fa1dd33cb3a4c0c8b17960d17edf01acaryclark}
285