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 {SkFixedToScalar(glyph.fAdvanceX >> 1), 351 SkFixedToScalar(glyph.fAdvanceY >> 1)}; 352 case SkPaint::kRight_Align: 353 return {SkFixedToScalar(glyph.fAdvanceX), 354 SkFixedToScalar(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 switch (axisAlignment) { 385 case kX_SkAxisAlignment: 386 return {SkScalarToFixed(position.fX + kSubpixelRounding), 0}; 387 case kY_SkAxisAlignment: 388 return {0, SkScalarToFixed(position.fY + kSubpixelRounding)}; 389 case kNone_SkAxisAlignment: 390 return {SkScalarToFixed(position.fX + kSubpixelRounding), 391 SkScalarToFixed(position.fY + kSubpixelRounding)}; 392 } 393 SkFAIL("Should not get here."); 394 return {0, 0}; 395 } 396 397 #undef kSubpixelRounding 398 399 // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does 400 // glyph specific position adjustment. The findAndPositionGlyph method takes text and 401 // position and calls processOneGlyph with the correct glyph, final position and rounding 402 // terms. The final position is not rounded yet and is the responsibility of processOneGlyph. 403 template<typename ProcessOneGlyph> 404 class GlyphFindAndPlaceInterface : SkNoncopyable { 405 public: 406 virtual ~GlyphFindAndPlaceInterface() { }; 407 408 // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and 409 // returns the position of where the next glyph will be using the glyph's advance and 410 // possibly kerning. The returned position is used by drawText, but ignored by drawPosText. 411 // The compiler should prune all this calculation if the return value is not used. 412 // 413 // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a 414 // compile error. 415 // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277 416 virtual SkPoint findAndPositionGlyph( 417 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) { 418 SkFAIL("Should never get here."); 419 return {0.0f, 0.0f}; 420 }; 421 }; 422 423 // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is 424 // requested. After it has found and placed the glyph it calls the templated function 425 // ProcessOneGlyph in order to actually perform an action. 426 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, 427 SkAxisAlignment kAxisAlignment> 428 class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { 429 public: 430 GlyphFindAndPlaceSubpixel(LookupGlyph& glyphFinder) 431 : fGlyphFinder(glyphFinder) { 432 FixGCC49Arm64Bug(1); 433 } 434 435 SkPoint findAndPositionGlyph( 436 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { 437 SkPoint finalPosition = position; 438 if (kTextAlignment != SkPaint::kLeft_Align) { 439 // Get the width of an un-sub-pixel positioned glyph for calculating the 440 // alignment. This is not needed for kLeftAlign because its adjustment is 441 // always {0, 0}. 442 const char* tempText = *text; 443 const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText); 444 445 if (metricGlyph.fWidth <= 0) { 446 // Exiting early, be sure to update text pointer. 447 *text = tempText; 448 return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX), 449 SkFixedToScalar(metricGlyph.fAdvanceY)}; 450 } 451 452 // Adjust the final position by the alignment adjustment. 453 finalPosition -= TextAlignmentAdjustment(kTextAlignment, metricGlyph); 454 } 455 456 // Find the glyph. 457 SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPosition); 458 const SkGlyph& renderGlyph = 459 fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY); 460 461 // If the glyph has no width (no pixels) then don't bother processing it. 462 if (renderGlyph.fWidth > 0) { 463 processOneGlyph(renderGlyph, finalPosition, 464 SubpixelPositionRounding(kAxisAlignment)); 465 } 466 return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX), 467 SkFixedToScalar(renderGlyph.fAdvanceY)}; 468 } 469 470 private: 471 LookupGlyph& fGlyphFinder; 472 }; 473 474 enum SelectKerning { 475 kNoKerning = false, 476 kUseKerning = true 477 }; 478 479 // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel 480 // positioning is requested. The kUseKerning argument should be true for drawText, and false 481 // for drawPosText. 482 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning> 483 class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { 484 public: 485 GlyphFindAndPlaceFullPixel(LookupGlyph& glyphFinder) 486 : fGlyphFinder(glyphFinder) { 487 FixGCC49Arm64Bug(2); 488 // Kerning can only be used with SkPaint::kLeft_Align 489 static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment, 490 "Kerning can only be used with left aligned text."); 491 } 492 493 SkPoint findAndPositionGlyph( 494 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { 495 SkPoint finalPosition = position; 496 const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text); 497 if (kUseKerning) { 498 finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f}; 499 } 500 if (glyph.fWidth > 0) { 501 finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph); 502 processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf}); 503 } 504 return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX), 505 SkFixedToScalar(glyph.fAdvanceY)}; 506 } 507 508 private: 509 LookupGlyph& fGlyphFinder; 510 511 SkAutoKern fAutoKern; 512 }; 513 514 // GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and 515 // placing a glyph. There are three factors that go into the different factors. 516 // * Is sub-pixel positioned - a boolean that says whether to use sub-pixel positioning. 517 // * Text alignment - indicates if the glyph should be placed to the right, centered or left 518 // of a given position. 519 // * Axis alignment - indicates if the glyphs final sub-pixel position should be rounded to a 520 // whole pixel if the glyph is aligned with an axis. This is only used for sub-pixel 521 // positioning and allows the baseline to look crisp. 522 template<typename ProcessOneGlyph> 523 using GlyphFindAndPlace = PolymorphicVariant< 524 GlyphFindAndPlaceInterface<ProcessOneGlyph>, 525 // Subpixel 526 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNone_SkAxisAlignment>, 527 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment >, 528 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kY_SkAxisAlignment >, 529 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNone_SkAxisAlignment>, 530 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kX_SkAxisAlignment >, 531 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kY_SkAxisAlignment >, 532 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kNone_SkAxisAlignment>, 533 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kX_SkAxisAlignment >, 534 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kY_SkAxisAlignment >, 535 // Full pixel 536 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNoKerning>, 537 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNoKerning>, 538 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align, kNoKerning> 539 >; 540 541 // InitSubpixel is a helper function for initializing all the variants of 542 // GlyphFindAndPlaceSubpixel. 543 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment> 544 static void InitSubpixel( 545 typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init, 546 SkAxisAlignment axisAlignment, 547 LookupGlyph& glyphFinder) { 548 switch (axisAlignment) { 549 case kX_SkAxisAlignment: 550 to_init->template initialize<GlyphFindAndPlaceSubpixel< 551 ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder); 552 break; 553 case kNone_SkAxisAlignment: 554 to_init->template initialize<GlyphFindAndPlaceSubpixel< 555 ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder); 556 break; 557 case kY_SkAxisAlignment: 558 to_init->template initialize<GlyphFindAndPlaceSubpixel< 559 ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder); 560 break; 561 } 562 } 563 564 static SkPoint MeasureText(LookupGlyph& glyphFinder, const char text[], size_t byteLength) { 565 SkFixed x = 0, y = 0; 566 const char* stop = text + byteLength; 567 568 SkAutoKern autokern; 569 570 while (text < stop) { 571 // don't need x, y here, since all subpixel variants will have the 572 // same advance 573 const SkGlyph& glyph = glyphFinder->lookupGlyph(&text); 574 575 x += autokern.adjust(glyph) + glyph.fAdvanceX; 576 y += glyph.fAdvanceY; 577 } 578 SkASSERT(text == stop); 579 return {SkFixedToScalar(x), SkFixedToScalar(y)}; 580 } 581 }; 582 583 template<typename ProcessOneGlyph> 584 inline void SkFindAndPlaceGlyph::ProcessPosText( 585 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength, 586 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition, 587 SkPaint::Align textAlignment, 588 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { 589 590 SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix); 591 uint32_t mtype = matrix.getType(); 592 593 LookupGlyph glyphFinder(textEncoding, cache); 594 595 // Specialized code for handling the most common case for blink. The while loop is totally 596 // de-virtualized. 597 if (scalarsPerPosition == 1 598 && textAlignment == SkPaint::kLeft_Align 599 && axisAlignment == kX_SkAxisAlignment 600 && cache->isSubpixel() 601 && mtype <= SkMatrix::kTranslate_Mask) { 602 typedef GlyphFindAndPlaceSubpixel< 603 ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment> Positioner; 604 HorizontalPositions positions{pos}; 605 TranslationMapper mapper{matrix, offset}; 606 Positioner positioner(glyphFinder); 607 const char* cursor = text; 608 const char* stop = text + byteLength; 609 while (cursor < stop) { 610 SkPoint mappedPoint = mapper.TranslationMapper::map( 611 positions.HorizontalPositions::nextPoint()); 612 positioner.Positioner::findAndPositionGlyph( 613 &cursor, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph)); 614 } 615 return; 616 } 617 618 PositionReader positionReader{ 619 [&](PositionReader::Variants* to_init) { 620 if (2 == scalarsPerPosition) { 621 to_init->initialize<ArbitraryPositions>(pos); 622 } else { 623 to_init->initialize<HorizontalPositions>(pos); 624 } 625 positionReader->forceUseForBug(); 626 } 627 }; 628 629 Mapper mapper{ 630 [&](Mapper::Variants* to_init) { 631 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask) 632 || scalarsPerPosition == 2) { 633 to_init->initialize<GeneralMapper>(matrix, offset); 634 } else if (mtype & SkMatrix::kScale_Mask) { 635 to_init->initialize<XScaleMapper>(matrix, offset); 636 } else { 637 to_init->initialize<TranslationMapper>(matrix, offset); 638 } 639 } 640 }; 641 642 GlyphFindAndPlace<ProcessOneGlyph> findAndPosition { 643 [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) { 644 if (cache->isSubpixel()) { 645 switch (textAlignment) { 646 case SkPaint::kLeft_Align: 647 InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( 648 to_init, axisAlignment, glyphFinder); 649 break; 650 case SkPaint::kCenter_Align: 651 InitSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>( 652 to_init, axisAlignment, glyphFinder); 653 break; 654 case SkPaint::kRight_Align: 655 InitSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>( 656 to_init, axisAlignment, glyphFinder); 657 break; 658 } 659 } else { 660 switch (textAlignment) { 661 case SkPaint::kLeft_Align: 662 to_init->template initialize< 663 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 664 SkPaint::kLeft_Align, kNoKerning>>(glyphFinder); 665 break; 666 case SkPaint::kCenter_Align: 667 to_init->template initialize< 668 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 669 SkPaint::kCenter_Align, kNoKerning>>(glyphFinder); 670 break; 671 case SkPaint::kRight_Align: 672 to_init->template initialize< 673 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 674 SkPaint::kRight_Align, kNoKerning>>(glyphFinder); 675 break; 676 } 677 } 678 } 679 }; 680 681 const char* stop = text + byteLength; 682 while (text < stop) { 683 SkPoint mappedPoint = mapper->map(positionReader->nextPoint()); 684 findAndPosition->findAndPositionGlyph( 685 &text, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph)); 686 } 687 } 688 689 template<typename ProcessOneGlyph> 690 inline void SkFindAndPlaceGlyph::ProcessText( 691 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength, 692 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment, 693 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { 694 695 // transform the starting point 696 matrix.mapPoints(&offset, 1); 697 698 LookupGlyph glyphFinder(textEncoding, cache); 699 700 // need to measure first 701 if (textAlignment != SkPaint::kLeft_Align) { 702 SkVector stop = MeasureText(glyphFinder, text, byteLength); 703 704 if (textAlignment == SkPaint::kCenter_Align) { 705 stop *= SK_ScalarHalf; 706 } 707 offset -= stop; 708 } 709 710 GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{ 711 [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) { 712 if (cache->isSubpixel()) { 713 SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix); 714 InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( 715 to_init, axisAlignment, glyphFinder); 716 } else { 717 to_init->template initialize< 718 GlyphFindAndPlaceFullPixel< 719 ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>>(glyphFinder); 720 } 721 } 722 }; 723 724 const char* stop = text + byteLength; 725 SkPoint current = offset; 726 while (text < stop) { 727 current = 728 findAndPosition->findAndPositionGlyph( 729 &text, current, std::forward<ProcessOneGlyph>(processOneGlyph)); 730 731 } 732 } 733 734 #endif // SkFindAndPositionGlyph_DEFINED 735