/src/skia/src/core/SkTextBlob.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2014 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 "include/core/SkTextBlob.h" |
9 | | |
10 | | #include "include/core/SkData.h" |
11 | | #include "include/core/SkMaskFilter.h" |
12 | | #include "include/core/SkMatrix.h" |
13 | | #include "include/core/SkPaint.h" |
14 | | #include "include/core/SkPathEffect.h" |
15 | | #include "include/core/SkPoint.h" |
16 | | #include "include/core/SkRSXform.h" |
17 | | #include "include/private/base/SkAlign.h" |
18 | | #include "include/private/base/SkFloatingPoint.h" |
19 | | #include "include/private/base/SkMalloc.h" |
20 | | #include "include/private/base/SkSpan_impl.h" |
21 | | #include "include/private/base/SkTo.h" |
22 | | #include "src/base/SkSafeMath.h" |
23 | | #include "src/base/SkTLazy.h" |
24 | | #include "src/core/SkFontPriv.h" |
25 | | #include "src/core/SkGlyph.h" |
26 | | #include "src/core/SkReadBuffer.h" |
27 | | #include "src/core/SkStrikeSpec.h" |
28 | | #include "src/core/SkTextBlobPriv.h" |
29 | | #include "src/core/SkWriteBuffer.h" |
30 | | #include "src/text/GlyphRun.h" |
31 | | |
32 | | #include <algorithm> |
33 | | #include <atomic> |
34 | | #include <limits> |
35 | | #include <new> |
36 | | #include <vector> |
37 | | |
38 | | using namespace skia_private; |
39 | | |
40 | | namespace { |
41 | | struct RunFontStorageEquivalent { |
42 | | SkScalar fSize, fScaleX; |
43 | | void* fTypeface; |
44 | | SkScalar fSkewX; |
45 | | uint32_t fFlags; |
46 | | }; |
47 | | static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed"); |
48 | | } // namespace |
49 | | |
50 | | size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize, |
51 | | SkTextBlob::GlyphPositioning positioning, |
52 | 126k | SkSafeMath* safe) { |
53 | 126k | static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment"); |
54 | | |
55 | 126k | auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)), |
56 | 126k | posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar)); |
57 | | |
58 | | // RunRecord object + (aligned) glyph buffer + position buffer |
59 | 126k | auto size = sizeof(SkTextBlob::RunRecord); |
60 | 126k | size = safe->add(size, safe->alignUp(glyphSize, 4)); |
61 | 126k | size = safe->add(size, posSize); |
62 | | |
63 | 126k | if (textSize) { // Extended run. |
64 | 868 | size = safe->add(size, sizeof(uint32_t)); |
65 | 868 | size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t))); |
66 | 868 | size = safe->add(size, textSize); |
67 | 868 | } |
68 | | |
69 | 126k | return safe->alignUp(size, sizeof(void*)); |
70 | 126k | } |
71 | | |
72 | 36.0k | const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) { |
73 | | // The first record (if present) is stored following the blob object. |
74 | | // (aligned up to make the RunRecord aligned too) |
75 | 36.0k | return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1))); |
76 | 36.0k | } |
77 | | |
78 | 115k | const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) { |
79 | 115k | return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run); |
80 | 115k | } |
81 | | |
82 | | namespace { |
83 | | struct RunRecordStorageEquivalent { |
84 | | SkFont fFont; |
85 | | SkPoint fOffset; |
86 | | uint32_t fCount; |
87 | | uint32_t fFlags; |
88 | | SkDEBUGCODE(unsigned fMagic;) |
89 | | }; |
90 | | } // namespace |
91 | | |
92 | 34.3k | void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const { |
93 | 34.3k | SkASSERT(kRunRecordMagic == fMagic); |
94 | 34.3k | SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop); |
95 | | |
96 | 34.3k | SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); |
97 | 34.3k | SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning()) |
98 | 34.3k | <= (const SkScalar*)NextUnchecked(this)); |
99 | 34.3k | if (isExtended()) { |
100 | 344 | SkASSERT(textSize() > 0); |
101 | 344 | SkASSERT(textSizePtr() < (const uint32_t*)NextUnchecked(this)); |
102 | 344 | SkASSERT(clusterBuffer() < (const uint32_t*)NextUnchecked(this)); |
103 | 344 | SkASSERT(textBuffer() + textSize() <= (const char*)NextUnchecked(this)); |
104 | 344 | } |
105 | 34.3k | static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent), |
106 | 34.3k | "runrecord_should_stay_packed"); |
107 | 34.3k | } SkTextBlob::RunRecord::validate(unsigned char const*) const Line | Count | Source | 92 | 34.3k | void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const { | 93 | 34.3k | SkASSERT(kRunRecordMagic == fMagic); | 94 | 34.3k | SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop); | 95 | | | 96 | 34.3k | SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); | 97 | 34.3k | SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning()) | 98 | 34.3k | <= (const SkScalar*)NextUnchecked(this)); | 99 | 34.3k | if (isExtended()) { | 100 | 344 | SkASSERT(textSize() > 0); | 101 | 344 | SkASSERT(textSizePtr() < (const uint32_t*)NextUnchecked(this)); | 102 | 344 | SkASSERT(clusterBuffer() < (const uint32_t*)NextUnchecked(this)); | 103 | 344 | SkASSERT(textBuffer() + textSize() <= (const char*)NextUnchecked(this)); | 104 | 344 | } | 105 | 34.3k | static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent), | 106 | 34.3k | "runrecord_should_stay_packed"); | 107 | 34.3k | } |
Unexecuted instantiation: SkTextBlob::RunRecord::validate(unsigned char const*) const |
108 | | |
109 | 89.4k | const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) { |
110 | 89.4k | SkSafeMath safe; |
111 | 89.4k | auto res = reinterpret_cast<const RunRecord*>( |
112 | 89.4k | reinterpret_cast<const uint8_t*>(run) |
113 | 89.4k | + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe)); |
114 | 89.4k | SkASSERT(safe); |
115 | 89.4k | return res; |
116 | 89.4k | } |
117 | | |
118 | | size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount, |
119 | | SkTextBlob::GlyphPositioning positioning, |
120 | 128k | SkSafeMath* safe) { |
121 | 128k | return safe->mul(glyphCount, ScalarsPerGlyph(positioning)); |
122 | 128k | } |
123 | | |
124 | 1.88k | uint32_t* SkTextBlob::RunRecord::textSizePtr() const { |
125 | | // textSize follows the position buffer. |
126 | 1.88k | SkASSERT(isExtended()); |
127 | 1.88k | SkSafeMath safe; |
128 | 1.88k | auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]); |
129 | 1.88k | SkASSERT(safe); |
130 | 1.88k | return res; |
131 | 1.88k | } |
132 | | |
133 | 2.69k | void SkTextBlob::RunRecord::grow(uint32_t count) { |
134 | 2.69k | SkScalar* initialPosBuffer = posBuffer(); |
135 | 2.69k | uint32_t initialCount = fCount; |
136 | 2.69k | fCount += count; |
137 | | |
138 | | // Move the initial pos scalars to their new location. |
139 | 2.69k | size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning()); |
140 | 2.69k | SkASSERT((uint8_t*)posBuffer() + copySize <= (const uint8_t*)NextUnchecked(this)); |
141 | | |
142 | | // memmove, as the buffers may overlap |
143 | 2.69k | memmove(posBuffer(), initialPosBuffer, copySize); |
144 | 2.69k | } |
145 | | |
146 | 9.81k | static uint32_t next_id() { |
147 | 9.81k | static std::atomic<uint32_t> nextID{1}; |
148 | 9.81k | uint32_t id; |
149 | 9.81k | do { |
150 | 9.81k | id = nextID.fetch_add(1, std::memory_order_relaxed); |
151 | 9.81k | } while (id == SK_InvalidGenID); |
152 | 9.81k | return id; |
153 | 9.81k | } |
154 | | |
155 | | SkTextBlob::SkTextBlob(const SkRect& bounds) |
156 | | : fBounds(bounds) |
157 | | , fUniqueID(next_id()) |
158 | | , fCacheID(SK_InvalidUniqueID) |
159 | 9.81k | , fPurgeDelegate(nullptr) {} |
160 | | |
161 | 9.81k | SkTextBlob::~SkTextBlob() { |
162 | 9.81k | if (SK_InvalidUniqueID != fCacheID.load()) { |
163 | 782 | PurgeDelegate f = fPurgeDelegate.load(); |
164 | 782 | SkASSERT(f); |
165 | 782 | f(fUniqueID, fCacheID); |
166 | 782 | } |
167 | | |
168 | 9.81k | const auto* run = RunRecord::First(this); |
169 | 31.6k | do { |
170 | 31.6k | const auto* nextRun = RunRecord::Next(run); |
171 | 31.6k | SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);) |
172 | 31.6k | run->~RunRecord(); |
173 | 31.6k | run = nextRun; |
174 | 31.6k | } while (run); |
175 | 9.81k | } |
176 | | |
177 | | namespace { |
178 | | |
179 | | union PositioningAndExtended { |
180 | | int32_t intValue; |
181 | | struct { |
182 | | uint8_t positioning; |
183 | | uint8_t extended; |
184 | | uint16_t padding; |
185 | | }; |
186 | | }; |
187 | | |
188 | | static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), ""); |
189 | | |
190 | | } // namespace |
191 | | |
192 | | enum SkTextBlob::GlyphPositioning : uint8_t { |
193 | | kDefault_Positioning = 0, // Default glyph advances -- zero scalars per glyph. |
194 | | kHorizontal_Positioning = 1, // Horizontal positioning -- one scalar per glyph. |
195 | | kFull_Positioning = 2, // Point positioning -- two scalars per glyph. |
196 | | kRSXform_Positioning = 3, // RSXform positioning -- four scalars per glyph. |
197 | | }; |
198 | | |
199 | 144k | unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) { |
200 | 144k | const uint8_t gScalarsPerPositioning[] = { |
201 | 144k | 0, // kDefault_Positioning |
202 | 144k | 1, // kHorizontal_Positioning |
203 | 144k | 2, // kFull_Positioning |
204 | 144k | 4, // kRSXform_Positioning |
205 | 144k | }; |
206 | 144k | SkASSERT((unsigned)pos <= 3); |
207 | 144k | return gScalarsPerPositioning[pos]; |
208 | 144k | } |
209 | | |
210 | 9.81k | void SkTextBlob::operator delete(void* p) { |
211 | 9.81k | sk_free(p); |
212 | 9.81k | } |
213 | | |
214 | 0 | void* SkTextBlob::operator new(size_t) { |
215 | 0 | SK_ABORT("All blobs are created by placement new."); |
216 | 0 | } |
217 | | |
218 | 9.81k | void* SkTextBlob::operator new(size_t, void* p) { |
219 | 9.81k | return p; |
220 | 9.81k | } |
221 | | |
222 | | SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob) |
223 | 32.4k | : fCurrentRun(SkTextBlob::RunRecord::First(blob)) { |
224 | 32.4k | SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;) |
225 | 32.4k | } SkTextBlobRunIterator::SkTextBlobRunIterator(SkTextBlob const*) Line | Count | Source | 223 | 16.2k | : fCurrentRun(SkTextBlob::RunRecord::First(blob)) { | 224 | 16.2k | SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;) | 225 | 16.2k | } |
SkTextBlobRunIterator::SkTextBlobRunIterator(SkTextBlob const*) Line | Count | Source | 223 | 16.2k | : fCurrentRun(SkTextBlob::RunRecord::First(blob)) { | 224 | 16.2k | SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;) | 225 | 16.2k | } |
|
226 | | |
227 | 56.4k | void SkTextBlobRunIterator::next() { |
228 | 56.4k | SkASSERT(!this->done()); |
229 | | |
230 | 56.4k | if (!this->done()) { |
231 | 56.4k | SkDEBUGCODE(fCurrentRun->validate(fStorageTop);) |
232 | 56.4k | fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun); |
233 | 56.4k | } |
234 | 56.4k | } |
235 | | |
236 | 84.4k | SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const { |
237 | 84.4k | SkASSERT(!this->done()); |
238 | 84.4k | static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) == |
239 | 84.4k | kDefault_Positioning, ""); |
240 | 84.4k | static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) == |
241 | 84.4k | kHorizontal_Positioning, ""); |
242 | 84.4k | static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) == |
243 | 84.4k | kFull_Positioning, ""); |
244 | 84.4k | static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) == |
245 | 84.4k | kRSXform_Positioning, ""); |
246 | | |
247 | 84.4k | return SkTo<GlyphPositioning>(fCurrentRun->positioning()); |
248 | 84.4k | } |
249 | | |
250 | 0 | unsigned SkTextBlobRunIterator::scalarsPerGlyph() const { |
251 | 0 | return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning()); |
252 | 0 | } |
253 | | |
254 | 0 | bool SkTextBlobRunIterator::isLCD() const { |
255 | 0 | return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias; |
256 | 0 | } |
257 | | |
258 | | SkTextBlobBuilder::SkTextBlobBuilder() |
259 | | : fStorageSize(0) |
260 | | , fStorageUsed(0) |
261 | | , fRunCount(0) |
262 | | , fDeferredBounds(false) |
263 | 13.3k | , fLastRun(0) { |
264 | 13.3k | fBounds.setEmpty(); |
265 | 13.3k | } |
266 | | |
267 | 13.3k | SkTextBlobBuilder::~SkTextBlobBuilder() { |
268 | 13.3k | if (nullptr != fStorage.get()) { |
269 | | // We are abandoning runs and must destruct the associated font data. |
270 | | // The easiest way to accomplish that is to use the blob destructor. |
271 | 509 | this->make(); |
272 | 509 | } |
273 | 13.3k | } |
274 | | |
275 | 19.5k | static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) { |
276 | 19.5k | return SkMatrix().setRSXform(xform).mapRect(rect); |
277 | 19.5k | } |
278 | | |
279 | 15.4k | SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { |
280 | 15.4k | const SkFont& font = run.font(); |
281 | 15.4k | SkRect bounds; |
282 | | |
283 | 15.4k | if (SkTextBlob::kDefault_Positioning == run.positioning()) { |
284 | 12.5k | font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), |
285 | 12.5k | SkTextEncoding::kGlyphID, &bounds); |
286 | 12.5k | return bounds.makeOffset(run.offset().x(), run.offset().y()); |
287 | 12.5k | } |
288 | | |
289 | 2.89k | AutoSTArray<16, SkRect> glyphBounds(run.glyphCount()); |
290 | 2.89k | font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr); |
291 | | |
292 | 2.89k | if (SkTextBlob::kRSXform_Positioning == run.positioning()) { |
293 | 0 | bounds.setEmpty(); |
294 | 0 | const SkRSXform* xform = run.xformBuffer(); |
295 | 0 | SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); |
296 | 0 | for (unsigned i = 0; i < run.glyphCount(); ++i) { |
297 | 0 | bounds.join(map_quad_to_rect(xform[i], glyphBounds[i])); |
298 | 0 | } |
299 | 2.89k | } else { |
300 | 2.89k | SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || |
301 | 2.89k | SkTextBlob::kHorizontal_Positioning == run.positioning()); |
302 | | // kFull_Positioning => [ x, y, x, y... ] |
303 | | // kHorizontal_Positioning => [ x, x, x... ] |
304 | | // (const y applied by runBounds.offset(run->offset()) later) |
305 | 2.89k | const SkScalar horizontalConstY = 0; |
306 | 2.89k | const SkScalar* glyphPosX = run.posBuffer(); |
307 | 2.89k | const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ? |
308 | 2.89k | glyphPosX + 1 : &horizontalConstY; |
309 | 2.89k | const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning()); |
310 | 2.89k | const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ? |
311 | 2.89k | posXInc : 0; |
312 | | |
313 | 2.89k | bounds.setEmpty(); |
314 | 18.8k | for (unsigned i = 0; i < run.glyphCount(); ++i) { |
315 | 15.9k | bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY)); |
316 | 15.9k | glyphPosX += posXInc; |
317 | 15.9k | glyphPosY += posYInc; |
318 | 15.9k | } |
319 | | |
320 | 2.89k | SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run)); |
321 | 2.89k | } |
322 | 2.89k | return bounds.makeOffset(run.offset().x(), run.offset().y()); |
323 | 15.4k | } SkTextBlobBuilder::TightRunBounds(SkTextBlob::RunRecord const&) Line | Count | Source | 279 | 15.4k | SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { | 280 | 15.4k | const SkFont& font = run.font(); | 281 | 15.4k | SkRect bounds; | 282 | | | 283 | 15.4k | if (SkTextBlob::kDefault_Positioning == run.positioning()) { | 284 | 12.5k | font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), | 285 | 12.5k | SkTextEncoding::kGlyphID, &bounds); | 286 | 12.5k | return bounds.makeOffset(run.offset().x(), run.offset().y()); | 287 | 12.5k | } | 288 | | | 289 | 2.89k | AutoSTArray<16, SkRect> glyphBounds(run.glyphCount()); | 290 | 2.89k | font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr); | 291 | | | 292 | 2.89k | if (SkTextBlob::kRSXform_Positioning == run.positioning()) { | 293 | 0 | bounds.setEmpty(); | 294 | 0 | const SkRSXform* xform = run.xformBuffer(); | 295 | 0 | SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); | 296 | 0 | for (unsigned i = 0; i < run.glyphCount(); ++i) { | 297 | 0 | bounds.join(map_quad_to_rect(xform[i], glyphBounds[i])); | 298 | 0 | } | 299 | 2.89k | } else { | 300 | 2.89k | SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || | 301 | 2.89k | SkTextBlob::kHorizontal_Positioning == run.positioning()); | 302 | | // kFull_Positioning => [ x, y, x, y... ] | 303 | | // kHorizontal_Positioning => [ x, x, x... ] | 304 | | // (const y applied by runBounds.offset(run->offset()) later) | 305 | 2.89k | const SkScalar horizontalConstY = 0; | 306 | 2.89k | const SkScalar* glyphPosX = run.posBuffer(); | 307 | 2.89k | const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ? | 308 | 2.89k | glyphPosX + 1 : &horizontalConstY; | 309 | 2.89k | const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning()); | 310 | 2.89k | const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ? | 311 | 2.89k | posXInc : 0; | 312 | | | 313 | 2.89k | bounds.setEmpty(); | 314 | 18.8k | for (unsigned i = 0; i < run.glyphCount(); ++i) { | 315 | 15.9k | bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY)); | 316 | 15.9k | glyphPosX += posXInc; | 317 | 15.9k | glyphPosY += posYInc; | 318 | 15.9k | } | 319 | | | 320 | 2.89k | SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run)); | 321 | 2.89k | } | 322 | 2.89k | return bounds.makeOffset(run.offset().x(), run.offset().y()); | 323 | 15.4k | } |
Unexecuted instantiation: SkTextBlobBuilder::TightRunBounds(SkTextBlob::RunRecord const&) |
324 | | |
325 | 12.4k | SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) { |
326 | 12.4k | SkASSERT(run.glyphCount() > 0); |
327 | 12.4k | SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || |
328 | 12.4k | SkTextBlob::kHorizontal_Positioning == run.positioning() || |
329 | 12.4k | SkTextBlob::kRSXform_Positioning == run.positioning()); |
330 | | |
331 | 12.4k | const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font()); |
332 | 12.4k | if (fontBounds.isEmpty()) { |
333 | | // Empty font bounds are likely a font bug. TightBounds has a better chance of |
334 | | // producing useful results in this case. |
335 | 2.89k | return TightRunBounds(run); |
336 | 2.89k | } |
337 | | |
338 | | // Compute the glyph position bbox. |
339 | 9.60k | SkRect bounds; |
340 | 9.60k | switch (run.positioning()) { |
341 | 1.67k | case SkTextBlob::kHorizontal_Positioning: { |
342 | 1.67k | const SkScalar* glyphPos = run.posBuffer(); |
343 | 1.67k | SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); |
344 | | |
345 | 1.67k | SkScalar minX = *glyphPos; |
346 | 1.67k | SkScalar maxX = *glyphPos; |
347 | 33.1k | for (unsigned i = 1; i < run.glyphCount(); ++i) { |
348 | 31.4k | SkScalar x = glyphPos[i]; |
349 | 31.4k | minX = std::min(x, minX); |
350 | 31.4k | maxX = std::max(x, maxX); |
351 | 31.4k | } |
352 | | |
353 | 1.67k | bounds.setLTRB(minX, 0, maxX, 0); |
354 | 1.67k | } break; |
355 | 7.56k | case SkTextBlob::kFull_Positioning: { |
356 | 7.56k | const SkPoint* glyphPosPts = run.pointBuffer(); |
357 | 7.56k | SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); |
358 | | |
359 | 7.56k | bounds.setBounds(glyphPosPts, run.glyphCount()); |
360 | 7.56k | } break; |
361 | 366 | case SkTextBlob::kRSXform_Positioning: { |
362 | 366 | const SkRSXform* xform = run.xformBuffer(); |
363 | 366 | SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); |
364 | 366 | bounds.setEmpty(); |
365 | 19.9k | for (unsigned i = 0; i < run.glyphCount(); ++i) { |
366 | 19.5k | bounds.join(map_quad_to_rect(xform[i], fontBounds)); |
367 | 19.5k | } |
368 | 366 | } break; |
369 | 0 | default: |
370 | 0 | SK_ABORT("unsupported positioning mode"); |
371 | 9.60k | } |
372 | | |
373 | 9.60k | if (run.positioning() != SkTextBlob::kRSXform_Positioning) { |
374 | | // Expand by typeface glyph bounds. |
375 | 9.23k | bounds.fLeft += fontBounds.left(); |
376 | 9.23k | bounds.fTop += fontBounds.top(); |
377 | 9.23k | bounds.fRight += fontBounds.right(); |
378 | 9.23k | bounds.fBottom += fontBounds.bottom(); |
379 | 9.23k | } |
380 | | |
381 | | // Offset by run position. |
382 | 9.60k | return bounds.makeOffset(run.offset().x(), run.offset().y()); |
383 | 9.60k | } |
384 | | |
385 | 41.4k | void SkTextBlobBuilder::updateDeferredBounds() { |
386 | 41.4k | SkASSERT(!fDeferredBounds || fRunCount > 0); |
387 | | |
388 | 41.4k | if (!fDeferredBounds) { |
389 | 16.3k | return; |
390 | 16.3k | } |
391 | | |
392 | 25.0k | SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob))); |
393 | 25.0k | SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + |
394 | 25.0k | fLastRun); |
395 | | |
396 | | // FIXME: we should also use conservative bounds for kDefault_Positioning. |
397 | 25.0k | SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ? |
398 | 12.5k | TightRunBounds(*run) : ConservativeRunBounds(*run); |
399 | 25.0k | fBounds.join(runBounds); |
400 | 25.0k | fDeferredBounds = false; |
401 | 25.0k | } |
402 | | |
403 | 34.3k | void SkTextBlobBuilder::reserve(size_t size) { |
404 | 34.3k | SkSafeMath safe; |
405 | | |
406 | | // We don't currently pre-allocate, but maybe someday... |
407 | 34.3k | if (safe.add(fStorageUsed, size) <= fStorageSize && safe) { |
408 | 36 | return; |
409 | 36 | } |
410 | | |
411 | 34.3k | if (0 == fRunCount) { |
412 | 9.81k | SkASSERT(nullptr == fStorage.get()); |
413 | 9.81k | SkASSERT(0 == fStorageSize); |
414 | 9.81k | SkASSERT(0 == fStorageUsed); |
415 | | |
416 | | // the first allocation also includes blob storage |
417 | | // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned. |
418 | 9.81k | fStorageUsed = SkAlignPtr(sizeof(SkTextBlob)); |
419 | 9.81k | } |
420 | | |
421 | 34.3k | fStorageSize = safe.add(fStorageUsed, size); |
422 | | |
423 | | // FYI: This relies on everything we store being relocatable, particularly SkPaint. |
424 | | // Also, this is counting on the underlying realloc to throw when passed max(). |
425 | 34.3k | fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max()); |
426 | 34.3k | } SkTextBlobBuilder::reserve(unsigned long) Line | Count | Source | 403 | 34.3k | void SkTextBlobBuilder::reserve(size_t size) { | 404 | 34.3k | SkSafeMath safe; | 405 | | | 406 | | // We don't currently pre-allocate, but maybe someday... | 407 | 34.3k | if (safe.add(fStorageUsed, size) <= fStorageSize && safe) { | 408 | 36 | return; | 409 | 36 | } | 410 | | | 411 | 34.3k | if (0 == fRunCount) { | 412 | 9.81k | SkASSERT(nullptr == fStorage.get()); | 413 | 9.81k | SkASSERT(0 == fStorageSize); | 414 | 9.81k | SkASSERT(0 == fStorageUsed); | 415 | | | 416 | | // the first allocation also includes blob storage | 417 | | // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned. | 418 | 9.81k | fStorageUsed = SkAlignPtr(sizeof(SkTextBlob)); | 419 | 9.81k | } | 420 | | | 421 | 34.3k | fStorageSize = safe.add(fStorageUsed, size); | 422 | | | 423 | | // FYI: This relies on everything we store being relocatable, particularly SkPaint. | 424 | | // Also, this is counting on the underlying realloc to throw when passed max(). | 425 | 34.3k | fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max()); | 426 | 34.3k | } |
Unexecuted instantiation: SkTextBlobBuilder::reserve(unsigned long) |
427 | | |
428 | | bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning, |
429 | 34.0k | uint32_t count, SkPoint offset) { |
430 | 34.0k | if (0 == fLastRun) { |
431 | 9.70k | SkASSERT(0 == fRunCount); |
432 | 9.70k | return false; |
433 | 9.70k | } |
434 | | |
435 | 24.3k | SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob))); |
436 | 24.3k | SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + |
437 | 24.3k | fLastRun); |
438 | 24.3k | SkASSERT(run->glyphCount() > 0); |
439 | | |
440 | 24.3k | if (run->textSize() != 0) { |
441 | 44 | return false; |
442 | 44 | } |
443 | | |
444 | 24.2k | if (run->positioning() != positioning |
445 | 24.2k | || run->font() != font |
446 | 24.2k | || (run->glyphCount() + count < run->glyphCount())) { |
447 | 14.3k | return false; |
448 | 14.3k | } |
449 | | |
450 | | // we can merge same-font/same-positioning runs in the following cases: |
451 | | // * fully positioned run following another fully positioned run |
452 | | // * horizontally postioned run following another horizontally positioned run with the same |
453 | | // y-offset |
454 | 9.87k | if (SkTextBlob::kFull_Positioning != positioning |
455 | 9.87k | && (SkTextBlob::kHorizontal_Positioning != positioning |
456 | 7.50k | || run->offset().y() != offset.y())) { |
457 | 7.18k | return false; |
458 | 7.18k | } |
459 | | |
460 | 2.69k | SkSafeMath safe; |
461 | 2.69k | size_t sizeDelta = |
462 | 2.69k | SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) - |
463 | 2.69k | SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe); |
464 | 2.69k | if (!safe) { |
465 | 0 | return false; |
466 | 0 | } |
467 | | |
468 | 2.69k | this->reserve(sizeDelta); |
469 | | |
470 | | // reserve may have realloced |
471 | 2.69k | run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); |
472 | 2.69k | uint32_t preMergeCount = run->glyphCount(); |
473 | 2.69k | run->grow(count); |
474 | | |
475 | | // Callers expect the buffers to point at the newly added slice, ant not at the beginning. |
476 | 2.69k | fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; |
477 | 2.69k | fCurrentRunBuffer.pos = run->posBuffer() |
478 | 2.69k | + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning); |
479 | | |
480 | 2.69k | fStorageUsed += sizeDelta; |
481 | | |
482 | 2.69k | SkASSERT(fStorageUsed <= fStorageSize); |
483 | 2.69k | run->validate(fStorage.get() + fStorageUsed); |
484 | | |
485 | 2.69k | return true; |
486 | 2.69k | } SkTextBlobBuilder::mergeRun(SkFont const&, SkTextBlob::GlyphPositioning, unsigned int, SkPoint) Line | Count | Source | 429 | 34.0k | uint32_t count, SkPoint offset) { | 430 | 34.0k | if (0 == fLastRun) { | 431 | 9.70k | SkASSERT(0 == fRunCount); | 432 | 9.70k | return false; | 433 | 9.70k | } | 434 | | | 435 | 24.3k | SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob))); | 436 | 24.3k | SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + | 437 | 24.3k | fLastRun); | 438 | 24.3k | SkASSERT(run->glyphCount() > 0); | 439 | | | 440 | 24.3k | if (run->textSize() != 0) { | 441 | 44 | return false; | 442 | 44 | } | 443 | | | 444 | 24.2k | if (run->positioning() != positioning | 445 | 24.2k | || run->font() != font | 446 | 24.2k | || (run->glyphCount() + count < run->glyphCount())) { | 447 | 14.3k | return false; | 448 | 14.3k | } | 449 | | | 450 | | // we can merge same-font/same-positioning runs in the following cases: | 451 | | // * fully positioned run following another fully positioned run | 452 | | // * horizontally postioned run following another horizontally positioned run with the same | 453 | | // y-offset | 454 | 9.87k | if (SkTextBlob::kFull_Positioning != positioning | 455 | 9.87k | && (SkTextBlob::kHorizontal_Positioning != positioning | 456 | 7.50k | || run->offset().y() != offset.y())) { | 457 | 7.18k | return false; | 458 | 7.18k | } | 459 | | | 460 | 2.69k | SkSafeMath safe; | 461 | 2.69k | size_t sizeDelta = | 462 | 2.69k | SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) - | 463 | 2.69k | SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe); | 464 | 2.69k | if (!safe) { | 465 | 0 | return false; | 466 | 0 | } | 467 | | | 468 | 2.69k | this->reserve(sizeDelta); | 469 | | | 470 | | // reserve may have realloced | 471 | 2.69k | run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); | 472 | 2.69k | uint32_t preMergeCount = run->glyphCount(); | 473 | 2.69k | run->grow(count); | 474 | | | 475 | | // Callers expect the buffers to point at the newly added slice, ant not at the beginning. | 476 | 2.69k | fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; | 477 | 2.69k | fCurrentRunBuffer.pos = run->posBuffer() | 478 | 2.69k | + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning); | 479 | | | 480 | 2.69k | fStorageUsed += sizeDelta; | 481 | | | 482 | 2.69k | SkASSERT(fStorageUsed <= fStorageSize); | 483 | 2.69k | run->validate(fStorage.get() + fStorageUsed); | 484 | | | 485 | 2.69k | return true; | 486 | 2.69k | } |
Unexecuted instantiation: SkTextBlobBuilder::mergeRun(SkFont const&, SkTextBlob::GlyphPositioning, unsigned int, SkPoint) |
487 | | |
488 | | void SkTextBlobBuilder::allocInternal(const SkFont& font, |
489 | | SkTextBlob::GlyphPositioning positioning, |
490 | | int count, int textSize, SkPoint offset, |
491 | 34.3k | const SkRect* bounds) { |
492 | 34.3k | if (count <= 0 || textSize < 0) { |
493 | 0 | fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr }; |
494 | 0 | return; |
495 | 0 | } |
496 | | |
497 | 34.3k | if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) { |
498 | 31.6k | this->updateDeferredBounds(); |
499 | | |
500 | 31.6k | SkSafeMath safe; |
501 | 31.6k | size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe); |
502 | 31.6k | if (!safe) { |
503 | 0 | fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr }; |
504 | 0 | return; |
505 | 0 | } |
506 | | |
507 | 31.6k | this->reserve(runSize); |
508 | | |
509 | 31.6k | SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob))); |
510 | 31.6k | SkASSERT(fStorageUsed + runSize <= fStorageSize); |
511 | | |
512 | 31.6k | SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) |
513 | 31.6k | SkTextBlob::RunRecord(count, textSize, offset, font, positioning); |
514 | 31.6k | fCurrentRunBuffer.glyphs = run->glyphBuffer(); |
515 | 31.6k | fCurrentRunBuffer.pos = run->posBuffer(); |
516 | 31.6k | fCurrentRunBuffer.utf8text = run->textBuffer(); |
517 | 31.6k | fCurrentRunBuffer.clusters = run->clusterBuffer(); |
518 | | |
519 | 31.6k | fLastRun = fStorageUsed; |
520 | 31.6k | fStorageUsed += runSize; |
521 | 31.6k | fRunCount++; |
522 | | |
523 | 31.6k | SkASSERT(fStorageUsed <= fStorageSize); |
524 | 31.6k | run->validate(fStorage.get() + fStorageUsed); |
525 | 31.6k | } |
526 | 34.3k | SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text); |
527 | 34.3k | SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters); |
528 | 34.3k | if (!fDeferredBounds) { |
529 | 31.8k | if (bounds) { |
530 | 6.78k | fBounds.join(*bounds); |
531 | 25.0k | } else { |
532 | 25.0k | fDeferredBounds = true; |
533 | 25.0k | } |
534 | 31.8k | } |
535 | 34.3k | } |
536 | | |
537 | | // SkFont versions |
538 | | |
539 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count, |
540 | | SkScalar x, SkScalar y, |
541 | 12.5k | const SkRect* bounds) { |
542 | 12.5k | this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds); |
543 | 12.5k | return fCurrentRunBuffer; |
544 | 12.5k | } |
545 | | |
546 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count, |
547 | | SkScalar y, |
548 | 1.83k | const SkRect* bounds) { |
549 | 1.83k | this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds); |
550 | 1.83k | return fCurrentRunBuffer; |
551 | 1.83k | } |
552 | | |
553 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count, |
554 | 12.7k | const SkRect* bounds) { |
555 | 12.7k | this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds); |
556 | 12.7k | return fCurrentRunBuffer; |
557 | 12.7k | } |
558 | | |
559 | | const SkTextBlobBuilder::RunBuffer& |
560 | 366 | SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) { |
561 | 366 | this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr); |
562 | 366 | return fCurrentRunBuffer; |
563 | 366 | } |
564 | | |
565 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count, |
566 | | SkScalar x, SkScalar y, |
567 | | int textByteCount, |
568 | 5.17k | const SkRect* bounds) { |
569 | 5.17k | this->allocInternal(font, |
570 | 5.17k | SkTextBlob::kDefault_Positioning, |
571 | 5.17k | count, |
572 | 5.17k | textByteCount, |
573 | 5.17k | SkPoint::Make(x, y), |
574 | 5.17k | bounds); |
575 | 5.17k | return fCurrentRunBuffer; |
576 | 5.17k | } |
577 | | |
578 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font, |
579 | | int count, |
580 | | SkScalar y, |
581 | | int textByteCount, |
582 | 505 | const SkRect* bounds) { |
583 | 505 | this->allocInternal(font, |
584 | 505 | SkTextBlob::kHorizontal_Positioning, |
585 | 505 | count, |
586 | 505 | textByteCount, |
587 | 505 | SkPoint::Make(0, y), |
588 | 505 | bounds); |
589 | 505 | return fCurrentRunBuffer; |
590 | 505 | } |
591 | | |
592 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font, |
593 | | int count, |
594 | | int textByteCount, |
595 | 143 | const SkRect *bounds) { |
596 | 143 | this->allocInternal(font, |
597 | 143 | SkTextBlob::kFull_Positioning, |
598 | 143 | count, textByteCount, |
599 | 143 | SkPoint::Make(0, 0), |
600 | 143 | bounds); |
601 | 143 | return fCurrentRunBuffer; |
602 | 143 | } |
603 | | |
604 | | const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextRSXform(const SkFont& font, |
605 | | int count, |
606 | | int textByteCount, |
607 | 967 | const SkRect *bounds) { |
608 | 967 | this->allocInternal(font, |
609 | 967 | SkTextBlob::kRSXform_Positioning, |
610 | 967 | count, |
611 | 967 | textByteCount, |
612 | 967 | {0, 0}, |
613 | 967 | bounds); |
614 | 967 | return fCurrentRunBuffer; |
615 | 967 | } |
616 | | |
617 | 9.84k | sk_sp<SkTextBlob> SkTextBlobBuilder::make() { |
618 | 9.84k | if (!fRunCount) { |
619 | | // We don't instantiate empty blobs. |
620 | 36 | SkASSERT(!fStorage.get()); |
621 | 36 | SkASSERT(fStorageUsed == 0); |
622 | 36 | SkASSERT(fStorageSize == 0); |
623 | 36 | SkASSERT(fLastRun == 0); |
624 | 36 | SkASSERT(fBounds.isEmpty()); |
625 | 36 | return nullptr; |
626 | 36 | } |
627 | | |
628 | 9.81k | this->updateDeferredBounds(); |
629 | | |
630 | | // Tag the last run as such. |
631 | 9.81k | auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); |
632 | 9.81k | lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag; |
633 | | |
634 | 9.81k | SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds); |
635 | 9.81k | SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) |
636 | | |
637 | 9.81k | SkDEBUGCODE( |
638 | 9.81k | SkSafeMath safe; |
639 | 9.81k | size_t validateSize = SkAlignPtr(sizeof(SkTextBlob)); |
640 | 9.81k | for (const auto* run = SkTextBlob::RunRecord::First(blob); run; |
641 | 9.81k | run = SkTextBlob::RunRecord::Next(run)) { |
642 | 9.81k | validateSize += SkTextBlob::RunRecord::StorageSize( |
643 | 9.81k | run->fCount, run->textSize(), run->positioning(), &safe); |
644 | 9.81k | run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed); |
645 | 9.81k | fRunCount--; |
646 | 9.81k | } |
647 | 9.81k | SkASSERT(validateSize == fStorageUsed); |
648 | 9.81k | SkASSERT(fRunCount == 0); |
649 | 9.81k | SkASSERT(safe); |
650 | 9.81k | ) |
651 | | |
652 | 9.81k | fStorageUsed = 0; |
653 | 9.81k | fStorageSize = 0; |
654 | 9.81k | fRunCount = 0; |
655 | 9.81k | fLastRun = 0; |
656 | 9.81k | fBounds.setEmpty(); |
657 | | |
658 | 9.81k | return sk_sp<SkTextBlob>(blob); |
659 | 9.84k | } SkTextBlobBuilder::make() Line | Count | Source | 617 | 9.84k | sk_sp<SkTextBlob> SkTextBlobBuilder::make() { | 618 | 9.84k | if (!fRunCount) { | 619 | | // We don't instantiate empty blobs. | 620 | 36 | SkASSERT(!fStorage.get()); | 621 | 36 | SkASSERT(fStorageUsed == 0); | 622 | 36 | SkASSERT(fStorageSize == 0); | 623 | 36 | SkASSERT(fLastRun == 0); | 624 | 36 | SkASSERT(fBounds.isEmpty()); | 625 | 36 | return nullptr; | 626 | 36 | } | 627 | | | 628 | 9.81k | this->updateDeferredBounds(); | 629 | | | 630 | | // Tag the last run as such. | 631 | 9.81k | auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); | 632 | 9.81k | lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag; | 633 | | | 634 | 9.81k | SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds); | 635 | 9.81k | SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) | 636 | | | 637 | 9.81k | SkDEBUGCODE( | 638 | 9.81k | SkSafeMath safe; | 639 | 9.81k | size_t validateSize = SkAlignPtr(sizeof(SkTextBlob)); | 640 | 9.81k | for (const auto* run = SkTextBlob::RunRecord::First(blob); run; | 641 | 9.81k | run = SkTextBlob::RunRecord::Next(run)) { | 642 | 9.81k | validateSize += SkTextBlob::RunRecord::StorageSize( | 643 | 9.81k | run->fCount, run->textSize(), run->positioning(), &safe); | 644 | 9.81k | run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed); | 645 | 9.81k | fRunCount--; | 646 | 9.81k | } | 647 | 9.81k | SkASSERT(validateSize == fStorageUsed); | 648 | 9.81k | SkASSERT(fRunCount == 0); | 649 | 9.81k | SkASSERT(safe); | 650 | 9.81k | ) | 651 | | | 652 | 9.81k | fStorageUsed = 0; | 653 | 9.81k | fStorageSize = 0; | 654 | 9.81k | fRunCount = 0; | 655 | 9.81k | fLastRun = 0; | 656 | 9.81k | fBounds.setEmpty(); | 657 | | | 658 | 9.81k | return sk_sp<SkTextBlob>(blob); | 659 | 9.84k | } |
Unexecuted instantiation: SkTextBlobBuilder::make() |
660 | | |
661 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
662 | | |
663 | 0 | void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) { |
664 | | // seems like we could skip this, and just recompute bounds in unflatten, but |
665 | | // some cc_unittests fail if we remove this... |
666 | 0 | buffer.writeRect(blob.bounds()); |
667 | |
|
668 | 0 | SkTextBlobRunIterator it(&blob); |
669 | 0 | while (!it.done()) { |
670 | 0 | SkASSERT(it.glyphCount() > 0); |
671 | |
|
672 | 0 | buffer.write32(it.glyphCount()); |
673 | 0 | PositioningAndExtended pe; |
674 | 0 | pe.intValue = 0; |
675 | 0 | pe.positioning = it.positioning(); |
676 | 0 | SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat. |
677 | |
|
678 | 0 | uint32_t textSize = it.textSize(); |
679 | 0 | pe.extended = textSize > 0; |
680 | 0 | buffer.write32(pe.intValue); |
681 | 0 | if (pe.extended) { |
682 | 0 | buffer.write32(textSize); |
683 | 0 | } |
684 | 0 | buffer.writePoint(it.offset()); |
685 | |
|
686 | 0 | SkFontPriv::Flatten(it.font(), buffer); |
687 | |
|
688 | 0 | buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t)); |
689 | 0 | buffer.writeByteArray(it.pos(), |
690 | 0 | it.glyphCount() * sizeof(SkScalar) * |
691 | 0 | SkTextBlob::ScalarsPerGlyph( |
692 | 0 | SkTo<SkTextBlob::GlyphPositioning>(it.positioning()))); |
693 | 0 | if (pe.extended) { |
694 | 0 | buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount()); |
695 | 0 | buffer.writeByteArray(it.text(), it.textSize()); |
696 | 0 | } |
697 | |
|
698 | 0 | it.next(); |
699 | 0 | } |
700 | | |
701 | | // Marker for the last run (0 is not a valid glyph count). |
702 | 0 | buffer.write32(0); |
703 | 0 | } Unexecuted instantiation: SkTextBlobPriv::Flatten(SkTextBlob const&, SkWriteBuffer&) Unexecuted instantiation: SkTextBlobPriv::Flatten(SkTextBlob const&, SkWriteBuffer&) |
704 | | |
705 | 2.30k | sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) { |
706 | 2.30k | SkRect bounds; |
707 | 2.30k | reader.readRect(&bounds); |
708 | | |
709 | 2.30k | SkTextBlobBuilder blobBuilder; |
710 | 2.30k | SkSafeMath safe; |
711 | 8.70k | for (;;) { |
712 | 8.70k | int glyphCount = reader.read32(); |
713 | 8.70k | if (glyphCount == 0) { |
714 | | // End-of-runs marker. |
715 | 1.16k | break; |
716 | 1.16k | } |
717 | | |
718 | 7.54k | PositioningAndExtended pe; |
719 | 7.54k | pe.intValue = reader.read32(); |
720 | 7.54k | const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning); |
721 | 7.54k | if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) { |
722 | 78 | return nullptr; |
723 | 78 | } |
724 | 7.46k | int textSize = pe.extended ? reader.read32() : 0; |
725 | 7.46k | if (textSize < 0) { |
726 | 80 | return nullptr; |
727 | 80 | } |
728 | | |
729 | 7.38k | SkPoint offset; |
730 | 7.38k | reader.readPoint(&offset); |
731 | 7.38k | SkFont font; |
732 | 7.38k | SkFontPriv::Unflatten(&font, reader); |
733 | | |
734 | | // Compute the expected size of the buffer and ensure we have enough to deserialize |
735 | | // a run before allocating it. |
736 | 7.38k | const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)), |
737 | 7.38k | posSize = |
738 | 7.38k | safe.mul(glyphCount, safe.mul(sizeof(SkScalar), |
739 | 7.38k | SkTextBlob::ScalarsPerGlyph(pos))), |
740 | 7.38k | clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0; |
741 | 7.38k | const size_t totalSize = |
742 | 7.38k | safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize)); |
743 | | |
744 | 7.38k | if (!reader.isValid() || !safe || totalSize > reader.available()) { |
745 | 595 | return nullptr; |
746 | 595 | } |
747 | | |
748 | 6.78k | const SkTextBlobBuilder::RunBuffer* buf = nullptr; |
749 | 6.78k | switch (pos) { |
750 | 5.17k | case SkTextBlob::kDefault_Positioning: |
751 | 5.17k | buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(), |
752 | 5.17k | textSize, &bounds); |
753 | 5.17k | break; |
754 | 505 | case SkTextBlob::kHorizontal_Positioning: |
755 | 505 | buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(), |
756 | 505 | textSize, &bounds); |
757 | 505 | break; |
758 | 143 | case SkTextBlob::kFull_Positioning: |
759 | 143 | buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, &bounds); |
760 | 143 | break; |
761 | 967 | case SkTextBlob::kRSXform_Positioning: |
762 | 967 | buf = &blobBuilder.allocRunTextRSXform(font, glyphCount, textSize, &bounds); |
763 | 967 | break; |
764 | 6.78k | } |
765 | | |
766 | 6.78k | if (!buf->glyphs || |
767 | 6.78k | !buf->pos || |
768 | 6.78k | (pe.extended && (!buf->clusters || !buf->utf8text))) { |
769 | 3 | return nullptr; |
770 | 3 | } |
771 | | |
772 | 6.78k | if (!reader.readByteArray(buf->glyphs, glyphSize) || |
773 | 6.78k | !reader.readByteArray(buf->pos, posSize)) { |
774 | 362 | return nullptr; |
775 | 362 | } |
776 | | |
777 | 6.42k | if (pe.extended) { |
778 | 326 | if (!reader.readByteArray(buf->clusters, clusterSize) || |
779 | 326 | !reader.readByteArray(buf->utf8text, textSize)) { |
780 | 21 | return nullptr; |
781 | 21 | } |
782 | 326 | } |
783 | 6.42k | } |
784 | | |
785 | 1.16k | return blobBuilder.make(); |
786 | 2.30k | } |
787 | | |
788 | | sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font, |
789 | 0 | SkTextEncoding encoding) { |
790 | | // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the |
791 | | // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now. |
792 | 0 | const int count = font.countText(text, byteLength, encoding); |
793 | 0 | if (count < 1) { |
794 | 0 | return nullptr; |
795 | 0 | } |
796 | 0 | SkTextBlobBuilder builder; |
797 | 0 | auto buffer = builder.allocRunPos(font, count); |
798 | 0 | font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); |
799 | 0 | font.getPos(buffer.glyphs, count, buffer.points(), {0, 0}); |
800 | 0 | return builder.make(); |
801 | 0 | } |
802 | | |
803 | | sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength, |
804 | | const SkPoint pos[], const SkFont& font, |
805 | 2.20k | SkTextEncoding encoding) { |
806 | 2.20k | const int count = font.countText(text, byteLength, encoding); |
807 | 2.20k | if (count < 1) { |
808 | 0 | return nullptr; |
809 | 0 | } |
810 | 2.20k | SkTextBlobBuilder builder; |
811 | 2.20k | auto buffer = builder.allocRunPos(font, count); |
812 | 2.20k | font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); |
813 | 2.20k | memcpy(buffer.points(), pos, count * sizeof(SkPoint)); |
814 | 2.20k | return builder.make(); |
815 | 2.20k | } |
816 | | |
817 | | sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength, |
818 | | const SkScalar xpos[], SkScalar constY, |
819 | 0 | const SkFont& font, SkTextEncoding encoding) { |
820 | 0 | const int count = font.countText(text, byteLength, encoding); |
821 | 0 | if (count < 1) { |
822 | 0 | return nullptr; |
823 | 0 | } |
824 | 0 | SkTextBlobBuilder builder; |
825 | 0 | auto buffer = builder.allocRunPosH(font, count, constY); |
826 | 0 | font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); |
827 | 0 | memcpy(buffer.pos, xpos, count * sizeof(SkScalar)); |
828 | 0 | return builder.make(); |
829 | 0 | } |
830 | | |
831 | | sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength, |
832 | | const SkRSXform xform[], const SkFont& font, |
833 | 0 | SkTextEncoding encoding) { |
834 | 0 | const int count = font.countText(text, byteLength, encoding); |
835 | 0 | if (count < 1) { |
836 | 0 | return nullptr; |
837 | 0 | } |
838 | 0 | SkTextBlobBuilder builder; |
839 | 0 | auto buffer = builder.allocRunRSXform(font, count); |
840 | 0 | font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); |
841 | 0 | memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform)); |
842 | 0 | return builder.make(); |
843 | 0 | } |
844 | | |
845 | 0 | sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const { |
846 | 0 | SkBinaryWriteBuffer buffer(procs); |
847 | 0 | SkTextBlobPriv::Flatten(*this, buffer); |
848 | |
|
849 | 0 | size_t total = buffer.bytesWritten(); |
850 | 0 | sk_sp<SkData> data = SkData::MakeUninitialized(total); |
851 | 0 | buffer.writeToMemory(data->writable_data()); |
852 | 0 | return data; |
853 | 0 | } |
854 | | |
855 | | sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length, |
856 | 0 | const SkDeserialProcs& procs) { |
857 | 0 | SkReadBuffer buffer(data, length); |
858 | 0 | buffer.setDeserialProcs(procs); |
859 | 0 | return SkTextBlobPriv::MakeFromBuffer(buffer); |
860 | 0 | } |
861 | | |
862 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
863 | | |
864 | 0 | size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const { |
865 | 0 | SkBinaryWriteBuffer buffer(memory, memory_size, procs); |
866 | 0 | SkTextBlobPriv::Flatten(*this, buffer); |
867 | 0 | return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u; |
868 | 0 | } |
869 | | |
870 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
871 | | |
872 | | namespace { |
873 | | int get_glyph_run_intercepts(const sktext::GlyphRun& glyphRun, |
874 | | const SkPaint& paint, |
875 | | const SkScalar bounds[2], |
876 | | SkScalar intervals[], |
877 | 0 | int* intervalCount) { |
878 | 0 | SkScalar scale = SK_Scalar1; |
879 | 0 | SkPaint interceptPaint{paint}; |
880 | 0 | SkFont interceptFont{glyphRun.font()}; |
881 | |
|
882 | 0 | interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup |
883 | | |
884 | | // can't use our canonical size if we need to apply path effects |
885 | 0 | if (interceptPaint.getPathEffect() == nullptr) { |
886 | | // If the wrong size is going to be used, don't hint anything. |
887 | 0 | interceptFont.setHinting(SkFontHinting::kNone); |
888 | 0 | interceptFont.setSubpixel(true); |
889 | 0 | scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths; |
890 | 0 | interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths)); |
891 | | // Note: fScale can be zero here (even if it wasn't before the divide). It can also |
892 | | // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior, |
893 | | // since downstream we will check for the resulting coordinates being non-finite anyway. |
894 | | // Thus we don't need to check for zero here. |
895 | 0 | if (interceptPaint.getStrokeWidth() > 0 |
896 | 0 | && interceptPaint.getStyle() != SkPaint::kFill_Style) { |
897 | 0 | interceptPaint.setStrokeWidth( |
898 | 0 | sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale)); |
899 | 0 | } |
900 | 0 | } |
901 | |
|
902 | 0 | interceptPaint.setStyle(SkPaint::kFill_Style); |
903 | 0 | interceptPaint.setPathEffect(nullptr); |
904 | |
|
905 | 0 | SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint); |
906 | 0 | SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec}; |
907 | |
|
908 | 0 | const SkPoint* posCursor = glyphRun.positions().begin(); |
909 | 0 | for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) { |
910 | 0 | SkPoint pos = *posCursor++; |
911 | |
|
912 | 0 | if (glyph->path() != nullptr) { |
913 | | // The typeface is scaled, so un-scale the bounds to be in the space of the typeface. |
914 | | // Also ensure the bounds are properly offset by the vertical positioning of the glyph. |
915 | 0 | SkScalar scaledBounds[2] = { |
916 | 0 | (bounds[0] - pos.y()) / scale, |
917 | 0 | (bounds[1] - pos.y()) / scale |
918 | 0 | }; |
919 | 0 | metricsAndPaths.findIntercepts( |
920 | 0 | scaledBounds, scale, pos.x(), glyph, intervals, intervalCount); |
921 | 0 | } |
922 | 0 | } |
923 | 0 | return *intervalCount; |
924 | 0 | } |
925 | | } // namespace |
926 | | |
927 | | int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[], |
928 | 0 | const SkPaint* paint) const { |
929 | 0 | SkTLazy<SkPaint> defaultPaint; |
930 | 0 | if (paint == nullptr) { |
931 | 0 | defaultPaint.init(); |
932 | 0 | paint = defaultPaint.get(); |
933 | 0 | } |
934 | |
|
935 | 0 | sktext::GlyphRunBuilder builder; |
936 | 0 | auto glyphRunList = builder.blobToGlyphRunList(*this, {0, 0}); |
937 | |
|
938 | 0 | int intervalCount = 0; |
939 | 0 | for (const sktext::GlyphRun& glyphRun : glyphRunList) { |
940 | | // Ignore RSXForm runs. |
941 | 0 | if (glyphRun.scaledRotations().empty()) { |
942 | 0 | intervalCount = get_glyph_run_intercepts( |
943 | 0 | glyphRun, *paint, bounds, intervals, &intervalCount); |
944 | 0 | } |
945 | 0 | } |
946 | |
|
947 | 0 | return intervalCount; |
948 | 0 | } |
949 | | |
950 | | std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count, |
951 | | const SkPoint positions[], |
952 | | SkScalar top, SkScalar bottom, |
953 | 0 | const SkPaint* paintPtr) const { |
954 | 0 | if (count <= 0) { |
955 | 0 | return std::vector<SkScalar>(); |
956 | 0 | } |
957 | | |
958 | 0 | const SkPaint paint(paintPtr ? *paintPtr : SkPaint()); |
959 | 0 | const SkScalar bounds[] = {top, bottom}; |
960 | 0 | const sktext::GlyphRun run(*this, |
961 | 0 | {positions, size_t(count)}, {glyphs, size_t(count)}, |
962 | 0 | {nullptr, 0}, {nullptr, 0}, {nullptr, 0}); |
963 | |
|
964 | 0 | std::vector<SkScalar> result; |
965 | 0 | result.resize(count * 2); // worst case allocation |
966 | 0 | int intervalCount = 0; |
967 | 0 | intervalCount = get_glyph_run_intercepts(run, paint, bounds, result.data(), &intervalCount); |
968 | 0 | result.resize(intervalCount); |
969 | 0 | return result; |
970 | 0 | } |
971 | | |
972 | | //////// |
973 | | |
974 | 9.94k | SkTextBlob::Iter::Iter(const SkTextBlob& blob) { |
975 | 9.94k | fRunRecord = RunRecord::First(&blob); |
976 | 9.94k | } |
977 | | |
978 | 47.3k | bool SkTextBlob::Iter::next(Run* rec) { |
979 | 47.3k | if (fRunRecord) { |
980 | 37.3k | if (rec) { |
981 | 37.3k | rec->fTypeface = fRunRecord->font().getTypeface(); |
982 | 37.3k | rec->fGlyphCount = fRunRecord->glyphCount(); |
983 | 37.3k | rec->fGlyphIndices = fRunRecord->glyphBuffer(); |
984 | | #ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED |
985 | | rec->fClusterIndex_forTest = fRunRecord->clusterBuffer(); |
986 | | rec->fUtf8Size_forTest = fRunRecord->textSize(); |
987 | | rec->fUtf8_forTest = fRunRecord->textBuffer(); |
988 | | #endif |
989 | 37.3k | } |
990 | 37.3k | if (fRunRecord->isLastRun()) { |
991 | 9.94k | fRunRecord = nullptr; |
992 | 27.4k | } else { |
993 | 27.4k | fRunRecord = RunRecord::Next(fRunRecord); |
994 | 27.4k | } |
995 | 37.3k | return true; |
996 | 37.3k | } |
997 | 9.94k | return false; |
998 | 47.3k | } |
999 | | |
1000 | 0 | bool SkTextBlob::Iter::experimentalNext(ExperimentalRun* rec) { |
1001 | 0 | if (fRunRecord) { |
1002 | 0 | if (rec) { |
1003 | 0 | rec->font = fRunRecord->font(); |
1004 | 0 | rec->count = fRunRecord->glyphCount(); |
1005 | 0 | rec->glyphs = fRunRecord->glyphBuffer(); |
1006 | 0 | rec->positions = fRunRecord->pointBuffer(); |
1007 | 0 | } |
1008 | 0 | if (fRunRecord->isLastRun()) { |
1009 | 0 | fRunRecord = nullptr; |
1010 | 0 | } else { |
1011 | 0 | fRunRecord = RunRecord::Next(fRunRecord); |
1012 | 0 | } |
1013 | 0 | return true; |
1014 | 0 | } |
1015 | 0 | return false; |
1016 | 0 | } |