Initial CL to add distance field support to GrAtlasTextContext
authorjoshualitt <joshualitt@chromium.org>
Fri, 17 Apr 2015 16:30:06 +0000 (09:30 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 17 Apr 2015 16:30:07 +0000 (09:30 -0700)
BUG=skia:

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

src/gpu/GrAtlasTextContext.cpp
src/gpu/GrAtlasTextContext.h
src/gpu/GrContext.cpp
src/gpu/GrDistanceFieldTextContext.cpp
src/gpu/GrStencilAndCoverTextContext.cpp

index f2e993aa7eb04106083709af5b7305567b89a873..34c691a9d718687bb0ae8c5991005ed256f33acb 100644 (file)
@@ -20,6 +20,8 @@
 
 #include "SkAutoKern.h"
 #include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDistanceFieldGen.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
 #include "SkDrawProcs.h"
@@ -33,7 +35,7 @@
 #include "SkTextMapStateProc.h"
 
 #include "effects/GrBitmapTextGeoProc.h"
-#include "effects/GrSimpleTextureEffect.h"
+#include "effects/GrDistanceFieldGeoProc.h"
 
 namespace {
 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
@@ -43,6 +45,16 @@ static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
 
 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
 
+static const int kMinDFFontSize = 18;
+static const int kSmallDFFontSize = 32;
+static const int kSmallDFFontLimit = 32;
+static const int kMediumDFFontSize = 72;
+static const int kMediumDFFontLimit = 72;
+static const int kLargeDFFontSize = 162;
+
+SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
+static const int kDistanceAdjustLumShift = 5;
+
 static const int kVerticesPerGlyph = 4;
 static const int kIndicesPerGlyph = 6;
 
@@ -57,30 +69,134 @@ static size_t get_vertex_stride(GrMaskFormat maskFormat) {
     }
 }
 
+static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
+    SkASSERT(maskFormat == kA8_GrMaskFormat);
+    if (useLCDText) {
+        return kLCDTextVASize;
+    } else {
+        return kGrayTextVASize;
+    }
+}
+
+static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
+    unsigned r = SkColorGetR(c);
+    unsigned g = SkColorGetG(c);
+    unsigned b = SkColorGetB(c);
+    return GrColorPackRGBA(r, g, b, 0xff);
+}
+
 };
 
 // TODO
-// Gamma slotting to preserve color
-// Better reuse on regeneration
-// Telemetry tests
-// possibly consider having a regeneration ratio on the textblob itself for animated textblobs
+// Distance field text in textblobs
 
 GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
                                        SkGpuDevice* gpuDevice,
-                                       const SkDeviceProperties& properties)
-    : INHERITED(context, gpuDevice, properties) {
+                                       const SkDeviceProperties& properties,
+                                       bool enableDistanceFields)
+    : INHERITED(context, gpuDevice, properties)
+    , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
     // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
     // vertexStride
     SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
                       vertex_attribute_changed);
     fCurrStrike = NULL;
     fCache = context->getTextBlobCache();
+
+#if SK_FORCE_DISTANCE_FIELD_TEXT
+    fEnableDFRendering = true;
+#else
+    fEnableDFRendering = enableDistanceFields;
+#endif
+}
+
+void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
+
+    // This is used for an approximation of the mask gamma hack, used by raster and bitmap
+    // text. The mask gamma hack is based off of guessing what the blend color is going to
+    // be, and adjusting the mask so that when run through the linear blend will
+    // produce the value closest to the desired result. However, in practice this means
+    // that the 'adjusted' mask is just increasing or decreasing the coverage of
+    // the mask depending on what it is thought it will blit against. For black (on
+    // assumed white) this means that coverages are decreased (on a curve). For white (on
+    // assumed black) this means that coverages are increased (on a a curve). At
+    // middle (perceptual) gray (which could be blit against anything) the coverages
+    // remain the same.
+    //
+    // The idea here is that instead of determining the initial (real) coverage and
+    // then adjusting that coverage, we determine an adjusted coverage directly by
+    // essentially manipulating the geometry (in this case, the distance to the glyph
+    // edge). So for black (on assumed white) this thins a bit; for white (on
+    // assumed black) this fake bolds the geometry a bit.
+    //
+    // The distance adjustment is calculated by determining the actual coverage value which
+    // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
+    // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
+    // actual edge. So by subtracting this distance adjustment and computing without the
+    // the coverage adjustment we should get 0.5 coverage at the same point.
+    //
+    // This has several implications:
+    //     For non-gray lcd smoothed text, each subpixel essentially is using a
+    //     slightly different geometry.
+    //
+    //     For black (on assumed white) this may not cover some pixels which were
+    //     previously covered; however those pixels would have been only slightly
+    //     covered and that slight coverage would have been decreased anyway. Also, some pixels
+    //     which were previously fully covered may no longer be fully covered.
+    //
+    //     For white (on assumed black) this may cover some pixels which weren't
+    //     previously covered at all.
+
+    int width, height;
+    size_t size;
+
+#ifdef SK_GAMMA_CONTRAST
+    SkScalar contrast = SK_GAMMA_CONTRAST;
+#else
+    SkScalar contrast = 0.5f;
+#endif
+    SkScalar paintGamma = gamma;
+    SkScalar deviceGamma = gamma;
+
+    size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
+        &width, &height);
+
+    SkASSERT(kExpectedDistanceAdjustTableSize == height);
+    fTable = SkNEW_ARRAY(SkScalar, height);
+
+    SkAutoTArray<uint8_t> data((int)size);
+    SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
+
+    // find the inverse points where we cross 0.5
+    // binsearch might be better, but we only need to do this once on creation
+    for (int row = 0; row < height; ++row) {
+        uint8_t* rowPtr = data.get() + row*width;
+        for (int col = 0; col < width - 1; ++col) {
+            if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
+                // compute point where a mask value will give us a result of 0.5
+                float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
+                float borderAlpha = (col + interp) / 255.f;
+
+                // compute t value for that alpha
+                // this is an approximate inverse for smoothstep()
+                float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
+
+                // compute distance which gives us that t value
+                const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
+                float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
+
+                fTable[row] = d;
+                break;
+            }
+        }
+    }
 }
 
 GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
                                                SkGpuDevice* gpuDevice,
-                                               const SkDeviceProperties& props) {
-    return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props));
+                                               const SkDeviceProperties& props,
+                                               bool enableDistanceFields) {
+    return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
 }
 
 bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
