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.set(fLeft, fTop, fLeft + fWidth, fTop + 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