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