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