Add standalone drawText for GrTextContext.

This unifies the interface between GrBitmapTextContext and
GrDistanceFieldTextContext so that they don't need special case code.
The future GrNVPRTextContext will also use this interface.

BUG=skia:2018
R=bsalomon@google.com, reed@google.com

Author: jvanverth@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13227 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp
index 6e33d90..da887a5 100755
--- a/src/gpu/GrBitmapTextContext.cpp
+++ b/src/gpu/GrBitmapTextContext.cpp
@@ -18,16 +18,21 @@
 #include "SkStrokeRec.h"
 #include "effects/GrCustomCoordsTextureEffect.h"
 
+#include "SkAutoKern.h"
+#include "SkGlyphCache.h"
+#include "SkGpuDevice.h"
+#include "SkGr.h"
+
 static const int kGlyphCoordsAttributeIndex = 1;
 
 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
                 "Dump the contents of the font cache before every purge.");
 
-GrBitmapTextContext::GrBitmapTextContext(GrContext* context, const GrPaint& paint,
-                                         const SkPaint& skPaint) :
-                                         GrTextContext(context, paint, skPaint) {
-    fAutoMatrix.setIdentity(fContext, &fPaint);
-
+GrBitmapTextContext::GrBitmapTextContext(GrContext* context, 
+                                         const GrPaint& grPaint,
+                                         const SkPaint& skPaint, 
+                                         const SkDeviceProperties& properties)
+                                       : GrTextContext(context, grPaint, skPaint, properties) {
     fStrike = NULL;
 
     fCurrTexture = NULL;
@@ -107,6 +112,330 @@
     }
 }
 
