blob: f33e2f966cb633794c45d04bea8d772f402d94ae [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"
10#include "GrBatchTarget.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"
17#include "GrIndexBuffer.h"
bsalomoned0bcad2015-05-04 10:36:42 -070018#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070019#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070020#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070021#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070022#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070023
24#include "SkAutoKern.h"
25#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070026#include "SkColorFilter.h"
27#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070028#include "SkDraw.h"
29#include "SkDrawFilter.h"
30#include "SkDrawProcs.h"
31#include "SkGlyphCache.h"
32#include "SkGpuDevice.h"
33#include "SkGr.h"
34#include "SkPath.h"
35#include "SkRTConf.h"
36#include "SkStrokeRec.h"
37#include "SkTextBlob.h"
38#include "SkTextMapStateProc.h"
39
joshualitt74417822015-08-07 11:42:16 -070040#include "batches/GrBatch.h"
41
joshualitt1d89e8d2015-04-01 12:40:54 -070042#include "effects/GrBitmapTextGeoProc.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070043#include "effects/GrDistanceFieldGeoProc.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070044
45namespace {
46static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
47
48// position + local coord
49static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
50
51static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
52
joshualitt9bd2daf2015-04-17 09:30:06 -070053static const int kMinDFFontSize = 18;
54static const int kSmallDFFontSize = 32;
55static const int kSmallDFFontLimit = 32;
56static const int kMediumDFFontSize = 72;
57static const int kMediumDFFontLimit = 72;
58static const int kLargeDFFontSize = 162;
jvanverth97c595f2015-06-19 11:06:28 -070059#ifdef SK_BUILD_FOR_ANDROID
60static const int kLargeDFFontLimit = 384;
61#else
joshualitta7c63892015-04-21 13:24:37 -070062static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
jvanverth97c595f2015-06-19 11:06:28 -070063#endif
joshualitt9bd2daf2015-04-17 09:30:06 -070064
65SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
66static const int kDistanceAdjustLumShift = 5;
67
joshualitt1d89e8d2015-04-01 12:40:54 -070068static const int kVerticesPerGlyph = 4;
69static const int kIndicesPerGlyph = 6;
70
71static size_t get_vertex_stride(GrMaskFormat maskFormat) {
72 switch (maskFormat) {
73 case kA8_GrMaskFormat:
74 return kGrayTextVASize;
75 case kARGB_GrMaskFormat:
76 return kColorTextVASize;
77 default:
78 return kLCDTextVASize;
79 }
80}
81
joshualitt9bd2daf2015-04-17 09:30:06 -070082static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
83 SkASSERT(maskFormat == kA8_GrMaskFormat);
84 if (useLCDText) {
85 return kLCDTextVASize;
86 } else {
87 return kGrayTextVASize;
88 }
89}
90
91static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
92 unsigned r = SkColorGetR(c);
93 unsigned g = SkColorGetG(c);
94 unsigned b = SkColorGetB(c);
95 return GrColorPackRGBA(r, g, b, 0xff);
96}
97
joshualitt1d89e8d2015-04-01 12:40:54 -070098};
99
joshualittdbd35932015-04-02 09:19:04 -0700100GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700101 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700102 const SkSurfaceProps& surfaceProps)
103 : INHERITED(context, drawContext, surfaceProps)
robertphillips9fc82752015-06-19 04:46:45 -0700104 , fDistanceAdjustTable(SkNEW(DistanceAdjustTable)) {
joshualittb7133be2015-04-08 09:08:31 -0700105 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
106 // vertexStride
107 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
108 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700109 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700110 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700111}
112
robertphillips9fc82752015-06-19 04:46:45 -0700113void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700114
115 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
116 // text. The mask gamma hack is based off of guessing what the blend color is going to
117 // be, and adjusting the mask so that when run through the linear blend will
118 // produce the value closest to the desired result. However, in practice this means
119 // that the 'adjusted' mask is just increasing or decreasing the coverage of
120 // the mask depending on what it is thought it will blit against. For black (on
121 // assumed white) this means that coverages are decreased (on a curve). For white (on
122 // assumed black) this means that coverages are increased (on a a curve). At
123 // middle (perceptual) gray (which could be blit against anything) the coverages
124 // remain the same.
125 //
126 // The idea here is that instead of determining the initial (real) coverage and
127 // then adjusting that coverage, we determine an adjusted coverage directly by
128 // essentially manipulating the geometry (in this case, the distance to the glyph
129 // edge). So for black (on assumed white) this thins a bit; for white (on
130 // assumed black) this fake bolds the geometry a bit.
131 //
132 // The distance adjustment is calculated by determining the actual coverage value which
133 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
134 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
135 // actual edge. So by subtracting this distance adjustment and computing without the
136 // the coverage adjustment we should get 0.5 coverage at the same point.
137 //
138 // This has several implications:
139 // For non-gray lcd smoothed text, each subpixel essentially is using a
140 // slightly different geometry.
141 //
142 // For black (on assumed white) this may not cover some pixels which were
143 // previously covered; however those pixels would have been only slightly
144 // covered and that slight coverage would have been decreased anyway. Also, some pixels
145 // which were previously fully covered may no longer be fully covered.
146 //
147 // For white (on assumed black) this may cover some pixels which weren't
148 // previously covered at all.
149
150 int width, height;
151 size_t size;
152
153#ifdef SK_GAMMA_CONTRAST
154 SkScalar contrast = SK_GAMMA_CONTRAST;
155#else
156 SkScalar contrast = 0.5f;
157#endif
robertphillips9fc82752015-06-19 04:46:45 -0700158 SkScalar paintGamma = SK_GAMMA_EXPONENT;
159 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700160
161 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
162 &width, &height);
163
164 SkASSERT(kExpectedDistanceAdjustTableSize == height);
165 fTable = SkNEW_ARRAY(SkScalar, height);
166
167 SkAutoTArray<uint8_t> data((int)size);
168 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
169
170 // find the inverse points where we cross 0.5
171 // binsearch might be better, but we only need to do this once on creation
172 for (int row = 0; row < height; ++row) {
173 uint8_t* rowPtr = data.get() + row*width;
174 for (int col = 0; col < width - 1; ++col) {
175 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
176 // compute point where a mask value will give us a result of 0.5
177 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
178 float borderAlpha = (col + interp) / 255.f;
179
180 // compute t value for that alpha
181 // this is an approximate inverse for smoothstep()
182 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
183
184 // compute distance which gives us that t value
185 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
186 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
187
188 fTable[row] = d;
189 break;
190 }
191 }
192 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700193}
194
joshualittdbd35932015-04-02 09:19:04 -0700195GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700196 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700197 const SkSurfaceProps& surfaceProps) {
198 return SkNEW_ARGS(GrAtlasTextContext, (context, drawContext, surfaceProps));
joshualitt1d89e8d2015-04-01 12:40:54 -0700199}
200
joshualittdbd35932015-04-02 09:19:04 -0700201bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
202 const GrClip&,
203 const GrPaint&,
204 const SkPaint& skPaint,
205 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700206 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
207 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700208}
209
joshualitt9e36c1a2015-04-14 12:17:27 -0700210GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
211 GrColor canonicalColor = paint.computeLuminanceColor();
212 if (lcd) {
213 // This is the correct computation, but there are tons of cases where LCD can be overridden.
214 // For now we just regenerate if any run in a textblob has LCD.
215 // TODO figure out where all of these overrides are and see if we can incorporate that logic
216 // at a higher level *OR* use sRGB
217 SkASSERT(false);
218 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
219 } else {
220 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
221 // gamma corrected masks anyways, nor color
222 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
223 SkColorGetG(canonicalColor),
224 SkColorGetB(canonicalColor));
225 // reduce to our finite number of bits
226 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
227 }
228 return canonicalColor;
229}
230
231// TODO if this function ever shows up in profiling, then we can compute this value when the
232// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
233// run so this is not a big deal to compute here.
234bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
235 SkTextBlob::RunIterator it(blob);
236 for (; !it.done(); it.next()) {
237 if (it.isLCD()) {
238 return true;
239 }
240 }
241 return false;
242}
243
joshualitt2a0e9f32015-04-13 06:12:21 -0700244bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
joshualitt374b2f72015-07-21 08:05:03 -0700245 const GrAtlasTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700246 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700247 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700248 // If we have LCD text then our canonical color will be set to transparent, in this case we have
249 // to regenerate the blob on any color change
250 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
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) {
joshualitt53b5f442015-04-13 06:33:59 -0700399 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700400 // We have to remake the blob because changes may invalidate our masks.
401 // TODO we could probably get away reuse most of the time if the pointer is unique,
402 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700403 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700404 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
405 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700406 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700407 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700408 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700409 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
joshualitt7e7b5c52015-07-21 12:56:56 -0700410 // offsets. Note, we offset the vertex bounds right before flushing
joshualitt2a0e9f32015-04-13 06:12:21 -0700411 cacheBlob->fViewMatrix = viewMatrix;
412 cacheBlob->fX = x;
413 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700414 fCache->makeMRU(cacheBlob);
joshualitt259fbf12015-07-21 11:39:34 -0700415#ifdef CACHE_SANITY_CHECK
416 {
417 int glyphCount = 0;
418 int runCount = 0;
419 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
420 SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyphCount, runCount,
421 kGrayTextVASize));
422 GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint);
423 this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix,
424 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
425 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
426 }
427
428#endif
joshualitt1d89e8d2015-04-01 12:40:54 -0700429 }
430 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700431 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700432 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
433 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700434 } else {
435 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
436 }
robertphillips9c240a12015-05-28 07:45:59 -0700437 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700438 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700439 }
440
robertphillips2334fb62015-06-17 05:43:33 -0700441 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700442 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700443}
444
joshualitt9bd2daf2015-04-17 09:30:06 -0700445inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
446 const SkMatrix& viewMatrix) {
447 // TODO: support perspective (need getMaxScale replacement)
448 if (viewMatrix.hasPerspective()) {
449 return false;
450 }
451
452 SkScalar maxScale = viewMatrix.getMaxScale();
453 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
454 // Hinted text looks far better at small resolutions
455 // Scaling up beyond 2x yields undesireable artifacts
jvanverth34d72882015-06-22 08:08:09 -0700456 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700457 return false;
458 }
459
robertphillipsfcf78292015-06-19 11:49:52 -0700460 bool useDFT = fSurfaceProps.isUseDistanceFieldFonts();
robertphillipsbcd7ab52015-06-18 05:27:18 -0700461#if SK_FORCE_DISTANCE_FIELD_TEXT
462 useDFT = true;
463#endif
464
jvanverth4854d132015-06-22 06:46:56 -0700465 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700466 return false;
467 }
468
469 // rasterizers and mask filters modify alpha, which doesn't
470 // translate well to distance
471 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700472 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700473 return false;
474 }
475
476 // TODO: add some stroking support
477 if (skPaint.getStyle() != SkPaint::kFill_Style) {
478 return false;
479 }
480
481 return true;
482}
483
joshualitt374b2f72015-07-21 08:05:03 -0700484void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700485 const SkPaint& skPaint, GrColor color,
486 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700487 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700488 SkDrawFilter* drawFilter, const SkIRect& clipRect,
489 GrRenderTarget* rt, const GrClip& clip,
490 const GrPaint& paint) {
joshualitt259fbf12015-07-21 11:39:34 -0700491 cacheBlob->fPaintColor = skPaint.getColor();
joshualitt1d89e8d2015-04-01 12:40:54 -0700492 cacheBlob->fViewMatrix = viewMatrix;
493 cacheBlob->fX = x;
494 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700495
496 // Regenerate textblob
497 SkPaint runPaint = skPaint;
498 SkTextBlob::RunIterator it(blob);
499 for (int run = 0; !it.done(); it.next(), run++) {
500 int glyphCount = it.glyphCount();
501 size_t textLen = glyphCount * sizeof(uint16_t);
502 const SkPoint& offset = it.offset();
503 // applyFontToPaint() always overwrites the exact same attributes,
504 // so it is safe to not re-seed the paint for this reason.
505 it.applyFontToPaint(&runPaint);
506
507 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
508 // A false return from filter() means we should abort the current draw.
509 runPaint = skPaint;
510 continue;
511 }
512
robertphillipsfcf78292015-06-19 11:49:52 -0700513 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700514
joshualitt1d89e8d2015-04-01 12:40:54 -0700515 // setup vertex / glyphIndex for the new run
516 if (run > 0) {
517 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
518 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
519
520 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
521 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
522
523 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
524 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
525 }
526
joshualittfcfb9fc2015-04-21 07:35:10 -0700527 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
528 cacheBlob->setHasDistanceField();
529 SkPaint dfPaint = runPaint;
530 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700531 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700532 Run& runIdx = cacheBlob->fRuns[run];
533 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
534 subRun.fUseLCDText = runPaint.isLCDRenderText();
535 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700536
joshualittfcfb9fc2015-04-21 07:35:10 -0700537 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
538
539 SkTDArray<char> fallbackTxt;
540 SkTDArray<SkScalar> fallbackPos;
541 SkPoint dfOffset;
542 int scalarsPerPosition = 2;
543 switch (it.positioning()) {
544 case SkTextBlob::kDefault_Positioning: {
545 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
546 (const char *)it.glyphs(), textLen,
547 x + offset.x(), y + offset.y(), clipRect, textRatio,
548 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
549 break;
550 }
551 case SkTextBlob::kHorizontal_Positioning: {
552 scalarsPerPosition = 1;
553 dfOffset = SkPoint::Make(x, y + offset.y());
554 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
555 (const char*)it.glyphs(), textLen, it.pos(),
556 scalarsPerPosition, dfOffset, clipRect, textRatio,
557 &fallbackTxt, &fallbackPos);
558 break;
559 }
560 case SkTextBlob::kFull_Positioning: {
561 dfOffset = SkPoint::Make(x, y);
562 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
563 (const char*)it.glyphs(), textLen, it.pos(),
564 scalarsPerPosition, dfOffset, clipRect, textRatio,
565 &fallbackTxt, &fallbackPos);
566 break;
567 }
568 }
569 if (fallbackTxt.count()) {
570 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
571 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
572 clipRect);
573 }
574
575 SkGlyphCache::AttachCache(cache);
576 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
577 cacheBlob->fRuns[run].fDrawAsPaths = true;
578 } else {
579 cacheBlob->setHasBitmap();
580 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
581 false);
582 switch (it.positioning()) {
583 case SkTextBlob::kDefault_Positioning:
584 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
585 (const char *)it.glyphs(), textLen,
586 x + offset.x(), y + offset.y(), clipRect);
587 break;
588 case SkTextBlob::kHorizontal_Positioning:
589 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
590 (const char*)it.glyphs(), textLen, it.pos(), 1,
591 SkPoint::Make(x, y + offset.y()), clipRect);
592 break;
593 case SkTextBlob::kFull_Positioning:
594 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
595 (const char*)it.glyphs(), textLen, it.pos(), 2,
596 SkPoint::Make(x, y), clipRect);
597 break;
598 }
599 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700600 }
601
602 if (drawFilter) {
603 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
604 runPaint = skPaint;
605 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700606 }
607}
608
joshualitt374b2f72015-07-21 08:05:03 -0700609inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
joshualitt64c99cc2015-04-21 09:43:03 -0700610 SkPaint* skPaint,
611 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700612 const SkMatrix& viewMatrix) {
613 // getMaxScale doesn't support perspective, so neither do we at the moment
614 SkASSERT(!viewMatrix.hasPerspective());
615 SkScalar maxScale = viewMatrix.getMaxScale();
616 SkScalar textSize = skPaint->getTextSize();
617 SkScalar scaledTextSize = textSize;
618 // if we have non-unity scale, we need to choose our base text size
619 // based on the SkPaint's text size multiplied by the max scale factor
620 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
621 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
622 scaledTextSize *= maxScale;
623 }
624
joshualitt64c99cc2015-04-21 09:43:03 -0700625 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
626 // and ceiling. A scale outside of this range would require regenerating the distance fields
627 SkScalar dfMaskScaleFloor;
628 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700629 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700630 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700631 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700632 *textRatio = textSize / kSmallDFFontSize;
633 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
634 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700635 dfMaskScaleFloor = kSmallDFFontLimit;
636 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700637 *textRatio = textSize / kMediumDFFontSize;
638 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
639 } else {
joshualitta7c63892015-04-21 13:24:37 -0700640 dfMaskScaleFloor = kMediumDFFontLimit;
641 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700642 *textRatio = textSize / kLargeDFFontSize;
643 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
644 }
645
joshualitt64c99cc2015-04-21 09:43:03 -0700646 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
647 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
648 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
649 // tolerate before we'd have to move to a large mip size. When we actually test these values
650 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
651 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
652 // level)
joshualitta7c63892015-04-21 13:24:37 -0700653 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700654 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
655 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
656
joshualitt9bd2daf2015-04-17 09:30:06 -0700657 skPaint->setLCDRenderText(false);
658 skPaint->setAutohinted(false);
659 skPaint->setHinting(SkPaint::kNormal_Hinting);
660 skPaint->setSubpixelText(true);
661}
662
joshualitt374b2f72015-07-21 08:05:03 -0700663inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700664 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700665 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700666 const GrPaint& paint,
667 const SkPaint& skPaint,
668 const SkMatrix& viewMatrix,
669 const SkTDArray<char>& fallbackTxt,
670 const SkTDArray<SkScalar>& fallbackPos,
671 int scalarsPerPosition,
672 const SkPoint& offset,
673 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700674 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700675 blob->setHasBitmap();
676 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700677 // Push back a new subrun to fill and set the override descriptor
678 run.push_back();
679 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
680 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700681 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700682 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700683 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700684 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700685 fallbackTxt.begin(), fallbackTxt.count(),
686 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
687 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700688}
689
joshualitt374b2f72015-07-21 08:05:03 -0700690inline GrAtlasTextBlob*
joshualitt9bd2daf2015-04-17 09:30:06 -0700691GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
692 const SkMatrix& viewMatrix, SkGlyphCache** cache,
693 SkPaint* dfPaint, SkScalar* textRatio) {
joshualitt374b2f72015-07-21 08:05:03 -0700694 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700695
696 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700697 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700698 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700699 Run& run = blob->fRuns[0];
700 PerSubRunInfo& subRun = run.fSubRunInfo.back();
701 subRun.fUseLCDText = origPaint.isLCDRenderText();
702 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700703
704 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
705 return blob;
706}
707
joshualitt374b2f72015-07-21 08:05:03 -0700708inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700709GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
710 const GrPaint& paint, const SkPaint& skPaint,
711 const SkMatrix& viewMatrix,
712 const char text[], size_t byteLength,
713 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700714 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700715 SkIRect clipRect;
716 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
717
joshualitt374b2f72015-07-21 08:05:03 -0700718 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700719 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
720 SkPaint dfPaint;
721 SkScalar textRatio;
722 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700723 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700724
joshualitt9bd2daf2015-04-17 09:30:06 -0700725 SkTDArray<char> fallbackTxt;
726 SkTDArray<SkScalar> fallbackPos;
727 SkPoint offset;
728 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
729 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
730 &offset, skPaint);
731 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700732 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700733 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700734 fallbackPos, 2, offset, clipRect);
735 }
736 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700737 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700738 blob->fViewMatrix = viewMatrix;
739
740 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
741 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
742 byteLength, x, y, clipRect);
743 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700744 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700745 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700746}
747
joshualitt374b2f72015-07-21 08:05:03 -0700748inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700749GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
750 const GrPaint& paint, const SkPaint& skPaint,
751 const SkMatrix& viewMatrix,
752 const char text[], size_t byteLength,
753 const SkScalar pos[], int scalarsPerPosition,
754 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700755 int glyphCount = skPaint.countText(text, byteLength);
756
757 SkIRect clipRect;
758 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
759
joshualitt374b2f72015-07-21 08:05:03 -0700760 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700761 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
762 SkPaint dfPaint;
763 SkScalar textRatio;
764 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700765 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700766
767 SkTDArray<char> fallbackTxt;
768 SkTDArray<SkScalar> fallbackPos;
769 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
770 byteLength, pos, scalarsPerPosition, offset, clipRect,
771 textRatio, &fallbackTxt, &fallbackPos);
772 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700773 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700774 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700775 fallbackPos, scalarsPerPosition, offset, clipRect);
776 }
777 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700778 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700779 blob->fViewMatrix = viewMatrix;
780 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
781 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
782 byteLength, pos, scalarsPerPosition, offset, clipRect);
783 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700784 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700785 return blob;
786}
787
robertphillips2334fb62015-06-17 05:43:33 -0700788void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700789 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700790 const GrPaint& paint, const SkPaint& skPaint,
791 const SkMatrix& viewMatrix,
792 const char text[], size_t byteLength,
793 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700794 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700795 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
796 text, byteLength, x, y, regionClipBounds));
797 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700798}
799
robertphillips2334fb62015-06-17 05:43:33 -0700800void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700801 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700802 const GrPaint& paint, const SkPaint& skPaint,
803 const SkMatrix& viewMatrix,
804 const char text[], size_t byteLength,
805 const SkScalar pos[], int scalarsPerPosition,
806 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700807 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700808 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
809 text, byteLength,
810 pos, scalarsPerPosition,
811 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700812
robertphillips2334fb62015-06-17 05:43:33 -0700813 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700814}
815
joshualitt374b2f72015-07-21 08:05:03 -0700816void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700817 SkGlyphCache* cache, const SkPaint& skPaint,
818 GrColor color,
819 const SkMatrix& viewMatrix,
820 const char text[], size_t byteLength,
821 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700822 SkASSERT(byteLength == 0 || text != NULL);
823
824 // nothing to draw
825 if (text == NULL || byteLength == 0) {
826 return;
827 }
828
829 fCurrStrike = NULL;
830 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
831
832 // Get GrFontScaler from cache
833 GrFontScaler* fontScaler = GetGrFontScaler(cache);
834
835 // transform our starting point
836 {
837 SkPoint loc;
838 viewMatrix.mapXY(x, y, &loc);
839 x = loc.fX;
840 y = loc.fY;
841 }
842
843 // need to measure first
844 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
845 SkVector stopVector;
846 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
847
848 SkScalar stopX = stopVector.fX;
849 SkScalar stopY = stopVector.fY;
850
851 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
852 stopX = SkScalarHalf(stopX);
853 stopY = SkScalarHalf(stopY);
854 }
855 x -= stopX;
856 y -= stopY;
857 }
858
859 const char* stop = text + byteLength;
860
861 SkAutoKern autokern;
862
863 SkFixed fxMask = ~0;
864 SkFixed fyMask = ~0;
865 SkScalar halfSampleX, halfSampleY;
866 if (cache->isSubpixel()) {
867 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
868 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
869 if (kX_SkAxisAlignment == baseline) {
870 fyMask = 0;
871 halfSampleY = SK_ScalarHalf;
872 } else if (kY_SkAxisAlignment == baseline) {
873 fxMask = 0;
874 halfSampleX = SK_ScalarHalf;
875 }
876 } else {
877 halfSampleX = halfSampleY = SK_ScalarHalf;
878 }
879
880 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
881 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
882
883 while (text < stop) {
884 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
885
886 fx += autokern.adjust(glyph);
887
888 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700889 this->bmpAppendGlyph(blob,
890 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700891 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700892 Sk48Dot16FloorToInt(fx),
893 Sk48Dot16FloorToInt(fy),
894 color,
895 fontScaler,
896 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700897 }
898
899 fx += glyph.fAdvanceX;
900 fy += glyph.fAdvanceY;
901 }
902}
903
joshualitt374b2f72015-07-21 08:05:03 -0700904void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700905 SkGlyphCache* cache, const SkPaint& skPaint,
906 GrColor color,
907 const SkMatrix& viewMatrix,
908 const char text[], size_t byteLength,
909 const SkScalar pos[], int scalarsPerPosition,
910 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700911 SkASSERT(byteLength == 0 || text != NULL);
912 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
913
914 // nothing to draw
915 if (text == NULL || byteLength == 0) {
916 return;
917 }
918
919 fCurrStrike = NULL;
920 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
921
922 // Get GrFontScaler from cache
923 GrFontScaler* fontScaler = GetGrFontScaler(cache);
924
925 const char* stop = text + byteLength;
926 SkTextAlignProc alignProc(skPaint.getTextAlign());
927 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
928
929 if (cache->isSubpixel()) {
930 // maybe we should skip the rounding if linearText is set
931 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
932
933 SkFixed fxMask = ~0;
934 SkFixed fyMask = ~0;
935 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
936 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
937 if (kX_SkAxisAlignment == baseline) {
938 fyMask = 0;
939 halfSampleY = SK_ScalarHalf;
940 } else if (kY_SkAxisAlignment == baseline) {
941 fxMask = 0;
942 halfSampleX = SK_ScalarHalf;
943 }
944
945 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
946 while (text < stop) {
947 SkPoint tmsLoc;
948 tmsProc(pos, &tmsLoc);
949 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
950 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
951
952 const SkGlyph& glyph = glyphCacheProc(cache, &text,
953 fx & fxMask, fy & fyMask);
954
955 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700956 this->bmpAppendGlyph(blob,
957 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700958 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700959 Sk48Dot16FloorToInt(fx),
960 Sk48Dot16FloorToInt(fy),
961 color,
962 fontScaler,
963 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700964 }
965 pos += scalarsPerPosition;
966 }
967 } else {
968 while (text < stop) {
969 const char* currentText = text;
970 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
971
972 if (metricGlyph.fWidth) {
973 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
974 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
975 SkPoint tmsLoc;
976 tmsProc(pos, &tmsLoc);
977 SkPoint alignLoc;
978 alignProc(tmsLoc, metricGlyph, &alignLoc);
979
980 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
981 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
982
983 // have to call again, now that we've been "aligned"
984 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
985 fx & fxMask, fy & fyMask);
986 // the assumption is that the metrics haven't changed
987 SkASSERT(prevAdvX == glyph.fAdvanceX);
988 SkASSERT(prevAdvY == glyph.fAdvanceY);
989 SkASSERT(glyph.fWidth);
990
joshualitt9bd2daf2015-04-17 09:30:06 -0700991 this->bmpAppendGlyph(blob,
992 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700993 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700994 Sk48Dot16FloorToInt(fx),
995 Sk48Dot16FloorToInt(fy),
996 color,
997 fontScaler,
998 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700999 }
1000 pos += scalarsPerPosition;
1001 }
1002 }
1003 } else { // not subpixel
1004
1005 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1006 while (text < stop) {
1007 // the last 2 parameters are ignored
1008 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1009
1010 if (glyph.fWidth) {
1011 SkPoint tmsLoc;
1012 tmsProc(pos, &tmsLoc);
1013
1014 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1015 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001016 this->bmpAppendGlyph(blob,
1017 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001018 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001019 Sk48Dot16FloorToInt(fx),
1020 Sk48Dot16FloorToInt(fy),
1021 color,
1022 fontScaler,
1023 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001024 }
1025 pos += scalarsPerPosition;
1026 }
1027 } else {
1028 while (text < stop) {
1029 // the last 2 parameters are ignored
1030 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1031
1032 if (glyph.fWidth) {
1033 SkPoint tmsLoc;
1034 tmsProc(pos, &tmsLoc);
1035
1036 SkPoint alignLoc;
1037 alignProc(tmsLoc, glyph, &alignLoc);
1038
1039 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1040 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001041 this->bmpAppendGlyph(blob,
1042 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001043 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001044 Sk48Dot16FloorToInt(fx),
1045 Sk48Dot16FloorToInt(fy),
1046 color,
1047 fontScaler,
1048 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001049 }
1050 pos += scalarsPerPosition;
1051 }
1052 }
1053 }
1054}
1055
joshualitt9bd2daf2015-04-17 09:30:06 -07001056
joshualitt374b2f72015-07-21 08:05:03 -07001057void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001058 SkGlyphCache* cache, const SkPaint& skPaint,
1059 GrColor color,
1060 const SkMatrix& viewMatrix,
1061 const char text[], size_t byteLength,
1062 SkScalar x, SkScalar y, const SkIRect& clipRect,
1063 SkScalar textRatio,
1064 SkTDArray<char>* fallbackTxt,
1065 SkTDArray<SkScalar>* fallbackPos,
1066 SkPoint* offset,
1067 const SkPaint& origPaint) {
1068 SkASSERT(byteLength == 0 || text != NULL);
1069
1070 // nothing to draw
1071 if (text == NULL || byteLength == 0) {
1072 return;
1073 }
1074
1075 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1076 SkAutoDescriptor desc;
robertphillipsfcf78292015-06-19 11:49:52 -07001077 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, NULL, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001078 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1079 desc.getDesc());
1080
1081 SkTArray<SkScalar> positions;
1082
1083 const char* textPtr = text;
1084 SkFixed stopX = 0;
1085 SkFixed stopY = 0;
1086 SkFixed origin = 0;
1087 switch (origPaint.getTextAlign()) {
1088 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1089 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1090 case SkPaint::kLeft_Align: origin = 0; break;
1091 }
1092
1093 SkAutoKern autokern;
1094 const char* stop = text + byteLength;
1095 while (textPtr < stop) {
1096 // don't need x, y here, since all subpixel variants will have the
1097 // same advance
1098 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1099
1100 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1101 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1102
1103 SkFixed height = glyph.fAdvanceY;
1104 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1105
1106 stopX += width;
1107 stopY += height;
1108 }
1109 SkASSERT(textPtr == stop);
1110
1111 // now adjust starting point depending on alignment
1112 SkScalar alignX = SkFixedToScalar(stopX);
1113 SkScalar alignY = SkFixedToScalar(stopY);
1114 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1115 alignX = SkScalarHalf(alignX);
1116 alignY = SkScalarHalf(alignY);
1117 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1118 alignX = 0;
1119 alignY = 0;
1120 }
1121 x -= alignX;
1122 y -= alignY;
1123 *offset = SkPoint::Make(x, y);
1124
1125 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1126 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1127 fallbackPos);
1128 SkGlyphCache::AttachCache(origPaintCache);
1129}
1130
joshualitt374b2f72015-07-21 08:05:03 -07001131void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -07001132 SkGlyphCache* cache, const SkPaint& skPaint,
1133 GrColor color,
1134 const SkMatrix& viewMatrix,
1135 const char text[], size_t byteLength,
1136 const SkScalar pos[], int scalarsPerPosition,
1137 const SkPoint& offset, const SkIRect& clipRect,
1138 SkScalar textRatio,
1139 SkTDArray<char>* fallbackTxt,
1140 SkTDArray<SkScalar>* fallbackPos) {
1141
1142 SkASSERT(byteLength == 0 || text != NULL);
1143 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1144
1145 // nothing to draw
1146 if (text == NULL || byteLength == 0) {
1147 return;
1148 }
1149
1150 fCurrStrike = NULL;
1151
1152 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1153 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1154
1155 const char* stop = text + byteLength;
1156
1157 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1158 while (text < stop) {
1159 const char* lastText = text;
1160 // the last 2 parameters are ignored
1161 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1162
1163 if (glyph.fWidth) {
1164 SkScalar x = offset.x() + pos[0];
1165 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1166
1167 if (!this->dfAppendGlyph(blob,
1168 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001169 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001170 x, y, color, fontScaler, clipRect,
1171 textRatio, viewMatrix)) {
1172 // couldn't append, send to fallback
1173 fallbackTxt->append(SkToInt(text-lastText), lastText);
1174 *fallbackPos->append() = pos[0];
1175 if (2 == scalarsPerPosition) {
1176 *fallbackPos->append() = pos[1];
1177 }
1178 }
1179 }
1180 pos += scalarsPerPosition;
1181 }
1182 } else {
1183 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1184 : SK_Scalar1;
1185 while (text < stop) {
1186 const char* lastText = text;
1187 // the last 2 parameters are ignored
1188 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1189
1190 if (glyph.fWidth) {
1191 SkScalar x = offset.x() + pos[0];
1192 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1193
1194 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1195 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1196
1197 if (!this->dfAppendGlyph(blob,
1198 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001199 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001200 x - advanceX, y - advanceY, color,
1201 fontScaler,
1202 clipRect,
1203 textRatio,
1204 viewMatrix)) {
1205 // couldn't append, send to fallback
1206 fallbackTxt->append(SkToInt(text-lastText), lastText);
1207 *fallbackPos->append() = pos[0];
1208 if (2 == scalarsPerPosition) {
1209 *fallbackPos->append() = pos[1];
1210 }
1211 }
1212 }
1213 pos += scalarsPerPosition;
1214 }
1215 }
1216}
1217
joshualitt374b2f72015-07-21 08:05:03 -07001218void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001219 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001220 int vx, int vy, GrColor color, GrFontScaler* scaler,
1221 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001222 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001223 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001224 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1225 }
1226
joshualitt6c2c2b02015-07-24 10:37:00 -07001227 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1228 skGlyph.getSubXFixed(),
1229 skGlyph.getSubYFixed(),
1230 GrGlyph::kCoverage_MaskStyle);
1231 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001232 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001233 return;
1234 }
1235
1236 int x = vx + glyph->fBounds.fLeft;
1237 int y = vy + glyph->fBounds.fTop;
1238
1239 // keep them as ints until we've done the clip-test
1240 int width = glyph->fBounds.width();
1241 int height = glyph->fBounds.height();
1242
joshualitt2a0e9f32015-04-13 06:12:21 -07001243#if 0
1244 // Not checking the clip bounds might introduce a performance regression. However, its not
1245 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1246 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1247 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1248 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001249 // check if we clipped out
1250 if (clipRect.quickReject(x, y, x + width, y + height)) {
1251 return;
1252 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001253#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001254
1255 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001256 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001257 this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001258 return;
1259 }
1260
joshualitt1d89e8d2015-04-01 12:40:54 -07001261 GrMaskFormat format = glyph->fMaskFormat;
1262
1263 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1264 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittd9f13ae2015-07-24 11:24:31 -07001265 subRun = &run.push_back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001266 subRun->fStrike.reset(SkRef(fCurrStrike));
1267 } else if (!run.fInitialized) {
1268 subRun->fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001269 }
1270
1271 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001272
1273 size_t vertexStride = get_vertex_stride(format);
1274
1275 SkRect r;
1276 r.fLeft = SkIntToScalar(x);
1277 r.fTop = SkIntToScalar(y);
1278 r.fRight = r.fLeft + SkIntToScalar(width);
1279 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001280 subRun->fMaskFormat = format;
1281 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001282 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001283}
joshualitt1d89e8d2015-04-01 12:40:54 -07001284
joshualitt374b2f72015-07-21 08:05:03 -07001285bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001286 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001287 SkScalar sx, SkScalar sy, GrColor color,
1288 GrFontScaler* scaler,
1289 const SkIRect& clipRect,
1290 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001291 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001292 if (!fCurrStrike) {
1293 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1294 }
1295
joshualitt6c2c2b02015-07-24 10:37:00 -07001296 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1297 skGlyph.getSubXFixed(),
1298 skGlyph.getSubYFixed(),
1299 GrGlyph::kDistance_MaskStyle);
1300 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001301 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001302 return true;
1303 }
1304
1305 // fallback to color glyph support
1306 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1307 return false;
1308 }
1309
1310 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1311 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1312 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1313 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1314
1315 SkScalar scale = textRatio;
1316 dx *= scale;
1317 dy *= scale;
1318 width *= scale;
1319 height *= scale;
1320 sx += dx;
1321 sy += dy;
1322 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1323
1324#if 0
1325 // check if we clipped out
1326 SkRect dstRect;
1327 viewMatrix.mapRect(&dstRect, glyphRect);
1328 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1329 SkScalarTruncToInt(dstRect.top()),
1330 SkScalarTruncToInt(dstRect.right()),
1331 SkScalarTruncToInt(dstRect.bottom()))) {
1332 return true;
1333 }
1334#endif
1335
1336 // TODO combine with the above
1337 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001338 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001339 this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001340 return true;
1341 }
1342
joshualitt9bd2daf2015-04-17 09:30:06 -07001343 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001344 if (!run.fInitialized) {
1345 subRun->fStrike.reset(SkRef(fCurrStrike));
1346 }
1347 run.fInitialized = true;
joshualitt9bd2daf2015-04-17 09:30:06 -07001348 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1349 subRun->fMaskFormat = kA8_GrMaskFormat;
1350
1351 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1352
1353 bool useColorVerts = !subRun->fUseLCDText;
1354 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001355 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001356 return true;
1357}
1358
joshualitt374b2f72015-07-21 08:05:03 -07001359inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001360 GrFontScaler* scaler, const SkGlyph& skGlyph,
1361 SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001362 if (NULL == glyph->fPath) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001363 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
1364 if (!glyphPath) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001365 return;
1366 }
joshualitt6c2c2b02015-07-24 10:37:00 -07001367
1368 glyph->fPath = SkNEW_ARGS(SkPath, (*glyphPath));
joshualitt9bd2daf2015-04-17 09:30:06 -07001369 }
joshualitt374b2f72015-07-21 08:05:03 -07001370 blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y));
joshualitt9bd2daf2015-04-17 09:30:06 -07001371}
1372
joshualitt374b2f72015-07-21 08:05:03 -07001373inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run,
joshualitt9bd2daf2015-04-17 09:30:06 -07001374 Run::SubRunInfo* subRun,
1375 const SkRect& positions, GrColor color,
1376 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001377 GrGlyph* glyph) {
1378 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001379 run->fVertexBounds.joinNonEmptyArg(positions);
1380 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001381
1382 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1383
joshualitt9bd2daf2015-04-17 09:30:06 -07001384 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001385 // V0
1386 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1387 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001388 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1389 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001390 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001391
joshualitt010db532015-04-21 10:07:26 -07001392 // V1
1393 position = reinterpret_cast<SkPoint*>(vertex);
1394 position->set(positions.fLeft, positions.fBottom);
1395 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001396 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001397 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001398
joshualitt010db532015-04-21 10:07:26 -07001399 // V2
1400 position = reinterpret_cast<SkPoint*>(vertex);
1401 position->set(positions.fRight, positions.fBottom);
1402 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001403 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001404 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001405
joshualitt010db532015-04-21 10:07:26 -07001406 // V3
1407 position = reinterpret_cast<SkPoint*>(vertex);
1408 position->set(positions.fRight, positions.fTop);
1409 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001410 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001411 } else {
1412 // V0
1413 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1414 position->set(positions.fLeft, positions.fTop);
1415 vertex += vertexStride;
1416
1417 // V1
1418 position = reinterpret_cast<SkPoint*>(vertex);
1419 position->set(positions.fLeft, positions.fBottom);
1420 vertex += vertexStride;
1421
1422 // V2
1423 position = reinterpret_cast<SkPoint*>(vertex);
1424 position->set(positions.fRight, positions.fBottom);
1425 vertex += vertexStride;
1426
1427 // V3
1428 position = reinterpret_cast<SkPoint*>(vertex);
1429 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001430 }
1431
1432 subRun->fGlyphEndIndex++;
1433 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1434}
1435
bsalomon265697d2015-07-22 10:17:26 -07001436class TextBatch : public GrBatch {
joshualitt1d89e8d2015-04-01 12:40:54 -07001437public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001438 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualitt374b2f72015-07-21 08:05:03 -07001439 typedef GrAtlasTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001440 typedef Blob::Run Run;
1441 typedef Run::SubRunInfo TextInfo;
1442 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001443 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001444 int fRun;
1445 int fSubRun;
1446 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001447 SkScalar fTransX;
1448 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001449 };
1450
bsalomon265697d2015-07-22 10:17:26 -07001451 static TextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
joshualittad802c62015-04-15 05:31:57 -07001452 GrBatchFontCache* fontCache) {
bsalomon265697d2015-07-22 10:17:26 -07001453 TextBatch* batch = SkNEW(TextBatch);
1454
1455 batch->initClassID<TextBatch>();
1456 batch->fFontCache = fontCache;
1457 switch (maskFormat) {
1458 case kA8_GrMaskFormat:
1459 batch->fMaskType = kGrayscaleCoverageMask_MaskType;
1460 break;
1461 case kA565_GrMaskFormat:
1462 batch->fMaskType = kLCDCoverageMask_MaskType;
1463 break;
1464 case kARGB_GrMaskFormat:
1465 batch->fMaskType = kColorBitmapMask_MaskType;
1466 break;
1467 }
1468 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001469 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001470 batch->fFilteredColor = 0;
1471 batch->fFontCache = fontCache;
1472 batch->fUseBGR = false;
1473 return batch;
joshualitt1d89e8d2015-04-01 12:40:54 -07001474 }
1475
bsalomon265697d2015-07-22 10:17:26 -07001476 static TextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache,
1477 DistanceAdjustTable* distanceAdjustTable,
1478 SkColor filteredColor, bool isLCD,
1479 bool useBGR) {
1480 TextBatch* batch = SkNEW(TextBatch);
1481 batch->initClassID<TextBatch>();
1482 batch->fFontCache = fontCache;
1483 batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
1484 batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
1485 batch->fFilteredColor = filteredColor;
1486 batch->fUseBGR = useBGR;
1487 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001488 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001489 return batch;
joshualitt9bd2daf2015-04-17 09:30:06 -07001490 }
1491
bsalomon265697d2015-07-22 10:17:26 -07001492 const char* name() const override { return "TextBatch"; }
joshualitt1d89e8d2015-04-01 12:40:54 -07001493
1494 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001495 if (kColorBitmapMask_MaskType == fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001496 out->setUnknownFourComponents();
1497 } else {
1498 out->setKnownFourComponents(fBatch.fColor);
1499 }
1500 }
1501
1502 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001503 switch (fMaskType) {
1504 case kGrayscaleDistanceField_MaskType:
1505 case kGrayscaleCoverageMask_MaskType:
joshualitt1d89e8d2015-04-01 12:40:54 -07001506 out->setUnknownSingleComponent();
bsalomon265697d2015-07-22 10:17:26 -07001507 break;
1508 case kLCDCoverageMask_MaskType:
1509 case kLCDDistanceField_MaskType:
1510 out->setUnknownOpaqueFourComponents();
joshualitt1d89e8d2015-04-01 12:40:54 -07001511 out->setUsingLCDCoverage();
bsalomon265697d2015-07-22 10:17:26 -07001512 break;
1513 case kColorBitmapMask_MaskType:
1514 out->setKnownSingleComponent(0xff);
joshualitt1d89e8d2015-04-01 12:40:54 -07001515 }
1516 }
1517
bsalomon91d844d2015-08-10 10:47:29 -07001518 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001519 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001520 if (!opt.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001521 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001522 }
bsalomon91d844d2015-08-10 10:47:29 -07001523 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001524
1525 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001526 fBatch.fColorIgnored = !opt.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001527 fBatch.fColor = fGeoData[0].fColor;
bsalomon91d844d2015-08-10 10:47:29 -07001528 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1529 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001530 }
1531
bsalomonb5238a72015-05-05 07:49:49 -07001532 struct FlushInfo {
1533 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1534 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1535 int fGlyphsToFlush;
1536 int fVertexOffset;
1537 };
1538
bsalomonfb1141a2015-08-06 08:52:49 -07001539 void generateGeometry(GrBatchTarget* batchTarget) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001540 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1541 // TODO actually only invert if we don't have RGBA
1542 SkMatrix localMatrix;
1543 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1544 SkDebugf("Cannot invert viewmatrix\n");
1545 return;
1546 }
1547
bsalomon265697d2015-07-22 10:17:26 -07001548 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
joshualitt62db8ba2015-04-09 08:22:37 -07001549 if (!texture) {
1550 SkDebugf("Could not allocate backing texture for atlas\n");
1551 return;
1552 }
1553
bsalomon265697d2015-07-22 10:17:26 -07001554 bool usesDistanceFields = this->usesDistanceFields();
1555 GrMaskFormat maskFormat = this->maskFormat();
1556 bool isLCD = this->isLCD();
1557
joshualitt9bd2daf2015-04-17 09:30:06 -07001558 SkAutoTUnref<const GrGeometryProcessor> gp;
bsalomon265697d2015-07-22 10:17:26 -07001559 if (usesDistanceFields) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001560 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1561 texture));
1562 } else {
1563 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001564 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1565 texture,
1566 params,
bsalomon265697d2015-07-22 10:17:26 -07001567 maskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001568 localMatrix,
1569 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001570 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001571
bsalomonb5238a72015-05-05 07:49:49 -07001572 FlushInfo flushInfo;
1573 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001574 size_t vertexStride = gp->getVertexStride();
bsalomon265697d2015-07-22 10:17:26 -07001575 SkASSERT(vertexStride == (usesDistanceFields ?
1576 get_vertex_stride_df(maskFormat, isLCD) :
1577 get_vertex_stride(maskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001578
bsalomonfb1141a2015-08-06 08:52:49 -07001579 batchTarget->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001580
1581 int glyphCount = this->numGlyphs();
bsalomon8415abe2015-05-04 11:41:41 -07001582 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001583
robertphillipse40d3972015-05-07 09:51:43 -07001584 void* vertices = batchTarget->makeVertSpace(vertexStride,
1585 glyphCount * kVerticesPerGlyph,
1586 &vertexBuffer,
1587 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001588 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1589 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1590 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001591 SkDebugf("Could not allocate vertices\n");
1592 return;
1593 }
1594
1595 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1596
joshualitt25ba7ea2015-04-21 07:49:49 -07001597 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1598 // in a row
1599 const SkDescriptor* desc = NULL;
1600 SkGlyphCache* cache = NULL;
1601 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001602 SkTypeface* typeface = NULL;
1603
bsalomond602f4d2015-07-27 06:12:01 -07001604 for (int i = 0; i < fGeoCount; i++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001605 Geometry& args = fGeoData[i];
1606 Blob* blob = args.fBlob;
1607 Run& run = blob->fRuns[args.fRun];
1608 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1609
bsalomon265697d2015-07-22 10:17:26 -07001610 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001611 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
joshualitt7e97b0b2015-07-31 15:18:08 -07001612 info.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001613 bool regenerateColors;
bsalomon265697d2015-07-22 10:17:26 -07001614 if (usesDistanceFields) {
1615 regenerateColors = !isLCD && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001616 } else {
bsalomon265697d2015-07-22 10:17:26 -07001617 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001618 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001619 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001620 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1621
1622 // We regenerate both texture coords and colors in the blob itself, and update the
1623 // atlas generation. If we don't end up purging any unused plots, we can avoid
1624 // regenerating the coords. We could take a finer grained approach to updating texture
1625 // coords but its not clear if the extra bookkeeping would offset any gains.
1626 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1627 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1628 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1629 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001630 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001631 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001632 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001633
1634 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1635 // generating its texture coords, we have to track whether or not the strike has
1636 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1637 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1638 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1639 // new strike, we instead keep our ref to the old strike and use the packed ids from
1640 // it. These ids will still be valid as long as we hold the ref. When we are done
1641 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1642 bool regenerateGlyphs = false;
1643 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001644 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001645 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001646
1647 // We can reuse if we have a valid strike and our descriptors / typeface are the
1648 // same
joshualitt97202d22015-04-22 13:47:02 -07001649 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1650 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001651 run.fDescriptor.getDesc();
1652 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1653 !(desc->equals(*newDesc))) {
1654 if (cache) {
1655 SkGlyphCache::AttachCache(cache);
1656 }
1657 desc = newDesc;
1658 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1659 scaler = GrTextContext::GetGrFontScaler(cache);
joshualitt7e97b0b2015-07-31 15:18:08 -07001660 strike = info.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001661 typeface = run.fTypeface;
1662 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001663
joshualitt7e97b0b2015-07-31 15:18:08 -07001664 if (info.fStrike->isAbandoned()) {
joshualittae32c102015-04-21 09:37:57 -07001665 regenerateGlyphs = true;
1666 strike = fFontCache->getStrike(scaler);
1667 } else {
joshualitt7e97b0b2015-07-31 15:18:08 -07001668 strike = info.fStrike;
joshualittae32c102015-04-21 09:37:57 -07001669 }
1670 }
1671
1672 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001673 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001674 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
joshualitt6c2c2b02015-07-24 10:37:00 -07001675
1676 GrGlyph* glyph = blob->fGlyphs[glyphOffset];
1677 GrGlyph::PackedID id = glyph->fPackedID;
1678 const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
joshualittae32c102015-04-21 09:37:57 -07001679 if (regenerateGlyphs) {
1680 // Get the id from the old glyph, and use the new strike to lookup
1681 // the glyph.
joshualitt76cc6572015-07-31 05:51:45 -07001682 blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, maskFormat,
1683 scaler);
joshualittae32c102015-04-21 09:37:57 -07001684 }
1685 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001686 SkASSERT(glyph);
joshualitt65e96b42015-07-31 11:45:22 -07001687 SkASSERT(id == glyph->fPackedID);
1688 // We want to be able to assert this but cannot for testing purposes.
1689 // once skbug:4143 has landed we can revist this assert
1690 //SkASSERT(glyph->fMaskFormat == this->maskFormat());
joshualitt1d89e8d2015-04-01 12:40:54 -07001691
1692 if (!fFontCache->hasGlyph(glyph) &&
joshualitt4f19ca32015-07-30 07:59:20 -07001693 !strike->addGlyphToAtlas(batchTarget, glyph, scaler, skGlyph,
1694 maskFormat)) {
bsalomonb5238a72015-05-05 07:49:49 -07001695 this->flush(batchTarget, &flushInfo);
bsalomonfb1141a2015-08-06 08:52:49 -07001696 batchTarget->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001697 brokenRun = glyphIdx > 0;
1698
joshualittae32c102015-04-21 09:37:57 -07001699 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1700 glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001701 scaler,
joshualitt4f19ca32015-07-30 07:59:20 -07001702 skGlyph,
1703 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001704 SkASSERT(success);
1705 }
joshualittb4c507e2015-04-08 08:07:59 -07001706 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1707 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001708
1709 // Texture coords are the last vertex attribute so we get a pointer to the
1710 // first one and then map with stride in regenerateTextureCoords
1711 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1712 vertex += info.fVertexStartIndex;
1713 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1714 vertex += vertexStride - sizeof(SkIPoint16);
1715
1716 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1717 }
1718
1719 if (regenerateColors) {
1720 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1721 vertex += info.fVertexStartIndex;
1722 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1723 this->regenerateColors(vertex, vertexStride, args.fColor);
1724 }
1725
joshualitt2a0e9f32015-04-13 06:12:21 -07001726 if (regeneratePositions) {
1727 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1728 vertex += info.fVertexStartIndex;
1729 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1730 SkScalar transX = args.fTransX;
1731 SkScalar transY = args.fTransY;
1732 this->regeneratePositions(vertex, vertexStride, transX, transY);
1733 }
bsalomonb5238a72015-05-05 07:49:49 -07001734 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001735 }
1736
joshualitt2a0e9f32015-04-13 06:12:21 -07001737 // We my have changed the color so update it here
1738 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001739 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001740 if (regenerateGlyphs) {
joshualitt7e97b0b2015-07-31 15:18:08 -07001741 info.fStrike.reset(SkRef(strike));
joshualittae32c102015-04-21 09:37:57 -07001742 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001743 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
bsalomon265697d2015-07-22 10:17:26 -07001744 fFontCache->atlasGeneration(maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001745 }
1746 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001747 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001748
1749 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1750 // have a valid atlas generation
1751 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1752 batchTarget->currentToken(),
bsalomon265697d2015-07-22 10:17:26 -07001753 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001754 }
1755
1756 // now copy all vertices
1757 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1758 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1759
1760 currVertex += byteCount;
1761 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001762 // Make sure to attach the last cache if applicable
1763 if (cache) {
1764 SkGlyphCache::AttachCache(cache);
1765 }
bsalomonb5238a72015-05-05 07:49:49 -07001766 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001767 }
1768
joshualittad802c62015-04-15 05:31:57 -07001769 // to avoid even the initial copy of the struct, we have a getter for the first item which
1770 // is used to seed the batch with its initial geometry. After seeding, the client should call
1771 // init() so the Batch can initialize itself
1772 Geometry& geometry() { return fGeoData[0]; }
1773 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001774 const Geometry& geo = fGeoData[0];
1775 fBatch.fColor = geo.fColor;
1776 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1777
1778 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1779 // into device space
1780 const Run& run = geo.fBlob->fRuns[geo.fRun];
1781 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1782 SkRect bounds = run.fVertexBounds;
1783 fBatch.fViewMatrix.mapRect(&bounds);
1784 this->setBounds(bounds);
1785 } else {
1786 this->setBounds(run.fVertexBounds);
1787 }
joshualittad802c62015-04-15 05:31:57 -07001788 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001789
1790private:
bsalomon265697d2015-07-22 10:17:26 -07001791 TextBatch() {} // initialized in factory functions.
joshualittad802c62015-04-15 05:31:57 -07001792
bsalomon265697d2015-07-22 10:17:26 -07001793 ~TextBatch() {
bsalomond602f4d2015-07-27 06:12:01 -07001794 for (int i = 0; i < fGeoCount; i++) {
joshualittad802c62015-04-15 05:31:57 -07001795 fGeoData[i].fBlob->unref();
1796 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001797 }
1798
bsalomon265697d2015-07-22 10:17:26 -07001799 GrMaskFormat maskFormat() const {
1800 switch (fMaskType) {
1801 case kLCDCoverageMask_MaskType:
1802 return kA565_GrMaskFormat;
1803 case kColorBitmapMask_MaskType:
1804 return kARGB_GrMaskFormat;
1805 case kGrayscaleCoverageMask_MaskType:
1806 case kGrayscaleDistanceField_MaskType:
1807 case kLCDDistanceField_MaskType:
1808 return kA8_GrMaskFormat;
1809 }
1810 return kA8_GrMaskFormat; // suppress warning
1811 }
1812
1813 bool usesDistanceFields() const {
1814 return kGrayscaleDistanceField_MaskType == fMaskType ||
1815 kLCDDistanceField_MaskType == fMaskType;
1816 }
1817
1818 bool isLCD() const {
1819 return kLCDCoverageMask_MaskType == fMaskType ||
1820 kLCDDistanceField_MaskType == fMaskType;
1821 }
1822
joshualitt1d89e8d2015-04-01 12:40:54 -07001823 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1824 int width = glyph->fBounds.width();
1825 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001826
joshualitt9bd2daf2015-04-17 09:30:06 -07001827 int u0, v0, u1, v1;
bsalomon265697d2015-07-22 10:17:26 -07001828 if (this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001829 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1830 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1831 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1832 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1833 } else {
1834 u0 = glyph->fAtlasLocation.fX;
1835 v0 = glyph->fAtlasLocation.fY;
1836 u1 = u0 + width;
1837 v1 = v0 + height;
1838 }
1839
joshualitt1d89e8d2015-04-01 12:40:54 -07001840 SkIPoint16* textureCoords;
1841 // V0
1842 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1843 textureCoords->set(u0, v0);
1844 vertex += vertexStride;
1845
1846 // V1
1847 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1848 textureCoords->set(u0, v1);
1849 vertex += vertexStride;
1850
1851 // V2
1852 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1853 textureCoords->set(u1, v1);
1854 vertex += vertexStride;
1855
1856 // V3
1857 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1858 textureCoords->set(u1, v0);
1859 }
1860
1861 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1862 for (int i = 0; i < kVerticesPerGlyph; i++) {
1863 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1864 *vcolor = color;
1865 vertex += vertexStride;
1866 }
1867 }
1868
joshualitt2a0e9f32015-04-13 06:12:21 -07001869 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1870 SkScalar transY) {
1871 for (int i = 0; i < kVerticesPerGlyph; i++) {
1872 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1873 point->fX += transX;
1874 point->fY += transY;
1875 vertex += vertexStride;
1876 }
1877 }
1878
bsalomonb5238a72015-05-05 07:49:49 -07001879 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001880 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001881 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001882 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001883 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001884 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001885 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001886 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001887 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1888 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001889 }
1890
1891 GrColor color() const { return fBatch.fColor; }
1892 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1893 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1894 int numGlyphs() const { return fBatch.fNumGlyphs; }
1895
1896 bool onCombineIfPossible(GrBatch* t) override {
joshualitt8cab9a72015-07-16 09:13:50 -07001897 if (!this->pipeline()->isEqual(*t->pipeline())) {
1898 return false;
1899 }
1900
bsalomon265697d2015-07-22 10:17:26 -07001901 TextBatch* that = t->cast<TextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001902
bsalomon265697d2015-07-22 10:17:26 -07001903 if (fMaskType != that->fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001904 return false;
1905 }
1906
bsalomon265697d2015-07-22 10:17:26 -07001907 if (!this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001908 // TODO we can often batch across LCD text if we have dual source blending and don't
1909 // have to use the blend constant
bsalomon265697d2015-07-22 10:17:26 -07001910 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001911 return false;
1912 }
joshualitt9bd2daf2015-04-17 09:30:06 -07001913 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1914 return false;
1915 }
1916 } else {
joshualitt9bd2daf2015-04-17 09:30:06 -07001917 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1918 return false;
1919 }
1920
1921 if (fFilteredColor != that->fFilteredColor) {
1922 return false;
1923 }
1924
joshualitt9bd2daf2015-04-17 09:30:06 -07001925 if (fUseBGR != that->fUseBGR) {
1926 return false;
1927 }
1928
joshualitt9bd2daf2015-04-17 09:30:06 -07001929 // TODO see note above
bsalomon265697d2015-07-22 10:17:26 -07001930 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001931 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001932 }
1933
1934 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001935
bsalomond602f4d2015-07-27 06:12:01 -07001936 // Reallocate space for geo data if necessary and then import that's geo data.
1937 int newGeoCount = that->fGeoCount + fGeoCount;
1938 // We assume (and here enforce) that the allocation size is the smallest power of two that
1939 // is greater than or equal to the number of geometries (and at least
1940 // kMinGeometryAllocated).
1941 int newAllocSize = GrNextPow2(newGeoCount);
1942 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
1943
bsalomon16ed6ad2015-07-29 06:54:33 -07001944 if (newGeoCount > currAllocSize) {
bsalomond602f4d2015-07-27 06:12:01 -07001945 fGeoData.realloc(newAllocSize);
joshualittad802c62015-04-15 05:31:57 -07001946 }
1947
bsalomond602f4d2015-07-27 06:12:01 -07001948 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
bsalomon1c634362015-07-27 07:00:00 -07001949 // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
1950 // it doesn't try to unref them.
1951#ifdef SK_DEBUG
1952 for (int i = 0; i < that->fGeoCount; ++i) {
1953 that->fGeoData.get()[i].fBlob = (Blob*)0x1;
joshualittad802c62015-04-15 05:31:57 -07001954 }
bsalomon1c634362015-07-27 07:00:00 -07001955#endif
1956 that->fGeoCount = 0;
bsalomond602f4d2015-07-27 06:12:01 -07001957 fGeoCount = newGeoCount;
joshualitt99c7c072015-05-01 13:43:30 -07001958
1959 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001960 return true;
1961 }
1962
joshualitt9bd2daf2015-04-17 09:30:06 -07001963 // TODO just use class params
1964 // TODO trying to figure out why lcd is so whack
1965 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1966 GrColor color, GrTexture* texture) {
1967 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
bsalomon265697d2015-07-22 10:17:26 -07001968 bool isLCD = this->isLCD();
joshualitt9bd2daf2015-04-17 09:30:06 -07001969 // set up any flags
bsalomon265697d2015-07-22 10:17:26 -07001970 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt9bd2daf2015-04-17 09:30:06 -07001971
1972 // see if we need to create a new effect
bsalomon265697d2015-07-22 10:17:26 -07001973 if (isLCD) {
1974 flags |= kUseLCD_DistanceFieldEffectFlag;
1975 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
1976 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1977
joshualitt9bd2daf2015-04-17 09:30:06 -07001978 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1979
1980 float redCorrection =
1981 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1982 float greenCorrection =
1983 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1984 float blueCorrection =
1985 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1986 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1987 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1988 greenCorrection,
1989 blueCorrection);
1990
1991 return GrDistanceFieldLCDTextGeoProc::Create(color,
1992 viewMatrix,
1993 texture,
1994 params,
1995 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001996 flags,
1997 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001998 } else {
1999 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07002000#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07002001 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07002002 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
2003 return GrDistanceFieldA8TextGeoProc::Create(color,
2004 viewMatrix,
2005 texture,
2006 params,
2007 correction,
joshualittb8c241a2015-05-19 08:23:30 -07002008 flags,
2009 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002010#else
2011 return GrDistanceFieldA8TextGeoProc::Create(color,
2012 viewMatrix,
2013 texture,
2014 params,
joshualittb8c241a2015-05-19 08:23:30 -07002015 flags,
2016 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002017#endif
2018 }
2019
2020 }
2021
joshualitt1d89e8d2015-04-01 12:40:54 -07002022 struct BatchTracker {
2023 GrColor fColor;
2024 SkMatrix fViewMatrix;
2025 bool fUsesLocalCoords;
2026 bool fColorIgnored;
2027 bool fCoverageIgnored;
2028 int fNumGlyphs;
2029 };
2030
2031 BatchTracker fBatch;
bsalomond602f4d2015-07-27 06:12:01 -07002032 // The minimum number of Geometry we will try to allocate.
2033 enum { kMinGeometryAllocated = 4 };
2034 SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
2035 int fGeoCount;
bsalomon265697d2015-07-22 10:17:26 -07002036
2037 enum MaskType {
2038 kGrayscaleCoverageMask_MaskType,
2039 kLCDCoverageMask_MaskType,
2040 kColorBitmapMask_MaskType,
2041 kGrayscaleDistanceField_MaskType,
2042 kLCDDistanceField_MaskType,
2043 } fMaskType;
2044 bool fUseBGR; // fold this into the enum?
2045
joshualitt1d89e8d2015-04-01 12:40:54 -07002046 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002047
2048 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002049 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002050 SkColor fFilteredColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07002051};
2052
robertphillips2334fb62015-06-17 05:43:33 -07002053void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002054 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002055 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2056 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2057 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002058
joshualitt9a27e632015-04-06 10:53:36 -07002059 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2060 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002061
joshualitt9a27e632015-04-06 10:53:36 -07002062 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002063
joshualitt9a27e632015-04-06 10:53:36 -07002064 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2065 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002066 }
2067
robertphillipsfcf78292015-06-19 11:49:52 -07002068 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002069
2070 switch (it.positioning()) {
2071 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002072 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002073 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002074 textLen, x + offset.x(), y + offset.y(), clipBounds);
2075 break;
2076 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002077 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002078 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002079 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2080 clipBounds);
2081 break;
2082 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002083 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002084 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002085 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2086 break;
2087 }
2088}
2089
bsalomon265697d2015-07-22 10:17:26 -07002090inline GrBatch*
joshualitt374b2f72015-07-21 08:05:03 -07002091GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info,
joshualitt79dfb2b2015-05-11 08:58:08 -07002092 int glyphCount, int run, int subRun,
2093 GrColor color, SkScalar transX, SkScalar transY,
2094 const SkPaint& skPaint) {
2095 GrMaskFormat format = info.fMaskFormat;
2096 GrColor subRunColor;
2097 if (kARGB_GrMaskFormat == format) {
2098 uint8_t paintAlpha = skPaint.getAlpha();
2099 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2100 } else {
2101 subRunColor = color;
2102 }
2103
bsalomon265697d2015-07-22 10:17:26 -07002104 TextBatch* batch;
joshualitt79dfb2b2015-05-11 08:58:08 -07002105 if (info.fDrawAsDistanceFields) {
2106 SkColor filteredColor;
2107 SkColorFilter* colorFilter = skPaint.getColorFilter();
2108 if (colorFilter) {
2109 filteredColor = colorFilter->filterColor(skPaint.getColor());
2110 } else {
2111 filteredColor = skPaint.getColor();
2112 }
robertphillipsfcf78292015-06-19 11:49:52 -07002113 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
bsalomon265697d2015-07-22 10:17:26 -07002114 batch = TextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
2115 fDistanceAdjustTable, filteredColor,
2116 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002117 } else {
bsalomon265697d2015-07-22 10:17:26 -07002118 batch = TextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
joshualitt79dfb2b2015-05-11 08:58:08 -07002119 }
bsalomon265697d2015-07-22 10:17:26 -07002120 TextBatch::Geometry& geometry = batch->geometry();
joshualitt79dfb2b2015-05-11 08:58:08 -07002121 geometry.fBlob = SkRef(cacheBlob);
2122 geometry.fRun = run;
2123 geometry.fSubRun = subRun;
2124 geometry.fColor = subRunColor;
2125 geometry.fTransX = transX;
2126 geometry.fTransY = transY;
2127 batch->init();
2128
2129 return batch;
2130}
2131
robertphillips2334fb62015-06-17 05:43:33 -07002132inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt374b2f72015-07-21 08:05:03 -07002133 GrAtlasTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002134 SkScalar transX, SkScalar transY,
2135 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002136 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2137 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2138 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2139 if (0 == glyphCount) {
2140 continue;
2141 }
2142
bsalomon265697d2015-07-22 10:17:26 -07002143 SkAutoTUnref<GrBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2144 subRun, color, transX, transY,
2145 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002146 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002147 }
2148}
2149
joshualitt374b2f72015-07-21 08:05:03 -07002150inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002151 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002152 SkScalar transX, SkScalar transY,
2153 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002154 if (!cacheBlob->fBigGlyphs.count()) {
2155 return;
2156 }
2157
2158 SkMatrix pathMatrix;
2159 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2160 SkDebugf("could not invert viewmatrix\n");
2161 return;
2162 }
2163
joshualitt9a27e632015-04-06 10:53:36 -07002164 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt374b2f72015-07-21 08:05:03 -07002165 GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002166 bigGlyph.fVx += transX;
2167 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002168 SkMatrix translate = cacheBlob->fViewMatrix;
2169 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2170
robertphillips2334fb62015-06-17 05:43:33 -07002171 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002172 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002173 }
2174}
joshualitt9a27e632015-04-06 10:53:36 -07002175
robertphillips2334fb62015-06-17 05:43:33 -07002176void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt374b2f72015-07-21 08:05:03 -07002177 GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002178 GrRenderTarget* rt,
2179 const SkPaint& skPaint,
2180 const GrPaint& grPaint,
2181 SkDrawFilter* drawFilter,
2182 const GrClip& clip,
2183 const SkMatrix& viewMatrix,
2184 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002185 SkScalar x, SkScalar y,
2186 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002187 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2188 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002189 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002190
2191 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002192
2193 SkTextBlob::RunIterator it(blob);
2194 for (int run = 0; !it.done(); it.next(), run++) {
2195 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002196 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002197 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002198 continue;
2199 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002200 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002201 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002202 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002203 }
2204
2205 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002206 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002207}
2208
joshualitt374b2f72015-07-21 08:05:03 -07002209void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002210 GrRenderTarget* rt,
2211 const SkPaint& skPaint,
2212 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002213 const GrClip& clip,
2214 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002215 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002216
2217 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002218 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillips2334fb62015-06-17 05:43:33 -07002219 this->flushRun(&pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002220 }
2221
2222 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002223 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002224}
joshualitt79dfb2b2015-05-11 08:58:08 -07002225
2226///////////////////////////////////////////////////////////////////////////////////////////////////
2227
2228#ifdef GR_TEST_UTILS
2229
joshualitt6c891102015-05-13 08:51:49 -07002230BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002231 static uint32_t gContextID = SK_InvalidGenID;
2232 static GrAtlasTextContext* gTextContext = NULL;
robertphillipsfcf78292015-06-19 11:49:52 -07002233 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002234
2235 if (context->uniqueID() != gContextID) {
2236 gContextID = context->uniqueID();
2237 SkDELETE(gTextContext);
robertphillips2334fb62015-06-17 05:43:33 -07002238
joshualitt79dfb2b2015-05-11 08:58:08 -07002239 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2240 // because we don't really want to have a gpu device here.
2241 // We enable distance fields by twiddling a knob on the paint
robertphillipsfcf78292015-06-19 11:49:52 -07002242 GrDrawContext* drawContext = context->drawContext(&gSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -07002243
robertphillipsfcf78292015-06-19 11:49:52 -07002244 gTextContext = GrAtlasTextContext::Create(context, drawContext, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002245 }
2246
2247 // create dummy render target
2248 GrSurfaceDesc desc;
2249 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2250 desc.fWidth = 1024;
2251 desc.fHeight = 1024;
2252 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002253 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002254 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2255 SkASSERT(texture);
2256 SkASSERT(NULL != texture->asRenderTarget());
2257 GrRenderTarget* rt = texture->asRenderTarget();
2258
2259 // Setup dummy SkPaint / GrPaint
2260 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002261 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002262 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002263 skPaint.setColor(color);
2264 skPaint.setLCDRenderText(random->nextBool());
2265 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2266 skPaint.setSubpixelText(random->nextBool());
2267
2268 GrPaint grPaint;
2269 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2270 SkFAIL("couldn't convert paint\n");
2271 }
2272
2273 const char* text = "The quick brown fox jumps over the lazy dog.";
2274 int textLen = (int)strlen(text);
2275
2276 // Setup clip
2277 GrClip clip;
2278 SkIRect noClip = SkIRect::MakeLargest();
2279
2280 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2281 // intend to test the batch with this unit test, that is okay.
joshualitt374b2f72015-07-21 08:05:03 -07002282 SkAutoTUnref<GrAtlasTextBlob> blob(
joshualitt79dfb2b2015-05-11 08:58:08 -07002283 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2284 static_cast<size_t>(textLen), 0, 0, noClip));
2285
2286 SkScalar transX = static_cast<SkScalar>(random->nextU());
2287 SkScalar transY = static_cast<SkScalar>(random->nextU());
joshualitt374b2f72015-07-21 08:05:03 -07002288 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
joshualitt79dfb2b2015-05-11 08:58:08 -07002289 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2290}
2291
2292#endif