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