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