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.
21template<typename... Ts> struct SkMaxSizeOf;
22
23template<>
24struct SkMaxSizeOf<> {
25    static const size_t value = 0;
26};
27
28template<typename H, typename... Ts>
29struct 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.
42extern void FixGCC49Arm64Bug(int v);
43
44class SkFindAndPlaceGlyph {
45public:
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
74private:
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
583template<typename ProcessOneGlyph>
584inline 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
689template<typename ProcessOneGlyph>
690inline 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