GlyphRun.cpp (14618B)
1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "src/text/GlyphRun.h" 9 10 #include "include/core/SkFont.h" 11 #include "include/core/SkMatrix.h" 12 #include "include/core/SkRSXform.h" 13 #include "include/core/SkScalar.h" 14 #include "include/private/base/SkTLogic.h" 15 #include "src/core/SkFontPriv.h" 16 #include "src/core/SkGlyph.h" 17 #include "src/core/SkStrikeSpec.h" 18 #include "src/core/SkTextBlobPriv.h" 19 20 #include <cstring> 21 22 class SkPaint; 23 24 namespace sktext { 25 // -- GlyphRun ------------------------------------------------------------------------------------- 26 GlyphRun::GlyphRun(const SkFont& font, 27 SkSpan<const SkPoint> positions, 28 SkSpan<const SkGlyphID> glyphIDs, 29 SkSpan<const char> text, 30 SkSpan<const uint32_t> clusters, 31 SkSpan<const SkVector> scaledRotations) 32 : fSource{SkMakeZip(glyphIDs, positions)} 33 , fText{text} 34 , fClusters{clusters} 35 , fScaledRotations{scaledRotations} 36 , fFont{font} {} 37 38 GlyphRun::GlyphRun(const GlyphRun& that, const SkFont& font) 39 : fSource{that.fSource} 40 , fText{that.fText} 41 , fClusters{that.fClusters} 42 , fFont{font} {} 43 44 // -- GlyphRunList --------------------------------------------------------------------------------- 45 GlyphRunList::GlyphRunList(const SkTextBlob* blob, 46 SkRect bounds, 47 SkPoint origin, 48 SkSpan<const GlyphRun> glyphRunList, 49 GlyphRunBuilder* builder) 50 : fGlyphRuns{glyphRunList} 51 , fOriginalTextBlob{blob} 52 , fSourceBounds{bounds} 53 , fOrigin{origin} 54 , fBuilder{builder} {} 55 56 GlyphRunList::GlyphRunList(const GlyphRun& glyphRun, 57 const SkRect& bounds, 58 SkPoint origin, 59 GlyphRunBuilder* builder) 60 : fGlyphRuns{SkSpan<const GlyphRun>{&glyphRun, 1}} 61 , fOriginalTextBlob{nullptr} 62 , fSourceBounds{bounds} 63 , fOrigin{origin} 64 , fBuilder{builder} {} 65 66 uint64_t GlyphRunList::uniqueID() const { 67 return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID() 68 : SK_InvalidUniqueID; 69 } 70 71 bool GlyphRunList::anyRunsLCD() const { 72 for (const auto& r : fGlyphRuns) { 73 if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) { 74 return true; 75 } 76 } 77 return false; 78 } 79 80 void GlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID, 81 SkTextBlob::PurgeDelegate pd) const { 82 SkASSERT(fOriginalTextBlob != nullptr); 83 SkASSERT(pd != nullptr); 84 fOriginalTextBlob->notifyAddedToCache(cacheID, pd); 85 } 86 87 sk_sp<SkTextBlob> GlyphRunList::makeBlob() const { 88 SkTextBlobBuilder builder; 89 for (auto& run : *this) { 90 SkTextBlobBuilder::RunBuffer buffer; 91 if (run.scaledRotations().empty()) { 92 if (run.text().empty()) { 93 buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr); 94 } else { 95 buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr); 96 auto text = run.text(); 97 memcpy(buffer.utf8text, text.data(), text.size_bytes()); 98 auto clusters = run.clusters(); 99 memcpy(buffer.clusters, clusters.data(), clusters.size_bytes()); 100 } 101 auto positions = run.positions(); 102 memcpy(buffer.points(), positions.data(), positions.size_bytes()); 103 } else { 104 buffer = builder.allocRunRSXform(run.font(), run.runSize()); 105 for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(), 106 run.positions(), 107 run.scaledRotations())) { 108 xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y()); 109 } 110 } 111 auto glyphIDs = run.glyphsIDs(); 112 memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes()); 113 } 114 return builder.make(); 115 } 116 117 // -- GlyphRunBuilder ------------------------------------------------------------------------------ 118 static SkRect glyphrun_source_bounds( 119 const SkFont& font, 120 const SkPaint& paint, 121 SkZip<const SkGlyphID, const SkPoint> source, 122 SkSpan<const SkVector> scaledRotations) { 123 SkASSERT(!source.empty()); 124 const SkRect fontBounds = SkFontPriv::GetFontBounds(font); 125 126 SkSpan<const SkGlyphID> glyphIDs = source.get<0>(); 127 SkSpan<const SkPoint> positions = source.get<1>(); 128 129 if (fontBounds.isEmpty()) { 130 // Empty font bounds are likely a font bug. TightBounds has a better chance of 131 // producing useful results in this case. 132 auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(font, &paint); 133 SkBulkGlyphMetrics metrics{strikeSpec}; 134 SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs); 135 if (scaledRotations.empty()) { 136 // No RSXForm data - glyphs x/y aligned. 137 auto scaleAndTranslateRect = 138 [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) { 139 return SkRect::MakeLTRB(in.left() * scale + pos.x(), 140 in.top() * scale + pos.y(), 141 in.right() * scale + pos.x(), 142 in.bottom() * scale + pos.y()); 143 }; 144 145 SkRect bounds = SkRect::MakeEmpty(); 146 for (auto [pos, glyph] : SkMakeZip(positions, glyphs)) { 147 if (SkRect r = glyph->rect(); !r.isEmpty()) { 148 bounds.join(scaleAndTranslateRect(r, pos)); 149 } 150 } 151 return bounds; 152 } else { 153 // RSXForm - glyphs can be any scale or rotation. 154 SkRect bounds = SkRect::MakeEmpty(); 155 for (auto [pos, scaleRotate, glyph] : SkMakeZip(positions, scaledRotations, glyphs)) { 156 if (!glyph->rect().isEmpty()) { 157 SkMatrix xform = SkMatrix().setRSXform( 158 SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}); 159 xform.preScale(strikeToSourceScale, strikeToSourceScale); 160 bounds.join(xform.mapRect(glyph->rect())); 161 } 162 } 163 return bounds; 164 } 165 } 166 167 // Use conservative bounds. All glyph have a box of fontBounds size. 168 if (scaledRotations.empty()) { 169 SkRect bounds = SkRect::BoundsOrEmpty(positions); 170 bounds.fLeft += fontBounds.left(); 171 bounds.fTop += fontBounds.top(); 172 bounds.fRight += fontBounds.right(); 173 bounds.fBottom += fontBounds.bottom(); 174 return bounds; 175 } else { 176 // RSXForm case glyphs can be any scale or rotation. 177 SkRect bounds; 178 bounds.setEmpty(); 179 for (auto [pos, scaleRotate] : SkMakeZip(positions, scaledRotations)) { 180 const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}; 181 bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds)); 182 } 183 return bounds; 184 } 185 } 186 187 GlyphRunList GlyphRunBuilder::makeGlyphRunList( 188 const GlyphRun& run, const SkPaint& paint, SkPoint origin) { 189 const SkRect bounds = 190 glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations()); 191 return GlyphRunList{run, bounds, origin, this}; 192 } 193 194 static SkSpan<const SkPoint> draw_text_positions( 195 const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) { 196 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font); 197 SkBulkGlyphMetrics storage{strikeSpec}; 198 auto glyphs = storage.glyphs(glyphIDs); 199 200 SkPoint* positionCursor = buffer; 201 SkPoint endOfLastGlyph = origin; 202 for (auto glyph : glyphs) { 203 *positionCursor++ = endOfLastGlyph; 204 endOfLastGlyph += glyph->advanceVector(); 205 } 206 return SkSpan(buffer, glyphIDs.size()); 207 } 208 209 const GlyphRunList& GlyphRunBuilder::textToGlyphRunList( 210 const SkFont& font, const SkPaint& paint, 211 const void* bytes, size_t byteLength, SkPoint origin, 212 SkTextEncoding encoding) { 213 auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding); 214 SkRect bounds = SkRect::MakeEmpty(); 215 this->prepareBuffers(glyphIDs.size(), 0); 216 if (!glyphIDs.empty()) { 217 SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions); 218 this->makeGlyphRun(font, 219 glyphIDs, 220 positions, 221 SkSpan<const char>{}, 222 SkSpan<const uint32_t>{}, 223 SkSpan<const SkVector>{}); 224 auto run = fGlyphRunListStorage.front(); 225 bounds = glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations()); 226 } 227 228 return this->setGlyphRunList(nullptr, bounds, origin); 229 } 230 231 const GlyphRunList& sktext::GlyphRunBuilder::blobToGlyphRunList( 232 const SkTextBlob& blob, SkPoint origin) { 233 // Pre-size all the buffers, so they don't move during processing. 234 this->initialize(blob); 235 236 SkPoint* positionCursor = fPositions; 237 SkVector* scaledRotationsCursor = fScaledRotations; 238 for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { 239 size_t runSize = it.glyphCount(); 240 if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) { 241 // If no glyphs or the font is not finite, don't add the run. 242 continue; 243 } 244 245 const SkFont& font = it.font(); 246 auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize}; 247 248 SkSpan<const SkPoint> positions; 249 SkSpan<const SkVector> scaledRotations; 250 switch (it.positioning()) { 251 case SkTextBlobRunIterator::kDefault_Positioning: { 252 positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor); 253 positionCursor += positions.size(); 254 break; 255 } 256 case SkTextBlobRunIterator::kHorizontal_Positioning: { 257 positions = SkSpan(positionCursor, runSize); 258 for (auto x : SkSpan<const SkScalar>{it.pos(), glyphIDs.size()}) { 259 *positionCursor++ = SkPoint::Make(x, it.offset().y()); 260 } 261 break; 262 } 263 case SkTextBlobRunIterator::kFull_Positioning: { 264 positions = SkSpan(it.points(), runSize); 265 break; 266 } 267 case SkTextBlobRunIterator::kRSXform_Positioning: { 268 positions = SkSpan(positionCursor, runSize); 269 scaledRotations = SkSpan(scaledRotationsCursor, runSize); 270 for (const SkRSXform& xform : SkSpan(it.xforms(), runSize)) { 271 *positionCursor++ = {xform.fTx, xform.fTy}; 272 *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin}; 273 } 274 break; 275 } 276 } 277 278 const uint32_t* clusters = it.clusters(); 279 this->makeGlyphRun( 280 font, 281 glyphIDs, 282 positions, 283 SkSpan<const char>(it.text(), it.textSize()), 284 SkSpan<const uint32_t>(clusters, clusters ? runSize : 0), 285 scaledRotations); 286 } 287 288 return this->setGlyphRunList(&blob, blob.bounds(), origin); 289 } 290 291 std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>> 292 GlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) { 293 const int count = SkCount(xforms); 294 this->prepareBuffers(count, count); 295 auto positions = SkSpan(fPositions.get(), count); 296 auto scaledRotations = SkSpan(fScaledRotations.get(), count); 297 for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) { 298 auto [scos, ssin, tx, ty] = xform; 299 pos = {tx, ty}; 300 sr = {scos, ssin}; 301 } 302 return {positions, scaledRotations}; 303 } 304 305 void GlyphRunBuilder::initialize(const SkTextBlob& blob) { 306 int positionCount = 0; 307 int rsxFormCount = 0; 308 for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { 309 if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) { 310 positionCount += it.glyphCount(); 311 } 312 if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) { 313 rsxFormCount += it.glyphCount(); 314 } 315 } 316 317 prepareBuffers(positionCount, rsxFormCount); 318 } 319 320 void GlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) { 321 if (positionCount > fMaxTotalRunSize) { 322 fMaxTotalRunSize = positionCount; 323 fPositions.reset(fMaxTotalRunSize); 324 } 325 326 if (RSXFormCount > fMaxScaledRotations) { 327 fMaxScaledRotations = RSXFormCount; 328 fScaledRotations.reset(RSXFormCount); 329 } 330 331 fGlyphRunListStorage.clear(); 332 } 333 334 SkSpan<const SkGlyphID> GlyphRunBuilder::textToGlyphIDs( 335 const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) { 336 if (encoding != SkTextEncoding::kGlyphID) { 337 int count = font.countText(bytes, byteLength, encoding); 338 if (count > 0) { 339 fScratchGlyphIDs.resize(count); 340 font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs); 341 return SkSpan(fScratchGlyphIDs); 342 } else { 343 return SkSpan<const SkGlyphID>(); 344 } 345 } else { 346 return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2); 347 } 348 } 349 350 void GlyphRunBuilder::makeGlyphRun( 351 const SkFont& font, 352 SkSpan<const SkGlyphID> glyphIDs, 353 SkSpan<const SkPoint> positions, 354 SkSpan<const char> text, 355 SkSpan<const uint32_t> clusters, 356 SkSpan<const SkVector> scaledRotations) { 357 358 // Ignore empty runs. 359 if (!glyphIDs.empty()) { 360 fGlyphRunListStorage.emplace_back( 361 font, 362 positions, 363 glyphIDs, 364 text, 365 clusters, 366 scaledRotations); 367 } 368 } 369 370 const GlyphRunList& sktext::GlyphRunBuilder::setGlyphRunList( 371 const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) { 372 fGlyphRunList.emplace(blob, bounds, origin, SkSpan(fGlyphRunListStorage), this); 373 return fGlyphRunList.value(); 374 } 375 } // namespace sktext