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