@@ -88,7 +204,8 @@ bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
                                  const GrPaint&,
                                  const SkPaint& skPaint,
                                  const SkMatrix& viewMatrix) {
-    return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
+    return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
+           !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
 }
 
 GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
@@ -203,8 +320,9 @@ bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTr
 
 inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
                                                     const SkPaint& skPaint,
-                                                    const SkMatrix& viewMatrix) {
-    skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, &viewMatrix, false);
+                                                    const SkMatrix* viewMatrix,
+                                                    bool noGamma) {
+    skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
     run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
     return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
 }
@@ -285,6 +403,41 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
                 clip, viewMatrix, clipBounds, x, y, transX, transY);
 }
 
+inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
+                                                        const SkMatrix& viewMatrix) {
+    // TODO: support perspective (need getMaxScale replacement)
+    if (viewMatrix.hasPerspective()) {
+        return false;
+    }
+
+    SkScalar maxScale = viewMatrix.getMaxScale();
+    SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
+    // Hinted text looks far better at small resolutions
+    // Scaling up beyond 2x yields undesireable artifacts
+    if (scaledTextSize < kMinDFFontSize || scaledTextSize > 2 * kLargeDFFontSize) {
+        return false;
+    }
+
+    if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
+        scaledTextSize < kLargeDFFontSize) {
+        return false;
+    }
+
+    // rasterizers and mask filters modify alpha, which doesn't
+    // translate well to distance
+    if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
+        !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
+        return false;
+    }
+
+    // TODO: add some stroking support
+    if (skPaint.getStyle() != SkPaint::kFill_Style) {
+        return false;
+    }
+
+    return true;
+}
+
 void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
                                             const SkPaint& skPaint, GrColor color,
                                             const SkMatrix& viewMatrix,
@@ -313,7 +466,8 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
 
         runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
 
-        SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, viewMatrix);
+        SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
+                                               false);
 
         // setup vertex / glyphIndex for the new run
         if (run > 0) {
@@ -335,19 +489,19 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
 
         switch (it.positioning()) {
             case SkTextBlob::kDefault_Positioning:
-                this->internalDrawText(cacheBlob, run, cache, runPaint, color, viewMatrix,
-                                       (const char *)it.glyphs(), textLen,
-                                       x + offset.x(), y + offset.y(), clipRect);
+                this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+                                          (const char *)it.glyphs(), textLen,
+                                          x + offset.x(), y + offset.y(), clipRect);
                 break;
             case SkTextBlob::kHorizontal_Positioning:
-                this->internalDrawPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
-                                          (const char*)it.glyphs(), textLen, it.pos(), 1,
-                                          SkPoint::Make(x, y + offset.y()), clipRect);
+                this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+                                             (const char*)it.glyphs(), textLen, it.pos(), 1,
+                                             SkPoint::Make(x, y + offset.y()), clipRect);
                 break;
             case SkTextBlob::kFull_Positioning:
-                this->internalDrawPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
-                                          (const char*)it.glyphs(), textLen, it.pos(), 2,
-                                          SkPoint::Make(x, y), clipRect);
+                this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+                                             (const char*)it.glyphs(), textLen, it.pos(), 2,
+                                             SkPoint::Make(x, y), clipRect);
                 break;
         }
 
@@ -360,35 +514,165 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
     }
 }
 
+inline void GrAtlasTextContext::initDistanceFieldPaint(SkPaint* skPaint, SkScalar* textRatio,
+                                                       const SkMatrix& viewMatrix) {
+    // getMaxScale doesn't support perspective, so neither do we at the moment
+    SkASSERT(!viewMatrix.hasPerspective());
+    SkScalar maxScale = viewMatrix.getMaxScale();
+    SkScalar textSize = skPaint->getTextSize();
+    SkScalar scaledTextSize = textSize;
+    // if we have non-unity scale, we need to choose our base text size
+    // based on the SkPaint's text size multiplied by the max scale factor
+    // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
+    if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
+        scaledTextSize *= maxScale;
+    }
+
+    if (scaledTextSize <= kSmallDFFontLimit) {
+        *textRatio = textSize / kSmallDFFontSize;
+        skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
+    } else if (scaledTextSize <= kMediumDFFontLimit) {
+        *textRatio = textSize / kMediumDFFontSize;
+        skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
+    } else {
+        *textRatio = textSize / kLargeDFFontSize;
+        skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
+    }
+
+    skPaint->setLCDRenderText(false);
+    skPaint->setAutohinted(false);
+    skPaint->setHinting(SkPaint::kNormal_Hinting);
+    skPaint->setSubpixelText(true);
+}
+
+inline void GrAtlasTextContext::fallbackDrawPosText(GrRenderTarget* rt, const GrClip& clip,
+                                                    const GrPaint& paint,
+                                                    const SkPaint& skPaint,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkTDArray<char>& fallbackTxt,
+                                                    const SkTDArray<SkScalar>& fallbackPos,
+                                                    int scalarsPerPosition,
+                                                    const SkPoint& offset,
+                                                    const SkIRect& clipRect) {
+    int glyphCount = fallbackTxt.count();
+    SkASSERT(glyphCount);
+    // TODO currently we have to create a whole new blob for fallback text.  This is because
+    // they have a different descriptor and we currently only have one descriptor per run.
+    // We should fix this and allow an override descriptor on each subrun
+    SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
+    blob->fViewMatrix = viewMatrix;
+
+    SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+    this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix,
+                                 fallbackTxt.begin(), fallbackTxt.count(),
+                                 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
+    SkGlyphCache::AttachCache(cache);
+    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
+}
+
+inline GrAtlasTextContext::BitmapTextBlob*
+GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
+                                const SkMatrix& viewMatrix, SkGlyphCache** cache,
+                                SkPaint* dfPaint, SkScalar* textRatio) {
+    BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
+
+    *dfPaint = origPaint;
+    this->initDistanceFieldPaint(dfPaint, textRatio, viewMatrix);
+    blob->fViewMatrix = viewMatrix;
+    blob->fRuns[0].fSubRunInfo.back().fUseLCDText = origPaint.isLCDRenderText();
+    blob->fRuns[0].fSubRunInfo.back().fDrawAsDistanceFields = true;
+
+    *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
+    return blob;
+}
+
 void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
                                     const GrPaint& paint, const SkPaint& skPaint,
                                     const SkMatrix& viewMatrix,
                                     const char text[], size_t byteLength,
                                     SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
     int glyphCount = skPaint.countText(text, byteLength);
