Convert drawText to using the find and place code.
authorherb <herb@google.com>
Wed, 18 Nov 2015 18:54:39 +0000 (10:54 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 18 Nov 2015 18:54:39 +0000 (10:54 -0800)
BUG=skia:

Review URL: https://codereview.chromium.org/1448453002

src/core/SkDraw.cpp
src/core/SkFindAndPlaceGlyph.h
src/gpu/GrAtlasTextContext.cpp

index 4be510c2c7c207394a11918c3f765b1cd7a6731b..ca84e629eeaf81a5d06e564f501180acb13bd55d 100644 (file)
@@ -34,7 +34,6 @@
 #include "SkUtils.h"
 #include "SkVertState.h"
 
-#include "SkAutoKern.h"
 #include "SkBitmapProcShader.h"
 #include "SkDrawProcs.h"
 #include "SkMatrixUtils.h"
@@ -53,7 +52,7 @@ public:
                         const SkPaint& paint, bool drawCoverage = false) {
         fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAllocator, drawCoverage);
     }
-    
+
     SkBlitter*  operator->() { return fBlitter; }
     SkBlitter*  get() const { return fBlitter; }
 
@@ -1374,26 +1373,6 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& ori
 #include "SkTextToPathIter.h"
 #include "SkUtils.h"
 
-static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
-                const char text[], size_t byteLength, SkVector* stopVector) {
-    SkFixed     x = 0, y = 0;
-    const char* stop = text + byteLength;
-
-    SkAutoKern  autokern;
-
-    while (text < stop) {
-        // don't need x, y here, since all subpixel variants will have the
-        // same advance
-        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
-
-        x += autokern.adjust(glyph) + glyph.fAdvanceX;
-        y += glyph.fAdvanceY;
-    }
-    stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
-
-    SkASSERT(text == stop);
-}
-
 bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
     // hairline glyphs are fast enough so we don't need to cache them
     if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
@@ -1601,38 +1580,10 @@ void SkDraw::drawText(const char text[], size_t byteLength,
         return;
     }
 
-    SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
-
+    SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
     SkAutoGlyphCache    autoCache(paint, &fDevice->surfaceProps(), fMatrix);
     SkGlyphCache*       cache = autoCache.getCache();
 
-    // transform our starting point
-    {
-        SkPoint loc;
-        fMatrix->mapXY(x, y, &loc);
-        x = loc.fX;
-        y = loc.fY;
-    }
-
-    // need to measure first
-    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
-        SkVector    stop;
-
-        measure_text(cache, glyphCacheProc, text, byteLength, &stop);
-
-        SkScalar    stopX = stop.fX;
-        SkScalar    stopY = stop.fY;
-
-        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
-            stopX = SkScalarHalf(stopX);
-            stopY = SkScalarHalf(stopY);
-        }
-        x -= stopX;
-        y -= stopY;
-    }
-
-    const char* stop = text + byteLength;
-
     SkAAClipBlitter     aaBlitter;
     SkAutoBlitterChoose blitterChooser;
     SkBlitter*          blitter = nullptr;
@@ -1645,38 +1596,16 @@ void SkDraw::drawText(const char text[], size_t byteLength,
         }
     }
 
-    SkAutoKern          autokern;
     SkDraw1Glyph        d1g;
     SkDraw1Glyph::Proc  proc = d1g.init(this, blitter, cache, paint);
 
-    SkFixed fxMask = ~0;
-    SkFixed fyMask = ~0;
-    if (cache->isSubpixel()) {
-        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
-        if (kX_SkAxisAlignment == baseline) {
-            fyMask = 0;
-            d1g.fHalfSampleY = SK_ScalarHalf;
-        } else if (kY_SkAxisAlignment == baseline) {
-            fxMask = 0;
-            d1g.fHalfSampleX = SK_ScalarHalf;
-        }
-    }
-
-    Sk48Dot16 fx = SkScalarTo48Dot16(x + d1g.fHalfSampleX);
-    Sk48Dot16 fy = SkScalarTo48Dot16(y + d1g.fHalfSampleY);
-
-    while (text < stop) {
-        const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
-
-        fx += autokern.adjust(glyph);
-
-        if (glyph.fWidth) {
-            proc(d1g, fx, fy, glyph);
+    SkFindAndPlaceGlyph::ProcessText(
+        text, byteLength, {x, y}, *fMatrix, paint.getTextAlign(), glyphCacheProc, cache,
+        [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
+            position += rounding;
+            proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph);
         }
-
-        fx += glyph.fAdvanceX;
-        fy += glyph.fAdvanceY;
-    }
+    );
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1773,7 +1702,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
         textAlignment, glyphCacheProc, cache,
         [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
             position += rounding;
-            proc(d1g, SkScalarToFixed(position.fX), SkScalarToFixed(position.fY), glyph);
+            proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph);
         }
     );
 }
