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