Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkGlyphTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google LLC
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
12#include "include/core/SkPath.h"
16#include "include/core/SkRect.h"
23#include "src/core/SkGlyph.h"
24#include "src/core/SkMask.h"
27#include "tests/Test.h"
28
29#include <cstddef>
30#include <cstdint>
31#include <initializer_list>
32#include <iterator>
33#include <optional>
34
35DEF_TEST(SkGlyphRectBasic, reporter) {
36 using namespace skglyph;
37 SkGlyphRect r{1, 1, 10, 10};
38 REPORTER_ASSERT(reporter, !r.empty());
39 SkGlyphRect a = rect_union(r, empty_rect());
40 REPORTER_ASSERT(reporter, a.rect() == SkRect::MakeLTRB(1, 1, 10, 10));
41 auto widthHeight = a.widthHeight();
42 REPORTER_ASSERT(reporter, widthHeight.x() == 9 && widthHeight.y() == 9);
43
44 a = rect_intersection(r, full_rect());
45 REPORTER_ASSERT(reporter, a.rect() == SkRect::MakeLTRB(1, 1, 10, 10));
46
47 SkGlyphRect acc = full_rect();
48 for (int x = -10; x < 10; x++) {
49 for(int y = -10; y < 10; y++) {
50 acc = rect_intersection(acc, SkGlyphRect(x, y, x + 20, y + 20));
51 }
52 }
53 REPORTER_ASSERT(reporter, acc.rect() == SkRect::MakeLTRB(9, 9, 10, 10));
54
55 acc = empty_rect();
56 for (int x = -10; x < 10; x++) {
57 for(int y = -10; y < 10; y++) {
58 acc = rect_union(acc, SkGlyphRect(x, y, x + 20, y + 20));
59 }
60 }
61 REPORTER_ASSERT(reporter, acc.rect() == SkRect::MakeLTRB(-10, -10, 29, 29));
62}
63
65public:
66 static void SetGlyph1(SkGlyph* glyph) {
67 glyph->fAdvanceX = 10;
68 glyph->fAdvanceY = 11;
69 glyph->fLeft = -1;
70 glyph->fTop = -2;
71 glyph->fWidth = 8;
72 glyph->fHeight = 9;
73 glyph->fMaskFormat = SkMask::Format::kA8_Format;
74 }
75
76 static void SetGlyph2(SkGlyph* glyph) {
77 glyph->fAdvanceX = 10;
78 glyph->fAdvanceY = 11;
79 glyph->fLeft = 0;
80 glyph->fTop = -1;
81 glyph->fWidth = 8;
82 glyph->fHeight = 9;
83 glyph->fMaskFormat = SkMask::Format::kA8_Format;
84 }
85};
86
87DEF_TEST(SkGlyph_SendMetrics, reporter) {
88 {
89 SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
91
92 SkBinaryWriteBuffer writeBuffer({});
93 srcGlyph.flattenMetrics(writeBuffer);
94
95 sk_sp<SkData> data = writeBuffer.snapshotAsData();
96
97 SkReadBuffer readBuffer{data->data(), data->size()};
98 std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
99 REPORTER_ASSERT(reporter, readBuffer.isValid());
100 REPORTER_ASSERT(reporter, dstGlyph.has_value());
101 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
102 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
103 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
104 }
105 {
106 SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
108
109 SkBinaryWriteBuffer writeBuffer({});
110 srcGlyph.flattenMetrics(writeBuffer);
111
112 sk_sp<SkData> data = writeBuffer.snapshotAsData();
113
114 SkReadBuffer readBuffer{data->data(), data->size()};
115 std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
116 REPORTER_ASSERT(reporter, readBuffer.isValid());
117 REPORTER_ASSERT(reporter, dstGlyph.has_value());
118 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
119 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
120 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
121 }
122
123 uint8_t badData[] = {1, 2, 3, 4, 5, 6, 7, 8};
124 SkReadBuffer badBuffer{badData, std::size(badData)};
125 std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(badBuffer);
126 REPORTER_ASSERT(reporter, !badBuffer.isValid());
127 REPORTER_ASSERT(reporter, !dstGlyph.has_value());
128}
129
130DEF_TEST(SkGlyph_SendWithImage, reporter) {
131 SkArenaAlloc alloc{256};
132 SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
134
135 static constexpr uint8_t X = 0xff;
136 static constexpr uint8_t O = 0x00;
137 uint8_t imageData[][8] = {
138 {X,X,X,X,X,X,X,X},
139 {X,O,O,O,O,O,O,X},
140 {X,O,O,O,O,O,O,X},
141 {X,O,O,O,O,O,O,X},
142 {X,O,O,X,X,O,O,X},
143 {X,O,O,O,O,O,O,X},
144 {X,O,O,O,O,O,O,X},
145 {X,O,O,O,O,O,O,X},
146 {X,X,X,X,X,X,X,X},
147 };
148
149 srcGlyph.setImage(&alloc, imageData);
150
151 SkBinaryWriteBuffer writeBuffer({});
152 srcGlyph.flattenMetrics(writeBuffer);
153 srcGlyph.flattenImage(writeBuffer);
154
155 sk_sp<SkData> data = writeBuffer.snapshotAsData();
156
157 SkReadBuffer readBuffer{data->data(), data->size()};
158 std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
159 REPORTER_ASSERT(reporter, readBuffer.isValid());
160 REPORTER_ASSERT(reporter, dstGlyph.has_value());
161 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
162 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
163 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
164
165 dstGlyph->addImageFromBuffer(readBuffer, &alloc);
166 REPORTER_ASSERT(reporter, readBuffer.isValid());
167 const uint8_t* dstImage = (const uint8_t*)dstGlyph->image();
168 for (int y = 0; y < dstGlyph->height(); ++y) {
169 for (int x = 0; x < dstGlyph->width(); ++x) {
170 REPORTER_ASSERT(reporter, imageData[y][x] == dstImage[y * dstGlyph->rowBytes() + x]);
171 }
172 }
173
174 // Add good metrics, but mess up image data
175 SkBinaryWriteBuffer badWriteBuffer({});
176 srcGlyph.flattenMetrics(badWriteBuffer);
177 badWriteBuffer.writeInt(7);
178 badWriteBuffer.writeInt(8);
179
180 data = badWriteBuffer.snapshotAsData();
181
182 SkReadBuffer badReadBuffer{data->data(), data->size()};
183 dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
184 REPORTER_ASSERT(reporter, badReadBuffer.isValid()); // Reading glyph metrics is okay.
185 REPORTER_ASSERT(reporter, dstGlyph.has_value());
186 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
187 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
188 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
189
190 dstGlyph->addImageFromBuffer(badReadBuffer, &alloc);
191 REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
192 REPORTER_ASSERT(reporter, !dstGlyph->setImageHasBeenCalled());
193}
194
195DEF_TEST(SkGlyph_SendWithPath, reporter) {
196 SkArenaAlloc alloc{256};
197 SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
199
200 SkPath srcPath;
201 srcPath.addRect(srcGlyph.rect());
202
203 srcGlyph.setPath(&alloc, &srcPath, false);
204
205 SkBinaryWriteBuffer writeBuffer({});
206 srcGlyph.flattenMetrics(writeBuffer);
207 srcGlyph.flattenPath(writeBuffer);
208
209 sk_sp<SkData> data = writeBuffer.snapshotAsData();
210
211 SkReadBuffer readBuffer{data->data(), data->size()};
212 std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
213 REPORTER_ASSERT(reporter, readBuffer.isValid());
214 REPORTER_ASSERT(reporter, dstGlyph.has_value());
215 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
216 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
217 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
218
219 dstGlyph->addPathFromBuffer(readBuffer, &alloc);
220 REPORTER_ASSERT(reporter, readBuffer.isValid());
221 REPORTER_ASSERT(reporter, dstGlyph->setPathHasBeenCalled());
222 const SkPath* dstPath = dstGlyph->path();
223 REPORTER_ASSERT(reporter, *dstPath == srcPath);
224
225 {
226 // Add good metrics, but mess up path data
227 SkBinaryWriteBuffer badWriteBuffer({});
228 srcGlyph.flattenMetrics(badWriteBuffer);
229 // Force a false value to be read in addPathFromBuffer for hasPath.
230 badWriteBuffer.writeInt(8);
231 badWriteBuffer.writeInt(9);
232
233 data = badWriteBuffer.snapshotAsData();
234
235 SkReadBuffer badReadBuffer{data->data(), data->size()};
236 dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
237 REPORTER_ASSERT(reporter, dstGlyph.has_value());
238 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
239 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
240 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
241
242 dstGlyph->addPathFromBuffer(badReadBuffer, &alloc);
243 REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
244 REPORTER_ASSERT(reporter, !dstGlyph->setPathHasBeenCalled());
245 }
246 {
247 // Add good metrics, but no path data.
248 SkBinaryWriteBuffer badWriteBuffer({});
249 srcGlyph.flattenMetrics(badWriteBuffer);
250
251 data = badWriteBuffer.snapshotAsData();
252
253 SkReadBuffer badReadBuffer{data->data(), data->size()};
254 dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
255 REPORTER_ASSERT(reporter, badReadBuffer.isValid()); // Reading glyph metrics is okay.
256 REPORTER_ASSERT(reporter, dstGlyph.has_value());
257 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
258 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
259 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
260
261 dstGlyph->addPathFromBuffer(badReadBuffer, &alloc);
262 REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
263 REPORTER_ASSERT(reporter, !dstGlyph->setPathHasBeenCalled());
264 }
265}
266
267DEF_TEST(SkGlyph_SendWithDrawable, reporter) {
268 SkArenaAlloc alloc{256};
269 SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
271
272 class TestDrawable final : public SkDrawable {
273 public:
274 TestDrawable(SkRect rect) : fRect(rect) {}
275 private:
276 const SkRect fRect;
277 SkRect onGetBounds() override { return fRect; }
278 size_t onApproximateBytesUsed() override {
279 return 0;
280 }
281 void onDraw(SkCanvas* canvas) override {
283 canvas->drawRect(fRect, paint);
284 }
285 };
286
287 sk_sp<SkDrawable> srcDrawable = sk_make_sp<TestDrawable>(srcGlyph.rect());
288 srcGlyph.setDrawable(&alloc, srcDrawable);
289 REPORTER_ASSERT(reporter, srcGlyph.setDrawableHasBeenCalled());
290
291 SkBinaryWriteBuffer writeBuffer({});
292 srcGlyph.flattenMetrics(writeBuffer);
293 srcGlyph.flattenDrawable(writeBuffer);
294
295 sk_sp<SkData> data = writeBuffer.snapshotAsData();
296
297 SkReadBuffer readBuffer{data->data(), data->size()};
298 std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
299 REPORTER_ASSERT(reporter, readBuffer.isValid());
300 REPORTER_ASSERT(reporter, dstGlyph.has_value());
301 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
302 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
303 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
304
305 dstGlyph->addDrawableFromBuffer(readBuffer, &alloc);
306 REPORTER_ASSERT(reporter, readBuffer.isValid());
307 REPORTER_ASSERT(reporter, dstGlyph->setDrawableHasBeenCalled());
308 SkDrawable* dstDrawable = dstGlyph->drawable();
309 REPORTER_ASSERT(reporter, dstDrawable->getBounds() == srcDrawable->getBounds());
310
311 // Add good metrics, but mess up drawable data
312 SkBinaryWriteBuffer badWriteBuffer({});
313 srcGlyph.flattenMetrics(badWriteBuffer);
314 badWriteBuffer.writeInt(7);
315 badWriteBuffer.writeInt(8);
316
317 data = badWriteBuffer.snapshotAsData();
318
319 SkReadBuffer badReadBuffer{data->data(), data->size()};
320 dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
321 REPORTER_ASSERT(reporter, badReadBuffer.isValid()); // Reading glyph metrics is okay.
322 REPORTER_ASSERT(reporter, dstGlyph.has_value());
323 REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
324 REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
325 REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
326
327 dstGlyph->addDrawableFromBuffer(badReadBuffer, &alloc);
328 REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
329 REPORTER_ASSERT(reporter, !dstGlyph->setDrawableHasBeenCalled());
330}
331
332DEF_TEST(SkPictureBackedGlyphDrawable_Basic, reporter) {
333 class TestDrawable final : public SkDrawable {
334 public:
335 TestDrawable(SkRect rect) : fRect(rect) {}
336 private:
337 const SkRect fRect;
338 SkRect onGetBounds() override { return fRect; }
339 size_t onApproximateBytesUsed() override {
340 return 0;
341 }
342 void onDraw(SkCanvas* canvas) override {
344 canvas->drawRect(fRect, paint);
345 }
346 };
347
348 sk_sp<SkDrawable> srcDrawable = sk_make_sp<TestDrawable>(SkRect::MakeWH(10, 20));
349 SkBinaryWriteBuffer writeBuffer({});
350 SkPictureBackedGlyphDrawable::FlattenDrawable(writeBuffer, srcDrawable.get());
351
352 sk_sp<SkData> data = writeBuffer.snapshotAsData();
353
354 SkReadBuffer readBuffer{data->data(), data->size()};
355
358
359 REPORTER_ASSERT(reporter, readBuffer.isValid());
360 REPORTER_ASSERT(reporter, dstDrawable != nullptr);
361 REPORTER_ASSERT(reporter, srcDrawable->getBounds() == dstDrawable->getBounds());
362
363 SkBinaryWriteBuffer badWriteBuffer({});
364 badWriteBuffer.writeInt(7);
365 badWriteBuffer.writeInt(8);
366
367 data = badWriteBuffer.snapshotAsData();
368
369 SkReadBuffer badReadBuffer{data->data(), data->size()};
370
373 REPORTER_ASSERT(reporter, badDrawable == nullptr);
374 REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
375}
376
378 SkRect rect = SkRect::MakeWH(50, 50);
379
380 SkPictureRecorder recorder;
381 SkCanvas* canvas = recorder.beginRecording(rect);
382
383 const sk_sp<SkRuntimeEffect> effect =
385 SkString("half4 main(float2 xy) { return half4(0, 1, 0, 1); }"))
386 .effect;
387 SkASSERT(effect);
388
390 paint.setShader(effect->makeShader(/*uniforms=*/nullptr, /*children=*/{}));
391 // See note in make_nested_sksl_drawable: We include enough ops that this drawable will be
392 // preserved as a sub-picture when we wrap it in a second layer.
393 for (int i = 0; i < kMaxPictureOpsToUnrollInsteadOfRef + 1; ++i) {
394 canvas->drawRect(rect, paint);
395 }
396
397 return recorder.finishRecordingAsDrawable();
398}
399
401 SkRect rect = SkRect::MakeWH(50, 50);
402
403 SkPictureRecorder recorder;
404 SkCanvas* canvas = recorder.beginRecording(rect);
405
406 auto sksl_drawable = make_sksl_drawable();
407 sk_sp<SkPicture> sksl_picture = sksl_drawable->makePictureSnapshot();
408
409 // We need to ensure that the op count of our picture is larger than this threshold, so we
410 // actually get a nested (embedded) picture, rather than just playing the ops back.
411 SkASSERT(sksl_picture->approximateOpCount() > kMaxPictureOpsToUnrollInsteadOfRef);
412 canvas->drawPicture(sksl_picture);
413
414 return recorder.finishRecordingAsDrawable();
415}
416
417DEF_TEST(SkPictureBackedGlyphDrawable_SkSL, reporter) {
418 for (const sk_sp<SkDrawable>& drawable : {make_sksl_drawable(), make_nested_sksl_drawable()}) {
419 for (bool allowSkSL : {true, false}) {
420 REPORTER_ASSERT(reporter, drawable);
421
422 SkBinaryWriteBuffer writeBuffer({});
423 SkPictureBackedGlyphDrawable::FlattenDrawable(writeBuffer, drawable.get());
424
425 sk_sp<SkData> data = writeBuffer.snapshotAsData();
426
427 SkReadBuffer readBuffer{data->data(), data->size()};
428 readBuffer.setAllowSkSL(allowSkSL);
429
432
433 REPORTER_ASSERT(reporter, readBuffer.isValid() == allowSkSL);
434 REPORTER_ASSERT(reporter, !!dstDrawable == allowSkSL);
435 }
436 }
437}
SkRect fRect
reporter
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr int kMaxPictureOpsToUnrollInsteadOfRef
static sk_sp< SkDrawable > make_sksl_drawable()
static sk_sp< SkDrawable > make_nested_sksl_drawable()
uint16_t SkGlyphID
Definition SkTypes.h:179
static const SkScalar X
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
void writeInt(int32_t value) override
void drawRect(const SkRect &rect, const SkPaint &paint)
void drawPicture(const SkPicture *picture)
Definition SkCanvas.h:1961
virtual SkRect onGetBounds()=0
SkRect getBounds()
virtual void onDraw(SkCanvas *)=0
virtual size_t onApproximateBytesUsed()
SkRect rect() const
Definition SkGlyph.h:259
static void SetGlyph2(SkGlyph *glyph)
static void SetGlyph1(SkGlyph *glyph)
static std::optional< SkGlyph > MakeFromBuffer(SkReadBuffer &)
Definition SkGlyph.cpp:95
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition SkPath.cpp:854
static sk_sp< SkPictureBackedGlyphDrawable > MakeFromBuffer(SkReadBuffer &buffer)
Definition SkGlyph.cpp:37
static void FlattenDrawable(SkWriteBuffer &buffer, SkDrawable *drawable)
Definition SkGlyph.cpp:59
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkDrawable > finishRecordingAsDrawable()
static Result MakeForShader(SkString sksl, const Options &)
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
struct MyStruct a[10]
double y
double x
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
sk_sp< SkRuntimeEffect > effect