blob: f5976392cc64107fae2dbfa41cbaddb67e77c75d [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;
jvanverth97c595f2015-06-19 11:06:28 -070058#ifdef SK_BUILD_FOR_ANDROID
59static const int kLargeDFFontLimit = 384;
60#else
joshualitta7c63892015-04-21 13:24:37 -070061static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
jvanverth97c595f2015-06-19 11:06:28 -070062#endif
joshualitt9bd2daf2015-04-17 09:30:06 -070063
64SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
65static const int kDistanceAdjustLumShift = 5;
66
joshualitt1d89e8d2015-04-01 12:40:54 -070067static const int kVerticesPerGlyph = 4;
68static const int kIndicesPerGlyph = 6;
69
70static size_t get_vertex_stride(GrMaskFormat maskFormat) {
71 switch (maskFormat) {
72 case kA8_GrMaskFormat:
73 return kGrayTextVASize;
74 case kARGB_GrMaskFormat:
75 return kColorTextVASize;
76 default:
77 return kLCDTextVASize;
78 }
79}
80
joshualitt9bd2daf2015-04-17 09:30:06 -070081static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
82 SkASSERT(maskFormat == kA8_GrMaskFormat);
83 if (useLCDText) {
84 return kLCDTextVASize;
85 } else {
86 return kGrayTextVASize;
87 }
88}
89
90static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
91 unsigned r = SkColorGetR(c);
92 unsigned g = SkColorGetG(c);
93 unsigned b = SkColorGetB(c);
94 return GrColorPackRGBA(r, g, b, 0xff);
95}
96
joshualitt1d89e8d2015-04-01 12:40:54 -070097};
98
joshualittdbd35932015-04-02 09:19:04 -070099GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700100 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700101 const SkSurfaceProps& surfaceProps)
102 : INHERITED(context, drawContext, surfaceProps)
robertphillips9fc82752015-06-19 04:46:45 -0700103 , fDistanceAdjustTable(SkNEW(DistanceAdjustTable)) {
joshualittb7133be2015-04-08 09:08:31 -0700104 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
105 // vertexStride
106 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
107 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700108 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700109 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700110}
111
robertphillips9fc82752015-06-19 04:46:45 -0700112void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700113
114 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
115 // text. The mask gamma hack is based off of guessing what the blend color is going to
116 // be, and adjusting the mask so that when run through the linear blend will
117 // produce the value closest to the desired result. However, in practice this means
118 // that the 'adjusted' mask is just increasing or decreasing the coverage of
119 // the mask depending on what it is thought it will blit against. For black (on
120 // assumed white) this means that coverages are decreased (on a curve). For white (on
121 // assumed black) this means that coverages are increased (on a a curve). At
122 // middle (perceptual) gray (which could be blit against anything) the coverages
123 // remain the same.
124 //
125 // The idea here is that instead of determining the initial (real) coverage and
126 // then adjusting that coverage, we determine an adjusted coverage directly by
127 // essentially manipulating the geometry (in this case, the distance to the glyph
128 // edge). So for black (on assumed white) this thins a bit; for white (on
129 // assumed black) this fake bolds the geometry a bit.
130 //
131 // The distance adjustment is calculated by determining the actual coverage value which
132 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
133 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
134 // actual edge. So by subtracting this distance adjustment and computing without the
135 // the coverage adjustment we should get 0.5 coverage at the same point.
136 //
137 // This has several implications:
138 // For non-gray lcd smoothed text, each subpixel essentially is using a
139 // slightly different geometry.
140 //
141 // For black (on assumed white) this may not cover some pixels which were
142 // previously covered; however those pixels would have been only slightly
143 // covered and that slight coverage would have been decreased anyway. Also, some pixels
144 // which were previously fully covered may no longer be fully covered.
145 //
146 // For white (on assumed black) this may cover some pixels which weren't
147 // previously covered at all.
148
149 int width, height;
150 size_t size;
151
152#ifdef SK_GAMMA_CONTRAST
153 SkScalar contrast = SK_GAMMA_CONTRAST;
154#else
155 SkScalar contrast = 0.5f;
156#endif
robertphillips9fc82752015-06-19 04:46:45 -0700157 SkScalar paintGamma = SK_GAMMA_EXPONENT;
158 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700159
160 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
161 &width, &height);
162
163 SkASSERT(kExpectedDistanceAdjustTableSize == height);
164 fTable = SkNEW_ARRAY(SkScalar, height);
165
166 SkAutoTArray<uint8_t> data((int)size);
167 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
168
169 // find the inverse points where we cross 0.5
170 // binsearch might be better, but we only need to do this once on creation
171 for (int row = 0; row < height; ++row) {
172 uint8_t* rowPtr = data.get() + row*width;
173 for (int col = 0; col < width - 1; ++col) {
174 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
175 // compute point where a mask value will give us a result of 0.5
176 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
177 float borderAlpha = (col + interp) / 255.f;
178
179 // compute t value for that alpha
180 // this is an approximate inverse for smoothstep()
181 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
182
183 // compute distance which gives us that t value
184 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
185 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
186
187 fTable[row] = d;
188 break;
189 }
190 }
191 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700192}
193
joshualittdbd35932015-04-02 09:19:04 -0700194GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700195 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700196 const SkSurfaceProps& surfaceProps) {
197 return SkNEW_ARGS(GrAtlasTextContext, (context, drawContext, surfaceProps));
joshualitt1d89e8d2015-04-01 12:40:54 -0700198}
199
joshualittdbd35932015-04-02 09:19:04 -0700200bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
201 const GrClip&,
202 const GrPaint&,
203 const SkPaint& skPaint,
204 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700205 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
206 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700207}
208
joshualitt9e36c1a2015-04-14 12:17:27 -0700209GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
210 GrColor canonicalColor = paint.computeLuminanceColor();
211 if (lcd) {
212 // This is the correct computation, but there are tons of cases where LCD can be overridden.
213 // For now we just regenerate if any run in a textblob has LCD.
214 // TODO figure out where all of these overrides are and see if we can incorporate that logic
215 // at a higher level *OR* use sRGB
216 SkASSERT(false);
217 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
218 } else {
219 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
220 // gamma corrected masks anyways, nor color
221 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
222 SkColorGetG(canonicalColor),
223 SkColorGetB(canonicalColor));
224 // reduce to our finite number of bits
225 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
226 }
227 return canonicalColor;
228}
229
230// TODO if this function ever shows up in profiling, then we can compute this value when the
231// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
232// run so this is not a big deal to compute here.
233bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
234 SkTextBlob::RunIterator it(blob);
235 for (; !it.done(); it.next()) {
236 if (it.isLCD()) {
237 return true;
238 }
239 }
240 return false;
241}
242
joshualitt2a0e9f32015-04-13 06:12:21 -0700243bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
joshualitt374b2f72015-07-21 08:05:03 -0700244 const GrAtlasTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700245 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700246 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700247 // If we have LCD text then our canonical color will be set to transparent, in this case we have
248 // to regenerate the blob on any color change
249 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700250 return true;
251 }
252
253 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
254 return true;
255 }
256
257 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
258 return true;
259 }
260
joshualitt53b5f442015-04-13 06:33:59 -0700261 // We only cache one masked version
262 if (blob.fKey.fHasBlur &&
263 (blob.fBlurRec.fSigma != blurRec.fSigma ||
264 blob.fBlurRec.fStyle != blurRec.fStyle ||
265 blob.fBlurRec.fQuality != blurRec.fQuality)) {
266 return true;
267 }
268
269 // Similarly, we only cache one version for each style
270 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
271 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
272 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
273 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
274 return true;
275 }
276
joshualittfcfb9fc2015-04-21 07:35:10 -0700277 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
278 // for mixed blobs if this becomes an issue.
279 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700280 // Identical viewmatrices and we can reuse in all cases
281 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
282 return false;
283 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700284 return true;
285 }
286
joshualittfcfb9fc2015-04-21 07:35:10 -0700287 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700288 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
289 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
290 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
291 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
292 return true;
293 }
294
joshualittfcfb9fc2015-04-21 07:35:10 -0700295 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
296 // but only for integer translations.
297 // This cool bit of math will determine the necessary translation to apply to the already
298 // generated vertex coordinates to move them to the correct position
299 SkScalar transX = viewMatrix.getTranslateX() +
300 viewMatrix.getScaleX() * (x - blob.fX) +
301 viewMatrix.getSkewX() * (y - blob.fY) -
302 blob.fViewMatrix.getTranslateX();
303 SkScalar transY = viewMatrix.getTranslateY() +
304 viewMatrix.getSkewY() * (x - blob.fX) +
305 viewMatrix.getScaleY() * (y - blob.fY) -
306 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700307 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700308 return true;
309 }
310
joshualittfcfb9fc2015-04-21 07:35:10 -0700311 (*outTransX) = transX;
312 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700313 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700314 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
315 // distance field being generated, so we have to regenerate in those cases
316 SkScalar newMaxScale = viewMatrix.getMaxScale();
317 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
318 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
319 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
320 return true;
321 }
322
323 (*outTransX) = x - blob.fX;
324 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700325 }
joshualitt374b2f72015-07-21 08:05:03 -0700326
joshualitta7c63892015-04-21 13:24:37 -0700327 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
328 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
329 // the blob anyways at flush time, so no need to regenerate explicitly
joshualitt2a0e9f32015-04-13 06:12:21 -0700330 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700331}
332
333
joshualitt374b2f72015-07-21 08:05:03 -0700334inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run,
joshualittdbd35932015-04-02 09:19:04 -0700335 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700336 const SkMatrix* viewMatrix,
337 bool noGamma) {
robertphillipsfcf78292015-06-19 11:49:52 -0700338 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700339 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
340 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
341}
342
robertphillips9c240a12015-05-28 07:45:59 -0700343void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700344 const GrClip& clip, const SkPaint& skPaint,
345 const SkMatrix& viewMatrix, const SkTextBlob* blob,
346 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700347 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700348 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700349 if (fContext->abandoned()) {
350 return;
351 }
352
joshualitt374b2f72015-07-21 08:05:03 -0700353 SkAutoTUnref<GrAtlasTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700354 SkMaskFilter::BlurRec blurRec;
joshualitt374b2f72015-07-21 08:05:03 -0700355 GrAtlasTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700356 // It might be worth caching these things, but its not clear at this time
357 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
358 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700359 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700360 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700361 drawFilter);
362
363 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700364 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700365
366 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
robertphillipsfcf78292015-06-19 11:49:52 -0700367 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700368 kUnknown_SkPixelGeometry;
369
joshualitt9e36c1a2015-04-14 12:17:27 -0700370 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
371 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
372 // ensure we always match the same key
373 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
374 ComputeCanonicalColor(skPaint, hasLCD);
375
joshualitte4cee1f2015-05-11 13:04:28 -0700376 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700377 key.fUniqueID = blob->uniqueID();
378 key.fStyle = skPaint.getStyle();
379 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700380 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700381 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700382 }
383
joshualitt1d89e8d2015-04-01 12:40:54 -0700384 SkIRect clipRect;
385 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
386
joshualitt2a0e9f32015-04-13 06:12:21 -0700387 SkScalar transX = 0.f;
388 SkScalar transY = 0.f;
389
joshualitt9e36c1a2015-04-14 12:17:27 -0700390 // Though for the time being runs in the textblob can override the paint, they only touch font
391 // info.
392 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700393 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
394 return;
395 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700396
joshualittb7133be2015-04-08 09:08:31 -0700397 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700398 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700399 // We have to remake the blob because changes may invalidate our masks.
400 // TODO we could probably get away reuse most of the time if the pointer is unique,
401 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700402 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700403 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
404 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700405 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700406 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700407 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700408 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
joshualitt7e7b5c52015-07-21 12:56:56 -0700409 // offsets. Note, we offset the vertex bounds right before flushing
joshualitt2a0e9f32015-04-13 06:12:21 -0700410 cacheBlob->fViewMatrix = viewMatrix;
411 cacheBlob->fX = x;
412 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700413 fCache->makeMRU(cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700414#ifdef CACHE_SANITY_CHECK
415 {
416 int glyphCount = 0;
417 int runCount = 0;
418 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
419 SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyphCount, runCount,
420 kGrayTextVASize));
421 GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint);
422 this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix,
423 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
424 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
425 }
426
427#endif
joshualitt1d89e8d2015-04-01 12:40:54 -0700428 }
429 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700430 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700431 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
432 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700433 } else {
434 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
435 }
robertphillips9c240a12015-05-28 07:45:59 -0700436 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700437 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700438 }
439
robertphillips2334fb62015-06-17 05:43:33 -0700440 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700441 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700442}
443
joshualitt9bd2daf2015-04-17 09:30:06 -0700444inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
445 const SkMatrix& viewMatrix) {
446 // TODO: support perspective (need getMaxScale replacement)
447 if (viewMatrix.hasPerspective()) {
448 return false;
449 }
450
451 SkScalar maxScale = viewMatrix.getMaxScale();
452 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
453 // Hinted text looks far better at small resolutions
454 // Scaling up beyond 2x yields undesireable artifacts
jvanverth34d72882015-06-22 08:08:09 -0700455 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700456 return false;
457 }
458
robertphillipsfcf78292015-06-19 11:49:52 -0700459 bool useDFT = fSurfaceProps.isUseDistanceFieldFonts();
robertphillipsbcd7ab52015-06-18 05:27:18 -0700460#if SK_FORCE_DISTANCE_FIELD_TEXT
461 useDFT = true;
462#endif
463
jvanverth4854d132015-06-22 06:46:56 -0700464 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700465 return false;
466 }
467
468 // rasterizers and mask filters modify alpha, which doesn't
469 // translate well to distance
470 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700471 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700472 return false;
473 }
474
475 // TODO: add some stroking support
476 if (skPaint.getStyle() != SkPaint::kFill_Style) {
477 return false;
478 }
479
480 return true;
481}
482
joshualitt374b2f72015-07-21 08:05:03 -0700483void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700484 const SkPaint& skPaint, GrColor color,
485 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700486 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700487 SkDrawFilter* drawFilter, const SkIRect& clipRect,
488 GrRenderTarget* rt, const GrClip& clip,
489 const GrPaint& paint) {
joshualitt259fbf12015-07-21 11:39:34 -0700490 cacheBlob->fPaintColor = skPaint.getColor();
joshualitt1d89e8d2015-04-01 12:40:54 -0700491 cacheBlob->fViewMatrix = viewMatrix;
492 cacheBlob->fX = x;
493 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700494
495 // Regenerate textblob
496 SkPaint runPaint = skPaint;
497 SkTextBlob::RunIterator it(blob);
498 for (int run = 0; !it.done(); it.next(), run++) {
499 int glyphCount = it.glyphCount();
500 size_t textLen = glyphCount * sizeof(uint16_t);
501 const SkPoint& offset = it.offset();
502 // applyFontToPaint() always overwrites the exact same attributes,
503 // so it is safe to not re-seed the paint for this reason.
504 it.applyFontToPaint(&runPaint);
505
506 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
507 // A false return from filter() means we should abort the current draw.
508 runPaint = skPaint;
509 continue;
510 }
511
robertphillipsfcf78292015-06-19 11:49:52 -0700512 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700513
joshualitt1d89e8d2015-04-01 12:40:54 -0700514 // setup vertex / glyphIndex for the new run
515 if (run > 0) {
516 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
517 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
518
519 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
520 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
521
522 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
523 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
524 }
525
joshualittfcfb9fc2015-04-21 07:35:10 -0700526 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
527 cacheBlob->setHasDistanceField();
528 SkPaint dfPaint = runPaint;
529 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700530 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700531 Run& runIdx = cacheBlob->fRuns[run];
532 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
533 subRun.fUseLCDText = runPaint.isLCDRenderText();
534 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700535
joshualittfcfb9fc2015-04-21 07:35:10 -0700536 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
537
538 SkTDArray<char> fallbackTxt;
539 SkTDArray<SkScalar> fallbackPos;
540 SkPoint dfOffset;
541 int scalarsPerPosition = 2;
542 switch (it.positioning()) {
543 case SkTextBlob::kDefault_Positioning: {
544 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
545 (const char *)it.glyphs(), textLen,
546 x + offset.x(), y + offset.y(), clipRect, textRatio,
547 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
548 break;
549 }
550 case SkTextBlob::kHorizontal_Positioning: {
551 scalarsPerPosition = 1;
552 dfOffset = SkPoint::Make(x, y + offset.y());
553 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
554 (const char*)it.glyphs(), textLen, it.pos(),
555 scalarsPerPosition, dfOffset, clipRect, textRatio,
556 &fallbackTxt, &fallbackPos);
557 break;
558 }
559 case SkTextBlob::kFull_Positioning: {
560 dfOffset = SkPoint::Make(x, y);
561 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
562 (const char*)it.glyphs(), textLen, it.pos(),
563 scalarsPerPosition, dfOffset, clipRect, textRatio,
564 &fallbackTxt, &fallbackPos);
565 break;
566 }
567 }
568 if (fallbackTxt.count()) {
569 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
570 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
571 clipRect);
572 }
573
574 SkGlyphCache::AttachCache(cache);
575 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
576 cacheBlob->fRuns[run].fDrawAsPaths = true;
577 } else {
578 cacheBlob->setHasBitmap();
579 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
580 false);
581 switch (it.positioning()) {
582 case SkTextBlob::kDefault_Positioning:
583 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
584 (const char *)it.glyphs(), textLen,
585 x + offset.x(), y + offset.y(), clipRect);
586 break;
587 case SkTextBlob::kHorizontal_Positioning:
588 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
589 (const char*)it.glyphs(), textLen, it.pos(), 1,
590 SkPoint::Make(x, y + offset.y()), clipRect);
591 break;
592 case SkTextBlob::kFull_Positioning:
593 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
594 (const char*)it.glyphs(), textLen, it.pos(), 2,
595 SkPoint::Make(x, y), clipRect);
596 break;
597 }
598 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700599 }
600
601 if (drawFilter) {
602 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
603 runPaint = skPaint;
604 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700605 }
606}
607
joshualitt374b2f72015-07-21 08:05:03 -0700608inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
joshualitt64c99cc2015-04-21 09:43:03 -0700609 SkPaint* skPaint,
610 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700611 const SkMatrix& viewMatrix) {
612 // getMaxScale doesn't support perspective, so neither do we at the moment
613 SkASSERT(!viewMatrix.hasPerspective());
614 SkScalar maxScale = viewMatrix.getMaxScale();
615 SkScalar textSize = skPaint->getTextSize();
616 SkScalar scaledTextSize = textSize;
617 // if we have non-unity scale, we need to choose our base text size
618 // based on the SkPaint's text size multiplied by the max scale factor
619 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
620 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
621 scaledTextSize *= maxScale;
622 }
623
joshualitt64c99cc2015-04-21 09:43:03 -0700624 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
625 // and ceiling. A scale outside of this range would require regenerating the distance fields
626 SkScalar dfMaskScaleFloor;
627 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700628 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700629 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700630 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700631 *textRatio = textSize / kSmallDFFontSize;
632 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
633 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700634 dfMaskScaleFloor = kSmallDFFontLimit;
635 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700636 *textRatio = textSize / kMediumDFFontSize;
637 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
638 } else {
joshualitta7c63892015-04-21 13:24:37 -0700639 dfMaskScaleFloor = kMediumDFFontLimit;
640 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700641 *textRatio = textSize / kLargeDFFontSize;
642 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
643 }
644
joshualitt64c99cc2015-04-21 09:43:03 -0700645 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
646 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
647 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
648 // tolerate before we'd have to move to a large mip size. When we actually test these values
649 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
650 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
651 // level)
joshualitta7c63892015-04-21 13:24:37 -0700652 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700653 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
654 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
655
joshualitt9bd2daf2015-04-17 09:30:06 -0700656 skPaint->setLCDRenderText(false);
657 skPaint->setAutohinted(false);
658 skPaint->setHinting(SkPaint::kNormal_Hinting);
659 skPaint->setSubpixelText(true);
660}
661
joshualitt374b2f72015-07-21 08:05:03 -0700662inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700663 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700664 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700665 const GrPaint& paint,
666 const SkPaint& skPaint,
667 const SkMatrix& viewMatrix,
668 const SkTDArray<char>& fallbackTxt,
669 const SkTDArray<SkScalar>& fallbackPos,
670 int scalarsPerPosition,
671 const SkPoint& offset,
672 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700673 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700674 blob->setHasBitmap();
675 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700676 // Push back a new subrun to fill and set the override descriptor
677 run.push_back();
678 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
679 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700680 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700681 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700682 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700683 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700684 fallbackTxt.begin(), fallbackTxt.count(),
685 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
686 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700687}
688
joshualitt374b2f72015-07-21 08:05:03 -0700689inline GrAtlasTextBlob*
joshualitt9bd2daf2015-04-17 09:30:06 -0700690GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
691 const SkMatrix& viewMatrix, SkGlyphCache** cache,
692 SkPaint* dfPaint, SkScalar* textRatio) {
joshualitt374b2f72015-07-21 08:05:03 -0700693 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700694
695 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700696 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700697 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700698 Run& run = blob->fRuns[0];
699 PerSubRunInfo& subRun = run.fSubRunInfo.back();
700 subRun.fUseLCDText = origPaint.isLCDRenderText();
701 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700702
703 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
704 return blob;
705}
706
joshualitt374b2f72015-07-21 08:05:03 -0700707inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700708GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
709 const GrPaint& paint, const SkPaint& skPaint,
710 const SkMatrix& viewMatrix,
711 const char text[], size_t byteLength,
712 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700713 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700714 SkIRect clipRect;
715 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
716
joshualitt374b2f72015-07-21 08:05:03 -0700717 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700718 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
719 SkPaint dfPaint;
720 SkScalar textRatio;
721 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700722 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700723
joshualitt9bd2daf2015-04-17 09:30:06 -0700724 SkTDArray<char> fallbackTxt;
725 SkTDArray<SkScalar> fallbackPos;
726 SkPoint offset;
727 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
728 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
729 &offset, skPaint);
730 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700731 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700732 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700733 fallbackPos, 2, offset, clipRect);
734 }
735 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700736 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700737 blob->fViewMatrix = viewMatrix;
738
739 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
740 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
741 byteLength, x, y, clipRect);
742 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700743 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700744 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700745}
746
joshualitt374b2f72015-07-21 08:05:03 -0700747inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700748GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
749 const GrPaint& paint, const SkPaint& skPaint,
750 const SkMatrix& viewMatrix,
751 const char text[], size_t byteLength,
752 const SkScalar pos[], int scalarsPerPosition,
753 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700754 int glyphCount = skPaint.countText(text, byteLength);
755
756 SkIRect clipRect;
757 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
758
joshualitt374b2f72015-07-21 08:05:03 -0700759 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700760 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
761 SkPaint dfPaint;
762 SkScalar textRatio;
763 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700764 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700765
766 SkTDArray<char> fallbackTxt;
767 SkTDArray<SkScalar> fallbackPos;
768 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
769 byteLength, pos, scalarsPerPosition, offset, clipRect,
770 textRatio, &fallbackTxt, &fallbackPos);
771 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700772 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700773 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700774 fallbackPos, scalarsPerPosition, offset, clipRect);
775 }
776 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700777 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700778 blob->fViewMatrix = viewMatrix;
779 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
780 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
781 byteLength, pos, scalarsPerPosition, offset, clipRect);
782 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700783 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700784 return blob;
785}
786
robertphillips2334fb62015-06-17 05:43:33 -0700787void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700788 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700789 const GrPaint& paint, const SkPaint& skPaint,
790 const SkMatrix& viewMatrix,
791 const char text[], size_t byteLength,
792 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700793 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700794 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
795 text, byteLength, x, y, regionClipBounds));
796 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700797}
798
robertphillips2334fb62015-06-17 05:43:33 -0700799void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700800 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700801 const GrPaint& paint, const SkPaint& skPaint,
802 const SkMatrix& viewMatrix,
803 const char text[], size_t byteLength,
804 const SkScalar pos[], int scalarsPerPosition,
805 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700806 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700807 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
808 text, byteLength,
809 pos, scalarsPerPosition,
810 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700811
robertphillips2334fb62015-06-17 05:43:33 -0700812 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700813}
814
joshualitt374b2f72015-07-21 08:05:03 -0700815void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700816 SkGlyphCache* cache, const SkPaint& skPaint,
817 GrColor color,
818 const SkMatrix& viewMatrix,
819 const char text[], size_t byteLength,
820 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700821 SkASSERT(byteLength == 0 || text != NULL);
822
823 // nothing to draw
824 if (text == NULL || byteLength == 0) {
825 return;
826 }
827
828 fCurrStrike = NULL;
829 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
830
831 // Get GrFontScaler from cache
832 GrFontScaler* fontScaler = GetGrFontScaler(cache);
833
834 // transform our starting point
835 {
836 SkPoint loc;
837 viewMatrix.mapXY(x, y, &loc);
838 x = loc.fX;
839 y = loc.fY;
840 }
841
842 // need to measure first
843 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
844 SkVector stopVector;
845 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
846
847 SkScalar stopX = stopVector.fX;
848 SkScalar stopY = stopVector.fY;
849
850 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
851 stopX = SkScalarHalf(stopX);
852 stopY = SkScalarHalf(stopY);
853 }
854 x -= stopX;
855 y -= stopY;
856 }
857
858 const char* stop = text + byteLength;
859
860 SkAutoKern autokern;
861
862 SkFixed fxMask = ~0;
863 SkFixed fyMask = ~0;
864 SkScalar halfSampleX, halfSampleY;
865 if (cache->isSubpixel()) {
866 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
867 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
868 if (kX_SkAxisAlignment == baseline) {
869 fyMask = 0;
870 halfSampleY = SK_ScalarHalf;
871 } else if (kY_SkAxisAlignment == baseline) {
872 fxMask = 0;
873 halfSampleX = SK_ScalarHalf;
874 }
875 } else {
876 halfSampleX = halfSampleY = SK_ScalarHalf;
877 }
878
879 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
880 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
881
882 while (text < stop) {
883 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
884
885 fx += autokern.adjust(glyph);
886
887 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700888 this->bmpAppendGlyph(blob,
889 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700890 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700891 Sk48Dot16FloorToInt(fx),
892 Sk48Dot16FloorToInt(fy),
893 color,
894 fontScaler,
895 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700896 }
897
898 fx += glyph.fAdvanceX;
899 fy += glyph.fAdvanceY;
900 }
901}
902
joshualitt374b2f72015-07-21 08:05:03 -0700903void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700904 SkGlyphCache* cache, const SkPaint& skPaint,
905 GrColor color,
906 const SkMatrix& viewMatrix,
907 const char text[], size_t byteLength,
908 const SkScalar pos[], int scalarsPerPosition,
909 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700910 SkASSERT(byteLength == 0 || text != NULL);
911 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
912
913 // nothing to draw
914 if (text == NULL || byteLength == 0) {
915 return;
916 }
917
918 fCurrStrike = NULL;
919 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
920
921 // Get GrFontScaler from cache
922 GrFontScaler* fontScaler = GetGrFontScaler(cache);
923
924 const char* stop = text + byteLength;
925 SkTextAlignProc alignProc(skPaint.getTextAlign());
926 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
927
928 if (cache->isSubpixel()) {
929 // maybe we should skip the rounding if linearText is set
930 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
931
932 SkFixed fxMask = ~0;
933 SkFixed fyMask = ~0;
934 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
935 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
936 if (kX_SkAxisAlignment == baseline) {
937 fyMask = 0;
938 halfSampleY = SK_ScalarHalf;
939 } else if (kY_SkAxisAlignment == baseline) {
940 fxMask = 0;
941 halfSampleX = SK_ScalarHalf;
942 }
943
944 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
945 while (text < stop) {
946 SkPoint tmsLoc;
947 tmsProc(pos, &tmsLoc);
948 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
949 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
950
951 const SkGlyph& glyph = glyphCacheProc(cache, &text,
952 fx & fxMask, fy & fyMask);
953
954 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700955 this->bmpAppendGlyph(blob,
956 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700957 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700958 Sk48Dot16FloorToInt(fx),
959 Sk48Dot16FloorToInt(fy),
960 color,
961 fontScaler,
962 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700963 }
964 pos += scalarsPerPosition;
965 }
966 } else {
967 while (text < stop) {
968 const char* currentText = text;
969 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
970
971 if (metricGlyph.fWidth) {
972 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
973 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
974 SkPoint tmsLoc;
975 tmsProc(pos, &tmsLoc);
976 SkPoint alignLoc;
977 alignProc(tmsLoc, metricGlyph, &alignLoc);
978
979 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
980 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
981
982 // have to call again, now that we've been "aligned"
983 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
984 fx & fxMask, fy & fyMask);
985 // the assumption is that the metrics haven't changed
986 SkASSERT(prevAdvX == glyph.fAdvanceX);
987 SkASSERT(prevAdvY == glyph.fAdvanceY);
988 SkASSERT(glyph.fWidth);
989
joshualitt9bd2daf2015-04-17 09:30:06 -0700990 this->bmpAppendGlyph(blob,
991 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700992 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700993 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,
joshualitt6c2c2b02015-07-24 10:37:00 -07001017 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001018 Sk48Dot16FloorToInt(fx),
1019 Sk48Dot16FloorToInt(fy),
1020 color,
1021 fontScaler,
1022 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001023 }
1024 pos += scalarsPerPosition;
1025 }
1026 } else {
1027 while (text < stop) {
1028 // the last 2 parameters are ignored
1029 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1030
1031 if (glyph.fWidth) {
1032 SkPoint tmsLoc;
1033 tmsProc(pos, &tmsLoc);
1034
1035 SkPoint alignLoc;
1036 alignProc(tmsLoc, glyph, &alignLoc);
1037
1038 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1039 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001040 this->bmpAppendGlyph(blob,
1041 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001042 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001043 Sk48Dot16FloorToInt(fx),
1044 Sk48Dot16FloorToInt(fy),
1045 color,
1046 fontScaler,
1047 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001048 }
1049 pos += scalarsPerPosition;
1050 }
1051 }
1052 }
1053}
1054
joshualitt9bd2daf2015-04-17 09:30:06 -07001055
joshualitt374b2f72015-07-21 08:05:03 -07001056void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001057 SkGlyphCache* cache, const SkPaint& skPaint,
1058 GrColor color,
1059 const SkMatrix& viewMatrix,
1060 const char text[], size_t byteLength,
1061 SkScalar x, SkScalar y, const SkIRect& clipRect,
1062 SkScalar textRatio,
1063 SkTDArray<char>* fallbackTxt,
1064 SkTDArray<SkScalar>* fallbackPos,
1065 SkPoint* offset,
1066 const SkPaint& origPaint) {
1067 SkASSERT(byteLength == 0 || text != NULL);
1068
1069 // nothing to draw
1070 if (text == NULL || byteLength == 0) {
1071 return;
1072 }
1073
1074 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1075 SkAutoDescriptor desc;
robertphillipsfcf78292015-06-19 11:49:52 -07001076 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, NULL, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001077 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1078 desc.getDesc());
1079
1080 SkTArray<SkScalar> positions;
1081
1082 const char* textPtr = text;
1083 SkFixed stopX = 0;
1084 SkFixed stopY = 0;
1085 SkFixed origin = 0;
1086 switch (origPaint.getTextAlign()) {
1087 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1088 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1089 case SkPaint::kLeft_Align: origin = 0; break;
1090 }
1091
1092 SkAutoKern autokern;
1093 const char* stop = text + byteLength;
1094 while (textPtr < stop) {
1095 // don't need x, y here, since all subpixel variants will have the
1096 // same advance
1097 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1098
1099 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1100 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1101
1102 SkFixed height = glyph.fAdvanceY;
1103 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1104
1105 stopX += width;
1106 stopY += height;
1107 }
1108 SkASSERT(textPtr == stop);
1109
1110 // now adjust starting point depending on alignment
1111 SkScalar alignX = SkFixedToScalar(stopX);
1112 SkScalar alignY = SkFixedToScalar(stopY);
1113 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1114 alignX = SkScalarHalf(alignX);
1115 alignY = SkScalarHalf(alignY);
1116 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1117 alignX = 0;
1118 alignY = 0;
1119 }
1120 x -= alignX;
1121 y -= alignY;
1122 *offset = SkPoint::Make(x, y);
1123
1124 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1125 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1126 fallbackPos);
1127 SkGlyphCache::AttachCache(origPaintCache);
1128}
1129
joshualitt374b2f72015-07-21 08:05:03 -07001130void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001131 SkGlyphCache* cache, const SkPaint& skPaint,
1132 GrColor color,
1133 const SkMatrix& viewMatrix,
1134 const char text[], size_t byteLength,
1135 const SkScalar pos[], int scalarsPerPosition,
1136 const SkPoint& offset, const SkIRect& clipRect,
1137 SkScalar textRatio,
1138 SkTDArray<char>* fallbackTxt,
1139 SkTDArray<SkScalar>* fallbackPos) {
1140
1141 SkASSERT(byteLength == 0 || text != NULL);
1142 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1143
1144 // nothing to draw
1145 if (text == NULL || byteLength == 0) {
1146 return;
1147 }
1148
1149 fCurrStrike = NULL;
1150
1151 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1152 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1153
1154 const char* stop = text + byteLength;
1155
1156 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1157 while (text < stop) {
1158 const char* lastText = text;
1159 // the last 2 parameters are ignored
1160 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1161
1162 if (glyph.fWidth) {
1163 SkScalar x = offset.x() + pos[0];
1164 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1165
1166 if (!this->dfAppendGlyph(blob,
1167 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001168 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001169 x, y, color, fontScaler, clipRect,
1170 textRatio, viewMatrix)) {
1171 // couldn't append, send to fallback
1172 fallbackTxt->append(SkToInt(text-lastText), lastText);
1173 *fallbackPos->append() = pos[0];
1174 if (2 == scalarsPerPosition) {
1175 *fallbackPos->append() = pos[1];
1176 }
1177 }
1178 }
1179 pos += scalarsPerPosition;
1180 }
1181 } else {
1182 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1183 : SK_Scalar1;
1184 while (text < stop) {
1185 const char* lastText = text;
1186 // the last 2 parameters are ignored
1187 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1188
1189 if (glyph.fWidth) {
1190 SkScalar x = offset.x() + pos[0];
1191 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1192
1193 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1194 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1195
1196 if (!this->dfAppendGlyph(blob,
1197 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001198 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001199 x - advanceX, y - advanceY, color,
1200 fontScaler,
1201 clipRect,
1202 textRatio,
1203 viewMatrix)) {
1204 // couldn't append, send to fallback
1205 fallbackTxt->append(SkToInt(text-lastText), lastText);
1206 *fallbackPos->append() = pos[0];
1207 if (2 == scalarsPerPosition) {
1208 *fallbackPos->append() = pos[1];
1209 }
1210 }
1211 }
1212 pos += scalarsPerPosition;
1213 }
1214 }
1215}
1216
joshualitt374b2f72015-07-21 08:05:03 -07001217void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001218 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001219 int vx, int vy, GrColor color, GrFontScaler* scaler,
1220 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001221 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001222 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001223 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001224 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001225 }
1226
joshualitt6c2c2b02015-07-24 10:37:00 -07001227 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1228 skGlyph.getSubXFixed(),
1229 skGlyph.getSubYFixed(),
1230 GrGlyph::kCoverage_MaskStyle);
1231 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001232 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001233 return;
1234 }
1235
1236 int x = vx + glyph->fBounds.fLeft;
1237 int y = vy + glyph->fBounds.fTop;
1238
1239 // keep them as ints until we've done the clip-test
1240 int width = glyph->fBounds.width();
1241 int height = glyph->fBounds.height();
1242
joshualitt2a0e9f32015-04-13 06:12:21 -07001243#if 0
1244 // Not checking the clip bounds might introduce a performance regression. However, its not
1245 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1246 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1247 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1248 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001249 // check if we clipped out
1250 if (clipRect.quickReject(x, y, x + width, y + height)) {
1251 return;
1252 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001253#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001254
1255 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001256 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001257 this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001258 return;
1259 }
1260
joshualitt1d89e8d2015-04-01 12:40:54 -07001261 GrMaskFormat format = glyph->fMaskFormat;
1262
1263 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1264 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittd9f13ae2015-07-24 11:24:31 -07001265 subRun = &run.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001266 }
1267
1268 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001269
1270 size_t vertexStride = get_vertex_stride(format);
1271
1272 SkRect r;
1273 r.fLeft = SkIntToScalar(x);
1274 r.fTop = SkIntToScalar(y);
1275 r.fRight = r.fLeft + SkIntToScalar(width);
1276 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001277 subRun->fMaskFormat = format;
1278 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001279 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001280}
joshualitt1d89e8d2015-04-01 12:40:54 -07001281
joshualitt374b2f72015-07-21 08:05:03 -07001282bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001283 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001284 SkScalar sx, SkScalar sy, GrColor color,
1285 GrFontScaler* scaler,
1286 const SkIRect& clipRect,
1287 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001288 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001289 if (!fCurrStrike) {
1290 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001291 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001292 }
1293
joshualitt6c2c2b02015-07-24 10:37:00 -07001294 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1295 skGlyph.getSubXFixed(),
1296 skGlyph.getSubYFixed(),
1297 GrGlyph::kDistance_MaskStyle);
1298 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001299 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001300 return true;
1301 }
1302
1303 // fallback to color glyph support
1304 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1305 return false;
1306 }
1307
1308 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1309 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1310 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1311 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1312
1313 SkScalar scale = textRatio;
1314 dx *= scale;
1315 dy *= scale;
1316 width *= scale;
1317 height *= scale;
1318 sx += dx;
1319 sy += dy;
1320 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1321
1322#if 0
1323 // check if we clipped out
1324 SkRect dstRect;
1325 viewMatrix.mapRect(&dstRect, glyphRect);
1326 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1327 SkScalarTruncToInt(dstRect.top()),
1328 SkScalarTruncToInt(dstRect.right()),
1329 SkScalarTruncToInt(dstRect.bottom()))) {
1330 return true;
1331 }
1332#endif
1333
1334 // TODO combine with the above
1335 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001336 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001337 this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001338 return true;
1339 }
1340
joshualitt9bd2daf2015-04-17 09:30:06 -07001341 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1342 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1343 subRun->fMaskFormat = kA8_GrMaskFormat;
1344
1345 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1346
1347 bool useColorVerts = !subRun->fUseLCDText;
1348 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001349 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001350 return true;
1351}
1352
joshualitt374b2f72015-07-21 08:05:03 -07001353inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001354 GrFontScaler* scaler, const SkGlyph& skGlyph,
1355 SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001356 if (NULL == glyph->fPath) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001357 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
1358 if (!glyphPath) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001359 return;
1360 }
joshualitt6c2c2b02015-07-24 10:37:00 -07001361
1362 glyph->fPath = SkNEW_ARGS(SkPath, (*glyphPath));
joshualitt9bd2daf2015-04-17 09:30:06 -07001363 }
joshualitt374b2f72015-07-21 08:05:03 -07001364 blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y));
joshualitt9bd2daf2015-04-17 09:30:06 -07001365}
1366
joshualitt374b2f72015-07-21 08:05:03 -07001367inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run,
joshualitt9bd2daf2015-04-17 09:30:06 -07001368 Run::SubRunInfo* subRun,
1369 const SkRect& positions, GrColor color,
1370 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001371 GrGlyph* glyph) {
1372 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001373 run->fVertexBounds.joinNonEmptyArg(positions);
1374 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001375
1376 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1377
joshualitt9bd2daf2015-04-17 09:30:06 -07001378 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001379 // V0
1380 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1381 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001382 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1383 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001384 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001385
joshualitt010db532015-04-21 10:07:26 -07001386 // V1
1387 position = reinterpret_cast<SkPoint*>(vertex);
1388 position->set(positions.fLeft, positions.fBottom);
1389 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001390 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001391 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001392
joshualitt010db532015-04-21 10:07:26 -07001393 // V2
1394 position = reinterpret_cast<SkPoint*>(vertex);
1395 position->set(positions.fRight, positions.fBottom);
1396 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001397 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001398 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001399
joshualitt010db532015-04-21 10:07:26 -07001400 // V3
1401 position = reinterpret_cast<SkPoint*>(vertex);
1402 position->set(positions.fRight, positions.fTop);
1403 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001404 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001405 } else {
1406 // V0
1407 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1408 position->set(positions.fLeft, positions.fTop);
1409 vertex += vertexStride;
1410
1411 // V1
1412 position = reinterpret_cast<SkPoint*>(vertex);
1413 position->set(positions.fLeft, positions.fBottom);
1414 vertex += vertexStride;
1415
1416 // V2
1417 position = reinterpret_cast<SkPoint*>(vertex);
1418 position->set(positions.fRight, positions.fBottom);
1419 vertex += vertexStride;
1420
1421 // V3
1422 position = reinterpret_cast<SkPoint*>(vertex);
1423 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001424 }
1425
1426 subRun->fGlyphEndIndex++;
1427 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1428}
1429
bsalomon265697d2015-07-22 10:17:26 -07001430class TextBatch : public GrBatch {
joshualitt1d89e8d2015-04-01 12:40:54 -07001431public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001432 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualitt374b2f72015-07-21 08:05:03 -07001433 typedef GrAtlasTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001434 typedef Blob::Run Run;
1435 typedef Run::SubRunInfo TextInfo;
1436 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001437 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001438 int fRun;
1439 int fSubRun;
1440 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001441 SkScalar fTransX;
1442 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001443 };
1444
bsalomon265697d2015-07-22 10:17:26 -07001445 static TextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
joshualittad802c62015-04-15 05:31:57 -07001446 GrBatchFontCache* fontCache) {
bsalomon265697d2015-07-22 10:17:26 -07001447 TextBatch* batch = SkNEW(TextBatch);
1448
1449 batch->initClassID<TextBatch>();
1450 batch->fFontCache = fontCache;
1451 switch (maskFormat) {
1452 case kA8_GrMaskFormat:
1453 batch->fMaskType = kGrayscaleCoverageMask_MaskType;
1454 break;
1455 case kA565_GrMaskFormat:
1456 batch->fMaskType = kLCDCoverageMask_MaskType;
1457 break;
1458 case kARGB_GrMaskFormat:
1459 batch->fMaskType = kColorBitmapMask_MaskType;
1460 break;
1461 }
1462 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001463 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001464 batch->fFilteredColor = 0;
1465 batch->fFontCache = fontCache;
1466 batch->fUseBGR = false;
1467 return batch;
joshualitt1d89e8d2015-04-01 12:40:54 -07001468 }
1469
bsalomon265697d2015-07-22 10:17:26 -07001470 static TextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache,
1471 DistanceAdjustTable* distanceAdjustTable,
1472 SkColor filteredColor, bool isLCD,
1473 bool useBGR) {
1474 TextBatch* batch = SkNEW(TextBatch);
1475 batch->initClassID<TextBatch>();
1476 batch->fFontCache = fontCache;
1477 batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
1478 batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
1479 batch->fFilteredColor = filteredColor;
1480 batch->fUseBGR = useBGR;
1481 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001482 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001483 return batch;
joshualitt9bd2daf2015-04-17 09:30:06 -07001484 }
1485
bsalomon265697d2015-07-22 10:17:26 -07001486 const char* name() const override { return "TextBatch"; }
joshualitt1d89e8d2015-04-01 12:40:54 -07001487
1488 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001489 if (kColorBitmapMask_MaskType == fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001490 out->setUnknownFourComponents();
1491 } else {
1492 out->setKnownFourComponents(fBatch.fColor);
1493 }
1494 }
1495
1496 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001497 switch (fMaskType) {
1498 case kGrayscaleDistanceField_MaskType:
1499 case kGrayscaleCoverageMask_MaskType:
joshualitt1d89e8d2015-04-01 12:40:54 -07001500 out->setUnknownSingleComponent();
bsalomon265697d2015-07-22 10:17:26 -07001501 break;
1502 case kLCDCoverageMask_MaskType:
1503 case kLCDDistanceField_MaskType:
1504 out->setUnknownOpaqueFourComponents();
joshualitt1d89e8d2015-04-01 12:40:54 -07001505 out->setUsingLCDCoverage();
bsalomon265697d2015-07-22 10:17:26 -07001506 break;
1507 case kColorBitmapMask_MaskType:
1508 out->setKnownSingleComponent(0xff);
joshualitt1d89e8d2015-04-01 12:40:54 -07001509 }
1510 }
1511
1512 void initBatchTracker(const GrPipelineInfo& init) override {
1513 // Handle any color overrides
bsalomon7765a472015-07-08 11:26:37 -07001514 if (!init.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001515 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001516 }
joshualitt416e14f2015-07-10 09:05:57 -07001517 init.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001518
1519 // setup batch properties
bsalomon7765a472015-07-08 11:26:37 -07001520 fBatch.fColorIgnored = !init.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001521 fBatch.fColor = fGeoData[0].fColor;
bsalomon7765a472015-07-08 11:26:37 -07001522 fBatch.fUsesLocalCoords = init.readsLocalCoords();
1523 fBatch.fCoverageIgnored = !init.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001524 }
1525
bsalomonb5238a72015-05-05 07:49:49 -07001526 struct FlushInfo {
1527 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1528 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1529 int fGlyphsToFlush;
1530 int fVertexOffset;
1531 };
1532
joshualitt1d89e8d2015-04-01 12:40:54 -07001533 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1534 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1535 // TODO actually only invert if we don't have RGBA
1536 SkMatrix localMatrix;
1537 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1538 SkDebugf("Cannot invert viewmatrix\n");
1539 return;
1540 }
1541
bsalomon265697d2015-07-22 10:17:26 -07001542 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
joshualitt62db8ba2015-04-09 08:22:37 -07001543 if (!texture) {
1544 SkDebugf("Could not allocate backing texture for atlas\n");
1545 return;
1546 }
1547
bsalomon265697d2015-07-22 10:17:26 -07001548 bool usesDistanceFields = this->usesDistanceFields();
1549 GrMaskFormat maskFormat = this->maskFormat();
1550 bool isLCD = this->isLCD();
1551
joshualitt9bd2daf2015-04-17 09:30:06 -07001552 SkAutoTUnref<const GrGeometryProcessor> gp;
bsalomon265697d2015-07-22 10:17:26 -07001553 if (usesDistanceFields) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001554 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1555 texture));
1556 } else {
1557 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001558 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1559 texture,
1560 params,
bsalomon265697d2015-07-22 10:17:26 -07001561 maskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001562 localMatrix,
1563 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001564 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001565
bsalomonb5238a72015-05-05 07:49:49 -07001566 FlushInfo flushInfo;
1567 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001568 size_t vertexStride = gp->getVertexStride();
bsalomon265697d2015-07-22 10:17:26 -07001569 SkASSERT(vertexStride == (usesDistanceFields ?
1570 get_vertex_stride_df(maskFormat, isLCD) :
1571 get_vertex_stride(maskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001572
joshualittb8c241a2015-05-19 08:23:30 -07001573 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001574
1575 int glyphCount = this->numGlyphs();
bsalomon8415abe2015-05-04 11:41:41 -07001576 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001577
robertphillipse40d3972015-05-07 09:51:43 -07001578 void* vertices = batchTarget->makeVertSpace(vertexStride,
1579 glyphCount * kVerticesPerGlyph,
1580 &vertexBuffer,
1581 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001582 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1583 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1584 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001585 SkDebugf("Could not allocate vertices\n");
1586 return;
1587 }
1588
1589 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1590
joshualitt25ba7ea2015-04-21 07:49:49 -07001591 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1592 // in a row
1593 const SkDescriptor* desc = NULL;
1594 SkGlyphCache* cache = NULL;
1595 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001596 SkTypeface* typeface = NULL;
1597
bsalomond602f4d2015-07-27 06:12:01 -07001598 for (int i = 0; i < fGeoCount; i++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001599 Geometry& args = fGeoData[i];
1600 Blob* blob = args.fBlob;
1601 Run& run = blob->fRuns[args.fRun];
1602 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1603
bsalomon265697d2015-07-22 10:17:26 -07001604 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001605 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
1606 run.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001607 bool regenerateColors;
bsalomon265697d2015-07-22 10:17:26 -07001608 if (usesDistanceFields) {
1609 regenerateColors = !isLCD && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001610 } else {
bsalomon265697d2015-07-22 10:17:26 -07001611 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001612 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001613 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001614 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1615
1616 // We regenerate both texture coords and colors in the blob itself, and update the
1617 // atlas generation. If we don't end up purging any unused plots, we can avoid
1618 // regenerating the coords. We could take a finer grained approach to updating texture
1619 // coords but its not clear if the extra bookkeeping would offset any gains.
1620 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1621 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1622 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1623 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001624 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001625 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001626 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001627
1628 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1629 // generating its texture coords, we have to track whether or not the strike has
1630 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1631 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1632 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1633 // new strike, we instead keep our ref to the old strike and use the packed ids from
1634 // it. These ids will still be valid as long as we hold the ref. When we are done
1635 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1636 bool regenerateGlyphs = false;
1637 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001638 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001639 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001640
1641 // We can reuse if we have a valid strike and our descriptors / typeface are the
1642 // same
joshualitt97202d22015-04-22 13:47:02 -07001643 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1644 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001645 run.fDescriptor.getDesc();
1646 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1647 !(desc->equals(*newDesc))) {
1648 if (cache) {
1649 SkGlyphCache::AttachCache(cache);
1650 }
1651 desc = newDesc;
1652 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1653 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001654 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001655 typeface = run.fTypeface;
1656 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001657
joshualittae32c102015-04-21 09:37:57 -07001658 if (run.fStrike->isAbandoned()) {
1659 regenerateGlyphs = true;
1660 strike = fFontCache->getStrike(scaler);
1661 } else {
1662 strike = run.fStrike;
1663 }
1664 }
1665
1666 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001667 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001668 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
joshualitt6c2c2b02015-07-24 10:37:00 -07001669
1670 GrGlyph* glyph = blob->fGlyphs[glyphOffset];
1671 GrGlyph::PackedID id = glyph->fPackedID;
1672 const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
joshualittae32c102015-04-21 09:37:57 -07001673 if (regenerateGlyphs) {
1674 // Get the id from the old glyph, and use the new strike to lookup
1675 // the glyph.
joshualitt76cc6572015-07-31 05:51:45 -07001676 blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, maskFormat,
1677 scaler);
joshualittae32c102015-04-21 09:37:57 -07001678 }
1679 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001680 SkASSERT(glyph);
joshualitt65e96b42015-07-31 11:45:22 -07001681 SkASSERT(id == glyph->fPackedID);
1682 // We want to be able to assert this but cannot for testing purposes.
1683 // once skbug:4143 has landed we can revist this assert
1684 //SkASSERT(glyph->fMaskFormat == this->maskFormat());
joshualitt1d89e8d2015-04-01 12:40:54 -07001685
1686 if (!fFontCache->hasGlyph(glyph) &&
joshualitt4f19ca32015-07-30 07:59:20 -07001687 !strike->addGlyphToAtlas(batchTarget, glyph, scaler, skGlyph,
1688 maskFormat)) {
bsalomonb5238a72015-05-05 07:49:49 -07001689 this->flush(batchTarget, &flushInfo);
joshualittb8c241a2015-05-19 08:23:30 -07001690 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001691 brokenRun = glyphIdx > 0;
1692
joshualittae32c102015-04-21 09:37:57 -07001693 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1694 glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001695 scaler,
joshualitt4f19ca32015-07-30 07:59:20 -07001696 skGlyph,
1697 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001698 SkASSERT(success);
1699 }
joshualittb4c507e2015-04-08 08:07:59 -07001700 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1701 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001702
1703 // Texture coords are the last vertex attribute so we get a pointer to the
1704 // first one and then map with stride in regenerateTextureCoords
1705 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1706 vertex += info.fVertexStartIndex;
1707 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1708 vertex += vertexStride - sizeof(SkIPoint16);
1709
1710 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1711 }
1712
1713 if (regenerateColors) {
1714 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1715 vertex += info.fVertexStartIndex;
1716 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1717 this->regenerateColors(vertex, vertexStride, args.fColor);
1718 }
1719
joshualitt2a0e9f32015-04-13 06:12:21 -07001720 if (regeneratePositions) {
1721 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1722 vertex += info.fVertexStartIndex;
1723 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1724 SkScalar transX = args.fTransX;
1725 SkScalar transY = args.fTransY;
1726 this->regeneratePositions(vertex, vertexStride, transX, transY);
1727 }
bsalomonb5238a72015-05-05 07:49:49 -07001728 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001729 }
1730
joshualitt2a0e9f32015-04-13 06:12:21 -07001731 // We my have changed the color so update it here
1732 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001733 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001734 if (regenerateGlyphs) {
1735 run.fStrike.reset(SkRef(strike));
1736 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001737 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
bsalomon265697d2015-07-22 10:17:26 -07001738 fFontCache->atlasGeneration(maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001739 }
1740 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001741 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001742
1743 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1744 // have a valid atlas generation
1745 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1746 batchTarget->currentToken(),
bsalomon265697d2015-07-22 10:17:26 -07001747 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001748 }
1749
1750 // now copy all vertices
1751 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1752 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1753
1754 currVertex += byteCount;
1755 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001756 // Make sure to attach the last cache if applicable
1757 if (cache) {
1758 SkGlyphCache::AttachCache(cache);
1759 }
bsalomonb5238a72015-05-05 07:49:49 -07001760 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001761 }
1762
joshualittad802c62015-04-15 05:31:57 -07001763 // to avoid even the initial copy of the struct, we have a getter for the first item which
1764 // is used to seed the batch with its initial geometry. After seeding, the client should call
1765 // init() so the Batch can initialize itself
1766 Geometry& geometry() { return fGeoData[0]; }
1767 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001768 const Geometry& geo = fGeoData[0];
1769 fBatch.fColor = geo.fColor;
1770 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1771
1772 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1773 // into device space
1774 const Run& run = geo.fBlob->fRuns[geo.fRun];
1775 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1776 SkRect bounds = run.fVertexBounds;
1777 fBatch.fViewMatrix.mapRect(&bounds);
1778 this->setBounds(bounds);
1779 } else {
1780 this->setBounds(run.fVertexBounds);
1781 }
joshualittad802c62015-04-15 05:31:57 -07001782 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001783
1784private:
bsalomon265697d2015-07-22 10:17:26 -07001785 TextBatch() {} // initialized in factory functions.
joshualittad802c62015-04-15 05:31:57 -07001786
bsalomon265697d2015-07-22 10:17:26 -07001787 ~TextBatch() {
bsalomond602f4d2015-07-27 06:12:01 -07001788 for (int i = 0; i < fGeoCount; i++) {
joshualittad802c62015-04-15 05:31:57 -07001789 fGeoData[i].fBlob->unref();
1790 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001791 }
1792
bsalomon265697d2015-07-22 10:17:26 -07001793 GrMaskFormat maskFormat() const {
1794 switch (fMaskType) {
1795 case kLCDCoverageMask_MaskType:
1796 return kA565_GrMaskFormat;
1797 case kColorBitmapMask_MaskType:
1798 return kARGB_GrMaskFormat;
1799 case kGrayscaleCoverageMask_MaskType:
1800 case kGrayscaleDistanceField_MaskType:
1801 case kLCDDistanceField_MaskType:
1802 return kA8_GrMaskFormat;
1803 }
1804 return kA8_GrMaskFormat; // suppress warning
1805 }
1806
1807 bool usesDistanceFields() const {
1808 return kGrayscaleDistanceField_MaskType == fMaskType ||
1809 kLCDDistanceField_MaskType == fMaskType;
1810 }
1811
1812 bool isLCD() const {
1813 return kLCDCoverageMask_MaskType == fMaskType ||
1814 kLCDDistanceField_MaskType == fMaskType;
1815 }
1816
joshualitt1d89e8d2015-04-01 12:40:54 -07001817 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1818 int width = glyph->fBounds.width();
1819 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001820
joshualitt9bd2daf2015-04-17 09:30:06 -07001821 int u0, v0, u1, v1;
bsalomon265697d2015-07-22 10:17:26 -07001822 if (this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001823 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1824 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1825 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1826 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1827 } else {
1828 u0 = glyph->fAtlasLocation.fX;
1829 v0 = glyph->fAtlasLocation.fY;
1830 u1 = u0 + width;
1831 v1 = v0 + height;
1832 }
1833
joshualitt1d89e8d2015-04-01 12:40:54 -07001834 SkIPoint16* textureCoords;
1835 // V0
1836 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1837 textureCoords->set(u0, v0);
1838 vertex += vertexStride;
1839
1840 // V1
1841 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1842 textureCoords->set(u0, v1);
1843 vertex += vertexStride;
1844
1845 // V2
1846 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1847 textureCoords->set(u1, v1);
1848 vertex += vertexStride;
1849
1850 // V3
1851 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1852 textureCoords->set(u1, v0);
1853 }
1854
1855 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1856 for (int i = 0; i < kVerticesPerGlyph; i++) {
1857 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1858 *vcolor = color;
1859 vertex += vertexStride;
1860 }
1861 }
1862
joshualitt2a0e9f32015-04-13 06:12:21 -07001863 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1864 SkScalar transY) {
1865 for (int i = 0; i < kVerticesPerGlyph; i++) {
1866 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1867 point->fX += transX;
1868 point->fY += transY;
1869 vertex += vertexStride;
1870 }
1871 }
1872
bsalomonb5238a72015-05-05 07:49:49 -07001873 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001874 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001875 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001876 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001877 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001878 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001879 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001880 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001881 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1882 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001883 }
1884
1885 GrColor color() const { return fBatch.fColor; }
1886 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1887 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1888 int numGlyphs() const { return fBatch.fNumGlyphs; }
1889
1890 bool onCombineIfPossible(GrBatch* t) override {
joshualitt8cab9a72015-07-16 09:13:50 -07001891 if (!this->pipeline()->isEqual(*t->pipeline())) {
1892 return false;
1893 }
1894
bsalomon265697d2015-07-22 10:17:26 -07001895 TextBatch* that = t->cast<TextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001896
bsalomon265697d2015-07-22 10:17:26 -07001897 if (fMaskType != that->fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001898 return false;
1899 }
1900
bsalomon265697d2015-07-22 10:17:26 -07001901 if (!this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001902 // TODO we can often batch across LCD text if we have dual source blending and don't
1903 // have to use the blend constant
bsalomon265697d2015-07-22 10:17:26 -07001904 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001905 return false;
1906 }
joshualitt9bd2daf2015-04-17 09:30:06 -07001907 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1908 return false;
1909 }
1910 } else {
joshualitt9bd2daf2015-04-17 09:30:06 -07001911 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1912 return false;
1913 }
1914
1915 if (fFilteredColor != that->fFilteredColor) {
1916 return false;
1917 }
1918
joshualitt9bd2daf2015-04-17 09:30:06 -07001919 if (fUseBGR != that->fUseBGR) {
1920 return false;
1921 }
1922
joshualitt9bd2daf2015-04-17 09:30:06 -07001923 // TODO see note above
bsalomon265697d2015-07-22 10:17:26 -07001924 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001925 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001926 }
1927
1928 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001929
bsalomond602f4d2015-07-27 06:12:01 -07001930 // Reallocate space for geo data if necessary and then import that's geo data.
1931 int newGeoCount = that->fGeoCount + fGeoCount;
1932 // We assume (and here enforce) that the allocation size is the smallest power of two that
1933 // is greater than or equal to the number of geometries (and at least
1934 // kMinGeometryAllocated).
1935 int newAllocSize = GrNextPow2(newGeoCount);
1936 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
1937
bsalomon16ed6ad2015-07-29 06:54:33 -07001938 if (newGeoCount > currAllocSize) {
bsalomond602f4d2015-07-27 06:12:01 -07001939 fGeoData.realloc(newAllocSize);
joshualittad802c62015-04-15 05:31:57 -07001940 }
1941
bsalomond602f4d2015-07-27 06:12:01 -07001942 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
bsalomon1c634362015-07-27 07:00:00 -07001943 // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
1944 // it doesn't try to unref them.
1945#ifdef SK_DEBUG
1946 for (int i = 0; i < that->fGeoCount; ++i) {
1947 that->fGeoData.get()[i].fBlob = (Blob*)0x1;
joshualittad802c62015-04-15 05:31:57 -07001948 }
bsalomon1c634362015-07-27 07:00:00 -07001949#endif
1950 that->fGeoCount = 0;
bsalomond602f4d2015-07-27 06:12:01 -07001951 fGeoCount = newGeoCount;
joshualitt99c7c072015-05-01 13:43:30 -07001952
1953 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001954 return true;
1955 }
1956
joshualitt9bd2daf2015-04-17 09:30:06 -07001957 // TODO just use class params
1958 // TODO trying to figure out why lcd is so whack
1959 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1960 GrColor color, GrTexture* texture) {
1961 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
bsalomon265697d2015-07-22 10:17:26 -07001962 bool isLCD = this->isLCD();
joshualitt9bd2daf2015-04-17 09:30:06 -07001963 // set up any flags
bsalomon265697d2015-07-22 10:17:26 -07001964 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt9bd2daf2015-04-17 09:30:06 -07001965
1966 // see if we need to create a new effect
bsalomon265697d2015-07-22 10:17:26 -07001967 if (isLCD) {
1968 flags |= kUseLCD_DistanceFieldEffectFlag;
1969 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
1970 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1971
joshualitt9bd2daf2015-04-17 09:30:06 -07001972 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
robertphillips9fc82752015-06-19 04:46:45 -07001995 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07001996 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;
bsalomond602f4d2015-07-27 06:12:01 -07002026 // The minimum number of Geometry we will try to allocate.
2027 enum { kMinGeometryAllocated = 4 };
2028 SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
2029 int fGeoCount;
bsalomon265697d2015-07-22 10:17:26 -07002030
2031 enum MaskType {
2032 kGrayscaleCoverageMask_MaskType,
2033 kLCDCoverageMask_MaskType,
2034 kColorBitmapMask_MaskType,
2035 kGrayscaleDistanceField_MaskType,
2036 kLCDDistanceField_MaskType,
2037 } fMaskType;
2038 bool fUseBGR; // fold this into the enum?
2039
joshualitt1d89e8d2015-04-01 12:40:54 -07002040 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002041
2042 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002043 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002044 SkColor fFilteredColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07002045};
2046
robertphillips2334fb62015-06-17 05:43:33 -07002047void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002048 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002049 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2050 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2051 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002052
joshualitt9a27e632015-04-06 10:53:36 -07002053 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2054 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002055
joshualitt9a27e632015-04-06 10:53:36 -07002056 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002057
joshualitt9a27e632015-04-06 10:53:36 -07002058 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2059 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002060 }
2061
robertphillipsfcf78292015-06-19 11:49:52 -07002062 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002063
2064 switch (it.positioning()) {
2065 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002066 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002067 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002068 textLen, x + offset.x(), y + offset.y(), clipBounds);
2069 break;
2070 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002071 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002072 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002073 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2074 clipBounds);
2075 break;
2076 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002077 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002078 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002079 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2080 break;
2081 }
2082}
2083
bsalomon265697d2015-07-22 10:17:26 -07002084inline GrBatch*
joshualitt374b2f72015-07-21 08:05:03 -07002085GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info,
joshualitt79dfb2b2015-05-11 08:58:08 -07002086 int glyphCount, int run, int subRun,
2087 GrColor color, SkScalar transX, SkScalar transY,
2088 const SkPaint& skPaint) {
2089 GrMaskFormat format = info.fMaskFormat;
2090 GrColor subRunColor;
2091 if (kARGB_GrMaskFormat == format) {
2092 uint8_t paintAlpha = skPaint.getAlpha();
2093 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2094 } else {
2095 subRunColor = color;
2096 }
2097
bsalomon265697d2015-07-22 10:17:26 -07002098 TextBatch* batch;
joshualitt79dfb2b2015-05-11 08:58:08 -07002099 if (info.fDrawAsDistanceFields) {
2100 SkColor filteredColor;
2101 SkColorFilter* colorFilter = skPaint.getColorFilter();
2102 if (colorFilter) {
2103 filteredColor = colorFilter->filterColor(skPaint.getColor());
2104 } else {
2105 filteredColor = skPaint.getColor();
2106 }
robertphillipsfcf78292015-06-19 11:49:52 -07002107 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
bsalomon265697d2015-07-22 10:17:26 -07002108 batch = TextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
2109 fDistanceAdjustTable, filteredColor,
2110 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002111 } else {
bsalomon265697d2015-07-22 10:17:26 -07002112 batch = TextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
joshualitt79dfb2b2015-05-11 08:58:08 -07002113 }
bsalomon265697d2015-07-22 10:17:26 -07002114 TextBatch::Geometry& geometry = batch->geometry();
joshualitt79dfb2b2015-05-11 08:58:08 -07002115 geometry.fBlob = SkRef(cacheBlob);
2116 geometry.fRun = run;
2117 geometry.fSubRun = subRun;
2118 geometry.fColor = subRunColor;
2119 geometry.fTransX = transX;
2120 geometry.fTransY = transY;
2121 batch->init();
2122
2123 return batch;
2124}
2125
robertphillips2334fb62015-06-17 05:43:33 -07002126inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt374b2f72015-07-21 08:05:03 -07002127 GrAtlasTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002128 SkScalar transX, SkScalar transY,
2129 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002130 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2131 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2132 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2133 if (0 == glyphCount) {
2134 continue;
2135 }
2136
bsalomon265697d2015-07-22 10:17:26 -07002137 SkAutoTUnref<GrBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2138 subRun, color, transX, transY,
2139 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002140 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002141 }
2142}
2143
joshualitt374b2f72015-07-21 08:05:03 -07002144inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002145 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002146 SkScalar transX, SkScalar transY,
2147 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002148 if (!cacheBlob->fBigGlyphs.count()) {
2149 return;
2150 }
2151
2152 SkMatrix pathMatrix;
2153 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2154 SkDebugf("could not invert viewmatrix\n");
2155 return;
2156 }
2157
joshualitt9a27e632015-04-06 10:53:36 -07002158 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt374b2f72015-07-21 08:05:03 -07002159 GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002160 bigGlyph.fVx += transX;
2161 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002162 SkMatrix translate = cacheBlob->fViewMatrix;
2163 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2164
robertphillips2334fb62015-06-17 05:43:33 -07002165 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002166 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002167 }
2168}
joshualitt9a27e632015-04-06 10:53:36 -07002169
robertphillips2334fb62015-06-17 05:43:33 -07002170void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt374b2f72015-07-21 08:05:03 -07002171 GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002172 GrRenderTarget* rt,
2173 const SkPaint& skPaint,
2174 const GrPaint& grPaint,
2175 SkDrawFilter* drawFilter,
2176 const GrClip& clip,
2177 const SkMatrix& viewMatrix,
2178 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002179 SkScalar x, SkScalar y,
2180 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002181 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2182 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002183 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002184
2185 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002186
2187 SkTextBlob::RunIterator it(blob);
2188 for (int run = 0; !it.done(); it.next(), run++) {
2189 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002190 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002191 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002192 continue;
2193 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002194 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002195 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002196 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002197 }
2198
2199 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002200 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002201}
2202
joshualitt374b2f72015-07-21 08:05:03 -07002203void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002204 GrRenderTarget* rt,
2205 const SkPaint& skPaint,
2206 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002207 const GrClip& clip,
2208 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002209 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002210
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;
robertphillipsfcf78292015-06-19 11:49:52 -07002227 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
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
joshualitt79dfb2b2015-05-11 08:58:08 -07002233 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2234 // because we don't really want to have a gpu device here.
2235 // We enable distance fields by twiddling a knob on the paint
robertphillipsfcf78292015-06-19 11:49:52 -07002236 GrDrawContext* drawContext = context->drawContext(&gSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -07002237
robertphillipsfcf78292015-06-19 11:49:52 -07002238 gTextContext = GrAtlasTextContext::Create(context, drawContext, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002239 }
2240
2241 // create dummy render target
2242 GrSurfaceDesc desc;
2243 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2244 desc.fWidth = 1024;
2245 desc.fHeight = 1024;
2246 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002247 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002248 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2249 SkASSERT(texture);
2250 SkASSERT(NULL != texture->asRenderTarget());
2251 GrRenderTarget* rt = texture->asRenderTarget();
2252
2253 // Setup dummy SkPaint / GrPaint
2254 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002255 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002256 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002257 skPaint.setColor(color);
2258 skPaint.setLCDRenderText(random->nextBool());
2259 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2260 skPaint.setSubpixelText(random->nextBool());
2261
2262 GrPaint grPaint;
2263 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2264 SkFAIL("couldn't convert paint\n");
2265 }
2266
2267 const char* text = "The quick brown fox jumps over the lazy dog.";
2268 int textLen = (int)strlen(text);
2269
2270 // Setup clip
2271 GrClip clip;
2272 SkIRect noClip = SkIRect::MakeLargest();
2273
2274 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2275 // intend to test the batch with this unit test, that is okay.
joshualitt374b2f72015-07-21 08:05:03 -07002276 SkAutoTUnref<GrAtlasTextBlob> blob(
joshualitt79dfb2b2015-05-11 08:58:08 -07002277 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2278 static_cast<size_t>(textLen), 0, 0, noClip));
2279
2280 SkScalar transX = static_cast<SkScalar>(random->nextU());
2281 SkScalar transY = static_cast<SkScalar>(random->nextU());
joshualitt374b2f72015-07-21 08:05:03 -07002282 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
joshualitt79dfb2b2015-05-11 08:58:08 -07002283 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2284}
2285
2286#endif