blob: fa96a8dddabfc9e3ed2e2aec8f7e8f91bbe60920 [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 "GrBatchFontCache.h"
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt79dfb2b2015-05-11 08:58:08 -070011#include "GrBatchTest.h"
robertphillipsccb1b572015-05-27 11:02:55 -070012#include "GrBlurUtils.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070013#include "GrDefaultGeoProcFactory.h"
robertphillipsea461502015-05-26 11:38:03 -070014#include "GrDrawContext.h"
robertphillips2334fb62015-06-17 05:43:33 -070015#include "GrDrawTarget.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070016#include "GrFontScaler.h"
bsalomoned0bcad2015-05-04 10:36:42 -070017#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070018#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070019#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070020#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070021#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070022
23#include "SkAutoKern.h"
24#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070025#include "SkColorFilter.h"
26#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070027#include "SkDraw.h"
28#include "SkDrawFilter.h"
29#include "SkDrawProcs.h"
30#include "SkGlyphCache.h"
31#include "SkGpuDevice.h"
32#include "SkGr.h"
33#include "SkPath.h"
34#include "SkRTConf.h"
35#include "SkStrokeRec.h"
36#include "SkTextBlob.h"
37#include "SkTextMapStateProc.h"
38
bsalomon16b99132015-08-13 14:55:50 -070039#include "batches/GrVertexBatch.h"
joshualitt74417822015-08-07 11:42:16 -070040
joshualitt1d89e8d2015-04-01 12:40:54 -070041#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
robertphillipsf6703fa2015-09-01 05:36:47 -070099GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps& surfaceProps)
100 : INHERITED(context, surfaceProps)
101 , fDistanceAdjustTable(new DistanceAdjustTable) {
joshualittb7133be2015-04-08 09:08:31 -0700102 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
103 // vertexStride
bungeman99fe8222015-08-20 07:57:51 -0700104 static_assert(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
105 "vertex_attribute_changed");
halcanary96fcdcc2015-08-27 07:41:13 -0700106 fCurrStrike = nullptr;
joshualittb7133be2015-04-08 09:08:31 -0700107 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700108}
109
robertphillips9fc82752015-06-19 04:46:45 -0700110void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700111
112 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
113 // text. The mask gamma hack is based off of guessing what the blend color is going to
114 // be, and adjusting the mask so that when run through the linear blend will
115 // produce the value closest to the desired result. However, in practice this means
116 // that the 'adjusted' mask is just increasing or decreasing the coverage of
117 // the mask depending on what it is thought it will blit against. For black (on
118 // assumed white) this means that coverages are decreased (on a curve). For white (on
119 // assumed black) this means that coverages are increased (on a a curve). At
120 // middle (perceptual) gray (which could be blit against anything) the coverages
121 // remain the same.
122 //
123 // The idea here is that instead of determining the initial (real) coverage and
124 // then adjusting that coverage, we determine an adjusted coverage directly by
125 // essentially manipulating the geometry (in this case, the distance to the glyph
126 // edge). So for black (on assumed white) this thins a bit; for white (on
127 // assumed black) this fake bolds the geometry a bit.
128 //
129 // The distance adjustment is calculated by determining the actual coverage value which
130 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
131 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
132 // actual edge. So by subtracting this distance adjustment and computing without the
133 // the coverage adjustment we should get 0.5 coverage at the same point.
134 //
135 // This has several implications:
136 // For non-gray lcd smoothed text, each subpixel essentially is using a
137 // slightly different geometry.
138 //
139 // For black (on assumed white) this may not cover some pixels which were
140 // previously covered; however those pixels would have been only slightly
141 // covered and that slight coverage would have been decreased anyway. Also, some pixels
142 // which were previously fully covered may no longer be fully covered.
143 //
144 // For white (on assumed black) this may cover some pixels which weren't
145 // previously covered at all.
146
147 int width, height;
148 size_t size;
149
150#ifdef SK_GAMMA_CONTRAST
151 SkScalar contrast = SK_GAMMA_CONTRAST;
152#else
153 SkScalar contrast = 0.5f;
154#endif
robertphillips9fc82752015-06-19 04:46:45 -0700155 SkScalar paintGamma = SK_GAMMA_EXPONENT;
156 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700157
158 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
159 &width, &height);
160
161 SkASSERT(kExpectedDistanceAdjustTableSize == height);
halcanary385fe4d2015-08-26 13:07:48 -0700162 fTable = new SkScalar[height];
joshualitt9bd2daf2015-04-17 09:30:06 -0700163
164 SkAutoTArray<uint8_t> data((int)size);
165 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
166
167 // find the inverse points where we cross 0.5
168 // binsearch might be better, but we only need to do this once on creation
169 for (int row = 0; row < height; ++row) {
170 uint8_t* rowPtr = data.get() + row*width;
171 for (int col = 0; col < width - 1; ++col) {
172 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
173 // compute point where a mask value will give us a result of 0.5
174 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
175 float borderAlpha = (col + interp) / 255.f;
176
177 // compute t value for that alpha
178 // this is an approximate inverse for smoothstep()
179 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
180
181 // compute distance which gives us that t value
182 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
183 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
184
185 fTable[row] = d;
186 break;
187 }
188 }
189 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700190}
191
joshualittdbd35932015-04-02 09:19:04 -0700192GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -0700193 const SkSurfaceProps& surfaceProps) {
robertphillipsf6703fa2015-09-01 05:36:47 -0700194 return new GrAtlasTextContext(context, surfaceProps);
joshualitt1d89e8d2015-04-01 12:40:54 -0700195}
196
joshualittdbd35932015-04-02 09:19:04 -0700197bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
198 const GrClip&,
199 const GrPaint&,
200 const SkPaint& skPaint,
201 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700202 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
203 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700204}
205
joshualitt9e36c1a2015-04-14 12:17:27 -0700206GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
207 GrColor canonicalColor = paint.computeLuminanceColor();
208 if (lcd) {
209 // This is the correct computation, but there are tons of cases where LCD can be overridden.
210 // For now we just regenerate if any run in a textblob has LCD.
211 // TODO figure out where all of these overrides are and see if we can incorporate that logic
212 // at a higher level *OR* use sRGB
213 SkASSERT(false);
214 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
215 } else {
216 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
217 // gamma corrected masks anyways, nor color
218 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
219 SkColorGetG(canonicalColor),
220 SkColorGetB(canonicalColor));
221 // reduce to our finite number of bits
222 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
223 }
224 return canonicalColor;
225}
226
227// TODO if this function ever shows up in profiling, then we can compute this value when the
228// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
229// run so this is not a big deal to compute here.
230bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
231 SkTextBlob::RunIterator it(blob);
232 for (; !it.done(); it.next()) {
233 if (it.isLCD()) {
234 return true;
235 }
236 }
237 return false;
238}
239
joshualitt2a0e9f32015-04-13 06:12:21 -0700240bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
joshualitt374b2f72015-07-21 08:05:03 -0700241 const GrAtlasTextBlob& blob, const SkPaint& paint,
jvanverth0628a522015-08-18 07:44:22 -0700242 GrColor color, const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700243 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700244 // If we have LCD text then our canonical color will be set to transparent, in this case we have
245 // to regenerate the blob on any color change
jvanverth0628a522015-08-18 07:44:22 -0700246 // We use the grPaint to get any color filter effects
247 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
248 blob.fPaintColor != color) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700249 return true;
250 }
251
252 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
253 return true;
254 }
255
256 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
257 return true;
258 }
259
joshualitt53b5f442015-04-13 06:33:59 -0700260 // We only cache one masked version
261 if (blob.fKey.fHasBlur &&
262 (blob.fBlurRec.fSigma != blurRec.fSigma ||
263 blob.fBlurRec.fStyle != blurRec.fStyle ||
264 blob.fBlurRec.fQuality != blurRec.fQuality)) {
265 return true;
266 }
267
268 // Similarly, we only cache one version for each style
269 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
270 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
271 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
272 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
273 return true;
274 }
275
joshualittfcfb9fc2015-04-21 07:35:10 -0700276 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
277 // for mixed blobs if this becomes an issue.
278 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700279 // Identical viewmatrices and we can reuse in all cases
280 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
281 return false;
282 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700283 return true;
284 }
285
joshualittfcfb9fc2015-04-21 07:35:10 -0700286 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700287 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
288 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
289 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
290 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
291 return true;
292 }
293
joshualittfcfb9fc2015-04-21 07:35:10 -0700294 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
295 // but only for integer translations.
296 // This cool bit of math will determine the necessary translation to apply to the already
297 // generated vertex coordinates to move them to the correct position
298 SkScalar transX = viewMatrix.getTranslateX() +
299 viewMatrix.getScaleX() * (x - blob.fX) +
300 viewMatrix.getSkewX() * (y - blob.fY) -
301 blob.fViewMatrix.getTranslateX();
302 SkScalar transY = viewMatrix.getTranslateY() +
303 viewMatrix.getSkewY() * (x - blob.fX) +
304 viewMatrix.getScaleY() * (y - blob.fY) -
305 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700306 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700307 return true;
308 }
309
joshualittfcfb9fc2015-04-21 07:35:10 -0700310 (*outTransX) = transX;
311 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700312 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700313 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
314 // distance field being generated, so we have to regenerate in those cases
315 SkScalar newMaxScale = viewMatrix.getMaxScale();
316 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
317 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
318 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
319 return true;
320 }
321
322 (*outTransX) = x - blob.fX;
323 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700324 }
joshualitt374b2f72015-07-21 08:05:03 -0700325
joshualitta7c63892015-04-21 13:24:37 -0700326 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
327 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
328 // the blob anyways at flush time, so no need to regenerate explicitly
joshualitt2a0e9f32015-04-13 06:12:21 -0700329 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700330}
331
332
joshualitt374b2f72015-07-21 08:05:03 -0700333inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run,
joshualittdbd35932015-04-02 09:19:04 -0700334 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700335 const SkMatrix* viewMatrix,
336 bool noGamma) {
robertphillipsfcf78292015-06-19 11:49:52 -0700337 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700338 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
339 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
340}
341
robertphillipsf6703fa2015-09-01 05:36:47 -0700342void GrAtlasTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700343 const GrClip& clip, const SkPaint& skPaint,
344 const SkMatrix& viewMatrix, const SkTextBlob* blob,
345 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700346 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700347 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700348 if (fContext->abandoned()) {
349 return;
350 }
351
joshualitt374b2f72015-07-21 08:05:03 -0700352 SkAutoTUnref<GrAtlasTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700353 SkMaskFilter::BlurRec blurRec;
joshualitt374b2f72015-07-21 08:05:03 -0700354 GrAtlasTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700355 // It might be worth caching these things, but its not clear at this time
356 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
357 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700358 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700359 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700360 drawFilter);
361
362 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700363 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700364
365 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
robertphillipsfcf78292015-06-19 11:49:52 -0700366 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700367 kUnknown_SkPixelGeometry;
368
joshualitt9e36c1a2015-04-14 12:17:27 -0700369 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
370 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
371 // ensure we always match the same key
372 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
373 ComputeCanonicalColor(skPaint, hasLCD);
374
joshualitte4cee1f2015-05-11 13:04:28 -0700375 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700376 key.fUniqueID = blob->uniqueID();
377 key.fStyle = skPaint.getStyle();
378 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700379 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700380 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700381 }
382
joshualitt1d89e8d2015-04-01 12:40:54 -0700383 SkIRect clipRect;
384 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
385
joshualitt2a0e9f32015-04-13 06:12:21 -0700386 SkScalar transX = 0.f;
387 SkScalar transY = 0.f;
388
joshualitt9e36c1a2015-04-14 12:17:27 -0700389 // Though for the time being runs in the textblob can override the paint, they only touch font
390 // info.
391 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700392 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
393 return;
394 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700395
joshualittb7133be2015-04-08 09:08:31 -0700396 if (cacheBlob) {
jvanverth0628a522015-08-18 07:44:22 -0700397 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, grPaint.getColor(), blurRec,
398 viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700399 // We have to remake the blob because changes may invalidate our masks.
400 // TODO we could probably get away reuse most of the time if the pointer is unique,
401 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700402 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700403 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
404 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700405 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
jvanverth0628a522015-08-18 07:44:22 -0700406 blob, x, y, drawFilter, clipRect, rt, clip);
joshualittb7133be2015-04-08 09:08:31 -0700407 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700408 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
joshualitt7e7b5c52015-07-21 12:56:56 -0700409 // offsets. Note, we offset the vertex bounds right before flushing
joshualitt2a0e9f32015-04-13 06:12:21 -0700410 cacheBlob->fViewMatrix = viewMatrix;
411 cacheBlob->fX = x;
412 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700413 fCache->makeMRU(cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700414#ifdef CACHE_SANITY_CHECK
415 {
416 int glyphCount = 0;
417 int runCount = 0;
418 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
419 SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyphCount, runCount,
420 kGrayTextVASize));
421 GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint);
422 this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix,
jvanverth0628a522015-08-18 07:44:22 -0700423 blob, x, y, drawFilter, clipRect, rt, clip);
joshualitt259fbf12015-07-21 11:39:34 -0700424 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
425 }
426
427#endif
joshualitt1d89e8d2015-04-01 12:40:54 -0700428 }
429 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700430 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700431 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
432 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700433 } else {
434 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
435 }
robertphillips9c240a12015-05-28 07:45:59 -0700436 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
jvanverth0628a522015-08-18 07:44:22 -0700437 blob, x, y, drawFilter, clipRect, rt, clip);
joshualitt1d89e8d2015-04-01 12:40:54 -0700438 }
439
robertphillipsf6703fa2015-09-01 05:36:47 -0700440 this->flush(blob, cacheBlob, dc, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700441 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700442}
443
joshualitt9bd2daf2015-04-17 09:30:06 -0700444inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
445 const SkMatrix& viewMatrix) {
446 // TODO: support perspective (need getMaxScale replacement)
447 if (viewMatrix.hasPerspective()) {
448 return false;
449 }
450
451 SkScalar maxScale = viewMatrix.getMaxScale();
452 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
453 // Hinted text looks far better at small resolutions
454 // Scaling up beyond 2x yields undesireable artifacts
jvanverth34d72882015-06-22 08:08:09 -0700455 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700456 return false;
457 }
458
bsalomonafcd7cd2015-08-31 12:39:41 -0700459 bool useDFT = fSurfaceProps.isUseDeviceIndependentFonts();
robertphillipsbcd7ab52015-06-18 05:27:18 -0700460#if SK_FORCE_DISTANCE_FIELD_TEXT
461 useDFT = true;
462#endif
463
jvanverth4854d132015-06-22 06:46:56 -0700464 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700465 return false;
466 }
467
468 // rasterizers and mask filters modify alpha, which doesn't
469 // translate well to distance
470 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700471 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700472 return false;
473 }
474
475 // TODO: add some stroking support
476 if (skPaint.getStyle() != SkPaint::kFill_Style) {
477 return false;
478 }
479
480 return true;
481}
482
joshualitt374b2f72015-07-21 08:05:03 -0700483void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700484 const SkPaint& skPaint, GrColor color,
485 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700486 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700487 SkDrawFilter* drawFilter, const SkIRect& clipRect,
jvanverth0628a522015-08-18 07:44:22 -0700488 GrRenderTarget* rt, const GrClip& clip) {
489 // The color here is the GrPaint color, and it is used to determine whether we
490 // have to regenerate LCD text blobs.
491 // We use this color vs the SkPaint color because it has the colorfilter applied.
492 cacheBlob->fPaintColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -0700493 cacheBlob->fViewMatrix = viewMatrix;
494 cacheBlob->fX = x;
495 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700496
497 // Regenerate textblob
498 SkPaint runPaint = skPaint;
499 SkTextBlob::RunIterator it(blob);
500 for (int run = 0; !it.done(); it.next(), run++) {
501 int glyphCount = it.glyphCount();
502 size_t textLen = glyphCount * sizeof(uint16_t);
503 const SkPoint& offset = it.offset();
504 // applyFontToPaint() always overwrites the exact same attributes,
505 // so it is safe to not re-seed the paint for this reason.
506 it.applyFontToPaint(&runPaint);
507
508 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
509 // A false return from filter() means we should abort the current draw.
510 runPaint = skPaint;
511 continue;
512 }
513
robertphillipsfcf78292015-06-19 11:49:52 -0700514 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700515
joshualitt1d89e8d2015-04-01 12:40:54 -0700516 // setup vertex / glyphIndex for the new run
517 if (run > 0) {
518 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
519 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
520
521 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
522 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
523
524 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
525 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
526 }
527
joshualittfcfb9fc2015-04-21 07:35:10 -0700528 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
529 cacheBlob->setHasDistanceField();
530 SkPaint dfPaint = runPaint;
531 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700532 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700533 Run& runIdx = cacheBlob->fRuns[run];
534 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
535 subRun.fUseLCDText = runPaint.isLCDRenderText();
536 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700537
joshualittfcfb9fc2015-04-21 07:35:10 -0700538 SkTDArray<char> fallbackTxt;
539 SkTDArray<SkScalar> fallbackPos;
540 SkPoint dfOffset;
541 int scalarsPerPosition = 2;
542 switch (it.positioning()) {
543 case SkTextBlob::kDefault_Positioning: {
jvanverth157e6482015-09-09 08:05:12 -0700544 this->internalDrawDFText(cacheBlob, run, dfPaint, color, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700545 (const char *)it.glyphs(), textLen,
546 x + offset.x(), y + offset.y(), clipRect, textRatio,
547 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
548 break;
549 }
550 case SkTextBlob::kHorizontal_Positioning: {
551 scalarsPerPosition = 1;
552 dfOffset = SkPoint::Make(x, y + offset.y());
jvanverth157e6482015-09-09 08:05:12 -0700553 this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700554 (const char*)it.glyphs(), textLen, it.pos(),
555 scalarsPerPosition, dfOffset, clipRect, textRatio,
556 &fallbackTxt, &fallbackPos);
557 break;
558 }
559 case SkTextBlob::kFull_Positioning: {
560 dfOffset = SkPoint::Make(x, y);
jvanverth157e6482015-09-09 08:05:12 -0700561 this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700562 (const char*)it.glyphs(), textLen, it.pos(),
563 scalarsPerPosition, dfOffset, clipRect, textRatio,
564 &fallbackTxt, &fallbackPos);
565 break;
566 }
567 }
568 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700569 this->fallbackDrawPosText(cacheBlob, run, rt, clip, color, runPaint, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700570 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
571 clipRect);
572 }
joshualittfcfb9fc2015-04-21 07:35:10 -0700573 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
574 cacheBlob->fRuns[run].fDrawAsPaths = true;
575 } else {
576 cacheBlob->setHasBitmap();
577 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
578 false);
579 switch (it.positioning()) {
580 case SkTextBlob::kDefault_Positioning:
581 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
582 (const char *)it.glyphs(), textLen,
583 x + offset.x(), y + offset.y(), clipRect);
584 break;
585 case SkTextBlob::kHorizontal_Positioning:
586 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
587 (const char*)it.glyphs(), textLen, it.pos(), 1,
588 SkPoint::Make(x, y + offset.y()), clipRect);
589 break;
590 case SkTextBlob::kFull_Positioning:
591 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
592 (const char*)it.glyphs(), textLen, it.pos(), 2,
593 SkPoint::Make(x, y), clipRect);
594 break;
595 }
596 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700597 }
598
599 if (drawFilter) {
600 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
601 runPaint = skPaint;
602 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700603 }
604}
605
joshualitt374b2f72015-07-21 08:05:03 -0700606inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
joshualitt64c99cc2015-04-21 09:43:03 -0700607 SkPaint* skPaint,
608 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700609 const SkMatrix& viewMatrix) {
610 // getMaxScale doesn't support perspective, so neither do we at the moment
611 SkASSERT(!viewMatrix.hasPerspective());
612 SkScalar maxScale = viewMatrix.getMaxScale();
613 SkScalar textSize = skPaint->getTextSize();
614 SkScalar scaledTextSize = textSize;
615 // if we have non-unity scale, we need to choose our base text size
616 // based on the SkPaint's text size multiplied by the max scale factor
617 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
618 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
619 scaledTextSize *= maxScale;
620 }
621
joshualitt64c99cc2015-04-21 09:43:03 -0700622 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
623 // and ceiling. A scale outside of this range would require regenerating the distance fields
624 SkScalar dfMaskScaleFloor;
625 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700626 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700627 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700628 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700629 *textRatio = textSize / kSmallDFFontSize;
630 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
631 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700632 dfMaskScaleFloor = kSmallDFFontLimit;
633 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700634 *textRatio = textSize / kMediumDFFontSize;
635 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
636 } else {
joshualitta7c63892015-04-21 13:24:37 -0700637 dfMaskScaleFloor = kMediumDFFontLimit;
638 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700639 *textRatio = textSize / kLargeDFFontSize;
640 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
641 }
642
joshualitt64c99cc2015-04-21 09:43:03 -0700643 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
644 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
645 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
646 // tolerate before we'd have to move to a large mip size. When we actually test these values
647 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
648 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
649 // level)
joshualitta7c63892015-04-21 13:24:37 -0700650 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700651 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
652 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
653
joshualitt9bd2daf2015-04-17 09:30:06 -0700654 skPaint->setLCDRenderText(false);
655 skPaint->setAutohinted(false);
656 skPaint->setHinting(SkPaint::kNormal_Hinting);
657 skPaint->setSubpixelText(true);
658}
659
joshualitt374b2f72015-07-21 08:05:03 -0700660inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700661 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700662 GrRenderTarget* rt, const GrClip& clip,
jvanverth0628a522015-08-18 07:44:22 -0700663 GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -0700664 const SkPaint& skPaint,
665 const SkMatrix& viewMatrix,
666 const SkTDArray<char>& fallbackTxt,
667 const SkTDArray<SkScalar>& fallbackPos,
668 int scalarsPerPosition,
669 const SkPoint& offset,
670 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700671 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700672 blob->setHasBitmap();
673 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700674 // Push back a new subrun to fill and set the override descriptor
675 run.push_back();
halcanary385fe4d2015-08-26 13:07:48 -0700676 run.fOverrideDescriptor.reset(new SkAutoDescriptor);
joshualitt97202d22015-04-22 13:47:02 -0700677 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700678 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700679 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700680 run.fOverrideDescriptor->getDesc());
jvanverth0628a522015-08-18 07:44:22 -0700681 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, color, viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700682 fallbackTxt.begin(), fallbackTxt.count(),
683 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
684 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700685}
686
joshualitt374b2f72015-07-21 08:05:03 -0700687inline GrAtlasTextBlob*
jvanverth157e6482015-09-09 08:05:12 -0700688GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
689 const SkMatrix& viewMatrix, SkPaint* dfPaint,
690 SkScalar* textRatio) {
joshualitt374b2f72015-07-21 08:05:03 -0700691 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700692
693 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700694 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700695 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700696 Run& run = blob->fRuns[0];
697 PerSubRunInfo& subRun = run.fSubRunInfo.back();
698 subRun.fUseLCDText = origPaint.isLCDRenderText();
699 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700700
joshualitt9bd2daf2015-04-17 09:30:06 -0700701 return blob;
702}
703
joshualitt374b2f72015-07-21 08:05:03 -0700704inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700705GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
706 const GrPaint& paint, const SkPaint& skPaint,
707 const SkMatrix& viewMatrix,
708 const char text[], size_t byteLength,
709 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700710 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700711 SkIRect clipRect;
712 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
713
joshualitt374b2f72015-07-21 08:05:03 -0700714 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700715 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
716 SkPaint dfPaint;
717 SkScalar textRatio;
jvanverth157e6482015-09-09 08:05:12 -0700718 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700719
joshualitt9bd2daf2015-04-17 09:30:06 -0700720 SkTDArray<char> fallbackTxt;
721 SkTDArray<SkScalar> fallbackPos;
722 SkPoint offset;
jvanverth157e6482015-09-09 08:05:12 -0700723 this->internalDrawDFText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text,
joshualitt9bd2daf2015-04-17 09:30:06 -0700724 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
725 &offset, skPaint);
joshualitt9bd2daf2015-04-17 09:30:06 -0700726 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700727 this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
728 fallbackTxt, fallbackPos, 2, offset, clipRect);
joshualitt9bd2daf2015-04-17 09:30:06 -0700729 }
730 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700731 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700732 blob->fViewMatrix = viewMatrix;
733
734 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
735 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
736 byteLength, x, y, clipRect);
737 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700738 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700739 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700740}
741
joshualitt374b2f72015-07-21 08:05:03 -0700742inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700743GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
744 const GrPaint& paint, const SkPaint& skPaint,
745 const SkMatrix& viewMatrix,
746 const char text[], size_t byteLength,
747 const SkScalar pos[], int scalarsPerPosition,
748 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700749 int glyphCount = skPaint.countText(text, byteLength);
750
751 SkIRect clipRect;
752 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
753
joshualitt374b2f72015-07-21 08:05:03 -0700754 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700755 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
756 SkPaint dfPaint;
757 SkScalar textRatio;
jvanverth157e6482015-09-09 08:05:12 -0700758 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700759
760 SkTDArray<char> fallbackTxt;
761 SkTDArray<SkScalar> fallbackPos;
jvanverth157e6482015-09-09 08:05:12 -0700762 this->internalDrawDFPosText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text,
joshualitt9bd2daf2015-04-17 09:30:06 -0700763 byteLength, pos, scalarsPerPosition, offset, clipRect,
764 textRatio, &fallbackTxt, &fallbackPos);
joshualitt9bd2daf2015-04-17 09:30:06 -0700765 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700766 this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
767 fallbackTxt, fallbackPos, scalarsPerPosition, offset,
768 clipRect);
joshualitt9bd2daf2015-04-17 09:30:06 -0700769 }
770 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700771 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700772 blob->fViewMatrix = viewMatrix;
773 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
774 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
775 byteLength, pos, scalarsPerPosition, offset, clipRect);
776 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700777 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700778 return blob;
779}
780
robertphillipsf6703fa2015-09-01 05:36:47 -0700781void GrAtlasTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700782 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700783 const GrPaint& paint, const SkPaint& skPaint,
784 const SkMatrix& viewMatrix,
785 const char text[], size_t byteLength,
786 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700787 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700788 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
789 text, byteLength, x, y, regionClipBounds));
robertphillipsf6703fa2015-09-01 05:36:47 -0700790 this->flush(blob, dc, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700791}
792
robertphillipsf6703fa2015-09-01 05:36:47 -0700793void GrAtlasTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700794 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700795 const GrPaint& paint, const SkPaint& skPaint,
796 const SkMatrix& viewMatrix,
797 const char text[], size_t byteLength,
798 const SkScalar pos[], int scalarsPerPosition,
799 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700800 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700801 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
802 text, byteLength,
803 pos, scalarsPerPosition,
804 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700805
robertphillipsf6703fa2015-09-01 05:36:47 -0700806 this->flush(blob, dc, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700807}
808
joshualitt374b2f72015-07-21 08:05:03 -0700809void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700810 SkGlyphCache* cache, const SkPaint& skPaint,
811 GrColor color,
812 const SkMatrix& viewMatrix,
813 const char text[], size_t byteLength,
814 SkScalar x, SkScalar y, const SkIRect& clipRect) {
halcanary96fcdcc2015-08-27 07:41:13 -0700815 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt1d89e8d2015-04-01 12:40:54 -0700816
817 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700818 if (text == nullptr || byteLength == 0) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700819 return;
820 }
821
halcanary96fcdcc2015-08-27 07:41:13 -0700822 fCurrStrike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -0700823 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
824
825 // Get GrFontScaler from cache
826 GrFontScaler* fontScaler = GetGrFontScaler(cache);
827
828 // transform our starting point
829 {
830 SkPoint loc;
831 viewMatrix.mapXY(x, y, &loc);
832 x = loc.fX;
833 y = loc.fY;
834 }
835
836 // need to measure first
837 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
838 SkVector stopVector;
839 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
840
841 SkScalar stopX = stopVector.fX;
842 SkScalar stopY = stopVector.fY;
843
844 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
845 stopX = SkScalarHalf(stopX);
846 stopY = SkScalarHalf(stopY);
847 }
848 x -= stopX;
849 y -= stopY;
850 }
851
852 const char* stop = text + byteLength;
853
854 SkAutoKern autokern;
855
856 SkFixed fxMask = ~0;
857 SkFixed fyMask = ~0;
858 SkScalar halfSampleX, halfSampleY;
859 if (cache->isSubpixel()) {
860 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
861 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
862 if (kX_SkAxisAlignment == baseline) {
863 fyMask = 0;
864 halfSampleY = SK_ScalarHalf;
865 } else if (kY_SkAxisAlignment == baseline) {
866 fxMask = 0;
867 halfSampleX = SK_ScalarHalf;
868 }
869 } else {
870 halfSampleX = halfSampleY = SK_ScalarHalf;
871 }
872
873 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
874 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
875
876 while (text < stop) {
877 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
878
879 fx += autokern.adjust(glyph);
880
881 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700882 this->bmpAppendGlyph(blob,
883 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700884 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700885 Sk48Dot16FloorToInt(fx),
886 Sk48Dot16FloorToInt(fy),
887 color,
888 fontScaler,
889 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700890 }
891
892 fx += glyph.fAdvanceX;
893 fy += glyph.fAdvanceY;
894 }
895}
896
joshualitt374b2f72015-07-21 08:05:03 -0700897void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700898 SkGlyphCache* cache, const SkPaint& skPaint,
899 GrColor color,
900 const SkMatrix& viewMatrix,
901 const char text[], size_t byteLength,
902 const SkScalar pos[], int scalarsPerPosition,
903 const SkPoint& offset, const SkIRect& clipRect) {
halcanary96fcdcc2015-08-27 07:41:13 -0700904 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt1d89e8d2015-04-01 12:40:54 -0700905 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
906
907 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700908 if (text == nullptr || byteLength == 0) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700909 return;
910 }
911
halcanary96fcdcc2015-08-27 07:41:13 -0700912 fCurrStrike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -0700913 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
914
915 // Get GrFontScaler from cache
916 GrFontScaler* fontScaler = GetGrFontScaler(cache);
917
918 const char* stop = text + byteLength;
919 SkTextAlignProc alignProc(skPaint.getTextAlign());
920 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
921
922 if (cache->isSubpixel()) {
923 // maybe we should skip the rounding if linearText is set
924 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
925
926 SkFixed fxMask = ~0;
927 SkFixed fyMask = ~0;
928 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
929 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
930 if (kX_SkAxisAlignment == baseline) {
931 fyMask = 0;
932 halfSampleY = SK_ScalarHalf;
933 } else if (kY_SkAxisAlignment == baseline) {
934 fxMask = 0;
935 halfSampleX = SK_ScalarHalf;
936 }
937
938 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
939 while (text < stop) {
940 SkPoint tmsLoc;
941 tmsProc(pos, &tmsLoc);
942 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
943 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
944
945 const SkGlyph& glyph = glyphCacheProc(cache, &text,
946 fx & fxMask, fy & fyMask);
947
948 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700949 this->bmpAppendGlyph(blob,
950 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700951 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700952 Sk48Dot16FloorToInt(fx),
953 Sk48Dot16FloorToInt(fy),
954 color,
955 fontScaler,
956 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700957 }
958 pos += scalarsPerPosition;
959 }
960 } else {
961 while (text < stop) {
962 const char* currentText = text;
963 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
964
965 if (metricGlyph.fWidth) {
966 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
967 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
968 SkPoint tmsLoc;
969 tmsProc(pos, &tmsLoc);
970 SkPoint alignLoc;
971 alignProc(tmsLoc, metricGlyph, &alignLoc);
972
973 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
974 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
975
976 // have to call again, now that we've been "aligned"
977 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
978 fx & fxMask, fy & fyMask);
979 // the assumption is that the metrics haven't changed
980 SkASSERT(prevAdvX == glyph.fAdvanceX);
981 SkASSERT(prevAdvY == glyph.fAdvanceY);
982 SkASSERT(glyph.fWidth);
983
joshualitt9bd2daf2015-04-17 09:30:06 -0700984 this->bmpAppendGlyph(blob,
985 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700986 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700987 Sk48Dot16FloorToInt(fx),
988 Sk48Dot16FloorToInt(fy),
989 color,
990 fontScaler,
991 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700992 }
993 pos += scalarsPerPosition;
994 }
995 }
996 } else { // not subpixel
997
998 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
999 while (text < stop) {
1000 // the last 2 parameters are ignored
1001 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1002
1003 if (glyph.fWidth) {
1004 SkPoint tmsLoc;
1005 tmsProc(pos, &tmsLoc);
1006
1007 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1008 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001009 this->bmpAppendGlyph(blob,
1010 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001011 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001012 Sk48Dot16FloorToInt(fx),
1013 Sk48Dot16FloorToInt(fy),
1014 color,
1015 fontScaler,
1016 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001017 }
1018 pos += scalarsPerPosition;
1019 }
1020 } else {
1021 while (text < stop) {
1022 // the last 2 parameters are ignored
1023 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1024
1025 if (glyph.fWidth) {
1026 SkPoint tmsLoc;
1027 tmsProc(pos, &tmsLoc);
1028
1029 SkPoint alignLoc;
1030 alignProc(tmsLoc, glyph, &alignLoc);
1031
1032 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1033 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001034 this->bmpAppendGlyph(blob,
1035 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001036 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001037 Sk48Dot16FloorToInt(fx),
1038 Sk48Dot16FloorToInt(fy),
1039 color,
1040 fontScaler,
1041 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001042 }
1043 pos += scalarsPerPosition;
1044 }
1045 }
1046 }
1047}
1048
joshualitt9bd2daf2015-04-17 09:30:06 -07001049
joshualitt374b2f72015-07-21 08:05:03 -07001050void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
jvanverth157e6482015-09-09 08:05:12 -07001051 const SkPaint& skPaint, GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -07001052 const SkMatrix& viewMatrix,
1053 const char text[], size_t byteLength,
1054 SkScalar x, SkScalar y, const SkIRect& clipRect,
1055 SkScalar textRatio,
1056 SkTDArray<char>* fallbackTxt,
1057 SkTDArray<SkScalar>* fallbackPos,
1058 SkPoint* offset,
1059 const SkPaint& origPaint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001060 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt9bd2daf2015-04-17 09:30:06 -07001061
1062 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001063 if (text == nullptr || byteLength == 0) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001064 return;
1065 }
1066
1067 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1068 SkAutoDescriptor desc;
halcanary96fcdcc2015-08-27 07:41:13 -07001069 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true);
jvanverth157e6482015-09-09 08:05:12 -07001070 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
joshualitt9bd2daf2015-04-17 09:30:06 -07001071 desc.getDesc());
1072
1073 SkTArray<SkScalar> positions;
1074
1075 const char* textPtr = text;
1076 SkFixed stopX = 0;
1077 SkFixed stopY = 0;
1078 SkFixed origin = 0;
1079 switch (origPaint.getTextAlign()) {
1080 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1081 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1082 case SkPaint::kLeft_Align: origin = 0; break;
1083 }
1084
1085 SkAutoKern autokern;
1086 const char* stop = text + byteLength;
1087 while (textPtr < stop) {
1088 // don't need x, y here, since all subpixel variants will have the
1089 // same advance
1090 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1091
1092 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1093 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1094
1095 SkFixed height = glyph.fAdvanceY;
1096 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1097
1098 stopX += width;
1099 stopY += height;
1100 }
1101 SkASSERT(textPtr == stop);
1102
jvanverth157e6482015-09-09 08:05:12 -07001103 SkGlyphCache::AttachCache(origPaintCache);
1104
joshualitt9bd2daf2015-04-17 09:30:06 -07001105 // now adjust starting point depending on alignment
1106 SkScalar alignX = SkFixedToScalar(stopX);
1107 SkScalar alignY = SkFixedToScalar(stopY);
1108 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1109 alignX = SkScalarHalf(alignX);
1110 alignY = SkScalarHalf(alignY);
1111 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1112 alignX = 0;
1113 alignY = 0;
1114 }
1115 x -= alignX;
1116 y -= alignY;
1117 *offset = SkPoint::Make(x, y);
1118
jvanverth157e6482015-09-09 08:05:12 -07001119 this->internalDrawDFPosText(blob, runIndex, skPaint, color, viewMatrix, text, byteLength,
joshualitt9bd2daf2015-04-17 09:30:06 -07001120 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1121 fallbackPos);
joshualitt9bd2daf2015-04-17 09:30:06 -07001122}
1123
joshualitt374b2f72015-07-21 08:05:03 -07001124void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
jvanverth157e6482015-09-09 08:05:12 -07001125 const SkPaint& skPaint, GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -07001126 const SkMatrix& viewMatrix,
1127 const char text[], size_t byteLength,
1128 const SkScalar pos[], int scalarsPerPosition,
1129 const SkPoint& offset, const SkIRect& clipRect,
1130 SkScalar textRatio,
1131 SkTDArray<char>* fallbackTxt,
1132 SkTDArray<SkScalar>* fallbackPos) {
1133
halcanary96fcdcc2015-08-27 07:41:13 -07001134 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt9bd2daf2015-04-17 09:30:06 -07001135 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1136
1137 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001138 if (text == nullptr || byteLength == 0) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001139 return;
1140 }
1141
halcanary96fcdcc2015-08-27 07:41:13 -07001142 fCurrStrike = nullptr;
joshualitt9bd2daf2015-04-17 09:30:06 -07001143
1144 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
jvanverth157e6482015-09-09 08:05:12 -07001145 SkGlyphCache* cache = this->setupCache(&blob->fRuns[runIndex], skPaint, nullptr, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001146 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1147
1148 const char* stop = text + byteLength;
1149
1150 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1151 while (text < stop) {
1152 const char* lastText = text;
1153 // the last 2 parameters are ignored
1154 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1155
1156 if (glyph.fWidth) {
1157 SkScalar x = offset.x() + pos[0];
1158 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1159
1160 if (!this->dfAppendGlyph(blob,
1161 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001162 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001163 x, y, color, fontScaler, clipRect,
1164 textRatio, viewMatrix)) {
1165 // couldn't append, send to fallback
1166 fallbackTxt->append(SkToInt(text-lastText), lastText);
1167 *fallbackPos->append() = pos[0];
1168 if (2 == scalarsPerPosition) {
1169 *fallbackPos->append() = pos[1];
1170 }
1171 }
1172 }
1173 pos += scalarsPerPosition;
1174 }
1175 } else {
1176 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1177 : SK_Scalar1;
1178 while (text < stop) {
1179 const char* lastText = text;
1180 // the last 2 parameters are ignored
1181 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1182
1183 if (glyph.fWidth) {
1184 SkScalar x = offset.x() + pos[0];
1185 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1186
1187 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1188 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1189
1190 if (!this->dfAppendGlyph(blob,
1191 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001192 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001193 x - advanceX, y - advanceY, color,
1194 fontScaler,
1195 clipRect,
1196 textRatio,
1197 viewMatrix)) {
1198 // couldn't append, send to fallback
1199 fallbackTxt->append(SkToInt(text-lastText), lastText);
1200 *fallbackPos->append() = pos[0];
1201 if (2 == scalarsPerPosition) {
1202 *fallbackPos->append() = pos[1];
1203 }
1204 }
1205 }
1206 pos += scalarsPerPosition;
1207 }
1208 }
jvanverth157e6482015-09-09 08:05:12 -07001209
1210 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -07001211}
1212
joshualitt374b2f72015-07-21 08:05:03 -07001213void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001214 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001215 int vx, int vy, GrColor color, GrFontScaler* scaler,
1216 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001217 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001218 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001219 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1220 }
1221
joshualitt6c2c2b02015-07-24 10:37:00 -07001222 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1223 skGlyph.getSubXFixed(),
1224 skGlyph.getSubYFixed(),
1225 GrGlyph::kCoverage_MaskStyle);
1226 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001227 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001228 return;
1229 }
1230
1231 int x = vx + glyph->fBounds.fLeft;
1232 int y = vy + glyph->fBounds.fTop;
1233
1234 // keep them as ints until we've done the clip-test
1235 int width = glyph->fBounds.width();
1236 int height = glyph->fBounds.height();
1237
joshualitt2a0e9f32015-04-13 06:12:21 -07001238#if 0
1239 // Not checking the clip bounds might introduce a performance regression. However, its not
1240 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1241 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1242 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1243 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001244 // check if we clipped out
1245 if (clipRect.quickReject(x, y, x + width, y + height)) {
1246 return;
1247 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001248#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001249
1250 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001251 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001252 this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001253 return;
1254 }
1255
joshualitt1d89e8d2015-04-01 12:40:54 -07001256 GrMaskFormat format = glyph->fMaskFormat;
1257
1258 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1259 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittd9f13ae2015-07-24 11:24:31 -07001260 subRun = &run.push_back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001261 subRun->fStrike.reset(SkRef(fCurrStrike));
1262 } else if (!run.fInitialized) {
1263 subRun->fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001264 }
1265
1266 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001267
1268 size_t vertexStride = get_vertex_stride(format);
1269
1270 SkRect r;
1271 r.fLeft = SkIntToScalar(x);
1272 r.fTop = SkIntToScalar(y);
1273 r.fRight = r.fLeft + SkIntToScalar(width);
1274 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001275 subRun->fMaskFormat = format;
1276 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001277 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001278}
joshualitt1d89e8d2015-04-01 12:40:54 -07001279
joshualitt374b2f72015-07-21 08:05:03 -07001280bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001281 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001282 SkScalar sx, SkScalar sy, GrColor color,
1283 GrFontScaler* scaler,
1284 const SkIRect& clipRect,
1285 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001286 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001287 if (!fCurrStrike) {
1288 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1289 }
1290
joshualitt6c2c2b02015-07-24 10:37:00 -07001291 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1292 skGlyph.getSubXFixed(),
1293 skGlyph.getSubYFixed(),
1294 GrGlyph::kDistance_MaskStyle);
1295 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001296 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001297 return true;
1298 }
1299
1300 // fallback to color glyph support
1301 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1302 return false;
1303 }
1304
1305 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1306 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1307 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1308 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1309
1310 SkScalar scale = textRatio;
1311 dx *= scale;
1312 dy *= scale;
1313 width *= scale;
1314 height *= scale;
1315 sx += dx;
1316 sy += dy;
1317 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1318
1319#if 0
1320 // check if we clipped out
1321 SkRect dstRect;
1322 viewMatrix.mapRect(&dstRect, glyphRect);
1323 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1324 SkScalarTruncToInt(dstRect.top()),
1325 SkScalarTruncToInt(dstRect.right()),
1326 SkScalarTruncToInt(dstRect.bottom()))) {
1327 return true;
1328 }
1329#endif
1330
1331 // TODO combine with the above
1332 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001333 if (glyph->fTooLargeForAtlas) {
joshualitt0fe04a22015-08-25 12:05:50 -07001334 this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy, scale, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001335 return true;
1336 }
1337
joshualitt9bd2daf2015-04-17 09:30:06 -07001338 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001339 if (!run.fInitialized) {
1340 subRun->fStrike.reset(SkRef(fCurrStrike));
1341 }
1342 run.fInitialized = true;
joshualitt9bd2daf2015-04-17 09:30:06 -07001343 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1344 subRun->fMaskFormat = kA8_GrMaskFormat;
1345
1346 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1347
1348 bool useColorVerts = !subRun->fUseLCDText;
1349 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001350 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001351 return true;
1352}
1353
joshualitt374b2f72015-07-21 08:05:03 -07001354inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001355 GrFontScaler* scaler, const SkGlyph& skGlyph,
joshualitt0fe04a22015-08-25 12:05:50 -07001356 SkScalar x, SkScalar y, SkScalar scale,
1357 bool applyVM) {
halcanary96fcdcc2015-08-27 07:41:13 -07001358 if (nullptr == glyph->fPath) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001359 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
1360 if (!glyphPath) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001361 return;
1362 }
joshualitt6c2c2b02015-07-24 10:37:00 -07001363
halcanary385fe4d2015-08-26 13:07:48 -07001364 glyph->fPath = new SkPath(*glyphPath);
joshualitt9bd2daf2015-04-17 09:30:06 -07001365 }
joshualitt0fe04a22015-08-25 12:05:50 -07001366 blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM));
joshualitt9bd2daf2015-04-17 09:30:06 -07001367}
1368
joshualitt374b2f72015-07-21 08:05:03 -07001369inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run,
joshualitt9bd2daf2015-04-17 09:30:06 -07001370 Run::SubRunInfo* subRun,
1371 const SkRect& positions, GrColor color,
1372 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001373 GrGlyph* glyph) {
1374 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001375 run->fVertexBounds.joinNonEmptyArg(positions);
1376 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001377
1378 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1379
joshualitt9bd2daf2015-04-17 09:30:06 -07001380 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001381 // V0
1382 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1383 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001384 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1385 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001386 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001387
joshualitt010db532015-04-21 10:07:26 -07001388 // V1
1389 position = reinterpret_cast<SkPoint*>(vertex);
1390 position->set(positions.fLeft, positions.fBottom);
1391 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001392 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001393 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001394
joshualitt010db532015-04-21 10:07:26 -07001395 // V2
1396 position = reinterpret_cast<SkPoint*>(vertex);
1397 position->set(positions.fRight, positions.fBottom);
1398 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001399 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001400 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001401
joshualitt010db532015-04-21 10:07:26 -07001402 // V3
1403 position = reinterpret_cast<SkPoint*>(vertex);
1404 position->set(positions.fRight, positions.fTop);
1405 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001406 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001407 } else {
1408 // V0
1409 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1410 position->set(positions.fLeft, positions.fTop);
1411 vertex += vertexStride;
1412
1413 // V1
1414 position = reinterpret_cast<SkPoint*>(vertex);
1415 position->set(positions.fLeft, positions.fBottom);
1416 vertex += vertexStride;
1417
1418 // V2
1419 position = reinterpret_cast<SkPoint*>(vertex);
1420 position->set(positions.fRight, positions.fBottom);
1421 vertex += vertexStride;
1422
1423 // V3
1424 position = reinterpret_cast<SkPoint*>(vertex);
1425 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001426 }
1427
1428 subRun->fGlyphEndIndex++;
1429 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1430}
1431
bsalomonabd30f52015-08-13 13:34:48 -07001432class TextBatch : public GrVertexBatch {
joshualitt1d89e8d2015-04-01 12:40:54 -07001433public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001434 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualitt374b2f72015-07-21 08:05:03 -07001435 typedef GrAtlasTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001436 typedef Blob::Run Run;
1437 typedef Run::SubRunInfo TextInfo;
1438 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001439 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001440 int fRun;
1441 int fSubRun;
1442 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001443 SkScalar fTransX;
1444 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001445 };
1446
bsalomon265697d2015-07-22 10:17:26 -07001447 static TextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
joshualittad802c62015-04-15 05:31:57 -07001448 GrBatchFontCache* fontCache) {
halcanary385fe4d2015-08-26 13:07:48 -07001449 TextBatch* batch = new TextBatch;
bsalomon265697d2015-07-22 10:17:26 -07001450
1451 batch->initClassID<TextBatch>();
1452 batch->fFontCache = fontCache;
1453 switch (maskFormat) {
1454 case kA8_GrMaskFormat:
1455 batch->fMaskType = kGrayscaleCoverageMask_MaskType;
1456 break;
1457 case kA565_GrMaskFormat:
1458 batch->fMaskType = kLCDCoverageMask_MaskType;
1459 break;
1460 case kARGB_GrMaskFormat:
1461 batch->fMaskType = kColorBitmapMask_MaskType;
1462 break;
1463 }
1464 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001465 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001466 batch->fFilteredColor = 0;
1467 batch->fFontCache = fontCache;
1468 batch->fUseBGR = false;
1469 return batch;
joshualitt1d89e8d2015-04-01 12:40:54 -07001470 }
1471
bsalomon265697d2015-07-22 10:17:26 -07001472 static TextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache,
robertphillipsf6703fa2015-09-01 05:36:47 -07001473 const DistanceAdjustTable* distanceAdjustTable,
bsalomon265697d2015-07-22 10:17:26 -07001474 SkColor filteredColor, bool isLCD,
1475 bool useBGR) {
halcanary385fe4d2015-08-26 13:07:48 -07001476 TextBatch* batch = new TextBatch;
bsalomon265697d2015-07-22 10:17:26 -07001477 batch->initClassID<TextBatch>();
1478 batch->fFontCache = fontCache;
1479 batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
1480 batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
1481 batch->fFilteredColor = filteredColor;
1482 batch->fUseBGR = useBGR;
1483 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001484 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001485 return batch;
joshualitt9bd2daf2015-04-17 09:30:06 -07001486 }
1487
bsalomone46f9fe2015-08-18 06:05:14 -07001488 // to avoid even the initial copy of the struct, we have a getter for the first item which
1489 // is used to seed the batch with its initial geometry. After seeding, the client should call
1490 // init() so the Batch can initialize itself
1491 Geometry& geometry() { return fGeoData[0]; }
1492
1493 void init() {
1494 const Geometry& geo = fGeoData[0];
1495 fBatch.fColor = geo.fColor;
1496 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1497
1498 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1499 // into device space
1500 const Run& run = geo.fBlob->fRuns[geo.fRun];
1501 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1502 SkRect bounds = run.fVertexBounds;
1503 fBatch.fViewMatrix.mapRect(&bounds);
1504 this->setBounds(bounds);
1505 } else {
1506 this->setBounds(run.fVertexBounds);
1507 }
1508 }
1509
bsalomon265697d2015-07-22 10:17:26 -07001510 const char* name() const override { return "TextBatch"; }
joshualitt1d89e8d2015-04-01 12:40:54 -07001511
1512 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001513 if (kColorBitmapMask_MaskType == fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001514 out->setUnknownFourComponents();
1515 } else {
1516 out->setKnownFourComponents(fBatch.fColor);
1517 }
1518 }
1519
1520 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001521 switch (fMaskType) {
1522 case kGrayscaleDistanceField_MaskType:
1523 case kGrayscaleCoverageMask_MaskType:
joshualitt1d89e8d2015-04-01 12:40:54 -07001524 out->setUnknownSingleComponent();
bsalomon265697d2015-07-22 10:17:26 -07001525 break;
1526 case kLCDCoverageMask_MaskType:
1527 case kLCDDistanceField_MaskType:
1528 out->setUnknownOpaqueFourComponents();
joshualitt1d89e8d2015-04-01 12:40:54 -07001529 out->setUsingLCDCoverage();
bsalomon265697d2015-07-22 10:17:26 -07001530 break;
1531 case kColorBitmapMask_MaskType:
1532 out->setKnownSingleComponent(0xff);
joshualitt1d89e8d2015-04-01 12:40:54 -07001533 }
1534 }
1535
bsalomone46f9fe2015-08-18 06:05:14 -07001536private:
bsalomon91d844d2015-08-10 10:47:29 -07001537 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001538 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001539 if (!opt.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001540 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001541 }
bsalomon91d844d2015-08-10 10:47:29 -07001542 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001543
1544 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001545 fBatch.fColorIgnored = !opt.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001546 fBatch.fColor = fGeoData[0].fColor;
bsalomon91d844d2015-08-10 10:47:29 -07001547 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1548 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001549 }
1550
bsalomonb5238a72015-05-05 07:49:49 -07001551 struct FlushInfo {
1552 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1553 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1554 int fGlyphsToFlush;
1555 int fVertexOffset;
1556 };
1557
bsalomon75398562015-08-17 12:55:38 -07001558 void onPrepareDraws(Target* target) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001559 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1560 // TODO actually only invert if we don't have RGBA
1561 SkMatrix localMatrix;
1562 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1563 SkDebugf("Cannot invert viewmatrix\n");
1564 return;
1565 }
1566
bsalomon265697d2015-07-22 10:17:26 -07001567 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
joshualitt62db8ba2015-04-09 08:22:37 -07001568 if (!texture) {
1569 SkDebugf("Could not allocate backing texture for atlas\n");
1570 return;
1571 }
1572
bsalomon265697d2015-07-22 10:17:26 -07001573 bool usesDistanceFields = this->usesDistanceFields();
1574 GrMaskFormat maskFormat = this->maskFormat();
1575 bool isLCD = this->isLCD();
1576
joshualitt9bd2daf2015-04-17 09:30:06 -07001577 SkAutoTUnref<const GrGeometryProcessor> gp;
bsalomon265697d2015-07-22 10:17:26 -07001578 if (usesDistanceFields) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001579 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1580 texture));
1581 } else {
1582 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001583 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1584 texture,
1585 params,
bsalomon265697d2015-07-22 10:17:26 -07001586 maskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001587 localMatrix,
1588 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001589 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001590
bsalomonb5238a72015-05-05 07:49:49 -07001591 FlushInfo flushInfo;
1592 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001593 size_t vertexStride = gp->getVertexStride();
bsalomon265697d2015-07-22 10:17:26 -07001594 SkASSERT(vertexStride == (usesDistanceFields ?
1595 get_vertex_stride_df(maskFormat, isLCD) :
1596 get_vertex_stride(maskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001597
bsalomon75398562015-08-17 12:55:38 -07001598 target->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001599
1600 int glyphCount = this->numGlyphs();
bsalomon8415abe2015-05-04 11:41:41 -07001601 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001602
bsalomon75398562015-08-17 12:55:38 -07001603 void* vertices = target->makeVertexSpace(vertexStride,
1604 glyphCount * kVerticesPerGlyph,
1605 &vertexBuffer,
1606 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001607 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -07001608 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -07001609 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001610 SkDebugf("Could not allocate vertices\n");
1611 return;
1612 }
1613
1614 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1615
joshualitt25ba7ea2015-04-21 07:49:49 -07001616 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1617 // in a row
halcanary96fcdcc2015-08-27 07:41:13 -07001618 const SkDescriptor* desc = nullptr;
1619 SkGlyphCache* cache = nullptr;
1620 GrFontScaler* scaler = nullptr;
1621 SkTypeface* typeface = nullptr;
joshualitt25ba7ea2015-04-21 07:49:49 -07001622
bsalomond602f4d2015-07-27 06:12:01 -07001623 for (int i = 0; i < fGeoCount; i++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001624 Geometry& args = fGeoData[i];
1625 Blob* blob = args.fBlob;
1626 Run& run = blob->fRuns[args.fRun];
1627 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1628
bsalomon265697d2015-07-22 10:17:26 -07001629 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001630 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
joshualitt7e97b0b2015-07-31 15:18:08 -07001631 info.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001632 bool regenerateColors;
bsalomon265697d2015-07-22 10:17:26 -07001633 if (usesDistanceFields) {
1634 regenerateColors = !isLCD && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001635 } else {
bsalomon265697d2015-07-22 10:17:26 -07001636 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001637 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001638 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001639 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1640
1641 // We regenerate both texture coords and colors in the blob itself, and update the
1642 // atlas generation. If we don't end up purging any unused plots, we can avoid
1643 // regenerating the coords. We could take a finer grained approach to updating texture
1644 // coords but its not clear if the extra bookkeeping would offset any gains.
1645 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1646 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1647 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1648 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001649 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001650 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001651 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001652
1653 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1654 // generating its texture coords, we have to track whether or not the strike has
1655 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1656 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1657 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1658 // new strike, we instead keep our ref to the old strike and use the packed ids from
1659 // it. These ids will still be valid as long as we hold the ref. When we are done
1660 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1661 bool regenerateGlyphs = false;
halcanary96fcdcc2015-08-27 07:41:13 -07001662 GrBatchTextStrike* strike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -07001663 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001664 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001665
1666 // We can reuse if we have a valid strike and our descriptors / typeface are the
joshualitt94469302015-09-02 06:13:39 -07001667 // same. The override descriptor is only for the non distance field text within
1668 // a run
1669 const SkDescriptor* newDesc = (run.fOverrideDescriptor && !usesDistanceFields) ?
joshualitt97202d22015-04-22 13:47:02 -07001670 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001671 run.fDescriptor.getDesc();
1672 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1673 !(desc->equals(*newDesc))) {
1674 if (cache) {
1675 SkGlyphCache::AttachCache(cache);
1676 }
1677 desc = newDesc;
1678 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1679 scaler = GrTextContext::GetGrFontScaler(cache);
joshualitt7e97b0b2015-07-31 15:18:08 -07001680 strike = info.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001681 typeface = run.fTypeface;
1682 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001683
joshualitt7e97b0b2015-07-31 15:18:08 -07001684 if (info.fStrike->isAbandoned()) {
joshualittae32c102015-04-21 09:37:57 -07001685 regenerateGlyphs = true;
1686 strike = fFontCache->getStrike(scaler);
1687 } else {
joshualitt7e97b0b2015-07-31 15:18:08 -07001688 strike = info.fStrike;
joshualittae32c102015-04-21 09:37:57 -07001689 }
1690 }
1691
1692 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001693 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001694 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
joshualitt6c2c2b02015-07-24 10:37:00 -07001695
1696 GrGlyph* glyph = blob->fGlyphs[glyphOffset];
1697 GrGlyph::PackedID id = glyph->fPackedID;
1698 const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
joshualittae32c102015-04-21 09:37:57 -07001699 if (regenerateGlyphs) {
1700 // Get the id from the old glyph, and use the new strike to lookup
1701 // the glyph.
joshualitt76cc6572015-07-31 05:51:45 -07001702 blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, maskFormat,
1703 scaler);
joshualittae32c102015-04-21 09:37:57 -07001704 }
1705 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001706 SkASSERT(glyph);
joshualitt65e96b42015-07-31 11:45:22 -07001707 SkASSERT(id == glyph->fPackedID);
1708 // We want to be able to assert this but cannot for testing purposes.
1709 // once skbug:4143 has landed we can revist this assert
1710 //SkASSERT(glyph->fMaskFormat == this->maskFormat());
joshualitt1d89e8d2015-04-01 12:40:54 -07001711
1712 if (!fFontCache->hasGlyph(glyph) &&
bsalomon75398562015-08-17 12:55:38 -07001713 !strike->addGlyphToAtlas(target, glyph, scaler, skGlyph, maskFormat)) {
1714 this->flush(target, &flushInfo);
1715 target->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001716 brokenRun = glyphIdx > 0;
1717
bsalomon75398562015-08-17 12:55:38 -07001718 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
joshualittae32c102015-04-21 09:37:57 -07001719 glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001720 scaler,
joshualitt4f19ca32015-07-30 07:59:20 -07001721 skGlyph,
1722 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001723 SkASSERT(success);
1724 }
joshualittb4c507e2015-04-08 08:07:59 -07001725 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
bsalomon75398562015-08-17 12:55:38 -07001726 target->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001727
1728 // Texture coords are the last vertex attribute so we get a pointer to the
1729 // first one and then map with stride in regenerateTextureCoords
1730 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1731 vertex += info.fVertexStartIndex;
1732 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1733 vertex += vertexStride - sizeof(SkIPoint16);
1734
1735 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1736 }
1737
1738 if (regenerateColors) {
1739 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1740 vertex += info.fVertexStartIndex;
1741 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1742 this->regenerateColors(vertex, vertexStride, args.fColor);
1743 }
1744
joshualitt2a0e9f32015-04-13 06:12:21 -07001745 if (regeneratePositions) {
1746 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1747 vertex += info.fVertexStartIndex;
1748 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1749 SkScalar transX = args.fTransX;
1750 SkScalar transY = args.fTransY;
1751 this->regeneratePositions(vertex, vertexStride, transX, transY);
1752 }
bsalomonb5238a72015-05-05 07:49:49 -07001753 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001754 }
1755
joshualitt2a0e9f32015-04-13 06:12:21 -07001756 // We my have changed the color so update it here
1757 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001758 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001759 if (regenerateGlyphs) {
joshualitt7e97b0b2015-07-31 15:18:08 -07001760 info.fStrike.reset(SkRef(strike));
joshualittae32c102015-04-21 09:37:57 -07001761 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001762 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
bsalomon265697d2015-07-22 10:17:26 -07001763 fFontCache->atlasGeneration(maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001764 }
1765 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001766 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001767
1768 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1769 // have a valid atlas generation
bsalomon75398562015-08-17 12:55:38 -07001770 fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001771 }
1772
1773 // now copy all vertices
1774 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1775 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1776
1777 currVertex += byteCount;
1778 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001779 // Make sure to attach the last cache if applicable
1780 if (cache) {
1781 SkGlyphCache::AttachCache(cache);
1782 }
bsalomon75398562015-08-17 12:55:38 -07001783 this->flush(target, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001784 }
1785
bsalomon265697d2015-07-22 10:17:26 -07001786 TextBatch() {} // initialized in factory functions.
joshualittad802c62015-04-15 05:31:57 -07001787
bsalomon265697d2015-07-22 10:17:26 -07001788 ~TextBatch() {
bsalomond602f4d2015-07-27 06:12:01 -07001789 for (int i = 0; i < fGeoCount; i++) {
joshualittad802c62015-04-15 05:31:57 -07001790 fGeoData[i].fBlob->unref();
1791 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001792 }
1793
bsalomon265697d2015-07-22 10:17:26 -07001794 GrMaskFormat maskFormat() const {
1795 switch (fMaskType) {
1796 case kLCDCoverageMask_MaskType:
1797 return kA565_GrMaskFormat;
1798 case kColorBitmapMask_MaskType:
1799 return kARGB_GrMaskFormat;
1800 case kGrayscaleCoverageMask_MaskType:
1801 case kGrayscaleDistanceField_MaskType:
1802 case kLCDDistanceField_MaskType:
1803 return kA8_GrMaskFormat;
1804 }
1805 return kA8_GrMaskFormat; // suppress warning
1806 }
1807
1808 bool usesDistanceFields() const {
1809 return kGrayscaleDistanceField_MaskType == fMaskType ||
1810 kLCDDistanceField_MaskType == fMaskType;
1811 }
1812
1813 bool isLCD() const {
1814 return kLCDCoverageMask_MaskType == fMaskType ||
1815 kLCDDistanceField_MaskType == fMaskType;
1816 }
1817
joshualitt1d89e8d2015-04-01 12:40:54 -07001818 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1819 int width = glyph->fBounds.width();
1820 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001821
joshualitt9bd2daf2015-04-17 09:30:06 -07001822 int u0, v0, u1, v1;
bsalomon265697d2015-07-22 10:17:26 -07001823 if (this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001824 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1825 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1826 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1827 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1828 } else {
1829 u0 = glyph->fAtlasLocation.fX;
1830 v0 = glyph->fAtlasLocation.fY;
1831 u1 = u0 + width;
1832 v1 = v0 + height;
1833 }
1834
joshualitt1d89e8d2015-04-01 12:40:54 -07001835 SkIPoint16* textureCoords;
1836 // V0
1837 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1838 textureCoords->set(u0, v0);
1839 vertex += vertexStride;
1840
1841 // V1
1842 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1843 textureCoords->set(u0, v1);
1844 vertex += vertexStride;
1845
1846 // V2
1847 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1848 textureCoords->set(u1, v1);
1849 vertex += vertexStride;
1850
1851 // V3
1852 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1853 textureCoords->set(u1, v0);
1854 }
1855
1856 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1857 for (int i = 0; i < kVerticesPerGlyph; i++) {
1858 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1859 *vcolor = color;
1860 vertex += vertexStride;
1861 }
1862 }
1863
joshualitt2a0e9f32015-04-13 06:12:21 -07001864 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1865 SkScalar transY) {
1866 for (int i = 0; i < kVerticesPerGlyph; i++) {
1867 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1868 point->fX += transX;
1869 point->fY += transY;
1870 vertex += vertexStride;
1871 }
1872 }
1873
bsalomon75398562015-08-17 12:55:38 -07001874 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001875 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001876 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001877 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001878 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001879 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001880 maxGlyphsPerDraw);
bsalomon75398562015-08-17 12:55:38 -07001881 target->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001882 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1883 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001884 }
1885
1886 GrColor color() const { return fBatch.fColor; }
1887 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1888 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1889 int numGlyphs() const { return fBatch.fNumGlyphs; }
1890
bsalomoncb02b382015-08-12 11:14:50 -07001891 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001892 TextBatch* that = t->cast<TextBatch>();
1893 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1894 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001895 return false;
1896 }
1897
bsalomon265697d2015-07-22 10:17:26 -07001898 if (fMaskType != that->fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001899 return false;
1900 }
1901
bsalomon265697d2015-07-22 10:17:26 -07001902 if (!this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001903 // TODO we can often batch across LCD text if we have dual source blending and don't
1904 // have to use the blend constant
bsalomon265697d2015-07-22 10:17:26 -07001905 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001906 return false;
1907 }
joshualitt9bd2daf2015-04-17 09:30:06 -07001908 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1909 return false;
1910 }
1911 } else {
joshualitt9bd2daf2015-04-17 09:30:06 -07001912 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1913 return false;
1914 }
1915
1916 if (fFilteredColor != that->fFilteredColor) {
1917 return false;
1918 }
1919
joshualitt9bd2daf2015-04-17 09:30:06 -07001920 if (fUseBGR != that->fUseBGR) {
1921 return false;
1922 }
1923
joshualitt9bd2daf2015-04-17 09:30:06 -07001924 // TODO see note above
bsalomon265697d2015-07-22 10:17:26 -07001925 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) {
joshualitt0fe04a22015-08-25 12:05:50 -07001926 return false;
joshualitt9bd2daf2015-04-17 09:30:06 -07001927 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001928 }
1929
1930 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001931
bsalomond602f4d2015-07-27 06:12:01 -07001932 // Reallocate space for geo data if necessary and then import that's geo data.
1933 int newGeoCount = that->fGeoCount + fGeoCount;
1934 // We assume (and here enforce) that the allocation size is the smallest power of two that
1935 // is greater than or equal to the number of geometries (and at least
1936 // kMinGeometryAllocated).
1937 int newAllocSize = GrNextPow2(newGeoCount);
1938 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
1939
bsalomon16ed6ad2015-07-29 06:54:33 -07001940 if (newGeoCount > currAllocSize) {
bsalomond602f4d2015-07-27 06:12:01 -07001941 fGeoData.realloc(newAllocSize);
joshualittad802c62015-04-15 05:31:57 -07001942 }
1943
bsalomond602f4d2015-07-27 06:12:01 -07001944 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
bsalomon1c634362015-07-27 07:00:00 -07001945 // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
1946 // it doesn't try to unref them.
1947#ifdef SK_DEBUG
1948 for (int i = 0; i < that->fGeoCount; ++i) {
1949 that->fGeoData.get()[i].fBlob = (Blob*)0x1;
joshualittad802c62015-04-15 05:31:57 -07001950 }
bsalomon1c634362015-07-27 07:00:00 -07001951#endif
1952 that->fGeoCount = 0;
bsalomond602f4d2015-07-27 06:12:01 -07001953 fGeoCount = newGeoCount;
joshualitt99c7c072015-05-01 13:43:30 -07001954
1955 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001956 return true;
1957 }
1958
joshualitt9bd2daf2015-04-17 09:30:06 -07001959 // TODO just use class params
1960 // TODO trying to figure out why lcd is so whack
1961 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1962 GrColor color, GrTexture* texture) {
1963 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
bsalomon265697d2015-07-22 10:17:26 -07001964 bool isLCD = this->isLCD();
joshualitt9bd2daf2015-04-17 09:30:06 -07001965 // set up any flags
bsalomon265697d2015-07-22 10:17:26 -07001966 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt9bd2daf2015-04-17 09:30:06 -07001967
1968 // see if we need to create a new effect
bsalomon265697d2015-07-22 10:17:26 -07001969 if (isLCD) {
1970 flags |= kUseLCD_DistanceFieldEffectFlag;
1971 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
1972 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1973
joshualitt9bd2daf2015-04-17 09:30:06 -07001974 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1975
1976 float redCorrection =
1977 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1978 float greenCorrection =
1979 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1980 float blueCorrection =
1981 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1982 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1983 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1984 greenCorrection,
1985 blueCorrection);
1986
1987 return GrDistanceFieldLCDTextGeoProc::Create(color,
1988 viewMatrix,
1989 texture,
1990 params,
1991 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001992 flags,
1993 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001994 } else {
1995 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07001996#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07001997 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07001998 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
1999 return GrDistanceFieldA8TextGeoProc::Create(color,
2000 viewMatrix,
2001 texture,
2002 params,
2003 correction,
joshualittb8c241a2015-05-19 08:23:30 -07002004 flags,
2005 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002006#else
2007 return GrDistanceFieldA8TextGeoProc::Create(color,
2008 viewMatrix,
2009 texture,
2010 params,
joshualittb8c241a2015-05-19 08:23:30 -07002011 flags,
2012 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002013#endif
2014 }
2015
2016 }
2017
joshualitt1d89e8d2015-04-01 12:40:54 -07002018 struct BatchTracker {
2019 GrColor fColor;
2020 SkMatrix fViewMatrix;
2021 bool fUsesLocalCoords;
2022 bool fColorIgnored;
2023 bool fCoverageIgnored;
2024 int fNumGlyphs;
2025 };
2026
2027 BatchTracker fBatch;
bsalomond602f4d2015-07-27 06:12:01 -07002028 // The minimum number of Geometry we will try to allocate.
2029 enum { kMinGeometryAllocated = 4 };
2030 SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
2031 int fGeoCount;
bsalomon265697d2015-07-22 10:17:26 -07002032
2033 enum MaskType {
2034 kGrayscaleCoverageMask_MaskType,
2035 kLCDCoverageMask_MaskType,
2036 kColorBitmapMask_MaskType,
2037 kGrayscaleDistanceField_MaskType,
2038 kLCDDistanceField_MaskType,
2039 } fMaskType;
2040 bool fUseBGR; // fold this into the enum?
2041
joshualitt1d89e8d2015-04-01 12:40:54 -07002042 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002043
2044 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002045 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002046 SkColor fFilteredColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07002047};
2048
robertphillipsf6703fa2015-09-01 05:36:47 -07002049void GrAtlasTextContext::flushRunAsPaths(GrDrawContext* dc, GrRenderTarget* rt,
2050 const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002051 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002052 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2053 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2054 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002055
joshualitt9a27e632015-04-06 10:53:36 -07002056 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2057 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002058
joshualitt9a27e632015-04-06 10:53:36 -07002059 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002060
joshualitt9a27e632015-04-06 10:53:36 -07002061 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2062 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002063 }
2064
robertphillipsfcf78292015-06-19 11:49:52 -07002065 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002066
2067 switch (it.positioning()) {
2068 case SkTextBlob::kDefault_Positioning:
robertphillipsf6703fa2015-09-01 05:36:47 -07002069 this->drawTextAsPath(dc, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002070 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002071 textLen, x + offset.x(), y + offset.y(), clipBounds);
2072 break;
2073 case SkTextBlob::kHorizontal_Positioning:
robertphillipsf6703fa2015-09-01 05:36:47 -07002074 this->drawPosTextAsPath(dc, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002075 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002076 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2077 clipBounds);
2078 break;
2079 case SkTextBlob::kFull_Positioning:
robertphillipsf6703fa2015-09-01 05:36:47 -07002080 this->drawPosTextAsPath(dc, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002081 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002082 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2083 break;
2084 }
2085}
2086
bsalomonabd30f52015-08-13 13:34:48 -07002087inline GrDrawBatch*
joshualitt374b2f72015-07-21 08:05:03 -07002088GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info,
joshualitt79dfb2b2015-05-11 08:58:08 -07002089 int glyphCount, int run, int subRun,
2090 GrColor color, SkScalar transX, SkScalar transY,
2091 const SkPaint& skPaint) {
2092 GrMaskFormat format = info.fMaskFormat;
2093 GrColor subRunColor;
2094 if (kARGB_GrMaskFormat == format) {
2095 uint8_t paintAlpha = skPaint.getAlpha();
2096 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2097 } else {
2098 subRunColor = color;
2099 }
2100
bsalomon265697d2015-07-22 10:17:26 -07002101 TextBatch* batch;
joshualitt79dfb2b2015-05-11 08:58:08 -07002102 if (info.fDrawAsDistanceFields) {
2103 SkColor filteredColor;
2104 SkColorFilter* colorFilter = skPaint.getColorFilter();
2105 if (colorFilter) {
2106 filteredColor = colorFilter->filterColor(skPaint.getColor());
2107 } else {
2108 filteredColor = skPaint.getColor();
2109 }
robertphillipsfcf78292015-06-19 11:49:52 -07002110 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
bsalomon265697d2015-07-22 10:17:26 -07002111 batch = TextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
2112 fDistanceAdjustTable, filteredColor,
2113 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002114 } else {
bsalomon265697d2015-07-22 10:17:26 -07002115 batch = TextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
joshualitt79dfb2b2015-05-11 08:58:08 -07002116 }
bsalomon265697d2015-07-22 10:17:26 -07002117 TextBatch::Geometry& geometry = batch->geometry();
joshualitt79dfb2b2015-05-11 08:58:08 -07002118 geometry.fBlob = SkRef(cacheBlob);
2119 geometry.fRun = run;
2120 geometry.fSubRun = subRun;
2121 geometry.fColor = subRunColor;
2122 geometry.fTransX = transX;
2123 geometry.fTransY = transY;
2124 batch->init();
2125
2126 return batch;
2127}
2128
robertphillipsf6703fa2015-09-01 05:36:47 -07002129inline void GrAtlasTextContext::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder,
joshualitt374b2f72015-07-21 08:05:03 -07002130 GrAtlasTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002131 SkScalar transX, SkScalar transY,
2132 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002133 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2134 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2135 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2136 if (0 == glyphCount) {
2137 continue;
2138 }
2139
bsalomonabd30f52015-08-13 13:34:48 -07002140 SkAutoTUnref<GrDrawBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2141 subRun, color, transX, transY,
2142 skPaint));
robertphillipsf6703fa2015-09-01 05:36:47 -07002143 dc->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002144 }
2145}
2146
robertphillipsf6703fa2015-09-01 05:36:47 -07002147inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob,
2148 GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002149 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002150 SkScalar transX, SkScalar transY,
2151 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002152 if (!cacheBlob->fBigGlyphs.count()) {
2153 return;
2154 }
2155
joshualitt9a27e632015-04-06 10:53:36 -07002156 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt374b2f72015-07-21 08:05:03 -07002157 GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002158 bigGlyph.fVx += transX;
2159 bigGlyph.fVy += transY;
joshualitt0fe04a22015-08-25 12:05:50 -07002160 SkMatrix ctm;
2161 ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
2162 ctm.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2163 if (bigGlyph.fApplyVM) {
2164 ctm.postConcat(cacheBlob->fViewMatrix);
2165 }
joshualittfc072562015-05-13 12:15:06 -07002166
robertphillipsf6703fa2015-09-01 05:36:47 -07002167 GrBlurUtils::drawPathWithMaskFilter(fContext, dc, rt, clip, bigGlyph.fPath,
joshualitt0fe04a22015-08-25 12:05:50 -07002168 skPaint, ctm, nullptr, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002169 }
2170}
joshualitt9a27e632015-04-06 10:53:36 -07002171
robertphillips2334fb62015-06-17 05:43:33 -07002172void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt374b2f72015-07-21 08:05:03 -07002173 GrAtlasTextBlob* cacheBlob,
robertphillipsf6703fa2015-09-01 05:36:47 -07002174 GrDrawContext* dc,
joshualitt9a27e632015-04-06 10:53:36 -07002175 GrRenderTarget* rt,
2176 const SkPaint& skPaint,
2177 const GrPaint& grPaint,
2178 SkDrawFilter* drawFilter,
2179 const GrClip& clip,
2180 const SkMatrix& viewMatrix,
2181 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002182 SkScalar x, SkScalar y,
2183 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002184 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2185 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002186 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002187
2188 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002189
2190 SkTextBlob::RunIterator it(blob);
2191 for (int run = 0; !it.done(); it.next(), run++) {
2192 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillipsf6703fa2015-09-01 05:36:47 -07002193 this->flushRunAsPaths(dc, rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002194 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002195 continue;
2196 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002197 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillipsf6703fa2015-09-01 05:36:47 -07002198 this->flushRun(dc, &pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002199 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002200 }
2201
2202 // Now flush big glyphs
robertphillipsf6703fa2015-09-01 05:36:47 -07002203 this->flushBigGlyphs(cacheBlob, dc, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002204}
2205
joshualitt374b2f72015-07-21 08:05:03 -07002206void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob,
robertphillipsf6703fa2015-09-01 05:36:47 -07002207 GrDrawContext* dc,
joshualitt9a27e632015-04-06 10:53:36 -07002208 GrRenderTarget* rt,
2209 const SkPaint& skPaint,
2210 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002211 const GrClip& clip,
2212 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002213 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002214
2215 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002216 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillipsf6703fa2015-09-01 05:36:47 -07002217 this->flushRun(dc, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002218 }
2219
2220 // Now flush big glyphs
robertphillipsf6703fa2015-09-01 05:36:47 -07002221 this->flushBigGlyphs(cacheBlob, dc, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002222}
joshualitt79dfb2b2015-05-11 08:58:08 -07002223
2224///////////////////////////////////////////////////////////////////////////////////////////////////
2225
2226#ifdef GR_TEST_UTILS
2227
bsalomonabd30f52015-08-13 13:34:48 -07002228DRAW_BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002229 static uint32_t gContextID = SK_InvalidGenID;
halcanary96fcdcc2015-08-27 07:41:13 -07002230 static GrAtlasTextContext* gTextContext = nullptr;
robertphillipsfcf78292015-06-19 11:49:52 -07002231 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002232
2233 if (context->uniqueID() != gContextID) {
2234 gContextID = context->uniqueID();
halcanary385fe4d2015-08-26 13:07:48 -07002235 delete gTextContext;
robertphillips2334fb62015-06-17 05:43:33 -07002236
joshualitt79dfb2b2015-05-11 08:58:08 -07002237 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2238 // because we don't really want to have a gpu device here.
2239 // We enable distance fields by twiddling a knob on the paint
robertphillipsf6703fa2015-09-01 05:36:47 -07002240 gTextContext = GrAtlasTextContext::Create(context, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002241 }
2242
2243 // create dummy render target
2244 GrSurfaceDesc desc;
2245 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2246 desc.fWidth = 1024;
2247 desc.fHeight = 1024;
2248 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002249 desc.fSampleCnt = 0;
halcanary96fcdcc2015-08-27 07:41:13 -07002250 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, nullptr, 0));
joshualitt79dfb2b2015-05-11 08:58:08 -07002251 SkASSERT(texture);
halcanary96fcdcc2015-08-27 07:41:13 -07002252 SkASSERT(nullptr != texture->asRenderTarget());
joshualitt79dfb2b2015-05-11 08:58:08 -07002253 GrRenderTarget* rt = texture->asRenderTarget();
2254
2255 // Setup dummy SkPaint / GrPaint
2256 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002257 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002258 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002259 skPaint.setColor(color);
2260 skPaint.setLCDRenderText(random->nextBool());
2261 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2262 skPaint.setSubpixelText(random->nextBool());
2263
2264 GrPaint grPaint;
2265 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2266 SkFAIL("couldn't convert paint\n");
2267 }
2268
2269 const char* text = "The quick brown fox jumps over the lazy dog.";
2270 int textLen = (int)strlen(text);
2271
2272 // Setup clip
2273 GrClip clip;
2274 SkIRect noClip = SkIRect::MakeLargest();
2275
2276 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2277 // intend to test the batch with this unit test, that is okay.
joshualitt374b2f72015-07-21 08:05:03 -07002278 SkAutoTUnref<GrAtlasTextBlob> blob(
joshualitt79dfb2b2015-05-11 08:58:08 -07002279 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2280 static_cast<size_t>(textLen), 0, 0, noClip));
2281
2282 SkScalar transX = static_cast<SkScalar>(random->nextU());
2283 SkScalar transY = static_cast<SkScalar>(random->nextU());
joshualitt374b2f72015-07-21 08:05:03 -07002284 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
joshualitt79dfb2b2015-05-11 08:58:08 -07002285 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2286}
2287
2288#endif