aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkFindAndPlaceGlyph.h
blob: c74a9820e4240e4a4d1c29dec7b4785223b0ff42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkFindAndPositionGlyph_DEFINED
#define SkFindAndPositionGlyph_DEFINED

#include "SkAutoKern.h"
#include "SkGlyph.h"
#include "SkGlyphCache.h"
#include "SkPaint.h"
#include "SkTemplates.h"
#include "SkUtils.h"
#include <utility>

// Calculate a type with the same size as the max of all the Ts.
// This must be top level because the is no specialization of inner classes.
template<typename... Ts> struct SkMaxSizeOf;

template<>
struct SkMaxSizeOf<> {
    static const size_t value = 0;
};

template<typename H, typename... Ts>
struct SkMaxSizeOf<H, Ts...> {
    static const size_t value =
        sizeof(H) >= SkMaxSizeOf<Ts...>::value ? sizeof(H) : SkMaxSizeOf<Ts...>::value;
};


// This is a temporary helper function to work around a bug in the code generation
// for aarch64 (arm) on GCC 4.9. This bug does not show up on other platforms, so it
// seems to be an aarch64 backend problem.
//
// GCC 4.9 on ARM64 does not generate the proper constructor code for PositionReader or
// GlyphFindAndPlace. The vtable is not set properly without adding the fixme code.
// The implementation is in SkDraw.cpp.
extern void FixGCC49Arm64Bug(int v);

class SkFindAndPlaceGlyph {
public:
    template<typename ProcessOneGlyph>
    static void ProcessText(
        SkPaint::TextEncoding, const char text[], size_t byteLength,
        SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
        SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
    // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
    // multiplicity. It figures out the glyph, position and rounding and pass those parameters to
    // processOneGlyph.
    //
    // The routine processOneGlyph passed in by the client has the following signature:
    // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding);
    //
    // * Sub-pixel positioning (2) - use sub-pixel positioning.
    // * Text alignment (3) - text alignment with respect to the glyph's width.
    // * Matrix type (3) - special cases for translation and X-coordinate scaling.
    // * Components per position (2) - the positions vector can have a common Y with different
    //   Xs, or XY-pairs.
    // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round
    //   to a whole coordinate instead of using sub-pixel positioning.
    // The number of variations is 108 for sub-pixel and 36 for full-pixel.
    // This routine handles all of them using inline polymorphic variable (no heap allocation).
    template<typename ProcessOneGlyph>
    static void ProcessPosText(
        SkPaint::TextEncoding, const char text[], size_t byteLength,
        SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
        SkPaint::Align textAlignment,
        SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);

private:
    // UntaggedVariant is a pile of memory that can hold one of the Ts. It provides a way
    // to initialize that memory in a typesafe way.
    template<typename... Ts>
    class UntaggedVariant {
    public:
        UntaggedVariant() { }

        ~UntaggedVariant() { }
        UntaggedVariant(const UntaggedVariant&) = delete;
        UntaggedVariant& operator=(const UntaggedVariant&) = delete;
        UntaggedVariant(UntaggedVariant&&) = delete;
        UntaggedVariant& operator=(UntaggedVariant&&) = delete;

        template<typename Variant, typename... Args>
        void initialize(Args&&... args) {
            SkASSERT(sizeof(Variant) <= sizeof(fSpace));
        #if defined(_MSC_VER) && _MSC_VER < 1900
            #define alignof __alignof
        #endif
            SkASSERT(alignof(Variant) <= alignof(Space));
            new(&fSpace) Variant(std::forward<Args>(args)...);
        }

    private:
        typedef SkAlignedSStorage<SkMaxSizeOf<Ts...>::value> Space;
        Space fSpace;
    };

    // PolymorphicVariant holds subclasses of Base without slicing. Ts must be subclasses of Base.
    template<typename Base, typename... Ts>
    class PolymorphicVariant {
    public:
        typedef UntaggedVariant<Ts...> Variants;

        template<typename Initializer>
        PolymorphicVariant(Initializer&& initializer) {
            initializer(&fVariants);
        }
        ~PolymorphicVariant() { get()->~Base(); }
        Base* get() const { return reinterpret_cast<Base*>(&fVariants); }
        Base* operator->() const { return get(); }
        Base& operator*() const { return *get(); }

    private:
        mutable Variants fVariants;
    };

    // GlyphFinderInterface is the polymorphic base for classes that parse a stream of chars into
    // the right UniChar (or GlyphID) and lookup up the glyph on the cache. The concrete
    // implementations are: Utf8GlyphFinder, Utf16GlyphFinder, Utf32GlyphFinder,
    // and GlyphIdGlyphFinder.
    class GlyphFinderInterface {
    public:
        virtual ~GlyphFinderInterface() {}
        virtual const SkGlyph& lookupGlyph(const char** text) = 0;
        virtual const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) = 0;
    };