-    SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
-    blob->fViewMatrix = viewMatrix;
-    blob->fX = x;
-    blob->fY = y;
-
     SkIRect clipRect;
     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
 
     // setup cache
-    SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
-    this->internalDrawText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text, byteLength,
-                           x, y, clipRect);
-    SkGlyphCache::AttachCache(cache);
+    if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
+        SkPaint dfPaint;
+        SkScalar textRatio;
+        SkGlyphCache* cache;
+        SkAutoTUnref<BitmapTextBlob> blob(this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache,
+                                                            &dfPaint, &textRatio));
+
+        SkTDArray<char> fallbackTxt;
+        SkTDArray<SkScalar> fallbackPos;
+        SkPoint offset;
+        this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
+                                 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
+                                 &offset, skPaint);
+        SkGlyphCache::AttachCache(cache);
+        this->flush(fContext->getTextTarget(), blob, rt, dfPaint, paint, clip);
+        if (fallbackTxt.count()) {
+            this->fallbackDrawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
+                                      fallbackPos, 2, offset, clipRect);
+        }
+    } else {
+        SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
+        blob->fViewMatrix = viewMatrix;
 
-    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
+        SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+        this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
+                                  byteLength, x, y, clipRect);
+        SkGlyphCache::AttachCache(cache);
+        this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
+    }
 }
 
-void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
-                                          SkGlyphCache* cache, const SkPaint& skPaint,
-                                          GrColor color,
-                                          const SkMatrix& viewMatrix,
-                                          const char text[], size_t byteLength,
-                                          SkScalar x, SkScalar y, const SkIRect& clipRect) {
+void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
+                                       const GrPaint& paint, const SkPaint& skPaint,
+                                       const SkMatrix& viewMatrix,
+                                       const char text[], size_t byteLength,
+                                       const SkScalar pos[], int scalarsPerPosition,
+                                       const SkPoint& offset, const SkIRect& regionClipBounds) {
+    int glyphCount = skPaint.countText(text, byteLength);
+
+    SkIRect clipRect;
+    clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
+
+    if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
+        SkPaint dfPaint;
+        SkScalar textRatio;
+        SkGlyphCache* cache;
+        SkAutoTUnref<BitmapTextBlob> blob(this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache,
+                                                            &dfPaint, &textRatio));
+
+        SkTDArray<char> fallbackTxt;
+        SkTDArray<SkScalar> fallbackPos;
+        this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
+                                    byteLength, pos, scalarsPerPosition, offset, clipRect,
+                                    textRatio, &fallbackTxt, &fallbackPos);
+        SkGlyphCache::AttachCache(cache);
+        this->flush(fContext->getTextTarget(), blob, rt, dfPaint, paint, clip);
+        if (fallbackTxt.count()) {
+            this->fallbackDrawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
+                                      fallbackPos, scalarsPerPosition, offset, clipRect);
+        }
+    } else {
+        SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
+        blob->fViewMatrix = viewMatrix;
+        SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+        this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
+                                     byteLength, pos, scalarsPerPosition, offset, clipRect);
+        SkGlyphCache::AttachCache(cache);
+        this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
+    }
+}
+
+void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
+                                             SkGlyphCache* cache, const SkPaint& skPaint,
+                                             GrColor color,
+                                             const SkMatrix& viewMatrix,
+                                             const char text[], size_t byteLength,
+                                             SkScalar x, SkScalar y, const SkIRect& clipRect) {
     SkASSERT(byteLength == 0 || text != NULL);
 
     // nothing to draw
@@ -456,17 +740,17 @@ void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
         fx += autokern.adjust(glyph);
 
         if (glyph.fWidth) {
-            this->appendGlyph(blob,
-                              runIndex,
-                              GrGlyph::Pack(glyph.getGlyphID(),
-                                            glyph.getSubXFixed(),
-                                            glyph.getSubYFixed(),
-                                            GrGlyph::kCoverage_MaskStyle),
-                              Sk48Dot16FloorToInt(fx),
-                              Sk48Dot16FloorToInt(fy),
-                              color,
-                              fontScaler,
-                              clipRect);
+            this->bmpAppendGlyph(blob,
+                                 runIndex,
+                                 GrGlyph::Pack(glyph.getGlyphID(),
+                                               glyph.getSubXFixed(),
+                                               glyph.getSubYFixed(),
+                                               GrGlyph::kCoverage_MaskStyle),
+                                 Sk48Dot16FloorToInt(fx),
+                                 Sk48Dot16FloorToInt(fy),
+                                 color,
+                                 fontScaler,
+                                 clipRect);
         }
 
         fx += glyph.fAdvanceX;
@@ -474,35 +758,13 @@ void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
     }
 }
 
-void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
-                                       const GrPaint& paint, const SkPaint& skPaint,
-                                       const SkMatrix& viewMatrix,
-                                       const char text[], size_t byteLength,
-                                       const SkScalar pos[], int scalarsPerPosition,
-                                       const SkPoint& offset, const SkIRect& regionClipBounds) {
-    int glyphCount = skPaint.countText(text, byteLength);
-    SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
-    blob->fViewMatrix = viewMatrix;
-
-    SkIRect clipRect;
-    clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
-
-    // setup cache
-    SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
-    this->internalDrawPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
-                              byteLength, pos, scalarsPerPosition, offset, clipRect);
-    SkGlyphCache::AttachCache(cache);
-
-    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
-}
-
-void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
-                                             SkGlyphCache* cache, const SkPaint& skPaint,
-                                             GrColor color,
-                                             const SkMatrix& viewMatrix,
-                                             const char text[], size_t byteLength,
-                                             const SkScalar pos[], int scalarsPerPosition,
-                                             const SkPoint& offset, const SkIRect& clipRect) {
+void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
+                                                SkGlyphCache* cache, const SkPaint& skPaint,
+                                                GrColor color,
+                                                const SkMatrix& viewMatrix,
+                                                const char text[], size_t byteLength,
+                                                const SkScalar pos[], int scalarsPerPosition,
+                                                const SkPoint& offset, const SkIRect& clipRect) {
     SkASSERT(byteLength == 0 || text != NULL);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
@@ -548,17 +810,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
                                                       fx & fxMask, fy & fyMask);
 
                 if (glyph.fWidth) {
-                    this->appendGlyph(blob,
-                                      runIndex,
-                                      GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed(),
-                                                    GrGlyph::kCoverage_MaskStyle),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      color,
-                                      fontScaler,
-                                      clipRect);
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
                 }
                 pos += scalarsPerPosition;
             }
