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