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