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