@@ -586,17 +848,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
                     SkASSERT(prevAdvY == glyph.fAdvanceY);
                     SkASSERT(glyph.fWidth);
 
-                    this->appendGlyph(blob,
-                                      runIndex,
-                                      GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed(),
-                                                    GrGlyph::kCoverage_MaskStyle),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      color,
-                                      fontScaler,
-                                      clipRect);
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
                 }
                 pos += scalarsPerPosition;
             }
@@ -614,17 +876,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
 
                     Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
                     Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
-                    this->appendGlyph(blob,
-                                      runIndex,
-                                      GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed(),
-                                                    GrGlyph::kCoverage_MaskStyle),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      color,
-                                      fontScaler,
-                                      clipRect);
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
                 }
                 pos += scalarsPerPosition;
             }
@@ -642,17 +904,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
 
                     Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
                     Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
-                    this->appendGlyph(blob,
-                                      runIndex,
-                                      GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed(),
-                                                    GrGlyph::kCoverage_MaskStyle),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      color,
-                                      fontScaler,
-                                      clipRect);
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
                 }
                 pos += scalarsPerPosition;
             }
@@ -660,15 +922,184 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
     }
 }
 
-void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph::PackedID packed,
-                                     int vx, int vy, GrColor color, GrFontScaler* scaler,
-                                     const SkIRect& clipRect) {
-    if (NULL == fCurrStrike) {
+
+void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
+                                            SkGlyphCache* cache, const SkPaint& skPaint,
+                                            GrColor color,
+                                            const SkMatrix& viewMatrix,
+                                            const char text[], size_t byteLength,
+                                            SkScalar x, SkScalar y, const SkIRect& clipRect,
+                                            SkScalar textRatio,
+                                            SkTDArray<char>* fallbackTxt,
+                                            SkTDArray<SkScalar>* fallbackPos,
+                                            SkPoint* offset,
+                                            const SkPaint& origPaint) {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0) {
+        return;
+    }
+
+    SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
+    SkAutoDescriptor desc;
+    origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
+    SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
+                                                             desc.getDesc());
+
+    SkTArray<SkScalar> positions;
+
+    const char* textPtr = text;
+    SkFixed stopX = 0;
+    SkFixed stopY = 0;
+    SkFixed origin = 0;
+    switch (origPaint.getTextAlign()) {
+        case SkPaint::kRight_Align: origin = SK_Fixed1; break;
+        case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
+        case SkPaint::kLeft_Align: origin = 0; break;
+    }
+
+    SkAutoKern autokern;
+    const char* stop = text + byteLength;
+    while (textPtr < stop) {
+        // don't need x, y here, since all subpixel variants will have the
+        // same advance
+        const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
+
+        SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
+        positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
+
+        SkFixed height = glyph.fAdvanceY;
+        positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
+
+        stopX += width;
+        stopY += height;
+    }
+    SkASSERT(textPtr == stop);
+
+    // now adjust starting point depending on alignment
+    SkScalar alignX = SkFixedToScalar(stopX);
+    SkScalar alignY = SkFixedToScalar(stopY);
+    if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
+        alignX = SkScalarHalf(alignX);
+        alignY = SkScalarHalf(alignY);
+    } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
+        alignX = 0;
+        alignY = 0;
+    }
+    x -= alignX;
+    y -= alignY;
+    *offset = SkPoint::Make(x, y);
+
+    this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
+                                positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
+                                fallbackPos);
+    SkGlyphCache::AttachCache(origPaintCache);
+}
+
+void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
+                                               SkGlyphCache* cache, const SkPaint& skPaint,
+                                               GrColor color,
+                                               const SkMatrix& viewMatrix,
+                                               const char text[], size_t byteLength,
+                                               const SkScalar pos[], int scalarsPerPosition,
+                                               const SkPoint& offset, const SkIRect& clipRect,
+                                               SkScalar textRatio,
+                                               SkTDArray<char>* fallbackTxt,
+                                               SkTDArray<SkScalar>* fallbackPos) {
+
+    SkASSERT(byteLength == 0 || text != NULL);
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0) {
+        return;
+    }
+
+    fCurrStrike = NULL;
+
+    SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
+    GrFontScaler* fontScaler = GetGrFontScaler(cache);
+
+    const char* stop = text + byteLength;
+
+    if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
+        while (text < stop) {
+            const char* lastText = text;
+            // the last 2 parameters are ignored
+            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+            if (glyph.fWidth) {
+                SkScalar x = offset.x() + pos[0];
+                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+                if (!this->dfAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kDistance_MaskStyle),
+                                         x, y, color, fontScaler, clipRect,
+                                         textRatio, viewMatrix)) {
+                    // couldn't append, send to fallback
+                    fallbackTxt->append(SkToInt(text-lastText), lastText);
+                    *fallbackPos->append() = pos[0];
+                    if (2 == scalarsPerPosition) {
+                        *fallbackPos->append() = pos[1];
+                    }
+                }
+            }
+            pos += scalarsPerPosition;
+        }
+    } else {
+        SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
+                                                                             : SK_Scalar1;
+        while (text < stop) {
+            const char* lastText = text;
+            // the last 2 parameters are ignored
+            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+            if (glyph.fWidth) {
+                SkScalar x = offset.x() + pos[0];
+                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+                SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
+                SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
+
+                if (!this->dfAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kDistance_MaskStyle),
+                                         x - advanceX, y - advanceY, color,
+                                         fontScaler,
+                                         clipRect,
+                                         textRatio,
+                                         viewMatrix)) {
+                    // couldn't append, send to fallback
+                    fallbackTxt->append(SkToInt(text-lastText), lastText);
+                    *fallbackPos->append() = pos[0];
+                    if (2 == scalarsPerPosition) {
+                        *fallbackPos->append() = pos[1];
+                    }
+                }
+            }
+            pos += scalarsPerPosition;
+        }
+    }
+}
+
+void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
+                                        GrGlyph::PackedID packed,
+                                        int vx, int vy, GrColor color, GrFontScaler* scaler,
+                                        const SkIRect& clipRect) {
+    if (!fCurrStrike) {
         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
     }
 
     GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
-    if (NULL == glyph || glyph->fBounds.isEmpty()) {
+    if (!glyph || glyph->fBounds.isEmpty()) {
         return;
     }
 
@@ -693,17 +1124,7 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
 
     // If the glyph is too large we fall back to paths
     if (fCurrStrike->glyphTooLargeForAtlas(glyph)) {
-        if (NULL == glyph->fPath) {
-            SkPath* path = SkNEW(SkPath);
-            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
-                // flag the glyph as being dead?
-                SkDELETE(path);
-                return;
-            }
-            glyph->fPath = path;
-        }
-        SkASSERT(glyph->fPath);
-        blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, vx, vy));
+        this->appendGlyphPath(blob, glyph, scaler, vx, vy);
         return;
     }
 
