blob: e20ca4bb7ffd07c84d3cdf689dce05aeab32494e [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
99// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -0700100// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -0700101
joshualittdbd35932015-04-02 09:19:04 -0700102GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700103 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700104 const SkSurfaceProps& surfaceProps)
105 : INHERITED(context, drawContext, surfaceProps)
robertphillips9fc82752015-06-19 04:46:45 -0700106 , fDistanceAdjustTable(SkNEW(DistanceAdjustTable)) {
joshualittb7133be2015-04-08 09:08:31 -0700107 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
108 // vertexStride
109 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
110 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700111 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700112 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700113}
114
robertphillips9fc82752015-06-19 04:46:45 -0700115void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700116
117 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
118 // text. The mask gamma hack is based off of guessing what the blend color is going to
119 // be, and adjusting the mask so that when run through the linear blend will
120 // produce the value closest to the desired result. However, in practice this means
121 // that the 'adjusted' mask is just increasing or decreasing the coverage of
122 // the mask depending on what it is thought it will blit against. For black (on
123 // assumed white) this means that coverages are decreased (on a curve). For white (on
124 // assumed black) this means that coverages are increased (on a a curve). At
125 // middle (perceptual) gray (which could be blit against anything) the coverages
126 // remain the same.
127 //
128 // The idea here is that instead of determining the initial (real) coverage and
129 // then adjusting that coverage, we determine an adjusted coverage directly by
130 // essentially manipulating the geometry (in this case, the distance to the glyph
131 // edge). So for black (on assumed white) this thins a bit; for white (on
132 // assumed black) this fake bolds the geometry a bit.
133 //
134 // The distance adjustment is calculated by determining the actual coverage value which
135 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
136 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
137 // actual edge. So by subtracting this distance adjustment and computing without the
138 // the coverage adjustment we should get 0.5 coverage at the same point.
139 //
140 // This has several implications:
141 // For non-gray lcd smoothed text, each subpixel essentially is using a
142 // slightly different geometry.
143 //
144 // For black (on assumed white) this may not cover some pixels which were
145 // previously covered; however those pixels would have been only slightly
146 // covered and that slight coverage would have been decreased anyway. Also, some pixels
147 // which were previously fully covered may no longer be fully covered.
148 //
149 // For white (on assumed black) this may cover some pixels which weren't
150 // previously covered at all.
151
152 int width, height;
153 size_t size;
154
155#ifdef SK_GAMMA_CONTRAST
156 SkScalar contrast = SK_GAMMA_CONTRAST;
157#else
158 SkScalar contrast = 0.5f;
159#endif
robertphillips9fc82752015-06-19 04:46:45 -0700160 SkScalar paintGamma = SK_GAMMA_EXPONENT;
161 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700162
163 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
164 &width, &height);
165
166 SkASSERT(kExpectedDistanceAdjustTableSize == height);
167 fTable = SkNEW_ARRAY(SkScalar, height);
168
169 SkAutoTArray<uint8_t> data((int)size);
170 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
171
172 // find the inverse points where we cross 0.5
173 // binsearch might be better, but we only need to do this once on creation
174 for (int row = 0; row < height; ++row) {
175 uint8_t* rowPtr = data.get() + row*width;
176 for (int col = 0; col < width - 1; ++col) {
177 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
178 // compute point where a mask value will give us a result of 0.5
179 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
180 float borderAlpha = (col + interp) / 255.f;
181
182 // compute t value for that alpha
183 // this is an approximate inverse for smoothstep()
184 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
185
186 // compute distance which gives us that t value
187 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
188 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
189
190 fTable[row] = d;
191 break;
192 }
193 }
194 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700195}
196
joshualittdbd35932015-04-02 09:19:04 -0700197GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700198 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700199 const SkSurfaceProps& surfaceProps) {
200 return SkNEW_ARGS(GrAtlasTextContext, (context, drawContext, surfaceProps));
joshualitt1d89e8d2015-04-01 12:40:54 -0700201}
202
joshualittdbd35932015-04-02 09:19:04 -0700203bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
204 const GrClip&,
205 const GrPaint&,
206 const SkPaint& skPaint,
207 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700208 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
209 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700210}
211
joshualitt9e36c1a2015-04-14 12:17:27 -0700212GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
213 GrColor canonicalColor = paint.computeLuminanceColor();
214 if (lcd) {
215 // This is the correct computation, but there are tons of cases where LCD can be overridden.
216 // For now we just regenerate if any run in a textblob has LCD.
217 // TODO figure out where all of these overrides are and see if we can incorporate that logic
218 // at a higher level *OR* use sRGB
219 SkASSERT(false);
220 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
221 } else {
222 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
223 // gamma corrected masks anyways, nor color
224 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
225 SkColorGetG(canonicalColor),
226 SkColorGetB(canonicalColor));
227 // reduce to our finite number of bits
228 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
229 }
230 return canonicalColor;
231}
232
233// TODO if this function ever shows up in profiling, then we can compute this value when the
234// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
235// run so this is not a big deal to compute here.
236bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
237 SkTextBlob::RunIterator it(blob);
238 for (; !it.done(); it.next()) {
239 if (it.isLCD()) {
240 return true;
241 }
242 }
243 return false;
244}
245
joshualitt2a0e9f32015-04-13 06:12:21 -0700246bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
joshualitt374b2f72015-07-21 08:05:03 -0700247 const GrAtlasTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700248 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700249 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700250 // If we have LCD text then our canonical color will be set to transparent, in this case we have
251 // to regenerate the blob on any color change
252 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700253 return true;
254 }
255
256 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
257 return true;
258 }
259
260 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
261 return true;
262 }
263
joshualitt53b5f442015-04-13 06:33:59 -0700264 // We only cache one masked version
265 if (blob.fKey.fHasBlur &&
266 (blob.fBlurRec.fSigma != blurRec.fSigma ||
267 blob.fBlurRec.fStyle != blurRec.fStyle ||
268 blob.fBlurRec.fQuality != blurRec.fQuality)) {
269 return true;
270 }
271
272 // Similarly, we only cache one version for each style
273 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
274 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
275 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
276 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
277 return true;
278 }
279
joshualittfcfb9fc2015-04-21 07:35:10 -0700280 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
281 // for mixed blobs if this becomes an issue.
282 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700283 // Identical viewmatrices and we can reuse in all cases
284 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
285 return false;
286 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700287 return true;
288 }
289
joshualittfcfb9fc2015-04-21 07:35:10 -0700290 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700291 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
292 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
293 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
294 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
295 return true;
296 }
297
joshualittfcfb9fc2015-04-21 07:35:10 -0700298 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
299 // but only for integer translations.
300 // This cool bit of math will determine the necessary translation to apply to the already
301 // generated vertex coordinates to move them to the correct position
302 SkScalar transX = viewMatrix.getTranslateX() +
303 viewMatrix.getScaleX() * (x - blob.fX) +
304 viewMatrix.getSkewX() * (y - blob.fY) -
305 blob.fViewMatrix.getTranslateX();
306 SkScalar transY = viewMatrix.getTranslateY() +
307 viewMatrix.getSkewY() * (x - blob.fX) +
308 viewMatrix.getScaleY() * (y - blob.fY) -
309 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700310 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700311 return true;
312 }
313
joshualittfcfb9fc2015-04-21 07:35:10 -0700314 (*outTransX) = transX;
315 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700316 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700317 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
318 // distance field being generated, so we have to regenerate in those cases
319 SkScalar newMaxScale = viewMatrix.getMaxScale();
320 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
321 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
322 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
323 return true;
324 }
325
326 (*outTransX) = x - blob.fX;
327 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700328 }
joshualitt374b2f72015-07-21 08:05:03 -0700329
joshualitta7c63892015-04-21 13:24:37 -0700330 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
331 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
332 // the blob anyways at flush time, so no need to regenerate explicitly
joshualitt2a0e9f32015-04-13 06:12:21 -0700333 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700334}
335
336
joshualitt374b2f72015-07-21 08:05:03 -0700337inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run,
joshualittdbd35932015-04-02 09:19:04 -0700338 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700339 const SkMatrix* viewMatrix,
340 bool noGamma) {
robertphillipsfcf78292015-06-19 11:49:52 -0700341 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700342 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
343 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
344}
345
robertphillips9c240a12015-05-28 07:45:59 -0700346void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700347 const GrClip& clip, const SkPaint& skPaint,
348 const SkMatrix& viewMatrix, const SkTextBlob* blob,
349 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700350 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700351 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700352 if (fContext->abandoned()) {
353 return;
354 }
355
joshualitt374b2f72015-07-21 08:05:03 -0700356 SkAutoTUnref<GrAtlasTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700357 SkMaskFilter::BlurRec blurRec;
joshualitt374b2f72015-07-21 08:05:03 -0700358 GrAtlasTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700359 // It might be worth caching these things, but its not clear at this time
360 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
361 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700362 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700363 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700364 drawFilter);
365
366 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700367 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700368
369 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
robertphillipsfcf78292015-06-19 11:49:52 -0700370 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700371 kUnknown_SkPixelGeometry;
372
joshualitt9e36c1a2015-04-14 12:17:27 -0700373 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
374 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
375 // ensure we always match the same key
376 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
377 ComputeCanonicalColor(skPaint, hasLCD);
378
joshualitte4cee1f2015-05-11 13:04:28 -0700379 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700380 key.fUniqueID = blob->uniqueID();
381 key.fStyle = skPaint.getStyle();
382 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700383 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700384 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700385 }
386
joshualitt1d89e8d2015-04-01 12:40:54 -0700387 SkIRect clipRect;
388 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
389
joshualitt2a0e9f32015-04-13 06:12:21 -0700390 SkScalar transX = 0.f;
391 SkScalar transY = 0.f;
392
joshualitt9e36c1a2015-04-14 12:17:27 -0700393 // Though for the time being runs in the textblob can override the paint, they only touch font
394 // info.
395 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700396 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
397 return;
398 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700399
joshualittb7133be2015-04-08 09:08:31 -0700400 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700401 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700402 // We have to remake the blob because changes may invalidate our masks.
403 // TODO we could probably get away reuse most of the time if the pointer is unique,
404 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700405 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700406 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
407 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700408 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700409 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700410 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700411 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
joshualitt7e7b5c52015-07-21 12:56:56 -0700412 // offsets. Note, we offset the vertex bounds right before flushing
joshualitt2a0e9f32015-04-13 06:12:21 -0700413 cacheBlob->fViewMatrix = viewMatrix;
414 cacheBlob->fX = x;
415 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700416 fCache->makeMRU(cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700417#ifdef CACHE_SANITY_CHECK
418 {
419 int glyphCount = 0;
420 int runCount = 0;
421 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
422 SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyphCount, runCount,
423 kGrayTextVASize));
424 GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint);
425 this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix,
426 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
427 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
428 }
429
430#endif
joshualitt1d89e8d2015-04-01 12:40:54 -0700431 }
432 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700433 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700434 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
435 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700436 } else {
437 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
438 }
robertphillips9c240a12015-05-28 07:45:59 -0700439 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700440 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700441 }
442
robertphillips2334fb62015-06-17 05:43:33 -0700443 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700444 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700445}
446
joshualitt9bd2daf2015-04-17 09:30:06 -0700447inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
448 const SkMatrix& viewMatrix) {
449 // TODO: support perspective (need getMaxScale replacement)
450 if (viewMatrix.hasPerspective()) {
451 return false;
452 }
453
454 SkScalar maxScale = viewMatrix.getMaxScale();
455 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
456 // Hinted text looks far better at small resolutions
457 // Scaling up beyond 2x yields undesireable artifacts
jvanverth34d72882015-06-22 08:08:09 -0700458 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700459 return false;
460 }
461
robertphillipsfcf78292015-06-19 11:49:52 -0700462 bool useDFT = fSurfaceProps.isUseDistanceFieldFonts();
robertphillipsbcd7ab52015-06-18 05:27:18 -0700463#if SK_FORCE_DISTANCE_FIELD_TEXT
464 useDFT = true;
465#endif
466
jvanverth4854d132015-06-22 06:46:56 -0700467 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700468 return false;
469 }
470
471 // rasterizers and mask filters modify alpha, which doesn't
472 // translate well to distance
473 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700474 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700475 return false;
476 }
477
478 // TODO: add some stroking support
479 if (skPaint.getStyle() != SkPaint::kFill_Style) {
480 return false;
481 }
482
483 return true;
484}
485
joshualitt374b2f72015-07-21 08:05:03 -0700486void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700487 const SkPaint& skPaint, GrColor color,
488 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700489 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700490 SkDrawFilter* drawFilter, const SkIRect& clipRect,
491 GrRenderTarget* rt, const GrClip& clip,
492 const GrPaint& paint) {
joshualitt259fbf12015-07-21 11:39:34 -0700493 cacheBlob->fPaintColor = skPaint.getColor();
joshualitt1d89e8d2015-04-01 12:40:54 -0700494 cacheBlob->fViewMatrix = viewMatrix;
495 cacheBlob->fX = x;
496 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700497
498 // Regenerate textblob
499 SkPaint runPaint = skPaint;
500 SkTextBlob::RunIterator it(blob);
501 for (int run = 0; !it.done(); it.next(), run++) {
502 int glyphCount = it.glyphCount();
503 size_t textLen = glyphCount * sizeof(uint16_t);
504 const SkPoint& offset = it.offset();
505 // applyFontToPaint() always overwrites the exact same attributes,
506 // so it is safe to not re-seed the paint for this reason.
507 it.applyFontToPaint(&runPaint);
508
509 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
510 // A false return from filter() means we should abort the current draw.
511 runPaint = skPaint;
512 continue;
513 }
514
robertphillipsfcf78292015-06-19 11:49:52 -0700515 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700516
joshualitt1d89e8d2015-04-01 12:40:54 -0700517 // setup vertex / glyphIndex for the new run
518 if (run > 0) {
519 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
520 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
521
522 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
523 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
524
525 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
526 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
527 }
528
joshualittfcfb9fc2015-04-21 07:35:10 -0700529 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
530 cacheBlob->setHasDistanceField();
531 SkPaint dfPaint = runPaint;
532 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700533 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700534 Run& runIdx = cacheBlob->fRuns[run];
535 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
536 subRun.fUseLCDText = runPaint.isLCDRenderText();
537 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700538
joshualittfcfb9fc2015-04-21 07:35:10 -0700539 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
540
541 SkTDArray<char> fallbackTxt;
542 SkTDArray<SkScalar> fallbackPos;
543 SkPoint dfOffset;
544 int scalarsPerPosition = 2;
545 switch (it.positioning()) {
546 case SkTextBlob::kDefault_Positioning: {
547 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
548 (const char *)it.glyphs(), textLen,
549 x + offset.x(), y + offset.y(), clipRect, textRatio,
550 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
551 break;
552 }
553 case SkTextBlob::kHorizontal_Positioning: {
554 scalarsPerPosition = 1;
555 dfOffset = SkPoint::Make(x, y + offset.y());
556 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
557 (const char*)it.glyphs(), textLen, it.pos(),
558 scalarsPerPosition, dfOffset, clipRect, textRatio,
559 &fallbackTxt, &fallbackPos);
560 break;
561 }
562 case SkTextBlob::kFull_Positioning: {
563 dfOffset = SkPoint::Make(x, y);
564 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
565 (const char*)it.glyphs(), textLen, it.pos(),
566 scalarsPerPosition, dfOffset, clipRect, textRatio,
567 &fallbackTxt, &fallbackPos);
568 break;
569 }
570 }
571 if (fallbackTxt.count()) {
572 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
573 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
574 clipRect);
575 }
576
577 SkGlyphCache::AttachCache(cache);
578 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
579 cacheBlob->fRuns[run].fDrawAsPaths = true;
580 } else {
581 cacheBlob->setHasBitmap();
582 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
583 false);
584 switch (it.positioning()) {
585 case SkTextBlob::kDefault_Positioning:
586 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
587 (const char *)it.glyphs(), textLen,
588 x + offset.x(), y + offset.y(), clipRect);
589 break;
590 case SkTextBlob::kHorizontal_Positioning:
591 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
592 (const char*)it.glyphs(), textLen, it.pos(), 1,
593 SkPoint::Make(x, y + offset.y()), clipRect);
594 break;
595 case SkTextBlob::kFull_Positioning:
596 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
597 (const char*)it.glyphs(), textLen, it.pos(), 2,
598 SkPoint::Make(x, y), clipRect);
599 break;
600 }
601 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700602 }
603
604 if (drawFilter) {
605 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
606 runPaint = skPaint;
607 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700608 }
609}
610
joshualitt374b2f72015-07-21 08:05:03 -0700611inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
joshualitt64c99cc2015-04-21 09:43:03 -0700612 SkPaint* skPaint,
613 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700614 const SkMatrix& viewMatrix) {
615 // getMaxScale doesn't support perspective, so neither do we at the moment
616 SkASSERT(!viewMatrix.hasPerspective());
617 SkScalar maxScale = viewMatrix.getMaxScale();
618 SkScalar textSize = skPaint->getTextSize();
619 SkScalar scaledTextSize = textSize;
620 // if we have non-unity scale, we need to choose our base text size
621 // based on the SkPaint's text size multiplied by the max scale factor
622 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
623 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
624 scaledTextSize *= maxScale;
625 }
626
joshualitt64c99cc2015-04-21 09:43:03 -0700627 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
628 // and ceiling. A scale outside of this range would require regenerating the distance fields
629 SkScalar dfMaskScaleFloor;
630 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700631 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700632 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700633 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700634 *textRatio = textSize / kSmallDFFontSize;
635 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
636 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700637 dfMaskScaleFloor = kSmallDFFontLimit;
638 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700639 *textRatio = textSize / kMediumDFFontSize;
640 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
641 } else {
joshualitta7c63892015-04-21 13:24:37 -0700642 dfMaskScaleFloor = kMediumDFFontLimit;
643 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700644 *textRatio = textSize / kLargeDFFontSize;
645 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
646 }
647
joshualitt64c99cc2015-04-21 09:43:03 -0700648 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
649 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
650 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
651 // tolerate before we'd have to move to a large mip size. When we actually test these values
652 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
653 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
654 // level)
joshualitta7c63892015-04-21 13:24:37 -0700655 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700656 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
657 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
658
joshualitt9bd2daf2015-04-17 09:30:06 -0700659 skPaint->setLCDRenderText(false);
660 skPaint->setAutohinted(false);
661 skPaint->setHinting(SkPaint::kNormal_Hinting);
662 skPaint->setSubpixelText(true);
663}
664
joshualitt374b2f72015-07-21 08:05:03 -0700665inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700666 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700667 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700668 const GrPaint& paint,
669 const SkPaint& skPaint,
670 const SkMatrix& viewMatrix,
671 const SkTDArray<char>& fallbackTxt,
672 const SkTDArray<SkScalar>& fallbackPos,
673 int scalarsPerPosition,
674 const SkPoint& offset,
675 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700676 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700677 blob->setHasBitmap();
678 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700679 // Push back a new subrun to fill and set the override descriptor
680 run.push_back();
681 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
682 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700683 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700684 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700685 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700686 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700687 fallbackTxt.begin(), fallbackTxt.count(),
688 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
689 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700690}
691
joshualitt374b2f72015-07-21 08:05:03 -0700692inline GrAtlasTextBlob*
joshualitt9bd2daf2015-04-17 09:30:06 -0700693GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
694 const SkMatrix& viewMatrix, SkGlyphCache** cache,
695 SkPaint* dfPaint, SkScalar* textRatio) {
joshualitt374b2f72015-07-21 08:05:03 -0700696 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700697
698 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700699 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700700 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700701 Run& run = blob->fRuns[0];
702 PerSubRunInfo& subRun = run.fSubRunInfo.back();
703 subRun.fUseLCDText = origPaint.isLCDRenderText();
704 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700705
706 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
707 return blob;
708}
709
joshualitt374b2f72015-07-21 08:05:03 -0700710inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700711GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
712 const GrPaint& paint, const SkPaint& skPaint,
713 const SkMatrix& viewMatrix,
714 const char text[], size_t byteLength,
715 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700716 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700717 SkIRect clipRect;
718 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
719
joshualitt374b2f72015-07-21 08:05:03 -0700720 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700721 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
722 SkPaint dfPaint;
723 SkScalar textRatio;
724 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700725 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700726
joshualitt9bd2daf2015-04-17 09:30:06 -0700727 SkTDArray<char> fallbackTxt;
728 SkTDArray<SkScalar> fallbackPos;
729 SkPoint offset;
730 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
731 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
732 &offset, skPaint);
733 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700734 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700735 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700736 fallbackPos, 2, offset, clipRect);
737 }
738 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700739 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700740 blob->fViewMatrix = viewMatrix;
741
742 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
743 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
744 byteLength, x, y, clipRect);
745 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700746 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700747 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700748}
749
joshualitt374b2f72015-07-21 08:05:03 -0700750inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700751GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
752 const GrPaint& paint, const SkPaint& skPaint,
753 const SkMatrix& viewMatrix,
754 const char text[], size_t byteLength,
755 const SkScalar pos[], int scalarsPerPosition,
756 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700757 int glyphCount = skPaint.countText(text, byteLength);
758
759 SkIRect clipRect;
760 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
761
joshualitt374b2f72015-07-21 08:05:03 -0700762 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700763 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
764 SkPaint dfPaint;
765 SkScalar textRatio;
766 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700767 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700768
769 SkTDArray<char> fallbackTxt;
770 SkTDArray<SkScalar> fallbackPos;
771 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
772 byteLength, pos, scalarsPerPosition, offset, clipRect,
773 textRatio, &fallbackTxt, &fallbackPos);
774 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700775 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700776 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700777 fallbackPos, scalarsPerPosition, offset, clipRect);
778 }
779 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700780 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700781 blob->fViewMatrix = viewMatrix;
782 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
783 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
784 byteLength, pos, scalarsPerPosition, offset, clipRect);
785 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700786 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700787 return blob;
788}
789
robertphillips2334fb62015-06-17 05:43:33 -0700790void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700791 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700792 const GrPaint& paint, const SkPaint& skPaint,
793 const SkMatrix& viewMatrix,
794 const char text[], size_t byteLength,
795 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700796 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700797 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
798 text, byteLength, x, y, regionClipBounds));
799 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700800}
801
robertphillips2334fb62015-06-17 05:43:33 -0700802void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700803 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700804 const GrPaint& paint, const SkPaint& skPaint,
805 const SkMatrix& viewMatrix,
806 const char text[], size_t byteLength,
807 const SkScalar pos[], int scalarsPerPosition,
808 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700809 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700810 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
811 text, byteLength,
812 pos, scalarsPerPosition,
813 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700814
robertphillips2334fb62015-06-17 05:43:33 -0700815 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700816}
817
joshualitt374b2f72015-07-21 08:05:03 -0700818void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700819 SkGlyphCache* cache, const SkPaint& skPaint,
820 GrColor color,
821 const SkMatrix& viewMatrix,
822 const char text[], size_t byteLength,
823 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700824 SkASSERT(byteLength == 0 || text != NULL);
825
826 // nothing to draw
827 if (text == NULL || byteLength == 0) {
828 return;
829 }
830
831 fCurrStrike = NULL;
832 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
833
834 // Get GrFontScaler from cache
835 GrFontScaler* fontScaler = GetGrFontScaler(cache);
836
837 // transform our starting point
838 {
839 SkPoint loc;
840 viewMatrix.mapXY(x, y, &loc);
841 x = loc.fX;
842 y = loc.fY;
843 }
844
845 // need to measure first
846 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
847 SkVector stopVector;
848 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
849
850 SkScalar stopX = stopVector.fX;
851 SkScalar stopY = stopVector.fY;
852
853 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
854 stopX = SkScalarHalf(stopX);
855 stopY = SkScalarHalf(stopY);
856 }
857 x -= stopX;
858 y -= stopY;
859 }
860
861 const char* stop = text + byteLength;
862
863 SkAutoKern autokern;
864
865 SkFixed fxMask = ~0;
866 SkFixed fyMask = ~0;
867 SkScalar halfSampleX, halfSampleY;
868 if (cache->isSubpixel()) {
869 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
870 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
871 if (kX_SkAxisAlignment == baseline) {
872 fyMask = 0;
873 halfSampleY = SK_ScalarHalf;
874 } else if (kY_SkAxisAlignment == baseline) {
875 fxMask = 0;
876 halfSampleX = SK_ScalarHalf;
877 }
878 } else {
879 halfSampleX = halfSampleY = SK_ScalarHalf;
880 }
881
882 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
883 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
884
885 while (text < stop) {
886 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
887
888 fx += autokern.adjust(glyph);
889
890 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700891 this->bmpAppendGlyph(blob,
892 runIndex,
893 GrGlyph::Pack(glyph.getGlyphID(),
894 glyph.getSubXFixed(),
895 glyph.getSubYFixed(),
896 GrGlyph::kCoverage_MaskStyle),
897 Sk48Dot16FloorToInt(fx),
898 Sk48Dot16FloorToInt(fy),
899 color,
900 fontScaler,
901 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700902 }
903
904 fx += glyph.fAdvanceX;
905 fy += glyph.fAdvanceY;
906 }
907}
908
joshualitt374b2f72015-07-21 08:05:03 -0700909void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700910 SkGlyphCache* cache, const SkPaint& skPaint,
911 GrColor color,
912 const SkMatrix& viewMatrix,
913 const char text[], size_t byteLength,
914 const SkScalar pos[], int scalarsPerPosition,
915 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700916 SkASSERT(byteLength == 0 || text != NULL);
917 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
918
919 // nothing to draw
920 if (text == NULL || byteLength == 0) {
921 return;
922 }
923
924 fCurrStrike = NULL;
925 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
926
927 // Get GrFontScaler from cache
928 GrFontScaler* fontScaler = GetGrFontScaler(cache);
929
930 const char* stop = text + byteLength;
931 SkTextAlignProc alignProc(skPaint.getTextAlign());
932 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
933
934 if (cache->isSubpixel()) {
935 // maybe we should skip the rounding if linearText is set
936 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
937
938 SkFixed fxMask = ~0;
939 SkFixed fyMask = ~0;
940 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
941 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
942 if (kX_SkAxisAlignment == baseline) {
943 fyMask = 0;
944 halfSampleY = SK_ScalarHalf;
945 } else if (kY_SkAxisAlignment == baseline) {
946 fxMask = 0;
947 halfSampleX = SK_ScalarHalf;
948 }
949
950 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
951 while (text < stop) {
952 SkPoint tmsLoc;
953 tmsProc(pos, &tmsLoc);
954 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
955 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
956
957 const SkGlyph& glyph = glyphCacheProc(cache, &text,
958 fx & fxMask, fy & fyMask);
959
960 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700961 this->bmpAppendGlyph(blob,
962 runIndex,
963 GrGlyph::Pack(glyph.getGlyphID(),
964 glyph.getSubXFixed(),
965 glyph.getSubYFixed(),
966 GrGlyph::kCoverage_MaskStyle),
967 Sk48Dot16FloorToInt(fx),
968 Sk48Dot16FloorToInt(fy),
969 color,
970 fontScaler,
971 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700972 }
973 pos += scalarsPerPosition;
974 }
975 } else {
976 while (text < stop) {
977 const char* currentText = text;
978 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
979
980 if (metricGlyph.fWidth) {
981 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
982 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
983 SkPoint tmsLoc;
984 tmsProc(pos, &tmsLoc);
985 SkPoint alignLoc;
986 alignProc(tmsLoc, metricGlyph, &alignLoc);
987
988 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
989 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
990
991 // have to call again, now that we've been "aligned"
992 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
993 fx & fxMask, fy & fyMask);
994 // the assumption is that the metrics haven't changed
995 SkASSERT(prevAdvX == glyph.fAdvanceX);
996 SkASSERT(prevAdvY == glyph.fAdvanceY);
997 SkASSERT(glyph.fWidth);
998
joshualitt9bd2daf2015-04-17 09:30:06 -0700999 this->bmpAppendGlyph(blob,
1000 runIndex,
1001 GrGlyph::Pack(glyph.getGlyphID(),
1002 glyph.getSubXFixed(),
1003 glyph.getSubYFixed(),
1004 GrGlyph::kCoverage_MaskStyle),
1005 Sk48Dot16FloorToInt(fx),
1006 Sk48Dot16FloorToInt(fy),
1007 color,
1008 fontScaler,
1009 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001010 }
1011 pos += scalarsPerPosition;
1012 }
1013 }
1014 } else { // not subpixel
1015
1016 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1017 while (text < stop) {
1018 // the last 2 parameters are ignored
1019 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1020
1021 if (glyph.fWidth) {
1022 SkPoint tmsLoc;
1023 tmsProc(pos, &tmsLoc);
1024
1025 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1026 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001027 this->bmpAppendGlyph(blob,
1028 runIndex,
1029 GrGlyph::Pack(glyph.getGlyphID(),
1030 glyph.getSubXFixed(),
1031 glyph.getSubYFixed(),
1032 GrGlyph::kCoverage_MaskStyle),
1033 Sk48Dot16FloorToInt(fx),
1034 Sk48Dot16FloorToInt(fy),
1035 color,
1036 fontScaler,
1037 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001038 }
1039 pos += scalarsPerPosition;
1040 }
1041 } else {
1042 while (text < stop) {
1043 // the last 2 parameters are ignored
1044 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1045
1046 if (glyph.fWidth) {
1047 SkPoint tmsLoc;
1048 tmsProc(pos, &tmsLoc);
1049
1050 SkPoint alignLoc;
1051 alignProc(tmsLoc, glyph, &alignLoc);
1052
1053 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1054 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001055 this->bmpAppendGlyph(blob,
1056 runIndex,
1057 GrGlyph::Pack(glyph.getGlyphID(),
1058 glyph.getSubXFixed(),
1059 glyph.getSubYFixed(),
1060 GrGlyph::kCoverage_MaskStyle),
1061 Sk48Dot16FloorToInt(fx),
1062 Sk48Dot16FloorToInt(fy),
1063 color,
1064 fontScaler,
1065 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001066 }
1067 pos += scalarsPerPosition;
1068 }
1069 }
1070 }
1071}
1072
joshualitt9bd2daf2015-04-17 09:30:06 -07001073
joshualitt374b2f72015-07-21 08:05:03 -07001074void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001075 SkGlyphCache* cache, const SkPaint& skPaint,
1076 GrColor color,
1077 const SkMatrix& viewMatrix,
1078 const char text[], size_t byteLength,
1079 SkScalar x, SkScalar y, const SkIRect& clipRect,
1080 SkScalar textRatio,
1081 SkTDArray<char>* fallbackTxt,
1082 SkTDArray<SkScalar>* fallbackPos,
1083 SkPoint* offset,
1084 const SkPaint& origPaint) {
1085 SkASSERT(byteLength == 0 || text != NULL);
1086
1087 // nothing to draw
1088 if (text == NULL || byteLength == 0) {
1089 return;
1090 }
1091
1092 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1093 SkAutoDescriptor desc;
robertphillipsfcf78292015-06-19 11:49:52 -07001094 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, NULL, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001095 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1096 desc.getDesc());
1097
1098 SkTArray<SkScalar> positions;
1099
1100 const char* textPtr = text;
1101 SkFixed stopX = 0;
1102 SkFixed stopY = 0;
1103 SkFixed origin = 0;
1104 switch (origPaint.getTextAlign()) {
1105 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1106 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1107 case SkPaint::kLeft_Align: origin = 0; break;
1108 }
1109
1110 SkAutoKern autokern;
1111 const char* stop = text + byteLength;
1112 while (textPtr < stop) {
1113 // don't need x, y here, since all subpixel variants will have the
1114 // same advance
1115 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1116
1117 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1118 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1119
1120 SkFixed height = glyph.fAdvanceY;
1121 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1122
1123 stopX += width;
1124 stopY += height;
1125 }
1126 SkASSERT(textPtr == stop);
1127
1128 // now adjust starting point depending on alignment
1129 SkScalar alignX = SkFixedToScalar(stopX);
1130 SkScalar alignY = SkFixedToScalar(stopY);
1131 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1132 alignX = SkScalarHalf(alignX);
1133 alignY = SkScalarHalf(alignY);
1134 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1135 alignX = 0;
1136 alignY = 0;
1137 }
1138 x -= alignX;
1139 y -= alignY;
1140 *offset = SkPoint::Make(x, y);
1141
1142 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1143 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1144 fallbackPos);
1145 SkGlyphCache::AttachCache(origPaintCache);
1146}
1147
joshualitt374b2f72015-07-21 08:05:03 -07001148void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001149 SkGlyphCache* cache, const SkPaint& skPaint,
1150 GrColor color,
1151 const SkMatrix& viewMatrix,
1152 const char text[], size_t byteLength,
1153 const SkScalar pos[], int scalarsPerPosition,
1154 const SkPoint& offset, const SkIRect& clipRect,
1155 SkScalar textRatio,
1156 SkTDArray<char>* fallbackTxt,
1157 SkTDArray<SkScalar>* fallbackPos) {
1158
1159 SkASSERT(byteLength == 0 || text != NULL);
1160 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1161
1162 // nothing to draw
1163 if (text == NULL || byteLength == 0) {
1164 return;
1165 }
1166
1167 fCurrStrike = NULL;
1168
1169 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1170 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1171
1172 const char* stop = text + byteLength;
1173
1174 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1175 while (text < stop) {
1176 const char* lastText = text;
1177 // the last 2 parameters are ignored
1178 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1179
1180 if (glyph.fWidth) {
1181 SkScalar x = offset.x() + pos[0];
1182 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1183
1184 if (!this->dfAppendGlyph(blob,
1185 runIndex,
1186 GrGlyph::Pack(glyph.getGlyphID(),
1187 glyph.getSubXFixed(),
1188 glyph.getSubYFixed(),
1189 GrGlyph::kDistance_MaskStyle),
1190 x, y, color, fontScaler, clipRect,
1191 textRatio, viewMatrix)) {
1192 // couldn't append, send to fallback
1193 fallbackTxt->append(SkToInt(text-lastText), lastText);
1194 *fallbackPos->append() = pos[0];
1195 if (2 == scalarsPerPosition) {
1196 *fallbackPos->append() = pos[1];
1197 }
1198 }
1199 }
1200 pos += scalarsPerPosition;
1201 }
1202 } else {
1203 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1204 : SK_Scalar1;
1205 while (text < stop) {
1206 const char* lastText = text;
1207 // the last 2 parameters are ignored
1208 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1209
1210 if (glyph.fWidth) {
1211 SkScalar x = offset.x() + pos[0];
1212 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1213
1214 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1215 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1216
1217 if (!this->dfAppendGlyph(blob,
1218 runIndex,
1219 GrGlyph::Pack(glyph.getGlyphID(),
1220 glyph.getSubXFixed(),
1221 glyph.getSubYFixed(),
1222 GrGlyph::kDistance_MaskStyle),
1223 x - advanceX, y - advanceY, color,
1224 fontScaler,
1225 clipRect,
1226 textRatio,
1227 viewMatrix)) {
1228 // couldn't append, send to fallback
1229 fallbackTxt->append(SkToInt(text-lastText), lastText);
1230 *fallbackPos->append() = pos[0];
1231 if (2 == scalarsPerPosition) {
1232 *fallbackPos->append() = pos[1];
1233 }
1234 }
1235 }
1236 pos += scalarsPerPosition;
1237 }
1238 }
1239}
1240
joshualitt374b2f72015-07-21 08:05:03 -07001241void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001242 GrGlyph::PackedID packed,
1243 int vx, int vy, GrColor color, GrFontScaler* scaler,
1244 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001245 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001246 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001247 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001248 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001249 }
1250
1251 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001252 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001253 return;
1254 }
1255
1256 int x = vx + glyph->fBounds.fLeft;
1257 int y = vy + glyph->fBounds.fTop;
1258
1259 // keep them as ints until we've done the clip-test
1260 int width = glyph->fBounds.width();
1261 int height = glyph->fBounds.height();
1262
joshualitt2a0e9f32015-04-13 06:12:21 -07001263#if 0
1264 // Not checking the clip bounds might introduce a performance regression. However, its not
1265 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1266 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1267 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1268 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001269 // check if we clipped out
1270 if (clipRect.quickReject(x, y, x + width, y + height)) {
1271 return;
1272 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001273#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001274
1275 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001276 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001277 this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001278 return;
1279 }
1280
joshualitt1d89e8d2015-04-01 12:40:54 -07001281 GrMaskFormat format = glyph->fMaskFormat;
1282
1283 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1284 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001285 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001286 }
1287
1288 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001289
1290 size_t vertexStride = get_vertex_stride(format);
1291
1292 SkRect r;
1293 r.fLeft = SkIntToScalar(x);
1294 r.fTop = SkIntToScalar(y);
1295 r.fRight = r.fLeft + SkIntToScalar(width);
1296 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001297 subRun->fMaskFormat = format;
1298 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001299 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001300}
joshualitt1d89e8d2015-04-01 12:40:54 -07001301
joshualitt374b2f72015-07-21 08:05:03 -07001302bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001303 GrGlyph::PackedID packed,
1304 SkScalar sx, SkScalar sy, GrColor color,
1305 GrFontScaler* scaler,
1306 const SkIRect& clipRect,
1307 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001308 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001309 if (!fCurrStrike) {
1310 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001311 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001312 }
1313
1314 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001315 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001316 return true;
1317 }
1318
1319 // fallback to color glyph support
1320 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1321 return false;
1322 }
1323
1324 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1325 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1326 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1327 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1328
1329 SkScalar scale = textRatio;
1330 dx *= scale;
1331 dy *= scale;
1332 width *= scale;
1333 height *= scale;
1334 sx += dx;
1335 sy += dy;
1336 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1337
1338#if 0
1339 // check if we clipped out
1340 SkRect dstRect;
1341 viewMatrix.mapRect(&dstRect, glyphRect);
1342 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1343 SkScalarTruncToInt(dstRect.top()),
1344 SkScalarTruncToInt(dstRect.right()),
1345 SkScalarTruncToInt(dstRect.bottom()))) {
1346 return true;
1347 }
1348#endif
1349
1350 // TODO combine with the above
1351 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001352 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001353 this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001354 return true;
1355 }
1356
joshualitt9bd2daf2015-04-17 09:30:06 -07001357 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1358 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1359 subRun->fMaskFormat = kA8_GrMaskFormat;
1360
1361 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1362
1363 bool useColorVerts = !subRun->fUseLCDText;
1364 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001365 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001366 return true;
1367}
1368
joshualitt374b2f72015-07-21 08:05:03 -07001369inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph,
joshualitt19e4c022015-05-13 11:23:03 -07001370 GrFontScaler* scaler, SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001371 if (NULL == glyph->fPath) {
1372 SkPath* path = SkNEW(SkPath);
1373 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1374 // flag the glyph as being dead?
1375 SkDELETE(path);
1376 return;
1377 }
1378 glyph->fPath = path;
1379 }
1380 SkASSERT(glyph->fPath);
joshualitt374b2f72015-07-21 08:05:03 -07001381 blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y));
joshualitt9bd2daf2015-04-17 09:30:06 -07001382}
1383
joshualitt374b2f72015-07-21 08:05:03 -07001384inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run,
joshualitt9bd2daf2015-04-17 09:30:06 -07001385 Run::SubRunInfo* subRun,
1386 const SkRect& positions, GrColor color,
1387 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001388 GrGlyph* glyph) {
1389 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001390 run->fVertexBounds.joinNonEmptyArg(positions);
1391 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001392
1393 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1394
joshualitt9bd2daf2015-04-17 09:30:06 -07001395 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001396 // V0
1397 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1398 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001399 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1400 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001401 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001402
joshualitt010db532015-04-21 10:07:26 -07001403 // V1
1404 position = reinterpret_cast<SkPoint*>(vertex);
1405 position->set(positions.fLeft, positions.fBottom);
1406 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001407 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001408 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001409
joshualitt010db532015-04-21 10:07:26 -07001410 // V2
1411 position = reinterpret_cast<SkPoint*>(vertex);
1412 position->set(positions.fRight, positions.fBottom);
1413 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001414 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001415 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001416
joshualitt010db532015-04-21 10:07:26 -07001417 // V3
1418 position = reinterpret_cast<SkPoint*>(vertex);
1419 position->set(positions.fRight, positions.fTop);
1420 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001421 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001422 } else {
1423 // V0
1424 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1425 position->set(positions.fLeft, positions.fTop);
1426 vertex += vertexStride;
1427
1428 // V1
1429 position = reinterpret_cast<SkPoint*>(vertex);
1430 position->set(positions.fLeft, positions.fBottom);
1431 vertex += vertexStride;
1432
1433 // V2
1434 position = reinterpret_cast<SkPoint*>(vertex);
1435 position->set(positions.fRight, positions.fBottom);
1436 vertex += vertexStride;
1437
1438 // V3
1439 position = reinterpret_cast<SkPoint*>(vertex);
1440 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001441 }
1442
1443 subRun->fGlyphEndIndex++;
1444 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1445}
1446
bsalomon265697d2015-07-22 10:17:26 -07001447class TextBatch : public GrBatch {
joshualitt1d89e8d2015-04-01 12:40:54 -07001448public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001449 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualitt374b2f72015-07-21 08:05:03 -07001450 typedef GrAtlasTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001451 typedef Blob::Run Run;
1452 typedef Run::SubRunInfo TextInfo;
1453 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001454 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001455 int fRun;
1456 int fSubRun;
1457 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001458 SkScalar fTransX;
1459 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001460 };
1461
bsalomon265697d2015-07-22 10:17:26 -07001462 static TextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
joshualittad802c62015-04-15 05:31:57 -07001463 GrBatchFontCache* fontCache) {
bsalomon265697d2015-07-22 10:17:26 -07001464 TextBatch* batch = SkNEW(TextBatch);
1465
1466 batch->initClassID<TextBatch>();
1467 batch->fFontCache = fontCache;
1468 switch (maskFormat) {
1469 case kA8_GrMaskFormat:
1470 batch->fMaskType = kGrayscaleCoverageMask_MaskType;
1471 break;
1472 case kA565_GrMaskFormat:
1473 batch->fMaskType = kLCDCoverageMask_MaskType;
1474 break;
1475 case kARGB_GrMaskFormat:
1476 batch->fMaskType = kColorBitmapMask_MaskType;
1477 break;
1478 }
1479 batch->fBatch.fNumGlyphs = glyphCount;
1480 batch->fInstanceCount = 1;
1481 batch->fAllocatedCount = kMinAllocated;
1482 batch->fFilteredColor = 0;
1483 batch->fFontCache = fontCache;
1484 batch->fUseBGR = false;
1485 return batch;
joshualitt1d89e8d2015-04-01 12:40:54 -07001486 }
1487
bsalomon265697d2015-07-22 10:17:26 -07001488 static TextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache,
1489 DistanceAdjustTable* distanceAdjustTable,
1490 SkColor filteredColor, bool isLCD,
1491 bool useBGR) {
1492 TextBatch* batch = SkNEW(TextBatch);
1493 batch->initClassID<TextBatch>();
1494 batch->fFontCache = fontCache;
1495 batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
1496 batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
1497 batch->fFilteredColor = filteredColor;
1498 batch->fUseBGR = useBGR;
1499 batch->fBatch.fNumGlyphs = glyphCount;
1500 batch->fInstanceCount = 1;
1501 batch->fAllocatedCount = kMinAllocated;
1502 return batch;
joshualitt9bd2daf2015-04-17 09:30:06 -07001503 }
1504
bsalomon265697d2015-07-22 10:17:26 -07001505 const char* name() const override { return "TextBatch"; }
joshualitt1d89e8d2015-04-01 12:40:54 -07001506
1507 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001508 if (kColorBitmapMask_MaskType == fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001509 out->setUnknownFourComponents();
1510 } else {
1511 out->setKnownFourComponents(fBatch.fColor);
1512 }
1513 }
1514
1515 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001516 switch (fMaskType) {
1517 case kGrayscaleDistanceField_MaskType:
1518 case kGrayscaleCoverageMask_MaskType:
joshualitt1d89e8d2015-04-01 12:40:54 -07001519 out->setUnknownSingleComponent();
bsalomon265697d2015-07-22 10:17:26 -07001520 break;
1521 case kLCDCoverageMask_MaskType:
1522 case kLCDDistanceField_MaskType:
1523 out->setUnknownOpaqueFourComponents();
joshualitt1d89e8d2015-04-01 12:40:54 -07001524 out->setUsingLCDCoverage();
bsalomon265697d2015-07-22 10:17:26 -07001525 break;
1526 case kColorBitmapMask_MaskType:
1527 out->setKnownSingleComponent(0xff);
joshualitt1d89e8d2015-04-01 12:40:54 -07001528 }
1529 }
1530
1531 void initBatchTracker(const GrPipelineInfo& init) override {
1532 // Handle any color overrides
bsalomon7765a472015-07-08 11:26:37 -07001533 if (!init.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001534 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001535 }
joshualitt416e14f2015-07-10 09:05:57 -07001536 init.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001537
1538 // setup batch properties
bsalomon7765a472015-07-08 11:26:37 -07001539 fBatch.fColorIgnored = !init.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001540 fBatch.fColor = fGeoData[0].fColor;
bsalomon7765a472015-07-08 11:26:37 -07001541 fBatch.fUsesLocalCoords = init.readsLocalCoords();
1542 fBatch.fCoverageIgnored = !init.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001543 }
1544
bsalomonb5238a72015-05-05 07:49:49 -07001545 struct FlushInfo {
1546 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1547 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1548 int fGlyphsToFlush;
1549 int fVertexOffset;
1550 };
1551
joshualitt1d89e8d2015-04-01 12:40:54 -07001552 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1553 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1554 // TODO actually only invert if we don't have RGBA
1555 SkMatrix localMatrix;
1556 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1557 SkDebugf("Cannot invert viewmatrix\n");
1558 return;
1559 }
1560
bsalomon265697d2015-07-22 10:17:26 -07001561 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
joshualitt62db8ba2015-04-09 08:22:37 -07001562 if (!texture) {
1563 SkDebugf("Could not allocate backing texture for atlas\n");
1564 return;
1565 }
1566
bsalomon265697d2015-07-22 10:17:26 -07001567 bool usesDistanceFields = this->usesDistanceFields();
1568 GrMaskFormat maskFormat = this->maskFormat();
1569 bool isLCD = this->isLCD();
1570
joshualitt9bd2daf2015-04-17 09:30:06 -07001571 SkAutoTUnref<const GrGeometryProcessor> gp;
bsalomon265697d2015-07-22 10:17:26 -07001572 if (usesDistanceFields) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001573 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1574 texture));
1575 } else {
1576 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001577 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1578 texture,
1579 params,
bsalomon265697d2015-07-22 10:17:26 -07001580 maskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001581 localMatrix,
1582 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001583 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001584
bsalomonb5238a72015-05-05 07:49:49 -07001585 FlushInfo flushInfo;
1586 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001587 size_t vertexStride = gp->getVertexStride();
bsalomon265697d2015-07-22 10:17:26 -07001588 SkASSERT(vertexStride == (usesDistanceFields ?
1589 get_vertex_stride_df(maskFormat, isLCD) :
1590 get_vertex_stride(maskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001591
joshualittb8c241a2015-05-19 08:23:30 -07001592 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001593
1594 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001595 int instanceCount = fInstanceCount;
bsalomon8415abe2015-05-04 11:41:41 -07001596 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001597
robertphillipse40d3972015-05-07 09:51:43 -07001598 void* vertices = batchTarget->makeVertSpace(vertexStride,
1599 glyphCount * kVerticesPerGlyph,
1600 &vertexBuffer,
1601 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001602 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1603 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1604 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001605 SkDebugf("Could not allocate vertices\n");
1606 return;
1607 }
1608
1609 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1610
joshualitt25ba7ea2015-04-21 07:49:49 -07001611 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1612 // in a row
1613 const SkDescriptor* desc = NULL;
1614 SkGlyphCache* cache = NULL;
1615 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001616 SkTypeface* typeface = NULL;
1617
joshualitt1d89e8d2015-04-01 12:40:54 -07001618 for (int i = 0; i < instanceCount; i++) {
1619 Geometry& args = fGeoData[i];
1620 Blob* blob = args.fBlob;
1621 Run& run = blob->fRuns[args.fRun];
1622 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1623
bsalomon265697d2015-07-22 10:17:26 -07001624 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001625 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
1626 run.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001627 bool regenerateColors;
bsalomon265697d2015-07-22 10:17:26 -07001628 if (usesDistanceFields) {
1629 regenerateColors = !isLCD && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001630 } else {
bsalomon265697d2015-07-22 10:17:26 -07001631 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001632 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001633 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001634 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1635
1636 // We regenerate both texture coords and colors in the blob itself, and update the
1637 // atlas generation. If we don't end up purging any unused plots, we can avoid
1638 // regenerating the coords. We could take a finer grained approach to updating texture
1639 // coords but its not clear if the extra bookkeeping would offset any gains.
1640 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1641 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1642 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1643 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001644 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001645 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001646 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001647
1648 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1649 // generating its texture coords, we have to track whether or not the strike has
1650 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1651 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1652 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1653 // new strike, we instead keep our ref to the old strike and use the packed ids from
1654 // it. These ids will still be valid as long as we hold the ref. When we are done
1655 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1656 bool regenerateGlyphs = false;
1657 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001658 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001659 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001660
1661 // We can reuse if we have a valid strike and our descriptors / typeface are the
1662 // same
joshualitt97202d22015-04-22 13:47:02 -07001663 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1664 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001665 run.fDescriptor.getDesc();
1666 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1667 !(desc->equals(*newDesc))) {
1668 if (cache) {
1669 SkGlyphCache::AttachCache(cache);
1670 }
1671 desc = newDesc;
1672 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1673 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001674 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001675 typeface = run.fTypeface;
1676 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001677
joshualittae32c102015-04-21 09:37:57 -07001678 if (run.fStrike->isAbandoned()) {
1679 regenerateGlyphs = true;
1680 strike = fFontCache->getStrike(scaler);
1681 } else {
1682 strike = run.fStrike;
1683 }
1684 }
1685
1686 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001687 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001688 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1689 GrGlyph* glyph;
1690 if (regenerateGlyphs) {
1691 // Get the id from the old glyph, and use the new strike to lookup
1692 // the glyph.
1693 glyph = blob->fGlyphs[glyphOffset];
1694 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1695 scaler);
1696 }
1697 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001698 SkASSERT(glyph);
bsalomon265697d2015-07-22 10:17:26 -07001699 SkASSERT(glyph->fMaskFormat == this->maskFormat());
joshualitt1d89e8d2015-04-01 12:40:54 -07001700
1701 if (!fFontCache->hasGlyph(glyph) &&
1702 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
bsalomonb5238a72015-05-05 07:49:49 -07001703 this->flush(batchTarget, &flushInfo);
joshualittb8c241a2015-05-19 08:23:30 -07001704 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001705 brokenRun = glyphIdx > 0;
1706
joshualittae32c102015-04-21 09:37:57 -07001707 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1708 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001709 scaler);
1710 SkASSERT(success);
1711 }
joshualittb4c507e2015-04-08 08:07:59 -07001712 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1713 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001714
1715 // Texture coords are the last vertex attribute so we get a pointer to the
1716 // first one and then map with stride in regenerateTextureCoords
1717 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1718 vertex += info.fVertexStartIndex;
1719 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1720 vertex += vertexStride - sizeof(SkIPoint16);
1721
1722 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1723 }
1724
1725 if (regenerateColors) {
1726 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1727 vertex += info.fVertexStartIndex;
1728 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1729 this->regenerateColors(vertex, vertexStride, args.fColor);
1730 }
1731
joshualitt2a0e9f32015-04-13 06:12:21 -07001732 if (regeneratePositions) {
1733 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1734 vertex += info.fVertexStartIndex;
1735 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1736 SkScalar transX = args.fTransX;
1737 SkScalar transY = args.fTransY;
1738 this->regeneratePositions(vertex, vertexStride, transX, transY);
1739 }
bsalomonb5238a72015-05-05 07:49:49 -07001740 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001741 }
1742
joshualitt2a0e9f32015-04-13 06:12:21 -07001743 // We my have changed the color so update it here
1744 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001745 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001746 if (regenerateGlyphs) {
1747 run.fStrike.reset(SkRef(strike));
1748 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001749 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
bsalomon265697d2015-07-22 10:17:26 -07001750 fFontCache->atlasGeneration(maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001751 }
1752 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001753 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001754
1755 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1756 // have a valid atlas generation
1757 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1758 batchTarget->currentToken(),
bsalomon265697d2015-07-22 10:17:26 -07001759 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001760 }
1761
1762 // now copy all vertices
1763 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1764 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1765
1766 currVertex += byteCount;
1767 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001768 // Make sure to attach the last cache if applicable
1769 if (cache) {
1770 SkGlyphCache::AttachCache(cache);
1771 }
bsalomonb5238a72015-05-05 07:49:49 -07001772 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001773 }
1774
joshualittad802c62015-04-15 05:31:57 -07001775 // The minimum number of Geometry we will try to allocate.
1776 static const int kMinAllocated = 32;
1777
1778 // Total number of Geometry this Batch owns
1779 int instanceCount() const { return fInstanceCount; }
1780 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1781
1782 // to avoid even the initial copy of the struct, we have a getter for the first item which
1783 // is used to seed the batch with its initial geometry. After seeding, the client should call
1784 // init() so the Batch can initialize itself
1785 Geometry& geometry() { return fGeoData[0]; }
1786 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001787 const Geometry& geo = fGeoData[0];
1788 fBatch.fColor = geo.fColor;
1789 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1790
1791 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1792 // into device space
1793 const Run& run = geo.fBlob->fRuns[geo.fRun];
1794 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1795 SkRect bounds = run.fVertexBounds;
1796 fBatch.fViewMatrix.mapRect(&bounds);
1797 this->setBounds(bounds);
1798 } else {
1799 this->setBounds(run.fVertexBounds);
1800 }
joshualittad802c62015-04-15 05:31:57 -07001801 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001802
1803private:
bsalomon265697d2015-07-22 10:17:26 -07001804 TextBatch() {} // initialized in factory functions.
joshualittad802c62015-04-15 05:31:57 -07001805
bsalomon265697d2015-07-22 10:17:26 -07001806 ~TextBatch() {
joshualittad802c62015-04-15 05:31:57 -07001807 for (int i = 0; i < fInstanceCount; i++) {
1808 fGeoData[i].fBlob->unref();
1809 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001810 }
1811
bsalomon265697d2015-07-22 10:17:26 -07001812 GrMaskFormat maskFormat() const {
1813 switch (fMaskType) {
1814 case kLCDCoverageMask_MaskType:
1815 return kA565_GrMaskFormat;
1816 case kColorBitmapMask_MaskType:
1817 return kARGB_GrMaskFormat;
1818 case kGrayscaleCoverageMask_MaskType:
1819 case kGrayscaleDistanceField_MaskType:
1820 case kLCDDistanceField_MaskType:
1821 return kA8_GrMaskFormat;
1822 }
1823 return kA8_GrMaskFormat; // suppress warning
1824 }
1825
1826 bool usesDistanceFields() const {
1827 return kGrayscaleDistanceField_MaskType == fMaskType ||
1828 kLCDDistanceField_MaskType == fMaskType;
1829 }
1830
1831 bool isLCD() const {
1832 return kLCDCoverageMask_MaskType == fMaskType ||
1833 kLCDDistanceField_MaskType == fMaskType;
1834 }
1835
joshualitt1d89e8d2015-04-01 12:40:54 -07001836 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1837 int width = glyph->fBounds.width();
1838 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001839
joshualitt9bd2daf2015-04-17 09:30:06 -07001840 int u0, v0, u1, v1;
bsalomon265697d2015-07-22 10:17:26 -07001841 if (this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001842 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1843 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1844 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1845 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1846 } else {
1847 u0 = glyph->fAtlasLocation.fX;
1848 v0 = glyph->fAtlasLocation.fY;
1849 u1 = u0 + width;
1850 v1 = v0 + height;
1851 }
1852
joshualitt1d89e8d2015-04-01 12:40:54 -07001853 SkIPoint16* textureCoords;
1854 // V0
1855 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1856 textureCoords->set(u0, v0);
1857 vertex += vertexStride;
1858
1859 // V1
1860 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1861 textureCoords->set(u0, v1);
1862 vertex += vertexStride;
1863
1864 // V2
1865 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1866 textureCoords->set(u1, v1);
1867 vertex += vertexStride;
1868
1869 // V3
1870 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1871 textureCoords->set(u1, v0);
1872 }
1873
1874 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1875 for (int i = 0; i < kVerticesPerGlyph; i++) {
1876 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1877 *vcolor = color;
1878 vertex += vertexStride;
1879 }
1880 }
1881
joshualitt2a0e9f32015-04-13 06:12:21 -07001882 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1883 SkScalar transY) {
1884 for (int i = 0; i < kVerticesPerGlyph; i++) {
1885 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1886 point->fX += transX;
1887 point->fY += transY;
1888 vertex += vertexStride;
1889 }
1890 }
1891
bsalomonb5238a72015-05-05 07:49:49 -07001892 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001893 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001894 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001895 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001896 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001897 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001898 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001899 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001900 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1901 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001902 }
1903
1904 GrColor color() const { return fBatch.fColor; }
1905 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1906 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1907 int numGlyphs() const { return fBatch.fNumGlyphs; }
1908
1909 bool onCombineIfPossible(GrBatch* t) override {
joshualitt8cab9a72015-07-16 09:13:50 -07001910 if (!this->pipeline()->isEqual(*t->pipeline())) {
1911 return false;
1912 }
1913
bsalomon265697d2015-07-22 10:17:26 -07001914 TextBatch* that = t->cast<TextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001915
bsalomon265697d2015-07-22 10:17:26 -07001916 if (fMaskType != that->fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001917 return false;
1918 }
1919
bsalomon265697d2015-07-22 10:17:26 -07001920 if (!this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001921 // TODO we can often batch across LCD text if we have dual source blending and don't
1922 // have to use the blend constant
bsalomon265697d2015-07-22 10:17:26 -07001923 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001924 return false;
1925 }
joshualitt9bd2daf2015-04-17 09:30:06 -07001926 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1927 return false;
1928 }
1929 } else {
joshualitt9bd2daf2015-04-17 09:30:06 -07001930 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1931 return false;
1932 }
1933
1934 if (fFilteredColor != that->fFilteredColor) {
1935 return false;
1936 }
1937
joshualitt9bd2daf2015-04-17 09:30:06 -07001938 if (fUseBGR != that->fUseBGR) {
1939 return false;
1940 }
1941
joshualitt9bd2daf2015-04-17 09:30:06 -07001942 // TODO see note above
bsalomon265697d2015-07-22 10:17:26 -07001943 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001944 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001945 }
1946
1947 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001948
1949 // copy that->geoData(). We do this manually for performance reasons
1950 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1951 int otherInstanceCount = that->instanceCount();
1952 int allocSize = otherInstanceCount + fInstanceCount;
1953 if (allocSize > fAllocatedCount) {
1954 while (allocSize > fAllocatedCount) {
1955 fAllocatedCount = fAllocatedCount << 1;
1956 }
1957 fGeoData.realloc(fAllocatedCount);
1958 }
1959
1960 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1961 otherInstanceCount * sizeof(Geometry));
1962 int total = fInstanceCount + otherInstanceCount;
1963 for (int i = fInstanceCount; i < total; i++) {
1964 fGeoData[i].fBlob->ref();
1965 }
1966 fInstanceCount = total;
joshualitt99c7c072015-05-01 13:43:30 -07001967
1968 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001969 return true;
1970 }
1971
joshualitt9bd2daf2015-04-17 09:30:06 -07001972 // TODO just use class params
1973 // TODO trying to figure out why lcd is so whack
1974 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1975 GrColor color, GrTexture* texture) {
1976 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
bsalomon265697d2015-07-22 10:17:26 -07001977 bool isLCD = this->isLCD();
joshualitt9bd2daf2015-04-17 09:30:06 -07001978 // set up any flags
bsalomon265697d2015-07-22 10:17:26 -07001979 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt9bd2daf2015-04-17 09:30:06 -07001980
1981 // see if we need to create a new effect
bsalomon265697d2015-07-22 10:17:26 -07001982 if (isLCD) {
1983 flags |= kUseLCD_DistanceFieldEffectFlag;
1984 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
1985 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1986
joshualitt9bd2daf2015-04-17 09:30:06 -07001987 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1988
1989 float redCorrection =
1990 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1991 float greenCorrection =
1992 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1993 float blueCorrection =
1994 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1995 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1996 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1997 greenCorrection,
1998 blueCorrection);
1999
2000 return GrDistanceFieldLCDTextGeoProc::Create(color,
2001 viewMatrix,
2002 texture,
2003 params,
2004 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07002005 flags,
2006 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002007 } else {
2008 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07002009#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07002010 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07002011 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
2012 return GrDistanceFieldA8TextGeoProc::Create(color,
2013 viewMatrix,
2014 texture,
2015 params,
2016 correction,
joshualittb8c241a2015-05-19 08:23:30 -07002017 flags,
2018 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002019#else
2020 return GrDistanceFieldA8TextGeoProc::Create(color,
2021 viewMatrix,
2022 texture,
2023 params,
joshualittb8c241a2015-05-19 08:23:30 -07002024 flags,
2025 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002026#endif
2027 }
2028
2029 }
2030
joshualitt1d89e8d2015-04-01 12:40:54 -07002031 struct BatchTracker {
2032 GrColor fColor;
2033 SkMatrix fViewMatrix;
2034 bool fUsesLocalCoords;
2035 bool fColorIgnored;
2036 bool fCoverageIgnored;
2037 int fNumGlyphs;
2038 };
2039
2040 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002041 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2042 int fInstanceCount;
2043 int fAllocatedCount;
bsalomon265697d2015-07-22 10:17:26 -07002044
2045 enum MaskType {
2046 kGrayscaleCoverageMask_MaskType,
2047 kLCDCoverageMask_MaskType,
2048 kColorBitmapMask_MaskType,
2049 kGrayscaleDistanceField_MaskType,
2050 kLCDDistanceField_MaskType,
2051 } fMaskType;
2052 bool fUseBGR; // fold this into the enum?
2053
joshualitt1d89e8d2015-04-01 12:40:54 -07002054 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002055
2056 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002057 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002058 SkColor fFilteredColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07002059};
2060
robertphillips2334fb62015-06-17 05:43:33 -07002061void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002062 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002063 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2064 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2065 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002066
joshualitt9a27e632015-04-06 10:53:36 -07002067 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2068 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002069
joshualitt9a27e632015-04-06 10:53:36 -07002070 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002071
joshualitt9a27e632015-04-06 10:53:36 -07002072 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2073 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002074 }
2075
robertphillipsfcf78292015-06-19 11:49:52 -07002076 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002077
2078 switch (it.positioning()) {
2079 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002080 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002081 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002082 textLen, x + offset.x(), y + offset.y(), clipBounds);
2083 break;
2084 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002085 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002086 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002087 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2088 clipBounds);
2089 break;
2090 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002091 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002092 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002093 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2094 break;
2095 }
2096}
2097
bsalomon265697d2015-07-22 10:17:26 -07002098inline GrBatch*
joshualitt374b2f72015-07-21 08:05:03 -07002099GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info,
joshualitt79dfb2b2015-05-11 08:58:08 -07002100 int glyphCount, int run, int subRun,
2101 GrColor color, SkScalar transX, SkScalar transY,
2102 const SkPaint& skPaint) {
2103 GrMaskFormat format = info.fMaskFormat;
2104 GrColor subRunColor;
2105 if (kARGB_GrMaskFormat == format) {
2106 uint8_t paintAlpha = skPaint.getAlpha();
2107 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2108 } else {
2109 subRunColor = color;
2110 }
2111
bsalomon265697d2015-07-22 10:17:26 -07002112 TextBatch* batch;
joshualitt79dfb2b2015-05-11 08:58:08 -07002113 if (info.fDrawAsDistanceFields) {
2114 SkColor filteredColor;
2115 SkColorFilter* colorFilter = skPaint.getColorFilter();
2116 if (colorFilter) {
2117 filteredColor = colorFilter->filterColor(skPaint.getColor());
2118 } else {
2119 filteredColor = skPaint.getColor();
2120 }
robertphillipsfcf78292015-06-19 11:49:52 -07002121 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
bsalomon265697d2015-07-22 10:17:26 -07002122 batch = TextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
2123 fDistanceAdjustTable, filteredColor,
2124 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002125 } else {
bsalomon265697d2015-07-22 10:17:26 -07002126 batch = TextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
joshualitt79dfb2b2015-05-11 08:58:08 -07002127 }
bsalomon265697d2015-07-22 10:17:26 -07002128 TextBatch::Geometry& geometry = batch->geometry();
joshualitt79dfb2b2015-05-11 08:58:08 -07002129 geometry.fBlob = SkRef(cacheBlob);
2130 geometry.fRun = run;
2131 geometry.fSubRun = subRun;
2132 geometry.fColor = subRunColor;
2133 geometry.fTransX = transX;
2134 geometry.fTransY = transY;
2135 batch->init();
2136
2137 return batch;
2138}
2139
robertphillips2334fb62015-06-17 05:43:33 -07002140inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt374b2f72015-07-21 08:05:03 -07002141 GrAtlasTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002142 SkScalar transX, SkScalar transY,
2143 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002144 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2145 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2146 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2147 if (0 == glyphCount) {
2148 continue;
2149 }
2150
bsalomon265697d2015-07-22 10:17:26 -07002151 SkAutoTUnref<GrBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2152 subRun, color, transX, transY,
2153 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002154 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002155 }
2156}
2157
joshualitt374b2f72015-07-21 08:05:03 -07002158inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002159 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002160 SkScalar transX, SkScalar transY,
2161 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002162 if (!cacheBlob->fBigGlyphs.count()) {
2163 return;
2164 }
2165
2166 SkMatrix pathMatrix;
2167 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2168 SkDebugf("could not invert viewmatrix\n");
2169 return;
2170 }
2171
joshualitt9a27e632015-04-06 10:53:36 -07002172 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt374b2f72015-07-21 08:05:03 -07002173 GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002174 bigGlyph.fVx += transX;
2175 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002176 SkMatrix translate = cacheBlob->fViewMatrix;
2177 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2178
robertphillips2334fb62015-06-17 05:43:33 -07002179 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002180 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002181 }
2182}
joshualitt9a27e632015-04-06 10:53:36 -07002183
robertphillips2334fb62015-06-17 05:43:33 -07002184void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt374b2f72015-07-21 08:05:03 -07002185 GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002186 GrRenderTarget* rt,
2187 const SkPaint& skPaint,
2188 const GrPaint& grPaint,
2189 SkDrawFilter* drawFilter,
2190 const GrClip& clip,
2191 const SkMatrix& viewMatrix,
2192 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002193 SkScalar x, SkScalar y,
2194 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002195 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2196 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002197 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002198
2199 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002200
2201 SkTextBlob::RunIterator it(blob);
2202 for (int run = 0; !it.done(); it.next(), run++) {
2203 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002204 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002205 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002206 continue;
2207 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002208 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002209 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002210 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002211 }
2212
2213 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002214 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002215}
2216
joshualitt374b2f72015-07-21 08:05:03 -07002217void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002218 GrRenderTarget* rt,
2219 const SkPaint& skPaint,
2220 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002221 const GrClip& clip,
2222 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002223 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002224
2225 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002226 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillips2334fb62015-06-17 05:43:33 -07002227 this->flushRun(&pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002228 }
2229
2230 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002231 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002232}
joshualitt79dfb2b2015-05-11 08:58:08 -07002233
2234///////////////////////////////////////////////////////////////////////////////////////////////////
2235
2236#ifdef GR_TEST_UTILS
2237
joshualitt6c891102015-05-13 08:51:49 -07002238BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002239 static uint32_t gContextID = SK_InvalidGenID;
2240 static GrAtlasTextContext* gTextContext = NULL;
robertphillipsfcf78292015-06-19 11:49:52 -07002241 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002242
2243 if (context->uniqueID() != gContextID) {
2244 gContextID = context->uniqueID();
2245 SkDELETE(gTextContext);
robertphillips2334fb62015-06-17 05:43:33 -07002246
joshualitt79dfb2b2015-05-11 08:58:08 -07002247 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2248 // because we don't really want to have a gpu device here.
2249 // We enable distance fields by twiddling a knob on the paint
robertphillipsfcf78292015-06-19 11:49:52 -07002250 GrDrawContext* drawContext = context->drawContext(&gSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -07002251
robertphillipsfcf78292015-06-19 11:49:52 -07002252 gTextContext = GrAtlasTextContext::Create(context, drawContext, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002253 }
2254
2255 // create dummy render target
2256 GrSurfaceDesc desc;
2257 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2258 desc.fWidth = 1024;
2259 desc.fHeight = 1024;
2260 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002261 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002262 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2263 SkASSERT(texture);
2264 SkASSERT(NULL != texture->asRenderTarget());
2265 GrRenderTarget* rt = texture->asRenderTarget();
2266
2267 // Setup dummy SkPaint / GrPaint
2268 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002269 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002270 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002271 skPaint.setColor(color);
2272 skPaint.setLCDRenderText(random->nextBool());
2273 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2274 skPaint.setSubpixelText(random->nextBool());
2275
2276 GrPaint grPaint;
2277 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2278 SkFAIL("couldn't convert paint\n");
2279 }
2280
2281 const char* text = "The quick brown fox jumps over the lazy dog.";
2282 int textLen = (int)strlen(text);
2283
2284 // Setup clip
2285 GrClip clip;
2286 SkIRect noClip = SkIRect::MakeLargest();
2287
2288 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2289 // intend to test the batch with this unit test, that is okay.
joshualitt374b2f72015-07-21 08:05:03 -07002290 SkAutoTUnref<GrAtlasTextBlob> blob(
joshualitt79dfb2b2015-05-11 08:58:08 -07002291 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2292 static_cast<size_t>(textLen), 0, 0, noClip));
2293
2294 SkScalar transX = static_cast<SkScalar>(random->nextU());
2295 SkScalar transY = static_cast<SkScalar>(random->nextU());
joshualitt374b2f72015-07-21 08:05:03 -07002296 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
joshualitt79dfb2b2015-05-11 08:58:08 -07002297 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2298}
2299
2300#endif