blob: 74675d7289ffebb8c69bc996486e5c53da31680b [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
mike@reedtribe.org3d1cb972012-08-13 00:52:07 +00007
8#include "gm.h"
Mike Klein33d20552017-03-22 13:47:51 -04009#include "sk_tool_utils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040010
robertphillips@google.comb7061172013-09-06 14:16:12 +000011#include "SkBlurMask.h"
12#include "SkBlurMaskFilter.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000013#include "SkReadBuffer.h"
fmalitaeae6a912016-07-28 09:47:24 -070014#include "SkTextBlob.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040015#include "SkTo.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000016#include "SkWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
18#include "Sk2DPathEffect.h"
19
caryclark0449bcf2016-02-09 13:25:45 -080020static SkPath create_underline(const SkTDArray<SkScalar>& intersections,
21 SkScalar last, SkScalar finalPos,
22 SkScalar uPos, SkScalar uWidth, SkScalar textSize) {
23 SkPath underline;
24 SkScalar end = last;
25 for (int index = 0; index < intersections.count(); index += 2) {
26 SkScalar start = intersections[index] - uWidth;;
27 end = intersections[index + 1] + uWidth;
28 if (start > last && last + textSize / 12 < start) {
29 underline.moveTo(last, uPos);
30 underline.lineTo(start, uPos);
31 }
32 last = end;
33 }
34 if (end < finalPos) {
35 underline.moveTo(end, uPos);
36 underline.lineTo(finalPos, uPos);
37 }
38 return underline;
39}
40
41static void find_intercepts(const char* test, size_t len, SkScalar x, SkScalar y,
42 const SkPaint& paint, SkScalar uWidth, SkTDArray<SkScalar>* intersections) {
43 SkScalar uPos = y + uWidth;
44 SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
45 int count = paint.getTextIntercepts(test, len, x, y, bounds, nullptr);
46 SkASSERT(!(count % 2));
47 if (count) {
48 intersections->setCount(count);
49 paint.getTextIntercepts(test, len, x, y, bounds, intersections->begin());
50 }
51}
52
53DEF_SIMPLE_GM(fancyunderline, canvas, 900, 1350) {
54 SkPaint paint;
55 paint.setAntiAlias(true);
56 const char* fam[] = { "sans-serif", "serif", "monospace" };
57 const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
58 SkPoint textPt = { 10, 80 };
fmalitaeae6a912016-07-28 09:47:24 -070059 for (size_t font = 0; font < SK_ARRAY_COUNT(fam); ++font) {
mbocee6a9912016-05-31 11:42:36 -070060 sk_tool_utils::set_portable_typeface(&paint, fam[font]);
caryclark0449bcf2016-02-09 13:25:45 -080061 for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
62 paint.setTextSize(textSize);
63 const SkScalar uWidth = textSize / 15;
64 paint.setStrokeWidth(uWidth);
65 paint.setStyle(SkPaint::kFill_Style);
66 canvas->drawText(test, sizeof(test) - 1, textPt.fX, textPt.fY, paint);
67
68 SkTDArray<SkScalar> intersections;
69 find_intercepts(test, sizeof(test) - 1, textPt.fX, textPt.fY, paint, uWidth,
70 &intersections);
71
72 SkScalar start = textPt.fX;
73 SkScalar end = paint.measureText(test, sizeof(test) - 1) + textPt.fX;
74 SkScalar uPos = textPt.fY + uWidth;
75 SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize);
76 paint.setStyle(SkPaint::kStroke_Style);
77 canvas->drawPath(underline, paint);
78
79 canvas->translate(0, textSize * 1.3f);
80 }
81 canvas->translate(0, 60);
82 }
83}
84
85static void find_intercepts(const char* test, size_t len, const SkPoint* pos, const SkPaint& paint,
86 SkScalar uWidth, SkTDArray<SkScalar>* intersections) {
87 SkScalar uPos = pos[0].fY + uWidth;
88 SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
89 int count = paint.getPosTextIntercepts(test, len, pos, bounds, nullptr);
90 SkASSERT(!(count % 2));
91 if (count) {
92 intersections->setCount(count);
93 paint.getPosTextIntercepts(test, len, pos, bounds, intersections->begin());
94 }
95}
96
97DEF_SIMPLE_GM(fancyposunderline, canvas, 900, 1350) {
98 SkPaint paint;
99 paint.setAntiAlias(true);
100 const char* fam[] = { "sans-serif", "serif", "monospace" };
101 const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
102 SkPoint textPt = { 10, 80 };
fmalitaeae6a912016-07-28 09:47:24 -0700103 for (size_t font = 0; font < SK_ARRAY_COUNT(fam); ++font) {
mbocee6a9912016-05-31 11:42:36 -0700104 sk_tool_utils::set_portable_typeface(&paint, fam[font]);
caryclark0449bcf2016-02-09 13:25:45 -0800105 for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
106 paint.setTextSize(textSize);
107 const SkScalar uWidth = textSize / 15;
108 paint.setStrokeWidth(uWidth);
109 paint.setStyle(SkPaint::kFill_Style);
110 int widthCount = paint.getTextWidths(test, sizeof(test) - 1, nullptr);
111 SkTDArray<SkScalar> widths;
112 widths.setCount(widthCount);
113 (void) paint.getTextWidths(test, sizeof(test) - 1, widths.begin());
114 SkTDArray<SkPoint> pos;
115 pos.setCount(widthCount);
116 SkScalar posX = textPt.fX;
117 for (int index = 0; index < widthCount; ++index) {
118 pos[index].fX = posX;
119 posX += widths[index];
120 pos[index].fY = textPt.fY + (textSize / 25) * (index % 4);
121 }
122 canvas->drawPosText(test, sizeof(test) - 1, pos.begin(), paint);
123
124 SkTDArray<SkScalar> intersections;
125 find_intercepts(test, sizeof(test) - 1, pos.begin(), paint, uWidth, &intersections);
126
127 SkScalar start = textPt.fX;
128 SkScalar end = posX;
129 SkScalar uPos = textPt.fY + uWidth;
130 SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize);
131 paint.setStyle(SkPaint::kStroke_Style);
132 canvas->drawPath(underline, paint);
133
134 canvas->translate(0, textSize * 1.3f);
135 }
136 canvas->translate(0, 60);
137 }
138}
139
fmalitaeae6a912016-07-28 09:47:24 -0700140namespace {
141
fmalita37283c22016-09-13 10:00:23 -0700142sk_sp<SkTextBlob> MakeFancyBlob(const SkPaint& paint, const char* text) {
fmalitaeae6a912016-07-28 09:47:24 -0700143 SkPaint blobPaint(paint);
144
145 const size_t textLen = strlen(text);
146 const int glyphCount = blobPaint.textToGlyphs(text, textLen, nullptr);
147 SkAutoTArray<SkGlyphID> glyphs(glyphCount);
148 blobPaint.textToGlyphs(text, textLen, glyphs.get());
149
150 blobPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
151 const size_t glyphTextBytes = SkTo<uint32_t>(glyphCount) * sizeof(SkGlyphID);
152 const int widthCount = blobPaint.getTextWidths(glyphs.get(), glyphTextBytes, nullptr);
153 SkAssertResult(widthCount == glyphCount);
154
155 SkAutoTArray<SkScalar> widths(glyphCount);
156 blobPaint.getTextWidths(glyphs.get(), glyphTextBytes, widths.get());
157
158 SkTextBlobBuilder blobBuilder;
159 int glyphIndex = 0;
160 SkScalar advance = 0;
161
162 // Default-positioned run.
163 {
164 const int defaultRunLen = glyphCount / 3;
165 const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRun(blobPaint,
166 defaultRunLen,
167 advance, 0);
168 memcpy(buf.glyphs, glyphs.get(), SkTo<uint32_t>(defaultRunLen) * sizeof(SkGlyphID));
169
170 for (int i = 0; i < defaultRunLen; ++i) {
171 advance += widths[glyphIndex++];
172 }
173 }
174
175 // Horizontal-positioned run.
176 {
177 const int horizontalRunLen = glyphCount / 3;
178 const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRunPosH(blobPaint,
179 horizontalRunLen,
180 0);
181 memcpy(buf.glyphs, glyphs.get() + glyphIndex,
182 SkTo<uint32_t>(horizontalRunLen) * sizeof(SkGlyphID));
183 for (int i = 0; i < horizontalRunLen; ++i) {
184 buf.pos[i] = advance;
185 advance += widths[glyphIndex++];
186 }
187 }
188
189 // Full-positioned run.
190 {
191 const int fullRunLen = glyphCount - glyphIndex;
192 const SkTextBlobBuilder::RunBuffer& buf = blobBuilder.allocRunPos(blobPaint, fullRunLen);
193 memcpy(buf.glyphs, glyphs.get() + glyphIndex,
194 SkTo<uint32_t>(fullRunLen) * sizeof(SkGlyphID));
195 for (int i = 0; i < fullRunLen; ++i) {
196 buf.pos[i * 2 + 0] = advance; // x offset
197 buf.pos[i * 2 + 1] = 0; // y offset
198 advance += widths[glyphIndex++];
199 }
200 }
201
fmalita37283c22016-09-13 10:00:23 -0700202 return blobBuilder.make();
fmalitaeae6a912016-07-28 09:47:24 -0700203}
204
205} // anonymous ns
206
207DEF_SIMPLE_GM(fancyblobunderline, canvas, 1480, 1380) {
208 SkPaint paint;
209 paint.setAntiAlias(true);
210 const char* fam[] = { "sans-serif", "serif", "monospace" };
211 const char test[] = "aAjJgGyY_|{-(~[,]qQ}pP}zZ";
212 const SkPoint blobOffset = { 10, 80 };
213
214 for (size_t font = 0; font < SK_ARRAY_COUNT(fam); ++font) {
215 sk_tool_utils::set_portable_typeface(&paint, fam[font]);
216 for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
217 paint.setTextSize(textSize);
218 const SkScalar uWidth = textSize / 15;
219 paint.setStrokeWidth(uWidth);
220 paint.setStyle(SkPaint::kFill_Style);
221
fmalita37283c22016-09-13 10:00:23 -0700222 sk_sp<SkTextBlob> blob = MakeFancyBlob(paint, test);
223 canvas->drawTextBlob(blob, blobOffset.x(), blobOffset.y(), paint);
fmalitaeae6a912016-07-28 09:47:24 -0700224
225 const SkScalar uPos = uWidth;
226 const SkScalar bounds[2] = { uPos - uWidth / 2, uPos + uWidth / 2 };
227 const int interceptCount = paint.getTextBlobIntercepts(blob.get(), bounds, nullptr);
228 SkASSERT(!(interceptCount % 2));
229
230 SkTDArray<SkScalar> intercepts;
231 intercepts.setCount(interceptCount);
232 paint.getTextBlobIntercepts(blob.get(), bounds, intercepts.begin());
233
234 const SkScalar start = blob->bounds().left();
235 const SkScalar end = blob->bounds().right();
236 SkPath underline = create_underline(intercepts, start, end, uPos, uWidth, textSize);
237 underline.offset(blobOffset.x(), blobOffset.y());
238 paint.setStyle(SkPaint::kStroke_Style);
239 canvas->drawPath(underline, paint);
240
241 canvas->translate(0, textSize * 1.3f);
242 }
243
244 canvas->translate(0, 60);
245 }
246}
247
caryclark0449bcf2016-02-09 13:25:45 -0800248DEF_SIMPLE_GM(fancyunderlinebars, canvas, 1500, 460) {
249 SkPaint paint;
250 paint.setAntiAlias(true);
251 const char test[] = " .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_ .}]_";
252 SkPoint textPt = { 10, 80 };
253 sk_tool_utils::set_portable_typeface(&paint, "serif");
254 for (SkScalar textSize = 100; textSize > 10; textSize -= 20) {
255 paint.setTextSize(textSize);
256 SkScalar uWidth = textSize / 15;
257 paint.setStrokeWidth(uWidth);
258 paint.setStyle(SkPaint::kFill_Style);
259 int widthCount = paint.getTextWidths(test, sizeof(test) - 1, nullptr);
260 SkTDArray<SkScalar> widths;
261 widths.setCount(widthCount);
262 (void) paint.getTextWidths(test, sizeof(test) - 1, widths.begin());
263 SkTDArray<SkPoint> pos;
264 pos.setCount(widthCount);
265 SkScalar posX = textPt.fX;
266 pos[0] = textPt;
267 posX += widths[0];
268 for (int index = 1; index < widthCount; ++index) {
269 pos[index].fX = posX;
270 posX += widths[index];
271 pos[index].fY = textPt.fY - (textSize / 50) * (index / 5) + textSize / 50 * 4;
272 }
273 canvas->drawPosText(test, sizeof(test) - 1, pos.begin(), paint);
274
275 SkTDArray<SkScalar> intersections;
276 find_intercepts(test, sizeof(test) - 1, pos.begin(), paint, uWidth, &intersections);
277
278 SkScalar start = textPt.fX;
279 SkScalar end = posX;
280 SkScalar uPos = pos[0].fY + uWidth;
281 SkPath underline = create_underline(intersections, start, end, uPos, uWidth, textSize);
282 paint.setStyle(SkPaint::kStroke_Style);
283 canvas->drawPath(underline, paint);
284 canvas->translate(0, textSize * 1.3f);
285 }
286}