index 3de4c61c42529d77b1c88216b4e70a9e68b40343..15000b3d58395ab6e028b3a0555052c5b710d46d 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef SkFindAndPositionGlyph_DEFINED
 #define SkFindAndPositionGlyph_DEFINED
 
+#include "SkAutoKern.h"
 #include "SkGlyph.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
@@ -30,6 +31,10 @@ struct SkMaxSizeOf<H, Ts...> {
 
 class SkFindAndPlaceGlyph {
 public:
+    template<typename ProcessOneGlyph>
+    static void ProcessText(const char text[], size_t byteLength, SkPoint offset, const
+    SkMatrix& matrix, SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc,
+                            SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
     // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
     // multiplicity. It figures out the glyph, position and rounding and pass those parameters to
     // processOneGlyph.
@@ -48,7 +53,7 @@ public:
     // This routine handles all of them using inline polymorphic variable (no heap allocation).
     template<typename ProcessOneGlyph>
     static void ProcessPosText(const char text[], size_t byteLength,
-                               const SkPoint& offset, const SkMatrix& matrix,
+                               SkPoint offset, const SkMatrix& matrix,
                                const SkScalar pos[], int scalarsPerPosition,
                                SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc,
                                SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
@@ -142,7 +147,8 @@ private:
     typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, ArbitraryPositions>
         PositionReader;
 
-    // MapperInterface given a point map it through the matrix. There are several shortcut variants.
+    // MapperInterface given a point map it through the matrix. There are several shortcut
+    // variants.
     // * TranslationMapper - assumes a translation only matrix.
     // * XScaleMapper - assumes an X scaling and a translation.
     // * GeneralMapper - Does all other matricies.
@@ -201,7 +207,7 @@ private:
     typedef PolymorphicVariant<
         MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper;
 
-    // Text alignment handles shifting the glyph based on its width.
+    // TextAlignmentAdjustment handles shifting the glyph based on its width.
     static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
         switch (textAlignment) {
             case SkPaint::kLeft_Align:
@@ -222,8 +228,7 @@ private:
     // Needs to be a macro because you can't have a const float unless you make it constexpr.
     #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound))
 
-    // Functions for handling sub-pixel aligned positions.
-    // The subpixel_position_rounding function returns a point suitable for rounding a sub-pixel
+    // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel
     // positioned glyph.
     static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
         switch (axisAlignment) {
@@ -238,7 +243,7 @@ private:
         return {0.0f, 0.0f};
     }
 
-    // The subpixel_position_alignment function produces a suitable position for the glyph cache to
+    // The SubpixelAlignment function produces a suitable position for the glyph cache to
     // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
     // of 0 is used for the sub-pixel position.
     static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) {
@@ -266,36 +271,47 @@ private:
     public:
         virtual ~GlyphFindAndPlaceInterface() { };
 
+        // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and
+        // returns the position of where the next glyph will be using the glyph's advance and
+        // possibly kerning. The returned position is used by drawText, but ignored by drawPosText.
+        // The compiler should prune all this calculation if the return value is not used.
+        //
         // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
         // compile error.
         // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
-        virtual void findAndPositionGlyph(const char** text, SkPoint position,
-                                          ProcessOneGlyph&& processOneGlyph) { };
+        virtual SkPoint findAndPositionGlyph(
+            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) {
+            SkFAIL("Should never get here.");
+            return {0.0f, 0.0f};
+        };
     };
 
     // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is
     // requested. After it has found and placed the glyph it calls the templated function
     // ProcessOneGlyph in order to actually perform an action.
-    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SkAxisAlignment kAxisAlignment>
+    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment,
+             SkAxisAlignment kAxisAlignment>
     class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
     public:
         GlyphFindAndPlaceSubpixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
-            : fCache(cache), fGlyphCacheProc(glyphCacheProc) {
-        }
+            : fCache(cache)
+            , fGlyphCacheProc(glyphCacheProc) { }
 
-        void findAndPositionGlyph(const char** text, SkPoint position,
-                                  ProcessOneGlyph&& processOneGlyph) override {
+        SkPoint findAndPositionGlyph(
+            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
             SkPoint finalPosition = position;
             if (kTextAlignment != SkPaint::kLeft_Align) {
-                // Get the width of an un-sub-pixel positioned glyph for calculating the alignment.
-                // This is not needed for kLeftAlign because its adjustment is always {0, 0}.
+                // Get the width of an un-sub-pixel positioned glyph for calculating the
+                // alignment. This is not needed for kLeftAlign because its adjustment is
+                // always {0, 0}.
                 const char* tempText = *text;
                 const SkGlyph &metricGlyph = fGlyphCacheProc(fCache, &tempText, 0, 0);
 
                 if (metricGlyph.fWidth <= 0) {
                     // Exiting early, be sure to update text pointer.
                     *text = tempText;
-                    return;
+                    return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX),
+                                                   SkFixedToScalar(metricGlyph.fAdvanceY)};
                 }
 
                 // Adjust the final position by the alignment adjustment.
@@ -304,14 +320,16 @@ private:
 
             // Find the glyph.
             SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPosition);
-            const SkGlyph& renderGlyph = fGlyphCacheProc(
-                fCache, text, lookupPosition.fX, lookupPosition.fY);
+            const SkGlyph& renderGlyph =
+                fGlyphCacheProc(fCache, text, lookupPosition.fX, lookupPosition.fY);
 
             // If the glyph has no width (no pixels) then don't bother processing it.
             if (renderGlyph.fWidth > 0) {
                 processOneGlyph(renderGlyph, finalPosition,
                                 SubpixelPositionRounding(kAxisAlignment));
             }
+            return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX),
+                                           SkFixedToScalar(renderGlyph.fAdvanceY)};
         }
 
     private:
@@ -319,28 +337,43 @@ private:
         SkDrawCacheProc fGlyphCacheProc;
     };
 
