1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "GrAtlasTextContext.h"
8 
9 #include "GrBatch.h"
10 #include "GrBatchFontCache.h"
11 #include "GrBatchTarget.h"
12 #include "GrBatchTest.h"
13 #include "GrDefaultGeoProcFactory.h"
14 #include "GrDrawTarget.h"
15 #include "GrFontScaler.h"
16 #include "GrIndexBuffer.h"
17 #include "GrResourceProvider.h"
18 #include "GrStrokeInfo.h"
19 #include "GrTextBlobCache.h"
20 #include "GrTexturePriv.h"
21 #include "GrVertexBuffer.h"
22 
23 #include "SkAutoKern.h"
24 #include "SkColorPriv.h"
25 #include "SkColorFilter.h"
26 #include "SkDistanceFieldGen.h"
27 #include "SkDraw.h"
28 #include "SkDrawFilter.h"
29 #include "SkDrawProcs.h"
30 #include "SkGlyphCache.h"
31 #include "SkGpuDevice.h"
32 #include "SkGr.h"
33 #include "SkPath.h"
34 #include "SkRTConf.h"
35 #include "SkStrokeRec.h"
36 #include "SkTextBlob.h"
37 #include "SkTextMapStateProc.h"
38 
39 #include "effects/GrBitmapTextGeoProc.h"
40 #include "effects/GrDistanceFieldGeoProc.h"
41 
42 namespace {
43 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
44 
45 // position + local coord
46 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
47 
48 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
49 
50 static const int kMinDFFontSize = 18;
51 static const int kSmallDFFontSize = 32;
52 static const int kSmallDFFontLimit = 32;
53 static const int kMediumDFFontSize = 72;
54 static const int kMediumDFFontLimit = 72;
55 static const int kLargeDFFontSize = 162;
56 static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
57 
58 SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
59 static const int kDistanceAdjustLumShift = 5;
60 
61 static const int kVerticesPerGlyph = 4;
62 static const int kIndicesPerGlyph = 6;
63 
get_vertex_stride(GrMaskFormat maskFormat)64 static size_t get_vertex_stride(GrMaskFormat maskFormat) {
65     switch (maskFormat) {
66         case kA8_GrMaskFormat:
67             return kGrayTextVASize;
68         case kARGB_GrMaskFormat:
69             return kColorTextVASize;
70         default:
71             return kLCDTextVASize;
72     }
73 }
74 
get_vertex_stride_df(GrMaskFormat maskFormat,bool useLCDText)75 static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
76     SkASSERT(maskFormat == kA8_GrMaskFormat);
77     if (useLCDText) {
78         return kLCDTextVASize;
79     } else {
80         return kGrayTextVASize;
81     }
82 }
83 
skcolor_to_grcolor_nopremultiply(SkColor c)84 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
85     unsigned r = SkColorGetR(c);
86     unsigned g = SkColorGetG(c);
87     unsigned b = SkColorGetB(c);
88     return GrColorPackRGBA(r, g, b, 0xff);
89 }
90 
91 };
92 
93 // TODO
94 // Distance field text in textblobs
95 
GrAtlasTextContext(GrContext * context,SkGpuDevice * gpuDevice,const SkDeviceProperties & properties,bool enableDistanceFields)96 GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
97                                        SkGpuDevice* gpuDevice,
98                                        const SkDeviceProperties& properties,
99                                        bool enableDistanceFields)
100     : INHERITED(context, gpuDevice, properties)
101     , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
102     // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
103     // vertexStride
104     SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
105                       vertex_attribute_changed);
106     fCurrStrike = NULL;
107     fCache = context->getTextBlobCache();
108 
109 #if SK_FORCE_DISTANCE_FIELD_TEXT
110     fEnableDFRendering = true;
111 #else
112     fEnableDFRendering = enableDistanceFields;
113 #endif
114 }
115 
buildDistanceAdjustTable(float gamma)116 void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
117 
118     // This is used for an approximation of the mask gamma hack, used by raster and bitmap
119     // text. The mask gamma hack is based off of guessing what the blend color is going to
120     // be, and adjusting the mask so that when run through the linear blend will
121     // produce the value closest to the desired result. However, in practice this means
122     // that the 'adjusted' mask is just increasing or decreasing the coverage of
123     // the mask depending on what it is thought it will blit against. For black (on
124     // assumed white) this means that coverages are decreased (on a curve). For white (on
125     // assumed black) this means that coverages are increased (on a a curve). At
126     // middle (perceptual) gray (which could be blit against anything) the coverages
127     // remain the same.
128     //
129     // The idea here is that instead of determining the initial (real) coverage and
130     // then adjusting that coverage, we determine an adjusted coverage directly by
131     // essentially manipulating the geometry (in this case, the distance to the glyph
132     // edge). So for black (on assumed white) this thins a bit; for white (on
133     // assumed black) this fake bolds the geometry a bit.
134     //
135     // The distance adjustment is calculated by determining the actual coverage value which
136     // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
137     // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
138     // actual edge. So by subtracting this distance adjustment and computing without the
139     // the coverage adjustment we should get 0.5 coverage at the same point.
140     //
141     // This has several implications:
142     //     For non-gray lcd smoothed text, each subpixel essentially is using a
143     //     slightly different geometry.
144     //
145     //     For black (on assumed white) this may not cover some pixels which were
146     //     previously covered; however those pixels would have been only slightly
147     //     covered and that slight coverage would have been decreased anyway. Also, some pixels
148     //     which were previously fully covered may no longer be fully covered.
149     //
150     //     For white (on assumed black) this may cover some pixels which weren't
151     //     previously covered at all.
152 
153     int width, height;
154     size_t size;
155 
156 #ifdef SK_GAMMA_CONTRAST
157     SkScalar contrast = SK_GAMMA_CONTRAST;
158 #else
159     SkScalar contrast = 0.5f;
160 #endif
161     SkScalar paintGamma = gamma;
162     SkScalar deviceGamma = gamma;
163 
164     size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
165         &width, &height);
166 
167     SkASSERT(kExpectedDistanceAdjustTableSize == height);
168     fTable = SkNEW_ARRAY(SkScalar, height);
169 
170     SkAutoTArray<uint8_t> data((int)size);
171     SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
172 
173     // find the inverse points where we cross 0.5
174     // binsearch might be better, but we only need to do this once on creation
175     for (int row = 0; row < height; ++row) {
176         uint8_t* rowPtr = data.get() + row*width;
177         for (int col = 0; col < width - 1; ++col) {
178             if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
179                 // compute point where a mask value will give us a result of 0.5
180                 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
181                 float borderAlpha = (col + interp) / 255.f;
182 
183                 // compute t value for that alpha
184                 // this is an approximate inverse for smoothstep()
185                 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
186 
187                 // compute distance which gives us that t value
188                 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
189                 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
190 
191                 fTable[row] = d;
192                 break;
193             }
194         }
195     }
196 }
197 
Create(GrContext * context,SkGpuDevice * gpuDevice,const SkDeviceProperties & props,bool enableDistanceFields)198 GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
199                                                SkGpuDevice* gpuDevice,
200                                                const SkDeviceProperties& props,
201                                                bool enableDistanceFields) {
202     return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
203 }
204 
canDraw(const GrRenderTarget *,const GrClip &,const GrPaint &,const SkPaint & skPaint,const SkMatrix & viewMatrix)205 bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
206                                  const GrClip&,
207                                  const GrPaint&,
208                                  const SkPaint& skPaint,
209                                  const SkMatrix& viewMatrix) {
210     return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
211            !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
212 }
213 
ComputeCanonicalColor(const SkPaint & paint,bool lcd)214 GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
215     GrColor canonicalColor = paint.computeLuminanceColor();
216     if (lcd) {
217         // This is the correct computation, but there are tons of cases where LCD can be overridden.
218         // For now we just regenerate if any run in a textblob has LCD.
219         // TODO figure out where all of these overrides are and see if we can incorporate that logic
220         // at a higher level *OR* use sRGB
221         SkASSERT(false);
222         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
223     } else {
224         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
225         // gamma corrected masks anyways, nor color
226         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
227                                        SkColorGetG(canonicalColor),
228                                        SkColorGetB(canonicalColor));
229         // reduce to our finite number of bits
230         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
231     }
232     return canonicalColor;
233 }
234 
235 // TODO if this function ever shows up in profiling, then we can compute this value when the
236 // textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
237 // run so this is not a big deal to compute here.
HasLCD(const SkTextBlob * blob)238 bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
239     SkTextBlob::RunIterator it(blob);
240     for (; !it.done(); it.next()) {
241         if (it.isLCD()) {
242             return true;
243         }
244     }
245     return false;
246 }
247 
MustRegenerateBlob(SkScalar * outTransX,SkScalar * outTransY,const BitmapTextBlob & blob,const SkPaint & paint,const SkMaskFilter::BlurRec & blurRec,const SkMatrix & viewMatrix,SkScalar x,SkScalar y)248 bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
249                                             const BitmapTextBlob& blob, const SkPaint& paint,
250                                             const SkMaskFilter::BlurRec& blurRec,
251                                             const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
252     // If we have LCD text then our canonical color will be set to transparent, in this case we have
253     // to regenerate the blob on any color change
254     if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
255         return true;
256     }
257 
258     if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
259         return true;
260     }
261 
262     if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
263         return true;
264     }
265 
266     // We only cache one masked version
267     if (blob.fKey.fHasBlur &&
268         (blob.fBlurRec.fSigma != blurRec.fSigma ||
269          blob.fBlurRec.fStyle != blurRec.fStyle ||
270          blob.fBlurRec.fQuality != blurRec.fQuality)) {
271         return true;
272     }
273 
274     // Similarly, we only cache one version for each style
275     if (blob.fKey.fStyle != SkPaint::kFill_Style &&
276         (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
277          blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
278          blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
279         return true;
280     }
281 
282     // Mixed blobs must be regenerated.  We could probably figure out a way to do integer scrolls
283     // for mixed blobs if this becomes an issue.
284     if (blob.hasBitmap() && blob.hasDistanceField()) {
285         // Identical viewmatrices and we can reuse in all cases
286         if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
287             return false;
288         }
289         return true;
290     }
291 
292     if (blob.hasBitmap()) {
293         if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
294             blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
295             blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
296             blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
297             return true;
298         }
299 
300         // We can update the positions in the cachedtextblobs without regenerating the whole blob,
301         // but only for integer translations.
302         // This cool bit of math will determine the necessary translation to apply to the already
303         // generated vertex coordinates to move them to the correct position
304         SkScalar transX = viewMatrix.getTranslateX() +
305                           viewMatrix.getScaleX() * (x - blob.fX) +
306                           viewMatrix.getSkewX() * (y - blob.fY) -
307                           blob.fViewMatrix.getTranslateX();
308         SkScalar transY = viewMatrix.getTranslateY() +
309                           viewMatrix.getSkewY() * (x - blob.fX) +
310                           viewMatrix.getScaleY() * (y - blob.fY) -
311                           blob.fViewMatrix.getTranslateY();
312         if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
313             return true;
314         }
315 
316         (*outTransX) = transX;
317         (*outTransY) = transY;
318     } else if (blob.hasDistanceField()) {
319         // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
320         // distance field being generated, so we have to regenerate in those cases
321         SkScalar newMaxScale = viewMatrix.getMaxScale();
322         SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
323         SkScalar scaleAdjust = newMaxScale / oldMaxScale;
324         if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
325             return true;
326         }
327 
328         (*outTransX) = x - blob.fX;
329         (*outTransY) = y - blob.fY;
330     }
331     // It is possible that a blob has neither distanceField nor bitmaptext.  This is in the case
332     // when all of the runs inside the blob are drawn as paths.  In this case, we always regenerate
333     // the blob anyways at flush time, so no need to regenerate explicitly
334 
335     return false;
336 }
337 
338 
setupCache(BitmapTextBlob::Run * run,const SkPaint & skPaint,const SkMatrix * viewMatrix,bool noGamma)339 inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
340                                                     const SkPaint& skPaint,
341                                                     const SkMatrix* viewMatrix,
342                                                     bool noGamma) {
343     skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
344     run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
345     return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
346 }
347 
drawTextBlob(GrRenderTarget * rt,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkTextBlob * blob,SkScalar x,SkScalar y,SkDrawFilter * drawFilter,const SkIRect & clipBounds)348 void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
349                                       const SkPaint& skPaint, const SkMatrix& viewMatrix,
350                                       const SkTextBlob* blob, SkScalar x, SkScalar y,
351                                       SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
352     // If we have been abandoned, then don't draw
353     if (!fContext->getTextTarget()) {
354         return;
355     }
356 
357     SkAutoTUnref<BitmapTextBlob> cacheBlob;
358     SkMaskFilter::BlurRec blurRec;
359     BitmapTextBlob::Key key;
360     // It might be worth caching these things, but its not clear at this time
361     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
362     const SkMaskFilter* mf = skPaint.getMaskFilter();
363     bool canCache = !(skPaint.getPathEffect() ||
364                       (mf && !mf->asABlur(&blurRec)) ||
365                       drawFilter);
366 
367     if (canCache) {
368         bool hasLCD = HasLCD(blob);
369 
370         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
371         SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() :
372                                                  kUnknown_SkPixelGeometry;
373 
374         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
375         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
376         // ensure we always match the same key
377         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
378                                           ComputeCanonicalColor(skPaint, hasLCD);
379 
380         key.fPixelGeometry = pixelGeometry;
381         key.fUniqueID = blob->uniqueID();
382         key.fStyle = skPaint.getStyle();
383         key.fHasBlur = SkToBool(mf);
384         key.fCanonicalColor = canonicalColor;
385         cacheBlob.reset(SkSafeRef(fCache->find(key)));
386     }
387 
388     SkIRect clipRect;
389     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
390 
391     SkScalar transX = 0.f;
392     SkScalar transY = 0.f;
393 
394     // Though for the time being runs in the textblob can override the paint, they only touch font
395     // info.
396     GrPaint grPaint;
397     if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
398         return;
399     }
400 
401     if (cacheBlob) {
402         if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
403             // We have to remake the blob because changes may invalidate our masks.
404             // TODO we could probably get away reuse most of the time if the pointer is unique,
405             // but we'd have to clear the subrun information
406             fCache->remove(cacheBlob);
407             cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
408                                                            kGrayTextVASize)));
409             this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
410                                      drawFilter, clipRect, rt, clip, grPaint);
411         } else {
412             // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
413             // offsets
414             cacheBlob->fViewMatrix = viewMatrix;
415             cacheBlob->fX = x;
416             cacheBlob->fY = y;
417             fCache->makeMRU(cacheBlob);
418         }
419     } else {
420         if (canCache) {
421             cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
422                                                            kGrayTextVASize)));
423         } else {
424             cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
425         }
426         this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
427                                  drawFilter, clipRect, rt, clip, grPaint);
428     }
429 
430     cacheBlob->fPaintColor = skPaint.getColor();
431     this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
432                 clip, viewMatrix, clipBounds, x, y, transX, transY);
433 }
434 
canDrawAsDistanceFields(const SkPaint & skPaint,const SkMatrix & viewMatrix)435 inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
436                                                         const SkMatrix& viewMatrix) {
437     // TODO: support perspective (need getMaxScale replacement)
438     if (viewMatrix.hasPerspective()) {
439         return false;
440     }
441 
442     SkScalar maxScale = viewMatrix.getMaxScale();
443     SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
444     // Hinted text looks far better at small resolutions
445     // Scaling up beyond 2x yields undesireable artifacts
446     if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
447         return false;
448     }
449 
450     if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
451         scaledTextSize < kLargeDFFontSize) {
452         return false;
453     }
454 
455     // rasterizers and mask filters modify alpha, which doesn't
456     // translate well to distance
457     if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
458         !fContext->getTextTarget()->caps()->shaderCaps()->shaderDerivativeSupport()) {
459         return false;
460     }
461 
462     // TODO: add some stroking support
463     if (skPaint.getStyle() != SkPaint::kFill_Style) {
464         return false;
465     }
466 
467     return true;
468 }
469 
regenerateTextBlob(BitmapTextBlob * cacheBlob,const SkPaint & skPaint,GrColor color,const SkMatrix & viewMatrix,const SkTextBlob * blob,SkScalar x,SkScalar y,SkDrawFilter * drawFilter,const SkIRect & clipRect,GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint)470 void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
471                                             const SkPaint& skPaint, GrColor color,
472                                             const SkMatrix& viewMatrix,
473                                             const SkTextBlob* blob, SkScalar x, SkScalar y,
474                                             SkDrawFilter* drawFilter, const SkIRect& clipRect,
475                                             GrRenderTarget* rt, const GrClip& clip,
476                                             const GrPaint& paint) {
477     cacheBlob->fViewMatrix = viewMatrix;
478     cacheBlob->fX = x;
479     cacheBlob->fY = y;
480 
481     // Regenerate textblob
482     SkPaint runPaint = skPaint;
483     SkTextBlob::RunIterator it(blob);
484     for (int run = 0; !it.done(); it.next(), run++) {
485         int glyphCount = it.glyphCount();
486         size_t textLen = glyphCount * sizeof(uint16_t);
487         const SkPoint& offset = it.offset();
488         // applyFontToPaint() always overwrites the exact same attributes,
489         // so it is safe to not re-seed the paint for this reason.
490         it.applyFontToPaint(&runPaint);
491 
492         if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
493             // A false return from filter() means we should abort the current draw.
494             runPaint = skPaint;
495             continue;
496         }
497 
498         runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
499 
500         // setup vertex / glyphIndex for the new run
501         if (run > 0) {
502             PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
503             PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
504 
505             newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
506             newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
507 
508             newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
509             newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
510         }
511 
512         if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
513             cacheBlob->setHasDistanceField();
514             SkPaint dfPaint = runPaint;
515             SkScalar textRatio;
516             this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
517             Run& runIdx = cacheBlob->fRuns[run];
518             PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
519             subRun.fUseLCDText = runPaint.isLCDRenderText();
520             subRun.fDrawAsDistanceFields = true;
521 
522             SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
523 
524             SkTDArray<char> fallbackTxt;
525             SkTDArray<SkScalar> fallbackPos;
526             SkPoint dfOffset;
527             int scalarsPerPosition = 2;
528             switch (it.positioning()) {
529                 case SkTextBlob::kDefault_Positioning: {
530                     this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
531                                              (const char *)it.glyphs(), textLen,
532                                              x + offset.x(), y + offset.y(), clipRect, textRatio,
533                                              &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
534                     break;
535                 }
536                 case SkTextBlob::kHorizontal_Positioning: {
537                     scalarsPerPosition = 1;
538                     dfOffset = SkPoint::Make(x, y + offset.y());
539                     this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
540                                                 (const char*)it.glyphs(), textLen, it.pos(),
541                                                 scalarsPerPosition, dfOffset, clipRect, textRatio,
542                                                 &fallbackTxt, &fallbackPos);
543                     break;
544                 }
545                 case SkTextBlob::kFull_Positioning: {
546                     dfOffset = SkPoint::Make(x, y);
547                     this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
548                                                 (const char*)it.glyphs(), textLen, it.pos(),
549                                                 scalarsPerPosition, dfOffset, clipRect, textRatio,
550                                                 &fallbackTxt, &fallbackPos);
551                     break;
552                 }
553             }
554             if (fallbackTxt.count()) {
555                 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
556                                           fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
557                                           clipRect);
558             }
559 
560             SkGlyphCache::AttachCache(cache);
561         } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
562             cacheBlob->fRuns[run].fDrawAsPaths = true;
563         } else {
564             cacheBlob->setHasBitmap();
565             SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
566                                                    false);
567             switch (it.positioning()) {
568                 case SkTextBlob::kDefault_Positioning:
569                     this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
570                                               (const char *)it.glyphs(), textLen,
571                                               x + offset.x(), y + offset.y(), clipRect);
572                     break;
573                 case SkTextBlob::kHorizontal_Positioning:
574                     this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
575                                                  (const char*)it.glyphs(), textLen, it.pos(), 1,
576                                                  SkPoint::Make(x, y + offset.y()), clipRect);
577                     break;
578                 case SkTextBlob::kFull_Positioning:
579                     this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
580                                                  (const char*)it.glyphs(), textLen, it.pos(), 2,
581                                                  SkPoint::Make(x, y), clipRect);
582                     break;
583             }
584             SkGlyphCache::AttachCache(cache);
585         }
586 
587         if (drawFilter) {
588             // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
589             runPaint = skPaint;
590         }
591     }
592 }
593 
initDistanceFieldPaint(BitmapTextBlob * blob,SkPaint * skPaint,SkScalar * textRatio,const SkMatrix & viewMatrix)594 inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
595                                                        SkPaint* skPaint,
596                                                        SkScalar* textRatio,
597                                                        const SkMatrix& viewMatrix) {
598     // getMaxScale doesn't support perspective, so neither do we at the moment
599     SkASSERT(!viewMatrix.hasPerspective());
600     SkScalar maxScale = viewMatrix.getMaxScale();
601     SkScalar textSize = skPaint->getTextSize();
602     SkScalar scaledTextSize = textSize;
603     // if we have non-unity scale, we need to choose our base text size
604     // based on the SkPaint's text size multiplied by the max scale factor
605     // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
606     if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
607         scaledTextSize *= maxScale;
608     }
609 
610     // We have three sizes of distance field text, and within each size 'bucket' there is a floor
611     // and ceiling.  A scale outside of this range would require regenerating the distance fields
612     SkScalar dfMaskScaleFloor;
613     SkScalar dfMaskScaleCeil;
614     if (scaledTextSize <= kSmallDFFontLimit) {
615         dfMaskScaleFloor = kMinDFFontSize;
616         dfMaskScaleCeil = kSmallDFFontLimit;
617         *textRatio = textSize / kSmallDFFontSize;
618         skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
619     } else if (scaledTextSize <= kMediumDFFontLimit) {
620         dfMaskScaleFloor = kSmallDFFontLimit;
621         dfMaskScaleCeil = kMediumDFFontLimit;
622         *textRatio = textSize / kMediumDFFontSize;
623         skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
624     } else {
625         dfMaskScaleFloor = kMediumDFFontLimit;
626         dfMaskScaleCeil = kLargeDFFontLimit;
627         *textRatio = textSize / kLargeDFFontSize;
628         skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
629     }
630 
631     // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
632     // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
633     // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
634     // tolerate before we'd have to move to a large mip size.  When we actually test these values
635     // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
636     // against these values to decide if we can reuse or not(ie, will a given scale change our mip
637     // level)
638     SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
639     blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
640     blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
641 
642     skPaint->setLCDRenderText(false);
643     skPaint->setAutohinted(false);
644     skPaint->setHinting(SkPaint::kNormal_Hinting);
645     skPaint->setSubpixelText(true);
646 }
647 
fallbackDrawPosText(BitmapTextBlob * blob,int runIndex,GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkTDArray<char> & fallbackTxt,const SkTDArray<SkScalar> & fallbackPos,int scalarsPerPosition,const SkPoint & offset,const SkIRect & clipRect)648 inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
649                                                     int runIndex,
650                                                     GrRenderTarget* rt, const GrClip& clip,
651                                                     const GrPaint& paint,
652                                                     const SkPaint& skPaint,
653                                                     const SkMatrix& viewMatrix,
654                                                     const SkTDArray<char>& fallbackTxt,
655                                                     const SkTDArray<SkScalar>& fallbackPos,
656                                                     int scalarsPerPosition,
657                                                     const SkPoint& offset,
658                                                     const SkIRect& clipRect) {
659     SkASSERT(fallbackTxt.count());
660     blob->setHasBitmap();
661     Run& run = blob->fRuns[runIndex];
662     // Push back a new subrun to fill and set the override descriptor
663     run.push_back();
664     run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
665     skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
666                                        &fDeviceProperties, &viewMatrix, false);
667     SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
668                                                     run.fOverrideDescriptor->getDesc());
669     this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
670                                  fallbackTxt.begin(), fallbackTxt.count(),
671                                  fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
672     SkGlyphCache::AttachCache(cache);
673 }
674 
675 inline GrAtlasTextContext::BitmapTextBlob*
setupDFBlob(int glyphCount,const SkPaint & origPaint,const SkMatrix & viewMatrix,SkGlyphCache ** cache,SkPaint * dfPaint,SkScalar * textRatio)676 GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
677                                 const SkMatrix& viewMatrix, SkGlyphCache** cache,
678                                 SkPaint* dfPaint, SkScalar* textRatio) {
679     BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
680 
681     *dfPaint = origPaint;
682     this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
683     blob->fViewMatrix = viewMatrix;
684     Run& run = blob->fRuns[0];
685     PerSubRunInfo& subRun = run.fSubRunInfo.back();
686     subRun.fUseLCDText = origPaint.isLCDRenderText();
687     subRun.fDrawAsDistanceFields = true;
688 
689     *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
690     return blob;
691 }
692 
693 inline GrAtlasTextContext::BitmapTextBlob*
createDrawTextBlob(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & regionClipBounds)694 GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
695                                        const GrPaint& paint, const SkPaint& skPaint,
696                                        const SkMatrix& viewMatrix,
697                                        const char text[], size_t byteLength,
698                                        SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
699     int glyphCount = skPaint.countText(text, byteLength);
700     SkIRect clipRect;
701     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
702 
703     BitmapTextBlob* blob;
704     if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
705         SkPaint dfPaint;
706         SkScalar textRatio;
707         SkGlyphCache* cache;
708         blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
709 
710         SkTDArray<char> fallbackTxt;
711         SkTDArray<SkScalar> fallbackPos;
712         SkPoint offset;
713         this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
714                                  byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
715                                  &offset, skPaint);
716         SkGlyphCache::AttachCache(cache);
717         if (fallbackTxt.count()) {
718             this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
719                                       fallbackPos, 2, offset, clipRect);
720         }
721     } else {
722         blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
723         blob->fViewMatrix = viewMatrix;
724 
725         SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
726         this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
727                                   byteLength, x, y, clipRect);
728         SkGlyphCache::AttachCache(cache);
729     }
730     return blob;
731 }
732 
733 inline GrAtlasTextContext::BitmapTextBlob*
createDrawPosTextBlob(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & regionClipBounds)734 GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
735                                           const GrPaint& paint, const SkPaint& skPaint,
736                                           const SkMatrix& viewMatrix,
737                                           const char text[], size_t byteLength,
738                                           const SkScalar pos[], int scalarsPerPosition,
739                                           const SkPoint& offset, const SkIRect& regionClipBounds) {
740     int glyphCount = skPaint.countText(text, byteLength);
741 
742     SkIRect clipRect;
743     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
744 
745     BitmapTextBlob* blob;
746     if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
747         SkPaint dfPaint;
748         SkScalar textRatio;
749         SkGlyphCache* cache;
750         blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
751 
752         SkTDArray<char> fallbackTxt;
753         SkTDArray<SkScalar> fallbackPos;
754         this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
755                                     byteLength, pos, scalarsPerPosition, offset, clipRect,
756                                     textRatio, &fallbackTxt, &fallbackPos);
757         SkGlyphCache::AttachCache(cache);
758         if (fallbackTxt.count()) {
759             this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
760                                       fallbackPos, scalarsPerPosition, offset, clipRect);
761         }
762     } else {
763         blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
764         blob->fViewMatrix = viewMatrix;
765         SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
766         this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
767                                      byteLength, pos, scalarsPerPosition, offset, clipRect);
768         SkGlyphCache::AttachCache(cache);
769     }
770     return blob;
771 }
772 
onDrawText(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & regionClipBounds)773 void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
774                                     const GrPaint& paint, const SkPaint& skPaint,
775                                     const SkMatrix& viewMatrix,
776                                     const char text[], size_t byteLength,
777                                     SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
778     SkAutoTUnref<BitmapTextBlob> blob(
779             this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
780                                      text, byteLength, x, y, regionClipBounds));
781     this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds);
782 }
783 
onDrawPosText(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & regionClipBounds)784 void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
785                                        const GrPaint& paint, const SkPaint& skPaint,
786                                        const SkMatrix& viewMatrix,
787                                        const char text[], size_t byteLength,
788                                        const SkScalar pos[], int scalarsPerPosition,
789                                        const SkPoint& offset, const SkIRect& regionClipBounds) {
790     SkAutoTUnref<BitmapTextBlob> blob(
791             this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
792                                         text, byteLength,
793                                         pos, scalarsPerPosition,
794                                         offset, regionClipBounds));
795 
796     this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds);
797 }
798 
internalDrawBMPText(BitmapTextBlob * blob,int runIndex,SkGlyphCache * cache,const SkPaint & skPaint,GrColor color,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & clipRect)799 void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
800                                              SkGlyphCache* cache, const SkPaint& skPaint,
801                                              GrColor color,
802                                              const SkMatrix& viewMatrix,
803                                              const char text[], size_t byteLength,
804                                              SkScalar x, SkScalar y, const SkIRect& clipRect) {
805     SkASSERT(byteLength == 0 || text != NULL);
806 
807     // nothing to draw
808     if (text == NULL || byteLength == 0) {
809         return;
810     }
811 
812     fCurrStrike = NULL;
813     SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
814 
815     // Get GrFontScaler from cache
816     GrFontScaler* fontScaler = GetGrFontScaler(cache);
817 
818     // transform our starting point
819     {
820         SkPoint loc;
821         viewMatrix.mapXY(x, y, &loc);
822         x = loc.fX;
823         y = loc.fY;
824     }
825 
826     // need to measure first
827     if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
828         SkVector    stopVector;
829         MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
830 
831         SkScalar    stopX = stopVector.fX;
832         SkScalar    stopY = stopVector.fY;
833 
834         if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
835             stopX = SkScalarHalf(stopX);
836             stopY = SkScalarHalf(stopY);
837         }
838         x -= stopX;
839         y -= stopY;
840     }
841 
842     const char* stop = text + byteLength;
843 
844     SkAutoKern autokern;
845 
846     SkFixed fxMask = ~0;
847     SkFixed fyMask = ~0;
848     SkScalar halfSampleX, halfSampleY;
849     if (cache->isSubpixel()) {
850         halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
851         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
852         if (kX_SkAxisAlignment == baseline) {
853             fyMask = 0;
854             halfSampleY = SK_ScalarHalf;
855         } else if (kY_SkAxisAlignment == baseline) {
856             fxMask = 0;
857             halfSampleX = SK_ScalarHalf;
858         }
859     } else {
860         halfSampleX = halfSampleY = SK_ScalarHalf;
861     }
862 
863     Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
864     Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
865 
866     while (text < stop) {
867         const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
868 
869         fx += autokern.adjust(glyph);
870 
871         if (glyph.fWidth) {
872             this->bmpAppendGlyph(blob,
873                                  runIndex,
874                                  GrGlyph::Pack(glyph.getGlyphID(),
875                                                glyph.getSubXFixed(),
876                                                glyph.getSubYFixed(),
877                                                GrGlyph::kCoverage_MaskStyle),
878                                  Sk48Dot16FloorToInt(fx),
879                                  Sk48Dot16FloorToInt(fy),
880                                  color,
881                                  fontScaler,
882                                  clipRect);
883         }
884 
885         fx += glyph.fAdvanceX;
886         fy += glyph.fAdvanceY;
887     }
888 }
889 
internalDrawBMPPosText(BitmapTextBlob * blob,int runIndex,SkGlyphCache * cache,const SkPaint & skPaint,GrColor color,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & clipRect)890 void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
891                                                 SkGlyphCache* cache, const SkPaint& skPaint,
892                                                 GrColor color,
893                                                 const SkMatrix& viewMatrix,
894                                                 const char text[], size_t byteLength,
895                                                 const SkScalar pos[], int scalarsPerPosition,
896                                                 const SkPoint& offset, const SkIRect& clipRect) {
897     SkASSERT(byteLength == 0 || text != NULL);
898     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
899 
900     // nothing to draw
901     if (text == NULL || byteLength == 0) {
902         return;
903     }
904 
905     fCurrStrike = NULL;
906     SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
907 
908     // Get GrFontScaler from cache
909     GrFontScaler* fontScaler = GetGrFontScaler(cache);
910 
911     const char*        stop = text + byteLength;
912     SkTextAlignProc    alignProc(skPaint.getTextAlign());
913     SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
914 
915     if (cache->isSubpixel()) {
916         // maybe we should skip the rounding if linearText is set
917         SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
918 
919         SkFixed fxMask = ~0;
920         SkFixed fyMask = ~0;
921         SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
922         SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
923         if (kX_SkAxisAlignment == baseline) {
924             fyMask = 0;
925             halfSampleY = SK_ScalarHalf;
926         } else if (kY_SkAxisAlignment == baseline) {
927             fxMask = 0;
928             halfSampleX = SK_ScalarHalf;
929         }
930 
931         if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
932             while (text < stop) {
933                 SkPoint tmsLoc;
934                 tmsProc(pos, &tmsLoc);
935                 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
936                 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
937 
938                 const SkGlyph& glyph = glyphCacheProc(cache, &text,
939                                                       fx & fxMask, fy & fyMask);
940 
941                 if (glyph.fWidth) {
942                     this->bmpAppendGlyph(blob,
943                                          runIndex,
944                                          GrGlyph::Pack(glyph.getGlyphID(),
945                                                        glyph.getSubXFixed(),
946                                                        glyph.getSubYFixed(),
947                                                        GrGlyph::kCoverage_MaskStyle),
948                                          Sk48Dot16FloorToInt(fx),
949                                          Sk48Dot16FloorToInt(fy),
950                                          color,
951                                          fontScaler,
952                                          clipRect);
953                 }
954                 pos += scalarsPerPosition;
955             }
956         } else {
957             while (text < stop) {
958                 const char* currentText = text;
959                 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
960 
961                 if (metricGlyph.fWidth) {
962                     SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
963                     SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
964                     SkPoint tmsLoc;
965                     tmsProc(pos, &tmsLoc);
966                     SkPoint alignLoc;
967                     alignProc(tmsLoc, metricGlyph, &alignLoc);
968 
969                     Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
970                     Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
971 
972                     // have to call again, now that we've been "aligned"
973                     const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
974                                                           fx & fxMask, fy & fyMask);
975                     // the assumption is that the metrics haven't changed
976                     SkASSERT(prevAdvX == glyph.fAdvanceX);
977                     SkASSERT(prevAdvY == glyph.fAdvanceY);
978                     SkASSERT(glyph.fWidth);
979 
980                     this->bmpAppendGlyph(blob,
981                                          runIndex,
982                                          GrGlyph::Pack(glyph.getGlyphID(),
983                                                        glyph.getSubXFixed(),
984                                                        glyph.getSubYFixed(),
985                                                        GrGlyph::kCoverage_MaskStyle),
986                                          Sk48Dot16FloorToInt(fx),
987                                          Sk48Dot16FloorToInt(fy),
988                                          color,
989                                          fontScaler,
990                                          clipRect);
991                 }
992                 pos += scalarsPerPosition;
993             }
994         }
995     } else {    // not subpixel
996 
997         if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
998             while (text < stop) {
999                 // the last 2 parameters are ignored
1000                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1001 
1002                 if (glyph.fWidth) {
1003                     SkPoint tmsLoc;
1004                     tmsProc(pos, &tmsLoc);
1005 
1006                     Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1007                     Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
1008                     this->bmpAppendGlyph(blob,
1009                                          runIndex,
1010                                          GrGlyph::Pack(glyph.getGlyphID(),
1011                                                        glyph.getSubXFixed(),
1012                                                        glyph.getSubYFixed(),
1013                                                        GrGlyph::kCoverage_MaskStyle),
1014                                          Sk48Dot16FloorToInt(fx),
1015                                          Sk48Dot16FloorToInt(fy),
1016                                          color,
1017                                          fontScaler,
1018                                          clipRect);
1019                 }
1020                 pos += scalarsPerPosition;
1021             }
1022         } else {
1023             while (text < stop) {
1024                 // the last 2 parameters are ignored
1025                 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1026 
1027                 if (glyph.fWidth) {
1028                     SkPoint tmsLoc;
1029                     tmsProc(pos, &tmsLoc);
1030 
1031                     SkPoint alignLoc;
1032                     alignProc(tmsLoc, glyph, &alignLoc);
1033 
1034                     Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1035                     Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
1036                     this->bmpAppendGlyph(blob,
1037                                          runIndex,
1038                                          GrGlyph::Pack(glyph.getGlyphID(),
1039                                                        glyph.getSubXFixed(),
1040                                                        glyph.getSubYFixed(),
1041                                                        GrGlyph::kCoverage_MaskStyle),
1042                                          Sk48Dot16FloorToInt(fx),
1043                                          Sk48Dot16FloorToInt(fy),
1044                                          color,
1045                                          fontScaler,
1046                                          clipRect);
1047                 }
1048                 pos += scalarsPerPosition;
1049             }
1050         }
1051     }
1052 }
1053 
1054 
internalDrawDFText(BitmapTextBlob * blob,int runIndex,SkGlyphCache * cache,const SkPaint & skPaint,GrColor color,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & clipRect,SkScalar textRatio,SkTDArray<char> * fallbackTxt,SkTDArray<SkScalar> * fallbackPos,SkPoint * offset,const SkPaint & origPaint)1055 void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1056                                             SkGlyphCache* cache, const SkPaint& skPaint,
1057                                             GrColor color,
1058                                             const SkMatrix& viewMatrix,
1059                                             const char text[], size_t byteLength,
1060                                             SkScalar x, SkScalar y, const SkIRect& clipRect,
1061                                             SkScalar textRatio,
1062                                             SkTDArray<char>* fallbackTxt,
1063                                             SkTDArray<SkScalar>* fallbackPos,
1064                                             SkPoint* offset,
1065                                             const SkPaint& origPaint) {
1066     SkASSERT(byteLength == 0 || text != NULL);
1067 
1068     // nothing to draw
1069     if (text == NULL || byteLength == 0) {
1070         return;
1071     }
1072 
1073     SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1074     SkAutoDescriptor desc;
1075     origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
1076     SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1077                                                              desc.getDesc());
1078 
1079     SkTArray<SkScalar> positions;
1080 
1081     const char* textPtr = text;
1082     SkFixed stopX = 0;
1083     SkFixed stopY = 0;
1084     SkFixed origin = 0;
1085     switch (origPaint.getTextAlign()) {
1086         case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1087         case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1088         case SkPaint::kLeft_Align: origin = 0; break;
1089     }
1090 
1091     SkAutoKern autokern;
1092     const char* stop = text + byteLength;
1093     while (textPtr < stop) {
1094         // don't need x, y here, since all subpixel variants will have the
1095         // same advance
1096         const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1097 
1098         SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1099         positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1100 
1101         SkFixed height = glyph.fAdvanceY;
1102         positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1103 
1104         stopX += width;
1105         stopY += height;
1106     }
1107     SkASSERT(textPtr == stop);
1108 
1109     // now adjust starting point depending on alignment
1110     SkScalar alignX = SkFixedToScalar(stopX);
1111     SkScalar alignY = SkFixedToScalar(stopY);
1112     if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1113         alignX = SkScalarHalf(alignX);
1114         alignY = SkScalarHalf(alignY);
1115     } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1116         alignX = 0;
1117         alignY = 0;
1118     }
1119     x -= alignX;
1120     y -= alignY;
1121     *offset = SkPoint::Make(x, y);
1122 
1123     this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1124                                 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1125                                 fallbackPos);
1126     SkGlyphCache::AttachCache(origPaintCache);
1127 }
1128 
internalDrawDFPosText(BitmapTextBlob * blob,int runIndex,SkGlyphCache * cache,const SkPaint & skPaint,GrColor color,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & clipRect,SkScalar textRatio,SkTDArray<char> * fallbackTxt,SkTDArray<SkScalar> * fallbackPos)1129 void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1130                                                SkGlyphCache* cache, const SkPaint& skPaint,
1131                                                GrColor color,
1132                                                const SkMatrix& viewMatrix,
1133                                                const char text[], size_t byteLength,
1134                                                const SkScalar pos[], int scalarsPerPosition,
1135                                                const SkPoint& offset, const SkIRect& clipRect,
1136                                                SkScalar textRatio,
1137                                                SkTDArray<char>* fallbackTxt,
1138                                                SkTDArray<SkScalar>* fallbackPos) {
1139 
1140     SkASSERT(byteLength == 0 || text != NULL);
1141     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1142 
1143     // nothing to draw
1144     if (text == NULL || byteLength == 0) {
1145         return;
1146     }
1147 
1148     fCurrStrike = NULL;
1149 
1150     SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1151     GrFontScaler* fontScaler = GetGrFontScaler(cache);
1152 
1153     const char* stop = text + byteLength;
1154 
1155     if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1156         while (text < stop) {
1157             const char* lastText = text;
1158             // the last 2 parameters are ignored
1159             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1160 
1161             if (glyph.fWidth) {
1162                 SkScalar x = offset.x() + pos[0];
1163                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1164 
1165                 if (!this->dfAppendGlyph(blob,
1166                                          runIndex,
1167                                          GrGlyph::Pack(glyph.getGlyphID(),
1168                                                        glyph.getSubXFixed(),
1169                                                        glyph.getSubYFixed(),
1170                                                        GrGlyph::kDistance_MaskStyle),
1171                                          x, y, color, fontScaler, clipRect,
1172                                          textRatio, viewMatrix)) {
1173                     // couldn't append, send to fallback
1174                     fallbackTxt->append(SkToInt(text-lastText), lastText);
1175                     *fallbackPos->append() = pos[0];
1176                     if (2 == scalarsPerPosition) {
1177                         *fallbackPos->append() = pos[1];
1178                     }
1179                 }
1180             }
1181             pos += scalarsPerPosition;
1182         }
1183     } else {
1184         SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1185                                                                              : SK_Scalar1;
1186         while (text < stop) {
1187             const char* lastText = text;
1188             // the last 2 parameters are ignored
1189             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1190 
1191             if (glyph.fWidth) {
1192                 SkScalar x = offset.x() + pos[0];
1193                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1194 
1195                 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1196                 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1197 
1198                 if (!this->dfAppendGlyph(blob,
1199                                          runIndex,
1200                                          GrGlyph::Pack(glyph.getGlyphID(),
1201                                                        glyph.getSubXFixed(),
1202                                                        glyph.getSubYFixed(),
1203                                                        GrGlyph::kDistance_MaskStyle),
1204                                          x - advanceX, y - advanceY, color,
1205                                          fontScaler,
1206                                          clipRect,
1207                                          textRatio,
1208                                          viewMatrix)) {
1209                     // couldn't append, send to fallback
1210                     fallbackTxt->append(SkToInt(text-lastText), lastText);
1211                     *fallbackPos->append() = pos[0];
1212                     if (2 == scalarsPerPosition) {
1213                         *fallbackPos->append() = pos[1];
1214                     }
1215                 }
1216             }
1217             pos += scalarsPerPosition;
1218         }
1219     }
1220 }
1221 
bmpAppendGlyph(BitmapTextBlob * blob,int runIndex,GrGlyph::PackedID packed,int vx,int vy,GrColor color,GrFontScaler * scaler,const SkIRect & clipRect)1222 void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1223                                         GrGlyph::PackedID packed,
1224                                         int vx, int vy, GrColor color, GrFontScaler* scaler,
1225                                         const SkIRect& clipRect) {
1226     Run& run = blob->fRuns[runIndex];
1227     if (!fCurrStrike) {
1228         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1229         run.fStrike.reset(SkRef(fCurrStrike));
1230     }
1231 
1232     GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
1233     if (!glyph) {
1234         return;
1235     }
1236 
1237     int x = vx + glyph->fBounds.fLeft;
1238     int y = vy + glyph->fBounds.fTop;
1239 
1240     // keep them as ints until we've done the clip-test
1241     int width = glyph->fBounds.width();
1242     int height = glyph->fBounds.height();
1243 
1244 #if 0
1245     // Not checking the clip bounds might introduce a performance regression.  However, its not
1246     // clear if this is still true today with the larger tiles we use in Chrome.  For repositionable
1247     // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1248     // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1249     // TODO verify this
1250     // check if we clipped out
1251     if (clipRect.quickReject(x, y, x + width, y + height)) {
1252         return;
1253     }
1254 #endif
1255 
1256     // If the glyph is too large we fall back to paths
1257     if (glyph->fTooLargeForAtlas) {
1258         this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
1259         return;
1260     }
1261 
1262     GrMaskFormat format = glyph->fMaskFormat;
1263 
1264     PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1265     if (run.fInitialized && subRun->fMaskFormat != format) {
1266         subRun = &run.fSubRunInfo.push_back();
1267     }
1268 
1269     run.fInitialized = true;
1270 
1271     size_t vertexStride = get_vertex_stride(format);
1272 
1273     SkRect r;
1274     r.fLeft = SkIntToScalar(x);
1275     r.fTop = SkIntToScalar(y);
1276     r.fRight = r.fLeft + SkIntToScalar(width);
1277     r.fBottom = r.fTop + SkIntToScalar(height);
1278     subRun->fMaskFormat = format;
1279     this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
1280                             glyph);
1281 }
1282 
dfAppendGlyph(BitmapTextBlob * blob,int runIndex,GrGlyph::PackedID packed,SkScalar sx,SkScalar sy,GrColor color,GrFontScaler * scaler,const SkIRect & clipRect,SkScalar textRatio,const SkMatrix & viewMatrix)1283 bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1284                                        GrGlyph::PackedID packed,
1285                                        SkScalar sx, SkScalar sy, GrColor color,
1286                                        GrFontScaler* scaler,
1287                                        const SkIRect& clipRect,
1288                                        SkScalar textRatio, const SkMatrix& viewMatrix) {
1289     Run& run = blob->fRuns[runIndex];
1290     if (!fCurrStrike) {
1291         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1292         run.fStrike.reset(SkRef(fCurrStrike));
1293     }
1294 
1295     GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
1296     if (!glyph) {
1297         return true;
1298     }
1299 
1300     // fallback to color glyph support
1301     if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1302         return false;
1303     }
1304 
1305     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1306     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1307     SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1308     SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1309 
1310     SkScalar scale = textRatio;
1311     dx *= scale;
1312     dy *= scale;
1313     width *= scale;
1314     height *= scale;
1315     sx += dx;
1316     sy += dy;
1317     SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1318 
1319 #if 0
1320     // check if we clipped out
1321     SkRect dstRect;
1322     viewMatrix.mapRect(&dstRect, glyphRect);
1323     if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1324                              SkScalarTruncToInt(dstRect.top()),
1325                              SkScalarTruncToInt(dstRect.right()),
1326                              SkScalarTruncToInt(dstRect.bottom()))) {
1327         return true;
1328     }
1329 #endif
1330 
1331     // TODO combine with the above
1332     // If the glyph is too large we fall back to paths
1333     if (glyph->fTooLargeForAtlas) {
1334         this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
1335         return true;
1336     }
1337 
1338     PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1339     SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1340     subRun->fMaskFormat = kA8_GrMaskFormat;
1341 
1342     size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1343 
1344     bool useColorVerts = !subRun->fUseLCDText;
1345     this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
1346                             glyph);
1347     return true;
1348 }
1349 
appendGlyphPath(BitmapTextBlob * blob,GrGlyph * glyph,GrFontScaler * scaler,SkScalar x,SkScalar y)1350 inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
1351                                                 GrFontScaler* scaler, SkScalar x, SkScalar y) {
1352     if (NULL == glyph->fPath) {
1353         SkPath* path = SkNEW(SkPath);
1354         if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1355             // flag the glyph as being dead?
1356             SkDELETE(path);
1357             return;
1358         }
1359         glyph->fPath = path;
1360     }
1361     SkASSERT(glyph->fPath);
1362     blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1363 }
1364 
appendGlyphCommon(BitmapTextBlob * blob,Run * run,Run::SubRunInfo * subRun,const SkRect & positions,GrColor color,size_t vertexStride,bool useVertexColor,GrGlyph * glyph)1365 inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1366                                                   Run::SubRunInfo* subRun,
1367                                                   const SkRect& positions, GrColor color,
1368                                                   size_t vertexStride, bool useVertexColor,
1369                                                   GrGlyph* glyph) {
1370     blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
1371     run->fVertexBounds.joinNonEmptyArg(positions);
1372     run->fColor = color;
1373 
1374     intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1375 
1376     if (useVertexColor) {
1377         // V0
1378         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1379         position->set(positions.fLeft, positions.fTop);
1380         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1381         *colorPtr = color;
1382         vertex += vertexStride;
1383 
1384         // V1
1385         position = reinterpret_cast<SkPoint*>(vertex);
1386         position->set(positions.fLeft, positions.fBottom);
1387         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1388         *colorPtr = color;
1389         vertex += vertexStride;
1390 
1391         // V2
1392         position = reinterpret_cast<SkPoint*>(vertex);
1393         position->set(positions.fRight, positions.fBottom);
1394         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1395         *colorPtr = color;
1396         vertex += vertexStride;
1397 
1398         // V3
1399         position = reinterpret_cast<SkPoint*>(vertex);
1400         position->set(positions.fRight, positions.fTop);
1401         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1402         *colorPtr = color;
1403     } else {
1404         // V0
1405         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1406         position->set(positions.fLeft, positions.fTop);
1407         vertex += vertexStride;
1408 
1409         // V1
1410         position = reinterpret_cast<SkPoint*>(vertex);
1411         position->set(positions.fLeft, positions.fBottom);
1412         vertex += vertexStride;
1413 
1414         // V2
1415         position = reinterpret_cast<SkPoint*>(vertex);
1416         position->set(positions.fRight, positions.fBottom);
1417         vertex += vertexStride;
1418 
1419         // V3
1420         position = reinterpret_cast<SkPoint*>(vertex);
1421         position->set(positions.fRight, positions.fTop);
1422     }
1423 
1424     subRun->fGlyphEndIndex++;
1425     subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1426 }
1427 
1428 class BitmapTextBatch : public GrBatch {
1429 public:
1430     typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
1431     typedef GrAtlasTextContext::BitmapTextBlob Blob;
1432     typedef Blob::Run Run;
1433     typedef Run::SubRunInfo TextInfo;
1434     struct Geometry {
1435         Blob* fBlob;
1436         int fRun;
1437         int fSubRun;
1438         GrColor fColor;
1439         SkScalar fTransX;
1440         SkScalar fTransY;
1441     };
1442 
Create(GrMaskFormat maskFormat,int glyphCount,GrBatchFontCache * fontCache)1443     static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1444                                    GrBatchFontCache* fontCache) {
1445         return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
1446     }
1447 
Create(GrMaskFormat maskFormat,int glyphCount,GrBatchFontCache * fontCache,DistanceAdjustTable * distanceAdjustTable,SkColor filteredColor,bool useLCDText,bool useBGR,float gamma)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 
name() const1457     const char* name() const override { return "BitmapTextBatch"; }
1458 
getInvariantOutputColor(GrInitInvariantOutput * out) const1459     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1460         if (kARGB_GrMaskFormat == fMaskFormat) {
1461             out->setUnknownFourComponents();
1462         } else {
1463             out->setKnownFourComponents(fBatch.fColor);
1464         }
1465     }
1466 
getInvariantOutputCoverage(GrInitInvariantOutput * out) const1467     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 
initBatchTracker(const GrPipelineInfo & init)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 
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)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
instanceCount() const1735     int instanceCount() const { return fInstanceCount; }
geoData()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
geometry()1741     Geometry& geometry() { return fGeoData[0]; }
init()1742     void init() {
1743         const Geometry& geo = fGeoData[0];
1744         fBatch.fColor = geo.fColor;
1745         fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1746 
1747         // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1748         // into device space
1749         const Run& run = geo.fBlob->fRuns[geo.fRun];
1750         if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1751             SkRect bounds = run.fVertexBounds;
1752             fBatch.fViewMatrix.mapRect(&bounds);
1753             this->setBounds(bounds);
1754         } else {
1755             this->setBounds(run.fVertexBounds);
1756         }
1757     }
1758 
1759 private:
BitmapTextBatch(GrMaskFormat maskFormat,int glyphCount,GrBatchFontCache * fontCache)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 
BitmapTextBatch(GrMaskFormat maskFormat,int glyphCount,GrBatchFontCache * fontCache,DistanceAdjustTable * distanceAdjustTable,SkColor filteredColor,bool useLCDText,bool useBGR,float gamma)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 
~BitmapTextBatch()1790     ~BitmapTextBatch() {
1791         for (int i = 0; i < fInstanceCount; i++) {
1792             fGeoData[i].fBlob->unref();
1793         }
1794     }
1795 
regenerateTextureCoords(GrGlyph * glyph,intptr_t vertex,size_t vertexStride)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 
regenerateColors(intptr_t vertex,size_t vertexStride,GrColor color)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 
regeneratePositions(intptr_t vertex,size_t vertexStride,SkScalar transX,SkScalar transY)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 
initDraw(GrBatchTarget * batchTarget,const GrGeometryProcessor * gp,const GrPipeline * pipeline)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 
flush(GrBatchTarget * batchTarget,FlushInfo * flushInfo)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 
color() const1878     GrColor color() const { return fBatch.fColor; }
viewMatrix() const1879     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
usesLocalCoords() const1880     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
numGlyphs() const1881     int numGlyphs() const { return fBatch.fNumGlyphs; }
1882 
onCombineIfPossible(GrBatch * t)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
setupDfProcessor(const SkMatrix & viewMatrix,SkColor filteredColor,GrColor color,GrTexture * texture)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 
flushRunAsPaths(const SkTextBlob::RunIterator & it,const SkPaint & skPaint,SkDrawFilter * drawFilter,const SkMatrix & viewMatrix,const SkIRect & clipBounds,SkScalar x,SkScalar y)2044 void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
2045                                          SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2046                                          const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2047     SkPaint runPaint = skPaint;
2048 
2049     size_t textLen = it.glyphCount() * sizeof(uint16_t);
2050     const SkPoint& offset = it.offset();
2051 
2052     it.applyFontToPaint(&runPaint);
2053 
2054     if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2055         return;
2056     }
2057 
2058     runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
2059 
2060     switch (it.positioning()) {
2061         case SkTextBlob::kDefault_Positioning:
2062             this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
2063                                  textLen, x + offset.x(), y + offset.y(), clipBounds);
2064             break;
2065         case SkTextBlob::kHorizontal_Positioning:
2066             this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
2067                                     textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2068                                     clipBounds);
2069             break;
2070         case SkTextBlob::kFull_Positioning:
2071             this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
2072                                     textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2073             break;
2074     }
2075 }
2076 
2077 
2078 inline BitmapTextBatch*
createBatch(BitmapTextBlob * cacheBlob,const PerSubRunInfo & info,int glyphCount,int run,int subRun,GrColor color,SkScalar transX,SkScalar transY,const SkPaint & skPaint)2079 GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
2080                                 int glyphCount, int run, int subRun,
2081                                 GrColor color, SkScalar transX, SkScalar transY,
2082                                 const SkPaint& skPaint) {
2083     GrMaskFormat format = info.fMaskFormat;
2084     GrColor subRunColor;
2085     if (kARGB_GrMaskFormat == format) {
2086         uint8_t paintAlpha = skPaint.getAlpha();
2087         subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2088     } else {
2089         subRunColor = color;
2090     }
2091 
2092     BitmapTextBatch* batch;
2093     if (info.fDrawAsDistanceFields) {
2094         SkColor filteredColor;
2095         SkColorFilter* colorFilter = skPaint.getColorFilter();
2096         if (colorFilter) {
2097             filteredColor = colorFilter->filterColor(skPaint.getColor());
2098         } else {
2099             filteredColor = skPaint.getColor();
2100         }
2101         bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
2102         float gamma = fDeviceProperties.gamma();
2103         batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
2104                                         fDistanceAdjustTable, filteredColor,
2105                                         info.fUseLCDText, useBGR,
2106                                         gamma);
2107     } else {
2108         batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
2109     }
2110     BitmapTextBatch::Geometry& geometry = batch->geometry();
2111     geometry.fBlob = SkRef(cacheBlob);
2112     geometry.fRun = run;
2113     geometry.fSubRun = subRun;
2114     geometry.fColor = subRunColor;
2115     geometry.fTransX = transX;
2116     geometry.fTransY = transY;
2117     batch->init();
2118 
2119     return batch;
2120 }
2121 
flushRun(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,BitmapTextBlob * cacheBlob,int run,GrColor color,SkScalar transX,SkScalar transY,const SkPaint & skPaint)2122 inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
2123                                          BitmapTextBlob* cacheBlob, int run, GrColor color,
2124                                          SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
2125     for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2126         const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2127         int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2128         if (0 == glyphCount) {
2129             continue;
2130         }
2131 
2132         SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2133                                                               subRun, color, transX, transY,
2134                                                               skPaint));
2135         target->drawBatch(pipelineBuilder, batch);
2136     }
2137 }
2138 
flushBigGlyphs(BitmapTextBlob * cacheBlob,GrRenderTarget * rt,const SkPaint & skPaint,SkScalar transX,SkScalar transY,const SkIRect & clipBounds)2139 inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
2140                                                const SkPaint& skPaint,
2141                                                SkScalar transX, SkScalar transY,
2142                                                const SkIRect& clipBounds) {
2143     if (!cacheBlob->fBigGlyphs.count()) {
2144         return;
2145     }
2146 
2147     SkMatrix pathMatrix;
2148     if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2149         SkDebugf("could not invert viewmatrix\n");
2150         return;
2151     }
2152 
2153     for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
2154         BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
2155         bigGlyph.fVx += transX;
2156         bigGlyph.fVy += transY;
2157         SkMatrix translate = cacheBlob->fViewMatrix;
2158         translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2159 
2160         fGpuDevice->internalDrawPath(bigGlyph.fPath, skPaint, translate, &pathMatrix, clipBounds,
2161                                      false);
2162     }
2163 }
2164 
flush(GrDrawTarget * target,const SkTextBlob * blob,BitmapTextBlob * cacheBlob,GrRenderTarget * rt,const SkPaint & skPaint,const GrPaint & grPaint,SkDrawFilter * drawFilter,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & clipBounds,SkScalar x,SkScalar y,SkScalar transX,SkScalar transY)2165 void GrAtlasTextContext::flush(GrDrawTarget* target,
2166                                const SkTextBlob* blob,
2167                                BitmapTextBlob* cacheBlob,
2168                                GrRenderTarget* rt,
2169                                const SkPaint& skPaint,
2170                                const GrPaint& grPaint,
2171                                SkDrawFilter* drawFilter,
2172                                const GrClip& clip,
2173                                const SkMatrix& viewMatrix,
2174                                const SkIRect& clipBounds,
2175                                SkScalar x, SkScalar y,
2176                                SkScalar transX, SkScalar transY) {
2177     // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
2178     // it as paths
2179     GrPipelineBuilder pipelineBuilder;
2180     pipelineBuilder.setFromPaint(grPaint, rt, clip);
2181 
2182     GrColor color = grPaint.getColor();
2183 
2184     SkTextBlob::RunIterator it(blob);
2185     for (int run = 0; !it.done(); it.next(), run++) {
2186         if (cacheBlob->fRuns[run].fDrawAsPaths) {
2187             this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
2188             continue;
2189         }
2190         cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
2191         this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
2192     }
2193 
2194     // Now flush big glyphs
2195     this->flushBigGlyphs(cacheBlob, rt, skPaint, transX, transY, clipBounds);
2196 }
2197 
flush(GrDrawTarget * target,BitmapTextBlob * cacheBlob,GrRenderTarget * rt,const SkPaint & skPaint,const GrPaint & grPaint,const GrClip & clip,const SkIRect & clipBounds)2198 void GrAtlasTextContext::flush(GrDrawTarget* target,
2199                                BitmapTextBlob* cacheBlob,
2200                                GrRenderTarget* rt,
2201                                const SkPaint& skPaint,
2202                                const GrPaint& grPaint,
2203                                const GrClip& clip,
2204                                const SkIRect& clipBounds) {
2205     GrPipelineBuilder pipelineBuilder;
2206     pipelineBuilder.setFromPaint(grPaint, rt, clip);
2207 
2208     GrColor color = grPaint.getColor();
2209     for (int run = 0; run < cacheBlob->fRunCount; run++) {
2210         this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
2211     }
2212 
2213     // Now flush big glyphs
2214     this->flushBigGlyphs(cacheBlob, rt, skPaint, 0, 0, clipBounds);
2215 }
2216 
2217 ///////////////////////////////////////////////////////////////////////////////////////////////////
2218 
2219 #ifdef GR_TEST_UTILS
2220 
BATCH_TEST_DEFINE(TextBlobBatch)2221 BATCH_TEST_DEFINE(TextBlobBatch) {
2222     static uint32_t gContextID = SK_InvalidGenID;
2223     static GrAtlasTextContext* gTextContext = NULL;
2224     static SkDeviceProperties gDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType);
2225 
2226     if (context->uniqueID() != gContextID) {
2227         gContextID = context->uniqueID();
2228         SkDELETE(gTextContext);
2229         // We don't yet test the fall back to paths in the GrTextContext base class.  This is mostly
2230         // because we don't really want to have a gpu device here.
2231         // We enable distance fields by twiddling a knob on the paint
2232         gTextContext = GrAtlasTextContext::Create(context, NULL, gDeviceProperties, false);
2233     }
2234 
2235     // create dummy render target
2236     GrSurfaceDesc desc;
2237     desc.fFlags = kRenderTarget_GrSurfaceFlag;
2238     desc.fWidth = 1024;
2239     desc.fHeight = 1024;
2240     desc.fConfig = kRGBA_8888_GrPixelConfig;
2241     desc.fSampleCnt = 0;
2242     SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2243     SkASSERT(texture);
2244     SkASSERT(NULL != texture->asRenderTarget());
2245     GrRenderTarget* rt = texture->asRenderTarget();
2246 
2247     // Setup dummy SkPaint / GrPaint
2248     GrColor color = GrRandomColor(random);
2249     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
2250     SkPaint skPaint;
2251     skPaint.setDistanceFieldTextTEMP(random->nextBool());
2252     skPaint.setColor(color);
2253     skPaint.setLCDRenderText(random->nextBool());
2254     skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2255     skPaint.setSubpixelText(random->nextBool());
2256 
2257     GrPaint grPaint;
2258     if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2259         SkFAIL("couldn't convert paint\n");
2260     }
2261 
2262     const char* text = "The quick brown fox jumps over the lazy dog.";
2263     int textLen = (int)strlen(text);
2264 
2265     // Setup clip
2266     GrClip clip;
2267     SkIRect noClip = SkIRect::MakeLargest();
2268 
2269     // right now we don't handle textblobs, nor do we handle drawPosText.  Since we only
2270     // intend to test the batch with this unit test, that is okay.
2271     SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
2272             gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2273                                              static_cast<size_t>(textLen), 0, 0, noClip));
2274 
2275     SkScalar transX = static_cast<SkScalar>(random->nextU());
2276     SkScalar transY = static_cast<SkScalar>(random->nextU());
2277     const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
2278     return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2279 }
2280 
2281 #endif
2282