blob: f8004656c10207c976daf982525506565607ff70 [file] [log] [blame]
joshualitt1d89e8d2015-04-01 12:40:54 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "GrAtlasTextContext.h"
8
joshualitt1d89e8d2015-04-01 12:40:54 -07009#include "GrBatchFontCache.h"
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt79dfb2b2015-05-11 08:58:08 -070011#include "GrBatchTest.h"
robertphillipsccb1b572015-05-27 11:02:55 -070012#include "GrBlurUtils.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070013#include "GrDefaultGeoProcFactory.h"
robertphillipsea461502015-05-26 11:38:03 -070014#include "GrDrawContext.h"
robertphillips2334fb62015-06-17 05:43:33 -070015#include "GrDrawTarget.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070016#include "GrFontScaler.h"
bsalomoned0bcad2015-05-04 10:36:42 -070017#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070018#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070019#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070020#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070021#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070022
23#include "SkAutoKern.h"
24#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070025#include "SkColorFilter.h"
26#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070027#include "SkDraw.h"
28#include "SkDrawFilter.h"
29#include "SkDrawProcs.h"
herb9be5ff62015-11-11 11:30:11 -080030#include "SkFindAndPlaceGlyph.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070031#include "SkGlyphCache.h"
32#include "SkGpuDevice.h"
bsalomonf1b7a1d2015-09-28 06:26:28 -070033#include "SkGrPriv.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070034#include "SkPath.h"
35#include "SkRTConf.h"
36#include "SkStrokeRec.h"
37#include "SkTextBlob.h"
38#include "SkTextMapStateProc.h"
39
bsalomon16b99132015-08-13 14:55:50 -070040#include "batches/GrVertexBatch.h"
joshualitt74417822015-08-07 11:42:16 -070041
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
robertphillipsf6703fa2015-09-01 05:36:47 -0700100GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps& surfaceProps)
101 : INHERITED(context, surfaceProps)
102 , fDistanceAdjustTable(new DistanceAdjustTable) {
joshualittb7133be2015-04-08 09:08:31 -0700103 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
104 // vertexStride
bungeman99fe8222015-08-20 07:57:51 -0700105 static_assert(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
106 "vertex_attribute_changed");
halcanary96fcdcc2015-08-27 07:41:13 -0700107 fCurrStrike = nullptr;
joshualittb7133be2015-04-08 09:08:31 -0700108 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700109}
110
robertphillips9fc82752015-06-19 04:46:45 -0700111void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700112
113 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
114 // text. The mask gamma hack is based off of guessing what the blend color is going to
115 // be, and adjusting the mask so that when run through the linear blend will
116 // produce the value closest to the desired result. However, in practice this means
117 // that the 'adjusted' mask is just increasing or decreasing the coverage of
118 // the mask depending on what it is thought it will blit against. For black (on
119 // assumed white) this means that coverages are decreased (on a curve). For white (on
120 // assumed black) this means that coverages are increased (on a a curve). At
121 // middle (perceptual) gray (which could be blit against anything) the coverages
122 // remain the same.
123 //
124 // The idea here is that instead of determining the initial (real) coverage and
125 // then adjusting that coverage, we determine an adjusted coverage directly by
126 // essentially manipulating the geometry (in this case, the distance to the glyph
127 // edge). So for black (on assumed white) this thins a bit; for white (on
128 // assumed black) this fake bolds the geometry a bit.
129 //
130 // The distance adjustment is calculated by determining the actual coverage value which
131 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
132 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
133 // actual edge. So by subtracting this distance adjustment and computing without the
134 // the coverage adjustment we should get 0.5 coverage at the same point.
135 //
136 // This has several implications:
137 // For non-gray lcd smoothed text, each subpixel essentially is using a
138 // slightly different geometry.
139 //
140 // For black (on assumed white) this may not cover some pixels which were
141 // previously covered; however those pixels would have been only slightly
142 // covered and that slight coverage would have been decreased anyway. Also, some pixels
143 // which were previously fully covered may no longer be fully covered.
144 //
145 // For white (on assumed black) this may cover some pixels which weren't
146 // previously covered at all.
147
148 int width, height;
149 size_t size;
150
151#ifdef SK_GAMMA_CONTRAST
152 SkScalar contrast = SK_GAMMA_CONTRAST;
153#else
154 SkScalar contrast = 0.5f;
155#endif
robertphillips9fc82752015-06-19 04:46:45 -0700156 SkScalar paintGamma = SK_GAMMA_EXPONENT;
157 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700158
159 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
160 &width, &height);
161
162 SkASSERT(kExpectedDistanceAdjustTableSize == height);
halcanary385fe4d2015-08-26 13:07:48 -0700163 fTable = new SkScalar[height];
joshualitt9bd2daf2015-04-17 09:30:06 -0700164
165 SkAutoTArray<uint8_t> data((int)size);
166 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
167
168 // find the inverse points where we cross 0.5
169 // binsearch might be better, but we only need to do this once on creation
170 for (int row = 0; row < height; ++row) {
171 uint8_t* rowPtr = data.get() + row*width;
172 for (int col = 0; col < width - 1; ++col) {
173 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
174 // compute point where a mask value will give us a result of 0.5
175 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
176 float borderAlpha = (col + interp) / 255.f;
177
178 // compute t value for that alpha
179 // this is an approximate inverse for smoothstep()
180 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
181
182 // compute distance which gives us that t value
183 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
184 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
185
186 fTable[row] = d;
187 break;
188 }
189 }
190 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700191}
192
joshualittdbd35932015-04-02 09:19:04 -0700193GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -0700194 const SkSurfaceProps& surfaceProps) {
robertphillipsf6703fa2015-09-01 05:36:47 -0700195 return new GrAtlasTextContext(context, surfaceProps);
joshualitt1d89e8d2015-04-01 12:40:54 -0700196}
197
joshualittdbd35932015-04-02 09:19:04 -0700198bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
199 const GrClip&,
200 const GrPaint&,
201 const SkPaint& skPaint,
202 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700203 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
204 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700205}
206
joshualitt9e36c1a2015-04-14 12:17:27 -0700207GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
208 GrColor canonicalColor = paint.computeLuminanceColor();
209 if (lcd) {
210 // This is the correct computation, but there are tons of cases where LCD can be overridden.
211 // For now we just regenerate if any run in a textblob has LCD.
212 // TODO figure out where all of these overrides are and see if we can incorporate that logic
213 // at a higher level *OR* use sRGB
214 SkASSERT(false);
215 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
216 } else {
217 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
218 // gamma corrected masks anyways, nor color
219 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
220 SkColorGetG(canonicalColor),
221 SkColorGetB(canonicalColor));
222 // reduce to our finite number of bits
223 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
224 }
225 return canonicalColor;
226}
227
228// TODO if this function ever shows up in profiling, then we can compute this value when the
229// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
230// run so this is not a big deal to compute here.
231bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
halcanary33779752015-10-27 14:01:05 -0700232 SkTextBlobRunIterator it(blob);
joshualitt9e36c1a2015-04-14 12:17:27 -0700233 for (; !it.done(); it.next()) {
234 if (it.isLCD()) {
235 return true;
236 }
237 }
238 return false;
239}
240
joshualitt2a0e9f32015-04-13 06:12:21 -0700241bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
joshualitt374b2f72015-07-21 08:05:03 -0700242 const GrAtlasTextBlob& blob, const SkPaint& paint,
jvanverth0628a522015-08-18 07:44:22 -0700243 GrColor color, const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700244 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700245 // If we have LCD text then our canonical color will be set to transparent, in this case we have
246 // to regenerate the blob on any color change
jvanverth0628a522015-08-18 07:44:22 -0700247 // We use the grPaint to get any color filter effects
248 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
249 blob.fPaintColor != color) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700250 return true;
251 }
252
253 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
254 return true;
255 }
256
257 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
258 return true;
259 }
260
joshualitt53b5f442015-04-13 06:33:59 -0700261 // We only cache one masked version
262 if (blob.fKey.fHasBlur &&
263 (blob.fBlurRec.fSigma != blurRec.fSigma ||
264 blob.fBlurRec.fStyle != blurRec.fStyle ||
265 blob.fBlurRec.fQuality != blurRec.fQuality)) {
266 return true;
267 }
268
269 // Similarly, we only cache one version for each style
270 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
271 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
272 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
273 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
274 return true;
275 }
276
joshualittfcfb9fc2015-04-21 07:35:10 -0700277 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
278 // for mixed blobs if this becomes an issue.
279 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700280 // Identical viewmatrices and we can reuse in all cases
281 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
282 return false;
283 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700284 return true;
285 }
286
joshualittfcfb9fc2015-04-21 07:35:10 -0700287 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700288 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
289 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
290 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
291 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
292 return true;
293 }
294
joshualittfcfb9fc2015-04-21 07:35:10 -0700295 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
296 // but only for integer translations.
297 // This cool bit of math will determine the necessary translation to apply to the already
298 // generated vertex coordinates to move them to the correct position
299 SkScalar transX = viewMatrix.getTranslateX() +
300 viewMatrix.getScaleX() * (x - blob.fX) +
301 viewMatrix.getSkewX() * (y - blob.fY) -
302 blob.fViewMatrix.getTranslateX();
303 SkScalar transY = viewMatrix.getTranslateY() +
304 viewMatrix.getSkewY() * (x - blob.fX) +
305 viewMatrix.getScaleY() * (y - blob.fY) -
306 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700307 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700308 return true;
309 }
310
joshualittfcfb9fc2015-04-21 07:35:10 -0700311 (*outTransX) = transX;
312 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700313 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700314 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
315 // distance field being generated, so we have to regenerate in those cases
316 SkScalar newMaxScale = viewMatrix.getMaxScale();
317 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
318 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
319 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
320 return true;
321 }
322
323 (*outTransX) = x - blob.fX;
324 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700325 }
joshualitt374b2f72015-07-21 08:05:03 -0700326
joshualitta7c63892015-04-21 13:24:37 -0700327 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
328 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
329 // the blob anyways at flush time, so no need to regenerate explicitly
joshualitt2a0e9f32015-04-13 06:12:21 -0700330 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700331}
332
333
joshualitt374b2f72015-07-21 08:05:03 -0700334inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run,
joshualittdbd35932015-04-02 09:19:04 -0700335 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700336 const SkMatrix* viewMatrix,
337 bool noGamma) {
robertphillipsfcf78292015-06-19 11:49:52 -0700338 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700339 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
340 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
341}
342
robertphillipsf6703fa2015-09-01 05:36:47 -0700343void GrAtlasTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700344 const GrClip& clip, const SkPaint& skPaint,
345 const SkMatrix& viewMatrix, const SkTextBlob* blob,
346 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700347 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700348 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700349 if (fContext->abandoned()) {
350 return;
351 }
352
joshualitt374b2f72015-07-21 08:05:03 -0700353 SkAutoTUnref<GrAtlasTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700354 SkMaskFilter::BlurRec blurRec;
joshualitt374b2f72015-07-21 08:05:03 -0700355 GrAtlasTextBlob::Key key;
joshualitt53b5f442015-04-13 06:33:59 -0700356 // It might be worth caching these things, but its not clear at this time
357 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
358 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700359 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700360 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700361 drawFilter);
362
363 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700364 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700365
366 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
robertphillipsfcf78292015-06-19 11:49:52 -0700367 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700368 kUnknown_SkPixelGeometry;
369
joshualitt9e36c1a2015-04-14 12:17:27 -0700370 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
371 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
372 // ensure we always match the same key
373 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
374 ComputeCanonicalColor(skPaint, hasLCD);
375
joshualitte4cee1f2015-05-11 13:04:28 -0700376 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700377 key.fUniqueID = blob->uniqueID();
378 key.fStyle = skPaint.getStyle();
379 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700380 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700381 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700382 }
383
joshualitt1d89e8d2015-04-01 12:40:54 -0700384 SkIRect clipRect;
385 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
386
joshualitt2a0e9f32015-04-13 06:12:21 -0700387 SkScalar transX = 0.f;
388 SkScalar transY = 0.f;
389
joshualitt9e36c1a2015-04-14 12:17:27 -0700390 // Though for the time being runs in the textblob can override the paint, they only touch font
391 // info.
392 GrPaint grPaint;
bsalomonf1b7a1d2015-09-28 06:26:28 -0700393 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &grPaint)) {
bsalomonbed83a62015-04-15 14:18:34 -0700394 return;
395 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700396
joshualittb7133be2015-04-08 09:08:31 -0700397 if (cacheBlob) {
jvanverth0628a522015-08-18 07:44:22 -0700398 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, grPaint.getColor(), blurRec,
399 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,
jvanverth0628a522015-08-18 07:44:22 -0700407 blob, x, y, drawFilter, clipRect, rt, clip);
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,
jvanverth0628a522015-08-18 07:44:22 -0700424 blob, x, y, drawFilter, clipRect, rt, clip);
joshualitt259fbf12015-07-21 11:39:34 -0700425 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,
jvanverth0628a522015-08-18 07:44:22 -0700438 blob, x, y, drawFilter, clipRect, rt, clip);
joshualitt1d89e8d2015-04-01 12:40:54 -0700439 }
440
robertphillipsf6703fa2015-09-01 05:36:47 -0700441 this->flush(blob, cacheBlob, dc, 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
bsalomonafcd7cd2015-08-31 12:39:41 -0700460 bool useDFT = fSurfaceProps.isUseDeviceIndependentFonts();
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,
jvanverth0628a522015-08-18 07:44:22 -0700489 GrRenderTarget* rt, const GrClip& clip) {
490 // The color here is the GrPaint color, and it is used to determine whether we
491 // have to regenerate LCD text blobs.
492 // We use this color vs the SkPaint color because it has the colorfilter applied.
493 cacheBlob->fPaintColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -0700494 cacheBlob->fViewMatrix = viewMatrix;
495 cacheBlob->fX = x;
496 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700497
498 // Regenerate textblob
499 SkPaint runPaint = skPaint;
halcanary33779752015-10-27 14:01:05 -0700500 SkTextBlobRunIterator it(blob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700501 for (int run = 0; !it.done(); it.next(), run++) {
502 int glyphCount = it.glyphCount();
503 size_t textLen = glyphCount * sizeof(uint16_t);
504 const SkPoint& offset = it.offset();
505 // applyFontToPaint() always overwrites the exact same attributes,
506 // so it is safe to not re-seed the paint for this reason.
507 it.applyFontToPaint(&runPaint);
508
509 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
510 // A false return from filter() means we should abort the current draw.
511 runPaint = skPaint;
512 continue;
513 }
514
robertphillipsfcf78292015-06-19 11:49:52 -0700515 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700516
joshualitt1d89e8d2015-04-01 12:40:54 -0700517 // setup vertex / glyphIndex for the new run
518 if (run > 0) {
519 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
520 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
521
522 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
523 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
524
525 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
526 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
527 }
528
joshualittfcfb9fc2015-04-21 07:35:10 -0700529 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
530 cacheBlob->setHasDistanceField();
531 SkPaint dfPaint = runPaint;
532 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700533 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700534 Run& runIdx = cacheBlob->fRuns[run];
535 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
536 subRun.fUseLCDText = runPaint.isLCDRenderText();
537 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700538
joshualittfcfb9fc2015-04-21 07:35:10 -0700539 SkTDArray<char> fallbackTxt;
540 SkTDArray<SkScalar> fallbackPos;
541 SkPoint dfOffset;
542 int scalarsPerPosition = 2;
543 switch (it.positioning()) {
544 case SkTextBlob::kDefault_Positioning: {
jvanverth157e6482015-09-09 08:05:12 -0700545 this->internalDrawDFText(cacheBlob, run, dfPaint, color, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700546 (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());
jvanverth157e6482015-09-09 08:05:12 -0700554 this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700555 (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);
jvanverth157e6482015-09-09 08:05:12 -0700562 this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700563 (const char*)it.glyphs(), textLen, it.pos(),
564 scalarsPerPosition, dfOffset, clipRect, textRatio,
565 &fallbackTxt, &fallbackPos);
566 break;
567 }
568 }
569 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700570 this->fallbackDrawPosText(cacheBlob, run, rt, clip, color, runPaint, viewMatrix,
joshualittfcfb9fc2015-04-21 07:35:10 -0700571 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
572 clipRect);
573 }
joshualittfcfb9fc2015-04-21 07:35:10 -0700574 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
575 cacheBlob->fRuns[run].fDrawAsPaths = true;
576 } else {
577 cacheBlob->setHasBitmap();
578 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
579 false);
580 switch (it.positioning()) {
581 case SkTextBlob::kDefault_Positioning:
582 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
583 (const char *)it.glyphs(), textLen,
584 x + offset.x(), y + offset.y(), clipRect);
585 break;
586 case SkTextBlob::kHorizontal_Positioning:
587 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
588 (const char*)it.glyphs(), textLen, it.pos(), 1,
589 SkPoint::Make(x, y + offset.y()), clipRect);
590 break;
591 case SkTextBlob::kFull_Positioning:
592 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
593 (const char*)it.glyphs(), textLen, it.pos(), 2,
594 SkPoint::Make(x, y), clipRect);
595 break;
596 }
597 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700598 }
599
600 if (drawFilter) {
601 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
602 runPaint = skPaint;
603 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700604 }
605}
606
joshualitt374b2f72015-07-21 08:05:03 -0700607inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
joshualitt64c99cc2015-04-21 09:43:03 -0700608 SkPaint* skPaint,
609 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700610 const SkMatrix& viewMatrix) {
611 // getMaxScale doesn't support perspective, so neither do we at the moment
612 SkASSERT(!viewMatrix.hasPerspective());
613 SkScalar maxScale = viewMatrix.getMaxScale();
614 SkScalar textSize = skPaint->getTextSize();
615 SkScalar scaledTextSize = textSize;
616 // if we have non-unity scale, we need to choose our base text size
617 // based on the SkPaint's text size multiplied by the max scale factor
618 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
619 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
620 scaledTextSize *= maxScale;
621 }
622
joshualitt64c99cc2015-04-21 09:43:03 -0700623 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
624 // and ceiling. A scale outside of this range would require regenerating the distance fields
625 SkScalar dfMaskScaleFloor;
626 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700627 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700628 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700629 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700630 *textRatio = textSize / kSmallDFFontSize;
631 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
632 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700633 dfMaskScaleFloor = kSmallDFFontLimit;
634 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700635 *textRatio = textSize / kMediumDFFontSize;
636 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
637 } else {
joshualitta7c63892015-04-21 13:24:37 -0700638 dfMaskScaleFloor = kMediumDFFontLimit;
639 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700640 *textRatio = textSize / kLargeDFFontSize;
641 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
642 }
643
joshualitt64c99cc2015-04-21 09:43:03 -0700644 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
645 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
646 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
647 // tolerate before we'd have to move to a large mip size. When we actually test these values
648 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
649 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
650 // level)
joshualitta7c63892015-04-21 13:24:37 -0700651 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700652 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
653 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
654
joshualitt9bd2daf2015-04-17 09:30:06 -0700655 skPaint->setLCDRenderText(false);
656 skPaint->setAutohinted(false);
657 skPaint->setHinting(SkPaint::kNormal_Hinting);
658 skPaint->setSubpixelText(true);
659}
660
joshualitt374b2f72015-07-21 08:05:03 -0700661inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700662 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700663 GrRenderTarget* rt, const GrClip& clip,
jvanverth0628a522015-08-18 07:44:22 -0700664 GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -0700665 const SkPaint& skPaint,
666 const SkMatrix& viewMatrix,
667 const SkTDArray<char>& fallbackTxt,
668 const SkTDArray<SkScalar>& fallbackPos,
669 int scalarsPerPosition,
670 const SkPoint& offset,
671 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700672 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700673 blob->setHasBitmap();
674 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700675 // Push back a new subrun to fill and set the override descriptor
676 run.push_back();
halcanary385fe4d2015-08-26 13:07:48 -0700677 run.fOverrideDescriptor.reset(new SkAutoDescriptor);
joshualitt97202d22015-04-22 13:47:02 -0700678 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700679 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700680 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700681 run.fOverrideDescriptor->getDesc());
jvanverth0628a522015-08-18 07:44:22 -0700682 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, color, viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700683 fallbackTxt.begin(), fallbackTxt.count(),
684 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
685 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700686}
687
joshualitt374b2f72015-07-21 08:05:03 -0700688inline GrAtlasTextBlob*
jvanverth157e6482015-09-09 08:05:12 -0700689GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
690 const SkMatrix& viewMatrix, SkPaint* dfPaint,
691 SkScalar* textRatio) {
joshualitt374b2f72015-07-21 08:05:03 -0700692 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700693
694 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700695 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700696 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700697 Run& run = blob->fRuns[0];
698 PerSubRunInfo& subRun = run.fSubRunInfo.back();
699 subRun.fUseLCDText = origPaint.isLCDRenderText();
700 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700701
joshualitt9bd2daf2015-04-17 09:30:06 -0700702 return blob;
703}
704
joshualitt374b2f72015-07-21 08:05:03 -0700705inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700706GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
707 const GrPaint& paint, const SkPaint& skPaint,
708 const SkMatrix& viewMatrix,
709 const char text[], size_t byteLength,
710 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700711 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700712 SkIRect clipRect;
713 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
714
joshualitt374b2f72015-07-21 08:05:03 -0700715 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700716 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
717 SkPaint dfPaint;
718 SkScalar textRatio;
jvanverth157e6482015-09-09 08:05:12 -0700719 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700720
joshualitt9bd2daf2015-04-17 09:30:06 -0700721 SkTDArray<char> fallbackTxt;
722 SkTDArray<SkScalar> fallbackPos;
723 SkPoint offset;
jvanverth157e6482015-09-09 08:05:12 -0700724 this->internalDrawDFText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text,
joshualitt9bd2daf2015-04-17 09:30:06 -0700725 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
726 &offset, skPaint);
joshualitt9bd2daf2015-04-17 09:30:06 -0700727 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700728 this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
729 fallbackTxt, fallbackPos, 2, offset, clipRect);
joshualitt9bd2daf2015-04-17 09:30:06 -0700730 }
731 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700732 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700733 blob->fViewMatrix = viewMatrix;
734
735 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
736 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
737 byteLength, x, y, clipRect);
738 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700739 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700740 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700741}
742
joshualitt374b2f72015-07-21 08:05:03 -0700743inline GrAtlasTextBlob*
joshualitt79dfb2b2015-05-11 08:58:08 -0700744GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
745 const GrPaint& paint, const SkPaint& skPaint,
746 const SkMatrix& viewMatrix,
747 const char text[], size_t byteLength,
748 const SkScalar pos[], int scalarsPerPosition,
749 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700750 int glyphCount = skPaint.countText(text, byteLength);
751
752 SkIRect clipRect;
753 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
754
joshualitt374b2f72015-07-21 08:05:03 -0700755 GrAtlasTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700756 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
757 SkPaint dfPaint;
758 SkScalar textRatio;
jvanverth157e6482015-09-09 08:05:12 -0700759 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700760
761 SkTDArray<char> fallbackTxt;
762 SkTDArray<SkScalar> fallbackPos;
jvanverth157e6482015-09-09 08:05:12 -0700763 this->internalDrawDFPosText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text,
joshualitt9bd2daf2015-04-17 09:30:06 -0700764 byteLength, pos, scalarsPerPosition, offset, clipRect,
765 textRatio, &fallbackTxt, &fallbackPos);
joshualitt9bd2daf2015-04-17 09:30:06 -0700766 if (fallbackTxt.count()) {
jvanverth0628a522015-08-18 07:44:22 -0700767 this->fallbackDrawPosText(blob, 0, rt, clip, paint.getColor(), skPaint, viewMatrix,
768 fallbackTxt, fallbackPos, scalarsPerPosition, offset,
769 clipRect);
joshualitt9bd2daf2015-04-17 09:30:06 -0700770 }
771 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700772 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700773 blob->fViewMatrix = viewMatrix;
774 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
775 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
776 byteLength, pos, scalarsPerPosition, offset, clipRect);
777 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700778 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700779 return blob;
780}
781
robertphillipsf6703fa2015-09-01 05:36:47 -0700782void GrAtlasTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700783 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700784 const GrPaint& paint, const SkPaint& skPaint,
785 const SkMatrix& viewMatrix,
786 const char text[], size_t byteLength,
787 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700788 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700789 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
790 text, byteLength, x, y, regionClipBounds));
robertphillipsf6703fa2015-09-01 05:36:47 -0700791 this->flush(blob, dc, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700792}
793
robertphillipsf6703fa2015-09-01 05:36:47 -0700794void GrAtlasTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700795 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700796 const GrPaint& paint, const SkPaint& skPaint,
797 const SkMatrix& viewMatrix,
798 const char text[], size_t byteLength,
799 const SkScalar pos[], int scalarsPerPosition,
800 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt374b2f72015-07-21 08:05:03 -0700801 SkAutoTUnref<GrAtlasTextBlob> blob(
robertphillips2334fb62015-06-17 05:43:33 -0700802 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
803 text, byteLength,
804 pos, scalarsPerPosition,
805 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700806
robertphillipsf6703fa2015-09-01 05:36:47 -0700807 this->flush(blob, dc, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700808}
809
joshualitt374b2f72015-07-21 08:05:03 -0700810void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700811 SkGlyphCache* cache, const SkPaint& skPaint,
812 GrColor color,
813 const SkMatrix& viewMatrix,
814 const char text[], size_t byteLength,
815 SkScalar x, SkScalar y, const SkIRect& clipRect) {
halcanary96fcdcc2015-08-27 07:41:13 -0700816 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt1d89e8d2015-04-01 12:40:54 -0700817
818 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700819 if (text == nullptr || byteLength == 0) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700820 return;
821 }
822
halcanary96fcdcc2015-08-27 07:41:13 -0700823 fCurrStrike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -0700824 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
825
826 // Get GrFontScaler from cache
827 GrFontScaler* fontScaler = GetGrFontScaler(cache);
828
829 // transform our starting point
830 {
831 SkPoint loc;
832 viewMatrix.mapXY(x, y, &loc);
833 x = loc.fX;
834 y = loc.fY;
835 }
836
837 // need to measure first
838 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
839 SkVector stopVector;
840 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
841
842 SkScalar stopX = stopVector.fX;
843 SkScalar stopY = stopVector.fY;
844
845 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
846 stopX = SkScalarHalf(stopX);
847 stopY = SkScalarHalf(stopY);
848 }
849 x -= stopX;
850 y -= stopY;
851 }
852
853 const char* stop = text + byteLength;
854
855 SkAutoKern autokern;
856
857 SkFixed fxMask = ~0;
858 SkFixed fyMask = ~0;
859 SkScalar halfSampleX, halfSampleY;
860 if (cache->isSubpixel()) {
861 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
862 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
863 if (kX_SkAxisAlignment == baseline) {
864 fyMask = 0;
865 halfSampleY = SK_ScalarHalf;
866 } else if (kY_SkAxisAlignment == baseline) {
867 fxMask = 0;
868 halfSampleX = SK_ScalarHalf;
869 }
870 } else {
871 halfSampleX = halfSampleY = SK_ScalarHalf;
872 }
873
874 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
875 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
876
877 while (text < stop) {
878 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
879
880 fx += autokern.adjust(glyph);
881
882 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700883 this->bmpAppendGlyph(blob,
884 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -0700885 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -0700886 Sk48Dot16FloorToInt(fx),
887 Sk48Dot16FloorToInt(fy),
888 color,
889 fontScaler,
890 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700891 }
892
893 fx += glyph.fAdvanceX;
894 fy += glyph.fAdvanceY;
895 }
896}
897
joshualitt374b2f72015-07-21 08:05:03 -0700898void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,
joshualitt9bd2daf2015-04-17 09:30:06 -0700899 SkGlyphCache* cache, const SkPaint& skPaint,
900 GrColor color,
901 const SkMatrix& viewMatrix,
902 const char text[], size_t byteLength,
903 const SkScalar pos[], int scalarsPerPosition,
904 const SkPoint& offset, const SkIRect& clipRect) {
halcanary96fcdcc2015-08-27 07:41:13 -0700905 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt1d89e8d2015-04-01 12:40:54 -0700906 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
907
908 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700909 if (text == nullptr || byteLength == 0) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700910 return;
911 }
912
halcanary96fcdcc2015-08-27 07:41:13 -0700913 fCurrStrike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -0700914 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
915
916 // Get GrFontScaler from cache
917 GrFontScaler* fontScaler = GetGrFontScaler(cache);
918
herb9be5ff62015-11-11 11:30:11 -0800919 SkFindAndPlaceGlyph::ProcessPosText(
920 text, byteLength, offset, viewMatrix, pos, scalarsPerPosition,
921 skPaint.getTextAlign(), glyphCacheProc, cache,
922 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
923 position += rounding;
924 this->bmpAppendGlyph(
925 blob, runIndex, glyph,
926 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
927 color, fontScaler, clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700928 }
herb9be5ff62015-11-11 11:30:11 -0800929 );
joshualitt1d89e8d2015-04-01 12:40:54 -0700930}
931
joshualitt374b2f72015-07-21 08:05:03 -0700932void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex,
jvanverth157e6482015-09-09 08:05:12 -0700933 const SkPaint& skPaint, GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -0700934 const SkMatrix& viewMatrix,
935 const char text[], size_t byteLength,
936 SkScalar x, SkScalar y, const SkIRect& clipRect,
937 SkScalar textRatio,
938 SkTDArray<char>* fallbackTxt,
939 SkTDArray<SkScalar>* fallbackPos,
940 SkPoint* offset,
941 const SkPaint& origPaint) {
halcanary96fcdcc2015-08-27 07:41:13 -0700942 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt9bd2daf2015-04-17 09:30:06 -0700943
944 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700945 if (text == nullptr || byteLength == 0) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700946 return;
947 }
948
949 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
950 SkAutoDescriptor desc;
halcanary96fcdcc2015-08-27 07:41:13 -0700951 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true);
jvanverth157e6482015-09-09 08:05:12 -0700952 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
joshualitt9bd2daf2015-04-17 09:30:06 -0700953 desc.getDesc());
954
955 SkTArray<SkScalar> positions;
956
957 const char* textPtr = text;
958 SkFixed stopX = 0;
959 SkFixed stopY = 0;
960 SkFixed origin = 0;
961 switch (origPaint.getTextAlign()) {
962 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
963 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
964 case SkPaint::kLeft_Align: origin = 0; break;
965 }
966
967 SkAutoKern autokern;
968 const char* stop = text + byteLength;
969 while (textPtr < stop) {
970 // don't need x, y here, since all subpixel variants will have the
971 // same advance
972 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
973
974 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
975 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
976
977 SkFixed height = glyph.fAdvanceY;
978 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
979
980 stopX += width;
981 stopY += height;
982 }
983 SkASSERT(textPtr == stop);
984
jvanverth157e6482015-09-09 08:05:12 -0700985 SkGlyphCache::AttachCache(origPaintCache);
986
joshualitt9bd2daf2015-04-17 09:30:06 -0700987 // now adjust starting point depending on alignment
988 SkScalar alignX = SkFixedToScalar(stopX);
989 SkScalar alignY = SkFixedToScalar(stopY);
990 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
991 alignX = SkScalarHalf(alignX);
992 alignY = SkScalarHalf(alignY);
993 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
994 alignX = 0;
995 alignY = 0;
996 }
997 x -= alignX;
998 y -= alignY;
999 *offset = SkPoint::Make(x, y);
1000
jvanverth157e6482015-09-09 08:05:12 -07001001 this->internalDrawDFPosText(blob, runIndex, skPaint, color, viewMatrix, text, byteLength,
joshualitt9bd2daf2015-04-17 09:30:06 -07001002 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1003 fallbackPos);
joshualitt9bd2daf2015-04-17 09:30:06 -07001004}
1005
joshualitt374b2f72015-07-21 08:05:03 -07001006void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
jvanverth157e6482015-09-09 08:05:12 -07001007 const SkPaint& skPaint, GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -07001008 const SkMatrix& viewMatrix,
1009 const char text[], size_t byteLength,
1010 const SkScalar pos[], int scalarsPerPosition,
1011 const SkPoint& offset, const SkIRect& clipRect,
1012 SkScalar textRatio,
1013 SkTDArray<char>* fallbackTxt,
1014 SkTDArray<SkScalar>* fallbackPos) {
1015
halcanary96fcdcc2015-08-27 07:41:13 -07001016 SkASSERT(byteLength == 0 || text != nullptr);
joshualitt9bd2daf2015-04-17 09:30:06 -07001017 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1018
1019 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001020 if (text == nullptr || byteLength == 0) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001021 return;
1022 }
1023
halcanary96fcdcc2015-08-27 07:41:13 -07001024 fCurrStrike = nullptr;
joshualitt9bd2daf2015-04-17 09:30:06 -07001025
1026 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
jvanverth157e6482015-09-09 08:05:12 -07001027 SkGlyphCache* cache = this->setupCache(&blob->fRuns[runIndex], skPaint, nullptr, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001028 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1029
1030 const char* stop = text + byteLength;
1031
1032 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1033 while (text < stop) {
1034 const char* lastText = text;
1035 // the last 2 parameters are ignored
1036 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1037
1038 if (glyph.fWidth) {
1039 SkScalar x = offset.x() + pos[0];
1040 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1041
1042 if (!this->dfAppendGlyph(blob,
1043 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001044 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001045 x, y, color, fontScaler, clipRect,
1046 textRatio, viewMatrix)) {
1047 // couldn't append, send to fallback
1048 fallbackTxt->append(SkToInt(text-lastText), lastText);
1049 *fallbackPos->append() = pos[0];
1050 if (2 == scalarsPerPosition) {
1051 *fallbackPos->append() = pos[1];
1052 }
1053 }
1054 }
1055 pos += scalarsPerPosition;
1056 }
1057 } else {
1058 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1059 : SK_Scalar1;
1060 while (text < stop) {
1061 const char* lastText = text;
1062 // the last 2 parameters are ignored
1063 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1064
1065 if (glyph.fWidth) {
1066 SkScalar x = offset.x() + pos[0];
1067 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1068
1069 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1070 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1071
1072 if (!this->dfAppendGlyph(blob,
1073 runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001074 glyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001075 x - advanceX, y - advanceY, color,
1076 fontScaler,
1077 clipRect,
1078 textRatio,
1079 viewMatrix)) {
1080 // couldn't append, send to fallback
1081 fallbackTxt->append(SkToInt(text-lastText), lastText);
1082 *fallbackPos->append() = pos[0];
1083 if (2 == scalarsPerPosition) {
1084 *fallbackPos->append() = pos[1];
1085 }
1086 }
1087 }
1088 pos += scalarsPerPosition;
1089 }
1090 }
jvanverth157e6482015-09-09 08:05:12 -07001091
1092 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -07001093}
1094
joshualitt374b2f72015-07-21 08:05:03 -07001095void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001096 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001097 int vx, int vy, GrColor color, GrFontScaler* scaler,
1098 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001099 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001100 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001101 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1102 }
1103
joshualitt6c2c2b02015-07-24 10:37:00 -07001104 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1105 skGlyph.getSubXFixed(),
1106 skGlyph.getSubYFixed(),
1107 GrGlyph::kCoverage_MaskStyle);
1108 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001109 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001110 return;
1111 }
1112
1113 int x = vx + glyph->fBounds.fLeft;
1114 int y = vy + glyph->fBounds.fTop;
1115
1116 // keep them as ints until we've done the clip-test
1117 int width = glyph->fBounds.width();
1118 int height = glyph->fBounds.height();
1119
joshualitt2a0e9f32015-04-13 06:12:21 -07001120#if 0
1121 // Not checking the clip bounds might introduce a performance regression. However, its not
1122 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1123 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1124 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1125 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001126 // check if we clipped out
1127 if (clipRect.quickReject(x, y, x + width, y + height)) {
1128 return;
1129 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001130#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001131
1132 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001133 if (glyph->fTooLargeForAtlas) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001134 this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001135 return;
1136 }
1137
joshualitt1d89e8d2015-04-01 12:40:54 -07001138 GrMaskFormat format = glyph->fMaskFormat;
1139
1140 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1141 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittd9f13ae2015-07-24 11:24:31 -07001142 subRun = &run.push_back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001143 subRun->fStrike.reset(SkRef(fCurrStrike));
1144 } else if (!run.fInitialized) {
1145 subRun->fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001146 }
1147
1148 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001149
1150 size_t vertexStride = get_vertex_stride(format);
1151
1152 SkRect r;
1153 r.fLeft = SkIntToScalar(x);
1154 r.fTop = SkIntToScalar(y);
1155 r.fRight = r.fLeft + SkIntToScalar(width);
1156 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001157 subRun->fMaskFormat = format;
1158 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001159 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001160}
joshualitt1d89e8d2015-04-01 12:40:54 -07001161
joshualitt374b2f72015-07-21 08:05:03 -07001162bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
joshualitt6c2c2b02015-07-24 10:37:00 -07001163 const SkGlyph& skGlyph,
joshualitt9bd2daf2015-04-17 09:30:06 -07001164 SkScalar sx, SkScalar sy, GrColor color,
1165 GrFontScaler* scaler,
1166 const SkIRect& clipRect,
1167 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001168 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001169 if (!fCurrStrike) {
1170 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
1171 }
1172
joshualitt6c2c2b02015-07-24 10:37:00 -07001173 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
1174 skGlyph.getSubXFixed(),
1175 skGlyph.getSubYFixed(),
1176 GrGlyph::kDistance_MaskStyle);
1177 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
joshualitt010db532015-04-21 10:07:26 -07001178 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001179 return true;
1180 }
1181
1182 // fallback to color glyph support
1183 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1184 return false;
1185 }
1186
1187 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1188 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1189 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1190 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1191
1192 SkScalar scale = textRatio;
1193 dx *= scale;
1194 dy *= scale;
1195 width *= scale;
1196 height *= scale;
1197 sx += dx;
1198 sy += dy;
1199 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1200
1201#if 0
1202 // check if we clipped out
1203 SkRect dstRect;
1204 viewMatrix.mapRect(&dstRect, glyphRect);
1205 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1206 SkScalarTruncToInt(dstRect.top()),
1207 SkScalarTruncToInt(dstRect.right()),
1208 SkScalarTruncToInt(dstRect.bottom()))) {
1209 return true;
1210 }
1211#endif
1212
1213 // TODO combine with the above
1214 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001215 if (glyph->fTooLargeForAtlas) {
joshualitt0fe04a22015-08-25 12:05:50 -07001216 this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy, scale, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001217 return true;
1218 }
1219
joshualitt9bd2daf2015-04-17 09:30:06 -07001220 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
joshualitt7e97b0b2015-07-31 15:18:08 -07001221 if (!run.fInitialized) {
1222 subRun->fStrike.reset(SkRef(fCurrStrike));
1223 }
1224 run.fInitialized = true;
joshualitt9bd2daf2015-04-17 09:30:06 -07001225 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1226 subRun->fMaskFormat = kA8_GrMaskFormat;
1227
1228 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1229
1230 bool useColorVerts = !subRun->fUseLCDText;
1231 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001232 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001233 return true;
1234}
1235
joshualitt374b2f72015-07-21 08:05:03 -07001236inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001237 GrFontScaler* scaler, const SkGlyph& skGlyph,
joshualitt0fe04a22015-08-25 12:05:50 -07001238 SkScalar x, SkScalar y, SkScalar scale,
1239 bool applyVM) {
halcanary96fcdcc2015-08-27 07:41:13 -07001240 if (nullptr == glyph->fPath) {
joshualitt6c2c2b02015-07-24 10:37:00 -07001241 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
1242 if (!glyphPath) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001243 return;
1244 }
joshualitt6c2c2b02015-07-24 10:37:00 -07001245
halcanary385fe4d2015-08-26 13:07:48 -07001246 glyph->fPath = new SkPath(*glyphPath);
joshualitt9bd2daf2015-04-17 09:30:06 -07001247 }
joshualitt0fe04a22015-08-25 12:05:50 -07001248 blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM));
joshualitt9bd2daf2015-04-17 09:30:06 -07001249}
1250
joshualitt374b2f72015-07-21 08:05:03 -07001251inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run,
joshualitt9bd2daf2015-04-17 09:30:06 -07001252 Run::SubRunInfo* subRun,
1253 const SkRect& positions, GrColor color,
1254 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001255 GrGlyph* glyph) {
1256 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001257 run->fVertexBounds.joinNonEmptyArg(positions);
1258 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001259
1260 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1261
joshualitt9bd2daf2015-04-17 09:30:06 -07001262 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001263 // V0
1264 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1265 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001266 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1267 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001268 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001269
joshualitt010db532015-04-21 10:07:26 -07001270 // V1
1271 position = reinterpret_cast<SkPoint*>(vertex);
1272 position->set(positions.fLeft, positions.fBottom);
1273 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001274 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001275 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001276
joshualitt010db532015-04-21 10:07:26 -07001277 // V2
1278 position = reinterpret_cast<SkPoint*>(vertex);
1279 position->set(positions.fRight, positions.fBottom);
1280 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001281 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001282 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001283
joshualitt010db532015-04-21 10:07:26 -07001284 // V3
1285 position = reinterpret_cast<SkPoint*>(vertex);
1286 position->set(positions.fRight, positions.fTop);
1287 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001288 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001289 } else {
1290 // V0
1291 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1292 position->set(positions.fLeft, positions.fTop);
1293 vertex += vertexStride;
1294
1295 // V1
1296 position = reinterpret_cast<SkPoint*>(vertex);
1297 position->set(positions.fLeft, positions.fBottom);
1298 vertex += vertexStride;
1299
1300 // V2
1301 position = reinterpret_cast<SkPoint*>(vertex);
1302 position->set(positions.fRight, positions.fBottom);
1303 vertex += vertexStride;
1304
1305 // V3
1306 position = reinterpret_cast<SkPoint*>(vertex);
1307 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001308 }
1309
1310 subRun->fGlyphEndIndex++;
1311 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1312}
1313
bsalomonabd30f52015-08-13 13:34:48 -07001314class TextBatch : public GrVertexBatch {
joshualitt1d89e8d2015-04-01 12:40:54 -07001315public:
reed1b55a962015-09-17 20:16:13 -07001316 DEFINE_BATCH_CLASS_ID
1317
joshualitt9bd2daf2015-04-17 09:30:06 -07001318 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualitt374b2f72015-07-21 08:05:03 -07001319 typedef GrAtlasTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001320 typedef Blob::Run Run;
1321 typedef Run::SubRunInfo TextInfo;
1322 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001323 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001324 int fRun;
1325 int fSubRun;
1326 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001327 SkScalar fTransX;
1328 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001329 };
1330
bsalomon265697d2015-07-22 10:17:26 -07001331 static TextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
joshualittad802c62015-04-15 05:31:57 -07001332 GrBatchFontCache* fontCache) {
halcanary385fe4d2015-08-26 13:07:48 -07001333 TextBatch* batch = new TextBatch;
bsalomon265697d2015-07-22 10:17:26 -07001334
bsalomon265697d2015-07-22 10:17:26 -07001335 batch->fFontCache = fontCache;
1336 switch (maskFormat) {
1337 case kA8_GrMaskFormat:
1338 batch->fMaskType = kGrayscaleCoverageMask_MaskType;
1339 break;
1340 case kA565_GrMaskFormat:
1341 batch->fMaskType = kLCDCoverageMask_MaskType;
1342 break;
1343 case kARGB_GrMaskFormat:
1344 batch->fMaskType = kColorBitmapMask_MaskType;
1345 break;
1346 }
1347 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001348 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001349 batch->fFilteredColor = 0;
1350 batch->fFontCache = fontCache;
1351 batch->fUseBGR = false;
1352 return batch;
joshualitt1d89e8d2015-04-01 12:40:54 -07001353 }
1354
bsalomon265697d2015-07-22 10:17:26 -07001355 static TextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache,
robertphillipsf6703fa2015-09-01 05:36:47 -07001356 const DistanceAdjustTable* distanceAdjustTable,
bsalomon265697d2015-07-22 10:17:26 -07001357 SkColor filteredColor, bool isLCD,
1358 bool useBGR) {
halcanary385fe4d2015-08-26 13:07:48 -07001359 TextBatch* batch = new TextBatch;
reed1b55a962015-09-17 20:16:13 -07001360
bsalomon265697d2015-07-22 10:17:26 -07001361 batch->fFontCache = fontCache;
1362 batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
1363 batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
1364 batch->fFilteredColor = filteredColor;
1365 batch->fUseBGR = useBGR;
1366 batch->fBatch.fNumGlyphs = glyphCount;
bsalomond602f4d2015-07-27 06:12:01 -07001367 batch->fGeoCount = 1;
bsalomon265697d2015-07-22 10:17:26 -07001368 return batch;
joshualitt9bd2daf2015-04-17 09:30:06 -07001369 }
1370
bsalomone46f9fe2015-08-18 06:05:14 -07001371 // to avoid even the initial copy of the struct, we have a getter for the first item which
1372 // is used to seed the batch with its initial geometry. After seeding, the client should call
1373 // init() so the Batch can initialize itself
1374 Geometry& geometry() { return fGeoData[0]; }
1375
1376 void init() {
1377 const Geometry& geo = fGeoData[0];
1378 fBatch.fColor = geo.fColor;
1379 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1380
1381 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1382 // into device space
1383 const Run& run = geo.fBlob->fRuns[geo.fRun];
1384 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1385 SkRect bounds = run.fVertexBounds;
1386 fBatch.fViewMatrix.mapRect(&bounds);
1387 this->setBounds(bounds);
1388 } else {
1389 this->setBounds(run.fVertexBounds);
1390 }
1391 }
1392
bsalomon265697d2015-07-22 10:17:26 -07001393 const char* name() const override { return "TextBatch"; }
joshualitt1d89e8d2015-04-01 12:40:54 -07001394
1395 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001396 if (kColorBitmapMask_MaskType == fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001397 out->setUnknownFourComponents();
1398 } else {
1399 out->setKnownFourComponents(fBatch.fColor);
1400 }
1401 }
1402
1403 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
bsalomon265697d2015-07-22 10:17:26 -07001404 switch (fMaskType) {
1405 case kGrayscaleDistanceField_MaskType:
1406 case kGrayscaleCoverageMask_MaskType:
joshualitt1d89e8d2015-04-01 12:40:54 -07001407 out->setUnknownSingleComponent();
bsalomon265697d2015-07-22 10:17:26 -07001408 break;
1409 case kLCDCoverageMask_MaskType:
1410 case kLCDDistanceField_MaskType:
1411 out->setUnknownOpaqueFourComponents();
joshualitt1d89e8d2015-04-01 12:40:54 -07001412 out->setUsingLCDCoverage();
bsalomon265697d2015-07-22 10:17:26 -07001413 break;
1414 case kColorBitmapMask_MaskType:
1415 out->setKnownSingleComponent(0xff);
joshualitt1d89e8d2015-04-01 12:40:54 -07001416 }
1417 }
1418
bsalomone46f9fe2015-08-18 06:05:14 -07001419private:
bsalomon91d844d2015-08-10 10:47:29 -07001420 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001421 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001422 if (!opt.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001423 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001424 }
bsalomon91d844d2015-08-10 10:47:29 -07001425 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001426
1427 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001428 fBatch.fColorIgnored = !opt.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001429 fBatch.fColor = fGeoData[0].fColor;
bsalomon91d844d2015-08-10 10:47:29 -07001430 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1431 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001432 }
1433
bsalomonb5238a72015-05-05 07:49:49 -07001434 struct FlushInfo {
1435 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1436 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1437 int fGlyphsToFlush;
1438 int fVertexOffset;
1439 };
1440
bsalomon75398562015-08-17 12:55:38 -07001441 void onPrepareDraws(Target* target) override {
joshualitt1d89e8d2015-04-01 12:40:54 -07001442 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1443 // TODO actually only invert if we don't have RGBA
1444 SkMatrix localMatrix;
1445 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1446 SkDebugf("Cannot invert viewmatrix\n");
1447 return;
1448 }
1449
bsalomon265697d2015-07-22 10:17:26 -07001450 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
joshualitt62db8ba2015-04-09 08:22:37 -07001451 if (!texture) {
1452 SkDebugf("Could not allocate backing texture for atlas\n");
1453 return;
1454 }
1455
bsalomon265697d2015-07-22 10:17:26 -07001456 bool usesDistanceFields = this->usesDistanceFields();
1457 GrMaskFormat maskFormat = this->maskFormat();
1458 bool isLCD = this->isLCD();
1459
joshualitt9bd2daf2015-04-17 09:30:06 -07001460 SkAutoTUnref<const GrGeometryProcessor> gp;
bsalomon265697d2015-07-22 10:17:26 -07001461 if (usesDistanceFields) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001462 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1463 texture));
1464 } else {
1465 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001466 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1467 texture,
1468 params,
bsalomon265697d2015-07-22 10:17:26 -07001469 maskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001470 localMatrix,
1471 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001472 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001473
bsalomonb5238a72015-05-05 07:49:49 -07001474 FlushInfo flushInfo;
1475 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001476 size_t vertexStride = gp->getVertexStride();
bsalomon265697d2015-07-22 10:17:26 -07001477 SkASSERT(vertexStride == (usesDistanceFields ?
1478 get_vertex_stride_df(maskFormat, isLCD) :
1479 get_vertex_stride(maskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001480
bsalomon75398562015-08-17 12:55:38 -07001481 target->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001482
1483 int glyphCount = this->numGlyphs();
bsalomon8415abe2015-05-04 11:41:41 -07001484 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001485
bsalomon75398562015-08-17 12:55:38 -07001486 void* vertices = target->makeVertexSpace(vertexStride,
1487 glyphCount * kVerticesPerGlyph,
1488 &vertexBuffer,
1489 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001490 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -07001491 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -07001492 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001493 SkDebugf("Could not allocate vertices\n");
1494 return;
1495 }
1496
1497 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1498
joshualitt25ba7ea2015-04-21 07:49:49 -07001499 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1500 // in a row
halcanary96fcdcc2015-08-27 07:41:13 -07001501 const SkDescriptor* desc = nullptr;
1502 SkGlyphCache* cache = nullptr;
1503 GrFontScaler* scaler = nullptr;
1504 SkTypeface* typeface = nullptr;
joshualitt25ba7ea2015-04-21 07:49:49 -07001505
bsalomond602f4d2015-07-27 06:12:01 -07001506 for (int i = 0; i < fGeoCount; i++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001507 Geometry& args = fGeoData[i];
1508 Blob* blob = args.fBlob;
1509 Run& run = blob->fRuns[args.fRun];
1510 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1511
bsalomon265697d2015-07-22 10:17:26 -07001512 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001513 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
joshualitt7e97b0b2015-07-31 15:18:08 -07001514 info.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001515 bool regenerateColors;
bsalomon265697d2015-07-22 10:17:26 -07001516 if (usesDistanceFields) {
1517 regenerateColors = !isLCD && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001518 } else {
bsalomon265697d2015-07-22 10:17:26 -07001519 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001520 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001521 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001522 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1523
1524 // We regenerate both texture coords and colors in the blob itself, and update the
1525 // atlas generation. If we don't end up purging any unused plots, we can avoid
1526 // regenerating the coords. We could take a finer grained approach to updating texture
1527 // coords but its not clear if the extra bookkeeping would offset any gains.
1528 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1529 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1530 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1531 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001532 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001533 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001534 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001535
1536 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1537 // generating its texture coords, we have to track whether or not the strike has
1538 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1539 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1540 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1541 // new strike, we instead keep our ref to the old strike and use the packed ids from
1542 // it. These ids will still be valid as long as we hold the ref. When we are done
1543 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1544 bool regenerateGlyphs = false;
halcanary96fcdcc2015-08-27 07:41:13 -07001545 GrBatchTextStrike* strike = nullptr;
joshualitt1d89e8d2015-04-01 12:40:54 -07001546 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001547 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001548
1549 // We can reuse if we have a valid strike and our descriptors / typeface are the
joshualitt94469302015-09-02 06:13:39 -07001550 // same. The override descriptor is only for the non distance field text within
1551 // a run
1552 const SkDescriptor* newDesc = (run.fOverrideDescriptor && !usesDistanceFields) ?
joshualitt97202d22015-04-22 13:47:02 -07001553 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001554 run.fDescriptor.getDesc();
1555 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1556 !(desc->equals(*newDesc))) {
1557 if (cache) {
1558 SkGlyphCache::AttachCache(cache);
1559 }
1560 desc = newDesc;
1561 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1562 scaler = GrTextContext::GetGrFontScaler(cache);
joshualitt7e97b0b2015-07-31 15:18:08 -07001563 strike = info.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001564 typeface = run.fTypeface;
1565 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001566
joshualitt7e97b0b2015-07-31 15:18:08 -07001567 if (info.fStrike->isAbandoned()) {
joshualittae32c102015-04-21 09:37:57 -07001568 regenerateGlyphs = true;
1569 strike = fFontCache->getStrike(scaler);
1570 } else {
joshualitt7e97b0b2015-07-31 15:18:08 -07001571 strike = info.fStrike;
joshualittae32c102015-04-21 09:37:57 -07001572 }
1573 }
1574
1575 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001576 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001577 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
joshualitt6c2c2b02015-07-24 10:37:00 -07001578
1579 GrGlyph* glyph = blob->fGlyphs[glyphOffset];
1580 GrGlyph::PackedID id = glyph->fPackedID;
1581 const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
joshualittae32c102015-04-21 09:37:57 -07001582 if (regenerateGlyphs) {
1583 // Get the id from the old glyph, and use the new strike to lookup
1584 // the glyph.
joshualitt76cc6572015-07-31 05:51:45 -07001585 blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, maskFormat,
1586 scaler);
joshualittae32c102015-04-21 09:37:57 -07001587 }
1588 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001589 SkASSERT(glyph);
joshualitt65e96b42015-07-31 11:45:22 -07001590 SkASSERT(id == glyph->fPackedID);
1591 // We want to be able to assert this but cannot for testing purposes.
1592 // once skbug:4143 has landed we can revist this assert
1593 //SkASSERT(glyph->fMaskFormat == this->maskFormat());
joshualitt1d89e8d2015-04-01 12:40:54 -07001594
1595 if (!fFontCache->hasGlyph(glyph) &&
bsalomon75398562015-08-17 12:55:38 -07001596 !strike->addGlyphToAtlas(target, glyph, scaler, skGlyph, maskFormat)) {
1597 this->flush(target, &flushInfo);
1598 target->initDraw(gp, this->pipeline());
joshualitt1d89e8d2015-04-01 12:40:54 -07001599 brokenRun = glyphIdx > 0;
1600
bsalomon75398562015-08-17 12:55:38 -07001601 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
joshualittae32c102015-04-21 09:37:57 -07001602 glyph,
joshualitt6c2c2b02015-07-24 10:37:00 -07001603 scaler,
joshualitt4f19ca32015-07-30 07:59:20 -07001604 skGlyph,
1605 maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001606 SkASSERT(success);
1607 }
joshualittb4c507e2015-04-08 08:07:59 -07001608 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
bsalomon75398562015-08-17 12:55:38 -07001609 target->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001610
1611 // Texture coords are the last vertex attribute so we get a pointer to the
1612 // first one and then map with stride in regenerateTextureCoords
1613 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1614 vertex += info.fVertexStartIndex;
1615 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1616 vertex += vertexStride - sizeof(SkIPoint16);
1617
1618 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1619 }
1620
1621 if (regenerateColors) {
1622 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1623 vertex += info.fVertexStartIndex;
1624 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1625 this->regenerateColors(vertex, vertexStride, args.fColor);
1626 }
1627
joshualitt2a0e9f32015-04-13 06:12:21 -07001628 if (regeneratePositions) {
1629 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1630 vertex += info.fVertexStartIndex;
1631 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1632 SkScalar transX = args.fTransX;
1633 SkScalar transY = args.fTransY;
1634 this->regeneratePositions(vertex, vertexStride, transX, transY);
1635 }
bsalomonb5238a72015-05-05 07:49:49 -07001636 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001637 }
1638
joshualitt2a0e9f32015-04-13 06:12:21 -07001639 // We my have changed the color so update it here
1640 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001641 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001642 if (regenerateGlyphs) {
joshualitt7e97b0b2015-07-31 15:18:08 -07001643 info.fStrike.reset(SkRef(strike));
joshualittae32c102015-04-21 09:37:57 -07001644 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001645 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
bsalomon265697d2015-07-22 10:17:26 -07001646 fFontCache->atlasGeneration(maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001647 }
1648 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001649 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001650
1651 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1652 // have a valid atlas generation
bsalomon75398562015-08-17 12:55:38 -07001653 fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001654 }
1655
1656 // now copy all vertices
1657 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1658 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1659
1660 currVertex += byteCount;
1661 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001662 // Make sure to attach the last cache if applicable
1663 if (cache) {
1664 SkGlyphCache::AttachCache(cache);
1665 }
bsalomon75398562015-08-17 12:55:38 -07001666 this->flush(target, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001667 }
1668
reed1b55a962015-09-17 20:16:13 -07001669 TextBatch() : INHERITED(ClassID()) {} // initialized in factory functions.
joshualittad802c62015-04-15 05:31:57 -07001670
bsalomon265697d2015-07-22 10:17:26 -07001671 ~TextBatch() {
bsalomond602f4d2015-07-27 06:12:01 -07001672 for (int i = 0; i < fGeoCount; i++) {
joshualittad802c62015-04-15 05:31:57 -07001673 fGeoData[i].fBlob->unref();
1674 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001675 }
1676
bsalomon265697d2015-07-22 10:17:26 -07001677 GrMaskFormat maskFormat() const {
1678 switch (fMaskType) {
1679 case kLCDCoverageMask_MaskType:
1680 return kA565_GrMaskFormat;
1681 case kColorBitmapMask_MaskType:
1682 return kARGB_GrMaskFormat;
1683 case kGrayscaleCoverageMask_MaskType:
1684 case kGrayscaleDistanceField_MaskType:
1685 case kLCDDistanceField_MaskType:
1686 return kA8_GrMaskFormat;
1687 }
1688 return kA8_GrMaskFormat; // suppress warning
1689 }
1690
1691 bool usesDistanceFields() const {
1692 return kGrayscaleDistanceField_MaskType == fMaskType ||
1693 kLCDDistanceField_MaskType == fMaskType;
1694 }
1695
1696 bool isLCD() const {
1697 return kLCDCoverageMask_MaskType == fMaskType ||
1698 kLCDDistanceField_MaskType == fMaskType;
1699 }
1700
joshualitt1d89e8d2015-04-01 12:40:54 -07001701 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1702 int width = glyph->fBounds.width();
1703 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001704
joshualitt9bd2daf2015-04-17 09:30:06 -07001705 int u0, v0, u1, v1;
bsalomon265697d2015-07-22 10:17:26 -07001706 if (this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001707 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1708 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1709 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1710 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1711 } else {
1712 u0 = glyph->fAtlasLocation.fX;
1713 v0 = glyph->fAtlasLocation.fY;
1714 u1 = u0 + width;
1715 v1 = v0 + height;
1716 }
1717
joshualitt1d89e8d2015-04-01 12:40:54 -07001718 SkIPoint16* textureCoords;
1719 // V0
1720 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1721 textureCoords->set(u0, v0);
1722 vertex += vertexStride;
1723
1724 // V1
1725 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1726 textureCoords->set(u0, v1);
1727 vertex += vertexStride;
1728
1729 // V2
1730 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1731 textureCoords->set(u1, v1);
1732 vertex += vertexStride;
1733
1734 // V3
1735 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1736 textureCoords->set(u1, v0);
1737 }
1738
1739 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1740 for (int i = 0; i < kVerticesPerGlyph; i++) {
1741 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1742 *vcolor = color;
1743 vertex += vertexStride;
1744 }
1745 }
1746
joshualitt2a0e9f32015-04-13 06:12:21 -07001747 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1748 SkScalar transY) {
1749 for (int i = 0; i < kVerticesPerGlyph; i++) {
1750 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1751 point->fX += transX;
1752 point->fY += transY;
1753 vertex += vertexStride;
1754 }
1755 }
1756
bsalomon75398562015-08-17 12:55:38 -07001757 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001758 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001759 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001760 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001761 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001762 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001763 maxGlyphsPerDraw);
bsalomon75398562015-08-17 12:55:38 -07001764 target->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001765 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1766 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001767 }
1768
1769 GrColor color() const { return fBatch.fColor; }
1770 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1771 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1772 int numGlyphs() const { return fBatch.fNumGlyphs; }
1773
bsalomoncb02b382015-08-12 11:14:50 -07001774 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001775 TextBatch* that = t->cast<TextBatch>();
1776 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1777 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001778 return false;
1779 }
1780
bsalomon265697d2015-07-22 10:17:26 -07001781 if (fMaskType != that->fMaskType) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001782 return false;
1783 }
1784
bsalomon265697d2015-07-22 10:17:26 -07001785 if (!this->usesDistanceFields()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001786 // TODO we can often batch across LCD text if we have dual source blending and don't
1787 // have to use the blend constant
bsalomon265697d2015-07-22 10:17:26 -07001788 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001789 return false;
1790 }
joshualitt9bd2daf2015-04-17 09:30:06 -07001791 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1792 return false;
1793 }
1794 } else {
joshualitt9bd2daf2015-04-17 09:30:06 -07001795 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1796 return false;
1797 }
1798
1799 if (fFilteredColor != that->fFilteredColor) {
1800 return false;
1801 }
1802
joshualitt9bd2daf2015-04-17 09:30:06 -07001803 if (fUseBGR != that->fUseBGR) {
1804 return false;
1805 }
1806
joshualitt9bd2daf2015-04-17 09:30:06 -07001807 // TODO see note above
bsalomon265697d2015-07-22 10:17:26 -07001808 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) {
joshualitt0fe04a22015-08-25 12:05:50 -07001809 return false;
joshualitt9bd2daf2015-04-17 09:30:06 -07001810 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001811 }
1812
1813 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001814
bsalomond602f4d2015-07-27 06:12:01 -07001815 // Reallocate space for geo data if necessary and then import that's geo data.
1816 int newGeoCount = that->fGeoCount + fGeoCount;
1817 // We assume (and here enforce) that the allocation size is the smallest power of two that
1818 // is greater than or equal to the number of geometries (and at least
1819 // kMinGeometryAllocated).
1820 int newAllocSize = GrNextPow2(newGeoCount);
1821 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
1822
bsalomon16ed6ad2015-07-29 06:54:33 -07001823 if (newGeoCount > currAllocSize) {
bsalomond602f4d2015-07-27 06:12:01 -07001824 fGeoData.realloc(newAllocSize);
joshualittad802c62015-04-15 05:31:57 -07001825 }
1826
bsalomond602f4d2015-07-27 06:12:01 -07001827 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
bsalomon1c634362015-07-27 07:00:00 -07001828 // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
1829 // it doesn't try to unref them.
1830#ifdef SK_DEBUG
1831 for (int i = 0; i < that->fGeoCount; ++i) {
1832 that->fGeoData.get()[i].fBlob = (Blob*)0x1;
joshualittad802c62015-04-15 05:31:57 -07001833 }
bsalomon1c634362015-07-27 07:00:00 -07001834#endif
1835 that->fGeoCount = 0;
bsalomond602f4d2015-07-27 06:12:01 -07001836 fGeoCount = newGeoCount;
joshualitt99c7c072015-05-01 13:43:30 -07001837
1838 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001839 return true;
1840 }
1841
joshualitt9bd2daf2015-04-17 09:30:06 -07001842 // TODO just use class params
1843 // TODO trying to figure out why lcd is so whack
1844 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1845 GrColor color, GrTexture* texture) {
1846 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
bsalomon265697d2015-07-22 10:17:26 -07001847 bool isLCD = this->isLCD();
joshualitt9bd2daf2015-04-17 09:30:06 -07001848 // set up any flags
bsalomon265697d2015-07-22 10:17:26 -07001849 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt9bd2daf2015-04-17 09:30:06 -07001850
1851 // see if we need to create a new effect
bsalomon265697d2015-07-22 10:17:26 -07001852 if (isLCD) {
1853 flags |= kUseLCD_DistanceFieldEffectFlag;
1854 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
1855 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1856
joshualitt9bd2daf2015-04-17 09:30:06 -07001857 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1858
1859 float redCorrection =
1860 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1861 float greenCorrection =
1862 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1863 float blueCorrection =
1864 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1865 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1866 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1867 greenCorrection,
1868 blueCorrection);
1869
1870 return GrDistanceFieldLCDTextGeoProc::Create(color,
1871 viewMatrix,
1872 texture,
1873 params,
1874 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001875 flags,
1876 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001877 } else {
1878 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07001879#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07001880 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07001881 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
1882 return GrDistanceFieldA8TextGeoProc::Create(color,
1883 viewMatrix,
1884 texture,
1885 params,
1886 correction,
joshualittb8c241a2015-05-19 08:23:30 -07001887 flags,
1888 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001889#else
1890 return GrDistanceFieldA8TextGeoProc::Create(color,
1891 viewMatrix,
1892 texture,
1893 params,
joshualittb8c241a2015-05-19 08:23:30 -07001894 flags,
1895 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001896#endif
1897 }
1898
1899 }
1900
joshualitt1d89e8d2015-04-01 12:40:54 -07001901 struct BatchTracker {
1902 GrColor fColor;
1903 SkMatrix fViewMatrix;
1904 bool fUsesLocalCoords;
1905 bool fColorIgnored;
1906 bool fCoverageIgnored;
1907 int fNumGlyphs;
1908 };
1909
1910 BatchTracker fBatch;
bsalomond602f4d2015-07-27 06:12:01 -07001911 // The minimum number of Geometry we will try to allocate.
1912 enum { kMinGeometryAllocated = 4 };
1913 SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
1914 int fGeoCount;
bsalomon265697d2015-07-22 10:17:26 -07001915
1916 enum MaskType {
1917 kGrayscaleCoverageMask_MaskType,
1918 kLCDCoverageMask_MaskType,
1919 kColorBitmapMask_MaskType,
1920 kGrayscaleDistanceField_MaskType,
1921 kLCDDistanceField_MaskType,
1922 } fMaskType;
1923 bool fUseBGR; // fold this into the enum?
1924
joshualitt1d89e8d2015-04-01 12:40:54 -07001925 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07001926
1927 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07001928 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07001929 SkColor fFilteredColor;
reed1b55a962015-09-17 20:16:13 -07001930
1931 typedef GrVertexBatch INHERITED;
joshualitt1d89e8d2015-04-01 12:40:54 -07001932};
1933
robertphillipsf6703fa2015-09-01 05:36:47 -07001934void GrAtlasTextContext::flushRunAsPaths(GrDrawContext* dc, GrRenderTarget* rt,
halcanary33779752015-10-27 14:01:05 -07001935 const SkTextBlobRunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07001936 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07001937 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
1938 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
1939 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07001940
joshualitt9a27e632015-04-06 10:53:36 -07001941 size_t textLen = it.glyphCount() * sizeof(uint16_t);
1942 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07001943
joshualitt9a27e632015-04-06 10:53:36 -07001944 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07001945
joshualitt9a27e632015-04-06 10:53:36 -07001946 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
1947 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07001948 }
1949
robertphillipsfcf78292015-06-19 11:49:52 -07001950 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07001951
1952 switch (it.positioning()) {
1953 case SkTextBlob::kDefault_Positioning:
robertphillipsf6703fa2015-09-01 05:36:47 -07001954 this->drawTextAsPath(dc, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07001955 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07001956 textLen, x + offset.x(), y + offset.y(), clipBounds);
1957 break;
1958 case SkTextBlob::kHorizontal_Positioning:
robertphillipsf6703fa2015-09-01 05:36:47 -07001959 this->drawPosTextAsPath(dc, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07001960 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07001961 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
1962 clipBounds);
1963 break;
1964 case SkTextBlob::kFull_Positioning:
robertphillipsf6703fa2015-09-01 05:36:47 -07001965 this->drawPosTextAsPath(dc, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07001966 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07001967 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
1968 break;
1969 }
1970}
1971
bsalomonabd30f52015-08-13 13:34:48 -07001972inline GrDrawBatch*
joshualitt374b2f72015-07-21 08:05:03 -07001973GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info,
joshualitt79dfb2b2015-05-11 08:58:08 -07001974 int glyphCount, int run, int subRun,
1975 GrColor color, SkScalar transX, SkScalar transY,
1976 const SkPaint& skPaint) {
1977 GrMaskFormat format = info.fMaskFormat;
1978 GrColor subRunColor;
1979 if (kARGB_GrMaskFormat == format) {
1980 uint8_t paintAlpha = skPaint.getAlpha();
1981 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
1982 } else {
1983 subRunColor = color;
1984 }
1985
bsalomon265697d2015-07-22 10:17:26 -07001986 TextBatch* batch;
joshualitt79dfb2b2015-05-11 08:58:08 -07001987 if (info.fDrawAsDistanceFields) {
1988 SkColor filteredColor;
1989 SkColorFilter* colorFilter = skPaint.getColorFilter();
1990 if (colorFilter) {
1991 filteredColor = colorFilter->filterColor(skPaint.getColor());
1992 } else {
1993 filteredColor = skPaint.getColor();
1994 }
robertphillipsfcf78292015-06-19 11:49:52 -07001995 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
bsalomon265697d2015-07-22 10:17:26 -07001996 batch = TextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
1997 fDistanceAdjustTable, filteredColor,
1998 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07001999 } else {
bsalomon265697d2015-07-22 10:17:26 -07002000 batch = TextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
joshualitt79dfb2b2015-05-11 08:58:08 -07002001 }
bsalomon265697d2015-07-22 10:17:26 -07002002 TextBatch::Geometry& geometry = batch->geometry();
joshualitt79dfb2b2015-05-11 08:58:08 -07002003 geometry.fBlob = SkRef(cacheBlob);
2004 geometry.fRun = run;
2005 geometry.fSubRun = subRun;
2006 geometry.fColor = subRunColor;
2007 geometry.fTransX = transX;
2008 geometry.fTransY = transY;
2009 batch->init();
2010
2011 return batch;
2012}
2013
robertphillipsf6703fa2015-09-01 05:36:47 -07002014inline void GrAtlasTextContext::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder,
joshualitt374b2f72015-07-21 08:05:03 -07002015 GrAtlasTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002016 SkScalar transX, SkScalar transY,
2017 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002018 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2019 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2020 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2021 if (0 == glyphCount) {
2022 continue;
2023 }
2024
bsalomonabd30f52015-08-13 13:34:48 -07002025 SkAutoTUnref<GrDrawBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2026 subRun, color, transX, transY,
2027 skPaint));
robertphillipsf6703fa2015-09-01 05:36:47 -07002028 dc->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002029 }
2030}
2031
robertphillipsf6703fa2015-09-01 05:36:47 -07002032inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob,
2033 GrDrawContext* dc, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002034 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002035 SkScalar transX, SkScalar transY,
2036 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002037 if (!cacheBlob->fBigGlyphs.count()) {
2038 return;
2039 }
2040
joshualitt9a27e632015-04-06 10:53:36 -07002041 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt374b2f72015-07-21 08:05:03 -07002042 GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002043 bigGlyph.fVx += transX;
2044 bigGlyph.fVy += transY;
joshualitt0fe04a22015-08-25 12:05:50 -07002045 SkMatrix ctm;
2046 ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
2047 ctm.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2048 if (bigGlyph.fApplyVM) {
2049 ctm.postConcat(cacheBlob->fViewMatrix);
2050 }
joshualittfc072562015-05-13 12:15:06 -07002051
robertphillipsf6703fa2015-09-01 05:36:47 -07002052 GrBlurUtils::drawPathWithMaskFilter(fContext, dc, rt, clip, bigGlyph.fPath,
joshualitt0fe04a22015-08-25 12:05:50 -07002053 skPaint, ctm, nullptr, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002054 }
2055}
joshualitt9a27e632015-04-06 10:53:36 -07002056
robertphillips2334fb62015-06-17 05:43:33 -07002057void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt374b2f72015-07-21 08:05:03 -07002058 GrAtlasTextBlob* cacheBlob,
robertphillipsf6703fa2015-09-01 05:36:47 -07002059 GrDrawContext* dc,
joshualitt9a27e632015-04-06 10:53:36 -07002060 GrRenderTarget* rt,
2061 const SkPaint& skPaint,
2062 const GrPaint& grPaint,
2063 SkDrawFilter* drawFilter,
2064 const GrClip& clip,
2065 const SkMatrix& viewMatrix,
2066 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002067 SkScalar x, SkScalar y,
2068 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002069 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2070 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002071 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002072
2073 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002074
halcanary33779752015-10-27 14:01:05 -07002075 SkTextBlobRunIterator it(blob);
joshualitt9a27e632015-04-06 10:53:36 -07002076 for (int run = 0; !it.done(); it.next(), run++) {
2077 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillipsf6703fa2015-09-01 05:36:47 -07002078 this->flushRunAsPaths(dc, rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002079 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002080 continue;
2081 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002082 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillipsf6703fa2015-09-01 05:36:47 -07002083 this->flushRun(dc, &pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002084 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002085 }
2086
2087 // Now flush big glyphs
robertphillipsf6703fa2015-09-01 05:36:47 -07002088 this->flushBigGlyphs(cacheBlob, dc, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002089}
2090
joshualitt374b2f72015-07-21 08:05:03 -07002091void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob,
robertphillipsf6703fa2015-09-01 05:36:47 -07002092 GrDrawContext* dc,
joshualitt9a27e632015-04-06 10:53:36 -07002093 GrRenderTarget* rt,
2094 const SkPaint& skPaint,
2095 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002096 const GrClip& clip,
2097 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002098 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002099
2100 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002101 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillipsf6703fa2015-09-01 05:36:47 -07002102 this->flushRun(dc, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002103 }
2104
2105 // Now flush big glyphs
robertphillipsf6703fa2015-09-01 05:36:47 -07002106 this->flushBigGlyphs(cacheBlob, dc, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002107}
joshualitt79dfb2b2015-05-11 08:58:08 -07002108
2109///////////////////////////////////////////////////////////////////////////////////////////////////
2110
2111#ifdef GR_TEST_UTILS
2112
bsalomonabd30f52015-08-13 13:34:48 -07002113DRAW_BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002114 static uint32_t gContextID = SK_InvalidGenID;
halcanary96fcdcc2015-08-27 07:41:13 -07002115 static GrAtlasTextContext* gTextContext = nullptr;
robertphillipsfcf78292015-06-19 11:49:52 -07002116 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002117
2118 if (context->uniqueID() != gContextID) {
2119 gContextID = context->uniqueID();
halcanary385fe4d2015-08-26 13:07:48 -07002120 delete gTextContext;
robertphillips2334fb62015-06-17 05:43:33 -07002121
joshualitt79dfb2b2015-05-11 08:58:08 -07002122 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2123 // because we don't really want to have a gpu device here.
2124 // We enable distance fields by twiddling a knob on the paint
robertphillipsf6703fa2015-09-01 05:36:47 -07002125 gTextContext = GrAtlasTextContext::Create(context, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002126 }
2127
2128 // create dummy render target
2129 GrSurfaceDesc desc;
2130 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2131 desc.fWidth = 1024;
2132 desc.fHeight = 1024;
2133 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002134 desc.fSampleCnt = 0;
halcanary96fcdcc2015-08-27 07:41:13 -07002135 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, nullptr, 0));
joshualitt79dfb2b2015-05-11 08:58:08 -07002136 SkASSERT(texture);
halcanary96fcdcc2015-08-27 07:41:13 -07002137 SkASSERT(nullptr != texture->asRenderTarget());
joshualitt79dfb2b2015-05-11 08:58:08 -07002138 GrRenderTarget* rt = texture->asRenderTarget();
2139
2140 // Setup dummy SkPaint / GrPaint
2141 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002142 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002143 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002144 skPaint.setColor(color);
2145 skPaint.setLCDRenderText(random->nextBool());
2146 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2147 skPaint.setSubpixelText(random->nextBool());
2148
2149 GrPaint grPaint;
bsalomonf1b7a1d2015-09-28 06:26:28 -07002150 if (!SkPaintToGrPaint(context, skPaint, viewMatrix, &grPaint)) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002151 SkFAIL("couldn't convert paint\n");
2152 }
2153
2154 const char* text = "The quick brown fox jumps over the lazy dog.";
2155 int textLen = (int)strlen(text);
2156
2157 // Setup clip
2158 GrClip clip;
2159 SkIRect noClip = SkIRect::MakeLargest();
2160
2161 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2162 // intend to test the batch with this unit test, that is okay.
joshualitt374b2f72015-07-21 08:05:03 -07002163 SkAutoTUnref<GrAtlasTextBlob> blob(
joshualitt79dfb2b2015-05-11 08:58:08 -07002164 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2165 static_cast<size_t>(textLen), 0, 0, noClip));
2166
2167 SkScalar transX = static_cast<SkScalar>(random->nextU());
2168 SkScalar transY = static_cast<SkScalar>(random->nextU());
joshualitt374b2f72015-07-21 08:05:03 -07002169 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
joshualitt79dfb2b2015-05-11 08:58:08 -07002170 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2171}
2172
2173#endif