1 /*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/core/SkGlyph.h"
9
10 #include "include/core/SkDrawable.h"
11 #include "include/core/SkScalar.h"
12 #include "include/private/base/SkFloatingPoint.h"
13 #include "include/private/base/SkTo.h"
14 #include "src/base/SkArenaAlloc.h"
15 #include "src/core/SkScalerContext.h"
16 #include "src/pathops/SkPathOpsCubic.h"
17 #include "src/pathops/SkPathOpsPoint.h"
18 #include "src/pathops/SkPathOpsQuad.h"
19 #include "src/text/StrikeForGPU.h"
20
21 #include <cstring>
22 #include <tuple>
23 #include <utility>
24
25 using namespace skglyph;
26 using namespace sktext;
27
28 //-- SkGlyph ---------------------------------------------------------------------------------------
29 SkGlyph::SkGlyph(const SkGlyph&) = default;
30 SkGlyph& SkGlyph::operator=(const SkGlyph&) = default;
31 SkGlyph::SkGlyph(SkGlyph&&) = default;
32 SkGlyph& SkGlyph::operator=(SkGlyph&&) = default;
33 SkGlyph::~SkGlyph() = default;
34
mask() const35 SkMask SkGlyph::mask() const {
36 SkMask mask;
37 mask.fImage = (uint8_t*)fImage;
38 mask.fBounds.setXYWH(fLeft, fTop, fWidth, fHeight);
39 mask.fRowBytes = this->rowBytes();
40 mask.fFormat = fMaskFormat;
41 return mask;
42 }
43
mask(SkPoint position) const44 SkMask SkGlyph::mask(SkPoint position) const {
45 SkASSERT(SkScalarIsInt(position.x()) && SkScalarIsInt(position.y()));
46 SkMask answer = this->mask();
47 answer.fBounds.offset(SkScalarFloorToInt(position.x()), SkScalarFloorToInt(position.y()));
48 return answer;
49 }
50
zeroMetrics()51 void SkGlyph::zeroMetrics() {
52 fAdvanceX = 0;
53 fAdvanceY = 0;
54 fWidth = 0;
55 fHeight = 0;
56 fTop = 0;
57 fLeft = 0;
58 }
59
bits_to_bytes(size_t bits)60 static size_t bits_to_bytes(size_t bits) {
61 return (bits + 7) >> 3;
62 }
63
format_alignment(SkMask::Format format)64 static size_t format_alignment(SkMask::Format format) {
65 switch (format) {
66 case SkMask::kBW_Format:
67 case SkMask::kA8_Format:
68 case SkMask::k3D_Format:
69 case SkMask::kSDF_Format:
70 return alignof(uint8_t);
71 case SkMask::kARGB32_Format:
72 return alignof(uint32_t);
73 case SkMask::kLCD16_Format:
74 return alignof(uint16_t);
75 default:
76 SK_ABORT("Unknown mask format.");
77 break;
78 }
79 return 0;
80 }
81
format_rowbytes(int width,SkMask::Format format)82 static size_t format_rowbytes(int width, SkMask::Format format) {
83 return format == SkMask::kBW_Format ? bits_to_bytes(width)
84 : width * format_alignment(format);
85 }
86
formatAlignment() const87 size_t SkGlyph::formatAlignment() const {
88 return format_alignment(this->maskFormat());
89 }
90
allocImage(SkArenaAlloc * alloc)91 size_t SkGlyph::allocImage(SkArenaAlloc* alloc) {
92 SkASSERT(!this->isEmpty());
93 auto size = this->imageSize();
94 fImage = alloc->makeBytesAlignedTo(size, this->formatAlignment());
95
96 return size;
97 }
98
setImage(SkArenaAlloc * alloc,SkScalerContext * scalerContext)99 bool SkGlyph::setImage(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
100 if (!this->setImageHasBeenCalled()) {
101 // It used to be that getImage() could change the fMaskFormat. Extra checking to make
102 // sure there are no regressions.
103 SkDEBUGCODE(SkMask::Format oldFormat = this->maskFormat());
104 this->allocImage(alloc);
105 scalerContext->getImage(*this);
106 SkASSERT(oldFormat == this->maskFormat());
107 return true;
108 }
109 return false;
110 }
111
setImage(SkArenaAlloc * alloc,const void * image)112 bool SkGlyph::setImage(SkArenaAlloc* alloc, const void* image) {
113 if (!this->setImageHasBeenCalled()) {
114 this->allocImage(alloc);
115 memcpy(fImage, image, this->imageSize());
116 return true;
117 }
118 return false;
119 }
120
setMetricsAndImage(SkArenaAlloc * alloc,const SkGlyph & from)121 size_t SkGlyph::setMetricsAndImage(SkArenaAlloc* alloc, const SkGlyph& from) {
122 // Since the code no longer tries to find replacement glyphs, the image should always be
123 // nullptr.
124 SkASSERT(fImage == nullptr || from.fImage == nullptr);
125
126 // TODO(herb): remove "if" when we are sure there are no colliding glyphs.
127 if (fImage == nullptr) {
128 fAdvanceX = from.fAdvanceX;
129 fAdvanceY = from.fAdvanceY;
130 fWidth = from.fWidth;
131 fHeight = from.fHeight;
132 fTop = from.fTop;
133 fLeft = from.fLeft;
134 fScalerContextBits = from.fScalerContextBits;
135 fMaskFormat = from.fMaskFormat;
136
137 // From glyph may not have an image because the glyph is too large.
138 if (from.fImage != nullptr && this->setImage(alloc, from.image())) {
139 return this->imageSize();
140 }
141
142 SkDEBUGCODE(fAdvancesBoundsFormatAndInitialPathDone = from.fAdvancesBoundsFormatAndInitialPathDone;)
143 }
144 return 0;
145 }
146
rowBytes() const147 size_t SkGlyph::rowBytes() const {
148 return format_rowbytes(fWidth, fMaskFormat);
149 }
150
rowBytesUsingFormat(SkMask::Format format) const151 size_t SkGlyph::rowBytesUsingFormat(SkMask::Format format) const {
152 return format_rowbytes(fWidth, format);
153 }
154
imageSize() const155 size_t SkGlyph::imageSize() const {
156 if (this->isEmpty() || this->imageTooLarge()) { return 0; }
157
158 size_t size = this->rowBytes() * fHeight;
159
160 if (fMaskFormat == SkMask::k3D_Format) {
161 size *= 3;
162 }
163
164 return size;
165 }
166
installPath(SkArenaAlloc * alloc,const SkPath * path,bool hairline)167 void SkGlyph::installPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline) {
168 SkASSERT(fPathData == nullptr);
169 SkASSERT(!this->setPathHasBeenCalled());
170 fPathData = alloc->make<SkGlyph::PathData>();
171 if (path != nullptr) {
172 fPathData->fPath = *path;
173 fPathData->fPath.updateBoundsCache();
174 fPathData->fPath.getGenerationID();
175 fPathData->fHasPath = true;
176 fPathData->fHairline = hairline;
177 }
178 }
179
setPath(SkArenaAlloc * alloc,SkScalerContext * scalerContext)180 bool SkGlyph::setPath(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
181 if (!this->setPathHasBeenCalled()) {
182 scalerContext->getPath(*this, alloc);
183 SkASSERT(this->setPathHasBeenCalled());
184 return this->path() != nullptr;
185 }
186
187 return false;
188 }
189
setPath(SkArenaAlloc * alloc,const SkPath * path,bool hairline)190 bool SkGlyph::setPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline) {
191 if (!this->setPathHasBeenCalled()) {
192 this->installPath(alloc, path, hairline);
193 return this->path() != nullptr;
194 }
195 return false;
196 }
197
path() const198 const SkPath* SkGlyph::path() const {
199 // setPath must have been called previously.
200 SkASSERT(this->setPathHasBeenCalled());
201 if (fPathData->fHasPath) {
202 return &fPathData->fPath;
203 }
204 return nullptr;
205 }
206
pathIsHairline() const207 bool SkGlyph::pathIsHairline() const {
208 // setPath must have been called previously.
209 SkASSERT(this->setPathHasBeenCalled());
210 return fPathData->fHairline;
211 }
212
installDrawable(SkArenaAlloc * alloc,sk_sp<SkDrawable> drawable)213 void SkGlyph::installDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable) {
214 SkASSERT(fDrawableData == nullptr);
215 SkASSERT(!this->setDrawableHasBeenCalled());
216 fDrawableData = alloc->make<SkGlyph::DrawableData>();
217 if (drawable != nullptr) {
218 fDrawableData->fDrawable = std::move(drawable);
219 fDrawableData->fDrawable->getGenerationID();
220 fDrawableData->fHasDrawable = true;
221 }
222 }
223
setDrawable(SkArenaAlloc * alloc,SkScalerContext * scalerContext)224 bool SkGlyph::setDrawable(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
225 if (!this->setDrawableHasBeenCalled()) {
226 sk_sp<SkDrawable> drawable = scalerContext->getDrawable(*this);
227 this->installDrawable(alloc, std::move(drawable));
228 return this->drawable() != nullptr;
229 }
230 return false;
231 }
232
setDrawable(SkArenaAlloc * alloc,sk_sp<SkDrawable> drawable)233 bool SkGlyph::setDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable) {
234 if (!this->setDrawableHasBeenCalled()) {
235 this->installDrawable(alloc, std::move(drawable));
236 return this->drawable() != nullptr;
237 }
238 return false;
239 }
240
drawable() const241 SkDrawable* SkGlyph::drawable() const {
242 // setDrawable must have been called previously.
243 SkASSERT(this->setDrawableHasBeenCalled());
244 if (fDrawableData->fHasDrawable) {
245 return fDrawableData->fDrawable.get();
246 }
247 return nullptr;
248 }
249
calculate_path_gap(SkScalar topOffset,SkScalar bottomOffset,const SkPath & path)250 static std::tuple<SkScalar, SkScalar> calculate_path_gap(
251 SkScalar topOffset, SkScalar bottomOffset, const SkPath& path) {
252
253 // Left and Right of an ever expanding gap around the path.
254 SkScalar left = SK_ScalarMax,
255 right = SK_ScalarMin;
256 auto expandGap = [&left, &right](SkScalar v) {
257 left = std::min(left, v);
258 right = std::max(right, v);
259 };
260
261 // Handle all the different verbs for the path.
262 SkPoint pts[4];
263 auto addLine = [&expandGap, &pts](SkScalar offset) {
264 SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY);
265 if (0 <= t && t < 1) { // this handles divide by zero above
266 expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX));
267 }
268 };
269
270 auto addQuad = [&expandGap, &pts](SkScalar offset) {
271 SkDQuad quad;
272 quad.set(pts);
273 double roots[2];
274 int count = quad.horizontalIntersect(offset, roots);
275 while (--count >= 0) {
276 expandGap(quad.ptAtT(roots[count]).asSkPoint().fX);
277 }
278 };
279
280 auto addCubic = [&expandGap, &pts](SkScalar offset) {
281 SkDCubic cubic;
282 cubic.set(pts);
283 double roots[3];
284 int count = cubic.horizontalIntersect(offset, roots);
285 while (--count >= 0) {
286 expandGap(cubic.ptAtT(roots[count]).asSkPoint().fX);
287 }
288 };
289
290 // Handle when a verb's points are in the gap between top and bottom.
291 auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) {
292 for (int i = 0; i < ptCount; ++i) {
293 if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) {
294 expandGap(pts[i].fX);
295 }
296 }
297 };
298
299 SkPath::Iter iter(path, false);
300 SkPath::Verb verb;
301 while (SkPath::kDone_Verb != (verb = iter.next(pts))) {
302 switch (verb) {
303 case SkPath::kMove_Verb: {
304 break;
305 }
306 case SkPath::kLine_Verb: {
307 addLine(topOffset);
308 addLine(bottomOffset);
309 addPts(2);
310 break;
311 }
312 case SkPath::kQuad_Verb: {
313 SkScalar quadTop = std::min(std::min(pts[0].fY, pts[1].fY), pts[2].fY);
314 if (bottomOffset < quadTop) { break; }
315 SkScalar quadBottom = std::max(std::max(pts[0].fY, pts[1].fY), pts[2].fY);
316 if (topOffset > quadBottom) { break; }
317 addQuad(topOffset);
318 addQuad(bottomOffset);
319 addPts(3);
320 break;
321 }
322 case SkPath::kConic_Verb: {
323 SkASSERT(0); // no support for text composed of conics
324 break;
325 }
326 case SkPath::kCubic_Verb: {
327 SkScalar quadTop =
328 std::min(std::min(std::min(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY);
329 if (bottomOffset < quadTop) { break; }
330 SkScalar quadBottom =
331 std::max(std::max(std::max(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY);
332 if (topOffset > quadBottom) { break; }
333 addCubic(topOffset);
334 addCubic(bottomOffset);
335 addPts(4);
336 break;
337 }
338 case SkPath::kClose_Verb: {
339 break;
340 }
341 default: {
342 SkASSERT(0);
343 break;
344 }
345 }
346 }
347
348 return std::tie(left, right);
349 }
350
ensureIntercepts(const SkScalar * bounds,SkScalar scale,SkScalar xPos,SkScalar * array,int * count,SkArenaAlloc * alloc)351 void SkGlyph::ensureIntercepts(const SkScalar* bounds, SkScalar scale, SkScalar xPos,
352 SkScalar* array, int* count, SkArenaAlloc* alloc) {
353
354 auto offsetResults = [scale, xPos](
355 const SkGlyph::Intercept* intercept,SkScalar* array, int* count) {
356 if (array) {
357 array += *count;
358 for (int index = 0; index < 2; index++) {
359 *array++ = intercept->fInterval[index] * scale + xPos;
360 }
361 }
362 *count += 2;
363 };
364
365 const SkGlyph::Intercept* match =
366 [this](const SkScalar bounds[2]) -> const SkGlyph::Intercept* {
367 if (!fPathData) {
368 return nullptr;
369 }
370 const SkGlyph::Intercept* intercept = fPathData->fIntercept;
371 while (intercept) {
372 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) {
373 return intercept;
374 }
375 intercept = intercept->fNext;
376 }
377 return nullptr;
378 }(bounds);
379
380 if (match) {
381 if (match->fInterval[0] < match->fInterval[1]) {
382 offsetResults(match, array, count);
383 }
384 return;
385 }
386
387 SkGlyph::Intercept* intercept = alloc->make<SkGlyph::Intercept>();
388 intercept->fNext = fPathData->fIntercept;
389 intercept->fBounds[0] = bounds[0];
390 intercept->fBounds[1] = bounds[1];
391 intercept->fInterval[0] = SK_ScalarMax;
392 intercept->fInterval[1] = SK_ScalarMin;
393 fPathData->fIntercept = intercept;
394 const SkPath* path = &(fPathData->fPath);
395 const SkRect& pathBounds = path->getBounds();
396 if (pathBounds.fBottom < bounds[0] || bounds[1] < pathBounds.fTop) {
397 return;
398 }
399
400 std::tie(intercept->fInterval[0], intercept->fInterval[1])
401 = calculate_path_gap(bounds[0], bounds[1], *path);
402
403 if (intercept->fInterval[0] >= intercept->fInterval[1]) {
404 intercept->fInterval[0] = SK_ScalarMax;
405 intercept->fInterval[1] = SK_ScalarMin;
406 return;
407 }
408 offsetResults(intercept, array, count);
409 }
410
411 namespace {
init_actions(const SkGlyph & glyph)412 uint32_t init_actions(const SkGlyph& glyph) {
413 constexpr uint32_t kAllUnset = 0;
414 constexpr uint32_t kDrop = SkTo<uint32_t>(GlyphAction::kDrop);
415 constexpr uint32_t kAllDrop =
416 kDrop << kDirectMask |
417 kDrop << kDirectMaskCPU |
418 kDrop << kMask |
419 kDrop << kSDFT |
420 kDrop << kPath |
421 kDrop << kDrawable;
422 return glyph.isEmpty() ? kAllDrop : kAllUnset;
423 }
424 } // namespace
425
426 // -- SkGlyphDigest --------------------------------------------------------------------------------
SkGlyphDigest(size_t index,const SkGlyph & glyph)427 SkGlyphDigest::SkGlyphDigest(size_t index, const SkGlyph& glyph)
428 : fIndex{SkTo<uint32_t>(index)}
429 , fIsEmpty(glyph.isEmpty())
430 , fFormat(glyph.maskFormat())
431 , fActions{init_actions(glyph)}
432 , fLeft{SkTo<int16_t>(glyph.left())}
433 , fTop{SkTo<int16_t>(glyph.top())}
434 , fWidth{SkTo<uint16_t>(glyph.width())}
435 , fHeight{SkTo<uint16_t>(glyph.height())} {}
436
setActionFor(skglyph::ActionType actionType,SkGlyph * glyph,StrikeForGPU * strike)437 void SkGlyphDigest::setActionFor(skglyph::ActionType actionType,
438 SkGlyph* glyph,
439 StrikeForGPU* strike) {
440 // We don't have to do any more if the glyph is marked as kDrop because it was isEmpty().
441 if (this->actionFor(actionType) == GlyphAction::kUnset) {
442 GlyphAction action = GlyphAction::kReject;
443 switch (actionType) {
444 case kDirectMask: {
445 if (this->fitsInAtlasDirect()) {
446 action = GlyphAction::kAccept;
447 }
448 break;
449 }
450 case kDirectMaskCPU: {
451 if (strike->prepareForImage(glyph)) {
452 action = GlyphAction::kAccept;
453 }
454 break;
455 }
456 case kMask: {
457 if (this->fitsInAtlasInterpolated()) {
458 action = GlyphAction::kAccept;
459 }
460 break;
461 }
462 case kSDFT: {
463 if (this->fitsInAtlasDirect() &&
464 this->maskFormat() == SkMask::Format::kSDF_Format) {
465 action = GlyphAction::kAccept;
466 }
467 break;
468 }
469 case kPath: {
470 if (strike->prepareForPath(glyph)) {
471 action = GlyphAction::kAccept;
472 }
473 break;
474 }
475 case kDrawable: {
476 if (strike->prepareForDrawable(glyph)) {
477 action = GlyphAction::kAccept;
478 }
479 break;
480 }
481 }
482 this->setAction(actionType, action);
483 }
484 }
485
FitsInAtlas(const SkGlyph & glyph)486 bool SkGlyphDigest::FitsInAtlas(const SkGlyph& glyph) {
487 return glyph.maxDimension() <= kSkSideTooBigForAtlas;
488 }
489
490 // -- SkGlyphPositionRoundingSpec ------------------------------------------------------------------
HalfAxisSampleFreq(bool isSubpixel,SkAxisAlignment axisAlignment)491 SkVector SkGlyphPositionRoundingSpec::HalfAxisSampleFreq(
492 bool isSubpixel, SkAxisAlignment axisAlignment) {
493 if (!isSubpixel) {
494 return {SK_ScalarHalf, SK_ScalarHalf};
495 } else {
496 switch (axisAlignment) {
497 case SkAxisAlignment::kX:
498 return {SkPackedGlyphID::kSubpixelRound, SK_ScalarHalf};
499 case SkAxisAlignment::kY:
500 return {SK_ScalarHalf, SkPackedGlyphID::kSubpixelRound};
501 case SkAxisAlignment::kNone:
502 return {SkPackedGlyphID::kSubpixelRound, SkPackedGlyphID::kSubpixelRound};
503 }
504 }
505
506 // Some compilers need this.
507 return {0, 0};
508 }
509
IgnorePositionMask(bool isSubpixel,SkAxisAlignment axisAlignment)510 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionMask(
511 bool isSubpixel, SkAxisAlignment axisAlignment) {
512 return SkIPoint::Make((!isSubpixel || axisAlignment == SkAxisAlignment::kY) ? 0 : ~0,
513 (!isSubpixel || axisAlignment == SkAxisAlignment::kX) ? 0 : ~0);
514 }
515
IgnorePositionFieldMask(bool isSubpixel,SkAxisAlignment axisAlignment)516 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionFieldMask(bool isSubpixel,
517 SkAxisAlignment axisAlignment) {
518 SkIPoint ignoreMask = IgnorePositionMask(isSubpixel, axisAlignment);
519 SkIPoint answer{ignoreMask.x() & SkPackedGlyphID::kXYFieldMask.x(),
520 ignoreMask.y() & SkPackedGlyphID::kXYFieldMask.y()};
521 return answer;
522 }
523
SkGlyphPositionRoundingSpec(bool isSubpixel,SkAxisAlignment axisAlignment)524 SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(
525 bool isSubpixel, SkAxisAlignment axisAlignment)
526 : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
527 , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)}
528 , ignorePositionFieldMask {IgnorePositionFieldMask(isSubpixel, axisAlignment)} {}
529