1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2017 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "gm.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_ATLAS_TEXT
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAtlasTextContext.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAtlasTextFont.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAtlasTextTarget.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkBitmap.h"
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkCanvas.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTypeface.h"
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkUtils.h"
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "gpu/TestContext.h"
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "gpu/atlastext/GLTestAtlasTextRenderer.h"
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "gpu/atlastext/TestAtlasTextRenderer.h"
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "sk_tool_utils.h"
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// GM that draws text using the Atlas Text interface offscreen and then blits that to the canvas.
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic SkScalar draw_string(SkAtlasTextTarget* target, const SkString& text, SkScalar x, SkScalar y,
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            uint32_t color, sk_sp<SkTypeface> typeface, float size) {
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!text.size()) {
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return x;
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto font = SkAtlasTextFont::Make(typeface, size);
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int cnt = SkUTF8_CountUnichars(text.c_str());
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[cnt]);
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    typeface->charsToGlyphs(text.c_str(), SkTypeface::Encoding::kUTF8_Encoding, glyphs.get(), cnt);
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Using a paint to get the positions for each glyph.
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint;
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setTextSize(size);
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setTypeface(std::move(typeface));
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    std::unique_ptr<SkScalar[]> widths(new SkScalar[cnt]);
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.getTextWidths(glyphs.get(), cnt * sizeof(SkGlyphID), widths.get(), nullptr);
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    std::unique_ptr<SkPoint[]> positions(new SkPoint[cnt]);
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    positions[0] = {x, y};
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 1; i < cnt; ++i) {
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        positions[i] = {positions[i - 1].fX + widths[i - 1], y};
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    target->drawText(glyphs.get(), positions.get(), cnt, color, *font);
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Return the width of the of draw.
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return positions[cnt - 1].fX + widths[cnt - 1] - positions[0].fX;
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass AtlasTextGM : public skiagm::GM {
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    AtlasTextGM() = default;
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprotected:
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString onShortName() override { return SkString("atlastext"); }
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkISize onISize() override { return SkISize::Make(kSize, kSize); }
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void onOnceBeforeDraw() override {
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fRenderer = sk_gpu_test::MakeGLTestAtlasTextRenderer();
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fRenderer) {
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fContext = SkAtlasTextContext::Make(fRenderer);
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto targetHandle = fRenderer->makeTargetHandle(kSize, kSize);
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTarget = SkAtlasTextTarget::Make(fContext, kSize, kSize, targetHandle);
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTypefaces[0] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Italic());
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTypefaces[1] =
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Italic());
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTypefaces[2] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Normal());
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTypefaces[3] =
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Normal());
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTypefaces[4] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Bold());
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTypefaces[5] = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Bold());
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void onDraw(SkCanvas* canvas) override {
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fRenderer) {
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->clear(SK_ColorRED);
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fRenderer->clearTarget(fTarget->handle(), 0xFF808080);
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto bmp = this->drawText();
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint paint;
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint.setBlendMode(SkBlendMode::kSrc);
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawBitmap(bmp, 0, 0);
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBitmap drawText() {
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        static const int kSizes[] = {8, 13, 18, 23, 30};
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        static const SkString kTexts[] = {SkString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkString("abcdefghijklmnopqrstuvwxyz"),
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkString("0123456789"),
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkString("!@#$%^&*()<>[]{}")};
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar x = 0;
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar y = 10;
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRandom random;
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        do {
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (auto s : kSizes) {
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                auto size = 2 * s;
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (const auto& typeface : fTypefaces) {
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    for (const auto& text : kTexts) {
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // Choose a random color but don't let alpha be too small to see.
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        uint32_t color = random.nextU() | 0x40000000;
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fTarget->save();
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // Randomly add a little bit of perspective
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if (random.nextBool()) {
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            SkMatrix persp;
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            persp.reset();
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            persp.setPerspY(0.0005f);
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            persp.preTranslate(-x, -y + s);
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            persp.postTranslate(x, y - s);
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            fTarget->concat(persp);
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // Randomly switch between positioning with a matrix vs x, y passed to draw.
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkScalar drawX = x, drawY = y;
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if (random.nextBool()) {
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            fTarget->translate(x, y);
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            drawX = drawY = 0;
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        x += size +
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             draw_string(fTarget.get(), text, drawX, drawY, color, typeface, size);
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        x = SkScalarCeilToScalar(x);
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fTarget->restore();
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // Flush periodically to test continued drawing after a flush.
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if ((random.nextU() % 8) == 0) {
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            fTarget->flush();
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if (x + 100 > kSize) {
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            x = 0;
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            y += SkScalarCeilToScalar(size + 3);
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            if (y > kSize) {
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                fTarget->flush();
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                return fRenderer->readTargetHandle(fTarget->handle());
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            }
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } while (true);
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static constexpr int kSize = 1280;
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkTypeface> fTypefaces[6];
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<sk_gpu_test::TestAtlasTextRenderer> fRenderer;
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    std::unique_ptr<SkAtlasTextTarget> fTarget;
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkAtlasTextContext> fContext;
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    typedef GM INHERITED;
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconstexpr int AtlasTextGM::kSize;
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//////////////////////////////////////////////////////////////////////////////
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotDEF_GM(return new AtlasTextGM;)
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
170