• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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