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