/src/skia/src/core/SkGlyph.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2006 The Android Open Source Project |
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 | | #ifndef SkGlyph_DEFINED |
9 | | #define SkGlyph_DEFINED |
10 | | |
11 | | #include "include/core/SkDrawable.h" |
12 | | #include "include/core/SkPath.h" |
13 | | #include "include/core/SkPicture.h" |
14 | | #include "include/core/SkPoint.h" |
15 | | #include "include/core/SkRect.h" |
16 | | #include "include/core/SkRefCnt.h" |
17 | | #include "include/core/SkScalar.h" |
18 | | #include "include/core/SkString.h" |
19 | | #include "include/core/SkTypes.h" |
20 | | #include "include/private/base/SkDebug.h" |
21 | | #include "include/private/base/SkFixed.h" |
22 | | #include "include/private/base/SkTo.h" |
23 | | #include "src/base/SkVx.h" |
24 | | #include "src/core/SkChecksum.h" |
25 | | #include "src/core/SkMask.h" |
26 | | |
27 | | #include <algorithm> |
28 | | #include <cmath> |
29 | | #include <cstddef> |
30 | | #include <cstdint> |
31 | | #include <limits> |
32 | | #include <optional> |
33 | | |
34 | | class SkArenaAlloc; |
35 | | class SkCanvas; |
36 | | class SkGlyph; |
37 | | class SkReadBuffer; |
38 | | class SkScalerContext; |
39 | | class SkWriteBuffer; |
40 | | namespace sktext { |
41 | | class StrikeForGPU; |
42 | | } // namespace sktext |
43 | | |
44 | | // -- SkPackedGlyphID ------------------------------------------------------------------------------ |
45 | | // A combination of SkGlyphID and sub-pixel position information. |
46 | | struct SkPackedGlyphID { |
47 | | inline static constexpr uint32_t kImpossibleID = ~0u; |
48 | | enum { |
49 | | // Lengths |
50 | | kGlyphIDLen = 16u, |
51 | | kSubPixelPosLen = 2u, |
52 | | |
53 | | // Bit positions |
54 | | kSubPixelX = 0u, |
55 | | kGlyphID = kSubPixelPosLen, |
56 | | kSubPixelY = kGlyphIDLen + kSubPixelPosLen, |
57 | | kEndData = kGlyphIDLen + 2 * kSubPixelPosLen, |
58 | | |
59 | | // Masks |
60 | | kGlyphIDMask = (1u << kGlyphIDLen) - 1, |
61 | | kSubPixelPosMask = (1u << kSubPixelPosLen) - 1, |
62 | | kMaskAll = (1u << kEndData) - 1, |
63 | | |
64 | | // Location of sub pixel info in a fixed pointer number. |
65 | | kFixedPointBinaryPointPos = 16u, |
66 | | kFixedPointSubPixelPosBits = kFixedPointBinaryPointPos - kSubPixelPosLen, |
67 | | }; |
68 | | |
69 | | inline static const constexpr SkScalar kSubpixelRound = |
70 | | 1.f / (1u << (SkPackedGlyphID::kSubPixelPosLen + 1)); |
71 | | |
72 | | inline static const constexpr SkIPoint kXYFieldMask{kSubPixelPosMask << kSubPixelX, |
73 | | kSubPixelPosMask << kSubPixelY}; |
74 | | |
75 | | struct Hash { |
76 | 0 | uint32_t operator() (SkPackedGlyphID packedID) const { |
77 | 0 | return packedID.hash(); |
78 | 0 | } |
79 | | }; |
80 | | |
81 | | constexpr explicit SkPackedGlyphID(SkGlyphID glyphID) |
82 | 1.01M | : fID{(uint32_t)glyphID << kGlyphID} { } |
83 | | |
84 | | constexpr SkPackedGlyphID(SkGlyphID glyphID, SkFixed x, SkFixed y) |
85 | 0 | : fID {PackIDXY(glyphID, x, y)} { } |
86 | | |
87 | | constexpr SkPackedGlyphID(SkGlyphID glyphID, uint32_t x, uint32_t y) |
88 | 0 | : fID {PackIDSubXSubY(glyphID, x, y)} { } |
89 | | |
90 | | SkPackedGlyphID(SkGlyphID glyphID, SkPoint pt, SkIPoint mask) |
91 | 196k | : fID{PackIDSkPoint(glyphID, pt, mask)} { } |
92 | | |
93 | 1.27M | constexpr explicit SkPackedGlyphID(uint32_t v) : fID{v & kMaskAll} { } |
94 | 69.4k | constexpr SkPackedGlyphID() : fID{kImpossibleID} {} |
95 | | |
96 | 1.14M | bool operator==(const SkPackedGlyphID& that) const { |
97 | 1.14M | return fID == that.fID; |
98 | 1.14M | } |
99 | 0 | bool operator!=(const SkPackedGlyphID& that) const { |
100 | 0 | return !(*this == that); |
101 | 0 | } |
102 | 0 | bool operator<(SkPackedGlyphID that) const { |
103 | 0 | return this->fID < that.fID; |
104 | 0 | } |
105 | | |
106 | 183k | SkGlyphID glyphID() const { |
107 | 183k | return (fID >> kGlyphID) & kGlyphIDMask; |
108 | 183k | } |
109 | | |
110 | 84.7k | uint32_t value() const { |
111 | 84.7k | return fID; |
112 | 84.7k | } |
113 | | |
114 | 28.5k | SkFixed getSubXFixed() const { |
115 | 28.5k | return this->subToFixed(kSubPixelX); |
116 | 28.5k | } |
117 | | |
118 | 28.5k | SkFixed getSubYFixed() const { |
119 | 28.5k | return this->subToFixed(kSubPixelY); |
120 | 28.5k | } |
121 | | |
122 | 1.37M | uint32_t hash() const { |
123 | 1.37M | return SkChecksum::CheapMix(fID); |
124 | 1.37M | } |
125 | | |
126 | 0 | SkString dump() const { |
127 | 0 | SkString str; |
128 | 0 | str.appendf("glyphID: %d, x: %d, y:%d", glyphID(), getSubXFixed(), getSubYFixed()); |
129 | 0 | return str; |
130 | 0 | } |
131 | | |
132 | 0 | SkString shortDump() const { |
133 | 0 | SkString str; |
134 | 0 | str.appendf("0x%x|%1u|%1u", this->glyphID(), |
135 | 0 | this->subPixelField(kSubPixelX), |
136 | 0 | this->subPixelField(kSubPixelY)); |
137 | 0 | return str; |
138 | 0 | } |
139 | | |
140 | | private: |
141 | 0 | static constexpr uint32_t PackIDSubXSubY(SkGlyphID glyphID, uint32_t x, uint32_t y) { |
142 | 0 | SkASSERT(x < (1u << kSubPixelPosLen)); |
143 | 0 | SkASSERT(y < (1u << kSubPixelPosLen)); |
144 | 0 |
|
145 | 0 | return (x << kSubPixelX) | (y << kSubPixelY) | (glyphID << kGlyphID); |
146 | 0 | } |
147 | | |
148 | | // Assumptions: pt is properly rounded. mask is set for the x or y fields. |
149 | | // |
150 | | // A sub-pixel field is a number on the interval [2^kSubPixel, 2^(kSubPixel + kSubPixelPosLen)). |
151 | | // Where kSubPixel is either kSubPixelX or kSubPixelY. Given a number x on [0, 1) we can |
152 | | // generate a sub-pixel field using: |
153 | | // sub-pixel-field = x * 2^(kSubPixel + kSubPixelPosLen) |
154 | | // |
155 | | // We can generate the integer sub-pixel field by &-ing the integer part of sub-filed with the |
156 | | // sub-pixel field mask. |
157 | | // int-sub-pixel-field = int(sub-pixel-field) & (kSubPixelPosMask << kSubPixel) |
158 | | // |
159 | | // The last trick is to extend the range from [0, 1) to [0, 2). The extend range is |
160 | | // necessary because the modulo 1 calculation (pt - floor(pt)) generates numbers on [-1, 1). |
161 | | // This does not round (floor) properly when converting to integer. Adding one to the range |
162 | | // causes truncation and floor to be the same. Coincidentally, masking to produce the field also |
163 | | // removes the +1. |
164 | 196k | static uint32_t PackIDSkPoint(SkGlyphID glyphID, SkPoint pt, SkIPoint mask) { |
165 | | #if 0 |
166 | | // TODO: why does this code not work on GCC 8.3 x86 Debug builds? |
167 | | using namespace skvx; |
168 | | using XY = Vec<2, float>; |
169 | | using SubXY = Vec<2, int>; |
170 | | |
171 | | const XY magic = {1.f * (1u << (kSubPixelPosLen + kSubPixelX)), |
172 | | 1.f * (1u << (kSubPixelPosLen + kSubPixelY))}; |
173 | | XY pos{pt.x(), pt.y()}; |
174 | | XY subPos = (pos - floor(pos)) + 1.0f; |
175 | | SubXY sub = cast<int>(subPos * magic) & SubXY{mask.x(), mask.y()}; |
176 | | #else |
177 | 196k | const float magicX = 1.f * (1u << (kSubPixelPosLen + kSubPixelX)), |
178 | 196k | magicY = 1.f * (1u << (kSubPixelPosLen + kSubPixelY)); |
179 | | |
180 | 196k | float x = pt.x(), |
181 | 196k | y = pt.y(); |
182 | 196k | x = (x - floorf(x)) + 1.0f; |
183 | 196k | y = (y - floorf(y)) + 1.0f; |
184 | 196k | int sub[] = { |
185 | 196k | (int)(x * magicX) & mask.x(), |
186 | 196k | (int)(y * magicY) & mask.y(), |
187 | 196k | }; |
188 | 196k | #endif |
189 | | |
190 | 196k | SkASSERT(sub[0] / (1u << kSubPixelX) < (1u << kSubPixelPosLen)); |
191 | 196k | SkASSERT(sub[1] / (1u << kSubPixelY) < (1u << kSubPixelPosLen)); |
192 | 196k | return (glyphID << kGlyphID) | sub[0] | sub[1]; |
193 | 196k | } |
194 | | |
195 | 0 | static constexpr uint32_t PackIDXY(SkGlyphID glyphID, SkFixed x, SkFixed y) { |
196 | 0 | return PackIDSubXSubY(glyphID, FixedToSub(x), FixedToSub(y)); |
197 | 0 | } |
198 | | |
199 | 0 | static constexpr uint32_t FixedToSub(SkFixed n) { |
200 | 0 | return ((uint32_t)n >> kFixedPointSubPixelPosBits) & kSubPixelPosMask; |
201 | 0 | } |
202 | | |
203 | 57.1k | constexpr uint32_t subPixelField(uint32_t subPixelPosBit) const { |
204 | 57.1k | return (fID >> subPixelPosBit) & kSubPixelPosMask; |
205 | 57.1k | } |
206 | | |
207 | 57.1k | constexpr SkFixed subToFixed(uint32_t subPixelPosBit) const { |
208 | 57.1k | uint32_t subPixelPosition = this->subPixelField(subPixelPosBit); |
209 | 57.1k | return subPixelPosition << kFixedPointSubPixelPosBits; |
210 | 57.1k | } |
211 | | |
212 | | uint32_t fID; |
213 | | }; |
214 | | |
215 | | // -- SkAxisAlignment ------------------------------------------------------------------------------ |
216 | | // SkAxisAlignment specifies the x component of a glyph's position is rounded when kX, and the y |
217 | | // component is rounded when kY. If kNone then neither are rounded. |
218 | | enum class SkAxisAlignment : uint32_t { |
219 | | kNone, |
220 | | kX, |
221 | | kY, |
222 | | }; |
223 | | |
224 | | // round and ignorePositionMask are used to calculate the subpixel position of a glyph. |
225 | | // The per component (x or y) calculation is: |
226 | | // |
227 | | // subpixelOffset = (floor((viewportPosition + rounding) & mask) >> 14) & 3 |
228 | | // |
229 | | // where mask is either 0 or ~0, and rounding is either |
230 | | // 1/2 for non-subpixel or 1/8 for subpixel. |
231 | | struct SkGlyphPositionRoundingSpec { |
232 | | SkGlyphPositionRoundingSpec(bool isSubpixel, SkAxisAlignment axisAlignment); |
233 | | const SkVector halfAxisSampleFreq; |
234 | | const SkIPoint ignorePositionMask; |
235 | | const SkIPoint ignorePositionFieldMask; |
236 | | |
237 | | private: |
238 | | static SkVector HalfAxisSampleFreq(bool isSubpixel, SkAxisAlignment axisAlignment); |
239 | | static SkIPoint IgnorePositionMask(bool isSubpixel, SkAxisAlignment axisAlignment); |
240 | | static SkIPoint IgnorePositionFieldMask(bool isSubpixel, SkAxisAlignment axisAlignment); |
241 | | }; |
242 | | |
243 | | class SkGlyphRect; |
244 | | namespace skglyph { |
245 | | SkGlyphRect rect_union(SkGlyphRect, SkGlyphRect); |
246 | | SkGlyphRect rect_intersection(SkGlyphRect, SkGlyphRect); |
247 | | } // namespace skglyph |
248 | | |
249 | | // SkGlyphRect encodes rectangles with coordinates using SkScalar. It is specialized for |
250 | | // rectangle union and intersection operations. |
251 | | class SkGlyphRect { |
252 | | public: |
253 | | SkGlyphRect() = default; |
254 | | SkGlyphRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) |
255 | 31.4k | : fRect{-left, -top, right, bottom} { } |
256 | 0 | bool empty() const { |
257 | 0 | return -fRect[0] >= fRect[2] || -fRect[1] >= fRect[3]; |
258 | 0 | } |
259 | 4.03k | SkRect rect() const { |
260 | 4.03k | return SkRect::MakeLTRB(-fRect[0], -fRect[1], fRect[2], fRect[3]); |
261 | 4.03k | } |
262 | 27.4k | SkGlyphRect offset(SkScalar x, SkScalar y) const { |
263 | 27.4k | return SkGlyphRect{fRect + Storage{-x, -y, x, y}}; |
264 | 27.4k | } |
265 | 27.4k | SkGlyphRect offset(SkPoint pt) const { |
266 | 27.4k | return this->offset(pt.x(), pt.y()); |
267 | 27.4k | } |
268 | 0 | SkGlyphRect scaleAndOffset(SkScalar scale, SkPoint offset) const { |
269 | 0 | auto [x, y] = offset; |
270 | 0 | return fRect * scale + Storage{-x, -y, x, y}; |
271 | 0 | } |
272 | 6.14k | SkGlyphRect inset(SkScalar dx, SkScalar dy) const { |
273 | 6.14k | return fRect - Storage{dx, dy, dx, dy}; |
274 | 6.14k | } |
275 | 27.4k | SkPoint leftTop() const { return -this->negLeftTop(); } |
276 | 0 | SkPoint rightBottom() const { return {fRect[2], fRect[3]}; } |
277 | 0 | SkPoint widthHeight() const { return this->rightBottom() + negLeftTop(); } |
278 | | friend SkGlyphRect skglyph::rect_union(SkGlyphRect, SkGlyphRect); |
279 | | friend SkGlyphRect skglyph::rect_intersection(SkGlyphRect, SkGlyphRect); |
280 | | |
281 | | private: |
282 | 27.4k | SkPoint negLeftTop() const { return {fRect[0], fRect[1]}; } |
283 | | using Storage = skvx::Vec<4, SkScalar>; |
284 | 61.0k | SkGlyphRect(Storage rect) : fRect{rect} { } |
285 | | Storage fRect; |
286 | | }; |
287 | | |
288 | | namespace skglyph { |
289 | 4.03k | inline SkGlyphRect empty_rect() { |
290 | 4.03k | constexpr SkScalar max = std::numeric_limits<SkScalar>::max(); |
291 | 4.03k | return {max, max, -max, -max}; |
292 | 4.03k | } |
293 | 0 | inline SkGlyphRect full_rect() { |
294 | 0 | constexpr SkScalar max = std::numeric_limits<SkScalar>::max(); |
295 | 0 | return {-max, -max, max, max}; |
296 | 0 | } |
297 | 27.4k | inline SkGlyphRect rect_union(SkGlyphRect a, SkGlyphRect b) { |
298 | 27.4k | return skvx::max(a.fRect, b.fRect); |
299 | 27.4k | } |
300 | 0 | inline SkGlyphRect rect_intersection(SkGlyphRect a, SkGlyphRect b) { |
301 | 0 | return skvx::min(a.fRect, b.fRect); |
302 | 0 | } |
303 | | |
304 | | enum class GlyphAction { |
305 | | kUnset, |
306 | | kAccept, |
307 | | kReject, |
308 | | kDrop, |
309 | | kSize, |
310 | | }; |
311 | | |
312 | | enum ActionType { |
313 | | kDirectMask = 0, |
314 | | kDirectMaskCPU = 2, |
315 | | kMask = 4, |
316 | | kSDFT = 6, |
317 | | kPath = 8, |
318 | | kDrawable = 10, |
319 | | }; |
320 | | |
321 | | enum ActionTypeSize { |
322 | | kTotalBits = 12 |
323 | | }; |
324 | | } // namespace skglyph |
325 | | |
326 | | // SkGlyphDigest contains a digest of information for making GPU drawing decisions. It can be |
327 | | // referenced instead of the glyph itself in many situations. In the remote glyphs cache the |
328 | | // SkGlyphDigest is the only information that needs to be stored in the cache. |
329 | | class SkGlyphDigest { |
330 | | public: |
331 | | // An atlas consists of plots, and plots hold glyphs. The minimum a plot can be is 256x256. |
332 | | // This means that the maximum size a glyph can be is 256x256. |
333 | | static constexpr uint16_t kSkSideTooBigForAtlas = 256; |
334 | | |
335 | | // Default ctor is only needed for the hash table. |
336 | | SkGlyphDigest() = default; |
337 | | SkGlyphDigest(size_t index, const SkGlyph& glyph); |
338 | 805k | int index() const { return fIndex; } |
339 | 0 | bool isEmpty() const { return fIsEmpty; } |
340 | 0 | bool isColor() const { return fFormat == SkMask::kARGB32_Format; } |
341 | 21.3k | SkMask::Format maskFormat() const { return static_cast<SkMask::Format>(fFormat); } |
342 | | |
343 | 1.78M | skglyph::GlyphAction actionFor(skglyph::ActionType actionType) const { |
344 | 1.78M | return static_cast<skglyph::GlyphAction>((fActions >> actionType) & 0b11); |
345 | 1.78M | } |
346 | | |
347 | | void setActionFor(skglyph::ActionType, SkGlyph*, sktext::StrikeForGPU*); |
348 | | |
349 | 18.3k | uint16_t maxDimension() const { |
350 | 18.3k | return std::max(fWidth, fHeight); |
351 | 18.3k | } |
352 | | |
353 | 17.8k | bool fitsInAtlasDirect() const { |
354 | 17.8k | return this->maxDimension() <= kSkSideTooBigForAtlas; |
355 | 17.8k | } |
356 | | |
357 | 29 | bool fitsInAtlasInterpolated() const { |
358 | | // Include the padding needed for interpolating the glyph when drawing. |
359 | 29 | return this->maxDimension() <= kSkSideTooBigForAtlas - 2; |
360 | 29 | } |
361 | | |
362 | 27.4k | SkGlyphRect bounds() const { |
363 | 27.4k | return SkGlyphRect(fLeft, fTop, (SkScalar)fLeft + fWidth, (SkScalar)fTop + fHeight); |
364 | 27.4k | } |
365 | | |
366 | | static bool FitsInAtlas(const SkGlyph& glyph); |
367 | | |
368 | | // GetKey and Hash implement the required methods for THashTable. |
369 | 1.27M | static SkPackedGlyphID GetKey(SkGlyphDigest digest) { |
370 | 1.27M | return SkPackedGlyphID{SkTo<uint32_t>(digest.fPackedID)}; |
371 | 1.27M | } |
372 | 1.35M | static uint32_t Hash(SkPackedGlyphID packedID) { |
373 | 1.35M | return packedID.hash(); |
374 | 1.35M | } |
375 | | |
376 | | private: |
377 | 40.5k | void setAction(skglyph::ActionType actionType, skglyph::GlyphAction action) { |
378 | 40.5k | using namespace skglyph; |
379 | 40.5k | SkASSERT(action != GlyphAction::kUnset); |
380 | 40.5k | SkASSERT(this->actionFor(actionType) == GlyphAction::kUnset); |
381 | 40.5k | const uint64_t mask = 0b11 << actionType; |
382 | 40.5k | fActions &= ~mask; |
383 | 40.5k | fActions |= SkTo<uint64_t>(action) << actionType; |
384 | 40.5k | } |
385 | | |
386 | | static_assert(SkPackedGlyphID::kEndData == 20); |
387 | | static_assert(SkMask::kCountMaskFormats <= 8); |
388 | | static_assert(SkTo<int>(skglyph::GlyphAction::kSize) <= 4); |
389 | | struct { |
390 | | uint64_t fPackedID : SkPackedGlyphID::kEndData; |
391 | | uint64_t fIndex : SkPackedGlyphID::kEndData; |
392 | | uint64_t fIsEmpty : 1; |
393 | | uint64_t fFormat : 3; |
394 | | uint64_t fActions : skglyph::ActionTypeSize::kTotalBits; |
395 | | }; |
396 | | int16_t fLeft, fTop; |
397 | | uint16_t fWidth, fHeight; |
398 | | }; |
399 | | |
400 | | class SkPictureBackedGlyphDrawable final : public SkDrawable { |
401 | | public: |
402 | | static sk_sp<SkPictureBackedGlyphDrawable>MakeFromBuffer(SkReadBuffer& buffer); |
403 | | static void FlattenDrawable(SkWriteBuffer& buffer, SkDrawable* drawable); |
404 | | SkPictureBackedGlyphDrawable(sk_sp<SkPicture> self); |
405 | | |
406 | | private: |
407 | | sk_sp<SkPicture> fPicture; |
408 | | SkRect onGetBounds() override; |
409 | | size_t onApproximateBytesUsed() override; |
410 | | void onDraw(SkCanvas* canvas) override; |
411 | | }; |
412 | | |
413 | | class SkGlyph { |
414 | | public: |
415 | | static std::optional<SkGlyph> MakeFromBuffer(SkReadBuffer&); |
416 | | // SkGlyph() is used for testing. |
417 | 13.2k | constexpr SkGlyph() : SkGlyph{SkPackedGlyphID()} { } |
418 | | SkGlyph(const SkGlyph&); |
419 | | SkGlyph& operator=(const SkGlyph&); |
420 | | SkGlyph(SkGlyph&&); |
421 | | SkGlyph& operator=(SkGlyph&&); |
422 | | ~SkGlyph(); |
423 | 100k | constexpr explicit SkGlyph(SkPackedGlyphID id) : fID{id} { } |
424 | | |
425 | 261k | SkVector advanceVector() const { return SkVector{fAdvanceX, fAdvanceY}; } |
426 | 192k | SkScalar advanceX() const { return fAdvanceX; } |
427 | 0 | SkScalar advanceY() const { return fAdvanceY; } |
428 | | |
429 | 183k | SkGlyphID getGlyphID() const { return fID.glyphID(); } |
430 | 140k | SkPackedGlyphID getPackedID() const { return fID; } |
431 | 5.24k | SkFixed getSubXFixed() const { return fID.getSubXFixed(); } |
432 | 5.24k | SkFixed getSubYFixed() const { return fID.getSubYFixed(); } |
433 | | |
434 | | size_t rowBytes() const; |
435 | | size_t rowBytesUsingFormat(SkMask::Format format) const; |
436 | | |
437 | | // Call this to set all the metrics fields to 0 (e.g. if the scaler |
438 | | // encounters an error measuring a glyph). Note: this does not alter the |
439 | | // fImage, fPath, fID, fMaskFormat fields. |
440 | | void zeroMetrics(); |
441 | | |
442 | | SkMask mask() const; |
443 | | |
444 | | SkMask mask(SkPoint position) const; |
445 | | |
446 | | // Image |
447 | | // If we haven't already tried to associate an image with this glyph |
448 | | // (i.e. setImageHasBeenCalled() returns false), then use the |
449 | | // SkScalerContext or const void* argument to set the image. |
450 | | bool setImage(SkArenaAlloc* alloc, SkScalerContext* scalerContext); |
451 | | bool setImage(SkArenaAlloc* alloc, const void* image); |
452 | | |
453 | | // Merge the 'from' glyph into this glyph using alloc to allocate image data. Return the number |
454 | | // of bytes allocated. Copy the width, height, top, left, format, and image into this glyph |
455 | | // making a copy of the image using the alloc. |
456 | | size_t setMetricsAndImage(SkArenaAlloc* alloc, const SkGlyph& from); |
457 | | |
458 | | // Returns true if the image has been set. |
459 | 15.0k | bool setImageHasBeenCalled() const { |
460 | | // Check for empty bounds first to guard against fImage somehow being set. |
461 | 15.0k | return this->isEmpty() || fImage != nullptr || this->imageTooLarge(); |
462 | 15.0k | } |
463 | | |
464 | | // Return a pointer to the path if the image exists, otherwise return nullptr. |
465 | 17.1k | const void* image() const { SkASSERT(this->setImageHasBeenCalled()); return fImage; } |
466 | | |
467 | | // Return the size of the image. |
468 | | size_t imageSize() const; |
469 | | |
470 | | // Path |
471 | | // If we haven't already tried to associate a path to this glyph |
472 | | // (i.e. setPathHasBeenCalled() returns false), then use the |
473 | | // SkScalerContext or SkPath argument to try to do so. N.B. this |
474 | | // may still result in no path being associated with this glyph, |
475 | | // e.g. if you pass a null SkPath or the typeface is bitmap-only. |
476 | | // |
477 | | // This setPath() call is sticky... once you call it, the glyph |
478 | | // stays in its state permanently, ignoring any future calls. |
479 | | // |
480 | | // Returns true if this is the first time you called setPath() |
481 | | // and there actually is a path; call path() to get it. |
482 | | bool setPath(SkArenaAlloc* alloc, SkScalerContext* scalerContext); |
483 | | bool setPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline, bool modified); |
484 | | |
485 | | // Returns true if that path has been set. |
486 | 210k | bool setPathHasBeenCalled() const { return fPathData != nullptr; } |
487 | | |
488 | | // Return a pointer to the path if it exists, otherwise return nullptr. Only works if the |
489 | | // path was previously set. |
490 | | const SkPath* path() const; |
491 | | bool pathIsHairline() const; |
492 | | bool pathIsModified() const; |
493 | | |
494 | | bool setDrawable(SkArenaAlloc* alloc, SkScalerContext* scalerContext); |
495 | | bool setDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable); |
496 | 2.17k | bool setDrawableHasBeenCalled() const { return fDrawableData != nullptr; } |
497 | | SkDrawable* drawable() const; |
498 | | |
499 | | // Format |
500 | 0 | bool isColor() const { return fMaskFormat == SkMask::kARGB32_Format; } |
501 | 213k | SkMask::Format maskFormat() const { return fMaskFormat; } |
502 | | size_t formatAlignment() const; |
503 | | |
504 | | // Bounds |
505 | 0 | int maxDimension() const { return std::max(fWidth, fHeight); } |
506 | 18.8k | SkIRect iRect() const { return SkIRect::MakeXYWH(fLeft, fTop, fWidth, fHeight); } |
507 | 241k | SkRect rect() const { return SkRect::MakeXYWH(fLeft, fTop, fWidth, fHeight); } |
508 | 0 | SkGlyphRect glyphRect() const { |
509 | 0 | return SkGlyphRect(fLeft, fTop, fLeft + fWidth, fTop + fHeight); |
510 | 0 | } |
511 | 85.3k | int left() const { return fLeft; } |
512 | 85.3k | int top() const { return fTop; } |
513 | 4.57M | int width() const { return fWidth; } |
514 | 98.4k | int height() const { return fHeight; } |
515 | 220k | bool isEmpty() const { |
516 | 220k | return fWidth == 0 || fHeight == 0; |
517 | 220k | } |
518 | 45.1k | bool imageTooLarge() const { return fWidth >= kMaxGlyphWidth; } |
519 | | |
520 | 13.3k | uint16_t extraBits() const { return fScalerContextBits; } |
521 | | |
522 | | // Make sure that the intercept information is on the glyph and return it, or return it if it |
523 | | // already exists. |
524 | | // * bounds - [0] - top of underline; [1] - bottom of underline. |
525 | | // * scale, xPos - information about how wide the gap is. |
526 | | // * array - accumulated gaps for many characters if not null. |
527 | | // * count - the number of gaps. |
528 | | void ensureIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, |
529 | | SkScalar* array, int* count, SkArenaAlloc* alloc); |
530 | | |
531 | | // Deprecated. Do not use. The last use is in SkChromeRemoteCache, and will be deleted soon. |
532 | 0 | void setImage(void* image) { fImage = image; } |
533 | | |
534 | | // Serialize/deserialize functions. |
535 | | // Flatten the metrics portions, but no drawing data. |
536 | | void flattenMetrics(SkWriteBuffer&) const; |
537 | | |
538 | | // Flatten just the the mask data. |
539 | | void flattenImage(SkWriteBuffer&) const; |
540 | | |
541 | | // Read the image data, store it in the alloc, and add it to the glyph. |
542 | | size_t addImageFromBuffer(SkReadBuffer&, SkArenaAlloc*); |
543 | | |
544 | | // Flatten just the path data. |
545 | | void flattenPath(SkWriteBuffer&) const; |
546 | | |
547 | | // Read the path data, create the glyph's path data in the alloc, and add it to the glyph. |
548 | | size_t addPathFromBuffer(SkReadBuffer&, SkArenaAlloc*); |
549 | | |
550 | | // Flatten just the drawable data. |
551 | | void flattenDrawable(SkWriteBuffer&) const; |
552 | | |
553 | | // Read the drawable data, create the glyph's drawable data in the alloc, and add it to the |
554 | | // glyph. |
555 | | size_t addDrawableFromBuffer(SkReadBuffer&, SkArenaAlloc*); |
556 | | |
557 | | private: |
558 | | // There are two sides to an SkGlyph, the scaler side (things that create glyph data) have |
559 | | // access to all the fields. Scalers are assumed to maintain all the SkGlyph invariants. The |
560 | | // consumer side has a tighter interface. |
561 | | friend class SkScalerContext; |
562 | | friend class SkGlyphTestPeer; |
563 | | |
564 | | inline static constexpr uint16_t kMaxGlyphWidth = 1u << 13u; |
565 | | |
566 | | // Support horizontal and vertical skipping strike-through / underlines. |
567 | | // The caller walks the linked list looking for a match. For a horizontal underline, |
568 | | // the fBounds contains the top and bottom of the underline. The fInterval pair contains the |
569 | | // beginning and end of the intersection of the bounds and the glyph's path. |
570 | | // If interval[0] >= interval[1], no intersection was found. |
571 | | struct Intercept { |
572 | | Intercept* fNext; |
573 | | SkScalar fBounds[2]; // for horz underlines, the boundaries in Y |
574 | | SkScalar fInterval[2]; // the outside intersections of the axis and the glyph |
575 | | }; |
576 | | |
577 | | struct PathData { |
578 | | Intercept* fIntercept{nullptr}; |
579 | | SkPath fPath; |
580 | | bool fHasPath{false}; |
581 | | // A normal user-path will have patheffects applied to it and eventually become a dev-path. |
582 | | // A dev-path is always a fill-path, except when it is hairline. |
583 | | // The fPath is a dev-path, so sidecar the paths hairline status. |
584 | | // This allows the user to avoid filling paths which should not be filled. |
585 | | bool fHairline{false}; |
586 | | // This is set if the path is significantly different from what a reasonable interpreter of |
587 | | // the underlying font data would produce. This is set if any non-identity matrix, stroke, |
588 | | // path effect, emboldening, etc is applied. |
589 | | // This allows Document implementations to know if a glyph should be drawn out of the font |
590 | | // data or needs to be embedded differently. |
591 | | bool fModified{false}; |
592 | | }; |
593 | | |
594 | | struct DrawableData { |
595 | | Intercept* fIntercept{nullptr}; |
596 | | sk_sp<SkDrawable> fDrawable; |
597 | | bool fHasDrawable{false}; |
598 | | }; |
599 | | |
600 | | size_t allocImage(SkArenaAlloc* alloc); |
601 | | |
602 | 0 | void installImage(void* imageData) { |
603 | 0 | SkASSERT(!this->setImageHasBeenCalled()); |
604 | 0 | fImage = imageData; |
605 | 0 | } Unexecuted instantiation: SkGlyph::installImage(void*) Unexecuted instantiation: SkGlyph::installImage(void*) |
606 | | |
607 | | // path == nullptr indicates that there is no path. |
608 | | void installPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline, bool modified); |
609 | | |
610 | | // drawable == nullptr indicates that there is no path. |
611 | | void installDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable); |
612 | | |
613 | | // The width and height of the glyph mask. |
614 | | uint16_t fWidth = 0, |
615 | | fHeight = 0; |
616 | | |
617 | | // The offset from the glyphs origin on the baseline to the top left of the glyph mask. |
618 | | int16_t fTop = 0, |
619 | | fLeft = 0; |
620 | | |
621 | | // fImage must remain null if the glyph is empty or if width > kMaxGlyphWidth. |
622 | | void* fImage = nullptr; |
623 | | |
624 | | // Path data has tricky state. If the glyph isEmpty, then fPathData should always be nullptr, |
625 | | // else if fPathData is not null, then a path has been requested. The fPath field of fPathData |
626 | | // may still be null after the request meaning that there is no path for this glyph. |
627 | | PathData* fPathData = nullptr; |
628 | | DrawableData* fDrawableData = nullptr; |
629 | | |
630 | | // The advance for this glyph. |
631 | | float fAdvanceX = 0, |
632 | | fAdvanceY = 0; |
633 | | |
634 | | SkMask::Format fMaskFormat{SkMask::kBW_Format}; |
635 | | |
636 | | // Used by the SkScalerContext to pass state from generateMetrics to generateImage. |
637 | | // Usually specifies which glyph representation was used to generate the metrics. |
638 | | uint16_t fScalerContextBits = 0; |
639 | | |
640 | | // An SkGlyph can be created with just a packedID, but generally speaking some glyph factory |
641 | | // needs to actually fill out the glyph before it can be used as part of that system. |
642 | | SkDEBUGCODE(bool fAdvancesBoundsFormatAndInitialPathDone{false};) |
643 | | |
644 | | SkPackedGlyphID fID; |
645 | | }; |
646 | | |
647 | | #endif |