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