@@ -724,8 +1145,6 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
     }
 
     run.fInitialized = true;
-    subRun->fMaskFormat = format;
-    blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed;
 
     size_t vertexStride = get_vertex_stride(format);
 
@@ -734,24 +1153,118 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
     r.fTop = SkIntToScalar(y);
     r.fRight = r.fLeft + SkIntToScalar(width);
     r.fBottom = r.fTop + SkIntToScalar(height);
+    subRun->fMaskFormat = format;
+    this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
+                            packed);
+}
+
+bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
+                                       GrGlyph::PackedID packed,
+                                       SkScalar sx, SkScalar sy, GrColor color,
+                                       GrFontScaler* scaler,
+                                       const SkIRect& clipRect,
+                                       SkScalar textRatio, const SkMatrix& viewMatrix) {
+    if (!fCurrStrike) {
+        fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
+    }
+
+    GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
+    if (!glyph || glyph->fBounds.isEmpty()) {
+        return true;
+    }
+
+    // fallback to color glyph support
+    if (kA8_GrMaskFormat != glyph->fMaskFormat) {
+        return false;
+    }
+
+    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
+    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
+    SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
+    SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
+
+    SkScalar scale = textRatio;
+    dx *= scale;
+    dy *= scale;
+    width *= scale;
+    height *= scale;
+    sx += dx;
+    sy += dy;
+    SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
+
+#if 0
+    // check if we clipped out
+    SkRect dstRect;
+    viewMatrix.mapRect(&dstRect, glyphRect);
+    if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
+                             SkScalarTruncToInt(dstRect.top()),
+                             SkScalarTruncToInt(dstRect.right()),
+                             SkScalarTruncToInt(dstRect.bottom()))) {
+        return true;
+    }
+#endif
+
+    // TODO combine with the above
+    // If the glyph is too large we fall back to paths
+    if (fCurrStrike->glyphTooLargeForAtlas(glyph)) {
+        this->appendGlyphPath(blob, glyph, scaler, SkScalarRoundToInt(sx - dx),
+                                                   SkScalarRoundToInt(sy - dy));
+        return true;
+    }
+
+    Run& run = blob->fRuns[runIndex];
+
+    PerSubRunInfo* subRun = &run.fSubRunInfo.back();
+    SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
+    subRun->fMaskFormat = kA8_GrMaskFormat;
+
+    size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
+
+    bool useColorVerts = !subRun->fUseLCDText;
+    this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
+                            packed);
+    return true;
+}
+
+inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
+                                                GrFontScaler* scaler, int x, int y) {
+    if (NULL == glyph->fPath) {
+        SkPath* path = SkNEW(SkPath);
+        if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+            // flag the glyph as being dead?
+            SkDELETE(path);
+            return;
+        }
+        glyph->fPath = path;
+    }
+    SkASSERT(glyph->fPath);
+    blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
+}
 
-    run.fVertexBounds.joinNonEmptyArg(r);
-    run.fColor = color;
+inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
+                                                  Run::SubRunInfo* subRun,
+                                                  const SkRect& positions, GrColor color,
+                                                  size_t vertexStride, bool useVertexColor,
+                                                  GrGlyph::PackedID packed) {
+    blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed;
+    run->fVertexBounds.joinNonEmptyArg(positions);
+    run->fColor = color;
 
     intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
 
     // V0
     SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fLeft, r.fTop);
-    if (kA8_GrMaskFormat == format) {
+    position->set(positions.fLeft, positions.fTop);
+    if (useVertexColor) {
         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
         *colorPtr = color;
     }
     vertex += vertexStride;
+
     // V1
     position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fLeft, r.fBottom);
-    if (kA8_GrMaskFormat == format) {
+    position->set(positions.fLeft, positions.fBottom);
+    if (useVertexColor) {
         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
         *colorPtr = color;
     }
@@ -759,8 +1272,8 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
 
     // V2
     position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fRight, r.fBottom);
-    if (kA8_GrMaskFormat == format) {
+    position->set(positions.fRight, positions.fBottom);
+    if (useVertexColor) {
         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
         *colorPtr = color;
     }
@@ -768,8 +1281,8 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
 
     // V3
     position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fRight, r.fTop);
-    if (kA8_GrMaskFormat == format) {
+    position->set(positions.fRight, positions.fTop);
+    if (useVertexColor) {
         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
         *colorPtr = color;
     }
@@ -780,6 +1293,7 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
 
 class BitmapTextBatch : public GrBatch {
 public:
+    typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
     typedef GrAtlasTextContext::BitmapTextBlob Blob;
     typedef Blob::Run Run;
     typedef Run::SubRunInfo TextInfo;
@@ -797,6 +1311,15 @@ public:
         return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
     }
 
+    static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
+                                   GrBatchFontCache* fontCache,
+                                   DistanceAdjustTable* distanceAdjustTable,
+                                   SkColor filteredColor, bool useLCDText,
+                                   bool useBGR, float gamma) {
+        return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
+                                            filteredColor, useLCDText, useBGR, gamma));
+    }
+
     const char* name() const override { return "BitmapTextBatch"; }
 
     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
@@ -808,18 +1331,29 @@ public:
     }
 
     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
-        if (kARGB_GrMaskFormat != fMaskFormat) {
-            if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
+        if (!fUseDistanceFields) {
+            // Bitmap Text
+            if (kARGB_GrMaskFormat != fMaskFormat) {
+                if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
+                    out->setUnknownSingleComponent();
+                } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
+                    out->setUnknownOpaqueFourComponents();
+                    out->setUsingLCDCoverage();
+                } else {
+                    out->setUnknownFourComponents();
+                    out->setUsingLCDCoverage();
+                }
+            } else {
+                out->setKnownSingleComponent(0xff);
+            }
+        } else {
+            // Distance fields
+            if (!fUseLCDText) {
                 out->setUnknownSingleComponent();
-            } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
-                out->setUnknownOpaqueFourComponents();
-                out->setUsingLCDCoverage();
             } else {
                 out->setUnknownFourComponents();
                 out->setUsingLCDCoverage();
             }
-        } else {
-            out->setKnownSingleComponent(0xff);
         }
     }
 
