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 
21 class SkFindAndPlaceGlyph {
22 public:
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 
51 private:
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:
~GlyphFinderInterface()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:
UtfNGlyphFinder(SkGlyphCache * cache)65         explicit UtfNGlyphFinder(SkGlyphCache* cache)
66             : fCache(cache) {
67             SkASSERT(cache != nullptr);
68         }
69 
lookupGlyph(const char ** text)70         const SkGlyph& lookupGlyph(const char** text) override {
71             SkASSERT(text != nullptr);
72             return fCache->getUnicharMetrics(nextUnichar(text));
73         }
lookupGlyphXY(const char ** text,SkFixed x,SkFixed y)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:
Utf8GlyphFinder(SkGlyphCache * cache)86         explicit Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
87 
88     private:
nextUnichar(const char ** text)89         SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); }
90     };
91 
92     class Utf16GlyphFinder final : public UtfNGlyphFinder {
93     public:
Utf16GlyphFinder(SkGlyphCache * cache)94         explicit Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
95 
96     private:
nextUnichar(const char ** text)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:
Utf32GlyphFinder(SkGlyphCache * cache)104         explicit Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
105 
106     private:
nextUnichar(const char ** text)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:
GlyphIdGlyphFinder(SkGlyphCache * cache)117         explicit GlyphIdGlyphFinder(SkGlyphCache* cache)
118             : fCache(cache) {
119             SkASSERT(cache != nullptr);
120         }
121 
lookupGlyph(const char ** text)122         const SkGlyph& lookupGlyph(const char** text) override {
123             return fCache->getGlyphIDMetrics(nextGlyphId(text));
124         }
lookupGlyphXY(const char ** text,SkFixed x,SkFixed y)125         const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
126             return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y);
127         }
128 
129     private:
nextGlyphId(const char ** text)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 
getGlyphFinder(SkArenaAlloc * arena,SkPaint::TextEncoding encoding,SkGlyphCache * cache)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:
~PositionReaderInterface()163         virtual ~PositionReaderInterface() { }
164         virtual SkPoint nextPoint() = 0;
165     };
166 
167     class HorizontalPositions final : public PositionReaderInterface {
168     public:
HorizontalPositions(const SkScalar * positions)169         explicit HorizontalPositions(const SkScalar* positions)
170             : fPositions(positions) { }
171 
nextPoint()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:
ArbitraryPositions(const SkScalar * positions)183         explicit ArbitraryPositions(const SkScalar* positions)
184             : fPositions(positions) { }
185 
nextPoint()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:
~MapperInterface()203         virtual ~MapperInterface() { }
204 
205         virtual SkPoint map(SkPoint position) const = 0;
206     };
207 
208     class TranslationMapper final : public MapperInterface {
209     public:
TranslationMapper(const SkMatrix & matrix,const SkPoint origin)210         TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
211             : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
212 
map(SkPoint position)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:
XScaleMapper(const SkMatrix & matrix,const SkPoint origin)223         XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
224             : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { }
225 
map(SkPoint position)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:
GeneralMapper(const SkMatrix & matrix,const SkPoint origin)238         GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
239             : fOrigin(origin), fMatrix(matrix), fMapProc(SkMatrixPriv::GetMapXYProc(matrix)) { }
240 
map(SkPoint position)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.
TextAlignmentAdjustment(SkPaint::Align textAlignment,const SkGlyph & glyph)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.
SubpixelPositionRounding(SkAxisAlignment axisAlignment)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.
SubpixelAlignment(SkAxisAlignment axisAlignment,SkPoint 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:
~GlyphFindAndPlaceInterface()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
findAndPositionGlyph(const char ** text,SkPoint position,ProcessOneGlyph && processOneGlyph)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:
GlyphFindAndPlaceSubpixel(GlyphFinderInterface * glyphFinder)341         explicit GlyphFindAndPlaceSubpixel(GlyphFinderInterface* glyphFinder)
342             : fGlyphFinder(glyphFinder) { }
343 
findAndPositionGlyph(const char ** text,SkPoint position,ProcessOneGlyph && processOneGlyph)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:
GlyphFindAndPlaceFullPixel(GlyphFinderInterface * glyphFinder)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 
findAndPositionGlyph(const char ** text,SkPoint position,ProcessOneGlyph && processOneGlyph)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>
getSubpixel(SkArenaAlloc * arena,SkAxisAlignment axisAlignment,GlyphFinderInterface * glyphFinder)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 
MeasureText(GlyphFinderInterface * glyphFinder,const char text[],size_t byteLength)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 
463 template<typename ProcessOneGlyph>
ProcessPosText(SkPaint::TextEncoding textEncoding,const char text[],size_t byteLength,SkPoint offset,const SkMatrix & matrix,const SkScalar pos[],int scalarsPerPosition,SkPaint::Align textAlignment,SkGlyphCache * cache,ProcessOneGlyph && processOneGlyph)464 inline 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 
569 template<typename ProcessOneGlyph>
ProcessText(SkPaint::TextEncoding textEncoding,const char text[],size_t byteLength,SkPoint offset,const SkMatrix & matrix,SkPaint::Align textAlignment,SkGlyphCache * cache,ProcessOneGlyph && processOneGlyph)570 inline 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