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 "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:
     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 
    463 template<typename 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>
    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