+    enum SelectKerning {
+        kNoKerning = false,
+        kUseKerning = true
+    };
+
     // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel
-    // positioning is requested.
-    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
+    // positioning is requested. The kUseKerning argument should be true for drawText, and false
+    // for drawPosText.
+    template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning>
     class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
     public:
         GlyphFindAndPlaceFullPixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
-            : fCache(cache), fGlyphCacheProc(glyphCacheProc) { }
+            : fCache(cache), fGlyphCacheProc(glyphCacheProc) {
+            // Kerning can only be used with SkPaint::kLeft_Align
+            static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment,
+                          "Kerning can only be used with left aligned text.");
+        }
 
-        void findAndPositionGlyph(const char** text, SkPoint position,
-                                  ProcessOneGlyph&& processOneGlyph) override {
+        SkPoint findAndPositionGlyph(
+            const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
             SkPoint finalPosition = position;
             const SkGlyph& glyph = fGlyphCacheProc(fCache, text, 0, 0);
-            if (glyph.fWidth <= 0) {
-                return;
+            if (kUseKerning) {
+                finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f};
             }
-            finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
-            processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
+            if (glyph.fWidth > 0) {
+                finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
+                processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
+            }
+            return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX),
+                                           SkFixedToScalar(glyph.fAdvanceY)};
         }
 
     private:
         SkGlyphCache* const fCache;
         SkDrawCacheProc fGlyphCacheProc;
+        SkAutoKern fAutoKern;
     };
 
     // GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and
@@ -365,9 +398,9 @@ private:
         GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kRight_Align,  kX_SkAxisAlignment   >,
         GlyphFindAndPlaceSubpixel<ProcessOneGlyph,  SkPaint::kRight_Align,  kY_SkAxisAlignment   >,
         // Full pixel
-        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align  >,
-        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align>,
-        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align >
+        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align,   kNoKerning>,
+        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNoKerning>,
+        GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align,  kNoKerning>
     >;
 
     // InitSubpixel is a helper function for initializing all the variants of
@@ -396,11 +429,30 @@ private:
                 break;
         }
     }
