blob: c908137477672544f727e5ab31839a90c6547b1c [file] [log] [blame]
joshualitt1d89e8d2015-04-01 12:40:54 -07001/*
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
joshualitt1d89e8d2015-04-01 12:40:54 -07009#include "GrBatch.h"
10#include "GrBatchFontCache.h"
11#include "GrBatchTarget.h"
joshualitt79dfb2b2015-05-11 08:58:08 -070012#include "GrBatchTest.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070013#include "GrDefaultGeoProcFactory.h"
14#include "GrDrawTarget.h"
15#include "GrFontScaler.h"
16#include "GrIndexBuffer.h"
bsalomoned0bcad2015-05-04 10:36:42 -070017#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070018#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070019#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070020#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070021#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070022
23#include "SkAutoKern.h"
24#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070025#include "SkColorFilter.h"
26#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070027#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"
joshualitt9bd2daf2015-04-17 09:30:06 -070040#include "effects/GrDistanceFieldGeoProc.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070041
42namespace {
43static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
44
45// position + local coord
46static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
47
48static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
49
joshualitt9bd2daf2015-04-17 09:30:06 -070050static const int kMinDFFontSize = 18;
51static const int kSmallDFFontSize = 32;
52static const int kSmallDFFontLimit = 32;
53static const int kMediumDFFontSize = 72;
54static const int kMediumDFFontLimit = 72;
55static const int kLargeDFFontSize = 162;
joshualitta7c63892015-04-21 13:24:37 -070056static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
joshualitt9bd2daf2015-04-17 09:30:06 -070057
58SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
59static const int kDistanceAdjustLumShift = 5;
60
joshualitt1d89e8d2015-04-01 12:40:54 -070061static const int kVerticesPerGlyph = 4;
62static const int kIndicesPerGlyph = 6;
63
64static size_t get_vertex_stride(GrMaskFormat maskFormat) {
65 switch (maskFormat) {
66 case kA8_GrMaskFormat:
67 return kGrayTextVASize;
68 case kARGB_GrMaskFormat:
69 return kColorTextVASize;
70 default:
71 return kLCDTextVASize;
72 }
73}
74
joshualitt9bd2daf2015-04-17 09:30:06 -070075static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
76 SkASSERT(maskFormat == kA8_GrMaskFormat);
77 if (useLCDText) {
78 return kLCDTextVASize;
79 } else {
80 return kGrayTextVASize;
81 }
82}
83
84static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
85 unsigned r = SkColorGetR(c);
86 unsigned g = SkColorGetG(c);
87 unsigned b = SkColorGetB(c);
88 return GrColorPackRGBA(r, g, b, 0xff);
89}
90
joshualitt1d89e8d2015-04-01 12:40:54 -070091};
92
93// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -070094// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -070095
joshualittdbd35932015-04-02 09:19:04 -070096GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
97 SkGpuDevice* gpuDevice,
joshualitt9bd2daf2015-04-17 09:30:06 -070098 const SkDeviceProperties& properties,
99 bool enableDistanceFields)
100 : INHERITED(context, gpuDevice, properties)
101 , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
joshualittb7133be2015-04-08 09:08:31 -0700102 // 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);
joshualitt1d89e8d2015-04-01 12:40:54 -0700106 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700107 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700108
109#if SK_FORCE_DISTANCE_FIELD_TEXT
110 fEnableDFRendering = true;
111#else
112 fEnableDFRendering = enableDistanceFields;
113#endif
114}
115
116void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
117
118 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
119 // text. The mask gamma hack is based off of guessing what the blend color is going to
120 // be, and adjusting the mask so that when run through the linear blend will
121 // produce the value closest to the desired result. However, in practice this means
122 // that the 'adjusted' mask is just increasing or decreasing the coverage of
123 // the mask depending on what it is thought it will blit against. For black (on
124 // assumed white) this means that coverages are decreased (on a curve). For white (on
125 // assumed black) this means that coverages are increased (on a a curve). At
126 // middle (perceptual) gray (which could be blit against anything) the coverages
127 // remain the same.
128 //
129 // The idea here is that instead of determining the initial (real) coverage and
130 // then adjusting that coverage, we determine an adjusted coverage directly by
131 // essentially manipulating the geometry (in this case, the distance to the glyph
132 // edge). So for black (on assumed white) this thins a bit; for white (on
133 // assumed black) this fake bolds the geometry a bit.
134 //
135 // The distance adjustment is calculated by determining the actual coverage value which
136 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
137 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
138 // actual edge. So by subtracting this distance adjustment and computing without the
139 // the coverage adjustment we should get 0.5 coverage at the same point.
140 //
141 // This has several implications:
142 // For non-gray lcd smoothed text, each subpixel essentially is using a
143 // slightly different geometry.
144 //
145 // For black (on assumed white) this may not cover some pixels which were
146 // previously covered; however those pixels would have been only slightly
147 // covered and that slight coverage would have been decreased anyway. Also, some pixels
148 // which were previously fully covered may no longer be fully covered.
149 //
150 // For white (on assumed black) this may cover some pixels which weren't
151 // previously covered at all.
152
153 int width, height;
154 size_t size;
155
156#ifdef SK_GAMMA_CONTRAST
157 SkScalar contrast = SK_GAMMA_CONTRAST;
158#else
159 SkScalar contrast = 0.5f;
160#endif
161 SkScalar paintGamma = gamma;
162 SkScalar deviceGamma = gamma;
163
164 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
165 &width, &height);
166
167 SkASSERT(kExpectedDistanceAdjustTableSize == height);
168 fTable = SkNEW_ARRAY(SkScalar, height);
169
170 SkAutoTArray<uint8_t> data((int)size);
171 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
172
173 // find the inverse points where we cross 0.5
174 // binsearch might be better, but we only need to do this once on creation
175 for (int row = 0; row < height; ++row) {
176 uint8_t* rowPtr = data.get() + row*width;
177 for (int col = 0; col < width - 1; ++col) {
178 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
179 // compute point where a mask value will give us a result of 0.5
180 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
181 float borderAlpha = (col + interp) / 255.f;
182
183 // compute t value for that alpha
184 // this is an approximate inverse for smoothstep()
185 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
186
187 // compute distance which gives us that t value
188 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
189 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
190
191 fTable[row] = d;
192 break;
193 }
194 }
195 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700196}
197
joshualittdbd35932015-04-02 09:19:04 -0700198GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
199 SkGpuDevice* gpuDevice,
joshualitt9bd2daf2015-04-17 09:30:06 -0700200 const SkDeviceProperties& props,
201 bool enableDistanceFields) {
202 return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
joshualitt1d89e8d2015-04-01 12:40:54 -0700203}
204
joshualittdbd35932015-04-02 09:19:04 -0700205bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
206 const GrClip&,
207 const GrPaint&,
208 const SkPaint& skPaint,
209 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700210 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
211 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700212}
213
joshualitt9e36c1a2015-04-14 12:17:27 -0700214GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
215 GrColor canonicalColor = paint.computeLuminanceColor();
216 if (lcd) {
217 // This is the correct computation, but there are tons of cases where LCD can be overridden.
218 // For now we just regenerate if any run in a textblob has LCD.
219 // TODO figure out where all of these overrides are and see if we can incorporate that logic
220 // at a higher level *OR* use sRGB
221 SkASSERT(false);
222 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
223 } else {
224 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
225 // gamma corrected masks anyways, nor color
226 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
227 SkColorGetG(canonicalColor),
228 SkColorGetB(canonicalColor));
229 // reduce to our finite number of bits
230 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
231 }
232 return canonicalColor;
233}
234
235// TODO if this function ever shows up in profiling, then we can compute this value when the
236// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
237// run so this is not a big deal to compute here.
238bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
239 SkTextBlob::RunIterator it(blob);
240 for (; !it.done(); it.next()) {
241 if (it.isLCD()) {
242 return true;
243 }
244 }
245 return false;
246}
247
joshualitt2a0e9f32015-04-13 06:12:21 -0700248bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
249 const BitmapTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700250 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700251 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700252 // 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()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700255 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
joshualitt53b5f442015-04-13 06:33:59 -0700266 // 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
joshualittfcfb9fc2015-04-21 07:35:10 -0700282 // 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()) {
joshualitt473ffa12015-04-22 18:23:15 -0700285 // 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 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700289 return true;
290 }
291
joshualittfcfb9fc2015-04-21 07:35:10 -0700292 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700293 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
joshualittfcfb9fc2015-04-21 07:35:10 -0700300 // 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();
joshualittf0c000d2015-04-27 09:36:55 -0700312 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700313 return true;
314 }
315
joshualittfcfb9fc2015-04-21 07:35:10 -0700316 (*outTransX) = transX;
317 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700318 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700319 // 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;
joshualittfcfb9fc2015-04-21 07:35:10 -0700330 }
joshualitta7c63892015-04-21 13:24:37 -0700331 // 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
joshualittfcfb9fc2015-04-21 07:35:10 -0700334
joshualitt2a0e9f32015-04-13 06:12:21 -0700335 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700336}
337
338
joshualittdbd35932015-04-02 09:19:04 -0700339inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
340 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700341 const SkMatrix* viewMatrix,
342 bool noGamma) {
343 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700344 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
345 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
346}
347
joshualittdbd35932015-04-02 09:19:04 -0700348void 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) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700352 // If we have been abandoned, then don't draw
353 if (!fContext->getTextTarget()) {
354 return;
355 }
356
joshualitt2a0e9f32015-04-13 06:12:21 -0700357 SkAutoTUnref<BitmapTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700358 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();
joshualitt2a0e9f32015-04-13 06:12:21 -0700363 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700364 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700365 drawFilter);
366
367 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700368 bool hasLCD = HasLCD(blob);
369 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
370 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
371 // ensure we always match the same key
372 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
373 ComputeCanonicalColor(skPaint, hasLCD);
374
joshualitt53b5f442015-04-13 06:33:59 -0700375 key.fUniqueID = blob->uniqueID();
376 key.fStyle = skPaint.getStyle();
377 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700378 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700379 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700380 }
381
joshualitt1d89e8d2015-04-01 12:40:54 -0700382 SkIRect clipRect;
383 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
384
joshualitt2a0e9f32015-04-13 06:12:21 -0700385 SkScalar transX = 0.f;
386 SkScalar transY = 0.f;
387
joshualitt9e36c1a2015-04-14 12:17:27 -0700388 // Though for the time being runs in the textblob can override the paint, they only touch font
389 // info.
390 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700391 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
392 return;
393 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700394
joshualittb7133be2015-04-08 09:08:31 -0700395 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700396 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700397 // We have to remake the blob because changes may invalidate our masks.
398 // TODO we could probably get away reuse most of the time if the pointer is unique,
399 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700400 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700401 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
402 kGrayTextVASize)));
joshualitt9e36c1a2015-04-14 12:17:27 -0700403 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700404 drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700405 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700406 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
407 // offsets
joshualitt2a0e9f32015-04-13 06:12:21 -0700408 cacheBlob->fViewMatrix = viewMatrix;
409 cacheBlob->fX = x;
410 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700411 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700412 }
413 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700414 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700415 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
416 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700417 } else {
418 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
419 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700420 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700421 drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700422 }
423
joshualitt9e36c1a2015-04-14 12:17:27 -0700424 cacheBlob->fPaintColor = skPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -0700425 this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700426 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700427}
428
joshualitt9bd2daf2015-04-17 09:30:06 -0700429inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
430 const SkMatrix& viewMatrix) {
431 // TODO: support perspective (need getMaxScale replacement)
432 if (viewMatrix.hasPerspective()) {
433 return false;
434 }
435
436 SkScalar maxScale = viewMatrix.getMaxScale();
437 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
438 // Hinted text looks far better at small resolutions
439 // Scaling up beyond 2x yields undesireable artifacts
joshualitta7c63892015-04-21 13:24:37 -0700440 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700441 return false;
442 }
443
444 if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
445 scaledTextSize < kLargeDFFontSize) {
446 return false;
447 }
448
449 // rasterizers and mask filters modify alpha, which doesn't
450 // translate well to distance
451 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
jvanverthe9c0fc62015-04-29 11:18:05 -0700452 !fContext->getTextTarget()->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700453 return false;
454 }
455
456 // TODO: add some stroking support
457 if (skPaint.getStyle() != SkPaint::kFill_Style) {
458 return false;
459 }
460
461 return true;
462}
463
joshualittdbd35932015-04-02 09:19:04 -0700464void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700465 const SkPaint& skPaint, GrColor color,
466 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700467 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700468 SkDrawFilter* drawFilter, const SkIRect& clipRect,
469 GrRenderTarget* rt, const GrClip& clip,
470 const GrPaint& paint) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700471 cacheBlob->fViewMatrix = viewMatrix;
472 cacheBlob->fX = x;
473 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700474
475 // Regenerate textblob
476 SkPaint runPaint = skPaint;
477 SkTextBlob::RunIterator it(blob);
478 for (int run = 0; !it.done(); it.next(), run++) {
479 int glyphCount = it.glyphCount();
480 size_t textLen = glyphCount * sizeof(uint16_t);
481 const SkPoint& offset = it.offset();
482 // applyFontToPaint() always overwrites the exact same attributes,
483 // so it is safe to not re-seed the paint for this reason.
484 it.applyFontToPaint(&runPaint);
485
486 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
487 // A false return from filter() means we should abort the current draw.
488 runPaint = skPaint;
489 continue;
490 }
491
492 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
493
joshualitt1d89e8d2015-04-01 12:40:54 -0700494 // setup vertex / glyphIndex for the new run
495 if (run > 0) {
496 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
497 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
498
499 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
500 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
501
502 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
503 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
504 }
505
joshualittfcfb9fc2015-04-21 07:35:10 -0700506 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
507 cacheBlob->setHasDistanceField();
508 SkPaint dfPaint = runPaint;
509 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700510 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700511 Run& runIdx = cacheBlob->fRuns[run];
512 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
513 subRun.fUseLCDText = runPaint.isLCDRenderText();
514 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700515
joshualittfcfb9fc2015-04-21 07:35:10 -0700516 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
517
518 SkTDArray<char> fallbackTxt;
519 SkTDArray<SkScalar> fallbackPos;
520 SkPoint dfOffset;
521 int scalarsPerPosition = 2;
522 switch (it.positioning()) {
523 case SkTextBlob::kDefault_Positioning: {
524 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
525 (const char *)it.glyphs(), textLen,
526 x + offset.x(), y + offset.y(), clipRect, textRatio,
527 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
528 break;
529 }
530 case SkTextBlob::kHorizontal_Positioning: {
531 scalarsPerPosition = 1;
532 dfOffset = SkPoint::Make(x, y + offset.y());
533 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
534 (const char*)it.glyphs(), textLen, it.pos(),
535 scalarsPerPosition, dfOffset, clipRect, textRatio,
536 &fallbackTxt, &fallbackPos);
537 break;
538 }
539 case SkTextBlob::kFull_Positioning: {
540 dfOffset = SkPoint::Make(x, y);
541 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
542 (const char*)it.glyphs(), textLen, it.pos(),
543 scalarsPerPosition, dfOffset, clipRect, textRatio,
544 &fallbackTxt, &fallbackPos);
545 break;
546 }
547 }
548 if (fallbackTxt.count()) {
549 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
550 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
551 clipRect);
552 }
553
554 SkGlyphCache::AttachCache(cache);
555 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
556 cacheBlob->fRuns[run].fDrawAsPaths = true;
557 } else {
558 cacheBlob->setHasBitmap();
559 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
560 false);
561 switch (it.positioning()) {
562 case SkTextBlob::kDefault_Positioning:
563 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
564 (const char *)it.glyphs(), textLen,
565 x + offset.x(), y + offset.y(), clipRect);
566 break;
567 case SkTextBlob::kHorizontal_Positioning:
568 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
569 (const char*)it.glyphs(), textLen, it.pos(), 1,
570 SkPoint::Make(x, y + offset.y()), clipRect);
571 break;
572 case SkTextBlob::kFull_Positioning:
573 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
574 (const char*)it.glyphs(), textLen, it.pos(), 2,
575 SkPoint::Make(x, y), clipRect);
576 break;
577 }
578 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700579 }
580
581 if (drawFilter) {
582 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
583 runPaint = skPaint;
584 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700585 }
586}
587
joshualitt64c99cc2015-04-21 09:43:03 -0700588inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
589 SkPaint* skPaint,
590 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700591 const SkMatrix& viewMatrix) {
592 // getMaxScale doesn't support perspective, so neither do we at the moment
593 SkASSERT(!viewMatrix.hasPerspective());
594 SkScalar maxScale = viewMatrix.getMaxScale();
595 SkScalar textSize = skPaint->getTextSize();
596 SkScalar scaledTextSize = textSize;
597 // if we have non-unity scale, we need to choose our base text size
598 // based on the SkPaint's text size multiplied by the max scale factor
599 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
600 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
601 scaledTextSize *= maxScale;
602 }
603
joshualitt64c99cc2015-04-21 09:43:03 -0700604 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
605 // and ceiling. A scale outside of this range would require regenerating the distance fields
606 SkScalar dfMaskScaleFloor;
607 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700608 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700609 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700610 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700611 *textRatio = textSize / kSmallDFFontSize;
612 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
613 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700614 dfMaskScaleFloor = kSmallDFFontLimit;
615 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700616 *textRatio = textSize / kMediumDFFontSize;
617 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
618 } else {
joshualitta7c63892015-04-21 13:24:37 -0700619 dfMaskScaleFloor = kMediumDFFontLimit;
620 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700621 *textRatio = textSize / kLargeDFFontSize;
622 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
623 }
624
joshualitt64c99cc2015-04-21 09:43:03 -0700625 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
626 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
627 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
628 // tolerate before we'd have to move to a large mip size. When we actually test these values
629 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
630 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
631 // level)
joshualitta7c63892015-04-21 13:24:37 -0700632 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700633 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
634 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
635
joshualitt9bd2daf2015-04-17 09:30:06 -0700636 skPaint->setLCDRenderText(false);
637 skPaint->setAutohinted(false);
638 skPaint->setHinting(SkPaint::kNormal_Hinting);
639 skPaint->setSubpixelText(true);
640}
641
joshualittfec19e12015-04-17 10:32:32 -0700642inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700643 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700644 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700645 const GrPaint& paint,
646 const SkPaint& skPaint,
647 const SkMatrix& viewMatrix,
648 const SkTDArray<char>& fallbackTxt,
649 const SkTDArray<SkScalar>& fallbackPos,
650 int scalarsPerPosition,
651 const SkPoint& offset,
652 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700653 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700654 blob->setHasBitmap();
655 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700656 // Push back a new subrun to fill and set the override descriptor
657 run.push_back();
658 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
659 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
joshualittfec19e12015-04-17 10:32:32 -0700660 &fDeviceProperties, &viewMatrix, false);
661 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700662 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700663 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700664 fallbackTxt.begin(), fallbackTxt.count(),
665 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
666 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700667}
668
669inline GrAtlasTextContext::BitmapTextBlob*
670GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
671 const SkMatrix& viewMatrix, SkGlyphCache** cache,
672 SkPaint* dfPaint, SkScalar* textRatio) {
673 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
674
675 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700676 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700677 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700678 Run& run = blob->fRuns[0];
679 PerSubRunInfo& subRun = run.fSubRunInfo.back();
680 subRun.fUseLCDText = origPaint.isLCDRenderText();
681 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700682
683 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
684 return blob;
685}
686
joshualitt79dfb2b2015-05-11 08:58:08 -0700687inline GrAtlasTextContext::BitmapTextBlob*
688GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
689 const GrPaint& paint, const SkPaint& skPaint,
690 const SkMatrix& viewMatrix,
691 const char text[], size_t byteLength,
692 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700693 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700694 SkIRect clipRect;
695 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
696
joshualitt79dfb2b2015-05-11 08:58:08 -0700697 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700698 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
699 SkPaint dfPaint;
700 SkScalar textRatio;
701 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700702 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700703
joshualitt9bd2daf2015-04-17 09:30:06 -0700704 SkTDArray<char> fallbackTxt;
705 SkTDArray<SkScalar> fallbackPos;
706 SkPoint offset;
707 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
708 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
709 &offset, skPaint);
710 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700711 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700712 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700713 fallbackPos, 2, offset, clipRect);
714 }
715 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700716 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700717 blob->fViewMatrix = viewMatrix;
718
719 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
720 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
721 byteLength, x, y, clipRect);
722 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700723 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700724 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700725}
726
joshualitt79dfb2b2015-05-11 08:58:08 -0700727inline GrAtlasTextContext::BitmapTextBlob*
728GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
729 const GrPaint& paint, const SkPaint& skPaint,
730 const SkMatrix& viewMatrix,
731 const char text[], size_t byteLength,
732 const SkScalar pos[], int scalarsPerPosition,
733 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700734 int glyphCount = skPaint.countText(text, byteLength);
735
736 SkIRect clipRect;
737 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
738
joshualitt79dfb2b2015-05-11 08:58:08 -0700739 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700740 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
741 SkPaint dfPaint;
742 SkScalar textRatio;
743 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700744 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700745
746 SkTDArray<char> fallbackTxt;
747 SkTDArray<SkScalar> fallbackPos;
748 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
749 byteLength, pos, scalarsPerPosition, offset, clipRect,
750 textRatio, &fallbackTxt, &fallbackPos);
751 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700752 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700753 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700754 fallbackPos, scalarsPerPosition, offset, clipRect);
755 }
756 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700757 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700758 blob->fViewMatrix = viewMatrix;
759 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
760 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
761 byteLength, pos, scalarsPerPosition, offset, clipRect);
762 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700763 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700764 return blob;
765}
766
767void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
768 const GrPaint& paint, const SkPaint& skPaint,
769 const SkMatrix& viewMatrix,
770 const char text[], size_t byteLength,
771 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
772 SkAutoTUnref<BitmapTextBlob> blob(
773 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
774 text, byteLength, x, y, regionClipBounds));
775 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
776}
777
778void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
779 const GrPaint& paint, const SkPaint& skPaint,
780 const SkMatrix& viewMatrix,
781 const char text[], size_t byteLength,
782 const SkScalar pos[], int scalarsPerPosition,
783 const SkPoint& offset, const SkIRect& regionClipBounds) {
784 SkAutoTUnref<BitmapTextBlob> blob(
785 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
786 text, byteLength,
787 pos, scalarsPerPosition,
788 offset, regionClipBounds));
789
790 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
joshualitt9bd2daf2015-04-17 09:30:06 -0700791}
792
793void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
794 SkGlyphCache* cache, const SkPaint& skPaint,
795 GrColor color,
796 const SkMatrix& viewMatrix,
797 const char text[], size_t byteLength,
798 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700799 SkASSERT(byteLength == 0 || text != NULL);
800
801 // nothing to draw
802 if (text == NULL || byteLength == 0) {
803 return;
804 }
805
806 fCurrStrike = NULL;
807 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
808
809 // Get GrFontScaler from cache
810 GrFontScaler* fontScaler = GetGrFontScaler(cache);
811
812 // transform our starting point
813 {
814 SkPoint loc;
815 viewMatrix.mapXY(x, y, &loc);
816 x = loc.fX;
817 y = loc.fY;
818 }
819
820 // need to measure first
821 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
822 SkVector stopVector;
823 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
824
825 SkScalar stopX = stopVector.fX;
826 SkScalar stopY = stopVector.fY;
827
828 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
829 stopX = SkScalarHalf(stopX);
830 stopY = SkScalarHalf(stopY);
831 }
832 x -= stopX;
833 y -= stopY;
834 }
835
836 const char* stop = text + byteLength;
837
838 SkAutoKern autokern;
839
840 SkFixed fxMask = ~0;
841 SkFixed fyMask = ~0;
842 SkScalar halfSampleX, halfSampleY;
843 if (cache->isSubpixel()) {
844 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
845 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
846 if (kX_SkAxisAlignment == baseline) {
847 fyMask = 0;
848 halfSampleY = SK_ScalarHalf;
849 } else if (kY_SkAxisAlignment == baseline) {
850 fxMask = 0;
851 halfSampleX = SK_ScalarHalf;
852 }
853 } else {
854 halfSampleX = halfSampleY = SK_ScalarHalf;
855 }
856
857 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
858 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
859
860 while (text < stop) {
861 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
862
863 fx += autokern.adjust(glyph);
864
865 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700866 this->bmpAppendGlyph(blob,
867 runIndex,
868 GrGlyph::Pack(glyph.getGlyphID(),
869 glyph.getSubXFixed(),
870 glyph.getSubYFixed(),
871 GrGlyph::kCoverage_MaskStyle),
872 Sk48Dot16FloorToInt(fx),
873 Sk48Dot16FloorToInt(fy),
874 color,
875 fontScaler,
876 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700877 }
878
879 fx += glyph.fAdvanceX;
880 fy += glyph.fAdvanceY;
881 }
882}
883
joshualitt9bd2daf2015-04-17 09:30:06 -0700884void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
885 SkGlyphCache* cache, const SkPaint& skPaint,
886 GrColor color,
887 const SkMatrix& viewMatrix,
888 const char text[], size_t byteLength,
889 const SkScalar pos[], int scalarsPerPosition,
890 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700891 SkASSERT(byteLength == 0 || text != NULL);
892 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
893
894 // nothing to draw
895 if (text == NULL || byteLength == 0) {
896 return;
897 }
898
899 fCurrStrike = NULL;
900 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
901
902 // Get GrFontScaler from cache
903 GrFontScaler* fontScaler = GetGrFontScaler(cache);
904
905 const char* stop = text + byteLength;
906 SkTextAlignProc alignProc(skPaint.getTextAlign());
907 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
908
909 if (cache->isSubpixel()) {
910 // maybe we should skip the rounding if linearText is set
911 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
912
913 SkFixed fxMask = ~0;
914 SkFixed fyMask = ~0;
915 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
916 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
917 if (kX_SkAxisAlignment == baseline) {
918 fyMask = 0;
919 halfSampleY = SK_ScalarHalf;
920 } else if (kY_SkAxisAlignment == baseline) {
921 fxMask = 0;
922 halfSampleX = SK_ScalarHalf;
923 }
924
925 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
926 while (text < stop) {
927 SkPoint tmsLoc;
928 tmsProc(pos, &tmsLoc);
929 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
930 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
931
932 const SkGlyph& glyph = glyphCacheProc(cache, &text,
933 fx & fxMask, fy & fyMask);
934
935 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700936 this->bmpAppendGlyph(blob,
937 runIndex,
938 GrGlyph::Pack(glyph.getGlyphID(),
939 glyph.getSubXFixed(),
940 glyph.getSubYFixed(),
941 GrGlyph::kCoverage_MaskStyle),
942 Sk48Dot16FloorToInt(fx),
943 Sk48Dot16FloorToInt(fy),
944 color,
945 fontScaler,
946 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700947 }
948 pos += scalarsPerPosition;
949 }
950 } else {
951 while (text < stop) {
952 const char* currentText = text;
953 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
954
955 if (metricGlyph.fWidth) {
956 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
957 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
958 SkPoint tmsLoc;
959 tmsProc(pos, &tmsLoc);
960 SkPoint alignLoc;
961 alignProc(tmsLoc, metricGlyph, &alignLoc);
962
963 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
964 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
965
966 // have to call again, now that we've been "aligned"
967 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
968 fx & fxMask, fy & fyMask);
969 // the assumption is that the metrics haven't changed
970 SkASSERT(prevAdvX == glyph.fAdvanceX);
971 SkASSERT(prevAdvY == glyph.fAdvanceY);
972 SkASSERT(glyph.fWidth);
973
joshualitt9bd2daf2015-04-17 09:30:06 -0700974 this->bmpAppendGlyph(blob,
975 runIndex,
976 GrGlyph::Pack(glyph.getGlyphID(),
977 glyph.getSubXFixed(),
978 glyph.getSubYFixed(),
979 GrGlyph::kCoverage_MaskStyle),
980 Sk48Dot16FloorToInt(fx),
981 Sk48Dot16FloorToInt(fy),
982 color,
983 fontScaler,
984 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700985 }
986 pos += scalarsPerPosition;
987 }
988 }
989 } else { // not subpixel
990
991 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
992 while (text < stop) {
993 // the last 2 parameters are ignored
994 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
995
996 if (glyph.fWidth) {
997 SkPoint tmsLoc;
998 tmsProc(pos, &tmsLoc);
999
1000 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1001 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001002 this->bmpAppendGlyph(blob,
1003 runIndex,
1004 GrGlyph::Pack(glyph.getGlyphID(),
1005 glyph.getSubXFixed(),
1006 glyph.getSubYFixed(),
1007 GrGlyph::kCoverage_MaskStyle),
1008 Sk48Dot16FloorToInt(fx),
1009 Sk48Dot16FloorToInt(fy),
1010 color,
1011 fontScaler,
1012 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001013 }
1014 pos += scalarsPerPosition;
1015 }
1016 } else {
1017 while (text < stop) {
1018 // the last 2 parameters are ignored
1019 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1020
1021 if (glyph.fWidth) {
1022 SkPoint tmsLoc;
1023 tmsProc(pos, &tmsLoc);
1024
1025 SkPoint alignLoc;
1026 alignProc(tmsLoc, glyph, &alignLoc);
1027
1028 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1029 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001030 this->bmpAppendGlyph(blob,
1031 runIndex,
1032 GrGlyph::Pack(glyph.getGlyphID(),
1033 glyph.getSubXFixed(),
1034 glyph.getSubYFixed(),
1035 GrGlyph::kCoverage_MaskStyle),
1036 Sk48Dot16FloorToInt(fx),
1037 Sk48Dot16FloorToInt(fy),
1038 color,
1039 fontScaler,
1040 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001041 }
1042 pos += scalarsPerPosition;
1043 }
1044 }
1045 }
1046}
1047
joshualitt9bd2daf2015-04-17 09:30:06 -07001048
1049void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1050 SkGlyphCache* cache, const SkPaint& skPaint,
1051 GrColor color,
1052 const SkMatrix& viewMatrix,
1053 const char text[], size_t byteLength,
1054 SkScalar x, SkScalar y, const SkIRect& clipRect,
1055 SkScalar textRatio,
1056 SkTDArray<char>* fallbackTxt,
1057 SkTDArray<SkScalar>* fallbackPos,
1058 SkPoint* offset,
1059 const SkPaint& origPaint) {
1060 SkASSERT(byteLength == 0 || text != NULL);
1061
1062 // nothing to draw
1063 if (text == NULL || byteLength == 0) {
1064 return;
1065 }
1066
1067 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1068 SkAutoDescriptor desc;
1069 origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
1070 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1071 desc.getDesc());
1072
1073 SkTArray<SkScalar> positions;
1074
1075 const char* textPtr = text;
1076 SkFixed stopX = 0;
1077 SkFixed stopY = 0;
1078 SkFixed origin = 0;
1079 switch (origPaint.getTextAlign()) {
1080 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1081 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1082 case SkPaint::kLeft_Align: origin = 0; break;
1083 }
1084
1085 SkAutoKern autokern;
1086 const char* stop = text + byteLength;
1087 while (textPtr < stop) {
1088 // don't need x, y here, since all subpixel variants will have the
1089 // same advance
1090 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1091
1092 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1093 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1094
1095 SkFixed height = glyph.fAdvanceY;
1096 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1097
1098 stopX += width;
1099 stopY += height;
1100 }
1101 SkASSERT(textPtr == stop);
1102
1103 // now adjust starting point depending on alignment
1104 SkScalar alignX = SkFixedToScalar(stopX);
1105 SkScalar alignY = SkFixedToScalar(stopY);
1106 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1107 alignX = SkScalarHalf(alignX);
1108 alignY = SkScalarHalf(alignY);
1109 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1110 alignX = 0;
1111 alignY = 0;
1112 }
1113 x -= alignX;
1114 y -= alignY;
1115 *offset = SkPoint::Make(x, y);
1116
1117 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1118 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1119 fallbackPos);
1120 SkGlyphCache::AttachCache(origPaintCache);
1121}
1122
1123void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1124 SkGlyphCache* cache, const SkPaint& skPaint,
1125 GrColor color,
1126 const SkMatrix& viewMatrix,
1127 const char text[], size_t byteLength,
1128 const SkScalar pos[], int scalarsPerPosition,
1129 const SkPoint& offset, const SkIRect& clipRect,
1130 SkScalar textRatio,
1131 SkTDArray<char>* fallbackTxt,
1132 SkTDArray<SkScalar>* fallbackPos) {
1133
1134 SkASSERT(byteLength == 0 || text != NULL);
1135 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1136
1137 // nothing to draw
1138 if (text == NULL || byteLength == 0) {
1139 return;
1140 }
1141
1142 fCurrStrike = NULL;
1143
1144 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1145 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1146
1147 const char* stop = text + byteLength;
1148
1149 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1150 while (text < stop) {
1151 const char* lastText = text;
1152 // the last 2 parameters are ignored
1153 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1154
1155 if (glyph.fWidth) {
1156 SkScalar x = offset.x() + pos[0];
1157 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1158
1159 if (!this->dfAppendGlyph(blob,
1160 runIndex,
1161 GrGlyph::Pack(glyph.getGlyphID(),
1162 glyph.getSubXFixed(),
1163 glyph.getSubYFixed(),
1164 GrGlyph::kDistance_MaskStyle),
1165 x, y, color, fontScaler, clipRect,
1166 textRatio, viewMatrix)) {
1167 // couldn't append, send to fallback
1168 fallbackTxt->append(SkToInt(text-lastText), lastText);
1169 *fallbackPos->append() = pos[0];
1170 if (2 == scalarsPerPosition) {
1171 *fallbackPos->append() = pos[1];
1172 }
1173 }
1174 }
1175 pos += scalarsPerPosition;
1176 }
1177 } else {
1178 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1179 : SK_Scalar1;
1180 while (text < stop) {
1181 const char* lastText = text;
1182 // the last 2 parameters are ignored
1183 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1184
1185 if (glyph.fWidth) {
1186 SkScalar x = offset.x() + pos[0];
1187 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1188
1189 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1190 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1191
1192 if (!this->dfAppendGlyph(blob,
1193 runIndex,
1194 GrGlyph::Pack(glyph.getGlyphID(),
1195 glyph.getSubXFixed(),
1196 glyph.getSubYFixed(),
1197 GrGlyph::kDistance_MaskStyle),
1198 x - advanceX, y - advanceY, color,
1199 fontScaler,
1200 clipRect,
1201 textRatio,
1202 viewMatrix)) {
1203 // couldn't append, send to fallback
1204 fallbackTxt->append(SkToInt(text-lastText), lastText);
1205 *fallbackPos->append() = pos[0];
1206 if (2 == scalarsPerPosition) {
1207 *fallbackPos->append() = pos[1];
1208 }
1209 }
1210 }
1211 pos += scalarsPerPosition;
1212 }
1213 }
1214}
1215
1216void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1217 GrGlyph::PackedID packed,
1218 int vx, int vy, GrColor color, GrFontScaler* scaler,
1219 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001220 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001221 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001222 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001223 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001224 }
1225
1226 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001227 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001228 return;
1229 }
1230
1231 int x = vx + glyph->fBounds.fLeft;
1232 int y = vy + glyph->fBounds.fTop;
1233
1234 // keep them as ints until we've done the clip-test
1235 int width = glyph->fBounds.width();
1236 int height = glyph->fBounds.height();
1237
joshualitt2a0e9f32015-04-13 06:12:21 -07001238#if 0
1239 // Not checking the clip bounds might introduce a performance regression. However, its not
1240 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1241 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1242 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1243 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001244 // check if we clipped out
1245 if (clipRect.quickReject(x, y, x + width, y + height)) {
1246 return;
1247 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001248#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001249
1250 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001251 if (glyph->fTooLargeForAtlas) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001252 this->appendGlyphPath(blob, glyph, scaler, vx, vy);
joshualitt1d89e8d2015-04-01 12:40:54 -07001253 return;
1254 }
1255
joshualitt1d89e8d2015-04-01 12:40:54 -07001256 GrMaskFormat format = glyph->fMaskFormat;
1257
1258 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1259 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001260 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001261 }
1262
1263 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001264
1265 size_t vertexStride = get_vertex_stride(format);
1266
1267 SkRect r;
1268 r.fLeft = SkIntToScalar(x);
1269 r.fTop = SkIntToScalar(y);
1270 r.fRight = r.fLeft + SkIntToScalar(width);
1271 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001272 subRun->fMaskFormat = format;
1273 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001274 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001275}
joshualitt1d89e8d2015-04-01 12:40:54 -07001276
joshualitt9bd2daf2015-04-17 09:30:06 -07001277bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1278 GrGlyph::PackedID packed,
1279 SkScalar sx, SkScalar sy, GrColor color,
1280 GrFontScaler* scaler,
1281 const SkIRect& clipRect,
1282 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001283 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001284 if (!fCurrStrike) {
1285 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001286 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001287 }
1288
1289 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001290 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001291 return true;
1292 }
1293
1294 // fallback to color glyph support
1295 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1296 return false;
1297 }
1298
1299 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1300 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1301 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1302 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1303
1304 SkScalar scale = textRatio;
1305 dx *= scale;
1306 dy *= scale;
1307 width *= scale;
1308 height *= scale;
1309 sx += dx;
1310 sy += dy;
1311 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1312
1313#if 0
1314 // check if we clipped out
1315 SkRect dstRect;
1316 viewMatrix.mapRect(&dstRect, glyphRect);
1317 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1318 SkScalarTruncToInt(dstRect.top()),
1319 SkScalarTruncToInt(dstRect.right()),
1320 SkScalarTruncToInt(dstRect.bottom()))) {
1321 return true;
1322 }
1323#endif
1324
1325 // TODO combine with the above
1326 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001327 if (glyph->fTooLargeForAtlas) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001328 this->appendGlyphPath(blob, glyph, scaler, SkScalarRoundToInt(sx - dx),
1329 SkScalarRoundToInt(sy - dy));
1330 return true;
1331 }
1332
joshualitt9bd2daf2015-04-17 09:30:06 -07001333 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1334 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1335 subRun->fMaskFormat = kA8_GrMaskFormat;
1336
1337 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1338
1339 bool useColorVerts = !subRun->fUseLCDText;
1340 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001341 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001342 return true;
1343}
1344
1345inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
1346 GrFontScaler* scaler, int x, int y) {
1347 if (NULL == glyph->fPath) {
1348 SkPath* path = SkNEW(SkPath);
1349 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1350 // flag the glyph as being dead?
1351 SkDELETE(path);
1352 return;
1353 }
1354 glyph->fPath = path;
1355 }
1356 SkASSERT(glyph->fPath);
1357 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1358}
1359
1360inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1361 Run::SubRunInfo* subRun,
1362 const SkRect& positions, GrColor color,
1363 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001364 GrGlyph* glyph) {
1365 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001366 run->fVertexBounds.joinNonEmptyArg(positions);
1367 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001368
1369 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1370
joshualitt9bd2daf2015-04-17 09:30:06 -07001371 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001372 // V0
1373 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1374 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001375 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1376 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001377 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001378
joshualitt010db532015-04-21 10:07:26 -07001379 // V1
1380 position = reinterpret_cast<SkPoint*>(vertex);
1381 position->set(positions.fLeft, positions.fBottom);
1382 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001383 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001384 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001385
joshualitt010db532015-04-21 10:07:26 -07001386 // V2
1387 position = reinterpret_cast<SkPoint*>(vertex);
1388 position->set(positions.fRight, positions.fBottom);
1389 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001390 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001391 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001392
joshualitt010db532015-04-21 10:07:26 -07001393 // V3
1394 position = reinterpret_cast<SkPoint*>(vertex);
1395 position->set(positions.fRight, positions.fTop);
1396 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001397 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001398 } else {
1399 // V0
1400 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1401 position->set(positions.fLeft, positions.fTop);
1402 vertex += vertexStride;
1403
1404 // V1
1405 position = reinterpret_cast<SkPoint*>(vertex);
1406 position->set(positions.fLeft, positions.fBottom);
1407 vertex += vertexStride;
1408
1409 // V2
1410 position = reinterpret_cast<SkPoint*>(vertex);
1411 position->set(positions.fRight, positions.fBottom);
1412 vertex += vertexStride;
1413
1414 // V3
1415 position = reinterpret_cast<SkPoint*>(vertex);
1416 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001417 }
1418
1419 subRun->fGlyphEndIndex++;
1420 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1421}
1422
1423class BitmapTextBatch : public GrBatch {
1424public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001425 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualittdbd35932015-04-02 09:19:04 -07001426 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001427 typedef Blob::Run Run;
1428 typedef Run::SubRunInfo TextInfo;
1429 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001430 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001431 int fRun;
1432 int fSubRun;
1433 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001434 SkScalar fTransX;
1435 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001436 };
1437
joshualittad802c62015-04-15 05:31:57 -07001438 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1439 GrBatchFontCache* fontCache) {
1440 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -07001441 }
1442
joshualitt9bd2daf2015-04-17 09:30:06 -07001443 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1444 GrBatchFontCache* fontCache,
1445 DistanceAdjustTable* distanceAdjustTable,
1446 SkColor filteredColor, bool useLCDText,
1447 bool useBGR, float gamma) {
1448 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
1449 filteredColor, useLCDText, useBGR, gamma));
1450 }
1451
joshualitt1d89e8d2015-04-01 12:40:54 -07001452 const char* name() const override { return "BitmapTextBatch"; }
1453
1454 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1455 if (kARGB_GrMaskFormat == fMaskFormat) {
1456 out->setUnknownFourComponents();
1457 } else {
1458 out->setKnownFourComponents(fBatch.fColor);
1459 }
1460 }
1461
1462 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt9bd2daf2015-04-17 09:30:06 -07001463 if (!fUseDistanceFields) {
1464 // Bitmap Text
1465 if (kARGB_GrMaskFormat != fMaskFormat) {
1466 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
1467 out->setUnknownSingleComponent();
1468 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
1469 out->setUnknownOpaqueFourComponents();
1470 out->setUsingLCDCoverage();
1471 } else {
1472 out->setUnknownFourComponents();
1473 out->setUsingLCDCoverage();
1474 }
1475 } else {
1476 out->setKnownSingleComponent(0xff);
1477 }
1478 } else {
1479 // Distance fields
1480 if (!fUseLCDText) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001481 out->setUnknownSingleComponent();
joshualitt1d89e8d2015-04-01 12:40:54 -07001482 } else {
1483 out->setUnknownFourComponents();
1484 out->setUsingLCDCoverage();
1485 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001486 }
1487 }
1488
1489 void initBatchTracker(const GrPipelineInfo& init) override {
1490 // Handle any color overrides
1491 if (init.fColorIgnored) {
1492 fBatch.fColor = GrColor_ILLEGAL;
1493 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1494 fBatch.fColor = init.fOverrideColor;
1495 }
1496
1497 // setup batch properties
1498 fBatch.fColorIgnored = init.fColorIgnored;
1499 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1500 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1501 }
1502
bsalomonb5238a72015-05-05 07:49:49 -07001503 struct FlushInfo {
1504 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1505 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1506 int fGlyphsToFlush;
1507 int fVertexOffset;
1508 };
1509
joshualitt1d89e8d2015-04-01 12:40:54 -07001510 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1511 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1512 // TODO actually only invert if we don't have RGBA
1513 SkMatrix localMatrix;
1514 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1515 SkDebugf("Cannot invert viewmatrix\n");
1516 return;
1517 }
1518
joshualitt62db8ba2015-04-09 08:22:37 -07001519 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
1520 if (!texture) {
1521 SkDebugf("Could not allocate backing texture for atlas\n");
1522 return;
1523 }
1524
joshualitt9bd2daf2015-04-17 09:30:06 -07001525 SkAutoTUnref<const GrGeometryProcessor> gp;
1526 if (fUseDistanceFields) {
1527 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1528 texture));
1529 } else {
1530 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt50cb76b2015-04-28 09:17:05 -07001531
1532 // This will be ignored in the non A8 case
1533 bool opaqueVertexColors = GrColorIsOpaque(this->color());
joshualitt9bd2daf2015-04-17 09:30:06 -07001534 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1535 texture,
1536 params,
1537 fMaskFormat,
joshualitt50cb76b2015-04-28 09:17:05 -07001538 opaqueVertexColors,
joshualitt9bd2daf2015-04-17 09:30:06 -07001539 localMatrix));
1540 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001541
bsalomonb5238a72015-05-05 07:49:49 -07001542 FlushInfo flushInfo;
1543 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001544 size_t vertexStride = gp->getVertexStride();
joshualitt9bd2daf2015-04-17 09:30:06 -07001545 SkASSERT(vertexStride == (fUseDistanceFields ?
1546 get_vertex_stride_df(fMaskFormat, fUseLCDText) :
1547 get_vertex_stride(fMaskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001548
1549 this->initDraw(batchTarget, gp, pipeline);
1550
1551 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001552 int instanceCount = fInstanceCount;
bsalomon8415abe2015-05-04 11:41:41 -07001553 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001554
robertphillipse40d3972015-05-07 09:51:43 -07001555 void* vertices = batchTarget->makeVertSpace(vertexStride,
1556 glyphCount * kVerticesPerGlyph,
1557 &vertexBuffer,
1558 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001559 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1560 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1561 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001562 SkDebugf("Could not allocate vertices\n");
1563 return;
1564 }
1565
1566 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1567
joshualitt25ba7ea2015-04-21 07:49:49 -07001568 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1569 // in a row
1570 const SkDescriptor* desc = NULL;
1571 SkGlyphCache* cache = NULL;
1572 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001573 SkTypeface* typeface = NULL;
1574
joshualitt1d89e8d2015-04-01 12:40:54 -07001575 for (int i = 0; i < instanceCount; i++) {
1576 Geometry& args = fGeoData[i];
1577 Blob* blob = args.fBlob;
1578 Run& run = blob->fRuns[args.fRun];
1579 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1580
1581 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
1582 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
joshualitt9bd2daf2015-04-17 09:30:06 -07001583 bool regenerateColors;
1584 if (fUseDistanceFields) {
joshualittfcfb9fc2015-04-21 07:35:10 -07001585 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001586 } else {
1587 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
1588 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001589 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001590 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1591
1592 // We regenerate both texture coords and colors in the blob itself, and update the
1593 // atlas generation. If we don't end up purging any unused plots, we can avoid
1594 // regenerating the coords. We could take a finer grained approach to updating texture
1595 // coords but its not clear if the extra bookkeeping would offset any gains.
1596 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1597 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1598 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1599 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001600 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001601 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001602 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001603
1604 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1605 // generating its texture coords, we have to track whether or not the strike has
1606 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1607 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1608 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1609 // new strike, we instead keep our ref to the old strike and use the packed ids from
1610 // it. These ids will still be valid as long as we hold the ref. When we are done
1611 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1612 bool regenerateGlyphs = false;
1613 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001614 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001615 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001616
1617 // We can reuse if we have a valid strike and our descriptors / typeface are the
1618 // same
joshualitt97202d22015-04-22 13:47:02 -07001619 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1620 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001621 run.fDescriptor.getDesc();
1622 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1623 !(desc->equals(*newDesc))) {
1624 if (cache) {
1625 SkGlyphCache::AttachCache(cache);
1626 }
1627 desc = newDesc;
1628 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1629 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001630 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001631 typeface = run.fTypeface;
1632 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001633
joshualittae32c102015-04-21 09:37:57 -07001634 if (run.fStrike->isAbandoned()) {
1635 regenerateGlyphs = true;
1636 strike = fFontCache->getStrike(scaler);
1637 } else {
1638 strike = run.fStrike;
1639 }
1640 }
1641
1642 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001643 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001644 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1645 GrGlyph* glyph;
1646 if (regenerateGlyphs) {
1647 // Get the id from the old glyph, and use the new strike to lookup
1648 // the glyph.
1649 glyph = blob->fGlyphs[glyphOffset];
1650 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1651 scaler);
1652 }
1653 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001654 SkASSERT(glyph);
1655
1656 if (!fFontCache->hasGlyph(glyph) &&
1657 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
bsalomonb5238a72015-05-05 07:49:49 -07001658 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001659 this->initDraw(batchTarget, gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001660 brokenRun = glyphIdx > 0;
1661
joshualittae32c102015-04-21 09:37:57 -07001662 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1663 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001664 scaler);
1665 SkASSERT(success);
1666 }
joshualittb4c507e2015-04-08 08:07:59 -07001667 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1668 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001669
1670 // Texture coords are the last vertex attribute so we get a pointer to the
1671 // first one and then map with stride in regenerateTextureCoords
1672 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1673 vertex += info.fVertexStartIndex;
1674 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1675 vertex += vertexStride - sizeof(SkIPoint16);
1676
1677 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1678 }
1679
1680 if (regenerateColors) {
1681 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1682 vertex += info.fVertexStartIndex;
1683 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1684 this->regenerateColors(vertex, vertexStride, args.fColor);
1685 }
1686
joshualitt2a0e9f32015-04-13 06:12:21 -07001687 if (regeneratePositions) {
1688 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1689 vertex += info.fVertexStartIndex;
1690 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1691 SkScalar transX = args.fTransX;
1692 SkScalar transY = args.fTransY;
1693 this->regeneratePositions(vertex, vertexStride, transX, transY);
1694 }
bsalomonb5238a72015-05-05 07:49:49 -07001695 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001696 }
1697
joshualitt2a0e9f32015-04-13 06:12:21 -07001698 // We my have changed the color so update it here
1699 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001700 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001701 if (regenerateGlyphs) {
1702 run.fStrike.reset(SkRef(strike));
1703 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001704 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
1705 fFontCache->atlasGeneration(fMaskFormat);
1706 }
1707 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001708 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001709
1710 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1711 // have a valid atlas generation
1712 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1713 batchTarget->currentToken(),
1714 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001715 }
1716
1717 // now copy all vertices
1718 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1719 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1720
1721 currVertex += byteCount;
1722 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001723 // Make sure to attach the last cache if applicable
1724 if (cache) {
1725 SkGlyphCache::AttachCache(cache);
1726 }
bsalomonb5238a72015-05-05 07:49:49 -07001727 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001728 }
1729
joshualittad802c62015-04-15 05:31:57 -07001730 // The minimum number of Geometry we will try to allocate.
1731 static const int kMinAllocated = 32;
1732
1733 // Total number of Geometry this Batch owns
1734 int instanceCount() const { return fInstanceCount; }
1735 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1736
1737 // to avoid even the initial copy of the struct, we have a getter for the first item which
1738 // is used to seed the batch with its initial geometry. After seeding, the client should call
1739 // init() so the Batch can initialize itself
1740 Geometry& geometry() { return fGeoData[0]; }
1741 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001742 const Geometry& geo = fGeoData[0];
1743 fBatch.fColor = geo.fColor;
1744 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1745
1746 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1747 // into device space
1748 const Run& run = geo.fBlob->fRuns[geo.fRun];
1749 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1750 SkRect bounds = run.fVertexBounds;
1751 fBatch.fViewMatrix.mapRect(&bounds);
1752 this->setBounds(bounds);
1753 } else {
1754 this->setBounds(run.fVertexBounds);
1755 }
joshualittad802c62015-04-15 05:31:57 -07001756 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001757
1758private:
joshualitt9bd2daf2015-04-17 09:30:06 -07001759 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
joshualitt1d89e8d2015-04-01 12:40:54 -07001760 : fMaskFormat(maskFormat)
1761 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
joshualitt9bd2daf2015-04-17 09:30:06 -07001762 , fFontCache(fontCache)
1763 , fUseDistanceFields(false) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001764 this->initClassID<BitmapTextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001765 fBatch.fNumGlyphs = glyphCount;
joshualittad802c62015-04-15 05:31:57 -07001766 fInstanceCount = 1;
1767 fAllocatedCount = kMinAllocated;
1768 }
1769
joshualitt9bd2daf2015-04-17 09:30:06 -07001770 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
1771 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
1772 bool useLCDText, bool useBGR, float gamma)
1773 : fMaskFormat(maskFormat)
1774 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
1775 , fFontCache(fontCache)
1776 , fDistanceAdjustTable(SkRef(distanceAdjustTable))
1777 , fFilteredColor(filteredColor)
1778 , fUseDistanceFields(true)
1779 , fUseLCDText(useLCDText)
1780 , fUseBGR(useBGR)
1781 , fGamma(gamma) {
1782 this->initClassID<BitmapTextBatch>();
1783 fBatch.fNumGlyphs = glyphCount;
1784 fInstanceCount = 1;
1785 fAllocatedCount = kMinAllocated;
1786 SkASSERT(fMaskFormat == kA8_GrMaskFormat);
1787 }
1788
joshualittad802c62015-04-15 05:31:57 -07001789 ~BitmapTextBatch() {
1790 for (int i = 0; i < fInstanceCount; i++) {
1791 fGeoData[i].fBlob->unref();
1792 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001793 }
1794
1795 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1796 int width = glyph->fBounds.width();
1797 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001798
joshualitt9bd2daf2015-04-17 09:30:06 -07001799 int u0, v0, u1, v1;
1800 if (fUseDistanceFields) {
1801 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1802 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1803 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1804 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1805 } else {
1806 u0 = glyph->fAtlasLocation.fX;
1807 v0 = glyph->fAtlasLocation.fY;
1808 u1 = u0 + width;
1809 v1 = v0 + height;
1810 }
1811
joshualitt1d89e8d2015-04-01 12:40:54 -07001812 SkIPoint16* textureCoords;
1813 // V0
1814 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1815 textureCoords->set(u0, v0);
1816 vertex += vertexStride;
1817
1818 // V1
1819 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1820 textureCoords->set(u0, v1);
1821 vertex += vertexStride;
1822
1823 // V2
1824 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1825 textureCoords->set(u1, v1);
1826 vertex += vertexStride;
1827
1828 // V3
1829 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1830 textureCoords->set(u1, v0);
1831 }
1832
1833 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1834 for (int i = 0; i < kVerticesPerGlyph; i++) {
1835 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1836 *vcolor = color;
1837 vertex += vertexStride;
1838 }
1839 }
1840
joshualitt2a0e9f32015-04-13 06:12:21 -07001841 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1842 SkScalar transY) {
1843 for (int i = 0; i < kVerticesPerGlyph; i++) {
1844 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1845 point->fX += transX;
1846 point->fY += transY;
1847 vertex += vertexStride;
1848 }
1849 }
1850
joshualitt1d89e8d2015-04-01 12:40:54 -07001851 void initDraw(GrBatchTarget* batchTarget,
1852 const GrGeometryProcessor* gp,
1853 const GrPipeline* pipeline) {
1854 batchTarget->initDraw(gp, pipeline);
1855
1856 // TODO remove this when batch is everywhere
1857 GrPipelineInfo init;
1858 init.fColorIgnored = fBatch.fColorIgnored;
1859 init.fOverrideColor = GrColor_ILLEGAL;
1860 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1861 init.fUsesLocalCoords = this->usesLocalCoords();
1862 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1863 }
1864
bsalomonb5238a72015-05-05 07:49:49 -07001865 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001866 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001867 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001868 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001869 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001870 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001871 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001872 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001873 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1874 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001875 }
1876
1877 GrColor color() const { return fBatch.fColor; }
1878 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1879 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1880 int numGlyphs() const { return fBatch.fNumGlyphs; }
1881
1882 bool onCombineIfPossible(GrBatch* t) override {
1883 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1884
joshualitt9bd2daf2015-04-17 09:30:06 -07001885 if (fUseDistanceFields != that->fUseDistanceFields) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001886 return false;
1887 }
1888
joshualitt9bd2daf2015-04-17 09:30:06 -07001889 if (!fUseDistanceFields) {
1890 // Bitmap Text
1891 if (fMaskFormat != that->fMaskFormat) {
1892 return false;
1893 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001894
joshualitt9bd2daf2015-04-17 09:30:06 -07001895 // TODO we can often batch across LCD text if we have dual source blending and don't
1896 // have to use the blend constant
1897 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1898 return false;
1899 }
1900
1901 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1902 return false;
1903 }
1904 } else {
1905 // Distance Fields
1906 SkASSERT(this->fMaskFormat == that->fMaskFormat &&
1907 this->fMaskFormat == kA8_GrMaskFormat);
1908
1909 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1910 return false;
1911 }
1912
1913 if (fFilteredColor != that->fFilteredColor) {
1914 return false;
1915 }
1916
1917 if (fUseLCDText != that->fUseLCDText) {
1918 return false;
1919 }
1920
1921 if (fUseBGR != that->fUseBGR) {
1922 return false;
1923 }
1924
1925 if (fGamma != that->fGamma) {
1926 return false;
1927 }
1928
1929 // TODO see note above
1930 if (fUseLCDText && this->color() != that->color()) {
1931 return false;
1932 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001933 }
1934
1935 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001936
1937 // copy that->geoData(). We do this manually for performance reasons
1938 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1939 int otherInstanceCount = that->instanceCount();
1940 int allocSize = otherInstanceCount + fInstanceCount;
1941 if (allocSize > fAllocatedCount) {
1942 while (allocSize > fAllocatedCount) {
1943 fAllocatedCount = fAllocatedCount << 1;
1944 }
1945 fGeoData.realloc(fAllocatedCount);
1946 }
1947
1948 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1949 otherInstanceCount * sizeof(Geometry));
1950 int total = fInstanceCount + otherInstanceCount;
1951 for (int i = fInstanceCount; i < total; i++) {
1952 fGeoData[i].fBlob->ref();
1953 }
1954 fInstanceCount = total;
joshualitt99c7c072015-05-01 13:43:30 -07001955
1956 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001957 return true;
1958 }
1959
joshualitt9bd2daf2015-04-17 09:30:06 -07001960 // TODO just use class params
1961 // TODO trying to figure out why lcd is so whack
1962 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1963 GrColor color, GrTexture* texture) {
1964 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
1965
1966 // set up any flags
1967 uint32_t flags = 0;
1968 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1969 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
1970 flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
1971 kRectToRect_DistanceFieldEffectFlag : 0;
1972 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1973
1974 // see if we need to create a new effect
1975 if (fUseLCDText) {
1976 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1977
1978 float redCorrection =
1979 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1980 float greenCorrection =
1981 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1982 float blueCorrection =
1983 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1984 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1985 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1986 greenCorrection,
1987 blueCorrection);
1988
1989 return GrDistanceFieldLCDTextGeoProc::Create(color,
1990 viewMatrix,
1991 texture,
1992 params,
1993 widthAdjust,
1994 flags);
1995 } else {
1996 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt50cb76b2015-04-28 09:17:05 -07001997 bool opaque = GrColorIsOpaque(color);
joshualitt9bd2daf2015-04-17 09:30:06 -07001998#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,
joshualitt50cb76b2015-04-28 09:17:05 -07002006 flags,
2007 opaque);
joshualitt9bd2daf2015-04-17 09:30:06 -07002008#else
2009 return GrDistanceFieldA8TextGeoProc::Create(color,
2010 viewMatrix,
2011 texture,
2012 params,
joshualitt50cb76b2015-04-28 09:17:05 -07002013 flags,
2014 opaque);
joshualitt9bd2daf2015-04-17 09:30:06 -07002015#endif
2016 }
2017
2018 }
2019
joshualitt1d89e8d2015-04-01 12:40:54 -07002020 struct BatchTracker {
2021 GrColor fColor;
2022 SkMatrix fViewMatrix;
2023 bool fUsesLocalCoords;
2024 bool fColorIgnored;
2025 bool fCoverageIgnored;
2026 int fNumGlyphs;
2027 };
2028
2029 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002030 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2031 int fInstanceCount;
2032 int fAllocatedCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07002033 GrMaskFormat fMaskFormat;
2034 GrPixelConfig fPixelConfig;
2035 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002036
2037 // Distance field properties
2038 SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
2039 SkColor fFilteredColor;
2040 bool fUseDistanceFields;
2041 bool fUseLCDText;
2042 bool fUseBGR;
2043 float fGamma;
joshualitt1d89e8d2015-04-01 12:40:54 -07002044};
2045
joshualitt9a27e632015-04-06 10:53:36 -07002046void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
2047 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2048 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2049 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002050
joshualitt9a27e632015-04-06 10:53:36 -07002051 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2052 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002053
joshualitt9a27e632015-04-06 10:53:36 -07002054 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002055
joshualitt9a27e632015-04-06 10:53:36 -07002056 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2057 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002058 }
2059
joshualitt9a27e632015-04-06 10:53:36 -07002060 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
2061
2062 switch (it.positioning()) {
2063 case SkTextBlob::kDefault_Positioning:
2064 this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
2065 textLen, x + offset.x(), y + offset.y(), clipBounds);
2066 break;
2067 case SkTextBlob::kHorizontal_Positioning:
2068 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
2069 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2070 clipBounds);
2071 break;
2072 case SkTextBlob::kFull_Positioning:
2073 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
2074 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2075 break;
2076 }
2077}
2078
joshualitt79dfb2b2015-05-11 08:58:08 -07002079
2080inline BitmapTextBatch*
2081GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
2082 int glyphCount, int run, int subRun,
2083 GrColor color, SkScalar transX, SkScalar transY,
2084 const SkPaint& skPaint) {
2085 GrMaskFormat format = info.fMaskFormat;
2086 GrColor subRunColor;
2087 if (kARGB_GrMaskFormat == format) {
2088 uint8_t paintAlpha = skPaint.getAlpha();
2089 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2090 } else {
2091 subRunColor = color;
2092 }
2093
2094 BitmapTextBatch* batch;
2095 if (info.fDrawAsDistanceFields) {
2096 SkColor filteredColor;
2097 SkColorFilter* colorFilter = skPaint.getColorFilter();
2098 if (colorFilter) {
2099 filteredColor = colorFilter->filterColor(skPaint.getColor());
2100 } else {
2101 filteredColor = skPaint.getColor();
2102 }
2103 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
2104 float gamma = fDeviceProperties.gamma();
2105 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
2106 fDistanceAdjustTable, filteredColor,
2107 info.fUseLCDText, useBGR,
2108 gamma);
2109 } else {
2110 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
2111 }
2112 BitmapTextBatch::Geometry& geometry = batch->geometry();
2113 geometry.fBlob = SkRef(cacheBlob);
2114 geometry.fRun = run;
2115 geometry.fSubRun = subRun;
2116 geometry.fColor = subRunColor;
2117 geometry.fTransX = transX;
2118 geometry.fTransY = transY;
2119 batch->init();
2120
2121 return batch;
2122}
2123
joshualitt9a27e632015-04-06 10:53:36 -07002124inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
2125 BitmapTextBlob* cacheBlob, int run, GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -07002126 SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002127 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2128 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2129 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2130 if (0 == glyphCount) {
2131 continue;
2132 }
2133
joshualitt79dfb2b2015-05-11 08:58:08 -07002134 SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2135 subRun, color, transX, transY,
2136 skPaint));
joshualitt99c7c072015-05-01 13:43:30 -07002137 target->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002138 }
2139}
2140
2141inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
joshualitt2a0e9f32015-04-13 06:12:21 -07002142 const GrPaint& grPaint, const GrClip& clip,
2143 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002144 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07002145 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
2146 bigGlyph.fVx += SkScalarTruncToInt(transX);
2147 bigGlyph.fVy += SkScalarTruncToInt(transY);
joshualitt1d89e8d2015-04-01 12:40:54 -07002148 SkMatrix translate;
joshualitt2a0e9f32015-04-13 06:12:21 -07002149 translate.setTranslate(SkIntToScalar(bigGlyph.fVx),
2150 SkIntToScalar(bigGlyph.fVy));
joshualitt1d89e8d2015-04-01 12:40:54 -07002151 SkPath tmpPath(bigGlyph.fPath);
2152 tmpPath.transform(translate);
2153 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt9a27e632015-04-06 10:53:36 -07002154 fContext->drawPath(rt, clip, grPaint, SkMatrix::I(), tmpPath, strokeInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07002155 }
2156}
joshualitt9a27e632015-04-06 10:53:36 -07002157
2158void GrAtlasTextContext::flush(GrDrawTarget* target,
2159 const SkTextBlob* blob,
2160 BitmapTextBlob* cacheBlob,
2161 GrRenderTarget* rt,
2162 const SkPaint& skPaint,
2163 const GrPaint& grPaint,
2164 SkDrawFilter* drawFilter,
2165 const GrClip& clip,
2166 const SkMatrix& viewMatrix,
2167 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002168 SkScalar x, SkScalar y,
2169 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002170 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2171 // it as paths
2172 GrPipelineBuilder pipelineBuilder;
2173 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2174
2175 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002176
2177 SkTextBlob::RunIterator it(blob);
2178 for (int run = 0; !it.done(); it.next(), run++) {
2179 if (cacheBlob->fRuns[run].fDrawAsPaths) {
2180 this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
2181 continue;
2182 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002183 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
joshualitt9bd2daf2015-04-17 09:30:06 -07002184 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002185 }
2186
2187 // Now flush big glyphs
joshualitt2a0e9f32015-04-13 06:12:21 -07002188 this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, transX, transY);
joshualitt9a27e632015-04-06 10:53:36 -07002189}
2190
2191void GrAtlasTextContext::flush(GrDrawTarget* target,
2192 BitmapTextBlob* cacheBlob,
2193 GrRenderTarget* rt,
2194 const SkPaint& skPaint,
2195 const GrPaint& grPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -07002196 const GrClip& clip) {
joshualitt9a27e632015-04-06 10:53:36 -07002197 GrPipelineBuilder pipelineBuilder;
2198 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2199
2200 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002201 for (int run = 0; run < cacheBlob->fRunCount; run++) {
joshualitt9bd2daf2015-04-17 09:30:06 -07002202 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002203 }
2204
2205 // Now flush big glyphs
joshualitt2a0e9f32015-04-13 06:12:21 -07002206 this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, 0, 0);
joshualitt9a27e632015-04-06 10:53:36 -07002207}
joshualitt79dfb2b2015-05-11 08:58:08 -07002208
2209///////////////////////////////////////////////////////////////////////////////////////////////////
2210
2211#ifdef GR_TEST_UTILS
2212
2213BATCH_TEST_DEFINE(TextBlob) {
2214 static uint32_t gContextID = SK_InvalidGenID;
2215 static GrAtlasTextContext* gTextContext = NULL;
2216 static SkDeviceProperties gDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType);
2217
2218 if (context->uniqueID() != gContextID) {
2219 gContextID = context->uniqueID();
2220 SkDELETE(gTextContext);
2221 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2222 // because we don't really want to have a gpu device here.
2223 // We enable distance fields by twiddling a knob on the paint
2224 gTextContext = GrAtlasTextContext::Create(context, NULL, gDeviceProperties, false);
2225 }
2226
2227 // create dummy render target
2228 GrSurfaceDesc desc;
2229 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2230 desc.fWidth = 1024;
2231 desc.fHeight = 1024;
2232 desc.fConfig = kRGBA_8888_GrPixelConfig;
2233 desc.fSampleCnt = 1;
2234 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2235 SkASSERT(texture);
2236 SkASSERT(NULL != texture->asRenderTarget());
2237 GrRenderTarget* rt = texture->asRenderTarget();
2238
2239 // Setup dummy SkPaint / GrPaint
2240 GrColor color = GrRandomColor(random);
2241 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2242 SkPaint skPaint;
2243 skPaint.setDistanceFieldTextTEMP(random->nextBool());
2244 skPaint.setColor(color);
2245 skPaint.setLCDRenderText(random->nextBool());
2246 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2247 skPaint.setSubpixelText(random->nextBool());
2248
2249 GrPaint grPaint;
2250 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2251 SkFAIL("couldn't convert paint\n");
2252 }
2253
2254 const char* text = "The quick brown fox jumps over the lazy dog.";
2255 int textLen = (int)strlen(text);
2256
2257 // Setup clip
2258 GrClip clip;
2259 SkIRect noClip = SkIRect::MakeLargest();
2260
2261 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2262 // intend to test the batch with this unit test, that is okay.
2263 SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
2264 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2265 static_cast<size_t>(textLen), 0, 0, noClip));
2266
2267 SkScalar transX = static_cast<SkScalar>(random->nextU());
2268 SkScalar transY = static_cast<SkScalar>(random->nextU());
2269 const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
2270 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2271}
2272
2273#endif