• 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 "src/core/SkArenaAlloc.h"
11 #include "src/core/SkScalerContext.h"
12 #include "src/pathops/SkPathOpsCubic.h"
13 #include "src/pathops/SkPathOpsQuad.h"
14 
15 constexpr SkIPoint SkPackedGlyphID::kXYFieldMask;
16 
mask() const17 SkMask SkGlyph::mask() const {
18     SkMask mask;
19     mask.fImage = (uint8_t*)fImage;
20     mask.fBounds.setXYWH(fLeft, fTop, fWidth, fHeight);
21     mask.fRowBytes = this->rowBytes();
22     mask.fFormat = fMaskFormat;
23     return mask;
24 }
25 
mask(SkPoint position) const26 SkMask SkGlyph::mask(SkPoint position) const {
27     SkMask answer = this->mask();
28     answer.fBounds.offset(SkScalarFloorToInt(position.x()), SkScalarFloorToInt(position.y()));
29     return answer;
30 }
31 
zeroMetrics()32 void SkGlyph::zeroMetrics() {
33     fAdvanceX = 0;
34     fAdvanceY = 0;
35     fWidth    = 0;
36     fHeight   = 0;
37     fTop      = 0;
38     fLeft     = 0;
39 }
40 
bits_to_bytes(size_t bits)41 static size_t bits_to_bytes(size_t bits) {
42     return (bits + 7) >> 3;
43 }
44 
format_alignment(SkMask::Format format)45 static size_t format_alignment(SkMask::Format format) {
46     switch (format) {
47         case SkMask::kBW_Format:
48         case SkMask::kA8_Format:
49         case SkMask::k3D_Format:
50         case SkMask::kSDF_Format:
51             return alignof(uint8_t);
52         case SkMask::kARGB32_Format:
53             return alignof(uint32_t);
54         case SkMask::kLCD16_Format:
55             return alignof(uint16_t);
56         default:
57             SK_ABORT("Unknown mask format.");
58             break;
59     }
60     return 0;
61 }
62 
format_rowbytes(int width,SkMask::Format format)63 static size_t format_rowbytes(int width, SkMask::Format format) {
64     return format == SkMask::kBW_Format ? bits_to_bytes(width)
65                                         : width * format_alignment(format);
66 }
67 
formatAlignment() const68 size_t SkGlyph::formatAlignment() const {
69     return format_alignment(this->maskFormat());
70 }
71 
allocImage(SkArenaAlloc * alloc)72 size_t SkGlyph::allocImage(SkArenaAlloc* alloc) {
73     SkASSERT(!this->isEmpty());
74     auto size = this->imageSize();
75     fImage = alloc->makeBytesAlignedTo(size, this->formatAlignment());
76 
77     return size;
78 }
79 
setImage(SkArenaAlloc * alloc,SkScalerContext * scalerContext)80 bool SkGlyph::setImage(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
81     if (!this->setImageHasBeenCalled()) {
82         // It used to be that getImage() could change the fMaskFormat. Extra checking to make
83         // sure there are no regressions.
84         SkDEBUGCODE(SkMask::Format oldFormat = this->maskFormat());
85         this->allocImage(alloc);
86         scalerContext->getImage(*this);
87         SkASSERT(oldFormat == this->maskFormat());
88         return true;
89     }
90     return false;
91 }
92 
setImage(SkArenaAlloc * alloc,const void * image)93 bool SkGlyph::setImage(SkArenaAlloc* alloc, const void* image) {
94     if (!this->setImageHasBeenCalled()) {
95         this->allocImage(alloc);
96         memcpy(fImage, image, this->imageSize());
97         return true;
98     }
99     return false;
100 }
101 
setMetricsAndImage(SkArenaAlloc * alloc,const SkGlyph & from)102 size_t SkGlyph::setMetricsAndImage(SkArenaAlloc* alloc, const SkGlyph& from) {
103     // Since the code no longer tries to find replacement glyphs, the image should always be
104     // nullptr.
105     SkASSERT(fImage == nullptr);
106 
107     // TODO(herb): remove "if" when we are sure there are no colliding glyphs.
108     if (fImage == nullptr) {
109         fAdvanceX = from.fAdvanceX;
110         fAdvanceY = from.fAdvanceY;
111         fWidth = from.fWidth;
112         fHeight = from.fHeight;
113         fTop = from.fTop;
114         fLeft = from.fLeft;
115         fForceBW = from.fForceBW;
116         fMaskFormat = from.fMaskFormat;
117 
118         // From glyph may not have an image because the glyph is too large.
119         if (from.fImage != nullptr && this->setImage(alloc, from.image())) {
120             return this->imageSize();
121         }
122     }
123     return 0;
124 }
125 
rowBytes() const126 size_t SkGlyph::rowBytes() const {
127     return format_rowbytes(fWidth, fMaskFormat);
128 }
129 
rowBytesUsingFormat(SkMask::Format format) const130 size_t SkGlyph::rowBytesUsingFormat(SkMask::Format format) const {
131     return format_rowbytes(fWidth, format);
132 }
133 
imageSize() const134 size_t SkGlyph::imageSize() const {
135     if (this->isEmpty() || this->imageTooLarge()) { return 0; }
136 
137     size_t size = this->rowBytes() * fHeight;
138 
139     if (fMaskFormat == SkMask::k3D_Format) {
140         size *= 3;
141     }
142 
143     return size;
144 }
145 
installPath(SkArenaAlloc * alloc,const SkPath * path)146 void SkGlyph::installPath(SkArenaAlloc* alloc, const SkPath* path) {
147     SkASSERT(fPathData == nullptr);
148     SkASSERT(!this->setPathHasBeenCalled());
149     fPathData = alloc->make<SkGlyph::PathData>();
150     if (path != nullptr) {
151         fPathData->fPath = *path;
152         fPathData->fPath.updateBoundsCache();
153         fPathData->fPath.getGenerationID();
154         fPathData->fHasPath = true;
155     }
156 }
157 
setPath(SkArenaAlloc * alloc,SkScalerContext * scalerContext)158 bool SkGlyph::setPath(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
159     if (!this->setPathHasBeenCalled()) {
160         SkPath path;
161         if (scalerContext->getPath(this->getPackedID(), &path)) {
162             this->installPath(alloc, &path);
163         } else {
164             this->installPath(alloc, nullptr);
165         }
166         return this->path() != nullptr;
167     }
168 
169     return false;
170 }
171 
setPath(SkArenaAlloc * alloc,const SkPath * path)172 bool SkGlyph::setPath(SkArenaAlloc* alloc, const SkPath* path) {
173     if (!this->setPathHasBeenCalled()) {
174         this->installPath(alloc, path);
175         return this->path() != nullptr;
176     }
177     return false;
178 }
179 
path() const180 const SkPath* SkGlyph::path() const {
181     // setPath must have been called previously.
182     SkASSERT(this->setPathHasBeenCalled());
183     if (fPathData->fHasPath) {
184         return &fPathData->fPath;
185     }
186     return nullptr;
187 }
188 
calculate_path_gap(SkScalar topOffset,SkScalar bottomOffset,const SkPath & path)189 static std::tuple<SkScalar, SkScalar> calculate_path_gap(
190         SkScalar topOffset, SkScalar bottomOffset, const SkPath& path) {
191 
192     // Left and Right of an ever expanding gap around the path.
193     SkScalar left  = SK_ScalarMax,
194              right = SK_ScalarMin;
195     auto expandGap = [&left, &right](SkScalar v) {
196         left  = std::min(left, v);
197         right = std::max(right, v);
198     };
199 
200     // Handle all the different verbs for the path.
201     SkPoint pts[4];
202     auto addLine = [&expandGap, &pts](SkScalar offset) {
203         SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY);
204         if (0 <= t && t < 1) {   // this handles divide by zero above
205             expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX));
206         }
207     };
208 
209     auto addQuad = [&expandGap, &pts](SkScalar offset) {
210         SkDQuad quad;
211         quad.set(pts);
212         double roots[2];
213         int count = quad.horizontalIntersect(offset, roots);
214         while (--count >= 0) {
215             expandGap(quad.ptAtT(roots[count]).asSkPoint().fX);
216         }
217     };
218 
219     auto addCubic = [&expandGap, &pts](SkScalar offset) {
220         SkDCubic cubic;
221         cubic.set(pts);
222         double roots[3];
223         int count = cubic.horizontalIntersect(offset, roots);
224         while (--count >= 0) {
225             expandGap(cubic.ptAtT(roots[count]).asSkPoint().fX);
226         }
227     };
228 
229     // Handle when a verb's points are in the gap between top and bottom.
230     auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) {
231         for (int i = 0; i < ptCount; ++i) {
232             if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) {
233                 expandGap(pts[i].fX);
234             }
235         }
236     };
237 
238     SkPath::Iter iter(path, false);
239     SkPath::Verb verb;
240     while (SkPath::kDone_Verb != (verb = iter.next(pts))) {
241         switch (verb) {
242             case SkPath::kMove_Verb: {
243                 break;
244             }
245             case SkPath::kLine_Verb: {
246                 addLine(topOffset);
247                 addLine(bottomOffset);
248                 addPts(2);
249                 break;
250             }
251             case SkPath::kQuad_Verb: {
252                 SkScalar quadTop = std::min(std::min(pts[0].fY, pts[1].fY), pts[2].fY);
253                 if (bottomOffset < quadTop) { break; }
254                 SkScalar quadBottom = std::max(std::max(pts[0].fY, pts[1].fY), pts[2].fY);
255                 if (topOffset > quadBottom) { break; }
256                 addQuad(topOffset);
257                 addQuad(bottomOffset);
258                 addPts(3);
259                 break;
260             }
261             case SkPath::kConic_Verb: {
262                 SkASSERT(0);  // no support for text composed of conics
263                 break;
264             }
265             case SkPath::kCubic_Verb: {
266                 SkScalar quadTop =
267                         std::min(std::min(std::min(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY);
268                 if (bottomOffset < quadTop) { break; }
269                 SkScalar quadBottom =
270                         std::max(std::max(std::max(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY);
271                 if (topOffset > quadBottom) { break; }
272                 addCubic(topOffset);
273                 addCubic(bottomOffset);
274                 addPts(4);
275                 break;
276             }
277             case SkPath::kClose_Verb: {
278                 break;
279             }
280             default: {
281                 SkASSERT(0);
282                 break;
283             }
284         }
285     }
286 
287     return std::tie(left, right);
288 }
289 
ensureIntercepts(const SkScalar * bounds,SkScalar scale,SkScalar xPos,SkScalar * array,int * count,SkArenaAlloc * alloc)290 void SkGlyph::ensureIntercepts(const SkScalar* bounds, SkScalar scale, SkScalar xPos,
291                                SkScalar* array, int* count, SkArenaAlloc* alloc) {
292 
293     auto offsetResults = [scale, xPos](
294             const SkGlyph::Intercept* intercept,SkScalar* array, int* count) {
295         if (array) {
296             array += *count;
297             for (int index = 0; index < 2; index++) {
298                 *array++ = intercept->fInterval[index] * scale + xPos;
299             }
300         }
301         *count += 2;
302     };
303 
304     const SkGlyph::Intercept* match =
305             [this](const SkScalar bounds[2]) -> const SkGlyph::Intercept* {
306                 if (!fPathData) {
307                     return nullptr;
308                 }
309                 const SkGlyph::Intercept* intercept = fPathData->fIntercept;
310                 while (intercept) {
311                     if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) {
312                         return intercept;
313                     }
314                     intercept = intercept->fNext;
315                 }
316                 return nullptr;
317             }(bounds);
318 
319     if (match) {
320         if (match->fInterval[0] < match->fInterval[1]) {
321             offsetResults(match, array, count);
322         }
323         return;
324     }
325 
326     SkGlyph::Intercept* intercept = alloc->make<SkGlyph::Intercept>();
327     intercept->fNext = fPathData->fIntercept;
328     intercept->fBounds[0] = bounds[0];
329     intercept->fBounds[1] = bounds[1];
330     intercept->fInterval[0] = SK_ScalarMax;
331     intercept->fInterval[1] = SK_ScalarMin;
332     fPathData->fIntercept = intercept;
333     const SkPath* path = &(fPathData->fPath);
334     const SkRect& pathBounds = path->getBounds();
335     if (pathBounds.fBottom < bounds[0] || bounds[1] < pathBounds.fTop) {
336         return;
337     }
338 
339     std::tie(intercept->fInterval[0], intercept->fInterval[1])
340             = calculate_path_gap(bounds[0], bounds[1], *path);
341 
342     if (intercept->fInterval[0] >= intercept->fInterval[1]) {
343         intercept->fInterval[0] = SK_ScalarMax;
344         intercept->fInterval[1] = SK_ScalarMin;
345         return;
346     }
347     offsetResults(intercept, array, count);
348 }
349