+void GrBitmapTextContext::drawText(const char text[], size_t byteLength,
+                                   SkScalar x, SkScalar y) {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
+        return;
+    }
+
+    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
+
+    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
+    SkGlyphCache*       cache = autoCache.getCache();
+    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
+
+    // transform our starting point
+    {
+        SkPoint loc;
+        fContext->getMatrix().mapXY(x, y, &loc);
+        x = loc.fX;
+        y = loc.fY;
+    }
+
+    // need to measure first
+    if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkVector    stop;
+
+        MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
+
+        SkScalar    stopX = stop.fX;
+        SkScalar    stopY = stop.fY;
+
+        if (fSkPaint.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;
+    SkFixed halfSampleX, halfSampleY;
+    if (cache->isSubpixel()) {
+        halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
+        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
+        if (kX_SkAxisAlignment == baseline) {
+            fyMask = 0;
+            halfSampleY = SK_FixedHalf;
+        } else if (kY_SkAxisAlignment == baseline) {
+            fxMask = 0;
+            halfSampleX = SK_FixedHalf;
+        }
+    } else {
+        halfSampleX = halfSampleY = SK_FixedHalf;
+    }
+
+    SkFixed fx = SkScalarToFixed(x) + halfSampleX;
+    SkFixed fy = SkScalarToFixed(y) + halfSampleY;
+    
+    GrContext::AutoMatrix  autoMatrix;
+    autoMatrix.setIdentity(fContext, &fPaint);
+    
+    while (text < stop) {
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
+
+        fx += autokern.adjust(glyph);
+
+        if (glyph.fWidth) {
+            this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                          glyph.getSubXFixed(),
+                                          glyph.getSubYFixed()),
+                                  SkFixedFloorToFixed(fx),
+                                  SkFixedFloorToFixed(fy),
+                                  fontScaler);
+        }
+
+        fx += glyph.fAdvanceX;
+        fy += glyph.fAdvanceY;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Copied from SkDraw
+
+// last parameter is interpreted as SkFixed [x, y]
+// return the fixed position, which may be rounded or not by the caller
+//   e.g. subpixel doesn't round
+typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
+
+static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
+}
+
+static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
+             SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
+}
+
+static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
+             SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
+}
+
+static AlignProc pick_align_proc(SkPaint::Align align) {
+    static const AlignProc gProcs[] = {
+        leftAlignProc, centerAlignProc, rightAlignProc
+    };
+
+    SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
+
+    return gProcs[align];
+}
+
+class BitmapTextMapState {
+public:
+    mutable SkPoint fLoc;
+
+    BitmapTextMapState(const SkMatrix& matrix, SkScalar y)
+        : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
+
+    typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]);
+
+    Proc pickProc(int scalarsPerPosition);
+
+private:
+    const SkMatrix&     fMatrix;
+    SkMatrix::MapXYProc fProc;
+    SkScalar            fY; // ignored by MapXYProc
+    // these are only used by Only... procs
+    SkScalar            fScaleX, fTransX, fTransformedY;
+
+    static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) {
+        state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
+    }
+
+    static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) {
+        state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
+    }
+
+    static void MapOnlyScaleXProc(const BitmapTextMapState& state,
+                                  const SkScalar pos[]) {
+        state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
+                       state.fTransformedY);
+    }
+
+    static void MapOnlyTransXProc(const BitmapTextMapState& state,
+                                  const SkScalar pos[]) {
+        state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
+    }
+};
+
+BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) {
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    if (1 == scalarsPerPosition) {
+        unsigned mtype = fMatrix.getType();
+        if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
+            return MapXProc;
+        } else {
+            fScaleX = fMatrix.getScaleX();
+            fTransX = fMatrix.getTranslateX();
+            fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
+                            fMatrix.getTranslateY();
+            return (mtype & SkMatrix::kScale_Mask) ?
+                        MapOnlyScaleXProc : MapOnlyTransXProc;
+        }
+    } else {
+        return MapXYProc;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrBitmapTextContext::drawPosText(const char text[], size_t byteLength,
+                                      const SkScalar pos[], SkScalar constY,
+                                      int scalarsPerPosition) {
+    SkASSERT(byteLength == 0 || text != NULL);
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
+        return;
+    }
+
+    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
+
+    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
+    SkGlyphCache*       cache = autoCache.getCache();
+    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
+
+    // store original matrix before we reset, so we can use it to transform positions
+    SkMatrix ctm = fContext->getMatrix();
+    GrContext::AutoMatrix  autoMatrix;
+    autoMatrix.setIdentity(fContext, &fPaint);
+
+    const char*        stop = text + byteLength;
+    AlignProc          alignProc = pick_align_proc(fSkPaint.getTextAlign());
+    BitmapTextMapState       tms(ctm, constY);
+    BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
+    SkFixed halfSampleX = 0, halfSampleY = 0;
+
+    if (cache->isSubpixel()) {
+        // maybe we should skip the rounding if linearText is set
+        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
+
+        SkFixed fxMask = ~0;
+        SkFixed fyMask = ~0;
+        if (kX_SkAxisAlignment == baseline) {
+            fyMask = 0;
+#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
+            halfSampleY = SK_FixedHalf;
+#endif
+        } else if (kY_SkAxisAlignment == baseline) {
+            fxMask = 0;
+#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
+            halfSampleX = SK_FixedHalf;
+#endif
+        }
+
+        if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
+            while (text < stop) {
+                tmsProc(tms, pos);
+                SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX;
+                SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY;
+
+                const SkGlyph& glyph = glyphCacheProc(cache, &text,
+                                                      fx & fxMask, fy & fyMask);
+
+                if (glyph.fWidth) {
+                    this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                                        glyph.getSubXFixed(),
+                                                        glyph.getSubYFixed()),
+                                          SkFixedFloorToFixed(fx),
+                                          SkFixedFloorToFixed(fy),
+                                          fontScaler);
+                }
+                pos += scalarsPerPosition;
+            }
+        } else {
+            while (text < stop) {
+                const char* currentText = text;
+                const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (metricGlyph.fWidth) {
+                    SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
+                    SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
+
+                    tmsProc(tms, pos);
+                    SkIPoint fixedLoc;
+                    alignProc(tms.fLoc, metricGlyph, &fixedLoc);
+
+                    SkFixed fx = fixedLoc.fX + halfSampleX;
+                    SkFixed fy = fixedLoc.fY + halfSampleY;
+
+                    // have to call again, now that we've been "aligned"
+                    const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
+                                                          fx & fxMask, fy & fyMask);
+                    // the assumption is that the metrics haven't changed
+                    SkASSERT(prevAdvX == glyph.fAdvanceX);
+                    SkASSERT(prevAdvY == glyph.fAdvanceY);
+                    SkASSERT(glyph.fWidth);
+
+                    this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                                        glyph.getSubXFixed(),
+                                                        glyph.getSubYFixed()),
+                                          SkFixedFloorToFixed(fx),
+                                          SkFixedFloorToFixed(fy),
+                                          fontScaler);
+                }
+                pos += scalarsPerPosition;
+            }
+        }
+    } else {    // not subpixel
+
+        if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
+            while (text < stop) {
+                // the last 2 parameters are ignored
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (glyph.fWidth) {
+                    tmsProc(tms, pos);
+
+                    SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
+                    SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
+                    this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                                        glyph.getSubXFixed(),
+                                                        glyph.getSubYFixed()),
+                                          SkFixedFloorToFixed(fx),
+                                          SkFixedFloorToFixed(fy),
+                                          fontScaler);
+                }
+                pos += scalarsPerPosition;
+            }
+        } else {
+            while (text < stop) {
+                // the last 2 parameters are ignored
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (glyph.fWidth) {
+                    tmsProc(tms, pos);
+
+                    SkIPoint fixedLoc;
+                    alignProc(tms.fLoc, glyph, &fixedLoc);
+
+                    SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
+                    SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
+                    this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                                        glyph.getSubXFixed(),
+                                                        glyph.getSubYFixed()),
+                                          SkFixedFloorToFixed(fx),
+                                          SkFixedFloorToFixed(fy),
+                                          fontScaler);
+                }
+                pos += scalarsPerPosition;
+            }
+        }
+    }
+}
+
 namespace {
 
 // position + texture coord
@@ -123,6 +452,7 @@
     if (NULL == fDrawTarget) {
         return;
     }
+
     if (NULL == fStrike) {
 #if SK_DISTANCEFIELD_FONTS
         fStrike = fContext->getFontCache()->getStrike(scaler, false);