1 /*
2 * Copyright 2018 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
8 #include "src/core/SkGlyph.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkDrawable.h"
13 #include "include/core/SkPicture.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkSerialProcs.h"
16 #include "include/core/SkSpan.h"
17 #include "include/private/base/SkFloatingPoint.h"
18 #include "include/private/base/SkTFitsIn.h"
19 #include "include/private/base/SkTo.h"
20 #include "src/base/SkArenaAlloc.h"
21 #include "src/base/SkBezierCurves.h"
22 #include "src/core/SkReadBuffer.h"
23 #include "src/core/SkScalerContext.h"
24 #include "src/core/SkWriteBuffer.h"
25 #include "src/text/StrikeForGPU.h"
26
27 #include <cstring>
28 #include <optional>
29 #include <tuple>
30 #include <utility>
31
32 using namespace skglyph;
33 using namespace sktext;
34
35 // -- SkPictureBackedGlyphDrawable -----------------------------------------------------------------
36 sk_sp<SkPictureBackedGlyphDrawable>
MakeFromBuffer(SkReadBuffer & buffer)37 SkPictureBackedGlyphDrawable::MakeFromBuffer(SkReadBuffer& buffer) {
38 SkASSERT(buffer.isValid());
39
40 sk_sp<SkData> pictureData = buffer.readByteArrayAsData();
41
42 // Return nullptr if invalid or there an empty drawable, which is represented by nullptr.
43 if (!buffer.isValid() || pictureData->size() == 0) {
44 return nullptr;
45 }
46
47 // Propagate the outer buffer's allow-SkSL setting to the picture decoder, using the flag on
48 // the deserial procs.
49 SkDeserialProcs procs;
50 procs.fAllowSkSL = buffer.allowSkSL();
51 sk_sp<SkPicture> picture = SkPicture::MakeFromData(pictureData.get(), &procs);
52 if (!buffer.validate(picture != nullptr)) {
53 return nullptr;
54 }
55
56 return sk_make_sp<SkPictureBackedGlyphDrawable>(std::move(picture));
57 }
58
FlattenDrawable(SkWriteBuffer & buffer,SkDrawable * drawable)59 void SkPictureBackedGlyphDrawable::FlattenDrawable(SkWriteBuffer& buffer, SkDrawable* drawable) {
60 if (drawable == nullptr) {
61 buffer.writeByteArray(nullptr, 0);
62 return;
63 }
64
65 sk_sp<SkPicture> picture = drawable->makePictureSnapshot();
66 // These drawables should not have SkImages, SkTypefaces or SkPictures inside of them, so
67 // the default SkSerialProcs are sufficient.
68 sk_sp<SkData> data = picture->serialize();
69
70 // If the picture is too big, or there is no picture, then drop by sending an empty byte array.
71 if (!SkTFitsIn<uint32_t>(data->size()) || data->size() == 0) {
72 buffer.writeByteArray(nullptr, 0);
73 return;
74 }
75
76 buffer.writeByteArray(data->data(), data->size());
77 }
78
SkPictureBackedGlyphDrawable(sk_sp<SkPicture> picture)79 SkPictureBackedGlyphDrawable::SkPictureBackedGlyphDrawable(sk_sp<SkPicture> picture)
80 : fPicture(std::move(picture)) {}
81
onGetBounds()82 SkRect SkPictureBackedGlyphDrawable::onGetBounds() {
83 return fPicture->cullRect();
84 }
85
onApproximateBytesUsed()86 size_t SkPictureBackedGlyphDrawable::onApproximateBytesUsed() {
87 return sizeof(SkPictureBackedGlyphDrawable) + fPicture->approximateBytesUsed();
88 }
89
onDraw(SkCanvas * canvas)90 void SkPictureBackedGlyphDrawable::onDraw(SkCanvas* canvas) {
91 canvas->drawPicture(fPicture);
92 }
93
94 //-- SkGlyph ---------------------------------------------------------------------------------------
MakeFromBuffer(SkReadBuffer & buffer)95 std::optional<SkGlyph> SkGlyph::MakeFromBuffer(SkReadBuffer& buffer) {
96 SkASSERT(buffer.isValid());
97 const SkPackedGlyphID packedID{buffer.readUInt()};
98 const SkVector advance = buffer.readPoint();
99 const uint32_t dimensions = buffer.readUInt();
100 const uint32_t leftTop = buffer.readUInt();
101 const SkMask::Format format = SkTo<SkMask::Format>(buffer.readUInt());
102
103 if (!buffer.validate(SkMask::IsValidFormat(format))) {
104 return std::nullopt;
105 }
106
107 SkGlyph glyph{packedID};
108 glyph.fAdvanceX = advance.x();
109 glyph.fAdvanceY = advance.y();
110 glyph.fWidth = dimensions >> 16;
111 glyph.fHeight = dimensions & 0xffffu;
112 glyph.fLeft = leftTop >> 16;
113 glyph.fTop = leftTop & 0xffffu;
114 glyph.fMaskFormat = format;
115 SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
116 return glyph;
117 }
118
mask() const119 SkMask SkGlyph::mask() const {
120 SkIRect bounds = SkIRect::MakeXYWH(fLeft, fTop, fWidth, fHeight);
121 return SkMask(static_cast<const uint8_t*>(fImage), bounds, this->rowBytes(), fMaskFormat);
122 }
123
mask(SkPoint position) const124 SkMask SkGlyph::mask(SkPoint position) const {
125 SkASSERT(SkScalarIsInt(position.x()) && SkScalarIsInt(position.y()));
126 SkIRect bounds = SkIRect::MakeXYWH(fLeft, fTop, fWidth, fHeight);
127 bounds.offset(SkScalarFloorToInt(position.x()), SkScalarFloorToInt(position.y()));
128 return SkMask(static_cast<const uint8_t*>(fImage), bounds, this->rowBytes(), fMaskFormat);
129 }
130
zeroMetrics()131 void SkGlyph::zeroMetrics() {
132 fAdvanceX = 0;
133 fAdvanceY = 0;
134 fWidth = 0;
135 fHeight = 0;
136 fTop = 0;
137 fLeft = 0;
138 }
139
bits_to_bytes(size_t bits)140 static size_t bits_to_bytes(size_t bits) {
141 return (bits + 7) >> 3;
142 }
143
format_alignment(SkMask::Format format)144 static size_t format_alignment(SkMask::Format format) {
145 switch (format) {
146 case SkMask::kBW_Format:
147 case SkMask::kA8_Format:
148 case SkMask::k3D_Format:
149 case SkMask::kSDF_Format:
150 return alignof(uint8_t);
151 case SkMask::kARGB32_Format:
152 return alignof(uint32_t);
153 case SkMask::kLCD16_Format:
154 return alignof(uint16_t);
155 default:
156 SK_ABORT("Unknown mask format.");
157 break;
158 }
159 return 0;
160 }
161
format_rowbytes(int width,SkMask::Format format)162 static size_t format_rowbytes(int width, SkMask::Format format) {
163 return format == SkMask::kBW_Format ? bits_to_bytes(width)
164 : width * format_alignment(format);
165 }
166
formatAlignment() const167 size_t SkGlyph::formatAlignment() const {
168 return format_alignment(this->maskFormat());
169 }
170
allocImage(SkArenaAlloc * alloc)171 size_t SkGlyph::allocImage(SkArenaAlloc* alloc) {
172 SkASSERT(!this->isEmpty());
173 auto size = this->imageSize();
174 fImage = alloc->makeBytesAlignedTo(size, this->formatAlignment());
175
176 return size;
177 }
178
setImage(SkArenaAlloc * alloc,SkScalerContext * scalerContext)179 bool SkGlyph::setImage(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
180 if (!this->setImageHasBeenCalled()) {
181 // It used to be that getImage() could change the fMaskFormat. Extra checking to make
182 // sure there are no regressions.
183 SkDEBUGCODE(SkMask::Format oldFormat = this->maskFormat());
184 this->allocImage(alloc);
185 scalerContext->getImage(*this);
186 SkASSERT(oldFormat == this->maskFormat());
187 return true;
188 }
189 return false;
190 }
191
setImage(SkArenaAlloc * alloc,const void * image)192 bool SkGlyph::setImage(SkArenaAlloc* alloc, const void* image) {
193 if (!this->setImageHasBeenCalled()) {
194 this->allocImage(alloc);
195 memcpy(fImage, image, this->imageSize());
196 return true;
197 }
198 return false;
199 }
200
setMetricsAndImage(SkArenaAlloc * alloc,const SkGlyph & from)201 size_t SkGlyph::setMetricsAndImage(SkArenaAlloc* alloc, const SkGlyph& from) {
202 // Since the code no longer tries to find replacement glyphs, the image should always be
203 // nullptr.
204 SkASSERT(fImage == nullptr || from.fImage == nullptr);
205
206 // TODO(herb): remove "if" when we are sure there are no colliding glyphs.
207 if (fImage == nullptr) {
208 fAdvanceX = from.fAdvanceX;
209 fAdvanceY = from.fAdvanceY;
210 fWidth = from.fWidth;
211 fHeight = from.fHeight;
212 fTop = from.fTop;
213 fLeft = from.fLeft;
214 fScalerContextBits = from.fScalerContextBits;
215 fMaskFormat = from.fMaskFormat;
216
217 // From glyph may not have an image because the glyph is too large.
218 if (from.fImage != nullptr && this->setImage(alloc, from.image())) {
219 return this->imageSize();
220 }
221
222 SkDEBUGCODE(fAdvancesBoundsFormatAndInitialPathDone = from.fAdvancesBoundsFormatAndInitialPathDone;)
223 }
224 return 0;
225 }
226
rowBytes() const227 size_t SkGlyph::rowBytes() const {
228 return format_rowbytes(fWidth, fMaskFormat);
229 }
230
rowBytesUsingFormat(SkMask::Format format) const231 size_t SkGlyph::rowBytesUsingFormat(SkMask::Format format) const {
232 return format_rowbytes(fWidth, format);
233 }
234
imageSize() const235 size_t SkGlyph::imageSize() const {
236 if (this->isEmpty() || this->imageTooLarge()) { return 0; }
237
238 size_t size = this->rowBytes() * fHeight;
239
240 if (fMaskFormat == SkMask::k3D_Format) {
241 size *= 3;
242 }
243
244 return size;
245 }
246
installPath(SkArenaAlloc * alloc,const SkPath * path,bool hairline,bool modified)247 void SkGlyph::installPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline, bool modified) {
248 SkASSERT(fPathData == nullptr);
249 SkASSERT(!this->setPathHasBeenCalled());
250 fPathData = alloc->make<SkGlyph::PathData>();
251 if (path != nullptr) {
252 fPathData->fPath = *path;
253 fPathData->fPath.updateBoundsCache();
254 fPathData->fPath.getGenerationID();
255 fPathData->fHasPath = true;
256 fPathData->fHairline = hairline;
257 fPathData->fModified = modified;
258 }
259 }
260
setPath(SkArenaAlloc * alloc,SkScalerContext * scalerContext)261 bool SkGlyph::setPath(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
262 if (!this->setPathHasBeenCalled()) {
263 scalerContext->getPath(*this, alloc);
264 SkASSERT(this->setPathHasBeenCalled());
265 return this->path() != nullptr;
266 }
267
268 return false;
269 }
270
setPath(SkArenaAlloc * alloc,const SkPath * path,bool hairline,bool modified)271 bool SkGlyph::setPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline, bool modified) {
272 if (!this->setPathHasBeenCalled()) {
273 this->installPath(alloc, path, hairline, modified);
274 return this->path() != nullptr;
275 }
276 return false;
277 }
278
path() const279 const SkPath* SkGlyph::path() const {
280 // setPath must have been called previously.
281 SkASSERT(this->setPathHasBeenCalled());
282 if (fPathData->fHasPath) {
283 return &fPathData->fPath;
284 }
285 return nullptr;
286 }
287
pathIsHairline() const288 bool SkGlyph::pathIsHairline() const {
289 // setPath must have been called previously.
290 SkASSERT(this->setPathHasBeenCalled());
291 return fPathData->fHairline;
292 }
293
pathIsModified() const294 bool SkGlyph::pathIsModified() const {
295 // setPath must have been called previously.
296 SkASSERT(this->setPathHasBeenCalled());
297 return fPathData->fModified;
298 }
299
installDrawable(SkArenaAlloc * alloc,sk_sp<SkDrawable> drawable)300 void SkGlyph::installDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable) {
301 SkASSERT(fDrawableData == nullptr);
302 SkASSERT(!this->setDrawableHasBeenCalled());
303 fDrawableData = alloc->make<SkGlyph::DrawableData>();
304 if (drawable != nullptr) {
305 fDrawableData->fDrawable = std::move(drawable);
306 fDrawableData->fDrawable->getGenerationID();
307 fDrawableData->fHasDrawable = true;
308 }
309 }
310
setDrawable(SkArenaAlloc * alloc,SkScalerContext * scalerContext)311 bool SkGlyph::setDrawable(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
312 if (!this->setDrawableHasBeenCalled()) {
313 sk_sp<SkDrawable> drawable = scalerContext->getDrawable(*this);
314 this->installDrawable(alloc, std::move(drawable));
315 return this->drawable() != nullptr;
316 }
317 return false;
318 }
319
setDrawable(SkArenaAlloc * alloc,sk_sp<SkDrawable> drawable)320 bool SkGlyph::setDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable) {
321 if (!this->setDrawableHasBeenCalled()) {
322 this->installDrawable(alloc, std::move(drawable));
323 return this->drawable() != nullptr;
324 }
325 return false;
326 }
327
drawable() const328 SkDrawable* SkGlyph::drawable() const {
329 // setDrawable must have been called previously.
330 SkASSERT(this->setDrawableHasBeenCalled());
331 if (fDrawableData->fHasDrawable) {
332 return fDrawableData->fDrawable.get();
333 }
334 return nullptr;
335 }
336
flattenMetrics(SkWriteBuffer & buffer) const337 void SkGlyph::flattenMetrics(SkWriteBuffer& buffer) const {
338 buffer.writeUInt(fID.value());
339 buffer.writePoint({fAdvanceX, fAdvanceY});
340 buffer.writeUInt(fWidth << 16 | fHeight);
341 // Note: << has undefined behavior for negative values, so convert everything to the bit
342 // values of uint16_t. Using the cast keeps the signed values fLeft and fTop from sign
343 // extending.
344 const uint32_t left = static_cast<uint16_t>(fLeft);
345 const uint32_t top = static_cast<uint16_t>(fTop);
346 buffer.writeUInt(left << 16 | top);
347 buffer.writeUInt(SkTo<uint32_t>(fMaskFormat));
348 }
349
flattenImage(SkWriteBuffer & buffer) const350 void SkGlyph::flattenImage(SkWriteBuffer& buffer) const {
351 SkASSERT(this->setImageHasBeenCalled());
352
353 // If the glyph is empty or too big, then no image data is sent.
354 if (!this->isEmpty() && SkGlyphDigest::FitsInAtlas(*this)) {
355 buffer.writeByteArray(this->image(), this->imageSize());
356 }
357 }
358
addImageFromBuffer(SkReadBuffer & buffer,SkArenaAlloc * alloc)359 size_t SkGlyph::addImageFromBuffer(SkReadBuffer& buffer, SkArenaAlloc* alloc) {
360 SkASSERT(buffer.isValid());
361
362 // If the glyph is empty or too big, then no image data is received.
363 if (this->isEmpty() || !SkGlyphDigest::FitsInAtlas(*this)) {
364 return 0;
365 }
366
367 size_t memoryIncrease = 0;
368
369 void* imageData = alloc->makeBytesAlignedTo(this->imageSize(), this->formatAlignment());
370 buffer.readByteArray(imageData, this->imageSize());
371 if (buffer.isValid()) {
372 this->installImage(imageData);
373 memoryIncrease += this->imageSize();
374 }
375
376 return memoryIncrease;
377 }
378
flattenPath(SkWriteBuffer & buffer) const379 void SkGlyph::flattenPath(SkWriteBuffer& buffer) const {
380 SkASSERT(this->setPathHasBeenCalled());
381
382 const bool hasPath = this->path() != nullptr;
383 buffer.writeBool(hasPath);
384 if (hasPath) {
385 buffer.writeBool(this->pathIsHairline());
386 buffer.writeBool(this->pathIsModified());
387 buffer.writePath(*this->path());
388 }
389 }
390
addPathFromBuffer(SkReadBuffer & buffer,SkArenaAlloc * alloc)391 size_t SkGlyph::addPathFromBuffer(SkReadBuffer& buffer, SkArenaAlloc* alloc) {
392 SkASSERT(buffer.isValid());
393
394 size_t memoryIncrease = 0;
395 const bool hasPath = buffer.readBool();
396 // Check if the buffer is invalid, so as to not make a logical decision on invalid data.
397 if (!buffer.isValid()) {
398 return 0;
399 }
400 if (hasPath) {
401 const bool pathIsHairline = buffer.readBool();
402 const bool pathIsModified = buffer.readBool();
403 SkPath path;
404 buffer.readPath(&path);
405 if (buffer.isValid()) {
406 if (this->setPath(alloc, &path, pathIsHairline, pathIsModified)) {
407 memoryIncrease += path.approximateBytesUsed();
408 }
409 }
410 } else {
411 this->setPath(alloc, nullptr, false, false);
412 }
413
414 return memoryIncrease;
415 }
416
flattenDrawable(SkWriteBuffer & buffer) const417 void SkGlyph::flattenDrawable(SkWriteBuffer& buffer) const {
418 SkASSERT(this->setDrawableHasBeenCalled());
419
420 if (this->isEmpty() || this->drawable() == nullptr) {
421 SkPictureBackedGlyphDrawable::FlattenDrawable(buffer, nullptr);
422 return;
423 }
424
425 SkPictureBackedGlyphDrawable::FlattenDrawable(buffer, this->drawable());
426 }
427
addDrawableFromBuffer(SkReadBuffer & buffer,SkArenaAlloc * alloc)428 size_t SkGlyph::addDrawableFromBuffer(SkReadBuffer& buffer, SkArenaAlloc* alloc) {
429 SkASSERT(buffer.isValid());
430
431 sk_sp<SkDrawable> drawable = SkPictureBackedGlyphDrawable::MakeFromBuffer(buffer);
432 if (!buffer.isValid()) {
433 return 0;
434 }
435
436 if (this->setDrawable(alloc, std::move(drawable))) {
437 return this->drawable()->approximateBytesUsed();
438 }
439
440 return 0;
441 }
442
calculate_path_gap(SkScalar topOffset,SkScalar bottomOffset,const SkPath & path)443 static std::tuple<SkScalar, SkScalar> calculate_path_gap(
444 SkScalar topOffset, SkScalar bottomOffset, const SkPath& path) {
445
446 // Left and Right of an ever expanding gap around the path.
447 SkScalar left = SK_ScalarMax,
448 right = SK_ScalarMin;
449
450 auto expandGap = [&left, &right](SkScalar v) {
451 left = std::min(left, v);
452 right = std::max(right, v);
453 };
454
455 // Handle all the different verbs for the path.
456 SkPoint pts[4];
457 auto addLine = [&](SkScalar offset) {
458 SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY);
459 if (0 <= t && t < 1) { // this handles divide by zero above
460 expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX));
461 }
462 };
463
464 auto addQuad = [&](SkScalar offset) {
465 SkScalar intersectionStorage[2];
466 auto intersections = SkBezierQuad::IntersectWithHorizontalLine(
467 SkSpan(pts, 3), offset, intersectionStorage);
468 for (SkScalar x : intersections) {
469 expandGap(x);
470 }
471 };
472
473 auto addCubic = [&](SkScalar offset) {
474 float intersectionStorage[3];
475 auto intersections = SkBezierCubic::IntersectWithHorizontalLine(
476 SkSpan{pts, 4}, offset, intersectionStorage);
477
478 for(double intersection : intersections) {
479 expandGap(intersection);
480 }
481 };
482
483 // Handle when a verb's points are in the gap between top and bottom.
484 auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) {
485 for (int i = 0; i < ptCount; ++i) {
486 if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) {
487 expandGap(pts[i].fX);
488 }
489 }
490 };
491
492 SkPath::Iter iter(path, false);
493 SkPath::Verb verb;
494 while (SkPath::kDone_Verb != (verb = iter.next(pts))) {
495 switch (verb) {
496 case SkPath::kMove_Verb: {
497 break;
498 }
499 case SkPath::kLine_Verb: {
500 auto [lineTop, lineBottom] = std::minmax({pts[0].fY, pts[1].fY});
501
502 // The y-coordinates of the points intersect the top and bottom offsets.
503 if (topOffset <= lineBottom && lineTop <= bottomOffset) {
504 addLine(topOffset);
505 addLine(bottomOffset);
506 addPts(2);
507 }
508 break;
509 }
510 case SkPath::kQuad_Verb: {
511 auto [quadTop, quadBottom] = std::minmax({pts[0].fY, pts[1].fY, pts[2].fY});
512
513 // The y-coordinates of the points intersect the top and bottom offsets.
514 if (topOffset <= quadBottom && quadTop <= bottomOffset) {
515 addQuad(topOffset);
516 addQuad(bottomOffset);
517 addPts(3);
518 }
519 break;
520 }
521 case SkPath::kConic_Verb: {
522 SkDEBUGFAIL("There should be no conic primitives in glyph outlines.");
523 break;
524 }
525 case SkPath::kCubic_Verb: {
526 auto [cubicTop, cubicBottom] =
527 std::minmax({pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY});
528
529 // The y-coordinates of the points intersect the top and bottom offsets.
530 if (topOffset <= cubicBottom && cubicTop <= bottomOffset) {
531 addCubic(topOffset);
532 addCubic(bottomOffset);
533 addPts(4);
534 }
535 break;
536 }
537 case SkPath::kClose_Verb: {
538 break;
539 }
540 default: {
541 SkDEBUGFAIL("Unknown path verb generating glyph underline.");
542 break;
543 }
544 }
545 }
546
547 return std::tie(left, right);
548 }
549
ensureIntercepts(const SkScalar * bounds,SkScalar scale,SkScalar xPos,SkScalar * array,int * count,SkArenaAlloc * alloc)550 void SkGlyph::ensureIntercepts(const SkScalar* bounds, SkScalar scale, SkScalar xPos,
551 SkScalar* array, int* count, SkArenaAlloc* alloc) {
552
553 auto offsetResults = [scale, xPos](
554 const SkGlyph::Intercept* intercept,SkScalar* array, int* count) {
555 if (array) {
556 array += *count;
557 for (int index = 0; index < 2; index++) {
558 *array++ = intercept->fInterval[index] * scale + xPos;
559 }
560 }
561 *count += 2;
562 };
563
564 const SkGlyph::Intercept* match =
565 [this](const SkScalar bounds[2]) -> const SkGlyph::Intercept* {
566 if (fPathData == nullptr) {
567 return nullptr;
568 }
569 const SkGlyph::Intercept* intercept = fPathData->fIntercept;
570 while (intercept != nullptr) {
571 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) {
572 return intercept;
573 }
574 intercept = intercept->fNext;
575 }
576 return nullptr;
577 }(bounds);
578
579 if (match != nullptr) {
580 if (match->fInterval[0] < match->fInterval[1]) {
581 offsetResults(match, array, count);
582 }
583 return;
584 }
585
586 SkGlyph::Intercept* intercept = alloc->make<SkGlyph::Intercept>();
587 intercept->fNext = fPathData->fIntercept;
588 intercept->fBounds[0] = bounds[0];
589 intercept->fBounds[1] = bounds[1];
590 intercept->fInterval[0] = SK_ScalarMax;
591 intercept->fInterval[1] = SK_ScalarMin;
592 fPathData->fIntercept = intercept;
593 const SkPath* path = &(fPathData->fPath);
594 const SkRect& pathBounds = path->getBounds();
595 if (pathBounds.fBottom < bounds[0] || bounds[1] < pathBounds.fTop) {
596 return;
597 }
598
599 std::tie(intercept->fInterval[0], intercept->fInterval[1])
600 = calculate_path_gap(bounds[0], bounds[1], *path);
601
602 if (intercept->fInterval[0] >= intercept->fInterval[1]) {
603 intercept->fInterval[0] = SK_ScalarMax;
604 intercept->fInterval[1] = SK_ScalarMin;
605 return;
606 }
607 offsetResults(intercept, array, count);
608 }
609
610 namespace {
init_actions(const SkGlyph & glyph)611 uint32_t init_actions(const SkGlyph& glyph) {
612 constexpr uint32_t kAllUnset = 0;
613 constexpr uint32_t kDrop = SkTo<uint32_t>(GlyphAction::kDrop);
614 constexpr uint32_t kAllDrop =
615 kDrop << kDirectMask |
616 kDrop << kDirectMaskCPU |
617 kDrop << kMask |
618 kDrop << kSDFT |
619 kDrop << kPath |
620 kDrop << kDrawable;
621 return glyph.isEmpty() ? kAllDrop : kAllUnset;
622 }
623 } // namespace
624
625 // -- SkGlyphDigest --------------------------------------------------------------------------------
SkGlyphDigest(size_t index,const SkGlyph & glyph)626 SkGlyphDigest::SkGlyphDigest(size_t index, const SkGlyph& glyph)
627 : fPackedID{SkTo<uint64_t>(glyph.getPackedID().value())}
628 , fIndex{SkTo<uint64_t>(index)}
629 , fIsEmpty(glyph.isEmpty())
630 , fFormat(glyph.maskFormat())
631 , fActions{init_actions(glyph)}
632 , fLeft{SkTo<int16_t>(glyph.left())}
633 , fTop{SkTo<int16_t>(glyph.top())}
634 , fWidth{SkTo<uint16_t>(glyph.width())}
635 , fHeight{SkTo<uint16_t>(glyph.height())} {}
636
setActionFor(skglyph::ActionType actionType,SkGlyph * glyph,StrikeForGPU * strike)637 void SkGlyphDigest::setActionFor(skglyph::ActionType actionType,
638 SkGlyph* glyph,
639 StrikeForGPU* strike) {
640 // We don't have to do any more if the glyph is marked as kDrop because it was isEmpty().
641 if (this->actionFor(actionType) == GlyphAction::kUnset) {
642 GlyphAction action = GlyphAction::kReject;
643 switch (actionType) {
644 case kDirectMask: {
645 if (this->fitsInAtlasDirect()) {
646 action = GlyphAction::kAccept;
647 }
648 break;
649 }
650 case kDirectMaskCPU: {
651 if (strike->prepareForImage(glyph)) {
652 SkASSERT(!glyph->isEmpty());
653 action = GlyphAction::kAccept;
654 }
655 break;
656 }
657 case kMask: {
658 if (this->fitsInAtlasInterpolated()) {
659 action = GlyphAction::kAccept;
660 }
661 break;
662 }
663 case kSDFT: {
664 if (this->fitsInAtlasDirect() &&
665 this->maskFormat() == SkMask::Format::kSDF_Format) {
666 action = GlyphAction::kAccept;
667 }
668 break;
669 }
670 case kPath: {
671 if (strike->prepareForPath(glyph)) {
672 action = GlyphAction::kAccept;
673 }
674 break;
675 }
676 case kDrawable: {
677 if (strike->prepareForDrawable(glyph)) {
678 action = GlyphAction::kAccept;
679 }
680 break;
681 }
682 }
683 this->setAction(actionType, action);
684 }
685 }
686
FitsInAtlas(const SkGlyph & glyph)687 bool SkGlyphDigest::FitsInAtlas(const SkGlyph& glyph) {
688 return glyph.maxDimension() <= kSkSideTooBigForAtlas;
689 }
690
691 // -- SkGlyphPositionRoundingSpec ------------------------------------------------------------------
HalfAxisSampleFreq(bool isSubpixel,SkAxisAlignment axisAlignment)692 SkVector SkGlyphPositionRoundingSpec::HalfAxisSampleFreq(
693 bool isSubpixel, SkAxisAlignment axisAlignment) {
694 if (!isSubpixel) {
695 return {SK_ScalarHalf, SK_ScalarHalf};
696 } else {
697 switch (axisAlignment) {
698 case SkAxisAlignment::kX:
699 return {SkPackedGlyphID::kSubpixelRound, SK_ScalarHalf};
700 case SkAxisAlignment::kY:
701 return {SK_ScalarHalf, SkPackedGlyphID::kSubpixelRound};
702 case SkAxisAlignment::kNone:
703 return {SkPackedGlyphID::kSubpixelRound, SkPackedGlyphID::kSubpixelRound};
704 }
705 }
706
707 // Some compilers need this.
708 return {0, 0};
709 }
710
IgnorePositionMask(bool isSubpixel,SkAxisAlignment axisAlignment)711 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionMask(
712 bool isSubpixel, SkAxisAlignment axisAlignment) {
713 return SkIPoint::Make((!isSubpixel || axisAlignment == SkAxisAlignment::kY) ? 0 : ~0,
714 (!isSubpixel || axisAlignment == SkAxisAlignment::kX) ? 0 : ~0);
715 }
716
IgnorePositionFieldMask(bool isSubpixel,SkAxisAlignment axisAlignment)717 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionFieldMask(bool isSubpixel,
718 SkAxisAlignment axisAlignment) {
719 SkIPoint ignoreMask = IgnorePositionMask(isSubpixel, axisAlignment);
720 SkIPoint answer{ignoreMask.x() & SkPackedGlyphID::kXYFieldMask.x(),
721 ignoreMask.y() & SkPackedGlyphID::kXYFieldMask.y()};
722 return answer;
723 }
724
SkGlyphPositionRoundingSpec(bool isSubpixel,SkAxisAlignment axisAlignment)725 SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(
726 bool isSubpixel, SkAxisAlignment axisAlignment)
727 : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
728 , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)}
729 , ignorePositionFieldMask {IgnorePositionFieldMask(isSubpixel, axisAlignment)} {}
730