1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2015 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#ifndef SkFindAndPositionGlyph_DEFINED
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#define SkFindAndPositionGlyph_DEFINED
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkArenaAlloc.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAutoKern.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGlyph.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGlyphCache.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMatrixPriv.h"
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPaint.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTemplates.h"
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkUtils.h"
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include <utility>
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass SkFindAndPlaceGlyph {
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    template<typename ProcessOneGlyph>
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static void ProcessText(
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::TextEncoding, const char text[], size_t byteLength,
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // multiplicity. It figures out the glyph, position and rounding and pass those parameters to
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // processOneGlyph.
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The routine processOneGlyph passed in by the client has the following signature:
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding);
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * Sub-pixel positioning (2) - use sub-pixel positioning.
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * Text alignment (3) - text alignment with respect to the glyph's width.
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * Matrix type (3) - special cases for translation and X-coordinate scaling.
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * Components per position (2) - the positions vector can have a common Y with different
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //   Xs, or XY-pairs.
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //   to a whole coordinate instead of using sub-pixel positioning.
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The number of variations is 108 for sub-pixel and 36 for full-pixel.
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // This routine handles all of them using inline polymorphic variable (no heap allocation).
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    template<typename ProcessOneGlyph>
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static void ProcessPosText(
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::TextEncoding, const char text[], size_t byteLength,
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::Align textAlignment,
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // GlyphFinderInterface is the polymorphic base for classes that parse a stream of chars into
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // the right UniChar (or GlyphID) and lookup up the glyph on the cache. The concrete
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // implementations are: Utf8GlyphFinder, Utf16GlyphFinder, Utf32GlyphFinder,
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // and GlyphIdGlyphFinder.
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class GlyphFinderInterface {
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual ~GlyphFinderInterface() {}
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual const SkGlyph& lookupGlyph(const char** text) = 0;
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) = 0;
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class UtfNGlyphFinder : public GlyphFinderInterface {
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit UtfNGlyphFinder(SkGlyphCache* cache)
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fCache(cache) {
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(cache != nullptr);
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkGlyph& lookupGlyph(const char** text) override {
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(text != nullptr);
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return fCache->getUnicharMetrics(nextUnichar(text));
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(text != nullptr);
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return fCache->getUnicharMetrics(nextUnichar(text), x, y);
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual SkUnichar nextUnichar(const char** text) = 0;
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkGlyphCache* fCache;
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class Utf8GlyphFinder final : public UtfNGlyphFinder {
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); }
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class Utf16GlyphFinder final : public UtfNGlyphFinder {
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkUnichar nextUnichar(const char** text) override {
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return SkUTF16_NextUnichar((const uint16_t**)text);
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class Utf32GlyphFinder final : public UtfNGlyphFinder {
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkUnichar nextUnichar(const char** text) override {
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const int32_t* ptr = *(const int32_t**)text;
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkUnichar uni = *ptr++;
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *text = (const char*)ptr;
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return uni;
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class GlyphIdGlyphFinder final : public GlyphFinderInterface {
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit GlyphIdGlyphFinder(SkGlyphCache* cache)
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fCache(cache) {
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(cache != nullptr);
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkGlyph& lookupGlyph(const char** text) override {
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return fCache->getGlyphIDMetrics(nextGlyphId(text));
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y);
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint16_t nextGlyphId(const char** text) {
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(text != nullptr);
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const uint16_t* ptr = *(const uint16_t**)text;
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint16_t glyphID = *ptr;
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ptr += 1;
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *text = (const char*)ptr;
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return glyphID;
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkGlyphCache* fCache;
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static GlyphFinderInterface* getGlyphFinder(
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkArenaAlloc* arena, SkPaint::TextEncoding encoding, SkGlyphCache* cache) {
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch(encoding) {
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kUTF8_TextEncoding:
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<Utf8GlyphFinder>(cache);
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kUTF16_TextEncoding:
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<Utf16GlyphFinder>(cache);
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kUTF32_TextEncoding:
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<Utf32GlyphFinder>(cache);
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kGlyphID_TextEncoding:
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<GlyphIdGlyphFinder>(cache);
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_ABORT("Should not get here.");
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // PositionReaderInterface reads a point from the pos vector.
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * HorizontalPositions - assumes a common Y for many X values.
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * ArbitraryPositions - a list of (X,Y) pairs.
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class PositionReaderInterface {
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual ~PositionReaderInterface() { }
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual SkPoint nextPoint() = 0;
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class HorizontalPositions final : public PositionReaderInterface {
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit HorizontalPositions(const SkScalar* positions)
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fPositions(positions) { }
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint nextPoint() override {
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkScalar x = *fPositions++;
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return {x, 0};
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkScalar* fPositions;
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class ArbitraryPositions final : public PositionReaderInterface {
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit ArbitraryPositions(const SkScalar* positions)
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fPositions(positions) { }
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint nextPoint() override {
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPoint to_return{fPositions[0], fPositions[1]};
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPositions += 2;
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return to_return;
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkScalar* fPositions;
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // MapperInterface given a point map it through the matrix. There are several shortcut
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // variants.
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * TranslationMapper - assumes a translation only matrix.
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * XScaleMapper - assumes an X scaling and a translation.
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // * GeneralMapper - Does all other matricies.
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class MapperInterface {
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual ~MapperInterface() { }
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual SkPoint map(SkPoint position) const = 0;
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class TranslationMapper final : public MapperInterface {
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint map(SkPoint position) const override {
214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return position + fTranslate;
215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPoint fTranslate;
219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class XScaleMapper final : public MapperInterface {
222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { }
225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint map(SkPoint position) const override {
227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return {fXScale * position.fX + fTranslate.fX, fTranslate.fY};
228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPoint fTranslate;
232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkScalar fXScale;
233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The caller must keep matrix alive while this class is used.
236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class GeneralMapper final : public MapperInterface {
237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fOrigin(origin), fMatrix(matrix), fMapProc(SkMatrixPriv::GetMapXYProc(matrix)) { }
240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint map(SkPoint position) const override {
242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPoint result;
243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result);
244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return result;
245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPoint fOrigin;
249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkMatrix& fMatrix;
250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkMatrixPriv::MapXYProc fMapProc;
251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TextAlignmentAdjustment handles shifting the glyph based on its width.
254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (textAlignment) {
256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kLeft_Align:
257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {0.0f, 0.0f};
258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kCenter_Align:
259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {SkFloatToScalar(glyph.fAdvanceX) / 2,
260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkFloatToScalar(glyph.fAdvanceY) / 2};
261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kRight_Align:
262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {SkFloatToScalar(glyph.fAdvanceX),
263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkFloatToScalar(glyph.fAdvanceY)};
264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy.
266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_ABORT("Should never get here.");
267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return {0.0f, 0.0f};
268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The "call" to SkFixedToScalar is actually a macro. It's macros all the way down.
271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Needs to be a macro because you can't have a const float unless you make it constexpr.
272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound))
273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel
275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // positioned glyph.
276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (axisAlignment) {
278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kX_SkAxisAlignment:
279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {kSubpixelRounding, SK_ScalarHalf};
280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kY_SkAxisAlignment:
281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {SK_ScalarHalf, kSubpixelRounding};
282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kNone_SkAxisAlignment:
283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {kSubpixelRounding, kSubpixelRounding};
284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_ABORT("Should not get here.");
286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return {0.0f, 0.0f};
287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The SubpixelAlignment function produces a suitable position for the glyph cache to
290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // of 0 is used for the sub-pixel position.
292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) {
293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Only the fractional part of position.fX and position.fY matter, because the result of
294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // this function will just be passed to FixedToSub.
295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (axisAlignment) {
296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kX_SkAxisAlignment:
297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 0};
298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kY_SkAxisAlignment:
299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {0, SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)};
300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kNone_SkAxisAlignment:
301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding),
302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)};
303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_ABORT("Should not get here.");
305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return {0, 0};
306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    #undef kSubpixelRounding
309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does
311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // glyph specific position adjustment. The findAndPositionGlyph method takes text and
312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // position and calls processOneGlyph with the correct glyph, final position and rounding
313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // terms. The final position is not rounded yet and is the responsibility of processOneGlyph.
314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    template<typename ProcessOneGlyph>
315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class GlyphFindAndPlaceInterface : SkNoncopyable {
316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual ~GlyphFindAndPlaceInterface() { }
318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and
320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // returns the position of where the next glyph will be using the glyph's advance and
321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // possibly kerning. The returned position is used by drawText, but ignored by drawPosText.
322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The compiler should prune all this calculation if the return value is not used.
323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        //
324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // compile error.
326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        virtual SkPoint findAndPositionGlyph(
328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) {
329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SK_ABORT("Should never get here.");
330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return {0.0f, 0.0f};
331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is
335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // requested. After it has found and placed the glyph it calls the templated function
336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // ProcessOneGlyph in order to actually perform an action.
337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment,
338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             SkAxisAlignment kAxisAlignment>
339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit GlyphFindAndPlaceSubpixel(GlyphFinderInterface* glyphFinder)
342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fGlyphFinder(glyphFinder) { }
343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint findAndPositionGlyph(
345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (kTextAlignment != SkPaint::kLeft_Align) {
348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // Get the width of an un-sub-pixel positioned glyph for calculating the
349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // alignment. This is not needed for kLeftAlign because its adjustment is
350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // always {0, 0}.
351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                const char* tempText = *text;
352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText);
353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (metricGlyph.fWidth <= 0) {
355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // Exiting early, be sure to update text pointer.
356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    *text = tempText;
357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    return position + SkPoint{SkFloatToScalar(metricGlyph.fAdvanceX),
358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              SkFloatToScalar(metricGlyph.fAdvanceY)};
359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // Adjust the final position by the alignment adjustment.
362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                position -= TextAlignmentAdjustment(kTextAlignment, metricGlyph);
363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Find the glyph.
366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkIPoint lookupPosition = SkScalarsAreFinite(position.fX, position.fY)
367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      ? SubpixelAlignment(kAxisAlignment, position)
368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      : SkIPoint{0, 0};
369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const SkGlyph& renderGlyph =
370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY);
371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // If the glyph has no width (no pixels) then don't bother processing it.
373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (renderGlyph.fWidth > 0) {
374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                processOneGlyph(renderGlyph, position,
375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                SubpixelPositionRounding(kAxisAlignment));
376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return position + SkPoint{SkFloatToScalar(renderGlyph.fAdvanceX),
378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      SkFloatToScalar(renderGlyph.fAdvanceY)};
379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GlyphFinderInterface* fGlyphFinder;
383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    enum SelectKerning {
386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        kNoKerning = false,
387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        kUseKerning = true
388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel
391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // positioning is requested. The kUseKerning argument should be true for drawText, and false
392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // for drawPosText.
393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning>
394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    public:
396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit GlyphFindAndPlaceFullPixel(GlyphFinderInterface* glyphFinder)
397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fGlyphFinder(glyphFinder) {
398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Kerning can only be used with SkPaint::kLeft_Align
399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment,
400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          "Kerning can only be used with left aligned text.");
401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint findAndPositionGlyph(
404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPoint finalPosition = position;
406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text);
407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (kUseKerning) {
408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                finalPosition += {fAutoKern.adjust(glyph), 0.0f};
409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (glyph.fWidth > 0) {
411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return finalPosition + SkPoint{SkFloatToScalar(glyph.fAdvanceX),
415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                           SkFloatToScalar(glyph.fAdvanceY)};
416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    private:
419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GlyphFinderInterface* fGlyphFinder;
420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAutoKern fAutoKern;
422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    template <typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static GlyphFindAndPlaceInterface<ProcessOneGlyph>* getSubpixel(
426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkArenaAlloc* arena, SkAxisAlignment axisAlignment, GlyphFinderInterface* glyphFinder)
427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (axisAlignment) {
429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kX_SkAxisAlignment:
430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<GlyphFindAndPlaceSubpixel<
431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder);
432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kNone_SkAxisAlignment:
433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<GlyphFindAndPlaceSubpixel<
434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder);
435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case kY_SkAxisAlignment:
436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return arena->make<GlyphFindAndPlaceSubpixel<
437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder);
438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_ABORT("Should never get here.");
440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static SkPoint MeasureText(
444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GlyphFinderInterface* glyphFinder, const char text[], size_t byteLength) {
445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar    x = 0, y = 0;
446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const char* stop = text + byteLength;
447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAutoKern  autokern;
449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        while (text < stop) {
451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // don't need x, y here, since all subpixel variants will have the
452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // same advance
453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const SkGlyph& glyph = glyphFinder->lookupGlyph(&text);
454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            x += autokern.adjust(glyph) + SkFloatToScalar(glyph.fAdvanceX);
456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            y += SkFloatToScalar(glyph.fAdvanceY);
457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(text == stop);
459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return {x, y};
460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robottemplate<typename ProcessOneGlyph>
464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotinline void SkFindAndPlaceGlyph::ProcessPosText(
465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint::Align textAlignment,
468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText();
471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t mtype = matrix.getType();
472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Specialized code for handling the most common case for blink.
474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (textEncoding == SkPaint::kGlyphID_TextEncoding
475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        && textAlignment == SkPaint::kLeft_Align
476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        && axisAlignment == kX_SkAxisAlignment
477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        && cache->isSubpixel()
478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        && mtype <= SkMatrix::kTranslate_Mask)
479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GlyphIdGlyphFinder glyphFinder(cache);
481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        using Positioner =
482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            GlyphFindAndPlaceSubpixel <
483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment>;
484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        HorizontalPositions hPositions{pos};
485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ArbitraryPositions  aPositions{pos};
486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        PositionReaderInterface* positions = nullptr;
487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (scalarsPerPosition == 2) {
488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            positions = &aPositions;
489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            positions = &hPositions;
491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        TranslationMapper mapper{matrix, offset};
493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        Positioner positioner(&glyphFinder);
494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const char* cursor = text;
495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const char* stop = text + byteLength;
496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        while (cursor < stop) {
497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPoint mappedPoint = mapper.TranslationMapper::map(positions->nextPoint());
498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            positioner.Positioner::findAndPositionGlyph(
499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                &cursor, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph));
500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSTArenaAlloc<120> arena;
505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache);
507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    PositionReaderInterface* positionReader = nullptr;
509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (2 == scalarsPerPosition) {
510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        positionReader = arena.make<ArbitraryPositions>(pos);
511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        positionReader = arena.make<HorizontalPositions>(pos);
513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    MapperInterface* mapper = nullptr;
516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        || scalarsPerPosition == 2) {
518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        mapper = arena.make<GeneralMapper>(matrix, offset);
519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (mtype & SkMatrix::kScale_Mask) {
520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        mapper = arena.make<XScaleMapper>(matrix, offset);
521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        mapper = arena.make<TranslationMapper>(matrix, offset);
523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr;
526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (cache->isSubpixel()) {
527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (textAlignment) {
528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kLeft_Align:
529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    &arena, axisAlignment, glyphFinder);
531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kCenter_Align:
533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>(
534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    &arena, axisAlignment, glyphFinder);
535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kRight_Align:
537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>(
538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    &arena, axisAlignment, glyphFinder);
539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (textAlignment) {
543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kLeft_Align:
544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                findAndPosition = arena.make<
545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkPaint::kLeft_Align, kNoKerning>>(glyphFinder);
547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kCenter_Align:
549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                findAndPosition = arena.make<
550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkPaint::kCenter_Align, kNoKerning>>(glyphFinder);
552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkPaint::kRight_Align:
554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                findAndPosition = arena.make<
555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkPaint::kRight_Align, kNoKerning>>(glyphFinder);
557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* stop = text + byteLength;
562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (text < stop) {
563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint mappedPoint = mapper->map(positionReader->nextPoint());
564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        findAndPosition->findAndPositionGlyph(
565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &text, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph));
566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robottemplate<typename ProcessOneGlyph>
570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotinline void SkFindAndPlaceGlyph::ProcessText(
571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSTArenaAlloc<64> arena;
575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // transform the starting point
577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    matrix.mapPoints(&offset, 1);
578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache);
580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // need to measure first
582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (textAlignment != SkPaint::kLeft_Align) {
583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkVector stop = MeasureText(glyphFinder, text, byteLength);
584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (textAlignment == SkPaint::kCenter_Align) {
586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            stop *= SK_ScalarHalf;
587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        offset -= stop;
589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
590fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
591fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr;
592fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (cache->isSubpixel()) {
593fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText();
594fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
595fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &arena, axisAlignment, glyphFinder);
596fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
597fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        using FullPixel =
598fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>;
599fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        findAndPosition = arena.make<FullPixel>(glyphFinder);
600fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
601fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
602fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* stop = text + byteLength;
603fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPoint current = offset;
604fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (text < stop) {
605fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        current =
606fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            findAndPosition->findAndPositionGlyph(
607fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                &text, current, std::forward<ProcessOneGlyph>(processOneGlyph));
608fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
609fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
610fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
611fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
612fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif  // SkFindAndPositionGlyph_DEFINED
613