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