1 /* 2 * Copyright 2015 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 #ifndef SkFindAndPositionGlyph_DEFINED 9 #define SkFindAndPositionGlyph_DEFINED 10 11 #include "SkArenaAlloc.h" 12 #include "SkAutoKern.h" 13 #include "SkGlyph.h" 14 #include "SkGlyphCache.h" 15 #include "SkMatrixPriv.h" 16 #include "SkPaint.h" 17 #include "SkTemplates.h" 18 #include "SkUtils.h" 19 #include <utility> 20 21 class SkFindAndPlaceGlyph { 22 public: 23 template<typename ProcessOneGlyph> 24 static void ProcessText( 25 SkPaint::TextEncoding, const char text[], size_t byteLength, 26 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment, 27 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph); 28 // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large 29 // multiplicity. It figures out the glyph, position and rounding and pass those parameters to 30 // processOneGlyph. 31 // 32 // The routine processOneGlyph passed in by the client has the following signature: 33 // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding); 34 // 35 // * Sub-pixel positioning (2) - use sub-pixel positioning. 36 // * Text alignment (3) - text alignment with respect to the glyph's width. 37 // * Matrix type (3) - special cases for translation and X-coordinate scaling. 38 // * Components per position (2) - the positions vector can have a common Y with different 39 // Xs, or XY-pairs. 40 // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round 41 // to a whole coordinate instead of using sub-pixel positioning. 42 // The number of variations is 108 for sub-pixel and 36 for full-pixel. 43 // This routine handles all of them using inline polymorphic variable (no heap allocation). 44 template<typename ProcessOneGlyph> 45 static void ProcessPosText( 46 SkPaint::TextEncoding, const char text[], size_t byteLength, 47 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition, 48 SkPaint::Align textAlignment, 49 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph); 50 51 private: 52 // GlyphFinderInterface is the polymorphic base for classes that parse a stream of chars into 53 // the right UniChar (or GlyphID) and lookup up the glyph on the cache. The concrete 54 // implementations are: Utf8GlyphFinder, Utf16GlyphFinder, Utf32GlyphFinder, 55 // and GlyphIdGlyphFinder. 56 class GlyphFinderInterface { 57 public: 58 virtual ~GlyphFinderInterface() {} 59 virtual const SkGlyph& lookupGlyph(const char** text) = 0; 60 virtual const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) = 0; 61 }; 62 63 class UtfNGlyphFinder : public GlyphFinderInterface { 64 public: 65 explicit UtfNGlyphFinder(SkGlyphCache* cache) 66 : fCache(cache) { 67 SkASSERT(cache != nullptr); 68 } 69 70 const SkGlyph& lookupGlyph(const char** text) override { 71 SkASSERT(text != nullptr); 72 return fCache->getUnicharMetrics(nextUnichar(text)); 73 } 74 const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override { 75 SkASSERT(text != nullptr); 76 return fCache->getUnicharMetrics(nextUnichar(text), x, y); 77 } 78 79 private: 80 virtual SkUnichar nextUnichar(const char** text) = 0; 81 SkGlyphCache* fCache; 82 }; 83 84 class Utf8GlyphFinder final : public UtfNGlyphFinder { 85 public: 86 explicit Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { } 87 88 private: 89 SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); } 90 }; 91 92 class Utf16GlyphFinder final : public UtfNGlyphFinder { 93 public: 94 explicit Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { } 95 96 private: 97 SkUnichar nextUnichar(const char** text) override { 98 return SkUTF16_NextUnichar((const uint16_t**)text); 99 } 100 }; 101 102 class Utf32GlyphFinder final : public UtfNGlyphFinder { 103 public: 104 explicit Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { } 105 106 private: 107 SkUnichar nextUnichar(const char** text) override { 108 const int32_t* ptr = *(const int32_t**)text; 109 SkUnichar uni = *ptr++; 110 *text = (const char*)ptr; 111 return uni; 112 } 113 }; 114 115 class GlyphIdGlyphFinder final : public GlyphFinderInterface { 116 public: 117 explicit GlyphIdGlyphFinder(SkGlyphCache* cache) 118 : fCache(cache) { 119 SkASSERT(cache != nullptr); 120 } 121 122 const SkGlyph& lookupGlyph(const char** text) override { 123 return fCache->getGlyphIDMetrics(nextGlyphId(text)); 124 } 125 const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override { 126 return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y); 127 } 128 129 private: 130 uint16_t nextGlyphId(const char** text) { 131 SkASSERT(text != nullptr); 132 133 const uint16_t* ptr = *(const uint16_t**)text; 134 uint16_t glyphID = *ptr; 135 ptr += 1; 136 *text = (const char*)ptr; 137 return glyphID; 138 } 139 SkGlyphCache* fCache; 140 }; 141 142 static GlyphFinderInterface* getGlyphFinder( 143 SkArenaAlloc* arena, SkPaint::TextEncoding encoding, SkGlyphCache* cache) { 144 switch(encoding) { 145 case SkPaint::kUTF8_TextEncoding: 146 return arena->make<Utf8GlyphFinder>(cache); 147 case SkPaint::kUTF16_TextEncoding: 148 return arena->make<Utf16GlyphFinder>(cache); 149 case SkPaint::kUTF32_TextEncoding: 150 return arena->make<Utf32GlyphFinder>(cache); 151 case SkPaint::kGlyphID_TextEncoding: 152 return arena->make<GlyphIdGlyphFinder>(cache); 153 } 154 SK_ABORT("Should not get here."); 155 return nullptr; 156 } 157 158 // PositionReaderInterface reads a point from the pos vector. 159 // * HorizontalPositions - assumes a common Y for many X values. 160 // * ArbitraryPositions - a list of (X,Y) pairs. 161 class PositionReaderInterface { 162 public: 163 virtual ~PositionReaderInterface() { } 164 virtual SkPoint nextPoint() = 0; 165 }; 166 167 class HorizontalPositions final : public PositionReaderInterface { 168 public: 169 explicit HorizontalPositions(const SkScalar* positions) 170 : fPositions(positions) { } 171 172 SkPoint nextPoint() override { 173 SkScalar x = *fPositions++; 174 return {x, 0}; 175 } 176 177 private: 178 const SkScalar* fPositions; 179 }; 180 181 class ArbitraryPositions final : public PositionReaderInterface { 182 public: 183 explicit ArbitraryPositions(const SkScalar* positions) 184 : fPositions(positions) { } 185 186 SkPoint nextPoint() override { 187 SkPoint to_return{fPositions[0], fPositions[1]}; 188 fPositions += 2; 189 return to_return; 190 } 191 192 private: 193 const SkScalar* fPositions; 194 }; 195 196 // MapperInterface given a point map it through the matrix. There are several shortcut 197 // variants. 198 // * TranslationMapper - assumes a translation only matrix. 199 // * XScaleMapper - assumes an X scaling and a translation. 200 // * GeneralMapper - Does all other matricies. 201 class MapperInterface { 202 public: 203 virtual ~MapperInterface() { } 204 205 virtual SkPoint map(SkPoint position) const = 0; 206 }; 207 208 class TranslationMapper final : public MapperInterface { 209 public: 210 TranslationMapper(const SkMatrix& matrix, const SkPoint origin) 211 : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { } 212 213 SkPoint map(SkPoint position) const override { 214 return position + fTranslate; 215 } 216 217 private: 218 const SkPoint fTranslate; 219 }; 220 221 class XScaleMapper final : public MapperInterface { 222 public: 223 XScaleMapper(const SkMatrix& matrix, const SkPoint origin) 224 : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { } 225 226 SkPoint map(SkPoint position) const override { 227 return {fXScale * position.fX + fTranslate.fX, fTranslate.fY}; 228 } 229 230 private: 231 const SkPoint fTranslate; 232 const SkScalar fXScale; 233 }; 234 235 // The caller must keep matrix alive while this class is used. 236 class GeneralMapper final : public MapperInterface { 237 public: 238 GeneralMapper(const SkMatrix& matrix, const SkPoint origin) 239 : fOrigin(origin), fMatrix(matrix), fMapProc(SkMatrixPriv::GetMapXYProc(matrix)) { } 240 241 SkPoint map(SkPoint position) const override { 242 SkPoint result; 243 fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result); 244 return result; 245 } 246 247 private: 248 const SkPoint fOrigin; 249 const SkMatrix& fMatrix; 250 const SkMatrixPriv::MapXYProc fMapProc; 251 }; 252 253 // TextAlignmentAdjustment handles shifting the glyph based on its width. 254 static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) { 255 switch (textAlignment) { 256 case SkPaint::kLeft_Align: 257 return {0.0f, 0.0f}; 258 case SkPaint::kCenter_Align: 259 return {SkFloatToScalar(glyph.fAdvanceX) / 2, 260 SkFloatToScalar(glyph.fAdvanceY) / 2}; 261 case SkPaint::kRight_Align: 262 return {SkFloatToScalar(glyph.fAdvanceX), 263 SkFloatToScalar(glyph.fAdvanceY)}; 264 } 265 // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy. 266 SK_ABORT("Should never get here."); 267 return {0.0f, 0.0f}; 268 } 269 270 // The "call" to SkFixedToScalar is actually a macro. It's macros all the way down. 271 // Needs to be a macro because you can't have a const float unless you make it constexpr. 272 #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound)) 273 274 // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel 275 // positioned glyph. 276 static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) { 277 switch (axisAlignment) { 278 case kX_SkAxisAlignment: 279 return {kSubpixelRounding, SK_ScalarHalf}; 280 case kY_SkAxisAlignment: 281 return {SK_ScalarHalf, kSubpixelRounding}; 282 case kNone_SkAxisAlignment: 283 return {kSubpixelRounding, kSubpixelRounding}; 284 } 285 SK_ABORT("Should not get here."); 286 return {0.0f, 0.0f}; 287 } 288 289 // The SubpixelAlignment function produces a suitable position for the glyph cache to 290 // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut 291 // of 0 is used for the sub-pixel position. 292 static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) { 293 // Only the fractional part of position.fX and position.fY matter, because the result of 294 // this function will just be passed to FixedToSub. 295 switch (axisAlignment) { 296 case kX_SkAxisAlignment: 297 return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 0}; 298 case kY_SkAxisAlignment: 299 return {0, SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)}; 300 case kNone_SkAxisAlignment: 301 return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 302 SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)}; 303 } 304 SK_ABORT("Should not get here."); 305 return {0, 0}; 306 } 307 308 #undef kSubpixelRounding 309 310 // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does 311 // glyph specific position adjustment. The findAndPositionGlyph method takes text and 312 // position and calls processOneGlyph with the correct glyph, final position and rounding 313 // terms. The final position is not rounded yet and is the responsibility of processOneGlyph. 314 template<typename ProcessOneGlyph> 315 class GlyphFindAndPlaceInterface : SkNoncopyable { 316 public: 317 virtual ~GlyphFindAndPlaceInterface() { } 318 319 // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and 320 // returns the position of where the next glyph will be using the glyph's advance and 321 // possibly kerning. The returned position is used by drawText, but ignored by drawPosText. 322 // The compiler should prune all this calculation if the return value is not used. 323 // 324 // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a 325 // compile error. 326 // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277 327 virtual SkPoint findAndPositionGlyph( 328 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) { 329 SK_ABORT("Should never get here."); 330 return {0.0f, 0.0f}; 331 } 332 }; 333 334 // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is 335 // requested. After it has found and placed the glyph it calls the templated function 336 // ProcessOneGlyph in order to actually perform an action. 337 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, 338 SkAxisAlignment kAxisAlignment> 339 class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { 340 public: 341 explicit GlyphFindAndPlaceSubpixel(GlyphFinderInterface* glyphFinder) 342 : fGlyphFinder(glyphFinder) { } 343 344 SkPoint findAndPositionGlyph( 345 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { 346 347 if (kTextAlignment != SkPaint::kLeft_Align) { 348 // Get the width of an un-sub-pixel positioned glyph for calculating the 349 // alignment. This is not needed for kLeftAlign because its adjustment is 350 // always {0, 0}. 351 const char* tempText = *text; 352 const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText); 353 354 if (metricGlyph.fWidth <= 0) { 355 // Exiting early, be sure to update text pointer. 356 *text = tempText; 357 return position + SkPoint{SkFloatToScalar(metricGlyph.fAdvanceX), 358 SkFloatToScalar(metricGlyph.fAdvanceY)}; 359 } 360 361 // Adjust the final position by the alignment adjustment. 362 position -= TextAlignmentAdjustment(kTextAlignment, metricGlyph); 363 } 364 365 // Find the glyph. 366 SkIPoint lookupPosition = SkScalarsAreFinite(position.fX, position.fY) 367 ? SubpixelAlignment(kAxisAlignment, position) 368 : SkIPoint{0, 0}; 369 const SkGlyph& renderGlyph = 370 fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY); 371 372 // If the glyph has no width (no pixels) then don't bother processing it. 373 if (renderGlyph.fWidth > 0) { 374 processOneGlyph(renderGlyph, position, 375 SubpixelPositionRounding(kAxisAlignment)); 376 } 377 return position + SkPoint{SkFloatToScalar(renderGlyph.fAdvanceX), 378 SkFloatToScalar(renderGlyph.fAdvanceY)}; 379 } 380 381 private: 382 GlyphFinderInterface* fGlyphFinder; 383 }; 384 385 enum SelectKerning { 386 kNoKerning = false, 387 kUseKerning = true 388 }; 389 390 // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel 391 // positioning is requested. The kUseKerning argument should be true for drawText, and false 392 // for drawPosText. 393 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning> 394 class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { 395 public: 396 explicit GlyphFindAndPlaceFullPixel(GlyphFinderInterface* glyphFinder) 397 : fGlyphFinder(glyphFinder) { 398 // Kerning can only be used with SkPaint::kLeft_Align 399 static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment, 400 "Kerning can only be used with left aligned text."); 401 } 402 403 SkPoint findAndPositionGlyph( 404 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { 405 SkPoint finalPosition = position; 406 const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text); 407 if (kUseKerning) { 408 finalPosition += {fAutoKern.adjust(glyph), 0.0f}; 409 } 410 if (glyph.fWidth > 0) { 411 finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph); 412 processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf}); 413 } 414 return finalPosition + SkPoint{SkFloatToScalar(glyph.fAdvanceX), 415 SkFloatToScalar(glyph.fAdvanceY)}; 416 } 417 418 private: 419 GlyphFinderInterface* fGlyphFinder; 420 421 SkAutoKern fAutoKern; 422 }; 423 424 template <typename ProcessOneGlyph, SkPaint::Align kTextAlignment> 425 static GlyphFindAndPlaceInterface<ProcessOneGlyph>* getSubpixel( 426 SkArenaAlloc* arena, SkAxisAlignment axisAlignment, GlyphFinderInterface* glyphFinder) 427 { 428 switch (axisAlignment) { 429 case kX_SkAxisAlignment: 430 return arena->make<GlyphFindAndPlaceSubpixel< 431 ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder); 432 case kNone_SkAxisAlignment: 433 return arena->make<GlyphFindAndPlaceSubpixel< 434 ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder); 435 case kY_SkAxisAlignment: 436 return arena->make<GlyphFindAndPlaceSubpixel< 437 ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder); 438 } 439 SK_ABORT("Should never get here."); 440 return nullptr; 441 } 442 443 static SkPoint MeasureText( 444 GlyphFinderInterface* glyphFinder, const char text[], size_t byteLength) { 445 SkScalar x = 0, y = 0; 446 const char* stop = text + byteLength; 447 448 SkAutoKern autokern; 449 450 while (text < stop) { 451 // don't need x, y here, since all subpixel variants will have the 452 // same advance 453 const SkGlyph& glyph = glyphFinder->lookupGlyph(&text); 454 455 x += autokern.adjust(glyph) + SkFloatToScalar(glyph.fAdvanceX); 456 y += SkFloatToScalar(glyph.fAdvanceY); 457 } 458 SkASSERT(text == stop); 459 return {x, y}; 460 } 461 }; 462 463 template<typename ProcessOneGlyph> 464 inline void SkFindAndPlaceGlyph::ProcessPosText( 465 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength, 466 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition, 467 SkPaint::Align textAlignment, 468 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { 469 470 SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); 471 uint32_t mtype = matrix.getType(); 472 473 // Specialized code for handling the most common case for blink. 474 if (textEncoding == SkPaint::kGlyphID_TextEncoding 475 && textAlignment == SkPaint::kLeft_Align 476 && axisAlignment == kX_SkAxisAlignment 477 && cache->isSubpixel() 478 && mtype <= SkMatrix::kTranslate_Mask) 479 { 480 GlyphIdGlyphFinder glyphFinder(cache); 481 using Positioner = 482 GlyphFindAndPlaceSubpixel < 483 ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment>; 484 HorizontalPositions hPositions{pos}; 485 ArbitraryPositions aPositions{pos}; 486 PositionReaderInterface* positions = nullptr; 487 if (scalarsPerPosition == 2) { 488 positions = &aPositions; 489 } else { 490 positions = &hPositions; 491 } 492 TranslationMapper mapper{matrix, offset}; 493 Positioner positioner(&glyphFinder); 494 const char* cursor = text; 495 const char* stop = text + byteLength; 496 while (cursor < stop) { 497 SkPoint mappedPoint = mapper.TranslationMapper::map(positions->nextPoint()); 498 positioner.Positioner::findAndPositionGlyph( 499 &cursor, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph)); 500 } 501 return; 502 } 503 504 SkSTArenaAlloc<120> arena; 505 506 GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache); 507 508 PositionReaderInterface* positionReader = nullptr; 509 if (2 == scalarsPerPosition) { 510 positionReader = arena.make<ArbitraryPositions>(pos); 511 } else { 512 positionReader = arena.make<HorizontalPositions>(pos); 513 } 514 515 MapperInterface* mapper = nullptr; 516 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask) 517 || scalarsPerPosition == 2) { 518 mapper = arena.make<GeneralMapper>(matrix, offset); 519 } else if (mtype & SkMatrix::kScale_Mask) { 520 mapper = arena.make<XScaleMapper>(matrix, offset); 521 } else { 522 mapper = arena.make<TranslationMapper>(matrix, offset); 523 } 524 525 GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr; 526 if (cache->isSubpixel()) { 527 switch (textAlignment) { 528 case SkPaint::kLeft_Align: 529 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( 530 &arena, axisAlignment, glyphFinder); 531 break; 532 case SkPaint::kCenter_Align: 533 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>( 534 &arena, axisAlignment, glyphFinder); 535 break; 536 case SkPaint::kRight_Align: 537 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>( 538 &arena, axisAlignment, glyphFinder); 539 break; 540 } 541 } else { 542 switch (textAlignment) { 543 case SkPaint::kLeft_Align: 544 findAndPosition = arena.make< 545 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 546 SkPaint::kLeft_Align, kNoKerning>>(glyphFinder); 547 break; 548 case SkPaint::kCenter_Align: 549 findAndPosition = arena.make< 550 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 551 SkPaint::kCenter_Align, kNoKerning>>(glyphFinder); 552 break; 553 case SkPaint::kRight_Align: 554 findAndPosition = arena.make< 555 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 556 SkPaint::kRight_Align, kNoKerning>>(glyphFinder); 557 break; 558 } 559 } 560 561 const char* stop = text + byteLength; 562 while (text < stop) { 563 SkPoint mappedPoint = mapper->map(positionReader->nextPoint()); 564 findAndPosition->findAndPositionGlyph( 565 &text, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph)); 566 } 567 } 568 569 template<typename ProcessOneGlyph> 570 inline void SkFindAndPlaceGlyph::ProcessText( 571 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength, 572 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment, 573 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { 574 SkSTArenaAlloc<64> arena; 575 576 // transform the starting point 577 matrix.mapPoints(&offset, 1); 578 579 GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache); 580 581 // need to measure first 582 if (textAlignment != SkPaint::kLeft_Align) { 583 SkVector stop = MeasureText(glyphFinder, text, byteLength); 584 585 if (textAlignment == SkPaint::kCenter_Align) { 586 stop *= SK_ScalarHalf; 587 } 588 offset -= stop; 589 } 590 591 GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr; 592 if (cache->isSubpixel()) { 593 SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); 594 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( 595 &arena, axisAlignment, glyphFinder); 596 } else { 597 using FullPixel = 598 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>; 599 findAndPosition = arena.make<FullPixel>(glyphFinder); 600 } 601 602 const char* stop = text + byteLength; 603 SkPoint current = offset; 604 while (text < stop) { 605 current = 606 findAndPosition->findAndPositionGlyph( 607 &text, current, std::forward<ProcessOneGlyph>(processOneGlyph)); 608 609 } 610 } 611 612 #endif // SkFindAndPositionGlyph_DEFINED 613