blob: 3de0ef875347ee7eff231832e80b02c4faf7afdd [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
joshualittdbd35932015-04-02 09:19:04 -070099GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700100 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700101 const SkSurfaceProps& surfaceProps)
halcanary385fe4d2015-08-26 13:07:48 -0700102 : INHERITED(context, drawContext, surfaceProps), fDistanceAdjustTable(new DistanceAdjustTable) {
joshualittb7133be2015-04-08 09:08:31 -0700103 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
104 // vertexStride
bungeman99fe8222015-08-20 07:57:51 -0700105 static_assert(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
106 "vertex_attribute_changed");
halcanary96fcdcc2015-08-27 07:41:13 -0700107 fCurrStrike = nullptr;
joshualittb7133be2015-04-08 09:08:31 -0700108 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700109}
110
robertphillips9fc82752015-06-19 04:46:45 -0700111void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700112
113 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
114 // text. The mask gamma hack is based off of guessing what the blend color is going to
115 // be, and adjusting the mask so that when run through the linear blend will
116 // produce the value closest to the desired result. However, in practice this means
117 // that the 'adjusted' mask is just increasing or decreasing the coverage of
118 // the mask depending on what it is thought it will blit against. For black (on
119 // assumed white) this means that coverages are decreased (on a curve). For white (on
120 // assumed black) this means that coverages are increased (on a a curve). At
121 // middle (perceptual) gray (which could be blit against anything) the coverages
122 // remain the same.
123 //
124 // The idea here is that instead of determining the initial (real) coverage and
125 // then adjusting that coverage, we determine an adjusted coverage directly by
126 // essentially manipulating the geometry (in this case, the distance to the glyph
127 // edge). So for black (on assumed white) this thins a bit; for white (on
128 // assumed black) this fake bolds the geometry a bit.
129 //
130 // The distance adjustment is calculated by determining the actual coverage value which
131 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
132 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
133 // actual edge. So by subtracting this distance adjustment and computing without the
134 // the coverage adjustment we should get 0.5 coverage at the same point.
135 //
136 // This has several implications:
137 // For non-gray lcd smoothed text, each subpixel essentially is using a
138 // slightly different geometry.
139 //
140 // For black (on assumed white) this may not cover some pixels which were
141 // previously covered; however those pixels would have been only slightly
142 // covered and that slight coverage would have been decreased anyway. Also, some pixels
143 // which were previously fully covered may no longer be fully covered.
144 //
145 // For white (on assumed black) this may cover some pixels which weren't
146 // previously covered at all.
147
148 int width, height;
149 size_t size;
150
151#ifdef SK_GAMMA_CONTRAST
152 SkScalar contrast = SK_GAMMA_CONTRAST;
153#else
154 SkScalar contrast = 0.5f;
155#endif
robertphillips9fc82752015-06-19 04:46:45 -0700156 SkScalar paintGamma = SK_GAMMA_EXPONENT;
157 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700158
159 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
160 &width, &height);
161
162 SkASSERT(kExpectedDistanceAdjustTableSize == height);
halcanary385fe4d2015-08-26 13:07:48 -0700163 fTable = new SkScalar[height];
joshualitt9bd2daf2015-04-17 09:30:06 -0700164
165 SkAutoTArray<uint8_t> data((int)size);
166 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
167
168 // find the inverse points where we cross 0.5
169 // binsearch might be better, but we only need to do this once on creation
170 for (int row = 0; row < height; ++row) {
171 uint8_t* rowPtr = data.get() + row*width;
172 for (int col = 0; col < width - 1; ++col) {
173 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
174 // compute point where a mask value will give us a result of 0.5
175 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
176 float borderAlpha = (col + interp) / 255.f;
177
178 // compute t value for that alpha
179 // this is an approximate inverse for smoothstep()
180 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
181
182 // compute distance which gives us that t value
183 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
184 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
185
186 fTable[row] = d;
187 break;
188 }
189 }
190 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700191}
192
joshualittdbd35932015-04-02 09:19:04 -0700193GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700194 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700195 const SkSurfaceProps& surfaceProps) {
halcanary385fe4d2015-08-26 13:07:48 -0700196 return new GrAtlasTextContext(context, drawContext, surfaceProps);
joshualitt1d89e8d2015-04-01 12:40:54 -0700197}
198
joshualittdbd35932015-04-02 09:19:04 -0700199bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
200 const GrClip&,
201 const GrPaint&,
202 const SkPaint& skPaint,
203 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700204 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
205 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700206}
207
joshualitt9e36c1a2015-04-14 12:17:27 -0700208GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
209 GrColor canonicalColor = paint.computeLuminanceColor();
210 if (lcd) {
211 // This is the correct computation, but there are tons of cases where LCD can be overridden.
212 // For now we just regenerate if any run in a textblob has LCD.
213 // TODO figure out where all of these overrides are and see if we can incorporate that logic
214 // at a higher level *OR* use sRGB
215 SkASSERT(false);
216 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
217 } else {
218 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
219 // gamma corrected masks anyways, nor color
220 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
221 SkColorGetG(canonicalColor),
222 SkColorGetB(canonicalColor));
223 // reduce to our finite number of bits
224 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
225 }
226 return canonicalColor;
227}
228
229// TODO if this function ever shows up in profiling, then we can compute this value when the
230// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
231// run so this is not a big deal to compute here.
232bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
233 SkTextBlob::RunIterator it(blob);
234 for (; !it.done(); it.next()) {
235 if (it.isLCD()) {
236 return true;
237 }
238 }
239 return false;
240}
241
joshualitt2a0e9f32015-04-13 06:12:21 -0700242bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
joshualitt374b2f72015-07-21 08:05:03 -0700243 const GrAtlasTextBlob& blob, const SkPaint& paint,
jvanverth0628a522015-08-18 07:44:22 -0700244 GrColor color, const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700245 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700246 // If we have LCD text then our canonical color will be set to transparent, in this case we have
247 // to regenerate the blob on any color change
jvanverth0628a522015-08-18 07:44:22 -0700248 // We use the grPaint to get any color filter effects
249 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
250 blob.fPaintColor != color) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700251 return true;
252 }
253
254 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
255 return true;
256 }
257
258 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
259 return true;
260 }
261
joshualitt53b5f442015-04-13 06:33:59 -0700262 // We only cache one masked version
263 if (blob.fKey.fHasBlur &&
264 (blob.fBlurRec.fSigma != blurRec.fSigma ||
265 blob.fBlurRec.fStyle != blurRec.fStyle ||
266 blob.fBlurRec.fQuality != blurRec.fQuality)) {
267 return true;
268 }
269
270 // Similarly, we only cache one version for each style
271 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
272 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
273 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
274 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
275 return true;
276 }
277
joshualittfcfb9fc2015-04-21 07:35:10 -0700278 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
279 // for mixed blobs if this becomes an issue.
280 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700281 // Identical viewmatrices and we can reuse in all cases
282 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
283 return false;
284 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700285 return true;
286 }
287
joshualittfcfb9fc2015-04-21 07:35:10 -0700288 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700289 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
290 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
291 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
292 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
293 return true;
294 }
295
joshualittfcfb9fc2015-04-21 07:35:10 -0700296 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
297 // but only for integer translations.
298 // This cool bit of math will determine the necessary translation to apply to the already
299 // generated vertex coordinates to move them to the correct position
300 SkScalar transX = viewMatrix.getTranslateX() +
301 viewMatrix.getScaleX() * (x - blob.fX) +
302 viewMatrix.getSkewX() * (y - blob.fY) -
303 blob.fViewMatrix.getTranslateX();
304 SkScalar transY = viewMatrix.getTranslateY() +
305 viewMatrix.getSkewY() * (x - blob.fX) +
306 viewMatrix.getScaleY() * (y - blob.fY) -
307 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700308 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700309 return true;
310 }
311
joshualittfcfb9fc2015-04-21 07:35:10 -0700312 (*outTransX) = transX;
313 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700314 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700315 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
316 // distance field being generated, so we have to regenerate in those cases
317 SkScalar newMaxScale = viewMatrix.getMaxScale();
318 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
319 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
320 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
321 return true;
322 }
323
324 (*outTransX) = x - blob.fX;
325 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700326 }
joshualitt374b2f72015-07-21 08:05:03 -0700327
joshualitta7c63892015-04-21 13:24:37 -0700328 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
329 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
330 // the blob anyways at flush time, so no need to regenerate explicitly
joshualitt2a0e9f32015-04-13 06:12:21 -0700331 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700332}
333
334
joshualitt374b2f72015-07-21 08:05:03 -0700335inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run,
joshualittdbd35932015-04-02 09:19:04 -0700336 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700337 const SkMatrix* viewMatrix,
338 bool noGamma) {
robertphillipsfcf78292015-06-19 11:49:52 -0700339 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700340 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
341 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
342}
343
robertphillips9c240a12015-05-28 07:45:59 -0700344void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700345 const GrClip& clip, const SkPaint& skPaint,
346 const SkMatrix& viewMatrix, const SkTextBlob* blob,
347 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700348 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700349 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700350 if (fContext->abandoned()) {
351 return;
352 }
353
joshualitt374b2f72015-07-21 08:05:03 -0700354 SkAutoTUnref<GrAtlasTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700355 SkMaskFilter::BlurRec blurRec;
joshualitt374b2f72015-07-21 08:05:03 -0700356 GrAtlasTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700357 // It might be worth caching these things, but its not clear at this time
358 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
359 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700360 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700361 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700362 drawFilter);
363
364 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700365 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700366
367 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
robertphillipsfcf78292015-06-19 11:49:52 -0700368 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700369 kUnknown_SkPixelGeometry;
370
joshualitt9e36c1a2015-04-14 12:17:27 -0700371 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
372 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
373 // ensure we always match the same key
374 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
375 ComputeCanonicalColor(skPaint, hasLCD);
376
joshualitte4cee1f2015-05-11 13:04:28 -0700377 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700378 key.fUniqueID = blob->uniqueID();
379 key.fStyle = skPaint.getStyle();
380 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700381 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700382 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700383 }
384
joshualitt1d89e8d2015-04-01 12:40:54 -0700385 SkIRect clipRect;
386 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
387
joshualitt2a0e9f32015-04-13 06:12:21 -0700388 SkScalar transX = 0.f;
389 SkScalar transY = 0.f;
390
joshualitt9e36c1a2015-04-14 12:17:27 -0700391 // Though for the time being runs in the textblob can override the paint, they only touch font
392 // info.
393 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700394 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
395 return;
396 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700397
joshualittb7133be2015-04-08 09:08:31 -0700398 if (cacheBlob) {
jvanverth0628a522015-08-18 07:44:22 -0700399 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, grPaint.getColor(), blurRec,
400 viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700401 // We have to remake the blob because changes may invalidate our masks.
402 // TODO we could probably get away reuse most of the time if the pointer is unique,
403 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700404 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700405 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
406 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700407 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
jvanverth0628a522015-08-18 07:44:22 -0700408 blob, x, y, drawFilter, clipRect, rt, clip);
joshualittb7133be2015-04-08 09:08:31 -0700409 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700410 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
joshualitt7e7b5c52015-07-21 12:56:56 -0700411 // offsets. Note, we offset the vertex bounds right before flushing
joshualitt2a0e9f32015-04-13 06:12:21 -0700412 cacheBlob->fViewMatrix = viewMatrix;
413 cacheBlob->fX = x;
414 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700415 fCache->makeMRU(cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700416#ifdef CACHE_SANITY_CHECK
417 {
418 int glyphCount = 0;
419 int runCount = 0;
420 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
421 SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyphCount, runCount,
422 kGrayTextVASize));
423 GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint);
424 this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix,
jvanverth0628a522015-08-18 07:44:22 -0700425 blob, x, y, drawFilter, clipRect, rt, clip);
joshualitt259fbf12015-07-21 11:39:34 -0700426 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
427 }
428
429#endif
joshualitt1d89e8d2015-04-01 12:40:54 -0700430 }
431 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700432 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700433 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
434 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700435 } else {
436 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
437 }
robertphillips9c240a12015-05-28 07:45:59 -0700438 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
jvanverth0628a522015-08-18 07:44:22 -0700439 blob, x, y, drawFilter, clipRect, rt, clip);
joshualitt1d89e8d2015-04-01 12:40:54 -0700440 }
441
robertphillips2334fb62015-06-17 05:43:33 -0700442 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700443 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700444}
445
joshualitt9bd2daf2015-04-17 09:30:06 -0700446inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
447 const SkMatrix& viewMatrix) {
448 // TODO: support perspective (need getMaxScale replacement)
449 if (viewMatrix.hasPerspective()) {
450 return false;
451 }
452
453 SkScalar maxScale = viewMatrix.getMaxScale();
454 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
455 // Hinted text looks far better at small resolutions
456 // Scaling up beyond 2x yields undesireable artifacts
jvanverth34d72882015-06-22 08:08:09 -0700457 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700458 return false;
459 }
460
bsalomonafcd7cd2015-08-31 12:39:41 -0700461 bool useDFT = fSurfaceProps.isUseDeviceIndependentFonts();
robertphillipsbcd7ab52015-06-18 05:27:18 -0700462#if SK_FORCE_DISTANCE_FIELD_TEXT
463 useDFT = true;
464#endif
465
jvanverth4854d132015-06-22 06:46:56 -0700466 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700467 return false;
468 }
469
470 // rasterizers and mask filters modify alpha, which doesn't
471 // translate well to distance
472 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700473 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700474 return false;
475 }
476
477 // TODO: add some stroking support
478 if (skPaint.getStyle() != SkPaint::kFill_Style) {
479 return false;
480 }
481
482 return true;
483}
484
joshualitt374b2f72015-07-21 08:05:03 -0700485void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700486 const SkPaint& skPaint, GrColor color,
487 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700488 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700489 SkDrawFilter* drawFilter, const SkIRect& clipRect,
jvanverth0628a522015-08-18 07:44:22 -0700490 GrRenderTarget* rt, const GrClip& clip) {
491 // The color here is the GrPaint color, and it is used to determine whether we
492 // have to regenerate LCD text blobs.
493 // We use this color vs the SkPaint color because it has the colorfilter applied.
494 cacheBlob->fPaintColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -0700495 cacheBlob->fViewMatrix = viewMatrix;
496 cacheBlob->fX = x;
497 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700498
499 // Regenerate textblob
500 SkPaint runPaint = skPaint;
501 SkTextBlob::RunIterator it(blob);
502 for (int run = 0; !it.done(); it.next(), run++) {
503 int glyphCount = it.glyphCount();
504 size_t textLen = glyphCount * sizeof(uint16_t);
505 const SkPoint& offset = it.offset();
506 // applyFontToPaint() always overwrites the exact same attributes,
507 // so it is safe to not re-seed the paint for this reason.
508 it.applyFontToPaint(&runPaint);
509
510 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
511 // A false return from filter() means we should abort the current draw.
512 runPaint = skPaint;
513 continue;
514 }
515
robertphillipsfcf78292015-06-19 11:49:52 -0700516 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700517
joshualitt1d89e8d2015-04-01 12:40:54 -0700518 // setup vertex / glyphIndex for the new run
519 if (run > 0) {
520 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
521 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
522
523 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
524 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
525
526 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
527 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
528 }
529
joshualittfcfb9fc2015-04-21 07:35:10 -0700530 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
531 cacheBlob->setHasDistanceField();
532 SkPaint dfPaint = runPaint;
533 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700534 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700535 Run& runIdx = cacheBlob->fRuns[run];
536 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
537 subRun.fUseLCDText = runPaint.isLCDRenderText();
538 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700539
halcanary96fcdcc2015-08-27 07:41:13 -0700540 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, nullptr, true);
joshualittfcfb9fc2015-04-21 07:35:10 -0700541
542 SkTDArray<char> fallbackTxt;
543 SkTDArray<SkScalar> fallbackPos;
544 SkPoint dfOffset;
545 int scalarsPerPosition = 2;
546 switch (it.positioning()) {
547 case SkTextBlob::kDefault_Positioning: {
548 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
549 (const char *)it.glyphs(), textLen,
550 x + offset.x(), y + offset.y(), clipRect, textRatio,
551 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
552 break;
553 }
554 case SkTextBlob::kHorizontal_Positioning: {
555 scalarsPerPosition = 1;
556 dfOffset = SkPoint::Make(x, y + offset.y());
557 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
558 (const char*)it.glyphs(), textLen, it.pos(),
559 scalarsPerPosition, dfOffset, clipRect, textRatio,
560 &fallbackTxt, &fallbackPos);
561 break;
562 }
563 case SkTextBlob::kFull_Positioning: {
564 dfOffset = SkPoint::Make(x, y);
565 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
566 (const char*)it.glyphs(), textLen, it.pos(),
567 scalarsPerPosition, dfOffset, clipRect, textRatio,
568 &fallbackTxt, &fallbackPos);
569 break;
570 }
571 }
572 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700573 this->fallbackDrawPosText(cacheBlob, run, rt, clip, color, runPaint, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700574 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
575 clipRect);
576 }
577
578 SkGlyphCache::AttachCache(cache);
579 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
580 cacheBlob->fRuns[run].fDrawAsPaths = true;
581 } else {
582 cacheBlob->setHasBitmap();
583 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
584 false);
585 switch (it.positioning()) {
586 case SkTextBlob::kDefault_Positioning:
587 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
588 (const char *)it.glyphs(), textLen,
589 x + offset.x(), y + offset.y(), clipRect);
590 break;
591 case SkTextBlob::kHorizontal_Positioning:
592 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
593 (const char*)it.glyphs(), textLen, it.pos(), 1,
594 SkPoint::Make(x, y + offset.y()), clipRect);
595 break;
596 case SkTextBlob::kFull_Positioning:
597 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
598 (const char*)it.glyphs(), textLen, it.pos(), 2,
599 SkPoint::Make(x, y), clipRect);
600 break;
601 }
602 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700603 }
604
605 if (drawFilter) {
606 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
607 runPaint = skPaint;
608 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700609 }
610}
611
joshualitt374b2f72015-07-21 08:05:03 -0700612inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
joshualitt64c99cc2015-04-21 09:43:03 -0700613 SkPaint* skPaint,
614 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700615 const SkMatrix& viewMatrix) {
616 // getMaxScale doesn't support perspective, so neither do we at the moment
617 SkASSERT(!viewMatrix.hasPerspective());
618 SkScalar maxScale = viewMatrix.getMaxScale();
619 SkScalar textSize = skPaint->getTextSize();
620 SkScalar scaledTextSize = textSize;
621 // if we have non-unity scale, we need to choose our base text size
622 // based on the SkPaint's text size multiplied by the max scale factor
623 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
624 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
625 scaledTextSize *= maxScale;
626 }
627
joshualitt64c99cc2015-04-21 09:43:03 -0700628 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
629 // and ceiling. A scale outside of this range would require regenerating the distance fields
630 SkScalar dfMaskScaleFloor;
631 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700632 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700633 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700634 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700635 *textRatio = textSize / kSmallDFFontSize;
636 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
637 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700638 dfMaskScaleFloor = kSmallDFFontLimit;
639 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700640 *textRatio = textSize / kMediumDFFontSize;
641 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
642 } else {
joshualitta7c63892015-04-21 13:24:37 -0700643 dfMaskScaleFloor = kMediumDFFontLimit;
644 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700645 *textRatio = textSize / kLargeDFFontSize;
646 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
647 }
648
joshualitt64c99cc2015-04-21 09:43:03 -0700649 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
650 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
651 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
652 // tolerate before we'd have to move to a large mip size. When we actually test these values
653 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
654 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
655 // level)
joshualitta7c63892015-04-21 13:24:37 -0700656 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700657 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
658 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
659
joshualitt9bd2daf2015-04-17 09:30:06 -0700660 skPaint->setLCDRenderText(false);
661 skPaint->setAutohinted(false);
662 skPaint->setHinting(SkPaint::kNormal_Hinting);
663 skPaint->setSubpixelText(true);
664}
665
joshualitt374b2f72015-07-21 08:05:03 -0700666inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700667 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700668 GrRenderTarget* rt, const GrClip& clip,
jvanverth0628a522015-08-18 07:44:22 -0700669 GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -0700670 const SkPaint& skPaint,
671 const SkMatrix& viewMatrix,
672 const SkTDArray<char>& fallbackTxt,
673 const SkTDArray<SkScalar>& fallbackPos,
674 int scalarsPerPosition,
675 const SkPoint& offset,
676 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700677 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700678 blob->setHasBitmap();
679 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700680 // Push back a new subrun to fill and set the override descriptor
681 run.push_back();
halcanary385fe4d2015-08-26 13:07:48 -0700682 run.fOverrideDescriptor.reset(new SkAutoDescriptor);
joshualitt97202d22015-04-22 13:47:02 -0700683 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700684 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700685 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700686 run.fOverrideDescriptor->getDesc());
jvanverth0628a522015-08-18 07:44:22 -0700687 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, color, viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700688 fallbackTxt.begin(), fallbackTxt.count(),
689 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
690 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700691}
692
joshualitt374b2f72015-07-21 08:05:03 -0700693inline GrAtlasTextBlob*
joshualitt9bd2daf2015-04-17 09:30:06 -0700694GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
695 const SkMatrix& viewMatrix, SkGlyphCache** cache,
696 SkPaint* dfPaint, SkScalar* textRatio) {
joshualitt374b2f72015-07-21 08:05:03 -0700697 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700698
699 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700700 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700701 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700702 Run& run = blob->fRuns[0];
703 PerSubRunInfo& subRun = run.fSubRunInfo.back();
704 subRun.fUseLCDText = origPaint.isLCDRenderText();
705 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700706
halcanary96fcdcc2015-08-27 07:41:13 -0700707 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, nullptr, true);
joshualitt9bd2daf2015-04-17 09:30:06 -0700708 return blob;
709}
710
joshualitt374b2f72015-07-21 08:05:03 -0700711inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700712GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
713 const GrPaint& paint, const SkPaint& skPaint,
714 const SkMatrix& viewMatrix,
715 const char text[], size_t byteLength,
716 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700717 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700718 SkIRect clipRect;
719 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
720
joshualitt374b2f72015-07-21 08:05:03 -0700721 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700722 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
723 SkPaint dfPaint;
724 SkScalar textRatio;
725 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700726 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700727
joshualitt9bd2daf2015-04-17 09:30:06 -0700728 SkTDArray<char> fallbackTxt;
729 SkTDArray<SkScalar> fallbackPos;
730 SkPoint offset;
731 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
732 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
733 &offset, skPaint);
734 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700735 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700736 this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
737 fallbackTxt, fallbackPos, 2, offset, clipRect);
joshualitt9bd2daf2015-04-17 09:30:06 -0700738 }
739 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700740 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700741 blob->fViewMatrix = viewMatrix;
742
743 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
744 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
745 byteLength, x, y, clipRect);
746 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700747 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700748 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700749}
750
joshualitt374b2f72015-07-21 08:05:03 -0700751inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700752GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
753 const GrPaint& paint, const SkPaint& skPaint,
754 const SkMatrix& viewMatrix,
755 const char text[], size_t byteLength,
756 const SkScalar pos[], int scalarsPerPosition,
757 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700758 int glyphCount = skPaint.countText(text, byteLength);
759
760 SkIRect clipRect;
761 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
762
joshualitt374b2f72015-07-21 08:05:03 -0700763 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700764 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
765 SkPaint dfPaint;
766 SkScalar textRatio;
767 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700768 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700769
770 SkTDArray<char> fallbackTxt;
771 SkTDArray<SkScalar> fallbackPos;
772 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
773 byteLength, pos, scalarsPerPosition, offset, clipRect,
774 textRatio, &fallbackTxt, &fallbackPos);
775 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700776 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700777 this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
778 fallbackTxt, fallbackPos, scalarsPerPosition, offset,
779 clipRect);
joshualitt9bd2daf2015-04-17 09:30:06 -0700780 }
781 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700782 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700783 blob->fViewMatrix = viewMatrix;
784 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
785 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
786 byteLength, pos, scalarsPerPosition, offset, clipRect);
787 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700788 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700789 return blob;
790}
791
robertphillips2334fb62015-06-17 05:43:33 -0700792void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700793 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700794 const GrPaint& paint, const SkPaint& skPaint,
795 const SkMatrix& viewMatrix,
796 const char text[], size_t byteLength,
797 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700798 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700799 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
800 text, byteLength, x, y, regionClipBounds));
801 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700802}
803
robertphillips2334fb62015-06-17 05:43:33 -0700804void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700805 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700806 const GrPaint& paint, const SkPaint& skPaint,
807 const SkMatrix& viewMatrix,
808 const char text[], size_t byteLength,
809 const SkScalar pos[], int scalarsPerPosition,
810 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700811 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700812 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
813 text, byteLength,
814 pos, scalarsPerPosition,
815 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700816
robertphillips2334fb62015-06-17 05:43:33 -0700817 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700818}
819
joshualitt374b2f72015-07-21 08:05:03 -0700820void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700821 SkGlyphCache* cache, const SkPaint& skPaint,
822 GrColor color,
823 const SkMatrix& viewMatrix,
824 const char text[], size_t byteLength,
825 SkScalar x, SkScalar y, const SkIRect& clipRect) {
halcanary96fcdcc2015-08-27 07:41:13 -0700826 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt1d89e8d2015-04-01 12:40:54 -0700827
828 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700829 if (text == nullptr || byteLength == 0) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700830 return;
831 }
832
halcanary96fcdcc2015-08-27 07:41:13 -0700833 fCurrStrike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -0700834 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
835
836 // Get GrFontScaler from cache
837 GrFontScaler* fontScaler = GetGrFontScaler(cache);
838
839 // transform our starting point
840 {
841 SkPoint loc;
842 viewMatrix.mapXY(x, y, &loc);
843 x = loc.fX;
844 y = loc.fY;
845 }
846
847 // need to measure first
848 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
849 SkVector stopVector;
850 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
851
852 SkScalar stopX = stopVector.fX;
853 SkScalar stopY = stopVector.fY;
854
855 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
856 stopX = SkScalarHalf(stopX);
857 stopY = SkScalarHalf(stopY);
858 }
859 x -= stopX;
860 y -= stopY;
861 }
862
863 const char* stop = text + byteLength;
864
865 SkAutoKern autokern;
866
867 SkFixed fxMask = ~0;
868 SkFixed fyMask = ~0;
869 SkScalar halfSampleX, halfSampleY;
870 if (cache->isSubpixel()) {
871 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
872 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
873 if (kX_SkAxisAlignment == baseline) {
874 fyMask = 0;
875 halfSampleY = SK_ScalarHalf;
876 } else if (kY_SkAxisAlignment == baseline) {
877 fxMask = 0;
878 halfSampleX = SK_ScalarHalf;
879 }
880 } else {
881 halfSampleX = halfSampleY = SK_ScalarHalf;
882 }
883
884 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
885 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
886
887 while (text < stop) {
888 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
889
890 fx += autokern.adjust(glyph);
891
892 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700893 this->bmpAppendGlyph(blob,
894 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700895 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700896 Sk48Dot16FloorToInt(fx),
897 Sk48Dot16FloorToInt(fy),
898 color,
899 fontScaler,
900 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700901 }
902
903 fx += glyph.fAdvanceX;
904 fy += glyph.fAdvanceY;
905 }
906}
907
joshualitt374b2f72015-07-21 08:05:03 -0700908void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700909 SkGlyphCache* cache, const SkPaint& skPaint,
910 GrColor color,
911 const SkMatrix& viewMatrix,
912 const char text[], size_t byteLength,
913 const SkScalar pos[], int scalarsPerPosition,
914 const SkPoint& offset, const SkIRect& clipRect) {
halcanary96fcdcc2015-08-27 07:41:13 -0700915 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt1d89e8d2015-04-01 12:40:54 -0700916 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
917
918 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700919 if (text == nullptr || byteLength == 0) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700920 return;
921 }
922
halcanary96fcdcc2015-08-27 07:41:13 -0700923 fCurrStrike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -0700924 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
925
926 // Get GrFontScaler from cache
927 GrFontScaler* fontScaler = GetGrFontScaler(cache);
928
929 const char* stop = text + byteLength;
930 SkTextAlignProc alignProc(skPaint.getTextAlign());
931 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
932
933 if (cache->isSubpixel()) {
934 // maybe we should skip the rounding if linearText is set
935 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
936
937 SkFixed fxMask = ~0;
938 SkFixed fyMask = ~0;
939 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
940 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
941 if (kX_SkAxisAlignment == baseline) {
942 fyMask = 0;
943 halfSampleY = SK_ScalarHalf;
944 } else if (kY_SkAxisAlignment == baseline) {
945 fxMask = 0;
946 halfSampleX = SK_ScalarHalf;
947 }
948
949 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
950 while (text < stop) {
951 SkPoint tmsLoc;
952 tmsProc(pos, &tmsLoc);
953 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
954 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
955
956 const SkGlyph& glyph = glyphCacheProc(cache, &text,
957 fx & fxMask, fy & fyMask);
958
959 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700960 this->bmpAppendGlyph(blob,
961 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700962 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700963 Sk48Dot16FloorToInt(fx),
964 Sk48Dot16FloorToInt(fy),
965 color,
966 fontScaler,
967 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700968 }
969 pos += scalarsPerPosition;
970 }
971 } else {
972 while (text < stop) {
973 const char* currentText = text;
974 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
975
976 if (metricGlyph.fWidth) {
977 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
978 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
979 SkPoint tmsLoc;
980 tmsProc(pos, &tmsLoc);
981 SkPoint alignLoc;
982 alignProc(tmsLoc, metricGlyph, &alignLoc);
983
984 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
985 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
986
987 // have to call again, now that we've been "aligned"
988 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
989 fx & fxMask, fy & fyMask);
990 // the assumption is that the metrics haven't changed
991 SkASSERT(prevAdvX == glyph.fAdvanceX);
992 SkASSERT(prevAdvY == glyph.fAdvanceY);
993 SkASSERT(glyph.fWidth);
994
joshualitt9bd2daf2015-04-17 09:30:06 -0700995 this->bmpAppendGlyph(blob,
996 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700997 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700998 Sk48Dot16FloorToInt(fx),
999 Sk48Dot16FloorToInt(fy),
1000 color,
1001 fontScaler,
1002 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001003 }
1004 pos += scalarsPerPosition;
1005 }
1006 }
1007 } else { // not subpixel
1008
1009 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1010 while (text < stop) {
1011 // the last 2 parameters are ignored
1012 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1013
1014 if (glyph.fWidth) {
1015 SkPoint tmsLoc;
1016 tmsProc(pos, &tmsLoc);
1017
1018 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1019 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001020 this->bmpAppendGlyph(blob,
1021 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001022 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001023 Sk48Dot16FloorToInt(fx),
1024 Sk48Dot16FloorToInt(fy),
1025 color,
1026 fontScaler,
1027 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001028 }
1029 pos += scalarsPerPosition;
1030 }
1031 } else {
1032 while (text < stop) {
1033 // the last 2 parameters are ignored
1034 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1035
1036 if (glyph.fWidth) {
1037 SkPoint tmsLoc;
1038 tmsProc(pos, &tmsLoc);
1039
1040 SkPoint alignLoc;
1041 alignProc(tmsLoc, glyph, &alignLoc);
1042
1043 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1044 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001045 this->bmpAppendGlyph(blob,
1046 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001047 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001048 Sk48Dot16FloorToInt(fx),
1049 Sk48Dot16FloorToInt(fy),
1050 color,
1051 fontScaler,
1052 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001053 }
1054 pos += scalarsPerPosition;
1055 }
1056 }
1057 }
1058}
1059
joshualitt9bd2daf2015-04-17 09:30:06 -07001060
joshualitt374b2f72015-07-21 08:05:03 -07001061void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001062 SkGlyphCache* cache, const SkPaint& skPaint,
1063 GrColor color,
1064 const SkMatrix& viewMatrix,
1065 const char text[], size_t byteLength,
1066 SkScalar x, SkScalar y, const SkIRect& clipRect,
1067 SkScalar textRatio,
1068 SkTDArray<char>* fallbackTxt,
1069 SkTDArray<SkScalar>* fallbackPos,
1070 SkPoint* offset,
1071 const SkPaint& origPaint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001072 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt9bd2daf2015-04-17 09:30:06 -07001073
1074 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001075 if (text == nullptr || byteLength == 0) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001076 return;
1077 }
1078
1079 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1080 SkAutoDescriptor desc;
halcanary96fcdcc2015-08-27 07:41:13 -07001081 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001082 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1083 desc.getDesc());
1084
1085 SkTArray<SkScalar> positions;
1086
1087 const char* textPtr = text;
1088 SkFixed stopX = 0;
1089 SkFixed stopY = 0;
1090 SkFixed origin = 0;
1091 switch (origPaint.getTextAlign()) {
1092 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1093 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1094 case SkPaint::kLeft_Align: origin = 0; break;
1095 }
1096
1097 SkAutoKern autokern;
1098 const char* stop = text + byteLength;
1099 while (textPtr < stop) {
1100 // don't need x, y here, since all subpixel variants will have the
1101 // same advance
1102 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1103
1104 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1105 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1106
1107 SkFixed height = glyph.fAdvanceY;
1108 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1109
1110 stopX += width;
1111 stopY += height;
1112 }
1113 SkASSERT(textPtr == stop);
1114
1115 // now adjust starting point depending on alignment
1116 SkScalar alignX = SkFixedToScalar(stopX);
1117 SkScalar alignY = SkFixedToScalar(stopY);
1118 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1119 alignX = SkScalarHalf(alignX);
1120 alignY = SkScalarHalf(alignY);
1121 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1122 alignX = 0;
1123 alignY = 0;
1124 }
1125 x -= alignX;
1126 y -= alignY;
1127 *offset = SkPoint::Make(x, y);
1128
1129 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1130 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1131 fallbackPos);
1132 SkGlyphCache::AttachCache(origPaintCache);
1133}
1134
joshualitt374b2f72015-07-21 08:05:03 -07001135void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001136 SkGlyphCache* cache, const SkPaint& skPaint,
1137 GrColor color,
1138 const SkMatrix& viewMatrix,
1139 const char text[], size_t byteLength,
1140 const SkScalar pos[], int scalarsPerPosition,
1141 const SkPoint& offset, const SkIRect& clipRect,
1142 SkScalar textRatio,
1143 SkTDArray<char>* fallbackTxt,
1144 SkTDArray<SkScalar>* fallbackPos) {
1145
halcanary96fcdcc2015-08-27 07:41:13 -07001146 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt9bd2daf2015-04-17 09:30:06 -07001147 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1148
1149 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001150 if (text == nullptr || byteLength == 0) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001151 return;
1152 }
1153
halcanary96fcdcc2015-08-27 07:41:13 -07001154 fCurrStrike = nullptr;
joshualitt9bd2daf2015-04-17 09:30:06 -07001155
1156 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1157 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1158
1159 const char* stop = text + byteLength;
1160
1161 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1162 while (text < stop) {
1163 const char* lastText = text;
1164 // the last 2 parameters are ignored
1165 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1166
1167 if (glyph.fWidth) {
1168 SkScalar x = offset.x() + pos[0];
1169 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1170
1171 if (!this->dfAppendGlyph(blob,
1172 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001173 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001174 x, y, color, fontScaler, clipRect,
1175 textRatio, viewMatrix)) {
1176 // couldn't append, send to fallback
1177 fallbackTxt->append(SkToInt(text-lastText), lastText);
1178 *fallbackPos->append() = pos[0];
1179 if (2 == scalarsPerPosition) {
1180 *fallbackPos->append() = pos[1];
1181 }
1182 }
1183 }
1184 pos += scalarsPerPosition;
1185 }
1186 } else {
1187 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1188 : SK_Scalar1;
1189 while (text < stop) {
1190 const char* lastText = text;
1191 // the last 2 parameters are ignored
1192 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1193
1194 if (glyph.fWidth) {
1195 SkScalar x = offset.x() + pos[0];
1196 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1197
1198 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1199 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1200
1201 if (!this->dfAppendGlyph(blob,
1202 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001203 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001204 x - advanceX, y - advanceY, color,
1205 fontScaler,
1206 clipRect,
1207 textRatio,
1208 viewMatrix)) {
1209 // couldn't append, send to fallback
1210 fallbackTxt->append(SkToInt(text-lastText), lastText);
1211 *fallbackPos->append() = pos[0];
1212 if (2 == scalarsPerPosition) {
1213 *fallbackPos->append() = pos[1];
1214 }
1215 }
1216 }
1217 pos += scalarsPerPosition;
1218 }
1219 }
1220}
1221
joshualitt374b2f72015-07-21 08:05:03 -07001222void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001223 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001224 int vx, int vy, GrColor color, GrFontScaler* scaler,
1225 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001226 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001227 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001228 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1229 }
1230
joshualitt6c2c2b02015-07-24 10:37:00 -07001231 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1232 skGlyph.getSubXFixed(),
1233 skGlyph.getSubYFixed(),
1234 GrGlyph::kCoverage_MaskStyle);
1235 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001236 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001237 return;
1238 }
1239
1240 int x = vx + glyph->fBounds.fLeft;
1241 int y = vy + glyph->fBounds.fTop;
1242
1243 // keep them as ints until we've done the clip-test
1244 int width = glyph->fBounds.width();
1245 int height = glyph->fBounds.height();
1246
joshualitt2a0e9f32015-04-13 06:12:21 -07001247#if 0
1248 // Not checking the clip bounds might introduce a performance regression. However, its not
1249 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1250 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1251 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1252 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001253 // check if we clipped out
1254 if (clipRect.quickReject(x, y, x + width, y + height)) {
1255 return;
1256 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001257#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001258
1259 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001260 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001261 this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001262 return;
1263 }
1264
joshualitt1d89e8d2015-04-01 12:40:54 -07001265 GrMaskFormat format = glyph->fMaskFormat;
1266
1267 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1268 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittd9f13ae2015-07-24 11:24:31 -07001269 subRun = &run.push_back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001270 subRun->fStrike.reset(SkRef(fCurrStrike));
1271 } else if (!run.fInitialized) {
1272 subRun->fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001273 }
1274
1275 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001276
1277 size_t vertexStride = get_vertex_stride(format);
1278
1279 SkRect r;
1280 r.fLeft = SkIntToScalar(x);
1281 r.fTop = SkIntToScalar(y);
1282 r.fRight = r.fLeft + SkIntToScalar(width);
1283 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001284 subRun->fMaskFormat = format;
1285 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001286 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001287}
joshualitt1d89e8d2015-04-01 12:40:54 -07001288
joshualitt374b2f72015-07-21 08:05:03 -07001289bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001290 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001291 SkScalar sx, SkScalar sy, GrColor color,
1292 GrFontScaler* scaler,
1293 const SkIRect& clipRect,
1294 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001295 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001296 if (!fCurrStrike) {
1297 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1298 }
1299
joshualitt6c2c2b02015-07-24 10:37:00 -07001300 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1301 skGlyph.getSubXFixed(),
1302 skGlyph.getSubYFixed(),
1303 GrGlyph::kDistance_MaskStyle);
1304 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001305 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001306 return true;
1307 }
1308
1309 // fallback to color glyph support
1310 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1311 return false;
1312 }
1313
1314 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1315 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1316 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1317 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1318
1319 SkScalar scale = textRatio;
1320 dx *= scale;
1321 dy *= scale;
1322 width *= scale;
1323 height *= scale;
1324 sx += dx;
1325 sy += dy;
1326 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1327
1328#if 0
1329 // check if we clipped out
1330 SkRect dstRect;
1331 viewMatrix.mapRect(&dstRect, glyphRect);
1332 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1333 SkScalarTruncToInt(dstRect.top()),
1334 SkScalarTruncToInt(dstRect.right()),
1335 SkScalarTruncToInt(dstRect.bottom()))) {
1336 return true;
1337 }
1338#endif
1339
1340 // TODO combine with the above
1341 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001342 if (glyph->fTooLargeForAtlas) {
joshualitt0fe04a22015-08-25 12:05:50 -07001343 this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy, scale, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001344 return true;
1345 }
1346
joshualitt9bd2daf2015-04-17 09:30:06 -07001347 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001348 if (!run.fInitialized) {
1349 subRun->fStrike.reset(SkRef(fCurrStrike));
1350 }
1351 run.fInitialized = true;
joshualitt9bd2daf2015-04-17 09:30:06 -07001352 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1353 subRun->fMaskFormat = kA8_GrMaskFormat;
1354
1355 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1356
1357 bool useColorVerts = !subRun->fUseLCDText;
1358 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001359 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001360 return true;
1361}
1362
joshualitt374b2f72015-07-21 08:05:03 -07001363inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001364 GrFontScaler* scaler, const SkGlyph& skGlyph,
joshualitt0fe04a22015-08-25 12:05:50 -07001365 SkScalar x, SkScalar y, SkScalar scale,
1366 bool applyVM) {
halcanary96fcdcc2015-08-27 07:41:13 -07001367 if (nullptr == glyph->fPath) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001368 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
1369 if (!glyphPath) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001370 return;
1371 }
joshualitt6c2c2b02015-07-24 10:37:00 -07001372
halcanary385fe4d2015-08-26 13:07:48 -07001373 glyph->fPath = new SkPath(*glyphPath);
joshualitt9bd2daf2015-04-17 09:30:06 -07001374 }
joshualitt0fe04a22015-08-25 12:05:50 -07001375 blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM));
joshualitt9bd2daf2015-04-17 09:30:06 -07001376}
1377
joshualitt374b2f72015-07-21 08:05:03 -07001378inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run,
joshualitt9bd2daf2015-04-17 09:30:06 -07001379 Run::SubRunInfo* subRun,
1380 const SkRect& positions, GrColor color,
1381 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001382 GrGlyph* glyph) {
1383 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001384 run->fVertexBounds.joinNonEmptyArg(positions);
1385 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001386
1387 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1388
joshualitt9bd2daf2015-04-17 09:30:06 -07001389 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001390 // V0
1391 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1392 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001393 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1394 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001395 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001396
joshualitt010db532015-04-21 10:07:26 -07001397 // V1
1398 position = reinterpret_cast<SkPoint*>(vertex);
1399 position->set(positions.fLeft, positions.fBottom);
1400 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001401 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001402 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001403
joshualitt010db532015-04-21 10:07:26 -07001404 // V2
1405 position = reinterpret_cast<SkPoint*>(vertex);
1406 position->set(positions.fRight, positions.fBottom);
1407 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001408 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001409 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001410
joshualitt010db532015-04-21 10:07:26 -07001411 // V3
1412 position = reinterpret_cast<SkPoint*>(vertex);
1413 position->set(positions.fRight, positions.fTop);
1414 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001415 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001416 } else {
1417 // V0
1418 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1419 position->set(positions.fLeft, positions.fTop);
1420 vertex += vertexStride;
1421
1422 // V1
1423 position = reinterpret_cast<SkPoint*>(vertex);
1424 position->set(positions.fLeft, positions.fBottom);
1425 vertex += vertexStride;
1426
1427 // V2
1428 position = reinterpret_cast<SkPoint*>(vertex);
1429 position->set(positions.fRight, positions.fBottom);
1430 vertex += vertexStride;
1431
1432 // V3
1433 position = reinterpret_cast<SkPoint*>(vertex);
1434 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001435 }
1436
1437 subRun->fGlyphEndIndex++;
1438 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1439}
1440
bsalomonabd30f52015-08-13 13:34:48 -07001441class TextBatch : public GrVertexBatch {
joshualitt1d89e8d2015-04-01 12:40:54 -07001442public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001443 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualitt374b2f72015-07-21 08:05:03 -07001444 typedef GrAtlasTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001445 typedef Blob::Run Run;
1446 typedef Run::SubRunInfo TextInfo;
1447 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001448 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001449 int fRun;
1450 int fSubRun;
1451 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001452 SkScalar fTransX;
1453 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001454 };
1455
bsalomon265697d2015-07-22 10:17:26 -07001456 static TextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
joshualittad802c62015-04-15 05:31:57 -07001457 GrBatchFontCache* fontCache) {
halcanary385fe4d2015-08-26 13:07:48 -07001458 TextBatch* batch = new TextBatch;
bsalomon265697d2015-07-22 10:17:26 -07001459
1460 batch->initClassID<TextBatch>();
1461 batch->fFontCache = fontCache;
1462 switch (maskFormat) {
1463 case kA8_GrMaskFormat:
1464 batch->fMaskType = kGrayscaleCoverageMask_MaskType;
1465 break;
1466 case kA565_GrMaskFormat:
1467 batch->fMaskType = kLCDCoverageMask_MaskType;
1468 break;
1469 case kARGB_GrMaskFormat:
1470 batch->fMaskType = kColorBitmapMask_MaskType;
1471 break;
1472 }
1473 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001474 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001475 batch->fFilteredColor = 0;
1476 batch->fFontCache = fontCache;
1477 batch->fUseBGR = false;
1478 return batch;
joshualitt1d89e8d2015-04-01 12:40:54 -07001479 }
1480
bsalomon265697d2015-07-22 10:17:26 -07001481 static TextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache,
1482 DistanceAdjustTable* distanceAdjustTable,
1483 SkColor filteredColor, bool isLCD,
1484 bool useBGR) {
halcanary385fe4d2015-08-26 13:07:48 -07001485 TextBatch* batch = new TextBatch;
bsalomon265697d2015-07-22 10:17:26 -07001486 batch->initClassID<TextBatch>();
1487 batch->fFontCache = fontCache;
1488 batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
1489 batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
1490 batch->fFilteredColor = filteredColor;
1491 batch->fUseBGR = useBGR;
1492 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001493 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001494 return batch;
joshualitt9bd2daf2015-04-17 09:30:06 -07001495 }
1496
bsalomone46f9fe2015-08-18 06:05:14 -07001497 // to avoid even the initial copy of the struct, we have a getter for the first item which
1498 // is used to seed the batch with its initial geometry. After seeding, the client should call
1499 // init() so the Batch can initialize itself
1500 Geometry& geometry() { return fGeoData[0]; }
1501
1502 void init() {
1503 const Geometry& geo = fGeoData[0];
1504 fBatch.fColor = geo.fColor;
1505 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1506
1507 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1508 // into device space
1509 const Run& run = geo.fBlob->fRuns[geo.fRun];
1510 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1511 SkRect bounds = run.fVertexBounds;
1512 fBatch.fViewMatrix.mapRect(&bounds);
1513 this->setBounds(bounds);
1514 } else {
1515 this->setBounds(run.fVertexBounds);
1516 }
1517 }
1518
bsalomon265697d2015-07-22 10:17:26 -07001519 const char* name() const override { return "TextBatch"; }
joshualitt1d89e8d2015-04-01 12:40:54 -07001520
1521 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001522 if (kColorBitmapMask_MaskType == fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001523 out->setUnknownFourComponents();
1524 } else {
1525 out->setKnownFourComponents(fBatch.fColor);
1526 }
1527 }
1528
1529 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001530 switch (fMaskType) {
1531 case kGrayscaleDistanceField_MaskType:
1532 case kGrayscaleCoverageMask_MaskType:
joshualitt1d89e8d2015-04-01 12:40:54 -07001533 out->setUnknownSingleComponent();
bsalomon265697d2015-07-22 10:17:26 -07001534 break;
1535 case kLCDCoverageMask_MaskType:
1536 case kLCDDistanceField_MaskType:
1537 out->setUnknownOpaqueFourComponents();
joshualitt1d89e8d2015-04-01 12:40:54 -07001538 out->setUsingLCDCoverage();
bsalomon265697d2015-07-22 10:17:26 -07001539 break;
1540 case kColorBitmapMask_MaskType:
1541 out->setKnownSingleComponent(0xff);
joshualitt1d89e8d2015-04-01 12:40:54 -07001542 }
1543 }
1544
bsalomone46f9fe2015-08-18 06:05:14 -07001545private:
bsalomon91d844d2015-08-10 10:47:29 -07001546 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001547 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001548 if (!opt.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001549 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001550 }
bsalomon91d844d2015-08-10 10:47:29 -07001551 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001552
1553 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001554 fBatch.fColorIgnored = !opt.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001555 fBatch.fColor = fGeoData[0].fColor;
bsalomon91d844d2015-08-10 10:47:29 -07001556 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1557 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001558 }
1559
bsalomonb5238a72015-05-05 07:49:49 -07001560 struct FlushInfo {
1561 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1562 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1563 int fGlyphsToFlush;
1564 int fVertexOffset;
1565 };
1566
bsalomon75398562015-08-17 12:55:38 -07001567 void onPrepareDraws(Target* target) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001568 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1569 // TODO actually only invert if we don't have RGBA
1570 SkMatrix localMatrix;
1571 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1572 SkDebugf("Cannot invert viewmatrix\n");
1573 return;
1574 }
1575
bsalomon265697d2015-07-22 10:17:26 -07001576 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
joshualitt62db8ba2015-04-09 08:22:37 -07001577 if (!texture) {
1578 SkDebugf("Could not allocate backing texture for atlas\n");
1579 return;
1580 }
1581
bsalomon265697d2015-07-22 10:17:26 -07001582 bool usesDistanceFields = this->usesDistanceFields();
1583 GrMaskFormat maskFormat = this->maskFormat();
1584 bool isLCD = this->isLCD();
1585
joshualitt9bd2daf2015-04-17 09:30:06 -07001586 SkAutoTUnref<const GrGeometryProcessor> gp;
bsalomon265697d2015-07-22 10:17:26 -07001587 if (usesDistanceFields) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001588 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1589 texture));
1590 } else {
1591 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001592 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1593 texture,
1594 params,
bsalomon265697d2015-07-22 10:17:26 -07001595 maskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001596 localMatrix,
1597 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001598 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001599
bsalomonb5238a72015-05-05 07:49:49 -07001600 FlushInfo flushInfo;
1601 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001602 size_t vertexStride = gp->getVertexStride();
bsalomon265697d2015-07-22 10:17:26 -07001603 SkASSERT(vertexStride == (usesDistanceFields ?
1604 get_vertex_stride_df(maskFormat, isLCD) :
1605 get_vertex_stride(maskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001606
bsalomon75398562015-08-17 12:55:38 -07001607 target->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001608
1609 int glyphCount = this->numGlyphs();
bsalomon8415abe2015-05-04 11:41:41 -07001610 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001611
bsalomon75398562015-08-17 12:55:38 -07001612 void* vertices = target->makeVertexSpace(vertexStride,
1613 glyphCount * kVerticesPerGlyph,
1614 &vertexBuffer,
1615 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001616 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -07001617 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -07001618 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001619 SkDebugf("Could not allocate vertices\n");
1620 return;
1621 }
1622
1623 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1624
joshualitt25ba7ea2015-04-21 07:49:49 -07001625 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1626 // in a row
halcanary96fcdcc2015-08-27 07:41:13 -07001627 const SkDescriptor* desc = nullptr;
1628 SkGlyphCache* cache = nullptr;
1629 GrFontScaler* scaler = nullptr;
1630 SkTypeface* typeface = nullptr;
joshualitt25ba7ea2015-04-21 07:49:49 -07001631
bsalomond602f4d2015-07-27 06:12:01 -07001632 for (int i = 0; i < fGeoCount; i++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001633 Geometry& args = fGeoData[i];
1634 Blob* blob = args.fBlob;
1635 Run& run = blob->fRuns[args.fRun];
1636 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1637
bsalomon265697d2015-07-22 10:17:26 -07001638 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001639 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
joshualitt7e97b0b2015-07-31 15:18:08 -07001640 info.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001641 bool regenerateColors;
bsalomon265697d2015-07-22 10:17:26 -07001642 if (usesDistanceFields) {
1643 regenerateColors = !isLCD && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001644 } else {
bsalomon265697d2015-07-22 10:17:26 -07001645 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001646 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001647 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001648 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1649
1650 // We regenerate both texture coords and colors in the blob itself, and update the
1651 // atlas generation. If we don't end up purging any unused plots, we can avoid
1652 // regenerating the coords. We could take a finer grained approach to updating texture
1653 // coords but its not clear if the extra bookkeeping would offset any gains.
1654 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1655 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1656 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1657 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001658 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001659 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001660 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001661
1662 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1663 // generating its texture coords, we have to track whether or not the strike has
1664 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1665 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1666 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1667 // new strike, we instead keep our ref to the old strike and use the packed ids from
1668 // it. These ids will still be valid as long as we hold the ref. When we are done
1669 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1670 bool regenerateGlyphs = false;
halcanary96fcdcc2015-08-27 07:41:13 -07001671 GrBatchTextStrike* strike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -07001672 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001673 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001674
1675 // We can reuse if we have a valid strike and our descriptors / typeface are the
1676 // same
joshualitt97202d22015-04-22 13:47:02 -07001677 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1678 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001679 run.fDescriptor.getDesc();
1680 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1681 !(desc->equals(*newDesc))) {
1682 if (cache) {
1683 SkGlyphCache::AttachCache(cache);
1684 }
1685 desc = newDesc;
1686 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1687 scaler = GrTextContext::GetGrFontScaler(cache);
joshualitt7e97b0b2015-07-31 15:18:08 -07001688 strike = info.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001689 typeface = run.fTypeface;
1690 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001691
joshualitt7e97b0b2015-07-31 15:18:08 -07001692 if (info.fStrike->isAbandoned()) {
joshualittae32c102015-04-21 09:37:57 -07001693 regenerateGlyphs = true;
1694 strike = fFontCache->getStrike(scaler);
1695 } else {
joshualitt7e97b0b2015-07-31 15:18:08 -07001696 strike = info.fStrike;
joshualittae32c102015-04-21 09:37:57 -07001697 }
1698 }
1699
1700 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001701 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001702 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
joshualitt6c2c2b02015-07-24 10:37:00 -07001703
1704 GrGlyph* glyph = blob->fGlyphs[glyphOffset];
1705 GrGlyph::PackedID id = glyph->fPackedID;
1706 const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
joshualittae32c102015-04-21 09:37:57 -07001707 if (regenerateGlyphs) {
1708 // Get the id from the old glyph, and use the new strike to lookup
1709 // the glyph.
joshualitt76cc6572015-07-31 05:51:45 -07001710 blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, maskFormat,
1711 scaler);
joshualittae32c102015-04-21 09:37:57 -07001712 }
1713 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001714 SkASSERT(glyph);
joshualitt65e96b42015-07-31 11:45:22 -07001715 SkASSERT(id == glyph->fPackedID);
1716 // We want to be able to assert this but cannot for testing purposes.
1717 // once skbug:4143 has landed we can revist this assert
1718 //SkASSERT(glyph->fMaskFormat == this->maskFormat());
joshualitt1d89e8d2015-04-01 12:40:54 -07001719
1720 if (!fFontCache->hasGlyph(glyph) &&
bsalomon75398562015-08-17 12:55:38 -07001721 !strike->addGlyphToAtlas(target, glyph, scaler, skGlyph, maskFormat)) {
1722 this->flush(target, &flushInfo);
1723 target->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001724 brokenRun = glyphIdx > 0;
1725
bsalomon75398562015-08-17 12:55:38 -07001726 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
joshualittae32c102015-04-21 09:37:57 -07001727 glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001728 scaler,
joshualitt4f19ca32015-07-30 07:59:20 -07001729 skGlyph,
1730 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001731 SkASSERT(success);
1732 }
joshualittb4c507e2015-04-08 08:07:59 -07001733 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
bsalomon75398562015-08-17 12:55:38 -07001734 target->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001735
1736 // Texture coords are the last vertex attribute so we get a pointer to the
1737 // first one and then map with stride in regenerateTextureCoords
1738 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1739 vertex += info.fVertexStartIndex;
1740 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1741 vertex += vertexStride - sizeof(SkIPoint16);
1742
1743 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1744 }
1745
1746 if (regenerateColors) {
1747 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1748 vertex += info.fVertexStartIndex;
1749 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1750 this->regenerateColors(vertex, vertexStride, args.fColor);
1751 }
1752
joshualitt2a0e9f32015-04-13 06:12:21 -07001753 if (regeneratePositions) {
1754 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1755 vertex += info.fVertexStartIndex;
1756 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1757 SkScalar transX = args.fTransX;
1758 SkScalar transY = args.fTransY;
1759 this->regeneratePositions(vertex, vertexStride, transX, transY);
1760 }
bsalomonb5238a72015-05-05 07:49:49 -07001761 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001762 }
1763
joshualitt2a0e9f32015-04-13 06:12:21 -07001764 // We my have changed the color so update it here
1765 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001766 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001767 if (regenerateGlyphs) {
joshualitt7e97b0b2015-07-31 15:18:08 -07001768 info.fStrike.reset(SkRef(strike));
joshualittae32c102015-04-21 09:37:57 -07001769 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001770 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
bsalomon265697d2015-07-22 10:17:26 -07001771 fFontCache->atlasGeneration(maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001772 }
1773 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001774 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001775
1776 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1777 // have a valid atlas generation
bsalomon75398562015-08-17 12:55:38 -07001778 fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001779 }
1780
1781 // now copy all vertices
1782 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1783 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1784
1785 currVertex += byteCount;
1786 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001787 // Make sure to attach the last cache if applicable
1788 if (cache) {
1789 SkGlyphCache::AttachCache(cache);
1790 }
bsalomon75398562015-08-17 12:55:38 -07001791 this->flush(target, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001792 }
1793
bsalomon265697d2015-07-22 10:17:26 -07001794 TextBatch() {} // initialized in factory functions.
joshualittad802c62015-04-15 05:31:57 -07001795
bsalomon265697d2015-07-22 10:17:26 -07001796 ~TextBatch() {
bsalomond602f4d2015-07-27 06:12:01 -07001797 for (int i = 0; i < fGeoCount; i++) {
joshualittad802c62015-04-15 05:31:57 -07001798 fGeoData[i].fBlob->unref();
1799 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001800 }
1801
bsalomon265697d2015-07-22 10:17:26 -07001802 GrMaskFormat maskFormat() const {
1803 switch (fMaskType) {
1804 case kLCDCoverageMask_MaskType:
1805 return kA565_GrMaskFormat;
1806 case kColorBitmapMask_MaskType:
1807 return kARGB_GrMaskFormat;
1808 case kGrayscaleCoverageMask_MaskType:
1809 case kGrayscaleDistanceField_MaskType:
1810 case kLCDDistanceField_MaskType:
1811 return kA8_GrMaskFormat;
1812 }
1813 return kA8_GrMaskFormat; // suppress warning
1814 }
1815
1816 bool usesDistanceFields() const {
1817 return kGrayscaleDistanceField_MaskType == fMaskType ||
1818 kLCDDistanceField_MaskType == fMaskType;
1819 }
1820
1821 bool isLCD() const {
1822 return kLCDCoverageMask_MaskType == fMaskType ||
1823 kLCDDistanceField_MaskType == fMaskType;
1824 }
1825
joshualitt1d89e8d2015-04-01 12:40:54 -07001826 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1827 int width = glyph->fBounds.width();
1828 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001829
joshualitt9bd2daf2015-04-17 09:30:06 -07001830 int u0, v0, u1, v1;
bsalomon265697d2015-07-22 10:17:26 -07001831 if (this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001832 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1833 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1834 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1835 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1836 } else {
1837 u0 = glyph->fAtlasLocation.fX;
1838 v0 = glyph->fAtlasLocation.fY;
1839 u1 = u0 + width;
1840 v1 = v0 + height;
1841 }
1842
joshualitt1d89e8d2015-04-01 12:40:54 -07001843 SkIPoint16* textureCoords;
1844 // V0
1845 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1846 textureCoords->set(u0, v0);
1847 vertex += vertexStride;
1848
1849 // V1
1850 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1851 textureCoords->set(u0, v1);
1852 vertex += vertexStride;
1853
1854 // V2
1855 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1856 textureCoords->set(u1, v1);
1857 vertex += vertexStride;
1858
1859 // V3
1860 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1861 textureCoords->set(u1, v0);
1862 }
1863
1864 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1865 for (int i = 0; i < kVerticesPerGlyph; i++) {
1866 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1867 *vcolor = color;
1868 vertex += vertexStride;
1869 }
1870 }
1871
joshualitt2a0e9f32015-04-13 06:12:21 -07001872 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1873 SkScalar transY) {
1874 for (int i = 0; i < kVerticesPerGlyph; i++) {
1875 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1876 point->fX += transX;
1877 point->fY += transY;
1878 vertex += vertexStride;
1879 }
1880 }
1881
bsalomon75398562015-08-17 12:55:38 -07001882 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001883 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001884 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001885 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001886 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001887 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001888 maxGlyphsPerDraw);
bsalomon75398562015-08-17 12:55:38 -07001889 target->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001890 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1891 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001892 }
1893
1894 GrColor color() const { return fBatch.fColor; }
1895 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1896 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1897 int numGlyphs() const { return fBatch.fNumGlyphs; }
1898
bsalomoncb02b382015-08-12 11:14:50 -07001899 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001900 TextBatch* that = t->cast<TextBatch>();
1901 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1902 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001903 return false;
1904 }
1905
bsalomon265697d2015-07-22 10:17:26 -07001906 if (fMaskType != that->fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001907 return false;
1908 }
1909
bsalomon265697d2015-07-22 10:17:26 -07001910 if (!this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001911 // TODO we can often batch across LCD text if we have dual source blending and don't
1912 // have to use the blend constant
bsalomon265697d2015-07-22 10:17:26 -07001913 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001914 return false;
1915 }
joshualitt9bd2daf2015-04-17 09:30:06 -07001916 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1917 return false;
1918 }
1919 } else {
joshualitt9bd2daf2015-04-17 09:30:06 -07001920 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1921 return false;
1922 }
1923
1924 if (fFilteredColor != that->fFilteredColor) {
1925 return false;
1926 }
1927
joshualitt9bd2daf2015-04-17 09:30:06 -07001928 if (fUseBGR != that->fUseBGR) {
1929 return false;
1930 }
1931
joshualitt9bd2daf2015-04-17 09:30:06 -07001932 // TODO see note above
bsalomon265697d2015-07-22 10:17:26 -07001933 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) {
joshualitt0fe04a22015-08-25 12:05:50 -07001934 return false;
joshualitt9bd2daf2015-04-17 09:30:06 -07001935 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001936 }
1937
1938 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001939
bsalomond602f4d2015-07-27 06:12:01 -07001940 // Reallocate space for geo data if necessary and then import that's geo data.
1941 int newGeoCount = that->fGeoCount + fGeoCount;
1942 // We assume (and here enforce) that the allocation size is the smallest power of two that
1943 // is greater than or equal to the number of geometries (and at least
1944 // kMinGeometryAllocated).
1945 int newAllocSize = GrNextPow2(newGeoCount);
1946 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
1947
bsalomon16ed6ad2015-07-29 06:54:33 -07001948 if (newGeoCount > currAllocSize) {
bsalomond602f4d2015-07-27 06:12:01 -07001949 fGeoData.realloc(newAllocSize);
joshualittad802c62015-04-15 05:31:57 -07001950 }
1951
bsalomond602f4d2015-07-27 06:12:01 -07001952 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
bsalomon1c634362015-07-27 07:00:00 -07001953 // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
1954 // it doesn't try to unref them.
1955#ifdef SK_DEBUG
1956 for (int i = 0; i < that->fGeoCount; ++i) {
1957 that->fGeoData.get()[i].fBlob = (Blob*)0x1;
joshualittad802c62015-04-15 05:31:57 -07001958 }
bsalomon1c634362015-07-27 07:00:00 -07001959#endif
1960 that->fGeoCount = 0;
bsalomond602f4d2015-07-27 06:12:01 -07001961 fGeoCount = newGeoCount;
joshualitt99c7c072015-05-01 13:43:30 -07001962
1963 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001964 return true;
1965 }
1966
joshualitt9bd2daf2015-04-17 09:30:06 -07001967 // TODO just use class params
1968 // TODO trying to figure out why lcd is so whack
1969 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1970 GrColor color, GrTexture* texture) {
1971 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
bsalomon265697d2015-07-22 10:17:26 -07001972 bool isLCD = this->isLCD();
joshualitt9bd2daf2015-04-17 09:30:06 -07001973 // set up any flags
bsalomon265697d2015-07-22 10:17:26 -07001974 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt9bd2daf2015-04-17 09:30:06 -07001975
1976 // see if we need to create a new effect
bsalomon265697d2015-07-22 10:17:26 -07001977 if (isLCD) {
1978 flags |= kUseLCD_DistanceFieldEffectFlag;
1979 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
1980 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1981
joshualitt9bd2daf2015-04-17 09:30:06 -07001982 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1983
1984 float redCorrection =
1985 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1986 float greenCorrection =
1987 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1988 float blueCorrection =
1989 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1990 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1991 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1992 greenCorrection,
1993 blueCorrection);
1994
1995 return GrDistanceFieldLCDTextGeoProc::Create(color,
1996 viewMatrix,
1997 texture,
1998 params,
1999 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07002000 flags,
2001 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002002 } else {
2003 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07002004#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07002005 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07002006 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
2007 return GrDistanceFieldA8TextGeoProc::Create(color,
2008 viewMatrix,
2009 texture,
2010 params,
2011 correction,
joshualittb8c241a2015-05-19 08:23:30 -07002012 flags,
2013 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002014#else
2015 return GrDistanceFieldA8TextGeoProc::Create(color,
2016 viewMatrix,
2017 texture,
2018 params,
joshualittb8c241a2015-05-19 08:23:30 -07002019 flags,
2020 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002021#endif
2022 }
2023
2024 }
2025
joshualitt1d89e8d2015-04-01 12:40:54 -07002026 struct BatchTracker {
2027 GrColor fColor;
2028 SkMatrix fViewMatrix;
2029 bool fUsesLocalCoords;
2030 bool fColorIgnored;
2031 bool fCoverageIgnored;
2032 int fNumGlyphs;
2033 };
2034
2035 BatchTracker fBatch;
bsalomond602f4d2015-07-27 06:12:01 -07002036 // The minimum number of Geometry we will try to allocate.
2037 enum { kMinGeometryAllocated = 4 };
2038 SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
2039 int fGeoCount;
bsalomon265697d2015-07-22 10:17:26 -07002040
2041 enum MaskType {
2042 kGrayscaleCoverageMask_MaskType,
2043 kLCDCoverageMask_MaskType,
2044 kColorBitmapMask_MaskType,
2045 kGrayscaleDistanceField_MaskType,
2046 kLCDDistanceField_MaskType,
2047 } fMaskType;
2048 bool fUseBGR; // fold this into the enum?
2049
joshualitt1d89e8d2015-04-01 12:40:54 -07002050 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002051
2052 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002053 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002054 SkColor fFilteredColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07002055};
2056
robertphillips2334fb62015-06-17 05:43:33 -07002057void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002058 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002059 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2060 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2061 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002062
joshualitt9a27e632015-04-06 10:53:36 -07002063 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2064 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002065
joshualitt9a27e632015-04-06 10:53:36 -07002066 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002067
joshualitt9a27e632015-04-06 10:53:36 -07002068 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2069 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002070 }
2071
robertphillipsfcf78292015-06-19 11:49:52 -07002072 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002073
2074 switch (it.positioning()) {
2075 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002076 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002077 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002078 textLen, x + offset.x(), y + offset.y(), clipBounds);
2079 break;
2080 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002081 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002082 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002083 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2084 clipBounds);
2085 break;
2086 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002087 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002088 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002089 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2090 break;
2091 }
2092}
2093
bsalomonabd30f52015-08-13 13:34:48 -07002094inline GrDrawBatch*
joshualitt374b2f72015-07-21 08:05:03 -07002095GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info,
joshualitt79dfb2b2015-05-11 08:58:08 -07002096 int glyphCount, int run, int subRun,
2097 GrColor color, SkScalar transX, SkScalar transY,
2098 const SkPaint& skPaint) {
2099 GrMaskFormat format = info.fMaskFormat;
2100 GrColor subRunColor;
2101 if (kARGB_GrMaskFormat == format) {
2102 uint8_t paintAlpha = skPaint.getAlpha();
2103 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2104 } else {
2105 subRunColor = color;
2106 }
2107
bsalomon265697d2015-07-22 10:17:26 -07002108 TextBatch* batch;
joshualitt79dfb2b2015-05-11 08:58:08 -07002109 if (info.fDrawAsDistanceFields) {
2110 SkColor filteredColor;
2111 SkColorFilter* colorFilter = skPaint.getColorFilter();
2112 if (colorFilter) {
2113 filteredColor = colorFilter->filterColor(skPaint.getColor());
2114 } else {
2115 filteredColor = skPaint.getColor();
2116 }
robertphillipsfcf78292015-06-19 11:49:52 -07002117 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
bsalomon265697d2015-07-22 10:17:26 -07002118 batch = TextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
2119 fDistanceAdjustTable, filteredColor,
2120 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002121 } else {
bsalomon265697d2015-07-22 10:17:26 -07002122 batch = TextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
joshualitt79dfb2b2015-05-11 08:58:08 -07002123 }
bsalomon265697d2015-07-22 10:17:26 -07002124 TextBatch::Geometry& geometry = batch->geometry();
joshualitt79dfb2b2015-05-11 08:58:08 -07002125 geometry.fBlob = SkRef(cacheBlob);
2126 geometry.fRun = run;
2127 geometry.fSubRun = subRun;
2128 geometry.fColor = subRunColor;
2129 geometry.fTransX = transX;
2130 geometry.fTransY = transY;
2131 batch->init();
2132
2133 return batch;
2134}
2135
robertphillips2334fb62015-06-17 05:43:33 -07002136inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt374b2f72015-07-21 08:05:03 -07002137 GrAtlasTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002138 SkScalar transX, SkScalar transY,
2139 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002140 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2141 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2142 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2143 if (0 == glyphCount) {
2144 continue;
2145 }
2146
bsalomonabd30f52015-08-13 13:34:48 -07002147 SkAutoTUnref<GrDrawBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2148 subRun, color, transX, transY,
2149 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002150 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002151 }
2152}
2153
joshualitt374b2f72015-07-21 08:05:03 -07002154inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002155 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002156 SkScalar transX, SkScalar transY,
2157 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002158 if (!cacheBlob->fBigGlyphs.count()) {
2159 return;
2160 }
2161
joshualitt9a27e632015-04-06 10:53:36 -07002162 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt374b2f72015-07-21 08:05:03 -07002163 GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002164 bigGlyph.fVx += transX;
2165 bigGlyph.fVy += transY;
joshualitt0fe04a22015-08-25 12:05:50 -07002166 SkMatrix ctm;
2167 ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
2168 ctm.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2169 if (bigGlyph.fApplyVM) {
2170 ctm.postConcat(cacheBlob->fViewMatrix);
2171 }
joshualittfc072562015-05-13 12:15:06 -07002172
robertphillips2334fb62015-06-17 05:43:33 -07002173 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
joshualitt0fe04a22015-08-25 12:05:50 -07002174 skPaint, ctm, nullptr, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002175 }
2176}
joshualitt9a27e632015-04-06 10:53:36 -07002177
robertphillips2334fb62015-06-17 05:43:33 -07002178void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt374b2f72015-07-21 08:05:03 -07002179 GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002180 GrRenderTarget* rt,
2181 const SkPaint& skPaint,
2182 const GrPaint& grPaint,
2183 SkDrawFilter* drawFilter,
2184 const GrClip& clip,
2185 const SkMatrix& viewMatrix,
2186 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002187 SkScalar x, SkScalar y,
2188 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002189 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2190 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002191 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002192
2193 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002194
2195 SkTextBlob::RunIterator it(blob);
2196 for (int run = 0; !it.done(); it.next(), run++) {
2197 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002198 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002199 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002200 continue;
2201 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002202 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002203 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002204 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002205 }
2206
2207 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002208 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002209}
2210
joshualitt374b2f72015-07-21 08:05:03 -07002211void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002212 GrRenderTarget* rt,
2213 const SkPaint& skPaint,
2214 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002215 const GrClip& clip,
2216 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002217 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002218
2219 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002220 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillips2334fb62015-06-17 05:43:33 -07002221 this->flushRun(&pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002222 }
2223
2224 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002225 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002226}
joshualitt79dfb2b2015-05-11 08:58:08 -07002227
2228///////////////////////////////////////////////////////////////////////////////////////////////////
2229
2230#ifdef GR_TEST_UTILS
2231
bsalomonabd30f52015-08-13 13:34:48 -07002232DRAW_BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002233 static uint32_t gContextID = SK_InvalidGenID;
halcanary96fcdcc2015-08-27 07:41:13 -07002234 static GrAtlasTextContext* gTextContext = nullptr;
robertphillipsfcf78292015-06-19 11:49:52 -07002235 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002236
2237 if (context->uniqueID() != gContextID) {
2238 gContextID = context->uniqueID();
halcanary385fe4d2015-08-26 13:07:48 -07002239 delete gTextContext;
robertphillips2334fb62015-06-17 05:43:33 -07002240
joshualitt79dfb2b2015-05-11 08:58:08 -07002241 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2242 // because we don't really want to have a gpu device here.
2243 // We enable distance fields by twiddling a knob on the paint
robertphillipsfcf78292015-06-19 11:49:52 -07002244 GrDrawContext* drawContext = context->drawContext(&gSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -07002245
robertphillipsfcf78292015-06-19 11:49:52 -07002246 gTextContext = GrAtlasTextContext::Create(context, drawContext, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002247 }
2248
2249 // create dummy render target
2250 GrSurfaceDesc desc;
2251 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2252 desc.fWidth = 1024;
2253 desc.fHeight = 1024;
2254 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002255 desc.fSampleCnt = 0;
halcanary96fcdcc2015-08-27 07:41:13 -07002256 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, nullptr, 0));
joshualitt79dfb2b2015-05-11 08:58:08 -07002257 SkASSERT(texture);
halcanary96fcdcc2015-08-27 07:41:13 -07002258 SkASSERT(nullptr != texture->asRenderTarget());
joshualitt79dfb2b2015-05-11 08:58:08 -07002259 GrRenderTarget* rt = texture->asRenderTarget();
2260
2261 // Setup dummy SkPaint / GrPaint
2262 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002263 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002264 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002265 skPaint.setColor(color);
2266 skPaint.setLCDRenderText(random->nextBool());
2267 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2268 skPaint.setSubpixelText(random->nextBool());
2269
2270 GrPaint grPaint;
2271 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2272 SkFAIL("couldn't convert paint\n");
2273 }
2274
2275 const char* text = "The quick brown fox jumps over the lazy dog.";
2276 int textLen = (int)strlen(text);
2277
2278 // Setup clip
2279 GrClip clip;
2280 SkIRect noClip = SkIRect::MakeLargest();
2281
2282 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2283 // intend to test the batch with this unit test, that is okay.
joshualitt374b2f72015-07-21 08:05:03 -07002284 SkAutoTUnref<GrAtlasTextBlob> blob(
joshualitt79dfb2b2015-05-11 08:58:08 -07002285 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2286 static_cast<size_t>(textLen), 0, 0, noClip));
2287
2288 SkScalar transX = static_cast<SkScalar>(random->nextU());
2289 SkScalar transY = static_cast<SkScalar>(random->nextU());
joshualitt374b2f72015-07-21 08:05:03 -07002290 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
joshualitt79dfb2b2015-05-11 08:58:08 -07002291 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2292}
2293
2294#endif