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
42namespace {
43static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
44
45// position + local coord
46static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
47
48static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
49
50static const int kMinDFFontSize = 18;
51static const int kSmallDFFontSize = 32;
52static const int kSmallDFFontLimit = 32;
53static const int kMediumDFFontSize = 72;
54static const int kMediumDFFontLimit = 72;
55static const int kLargeDFFontSize = 162;
56static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
57
58SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
59static const int kDistanceAdjustLumShift = 5;
60
61static const int kVerticesPerGlyph = 4;
62static const int kIndicesPerGlyph = 6;
63
64static 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
75static 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
84static 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
96GrAtlasTextContext::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
116void 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
198GrAtlasTextContext* 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
205bool 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
214GrColor 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.
238bool 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
248bool 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
339inline 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
348void 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
435inline 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
470void 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
594inline 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
648inline 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
675inline GrAtlasTextContext::BitmapTextBlob*
676GrAtlasTextContext::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
693inline GrAtlasTextContext::BitmapTextBlob*
694GrAtlasTextContext::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
733inline GrAtlasTextContext::BitmapTextBlob*
734GrAtlasTextContext::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
773void 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
784void 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
799void 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
890void 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
1055void 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
1129void 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
1222void 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
1283bool 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
1350inline 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
1365inline 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
1428class BitmapTextBatch : public GrBatch {
1429public:
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
1759private:
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
2044void 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
2078inline BitmapTextBatch*
2079GrAtlasTextContext::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
2122inline 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
2139inline 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
2165void 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
2198void 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
2221BATCH_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