Flutter Engine
The Flutter Engine
texteffects.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 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"
10#include "include/core/SkFont.h"
14#include "include/core/SkPath.h"
16#include "include/core/SkRect.h"
25#include "tools/ToolUtils.h"
27
28#include <string.h>
29
30using namespace skia_private;
31
32static SkPath create_underline(const SkTDArray<SkScalar>& intersections,
33 SkScalar last, SkScalar finalPos,
34 SkScalar uPos, SkScalar uWidth, SkScalar textSize) {
35 SkPath underline;
36 SkScalar end = last;
37 for (int index = 0; index < intersections.size(); index += 2) {
38 SkScalar start = intersections[index] - uWidth;
39 end = intersections[index + 1] + uWidth;
40 if (start > last && last + textSize / 12 < start) {
41 underline.moveTo(last, uPos);
42 underline.lineTo(start, uPos);
43 }
44 last = end;
45 }
46 if (end < finalPos) {
47 underline.moveTo(end, uPos);
48 underline.lineTo(finalPos, uPos);
49 }
50 return underline;
51}
52
53namespace {
54
55sk_sp<SkTextBlob> MakeFancyBlob(const SkPaint& paint, const SkFont& font, const char* text) {
56 const size_t textLen = strlen(text);
57 const int glyphCount = font.countText(text, textLen, SkTextEncoding::kUTF8);
58 AutoTArray<SkGlyphID> glyphs(glyphCount);
59 font.textToGlyphs(text, textLen, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
60 AutoTArray<SkScalar> widths(glyphCount);
61 font.getWidths(glyphs.get(), glyphCount, widths.get());
62
63 SkTextBlobBuilder blobBuilder;
64 int glyphIndex = 0;
65 SkScalar advance = 0;
66
67 // Default-positioned run.
68 {
69 const int defaultRunLen = glyphCount / 3;
70 const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRun(font,
71 defaultRunLen,
72 advance, 0);
73 memcpy(buf.glyphs, glyphs.get(), SkTo<uint32_t>(defaultRunLen) * sizeof(SkGlyphID));
74
75 for (int i = 0; i < defaultRunLen; ++i) {
76 advance += widths[glyphIndex++];
77 }
78 }
79
80 // Horizontal-positioned run.
81 {
82 const int horizontalRunLen = glyphCount / 3;
83 const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRunPosH(font,
84 horizontalRunLen,
85 0);
86 memcpy(buf.glyphs, glyphs.get() + glyphIndex,
87 SkTo<uint32_t>(horizontalRunLen) * sizeof(SkGlyphID));
88 for (int i = 0; i < horizontalRunLen; ++i) {
89 buf.pos[i] = advance;
90 advance += widths[glyphIndex++];
91 }
92 }
93
94 // Full-positioned run.
95 {
96 const int fullRunLen = glyphCount - glyphIndex;
97 const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRunPos(font, fullRunLen);
98 memcpy(buf.glyphs, glyphs.get() + glyphIndex,
99 SkTo<uint32_t>(fullRunLen) * sizeof(SkGlyphID));
100 for (int i = 0; i < fullRunLen; ++i) {
101 buf.pos[i * 2 + 0] = advance; // x offset
102 buf.pos[i * 2 + 1] = 0; // y offset
103 advance += widths[glyphIndex++];
104 }
105 }
106
107 return blobBuilder.make();
108}
109
110} // namespace
111
112DEF_SIMPLE_GM(fancyblobunderline, canvas, 1480, 1380) {
114 paint.setAntiAlias(true);
115 const char* fam[] = { "sans-serif", "serif", "monospace" };
116 const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
117 const SkPoint blobOffset = { 10, 80 };
118
119 for (size_t font = 0; font < std::size(fam); ++font) {
120 for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
121 SkFont skFont(ToolUtils::CreatePortableTypeface(fam[font], SkFontStyle()), textSize);
122 const SkScalar uWidth = textSize / 15;
123 paint.setStrokeWidth(uWidth);
124 paint.setStyle(SkPaint::kFill_Style);
125
126 sk_sp<SkTextBlob> blob = MakeFancyBlob(paint, skFont, test);
127 canvas->drawTextBlob(blob, blobOffset.x(), blobOffset.y(), paint);
128
129 const SkScalar uPos = uWidth;
130 const SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
131 const int interceptCount = blob->getIntercepts(bounds, nullptr, &paint);
132 SkASSERT(!(interceptCount % 2));
133
134 SkTDArray<SkScalar> intercepts;
135 intercepts.resize(interceptCount);
136 blob->getIntercepts(bounds, intercepts.begin(), &paint);
137
138 const SkScalar start = blob->bounds().left();
139 const SkScalar end = blob->bounds().right();
140 SkPath underline = create_underline(intercepts, start, end, uPos, uWidth, textSize);
141 underline.offset(blobOffset.x(), blobOffset.y());
143 canvas->drawPath(underline, paint);
144
145 canvas->translate(0, textSize * 1.3f);
146 }
147
148 canvas->translate(0, 60);
149 }
150}
151
152///////////////////////////////////////////////////////////////////////////////////////////////////
153
157}
158
160 SkScalar spacing) {
162 font.getXPos(glyphs, count, xpos.get());
163 for (int i = 1; i < count; ++i) {
164 xpos[i] += spacing * i;
165 }
166 return SkTextBlob::MakeFromPosTextH(glyphs, count * sizeof(SkGlyphID), xpos.get(), 0, font,
168}
169
171 SkScalar spacing) {
173 font.getPos(glyphs, count, pos.get());
174 for (int i = 1; i < count; ++i) {
175 pos[i].fX += spacing * i;
176 }
177 return SkTextBlob::MakeFromPosText(glyphs, count * sizeof(SkGlyphID), pos.get(), font,
179}
180
181// widen the gaps with a margin (on each side of the gap), elimnating segments that go away
182static int trim_with_halo(SkScalar intervals[], int count, SkScalar margin) {
183 SkASSERT(count > 0 && (count & 1) == 0);
184
185 int n = count;
186 SkScalar* stop = intervals + count;
187 *intervals++ -= margin;
188 while (intervals < stop - 1) {
189 intervals[0] += margin;
190 intervals[1] -= margin;
191 if (intervals[0] >= intervals[1]) { // went away
192 int remaining = stop - intervals - 2;
193 SkASSERT(remaining >= 0 && (remaining & 1) == 1);
194 if (remaining > 0) {
195 memmove(intervals, intervals + 2, remaining * sizeof(SkScalar));
196 }
197 stop -= 2;
198 n -= 2;
199 } else {
200 intervals += 2;
201 }
202 }
203 *intervals += margin;
204 return n;
205}
206
207static void draw_blob_adorned(SkCanvas* canvas, sk_sp<SkTextBlob> blob) {
209
210 canvas->drawTextBlob(blob.get(), 0, 0, paint);
211
212 const SkScalar yminmax[] = { 8, 16 };
213 int count = blob->getIntercepts(yminmax, nullptr);
214 if (!count) {
215 return;
216 }
217
218 AutoTArray<SkScalar> intervals(count);
219 blob->getIntercepts(yminmax, intervals.get());
220 count = trim_with_halo(intervals.get(), count, SkScalarHalf(yminmax[1] - yminmax[0]) * 1.5f);
221 SkASSERT(count >= 2);
222
223 const SkScalar y = SkScalarAve(yminmax[0], yminmax[1]);
224 SkScalar end = 900;
225 SkPath path;
226 path.moveTo({0, y});
227 for (int i = 0; i < count; i += 2) {
228 path.lineTo(intervals[i], y).moveTo(intervals[i+1], y);
229 }
230 if (intervals[count - 1] < end) {
231 path.lineTo(end, y);
232 }
233
234 paint.setAntiAlias(true);
236 paint.setStrokeWidth(yminmax[1] - yminmax[0]);
237 canvas->drawPath(path, paint);
238}
239
240DEF_SIMPLE_GM(textblob_intercepts, canvas, 940, 800) {
241 const char text[] = "Hyjay {worlp}.";
242 const size_t length = strlen(text);
244 font.setSize(100);
246 const int count = font.countText(text, length, SkTextEncoding::kUTF8);
248 font.textToGlyphs(text, length, SkTextEncoding::kUTF8, glyphs.get(), count);
249
250 auto b0 = make_text(font, glyphs.get(), count);
251
252 canvas->translate(20, 120);
253 draw_blob_adorned(canvas, b0);
254 for (SkScalar spacing = 0; spacing < 30; spacing += 20) {
255 auto b1 = make_posh(font, glyphs.get(), count, spacing);
256 auto b2 = make_pos( font, glyphs.get(), count, spacing);
257 canvas->translate(0, 150);
258 draw_blob_adorned(canvas, b1);
259 canvas->translate(0, 150);
260 draw_blob_adorned(canvas, b2);
261 }
262}
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
int count
Definition: FontMgrTest.cpp:50
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kUTF8
uses bytes to represent UTF-8 or ASCII
@ kGlyphID
uses two byte words to represent glyph indices
#define SkScalarHalf(a)
Definition: SkScalar.h:75
#define SkScalarAve(a, b)
Definition: SkScalar.h:74
uint16_t SkGlyphID
Definition: SkTypes.h:179
const SkScalar widths[]
Definition: StrokerTest.cpp:39
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
Definition: SkCanvas.cpp:2484
Definition: SkFont.h:35
@ kAntiAlias
may have transparent pixels on glyph edges
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
Definition: SkPath.h:59
void offset(SkScalar dx, SkScalar dy, SkPath *dst) const
Definition: SkPath.cpp:1691
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & lineTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:728
int size() const
Definition: SkTDArray.h:138
T * begin()
Definition: SkTDArray.h:150
void resize(int count)
Definition: SkTDArray.h:183
const RunBuffer & allocRunPosH(const SkFont &font, int count, SkScalar y, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:546
const RunBuffer & allocRun(const SkFont &font, int count, SkScalar x, SkScalar y, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:539
sk_sp< SkTextBlob > make()
Definition: SkTextBlob.cpp:617
const RunBuffer & allocRunPos(const SkFont &font, int count, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:553
int getIntercepts(const SkScalar bounds[2], SkScalar intervals[], const SkPaint *paint=nullptr) const
Definition: SkTextBlob.cpp:927
static sk_sp< SkTextBlob > MakeFromPosTextH(const void *text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:817
const SkRect & bounds() const
Definition: SkTextBlob.h:53
static sk_sp< SkTextBlob > MakeFromText(const void *text, size_t byteLength, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:788
static sk_sp< SkTextBlob > MakeFromPosText(const void *text, size_t byteLength, const SkPoint pos[], const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:803
T * get() const
Definition: SkRefCnt.h:303
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
glong glong end
size_t length
std::u16string text
double y
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkFont DefaultPortableFont()
sk_sp< SkTypeface > CreatePortableTypeface(const char *name, SkFontStyle style)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.
float fX
x-axis value
Definition: SkPoint_impl.h:164
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
constexpr float left() const
Definition: SkRect.h:734
constexpr float right() const
Definition: SkRect.h:748
SkScalar * pos
storage for glyph positions in run
Definition: SkTextBlob.h:330
SkGlyphID * glyphs
storage for glyph indexes in run
Definition: SkTextBlob.h:329
static void draw_blob_adorned(SkCanvas *canvas, sk_sp< SkTextBlob > blob)
static sk_sp< SkTextBlob > make_text(const SkFont &font, const SkGlyphID glyphs[], int count)
DEF_SIMPLE_GM(fancyblobunderline, canvas, 1480, 1380)
static SkPath create_underline(const SkTDArray< SkScalar > &intersections, SkScalar last, SkScalar finalPos, SkScalar uPos, SkScalar uWidth, SkScalar textSize)
Definition: texteffects.cpp:32
static int trim_with_halo(SkScalar intervals[], int count, SkScalar margin)
static sk_sp< SkTextBlob > make_posh(const SkFont &font, const SkGlyphID glyphs[], int count, SkScalar spacing)
static sk_sp< SkTextBlob > make_pos(const SkFont &font, const SkGlyphID glyphs[], int count, SkScalar spacing)