1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm.h"
9#include "sk_tool_utils.h"
10
11#include "SkCanvas.h"
12#include "SkPoint.h"
13#include "SkTextBlob.h"
14#include "SkTDArray.h"
15
16namespace  {
17
18enum Pos {
19    kDefault_Pos = 0,
20    kScalar_Pos  = 1,
21    kPoint_Pos   = 2,
22};
23
24const struct BlobCfg {
25    unsigned count;
26    Pos      pos;
27    SkScalar scale;
28} blobConfigs[][3][3] = {
29    {
30        { { 1024, kDefault_Pos, 1 }, { 0, kDefault_Pos, 0 }, { 0, kDefault_Pos, 0 } },
31        { { 1024,  kScalar_Pos, 1 }, { 0,  kScalar_Pos, 0 }, { 0,  kScalar_Pos, 0 } },
32        { { 1024,   kPoint_Pos, 1 }, { 0,   kPoint_Pos, 0 }, { 0,   kPoint_Pos, 0 } },
33    },
34    {
35        { { 4, kDefault_Pos, 1 },     { 4, kDefault_Pos, 1 },  { 4, kDefault_Pos, 1 } },
36        { { 4,  kScalar_Pos, 1 },     { 4,  kScalar_Pos, 1 },  { 4,  kScalar_Pos, 1 } },
37        { { 4,   kPoint_Pos, 1 },     { 4,   kPoint_Pos, 1 },  { 4,   kPoint_Pos, 1 } },
38    },
39
40    {
41        { { 4, kDefault_Pos, 1 },     { 4, kDefault_Pos, 1 },  { 4,  kScalar_Pos, 1 } },
42        { { 4,  kScalar_Pos, 1 },     { 4,  kScalar_Pos, 1 },  { 4,   kPoint_Pos, 1 } },
43        { { 4,   kPoint_Pos, 1 },     { 4,   kPoint_Pos, 1 },  { 4, kDefault_Pos, 1 } },
44    },
45
46    {
47        { { 4, kDefault_Pos, 1 },     { 4,  kScalar_Pos, 1 },  { 4,   kPoint_Pos, 1 } },
48        { { 4,  kScalar_Pos, 1 },     { 4,   kPoint_Pos, 1 },  { 4, kDefault_Pos, 1 } },
49        { { 4,   kPoint_Pos, 1 },     { 4, kDefault_Pos, 1 },  { 4,  kScalar_Pos, 1 } },
50    },
51
52    {
53        { { 4, kDefault_Pos, .75f },     { 4, kDefault_Pos, 1 },  { 4,  kScalar_Pos, 1.25f } },
54        { { 4,  kScalar_Pos, .75f },     { 4,  kScalar_Pos, 1 },  { 4,   kPoint_Pos, 1.25f } },
55        { { 4,   kPoint_Pos, .75f },     { 4,   kPoint_Pos, 1 },  { 4, kDefault_Pos, 1.25f } },
56    },
57
58    {
59        { { 4, kDefault_Pos, 1 },     { 4,  kScalar_Pos, .75f },  { 4,   kPoint_Pos, 1.25f } },
60        { { 4,  kScalar_Pos, 1 },     { 4,   kPoint_Pos, .75f },  { 4, kDefault_Pos, 1.25f } },
61        { { 4,   kPoint_Pos, 1 },     { 4, kDefault_Pos, .75f },  { 4,  kScalar_Pos, 1.25f } },
62    },
63};
64
65const SkScalar kFontSize = 16;
66}
67
68class TextBlobGM : public skiagm::GM {
69public:
70    TextBlobGM(const char* txt)
71        : fText(txt) {
72    }
73
74protected:
75    void onOnceBeforeDraw() override {
76        fTypeface = sk_tool_utils::create_portable_typeface("serif", SkFontStyle());
77        SkPaint p;
78        p.setTypeface(fTypeface);
79        size_t txtLen = strlen(fText);
80        int glyphCount = p.textToGlyphs(fText, txtLen, nullptr);
81
82        fGlyphs.append(glyphCount);
83        p.textToGlyphs(fText, txtLen, fGlyphs.begin());
84    }
85
86    SkString onShortName() override {
87        return SkString("textblob");
88    }
89
90    SkISize onISize() override {
91        return SkISize::Make(640, 480);
92    }
93
94    void onDraw(SkCanvas* canvas) override {
95        for (unsigned b = 0; b < SK_ARRAY_COUNT(blobConfigs); ++b) {
96            sk_sp<SkTextBlob> blob(this->makeBlob(b));
97
98            SkPaint p;
99            SkPoint offset = SkPoint::Make(SkIntToScalar(10 + 300 * (b % 2)),
100                                           SkIntToScalar(20 + 150 * (b / 2)));
101
102            canvas->drawTextBlob(blob, offset.x(), offset.y(), p);
103
104            p.setColor(SK_ColorBLUE);
105            p.setStyle(SkPaint::kStroke_Style);
106            SkRect box = blob->bounds();
107            box.offset(offset);
108            canvas->drawRect(box, p);
109
110        }
111    }
112
113private:
114    sk_sp<SkTextBlob> makeBlob(unsigned blobIndex) {
115        SkTextBlobBuilder builder;
116
117        SkPaint font;
118        font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
119        font.setAntiAlias(true);
120        font.setSubpixelText(true);
121        font.setTypeface(fTypeface);
122
123        for (unsigned l = 0; l < SK_ARRAY_COUNT(blobConfigs[blobIndex]); ++l) {
124            unsigned currentGlyph = 0;
125
126            for (unsigned c = 0; c < SK_ARRAY_COUNT(blobConfigs[blobIndex][l]); ++c) {
127                const BlobCfg* cfg = &blobConfigs[blobIndex][l][c];
128                unsigned count = cfg->count;
129
130                if (count > fGlyphs.count() - currentGlyph) {
131                    count = fGlyphs.count() - currentGlyph;
132                }
133                if (0 == count) {
134                    break;
135                }
136
137                font.setTextSize(kFontSize * cfg->scale);
138                const SkScalar advanceX = font.getTextSize() * 0.85f;
139                const SkScalar advanceY = font.getTextSize() * 1.5f;
140
141                SkPoint offset = SkPoint::Make(currentGlyph * advanceX + c * advanceX,
142                                               advanceY * l);
143                switch (cfg->pos) {
144                case kDefault_Pos: {
145                    const SkTextBlobBuilder::RunBuffer& buf = builder.allocRun(font, count,
146                                                                               offset.x(),
147                                                                               offset.y());
148                    memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t));
149                } break;
150                case kScalar_Pos: {
151                    const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPosH(font, count,
152                                                                                   offset.y());
153                    SkTDArray<SkScalar> pos;
154                    for (unsigned i = 0; i < count; ++i) {
155                        *pos.append() = offset.x() + i * advanceX;
156                    }
157
158                    memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t));
159                    memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar));
160                } break;
161                case kPoint_Pos: {
162                    const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPos(font, count);
163
164                    SkTDArray<SkScalar> pos;
165                    for (unsigned i = 0; i < count; ++i) {
166                        *pos.append() = offset.x() + i * advanceX;
167                        *pos.append() = offset.y() + i * (advanceY / count);
168                    }
169
170                    memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t));
171                    memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar) * 2);
172                } break;
173                default:
174                    SK_ABORT("unhandled pos value");
175                }
176
177                currentGlyph += count;
178            }
179        }
180
181        return builder.make();
182    }
183
184    SkTDArray<uint16_t> fGlyphs;
185    sk_sp<SkTypeface>   fTypeface;
186    const char*         fText;
187    typedef skiagm::GM INHERITED;
188};
189
190DEF_GM(return new TextBlobGM("hamburgefons");)
191