@@ -852,20 +1386,27 @@ public:
             return;
         }
 
-        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
-
-        // This will be ignored in the non A8 case
-        bool opaqueVertexColors = GrColorIsOpaque(this->color());
-        SkAutoTUnref<const GrGeometryProcessor> gp(
-                GrBitmapTextGeoProc::Create(this->color(),
-                                            texture,
-                                            params,
-                                            fMaskFormat,
-                                            opaqueVertexColors,
-                                            localMatrix));
+        SkAutoTUnref<const GrGeometryProcessor> gp;
+        if (fUseDistanceFields) {
+            gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
+                                            texture));
+        } else {
+            GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
+
+            // This will be ignored in the non A8 case
+            bool opaqueVertexColors = GrColorIsOpaque(this->color());
+            gp.reset(GrBitmapTextGeoProc::Create(this->color(),
+                                                 texture,
+                                                 params,
+                                                 fMaskFormat,
+                                                 opaqueVertexColors,
+                                                 localMatrix));
+        }
 
         size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == get_vertex_stride(fMaskFormat));
+        SkASSERT(vertexStride == (fUseDistanceFields ?
+                                  get_vertex_stride_df(fMaskFormat, fUseLCDText) :
+                                  get_vertex_stride(fMaskFormat)));
 
         this->initDraw(batchTarget, gp, pipeline);
 
@@ -908,7 +1449,12 @@ public:
 
             uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
             bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
-            bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
+            bool regenerateColors;
+            if (fUseDistanceFields) {
+                regenerateColors = fUseLCDText && run.fColor != args.fColor;
+            } else {
+                regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
+            }
             bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
             int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
 
@@ -1029,15 +1575,34 @@ public:
     }
 
 private:
-    BitmapTextBatch(GrMaskFormat maskFormat,
-                    int glyphCount, GrBatchFontCache* fontCache)
+    BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
+            : fMaskFormat(maskFormat)
+            , fPixelConfig(fontCache->getPixelConfig(maskFormat))
+            , fFontCache(fontCache)
+            , fUseDistanceFields(false) {
+        this->initClassID<BitmapTextBatch>();
+        fBatch.fNumGlyphs = glyphCount;
+        fInstanceCount = 1;
+        fAllocatedCount = kMinAllocated;
+    }
+
+    BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
+                    DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
+                    bool useLCDText, bool useBGR, float gamma)
             : fMaskFormat(maskFormat)
             , fPixelConfig(fontCache->getPixelConfig(maskFormat))
-            , fFontCache(fontCache) {
+            , fFontCache(fontCache)
+            , fDistanceAdjustTable(SkRef(distanceAdjustTable))
+            , fFilteredColor(filteredColor)
+            , fUseDistanceFields(true)
+            , fUseLCDText(useLCDText)
+            , fUseBGR(useBGR)
+            , fGamma(gamma) {
         this->initClassID<BitmapTextBatch>();
         fBatch.fNumGlyphs = glyphCount;
         fInstanceCount = 1;
         fAllocatedCount = kMinAllocated;
+        SkASSERT(fMaskFormat == kA8_GrMaskFormat);
     }
 
     ~BitmapTextBatch() {
@@ -1049,13 +1614,20 @@ private:
     void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
         int width = glyph->fBounds.width();
         int height = glyph->fBounds.height();
-        int u0 = glyph->fAtlasLocation.fX;
-        int v0 = glyph->fAtlasLocation.fY;
-        int u1 = u0 + width;
-        int v1 = v0 + height;
 
-        // we assume texture coords are the last vertex attribute, this is a bit fragile.
-        // TODO pass in this offset or something
+        int u0, v0, u1, v1;
+        if (fUseDistanceFields) {
+            u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
+            v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
+            u1 = u0 + width - 2 * SK_DistanceFieldInset;
+            v1 = v0 + height - 2 * SK_DistanceFieldInset;
+        } else {
+            u0 = glyph->fAtlasLocation.fX;
+            v0 = glyph->fAtlasLocation.fY;
+            u1 = u0 + width;
+            v1 = v0 + height;
+        }
+
         SkIPoint16* textureCoords;
         // V0
         textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
@@ -1133,16 +1705,54 @@ private:
     bool onCombineIfPossible(GrBatch* t) override {
         BitmapTextBatch* that = t->cast<BitmapTextBatch>();
 
-        if (this->fMaskFormat != that->fMaskFormat) {
+        if (fUseDistanceFields != that->fUseDistanceFields) {
             return false;
         }
 
-        if (this->fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
-            return false;
-        }
+        if (!fUseDistanceFields) {
+            // Bitmap Text
+            if (fMaskFormat != that->fMaskFormat) {
+                return false;
+            }
 
-        if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
-            return false;
+            // TODO we can often batch across LCD text if we have dual source blending and don't
+            // have to use the blend constant
+            if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
+                return false;
+            }
+
+            if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+                return false;
+            }
+        } else {
+            // Distance Fields
+            SkASSERT(this->fMaskFormat == that->fMaskFormat &&
+                     this->fMaskFormat == kA8_GrMaskFormat);
+
+            if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+                return false;
+            }
+
+            if (fFilteredColor != that->fFilteredColor) {
+                return false;
+            }
+
+            if (fUseLCDText != that->fUseLCDText) {
+                return false;
+            }
+
+            if (fUseBGR != that->fUseBGR) {
+                return false;
+            }
+
+            if (fGamma != that->fGamma) {
+                return false;
+            }
+
+            // TODO see note above
+            if (fUseLCDText && this->color() != that->color()) {
+                return false;
+            }
         }
 
         fBatch.fNumGlyphs += that->numGlyphs();
@@ -1168,6 +1778,66 @@ private:
         return true;
     }
 