    class UtfNGlyphFinder : public GlyphFinderInterface {
    public:
        UtfNGlyphFinder(SkGlyphCache* cache) : fCache(cache) { SkASSERT(cache != nullptr); }

        const SkGlyph& lookupGlyph(const char** text) override {
            SkASSERT(text != nullptr);
            return fCache->getUnicharMetrics(nextUnichar(text));
        }
        const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
            SkASSERT(text != nullptr);
            return fCache->getUnicharMetrics(nextUnichar(text), x, y);
        }

    private:
        virtual SkUnichar nextUnichar(const char** text) = 0;
        SkGlyphCache* fCache;
    };

    class Utf8GlyphFinder final : public UtfNGlyphFinder {
    public:
        Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }

    private:
        SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); }
    };

    class Utf16GlyphFinder final : public UtfNGlyphFinder {
    public:
        Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }

    private:
        SkUnichar nextUnichar(const char** text) override {
            return SkUTF16_NextUnichar((const uint16_t**)text);
        }
    };

    class Utf32GlyphFinder final : public UtfNGlyphFinder {
    public:
        Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }

    private:
        SkUnichar nextUnichar(const char** text) override {
            const int32_t* ptr = *(const int32_t**)text;
            SkUnichar uni = *ptr++;
            *text = (const char*)ptr;
            return uni;
        }
    };

    class GlyphIdGlyphFinder final : public GlyphFinderInterface {
    public:
        GlyphIdGlyphFinder(SkGlyphCache* cache) : fCache(cache) { SkASSERT(cache != nullptr); }

        const SkGlyph& lookupGlyph(const char** text) override {
            return fCache->getGlyphIDMetrics(nextGlyphId(text));
        }
        const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
            return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y);
        }

    private:
        uint16_t nextGlyphId(const char** text) {
            SkASSERT(text != nullptr);

            const uint16_t* ptr = *(const uint16_t**)text;
            uint16_t glyphID = *ptr;
            ptr += 1;
            *text = (const char*)ptr;
            return glyphID;
        }
        SkGlyphCache* fCache;
    };

    typedef PolymorphicVariant<
        GlyphFinderInterface,
        Utf8GlyphFinder,
        Utf16GlyphFinder,
        Utf32GlyphFinder,
        GlyphIdGlyphFinder> LookupGlyphVariant;

    class LookupGlyph : public LookupGlyphVariant {
    public:
        LookupGlyph(SkPaint::TextEncoding encoding, SkGlyphCache* cache)
            : LookupGlyphVariant(
            [&](LookupGlyphVariant::Variants* to_init) {
                switch(encoding) {
                    case SkPaint::kUTF8_TextEncoding:
                        to_init->initialize<Utf8GlyphFinder>(cache);
                        break;
                    case SkPaint::kUTF16_TextEncoding:
                        to_init->initialize<Utf16GlyphFinder>(cache);
                        break;
                    case SkPaint::kUTF32_TextEncoding:
                        to_init->initialize<Utf32GlyphFinder>(cache);
                        break;
                    case SkPaint::kGlyphID_TextEncoding:
                        to_init->initialize<GlyphIdGlyphFinder>(cache);
                        break;
                }
            }
        ) { }
    };

    // PositionReaderInterface reads a point from the pos vector.
    // * HorizontalPositions - assumes a common Y for many X values.
    // * ArbitraryPositions - a list of (X,Y) pairs.
    class PositionReaderInterface {
    public:
        virtual ~PositionReaderInterface() { }
        virtual SkPoint nextPoint() = 0;
        // This is only here to fix a GCC 4.9 aarch64 code gen bug.
        // See comment at the top of the file.
        virtual int forceUseForBug() = 0;
    };

    class HorizontalPositions final : public PositionReaderInterface {
    public:
        explicit HorizontalPositions(const SkScalar* positions)
            : fPositions(positions) { }

        SkPoint nextPoint() override {
            SkScalar x = *fPositions++;
            return {x, 0};
        }

        int forceUseForBug() override { return 1; }

    private:
        const SkScalar* fPositions;
    };

    class ArbitraryPositions final : public PositionReaderInterface {
    public:
        explicit ArbitraryPositions(const SkScalar* positions)
            : fPositions(positions) { }

        SkPoint nextPoint() override {
            SkPoint to_return{fPositions[0], fPositions[1]};
            fPositions += 2;
            return to_return;
        }

        int forceUseForBug() override { return 2; }

    private:
        const SkScalar* fPositions;
    };

    typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, ArbitraryPositions>
        PositionReader;

    // MapperInterface given a point map it through the matrix. There are several shortcut
    // variants.
    // * TranslationMapper - assumes a translation only matrix.
    // * XScaleMapper - assumes an X scaling and a translation.
    // * GeneralMapper - Does all other matricies.
    class MapperInterface {
    public:
        virtual ~MapperInterface() { }

        virtual SkPoint map(SkPoint position) const = 0;
    };

    class TranslationMapper final : public MapperInterface {
    public:
        TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
            : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }

        SkPoint map(SkPoint position) const override {
            return position + fTranslate;
        }

    private:
        const SkPoint fTranslate;
    };

    class XScaleMapper final : public MapperInterface {
    public:
        XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
            : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { }

        SkPoint map(SkPoint position) const override {
            return {fXScale * position.fX + fTranslate.fX, fTranslate.fY};
        }

    private:
        const SkPoint fTranslate;
        const SkScalar fXScale;
    };

    // The caller must keep matrix alive while this class is used.
    class GeneralMapper final : public MapperInterface {
    public:
        GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
            : fOrigin(origin), fMatrix(matrix), fMapProc(matrix.getMapXYProc()) { }

        SkPoint map(SkPoint position) const override {
            SkPoint result;
            fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result);
            return result;
        }

    private:
        const SkPoint fOrigin;
        const SkMatrix& fMatrix;
        const SkMatrix::MapXYProc fMapProc;
    };

    typedef PolymorphicVariant<
        MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper;

    // TextAlignmentAdjustment handles shifting the glyph based on its width.
    static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
        switch (textAlignment) {
            case SkPaint::kLeft_Align:
                return {0.0f, 0.0f};
            case SkPaint::kCenter_Align:
                return {SkFixedToScalar(glyph.fAdvanceX >> 1),
                        SkFixedToScalar(glyph.fAdvanceY >> 1)};
            case SkPaint::kRight_Align:
                return {SkFixedToScalar(glyph.fAdvanceX),
                        SkFixedToScalar(glyph.fAdvanceY)};
        }
        // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy.
        SkFAIL("Should never get here.");
        return {0.0f, 0.0f};
    }

    // The "call" to SkFixedToScalar is actually a macro. It's macros all the way down.
    // Needs to be a macro because you can't have a const float unless you make it constexpr.
    #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound))

    // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel
    // positioned glyph.
    static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
        switch (axisAlignment) {
            case kX_SkAxisAlignment:
                return {SkFixedToScalar(SkGlyph::kSubpixelRound), SK_ScalarHalf};
            case kY_SkAxisAlignment:
                return {SK_ScalarHalf, kSubpixelRounding};
            case kNone_SkAxisAlignment:
                return {kSubpixelRounding, kSubpixelRounding};
        }
        SkFAIL("Should not get here.");
        return {0.0f, 0.0f};
    }

    // The SubpixelAlignment function produces a suitable position for the glyph cache to
    // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
    // of 0 is used for the sub-pixel position.
    static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) {
        switch (axisAlignment) {
            case kX_SkAxisAlignment:
                return {SkScalarToFixed(position.fX + kSubpixelRounding), 0};
            case kY_SkAxisAlignment:
                return {0, SkScalarToFixed(position.fY + kSubpixelRounding)};
            case kNone_SkAxisAlignment:
                return {SkScalarToFixed(position.fX + kSubpixelRounding),
                        SkScalarToFixed(position.fY + kSubpixelRounding)};
        }
        SkFAIL("Should not get here.");
        return {0, 0};
    }

    #undef kSubpixelRounding

    // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does
    // glyph specific position adjustment. The findAndPositionGlyph method takes text and
    // position and calls processOneGlyph with the correct glyph, final position and rounding
    // terms. The final position is not rounded yet and is the responsibility of processOneGlyph.
    template<typename ProcessOneGlyph>
    class GlyphFindAndPlaceInterface : SkNoncopyable {
    public:
        virtual ~GlyphFindAndPlaceInterface() { };

        // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and
        // returns the position of where the next glyph will be using the glyph's advance and
        // possibly kerning. The returned position is used by drawText, but ignored by drawPosText.
        // The compiler should prune all this calculation if the return value is not used.
        //
        // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
        // compile error.
        // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
        virtual SkPoint findAndPositionGlyph(
            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) {
            SkFAIL("Should never get here.");
            return {0.0f, 0.0f};
        };
    };

    // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is
    // requested. After it has found and placed the glyph it calls the templated function
    // ProcessOneGlyph in order to actually perform an action.
    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment,
             SkAxisAlignment kAxisAlignment>
    class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
    public:
        GlyphFindAndPlaceSubpixel(LookupGlyph& glyphFinder)
            : fGlyphFinder(glyphFinder) {
            FixGCC49Arm64Bug(1);
        }

        SkPoint findAndPositionGlyph(
            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
            SkPoint finalPosition = position;
            if (kTextAlignment != SkPaint::kLeft_Align) {
                // Get the width of an un-sub-pixel positioned glyph for calculating the
                // alignment. This is not needed for kLeftAlign because its adjustment is
                // always {0, 0}.
                const char* tempText = *text;
                const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText);

                if (metricGlyph.fWidth <= 0) {
                    // Exiting early, be sure to update text pointer.
                    *text = tempText;
                    return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX),
                                                   SkFixedToScalar(metricGlyph.fAdvanceY)};
                }

                // Adjust the final position by the alignment adjustment.
                finalPosition -= TextAlignmentAdjustment(kTextAlignment, metricGlyph);
            }

            // Find the glyph.
            SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPosition);
            const SkGlyph& renderGlyph =
                fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY);

            // If the glyph has no width (no pixels) then don't bother processing it.
            if (renderGlyph.fWidth > 0) {
                processOneGlyph(renderGlyph, finalPosition,
                                SubpixelPositionRounding(kAxisAlignment));
            }
            return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX),
                                           SkFixedToScalar(renderGlyph.fAdvanceY)};
        }

    private:
        LookupGlyph& fGlyphFinder;
    };

    enum SelectKerning {
        kNoKerning = false,
        kUseKerning = true
    };

    // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel
    // positioning is requested. The kUseKerning argument should be true for drawText, and false
    // for drawPosText.
    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning>
    class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
    public:
        GlyphFindAndPlaceFullPixel(LookupGlyph& glyphFinder)
            : fGlyphFinder(glyphFinder) {
            FixGCC49Arm64Bug(2);
            // Kerning can only be used with SkPaint::kLeft_Align
            static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment,
                          "Kerning can only be used with left aligned text.");
        }

        SkPoint findAndPositionGlyph(
            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
            SkPoint finalPosition = position;
            const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text);
            if (kUseKerning) {
                finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f};
            }
            if (glyph.fWidth > 0) {
                finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
                processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
            }
            return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX),
                                           SkFixedToScalar(glyph.fAdvanceY)};
        }

    private:
        LookupGlyph& fGlyphFinder;

        SkAutoKern fAutoKern;
    };

    // GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and
    // placing a glyph. There are three factors that go into the different factors.
    // * Is sub-pixel positioned - a boolean that says whether to use sub-pixel positioning.
    // * Text alignment - indicates if the glyph should be placed to the right, centered or left
    //   of a given position.
    // * Axis alignment - indicates if the glyphs final sub-pixel position should be rounded to a
    //   whole pixel if the glyph is aligned with an axis. This is only used for sub-pixel
    //   positioning and allows the baseline to look crisp.
    template<typename ProcessOneGlyph>
    using GlyphFindAndPlace = PolymorphicVariant<
        GlyphFindAndPlaceInterface<ProcessOneGlyph>,
        // Subpixel
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kLeft_Align,   kNone_SkAxisAlignment>,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kLeft_Align,   kX_SkAxisAlignment   >,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kLeft_Align,   kY_SkAxisAlignment   >,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kCenter_Align, kNone_SkAxisAlignment>,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kCenter_Align, kX_SkAxisAlignment   >,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kCenter_Align, kY_SkAxisAlignment   >,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kRight_Align,  kNone_SkAxisAlignment>,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kRight_Align,  kX_SkAxisAlignment   >,
        GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kRight_Align,  kY_SkAxisAlignment   >,
        // Full pixel
        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align,   kNoKerning>,
        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNoKerning>,
        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align,  kNoKerning>
    >;

    // InitSubpixel is a helper function for initializing all the variants of
    // GlyphFindAndPlaceSubpixel.
    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
    static void InitSubpixel(
        typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init,
        SkAxisAlignment axisAlignment,
        LookupGlyph& glyphFinder) {
        switch (axisAlignment) {
            case kX_SkAxisAlignment:
                to_init->template initialize<GlyphFindAndPlaceSubpixel<
                    ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder);
                break;
            case kNone_SkAxisAlignment:
                to_init->template initialize<GlyphFindAndPlaceSubpixel<
                    ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder);
                break;
            case kY_SkAxisAlignment:
                to_init->template initialize<GlyphFindAndPlaceSubpixel<
                    ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder);
                break;
        }
    }

    static SkPoint MeasureText(LookupGlyph& glyphFinder, const char text[], size_t byteLength) {
        SkFixed     x = 0, y = 0;
        const char* stop = text + byteLength;

        SkAutoKern  autokern;

        while (text < stop) {
            // don't need x, y here, since all subpixel variants will have the
            // same advance
            const SkGlyph& glyph = glyphFinder->lookupGlyph(&text);

            x += autokern.adjust(glyph) + glyph.fAdvanceX;
            y += glyph.fAdvanceY;
        }
        SkASSERT(text == stop);
        return {SkFixedToScalar(x), SkFixedToScalar(y)};
    }
};

