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