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