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