+    // TODO just use class params
+    // TODO trying to figure out why lcd is so whack
+    GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
+                                          GrColor color, GrTexture* texture) {
+        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
+
+        // set up any flags
+        uint32_t flags = 0;
+        flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+        flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
+        flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
+                                kRectToRect_DistanceFieldEffectFlag : 0;
+        flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
+
+        // see if we need to create a new effect
+        if (fUseLCDText) {
+            GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
+
+            float redCorrection =
+                (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
+            float greenCorrection =
+                (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
+            float blueCorrection =
+                (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
+            GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
+                GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
+                                                                    greenCorrection,
+                                                                    blueCorrection);
+
+            return GrDistanceFieldLCDTextGeoProc::Create(color,
+                                                         viewMatrix,
+                                                         texture,
+                                                         params,
+                                                         widthAdjust,
+                                                         flags);
+        } else {
+            flags |= kColorAttr_DistanceFieldEffectFlag;
+            bool opaque = GrColorIsOpaque(color);
+#ifdef SK_GAMMA_APPLY_TO_A8
+            U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
+            float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
+            return GrDistanceFieldA8TextGeoProc::Create(color,
+                                                        viewMatrix,
+                                                        texture,
+                                                        params,
+                                                        correction,
+                                                        flags,
+                                                        opaque);
+#else
+            return GrDistanceFieldA8TextGeoProc::Create(color,
+                                                        viewMatrix,
+                                                        texture,
+                                                        params,
+                                                        flags,
+                                                        opaque);
+#endif
+        }
+
+    }
+
     struct BatchTracker {
         GrColor fColor;
         SkMatrix fViewMatrix;
@@ -1184,6 +1854,14 @@ private:
     GrMaskFormat fMaskFormat;
     GrPixelConfig fPixelConfig;
     GrBatchFontCache* fFontCache;
+
+    // Distance field properties
+    SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
+    SkColor fFilteredColor;
+    bool fUseDistanceFields;
+    bool fUseLCDText;
+    bool fUseBGR;
+    float fGamma;
 };
 
 void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
@@ -1221,7 +1899,7 @@ void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, cons
 
 inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
                                          BitmapTextBlob* cacheBlob, int run, GrColor color,
-                                         uint8_t paintAlpha, SkScalar transX, SkScalar transY) {
+                                         SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
     for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
         const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
         int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
@@ -1230,12 +1908,32 @@ inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder
         }
 
         GrMaskFormat format = info.fMaskFormat;
-        GrColor subRunColor = kARGB_GrMaskFormat == format ?
-                              SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) :
-                              color;
+        GrColor subRunColor;
+        if (kARGB_GrMaskFormat == format) {
+            uint8_t paintAlpha = skPaint.getAlpha();
+            subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
+        } else {
+            subRunColor = color;
+        }
 
-        SkAutoTUnref<BitmapTextBatch> batch(BitmapTextBatch::Create(format, glyphCount,
-                                                            fContext->getBatchFontCache()));
+        SkAutoTUnref<BitmapTextBatch> batch;
+        if (info.fDrawAsDistanceFields) {
+            SkColor filteredColor;
+            SkColorFilter* colorFilter = skPaint.getColorFilter();
+            if (colorFilter) {
+                filteredColor = colorFilter->filterColor(skPaint.getColor());
+            } else {
+                filteredColor = skPaint.getColor();
+            }
+            bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
+            float gamma = fDeviceProperties.gamma();
+            batch.reset(BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
+                                                fDistanceAdjustTable, filteredColor,
+                                                info.fUseLCDText, useBGR,
+                                                gamma));
+        } else {
+            batch.reset(BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache()));
+        }
         BitmapTextBatch::Geometry& geometry = batch->geometry();
         geometry.fBlob = SkRef(cacheBlob);
         geometry.fRun = run;
@@ -1284,7 +1982,6 @@ void GrAtlasTextContext::flush(GrDrawTarget* target,
     pipelineBuilder.setFromPaint(grPaint, rt, clip);
 
     GrColor color = grPaint.getColor();
-    uint8_t paintAlpha = skPaint.getAlpha();
 
     SkTextBlob::RunIterator it(blob);
     for (int run = 0; !it.done(); it.next(), run++) {
@@ -1293,7 +1990,7 @@ void GrAtlasTextContext::flush(GrDrawTarget* target,
             continue;
         }
         cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
-        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, transX, transY);
+        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
     }
 
     // Now flush big glyphs
@@ -1305,15 +2002,13 @@ void GrAtlasTextContext::flush(GrDrawTarget* target,
                                GrRenderTarget* rt,
                                const SkPaint& skPaint,
                                const GrPaint& grPaint,
-                               const GrClip& clip,
-                               const SkMatrix& viewMatrix) {
+                               const GrClip& clip) {
     GrPipelineBuilder pipelineBuilder;
     pipelineBuilder.setFromPaint(grPaint, rt, clip);
 
     GrColor color = grPaint.getColor();
-    uint8_t paintAlpha = skPaint.getAlpha();
     for (int run = 0; run < cacheBlob->fRunCount; run++) {
-        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, 0, 0);
+        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
     }
 
     // Now flush big glyphs
index 79cb7a4169da545ebdf3128dc45006b0316412c2..6fd35538f67750f906d795a6f8db6e2c69226978 100644 (file)
@@ -28,10 +28,13 @@ class GrTextBlobCache;
  */
 class GrAtlasTextContext : public GrTextContext {
 public:
-    static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
+    static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
+                                      bool enableDistanceFields);
 
 private:
-    GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
+    GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
+                       bool enableDistanceFields);
+    ~GrAtlasTextContext() override {}
 
     bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
                  const SkPaint&, const SkMatrix& viewMatrix) override;
@@ -98,7 +101,8 @@ private:
                     , fGlyphStartIndex(0)
                     , fGlyphEndIndex(0)
                     , fVertexStartIndex(0)
-                    , fVertexEndIndex(0) {}
+                    , fVertexEndIndex(0)
+                    , fDrawAsDistanceFields(false) {}
                 GrMaskFormat fMaskFormat;
                 uint64_t fAtlasGeneration;
                 uint32_t fGlyphStartIndex;
@@ -106,6 +110,10 @@ private:
                 size_t fVertexStartIndex;
                 size_t fVertexEndIndex;
                 GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken;