+
+    static SkPoint MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
+                                const char text[], size_t byteLength) {
+        SkFixed     x = 0, y = 0;
+        const char* stop = text + byteLength;
+
+        SkAutoKern  autokern;
+
+        while (text < stop) {
+            // don't need x, y here, since all subpixel variants will have the
+            // same advance
+            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+            x += autokern.adjust(glyph) + glyph.fAdvanceX;
+            y += glyph.fAdvanceY;
+        }
+        SkASSERT(text == stop);
+        return {SkFixedToScalar(x), SkFixedToScalar(y)};
+    }
 };
 
 template<typename ProcessOneGlyph>
 inline void SkFindAndPlaceGlyph::ProcessPosText(
-    const char text[], size_t byteLength, const SkPoint& offset, const SkMatrix& matrix,
+    const char text[], size_t byteLength, SkPoint offset, const SkMatrix& matrix,
     const SkScalar pos[], int scalarsPerPosition, SkPaint::Align textAlignment,
     SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
 
@@ -475,17 +527,17 @@ inline void SkFindAndPlaceGlyph::ProcessPosText(
                     case SkPaint::kLeft_Align:
                         to_init->template initialize<
                             GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
-                            SkPaint::kLeft_Align>>(cache, glyphCacheProc);
+                            SkPaint::kLeft_Align, kNoKerning>>(cache, glyphCacheProc);
                         break;
                     case SkPaint::kCenter_Align:
                         to_init->template initialize<
                             GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
-                            SkPaint::kCenter_Align>>(cache, glyphCacheProc);
+                            SkPaint::kCenter_Align, kNoKerning>>(cache, glyphCacheProc);
                         break;
                     case SkPaint::kRight_Align:
                         to_init->template initialize<
                             GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
-                            SkPaint::kRight_Align>>(cache, glyphCacheProc);
+                            SkPaint::kRight_Align, kNoKerning>>(cache, glyphCacheProc);
                         break;
                 }
             }
@@ -500,4 +552,48 @@ inline void SkFindAndPlaceGlyph::ProcessPosText(
     }
 }
 
+template<typename ProcessOneGlyph>
+inline void SkFindAndPlaceGlyph::ProcessText(
+    const char text[], size_t byteLength, SkPoint offset, const SkMatrix& matrix,
+    SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache,
+    ProcessOneGlyph&& processOneGlyph) {
+
+    // transform the starting point
+    matrix.mapPoints(&offset, 1);
+
+    // need to measure first
+    if (textAlignment != SkPaint::kLeft_Align) {
+        SkVector stop = MeasureText(cache, glyphCacheProc, text, byteLength);
+
+        if (textAlignment == SkPaint::kCenter_Align) {
+            stop *= SK_ScalarHalf;
+        }
+        offset -= stop;
+    }
+
+    GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
+        [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
+            if (cache->isSubpixel()) {
+                SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
+                InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
+                    to_init, axisAlignment, cache, glyphCacheProc);
+            } else {
+                to_init->template initialize<
+                    GlyphFindAndPlaceFullPixel<
+                        ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>>(
+                        cache, glyphCacheProc);
+            }
+        }
+    };
+
+    const char* stop = text + byteLength;
+    SkPoint current = offset;
+    while (text < stop) {
+        current =
+            findAndPosition->findAndPositionGlyph(
+                &text, current, skstd::forward<ProcessOneGlyph>(processOneGlyph));
+
+    }
+}
+
 #endif  // SkFindAndPositionGlyph_DEFINED
index f8004656c10207c976daf982525506565607ff70..37fe092496d0f4521d4f8ce77a36544ac880072d 100644 (file)
@@ -487,7 +487,7 @@ void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
                                             const SkTextBlob* blob, SkScalar x, SkScalar y,
                                             SkDrawFilter* drawFilter, const SkIRect& clipRect,
                                             GrRenderTarget* rt, const GrClip& clip) {
-    // The color here is the GrPaint color, and it is used to determine whether we 
+    // The color here is the GrPaint color, and it is used to determine whether we
     // have to regenerate LCD text blobs.
     // We use this color vs the SkPaint color because it has the colorfilter applied.
     cacheBlob->fPaintColor = color;
@@ -686,8 +686,8 @@ inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
 }
 
 inline GrAtlasTextBlob*
-GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint, 
-                                const SkMatrix& viewMatrix, SkPaint* dfPaint, 
+GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
+                                const SkMatrix& viewMatrix, SkPaint* dfPaint,
                                 SkScalar* textRatio) {
     GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
 
@@ -725,7 +725,7 @@ GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
                                  byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
                                  &offset, skPaint);
         if (fallbackTxt.count()) {
-            this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix, 
+            this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
                                       fallbackTxt, fallbackPos, 2, offset, clipRect);
         }
     } else {
@@ -764,8 +764,8 @@ GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip
                                     byteLength, pos, scalarsPerPosition, offset, clipRect,
                                     textRatio, &fallbackTxt, &fallbackPos);
         if (fallbackTxt.count()) {
-            this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix, 
-                                      fallbackTxt, fallbackPos, scalarsPerPosition, offset, 
+            this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
+                                      fallbackTxt, fallbackPos, scalarsPerPosition, offset,
                                       clipRect);
         }
     } else {
@@ -780,7 +780,7 @@ GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip
 }
 
 void GrAtlasTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
