Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
154static sk_sp<SkTextBlob> make_text(const SkFont& font, const SkGlyphID glyphs[], int count) {
155 return SkTextBlob::MakeFromText(glyphs, count * sizeof(SkGlyphID), font,
157}
158
159static sk_sp<SkTextBlob> make_posh(const SkFont& font, const SkGlyphID glyphs[], int count,
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
170static sk_sp<SkTextBlob> make_pos(const SkFont& font, const SkGlyphID glyphs[], int count,
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);
245 font.setEdging(SkFont::Edging::kAntiAlias);
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]
int count
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[]
void drawPath(const SkPath &path, const SkPaint &paint)
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
@ 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
void offset(SkScalar dx, SkScalar dy, SkPath *dst) const
Definition SkPath.cpp:1627
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
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)
const RunBuffer & allocRun(const SkFont &font, int count, SkScalar x, SkScalar y, const SkRect *bounds=nullptr)
sk_sp< SkTextBlob > make()
const RunBuffer & allocRunPos(const SkFont &font, int count, const SkRect *bounds=nullptr)
static sk_sp< SkTextBlob > MakeFromPosTextH(const void *text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
static sk_sp< SkTextBlob > MakeFromText(const void *text, size_t byteLength, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
static sk_sp< SkTextBlob > MakeFromPosText(const void *text, size_t byteLength, const SkPoint pos[], const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
float SkScalar
Definition extension.cpp:12
glong glong end
#define DEF_SIMPLE_GM(NAME, CANVAS, W, H)
Definition gm.h:50
size_t length
std::u16string text
double y
SkFont DefaultPortableFont()
sk_sp< SkTypeface > CreatePortableTypeface(const char *name, SkFontStyle style)
font
Font Metadata and Metrics.
float fX
x-axis value
constexpr float y() const
constexpr float x() const
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)
static SkPath create_underline(const SkTDArray< SkScalar > &intersections, SkScalar last, SkScalar finalPos, SkScalar uPos, SkScalar uWidth, SkScalar textSize)
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)