template<typename ProcessOneGlyph>
inline void SkFindAndPlaceGlyph::ProcessPosText(
    SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
    SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
    SkPaint::Align textAlignment,
    SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {

    SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
    uint32_t mtype = matrix.getType();

    LookupGlyph glyphFinder(textEncoding, cache);

    // Specialized code for handling the most common case for blink. The while loop is totally
    // de-virtualized.
    if (scalarsPerPosition == 1
        && textAlignment == SkPaint::kLeft_Align
        && axisAlignment == kX_SkAxisAlignment
        && cache->isSubpixel()
        && mtype <= SkMatrix::kTranslate_Mask) {
        typedef GlyphFindAndPlaceSubpixel<
            ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment> Positioner;
        HorizontalPositions positions{pos};
        TranslationMapper mapper{matrix, offset};
        Positioner positioner(glyphFinder);
        const char* cursor = text;
        const char* stop = text + byteLength;
        while (cursor < stop) {
            SkPoint mappedPoint = mapper.TranslationMapper::map(
                positions.HorizontalPositions::nextPoint());
            positioner.Positioner::findAndPositionGlyph(
                &cursor, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph));
        }
        return;
    }

    PositionReader positionReader{
        [&](PositionReader::Variants* to_init) {
            if (2 == scalarsPerPosition) {
                to_init->initialize<ArbitraryPositions>(pos);
            } else {
                to_init->initialize<HorizontalPositions>(pos);
            }
            positionReader->forceUseForBug();
        }
    };

    Mapper mapper{
        [&](Mapper::Variants* to_init) {
            if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
                || scalarsPerPosition == 2) {
                to_init->initialize<GeneralMapper>(matrix, offset);
            } else if (mtype & SkMatrix::kScale_Mask) {
                to_init->initialize<XScaleMapper>(matrix, offset);
            } else {
                to_init->initialize<TranslationMapper>(matrix, offset);
            }
        }
    };

    GlyphFindAndPlace<ProcessOneGlyph> findAndPosition {
        [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
            if (cache->isSubpixel()) {
                switch (textAlignment) {
                    case SkPaint::kLeft_Align:
                        InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
                            to_init, axisAlignment, glyphFinder);
                        break;
                    case SkPaint::kCenter_Align:
                        InitSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>(
                            to_init, axisAlignment, glyphFinder);
                        break;
                    case SkPaint::kRight_Align:
                        InitSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>(
                            to_init, axisAlignment, glyphFinder);
                        break;
                }
            } else {
                switch (textAlignment) {
                    case SkPaint::kLeft_Align:
                        to_init->template initialize<
                            GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
                            SkPaint::kLeft_Align, kNoKerning>>(glyphFinder);
                        break;
                    case SkPaint::kCenter_Align:
                        to_init->template initialize<
                            GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
                            SkPaint::kCenter_Align, kNoKerning>>(glyphFinder);
                        break;
                    case SkPaint::kRight_Align:
                        to_init->template initialize<
                            GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
                            SkPaint::kRight_Align, kNoKerning>>(glyphFinder);
                        break;
                }
            }
        }
    };

    const char* stop = text + byteLength;
    while (text < stop) {
        SkPoint mappedPoint = mapper->map(positionReader->nextPoint());
        findAndPosition->findAndPositionGlyph(
            &text, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph));
    }
}

template<typename ProcessOneGlyph>
inline void SkFindAndPlaceGlyph::ProcessText(
    SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
    SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
    SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {

    // transform the starting point
    matrix.mapPoints(&offset, 1);

    LookupGlyph glyphFinder(textEncoding, cache);

    // need to measure first
    if (textAlignment != SkPaint::kLeft_Align) {
        SkVector stop = MeasureText(glyphFinder, text, byteLength);

        if (textAlignment == SkPaint::kCenter_Align) {
            stop *= SK_ScalarHalf;
        }
        offset -= stop;
    }

    GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
        [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
            if (cache->isSubpixel()) {
                SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
                InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
                    to_init, axisAlignment, glyphFinder);
            } else {
                to_init->template initialize<
                    GlyphFindAndPlaceFullPixel<
                        ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>>(glyphFinder);
            }
        }
    };

    const char* stop = text + byteLength;
    SkPoint current = offset;
    while (text < stop) {
        current =
            findAndPosition->findAndPositionGlyph(
                &text, current, std::forward<ProcessOneGlyph>(processOneGlyph));

    }
}

#endif  // SkFindAndPositionGlyph_DEFINED