+
+                // distance field properties
+                bool fDrawAsDistanceFields;
+                bool fUseLCDText;
             };
 
             class SubRunInfoArray {
@@ -227,14 +235,27 @@ private:
     typedef BitmapTextBlob::Run Run;
     typedef Run::SubRunInfo PerSubRunInfo;
 
-    void appendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
-                     GrColor color, GrFontScaler*, const SkIRect& clipRect);
+    inline bool canDrawAsDistanceFields(const SkPaint&, const SkMatrix& viewMatrix);
+    BitmapTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint,
+                                const SkMatrix& viewMatrix, SkGlyphCache** cache,
+                                SkPaint* dfPaint, SkScalar* textRatio);
+    void bmpAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
+                        GrColor color, GrFontScaler*, const SkIRect& clipRect);
+    bool dfAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, SkScalar sx, SkScalar sy,
+                       GrColor color, GrFontScaler*, const SkIRect& clipRect, SkScalar textRatio,
+                       const SkMatrix& viewMatrix);
+    inline void appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
+                                GrFontScaler* scaler, int x, int y);
+    inline void appendGlyphCommon(BitmapTextBlob*, Run*, Run::SubRunInfo*,
+                                  const SkRect& positions, GrColor color,
+                                  size_t vertexStride, bool useVertexColor,
+                                  GrGlyph::PackedID);
 
     inline void flushRunAsPaths(const SkTextBlob::RunIterator&, const SkPaint&, SkDrawFilter*,
                                 const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x,
                                 SkScalar y);
     inline void flushRun(GrDrawTarget*, GrPipelineBuilder*, BitmapTextBlob*, int run, GrColor,
-                         uint8_t paintAlpha, SkScalar transX, SkScalar transY);
+                         SkScalar transX, SkScalar transY, const SkPaint&);
     inline void flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
                                const GrPaint& grPaint, const GrClip& clip,
                                SkScalar transX, SkScalar transY);
@@ -244,21 +265,48 @@ private:
                const GrPaint&, SkDrawFilter*, const GrClip&, const SkMatrix& viewMatrix,
                const SkIRect& clipBounds, SkScalar x, SkScalar y, SkScalar transX, SkScalar transY);
     void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
-               const GrPaint&, const GrClip&, const SkMatrix& viewMatrix);
+               const GrPaint&, const GrClip&);
+
+    // A helper for drawing BitmapText in a run of distance fields
+    inline void fallbackDrawPosText(GrRenderTarget*, const GrClip&,
+                                    const GrPaint&,
+                                    const SkPaint&, const SkMatrix& viewMatrix,
+                                    const SkTDArray<char>& fallbackTxt,
+                                    const SkTDArray<SkScalar>& fallbackPos,
+                                    int scalarsPerPosition,
+                                    const SkPoint& offset,
+                                    const SkIRect& clipRect);
 
-    void internalDrawText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
-                          GrColor color, const SkMatrix& viewMatrix,
-                          const char text[], size_t byteLength,
-                          SkScalar x, SkScalar y, const SkIRect& clipRect);
-    void internalDrawPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+    void internalDrawBMPText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
                              GrColor color, const SkMatrix& viewMatrix,
                              const char text[], size_t byteLength,
-                             const SkScalar pos[], int scalarsPerPosition,
-                             const SkPoint& offset, const SkIRect& clipRect);
+                             SkScalar x, SkScalar y, const SkIRect& clipRect);
+    void internalDrawBMPPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                                GrColor color, const SkMatrix& viewMatrix,
+                                const char text[], size_t byteLength,
+                                const SkScalar pos[], int scalarsPerPosition,
+                                const SkPoint& offset, const SkIRect& clipRect);
+
+    void internalDrawDFText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                            GrColor color, const SkMatrix& viewMatrix,
+                            const char text[], size_t byteLength,
+                            SkScalar x, SkScalar y, const SkIRect& clipRect,
+                            SkScalar textRatio,
+                            SkTDArray<char>* fallbackTxt,
+                            SkTDArray<SkScalar>* fallbackPos,
+                            SkPoint* offset, const SkPaint& origPaint);
+    void internalDrawDFPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                               GrColor color, const SkMatrix& viewMatrix,
+                               const char text[], size_t byteLength,
+                               const SkScalar pos[], int scalarsPerPosition,
+                               const SkPoint& offset, const SkIRect& clipRect,
+                               SkScalar textRatio,
+                               SkTDArray<char>* fallbackTxt,
+                               SkTDArray<SkScalar>* fallbackPos);
 
     // sets up the descriptor on the blob and returns a detached cache.  Client must attach
     inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd);
-    inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix& viewMatrix);
+    inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix* viewMatrix, bool noGamma);
     static inline bool MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
                                           const BitmapTextBlob&, const SkPaint&,
                                           const SkMaskFilter::BlurRec&,
@@ -268,9 +316,32 @@ private:
                             const SkTextBlob* blob, SkScalar x, SkScalar y,
                             SkDrawFilter* drawFilter, const SkIRect& clipRect);
     inline static bool HasLCD(const SkTextBlob*);
+    inline void initDistanceFieldPaint(SkPaint*, SkScalar* textRatio, const SkMatrix&);
+
+    // Distance field text needs this table to compute a value for use in the fragment shader.
+    // Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be
+    // refcnted and malloced
+    struct DistanceAdjustTable : public SkNVRefCnt<DistanceAdjustTable> {
+        DistanceAdjustTable(float gamma) { this->buildDistanceAdjustTable(gamma); }
+        ~DistanceAdjustTable() { SkDELETE_ARRAY(fTable); }
+
+        void buildDistanceAdjustTable(float gamma);
+
+        SkScalar& operator[] (int i) {
+            return fTable[i];
+        }
+
+        const SkScalar& operator[] (int i) const {
+            return fTable[i];
+        }
+
+        SkScalar* fTable;
+    };
 
     GrBatchTextStrike* fCurrStrike;
     GrTextBlobCache* fCache;
+    bool fEnableDFRendering;
+    SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
 
     friend class GrTextBlobCache;
     friend class BitmapTextBatch;
index be404790ea46c81e8c029e20545497a9097142c1..43ac464c24b4b705043aa0146ea22b8b974af630 100755 (executable)
@@ -242,7 +242,7 @@ GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
     } 
 
 #ifdef USE_BITMAP_TEXTBLOBS
-    return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties);
+    return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties, enableDistanceFieldFonts);
 #else
     return GrDistanceFieldTextContext::Create(this, gpuDevice, leakyProperties,
                                               enableDistanceFieldFonts);
index cc592833c9b787bab2eb4aecfa9b43ccbc00ba0b..a5d11e6fb9fbc4afc5f1dcf653e90672b9ff41db 100755 (executable)
@@ -82,7 +82,8 @@ GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* contex
                                                          (context, gpuDevice, props, enable));
     textContext->buildDistanceAdjustTable();
 #ifdef USE_BITMAP_TEXTBLOBS
-    textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props);
+    textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
+                                                                   enable);
 #else
     textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
 #endif
index 1456aa22b5802d0e860dbcf1c17d18258e6499e1..330f79d9465fdde3e4568ed503913f8c256a096b 100644 (file)
@@ -36,7 +36,8 @@ GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
     GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
                                                            (context, gpuDevice, props));
 #ifdef USE_BITMAP_TEXTBLOBS
-    textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props);
+    textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
+                                                                   false);
 #else
     textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
 #endif