blob: d1076bbcd080d6c880dfada8edf041387d94edf0 [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"
robertphillipsccb1b572015-05-27 11:02:55 -070013#include "GrBlurUtils.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070014#include "GrDefaultGeoProcFactory.h"
robertphillipsea461502015-05-26 11:38:03 -070015#include "GrDrawContext.h"
robertphillips2334fb62015-06-17 05:43:33 -070016#include "GrDrawTarget.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070017#include "GrFontScaler.h"
18#include "GrIndexBuffer.h"
bsalomoned0bcad2015-05-04 10:36:42 -070019#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070020#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070021#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070022#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070023#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070024
25#include "SkAutoKern.h"
26#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070027#include "SkColorFilter.h"
28#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070029#include "SkDraw.h"
30#include "SkDrawFilter.h"
31#include "SkDrawProcs.h"
32#include "SkGlyphCache.h"
33#include "SkGpuDevice.h"
34#include "SkGr.h"
35#include "SkPath.h"
36#include "SkRTConf.h"
37#include "SkStrokeRec.h"
38#include "SkTextBlob.h"
39#include "SkTextMapStateProc.h"
40
41#include "effects/GrBitmapTextGeoProc.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070042#include "effects/GrDistanceFieldGeoProc.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070043
44namespace {
45static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
46
47// position + local coord
48static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
49
50static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
51
joshualitt9bd2daf2015-04-17 09:30:06 -070052static const int kMinDFFontSize = 18;
53static const int kSmallDFFontSize = 32;
54static const int kSmallDFFontLimit = 32;
55static const int kMediumDFFontSize = 72;
56static const int kMediumDFFontLimit = 72;
57static const int kLargeDFFontSize = 162;
joshualitta7c63892015-04-21 13:24:37 -070058static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
joshualitt9bd2daf2015-04-17 09:30:06 -070059
jvanverth0fce1fb2015-06-16 08:46:24 -070060static const int kLargeGlyphLimit = 192;
61
joshualitt9bd2daf2015-04-17 09:30:06 -070062SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
63static const int kDistanceAdjustLumShift = 5;
64
joshualitt1d89e8d2015-04-01 12:40:54 -070065static const int kVerticesPerGlyph = 4;
66static const int kIndicesPerGlyph = 6;
67
68static size_t get_vertex_stride(GrMaskFormat maskFormat) {
69 switch (maskFormat) {
70 case kA8_GrMaskFormat:
71 return kGrayTextVASize;
72 case kARGB_GrMaskFormat:
73 return kColorTextVASize;
74 default:
75 return kLCDTextVASize;
76 }
77}
78
joshualitt9bd2daf2015-04-17 09:30:06 -070079static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
80 SkASSERT(maskFormat == kA8_GrMaskFormat);
81 if (useLCDText) {
82 return kLCDTextVASize;
83 } else {
84 return kGrayTextVASize;
85 }
86}
87
88static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
89 unsigned r = SkColorGetR(c);
90 unsigned g = SkColorGetG(c);
91 unsigned b = SkColorGetB(c);
92 return GrColorPackRGBA(r, g, b, 0xff);
93}
94
joshualitt1d89e8d2015-04-01 12:40:54 -070095};
96
97// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -070098// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -070099
joshualittdbd35932015-04-02 09:19:04 -0700100GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700101 GrDrawContext* drawContext,
joshualitt9bd2daf2015-04-17 09:30:06 -0700102 const SkDeviceProperties& properties,
robertphillips2334fb62015-06-17 05:43:33 -0700103 bool useDFT)
104 : INHERITED(context, drawContext, properties)
joshualitt9bd2daf2015-04-17 09:30:06 -0700105 , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
joshualittb7133be2015-04-08 09:08:31 -0700106 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
107 // vertexStride
108 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
109 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700110 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700111 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700112
113#if SK_FORCE_DISTANCE_FIELD_TEXT
114 fEnableDFRendering = true;
115#else
robertphillips2334fb62015-06-17 05:43:33 -0700116 fEnableDFRendering = useDFT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700117#endif
118}
119
120void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
121
122 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
123 // text. The mask gamma hack is based off of guessing what the blend color is going to
124 // be, and adjusting the mask so that when run through the linear blend will
125 // produce the value closest to the desired result. However, in practice this means
126 // that the 'adjusted' mask is just increasing or decreasing the coverage of
127 // the mask depending on what it is thought it will blit against. For black (on
128 // assumed white) this means that coverages are decreased (on a curve). For white (on
129 // assumed black) this means that coverages are increased (on a a curve). At
130 // middle (perceptual) gray (which could be blit against anything) the coverages
131 // remain the same.
132 //
133 // The idea here is that instead of determining the initial (real) coverage and
134 // then adjusting that coverage, we determine an adjusted coverage directly by
135 // essentially manipulating the geometry (in this case, the distance to the glyph
136 // edge). So for black (on assumed white) this thins a bit; for white (on
137 // assumed black) this fake bolds the geometry a bit.
138 //
139 // The distance adjustment is calculated by determining the actual coverage value which
140 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
141 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
142 // actual edge. So by subtracting this distance adjustment and computing without the
143 // the coverage adjustment we should get 0.5 coverage at the same point.
144 //
145 // This has several implications:
146 // For non-gray lcd smoothed text, each subpixel essentially is using a
147 // slightly different geometry.
148 //
149 // For black (on assumed white) this may not cover some pixels which were
150 // previously covered; however those pixels would have been only slightly
151 // covered and that slight coverage would have been decreased anyway. Also, some pixels
152 // which were previously fully covered may no longer be fully covered.
153 //
154 // For white (on assumed black) this may cover some pixels which weren't
155 // previously covered at all.
156
157 int width, height;
158 size_t size;
159
160#ifdef SK_GAMMA_CONTRAST
161 SkScalar contrast = SK_GAMMA_CONTRAST;
162#else
163 SkScalar contrast = 0.5f;
164#endif
165 SkScalar paintGamma = gamma;
166 SkScalar deviceGamma = gamma;
167
168 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
169 &width, &height);
170
171 SkASSERT(kExpectedDistanceAdjustTableSize == height);
172 fTable = SkNEW_ARRAY(SkScalar, height);
173
174 SkAutoTArray<uint8_t> data((int)size);
175 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
176
177 // find the inverse points where we cross 0.5
178 // binsearch might be better, but we only need to do this once on creation
179 for (int row = 0; row < height; ++row) {
180 uint8_t* rowPtr = data.get() + row*width;
181 for (int col = 0; col < width - 1; ++col) {
182 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
183 // compute point where a mask value will give us a result of 0.5
184 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
185 float borderAlpha = (col + interp) / 255.f;
186
187 // compute t value for that alpha
188 // this is an approximate inverse for smoothstep()
189 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
190
191 // compute distance which gives us that t value
192 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
193 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
194
195 fTable[row] = d;
196 break;
197 }
198 }
199 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700200}
201
joshualittdbd35932015-04-02 09:19:04 -0700202GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700203 GrDrawContext* drawContext,
joshualitt9bd2daf2015-04-17 09:30:06 -0700204 const SkDeviceProperties& props,
robertphillips2334fb62015-06-17 05:43:33 -0700205 bool useDFT) {
206 return SkNEW_ARGS(GrAtlasTextContext, (context, drawContext, props, useDFT));
joshualitt1d89e8d2015-04-01 12:40:54 -0700207}
208
joshualittdbd35932015-04-02 09:19:04 -0700209bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
210 const GrClip&,
211 const GrPaint&,
212 const SkPaint& skPaint,
213 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700214 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
215 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700216}
217
joshualitt9e36c1a2015-04-14 12:17:27 -0700218GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
219 GrColor canonicalColor = paint.computeLuminanceColor();
220 if (lcd) {
221 // This is the correct computation, but there are tons of cases where LCD can be overridden.
222 // For now we just regenerate if any run in a textblob has LCD.
223 // TODO figure out where all of these overrides are and see if we can incorporate that logic
224 // at a higher level *OR* use sRGB
225 SkASSERT(false);
226 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
227 } else {
228 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
229 // gamma corrected masks anyways, nor color
230 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
231 SkColorGetG(canonicalColor),
232 SkColorGetB(canonicalColor));
233 // reduce to our finite number of bits
234 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
235 }
236 return canonicalColor;
237}
238
239// TODO if this function ever shows up in profiling, then we can compute this value when the
240// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
241// run so this is not a big deal to compute here.
242bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
243 SkTextBlob::RunIterator it(blob);
244 for (; !it.done(); it.next()) {
245 if (it.isLCD()) {
246 return true;
247 }
248 }
249 return false;
250}
251
joshualitt2a0e9f32015-04-13 06:12:21 -0700252bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
253 const BitmapTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700254 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700255 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700256 // If we have LCD text then our canonical color will be set to transparent, in this case we have
257 // to regenerate the blob on any color change
258 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700259 return true;
260 }
261
262 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
263 return true;
264 }
265
266 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
267 return true;
268 }
269
joshualitt53b5f442015-04-13 06:33:59 -0700270 // We only cache one masked version
271 if (blob.fKey.fHasBlur &&
272 (blob.fBlurRec.fSigma != blurRec.fSigma ||
273 blob.fBlurRec.fStyle != blurRec.fStyle ||
274 blob.fBlurRec.fQuality != blurRec.fQuality)) {
275 return true;
276 }
277
278 // Similarly, we only cache one version for each style
279 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
280 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
281 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
282 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
283 return true;
284 }
285
joshualittfcfb9fc2015-04-21 07:35:10 -0700286 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
287 // for mixed blobs if this becomes an issue.
288 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700289 // Identical viewmatrices and we can reuse in all cases
290 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
291 return false;
292 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700293 return true;
294 }
295
joshualittfcfb9fc2015-04-21 07:35:10 -0700296 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700297 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
298 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
299 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
300 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
301 return true;
302 }
303
joshualittfcfb9fc2015-04-21 07:35:10 -0700304 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
305 // but only for integer translations.
306 // This cool bit of math will determine the necessary translation to apply to the already
307 // generated vertex coordinates to move them to the correct position
308 SkScalar transX = viewMatrix.getTranslateX() +
309 viewMatrix.getScaleX() * (x - blob.fX) +
310 viewMatrix.getSkewX() * (y - blob.fY) -
311 blob.fViewMatrix.getTranslateX();
312 SkScalar transY = viewMatrix.getTranslateY() +
313 viewMatrix.getSkewY() * (x - blob.fX) +
314 viewMatrix.getScaleY() * (y - blob.fY) -
315 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700316 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700317 return true;
318 }
319
joshualittfcfb9fc2015-04-21 07:35:10 -0700320 (*outTransX) = transX;
321 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700322 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700323 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
324 // distance field being generated, so we have to regenerate in those cases
325 SkScalar newMaxScale = viewMatrix.getMaxScale();
326 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
327 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
328 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
329 return true;
330 }
331
332 (*outTransX) = x - blob.fX;
333 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700334 }
joshualitta7c63892015-04-21 13:24:37 -0700335 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
336 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
337 // the blob anyways at flush time, so no need to regenerate explicitly
joshualittfcfb9fc2015-04-21 07:35:10 -0700338
joshualitt2a0e9f32015-04-13 06:12:21 -0700339 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700340}
341
342
joshualittdbd35932015-04-02 09:19:04 -0700343inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
344 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700345 const SkMatrix* viewMatrix,
346 bool noGamma) {
347 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700348 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
349 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
350}
351
robertphillips9c240a12015-05-28 07:45:59 -0700352void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700353 const GrClip& clip, const SkPaint& skPaint,
354 const SkMatrix& viewMatrix, const SkTextBlob* blob,
355 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700356 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700357 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700358 if (fContext->abandoned()) {
359 return;
360 }
361
joshualitt2a0e9f32015-04-13 06:12:21 -0700362 SkAutoTUnref<BitmapTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700363 SkMaskFilter::BlurRec blurRec;
364 BitmapTextBlob::Key key;
365 // It might be worth caching these things, but its not clear at this time
366 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
367 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700368 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700369 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700370 drawFilter);
371
372 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700373 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700374
375 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
376 SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() :
377 kUnknown_SkPixelGeometry;
378
joshualitt9e36c1a2015-04-14 12:17:27 -0700379 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
380 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
381 // ensure we always match the same key
382 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
383 ComputeCanonicalColor(skPaint, hasLCD);
384
joshualitte4cee1f2015-05-11 13:04:28 -0700385 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700386 key.fUniqueID = blob->uniqueID();
387 key.fStyle = skPaint.getStyle();
388 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700389 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700390 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700391 }
392
joshualitt1d89e8d2015-04-01 12:40:54 -0700393 SkIRect clipRect;
394 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
395
joshualitt2a0e9f32015-04-13 06:12:21 -0700396 SkScalar transX = 0.f;
397 SkScalar transY = 0.f;
398
joshualitt9e36c1a2015-04-14 12:17:27 -0700399 // Though for the time being runs in the textblob can override the paint, they only touch font
400 // info.
401 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700402 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
403 return;
404 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700405
joshualittb7133be2015-04-08 09:08:31 -0700406 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700407 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700408 // We have to remake the blob because changes may invalidate our masks.
409 // TODO we could probably get away reuse most of the time if the pointer is unique,
410 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700411 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700412 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
413 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700414 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700415 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700416 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700417 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
418 // offsets
joshualitt2a0e9f32015-04-13 06:12:21 -0700419 cacheBlob->fViewMatrix = viewMatrix;
420 cacheBlob->fX = x;
421 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700422 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700423 }
424 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700425 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700426 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
427 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700428 } else {
429 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
430 }
robertphillips9c240a12015-05-28 07:45:59 -0700431 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700432 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700433 }
434
joshualitt9e36c1a2015-04-14 12:17:27 -0700435 cacheBlob->fPaintColor = skPaint.getColor();
robertphillips2334fb62015-06-17 05:43:33 -0700436 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700437 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700438}
439
joshualitt9bd2daf2015-04-17 09:30:06 -0700440inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
441 const SkMatrix& viewMatrix) {
442 // TODO: support perspective (need getMaxScale replacement)
443 if (viewMatrix.hasPerspective()) {
444 return false;
445 }
446
447 SkScalar maxScale = viewMatrix.getMaxScale();
448 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
449 // Hinted text looks far better at small resolutions
450 // Scaling up beyond 2x yields undesireable artifacts
joshualitta7c63892015-04-21 13:24:37 -0700451 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700452 return false;
453 }
454
455 if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
jvanverth0fce1fb2015-06-16 08:46:24 -0700456 scaledTextSize < kLargeGlyphLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700457 return false;
458 }
459
460 // rasterizers and mask filters modify alpha, which doesn't
461 // translate well to distance
462 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700463 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700464 return false;
465 }
466
467 // TODO: add some stroking support
468 if (skPaint.getStyle() != SkPaint::kFill_Style) {
469 return false;
470 }
471
472 return true;
473}
474
robertphillips9c240a12015-05-28 07:45:59 -0700475void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700476 const SkPaint& skPaint, GrColor color,
477 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700478 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700479 SkDrawFilter* drawFilter, const SkIRect& clipRect,
480 GrRenderTarget* rt, const GrClip& clip,
481 const GrPaint& paint) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700482 cacheBlob->fViewMatrix = viewMatrix;
483 cacheBlob->fX = x;
484 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700485
486 // Regenerate textblob
487 SkPaint runPaint = skPaint;
488 SkTextBlob::RunIterator it(blob);
489 for (int run = 0; !it.done(); it.next(), run++) {
490 int glyphCount = it.glyphCount();
491 size_t textLen = glyphCount * sizeof(uint16_t);
492 const SkPoint& offset = it.offset();
493 // applyFontToPaint() always overwrites the exact same attributes,
494 // so it is safe to not re-seed the paint for this reason.
495 it.applyFontToPaint(&runPaint);
496
497 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
498 // A false return from filter() means we should abort the current draw.
499 runPaint = skPaint;
500 continue;
501 }
502
robertphillips9c240a12015-05-28 07:45:59 -0700503 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700504
joshualitt1d89e8d2015-04-01 12:40:54 -0700505 // setup vertex / glyphIndex for the new run
506 if (run > 0) {
507 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
508 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
509
510 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
511 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
512
513 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
514 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
515 }
516
joshualittfcfb9fc2015-04-21 07:35:10 -0700517 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
518 cacheBlob->setHasDistanceField();
519 SkPaint dfPaint = runPaint;
520 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700521 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700522 Run& runIdx = cacheBlob->fRuns[run];
523 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
524 subRun.fUseLCDText = runPaint.isLCDRenderText();
525 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700526
joshualittfcfb9fc2015-04-21 07:35:10 -0700527 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
528
529 SkTDArray<char> fallbackTxt;
530 SkTDArray<SkScalar> fallbackPos;
531 SkPoint dfOffset;
532 int scalarsPerPosition = 2;
533 switch (it.positioning()) {
534 case SkTextBlob::kDefault_Positioning: {
535 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
536 (const char *)it.glyphs(), textLen,
537 x + offset.x(), y + offset.y(), clipRect, textRatio,
538 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
539 break;
540 }
541 case SkTextBlob::kHorizontal_Positioning: {
542 scalarsPerPosition = 1;
543 dfOffset = SkPoint::Make(x, y + offset.y());
544 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
545 (const char*)it.glyphs(), textLen, it.pos(),
546 scalarsPerPosition, dfOffset, clipRect, textRatio,
547 &fallbackTxt, &fallbackPos);
548 break;
549 }
550 case SkTextBlob::kFull_Positioning: {
551 dfOffset = SkPoint::Make(x, y);
552 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
553 (const char*)it.glyphs(), textLen, it.pos(),
554 scalarsPerPosition, dfOffset, clipRect, textRatio,
555 &fallbackTxt, &fallbackPos);
556 break;
557 }
558 }
559 if (fallbackTxt.count()) {
560 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
561 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
562 clipRect);
563 }
564
565 SkGlyphCache::AttachCache(cache);
566 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
567 cacheBlob->fRuns[run].fDrawAsPaths = true;
568 } else {
569 cacheBlob->setHasBitmap();
570 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
571 false);
572 switch (it.positioning()) {
573 case SkTextBlob::kDefault_Positioning:
574 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
575 (const char *)it.glyphs(), textLen,
576 x + offset.x(), y + offset.y(), clipRect);
577 break;
578 case SkTextBlob::kHorizontal_Positioning:
579 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
580 (const char*)it.glyphs(), textLen, it.pos(), 1,
581 SkPoint::Make(x, y + offset.y()), clipRect);
582 break;
583 case SkTextBlob::kFull_Positioning:
584 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
585 (const char*)it.glyphs(), textLen, it.pos(), 2,
586 SkPoint::Make(x, y), clipRect);
587 break;
588 }
589 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700590 }
591
592 if (drawFilter) {
593 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
594 runPaint = skPaint;
595 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700596 }
597}
598
joshualitt64c99cc2015-04-21 09:43:03 -0700599inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
600 SkPaint* skPaint,
601 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700602 const SkMatrix& viewMatrix) {
603 // getMaxScale doesn't support perspective, so neither do we at the moment
604 SkASSERT(!viewMatrix.hasPerspective());
605 SkScalar maxScale = viewMatrix.getMaxScale();
606 SkScalar textSize = skPaint->getTextSize();
607 SkScalar scaledTextSize = textSize;
608 // if we have non-unity scale, we need to choose our base text size
609 // based on the SkPaint's text size multiplied by the max scale factor
610 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
611 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
612 scaledTextSize *= maxScale;
613 }
614
joshualitt64c99cc2015-04-21 09:43:03 -0700615 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
616 // and ceiling. A scale outside of this range would require regenerating the distance fields
617 SkScalar dfMaskScaleFloor;
618 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700619 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700620 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700621 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700622 *textRatio = textSize / kSmallDFFontSize;
623 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
624 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700625 dfMaskScaleFloor = kSmallDFFontLimit;
626 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700627 *textRatio = textSize / kMediumDFFontSize;
628 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
629 } else {
joshualitta7c63892015-04-21 13:24:37 -0700630 dfMaskScaleFloor = kMediumDFFontLimit;
631 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700632 *textRatio = textSize / kLargeDFFontSize;
633 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
634 }
635
joshualitt64c99cc2015-04-21 09:43:03 -0700636 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
637 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
638 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
639 // tolerate before we'd have to move to a large mip size. When we actually test these values
640 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
641 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
642 // level)
joshualitta7c63892015-04-21 13:24:37 -0700643 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700644 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
645 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
646
joshualitt9bd2daf2015-04-17 09:30:06 -0700647 skPaint->setLCDRenderText(false);
648 skPaint->setAutohinted(false);
649 skPaint->setHinting(SkPaint::kNormal_Hinting);
650 skPaint->setSubpixelText(true);
651}
652
joshualittfec19e12015-04-17 10:32:32 -0700653inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700654 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700655 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700656 const GrPaint& paint,
657 const SkPaint& skPaint,
658 const SkMatrix& viewMatrix,
659 const SkTDArray<char>& fallbackTxt,
660 const SkTDArray<SkScalar>& fallbackPos,
661 int scalarsPerPosition,
662 const SkPoint& offset,
663 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700664 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700665 blob->setHasBitmap();
666 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700667 // Push back a new subrun to fill and set the override descriptor
668 run.push_back();
669 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
670 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
joshualittfec19e12015-04-17 10:32:32 -0700671 &fDeviceProperties, &viewMatrix, false);
672 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700673 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700674 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700675 fallbackTxt.begin(), fallbackTxt.count(),
676 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
677 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700678}
679
680inline GrAtlasTextContext::BitmapTextBlob*
681GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
682 const SkMatrix& viewMatrix, SkGlyphCache** cache,
683 SkPaint* dfPaint, SkScalar* textRatio) {
684 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
685
686 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700687 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700688 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700689 Run& run = blob->fRuns[0];
690 PerSubRunInfo& subRun = run.fSubRunInfo.back();
691 subRun.fUseLCDText = origPaint.isLCDRenderText();
692 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700693
694 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
695 return blob;
696}
697
joshualitt79dfb2b2015-05-11 08:58:08 -0700698inline GrAtlasTextContext::BitmapTextBlob*
699GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
700 const GrPaint& paint, const SkPaint& skPaint,
701 const SkMatrix& viewMatrix,
702 const char text[], size_t byteLength,
703 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700704 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700705 SkIRect clipRect;
706 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
707
joshualitt79dfb2b2015-05-11 08:58:08 -0700708 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700709 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
710 SkPaint dfPaint;
711 SkScalar textRatio;
712 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700713 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700714
joshualitt9bd2daf2015-04-17 09:30:06 -0700715 SkTDArray<char> fallbackTxt;
716 SkTDArray<SkScalar> fallbackPos;
717 SkPoint offset;
718 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
719 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
720 &offset, skPaint);
721 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700722 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700723 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700724 fallbackPos, 2, offset, clipRect);
725 }
726 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700727 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700728 blob->fViewMatrix = viewMatrix;
729
730 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
731 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
732 byteLength, x, y, clipRect);
733 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700734 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700735 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700736}
737
joshualitt79dfb2b2015-05-11 08:58:08 -0700738inline GrAtlasTextContext::BitmapTextBlob*
739GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
740 const GrPaint& paint, const SkPaint& skPaint,
741 const SkMatrix& viewMatrix,
742 const char text[], size_t byteLength,
743 const SkScalar pos[], int scalarsPerPosition,
744 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700745 int glyphCount = skPaint.countText(text, byteLength);
746
747 SkIRect clipRect;
748 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
749
joshualitt79dfb2b2015-05-11 08:58:08 -0700750 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700751 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
752 SkPaint dfPaint;
753 SkScalar textRatio;
754 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700755 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700756
757 SkTDArray<char> fallbackTxt;
758 SkTDArray<SkScalar> fallbackPos;
759 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
760 byteLength, pos, scalarsPerPosition, offset, clipRect,
761 textRatio, &fallbackTxt, &fallbackPos);
762 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700763 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700764 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700765 fallbackPos, scalarsPerPosition, offset, clipRect);
766 }
767 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700768 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700769 blob->fViewMatrix = viewMatrix;
770 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
771 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
772 byteLength, pos, scalarsPerPosition, offset, clipRect);
773 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700774 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700775 return blob;
776}
777
robertphillips2334fb62015-06-17 05:43:33 -0700778void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700779 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700780 const GrPaint& paint, const SkPaint& skPaint,
781 const SkMatrix& viewMatrix,
782 const char text[], size_t byteLength,
783 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -0700784 SkAutoTUnref<BitmapTextBlob> blob(
785 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
786 text, byteLength, x, y, regionClipBounds));
787 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700788}
789
robertphillips2334fb62015-06-17 05:43:33 -0700790void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700791 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700792 const GrPaint& paint, const SkPaint& skPaint,
793 const SkMatrix& viewMatrix,
794 const char text[], size_t byteLength,
795 const SkScalar pos[], int scalarsPerPosition,
796 const SkPoint& offset, const SkIRect& regionClipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -0700797 SkAutoTUnref<BitmapTextBlob> blob(
798 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
799 text, byteLength,
800 pos, scalarsPerPosition,
801 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700802
robertphillips2334fb62015-06-17 05:43:33 -0700803 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700804}
805
806void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
807 SkGlyphCache* cache, const SkPaint& skPaint,
808 GrColor color,
809 const SkMatrix& viewMatrix,
810 const char text[], size_t byteLength,
811 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700812 SkASSERT(byteLength == 0 || text != NULL);
813
814 // nothing to draw
815 if (text == NULL || byteLength == 0) {
816 return;
817 }
818
819 fCurrStrike = NULL;
820 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
821
822 // Get GrFontScaler from cache
823 GrFontScaler* fontScaler = GetGrFontScaler(cache);
824
825 // transform our starting point
826 {
827 SkPoint loc;
828 viewMatrix.mapXY(x, y, &loc);
829 x = loc.fX;
830 y = loc.fY;
831 }
832
833 // need to measure first
834 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
835 SkVector stopVector;
836 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
837
838 SkScalar stopX = stopVector.fX;
839 SkScalar stopY = stopVector.fY;
840
841 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
842 stopX = SkScalarHalf(stopX);
843 stopY = SkScalarHalf(stopY);
844 }
845 x -= stopX;
846 y -= stopY;
847 }
848
849 const char* stop = text + byteLength;
850
851 SkAutoKern autokern;
852
853 SkFixed fxMask = ~0;
854 SkFixed fyMask = ~0;
855 SkScalar halfSampleX, halfSampleY;
856 if (cache->isSubpixel()) {
857 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
858 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
859 if (kX_SkAxisAlignment == baseline) {
860 fyMask = 0;
861 halfSampleY = SK_ScalarHalf;
862 } else if (kY_SkAxisAlignment == baseline) {
863 fxMask = 0;
864 halfSampleX = SK_ScalarHalf;
865 }
866 } else {
867 halfSampleX = halfSampleY = SK_ScalarHalf;
868 }
869
870 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
871 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
872
873 while (text < stop) {
874 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
875
876 fx += autokern.adjust(glyph);
877
878 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700879 this->bmpAppendGlyph(blob,
880 runIndex,
881 GrGlyph::Pack(glyph.getGlyphID(),
882 glyph.getSubXFixed(),
883 glyph.getSubYFixed(),
884 GrGlyph::kCoverage_MaskStyle),
885 Sk48Dot16FloorToInt(fx),
886 Sk48Dot16FloorToInt(fy),
887 color,
888 fontScaler,
889 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700890 }
891
892 fx += glyph.fAdvanceX;
893 fy += glyph.fAdvanceY;
894 }
895}
896
joshualitt9bd2daf2015-04-17 09:30:06 -0700897void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
898 SkGlyphCache* cache, const SkPaint& skPaint,
899 GrColor color,
900 const SkMatrix& viewMatrix,
901 const char text[], size_t byteLength,
902 const SkScalar pos[], int scalarsPerPosition,
903 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700904 SkASSERT(byteLength == 0 || text != NULL);
905 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
906
907 // nothing to draw
908 if (text == NULL || byteLength == 0) {
909 return;
910 }
911
912 fCurrStrike = NULL;
913 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
914
915 // Get GrFontScaler from cache
916 GrFontScaler* fontScaler = GetGrFontScaler(cache);
917
918 const char* stop = text + byteLength;
919 SkTextAlignProc alignProc(skPaint.getTextAlign());
920 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
921
922 if (cache->isSubpixel()) {
923 // maybe we should skip the rounding if linearText is set
924 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
925
926 SkFixed fxMask = ~0;
927 SkFixed fyMask = ~0;
928 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
929 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
930 if (kX_SkAxisAlignment == baseline) {
931 fyMask = 0;
932 halfSampleY = SK_ScalarHalf;
933 } else if (kY_SkAxisAlignment == baseline) {
934 fxMask = 0;
935 halfSampleX = SK_ScalarHalf;
936 }
937
938 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
939 while (text < stop) {
940 SkPoint tmsLoc;
941 tmsProc(pos, &tmsLoc);
942 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
943 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
944
945 const SkGlyph& glyph = glyphCacheProc(cache, &text,
946 fx & fxMask, fy & fyMask);
947
948 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700949 this->bmpAppendGlyph(blob,
950 runIndex,
951 GrGlyph::Pack(glyph.getGlyphID(),
952 glyph.getSubXFixed(),
953 glyph.getSubYFixed(),
954 GrGlyph::kCoverage_MaskStyle),
955 Sk48Dot16FloorToInt(fx),
956 Sk48Dot16FloorToInt(fy),
957 color,
958 fontScaler,
959 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700960 }
961 pos += scalarsPerPosition;
962 }
963 } else {
964 while (text < stop) {
965 const char* currentText = text;
966 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
967
968 if (metricGlyph.fWidth) {
969 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
970 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
971 SkPoint tmsLoc;
972 tmsProc(pos, &tmsLoc);
973 SkPoint alignLoc;
974 alignProc(tmsLoc, metricGlyph, &alignLoc);
975
976 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
977 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
978
979 // have to call again, now that we've been "aligned"
980 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
981 fx & fxMask, fy & fyMask);
982 // the assumption is that the metrics haven't changed
983 SkASSERT(prevAdvX == glyph.fAdvanceX);
984 SkASSERT(prevAdvY == glyph.fAdvanceY);
985 SkASSERT(glyph.fWidth);
986
joshualitt9bd2daf2015-04-17 09:30:06 -0700987 this->bmpAppendGlyph(blob,
988 runIndex,
989 GrGlyph::Pack(glyph.getGlyphID(),
990 glyph.getSubXFixed(),
991 glyph.getSubYFixed(),
992 GrGlyph::kCoverage_MaskStyle),
993 Sk48Dot16FloorToInt(fx),
994 Sk48Dot16FloorToInt(fy),
995 color,
996 fontScaler,
997 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700998 }
999 pos += scalarsPerPosition;
1000 }
1001 }
1002 } else { // not subpixel
1003
1004 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1005 while (text < stop) {
1006 // the last 2 parameters are ignored
1007 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1008
1009 if (glyph.fWidth) {
1010 SkPoint tmsLoc;
1011 tmsProc(pos, &tmsLoc);
1012
1013 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1014 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001015 this->bmpAppendGlyph(blob,
1016 runIndex,
1017 GrGlyph::Pack(glyph.getGlyphID(),
1018 glyph.getSubXFixed(),
1019 glyph.getSubYFixed(),
1020 GrGlyph::kCoverage_MaskStyle),
1021 Sk48Dot16FloorToInt(fx),
1022 Sk48Dot16FloorToInt(fy),
1023 color,
1024 fontScaler,
1025 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001026 }
1027 pos += scalarsPerPosition;
1028 }
1029 } else {
1030 while (text < stop) {
1031 // the last 2 parameters are ignored
1032 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1033
1034 if (glyph.fWidth) {
1035 SkPoint tmsLoc;
1036 tmsProc(pos, &tmsLoc);
1037
1038 SkPoint alignLoc;
1039 alignProc(tmsLoc, glyph, &alignLoc);
1040
1041 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1042 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001043 this->bmpAppendGlyph(blob,
1044 runIndex,
1045 GrGlyph::Pack(glyph.getGlyphID(),
1046 glyph.getSubXFixed(),
1047 glyph.getSubYFixed(),
1048 GrGlyph::kCoverage_MaskStyle),
1049 Sk48Dot16FloorToInt(fx),
1050 Sk48Dot16FloorToInt(fy),
1051 color,
1052 fontScaler,
1053 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001054 }
1055 pos += scalarsPerPosition;
1056 }
1057 }
1058 }
1059}
1060
joshualitt9bd2daf2015-04-17 09:30:06 -07001061
1062void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1063 SkGlyphCache* cache, const SkPaint& skPaint,
1064 GrColor color,
1065 const SkMatrix& viewMatrix,
1066 const char text[], size_t byteLength,
1067 SkScalar x, SkScalar y, const SkIRect& clipRect,
1068 SkScalar textRatio,
1069 SkTDArray<char>* fallbackTxt,
1070 SkTDArray<SkScalar>* fallbackPos,
1071 SkPoint* offset,
1072 const SkPaint& origPaint) {
1073 SkASSERT(byteLength == 0 || text != NULL);
1074
1075 // nothing to draw
1076 if (text == NULL || byteLength == 0) {
1077 return;
1078 }
1079
1080 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1081 SkAutoDescriptor desc;
1082 origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
1083 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1084 desc.getDesc());
1085
1086 SkTArray<SkScalar> positions;
1087
1088 const char* textPtr = text;
1089 SkFixed stopX = 0;
1090 SkFixed stopY = 0;
1091 SkFixed origin = 0;
1092 switch (origPaint.getTextAlign()) {
1093 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1094 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1095 case SkPaint::kLeft_Align: origin = 0; break;
1096 }
1097
1098 SkAutoKern autokern;
1099 const char* stop = text + byteLength;
1100 while (textPtr < stop) {
1101 // don't need x, y here, since all subpixel variants will have the
1102 // same advance
1103 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1104
1105 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1106 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1107
1108 SkFixed height = glyph.fAdvanceY;
1109 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1110
1111 stopX += width;
1112 stopY += height;
1113 }
1114 SkASSERT(textPtr == stop);
1115
1116 // now adjust starting point depending on alignment
1117 SkScalar alignX = SkFixedToScalar(stopX);
1118 SkScalar alignY = SkFixedToScalar(stopY);
1119 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1120 alignX = SkScalarHalf(alignX);
1121 alignY = SkScalarHalf(alignY);
1122 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1123 alignX = 0;
1124 alignY = 0;
1125 }
1126 x -= alignX;
1127 y -= alignY;
1128 *offset = SkPoint::Make(x, y);
1129
1130 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1131 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1132 fallbackPos);
1133 SkGlyphCache::AttachCache(origPaintCache);
1134}
1135
1136void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1137 SkGlyphCache* cache, const SkPaint& skPaint,
1138 GrColor color,
1139 const SkMatrix& viewMatrix,
1140 const char text[], size_t byteLength,
1141 const SkScalar pos[], int scalarsPerPosition,
1142 const SkPoint& offset, const SkIRect& clipRect,
1143 SkScalar textRatio,
1144 SkTDArray<char>* fallbackTxt,
1145 SkTDArray<SkScalar>* fallbackPos) {
1146
1147 SkASSERT(byteLength == 0 || text != NULL);
1148 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1149
1150 // nothing to draw
1151 if (text == NULL || byteLength == 0) {
1152 return;
1153 }
1154
1155 fCurrStrike = NULL;
1156
1157 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1158 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1159
1160 const char* stop = text + byteLength;
1161
1162 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1163 while (text < stop) {
1164 const char* lastText = text;
1165 // the last 2 parameters are ignored
1166 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1167
1168 if (glyph.fWidth) {
1169 SkScalar x = offset.x() + pos[0];
1170 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1171
1172 if (!this->dfAppendGlyph(blob,
1173 runIndex,
1174 GrGlyph::Pack(glyph.getGlyphID(),
1175 glyph.getSubXFixed(),
1176 glyph.getSubYFixed(),
1177 GrGlyph::kDistance_MaskStyle),
1178 x, y, color, fontScaler, clipRect,
1179 textRatio, viewMatrix)) {
1180 // couldn't append, send to fallback
1181 fallbackTxt->append(SkToInt(text-lastText), lastText);
1182 *fallbackPos->append() = pos[0];
1183 if (2 == scalarsPerPosition) {
1184 *fallbackPos->append() = pos[1];
1185 }
1186 }
1187 }
1188 pos += scalarsPerPosition;
1189 }
1190 } else {
1191 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1192 : SK_Scalar1;
1193 while (text < stop) {
1194 const char* lastText = text;
1195 // the last 2 parameters are ignored
1196 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1197
1198 if (glyph.fWidth) {
1199 SkScalar x = offset.x() + pos[0];
1200 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1201
1202 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1203 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1204
1205 if (!this->dfAppendGlyph(blob,
1206 runIndex,
1207 GrGlyph::Pack(glyph.getGlyphID(),
1208 glyph.getSubXFixed(),
1209 glyph.getSubYFixed(),
1210 GrGlyph::kDistance_MaskStyle),
1211 x - advanceX, y - advanceY, color,
1212 fontScaler,
1213 clipRect,
1214 textRatio,
1215 viewMatrix)) {
1216 // couldn't append, send to fallback
1217 fallbackTxt->append(SkToInt(text-lastText), lastText);
1218 *fallbackPos->append() = pos[0];
1219 if (2 == scalarsPerPosition) {
1220 *fallbackPos->append() = pos[1];
1221 }
1222 }
1223 }
1224 pos += scalarsPerPosition;
1225 }
1226 }
1227}
1228
1229void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1230 GrGlyph::PackedID packed,
1231 int vx, int vy, GrColor color, GrFontScaler* scaler,
1232 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001233 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001234 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001235 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001236 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001237 }
1238
1239 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001240 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001241 return;
1242 }
1243
1244 int x = vx + glyph->fBounds.fLeft;
1245 int y = vy + glyph->fBounds.fTop;
1246
1247 // keep them as ints until we've done the clip-test
1248 int width = glyph->fBounds.width();
1249 int height = glyph->fBounds.height();
1250
joshualitt2a0e9f32015-04-13 06:12:21 -07001251#if 0
1252 // Not checking the clip bounds might introduce a performance regression. However, its not
1253 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1254 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1255 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1256 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001257 // check if we clipped out
1258 if (clipRect.quickReject(x, y, x + width, y + height)) {
1259 return;
1260 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001261#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001262
1263 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001264 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001265 this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001266 return;
1267 }
1268
joshualitt1d89e8d2015-04-01 12:40:54 -07001269 GrMaskFormat format = glyph->fMaskFormat;
1270
1271 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1272 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001273 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001274 }
1275
1276 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001277
1278 size_t vertexStride = get_vertex_stride(format);
1279
1280 SkRect r;
1281 r.fLeft = SkIntToScalar(x);
1282 r.fTop = SkIntToScalar(y);
1283 r.fRight = r.fLeft + SkIntToScalar(width);
1284 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001285 subRun->fMaskFormat = format;
1286 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001287 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001288}
joshualitt1d89e8d2015-04-01 12:40:54 -07001289
joshualitt9bd2daf2015-04-17 09:30:06 -07001290bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1291 GrGlyph::PackedID packed,
1292 SkScalar sx, SkScalar sy, GrColor color,
1293 GrFontScaler* scaler,
1294 const SkIRect& clipRect,
1295 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001296 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001297 if (!fCurrStrike) {
1298 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001299 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001300 }
1301
1302 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001303 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001304 return true;
1305 }
1306
1307 // fallback to color glyph support
1308 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1309 return false;
1310 }
1311
1312 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1313 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1314 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1315 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1316
1317 SkScalar scale = textRatio;
1318 dx *= scale;
1319 dy *= scale;
1320 width *= scale;
1321 height *= scale;
1322 sx += dx;
1323 sy += dy;
1324 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1325
1326#if 0
1327 // check if we clipped out
1328 SkRect dstRect;
1329 viewMatrix.mapRect(&dstRect, glyphRect);
1330 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1331 SkScalarTruncToInt(dstRect.top()),
1332 SkScalarTruncToInt(dstRect.right()),
1333 SkScalarTruncToInt(dstRect.bottom()))) {
1334 return true;
1335 }
1336#endif
1337
1338 // TODO combine with the above
1339 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001340 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001341 this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001342 return true;
1343 }
1344
joshualitt9bd2daf2015-04-17 09:30:06 -07001345 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1346 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1347 subRun->fMaskFormat = kA8_GrMaskFormat;
1348
1349 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1350
1351 bool useColorVerts = !subRun->fUseLCDText;
1352 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001353 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001354 return true;
1355}
1356
1357inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
joshualitt19e4c022015-05-13 11:23:03 -07001358 GrFontScaler* scaler, SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001359 if (NULL == glyph->fPath) {
1360 SkPath* path = SkNEW(SkPath);
1361 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1362 // flag the glyph as being dead?
1363 SkDELETE(path);
1364 return;
1365 }
1366 glyph->fPath = path;
1367 }
1368 SkASSERT(glyph->fPath);
1369 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1370}
1371
1372inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1373 Run::SubRunInfo* subRun,
1374 const SkRect& positions, GrColor color,
1375 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001376 GrGlyph* glyph) {
1377 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001378 run->fVertexBounds.joinNonEmptyArg(positions);
1379 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001380
1381 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1382
joshualitt9bd2daf2015-04-17 09:30:06 -07001383 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001384 // V0
1385 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1386 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001387 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1388 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001389 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001390
joshualitt010db532015-04-21 10:07:26 -07001391 // V1
1392 position = reinterpret_cast<SkPoint*>(vertex);
1393 position->set(positions.fLeft, positions.fBottom);
1394 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001395 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001396 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001397
joshualitt010db532015-04-21 10:07:26 -07001398 // V2
1399 position = reinterpret_cast<SkPoint*>(vertex);
1400 position->set(positions.fRight, positions.fBottom);
1401 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001402 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001403 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001404
joshualitt010db532015-04-21 10:07:26 -07001405 // V3
1406 position = reinterpret_cast<SkPoint*>(vertex);
1407 position->set(positions.fRight, positions.fTop);
1408 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001409 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001410 } else {
1411 // V0
1412 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1413 position->set(positions.fLeft, positions.fTop);
1414 vertex += vertexStride;
1415
1416 // V1
1417 position = reinterpret_cast<SkPoint*>(vertex);
1418 position->set(positions.fLeft, positions.fBottom);
1419 vertex += vertexStride;
1420
1421 // V2
1422 position = reinterpret_cast<SkPoint*>(vertex);
1423 position->set(positions.fRight, positions.fBottom);
1424 vertex += vertexStride;
1425
1426 // V3
1427 position = reinterpret_cast<SkPoint*>(vertex);
1428 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001429 }
1430
1431 subRun->fGlyphEndIndex++;
1432 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1433}
1434
1435class BitmapTextBatch : public GrBatch {
1436public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001437 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualittdbd35932015-04-02 09:19:04 -07001438 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001439 typedef Blob::Run Run;
1440 typedef Run::SubRunInfo TextInfo;
1441 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001442 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001443 int fRun;
1444 int fSubRun;
1445 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001446 SkScalar fTransX;
1447 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001448 };
1449
joshualittad802c62015-04-15 05:31:57 -07001450 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1451 GrBatchFontCache* fontCache) {
1452 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -07001453 }
1454
joshualitt9bd2daf2015-04-17 09:30:06 -07001455 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1456 GrBatchFontCache* fontCache,
1457 DistanceAdjustTable* distanceAdjustTable,
1458 SkColor filteredColor, bool useLCDText,
1459 bool useBGR, float gamma) {
1460 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
1461 filteredColor, useLCDText, useBGR, gamma));
1462 }
1463
joshualitt1d89e8d2015-04-01 12:40:54 -07001464 const char* name() const override { return "BitmapTextBatch"; }
1465
1466 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1467 if (kARGB_GrMaskFormat == fMaskFormat) {
1468 out->setUnknownFourComponents();
1469 } else {
1470 out->setKnownFourComponents(fBatch.fColor);
1471 }
1472 }
1473
1474 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt9bd2daf2015-04-17 09:30:06 -07001475 if (!fUseDistanceFields) {
1476 // Bitmap Text
1477 if (kARGB_GrMaskFormat != fMaskFormat) {
1478 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
1479 out->setUnknownSingleComponent();
1480 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
1481 out->setUnknownOpaqueFourComponents();
1482 out->setUsingLCDCoverage();
1483 } else {
1484 out->setUnknownFourComponents();
1485 out->setUsingLCDCoverage();
1486 }
1487 } else {
1488 out->setKnownSingleComponent(0xff);
1489 }
1490 } else {
1491 // Distance fields
1492 if (!fUseLCDText) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001493 out->setUnknownSingleComponent();
joshualitt1d89e8d2015-04-01 12:40:54 -07001494 } else {
1495 out->setUnknownFourComponents();
1496 out->setUsingLCDCoverage();
1497 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001498 }
1499 }
1500
1501 void initBatchTracker(const GrPipelineInfo& init) override {
1502 // Handle any color overrides
1503 if (init.fColorIgnored) {
1504 fBatch.fColor = GrColor_ILLEGAL;
1505 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1506 fBatch.fColor = init.fOverrideColor;
1507 }
1508
1509 // setup batch properties
1510 fBatch.fColorIgnored = init.fColorIgnored;
1511 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1512 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1513 }
1514
bsalomonb5238a72015-05-05 07:49:49 -07001515 struct FlushInfo {
1516 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1517 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1518 int fGlyphsToFlush;
1519 int fVertexOffset;
1520 };
1521
joshualitt1d89e8d2015-04-01 12:40:54 -07001522 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1523 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1524 // TODO actually only invert if we don't have RGBA
1525 SkMatrix localMatrix;
1526 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1527 SkDebugf("Cannot invert viewmatrix\n");
1528 return;
1529 }
1530
joshualitt62db8ba2015-04-09 08:22:37 -07001531 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
1532 if (!texture) {
1533 SkDebugf("Could not allocate backing texture for atlas\n");
1534 return;
1535 }
1536
joshualitt9bd2daf2015-04-17 09:30:06 -07001537 SkAutoTUnref<const GrGeometryProcessor> gp;
1538 if (fUseDistanceFields) {
1539 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1540 texture));
1541 } else {
1542 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001543 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1544 texture,
1545 params,
1546 fMaskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001547 localMatrix,
1548 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001549 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001550
bsalomonb5238a72015-05-05 07:49:49 -07001551 FlushInfo flushInfo;
1552 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001553 size_t vertexStride = gp->getVertexStride();
joshualitt9bd2daf2015-04-17 09:30:06 -07001554 SkASSERT(vertexStride == (fUseDistanceFields ?
1555 get_vertex_stride_df(fMaskFormat, fUseLCDText) :
1556 get_vertex_stride(fMaskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001557
joshualittb8c241a2015-05-19 08:23:30 -07001558 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001559
1560 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001561 int instanceCount = fInstanceCount;
bsalomon8415abe2015-05-04 11:41:41 -07001562 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001563
robertphillipse40d3972015-05-07 09:51:43 -07001564 void* vertices = batchTarget->makeVertSpace(vertexStride,
1565 glyphCount * kVerticesPerGlyph,
1566 &vertexBuffer,
1567 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001568 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1569 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1570 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001571 SkDebugf("Could not allocate vertices\n");
1572 return;
1573 }
1574
1575 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1576
joshualitt25ba7ea2015-04-21 07:49:49 -07001577 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1578 // in a row
1579 const SkDescriptor* desc = NULL;
1580 SkGlyphCache* cache = NULL;
1581 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001582 SkTypeface* typeface = NULL;
1583
joshualitt1d89e8d2015-04-01 12:40:54 -07001584 for (int i = 0; i < instanceCount; i++) {
1585 Geometry& args = fGeoData[i];
1586 Blob* blob = args.fBlob;
1587 Run& run = blob->fRuns[args.fRun];
1588 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1589
1590 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001591 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
1592 run.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001593 bool regenerateColors;
1594 if (fUseDistanceFields) {
joshualittfcfb9fc2015-04-21 07:35:10 -07001595 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001596 } else {
1597 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
1598 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001599 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001600 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1601
1602 // We regenerate both texture coords and colors in the blob itself, and update the
1603 // atlas generation. If we don't end up purging any unused plots, we can avoid
1604 // regenerating the coords. We could take a finer grained approach to updating texture
1605 // coords but its not clear if the extra bookkeeping would offset any gains.
1606 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1607 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1608 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1609 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001610 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001611 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001612 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001613
1614 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1615 // generating its texture coords, we have to track whether or not the strike has
1616 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1617 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1618 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1619 // new strike, we instead keep our ref to the old strike and use the packed ids from
1620 // it. These ids will still be valid as long as we hold the ref. When we are done
1621 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1622 bool regenerateGlyphs = false;
1623 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001624 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001625 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001626
1627 // We can reuse if we have a valid strike and our descriptors / typeface are the
1628 // same
joshualitt97202d22015-04-22 13:47:02 -07001629 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1630 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001631 run.fDescriptor.getDesc();
1632 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1633 !(desc->equals(*newDesc))) {
1634 if (cache) {
1635 SkGlyphCache::AttachCache(cache);
1636 }
1637 desc = newDesc;
1638 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1639 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001640 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001641 typeface = run.fTypeface;
1642 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001643
joshualittae32c102015-04-21 09:37:57 -07001644 if (run.fStrike->isAbandoned()) {
1645 regenerateGlyphs = true;
1646 strike = fFontCache->getStrike(scaler);
1647 } else {
1648 strike = run.fStrike;
1649 }
1650 }
1651
1652 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001653 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001654 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1655 GrGlyph* glyph;
1656 if (regenerateGlyphs) {
1657 // Get the id from the old glyph, and use the new strike to lookup
1658 // the glyph.
1659 glyph = blob->fGlyphs[glyphOffset];
1660 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1661 scaler);
1662 }
1663 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001664 SkASSERT(glyph);
1665
1666 if (!fFontCache->hasGlyph(glyph) &&
1667 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
bsalomonb5238a72015-05-05 07:49:49 -07001668 this->flush(batchTarget, &flushInfo);
joshualittb8c241a2015-05-19 08:23:30 -07001669 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001670 brokenRun = glyphIdx > 0;
1671
joshualittae32c102015-04-21 09:37:57 -07001672 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1673 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001674 scaler);
1675 SkASSERT(success);
1676 }
joshualittb4c507e2015-04-08 08:07:59 -07001677 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1678 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001679
1680 // Texture coords are the last vertex attribute so we get a pointer to the
1681 // first one and then map with stride in regenerateTextureCoords
1682 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1683 vertex += info.fVertexStartIndex;
1684 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1685 vertex += vertexStride - sizeof(SkIPoint16);
1686
1687 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1688 }
1689
1690 if (regenerateColors) {
1691 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1692 vertex += info.fVertexStartIndex;
1693 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1694 this->regenerateColors(vertex, vertexStride, args.fColor);
1695 }
1696
joshualitt2a0e9f32015-04-13 06:12:21 -07001697 if (regeneratePositions) {
1698 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1699 vertex += info.fVertexStartIndex;
1700 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1701 SkScalar transX = args.fTransX;
1702 SkScalar transY = args.fTransY;
1703 this->regeneratePositions(vertex, vertexStride, transX, transY);
1704 }
bsalomonb5238a72015-05-05 07:49:49 -07001705 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001706 }
1707
joshualitt2a0e9f32015-04-13 06:12:21 -07001708 // We my have changed the color so update it here
1709 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001710 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001711 if (regenerateGlyphs) {
1712 run.fStrike.reset(SkRef(strike));
1713 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001714 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
1715 fFontCache->atlasGeneration(fMaskFormat);
1716 }
1717 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001718 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001719
1720 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1721 // have a valid atlas generation
1722 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1723 batchTarget->currentToken(),
1724 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001725 }
1726
1727 // now copy all vertices
1728 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1729 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1730
1731 currVertex += byteCount;
1732 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001733 // Make sure to attach the last cache if applicable
1734 if (cache) {
1735 SkGlyphCache::AttachCache(cache);
1736 }
bsalomonb5238a72015-05-05 07:49:49 -07001737 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001738 }
1739
joshualittad802c62015-04-15 05:31:57 -07001740 // The minimum number of Geometry we will try to allocate.
1741 static const int kMinAllocated = 32;
1742
1743 // Total number of Geometry this Batch owns
1744 int instanceCount() const { return fInstanceCount; }
1745 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1746
1747 // to avoid even the initial copy of the struct, we have a getter for the first item which
1748 // is used to seed the batch with its initial geometry. After seeding, the client should call
1749 // init() so the Batch can initialize itself
1750 Geometry& geometry() { return fGeoData[0]; }
1751 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001752 const Geometry& geo = fGeoData[0];
1753 fBatch.fColor = geo.fColor;
1754 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1755
1756 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1757 // into device space
1758 const Run& run = geo.fBlob->fRuns[geo.fRun];
1759 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1760 SkRect bounds = run.fVertexBounds;
1761 fBatch.fViewMatrix.mapRect(&bounds);
1762 this->setBounds(bounds);
1763 } else {
1764 this->setBounds(run.fVertexBounds);
1765 }
joshualittad802c62015-04-15 05:31:57 -07001766 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001767
1768private:
joshualitt9bd2daf2015-04-17 09:30:06 -07001769 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
joshualitt1d89e8d2015-04-01 12:40:54 -07001770 : fMaskFormat(maskFormat)
1771 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
joshualitt9bd2daf2015-04-17 09:30:06 -07001772 , fFontCache(fontCache)
1773 , fUseDistanceFields(false) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001774 this->initClassID<BitmapTextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001775 fBatch.fNumGlyphs = glyphCount;
joshualittad802c62015-04-15 05:31:57 -07001776 fInstanceCount = 1;
1777 fAllocatedCount = kMinAllocated;
1778 }
1779
joshualitt9bd2daf2015-04-17 09:30:06 -07001780 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
1781 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
1782 bool useLCDText, bool useBGR, float gamma)
1783 : fMaskFormat(maskFormat)
1784 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
1785 , fFontCache(fontCache)
1786 , fDistanceAdjustTable(SkRef(distanceAdjustTable))
1787 , fFilteredColor(filteredColor)
1788 , fUseDistanceFields(true)
1789 , fUseLCDText(useLCDText)
1790 , fUseBGR(useBGR)
1791 , fGamma(gamma) {
1792 this->initClassID<BitmapTextBatch>();
1793 fBatch.fNumGlyphs = glyphCount;
1794 fInstanceCount = 1;
1795 fAllocatedCount = kMinAllocated;
1796 SkASSERT(fMaskFormat == kA8_GrMaskFormat);
1797 }
1798
joshualittad802c62015-04-15 05:31:57 -07001799 ~BitmapTextBatch() {
1800 for (int i = 0; i < fInstanceCount; i++) {
1801 fGeoData[i].fBlob->unref();
1802 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001803 }
1804
1805 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1806 int width = glyph->fBounds.width();
1807 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001808
joshualitt9bd2daf2015-04-17 09:30:06 -07001809 int u0, v0, u1, v1;
1810 if (fUseDistanceFields) {
1811 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1812 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1813 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1814 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1815 } else {
1816 u0 = glyph->fAtlasLocation.fX;
1817 v0 = glyph->fAtlasLocation.fY;
1818 u1 = u0 + width;
1819 v1 = v0 + height;
1820 }
1821
joshualitt1d89e8d2015-04-01 12:40:54 -07001822 SkIPoint16* textureCoords;
1823 // V0
1824 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1825 textureCoords->set(u0, v0);
1826 vertex += vertexStride;
1827
1828 // V1
1829 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1830 textureCoords->set(u0, v1);
1831 vertex += vertexStride;
1832
1833 // V2
1834 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1835 textureCoords->set(u1, v1);
1836 vertex += vertexStride;
1837
1838 // V3
1839 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1840 textureCoords->set(u1, v0);
1841 }
1842
1843 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1844 for (int i = 0; i < kVerticesPerGlyph; i++) {
1845 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1846 *vcolor = color;
1847 vertex += vertexStride;
1848 }
1849 }
1850
joshualitt2a0e9f32015-04-13 06:12:21 -07001851 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1852 SkScalar transY) {
1853 for (int i = 0; i < kVerticesPerGlyph; i++) {
1854 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1855 point->fX += transX;
1856 point->fY += transY;
1857 vertex += vertexStride;
1858 }
1859 }
1860
bsalomonb5238a72015-05-05 07:49:49 -07001861 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001862 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001863 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001864 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001865 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001866 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001867 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001868 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001869 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1870 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001871 }
1872
1873 GrColor color() const { return fBatch.fColor; }
1874 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1875 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1876 int numGlyphs() const { return fBatch.fNumGlyphs; }
1877
1878 bool onCombineIfPossible(GrBatch* t) override {
1879 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1880
joshualitt9bd2daf2015-04-17 09:30:06 -07001881 if (fUseDistanceFields != that->fUseDistanceFields) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001882 return false;
1883 }
1884
joshualitt9bd2daf2015-04-17 09:30:06 -07001885 if (!fUseDistanceFields) {
1886 // Bitmap Text
1887 if (fMaskFormat != that->fMaskFormat) {
1888 return false;
1889 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001890
joshualitt9bd2daf2015-04-17 09:30:06 -07001891 // TODO we can often batch across LCD text if we have dual source blending and don't
1892 // have to use the blend constant
1893 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1894 return false;
1895 }
1896
1897 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1898 return false;
1899 }
1900 } else {
1901 // Distance Fields
1902 SkASSERT(this->fMaskFormat == that->fMaskFormat &&
1903 this->fMaskFormat == kA8_GrMaskFormat);
1904
1905 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1906 return false;
1907 }
1908
1909 if (fFilteredColor != that->fFilteredColor) {
1910 return false;
1911 }
1912
1913 if (fUseLCDText != that->fUseLCDText) {
1914 return false;
1915 }
1916
1917 if (fUseBGR != that->fUseBGR) {
1918 return false;
1919 }
1920
1921 if (fGamma != that->fGamma) {
1922 return false;
1923 }
1924
1925 // TODO see note above
1926 if (fUseLCDText && this->color() != that->color()) {
1927 return false;
1928 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001929 }
1930
1931 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001932
1933 // copy that->geoData(). We do this manually for performance reasons
1934 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1935 int otherInstanceCount = that->instanceCount();
1936 int allocSize = otherInstanceCount + fInstanceCount;
1937 if (allocSize > fAllocatedCount) {
1938 while (allocSize > fAllocatedCount) {
1939 fAllocatedCount = fAllocatedCount << 1;
1940 }
1941 fGeoData.realloc(fAllocatedCount);
1942 }
1943
1944 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1945 otherInstanceCount * sizeof(Geometry));
1946 int total = fInstanceCount + otherInstanceCount;
1947 for (int i = fInstanceCount; i < total; i++) {
1948 fGeoData[i].fBlob->ref();
1949 }
1950 fInstanceCount = total;
joshualitt99c7c072015-05-01 13:43:30 -07001951
1952 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001953 return true;
1954 }
1955
joshualitt9bd2daf2015-04-17 09:30:06 -07001956 // TODO just use class params
1957 // TODO trying to figure out why lcd is so whack
1958 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1959 GrColor color, GrTexture* texture) {
1960 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
1961
1962 // set up any flags
1963 uint32_t flags = 0;
1964 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1965 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
1966 flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
1967 kRectToRect_DistanceFieldEffectFlag : 0;
1968 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1969
1970 // see if we need to create a new effect
1971 if (fUseLCDText) {
1972 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1973
1974 float redCorrection =
1975 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1976 float greenCorrection =
1977 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1978 float blueCorrection =
1979 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1980 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1981 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1982 greenCorrection,
1983 blueCorrection);
1984
1985 return GrDistanceFieldLCDTextGeoProc::Create(color,
1986 viewMatrix,
1987 texture,
1988 params,
1989 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001990 flags,
1991 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001992 } else {
1993 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07001994#ifdef SK_GAMMA_APPLY_TO_A8
1995 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
1996 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
1997 return GrDistanceFieldA8TextGeoProc::Create(color,
1998 viewMatrix,
1999 texture,
2000 params,
2001 correction,
joshualittb8c241a2015-05-19 08:23:30 -07002002 flags,
2003 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002004#else
2005 return GrDistanceFieldA8TextGeoProc::Create(color,
2006 viewMatrix,
2007 texture,
2008 params,
joshualittb8c241a2015-05-19 08:23:30 -07002009 flags,
2010 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002011#endif
2012 }
2013
2014 }
2015
joshualitt1d89e8d2015-04-01 12:40:54 -07002016 struct BatchTracker {
2017 GrColor fColor;
2018 SkMatrix fViewMatrix;
2019 bool fUsesLocalCoords;
2020 bool fColorIgnored;
2021 bool fCoverageIgnored;
2022 int fNumGlyphs;
2023 };
2024
2025 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002026 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2027 int fInstanceCount;
2028 int fAllocatedCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07002029 GrMaskFormat fMaskFormat;
2030 GrPixelConfig fPixelConfig;
2031 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002032
2033 // Distance field properties
2034 SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
2035 SkColor fFilteredColor;
2036 bool fUseDistanceFields;
2037 bool fUseLCDText;
2038 bool fUseBGR;
2039 float fGamma;
joshualitt1d89e8d2015-04-01 12:40:54 -07002040};
2041
robertphillips2334fb62015-06-17 05:43:33 -07002042void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002043 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002044 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2045 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2046 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002047
joshualitt9a27e632015-04-06 10:53:36 -07002048 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2049 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002050
joshualitt9a27e632015-04-06 10:53:36 -07002051 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002052
joshualitt9a27e632015-04-06 10:53:36 -07002053 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2054 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002055 }
2056
robertphillips9c240a12015-05-28 07:45:59 -07002057 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002058
2059 switch (it.positioning()) {
2060 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002061 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002062 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002063 textLen, x + offset.x(), y + offset.y(), clipBounds);
2064 break;
2065 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002066 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002067 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002068 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2069 clipBounds);
2070 break;
2071 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002072 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002073 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002074 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
robertphillips2334fb62015-06-17 05:43:33 -07002124inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt9a27e632015-04-06 10:53:36 -07002125 BitmapTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002126 SkScalar transX, SkScalar transY,
2127 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002128 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2129 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2130 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2131 if (0 == glyphCount) {
2132 continue;
2133 }
2134
joshualitt79dfb2b2015-05-11 08:58:08 -07002135 SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2136 subRun, color, transX, transY,
2137 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002138 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002139 }
2140}
2141
robertphillips2334fb62015-06-17 05:43:33 -07002142inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002143 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002144 SkScalar transX, SkScalar transY,
2145 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002146 if (!cacheBlob->fBigGlyphs.count()) {
2147 return;
2148 }
2149
2150 SkMatrix pathMatrix;
2151 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2152 SkDebugf("could not invert viewmatrix\n");
2153 return;
2154 }
2155
joshualitt9a27e632015-04-06 10:53:36 -07002156 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07002157 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002158 bigGlyph.fVx += transX;
2159 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002160 SkMatrix translate = cacheBlob->fViewMatrix;
2161 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2162
robertphillips2334fb62015-06-17 05:43:33 -07002163 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002164 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002165 }
2166}
joshualitt9a27e632015-04-06 10:53:36 -07002167
robertphillips2334fb62015-06-17 05:43:33 -07002168void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt9a27e632015-04-06 10:53:36 -07002169 BitmapTextBlob* cacheBlob,
2170 GrRenderTarget* rt,
2171 const SkPaint& skPaint,
2172 const GrPaint& grPaint,
2173 SkDrawFilter* drawFilter,
2174 const GrClip& clip,
2175 const SkMatrix& viewMatrix,
2176 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002177 SkScalar x, SkScalar y,
2178 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002179 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2180 // it as paths
2181 GrPipelineBuilder pipelineBuilder;
2182 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2183
2184 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002185
2186 SkTextBlob::RunIterator it(blob);
2187 for (int run = 0; !it.done(); it.next(), run++) {
2188 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002189 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002190 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002191 continue;
2192 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002193 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002194 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002195 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002196 }
2197
2198 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002199 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002200}
2201
robertphillips2334fb62015-06-17 05:43:33 -07002202void GrAtlasTextContext::flush(BitmapTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002203 GrRenderTarget* rt,
2204 const SkPaint& skPaint,
2205 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002206 const GrClip& clip,
2207 const SkIRect& clipBounds) {
joshualitt9a27e632015-04-06 10:53:36 -07002208 GrPipelineBuilder pipelineBuilder;
2209 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2210
2211 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002212 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillips2334fb62015-06-17 05:43:33 -07002213 this->flushRun(&pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002214 }
2215
2216 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002217 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002218}
joshualitt79dfb2b2015-05-11 08:58:08 -07002219
2220///////////////////////////////////////////////////////////////////////////////////////////////////
2221
2222#ifdef GR_TEST_UTILS
2223
joshualitt6c891102015-05-13 08:51:49 -07002224BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002225 static uint32_t gContextID = SK_InvalidGenID;
2226 static GrAtlasTextContext* gTextContext = NULL;
robertphillips2334fb62015-06-17 05:43:33 -07002227 static SkDeviceProperties gDevProperties;
joshualitt79dfb2b2015-05-11 08:58:08 -07002228
2229 if (context->uniqueID() != gContextID) {
2230 gContextID = context->uniqueID();
2231 SkDELETE(gTextContext);
robertphillips2334fb62015-06-17 05:43:33 -07002232
2233 static const bool kUseDFT = false;
joshualitt79dfb2b2015-05-11 08:58:08 -07002234 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2235 // because we don't really want to have a gpu device here.
2236 // We enable distance fields by twiddling a knob on the paint
robertphillips2334fb62015-06-17 05:43:33 -07002237 GrDrawContext* drawContext = context->drawContext(&gDevProperties, kUseDFT);
2238
2239 gTextContext = GrAtlasTextContext::Create(context, drawContext, gDevProperties, kUseDFT);
joshualitt79dfb2b2015-05-11 08:58:08 -07002240 }
2241
2242 // create dummy render target
2243 GrSurfaceDesc desc;
2244 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2245 desc.fWidth = 1024;
2246 desc.fHeight = 1024;
2247 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002248 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002249 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2250 SkASSERT(texture);
2251 SkASSERT(NULL != texture->asRenderTarget());
2252 GrRenderTarget* rt = texture->asRenderTarget();
2253
2254 // Setup dummy SkPaint / GrPaint
2255 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002256 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002257 SkPaint skPaint;
2258 skPaint.setDistanceFieldTextTEMP(random->nextBool());
2259 skPaint.setColor(color);
2260 skPaint.setLCDRenderText(random->nextBool());
2261 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2262 skPaint.setSubpixelText(random->nextBool());
2263
2264 GrPaint grPaint;
2265 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2266 SkFAIL("couldn't convert paint\n");
2267 }
2268
2269 const char* text = "The quick brown fox jumps over the lazy dog.";
2270 int textLen = (int)strlen(text);
2271
2272 // Setup clip
2273 GrClip clip;
2274 SkIRect noClip = SkIRect::MakeLargest();
2275
2276 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2277 // intend to test the batch with this unit test, that is okay.
2278 SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
2279 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2280 static_cast<size_t>(textLen), 0, 0, noClip));
2281
2282 SkScalar transX = static_cast<SkScalar>(random->nextU());
2283 SkScalar transY = static_cast<SkScalar>(random->nextU());
2284 const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
2285 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2286}
2287
2288#endif