1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2020 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "gm/gm.h"
9cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
10cb93a386Sopenharmony_ci#include "include/core/SkColor.h"
11cb93a386Sopenharmony_ci#include "include/core/SkFont.h"
12cb93a386Sopenharmony_ci#include "include/core/SkFontTypes.h"
13cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
14cb93a386Sopenharmony_ci#include "include/core/SkPoint.h"
15cb93a386Sopenharmony_ci#include "include/core/SkRect.h"
16cb93a386Sopenharmony_ci#include "include/core/SkScalar.h"
17cb93a386Sopenharmony_ci#include "include/core/SkShader.h"
18cb93a386Sopenharmony_ci#include "include/core/SkSize.h"
19cb93a386Sopenharmony_ci#include "include/core/SkString.h"
20cb93a386Sopenharmony_ci#include "include/core/SkTypeface.h"
21cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci#include "modules/skparagraph/include/Paragraph.h"
24cb93a386Sopenharmony_ci#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_cistatic const char* gSpeach = "Five score years ago, a great American, in whose symbolic shadow we stand today, signed the Emancipation Proclamation. This momentous decree came as a great beacon light of hope to millions of Negro slaves who had been seared in the flames of withering injustice. It came as a joyous daybreak to end the long night of their captivity.";
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_cinamespace {
29cb93a386Sopenharmony_cienum ParaFlags {
30cb93a386Sopenharmony_ci    kTimeLayout     = 1 << 0,
31cb93a386Sopenharmony_ci    kUseUnderline   = 1 << 1,
32cb93a386Sopenharmony_ci    kShowVisitor    = 1 << 2,
33cb93a386Sopenharmony_ci};
34cb93a386Sopenharmony_ci}  // namespace
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ciclass ParagraphGM : public skiagm::GM {
37cb93a386Sopenharmony_ci    std::unique_ptr<skia::textlayout::Paragraph> fPara;
38cb93a386Sopenharmony_ci    const unsigned fFlags;
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_cipublic:
41cb93a386Sopenharmony_ci    ParagraphGM(unsigned flags) : fFlags(flags) {}
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci    void buildParagraph() {
44cb93a386Sopenharmony_ci        skia::textlayout::TextStyle style;
45cb93a386Sopenharmony_ci        style.setForegroundColor(SkPaint());
46cb93a386Sopenharmony_ci        style.setFontFamilies({SkString("sans-serif")});
47cb93a386Sopenharmony_ci        style.setFontSize(30);
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci        if (fFlags & kUseUnderline) {
50cb93a386Sopenharmony_ci            style.setDecoration(skia::textlayout::TextDecoration::kUnderline);
51cb93a386Sopenharmony_ci            style.setDecorationMode(skia::textlayout::TextDecorationMode::kThrough);
52cb93a386Sopenharmony_ci            style.setDecorationColor(SK_ColorBLACK);
53cb93a386Sopenharmony_ci            style.setDecorationThicknessMultiplier(2);
54cb93a386Sopenharmony_ci        }
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci        skia::textlayout::ParagraphStyle paraStyle;
57cb93a386Sopenharmony_ci        paraStyle.setTextStyle(style);
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ci        auto collection = sk_make_sp<skia::textlayout::FontCollection>();
60cb93a386Sopenharmony_ci        collection->setDefaultFontManager(SkFontMgr::RefDefault());
61cb93a386Sopenharmony_ci        auto builder = skia::textlayout::ParagraphBuilderImpl::make(
62cb93a386Sopenharmony_ci                paraStyle, collection, SkUnicode::Make());
63cb93a386Sopenharmony_ci        if (nullptr == builder) {
64cb93a386Sopenharmony_ci            fPara = nullptr;
65cb93a386Sopenharmony_ci            return;
66cb93a386Sopenharmony_ci        }
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ci        builder->addText(gSpeach, strlen(gSpeach));
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci        fPara = builder->Build();
71cb93a386Sopenharmony_ci        fPara->layout(400);
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ciprotected:
75cb93a386Sopenharmony_ci    void onOnceBeforeDraw() override {
76cb93a386Sopenharmony_ci        this->buildParagraph();
77cb93a386Sopenharmony_ci    }
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci    SkString onShortName() override {
80cb93a386Sopenharmony_ci        SkString name;
81cb93a386Sopenharmony_ci        name.printf("paragraph%s_%s",
82cb93a386Sopenharmony_ci                    fFlags & kTimeLayout   ? "_layout"    : "",
83cb93a386Sopenharmony_ci                    fFlags & kUseUnderline ? "_underline" : "");
84cb93a386Sopenharmony_ci        if (fFlags & kShowVisitor) {
85cb93a386Sopenharmony_ci            name.append("_visitor");
86cb93a386Sopenharmony_ci        }
87cb93a386Sopenharmony_ci        return name;
88cb93a386Sopenharmony_ci    }
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    SkISize onISize() override {
91cb93a386Sopenharmony_ci        if (fFlags & kShowVisitor) {
92cb93a386Sopenharmony_ci            return SkISize::Make(810, 420);
93cb93a386Sopenharmony_ci        }
94cb93a386Sopenharmony_ci        return SkISize::Make(412, 420);
95cb93a386Sopenharmony_ci    }
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_ci    void drawFromVisitor(SkCanvas* canvas, skia::textlayout::Paragraph* para) const {
98cb93a386Sopenharmony_ci        SkPaint p, p2;
99cb93a386Sopenharmony_ci        p.setColor(0xFF0000FF);
100cb93a386Sopenharmony_ci        p2.setColor(0xFFFF0000);
101cb93a386Sopenharmony_ci        p2.setStrokeWidth(4);
102cb93a386Sopenharmony_ci        p2.setStrokeCap(SkPaint::kSquare_Cap);
103cb93a386Sopenharmony_ci        SkPaint underp;
104cb93a386Sopenharmony_ci        underp.setStroke(true);
105cb93a386Sopenharmony_ci        underp.setStrokeWidth(2);
106cb93a386Sopenharmony_ci        underp.setAntiAlias(true);
107cb93a386Sopenharmony_ci        underp.setColor(p.getColor());
108cb93a386Sopenharmony_ci        const SkScalar GAP = 2;
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci        para->visit([&](int, const skia::textlayout::Paragraph::VisitorInfo* info) {
111cb93a386Sopenharmony_ci            if (!info) {
112cb93a386Sopenharmony_ci                return;
113cb93a386Sopenharmony_ci            }
114cb93a386Sopenharmony_ci            canvas->drawGlyphs(info->count, info->glyphs, info->positions, info->origin,
115cb93a386Sopenharmony_ci                               info->font, p);
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ci            if (fFlags & kUseUnderline) {
118cb93a386Sopenharmony_ci                // Need to modify positions to roll-in the orign
119cb93a386Sopenharmony_ci                std::vector<SkPoint> pos;
120cb93a386Sopenharmony_ci                for (int i = 0; i < info->count; ++i) {
121cb93a386Sopenharmony_ci                    pos.push_back({info->origin.fX + info->positions[i].fX,
122cb93a386Sopenharmony_ci                                   info->origin.fY + info->positions[i].fY});
123cb93a386Sopenharmony_ci                }
124cb93a386Sopenharmony_ci
125cb93a386Sopenharmony_ci                const SkScalar X0 = pos[0].fX;
126cb93a386Sopenharmony_ci                const SkScalar X1 = X0 + info->advanceX;
127cb93a386Sopenharmony_ci                const SkScalar Y  = pos[0].fY;
128cb93a386Sopenharmony_ci                auto sects = info->font.getIntercepts(info->glyphs, info->count, pos.data(),
129cb93a386Sopenharmony_ci                                                      Y+1, Y+3);
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci                SkScalar x0 = X0;
132cb93a386Sopenharmony_ci                for (size_t i = 0; i < sects.size(); i += 2) {
133cb93a386Sopenharmony_ci                    SkScalar x1 = sects[i] - GAP;
134cb93a386Sopenharmony_ci                    if (x0 < x1) {
135cb93a386Sopenharmony_ci                        canvas->drawLine(x0, Y+2, x1, Y+2, underp);
136cb93a386Sopenharmony_ci                    }
137cb93a386Sopenharmony_ci                    x0 = sects[i+1] + GAP;
138cb93a386Sopenharmony_ci                }
139cb93a386Sopenharmony_ci                canvas->drawLine(x0, Y+2, X1, Y+2, underp);
140cb93a386Sopenharmony_ci            }
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci            if ((false)) {
143cb93a386Sopenharmony_ci                if (info->utf8Starts) {
144cb93a386Sopenharmony_ci                    SkString str;
145cb93a386Sopenharmony_ci                    for (int i = 0; i < info->count; ++i) {
146cb93a386Sopenharmony_ci                        str.appendUnichar(gSpeach[info->utf8Starts[i]]);
147cb93a386Sopenharmony_ci                    }
148cb93a386Sopenharmony_ci                    SkDebugf("'%s'\n", str.c_str());
149cb93a386Sopenharmony_ci                }
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci                // show position points
152cb93a386Sopenharmony_ci                for (int i = 0; i < info->count; ++i) {
153cb93a386Sopenharmony_ci                    auto pos = info->positions[i];
154cb93a386Sopenharmony_ci                    canvas->drawPoint(pos.fX + info->origin.fX, pos.fY + info->origin.fY, p2);
155cb93a386Sopenharmony_ci                }
156cb93a386Sopenharmony_ci            }
157cb93a386Sopenharmony_ci        });
158cb93a386Sopenharmony_ci    }
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci    DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
161cb93a386Sopenharmony_ci        if (nullptr == fPara) {
162cb93a386Sopenharmony_ci            return DrawResult::kSkip;
163cb93a386Sopenharmony_ci        }
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci        if (fFlags & kShowVisitor) {
166cb93a386Sopenharmony_ci            canvas->clear(SK_ColorWHITE);
167cb93a386Sopenharmony_ci            fPara->layout(400);
168cb93a386Sopenharmony_ci            fPara->paint(canvas, 10, 10);
169cb93a386Sopenharmony_ci            canvas->translate(400+10, 10);
170cb93a386Sopenharmony_ci            this->drawFromVisitor(canvas, fPara.get());
171cb93a386Sopenharmony_ci            return DrawResult::kOk;
172cb93a386Sopenharmony_ci        }
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_ci        const int loop = (this->getMode() == kGM_Mode) ? 1 : 50;
175cb93a386Sopenharmony_ci
176cb93a386Sopenharmony_ci        int parity = 0;
177cb93a386Sopenharmony_ci        for (int i = 0; i < loop; ++i) {
178cb93a386Sopenharmony_ci            SkAutoCanvasRestore acr(canvas, true);
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci            if (fFlags & kTimeLayout) {
181cb93a386Sopenharmony_ci                fPara->layout(400 + parity);
182cb93a386Sopenharmony_ci                parity = (parity + 1) & 1;
183cb93a386Sopenharmony_ci            }
184cb93a386Sopenharmony_ci            fPara->paint(canvas, 10, 10);
185cb93a386Sopenharmony_ci        }
186cb93a386Sopenharmony_ci        // clean up if we've been looping
187cb93a386Sopenharmony_ci        if (loop > 1) {
188cb93a386Sopenharmony_ci            canvas->clear(SK_ColorWHITE);
189cb93a386Sopenharmony_ci            fPara->layout(400);
190cb93a386Sopenharmony_ci            fPara->paint(canvas, 10, 10);
191cb93a386Sopenharmony_ci        }
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci        if ((this->getMode() == kGM_Mode) && (fFlags & kTimeLayout)) {
194cb93a386Sopenharmony_ci            return DrawResult::kSkip;
195cb93a386Sopenharmony_ci        }
196cb93a386Sopenharmony_ci        return DrawResult::kOk;
197cb93a386Sopenharmony_ci    }
198cb93a386Sopenharmony_ci
199cb93a386Sopenharmony_ci    bool runAsBench() const override { return true; }
200cb93a386Sopenharmony_ci
201cb93a386Sopenharmony_ci    bool onAnimate(double /*nanos*/) override {
202cb93a386Sopenharmony_ci        return false;
203cb93a386Sopenharmony_ci    }
204cb93a386Sopenharmony_ci
205cb93a386Sopenharmony_ciprivate:
206cb93a386Sopenharmony_ci    using INHERITED = skiagm::GM;
207cb93a386Sopenharmony_ci};
208cb93a386Sopenharmony_ciDEF_GM(return new ParagraphGM(0);)
209cb93a386Sopenharmony_ciDEF_GM(return new ParagraphGM(kTimeLayout);)
210cb93a386Sopenharmony_ciDEF_GM(return new ParagraphGM(kUseUnderline);)
211cb93a386Sopenharmony_ciDEF_GM(return new ParagraphGM(kShowVisitor);)
212cb93a386Sopenharmony_ciDEF_GM(return new ParagraphGM(kShowVisitor | kUseUnderline);)
213