Home | History | Annotate | Download | only in core
      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:
     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:
     64         explicit UtfNGlyphFinder(SkGlyphCache* cache)
     65             : fCache(cache) {
     66             SkASSERT(cache != nullptr);
     67         }
     68 
     69         const SkGlyph& lookupGlyph(const char** text) override {
     70             SkASSERT(text != nullptr);
     71             return fCache->getUnicharMetrics(nextUnichar(text));
     72         }
     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:
     85         explicit Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
     86 
     87     private:
     88         SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); }
     89     };
     90 
     91     class Utf16GlyphFinder final : public UtfNGlyphFinder {
     92     public:
     93         explicit Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
     94 
     95     private:
     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:
    103         explicit Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
    104 
    105     private:
    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:
    116         explicit GlyphIdGlyphFinder(SkGlyphCache* cache)
    117             : fCache(cache) {
    118             SkASSERT(cache != nullptr);
    119         }
    120 
    121         const SkGlyph& lookupGlyph(const char** text) override {
    122             return fCache->getGlyphIDMetrics(nextGlyphId(text));
    123         }
    124         const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
    125             return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y);
    126         }
    127 
    128     private:
    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 
    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:
    162         virtual ~PositionReaderInterface() { }
    163         virtual SkPoint nextPoint() = 0;
    164     };
    165 
    166     class HorizontalPositions final : public PositionReaderInterface {
    167     public:
    168         explicit HorizontalPositions(const SkScalar* positions)
    169             : fPositions(positions) { }
    170 
    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:
    182         explicit ArbitraryPositions(const SkScalar* positions)
    183             : fPositions(positions) { }
    184 
    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:
    202         virtual ~MapperInterface() { }
    203 
    204         virtual SkPoint map(SkPoint position) const = 0;
    205     };
    206 
    207     class TranslationMapper final : public MapperInterface {
    208     public:
    209         TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
    210             : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
    211 
    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:
    222         XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
    223             : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { }
    224 
    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:
    237         GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
    238             : fOrigin(origin), fMatrix(matrix), fMapProc(matrix.getMapXYProc()) { }
    239 
    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.
    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.
    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.
    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:
    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
    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:
    340         explicit GlyphFindAndPlaceSubpixel(GlyphFinderInterface* glyphFinder)
    341             : fGlyphFinder(glyphFinder) { }
    342 
    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:
    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 
    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>
    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 
    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>
    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>
    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