blob: f20b6cd415ebf7629420224b3a849a2b4fe75980 [file] [log] [blame]
fmalita00d5c2c2014-08-21 08:53:26 -07001/*
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
John Stiles008d60e2022-06-15 21:58:25 +00008#include "include/core/SkTextBlob.h"
Herb Derby1aea9032022-06-14 14:59:21 -04009
Kevin Lubick78fced82023-09-20 15:56:21 -040010#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"
Herb Derby1aea9032022-06-14 14:59:21 -040016#include "include/core/SkRSXform.h"
Kevin Lubick78fced82023-09-20 15:56:21 -040017#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"
Kevin Lubick46572b42023-01-18 13:11:06 -050022#include "src/base/SkSafeMath.h"
Kevin Lubick8fdbbca2023-06-07 16:14:51 -040023#include "src/base/SkTLazy.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkFontPriv.h"
Kevin Lubick78fced82023-09-20 15:56:21 -040025#include "src/core/SkGlyph.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkReadBuffer.h"
Herb Derbybaf64782019-04-17 18:01:04 -040027#include "src/core/SkStrikeSpec.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "src/core/SkTextBlobPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050029#include "src/core/SkWriteBuffer.h"
Herb Derby1aea9032022-06-14 14:59:21 -040030#include "src/text/GlyphRun.h"
fmalitab7425172014-08-26 07:56:44 -070031
Kevin Lubick78fced82023-09-20 15:56:21 -040032#include <algorithm>
Mike Klein0ec1c572018-12-04 11:52:51 -050033#include <atomic>
Florin Malitad923a712017-11-22 10:11:12 -050034#include <limits>
Mike Klein79aea6a2018-06-11 10:45:26 -040035#include <new>
Kevin Lubick78fced82023-09-20 15:56:21 -040036#include <vector>
Florin Malitad923a712017-11-22 10:11:12 -050037
Herb Derbya183f792023-01-18 17:05:36 -050038using namespace skia_private;
39
Herb Derby434377a2018-10-02 16:48:17 -040040namespace {
fmalita055f6b52015-04-09 08:49:32 -070041struct RunFontStorageEquivalent {
42 SkScalar fSize, fScaleX;
43 void* fTypeface;
44 SkScalar fSkewX;
45 uint32_t fFlags;
46};
Mike Reed7b7ab592018-12-05 21:32:05 -050047static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
John Stilesa6841be2020-08-06 14:11:56 -040048} // namespace
fmalita055f6b52015-04-09 08:49:32 -070049
Herb Derby434377a2018-10-02 16:48:17 -040050size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
51 SkTextBlob::GlyphPositioning positioning,
52 SkSafeMath* safe) {
53 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
fmalita055f6b52015-04-09 08:49:32 -070054
Herb Derby434377a2018-10-02 16:48:17 -040055 auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
56 posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
fmalita3c196de2014-09-20 05:40:22 -070057
Herb Derby434377a2018-10-02 16:48:17 -040058 // RunRecord object + (aligned) glyph buffer + position buffer
59 auto size = sizeof(SkTextBlob::RunRecord);
60 size = safe->add(size, safe->alignUp(glyphSize, 4));
61 size = safe->add(size, posSize);
62
63 if (textSize) { // Extended run.
64 size = safe->add(size, sizeof(uint32_t));
65 size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
66 size = safe->add(size, textSize);
67 }
68
69 return safe->alignUp(size, sizeof(void*));
70}
71
72const 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 return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
76}
77
78const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
79 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
80}
fmalita3c196de2014-09-20 05:40:22 -070081
halcanary4f0a23a2016-08-30 11:58:33 -070082namespace {
83struct RunRecordStorageEquivalent {
Mike Reed7b7ab592018-12-05 21:32:05 -050084 SkFont fFont;
halcanary4f0a23a2016-08-30 11:58:33 -070085 SkPoint fOffset;
86 uint32_t fCount;
87 uint32_t fFlags;
88 SkDEBUGCODE(unsigned fMagic;)
89};
John Stilesa6841be2020-08-06 14:11:56 -040090} // namespace
halcanary4f0a23a2016-08-30 11:58:33 -070091
Herb Derby434377a2018-10-02 16:48:17 -040092void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
93 SkASSERT(kRunRecordMagic == fMagic);
Brian Osmanc9943f12023-11-17 16:39:23 -050094 SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop);
Florin Malita3a9a7a32017-03-13 09:03:24 -040095
Herb Derby434377a2018-10-02 16:48:17 -040096 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
97 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
Brian Osmanc9943f12023-11-17 16:39:23 -050098 <= (const SkScalar*)NextUnchecked(this));
Herb Derby434377a2018-10-02 16:48:17 -040099 if (isExtended()) {
100 SkASSERT(textSize() > 0);
Brian Osmanc9943f12023-11-17 16:39:23 -0500101 SkASSERT(textSizePtr() < (const uint32_t*)NextUnchecked(this));
102 SkASSERT(clusterBuffer() < (const uint32_t*)NextUnchecked(this));
103 SkASSERT(textBuffer() + textSize() <= (const char*)NextUnchecked(this));
fmalita3c196de2014-09-20 05:40:22 -0700104 }
Herb Derby434377a2018-10-02 16:48:17 -0400105 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
106 "runrecord_should_stay_packed");
107}
fmalita3c196de2014-09-20 05:40:22 -0700108
Herb Derby434377a2018-10-02 16:48:17 -0400109const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) {
110 SkSafeMath safe;
111 auto res = reinterpret_cast<const RunRecord*>(
112 reinterpret_cast<const uint8_t*>(run)
113 + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
114 SkASSERT(safe);
115 return res;
116}
fmalita3c196de2014-09-20 05:40:22 -0700117
Herb Derby434377a2018-10-02 16:48:17 -0400118size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
119 SkTextBlob::GlyphPositioning positioning,
120 SkSafeMath* safe) {
121 return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
122}
fmalita3c196de2014-09-20 05:40:22 -0700123
Herb Derby434377a2018-10-02 16:48:17 -0400124uint32_t* SkTextBlob::RunRecord::textSizePtr() const {
125 // textSize follows the position buffer.
126 SkASSERT(isExtended());
127 SkSafeMath safe;
128 auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
129 SkASSERT(safe);
130 return res;
131}
fmalita3c196de2014-09-20 05:40:22 -0700132
Herb Derby434377a2018-10-02 16:48:17 -0400133void SkTextBlob::RunRecord::grow(uint32_t count) {
134 SkScalar* initialPosBuffer = posBuffer();
135 uint32_t initialCount = fCount;
136 fCount += count;
fmalita3c196de2014-09-20 05:40:22 -0700137
Herb Derby434377a2018-10-02 16:48:17 -0400138 // Move the initial pos scalars to their new location.
139 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
Brian Osmanc9943f12023-11-17 16:39:23 -0500140 SkASSERT((uint8_t*)posBuffer() + copySize <= (const uint8_t*)NextUnchecked(this));
fmalita3c196de2014-09-20 05:40:22 -0700141
Herb Derby434377a2018-10-02 16:48:17 -0400142 // memmove, as the buffers may overlap
143 memmove(posBuffer(), initialPosBuffer, copySize);
144}
fmalita3c196de2014-09-20 05:40:22 -0700145
Jim Van Verthfe736882023-09-22 11:00:06 -0400146static uint32_t next_id() {
147 static std::atomic<uint32_t> nextID{1};
148 uint32_t id;
joshualitt2af85832015-03-25 13:40:13 -0700149 do {
Adlai Holler4888cda2020-11-06 16:37:37 -0500150 id = nextID.fetch_add(1, std::memory_order_relaxed);
joshualitt2af85832015-03-25 13:40:13 -0700151 } while (id == SK_InvalidGenID);
152 return id;
153}
154
Florin Malita3a9a7a32017-03-13 09:03:24 -0400155SkTextBlob::SkTextBlob(const SkRect& bounds)
156 : fBounds(bounds)
Florin Malita4a01ac92017-03-13 16:45:28 -0400157 , fUniqueID(next_id())
Kevin Lubicka8d01022023-09-18 11:33:07 -0400158 , fCacheID(SK_InvalidUniqueID)
159 , fPurgeDelegate(nullptr) {}
fmalita00d5c2c2014-08-21 08:53:26 -0700160
fmalita3c196de2014-09-20 05:40:22 -0700161SkTextBlob::~SkTextBlob() {
Jim Van Verth474d6872017-12-14 13:00:05 -0500162 if (SK_InvalidUniqueID != fCacheID.load()) {
Kevin Lubicka8d01022023-09-18 11:33:07 -0400163 PurgeDelegate f = fPurgeDelegate.load();
164 SkASSERT(f);
165 f(fUniqueID, fCacheID);
Florin Malita4a01ac92017-03-13 16:45:28 -0400166 }
Florin Malita4a01ac92017-03-13 16:45:28 -0400167
Florin Malita3a9a7a32017-03-13 09:03:24 -0400168 const auto* run = RunRecord::First(this);
169 do {
170 const auto* nextRun = RunRecord::Next(run);
fmalita3c196de2014-09-20 05:40:22 -0700171 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
172 run->~RunRecord();
173 run = nextRun;
Florin Malita3a9a7a32017-03-13 09:03:24 -0400174 } while (run);
fmalita3c196de2014-09-20 05:40:22 -0700175}
176
halcanary4f0a23a2016-08-30 11:58:33 -0700177namespace {
Florin Malita90dcafc2017-11-22 10:53:33 -0500178
halcanary4f0a23a2016-08-30 11:58:33 -0700179union PositioningAndExtended {
180 int32_t intValue;
181 struct {
Florin Malitaab54e732018-07-27 09:47:15 -0400182 uint8_t positioning;
Florin Malita90dcafc2017-11-22 10:53:33 -0500183 uint8_t extended;
halcanary4f0a23a2016-08-30 11:58:33 -0700184 uint16_t padding;
185 };
186};
Florin Malita90dcafc2017-11-22 10:53:33 -0500187
188static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
189
halcanary4f0a23a2016-08-30 11:58:33 -0700190} // namespace
191
Florin Malitaab54e732018-07-27 09:47:15 -0400192enum SkTextBlob::GlyphPositioning : uint8_t {
Mike Reed30cf62b2018-12-20 11:18:24 -0500193 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.
Florin Malitaab54e732018-07-27 09:47:15 -0400197};
198
fmalita3c196de2014-09-20 05:40:22 -0700199unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
Mike Reed30cf62b2018-12-20 11:18:24 -0500200 const uint8_t gScalarsPerPositioning[] = {
201 0, // kDefault_Positioning
202 1, // kHorizontal_Positioning
203 2, // kFull_Positioning
204 4, // kRSXform_Positioning
205 };
206 SkASSERT((unsigned)pos <= 3);
207 return gScalarsPerPositioning[pos];
fmalita3c196de2014-09-20 05:40:22 -0700208}
209
Yong-Hwan Baek688a8e52018-07-09 14:14:26 +0900210void SkTextBlob::operator delete(void* p) {
211 sk_free(p);
212}
213
214void* SkTextBlob::operator new(size_t) {
215 SK_ABORT("All blobs are created by placement new.");
Yong-Hwan Baek688a8e52018-07-09 14:14:26 +0900216}
217
218void* SkTextBlob::operator new(size_t, void* p) {
219 return p;
220}
221
halcanary33779752015-10-27 14:01:05 -0700222SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
Florin Malita3a9a7a32017-03-13 09:03:24 -0400223 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
Brian Osmanc9943f12023-11-17 16:39:23 -0500224 SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;)
fmalita00d5c2c2014-08-21 08:53:26 -0700225}
226
halcanary33779752015-10-27 14:01:05 -0700227void SkTextBlobRunIterator::next() {
fmalita00d5c2c2014-08-21 08:53:26 -0700228 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700229
230 if (!this->done()) {
231 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
halcanary33779752015-10-27 14:01:05 -0700232 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
fmalita3c196de2014-09-20 05:40:22 -0700233 }
fmalita00d5c2c2014-08-21 08:53:26 -0700234}
235
Florin Malitaab54e732018-07-27 09:47:15 -0400236SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const {
fmalita00d5c2c2014-08-21 08:53:26 -0700237 SkASSERT(!this->done());
Florin Malitaab54e732018-07-27 09:47:15 -0400238 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) ==
239 kDefault_Positioning, "");
240 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) ==
241 kHorizontal_Positioning, "");
242 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) ==
243 kFull_Positioning, "");
Mike Reed30cf62b2018-12-20 11:18:24 -0500244 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) ==
245 kRSXform_Positioning, "");
Florin Malitaab54e732018-07-27 09:47:15 -0400246
247 return SkTo<GlyphPositioning>(fCurrentRun->positioning());
fmalita00d5c2c2014-08-21 08:53:26 -0700248}
249
Ben Wagnere5736262021-02-08 16:52:08 -0500250unsigned SkTextBlobRunIterator::scalarsPerGlyph() const {
251 return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning());
252}
253
halcanary33779752015-10-27 14:01:05 -0700254bool SkTextBlobRunIterator::isLCD() const {
Mike Reedb3f4aac2018-12-05 15:40:29 -0500255 return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias;
joshualitt9e36c1a2015-04-14 12:17:27 -0700256}
257
fmalita3c196de2014-09-20 05:40:22 -0700258SkTextBlobBuilder::SkTextBlobBuilder()
259 : fStorageSize(0)
260 , fStorageUsed(0)
261 , fRunCount(0)
262 , fDeferredBounds(false)
263 , fLastRun(0) {
fmalita00d5c2c2014-08-21 08:53:26 -0700264 fBounds.setEmpty();
265}
266
267SkTextBlobBuilder::~SkTextBlobBuilder() {
halcanary96fcdcc2015-08-27 07:41:13 -0700268 if (nullptr != fStorage.get()) {
fmalita3c196de2014-09-20 05:40:22 -0700269 // We are abandoning runs and must destruct the associated font data.
270 // The easiest way to accomplish that is to use the blob destructor.
fmalita37283c22016-09-13 10:00:23 -0700271 this->make();
fmalita3c196de2014-09-20 05:40:22 -0700272 }
fmalita00d5c2c2014-08-21 08:53:26 -0700273}
274
Ben Wagner6c1c5382020-07-08 19:54:24 -0400275static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) {
276 return SkMatrix().setRSXform(xform).mapRect(rect);
277}
278
fmalita3dc40ac2015-01-28 10:56:06 -0800279SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
Mike Reedd1ca6722018-12-10 14:53:16 -0500280 const SkFont& font = run.font();
fmalita3dc40ac2015-01-28 10:56:06 -0800281 SkRect bounds;
fmalitab0b45d32015-10-09 14:46:28 -0700282
283 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
Mike Reedd1ca6722018-12-10 14:53:16 -0500284 font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
Ben Wagner51e15a62019-05-07 15:38:46 -0400285 SkTextEncoding::kGlyphID, &bounds);
fmalitab0b45d32015-10-09 14:46:28 -0700286 return bounds.makeOffset(run.offset().x(), run.offset().y());
287 }
288
Herb Derbya183f792023-01-18 17:05:36 -0500289 AutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
Mike Reedd1ca6722018-12-10 14:53:16 -0500290 font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
fmalitab0b45d32015-10-09 14:46:28 -0700291
Ben Wagner6c1c5382020-07-08 19:54:24 -0400292 if (SkTextBlob::kRSXform_Positioning == run.positioning()) {
293 bounds.setEmpty();
294 const SkRSXform* xform = run.xformBuffer();
295 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
296 for (unsigned i = 0; i < run.glyphCount(); ++i) {
297 bounds.join(map_quad_to_rect(xform[i], glyphBounds[i]));
298 }
299 } else {
300 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
301 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 const SkScalar horizontalConstY = 0;
306 const SkScalar* glyphPosX = run.posBuffer();
307 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
308 glyphPosX + 1 : &horizontalConstY;
309 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
310 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
311 posXInc : 0;
fmalitab0b45d32015-10-09 14:46:28 -0700312
Ben Wagner6c1c5382020-07-08 19:54:24 -0400313 bounds.setEmpty();
314 for (unsigned i = 0; i < run.glyphCount(); ++i) {
315 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
316 glyphPosX += posXInc;
317 glyphPosY += posYInc;
318 }
319
320 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
fmalitab0b45d32015-10-09 14:46:28 -0700321 }
fmalita3dc40ac2015-01-28 10:56:06 -0800322 return bounds.makeOffset(run.offset().x(), run.offset().y());
323}
324
325SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
fmalita3dc40ac2015-01-28 10:56:06 -0800326 SkASSERT(run.glyphCount() > 0);
fmalitaf9a40722015-01-29 12:24:24 -0800327 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
Mike Reed30cf62b2018-12-20 11:18:24 -0500328 SkTextBlob::kHorizontal_Positioning == run.positioning() ||
329 SkTextBlob::kRSXform_Positioning == run.positioning());
fmalita3dc40ac2015-01-28 10:56:06 -0800330
Mike Reedb3f4aac2018-12-05 15:40:29 -0500331 const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font());
fmalitab0b45d32015-10-09 14:46:28 -0700332 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 return TightRunBounds(run);
336 }
337
338 // Compute the glyph position bbox.
fmalitaf9a40722015-01-29 12:24:24 -0800339 SkRect bounds;
340 switch (run.positioning()) {
341 case SkTextBlob::kHorizontal_Positioning: {
342 const SkScalar* glyphPos = run.posBuffer();
343 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
344
345 SkScalar minX = *glyphPos;
346 SkScalar maxX = *glyphPos;
347 for (unsigned i = 1; i < run.glyphCount(); ++i) {
348 SkScalar x = glyphPos[i];
Brian Osman116b33e2020-02-05 13:34:09 -0500349 minX = std::min(x, minX);
350 maxX = std::max(x, maxX);
fmalitaf9a40722015-01-29 12:24:24 -0800351 }
352
353 bounds.setLTRB(minX, 0, maxX, 0);
354 } break;
355 case SkTextBlob::kFull_Positioning: {
Mike Reed22451cc2019-01-01 15:40:28 -0500356 const SkPoint* glyphPosPts = run.pointBuffer();
fmalitaf9a40722015-01-29 12:24:24 -0800357 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
358
359 bounds.setBounds(glyphPosPts, run.glyphCount());
360 } break;
Mike Reed30cf62b2018-12-20 11:18:24 -0500361 case SkTextBlob::kRSXform_Positioning: {
Mike Reed22451cc2019-01-01 15:40:28 -0500362 const SkRSXform* xform = run.xformBuffer();
Mike Reed30cf62b2018-12-20 11:18:24 -0500363 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
Ben Wagner6c1c5382020-07-08 19:54:24 -0400364 bounds.setEmpty();
365 for (unsigned i = 0; i < run.glyphCount(); ++i) {
Mike Reed30cf62b2018-12-20 11:18:24 -0500366 bounds.join(map_quad_to_rect(xform[i], fontBounds));
367 }
368 } break;
fmalitaf9a40722015-01-29 12:24:24 -0800369 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400370 SK_ABORT("unsupported positioning mode");
fmalita3dc40ac2015-01-28 10:56:06 -0800371 }
372
Mike Reed30cf62b2018-12-20 11:18:24 -0500373 if (run.positioning() != SkTextBlob::kRSXform_Positioning) {
374 // Expand by typeface glyph bounds.
375 bounds.fLeft += fontBounds.left();
376 bounds.fTop += fontBounds.top();
377 bounds.fRight += fontBounds.right();
378 bounds.fBottom += fontBounds.bottom();
379 }
fmalita3dc40ac2015-01-28 10:56:06 -0800380
381 // Offset by run position.
382 return bounds.makeOffset(run.offset().x(), run.offset().y());
383}
384
fmalita00d5c2c2014-08-21 08:53:26 -0700385void SkTextBlobBuilder::updateDeferredBounds() {
fmalita3c196de2014-09-20 05:40:22 -0700386 SkASSERT(!fDeferredBounds || fRunCount > 0);
fmalita00d5c2c2014-08-21 08:53:26 -0700387
388 if (!fDeferredBounds) {
389 return;
390 }
391
Mike Klein93ce79d2018-08-10 12:41:57 +0000392 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
fmalita19653d12014-10-16 11:53:30 -0700393 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
394 fLastRun);
fmalita19653d12014-10-16 11:53:30 -0700395
fmalitaf9a40722015-01-29 12:24:24 -0800396 // FIXME: we should also use conservative bounds for kDefault_Positioning.
397 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
398 TightRunBounds(*run) : ConservativeRunBounds(*run);
fmalita19653d12014-10-16 11:53:30 -0700399 fBounds.join(runBounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700400 fDeferredBounds = false;
401}
402
fmalita3c196de2014-09-20 05:40:22 -0700403void SkTextBlobBuilder::reserve(size_t size) {
Florin Malitad923a712017-11-22 10:11:12 -0500404 SkSafeMath safe;
405
fmalita3c196de2014-09-20 05:40:22 -0700406 // We don't currently pre-allocate, but maybe someday...
Florin Malitad923a712017-11-22 10:11:12 -0500407 if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
fmalita3c196de2014-09-20 05:40:22 -0700408 return;
409 }
fmalita00d5c2c2014-08-21 08:53:26 -0700410
fmalita3c196de2014-09-20 05:40:22 -0700411 if (0 == fRunCount) {
halcanary96fcdcc2015-08-27 07:41:13 -0700412 SkASSERT(nullptr == fStorage.get());
fmalita3c196de2014-09-20 05:40:22 -0700413 SkASSERT(0 == fStorageSize);
414 SkASSERT(0 == fStorageUsed);
415
416 // the first allocation also includes blob storage
Mike Klein93ce79d2018-08-10 12:41:57 +0000417 // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
418 fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
fmalita3c196de2014-09-20 05:40:22 -0700419 }
420
Florin Malitad923a712017-11-22 10:11:12 -0500421 fStorageSize = safe.add(fStorageUsed, size);
422
fmalita3c196de2014-09-20 05:40:22 -0700423 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
Florin Malitad923a712017-11-22 10:11:12 -0500424 // Also, this is counting on the underlying realloc to throw when passed max().
425 fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
fmalita3c196de2014-09-20 05:40:22 -0700426}
427
Mike Reedb3f4aac2018-12-05 15:40:29 -0500428bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
Florin Malitad923a712017-11-22 10:11:12 -0500429 uint32_t count, SkPoint offset) {
fmalita3c196de2014-09-20 05:40:22 -0700430 if (0 == fLastRun) {
431 SkASSERT(0 == fRunCount);
432 return false;
433 }
434
Mike Klein93ce79d2018-08-10 12:41:57 +0000435 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
fmalita3c196de2014-09-20 05:40:22 -0700436 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
437 fLastRun);
438 SkASSERT(run->glyphCount() > 0);
439
halcanary4f0a23a2016-08-30 11:58:33 -0700440 if (run->textSize() != 0) {
441 return false;
442 }
443
fmalita3c196de2014-09-20 05:40:22 -0700444 if (run->positioning() != positioning
445 || run->font() != font
446 || (run->glyphCount() + count < run->glyphCount())) {
447 return false;
fmalita00d5c2c2014-08-21 08:53:26 -0700448 }
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
fmalita3c196de2014-09-20 05:40:22 -0700454 if (SkTextBlob::kFull_Positioning != positioning
455 && (SkTextBlob::kHorizontal_Positioning != positioning
456 || run->offset().y() != offset.y())) {
457 return false;
fmalita00d5c2c2014-08-21 08:53:26 -0700458 }
459
Florin Malitad923a712017-11-22 10:11:12 -0500460 SkSafeMath safe;
461 size_t sizeDelta =
462 SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
463 SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe);
464 if (!safe) {
465 return false;
466 }
467
fmalita3c196de2014-09-20 05:40:22 -0700468 this->reserve(sizeDelta);
fmalita00d5c2c2014-08-21 08:53:26 -0700469
fmalita3c196de2014-09-20 05:40:22 -0700470 // reserve may have realloced
471 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
472 uint32_t preMergeCount = run->glyphCount();
473 run->grow(count);
474
475 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
476 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
477 fCurrentRunBuffer.pos = run->posBuffer()
478 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
479
480 fStorageUsed += sizeDelta;
481
482 SkASSERT(fStorageUsed <= fStorageSize);
483 run->validate(fStorage.get() + fStorageUsed);
484
485 return true;
fmalita00d5c2c2014-08-21 08:53:26 -0700486}
487
Mike Reedb3f4aac2018-12-05 15:40:29 -0500488void SkTextBlobBuilder::allocInternal(const SkFont& font,
fmalita00d5c2c2014-08-21 08:53:26 -0700489 SkTextBlob::GlyphPositioning positioning,
Florin Malita1e18aa62017-11-19 10:22:22 -0500490 int count, int textSize, SkPoint offset,
491 const SkRect* bounds) {
Mike Reedb3f4aac2018-12-05 15:40:29 -0500492 if (count <= 0 || textSize < 0) {
Florin Malita1e18aa62017-11-19 10:22:22 -0500493 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
494 return;
495 }
496
halcanary4f0a23a2016-08-30 11:58:33 -0700497 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
fmalita19653d12014-10-16 11:53:30 -0700498 this->updateDeferredBounds();
fmalita00d5c2c2014-08-21 08:53:26 -0700499
Florin Malitad923a712017-11-22 10:11:12 -0500500 SkSafeMath safe;
501 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
502 if (!safe) {
503 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
504 return;
505 }
506
fmalita3c196de2014-09-20 05:40:22 -0700507 this->reserve(runSize);
fmalita00d5c2c2014-08-21 08:53:26 -0700508
Mike Klein93ce79d2018-08-10 12:41:57 +0000509 SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob)));
fmalita3c196de2014-09-20 05:40:22 -0700510 SkASSERT(fStorageUsed + runSize <= fStorageSize);
fmalita00d5c2c2014-08-21 08:53:26 -0700511
fmalita3c196de2014-09-20 05:40:22 -0700512 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
halcanary4f0a23a2016-08-30 11:58:33 -0700513 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
fmalita3c196de2014-09-20 05:40:22 -0700514 fCurrentRunBuffer.glyphs = run->glyphBuffer();
515 fCurrentRunBuffer.pos = run->posBuffer();
halcanary4f0a23a2016-08-30 11:58:33 -0700516 fCurrentRunBuffer.utf8text = run->textBuffer();
517 fCurrentRunBuffer.clusters = run->clusterBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700518
fmalita3c196de2014-09-20 05:40:22 -0700519 fLastRun = fStorageUsed;
520 fStorageUsed += runSize;
521 fRunCount++;
522
523 SkASSERT(fStorageUsed <= fStorageSize);
524 run->validate(fStorage.get() + fStorageUsed);
525 }
halcanary4f0a23a2016-08-30 11:58:33 -0700526 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
527 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
fmalita00d5c2c2014-08-21 08:53:26 -0700528 if (!fDeferredBounds) {
bsalomon49f085d2014-09-05 13:34:00 -0700529 if (bounds) {
fmalita00d5c2c2014-08-21 08:53:26 -0700530 fBounds.join(*bounds);
531 } else {
532 fDeferredBounds = true;
533 }
534 }
535}
536
Mike Reed3185f902018-10-26 16:33:00 -0400537// SkFont versions
538
539const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count,
540 SkScalar x, SkScalar y,
541 const SkRect* bounds) {
Mike Reedb3f4aac2018-12-05 15:40:29 -0500542 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds);
543 return fCurrentRunBuffer;
Mike Reed3185f902018-10-26 16:33:00 -0400544}
545
546const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count,
547 SkScalar y,
548 const SkRect* bounds) {
Mike Reedb3f4aac2018-12-05 15:40:29 -0500549 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds);
550 return fCurrentRunBuffer;
Mike Reed3185f902018-10-26 16:33:00 -0400551}
552
553const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count,
554 const SkRect* bounds) {
Mike Reedb3f4aac2018-12-05 15:40:29 -0500555 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds);
556 return fCurrentRunBuffer;
Mike Reed3185f902018-10-26 16:33:00 -0400557}
558
Mike Reed30cf62b2018-12-20 11:18:24 -0500559const SkTextBlobBuilder::RunBuffer&
560SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) {
561 this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr);
562 return fCurrentRunBuffer;
563}
564
Mike Reed6d595682018-12-05 17:28:14 -0500565const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count,
halcanary4f0a23a2016-08-30 11:58:33 -0700566 SkScalar x, SkScalar y,
567 int textByteCount,
halcanary4f0a23a2016-08-30 11:58:33 -0700568 const SkRect* bounds) {
Herb Derbyb3043e42019-05-29 10:59:38 -0400569 this->allocInternal(font,
570 SkTextBlob::kDefault_Positioning,
571 count,
572 textByteCount,
573 SkPoint::Make(x, y),
574 bounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700575 return fCurrentRunBuffer;
576}
577
Ben Wagner0baacb52021-02-24 10:58:15 -0500578const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font,
579 int count,
halcanary4f0a23a2016-08-30 11:58:33 -0700580 SkScalar y,
581 int textByteCount,
halcanary4f0a23a2016-08-30 11:58:33 -0700582 const SkRect* bounds) {
Herb Derbyb3043e42019-05-29 10:59:38 -0400583 this->allocInternal(font,
584 SkTextBlob::kHorizontal_Positioning,
585 count,
586 textByteCount,
587 SkPoint::Make(0, y),
fmalita00d5c2c2014-08-21 08:53:26 -0700588 bounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700589 return fCurrentRunBuffer;
590}
591
Ben Wagner0baacb52021-02-24 10:58:15 -0500592const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font,
593 int count,
halcanary4f0a23a2016-08-30 11:58:33 -0700594 int textByteCount,
halcanary4f0a23a2016-08-30 11:58:33 -0700595 const SkRect *bounds) {
Herb Derbyb3043e42019-05-29 10:59:38 -0400596 this->allocInternal(font,
597 SkTextBlob::kFull_Positioning,
598 count, textByteCount,
599 SkPoint::Make(0, 0),
600 bounds);
fmalita00d5c2c2014-08-21 08:53:26 -0700601 return fCurrentRunBuffer;
602}
603
Ben Wagner5d9c20e2021-02-24 11:43:07 -0500604const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextRSXform(const SkFont& font,
605 int count,
606 int textByteCount,
607 const SkRect *bounds) {
Herb Derbyb3043e42019-05-29 10:59:38 -0400608 this->allocInternal(font,
609 SkTextBlob::kRSXform_Positioning,
610 count,
611 textByteCount,
612 {0, 0},
613 bounds);
Mike Reed30cf62b2018-12-20 11:18:24 -0500614 return fCurrentRunBuffer;
615}
616
reed2ab90572016-08-10 14:16:41 -0700617sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
Florin Malita3a9a7a32017-03-13 09:03:24 -0400618 if (!fRunCount) {
619 // We don't instantiate empty blobs.
620 SkASSERT(!fStorage.get());
621 SkASSERT(fStorageUsed == 0);
622 SkASSERT(fStorageSize == 0);
623 SkASSERT(fLastRun == 0);
624 SkASSERT(fBounds.isEmpty());
625 return nullptr;
626 }
fmalita00d5c2c2014-08-21 08:53:26 -0700627
fmalita3c196de2014-09-20 05:40:22 -0700628 this->updateDeferredBounds();
fmalita00d5c2c2014-08-21 08:53:26 -0700629
Florin Malita3a9a7a32017-03-13 09:03:24 -0400630 // Tag the last run as such.
631 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
632 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
fmalita00d5c2c2014-08-21 08:53:26 -0700633
Florin Malita3a9a7a32017-03-13 09:03:24 -0400634 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
fmalita92d976c2015-10-05 11:09:57 -0700635 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
636
fmalita3c196de2014-09-20 05:40:22 -0700637 SkDEBUGCODE(
Florin Malitad923a712017-11-22 10:11:12 -0500638 SkSafeMath safe;
Mike Klein93ce79d2018-08-10 12:41:57 +0000639 size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
Florin Malita3a9a7a32017-03-13 09:03:24 -0400640 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
641 run = SkTextBlob::RunRecord::Next(run)) {
halcanary4f0a23a2016-08-30 11:58:33 -0700642 validateSize += SkTextBlob::RunRecord::StorageSize(
Florin Malitad923a712017-11-22 10:11:12 -0500643 run->fCount, run->textSize(), run->positioning(), &safe);
fmalita92d976c2015-10-05 11:09:57 -0700644 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400645 fRunCount--;
fmalita3c196de2014-09-20 05:40:22 -0700646 }
647 SkASSERT(validateSize == fStorageUsed);
Florin Malita3a9a7a32017-03-13 09:03:24 -0400648 SkASSERT(fRunCount == 0);
Florin Malitad923a712017-11-22 10:11:12 -0500649 SkASSERT(safe);
fmalita3c196de2014-09-20 05:40:22 -0700650 )
651
fmalita3c196de2014-09-20 05:40:22 -0700652 fStorageUsed = 0;
653 fStorageSize = 0;
654 fRunCount = 0;
655 fLastRun = 0;
656 fBounds.setEmpty();
657
reed2ab90572016-08-10 14:16:41 -0700658 return sk_sp<SkTextBlob>(blob);
fmalita00d5c2c2014-08-21 08:53:26 -0700659}
Mike Reedb99bedd2017-07-11 10:27:40 -0400660
661///////////////////////////////////////////////////////////////////////////////////////////////////
Mike Reedcfb01202018-12-11 15:09:04 -0500662
Cary Clark53c87692018-07-17 08:59:34 -0400663void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) {
Mike Reed0951cc22018-12-21 16:19:24 -0500664 // seems like we could skip this, and just recompute bounds in unflatten, but
665 // some cc_unittests fail if we remove this...
666 buffer.writeRect(blob.bounds());
Mike Reedf4dd96c2018-12-21 20:49:47 +0000667
Cary Clark53c87692018-07-17 08:59:34 -0400668 SkTextBlobRunIterator it(&blob);
Mike Reedb99bedd2017-07-11 10:27:40 -0400669 while (!it.done()) {
670 SkASSERT(it.glyphCount() > 0);
671
672 buffer.write32(it.glyphCount());
673 PositioningAndExtended pe;
674 pe.intValue = 0;
675 pe.positioning = it.positioning();
676 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
677
678 uint32_t textSize = it.textSize();
679 pe.extended = textSize > 0;
680 buffer.write32(pe.intValue);
681 if (pe.extended) {
682 buffer.write32(textSize);
683 }
684 buffer.writePoint(it.offset());
Mike Reed0951cc22018-12-21 16:19:24 -0500685
686 SkFontPriv::Flatten(it.font(), buffer);
Mike Reedb99bedd2017-07-11 10:27:40 -0400687
688 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
689 buffer.writeByteArray(it.pos(),
Cary Clark53c87692018-07-17 08:59:34 -0400690 it.glyphCount() * sizeof(SkScalar) *
Florin Malitaab54e732018-07-27 09:47:15 -0400691 SkTextBlob::ScalarsPerGlyph(
692 SkTo<SkTextBlob::GlyphPositioning>(it.positioning())));
Mike Reedb99bedd2017-07-11 10:27:40 -0400693 if (pe.extended) {
694 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
695 buffer.writeByteArray(it.text(), it.textSize());
696 }
697
698 it.next();
699 }
700
701 // Marker for the last run (0 is not a valid glyph count).
702 buffer.write32(0);
703}
704
Cary Clark53c87692018-07-17 08:59:34 -0400705sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) {
Mike Reedf4dd96c2018-12-21 20:49:47 +0000706 SkRect bounds;
707 reader.readRect(&bounds);
Mike Reedb99bedd2017-07-11 10:27:40 -0400708
709 SkTextBlobBuilder blobBuilder;
Khushalcea8e3d2018-05-07 13:57:43 -0700710 SkSafeMath safe;
Mike Reedc5166a92018-01-03 13:17:18 -0500711 for (;;) {
Mike Reedb99bedd2017-07-11 10:27:40 -0400712 int glyphCount = reader.read32();
Mike Reedc5166a92018-01-03 13:17:18 -0500713 if (glyphCount == 0) {
Mike Reedb99bedd2017-07-11 10:27:40 -0400714 // End-of-runs marker.
715 break;
716 }
717
718 PositioningAndExtended pe;
719 pe.intValue = reader.read32();
Florin Malitaab54e732018-07-27 09:47:15 -0400720 const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning);
Mike Reed30cf62b2018-12-20 11:18:24 -0500721 if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) {
Mike Reedb99bedd2017-07-11 10:27:40 -0400722 return nullptr;
723 }
Florin Malita1e18aa62017-11-19 10:22:22 -0500724 int textSize = pe.extended ? reader.read32() : 0;
Khushalcea8e3d2018-05-07 13:57:43 -0700725 if (textSize < 0) {
Florin Malita1e18aa62017-11-19 10:22:22 -0500726 return nullptr;
727 }
Mike Reedb99bedd2017-07-11 10:27:40 -0400728
729 SkPoint offset;
730 reader.readPoint(&offset);
Mike Reed0951cc22018-12-21 16:19:24 -0500731 SkFont font;
Mike Reedc4777862020-07-15 06:42:28 -0400732 SkFontPriv::Unflatten(&font, reader);
Mike Reedb99bedd2017-07-11 10:27:40 -0400733
Khushalcea8e3d2018-05-07 13:57:43 -0700734 // Compute the expected size of the buffer and ensure we have enough to deserialize
735 // a run before allocating it.
736 const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)),
737 posSize =
Cary Clark53c87692018-07-17 08:59:34 -0400738 safe.mul(glyphCount, safe.mul(sizeof(SkScalar),
739 SkTextBlob::ScalarsPerGlyph(pos))),
Khushalcea8e3d2018-05-07 13:57:43 -0700740 clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0;
741 const size_t totalSize =
742 safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize));
743
744 if (!reader.isValid() || !safe || totalSize > reader.available()) {
Florin Malita1e18aa62017-11-19 10:22:22 -0500745 return nullptr;
746 }
747
Mike Reedb99bedd2017-07-11 10:27:40 -0400748 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
749 switch (pos) {
Cary Clark53c87692018-07-17 08:59:34 -0400750 case SkTextBlob::kDefault_Positioning:
Mike Reedb99bedd2017-07-11 10:27:40 -0400751 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
Ben Wagner0baacb52021-02-24 10:58:15 -0500752 textSize, &bounds);
Mike Reedb99bedd2017-07-11 10:27:40 -0400753 break;
Cary Clark53c87692018-07-17 08:59:34 -0400754 case SkTextBlob::kHorizontal_Positioning:
Mike Reedb99bedd2017-07-11 10:27:40 -0400755 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
Ben Wagner0baacb52021-02-24 10:58:15 -0500756 textSize, &bounds);
Mike Reedb99bedd2017-07-11 10:27:40 -0400757 break;
Cary Clark53c87692018-07-17 08:59:34 -0400758 case SkTextBlob::kFull_Positioning:
Ben Wagner0baacb52021-02-24 10:58:15 -0500759 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, &bounds);
Mike Reedb99bedd2017-07-11 10:27:40 -0400760 break;
Mike Reed30cf62b2018-12-20 11:18:24 -0500761 case SkTextBlob::kRSXform_Positioning:
Ben Wagner5d9c20e2021-02-24 11:43:07 -0500762 buf = &blobBuilder.allocRunTextRSXform(font, glyphCount, textSize, &bounds);
Mike Reed30cf62b2018-12-20 11:18:24 -0500763 break;
Mike Reedb99bedd2017-07-11 10:27:40 -0400764 }
765
Florin Malita90dcafc2017-11-22 10:53:33 -0500766 if (!buf->glyphs ||
767 !buf->pos ||
768 (pe.extended && (!buf->clusters || !buf->utf8text))) {
769 return nullptr;
770 }
771
Khushalcea8e3d2018-05-07 13:57:43 -0700772 if (!reader.readByteArray(buf->glyphs, glyphSize) ||
773 !reader.readByteArray(buf->pos, posSize)) {
774 return nullptr;
Mike Reedb99bedd2017-07-11 10:27:40 -0400775 }
776
777 if (pe.extended) {
Khushalcea8e3d2018-05-07 13:57:43 -0700778 if (!reader.readByteArray(buf->clusters, clusterSize) ||
Mike Reedb99bedd2017-07-11 10:27:40 -0400779 !reader.readByteArray(buf->utf8text, textSize)) {
780 return nullptr;
781 }
782 }
783 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400784
Mike Reedb99bedd2017-07-11 10:27:40 -0400785 return blobBuilder.make();
786}
787
Mike Reed3185f902018-10-26 16:33:00 -0400788sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font,
Mike Reed97f3cc22018-12-03 09:45:17 -0500789 SkTextEncoding encoding) {
Mike Reedefb518d2018-12-06 13:30:23 -0500790 // 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 const int count = font.countText(text, byteLength, encoding);
Ben Wagner5da8e552019-12-06 16:24:00 -0500793 if (count < 1) {
794 return nullptr;
795 }
Mike Reedefb518d2018-12-06 13:30:23 -0500796 SkTextBlobBuilder builder;
797 auto buffer = builder.allocRunPos(font, count);
798 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
Mike Reed22451cc2019-01-01 15:40:28 -0500799 font.getPos(buffer.glyphs, count, buffer.points(), {0, 0});
Mike Reedefb518d2018-12-06 13:30:23 -0500800 return builder.make();
801}
Mike Reed3185f902018-10-26 16:33:00 -0400802
Mike Reedefb518d2018-12-06 13:30:23 -0500803sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength,
804 const SkPoint pos[], const SkFont& font,
805 SkTextEncoding encoding) {
806 const int count = font.countText(text, byteLength, encoding);
Ben Wagner5da8e552019-12-06 16:24:00 -0500807 if (count < 1) {
808 return nullptr;
809 }
Mike Reedefb518d2018-12-06 13:30:23 -0500810 SkTextBlobBuilder builder;
811 auto buffer = builder.allocRunPos(font, count);
812 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
Mike Reed22451cc2019-01-01 15:40:28 -0500813 memcpy(buffer.points(), pos, count * sizeof(SkPoint));
Mike Reedefb518d2018-12-06 13:30:23 -0500814 return builder.make();
815}
Herb Derby4b3a5152018-07-17 16:10:30 -0400816
Mike Reedefb518d2018-12-06 13:30:23 -0500817sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength,
818 const SkScalar xpos[], SkScalar constY,
819 const SkFont& font, SkTextEncoding encoding) {
820 const int count = font.countText(text, byteLength, encoding);
Ben Wagner5da8e552019-12-06 16:24:00 -0500821 if (count < 1) {
822 return nullptr;
823 }
Mike Reedefb518d2018-12-06 13:30:23 -0500824 SkTextBlobBuilder builder;
825 auto buffer = builder.allocRunPosH(font, count, constY);
826 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
827 memcpy(buffer.pos, xpos, count * sizeof(SkScalar));
828 return builder.make();
Herb Derby4b3a5152018-07-17 16:10:30 -0400829}
830
Mike Reed30cf62b2018-12-20 11:18:24 -0500831sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength,
832 const SkRSXform xform[], const SkFont& font,
833 SkTextEncoding encoding) {
834 const int count = font.countText(text, byteLength, encoding);
Ben Wagner5da8e552019-12-06 16:24:00 -0500835 if (count < 1) {
836 return nullptr;
837 }
Mike Reed30cf62b2018-12-20 11:18:24 -0500838 SkTextBlobBuilder builder;
839 auto buffer = builder.allocRunRSXform(font, count);
840 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
Mike Reed22451cc2019-01-01 15:40:28 -0500841 memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform));
Mike Reed30cf62b2018-12-20 11:18:24 -0500842 return builder.make();
843}
844
Mike Reed8e74cbc2017-12-08 13:20:01 -0500845sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const {
Kevin Lubicka2d4f1e2023-09-15 11:21:18 -0400846 SkBinaryWriteBuffer buffer(procs);
Cary Clark53c87692018-07-17 08:59:34 -0400847 SkTextBlobPriv::Flatten(*this, buffer);
Mike Reedb99bedd2017-07-11 10:27:40 -0400848
849 size_t total = buffer.bytesWritten();
850 sk_sp<SkData> data = SkData::MakeUninitialized(total);
851 buffer.writeToMemory(data->writable_data());
852 return data;
853}
854
Mike Reed8e74cbc2017-12-08 13:20:01 -0500855sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length,
856 const SkDeserialProcs& procs) {
857 SkReadBuffer buffer(data, length);
858 buffer.setDeserialProcs(procs);
Cary Clark53c87692018-07-17 08:59:34 -0400859 return SkTextBlobPriv::MakeFromBuffer(buffer);
Mike Reedf6eb1f92017-12-19 17:12:13 -0500860}
861
Mike Reed8e74cbc2017-12-08 13:20:01 -0500862///////////////////////////////////////////////////////////////////////////////////////////////////
863
Khushal42f8bc42018-04-03 17:51:40 -0700864size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const {
Kevin Lubicka2d4f1e2023-09-15 11:21:18 -0400865 SkBinaryWriteBuffer buffer(memory, memory_size, procs);
Cary Clark53c87692018-07-17 08:59:34 -0400866 SkTextBlobPriv::Flatten(*this, buffer);
Khushal42f8bc42018-04-03 17:51:40 -0700867 return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u;
868}
Mike Reedd3bde0b2019-01-29 10:41:54 -0500869
Herb Derbyb3043e42019-05-29 10:59:38 -0400870///////////////////////////////////////////////////////////////////////////////////////////////////
Mike Reedd3bde0b2019-01-29 10:41:54 -0500871
Herb Derbyb3043e42019-05-29 10:59:38 -0400872namespace {
Herb Derby1aea9032022-06-14 14:59:21 -0400873int get_glyph_run_intercepts(const sktext::GlyphRun& glyphRun,
Herb Derby7924d9a2019-06-19 17:57:04 -0400874 const SkPaint& paint,
875 const SkScalar bounds[2],
876 SkScalar intervals[],
877 int* intervalCount) {
878 SkScalar scale = SK_Scalar1;
879 SkPaint interceptPaint{paint};
880 SkFont interceptFont{glyphRun.font()};
Mike Reedd3bde0b2019-01-29 10:41:54 -0500881
Herb Derby7924d9a2019-06-19 17:57:04 -0400882 interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
Mike Reedd3bde0b2019-01-29 10:41:54 -0500883
Herb Derby7924d9a2019-06-19 17:57:04 -0400884 // can't use our canonical size if we need to apply path effects
885 if (interceptPaint.getPathEffect() == nullptr) {
Ben Wagnerf460eee2019-04-18 16:37:45 -0400886 // If the wrong size is going to be used, don't hint anything.
Herb Derby7924d9a2019-06-19 17:57:04 -0400887 interceptFont.setHinting(SkFontHinting::kNone);
888 interceptFont.setSubpixel(true);
889 scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
890 interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
Mike Reedd3bde0b2019-01-29 10:41:54 -0500891 // 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.
Herb Derby7924d9a2019-06-19 17:57:04 -0400895 if (interceptPaint.getStrokeWidth() > 0
896 && interceptPaint.getStyle() != SkPaint::kFill_Style) {
897 interceptPaint.setStrokeWidth(
898 sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
Mike Reedd3bde0b2019-01-29 10:41:54 -0500899 }
Mike Reedd3bde0b2019-01-29 10:41:54 -0500900 }
901
Herb Derby7924d9a2019-06-19 17:57:04 -0400902 interceptPaint.setStyle(SkPaint::kFill_Style);
903 interceptPaint.setPathEffect(nullptr);
Mike Reedd3bde0b2019-01-29 10:41:54 -0500904
Herb Derby7924d9a2019-06-19 17:57:04 -0400905 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
Herb Derbycb443a52019-11-11 18:01:36 -0500906 SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
Mike Reedd3bde0b2019-01-29 10:41:54 -0500907
Herb Derby7924d9a2019-06-19 17:57:04 -0400908 const SkPoint* posCursor = glyphRun.positions().begin();
Herb Derbycb443a52019-11-11 18:01:36 -0500909 for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
Herb Derby7924d9a2019-06-19 17:57:04 -0400910 SkPoint pos = *posCursor++;
Mike Reedd3bde0b2019-01-29 10:41:54 -0500911
Herb Derbycb443a52019-11-11 18:01:36 -0500912 if (glyph->path() != nullptr) {
Lee Salzman634d1502019-10-15 13:53:09 -0400913 // 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 SkScalar scaledBounds[2] = {
916 (bounds[0] - pos.y()) / scale,
917 (bounds[1] - pos.y()) / scale
918 };
Herb Derbycb443a52019-11-11 18:01:36 -0500919 metricsAndPaths.findIntercepts(
920 scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
Herb Derbyb3043e42019-05-29 10:59:38 -0400921 }
922 }
Herb Derby7924d9a2019-06-19 17:57:04 -0400923 return *intervalCount;
Herb Derbyb3043e42019-05-29 10:59:38 -0400924}
Herb Derbyb3043e42019-05-29 10:59:38 -0400925} // namespace
926
927int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
928 const SkPaint* paint) const {
Herb Derby7924d9a2019-06-19 17:57:04 -0400929 SkTLazy<SkPaint> defaultPaint;
930 if (paint == nullptr) {
931 defaultPaint.init();
932 paint = defaultPaint.get();
Herb Derbyb3043e42019-05-29 10:59:38 -0400933 }
934
Herb Derby1aea9032022-06-14 14:59:21 -0400935 sktext::GlyphRunBuilder builder;
Herb Derby97abbee2021-04-09 15:06:21 -0400936 auto glyphRunList = builder.blobToGlyphRunList(*this, {0, 0});
Herb Derby7924d9a2019-06-19 17:57:04 -0400937
938 int intervalCount = 0;
Herb Derby1aea9032022-06-14 14:59:21 -0400939 for (const sktext::GlyphRun& glyphRun : glyphRunList) {
Herb Derby97abbee2021-04-09 15:06:21 -0400940 // Ignore RSXForm runs.
941 if (glyphRun.scaledRotations().empty()) {
942 intervalCount = get_glyph_run_intercepts(
943 glyphRun, *paint, bounds, intervals, &intervalCount);
944 }
Herb Derby7924d9a2019-06-19 17:57:04 -0400945 }
946
947 return intervalCount;
Herb Derbyb3043e42019-05-29 10:59:38 -0400948}
Mike Reeda32654c2019-10-04 10:18:34 -0400949
Mike Reed81e2f932021-05-14 09:53:30 -0400950std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count,
951 const SkPoint positions[],
952 SkScalar top, SkScalar bottom,
953 const SkPaint* paintPtr) const {
954 if (count <= 0) {
955 return std::vector<SkScalar>();
956 }
957
958 const SkPaint paint(paintPtr ? *paintPtr : SkPaint());
959 const SkScalar bounds[] = {top, bottom};
Herb Derby1aea9032022-06-14 14:59:21 -0400960 const sktext::GlyphRun run(*this,
Mike Reed81e2f932021-05-14 09:53:30 -0400961 {positions, size_t(count)}, {glyphs, size_t(count)},
962 {nullptr, 0}, {nullptr, 0}, {nullptr, 0});
963
964 std::vector<SkScalar> result;
965 result.resize(count * 2); // worst case allocation
966 int intervalCount = 0;
967 intervalCount = get_glyph_run_intercepts(run, paint, bounds, result.data(), &intervalCount);
968 result.resize(intervalCount);
969 return result;
970}
971
Mike Reeda32654c2019-10-04 10:18:34 -0400972////////
973
974SkTextBlob::Iter::Iter(const SkTextBlob& blob) {
975 fRunRecord = RunRecord::First(&blob);
976}
977
978bool SkTextBlob::Iter::next(Run* rec) {
979 if (fRunRecord) {
980 if (rec) {
981 rec->fTypeface = fRunRecord->font().getTypeface();
982 rec->fGlyphCount = fRunRecord->glyphCount();
983 rec->fGlyphIndices = fRunRecord->glyphBuffer();
Ben Wagnerf913c662021-03-12 15:20:18 -0500984#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
Mike Reeda32654c2019-10-04 10:18:34 -0400989 }
990 if (fRunRecord->isLastRun()) {
991 fRunRecord = nullptr;
992 } else {
993 fRunRecord = RunRecord::Next(fRunRecord);
994 }
995 return true;
996 }
997 return false;
998}
Mike Reedb4e08be2021-04-16 11:59:00 -0400999
1000bool SkTextBlob::Iter::experimentalNext(ExperimentalRun* rec) {
1001 if (fRunRecord) {
1002 if (rec) {
1003 rec->font = fRunRecord->font();
1004 rec->count = fRunRecord->glyphCount();
1005 rec->glyphs = fRunRecord->glyphBuffer();
1006 rec->positions = fRunRecord->pointBuffer();
1007 }
1008 if (fRunRecord->isLastRun()) {
1009 fRunRecord = nullptr;
1010 } else {
1011 fRunRecord = RunRecord::Next(fRunRecord);
1012 }
1013 return true;
1014 }
1015 return false;
1016}