-                                    const GrClip& clip, 
+                                    const GrClip& clip,
                                     const GrPaint& paint, const SkPaint& skPaint,
                                     const SkMatrix& viewMatrix,
                                     const char text[], size_t byteLength,
@@ -826,73 +826,16 @@ void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex
     // Get GrFontScaler from cache
     GrFontScaler* fontScaler = GetGrFontScaler(cache);
 
-    // transform our starting point
-    {
-        SkPoint loc;
-        viewMatrix.mapXY(x, y, &loc);
-        x = loc.fX;
-        y = loc.fY;
-    }
-
-    // need to measure first
-    if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
-        SkVector    stopVector;
-        MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
-
-        SkScalar    stopX = stopVector.fX;
-        SkScalar    stopY = stopVector.fY;
-
-        if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
-            stopX = SkScalarHalf(stopX);
-            stopY = SkScalarHalf(stopY);
-        }
-        x -= stopX;
-        y -= stopY;
-    }
-
-    const char* stop = text + byteLength;
-
-    SkAutoKern autokern;
-
-    SkFixed fxMask = ~0;
-    SkFixed fyMask = ~0;
-    SkScalar halfSampleX, halfSampleY;
-    if (cache->isSubpixel()) {
-        halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
-        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
-        if (kX_SkAxisAlignment == baseline) {
-            fyMask = 0;
-            halfSampleY = SK_ScalarHalf;
-        } else if (kY_SkAxisAlignment == baseline) {
-            fxMask = 0;
-            halfSampleX = SK_ScalarHalf;
-        }
-    } else {
-        halfSampleX = halfSampleY = SK_ScalarHalf;
-    }
-
-    Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
-    Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
-
-    while (text < stop) {
-        const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
-
-        fx += autokern.adjust(glyph);
-
-        if (glyph.fWidth) {
-            this->bmpAppendGlyph(blob,
-                                 runIndex,
-                                 glyph,
-                                 Sk48Dot16FloorToInt(fx),
-                                 Sk48Dot16FloorToInt(fy),
-                                 color,
-                                 fontScaler,
-                                 clipRect);
+    SkFindAndPlaceGlyph::ProcessText(
+        text, byteLength, {x, y}, viewMatrix, skPaint.getTextAlign(), glyphCacheProc, cache,
+        [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
+            position += rounding;
+            this->bmpAppendGlyph(
+                blob, runIndex, glyph,
+                SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
+                color, fontScaler, clipRect);
         }
-
-        fx += glyph.fAdvanceX;
-        fy += glyph.fAdvanceY;
-    }
+    );
 }
 
 void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
@@ -949,7 +892,7 @@ void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
     SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
     SkAutoDescriptor desc;
     origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true);
-    SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(), 
+    SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
                                                              desc.getDesc());
 
     SkTArray<SkScalar> positions;
@@ -1932,7 +1875,7 @@ private:
 };
 
 void GrAtlasTextContext::flushRunAsPaths(GrDrawContext* dc, GrRenderTarget* rt,
-                                         const SkTextBlobRunIterator& it, 
+                                         const SkTextBlobRunIterator& it,
                                          const GrClip& clip, const SkPaint& skPaint,
                                          SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
                                          const SkIRect& clipBounds, SkScalar x, SkScalar y) {
@@ -2029,7 +1972,7 @@ inline void GrAtlasTextContext::flushRun(GrDrawContext* dc, GrPipelineBuilder* p
     }
 }
 
-inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob, 
+inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob,
                                                GrDrawContext* dc, GrRenderTarget* rt,
                                                const GrClip& clip, const SkPaint& skPaint,
                                                SkScalar transX, SkScalar transY,
@@ -2056,7 +1999,7 @@ inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob,
 
 void GrAtlasTextContext::flush(const SkTextBlob* blob,
                                GrAtlasTextBlob* cacheBlob,
-                               GrDrawContext* dc, 
+                               GrDrawContext* dc,
                                GrRenderTarget* rt,
                                const SkPaint& skPaint,
                                const GrPaint& grPaint,