blob: 75aaf047b5d274ba0711e4f84daf18f230b0eec6 [file] [log] [blame]
joshualitt1d89e8d2015-04-01 12:40:54 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "GrAtlasTextContext.h"
8
joshualitt1d89e8d2015-04-01 12:40:54 -07009#include "GrBatch.h"
10#include "GrBatchFontCache.h"
11#include "GrBatchTarget.h"
joshualitt79dfb2b2015-05-11 08:58:08 -070012#include "GrBatchTest.h"
robertphillipsccb1b572015-05-27 11:02:55 -070013#include "GrBlurUtils.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070014#include "GrDefaultGeoProcFactory.h"
robertphillipsea461502015-05-26 11:38:03 -070015#include "GrDrawContext.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070016#include "GrFontScaler.h"
17#include "GrIndexBuffer.h"
bsalomoned0bcad2015-05-04 10:36:42 -070018#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070019#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070020#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070021#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070022#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070023
24#include "SkAutoKern.h"
25#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070026#include "SkColorFilter.h"
27#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070028#include "SkDraw.h"
29#include "SkDrawFilter.h"
30#include "SkDrawProcs.h"
31#include "SkGlyphCache.h"
32#include "SkGpuDevice.h"
33#include "SkGr.h"
34#include "SkPath.h"
35#include "SkRTConf.h"
36#include "SkStrokeRec.h"
37#include "SkTextBlob.h"
38#include "SkTextMapStateProc.h"
39
40#include "effects/GrBitmapTextGeoProc.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070041#include "effects/GrDistanceFieldGeoProc.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070042
43namespace {
44static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
45
46// position + local coord
47static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
48
49static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
50
joshualitt9bd2daf2015-04-17 09:30:06 -070051static const int kMinDFFontSize = 18;
52static const int kSmallDFFontSize = 32;
53static const int kSmallDFFontLimit = 32;
54static const int kMediumDFFontSize = 72;
55static const int kMediumDFFontLimit = 72;
56static const int kLargeDFFontSize = 162;
joshualitta7c63892015-04-21 13:24:37 -070057static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
joshualitt9bd2daf2015-04-17 09:30:06 -070058
jvanverth0fce1fb2015-06-16 08:46:24 -070059static const int kLargeGlyphLimit = 192;
60
joshualitt9bd2daf2015-04-17 09:30:06 -070061SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
62static const int kDistanceAdjustLumShift = 5;
63
joshualitt1d89e8d2015-04-01 12:40:54 -070064static const int kVerticesPerGlyph = 4;
65static const int kIndicesPerGlyph = 6;
66
67static size_t get_vertex_stride(GrMaskFormat maskFormat) {
68 switch (maskFormat) {
69 case kA8_GrMaskFormat:
70 return kGrayTextVASize;
71 case kARGB_GrMaskFormat:
72 return kColorTextVASize;
73 default:
74 return kLCDTextVASize;
75 }
76}
77
joshualitt9bd2daf2015-04-17 09:30:06 -070078static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
79 SkASSERT(maskFormat == kA8_GrMaskFormat);
80 if (useLCDText) {
81 return kLCDTextVASize;
82 } else {
83 return kGrayTextVASize;
84 }
85}
86
87static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
88 unsigned r = SkColorGetR(c);
89 unsigned g = SkColorGetG(c);
90 unsigned b = SkColorGetB(c);
91 return GrColorPackRGBA(r, g, b, 0xff);
92}
93
joshualitt1d89e8d2015-04-01 12:40:54 -070094};
95
96// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -070097// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -070098
joshualittdbd35932015-04-02 09:19:04 -070099GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
joshualitt9bd2daf2015-04-17 09:30:06 -0700100 const SkDeviceProperties& properties,
bsalomon781d5822015-06-16 15:03:11 -0700101 bool enableDistanceFields)
102 : INHERITED(context, properties)
joshualitt9bd2daf2015-04-17 09:30:06 -0700103 , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
joshualittb7133be2015-04-08 09:08:31 -0700104 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
105 // vertexStride
106 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
107 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700108 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700109 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700110
111#if SK_FORCE_DISTANCE_FIELD_TEXT
112 fEnableDFRendering = true;
113#else
bsalomon781d5822015-06-16 15:03:11 -0700114 fEnableDFRendering = enableDistanceFields;
joshualitt9bd2daf2015-04-17 09:30:06 -0700115#endif
116}
117
118void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
119
120 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
121 // text. The mask gamma hack is based off of guessing what the blend color is going to
122 // be, and adjusting the mask so that when run through the linear blend will
123 // produce the value closest to the desired result. However, in practice this means
124 // that the 'adjusted' mask is just increasing or decreasing the coverage of
125 // the mask depending on what it is thought it will blit against. For black (on
126 // assumed white) this means that coverages are decreased (on a curve). For white (on
127 // assumed black) this means that coverages are increased (on a a curve). At
128 // middle (perceptual) gray (which could be blit against anything) the coverages
129 // remain the same.
130 //
131 // The idea here is that instead of determining the initial (real) coverage and
132 // then adjusting that coverage, we determine an adjusted coverage directly by
133 // essentially manipulating the geometry (in this case, the distance to the glyph
134 // edge). So for black (on assumed white) this thins a bit; for white (on
135 // assumed black) this fake bolds the geometry a bit.
136 //
137 // The distance adjustment is calculated by determining the actual coverage value which
138 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
139 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
140 // actual edge. So by subtracting this distance adjustment and computing without the
141 // the coverage adjustment we should get 0.5 coverage at the same point.
142 //
143 // This has several implications:
144 // For non-gray lcd smoothed text, each subpixel essentially is using a
145 // slightly different geometry.
146 //
147 // For black (on assumed white) this may not cover some pixels which were
148 // previously covered; however those pixels would have been only slightly
149 // covered and that slight coverage would have been decreased anyway. Also, some pixels
150 // which were previously fully covered may no longer be fully covered.
151 //
152 // For white (on assumed black) this may cover some pixels which weren't
153 // previously covered at all.
154
155 int width, height;
156 size_t size;
157
158#ifdef SK_GAMMA_CONTRAST
159 SkScalar contrast = SK_GAMMA_CONTRAST;
160#else
161 SkScalar contrast = 0.5f;
162#endif
163 SkScalar paintGamma = gamma;
164 SkScalar deviceGamma = gamma;
165
166 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
167 &width, &height);
168
169 SkASSERT(kExpectedDistanceAdjustTableSize == height);
170 fTable = SkNEW_ARRAY(SkScalar, height);
171
172 SkAutoTArray<uint8_t> data((int)size);
173 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
174
175 // find the inverse points where we cross 0.5
176 // binsearch might be better, but we only need to do this once on creation
177 for (int row = 0; row < height; ++row) {
178 uint8_t* rowPtr = data.get() + row*width;
179 for (int col = 0; col < width - 1; ++col) {
180 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
181 // compute point where a mask value will give us a result of 0.5
182 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
183 float borderAlpha = (col + interp) / 255.f;
184
185 // compute t value for that alpha
186 // this is an approximate inverse for smoothstep()
187 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
188
189 // compute distance which gives us that t value
190 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
191 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
192
193 fTable[row] = d;
194 break;
195 }
196 }
197 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700198}
199
joshualittdbd35932015-04-02 09:19:04 -0700200GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
joshualitt9bd2daf2015-04-17 09:30:06 -0700201 const SkDeviceProperties& props,
bsalomon781d5822015-06-16 15:03:11 -0700202 bool enableDistanceFields) {
203 return SkNEW_ARGS(GrAtlasTextContext, (context, props, enableDistanceFields));
joshualitt1d89e8d2015-04-01 12:40:54 -0700204}
205
joshualittdbd35932015-04-02 09:19:04 -0700206bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
207 const GrClip&,
208 const GrPaint&,
209 const SkPaint& skPaint,
210 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700211 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
212 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700213}
214
joshualitt9e36c1a2015-04-14 12:17:27 -0700215GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
216 GrColor canonicalColor = paint.computeLuminanceColor();
217 if (lcd) {
218 // This is the correct computation, but there are tons of cases where LCD can be overridden.
219 // For now we just regenerate if any run in a textblob has LCD.
220 // TODO figure out where all of these overrides are and see if we can incorporate that logic
221 // at a higher level *OR* use sRGB
222 SkASSERT(false);
223 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
224 } else {
225 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
226 // gamma corrected masks anyways, nor color
227 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
228 SkColorGetG(canonicalColor),
229 SkColorGetB(canonicalColor));
230 // reduce to our finite number of bits
231 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
232 }
233 return canonicalColor;
234}
235
236// TODO if this function ever shows up in profiling, then we can compute this value when the
237// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
238// run so this is not a big deal to compute here.
239bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
240 SkTextBlob::RunIterator it(blob);
241 for (; !it.done(); it.next()) {
242 if (it.isLCD()) {
243 return true;
244 }
245 }
246 return false;
247}
248
joshualitt2a0e9f32015-04-13 06:12:21 -0700249bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
250 const BitmapTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700251 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700252 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700253 // If we have LCD text then our canonical color will be set to transparent, in this case we have
254 // to regenerate the blob on any color change
255 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700256 return true;
257 }
258
259 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
260 return true;
261 }
262
263 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
264 return true;
265 }
266
joshualitt53b5f442015-04-13 06:33:59 -0700267 // We only cache one masked version
268 if (blob.fKey.fHasBlur &&
269 (blob.fBlurRec.fSigma != blurRec.fSigma ||
270 blob.fBlurRec.fStyle != blurRec.fStyle ||
271 blob.fBlurRec.fQuality != blurRec.fQuality)) {
272 return true;
273 }
274
275 // Similarly, we only cache one version for each style
276 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
277 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
278 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
279 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
280 return true;
281 }
282
joshualittfcfb9fc2015-04-21 07:35:10 -0700283 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
284 // for mixed blobs if this becomes an issue.
285 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700286 // Identical viewmatrices and we can reuse in all cases
287 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
288 return false;
289 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700290 return true;
291 }
292
joshualittfcfb9fc2015-04-21 07:35:10 -0700293 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700294 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
295 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
296 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
297 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
298 return true;
299 }
300
joshualittfcfb9fc2015-04-21 07:35:10 -0700301 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
302 // but only for integer translations.
303 // This cool bit of math will determine the necessary translation to apply to the already
304 // generated vertex coordinates to move them to the correct position
305 SkScalar transX = viewMatrix.getTranslateX() +
306 viewMatrix.getScaleX() * (x - blob.fX) +
307 viewMatrix.getSkewX() * (y - blob.fY) -
308 blob.fViewMatrix.getTranslateX();
309 SkScalar transY = viewMatrix.getTranslateY() +
310 viewMatrix.getSkewY() * (x - blob.fX) +
311 viewMatrix.getScaleY() * (y - blob.fY) -
312 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700313 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700314 return true;
315 }
316
joshualittfcfb9fc2015-04-21 07:35:10 -0700317 (*outTransX) = transX;
318 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700319 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700320 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
321 // distance field being generated, so we have to regenerate in those cases
322 SkScalar newMaxScale = viewMatrix.getMaxScale();
323 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
324 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
325 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
326 return true;
327 }
328
329 (*outTransX) = x - blob.fX;
330 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700331 }
joshualitta7c63892015-04-21 13:24:37 -0700332 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
333 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
334 // the blob anyways at flush time, so no need to regenerate explicitly
joshualittfcfb9fc2015-04-21 07:35:10 -0700335
joshualitt2a0e9f32015-04-13 06:12:21 -0700336 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700337}
338
339
joshualittdbd35932015-04-02 09:19:04 -0700340inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
341 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700342 const SkMatrix* viewMatrix,
343 bool noGamma) {
344 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700345 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
346 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
347}
348
robertphillips9c240a12015-05-28 07:45:59 -0700349void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700350 const GrClip& clip, const SkPaint& skPaint,
351 const SkMatrix& viewMatrix, const SkTextBlob* blob,
352 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700353 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700354 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700355 if (fContext->abandoned()) {
356 return;
357 }
358
bsalomon781d5822015-06-16 15:03:11 -0700359 GrDrawContext* drawContext = fContext->drawContext();
360 if (!drawContext) {
361 return;
362 }
363
joshualitt2a0e9f32015-04-13 06:12:21 -0700364 SkAutoTUnref<BitmapTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700365 SkMaskFilter::BlurRec blurRec;
366 BitmapTextBlob::Key key;
367 // It might be worth caching these things, but its not clear at this time
368 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
369 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700370 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700371 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700372 drawFilter);
373
374 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700375 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700376
377 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
378 SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() :
379 kUnknown_SkPixelGeometry;
380
joshualitt9e36c1a2015-04-14 12:17:27 -0700381 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
382 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
383 // ensure we always match the same key
384 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
385 ComputeCanonicalColor(skPaint, hasLCD);
386
joshualitte4cee1f2015-05-11 13:04:28 -0700387 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700388 key.fUniqueID = blob->uniqueID();
389 key.fStyle = skPaint.getStyle();
390 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700391 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700392 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700393 }
394
joshualitt1d89e8d2015-04-01 12:40:54 -0700395 SkIRect clipRect;
396 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
397
joshualitt2a0e9f32015-04-13 06:12:21 -0700398 SkScalar transX = 0.f;
399 SkScalar transY = 0.f;
400
joshualitt9e36c1a2015-04-14 12:17:27 -0700401 // Though for the time being runs in the textblob can override the paint, they only touch font
402 // info.
403 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700404 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
405 return;
406 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700407
joshualittb7133be2015-04-08 09:08:31 -0700408 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700409 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700410 // We have to remake the blob because changes may invalidate our masks.
411 // TODO we could probably get away reuse most of the time if the pointer is unique,
412 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700413 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700414 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
415 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700416 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700417 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700418 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700419 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
420 // offsets
joshualitt2a0e9f32015-04-13 06:12:21 -0700421 cacheBlob->fViewMatrix = viewMatrix;
422 cacheBlob->fX = x;
423 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700424 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700425 }
426 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700427 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700428 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
429 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700430 } else {
431 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
432 }
robertphillips9c240a12015-05-28 07:45:59 -0700433 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700434 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700435 }
436
joshualitt9e36c1a2015-04-14 12:17:27 -0700437 cacheBlob->fPaintColor = skPaint.getColor();
bsalomon781d5822015-06-16 15:03:11 -0700438 this->flush(drawContext, blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700439 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700440}
441
joshualitt9bd2daf2015-04-17 09:30:06 -0700442inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
443 const SkMatrix& viewMatrix) {
444 // TODO: support perspective (need getMaxScale replacement)
445 if (viewMatrix.hasPerspective()) {
446 return false;
447 }
448
449 SkScalar maxScale = viewMatrix.getMaxScale();
450 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
451 // Hinted text looks far better at small resolutions
452 // Scaling up beyond 2x yields undesireable artifacts
joshualitta7c63892015-04-21 13:24:37 -0700453 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700454 return false;
455 }
456
457 if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
jvanverth0fce1fb2015-06-16 08:46:24 -0700458 scaledTextSize < kLargeGlyphLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700459 return false;
460 }
461
462 // rasterizers and mask filters modify alpha, which doesn't
463 // translate well to distance
464 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700465 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700466 return false;
467 }
468
469 // TODO: add some stroking support
470 if (skPaint.getStyle() != SkPaint::kFill_Style) {
471 return false;
472 }
473
474 return true;
475}
476
robertphillips9c240a12015-05-28 07:45:59 -0700477void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700478 const SkPaint& skPaint, GrColor color,
479 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700480 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700481 SkDrawFilter* drawFilter, const SkIRect& clipRect,
482 GrRenderTarget* rt, const GrClip& clip,
483 const GrPaint& paint) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700484 cacheBlob->fViewMatrix = viewMatrix;
485 cacheBlob->fX = x;
486 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700487
488 // Regenerate textblob
489 SkPaint runPaint = skPaint;
490 SkTextBlob::RunIterator it(blob);
491 for (int run = 0; !it.done(); it.next(), run++) {
492 int glyphCount = it.glyphCount();
493 size_t textLen = glyphCount * sizeof(uint16_t);
494 const SkPoint& offset = it.offset();
495 // applyFontToPaint() always overwrites the exact same attributes,
496 // so it is safe to not re-seed the paint for this reason.
497 it.applyFontToPaint(&runPaint);
498
499 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
500 // A false return from filter() means we should abort the current draw.
501 runPaint = skPaint;
502 continue;
503 }
504
robertphillips9c240a12015-05-28 07:45:59 -0700505 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700506
joshualitt1d89e8d2015-04-01 12:40:54 -0700507 // setup vertex / glyphIndex for the new run
508 if (run > 0) {
509 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
510 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
511
512 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
513 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
514
515 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
516 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
517 }
518
joshualittfcfb9fc2015-04-21 07:35:10 -0700519 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
520 cacheBlob->setHasDistanceField();
521 SkPaint dfPaint = runPaint;
522 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700523 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700524 Run& runIdx = cacheBlob->fRuns[run];
525 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
526 subRun.fUseLCDText = runPaint.isLCDRenderText();
527 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700528
joshualittfcfb9fc2015-04-21 07:35:10 -0700529 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
530
531 SkTDArray<char> fallbackTxt;
532 SkTDArray<SkScalar> fallbackPos;
533 SkPoint dfOffset;
534 int scalarsPerPosition = 2;
535 switch (it.positioning()) {
536 case SkTextBlob::kDefault_Positioning: {
537 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
538 (const char *)it.glyphs(), textLen,
539 x + offset.x(), y + offset.y(), clipRect, textRatio,
540 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
541 break;
542 }
543 case SkTextBlob::kHorizontal_Positioning: {
544 scalarsPerPosition = 1;
545 dfOffset = SkPoint::Make(x, y + offset.y());
546 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
547 (const char*)it.glyphs(), textLen, it.pos(),
548 scalarsPerPosition, dfOffset, clipRect, textRatio,
549 &fallbackTxt, &fallbackPos);
550 break;
551 }
552 case SkTextBlob::kFull_Positioning: {
553 dfOffset = SkPoint::Make(x, y);
554 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
555 (const char*)it.glyphs(), textLen, it.pos(),
556 scalarsPerPosition, dfOffset, clipRect, textRatio,
557 &fallbackTxt, &fallbackPos);
558 break;
559 }
560 }
561 if (fallbackTxt.count()) {
562 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
563 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
564 clipRect);
565 }
566
567 SkGlyphCache::AttachCache(cache);
568 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
569 cacheBlob->fRuns[run].fDrawAsPaths = true;
570 } else {
571 cacheBlob->setHasBitmap();
572 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
573 false);
574 switch (it.positioning()) {
575 case SkTextBlob::kDefault_Positioning:
576 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
577 (const char *)it.glyphs(), textLen,
578 x + offset.x(), y + offset.y(), clipRect);
579 break;
580 case SkTextBlob::kHorizontal_Positioning:
581 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
582 (const char*)it.glyphs(), textLen, it.pos(), 1,
583 SkPoint::Make(x, y + offset.y()), clipRect);
584 break;
585 case SkTextBlob::kFull_Positioning:
586 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
587 (const char*)it.glyphs(), textLen, it.pos(), 2,
588 SkPoint::Make(x, y), clipRect);
589 break;
590 }
591 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700592 }
593
594 if (drawFilter) {
595 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
596 runPaint = skPaint;
597 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700598 }
599}
600
joshualitt64c99cc2015-04-21 09:43:03 -0700601inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
602 SkPaint* skPaint,
603 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700604 const SkMatrix& viewMatrix) {
605 // getMaxScale doesn't support perspective, so neither do we at the moment
606 SkASSERT(!viewMatrix.hasPerspective());
607 SkScalar maxScale = viewMatrix.getMaxScale();
608 SkScalar textSize = skPaint->getTextSize();
609 SkScalar scaledTextSize = textSize;
610 // if we have non-unity scale, we need to choose our base text size
611 // based on the SkPaint's text size multiplied by the max scale factor
612 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
613 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
614 scaledTextSize *= maxScale;
615 }
616
joshualitt64c99cc2015-04-21 09:43:03 -0700617 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
618 // and ceiling. A scale outside of this range would require regenerating the distance fields
619 SkScalar dfMaskScaleFloor;
620 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700621 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700622 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700623 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700624 *textRatio = textSize / kSmallDFFontSize;
625 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
626 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700627 dfMaskScaleFloor = kSmallDFFontLimit;
628 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700629 *textRatio = textSize / kMediumDFFontSize;
630 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
631 } else {
joshualitta7c63892015-04-21 13:24:37 -0700632 dfMaskScaleFloor = kMediumDFFontLimit;
633 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700634 *textRatio = textSize / kLargeDFFontSize;
635 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
636 }
637
joshualitt64c99cc2015-04-21 09:43:03 -0700638 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
639 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
640 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
641 // tolerate before we'd have to move to a large mip size. When we actually test these values
642 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
643 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
644 // level)
joshualitta7c63892015-04-21 13:24:37 -0700645 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700646 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
647 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
648
joshualitt9bd2daf2015-04-17 09:30:06 -0700649 skPaint->setLCDRenderText(false);
650 skPaint->setAutohinted(false);
651 skPaint->setHinting(SkPaint::kNormal_Hinting);
652 skPaint->setSubpixelText(true);
653}
654
joshualittfec19e12015-04-17 10:32:32 -0700655inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700656 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700657 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700658 const GrPaint& paint,
659 const SkPaint& skPaint,
660 const SkMatrix& viewMatrix,
661 const SkTDArray<char>& fallbackTxt,
662 const SkTDArray<SkScalar>& fallbackPos,
663 int scalarsPerPosition,
664 const SkPoint& offset,
665 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700666 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700667 blob->setHasBitmap();
668 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700669 // Push back a new subrun to fill and set the override descriptor
670 run.push_back();
671 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
672 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
joshualittfec19e12015-04-17 10:32:32 -0700673 &fDeviceProperties, &viewMatrix, false);
674 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700675 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700676 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700677 fallbackTxt.begin(), fallbackTxt.count(),
678 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
679 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700680}
681
682inline GrAtlasTextContext::BitmapTextBlob*
683GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
684 const SkMatrix& viewMatrix, SkGlyphCache** cache,
685 SkPaint* dfPaint, SkScalar* textRatio) {
686 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
687
688 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700689 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700690 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700691 Run& run = blob->fRuns[0];
692 PerSubRunInfo& subRun = run.fSubRunInfo.back();
693 subRun.fUseLCDText = origPaint.isLCDRenderText();
694 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700695
696 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
697 return blob;
698}
699
joshualitt79dfb2b2015-05-11 08:58:08 -0700700inline GrAtlasTextContext::BitmapTextBlob*
701GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
702 const GrPaint& paint, const SkPaint& skPaint,
703 const SkMatrix& viewMatrix,
704 const char text[], size_t byteLength,
705 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700706 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700707 SkIRect clipRect;
708 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
709
joshualitt79dfb2b2015-05-11 08:58:08 -0700710 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700711 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
712 SkPaint dfPaint;
713 SkScalar textRatio;
714 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700715 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700716
joshualitt9bd2daf2015-04-17 09:30:06 -0700717 SkTDArray<char> fallbackTxt;
718 SkTDArray<SkScalar> fallbackPos;
719 SkPoint offset;
720 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
721 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
722 &offset, skPaint);
723 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700724 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700725 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700726 fallbackPos, 2, offset, clipRect);
727 }
728 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700729 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700730 blob->fViewMatrix = viewMatrix;
731
732 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
733 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
734 byteLength, x, y, clipRect);
735 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700736 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700737 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700738}
739
joshualitt79dfb2b2015-05-11 08:58:08 -0700740inline GrAtlasTextContext::BitmapTextBlob*
741GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
742 const GrPaint& paint, const SkPaint& skPaint,
743 const SkMatrix& viewMatrix,
744 const char text[], size_t byteLength,
745 const SkScalar pos[], int scalarsPerPosition,
746 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700747 int glyphCount = skPaint.countText(text, byteLength);
748
749 SkIRect clipRect;
750 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
751
joshualitt79dfb2b2015-05-11 08:58:08 -0700752 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700753 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
754 SkPaint dfPaint;
755 SkScalar textRatio;
756 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700757 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700758
759 SkTDArray<char> fallbackTxt;
760 SkTDArray<SkScalar> fallbackPos;
761 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
762 byteLength, pos, scalarsPerPosition, offset, clipRect,
763 textRatio, &fallbackTxt, &fallbackPos);
764 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700765 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700766 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700767 fallbackPos, scalarsPerPosition, offset, clipRect);
768 }
769 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700770 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700771 blob->fViewMatrix = viewMatrix;
772 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
773 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
774 byteLength, pos, scalarsPerPosition, offset, clipRect);
775 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700776 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700777 return blob;
778}
779
bsalomon781d5822015-06-16 15:03:11 -0700780void GrAtlasTextContext::onDrawText(GrDrawContext* drawContext, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700781 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700782 const GrPaint& paint, const SkPaint& skPaint,
783 const SkMatrix& viewMatrix,
784 const char text[], size_t byteLength,
785 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
bsalomon781d5822015-06-16 15:03:11 -0700786 if (drawContext) {
787 SkAutoTUnref<BitmapTextBlob> blob(
788 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
789 text, byteLength, x, y, regionClipBounds));
790 this->flush(drawContext, blob, rt, skPaint, paint, clip, regionClipBounds);
791 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700792}
793
bsalomon781d5822015-06-16 15:03:11 -0700794void GrAtlasTextContext::onDrawPosText(GrDrawContext* drawContext, 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) {
bsalomon781d5822015-06-16 15:03:11 -0700801 if (drawContext) {
802 SkAutoTUnref<BitmapTextBlob> blob(
803 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
804 text, byteLength,
805 pos, scalarsPerPosition,
806 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700807
bsalomon781d5822015-06-16 15:03:11 -0700808 this->flush(drawContext, blob, rt, skPaint, paint, clip, regionClipBounds);
809 }
joshualitt9bd2daf2015-04-17 09:30:06 -0700810}
811
812void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
813 SkGlyphCache* cache, const SkPaint& skPaint,
814 GrColor color,
815 const SkMatrix& viewMatrix,
816 const char text[], size_t byteLength,
817 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700818 SkASSERT(byteLength == 0 || text != NULL);
819
820 // nothing to draw
821 if (text == NULL || byteLength == 0) {
822 return;
823 }
824
825 fCurrStrike = NULL;
826 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
827
828 // Get GrFontScaler from cache
829 GrFontScaler* fontScaler = GetGrFontScaler(cache);
830
831 // transform our starting point
832 {
833 SkPoint loc;
834 viewMatrix.mapXY(x, y, &loc);
835 x = loc.fX;
836 y = loc.fY;
837 }
838
839 // need to measure first
840 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
841 SkVector stopVector;
842 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
843
844 SkScalar stopX = stopVector.fX;
845 SkScalar stopY = stopVector.fY;
846
847 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
848 stopX = SkScalarHalf(stopX);
849 stopY = SkScalarHalf(stopY);
850 }
851 x -= stopX;
852 y -= stopY;
853 }
854
855 const char* stop = text + byteLength;
856
857 SkAutoKern autokern;
858
859 SkFixed fxMask = ~0;
860 SkFixed fyMask = ~0;
861 SkScalar halfSampleX, halfSampleY;
862 if (cache->isSubpixel()) {
863 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
864 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
865 if (kX_SkAxisAlignment == baseline) {
866 fyMask = 0;
867 halfSampleY = SK_ScalarHalf;
868 } else if (kY_SkAxisAlignment == baseline) {
869 fxMask = 0;
870 halfSampleX = SK_ScalarHalf;
871 }
872 } else {
873 halfSampleX = halfSampleY = SK_ScalarHalf;
874 }
875
876 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
877 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
878
879 while (text < stop) {
880 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
881
882 fx += autokern.adjust(glyph);
883
884 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700885 this->bmpAppendGlyph(blob,
886 runIndex,
887 GrGlyph::Pack(glyph.getGlyphID(),
888 glyph.getSubXFixed(),
889 glyph.getSubYFixed(),
890 GrGlyph::kCoverage_MaskStyle),
891 Sk48Dot16FloorToInt(fx),
892 Sk48Dot16FloorToInt(fy),
893 color,
894 fontScaler,
895 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700896 }
897
898 fx += glyph.fAdvanceX;
899 fy += glyph.fAdvanceY;
900 }
901}
902
joshualitt9bd2daf2015-04-17 09:30:06 -0700903void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
904 SkGlyphCache* cache, const SkPaint& skPaint,
905 GrColor color,
906 const SkMatrix& viewMatrix,
907 const char text[], size_t byteLength,
908 const SkScalar pos[], int scalarsPerPosition,
909 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700910 SkASSERT(byteLength == 0 || text != NULL);
911 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
912
913 // nothing to draw
914 if (text == NULL || byteLength == 0) {
915 return;
916 }
917
918 fCurrStrike = NULL;
919 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
920
921 // Get GrFontScaler from cache
922 GrFontScaler* fontScaler = GetGrFontScaler(cache);
923
924 const char* stop = text + byteLength;
925 SkTextAlignProc alignProc(skPaint.getTextAlign());
926 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
927
928 if (cache->isSubpixel()) {
929 // maybe we should skip the rounding if linearText is set
930 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
931
932 SkFixed fxMask = ~0;
933 SkFixed fyMask = ~0;
934 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
935 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
936 if (kX_SkAxisAlignment == baseline) {
937 fyMask = 0;
938 halfSampleY = SK_ScalarHalf;
939 } else if (kY_SkAxisAlignment == baseline) {
940 fxMask = 0;
941 halfSampleX = SK_ScalarHalf;
942 }
943
944 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
945 while (text < stop) {
946 SkPoint tmsLoc;
947 tmsProc(pos, &tmsLoc);
948 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
949 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
950
951 const SkGlyph& glyph = glyphCacheProc(cache, &text,
952 fx & fxMask, fy & fyMask);
953
954 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700955 this->bmpAppendGlyph(blob,
956 runIndex,
957 GrGlyph::Pack(glyph.getGlyphID(),
958 glyph.getSubXFixed(),
959 glyph.getSubYFixed(),
960 GrGlyph::kCoverage_MaskStyle),
961 Sk48Dot16FloorToInt(fx),
962 Sk48Dot16FloorToInt(fy),
963 color,
964 fontScaler,
965 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700966 }
967 pos += scalarsPerPosition;
968 }
969 } else {
970 while (text < stop) {
971 const char* currentText = text;
972 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
973
974 if (metricGlyph.fWidth) {
975 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
976 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
977 SkPoint tmsLoc;
978 tmsProc(pos, &tmsLoc);
979 SkPoint alignLoc;
980 alignProc(tmsLoc, metricGlyph, &alignLoc);
981
982 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
983 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
984
985 // have to call again, now that we've been "aligned"
986 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
987 fx & fxMask, fy & fyMask);
988 // the assumption is that the metrics haven't changed
989 SkASSERT(prevAdvX == glyph.fAdvanceX);
990 SkASSERT(prevAdvY == glyph.fAdvanceY);
991 SkASSERT(glyph.fWidth);
992
joshualitt9bd2daf2015-04-17 09:30:06 -0700993 this->bmpAppendGlyph(blob,
994 runIndex,
995 GrGlyph::Pack(glyph.getGlyphID(),
996 glyph.getSubXFixed(),
997 glyph.getSubYFixed(),
998 GrGlyph::kCoverage_MaskStyle),
999 Sk48Dot16FloorToInt(fx),
1000 Sk48Dot16FloorToInt(fy),
1001 color,
1002 fontScaler,
1003 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001004 }
1005 pos += scalarsPerPosition;
1006 }
1007 }
1008 } else { // not subpixel
1009
1010 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1011 while (text < stop) {
1012 // the last 2 parameters are ignored
1013 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1014
1015 if (glyph.fWidth) {
1016 SkPoint tmsLoc;
1017 tmsProc(pos, &tmsLoc);
1018
1019 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1020 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001021 this->bmpAppendGlyph(blob,
1022 runIndex,
1023 GrGlyph::Pack(glyph.getGlyphID(),
1024 glyph.getSubXFixed(),
1025 glyph.getSubYFixed(),
1026 GrGlyph::kCoverage_MaskStyle),
1027 Sk48Dot16FloorToInt(fx),
1028 Sk48Dot16FloorToInt(fy),
1029 color,
1030 fontScaler,
1031 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001032 }
1033 pos += scalarsPerPosition;
1034 }
1035 } else {
1036 while (text < stop) {
1037 // the last 2 parameters are ignored
1038 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1039
1040 if (glyph.fWidth) {
1041 SkPoint tmsLoc;
1042 tmsProc(pos, &tmsLoc);
1043
1044 SkPoint alignLoc;
1045 alignProc(tmsLoc, glyph, &alignLoc);
1046
1047 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1048 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001049 this->bmpAppendGlyph(blob,
1050 runIndex,
1051 GrGlyph::Pack(glyph.getGlyphID(),
1052 glyph.getSubXFixed(),
1053 glyph.getSubYFixed(),
1054 GrGlyph::kCoverage_MaskStyle),
1055 Sk48Dot16FloorToInt(fx),
1056 Sk48Dot16FloorToInt(fy),
1057 color,
1058 fontScaler,
1059 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001060 }
1061 pos += scalarsPerPosition;
1062 }
1063 }
1064 }
1065}
1066
joshualitt9bd2daf2015-04-17 09:30:06 -07001067
1068void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1069 SkGlyphCache* cache, const SkPaint& skPaint,
1070 GrColor color,
1071 const SkMatrix& viewMatrix,
1072 const char text[], size_t byteLength,
1073 SkScalar x, SkScalar y, const SkIRect& clipRect,
1074 SkScalar textRatio,
1075 SkTDArray<char>* fallbackTxt,
1076 SkTDArray<SkScalar>* fallbackPos,
1077 SkPoint* offset,
1078 const SkPaint& origPaint) {
1079 SkASSERT(byteLength == 0 || text != NULL);
1080
1081 // nothing to draw
1082 if (text == NULL || byteLength == 0) {
1083 return;
1084 }
1085
1086 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1087 SkAutoDescriptor desc;
1088 origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
1089 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1090 desc.getDesc());
1091
1092 SkTArray<SkScalar> positions;
1093
1094 const char* textPtr = text;
1095 SkFixed stopX = 0;
1096 SkFixed stopY = 0;
1097 SkFixed origin = 0;
1098 switch (origPaint.getTextAlign()) {
1099 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1100 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1101 case SkPaint::kLeft_Align: origin = 0; break;
1102 }
1103
1104 SkAutoKern autokern;
1105 const char* stop = text + byteLength;
1106 while (textPtr < stop) {
1107 // don't need x, y here, since all subpixel variants will have the
1108 // same advance
1109 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1110
1111 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1112 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1113
1114 SkFixed height = glyph.fAdvanceY;
1115 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1116
1117 stopX += width;
1118 stopY += height;
1119 }
1120 SkASSERT(textPtr == stop);
1121
1122 // now adjust starting point depending on alignment
1123 SkScalar alignX = SkFixedToScalar(stopX);
1124 SkScalar alignY = SkFixedToScalar(stopY);
1125 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1126 alignX = SkScalarHalf(alignX);
1127 alignY = SkScalarHalf(alignY);
1128 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1129 alignX = 0;
1130 alignY = 0;
1131 }
1132 x -= alignX;
1133 y -= alignY;
1134 *offset = SkPoint::Make(x, y);
1135
1136 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1137 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1138 fallbackPos);
1139 SkGlyphCache::AttachCache(origPaintCache);
1140}
1141
1142void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1143 SkGlyphCache* cache, const SkPaint& skPaint,
1144 GrColor color,
1145 const SkMatrix& viewMatrix,
1146 const char text[], size_t byteLength,
1147 const SkScalar pos[], int scalarsPerPosition,
1148 const SkPoint& offset, const SkIRect& clipRect,
1149 SkScalar textRatio,
1150 SkTDArray<char>* fallbackTxt,
1151 SkTDArray<SkScalar>* fallbackPos) {
1152
1153 SkASSERT(byteLength == 0 || text != NULL);
1154 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1155
1156 // nothing to draw
1157 if (text == NULL || byteLength == 0) {
1158 return;
1159 }
1160
1161 fCurrStrike = NULL;
1162
1163 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1164 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1165
1166 const char* stop = text + byteLength;
1167
1168 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1169 while (text < stop) {
1170 const char* lastText = text;
1171 // the last 2 parameters are ignored
1172 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1173
1174 if (glyph.fWidth) {
1175 SkScalar x = offset.x() + pos[0];
1176 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1177
1178 if (!this->dfAppendGlyph(blob,
1179 runIndex,
1180 GrGlyph::Pack(glyph.getGlyphID(),
1181 glyph.getSubXFixed(),
1182 glyph.getSubYFixed(),
1183 GrGlyph::kDistance_MaskStyle),
1184 x, y, color, fontScaler, clipRect,
1185 textRatio, viewMatrix)) {
1186 // couldn't append, send to fallback
1187 fallbackTxt->append(SkToInt(text-lastText), lastText);
1188 *fallbackPos->append() = pos[0];
1189 if (2 == scalarsPerPosition) {
1190 *fallbackPos->append() = pos[1];
1191 }
1192 }
1193 }
1194 pos += scalarsPerPosition;
1195 }
1196 } else {
1197 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1198 : SK_Scalar1;
1199 while (text < stop) {
1200 const char* lastText = text;
1201 // the last 2 parameters are ignored
1202 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1203
1204 if (glyph.fWidth) {
1205 SkScalar x = offset.x() + pos[0];
1206 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1207
1208 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1209 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1210
1211 if (!this->dfAppendGlyph(blob,
1212 runIndex,
1213 GrGlyph::Pack(glyph.getGlyphID(),
1214 glyph.getSubXFixed(),
1215 glyph.getSubYFixed(),
1216 GrGlyph::kDistance_MaskStyle),
1217 x - advanceX, y - advanceY, color,
1218 fontScaler,
1219 clipRect,
1220 textRatio,
1221 viewMatrix)) {
1222 // couldn't append, send to fallback
1223 fallbackTxt->append(SkToInt(text-lastText), lastText);
1224 *fallbackPos->append() = pos[0];
1225 if (2 == scalarsPerPosition) {
1226 *fallbackPos->append() = pos[1];
1227 }
1228 }
1229 }
1230 pos += scalarsPerPosition;
1231 }
1232 }
1233}
1234
1235void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1236 GrGlyph::PackedID packed,
1237 int vx, int vy, GrColor color, GrFontScaler* scaler,
1238 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001239 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001240 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001241 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001242 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001243 }
1244
1245 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001246 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001247 return;
1248 }
1249
1250 int x = vx + glyph->fBounds.fLeft;
1251 int y = vy + glyph->fBounds.fTop;
1252
1253 // keep them as ints until we've done the clip-test
1254 int width = glyph->fBounds.width();
1255 int height = glyph->fBounds.height();
1256
joshualitt2a0e9f32015-04-13 06:12:21 -07001257#if 0
1258 // Not checking the clip bounds might introduce a performance regression. However, its not
1259 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1260 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1261 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1262 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001263 // check if we clipped out
1264 if (clipRect.quickReject(x, y, x + width, y + height)) {
1265 return;
1266 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001267#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001268
1269 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001270 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001271 this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001272 return;
1273 }
1274
joshualitt1d89e8d2015-04-01 12:40:54 -07001275 GrMaskFormat format = glyph->fMaskFormat;
1276
1277 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1278 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001279 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001280 }
1281
1282 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001283
1284 size_t vertexStride = get_vertex_stride(format);
1285
1286 SkRect r;
1287 r.fLeft = SkIntToScalar(x);
1288 r.fTop = SkIntToScalar(y);
1289 r.fRight = r.fLeft + SkIntToScalar(width);
1290 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001291 subRun->fMaskFormat = format;
1292 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001293 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001294}
joshualitt1d89e8d2015-04-01 12:40:54 -07001295
joshualitt9bd2daf2015-04-17 09:30:06 -07001296bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1297 GrGlyph::PackedID packed,
1298 SkScalar sx, SkScalar sy, GrColor color,
1299 GrFontScaler* scaler,
1300 const SkIRect& clipRect,
1301 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001302 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001303 if (!fCurrStrike) {
1304 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001305 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001306 }
1307
1308 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001309 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001310 return true;
1311 }
1312
1313 // fallback to color glyph support
1314 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1315 return false;
1316 }
1317
1318 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1319 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1320 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1321 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1322
1323 SkScalar scale = textRatio;
1324 dx *= scale;
1325 dy *= scale;
1326 width *= scale;
1327 height *= scale;
1328 sx += dx;
1329 sy += dy;
1330 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1331
1332#if 0
1333 // check if we clipped out
1334 SkRect dstRect;
1335 viewMatrix.mapRect(&dstRect, glyphRect);
1336 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1337 SkScalarTruncToInt(dstRect.top()),
1338 SkScalarTruncToInt(dstRect.right()),
1339 SkScalarTruncToInt(dstRect.bottom()))) {
1340 return true;
1341 }
1342#endif
1343
1344 // TODO combine with the above
1345 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001346 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001347 this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001348 return true;
1349 }
1350
joshualitt9bd2daf2015-04-17 09:30:06 -07001351 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1352 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1353 subRun->fMaskFormat = kA8_GrMaskFormat;
1354
1355 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1356
1357 bool useColorVerts = !subRun->fUseLCDText;
1358 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001359 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001360 return true;
1361}
1362
1363inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
joshualitt19e4c022015-05-13 11:23:03 -07001364 GrFontScaler* scaler, SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001365 if (NULL == glyph->fPath) {
1366 SkPath* path = SkNEW(SkPath);
1367 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1368 // flag the glyph as being dead?
1369 SkDELETE(path);
1370 return;
1371 }
1372 glyph->fPath = path;
1373 }
1374 SkASSERT(glyph->fPath);
1375 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1376}
1377
1378inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1379 Run::SubRunInfo* subRun,
1380 const SkRect& positions, GrColor color,
1381 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001382 GrGlyph* glyph) {
1383 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001384 run->fVertexBounds.joinNonEmptyArg(positions);
1385 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001386
1387 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1388
joshualitt9bd2daf2015-04-17 09:30:06 -07001389 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001390 // V0
1391 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1392 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001393 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1394 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001395 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001396
joshualitt010db532015-04-21 10:07:26 -07001397 // V1
1398 position = reinterpret_cast<SkPoint*>(vertex);
1399 position->set(positions.fLeft, positions.fBottom);
1400 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001401 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001402 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001403
joshualitt010db532015-04-21 10:07:26 -07001404 // V2
1405 position = reinterpret_cast<SkPoint*>(vertex);
1406 position->set(positions.fRight, positions.fBottom);
1407 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001408 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001409 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001410
joshualitt010db532015-04-21 10:07:26 -07001411 // V3
1412 position = reinterpret_cast<SkPoint*>(vertex);
1413 position->set(positions.fRight, positions.fTop);
1414 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001415 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001416 } else {
1417 // V0
1418 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1419 position->set(positions.fLeft, positions.fTop);
1420 vertex += vertexStride;
1421
1422 // V1
1423 position = reinterpret_cast<SkPoint*>(vertex);
1424 position->set(positions.fLeft, positions.fBottom);
1425 vertex += vertexStride;
1426
1427 // V2
1428 position = reinterpret_cast<SkPoint*>(vertex);
1429 position->set(positions.fRight, positions.fBottom);
1430 vertex += vertexStride;
1431
1432 // V3
1433 position = reinterpret_cast<SkPoint*>(vertex);
1434 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001435 }
1436
1437 subRun->fGlyphEndIndex++;
1438 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1439}
1440
1441class BitmapTextBatch : public GrBatch {
1442public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001443 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualittdbd35932015-04-02 09:19:04 -07001444 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001445 typedef Blob::Run Run;
1446 typedef Run::SubRunInfo TextInfo;
1447 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001448 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001449 int fRun;
1450 int fSubRun;
1451 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001452 SkScalar fTransX;
1453 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001454 };
1455
joshualittad802c62015-04-15 05:31:57 -07001456 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1457 GrBatchFontCache* fontCache) {
1458 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -07001459 }
1460
joshualitt9bd2daf2015-04-17 09:30:06 -07001461 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1462 GrBatchFontCache* fontCache,
1463 DistanceAdjustTable* distanceAdjustTable,
1464 SkColor filteredColor, bool useLCDText,
1465 bool useBGR, float gamma) {
1466 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
1467 filteredColor, useLCDText, useBGR, gamma));
1468 }
1469
joshualitt1d89e8d2015-04-01 12:40:54 -07001470 const char* name() const override { return "BitmapTextBatch"; }
1471
1472 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1473 if (kARGB_GrMaskFormat == fMaskFormat) {
1474 out->setUnknownFourComponents();
1475 } else {
1476 out->setKnownFourComponents(fBatch.fColor);
1477 }
1478 }
1479
1480 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt9bd2daf2015-04-17 09:30:06 -07001481 if (!fUseDistanceFields) {
1482 // Bitmap Text
1483 if (kARGB_GrMaskFormat != fMaskFormat) {
1484 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
1485 out->setUnknownSingleComponent();
1486 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
1487 out->setUnknownOpaqueFourComponents();
1488 out->setUsingLCDCoverage();
1489 } else {
1490 out->setUnknownFourComponents();
1491 out->setUsingLCDCoverage();
1492 }
1493 } else {
1494 out->setKnownSingleComponent(0xff);
1495 }
1496 } else {
1497 // Distance fields
1498 if (!fUseLCDText) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001499 out->setUnknownSingleComponent();
joshualitt1d89e8d2015-04-01 12:40:54 -07001500 } else {
1501 out->setUnknownFourComponents();
1502 out->setUsingLCDCoverage();
1503 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001504 }
1505 }
1506
1507 void initBatchTracker(const GrPipelineInfo& init) override {
1508 // Handle any color overrides
1509 if (init.fColorIgnored) {
1510 fBatch.fColor = GrColor_ILLEGAL;
1511 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1512 fBatch.fColor = init.fOverrideColor;
1513 }
1514
1515 // setup batch properties
1516 fBatch.fColorIgnored = init.fColorIgnored;
1517 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1518 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1519 }
1520
bsalomonb5238a72015-05-05 07:49:49 -07001521 struct FlushInfo {
1522 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1523 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1524 int fGlyphsToFlush;
1525 int fVertexOffset;
1526 };
1527
joshualitt1d89e8d2015-04-01 12:40:54 -07001528 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1529 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1530 // TODO actually only invert if we don't have RGBA
1531 SkMatrix localMatrix;
1532 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1533 SkDebugf("Cannot invert viewmatrix\n");
1534 return;
1535 }
1536
joshualitt62db8ba2015-04-09 08:22:37 -07001537 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
1538 if (!texture) {
1539 SkDebugf("Could not allocate backing texture for atlas\n");
1540 return;
1541 }
1542
joshualitt9bd2daf2015-04-17 09:30:06 -07001543 SkAutoTUnref<const GrGeometryProcessor> gp;
1544 if (fUseDistanceFields) {
1545 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1546 texture));
1547 } else {
1548 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001549 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1550 texture,
1551 params,
1552 fMaskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001553 localMatrix,
1554 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001555 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001556
bsalomonb5238a72015-05-05 07:49:49 -07001557 FlushInfo flushInfo;
1558 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001559 size_t vertexStride = gp->getVertexStride();
joshualitt9bd2daf2015-04-17 09:30:06 -07001560 SkASSERT(vertexStride == (fUseDistanceFields ?
1561 get_vertex_stride_df(fMaskFormat, fUseLCDText) :
1562 get_vertex_stride(fMaskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001563
joshualittb8c241a2015-05-19 08:23:30 -07001564 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001565
1566 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001567 int instanceCount = fInstanceCount;
bsalomon8415abe2015-05-04 11:41:41 -07001568 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001569
robertphillipse40d3972015-05-07 09:51:43 -07001570 void* vertices = batchTarget->makeVertSpace(vertexStride,
1571 glyphCount * kVerticesPerGlyph,
1572 &vertexBuffer,
1573 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001574 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1575 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1576 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001577 SkDebugf("Could not allocate vertices\n");
1578 return;
1579 }
1580
1581 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1582
joshualitt25ba7ea2015-04-21 07:49:49 -07001583 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1584 // in a row
1585 const SkDescriptor* desc = NULL;
1586 SkGlyphCache* cache = NULL;
1587 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001588 SkTypeface* typeface = NULL;
1589
joshualitt1d89e8d2015-04-01 12:40:54 -07001590 for (int i = 0; i < instanceCount; i++) {
1591 Geometry& args = fGeoData[i];
1592 Blob* blob = args.fBlob;
1593 Run& run = blob->fRuns[args.fRun];
1594 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1595
1596 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001597 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
1598 run.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001599 bool regenerateColors;
1600 if (fUseDistanceFields) {
joshualittfcfb9fc2015-04-21 07:35:10 -07001601 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001602 } else {
1603 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
1604 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001605 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001606 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1607
1608 // We regenerate both texture coords and colors in the blob itself, and update the
1609 // atlas generation. If we don't end up purging any unused plots, we can avoid
1610 // regenerating the coords. We could take a finer grained approach to updating texture
1611 // coords but its not clear if the extra bookkeeping would offset any gains.
1612 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1613 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1614 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1615 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001616 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001617 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001618 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001619
1620 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1621 // generating its texture coords, we have to track whether or not the strike has
1622 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1623 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1624 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1625 // new strike, we instead keep our ref to the old strike and use the packed ids from
1626 // it. These ids will still be valid as long as we hold the ref. When we are done
1627 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1628 bool regenerateGlyphs = false;
1629 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001630 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001631 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001632
1633 // We can reuse if we have a valid strike and our descriptors / typeface are the
1634 // same
joshualitt97202d22015-04-22 13:47:02 -07001635 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1636 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001637 run.fDescriptor.getDesc();
1638 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1639 !(desc->equals(*newDesc))) {
1640 if (cache) {
1641 SkGlyphCache::AttachCache(cache);
1642 }
1643 desc = newDesc;
1644 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1645 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001646 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001647 typeface = run.fTypeface;
1648 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001649
joshualittae32c102015-04-21 09:37:57 -07001650 if (run.fStrike->isAbandoned()) {
1651 regenerateGlyphs = true;
1652 strike = fFontCache->getStrike(scaler);
1653 } else {
1654 strike = run.fStrike;
1655 }
1656 }
1657
1658 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001659 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001660 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1661 GrGlyph* glyph;
1662 if (regenerateGlyphs) {
1663 // Get the id from the old glyph, and use the new strike to lookup
1664 // the glyph.
1665 glyph = blob->fGlyphs[glyphOffset];
1666 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1667 scaler);
1668 }
1669 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001670 SkASSERT(glyph);
1671
1672 if (!fFontCache->hasGlyph(glyph) &&
1673 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
bsalomonb5238a72015-05-05 07:49:49 -07001674 this->flush(batchTarget, &flushInfo);
joshualittb8c241a2015-05-19 08:23:30 -07001675 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001676 brokenRun = glyphIdx > 0;
1677
joshualittae32c102015-04-21 09:37:57 -07001678 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1679 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001680 scaler);
1681 SkASSERT(success);
1682 }
joshualittb4c507e2015-04-08 08:07:59 -07001683 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1684 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001685
1686 // Texture coords are the last vertex attribute so we get a pointer to the
1687 // first one and then map with stride in regenerateTextureCoords
1688 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1689 vertex += info.fVertexStartIndex;
1690 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1691 vertex += vertexStride - sizeof(SkIPoint16);
1692
1693 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1694 }
1695
1696 if (regenerateColors) {
1697 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1698 vertex += info.fVertexStartIndex;
1699 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1700 this->regenerateColors(vertex, vertexStride, args.fColor);
1701 }
1702
joshualitt2a0e9f32015-04-13 06:12:21 -07001703 if (regeneratePositions) {
1704 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1705 vertex += info.fVertexStartIndex;
1706 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1707 SkScalar transX = args.fTransX;
1708 SkScalar transY = args.fTransY;
1709 this->regeneratePositions(vertex, vertexStride, transX, transY);
1710 }
bsalomonb5238a72015-05-05 07:49:49 -07001711 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001712 }
1713
joshualitt2a0e9f32015-04-13 06:12:21 -07001714 // We my have changed the color so update it here
1715 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001716 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001717 if (regenerateGlyphs) {
1718 run.fStrike.reset(SkRef(strike));
1719 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001720 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
1721 fFontCache->atlasGeneration(fMaskFormat);
1722 }
1723 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001724 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001725
1726 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1727 // have a valid atlas generation
1728 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1729 batchTarget->currentToken(),
1730 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001731 }
1732
1733 // now copy all vertices
1734 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1735 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1736
1737 currVertex += byteCount;
1738 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001739 // Make sure to attach the last cache if applicable
1740 if (cache) {
1741 SkGlyphCache::AttachCache(cache);
1742 }
bsalomonb5238a72015-05-05 07:49:49 -07001743 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001744 }
1745
joshualittad802c62015-04-15 05:31:57 -07001746 // The minimum number of Geometry we will try to allocate.
1747 static const int kMinAllocated = 32;
1748
1749 // Total number of Geometry this Batch owns
1750 int instanceCount() const { return fInstanceCount; }
1751 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1752
1753 // to avoid even the initial copy of the struct, we have a getter for the first item which
1754 // is used to seed the batch with its initial geometry. After seeding, the client should call
1755 // init() so the Batch can initialize itself
1756 Geometry& geometry() { return fGeoData[0]; }
1757 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001758 const Geometry& geo = fGeoData[0];
1759 fBatch.fColor = geo.fColor;
1760 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1761
1762 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1763 // into device space
1764 const Run& run = geo.fBlob->fRuns[geo.fRun];
1765 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1766 SkRect bounds = run.fVertexBounds;
1767 fBatch.fViewMatrix.mapRect(&bounds);
1768 this->setBounds(bounds);
1769 } else {
1770 this->setBounds(run.fVertexBounds);
1771 }
joshualittad802c62015-04-15 05:31:57 -07001772 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001773
1774private:
joshualitt9bd2daf2015-04-17 09:30:06 -07001775 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
joshualitt1d89e8d2015-04-01 12:40:54 -07001776 : fMaskFormat(maskFormat)
1777 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
joshualitt9bd2daf2015-04-17 09:30:06 -07001778 , fFontCache(fontCache)
1779 , fUseDistanceFields(false) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001780 this->initClassID<BitmapTextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001781 fBatch.fNumGlyphs = glyphCount;
joshualittad802c62015-04-15 05:31:57 -07001782 fInstanceCount = 1;
1783 fAllocatedCount = kMinAllocated;
1784 }
1785
joshualitt9bd2daf2015-04-17 09:30:06 -07001786 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
1787 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
1788 bool useLCDText, bool useBGR, float gamma)
1789 : fMaskFormat(maskFormat)
1790 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
1791 , fFontCache(fontCache)
1792 , fDistanceAdjustTable(SkRef(distanceAdjustTable))
1793 , fFilteredColor(filteredColor)
1794 , fUseDistanceFields(true)
1795 , fUseLCDText(useLCDText)
1796 , fUseBGR(useBGR)
1797 , fGamma(gamma) {
1798 this->initClassID<BitmapTextBatch>();
1799 fBatch.fNumGlyphs = glyphCount;
1800 fInstanceCount = 1;
1801 fAllocatedCount = kMinAllocated;
1802 SkASSERT(fMaskFormat == kA8_GrMaskFormat);
1803 }
1804
joshualittad802c62015-04-15 05:31:57 -07001805 ~BitmapTextBatch() {
1806 for (int i = 0; i < fInstanceCount; i++) {
1807 fGeoData[i].fBlob->unref();
1808 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001809 }
1810
1811 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1812 int width = glyph->fBounds.width();
1813 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001814
joshualitt9bd2daf2015-04-17 09:30:06 -07001815 int u0, v0, u1, v1;
1816 if (fUseDistanceFields) {
1817 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1818 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1819 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1820 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1821 } else {
1822 u0 = glyph->fAtlasLocation.fX;
1823 v0 = glyph->fAtlasLocation.fY;
1824 u1 = u0 + width;
1825 v1 = v0 + height;
1826 }
1827
joshualitt1d89e8d2015-04-01 12:40:54 -07001828 SkIPoint16* textureCoords;
1829 // V0
1830 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1831 textureCoords->set(u0, v0);
1832 vertex += vertexStride;
1833
1834 // V1
1835 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1836 textureCoords->set(u0, v1);
1837 vertex += vertexStride;
1838
1839 // V2
1840 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1841 textureCoords->set(u1, v1);
1842 vertex += vertexStride;
1843
1844 // V3
1845 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1846 textureCoords->set(u1, v0);
1847 }
1848
1849 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1850 for (int i = 0; i < kVerticesPerGlyph; i++) {
1851 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1852 *vcolor = color;
1853 vertex += vertexStride;
1854 }
1855 }
1856
joshualitt2a0e9f32015-04-13 06:12:21 -07001857 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1858 SkScalar transY) {
1859 for (int i = 0; i < kVerticesPerGlyph; i++) {
1860 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1861 point->fX += transX;
1862 point->fY += transY;
1863 vertex += vertexStride;
1864 }
1865 }
1866
bsalomonb5238a72015-05-05 07:49:49 -07001867 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001868 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001869 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001870 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001871 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001872 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001873 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001874 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001875 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1876 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001877 }
1878
1879 GrColor color() const { return fBatch.fColor; }
1880 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1881 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1882 int numGlyphs() const { return fBatch.fNumGlyphs; }
1883
1884 bool onCombineIfPossible(GrBatch* t) override {
1885 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1886
joshualitt9bd2daf2015-04-17 09:30:06 -07001887 if (fUseDistanceFields != that->fUseDistanceFields) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001888 return false;
1889 }
1890
joshualitt9bd2daf2015-04-17 09:30:06 -07001891 if (!fUseDistanceFields) {
1892 // Bitmap Text
1893 if (fMaskFormat != that->fMaskFormat) {
1894 return false;
1895 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001896
joshualitt9bd2daf2015-04-17 09:30:06 -07001897 // TODO we can often batch across LCD text if we have dual source blending and don't
1898 // have to use the blend constant
1899 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1900 return false;
1901 }
1902
1903 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1904 return false;
1905 }
1906 } else {
1907 // Distance Fields
1908 SkASSERT(this->fMaskFormat == that->fMaskFormat &&
1909 this->fMaskFormat == kA8_GrMaskFormat);
1910
1911 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1912 return false;
1913 }
1914
1915 if (fFilteredColor != that->fFilteredColor) {
1916 return false;
1917 }
1918
1919 if (fUseLCDText != that->fUseLCDText) {
1920 return false;
1921 }
1922
1923 if (fUseBGR != that->fUseBGR) {
1924 return false;
1925 }
1926
1927 if (fGamma != that->fGamma) {
1928 return false;
1929 }
1930
1931 // TODO see note above
1932 if (fUseLCDText && this->color() != that->color()) {
1933 return false;
1934 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001935 }
1936
1937 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001938
1939 // copy that->geoData(). We do this manually for performance reasons
1940 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1941 int otherInstanceCount = that->instanceCount();
1942 int allocSize = otherInstanceCount + fInstanceCount;
1943 if (allocSize > fAllocatedCount) {
1944 while (allocSize > fAllocatedCount) {
1945 fAllocatedCount = fAllocatedCount << 1;
1946 }
1947 fGeoData.realloc(fAllocatedCount);
1948 }
1949
1950 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1951 otherInstanceCount * sizeof(Geometry));
1952 int total = fInstanceCount + otherInstanceCount;
1953 for (int i = fInstanceCount; i < total; i++) {
1954 fGeoData[i].fBlob->ref();
1955 }
1956 fInstanceCount = total;
joshualitt99c7c072015-05-01 13:43:30 -07001957
1958 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001959 return true;
1960 }
1961
joshualitt9bd2daf2015-04-17 09:30:06 -07001962 // TODO just use class params
1963 // TODO trying to figure out why lcd is so whack
1964 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1965 GrColor color, GrTexture* texture) {
1966 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
1967
1968 // set up any flags
1969 uint32_t flags = 0;
1970 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1971 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
1972 flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
1973 kRectToRect_DistanceFieldEffectFlag : 0;
1974 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1975
1976 // see if we need to create a new effect
1977 if (fUseLCDText) {
1978 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1979
1980 float redCorrection =
1981 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1982 float greenCorrection =
1983 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1984 float blueCorrection =
1985 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1986 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1987 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1988 greenCorrection,
1989 blueCorrection);
1990
1991 return GrDistanceFieldLCDTextGeoProc::Create(color,
1992 viewMatrix,
1993 texture,
1994 params,
1995 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001996 flags,
1997 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001998 } else {
1999 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07002000#ifdef SK_GAMMA_APPLY_TO_A8
2001 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
2002 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
2003 return GrDistanceFieldA8TextGeoProc::Create(color,
2004 viewMatrix,
2005 texture,
2006 params,
2007 correction,
joshualittb8c241a2015-05-19 08:23:30 -07002008 flags,
2009 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002010#else
2011 return GrDistanceFieldA8TextGeoProc::Create(color,
2012 viewMatrix,
2013 texture,
2014 params,
joshualittb8c241a2015-05-19 08:23:30 -07002015 flags,
2016 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002017#endif
2018 }
2019
2020 }
2021
joshualitt1d89e8d2015-04-01 12:40:54 -07002022 struct BatchTracker {
2023 GrColor fColor;
2024 SkMatrix fViewMatrix;
2025 bool fUsesLocalCoords;
2026 bool fColorIgnored;
2027 bool fCoverageIgnored;
2028 int fNumGlyphs;
2029 };
2030
2031 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002032 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2033 int fInstanceCount;
2034 int fAllocatedCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07002035 GrMaskFormat fMaskFormat;
2036 GrPixelConfig fPixelConfig;
2037 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002038
2039 // Distance field properties
2040 SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
2041 SkColor fFilteredColor;
2042 bool fUseDistanceFields;
2043 bool fUseLCDText;
2044 bool fUseBGR;
2045 float fGamma;
joshualitt1d89e8d2015-04-01 12:40:54 -07002046};
2047
bsalomon781d5822015-06-16 15:03:11 -07002048void GrAtlasTextContext::flushRunAsPaths(GrDrawContext* drawContext,
2049 GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002050 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002051 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2052 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2053 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002054
joshualitt9a27e632015-04-06 10:53:36 -07002055 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2056 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002057
joshualitt9a27e632015-04-06 10:53:36 -07002058 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002059
joshualitt9a27e632015-04-06 10:53:36 -07002060 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2061 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002062 }
2063
robertphillips9c240a12015-05-28 07:45:59 -07002064 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002065
2066 switch (it.positioning()) {
2067 case SkTextBlob::kDefault_Positioning:
bsalomon781d5822015-06-16 15:03:11 -07002068 this->drawTextAsPath(drawContext, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002069 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002070 textLen, x + offset.x(), y + offset.y(), clipBounds);
2071 break;
2072 case SkTextBlob::kHorizontal_Positioning:
bsalomon781d5822015-06-16 15:03:11 -07002073 this->drawPosTextAsPath(drawContext, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002074 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002075 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2076 clipBounds);
2077 break;
2078 case SkTextBlob::kFull_Positioning:
bsalomon781d5822015-06-16 15:03:11 -07002079 this->drawPosTextAsPath(drawContext, rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002080 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002081 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2082 break;
2083 }
2084}
2085
joshualitt79dfb2b2015-05-11 08:58:08 -07002086
2087inline BitmapTextBatch*
2088GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
2089 int glyphCount, int run, int subRun,
2090 GrColor color, SkScalar transX, SkScalar transY,
2091 const SkPaint& skPaint) {
2092 GrMaskFormat format = info.fMaskFormat;
2093 GrColor subRunColor;
2094 if (kARGB_GrMaskFormat == format) {
2095 uint8_t paintAlpha = skPaint.getAlpha();
2096 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2097 } else {
2098 subRunColor = color;
2099 }
2100
2101 BitmapTextBatch* batch;
2102 if (info.fDrawAsDistanceFields) {
2103 SkColor filteredColor;
2104 SkColorFilter* colorFilter = skPaint.getColorFilter();
2105 if (colorFilter) {
2106 filteredColor = colorFilter->filterColor(skPaint.getColor());
2107 } else {
2108 filteredColor = skPaint.getColor();
2109 }
2110 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
2111 float gamma = fDeviceProperties.gamma();
2112 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
2113 fDistanceAdjustTable, filteredColor,
2114 info.fUseLCDText, useBGR,
2115 gamma);
2116 } else {
2117 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
2118 }
2119 BitmapTextBatch::Geometry& geometry = batch->geometry();
2120 geometry.fBlob = SkRef(cacheBlob);
2121 geometry.fRun = run;
2122 geometry.fSubRun = subRun;
2123 geometry.fColor = subRunColor;
2124 geometry.fTransX = transX;
2125 geometry.fTransY = transY;
2126 batch->init();
2127
2128 return batch;
2129}
2130
bsalomon781d5822015-06-16 15:03:11 -07002131inline void GrAtlasTextContext::flushRun(GrDrawContext* drawContext,
2132 GrPipelineBuilder* pipelineBuilder,
joshualitt9a27e632015-04-06 10:53:36 -07002133 BitmapTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002134 SkScalar transX, SkScalar transY,
2135 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002136 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2137 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2138 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2139 if (0 == glyphCount) {
2140 continue;
2141 }
2142
joshualitt79dfb2b2015-05-11 08:58:08 -07002143 SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2144 subRun, color, transX, transY,
2145 skPaint));
bsalomon781d5822015-06-16 15:03:11 -07002146 drawContext->drawText(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002147 }
2148}
2149
bsalomon781d5822015-06-16 15:03:11 -07002150inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob,
2151 GrDrawContext* drawContext, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002152 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002153 SkScalar transX, SkScalar transY,
2154 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002155 if (!cacheBlob->fBigGlyphs.count()) {
2156 return;
2157 }
2158
2159 SkMatrix pathMatrix;
2160 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2161 SkDebugf("could not invert viewmatrix\n");
2162 return;
2163 }
2164
joshualitt9a27e632015-04-06 10:53:36 -07002165 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07002166 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002167 bigGlyph.fVx += transX;
2168 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002169 SkMatrix translate = cacheBlob->fViewMatrix;
2170 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2171
bsalomon781d5822015-06-16 15:03:11 -07002172 GrBlurUtils::drawPathWithMaskFilter(fContext, drawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002173 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002174 }
2175}
joshualitt9a27e632015-04-06 10:53:36 -07002176
bsalomon781d5822015-06-16 15:03:11 -07002177void GrAtlasTextContext::flush(GrDrawContext* drawContext,
2178 const SkTextBlob* blob,
joshualitt9a27e632015-04-06 10:53:36 -07002179 BitmapTextBlob* cacheBlob,
2180 GrRenderTarget* rt,
2181 const SkPaint& skPaint,
2182 const GrPaint& grPaint,
2183 SkDrawFilter* drawFilter,
2184 const GrClip& clip,
2185 const SkMatrix& viewMatrix,
2186 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002187 SkScalar x, SkScalar y,
2188 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002189 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2190 // it as paths
2191 GrPipelineBuilder pipelineBuilder;
2192 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2193
2194 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002195
2196 SkTextBlob::RunIterator it(blob);
2197 for (int run = 0; !it.done(); it.next(), run++) {
2198 if (cacheBlob->fRuns[run].fDrawAsPaths) {
bsalomon781d5822015-06-16 15:03:11 -07002199 this->flushRunAsPaths(drawContext, rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002200 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002201 continue;
2202 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002203 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
bsalomon781d5822015-06-16 15:03:11 -07002204 this->flushRun(drawContext, &pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002205 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002206 }
2207
2208 // Now flush big glyphs
bsalomon781d5822015-06-16 15:03:11 -07002209 this->flushBigGlyphs(cacheBlob, drawContext, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002210}
2211
bsalomon781d5822015-06-16 15:03:11 -07002212void GrAtlasTextContext::flush(GrDrawContext* drawContext,
2213 BitmapTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002214 GrRenderTarget* rt,
2215 const SkPaint& skPaint,
2216 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002217 const GrClip& clip,
2218 const SkIRect& clipBounds) {
joshualitt9a27e632015-04-06 10:53:36 -07002219 GrPipelineBuilder pipelineBuilder;
2220 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2221
2222 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002223 for (int run = 0; run < cacheBlob->fRunCount; run++) {
bsalomon781d5822015-06-16 15:03:11 -07002224 this->flushRun(drawContext, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002225 }
2226
2227 // Now flush big glyphs
bsalomon781d5822015-06-16 15:03:11 -07002228 this->flushBigGlyphs(cacheBlob, drawContext, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002229}
joshualitt79dfb2b2015-05-11 08:58:08 -07002230
2231///////////////////////////////////////////////////////////////////////////////////////////////////
2232
2233#ifdef GR_TEST_UTILS
2234
joshualitt6c891102015-05-13 08:51:49 -07002235BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002236 static uint32_t gContextID = SK_InvalidGenID;
2237 static GrAtlasTextContext* gTextContext = NULL;
bsalomon781d5822015-06-16 15:03:11 -07002238 static SkDeviceProperties gDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002239
2240 if (context->uniqueID() != gContextID) {
2241 gContextID = context->uniqueID();
2242 SkDELETE(gTextContext);
2243 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2244 // because we don't really want to have a gpu device here.
2245 // We enable distance fields by twiddling a knob on the paint
bsalomon781d5822015-06-16 15:03:11 -07002246 gTextContext = GrAtlasTextContext::Create(context, gDeviceProperties, false);
joshualitt79dfb2b2015-05-11 08:58:08 -07002247 }
2248
2249 // create dummy render target
2250 GrSurfaceDesc desc;
2251 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2252 desc.fWidth = 1024;
2253 desc.fHeight = 1024;
2254 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002255 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002256 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2257 SkASSERT(texture);
2258 SkASSERT(NULL != texture->asRenderTarget());
2259 GrRenderTarget* rt = texture->asRenderTarget();
2260
2261 // Setup dummy SkPaint / GrPaint
2262 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002263 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002264 SkPaint skPaint;
2265 skPaint.setDistanceFieldTextTEMP(random->nextBool());
2266 skPaint.setColor(color);
2267 skPaint.setLCDRenderText(random->nextBool());
2268 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2269 skPaint.setSubpixelText(random->nextBool());
2270
2271 GrPaint grPaint;
2272 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2273 SkFAIL("couldn't convert paint\n");
2274 }
2275
2276 const char* text = "The quick brown fox jumps over the lazy dog.";
2277 int textLen = (int)strlen(text);
2278
2279 // Setup clip
2280 GrClip clip;
2281 SkIRect noClip = SkIRect::MakeLargest();
2282
2283 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2284 // intend to test the batch with this unit test, that is okay.
2285 SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
2286 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2287 static_cast<size_t>(textLen), 0, 0, noClip));
2288
2289 SkScalar transX = static_cast<SkScalar>(random->nextU());
2290 SkScalar transY = static_cast<SkScalar>(random->nextU());
2291 const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
2292 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2293}
2294
2295#endif