Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 #include "GrAtlasTextContext.h"
      8 
      9 #include "GrBatch.h"
     10 #include "GrBatchFontCache.h"
     11 #include "GrBatchTarget.h"
     12 #include "GrBatchTest.h"
     13 #include "GrDefaultGeoProcFactory.h"
     14 #include "GrDrawTarget.h"
     15 #include "GrFontScaler.h"
     16 #include "GrIndexBuffer.h"
     17 #include "GrResourceProvider.h"
     18 #include "GrStrokeInfo.h"
     19 #include "GrTextBlobCache.h"
     20 #include "GrTexturePriv.h"
     21 #include "GrVertexBuffer.h"
     22 
     23 #include "SkAutoKern.h"
     24 #include "SkColorPriv.h"
     25 #include "SkColorFilter.h"
     26 #include "SkDistanceFieldGen.h"
     27 #include "SkDraw.h"
     28 #include "SkDrawFilter.h"
     29 #include "SkDrawProcs.h"
     30 #include "SkGlyphCache.h"
     31 #include "SkGpuDevice.h"
     32 #include "SkGr.h"
     33 #include "SkPath.h"
     34 #include "SkRTConf.h"
     35 #include "SkStrokeRec.h"
     36 #include "SkTextBlob.h"
     37 #include "SkTextMapStateProc.h"
     38 
     39 #include "effects/GrBitmapTextGeoProc.h"
     40 #include "effects/GrDistanceFieldGeoProc.h"
     41 
     42 namespace {
     43 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
     44 
     45 // position + local coord
     46 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
     47 
     48 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
     49 
     50 static const int kMinDFFontSize = 18;
     51 static const int kSmallDFFontSize = 32;
     52 static const int kSmallDFFontLimit = 32;
     53 static const int kMediumDFFontSize = 72;
     54 static const int kMediumDFFontLimit = 72;
     55 static const int kLargeDFFontSize = 162;
     56 static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
     57 
     58 SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
     59 static const int kDistanceAdjustLumShift = 5;
     60 
     61 static const int kVerticesPerGlyph = 4;
     62 static const int kIndicesPerGlyph = 6;
     63 
     64 static size_t get_vertex_stride(GrMaskFormat maskFormat) {
     65     switch (maskFormat) {
     66         case kA8_GrMaskFormat:
     67             return kGrayTextVASize;
     68         case kARGB_GrMaskFormat:
     69             return kColorTextVASize;
     70         default:
     71             return kLCDTextVASize;
     72     }
     73 }
     74 
     75 static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
     76     SkASSERT(maskFormat == kA8_GrMaskFormat);
     77     if (useLCDText) {
     78         return kLCDTextVASize;
     79     } else {
     80         return kGrayTextVASize;
     81     }
     82 }
     83 
     84 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
     85     unsigned r = SkColorGetR(c);
     86     unsigned g = SkColorGetG(c);
     87     unsigned b = SkColorGetB(c);
     88     return GrColorPackRGBA(r, g, b, 0xff);
     89 }
     90 
     91 };
     92 
     93 // TODO
     94 // Distance field text in textblobs
     95 
     96 GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
     97                                        SkGpuDevice* gpuDevice,
     98                                        const SkDeviceProperties& properties,
     99                                        bool enableDistanceFields)
    100     : INHERITED(context, gpuDevice, properties)
    101     , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
    102     // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
    103     // vertexStride
    104     SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
    105                       vertex_attribute_changed);
    106     fCurrStrike = NULL;
    107     fCache = context->getTextBlobCache();
    108 
    109 #if SK_FORCE_DISTANCE_FIELD_TEXT
    110     fEnableDFRendering = true;
    111 #else
    112     fEnableDFRendering = enableDistanceFields;
    113 #endif
    114 }
    115 
    116 void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
    117 
    118     // This is used for an approximation of the mask gamma hack, used by raster and bitmap
    119     // text. The mask gamma hack is based off of guessing what the blend color is going to
    120     // be, and adjusting the mask so that when run through the linear blend will
    121     // produce the value closest to the desired result. However, in practice this means
    122     // that the 'adjusted' mask is just increasing or decreasing the coverage of
    123     // the mask depending on what it is thought it will blit against. For black (on
    124     // assumed white) this means that coverages are decreased (on a curve). For white (on
    125     // assumed black) this means that coverages are increased (on a a curve). At
    126     // middle (perceptual) gray (which could be blit against anything) the coverages
    127     // remain the same.
    128     //
    129     // The idea here is that instead of determining the initial (real) coverage and
    130     // then adjusting that coverage, we determine an adjusted coverage directly by
    131     // essentially manipulating the geometry (in this case, the distance to the glyph
    132     // edge). So for black (on assumed white) this thins a bit; for white (on
    133     // assumed black) this fake bolds the geometry a bit.
    134     //
    135     // The distance adjustment is calculated by determining the actual coverage value which
    136     // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
    137     // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
    138     // actual edge. So by subtracting this distance adjustment and computing without the
    139     // the coverage adjustment we should get 0.5 coverage at the same point.
    140     //
    141     // This has several implications:
    142     //     For non-gray lcd smoothed text, each subpixel essentially is using a
    143     //     slightly different geometry.
    144     //
    145     //     For black (on assumed white) this may not cover some pixels which were
    146     //     previously covered; however those pixels would have been only slightly
    147     //     covered and that slight coverage would have been decreased anyway. Also, some pixels
    148     //     which were previously fully covered may no longer be fully covered.
    149     //
    150     //     For white (on assumed black) this may cover some pixels which weren't
    151     //     previously covered at all.
    152 
    153     int width, height;
    154     size_t size;
    155 
    156 #ifdef SK_GAMMA_CONTRAST
    157     SkScalar contrast = SK_GAMMA_CONTRAST;
    158 #else
    159     SkScalar contrast = 0.5f;
    160 #endif
    161     SkScalar paintGamma = gamma;
    162     SkScalar deviceGamma = gamma;
    163 
    164     size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
    165         &width, &height);
    166 
    167     SkASSERT(kExpectedDistanceAdjustTableSize == height);
    168     fTable = SkNEW_ARRAY(SkScalar, height);
    169 
    170     SkAutoTArray<uint8_t> data((int)size);
    171     SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
    172 
    173     // find the inverse points where we cross 0.5
    174     // binsearch might be better, but we only need to do this once on creation
    175     for (int row = 0; row < height; ++row) {
    176         uint8_t* rowPtr = data.get() + row*width;
    177         for (int col = 0; col < width - 1; ++col) {
    178             if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
    179                 // compute point where a mask value will give us a result of 0.5
    180                 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
    181                 float borderAlpha = (col + interp) / 255.f;
    182 
    183                 // compute t value for that alpha
    184                 // this is an approximate inverse for smoothstep()
    185                 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
    186 
    187                 // compute distance which gives us that t value
    188                 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
    189                 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
    190 
    191                 fTable[row] = d;
    192                 break;
    193             }
    194         }
    195     }
    196 }
    197 
    198 GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
    199                                                SkGpuDevice* gpuDevice,
    200                                                const SkDeviceProperties& props,
    201                                                bool enableDistanceFields) {
    202     return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
    203 }
    204 
    205 bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
    206                                  const GrClip&,
    207                                  const GrPaint&,
    208                                  const SkPaint& skPaint,
    209                                  const SkMatrix& viewMatrix) {
    210     return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
    211            !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
    212 }
    213 
    214 GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
    215     GrColor canonicalColor = paint.computeLuminanceColor();
    216     if (lcd) {
    217         // This is the correct computation, but there are tons of cases where LCD can be overridden.
    218         // For now we just regenerate if any run in a textblob has LCD.
    219         // TODO figure out where all of these overrides are and see if we can incorporate that logic
    220         // at a higher level *OR* use sRGB
    221         SkASSERT(false);
    222         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
    223     } else {
    224         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
    225         // gamma corrected masks anyways, nor color
    226         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
    227                                        SkColorGetG(canonicalColor),
    228                                        SkColorGetB(canonicalColor));
    229         // reduce to our finite number of bits
    230         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
    231     }
    232     return canonicalColor;
    233 }
    234 
    235 // TODO if this function ever shows up in profiling, then we can compute this value when the
    236 // textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
    237 // run so this is not a big deal to compute here.
    238 bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
    239     SkTextBlob::RunIterator it(blob);
    240     for (; !it.done(); it.next()) {
    241         if (it.isLCD()) {
    242             return true;
    243         }
    244     }
    245     return false;
    246 }
    247 
    248 bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
    249                                             const BitmapTextBlob& blob, const SkPaint& paint,
    250                                             const SkMaskFilter::BlurRec& blurRec,
    251                                             const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    252     // If we have LCD text then our canonical color will be set to transparent, in this case we have
    253     // to regenerate the blob on any color change
    254     if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
    255         return true;
    256     }
    257 
    258     if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
    259         return true;
    260     }
    261 
    262     if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
    263         return true;
    264     }
    265 
    266     // We only cache one masked version
    267     if (blob.fKey.fHasBlur &&
    268         (blob.fBlurRec.fSigma != blurRec.fSigma ||
    269          blob.fBlurRec.fStyle != blurRec.fStyle ||
    270          blob.fBlurRec.fQuality != blurRec.fQuality)) {
    271         return true;
    272     }
    273 
    274     // Similarly, we only cache one version for each style
    275     if (blob.fKey.fStyle != SkPaint::kFill_Style &&
    276         (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
    277          blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
    278          blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
    279         return true;
    280     }
    281 
    282     // Mixed blobs must be regenerated.  We could probably figure out a way to do integer scrolls
    283     // for mixed blobs if this becomes an issue.
    284     if (blob.hasBitmap() && blob.hasDistanceField()) {
    285         // Identical viewmatrices and we can reuse in all cases
    286         if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
    287             return false;
    288         }
    289         return true;
    290     }
    291 
    292     if (blob.hasBitmap()) {
    293         if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
    294             blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
    295             blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
    296             blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
    297             return true;
    298         }
    299 
    300         // We can update the positions in the cachedtextblobs without regenerating the whole blob,
    301         // but only for integer translations.
    302         // This cool bit of math will determine the necessary translation to apply to the already
    303         // generated vertex coordinates to move them to the correct position
    304         SkScalar transX = viewMatrix.getTranslateX() +
    305                           viewMatrix.getScaleX() * (x - blob.fX) +
    306                           viewMatrix.getSkewX() * (y - blob.fY) -
    307                           blob.fViewMatrix.getTranslateX();
    308         SkScalar transY = viewMatrix.getTranslateY() +
    309                           viewMatrix.getSkewY() * (x - blob.fX) +
    310                           viewMatrix.getScaleY() * (y - blob.fY) -
    311                           blob.fViewMatrix.getTranslateY();
    312         if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
    313             return true;
    314         }
    315 
    316         (*outTransX) = transX;
    317         (*outTransY) = transY;
    318     } else if (blob.hasDistanceField()) {
    319         // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
    320         // distance field being generated, so we have to regenerate in those cases
    321         SkScalar newMaxScale = viewMatrix.getMaxScale();
    322         SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
    323         SkScalar scaleAdjust = newMaxScale / oldMaxScale;
    324         if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
    325             return true;
    326         }
    327 
    328         (*outTransX) = x - blob.fX;
    329         (*outTransY) = y - blob.fY;
    330     }
    331     // It is possible that a blob has neither distanceField nor bitmaptext.  This is in the case
    332     // when all of the runs inside the blob are drawn as paths.  In this case, we always regenerate
    333     // the blob anyways at flush time, so no need to regenerate explicitly
    334 
    335     return false;
    336 }
    337 
    338 
    339 inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
    340                                                     const SkPaint& skPaint,
    341                                                     const SkMatrix* viewMatrix,
    342                                                     bool noGamma) {
    343     skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
    344     run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
    345     return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
    346 }
    347 
    348 void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
    349                                       const SkPaint& skPaint, const SkMatrix& viewMatrix,
    350                                       const SkTextBlob* blob, SkScalar x, SkScalar y,
    351                                       SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
    352     // If we have been abandoned, then don't draw
    353     if (!fContext->getTextTarget()) {
    354         return;
    355     }
    356 
    357     SkAutoTUnref<BitmapTextBlob> cacheBlob;
    358     SkMaskFilter::BlurRec blurRec;
    359     BitmapTextBlob::Key key;
    360     // It might be worth caching these things, but its not clear at this time
    361     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
    362     const SkMaskFilter* mf = skPaint.getMaskFilter();
    363     bool canCache = !(skPaint.getPathEffect() ||
    364                       (mf && !mf->asABlur(&blurRec)) ||
    365                       drawFilter);
    366 
    367     if (canCache) {
    368         bool hasLCD = HasLCD(blob);
    369 
    370         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
    371         SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() :
    372                                                  kUnknown_SkPixelGeometry;
    373 
    374         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
    375         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
    376         // ensure we always match the same key
    377         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
    378                                           ComputeCanonicalColor(skPaint, hasLCD);
    379 
    380         key.fPixelGeometry = pixelGeometry;
    381         key.fUniqueID = blob->uniqueID();
    382         key.fStyle = skPaint.getStyle();
    383         key.fHasBlur = SkToBool(mf);
    384         key.fCanonicalColor = canonicalColor;
    385         cacheBlob.reset(SkSafeRef(fCache->find(key)));
    386     }
    387 
    388     SkIRect clipRect;
    389     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
    390 
    391     SkScalar transX = 0.f;
    392     SkScalar transY = 0.f;
    393 
    394     // Though for the time being runs in the textblob can override the paint, they only touch font
    395     // info.
    396     GrPaint grPaint;
    397     if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
    398         return;
    399     }
    400 
    401     if (cacheBlob) {
    402         if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
    403             // We have to remake the blob because changes may invalidate our masks.
    404             // TODO we could probably get away reuse most of the time if the pointer is unique,
    405             // but we'd have to clear the subrun information
    406             fCache->remove(cacheBlob);
    407             cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
    408                                                            kGrayTextVASize)));
    409             this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
    410                                      drawFilter, clipRect, rt, clip, grPaint);
    411         } else {
    412             // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
    413             // offsets
    414             cacheBlob->fViewMatrix = viewMatrix;
    415             cacheBlob->fX = x;
    416             cacheBlob->fY = y;
    417             fCache->makeMRU(cacheBlob);
    418         }
    419     } else {
    420         if (canCache) {
    421             cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
    422                                                            kGrayTextVASize)));
    423         } else {
    424             cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
    425         }
    426         this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
    427                                  drawFilter, clipRect, rt, clip, grPaint);
    428     }
    429 
    430     cacheBlob->fPaintColor = skPaint.getColor();
    431     this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
    432                 clip, viewMatrix, clipBounds, x, y, transX, transY);
    433 }
    434 
    435 inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
    436                                                         const SkMatrix& viewMatrix) {
    437     // TODO: support perspective (need getMaxScale replacement)
    438     if (viewMatrix.hasPerspective()) {
    439         return false;
    440     }
    441 
    442     SkScalar maxScale = viewMatrix.getMaxScale();
    443     SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
    444     // Hinted text looks far better at small resolutions
    445     // Scaling up beyond 2x yields undesireable artifacts
    446     if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
    447         return false;
    448     }
    449 
    450     if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
    451         scaledTextSize < kLargeDFFontSize) {
    452         return false;
    453     }
    454 
    455     // rasterizers and mask filters modify alpha, which doesn't
    456     // translate well to distance
    457     if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
    458         !fContext->getTextTarget()->caps()->shaderCaps()->shaderDerivativeSupport()) {
    459         return false;
    460     }
    461 
    462     // TODO: add some stroking support
    463     if (skPaint.getStyle() != SkPaint::kFill_Style) {
    464         return false;
    465     }
    466 
    467     return true;
    468 }
    469 
    470 void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
    471                                             const SkPaint& skPaint, GrColor color,
    472                                             const SkMatrix& viewMatrix,
    473                                             const SkTextBlob* blob, SkScalar x, SkScalar y,
    474                                             SkDrawFilter* drawFilter, const SkIRect& clipRect,
    475                                             GrRenderTarget* rt, const GrClip& clip,
    476                                             const GrPaint& paint) {
    477     cacheBlob->fViewMatrix = viewMatrix;
    478     cacheBlob->fX = x;
    479     cacheBlob->fY = y;
    480 
    481     // Regenerate textblob
    482     SkPaint runPaint = skPaint;
    483     SkTextBlob::RunIterator it(blob);
    484     for (int run = 0; !it.done(); it.next(), run++) {
    485         int glyphCount = it.glyphCount();
    486         size_t textLen = glyphCount * sizeof(uint16_t);
    487         const SkPoint& offset = it.offset();
    488         // applyFontToPaint() always overwrites the exact same attributes,
    489         // so it is safe to not re-seed the paint for this reason.
    490         it.applyFontToPaint(&runPaint);
    491 
    492         if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
    493             // A false return from filter() means we should abort the current draw.
    494             runPaint = skPaint;
    495             continue;
    496         }
    497 
    498         runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
    499 
    500         // setup vertex / glyphIndex for the new run
    501         if (run > 0) {
    502             PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
    503             PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
    504 
    505             newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
    506             newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
    507 
    508             newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
    509             newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
    510         }
    511 
    512         if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
    513             cacheBlob->setHasDistanceField();
    514             SkPaint dfPaint = runPaint;
    515             SkScalar textRatio;
    516             this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
    517             Run& runIdx = cacheBlob->fRuns[run];
    518             PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
    519             subRun.fUseLCDText = runPaint.isLCDRenderText();
    520             subRun.fDrawAsDistanceFields = true;
    521 
    522             SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
    523 
    524             SkTDArray<char> fallbackTxt;
    525             SkTDArray<SkScalar> fallbackPos;
    526             SkPoint dfOffset;
    527             int scalarsPerPosition = 2;
    528             switch (it.positioning()) {
    529                 case SkTextBlob::kDefault_Positioning: {
    530                     this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
    531                                              (const char *)it.glyphs(), textLen,
    532                                              x + offset.x(), y + offset.y(), clipRect, textRatio,
    533                                              &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
    534                     break;
    535                 }
    536                 case SkTextBlob::kHorizontal_Positioning: {
    537                     scalarsPerPosition = 1;
    538                     dfOffset = SkPoint::Make(x, y + offset.y());
    539                     this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
    540                                                 (const char*)it.glyphs(), textLen, it.pos(),
    541                                                 scalarsPerPosition, dfOffset, clipRect, textRatio,
    542                                                 &fallbackTxt, &fallbackPos);
    543                     break;
    544                 }
    545                 case SkTextBlob::kFull_Positioning: {
    546                     dfOffset = SkPoint::Make(x, y);
    547                     this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
    548                                                 (const char*)it.glyphs(), textLen, it.pos(),
    549                                                 scalarsPerPosition, dfOffset, clipRect, textRatio,
    550                                                 &fallbackTxt, &fallbackPos);
    551                     break;
    552                 }
    553             }
    554             if (fallbackTxt.count()) {
    555                 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
    556                                           fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
    557                                           clipRect);
    558             }
    559 
    560             SkGlyphCache::AttachCache(cache);
    561         } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
    562             cacheBlob->fRuns[run].fDrawAsPaths = true;
    563         } else {
    564             cacheBlob->setHasBitmap();
    565             SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
    566                                                    false);
    567             switch (it.positioning()) {
    568                 case SkTextBlob::kDefault_Positioning:
    569                     this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
    570                                               (const char *)it.glyphs(), textLen,
    571                                               x + offset.x(), y + offset.y(), clipRect);
    572                     break;
    573                 case SkTextBlob::kHorizontal_Positioning:
    574                     this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
    575                                                  (const char*)it.glyphs(), textLen, it.pos(), 1,
    576                                                  SkPoint::Make(x, y + offset.y()), clipRect);
    577                     break;
    578                 case SkTextBlob::kFull_Positioning:
    579                     this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
    580                                                  (const char*)it.glyphs(), textLen, it.pos(), 2,
    581                                                  SkPoint::Make(x, y), clipRect);
    582                     break;
    583             }
    584             SkGlyphCache::AttachCache(cache);
    585         }
    586 
    587         if (drawFilter) {
    588             // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
    589             runPaint = skPaint;
    590         }
    591     }
    592 }
    593 
    594 inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
    595                                                        SkPaint* skPaint,
    596                                                        SkScalar* textRatio,
    597                                                        const SkMatrix& viewMatrix) {
    598     // getMaxScale doesn't support perspective, so neither do we at the moment
    599     SkASSERT(!viewMatrix.hasPerspective());
    600     SkScalar maxScale = viewMatrix.getMaxScale();
    601     SkScalar textSize = skPaint->getTextSize();
    602     SkScalar scaledTextSize = textSize;
    603     // if we have non-unity scale, we need to choose our base text size
    604     // based on the SkPaint's text size multiplied by the max scale factor
    605     // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
    606     if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
    607         scaledTextSize *= maxScale;
    608     }
    609 
    610     // We have three sizes of distance field text, and within each size 'bucket' there is a floor
    611     // and ceiling.  A scale outside of this range would require regenerating the distance fields
    612     SkScalar dfMaskScaleFloor;
    613     SkScalar dfMaskScaleCeil;
    614     if (scaledTextSize <= kSmallDFFontLimit) {
    615         dfMaskScaleFloor = kMinDFFontSize;
    616         dfMaskScaleCeil = kSmallDFFontLimit;
    617         *textRatio = textSize / kSmallDFFontSize;
    618         skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
    619     } else if (scaledTextSize <= kMediumDFFontLimit) {
    620         dfMaskScaleFloor = kSmallDFFontLimit;
    621         dfMaskScaleCeil = kMediumDFFontLimit;
    622         *textRatio = textSize / kMediumDFFontSize;
    623         skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
    624     } else {
    625         dfMaskScaleFloor = kMediumDFFontLimit;
    626         dfMaskScaleCeil = kLargeDFFontLimit;
    627         *textRatio = textSize / kLargeDFFontSize;
    628         skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
    629     }
    630 
    631     // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
    632     // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
    633     // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
    634     // tolerate before we'd have to move to a large mip size.  When we actually test these values
    635     // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
    636     // against these values to decide if we can reuse or not(ie, will a given scale change our mip
    637     // level)
    638     SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
    639     blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
    640     blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
    641 
    642     skPaint->setLCDRenderText(false);
    643     skPaint->setAutohinted(false);
    644     skPaint->setHinting(SkPaint::kNormal_Hinting);
    645     skPaint->setSubpixelText(true);
    646 }
    647 
    648 inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
    649                                                     int runIndex,
    650                                                     GrRenderTarget* rt, const GrClip& clip,
    651                                                     const GrPaint& paint,
    652                                                     const SkPaint& skPaint,
    653                                                     const SkMatrix& viewMatrix,
    654                                                     const SkTDArray<char>& fallbackTxt,
    655                                                     const SkTDArray<SkScalar>& fallbackPos,
    656                                                     int scalarsPerPosition,
    657                                                     const SkPoint& offset,
    658                                                     const SkIRect& clipRect) {
    659     SkASSERT(fallbackTxt.count());
    660     blob->setHasBitmap();
    661     Run& run = blob->fRuns[runIndex];
    662     // Push back a new subrun to fill and set the override descriptor
    663     run.push_back();
    664     run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
    665     skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
    666                                        &fDeviceProperties, &viewMatrix, false);
    667     SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
    668                                                     run.fOverrideDescriptor->getDesc());
    669     this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
    670                                  fallbackTxt.begin(), fallbackTxt.count(),
    671                                  fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
    672     SkGlyphCache::AttachCache(cache);
    673 }
    674 
    675 inline GrAtlasTextContext::BitmapTextBlob*
    676 GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
    677                                 const SkMatrix& viewMatrix, SkGlyphCache** cache,
    678                                 SkPaint* dfPaint, SkScalar* textRatio) {
    679     BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
    680 
    681     *dfPaint = origPaint;
    682     this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
    683     blob->fViewMatrix = viewMatrix;
    684     Run& run = blob->fRuns[0];
    685     PerSubRunInfo& subRun = run.fSubRunInfo.back();
    686     subRun.fUseLCDText = origPaint.isLCDRenderText();
    687     subRun.fDrawAsDistanceFields = true;
    688 
    689     *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
    690     return blob;
    691 }
    692 
    693 inline GrAtlasTextContext::BitmapTextBlob*
    694 GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
    695                                        const GrPaint& paint, const SkPaint& skPaint,
    696                                        const SkMatrix& viewMatrix,
    697                                        const char text[], size_t byteLength,
    698                                        SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
    699     int glyphCount = skPaint.countText(text, byteLength);
    700     SkIRect clipRect;
    701     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
    702 
    703     BitmapTextBlob* blob;
    704     if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
    705         SkPaint dfPaint;
    706         SkScalar textRatio;
    707         SkGlyphCache* cache;
    708         blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
    709 
    710         SkTDArray<char> fallbackTxt;
    711         SkTDArray<SkScalar> fallbackPos;
    712         SkPoint offset;
    713         this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
    714                                  byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
    715                                  &offset, skPaint);
    716         SkGlyphCache::AttachCache(cache);
    717         if (fallbackTxt.count()) {
    718             this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
    719                                       fallbackPos, 2, offset, clipRect);
    720         }
    721     } else {
    722         blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
    723         blob->fViewMatrix = viewMatrix;
    724 
    725         SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
    726         this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
    727                                   byteLength, x, y, clipRect);
    728         SkGlyphCache::AttachCache(cache);
    729     }
    730     return blob;
    731 }
    732 
    733 inline GrAtlasTextContext::BitmapTextBlob*
    734 GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
    735                                           const GrPaint& paint, const SkPaint& skPaint,
    736                                           const SkMatrix& viewMatrix,
    737                                           const char text[], size_t byteLength,
    738                                           const SkScalar pos[], int scalarsPerPosition,
    739                                           const SkPoint& offset, const SkIRect& regionClipBounds) {
    740     int glyphCount = skPaint.countText(text, byteLength);
    741 
    742     SkIRect clipRect;
    743     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
    744 
    745     BitmapTextBlob* blob;
    746     if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
    747         SkPaint dfPaint;
    748         SkScalar textRatio;
    749         SkGlyphCache* cache;
    750         blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
    751 
    752         SkTDArray<char> fallbackTxt;
    753         SkTDArray<SkScalar> fallbackPos;
    754         this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
    755                                     byteLength, pos, scalarsPerPosition, offset, clipRect,
    756                                     textRatio, &fallbackTxt, &fallbackPos);
    757         SkGlyphCache::AttachCache(cache);
    758         if (fallbackTxt.count()) {
    759             this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
    760                                       fallbackPos, scalarsPerPosition, offset, clipRect);
    761         }
    762     } else {
    763         blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
    764         blob->fViewMatrix = viewMatrix;
    765         SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
    766         this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
    767                                      byteLength, pos, scalarsPerPosition, offset, clipRect);
    768         SkGlyphCache::AttachCache(cache);
    769     }
    770     return blob;
    771 }
    772 
    773 void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
    774                                     const GrPaint& paint, const SkPaint& skPaint,
    775                                     const SkMatrix& viewMatrix,
    776                                     const char text[], size_t byteLength,
    777                                     SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
    778     SkAutoTUnref<BitmapTextBlob> blob(
    779             this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
    780                                      text, byteLength, x, y, regionClipBounds));
    781     this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds);
    782 }
    783 
    784 void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
    785                                        const GrPaint& paint, const SkPaint& skPaint,
    786                                        const SkMatrix& viewMatrix,
    787                                        const char text[], size_t byteLength,
    788                                        const SkScalar pos[], int scalarsPerPosition,
    789                                        const SkPoint& offset, const SkIRect& regionClipBounds) {
    790     SkAutoTUnref<BitmapTextBlob> blob(
    791             this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
    792                                         text, byteLength,
    793                                         pos, scalarsPerPosition,
    794                                         offset, regionClipBounds));
    795 
    796     this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds);
    797 }
    798 
    799 void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
    800                                              SkGlyphCache* cache, const SkPaint& skPaint,
    801                                              GrColor color,
    802                                              const SkMatrix& viewMatrix,
    803                                              const char text[], size_t byteLength,
    804                                              SkScalar x, SkScalar y, const SkIRect& clipRect) {
    805     SkASSERT(byteLength == 0 || text != NULL);
    806 
    807     // nothing to draw
    808     if (text == NULL || byteLength == 0) {
    809         return;
    810     }
    811 
    812     fCurrStrike = NULL;
    813     SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
    814 
    815     // Get GrFontScaler from cache
    816     GrFontScaler* fontScaler = GetGrFontScaler(cache);
    817 
    818     // transform our starting point
    819     {
    820         SkPoint loc;
    821         viewMatrix.mapXY(x, y, &loc);
    822         x = loc.fX;
    823         y = loc.fY;
    824     }
    825 
    826     // need to measure first
    827     if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
    828         SkVector    stopVector;
    829         MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
    830 
    831         SkScalar    stopX = stopVector.fX;
    832         SkScalar    stopY = stopVector.fY;
    833 
    834         if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
    835             stopX = SkScalarHalf(stopX);
    836             stopY = SkScalarHalf(stopY);
    837         }
    838         x -= stopX;
    839         y -= stopY;
    840     }
    841 
    842     const char* stop = text + byteLength;
    843 
    844     SkAutoKern autokern;
    845 
    846     SkFixed fxMask = ~0;
    847     SkFixed fyMask = ~0;
    848     SkScalar halfSampleX, halfSampleY;
    849     if (cache->isSubpixel()) {
    850         halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
    851         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
    852         if (kX_SkAxisAlignment == baseline) {
    853             fyMask = 0;
    854             halfSampleY = SK_ScalarHalf;
    855         } else if (kY_SkAxisAlignment == baseline) {
    856             fxMask = 0;
    857             halfSampleX = SK_ScalarHalf;
    858         }
    859     } else {
    860         halfSampleX = halfSampleY = SK_ScalarHalf;
    861     }
    862 
    863     Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
    864     Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
    865 
    866     while (text < stop) {
    867         const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
    868 
    869         fx += autokern.adjust(glyph);
    870 
    871         if (glyph.fWidth) {
    872             this->bmpAppendGlyph(blob,
    873                                  runIndex,
    874                                  GrGlyph::Pack(glyph.getGlyphID(),
    875                                                glyph.getSubXFixed(),
    876                                                glyph.getSubYFixed(),
    877                                                GrGlyph::kCoverage_MaskStyle),
    878                                  Sk48Dot16FloorToInt(fx),
    879                                  Sk48Dot16FloorToInt(fy),
    880                                  color,
    881                                  fontScaler,
    882                                  clipRect);
    883         }
    884 
    885         fx += glyph.fAdvanceX;
    886         fy += glyph.fAdvanceY;
    887     }
    888 }
    889 
    890 void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
    891                                                 SkGlyphCache* cache, const SkPaint& skPaint,
    892                                                 GrColor color,
    893                                                 const SkMatrix& viewMatrix,
    894                                                 const char text[], size_t byteLength,
    895                                                 const SkScalar pos[], int scalarsPerPosition,
    896                                                 const SkPoint& offset, const SkIRect& clipRect) {
    897     SkASSERT(byteLength == 0 || text != NULL);
    898     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
    899 
    900     // nothing to draw
    901     if (text == NULL || byteLength == 0) {
    902         return;
    903     }
    904 
    905     fCurrStrike = NULL;
    906     SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
    907 
    908     // Get GrFontScaler from cache
    909     GrFontScaler* fontScaler = GetGrFontScaler(cache);
    910 
    911     const char*        stop = text + byteLength;
    912     SkTextAlignProc    alignProc(skPaint.getTextAlign());
    913     SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
    914 
    915     if (cache->isSubpixel()) {
    916         // maybe we should skip the rounding if linearText is set
    917         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
    918 
    919         SkFixed fxMask = ~0;
    920         SkFixed fyMask = ~0;
    921         SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
    922         SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
    923         if (kX_SkAxisAlignment == baseline) {
    924             fyMask = 0;
    925             halfSampleY = SK_ScalarHalf;
    926         } else if (kY_SkAxisAlignment == baseline) {
    927             fxMask = 0;
    928             halfSampleX = SK_ScalarHalf;
    929         }
    930 
    931         if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
    932             while (text < stop) {
    933                 SkPoint tmsLoc;
    934                 tmsProc(pos, &tmsLoc);
    935                 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
    936                 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
    937 
    938                 const SkGlyph& glyph = glyphCacheProc(cache, &text,
    939                                                       fx & fxMask, fy & fyMask);
    940 
    941                 if (glyph.fWidth) {
    942                     this->bmpAppendGlyph(blob,
    943                                          runIndex,
    944                                          GrGlyph::Pack(glyph.getGlyphID(),
    945                                                        glyph.getSubXFixed(),
    946                                                        glyph.getSubYFixed(),
    947                                                        GrGlyph::kCoverage_MaskStyle),
    948                                          Sk48Dot16FloorToInt(fx),
    949                                          Sk48Dot16FloorToInt(fy),
    950                                          color,
    951                                          fontScaler,
    952                                          clipRect);
    953                 }
    954                 pos += scalarsPerPosition;
    955             }
    956         } else {
    957             while (text < stop) {
    958                 const char* currentText = text;
    959                 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
    960 
    961                 if (metricGlyph.fWidth) {
    962                     SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
    963                     SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
    964                     SkPoint tmsLoc;
    965                     tmsProc(pos, &tmsLoc);
    966                     SkPoint alignLoc;
    967                     alignProc(tmsLoc, metricGlyph, &alignLoc);
    968 
    969                     Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
    970                     Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
    971 
    972                     // have to call again, now that we've been "aligned"
    973                     const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
    974                                                           fx & fxMask, fy & fyMask);
    975                     // the assumption is that the metrics haven't changed
    976                     SkASSERT(prevAdvX == glyph.fAdvanceX);
    977                     SkASSERT(prevAdvY == glyph.fAdvanceY);
    978                     SkASSERT(glyph.fWidth);
    979 
    980                     this->bmpAppendGlyph(blob,
    981                                          runIndex,
    982                                          GrGlyph::Pack(glyph.getGlyphID(),
    983                                                        glyph.getSubXFixed(),
    984                                                        glyph.getSubYFixed(),
    985                                                        GrGlyph::kCoverage_MaskStyle),
    986                                          Sk48Dot16FloorToInt(fx),
    987                                          Sk48Dot16FloorToInt(fy),
    988                                          color,
    989                                          fontScaler,
    990                                          clipRect);
    991                 }
    992                 pos += scalarsPerPosition;
    993             }
    994         }
    995     } else {    // not subpixel
    996 
    997         if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
    998             while (text < stop) {
    999                 // the last 2 parameters are ignored
   1000                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
   1001 
   1002                 if (glyph.fWidth) {
   1003                     SkPoint tmsLoc;
   1004                     tmsProc(pos, &tmsLoc);
   1005 
   1006                     Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
   1007                     Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
   1008                     this->bmpAppendGlyph(blob,
   1009                                          runIndex,
   1010                                          GrGlyph::Pack(glyph.getGlyphID(),
   1011                                                        glyph.getSubXFixed(),
   1012                                                        glyph.getSubYFixed(),
   1013                                                        GrGlyph::kCoverage_MaskStyle),
   1014                                          Sk48Dot16FloorToInt(fx),
   1015                                          Sk48Dot16FloorToInt(fy),
   1016                                          color,
   1017                                          fontScaler,
   1018                                          clipRect);
   1019                 }
   1020                 pos += scalarsPerPosition;
   1021             }
   1022         } else {
   1023             while (text < stop) {
   1024                 // the last 2 parameters are ignored
   1025                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
   1026 
   1027                 if (glyph.fWidth) {
   1028                     SkPoint tmsLoc;
   1029                     tmsProc(pos, &tmsLoc);
   1030 
   1031                     SkPoint alignLoc;
   1032                     alignProc(tmsLoc, glyph, &alignLoc);
   1033 
   1034                     Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
   1035                     Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
   1036                     this->bmpAppendGlyph(blob,
   1037                                          runIndex,
   1038                                          GrGlyph::Pack(glyph.getGlyphID(),
   1039                                                        glyph.getSubXFixed(),
   1040                                                        glyph.getSubYFixed(),
   1041                                                        GrGlyph::kCoverage_MaskStyle),
   1042                                          Sk48Dot16FloorToInt(fx),
   1043                                          Sk48Dot16FloorToInt(fy),
   1044                                          color,
   1045                                          fontScaler,
   1046                                          clipRect);
   1047                 }
   1048                 pos += scalarsPerPosition;
   1049             }
   1050         }
   1051     }
   1052 }
   1053 
   1054 
   1055 void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
   1056                                             SkGlyphCache* cache, const SkPaint& skPaint,
   1057                                             GrColor color,
   1058                                             const SkMatrix& viewMatrix,
   1059                                             const char text[], size_t byteLength,
   1060                                             SkScalar x, SkScalar y, const SkIRect& clipRect,
   1061                                             SkScalar textRatio,
   1062                                             SkTDArray<char>* fallbackTxt,
   1063                                             SkTDArray<SkScalar>* fallbackPos,
   1064                                             SkPoint* offset,
   1065                                             const SkPaint& origPaint) {
   1066     SkASSERT(byteLength == 0 || text != NULL);
   1067 
   1068     // nothing to draw
   1069     if (text == NULL || byteLength == 0) {
   1070         return;
   1071     }
   1072 
   1073     SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
   1074     SkAutoDescriptor desc;
   1075     origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
   1076     SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
   1077                                                              desc.getDesc());
   1078 
   1079     SkTArray<SkScalar> positions;
   1080 
   1081     const char* textPtr = text;
   1082     SkFixed stopX = 0;
   1083     SkFixed stopY = 0;
   1084     SkFixed origin = 0;
   1085     switch (origPaint.getTextAlign()) {
   1086         case SkPaint::kRight_Align: origin = SK_Fixed1; break;
   1087         case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
   1088         case SkPaint::kLeft_Align: origin = 0; break;
   1089     }
   1090 
   1091     SkAutoKern autokern;
   1092     const char* stop = text + byteLength;
   1093     while (textPtr < stop) {
   1094         // don't need x, y here, since all subpixel variants will have the
   1095         // same advance
   1096         const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
   1097 
   1098         SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
   1099         positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
   1100 
   1101         SkFixed height = glyph.fAdvanceY;
   1102         positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
   1103 
   1104         stopX += width;
   1105         stopY += height;
   1106     }
   1107     SkASSERT(textPtr == stop);
   1108 
   1109     // now adjust starting point depending on alignment
   1110     SkScalar alignX = SkFixedToScalar(stopX);
   1111     SkScalar alignY = SkFixedToScalar(stopY);
   1112     if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
   1113         alignX = SkScalarHalf(alignX);
   1114         alignY = SkScalarHalf(alignY);
   1115     } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
   1116         alignX = 0;
   1117         alignY = 0;
   1118     }
   1119     x -= alignX;
   1120     y -= alignY;
   1121     *offset = SkPoint::Make(x, y);
   1122 
   1123     this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
   1124                                 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
   1125                                 fallbackPos);
   1126     SkGlyphCache::AttachCache(origPaintCache);
   1127 }
   1128 
   1129 void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
   1130                                                SkGlyphCache* cache, const SkPaint& skPaint,
   1131                                                GrColor color,
   1132                                                const SkMatrix& viewMatrix,
   1133                                                const char text[], size_t byteLength,
   1134                                                const SkScalar pos[], int scalarsPerPosition,
   1135                                                const SkPoint& offset, const SkIRect& clipRect,
   1136                                                SkScalar textRatio,
   1137                                                SkTDArray<char>* fallbackTxt,
   1138                                                SkTDArray<SkScalar>* fallbackPos) {
   1139 
   1140     SkASSERT(byteLength == 0 || text != NULL);
   1141     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
   1142 
   1143     // nothing to draw
   1144     if (text == NULL || byteLength == 0) {
   1145         return;
   1146     }
   1147 
   1148     fCurrStrike = NULL;
   1149 
   1150     SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
   1151     GrFontScaler* fontScaler = GetGrFontScaler(cache);
   1152 
   1153     const char* stop = text + byteLength;
   1154 
   1155     if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
   1156         while (text < stop) {
   1157             const char* lastText = text;
   1158             // the last 2 parameters are ignored
   1159             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
   1160 
   1161             if (glyph.fWidth) {
   1162                 SkScalar x = offset.x() + pos[0];
   1163                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
   1164 
   1165                 if (!this->dfAppendGlyph(blob,
   1166                                          runIndex,
   1167                                          GrGlyph::Pack(glyph.getGlyphID(),
   1168                                                        glyph.getSubXFixed(),
   1169                                                        glyph.getSubYFixed(),
   1170                                                        GrGlyph::kDistance_MaskStyle),
   1171                                          x, y, color, fontScaler, clipRect,
   1172                                          textRatio, viewMatrix)) {
   1173                     // couldn't append, send to fallback
   1174                     fallbackTxt->append(SkToInt(text-lastText), lastText);
   1175                     *fallbackPos->append() = pos[0];
   1176                     if (2 == scalarsPerPosition) {
   1177                         *fallbackPos->append() = pos[1];
   1178                     }
   1179                 }
   1180             }
   1181             pos += scalarsPerPosition;
   1182         }
   1183     } else {
   1184         SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
   1185                                                                              : SK_Scalar1;
   1186         while (text < stop) {
   1187             const char* lastText = text;
   1188             // the last 2 parameters are ignored
   1189             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
   1190 
   1191             if (glyph.fWidth) {
   1192                 SkScalar x = offset.x() + pos[0];
   1193                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
   1194 
   1195                 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
   1196                 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
   1197 
   1198                 if (!this->dfAppendGlyph(blob,
   1199                                          runIndex,
   1200                                          GrGlyph::Pack(glyph.getGlyphID(),
   1201                                                        glyph.getSubXFixed(),
   1202                                                        glyph.getSubYFixed(),
   1203                                                        GrGlyph::kDistance_MaskStyle),
   1204                                          x - advanceX, y - advanceY, color,
   1205                                          fontScaler,
   1206                                          clipRect,
   1207                                          textRatio,
   1208                                          viewMatrix)) {
   1209                     // couldn't append, send to fallback
   1210                     fallbackTxt->append(SkToInt(text-lastText), lastText);
   1211                     *fallbackPos->append() = pos[0];
   1212                     if (2 == scalarsPerPosition) {
   1213                         *fallbackPos->append() = pos[1];
   1214                     }
   1215                 }
   1216             }
   1217             pos += scalarsPerPosition;
   1218         }
   1219     }
   1220 }
   1221 
   1222 void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
   1223                                         GrGlyph::PackedID packed,
   1224                                         int vx, int vy, GrColor color, GrFontScaler* scaler,
   1225                                         const SkIRect& clipRect) {
   1226     Run& run = blob->fRuns[runIndex];
   1227     if (!fCurrStrike) {
   1228         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
   1229         run.fStrike.reset(SkRef(fCurrStrike));
   1230     }
   1231 
   1232     GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
   1233     if (!glyph) {
   1234         return;
   1235     }
   1236 
   1237     int x = vx + glyph->fBounds.fLeft;
   1238     int y = vy + glyph->fBounds.fTop;
   1239 
   1240     // keep them as ints until we've done the clip-test
   1241     int width = glyph->fBounds.width();
   1242     int height = glyph->fBounds.height();
   1243 
   1244 #if 0
   1245     // Not checking the clip bounds might introduce a performance regression.  However, its not
   1246     // clear if this is still true today with the larger tiles we use in Chrome.  For repositionable
   1247     // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
   1248     // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
   1249     // TODO verify this
   1250     // check if we clipped out
   1251     if (clipRect.quickReject(x, y, x + width, y + height)) {
   1252         return;
   1253     }
   1254 #endif
   1255 
   1256     // If the glyph is too large we fall back to paths
   1257     if (glyph->fTooLargeForAtlas) {
   1258         this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
   1259         return;
   1260     }
   1261 
   1262     GrMaskFormat format = glyph->fMaskFormat;
   1263 
   1264     PerSubRunInfo* subRun = &run.fSubRunInfo.back();
   1265     if (run.fInitialized && subRun->fMaskFormat != format) {
   1266         subRun = &run.fSubRunInfo.push_back();
   1267     }
   1268 
   1269     run.fInitialized = true;
   1270 
   1271     size_t vertexStride = get_vertex_stride(format);
   1272 
   1273     SkRect r;
   1274     r.fLeft = SkIntToScalar(x);
   1275     r.fTop = SkIntToScalar(y);
   1276     r.fRight = r.fLeft + SkIntToScalar(width);
   1277     r.fBottom = r.fTop + SkIntToScalar(height);
   1278     subRun->fMaskFormat = format;
   1279     this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
   1280                             glyph);
   1281 }
   1282 
   1283 bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
   1284                                        GrGlyph::PackedID packed,
   1285                                        SkScalar sx, SkScalar sy, GrColor color,
   1286                                        GrFontScaler* scaler,
   1287                                        const SkIRect& clipRect,
   1288                                        SkScalar textRatio, const SkMatrix& viewMatrix) {
   1289     Run& run = blob->fRuns[runIndex];
   1290     if (!fCurrStrike) {
   1291         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
   1292         run.fStrike.reset(SkRef(fCurrStrike));
   1293     }
   1294 
   1295     GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
   1296     if (!glyph) {
   1297         return true;
   1298     }
   1299 
   1300     // fallback to color glyph support
   1301     if (kA8_GrMaskFormat != glyph->fMaskFormat) {
   1302         return false;
   1303     }
   1304 
   1305     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
   1306     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
   1307     SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
   1308     SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
   1309 
   1310     SkScalar scale = textRatio;
   1311     dx *= scale;
   1312     dy *= scale;
   1313     width *= scale;
   1314     height *= scale;
   1315     sx += dx;
   1316     sy += dy;
   1317     SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
   1318 
   1319 #if 0
   1320     // check if we clipped out
   1321     SkRect dstRect;
   1322     viewMatrix.mapRect(&dstRect, glyphRect);
   1323     if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
   1324                              SkScalarTruncToInt(dstRect.top()),
   1325                              SkScalarTruncToInt(dstRect.right()),
   1326                              SkScalarTruncToInt(dstRect.bottom()))) {
   1327         return true;
   1328     }
   1329 #endif
   1330 
   1331     // TODO combine with the above
   1332     // If the glyph is too large we fall back to paths
   1333     if (glyph->fTooLargeForAtlas) {
   1334         this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
   1335         return true;
   1336     }
   1337 
   1338     PerSubRunInfo* subRun = &run.fSubRunInfo.back();
   1339     SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
   1340     subRun->fMaskFormat = kA8_GrMaskFormat;
   1341 
   1342     size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
   1343 
   1344     bool useColorVerts = !subRun->fUseLCDText;
   1345     this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
   1346                             glyph);
   1347     return true;
   1348 }
   1349 
   1350 inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
   1351                                                 GrFontScaler* scaler, SkScalar x, SkScalar y) {
   1352     if (NULL == glyph->fPath) {
   1353         SkPath* path = SkNEW(SkPath);
   1354         if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
   1355             // flag the glyph as being dead?
   1356             SkDELETE(path);
   1357             return;
   1358         }
   1359         glyph->fPath = path;
   1360     }
   1361     SkASSERT(glyph->fPath);
   1362     blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
   1363 }
   1364 
   1365 inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
   1366                                                   Run::SubRunInfo* subRun,
   1367                                                   const SkRect& positions, GrColor color,
   1368                                                   size_t vertexStride, bool useVertexColor,
   1369                                                   GrGlyph* glyph) {
   1370     blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
   1371     run->fVertexBounds.joinNonEmptyArg(positions);
   1372     run->fColor = color;
   1373 
   1374     intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
   1375 
   1376     if (useVertexColor) {
   1377         // V0
   1378         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
   1379         position->set(positions.fLeft, positions.fTop);
   1380         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
   1381         *colorPtr = color;
   1382         vertex += vertexStride;
   1383 
   1384         // V1
   1385         position = reinterpret_cast<SkPoint*>(vertex);
   1386         position->set(positions.fLeft, positions.fBottom);
   1387         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
   1388         *colorPtr = color;
   1389         vertex += vertexStride;
   1390 
   1391         // V2
   1392         position = reinterpret_cast<SkPoint*>(vertex);
   1393         position->set(positions.fRight, positions.fBottom);
   1394         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
   1395         *colorPtr = color;
   1396         vertex += vertexStride;
   1397 
   1398         // V3
   1399         position = reinterpret_cast<SkPoint*>(vertex);
   1400         position->set(positions.fRight, positions.fTop);
   1401         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
   1402         *colorPtr = color;
   1403     } else {
   1404         // V0
   1405         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
   1406         position->set(positions.fLeft, positions.fTop);
   1407         vertex += vertexStride;
   1408 
   1409         // V1
   1410         position = reinterpret_cast<SkPoint*>(vertex);
   1411         position->set(positions.fLeft, positions.fBottom);
   1412         vertex += vertexStride;
   1413 
   1414         // V2
   1415         position = reinterpret_cast<SkPoint*>(vertex);
   1416         position->set(positions.fRight, positions.fBottom);
   1417         vertex += vertexStride;
   1418 
   1419         // V3
   1420         position = reinterpret_cast<SkPoint*>(vertex);
   1421         position->set(positions.fRight, positions.fTop);
   1422     }
   1423 
   1424     subRun->fGlyphEndIndex++;
   1425     subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
   1426 }
   1427 
   1428 class BitmapTextBatch : public GrBatch {
   1429 public:
   1430     typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
   1431     typedef GrAtlasTextContext::BitmapTextBlob Blob;
   1432     typedef Blob::Run Run;
   1433     typedef Run::SubRunInfo TextInfo;
   1434     struct Geometry {
   1435         Blob* fBlob;
   1436         int fRun;
   1437         int fSubRun;
   1438         GrColor fColor;
   1439         SkScalar fTransX;
   1440         SkScalar fTransY;
   1441     };
   1442 
   1443     static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
   1444                                    GrBatchFontCache* fontCache) {
   1445         return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
   1446     }
   1447 
   1448     static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
   1449                                    GrBatchFontCache* fontCache,
   1450                                    DistanceAdjustTable* distanceAdjustTable,
   1451                                    SkColor filteredColor, bool useLCDText,
   1452                                    bool useBGR, float gamma) {
   1453         return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
   1454                                             filteredColor, useLCDText, useBGR, gamma));
   1455     }
   1456 
   1457     const char* name() const override { return "BitmapTextBatch"; }
   1458 
   1459     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
   1460         if (kARGB_GrMaskFormat == fMaskFormat) {
   1461             out->setUnknownFourComponents();
   1462         } else {
   1463             out->setKnownFourComponents(fBatch.fColor);
   1464         }
   1465     }
   1466 
   1467     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
   1468         if (!fUseDistanceFields) {
   1469             // Bitmap Text
   1470             if (kARGB_GrMaskFormat != fMaskFormat) {
   1471                 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
   1472                     out->setUnknownSingleComponent();
   1473                 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
   1474                     out->setUnknownOpaqueFourComponents();
   1475                     out->setUsingLCDCoverage();
   1476                 } else {
   1477                     out->setUnknownFourComponents();
   1478                     out->setUsingLCDCoverage();
   1479                 }
   1480             } else {
   1481                 out->setKnownSingleComponent(0xff);
   1482             }
   1483         } else {
   1484             // Distance fields
   1485             if (!fUseLCDText) {
   1486                 out->setUnknownSingleComponent();
   1487             } else {
   1488                 out->setUnknownFourComponents();
   1489                 out->setUsingLCDCoverage();
   1490             }
   1491         }
   1492     }
   1493 
   1494     void initBatchTracker(const GrPipelineInfo& init) override {
   1495         // Handle any color overrides
   1496         if (init.fColorIgnored) {
   1497             fBatch.fColor = GrColor_ILLEGAL;
   1498         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
   1499             fBatch.fColor = init.fOverrideColor;
   1500         }
   1501 
   1502         // setup batch properties
   1503         fBatch.fColorIgnored = init.fColorIgnored;
   1504         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
   1505         fBatch.fCoverageIgnored = init.fCoverageIgnored;
   1506     }
   1507 
   1508     struct FlushInfo {
   1509         SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
   1510         SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
   1511         int fGlyphsToFlush;
   1512         int fVertexOffset;
   1513     };
   1514 
   1515     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
   1516         // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
   1517         // TODO actually only invert if we don't have RGBA
   1518         SkMatrix localMatrix;
   1519         if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
   1520             SkDebugf("Cannot invert viewmatrix\n");
   1521             return;
   1522         }
   1523 
   1524         GrTexture* texture = fFontCache->getTexture(fMaskFormat);
   1525         if (!texture) {
   1526             SkDebugf("Could not allocate backing texture for atlas\n");
   1527             return;
   1528         }
   1529 
   1530         SkAutoTUnref<const GrGeometryProcessor> gp;
   1531         if (fUseDistanceFields) {
   1532             gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
   1533                                             texture));
   1534         } else {
   1535             GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
   1536             gp.reset(GrBitmapTextGeoProc::Create(this->color(),
   1537                                                  texture,
   1538                                                  params,
   1539                                                  fMaskFormat,
   1540                                                  localMatrix));
   1541         }
   1542 
   1543         FlushInfo flushInfo;
   1544         flushInfo.fGlyphsToFlush = 0;
   1545         size_t vertexStride = gp->getVertexStride();
   1546         SkASSERT(vertexStride == (fUseDistanceFields ?
   1547                                   get_vertex_stride_df(fMaskFormat, fUseLCDText) :
   1548                                   get_vertex_stride(fMaskFormat)));
   1549 
   1550         this->initDraw(batchTarget, gp, pipeline);
   1551 
   1552         int glyphCount = this->numGlyphs();
   1553         int instanceCount = fInstanceCount;
   1554         const GrVertexBuffer* vertexBuffer;
   1555 
   1556         void* vertices = batchTarget->makeVertSpace(vertexStride,
   1557                                                     glyphCount * kVerticesPerGlyph,
   1558                                                     &vertexBuffer,
   1559                                                     &flushInfo.fVertexOffset);
   1560         flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
   1561         flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
   1562         if (!vertices || !flushInfo.fVertexBuffer) {
   1563             SkDebugf("Could not allocate vertices\n");
   1564             return;
   1565         }
   1566 
   1567         unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
   1568 
   1569         // We cache some values to avoid going to the glyphcache for the same fontScaler twice
   1570         // in a row
   1571         const SkDescriptor* desc = NULL;
   1572         SkGlyphCache* cache = NULL;
   1573         GrFontScaler* scaler = NULL;
   1574         SkTypeface* typeface = NULL;
   1575 
   1576         for (int i = 0; i < instanceCount; i++) {
   1577             Geometry& args = fGeoData[i];
   1578             Blob* blob = args.fBlob;
   1579             Run& run = blob->fRuns[args.fRun];
   1580             TextInfo& info = run.fSubRunInfo[args.fSubRun];
   1581 
   1582             uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
   1583             bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
   1584             bool regenerateColors;
   1585             if (fUseDistanceFields) {
   1586                 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
   1587             } else {
   1588                 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
   1589             }
   1590             bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
   1591             int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
   1592 
   1593             // We regenerate both texture coords and colors in the blob itself, and update the
   1594             // atlas generation.  If we don't end up purging any unused plots, we can avoid
   1595             // regenerating the coords.  We could take a finer grained approach to updating texture
   1596             // coords but its not clear if the extra bookkeeping would offset any gains.
   1597             // To avoid looping over the glyphs twice, we do one loop and conditionally update color
   1598             // or coords as needed.  One final note, if we have to break a run for an atlas eviction
   1599             // then we can't really trust the atlas has all of the correct data.  Atlas evictions
   1600             // should be pretty rare, so we just always regenerate in those cases
   1601             if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
   1602                 // first regenerate texture coordinates / colors if need be
   1603                 bool brokenRun = false;
   1604 
   1605                 // Because the GrBatchFontCache may evict the strike a blob depends on using for
   1606                 // generating its texture coords, we have to track whether or not the strike has
   1607                 // been abandoned.  If it hasn't been abandoned, then we can use the GrGlyph*s as is
   1608                 // otherwise we have to get the new strike, and use that to get the correct glyphs.
   1609                 // Because we do not have the packed ids, and thus can't look up our glyphs in the
   1610                 // new strike, we instead keep our ref to the old strike and use the packed ids from
   1611                 // it.  These ids will still be valid as long as we hold the ref.  When we are done
   1612                 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
   1613                 bool regenerateGlyphs = false;
   1614                 GrBatchTextStrike* strike = NULL;
   1615                 if (regenerateTextureCoords) {
   1616                     info.fBulkUseToken.reset();
   1617 
   1618                     // We can reuse if we have a valid strike and our descriptors / typeface are the
   1619                     // same
   1620                     const SkDescriptor* newDesc = run.fOverrideDescriptor ?
   1621                                                   run.fOverrideDescriptor->getDesc() :
   1622                                                   run.fDescriptor.getDesc();
   1623                     if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
   1624                                   !(desc->equals(*newDesc))) {
   1625                         if (cache) {
   1626                             SkGlyphCache::AttachCache(cache);
   1627                         }
   1628                         desc = newDesc;
   1629                         cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
   1630                         scaler = GrTextContext::GetGrFontScaler(cache);
   1631                         strike = run.fStrike;
   1632                         typeface = run.fTypeface;
   1633                     }
   1634 
   1635                     if (run.fStrike->isAbandoned()) {
   1636                         regenerateGlyphs = true;
   1637                         strike = fFontCache->getStrike(scaler);
   1638                     } else {
   1639                         strike = run.fStrike;
   1640                     }
   1641                 }
   1642 
   1643                 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
   1644                     if (regenerateTextureCoords) {
   1645                         size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
   1646                         GrGlyph* glyph;
   1647                         if (regenerateGlyphs) {
   1648                             // Get the id from the old glyph, and use the new strike to lookup
   1649                             // the glyph.
   1650                             glyph = blob->fGlyphs[glyphOffset];
   1651                             blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
   1652                                                                           scaler);
   1653                         }
   1654                         glyph = blob->fGlyphs[glyphOffset];
   1655                         SkASSERT(glyph);
   1656 
   1657                         if (!fFontCache->hasGlyph(glyph) &&
   1658                             !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
   1659                             this->flush(batchTarget, &flushInfo);
   1660                             this->initDraw(batchTarget, gp, pipeline);
   1661                             brokenRun = glyphIdx > 0;
   1662 
   1663                             SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
   1664                                                                                 glyph,
   1665                                                                                 scaler);
   1666                             SkASSERT(success);
   1667                         }
   1668                         fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
   1669                                                                  batchTarget->currentToken());
   1670 
   1671                         // Texture coords are the last vertex attribute so we get a pointer to the
   1672                         // first one and then map with stride in regenerateTextureCoords
   1673                         intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
   1674                         vertex += info.fVertexStartIndex;
   1675                         vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
   1676                         vertex += vertexStride - sizeof(SkIPoint16);
   1677 
   1678                         this->regenerateTextureCoords(glyph, vertex, vertexStride);
   1679                     }
   1680 
   1681                     if (regenerateColors) {
   1682                         intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
   1683                         vertex += info.fVertexStartIndex;
   1684                         vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
   1685                         this->regenerateColors(vertex, vertexStride, args.fColor);
   1686                     }
   1687 
   1688                     if (regeneratePositions) {
   1689                         intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
   1690                         vertex += info.fVertexStartIndex;
   1691                         vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
   1692                         SkScalar transX = args.fTransX;
   1693                         SkScalar transY = args.fTransY;
   1694                         this->regeneratePositions(vertex, vertexStride, transX, transY);
   1695                     }
   1696                     flushInfo.fGlyphsToFlush++;
   1697                 }
   1698 
   1699                 // We my have changed the color so update it here
   1700                 run.fColor = args.fColor;
   1701                 if (regenerateTextureCoords) {
   1702                     if (regenerateGlyphs) {
   1703                         run.fStrike.reset(SkRef(strike));
   1704                     }
   1705                     info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
   1706                                                         fFontCache->atlasGeneration(fMaskFormat);
   1707                 }
   1708             } else {
   1709                 flushInfo.fGlyphsToFlush += glyphCount;
   1710 
   1711                 // set use tokens for all of the glyphs in our subrun.  This is only valid if we
   1712                 // have a valid atlas generation
   1713                 fFontCache->setUseTokenBulk(info.fBulkUseToken,
   1714                                             batchTarget->currentToken(),
   1715                                             fMaskFormat);
   1716             }
   1717 
   1718             // now copy all vertices
   1719             size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
   1720             memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
   1721 
   1722             currVertex += byteCount;
   1723         }
   1724         // Make sure to attach the last cache if applicable
   1725         if (cache) {
   1726             SkGlyphCache::AttachCache(cache);
   1727         }
   1728         this->flush(batchTarget, &flushInfo);
   1729     }
   1730 
   1731     // The minimum number of Geometry we will try to allocate.
   1732     static const int kMinAllocated = 32;
   1733 
   1734     // Total number of Geometry this Batch owns
   1735     int instanceCount() const { return fInstanceCount; }
   1736     SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
   1737 
   1738     // to avoid even the initial copy of the struct, we have a getter for the first item which
   1739     // is used to seed the batch with its initial geometry.  After seeding, the client should call
   1740     // init() so the Batch can initialize itself
   1741     Geometry& geometry() { return fGeoData[0]; }
   1742     void init() {
   1743         const Geometry& geo = fGeoData[0];
   1744         fBatch.fColor = geo.fColor;
   1745         fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
   1746 
   1747         // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
   1748         // into device space
   1749         const Run& run = geo.fBlob->fRuns[geo.fRun];
   1750         if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
   1751             SkRect bounds = run.fVertexBounds;
   1752             fBatch.fViewMatrix.mapRect(&bounds);
   1753             this->setBounds(bounds);
   1754         } else {
   1755             this->setBounds(run.fVertexBounds);
   1756         }
   1757     }
   1758 
   1759 private:
   1760     BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
   1761             : fMaskFormat(maskFormat)
   1762             , fPixelConfig(fontCache->getPixelConfig(maskFormat))
   1763             , fFontCache(fontCache)
   1764             , fUseDistanceFields(false) {
   1765         this->initClassID<BitmapTextBatch>();
   1766         fBatch.fNumGlyphs = glyphCount;
   1767         fInstanceCount = 1;
   1768         fAllocatedCount = kMinAllocated;
   1769     }
   1770 
   1771     BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
   1772                     DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
   1773                     bool useLCDText, bool useBGR, float gamma)
   1774             : fMaskFormat(maskFormat)
   1775             , fPixelConfig(fontCache->getPixelConfig(maskFormat))
   1776             , fFontCache(fontCache)
   1777             , fDistanceAdjustTable(SkRef(distanceAdjustTable))
   1778             , fFilteredColor(filteredColor)
   1779             , fUseDistanceFields(true)
   1780             , fUseLCDText(useLCDText)
   1781             , fUseBGR(useBGR)
   1782             , fGamma(gamma) {
   1783         this->initClassID<BitmapTextBatch>();
   1784         fBatch.fNumGlyphs = glyphCount;
   1785         fInstanceCount = 1;
   1786         fAllocatedCount = kMinAllocated;
   1787         SkASSERT(fMaskFormat == kA8_GrMaskFormat);
   1788     }
   1789 
   1790     ~BitmapTextBatch() {
   1791         for (int i = 0; i < fInstanceCount; i++) {
   1792             fGeoData[i].fBlob->unref();
   1793         }
   1794     }
   1795 
   1796     void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
   1797         int width = glyph->fBounds.width();
   1798         int height = glyph->fBounds.height();
   1799 
   1800         int u0, v0, u1, v1;
   1801         if (fUseDistanceFields) {
   1802             u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
   1803             v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
   1804             u1 = u0 + width - 2 * SK_DistanceFieldInset;
   1805             v1 = v0 + height - 2 * SK_DistanceFieldInset;
   1806         } else {
   1807             u0 = glyph->fAtlasLocation.fX;
   1808             v0 = glyph->fAtlasLocation.fY;
   1809             u1 = u0 + width;
   1810             v1 = v0 + height;
   1811         }
   1812 
   1813         SkIPoint16* textureCoords;
   1814         // V0
   1815         textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
   1816         textureCoords->set(u0, v0);
   1817         vertex += vertexStride;
   1818 
   1819         // V1
   1820         textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
   1821         textureCoords->set(u0, v1);
   1822         vertex += vertexStride;
   1823 
   1824         // V2
   1825         textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
   1826         textureCoords->set(u1, v1);
   1827         vertex += vertexStride;
   1828 
   1829         // V3
   1830         textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
   1831         textureCoords->set(u1, v0);
   1832     }
   1833 
   1834     void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
   1835         for (int i = 0; i < kVerticesPerGlyph; i++) {
   1836             SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
   1837             *vcolor = color;
   1838             vertex += vertexStride;
   1839         }
   1840     }
   1841 
   1842     void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
   1843                              SkScalar transY) {
   1844         for (int i = 0; i < kVerticesPerGlyph; i++) {
   1845             SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
   1846             point->fX += transX;
   1847             point->fY += transY;
   1848             vertex += vertexStride;
   1849         }
   1850     }
   1851 
   1852     void initDraw(GrBatchTarget* batchTarget,
   1853                   const GrGeometryProcessor* gp,
   1854                   const GrPipeline* pipeline) {
   1855         batchTarget->initDraw(gp, pipeline);
   1856 
   1857         // TODO remove this when batch is everywhere
   1858         GrPipelineInfo init;
   1859         init.fColorIgnored = fBatch.fColorIgnored;
   1860         init.fOverrideColor = GrColor_ILLEGAL;
   1861         init.fCoverageIgnored = fBatch.fCoverageIgnored;
   1862         init.fUsesLocalCoords = this->usesLocalCoords();
   1863         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
   1864     }
   1865 
   1866     void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
   1867         GrVertices vertices;
   1868         int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
   1869         vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
   1870                                flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
   1871                                kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
   1872                                maxGlyphsPerDraw);
   1873         batchTarget->draw(vertices);
   1874         flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
   1875         flushInfo->fGlyphsToFlush = 0;
   1876     }
   1877 
   1878     GrColor color() const { return fBatch.fColor; }
   1879     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
   1880     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
   1881     int numGlyphs() const { return fBatch.fNumGlyphs; }
   1882 
   1883     bool onCombineIfPossible(GrBatch* t) override {
   1884         BitmapTextBatch* that = t->cast<BitmapTextBatch>();
   1885 
   1886         if (fUseDistanceFields != that->fUseDistanceFields) {
   1887             return false;
   1888         }
   1889 
   1890         if (!fUseDistanceFields) {
   1891             // Bitmap Text
   1892             if (fMaskFormat != that->fMaskFormat) {
   1893                 return false;
   1894             }
   1895 
   1896             // TODO we can often batch across LCD text if we have dual source blending and don't
   1897             // have to use the blend constant
   1898             if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
   1899                 return false;
   1900             }
   1901 
   1902             if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
   1903                 return false;
   1904             }
   1905         } else {
   1906             // Distance Fields
   1907             SkASSERT(this->fMaskFormat == that->fMaskFormat &&
   1908                      this->fMaskFormat == kA8_GrMaskFormat);
   1909 
   1910             if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
   1911                 return false;
   1912             }
   1913 
   1914             if (fFilteredColor != that->fFilteredColor) {
   1915                 return false;
   1916             }
   1917 
   1918             if (fUseLCDText != that->fUseLCDText) {
   1919                 return false;
   1920             }
   1921 
   1922             if (fUseBGR != that->fUseBGR) {
   1923                 return false;
   1924             }
   1925 
   1926             if (fGamma != that->fGamma) {
   1927                 return false;
   1928             }
   1929 
   1930             // TODO see note above
   1931             if (fUseLCDText && this->color() != that->color()) {
   1932                 return false;
   1933             }
   1934         }
   1935 
   1936         fBatch.fNumGlyphs += that->numGlyphs();
   1937 
   1938         // copy that->geoData().  We do this manually for performance reasons
   1939         SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
   1940         int otherInstanceCount = that->instanceCount();
   1941         int allocSize = otherInstanceCount + fInstanceCount;
   1942         if (allocSize > fAllocatedCount) {
   1943             while (allocSize > fAllocatedCount) {
   1944                 fAllocatedCount = fAllocatedCount << 1;
   1945             }
   1946             fGeoData.realloc(fAllocatedCount);
   1947         }
   1948 
   1949         memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
   1950                otherInstanceCount * sizeof(Geometry));
   1951         int total = fInstanceCount + otherInstanceCount;
   1952         for (int i = fInstanceCount; i < total; i++) {
   1953             fGeoData[i].fBlob->ref();
   1954         }
   1955         fInstanceCount = total;
   1956 
   1957         this->joinBounds(that->bounds());
   1958         return true;
   1959     }
   1960 
   1961     // TODO just use class params
   1962     // TODO trying to figure out why lcd is so whack
   1963     GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
   1964                                           GrColor color, GrTexture* texture) {
   1965         GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
   1966 
   1967         // set up any flags
   1968         uint32_t flags = 0;
   1969         flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
   1970         flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
   1971         flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
   1972                                 kRectToRect_DistanceFieldEffectFlag : 0;
   1973         flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
   1974 
   1975         // see if we need to create a new effect
   1976         if (fUseLCDText) {
   1977             GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
   1978 
   1979             float redCorrection =
   1980                 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
   1981             float greenCorrection =
   1982                 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
   1983             float blueCorrection =
   1984                 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
   1985             GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
   1986                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
   1987                                                                     greenCorrection,
   1988                                                                     blueCorrection);
   1989 
   1990             return GrDistanceFieldLCDTextGeoProc::Create(color,
   1991                                                          viewMatrix,
   1992                                                          texture,
   1993                                                          params,
   1994                                                          widthAdjust,
   1995                                                          flags);
   1996         } else {
   1997             flags |= kColorAttr_DistanceFieldEffectFlag;
   1998 #ifdef SK_GAMMA_APPLY_TO_A8
   1999             U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
   2000             float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
   2001             return GrDistanceFieldA8TextGeoProc::Create(color,
   2002                                                         viewMatrix,
   2003                                                         texture,
   2004                                                         params,
   2005                                                         correction,
   2006                                                         flags);
   2007 #else
   2008             return GrDistanceFieldA8TextGeoProc::Create(color,
   2009                                                         viewMatrix,
   2010                                                         texture,
   2011                                                         params,
   2012                                                         flags);
   2013 #endif
   2014         }
   2015 
   2016     }
   2017 
   2018     struct BatchTracker {
   2019         GrColor fColor;
   2020         SkMatrix fViewMatrix;
   2021         bool fUsesLocalCoords;
   2022         bool fColorIgnored;
   2023         bool fCoverageIgnored;
   2024         int fNumGlyphs;
   2025     };
   2026 
   2027     BatchTracker fBatch;
   2028     SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
   2029     int fInstanceCount;
   2030     int fAllocatedCount;
   2031     GrMaskFormat fMaskFormat;
   2032     GrPixelConfig fPixelConfig;
   2033     GrBatchFontCache* fFontCache;
   2034 
   2035     // Distance field properties
   2036     SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
   2037     SkColor fFilteredColor;
   2038     bool fUseDistanceFields;
   2039     bool fUseLCDText;
   2040     bool fUseBGR;
   2041     float fGamma;
   2042 };
   2043 
   2044 void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
   2045                                          SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
   2046                                          const SkIRect& clipBounds, SkScalar x, SkScalar y) {
   2047     SkPaint runPaint = skPaint;
   2048 
   2049     size_t textLen = it.glyphCount() * sizeof(uint16_t);
   2050     const SkPoint& offset = it.offset();
   2051 
   2052     it.applyFontToPaint(&runPaint);
   2053 
   2054     if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
   2055         return;
   2056     }
   2057 
   2058     runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
   2059 
   2060     switch (it.positioning()) {
   2061         case SkTextBlob::kDefault_Positioning:
   2062             this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
   2063                                  textLen, x + offset.x(), y + offset.y(), clipBounds);
   2064             break;
   2065         case SkTextBlob::kHorizontal_Positioning:
   2066             this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
   2067                                     textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
   2068                                     clipBounds);
   2069             break;
   2070         case SkTextBlob::kFull_Positioning:
   2071             this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
   2072                                     textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
   2073             break;
   2074     }
   2075 }
   2076 
   2077 
   2078 inline BitmapTextBatch*
   2079 GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
   2080                                 int glyphCount, int run, int subRun,
   2081                                 GrColor color, SkScalar transX, SkScalar transY,
   2082                                 const SkPaint& skPaint) {
   2083     GrMaskFormat format = info.fMaskFormat;
   2084     GrColor subRunColor;
   2085     if (kARGB_GrMaskFormat == format) {
   2086         uint8_t paintAlpha = skPaint.getAlpha();
   2087         subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
   2088     } else {
   2089         subRunColor = color;
   2090     }
   2091 
   2092     BitmapTextBatch* batch;
   2093     if (info.fDrawAsDistanceFields) {
   2094         SkColor filteredColor;
   2095         SkColorFilter* colorFilter = skPaint.getColorFilter();
   2096         if (colorFilter) {
   2097             filteredColor = colorFilter->filterColor(skPaint.getColor());
   2098         } else {
   2099             filteredColor = skPaint.getColor();
   2100         }
   2101         bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
   2102         float gamma = fDeviceProperties.gamma();
   2103         batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
   2104                                         fDistanceAdjustTable, filteredColor,
   2105                                         info.fUseLCDText, useBGR,
   2106                                         gamma);
   2107     } else {
   2108         batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
   2109     }
   2110     BitmapTextBatch::Geometry& geometry = batch->geometry();
   2111     geometry.fBlob = SkRef(cacheBlob);
   2112     geometry.fRun = run;
   2113     geometry.fSubRun = subRun;
   2114     geometry.fColor = subRunColor;
   2115     geometry.fTransX = transX;
   2116     geometry.fTransY = transY;
   2117     batch->init();
   2118 
   2119     return batch;
   2120 }
   2121 
   2122 inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
   2123                                          BitmapTextBlob* cacheBlob, int run, GrColor color,
   2124                                          SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
   2125     for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
   2126         const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
   2127         int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
   2128         if (0 == glyphCount) {
   2129             continue;
   2130         }
   2131 
   2132         SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
   2133                                                               subRun, color, transX, transY,
   2134                                                               skPaint));
   2135         target->drawBatch(pipelineBuilder, batch);
   2136     }
   2137 }
   2138 
   2139 inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
   2140                                                const SkPaint& skPaint,
   2141                                                SkScalar transX, SkScalar transY,
   2142                                                const SkIRect& clipBounds) {
   2143     if (!cacheBlob->fBigGlyphs.count()) {
   2144         return;
   2145     }
   2146 
   2147     SkMatrix pathMatrix;
   2148     if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
   2149         SkDebugf("could not invert viewmatrix\n");
   2150         return;
   2151     }
   2152 
   2153     for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
   2154         BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
   2155         bigGlyph.fVx += transX;
   2156         bigGlyph.fVy += transY;
   2157         SkMatrix translate = cacheBlob->fViewMatrix;
   2158         translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
   2159 
   2160         fGpuDevice->internalDrawPath(bigGlyph.fPath, skPaint, translate, &pathMatrix, clipBounds,
   2161                                      false);
   2162     }
   2163 }
   2164 
   2165 void GrAtlasTextContext::flush(GrDrawTarget* target,
   2166                                const SkTextBlob* blob,
   2167                                BitmapTextBlob* cacheBlob,
   2168                                GrRenderTarget* rt,
   2169                                const SkPaint& skPaint,
   2170                                const GrPaint& grPaint,
   2171                                SkDrawFilter* drawFilter,
   2172                                const GrClip& clip,
   2173                                const SkMatrix& viewMatrix,
   2174                                const SkIRect& clipBounds,
   2175                                SkScalar x, SkScalar y,
   2176                                SkScalar transX, SkScalar transY) {
   2177     // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
   2178     // it as paths
   2179     GrPipelineBuilder pipelineBuilder;
   2180     pipelineBuilder.setFromPaint(grPaint, rt, clip);
   2181 
   2182     GrColor color = grPaint.getColor();
   2183 
   2184     SkTextBlob::RunIterator it(blob);
   2185     for (int run = 0; !it.done(); it.next(), run++) {
   2186         if (cacheBlob->fRuns[run].fDrawAsPaths) {
   2187             this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
   2188             continue;
   2189         }
   2190         cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
   2191         this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
   2192     }
   2193 
   2194     // Now flush big glyphs
   2195     this->flushBigGlyphs(cacheBlob, rt, skPaint, transX, transY, clipBounds);
   2196 }
   2197 
   2198 void GrAtlasTextContext::flush(GrDrawTarget* target,
   2199                                BitmapTextBlob* cacheBlob,
   2200                                GrRenderTarget* rt,
   2201                                const SkPaint& skPaint,
   2202                                const GrPaint& grPaint,
   2203                                const GrClip& clip,
   2204                                const SkIRect& clipBounds) {
   2205     GrPipelineBuilder pipelineBuilder;
   2206     pipelineBuilder.setFromPaint(grPaint, rt, clip);
   2207 
   2208     GrColor color = grPaint.getColor();
   2209     for (int run = 0; run < cacheBlob->fRunCount; run++) {
   2210         this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
   2211     }
   2212 
   2213     // Now flush big glyphs
   2214     this->flushBigGlyphs(cacheBlob, rt, skPaint, 0, 0, clipBounds);
   2215 }
   2216 
   2217 ///////////////////////////////////////////////////////////////////////////////////////////////////
   2218 
   2219 #ifdef GR_TEST_UTILS
   2220 
   2221 BATCH_TEST_DEFINE(TextBlobBatch) {
   2222     static uint32_t gContextID = SK_InvalidGenID;
   2223     static GrAtlasTextContext* gTextContext = NULL;
   2224     static SkDeviceProperties gDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType);
   2225 
   2226     if (context->uniqueID() != gContextID) {
   2227         gContextID = context->uniqueID();
   2228         SkDELETE(gTextContext);
   2229         // We don't yet test the fall back to paths in the GrTextContext base class.  This is mostly
   2230         // because we don't really want to have a gpu device here.
   2231         // We enable distance fields by twiddling a knob on the paint
   2232         gTextContext = GrAtlasTextContext::Create(context, NULL, gDeviceProperties, false);
   2233     }
   2234 
   2235     // create dummy render target
   2236     GrSurfaceDesc desc;
   2237     desc.fFlags = kRenderTarget_GrSurfaceFlag;
   2238     desc.fWidth = 1024;
   2239     desc.fHeight = 1024;
   2240     desc.fConfig = kRGBA_8888_GrPixelConfig;
   2241     desc.fSampleCnt = 0;
   2242     SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
   2243     SkASSERT(texture);
   2244     SkASSERT(NULL != texture->asRenderTarget());
   2245     GrRenderTarget* rt = texture->asRenderTarget();
   2246 
   2247     // Setup dummy SkPaint / GrPaint
   2248     GrColor color = GrRandomColor(random);
   2249     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
   2250     SkPaint skPaint;
   2251     skPaint.setDistanceFieldTextTEMP(random->nextBool());
   2252     skPaint.setColor(color);
   2253     skPaint.setLCDRenderText(random->nextBool());
   2254     skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
   2255     skPaint.setSubpixelText(random->nextBool());
   2256 
   2257     GrPaint grPaint;
   2258     if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
   2259         SkFAIL("couldn't convert paint\n");
   2260     }
   2261 
   2262     const char* text = "The quick brown fox jumps over the lazy dog.";
   2263     int textLen = (int)strlen(text);
   2264 
   2265     // Setup clip
   2266     GrClip clip;
   2267     SkIRect noClip = SkIRect::MakeLargest();
   2268 
   2269     // right now we don't handle textblobs, nor do we handle drawPosText.  Since we only
   2270     // intend to test the batch with this unit test, that is okay.
   2271     SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
   2272             gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
   2273                                              static_cast<size_t>(textLen), 0, 0, noClip));
   2274 
   2275     SkScalar transX = static_cast<SkScalar>(random->nextU());
   2276     SkScalar transY = static_cast<SkScalar>(random->nextU());
   2277     const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
   2278     return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
   2279 }
   2280 
   2281 #endif
   2282