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