blob: 73526ed4d58428716bcfac578c67aa3403cc679c [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"
robertphillips2334fb62015-06-17 05:43:33 -070016#include "GrDrawTarget.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070017#include "GrFontScaler.h"
18#include "GrIndexBuffer.h"
bsalomoned0bcad2015-05-04 10:36:42 -070019#include "GrResourceProvider.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070020#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070021#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070022#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070023#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070024
25#include "SkAutoKern.h"
26#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070027#include "SkColorFilter.h"
28#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070029#include "SkDraw.h"
30#include "SkDrawFilter.h"
31#include "SkDrawProcs.h"
32#include "SkGlyphCache.h"
33#include "SkGpuDevice.h"
34#include "SkGr.h"
35#include "SkPath.h"
36#include "SkRTConf.h"
37#include "SkStrokeRec.h"
38#include "SkTextBlob.h"
39#include "SkTextMapStateProc.h"
40
41#include "effects/GrBitmapTextGeoProc.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070042#include "effects/GrDistanceFieldGeoProc.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070043
44namespace {
45static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
46
47// position + local coord
48static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
49
50static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
51
joshualitt9bd2daf2015-04-17 09:30:06 -070052static const int kMinDFFontSize = 18;
53static const int kSmallDFFontSize = 32;
54static const int kSmallDFFontLimit = 32;
55static const int kMediumDFFontSize = 72;
56static const int kMediumDFFontLimit = 72;
57static const int kLargeDFFontSize = 162;
jvanverth97c595f2015-06-19 11:06:28 -070058#ifdef SK_BUILD_FOR_ANDROID
59static const int kLargeDFFontLimit = 384;
60#else
joshualitta7c63892015-04-21 13:24:37 -070061static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
jvanverth97c595f2015-06-19 11:06:28 -070062#endif
joshualitt9bd2daf2015-04-17 09:30:06 -070063
64SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
65static const int kDistanceAdjustLumShift = 5;
66
joshualitt1d89e8d2015-04-01 12:40:54 -070067static const int kVerticesPerGlyph = 4;
68static const int kIndicesPerGlyph = 6;
69
70static size_t get_vertex_stride(GrMaskFormat maskFormat) {
71 switch (maskFormat) {
72 case kA8_GrMaskFormat:
73 return kGrayTextVASize;
74 case kARGB_GrMaskFormat:
75 return kColorTextVASize;
76 default:
77 return kLCDTextVASize;
78 }
79}
80
joshualitt9bd2daf2015-04-17 09:30:06 -070081static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
82 SkASSERT(maskFormat == kA8_GrMaskFormat);
83 if (useLCDText) {
84 return kLCDTextVASize;
85 } else {
86 return kGrayTextVASize;
87 }
88}
89
90static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
91 unsigned r = SkColorGetR(c);
92 unsigned g = SkColorGetG(c);
93 unsigned b = SkColorGetB(c);
94 return GrColorPackRGBA(r, g, b, 0xff);
95}
96
joshualitt1d89e8d2015-04-01 12:40:54 -070097};
98
99// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -0700100// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -0700101
joshualittdbd35932015-04-02 09:19:04 -0700102GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700103 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700104 const SkSurfaceProps& surfaceProps)
105 : INHERITED(context, drawContext, surfaceProps)
robertphillips9fc82752015-06-19 04:46:45 -0700106 , fDistanceAdjustTable(SkNEW(DistanceAdjustTable)) {
joshualittb7133be2015-04-08 09:08:31 -0700107 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
108 // vertexStride
109 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
110 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700111 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700112 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700113}
114
robertphillips9fc82752015-06-19 04:46:45 -0700115void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700116
117 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
118 // text. The mask gamma hack is based off of guessing what the blend color is going to
119 // be, and adjusting the mask so that when run through the linear blend will
120 // produce the value closest to the desired result. However, in practice this means
121 // that the 'adjusted' mask is just increasing or decreasing the coverage of
122 // the mask depending on what it is thought it will blit against. For black (on
123 // assumed white) this means that coverages are decreased (on a curve). For white (on
124 // assumed black) this means that coverages are increased (on a a curve). At
125 // middle (perceptual) gray (which could be blit against anything) the coverages
126 // remain the same.
127 //
128 // The idea here is that instead of determining the initial (real) coverage and
129 // then adjusting that coverage, we determine an adjusted coverage directly by
130 // essentially manipulating the geometry (in this case, the distance to the glyph
131 // edge). So for black (on assumed white) this thins a bit; for white (on
132 // assumed black) this fake bolds the geometry a bit.
133 //
134 // The distance adjustment is calculated by determining the actual coverage value which
135 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
136 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
137 // actual edge. So by subtracting this distance adjustment and computing without the
138 // the coverage adjustment we should get 0.5 coverage at the same point.
139 //
140 // This has several implications:
141 // For non-gray lcd smoothed text, each subpixel essentially is using a
142 // slightly different geometry.
143 //
144 // For black (on assumed white) this may not cover some pixels which were
145 // previously covered; however those pixels would have been only slightly
146 // covered and that slight coverage would have been decreased anyway. Also, some pixels
147 // which were previously fully covered may no longer be fully covered.
148 //
149 // For white (on assumed black) this may cover some pixels which weren't
150 // previously covered at all.
151
152 int width, height;
153 size_t size;
154
155#ifdef SK_GAMMA_CONTRAST
156 SkScalar contrast = SK_GAMMA_CONTRAST;
157#else
158 SkScalar contrast = 0.5f;
159#endif
robertphillips9fc82752015-06-19 04:46:45 -0700160 SkScalar paintGamma = SK_GAMMA_EXPONENT;
161 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700162
163 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
164 &width, &height);
165
166 SkASSERT(kExpectedDistanceAdjustTableSize == height);
167 fTable = SkNEW_ARRAY(SkScalar, height);
168
169 SkAutoTArray<uint8_t> data((int)size);
170 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
171
172 // find the inverse points where we cross 0.5
173 // binsearch might be better, but we only need to do this once on creation
174 for (int row = 0; row < height; ++row) {
175 uint8_t* rowPtr = data.get() + row*width;
176 for (int col = 0; col < width - 1; ++col) {
177 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
178 // compute point where a mask value will give us a result of 0.5
179 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
180 float borderAlpha = (col + interp) / 255.f;
181
182 // compute t value for that alpha
183 // this is an approximate inverse for smoothstep()
184 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
185
186 // compute distance which gives us that t value
187 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
188 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
189
190 fTable[row] = d;
191 break;
192 }
193 }
194 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700195}
196
joshualittdbd35932015-04-02 09:19:04 -0700197GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700198 GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -0700199 const SkSurfaceProps& surfaceProps) {
200 return SkNEW_ARGS(GrAtlasTextContext, (context, drawContext, surfaceProps));
joshualitt1d89e8d2015-04-01 12:40:54 -0700201}
202
joshualittdbd35932015-04-02 09:19:04 -0700203bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
204 const GrClip&,
205 const GrPaint&,
206 const SkPaint& skPaint,
207 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700208 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
209 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700210}
211
joshualitt9e36c1a2015-04-14 12:17:27 -0700212GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
213 GrColor canonicalColor = paint.computeLuminanceColor();
214 if (lcd) {
215 // This is the correct computation, but there are tons of cases where LCD can be overridden.
216 // For now we just regenerate if any run in a textblob has LCD.
217 // TODO figure out where all of these overrides are and see if we can incorporate that logic
218 // at a higher level *OR* use sRGB
219 SkASSERT(false);
220 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
221 } else {
222 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
223 // gamma corrected masks anyways, nor color
224 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
225 SkColorGetG(canonicalColor),
226 SkColorGetB(canonicalColor));
227 // reduce to our finite number of bits
228 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
229 }
230 return canonicalColor;
231}
232
233// TODO if this function ever shows up in profiling, then we can compute this value when the
234// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
235// run so this is not a big deal to compute here.
236bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
237 SkTextBlob::RunIterator it(blob);
238 for (; !it.done(); it.next()) {
239 if (it.isLCD()) {
240 return true;
241 }
242 }
243 return false;
244}
245
joshualitt2a0e9f32015-04-13 06:12:21 -0700246bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
247 const BitmapTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700248 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700249 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700250 // If we have LCD text then our canonical color will be set to transparent, in this case we have
251 // to regenerate the blob on any color change
252 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700253 return true;
254 }
255
256 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
257 return true;
258 }
259
260 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
261 return true;
262 }
263
joshualitt53b5f442015-04-13 06:33:59 -0700264 // We only cache one masked version
265 if (blob.fKey.fHasBlur &&
266 (blob.fBlurRec.fSigma != blurRec.fSigma ||
267 blob.fBlurRec.fStyle != blurRec.fStyle ||
268 blob.fBlurRec.fQuality != blurRec.fQuality)) {
269 return true;
270 }
271
272 // Similarly, we only cache one version for each style
273 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
274 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
275 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
276 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
277 return true;
278 }
279
joshualittfcfb9fc2015-04-21 07:35:10 -0700280 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
281 // for mixed blobs if this becomes an issue.
282 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700283 // Identical viewmatrices and we can reuse in all cases
284 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
285 return false;
286 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700287 return true;
288 }
289
joshualittfcfb9fc2015-04-21 07:35:10 -0700290 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700291 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
292 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
293 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
294 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
295 return true;
296 }
297
joshualittfcfb9fc2015-04-21 07:35:10 -0700298 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
299 // but only for integer translations.
300 // This cool bit of math will determine the necessary translation to apply to the already
301 // generated vertex coordinates to move them to the correct position
302 SkScalar transX = viewMatrix.getTranslateX() +
303 viewMatrix.getScaleX() * (x - blob.fX) +
304 viewMatrix.getSkewX() * (y - blob.fY) -
305 blob.fViewMatrix.getTranslateX();
306 SkScalar transY = viewMatrix.getTranslateY() +
307 viewMatrix.getSkewY() * (x - blob.fX) +
308 viewMatrix.getScaleY() * (y - blob.fY) -
309 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700310 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700311 return true;
312 }
313
joshualittfcfb9fc2015-04-21 07:35:10 -0700314 (*outTransX) = transX;
315 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700316 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700317 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
318 // distance field being generated, so we have to regenerate in those cases
319 SkScalar newMaxScale = viewMatrix.getMaxScale();
320 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
321 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
322 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
323 return true;
324 }
325
326 (*outTransX) = x - blob.fX;
327 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700328 }
joshualitta7c63892015-04-21 13:24:37 -0700329 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
330 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
331 // the blob anyways at flush time, so no need to regenerate explicitly
joshualittfcfb9fc2015-04-21 07:35:10 -0700332
joshualitt2a0e9f32015-04-13 06:12:21 -0700333 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700334}
335
336
joshualittdbd35932015-04-02 09:19:04 -0700337inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
338 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700339 const SkMatrix* viewMatrix,
340 bool noGamma) {
robertphillipsfcf78292015-06-19 11:49:52 -0700341 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700342 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
343 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
344}
345
robertphillips9c240a12015-05-28 07:45:59 -0700346void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700347 const GrClip& clip, const SkPaint& skPaint,
348 const SkMatrix& viewMatrix, const SkTextBlob* blob,
349 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700350 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700351 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700352 if (fContext->abandoned()) {
353 return;
354 }
355
joshualitt2a0e9f32015-04-13 06:12:21 -0700356 SkAutoTUnref<BitmapTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700357 SkMaskFilter::BlurRec blurRec;
358 BitmapTextBlob::Key key;
359 // It might be worth caching these things, but its not clear at this time
360 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
361 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700362 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700363 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700364 drawFilter);
365
366 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700367 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700368
369 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
robertphillipsfcf78292015-06-19 11:49:52 -0700370 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() :
joshualitte4cee1f2015-05-11 13:04:28 -0700371 kUnknown_SkPixelGeometry;
372
joshualitt9e36c1a2015-04-14 12:17:27 -0700373 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
374 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
375 // ensure we always match the same key
376 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
377 ComputeCanonicalColor(skPaint, hasLCD);
378
joshualitte4cee1f2015-05-11 13:04:28 -0700379 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700380 key.fUniqueID = blob->uniqueID();
381 key.fStyle = skPaint.getStyle();
382 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700383 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700384 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700385 }
386
joshualitt1d89e8d2015-04-01 12:40:54 -0700387 SkIRect clipRect;
388 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
389
joshualitt2a0e9f32015-04-13 06:12:21 -0700390 SkScalar transX = 0.f;
391 SkScalar transY = 0.f;
392
joshualitt9e36c1a2015-04-14 12:17:27 -0700393 // Though for the time being runs in the textblob can override the paint, they only touch font
394 // info.
395 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700396 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
397 return;
398 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700399
joshualittb7133be2015-04-08 09:08:31 -0700400 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700401 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700402 // We have to remake the blob because changes may invalidate our masks.
403 // TODO we could probably get away reuse most of the time if the pointer is unique,
404 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700405 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700406 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
407 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700408 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700409 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700410 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700411 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
412 // offsets
joshualitt2a0e9f32015-04-13 06:12:21 -0700413 cacheBlob->fViewMatrix = viewMatrix;
414 cacheBlob->fX = x;
415 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700416 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700417 }
418 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700419 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700420 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
421 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700422 } else {
423 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
424 }
robertphillips9c240a12015-05-28 07:45:59 -0700425 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700426 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700427 }
428
joshualitt9e36c1a2015-04-14 12:17:27 -0700429 cacheBlob->fPaintColor = skPaint.getColor();
robertphillips2334fb62015-06-17 05:43:33 -0700430 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700431 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700432}
433
joshualitt9bd2daf2015-04-17 09:30:06 -0700434inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
435 const SkMatrix& viewMatrix) {
436 // TODO: support perspective (need getMaxScale replacement)
437 if (viewMatrix.hasPerspective()) {
438 return false;
439 }
440
441 SkScalar maxScale = viewMatrix.getMaxScale();
442 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
443 // Hinted text looks far better at small resolutions
444 // Scaling up beyond 2x yields undesireable artifacts
jvanverth34d72882015-06-22 08:08:09 -0700445 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700446 return false;
447 }
448
robertphillipsfcf78292015-06-19 11:49:52 -0700449 bool useDFT = fSurfaceProps.isUseDistanceFieldFonts();
robertphillipsbcd7ab52015-06-18 05:27:18 -0700450#if SK_FORCE_DISTANCE_FIELD_TEXT
451 useDFT = true;
452#endif
453
jvanverth4854d132015-06-22 06:46:56 -0700454 if (!useDFT && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700455 return false;
456 }
457
458 // rasterizers and mask filters modify alpha, which doesn't
459 // translate well to distance
460 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700461 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700462 return false;
463 }
464
465 // TODO: add some stroking support
466 if (skPaint.getStyle() != SkPaint::kFill_Style) {
467 return false;
468 }
469
470 return true;
471}
472
robertphillips9c240a12015-05-28 07:45:59 -0700473void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700474 const SkPaint& skPaint, GrColor color,
475 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700476 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700477 SkDrawFilter* drawFilter, const SkIRect& clipRect,
478 GrRenderTarget* rt, const GrClip& clip,
479 const GrPaint& paint) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700480 cacheBlob->fViewMatrix = viewMatrix;
481 cacheBlob->fX = x;
482 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700483
484 // Regenerate textblob
485 SkPaint runPaint = skPaint;
486 SkTextBlob::RunIterator it(blob);
487 for (int run = 0; !it.done(); it.next(), run++) {
488 int glyphCount = it.glyphCount();
489 size_t textLen = glyphCount * sizeof(uint16_t);
490 const SkPoint& offset = it.offset();
491 // applyFontToPaint() always overwrites the exact same attributes,
492 // so it is safe to not re-seed the paint for this reason.
493 it.applyFontToPaint(&runPaint);
494
495 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
496 // A false return from filter() means we should abort the current draw.
497 runPaint = skPaint;
498 continue;
499 }
500
robertphillipsfcf78292015-06-19 11:49:52 -0700501 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700502
joshualitt1d89e8d2015-04-01 12:40:54 -0700503 // setup vertex / glyphIndex for the new run
504 if (run > 0) {
505 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
506 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
507
508 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
509 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
510
511 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
512 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
513 }
514
joshualittfcfb9fc2015-04-21 07:35:10 -0700515 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
516 cacheBlob->setHasDistanceField();
517 SkPaint dfPaint = runPaint;
518 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700519 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700520 Run& runIdx = cacheBlob->fRuns[run];
521 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
522 subRun.fUseLCDText = runPaint.isLCDRenderText();
523 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700524
joshualittfcfb9fc2015-04-21 07:35:10 -0700525 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
526
527 SkTDArray<char> fallbackTxt;
528 SkTDArray<SkScalar> fallbackPos;
529 SkPoint dfOffset;
530 int scalarsPerPosition = 2;
531 switch (it.positioning()) {
532 case SkTextBlob::kDefault_Positioning: {
533 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
534 (const char *)it.glyphs(), textLen,
535 x + offset.x(), y + offset.y(), clipRect, textRatio,
536 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
537 break;
538 }
539 case SkTextBlob::kHorizontal_Positioning: {
540 scalarsPerPosition = 1;
541 dfOffset = SkPoint::Make(x, y + offset.y());
542 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
543 (const char*)it.glyphs(), textLen, it.pos(),
544 scalarsPerPosition, dfOffset, clipRect, textRatio,
545 &fallbackTxt, &fallbackPos);
546 break;
547 }
548 case SkTextBlob::kFull_Positioning: {
549 dfOffset = SkPoint::Make(x, y);
550 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
551 (const char*)it.glyphs(), textLen, it.pos(),
552 scalarsPerPosition, dfOffset, clipRect, textRatio,
553 &fallbackTxt, &fallbackPos);
554 break;
555 }
556 }
557 if (fallbackTxt.count()) {
558 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
559 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
560 clipRect);
561 }
562
563 SkGlyphCache::AttachCache(cache);
564 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
565 cacheBlob->fRuns[run].fDrawAsPaths = true;
566 } else {
567 cacheBlob->setHasBitmap();
568 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
569 false);
570 switch (it.positioning()) {
571 case SkTextBlob::kDefault_Positioning:
572 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
573 (const char *)it.glyphs(), textLen,
574 x + offset.x(), y + offset.y(), clipRect);
575 break;
576 case SkTextBlob::kHorizontal_Positioning:
577 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
578 (const char*)it.glyphs(), textLen, it.pos(), 1,
579 SkPoint::Make(x, y + offset.y()), clipRect);
580 break;
581 case SkTextBlob::kFull_Positioning:
582 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
583 (const char*)it.glyphs(), textLen, it.pos(), 2,
584 SkPoint::Make(x, y), clipRect);
585 break;
586 }
587 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700588 }
589
590 if (drawFilter) {
591 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
592 runPaint = skPaint;
593 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700594 }
595}
596
joshualitt64c99cc2015-04-21 09:43:03 -0700597inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
598 SkPaint* skPaint,
599 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700600 const SkMatrix& viewMatrix) {
601 // getMaxScale doesn't support perspective, so neither do we at the moment
602 SkASSERT(!viewMatrix.hasPerspective());
603 SkScalar maxScale = viewMatrix.getMaxScale();
604 SkScalar textSize = skPaint->getTextSize();
605 SkScalar scaledTextSize = textSize;
606 // if we have non-unity scale, we need to choose our base text size
607 // based on the SkPaint's text size multiplied by the max scale factor
608 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
609 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
610 scaledTextSize *= maxScale;
611 }
612
joshualitt64c99cc2015-04-21 09:43:03 -0700613 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
614 // and ceiling. A scale outside of this range would require regenerating the distance fields
615 SkScalar dfMaskScaleFloor;
616 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700617 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700618 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700619 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700620 *textRatio = textSize / kSmallDFFontSize;
621 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
622 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700623 dfMaskScaleFloor = kSmallDFFontLimit;
624 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700625 *textRatio = textSize / kMediumDFFontSize;
626 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
627 } else {
joshualitta7c63892015-04-21 13:24:37 -0700628 dfMaskScaleFloor = kMediumDFFontLimit;
629 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700630 *textRatio = textSize / kLargeDFFontSize;
631 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
632 }
633
joshualitt64c99cc2015-04-21 09:43:03 -0700634 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
635 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
636 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
637 // tolerate before we'd have to move to a large mip size. When we actually test these values
638 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
639 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
640 // level)
joshualitta7c63892015-04-21 13:24:37 -0700641 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700642 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
643 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
644
joshualitt9bd2daf2015-04-17 09:30:06 -0700645 skPaint->setLCDRenderText(false);
646 skPaint->setAutohinted(false);
647 skPaint->setHinting(SkPaint::kNormal_Hinting);
648 skPaint->setSubpixelText(true);
649}
650
joshualittfec19e12015-04-17 10:32:32 -0700651inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700652 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700653 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700654 const GrPaint& paint,
655 const SkPaint& skPaint,
656 const SkMatrix& viewMatrix,
657 const SkTDArray<char>& fallbackTxt,
658 const SkTDArray<SkScalar>& fallbackPos,
659 int scalarsPerPosition,
660 const SkPoint& offset,
661 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700662 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700663 blob->setHasBitmap();
664 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700665 // Push back a new subrun to fill and set the override descriptor
666 run.push_back();
667 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
668 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
robertphillipsfcf78292015-06-19 11:49:52 -0700669 fSurfaceProps, &viewMatrix, false);
joshualittfec19e12015-04-17 10:32:32 -0700670 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700671 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700672 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700673 fallbackTxt.begin(), fallbackTxt.count(),
674 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
675 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700676}
677
678inline GrAtlasTextContext::BitmapTextBlob*
679GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
680 const SkMatrix& viewMatrix, SkGlyphCache** cache,
681 SkPaint* dfPaint, SkScalar* textRatio) {
682 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
683
684 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700685 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700686 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700687 Run& run = blob->fRuns[0];
688 PerSubRunInfo& subRun = run.fSubRunInfo.back();
689 subRun.fUseLCDText = origPaint.isLCDRenderText();
690 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700691
692 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
693 return blob;
694}
695
joshualitt79dfb2b2015-05-11 08:58:08 -0700696inline GrAtlasTextContext::BitmapTextBlob*
697GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
698 const GrPaint& paint, const SkPaint& skPaint,
699 const SkMatrix& viewMatrix,
700 const char text[], size_t byteLength,
701 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700702 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700703 SkIRect clipRect;
704 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
705
joshualitt79dfb2b2015-05-11 08:58:08 -0700706 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700707 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
708 SkPaint dfPaint;
709 SkScalar textRatio;
710 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700711 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700712
joshualitt9bd2daf2015-04-17 09:30:06 -0700713 SkTDArray<char> fallbackTxt;
714 SkTDArray<SkScalar> fallbackPos;
715 SkPoint offset;
716 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
717 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
718 &offset, skPaint);
719 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700720 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700721 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700722 fallbackPos, 2, offset, clipRect);
723 }
724 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700725 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700726 blob->fViewMatrix = viewMatrix;
727
728 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
729 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
730 byteLength, x, y, clipRect);
731 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700732 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700733 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700734}
735
joshualitt79dfb2b2015-05-11 08:58:08 -0700736inline GrAtlasTextContext::BitmapTextBlob*
737GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
738 const GrPaint& paint, const SkPaint& skPaint,
739 const SkMatrix& viewMatrix,
740 const char text[], size_t byteLength,
741 const SkScalar pos[], int scalarsPerPosition,
742 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700743 int glyphCount = skPaint.countText(text, byteLength);
744
745 SkIRect clipRect;
746 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
747
joshualitt79dfb2b2015-05-11 08:58:08 -0700748 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700749 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
750 SkPaint dfPaint;
751 SkScalar textRatio;
752 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700753 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700754
755 SkTDArray<char> fallbackTxt;
756 SkTDArray<SkScalar> fallbackPos;
757 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
758 byteLength, pos, scalarsPerPosition, offset, clipRect,
759 textRatio, &fallbackTxt, &fallbackPos);
760 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700761 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700762 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700763 fallbackPos, scalarsPerPosition, offset, clipRect);
764 }
765 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700766 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700767 blob->fViewMatrix = viewMatrix;
768 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
769 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
770 byteLength, pos, scalarsPerPosition, offset, clipRect);
771 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700772 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700773 return blob;
774}
775
robertphillips2334fb62015-06-17 05:43:33 -0700776void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700777 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700778 const GrPaint& paint, const SkPaint& skPaint,
779 const SkMatrix& viewMatrix,
780 const char text[], size_t byteLength,
781 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -0700782 SkAutoTUnref<BitmapTextBlob> blob(
783 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
784 text, byteLength, x, y, regionClipBounds));
785 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700786}
787
robertphillips2334fb62015-06-17 05:43:33 -0700788void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700789 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700790 const GrPaint& paint, const SkPaint& skPaint,
791 const SkMatrix& viewMatrix,
792 const char text[], size_t byteLength,
793 const SkScalar pos[], int scalarsPerPosition,
794 const SkPoint& offset, const SkIRect& regionClipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -0700795 SkAutoTUnref<BitmapTextBlob> blob(
796 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
797 text, byteLength,
798 pos, scalarsPerPosition,
799 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700800
robertphillips2334fb62015-06-17 05:43:33 -0700801 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700802}
803
804void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
805 SkGlyphCache* cache, const SkPaint& skPaint,
806 GrColor color,
807 const SkMatrix& viewMatrix,
808 const char text[], size_t byteLength,
809 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700810 SkASSERT(byteLength == 0 || text != NULL);
811
812 // nothing to draw
813 if (text == NULL || byteLength == 0) {
814 return;
815 }
816
817 fCurrStrike = NULL;
818 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
819
820 // Get GrFontScaler from cache
821 GrFontScaler* fontScaler = GetGrFontScaler(cache);
822
823 // transform our starting point
824 {
825 SkPoint loc;
826 viewMatrix.mapXY(x, y, &loc);
827 x = loc.fX;
828 y = loc.fY;
829 }
830
831 // need to measure first
832 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
833 SkVector stopVector;
834 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
835
836 SkScalar stopX = stopVector.fX;
837 SkScalar stopY = stopVector.fY;
838
839 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
840 stopX = SkScalarHalf(stopX);
841 stopY = SkScalarHalf(stopY);
842 }
843 x -= stopX;
844 y -= stopY;
845 }
846
847 const char* stop = text + byteLength;
848
849 SkAutoKern autokern;
850
851 SkFixed fxMask = ~0;
852 SkFixed fyMask = ~0;
853 SkScalar halfSampleX, halfSampleY;
854 if (cache->isSubpixel()) {
855 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
856 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
857 if (kX_SkAxisAlignment == baseline) {
858 fyMask = 0;
859 halfSampleY = SK_ScalarHalf;
860 } else if (kY_SkAxisAlignment == baseline) {
861 fxMask = 0;
862 halfSampleX = SK_ScalarHalf;
863 }
864 } else {
865 halfSampleX = halfSampleY = SK_ScalarHalf;
866 }
867
868 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
869 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
870
871 while (text < stop) {
872 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
873
874 fx += autokern.adjust(glyph);
875
876 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700877 this->bmpAppendGlyph(blob,
878 runIndex,
879 GrGlyph::Pack(glyph.getGlyphID(),
880 glyph.getSubXFixed(),
881 glyph.getSubYFixed(),
882 GrGlyph::kCoverage_MaskStyle),
883 Sk48Dot16FloorToInt(fx),
884 Sk48Dot16FloorToInt(fy),
885 color,
886 fontScaler,
887 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700888 }
889
890 fx += glyph.fAdvanceX;
891 fy += glyph.fAdvanceY;
892 }
893}
894
joshualitt9bd2daf2015-04-17 09:30:06 -0700895void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
896 SkGlyphCache* cache, const SkPaint& skPaint,
897 GrColor color,
898 const SkMatrix& viewMatrix,
899 const char text[], size_t byteLength,
900 const SkScalar pos[], int scalarsPerPosition,
901 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700902 SkASSERT(byteLength == 0 || text != NULL);
903 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
904
905 // nothing to draw
906 if (text == NULL || byteLength == 0) {
907 return;
908 }
909
910 fCurrStrike = NULL;
911 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
912
913 // Get GrFontScaler from cache
914 GrFontScaler* fontScaler = GetGrFontScaler(cache);
915
916 const char* stop = text + byteLength;
917 SkTextAlignProc alignProc(skPaint.getTextAlign());
918 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
919
920 if (cache->isSubpixel()) {
921 // maybe we should skip the rounding if linearText is set
922 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
923
924 SkFixed fxMask = ~0;
925 SkFixed fyMask = ~0;
926 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
927 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
928 if (kX_SkAxisAlignment == baseline) {
929 fyMask = 0;
930 halfSampleY = SK_ScalarHalf;
931 } else if (kY_SkAxisAlignment == baseline) {
932 fxMask = 0;
933 halfSampleX = SK_ScalarHalf;
934 }
935
936 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
937 while (text < stop) {
938 SkPoint tmsLoc;
939 tmsProc(pos, &tmsLoc);
940 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
941 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
942
943 const SkGlyph& glyph = glyphCacheProc(cache, &text,
944 fx & fxMask, fy & fyMask);
945
946 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700947 this->bmpAppendGlyph(blob,
948 runIndex,
949 GrGlyph::Pack(glyph.getGlyphID(),
950 glyph.getSubXFixed(),
951 glyph.getSubYFixed(),
952 GrGlyph::kCoverage_MaskStyle),
953 Sk48Dot16FloorToInt(fx),
954 Sk48Dot16FloorToInt(fy),
955 color,
956 fontScaler,
957 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700958 }
959 pos += scalarsPerPosition;
960 }
961 } else {
962 while (text < stop) {
963 const char* currentText = text;
964 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
965
966 if (metricGlyph.fWidth) {
967 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
968 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
969 SkPoint tmsLoc;
970 tmsProc(pos, &tmsLoc);
971 SkPoint alignLoc;
972 alignProc(tmsLoc, metricGlyph, &alignLoc);
973
974 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
975 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
976
977 // have to call again, now that we've been "aligned"
978 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
979 fx & fxMask, fy & fyMask);
980 // the assumption is that the metrics haven't changed
981 SkASSERT(prevAdvX == glyph.fAdvanceX);
982 SkASSERT(prevAdvY == glyph.fAdvanceY);
983 SkASSERT(glyph.fWidth);
984
joshualitt9bd2daf2015-04-17 09:30:06 -0700985 this->bmpAppendGlyph(blob,
986 runIndex,
987 GrGlyph::Pack(glyph.getGlyphID(),
988 glyph.getSubXFixed(),
989 glyph.getSubYFixed(),
990 GrGlyph::kCoverage_MaskStyle),
991 Sk48Dot16FloorToInt(fx),
992 Sk48Dot16FloorToInt(fy),
993 color,
994 fontScaler,
995 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700996 }
997 pos += scalarsPerPosition;
998 }
999 }
1000 } else { // not subpixel
1001
1002 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1003 while (text < stop) {
1004 // the last 2 parameters are ignored
1005 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1006
1007 if (glyph.fWidth) {
1008 SkPoint tmsLoc;
1009 tmsProc(pos, &tmsLoc);
1010
1011 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1012 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001013 this->bmpAppendGlyph(blob,
1014 runIndex,
1015 GrGlyph::Pack(glyph.getGlyphID(),
1016 glyph.getSubXFixed(),
1017 glyph.getSubYFixed(),
1018 GrGlyph::kCoverage_MaskStyle),
1019 Sk48Dot16FloorToInt(fx),
1020 Sk48Dot16FloorToInt(fy),
1021 color,
1022 fontScaler,
1023 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001024 }
1025 pos += scalarsPerPosition;
1026 }
1027 } else {
1028 while (text < stop) {
1029 // the last 2 parameters are ignored
1030 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1031
1032 if (glyph.fWidth) {
1033 SkPoint tmsLoc;
1034 tmsProc(pos, &tmsLoc);
1035
1036 SkPoint alignLoc;
1037 alignProc(tmsLoc, glyph, &alignLoc);
1038
1039 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1040 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001041 this->bmpAppendGlyph(blob,
1042 runIndex,
1043 GrGlyph::Pack(glyph.getGlyphID(),
1044 glyph.getSubXFixed(),
1045 glyph.getSubYFixed(),
1046 GrGlyph::kCoverage_MaskStyle),
1047 Sk48Dot16FloorToInt(fx),
1048 Sk48Dot16FloorToInt(fy),
1049 color,
1050 fontScaler,
1051 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001052 }
1053 pos += scalarsPerPosition;
1054 }
1055 }
1056 }
1057}
1058
joshualitt9bd2daf2015-04-17 09:30:06 -07001059
1060void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1061 SkGlyphCache* cache, const SkPaint& skPaint,
1062 GrColor color,
1063 const SkMatrix& viewMatrix,
1064 const char text[], size_t byteLength,
1065 SkScalar x, SkScalar y, const SkIRect& clipRect,
1066 SkScalar textRatio,
1067 SkTDArray<char>* fallbackTxt,
1068 SkTDArray<SkScalar>* fallbackPos,
1069 SkPoint* offset,
1070 const SkPaint& origPaint) {
1071 SkASSERT(byteLength == 0 || text != NULL);
1072
1073 // nothing to draw
1074 if (text == NULL || byteLength == 0) {
1075 return;
1076 }
1077
1078 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1079 SkAutoDescriptor desc;
robertphillipsfcf78292015-06-19 11:49:52 -07001080 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, NULL, true);
joshualitt9bd2daf2015-04-17 09:30:06 -07001081 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1082 desc.getDesc());
1083
1084 SkTArray<SkScalar> positions;
1085
1086 const char* textPtr = text;
1087 SkFixed stopX = 0;
1088 SkFixed stopY = 0;
1089 SkFixed origin = 0;
1090 switch (origPaint.getTextAlign()) {
1091 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1092 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1093 case SkPaint::kLeft_Align: origin = 0; break;
1094 }
1095
1096 SkAutoKern autokern;
1097 const char* stop = text + byteLength;
1098 while (textPtr < stop) {
1099 // don't need x, y here, since all subpixel variants will have the
1100 // same advance
1101 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1102
1103 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1104 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1105
1106 SkFixed height = glyph.fAdvanceY;
1107 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1108
1109 stopX += width;
1110 stopY += height;
1111 }
1112 SkASSERT(textPtr == stop);
1113
1114 // now adjust starting point depending on alignment
1115 SkScalar alignX = SkFixedToScalar(stopX);
1116 SkScalar alignY = SkFixedToScalar(stopY);
1117 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1118 alignX = SkScalarHalf(alignX);
1119 alignY = SkScalarHalf(alignY);
1120 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1121 alignX = 0;
1122 alignY = 0;
1123 }
1124 x -= alignX;
1125 y -= alignY;
1126 *offset = SkPoint::Make(x, y);
1127
1128 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1129 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1130 fallbackPos);
1131 SkGlyphCache::AttachCache(origPaintCache);
1132}
1133
1134void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1135 SkGlyphCache* cache, const SkPaint& skPaint,
1136 GrColor color,
1137 const SkMatrix& viewMatrix,
1138 const char text[], size_t byteLength,
1139 const SkScalar pos[], int scalarsPerPosition,
1140 const SkPoint& offset, const SkIRect& clipRect,
1141 SkScalar textRatio,
1142 SkTDArray<char>* fallbackTxt,
1143 SkTDArray<SkScalar>* fallbackPos) {
1144
1145 SkASSERT(byteLength == 0 || text != NULL);
1146 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1147
1148 // nothing to draw
1149 if (text == NULL || byteLength == 0) {
1150 return;
1151 }
1152
1153 fCurrStrike = NULL;
1154
1155 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1156 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1157
1158 const char* stop = text + byteLength;
1159
1160 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1161 while (text < stop) {
1162 const char* lastText = text;
1163 // the last 2 parameters are ignored
1164 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1165
1166 if (glyph.fWidth) {
1167 SkScalar x = offset.x() + pos[0];
1168 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1169
1170 if (!this->dfAppendGlyph(blob,
1171 runIndex,
1172 GrGlyph::Pack(glyph.getGlyphID(),
1173 glyph.getSubXFixed(),
1174 glyph.getSubYFixed(),
1175 GrGlyph::kDistance_MaskStyle),
1176 x, y, color, fontScaler, clipRect,
1177 textRatio, viewMatrix)) {
1178 // couldn't append, send to fallback
1179 fallbackTxt->append(SkToInt(text-lastText), lastText);
1180 *fallbackPos->append() = pos[0];
1181 if (2 == scalarsPerPosition) {
1182 *fallbackPos->append() = pos[1];
1183 }
1184 }
1185 }
1186 pos += scalarsPerPosition;
1187 }
1188 } else {
1189 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1190 : SK_Scalar1;
1191 while (text < stop) {
1192 const char* lastText = text;
1193 // the last 2 parameters are ignored
1194 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1195
1196 if (glyph.fWidth) {
1197 SkScalar x = offset.x() + pos[0];
1198 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1199
1200 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1201 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1202
1203 if (!this->dfAppendGlyph(blob,
1204 runIndex,
1205 GrGlyph::Pack(glyph.getGlyphID(),
1206 glyph.getSubXFixed(),
1207 glyph.getSubYFixed(),
1208 GrGlyph::kDistance_MaskStyle),
1209 x - advanceX, y - advanceY, color,
1210 fontScaler,
1211 clipRect,
1212 textRatio,
1213 viewMatrix)) {
1214 // couldn't append, send to fallback
1215 fallbackTxt->append(SkToInt(text-lastText), lastText);
1216 *fallbackPos->append() = pos[0];
1217 if (2 == scalarsPerPosition) {
1218 *fallbackPos->append() = pos[1];
1219 }
1220 }
1221 }
1222 pos += scalarsPerPosition;
1223 }
1224 }
1225}
1226
1227void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1228 GrGlyph::PackedID packed,
1229 int vx, int vy, GrColor color, GrFontScaler* scaler,
1230 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001231 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001232 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001233 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001234 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001235 }
1236
1237 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001238 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001239 return;
1240 }
1241
1242 int x = vx + glyph->fBounds.fLeft;
1243 int y = vy + glyph->fBounds.fTop;
1244
1245 // keep them as ints until we've done the clip-test
1246 int width = glyph->fBounds.width();
1247 int height = glyph->fBounds.height();
1248
joshualitt2a0e9f32015-04-13 06:12:21 -07001249#if 0
1250 // Not checking the clip bounds might introduce a performance regression. However, its not
1251 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1252 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1253 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1254 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001255 // check if we clipped out
1256 if (clipRect.quickReject(x, y, x + width, y + height)) {
1257 return;
1258 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001259#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001260
1261 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001262 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001263 this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001264 return;
1265 }
1266
joshualitt1d89e8d2015-04-01 12:40:54 -07001267 GrMaskFormat format = glyph->fMaskFormat;
1268
1269 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1270 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001271 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001272 }
1273
1274 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001275
1276 size_t vertexStride = get_vertex_stride(format);
1277
1278 SkRect r;
1279 r.fLeft = SkIntToScalar(x);
1280 r.fTop = SkIntToScalar(y);
1281 r.fRight = r.fLeft + SkIntToScalar(width);
1282 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001283 subRun->fMaskFormat = format;
1284 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001285 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001286}
joshualitt1d89e8d2015-04-01 12:40:54 -07001287
joshualitt9bd2daf2015-04-17 09:30:06 -07001288bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1289 GrGlyph::PackedID packed,
1290 SkScalar sx, SkScalar sy, GrColor color,
1291 GrFontScaler* scaler,
1292 const SkIRect& clipRect,
1293 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001294 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001295 if (!fCurrStrike) {
1296 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001297 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001298 }
1299
1300 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001301 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001302 return true;
1303 }
1304
1305 // fallback to color glyph support
1306 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1307 return false;
1308 }
1309
1310 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1311 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1312 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1313 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1314
1315 SkScalar scale = textRatio;
1316 dx *= scale;
1317 dy *= scale;
1318 width *= scale;
1319 height *= scale;
1320 sx += dx;
1321 sy += dy;
1322 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1323
1324#if 0
1325 // check if we clipped out
1326 SkRect dstRect;
1327 viewMatrix.mapRect(&dstRect, glyphRect);
1328 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1329 SkScalarTruncToInt(dstRect.top()),
1330 SkScalarTruncToInt(dstRect.right()),
1331 SkScalarTruncToInt(dstRect.bottom()))) {
1332 return true;
1333 }
1334#endif
1335
1336 // TODO combine with the above
1337 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001338 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001339 this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001340 return true;
1341 }
1342
joshualitt9bd2daf2015-04-17 09:30:06 -07001343 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1344 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1345 subRun->fMaskFormat = kA8_GrMaskFormat;
1346
1347 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1348
1349 bool useColorVerts = !subRun->fUseLCDText;
1350 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001351 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001352 return true;
1353}
1354
1355inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
joshualitt19e4c022015-05-13 11:23:03 -07001356 GrFontScaler* scaler, SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001357 if (NULL == glyph->fPath) {
1358 SkPath* path = SkNEW(SkPath);
1359 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1360 // flag the glyph as being dead?
1361 SkDELETE(path);
1362 return;
1363 }
1364 glyph->fPath = path;
1365 }
1366 SkASSERT(glyph->fPath);
1367 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1368}
1369
1370inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1371 Run::SubRunInfo* subRun,
1372 const SkRect& positions, GrColor color,
1373 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001374 GrGlyph* glyph) {
1375 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001376 run->fVertexBounds.joinNonEmptyArg(positions);
1377 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001378
1379 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1380
joshualitt9bd2daf2015-04-17 09:30:06 -07001381 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001382 // V0
1383 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1384 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001385 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1386 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001387 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001388
joshualitt010db532015-04-21 10:07:26 -07001389 // V1
1390 position = reinterpret_cast<SkPoint*>(vertex);
1391 position->set(positions.fLeft, positions.fBottom);
1392 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001393 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001394 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001395
joshualitt010db532015-04-21 10:07:26 -07001396 // V2
1397 position = reinterpret_cast<SkPoint*>(vertex);
1398 position->set(positions.fRight, positions.fBottom);
1399 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001400 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001401 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001402
joshualitt010db532015-04-21 10:07:26 -07001403 // V3
1404 position = reinterpret_cast<SkPoint*>(vertex);
1405 position->set(positions.fRight, positions.fTop);
1406 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001407 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001408 } else {
1409 // V0
1410 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1411 position->set(positions.fLeft, positions.fTop);
1412 vertex += vertexStride;
1413
1414 // V1
1415 position = reinterpret_cast<SkPoint*>(vertex);
1416 position->set(positions.fLeft, positions.fBottom);
1417 vertex += vertexStride;
1418
1419 // V2
1420 position = reinterpret_cast<SkPoint*>(vertex);
1421 position->set(positions.fRight, positions.fBottom);
1422 vertex += vertexStride;
1423
1424 // V3
1425 position = reinterpret_cast<SkPoint*>(vertex);
1426 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001427 }
1428
1429 subRun->fGlyphEndIndex++;
1430 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1431}
1432
1433class BitmapTextBatch : public GrBatch {
1434public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001435 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualittdbd35932015-04-02 09:19:04 -07001436 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001437 typedef Blob::Run Run;
1438 typedef Run::SubRunInfo TextInfo;
1439 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001440 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001441 int fRun;
1442 int fSubRun;
1443 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001444 SkScalar fTransX;
1445 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001446 };
1447
joshualittad802c62015-04-15 05:31:57 -07001448 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1449 GrBatchFontCache* fontCache) {
1450 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -07001451 }
1452
joshualitt9bd2daf2015-04-17 09:30:06 -07001453 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1454 GrBatchFontCache* fontCache,
1455 DistanceAdjustTable* distanceAdjustTable,
1456 SkColor filteredColor, bool useLCDText,
robertphillips9fc82752015-06-19 04:46:45 -07001457 bool useBGR) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001458 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
robertphillips9fc82752015-06-19 04:46:45 -07001459 filteredColor, useLCDText, useBGR));
joshualitt9bd2daf2015-04-17 09:30:06 -07001460 }
1461
joshualitt1d89e8d2015-04-01 12:40:54 -07001462 const char* name() const override { return "BitmapTextBatch"; }
1463
1464 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1465 if (kARGB_GrMaskFormat == fMaskFormat) {
1466 out->setUnknownFourComponents();
1467 } else {
1468 out->setKnownFourComponents(fBatch.fColor);
1469 }
1470 }
1471
1472 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt9bd2daf2015-04-17 09:30:06 -07001473 if (!fUseDistanceFields) {
1474 // Bitmap Text
1475 if (kARGB_GrMaskFormat != fMaskFormat) {
1476 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
1477 out->setUnknownSingleComponent();
1478 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
1479 out->setUnknownOpaqueFourComponents();
1480 out->setUsingLCDCoverage();
1481 } else {
1482 out->setUnknownFourComponents();
1483 out->setUsingLCDCoverage();
1484 }
1485 } else {
1486 out->setKnownSingleComponent(0xff);
1487 }
1488 } else {
1489 // Distance fields
1490 if (!fUseLCDText) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001491 out->setUnknownSingleComponent();
joshualitt1d89e8d2015-04-01 12:40:54 -07001492 } else {
1493 out->setUnknownFourComponents();
1494 out->setUsingLCDCoverage();
1495 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001496 }
1497 }
1498
1499 void initBatchTracker(const GrPipelineInfo& init) override {
1500 // Handle any color overrides
bsalomon7765a472015-07-08 11:26:37 -07001501 if (!init.readsColor()) {
joshualitt416e14f2015-07-10 09:05:57 -07001502 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001503 }
joshualitt416e14f2015-07-10 09:05:57 -07001504 init.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt1d89e8d2015-04-01 12:40:54 -07001505
1506 // setup batch properties
bsalomon7765a472015-07-08 11:26:37 -07001507 fBatch.fColorIgnored = !init.readsColor();
joshualitt416e14f2015-07-10 09:05:57 -07001508 fBatch.fColor = fGeoData[0].fColor;
bsalomon7765a472015-07-08 11:26:37 -07001509 fBatch.fUsesLocalCoords = init.readsLocalCoords();
1510 fBatch.fCoverageIgnored = !init.readsCoverage();
joshualitt1d89e8d2015-04-01 12:40:54 -07001511 }
1512
bsalomonb5238a72015-05-05 07:49:49 -07001513 struct FlushInfo {
1514 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1515 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1516 int fGlyphsToFlush;
1517 int fVertexOffset;
1518 };
1519
joshualitt1d89e8d2015-04-01 12:40:54 -07001520 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1521 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1522 // TODO actually only invert if we don't have RGBA
1523 SkMatrix localMatrix;
1524 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1525 SkDebugf("Cannot invert viewmatrix\n");
1526 return;
1527 }
1528
joshualitt62db8ba2015-04-09 08:22:37 -07001529 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
1530 if (!texture) {
1531 SkDebugf("Could not allocate backing texture for atlas\n");
1532 return;
1533 }
1534
joshualitt9bd2daf2015-04-17 09:30:06 -07001535 SkAutoTUnref<const GrGeometryProcessor> gp;
1536 if (fUseDistanceFields) {
1537 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1538 texture));
1539 } else {
1540 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001541 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1542 texture,
1543 params,
1544 fMaskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001545 localMatrix,
1546 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001547 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001548
bsalomonb5238a72015-05-05 07:49:49 -07001549 FlushInfo flushInfo;
1550 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001551 size_t vertexStride = gp->getVertexStride();
joshualitt9bd2daf2015-04-17 09:30:06 -07001552 SkASSERT(vertexStride == (fUseDistanceFields ?
1553 get_vertex_stride_df(fMaskFormat, fUseLCDText) :
1554 get_vertex_stride(fMaskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001555
joshualittb8c241a2015-05-19 08:23:30 -07001556 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001557
1558 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001559 int instanceCount = fInstanceCount;
bsalomon8415abe2015-05-04 11:41:41 -07001560 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001561
robertphillipse40d3972015-05-07 09:51:43 -07001562 void* vertices = batchTarget->makeVertSpace(vertexStride,
1563 glyphCount * kVerticesPerGlyph,
1564 &vertexBuffer,
1565 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001566 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1567 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1568 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001569 SkDebugf("Could not allocate vertices\n");
1570 return;
1571 }
1572
1573 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1574
joshualitt25ba7ea2015-04-21 07:49:49 -07001575 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1576 // in a row
1577 const SkDescriptor* desc = NULL;
1578 SkGlyphCache* cache = NULL;
1579 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001580 SkTypeface* typeface = NULL;
1581
joshualitt1d89e8d2015-04-01 12:40:54 -07001582 for (int i = 0; i < instanceCount; i++) {
1583 Geometry& args = fGeoData[i];
1584 Blob* blob = args.fBlob;
1585 Run& run = blob->fRuns[args.fRun];
1586 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1587
1588 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001589 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
1590 run.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001591 bool regenerateColors;
1592 if (fUseDistanceFields) {
joshualittfcfb9fc2015-04-21 07:35:10 -07001593 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001594 } else {
1595 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
1596 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001597 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001598 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1599
1600 // We regenerate both texture coords and colors in the blob itself, and update the
1601 // atlas generation. If we don't end up purging any unused plots, we can avoid
1602 // regenerating the coords. We could take a finer grained approach to updating texture
1603 // coords but its not clear if the extra bookkeeping would offset any gains.
1604 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1605 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1606 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1607 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001608 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001609 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001610 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001611
1612 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1613 // generating its texture coords, we have to track whether or not the strike has
1614 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1615 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1616 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1617 // new strike, we instead keep our ref to the old strike and use the packed ids from
1618 // it. These ids will still be valid as long as we hold the ref. When we are done
1619 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1620 bool regenerateGlyphs = false;
1621 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001622 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001623 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001624
1625 // We can reuse if we have a valid strike and our descriptors / typeface are the
1626 // same
joshualitt97202d22015-04-22 13:47:02 -07001627 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1628 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001629 run.fDescriptor.getDesc();
1630 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1631 !(desc->equals(*newDesc))) {
1632 if (cache) {
1633 SkGlyphCache::AttachCache(cache);
1634 }
1635 desc = newDesc;
1636 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1637 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001638 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001639 typeface = run.fTypeface;
1640 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001641
joshualittae32c102015-04-21 09:37:57 -07001642 if (run.fStrike->isAbandoned()) {
1643 regenerateGlyphs = true;
1644 strike = fFontCache->getStrike(scaler);
1645 } else {
1646 strike = run.fStrike;
1647 }
1648 }
1649
1650 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001651 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001652 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1653 GrGlyph* glyph;
1654 if (regenerateGlyphs) {
1655 // Get the id from the old glyph, and use the new strike to lookup
1656 // the glyph.
1657 glyph = blob->fGlyphs[glyphOffset];
1658 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1659 scaler);
1660 }
1661 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001662 SkASSERT(glyph);
1663
1664 if (!fFontCache->hasGlyph(glyph) &&
1665 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
bsalomonb5238a72015-05-05 07:49:49 -07001666 this->flush(batchTarget, &flushInfo);
joshualittb8c241a2015-05-19 08:23:30 -07001667 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001668 brokenRun = glyphIdx > 0;
1669
joshualittae32c102015-04-21 09:37:57 -07001670 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1671 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001672 scaler);
1673 SkASSERT(success);
1674 }
joshualittb4c507e2015-04-08 08:07:59 -07001675 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1676 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001677
1678 // Texture coords are the last vertex attribute so we get a pointer to the
1679 // first one and then map with stride in regenerateTextureCoords
1680 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1681 vertex += info.fVertexStartIndex;
1682 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1683 vertex += vertexStride - sizeof(SkIPoint16);
1684
1685 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1686 }
1687
1688 if (regenerateColors) {
1689 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1690 vertex += info.fVertexStartIndex;
1691 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1692 this->regenerateColors(vertex, vertexStride, args.fColor);
1693 }
1694
joshualitt2a0e9f32015-04-13 06:12:21 -07001695 if (regeneratePositions) {
1696 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1697 vertex += info.fVertexStartIndex;
1698 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1699 SkScalar transX = args.fTransX;
1700 SkScalar transY = args.fTransY;
1701 this->regeneratePositions(vertex, vertexStride, transX, transY);
1702 }
bsalomonb5238a72015-05-05 07:49:49 -07001703 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001704 }
1705
joshualitt2a0e9f32015-04-13 06:12:21 -07001706 // We my have changed the color so update it here
1707 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001708 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001709 if (regenerateGlyphs) {
1710 run.fStrike.reset(SkRef(strike));
1711 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001712 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
1713 fFontCache->atlasGeneration(fMaskFormat);
1714 }
1715 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001716 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001717
1718 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1719 // have a valid atlas generation
1720 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1721 batchTarget->currentToken(),
1722 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001723 }
1724
1725 // now copy all vertices
1726 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1727 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1728
1729 currVertex += byteCount;
1730 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001731 // Make sure to attach the last cache if applicable
1732 if (cache) {
1733 SkGlyphCache::AttachCache(cache);
1734 }
bsalomonb5238a72015-05-05 07:49:49 -07001735 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001736 }
1737
joshualittad802c62015-04-15 05:31:57 -07001738 // The minimum number of Geometry we will try to allocate.
1739 static const int kMinAllocated = 32;
1740
1741 // Total number of Geometry this Batch owns
1742 int instanceCount() const { return fInstanceCount; }
1743 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1744
1745 // to avoid even the initial copy of the struct, we have a getter for the first item which
1746 // is used to seed the batch with its initial geometry. After seeding, the client should call
1747 // init() so the Batch can initialize itself
1748 Geometry& geometry() { return fGeoData[0]; }
1749 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001750 const Geometry& geo = fGeoData[0];
1751 fBatch.fColor = geo.fColor;
1752 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1753
1754 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1755 // into device space
1756 const Run& run = geo.fBlob->fRuns[geo.fRun];
1757 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1758 SkRect bounds = run.fVertexBounds;
1759 fBatch.fViewMatrix.mapRect(&bounds);
1760 this->setBounds(bounds);
1761 } else {
1762 this->setBounds(run.fVertexBounds);
1763 }
joshualittad802c62015-04-15 05:31:57 -07001764 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001765
1766private:
joshualitt9bd2daf2015-04-17 09:30:06 -07001767 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
joshualitt1d89e8d2015-04-01 12:40:54 -07001768 : fMaskFormat(maskFormat)
1769 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
joshualitt9bd2daf2015-04-17 09:30:06 -07001770 , fFontCache(fontCache)
1771 , fUseDistanceFields(false) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001772 this->initClassID<BitmapTextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001773 fBatch.fNumGlyphs = glyphCount;
joshualittad802c62015-04-15 05:31:57 -07001774 fInstanceCount = 1;
1775 fAllocatedCount = kMinAllocated;
1776 }
1777
joshualitt9bd2daf2015-04-17 09:30:06 -07001778 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
1779 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
robertphillips9fc82752015-06-19 04:46:45 -07001780 bool useLCDText, bool useBGR)
joshualitt9bd2daf2015-04-17 09:30:06 -07001781 : fMaskFormat(maskFormat)
1782 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
1783 , fFontCache(fontCache)
1784 , fDistanceAdjustTable(SkRef(distanceAdjustTable))
1785 , fFilteredColor(filteredColor)
1786 , fUseDistanceFields(true)
1787 , fUseLCDText(useLCDText)
robertphillips9fc82752015-06-19 04:46:45 -07001788 , fUseBGR(useBGR) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001789 this->initClassID<BitmapTextBatch>();
1790 fBatch.fNumGlyphs = glyphCount;
1791 fInstanceCount = 1;
1792 fAllocatedCount = kMinAllocated;
1793 SkASSERT(fMaskFormat == kA8_GrMaskFormat);
1794 }
1795
joshualittad802c62015-04-15 05:31:57 -07001796 ~BitmapTextBatch() {
1797 for (int i = 0; i < fInstanceCount; i++) {
1798 fGeoData[i].fBlob->unref();
1799 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001800 }
1801
1802 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1803 int width = glyph->fBounds.width();
1804 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001805
joshualitt9bd2daf2015-04-17 09:30:06 -07001806 int u0, v0, u1, v1;
1807 if (fUseDistanceFields) {
1808 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1809 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1810 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1811 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1812 } else {
1813 u0 = glyph->fAtlasLocation.fX;
1814 v0 = glyph->fAtlasLocation.fY;
1815 u1 = u0 + width;
1816 v1 = v0 + height;
1817 }
1818
joshualitt1d89e8d2015-04-01 12:40:54 -07001819 SkIPoint16* textureCoords;
1820 // V0
1821 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1822 textureCoords->set(u0, v0);
1823 vertex += vertexStride;
1824
1825 // V1
1826 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1827 textureCoords->set(u0, v1);
1828 vertex += vertexStride;
1829
1830 // V2
1831 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1832 textureCoords->set(u1, v1);
1833 vertex += vertexStride;
1834
1835 // V3
1836 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1837 textureCoords->set(u1, v0);
1838 }
1839
1840 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1841 for (int i = 0; i < kVerticesPerGlyph; i++) {
1842 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1843 *vcolor = color;
1844 vertex += vertexStride;
1845 }
1846 }
1847
joshualitt2a0e9f32015-04-13 06:12:21 -07001848 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1849 SkScalar transY) {
1850 for (int i = 0; i < kVerticesPerGlyph; i++) {
1851 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1852 point->fX += transX;
1853 point->fY += transY;
1854 vertex += vertexStride;
1855 }
1856 }
1857
bsalomonb5238a72015-05-05 07:49:49 -07001858 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001859 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001860 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001861 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001862 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001863 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001864 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001865 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001866 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1867 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001868 }
1869
1870 GrColor color() const { return fBatch.fColor; }
1871 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1872 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1873 int numGlyphs() const { return fBatch.fNumGlyphs; }
1874
1875 bool onCombineIfPossible(GrBatch* t) override {
1876 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1877
joshualitt9bd2daf2015-04-17 09:30:06 -07001878 if (fUseDistanceFields != that->fUseDistanceFields) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001879 return false;
1880 }
1881
joshualitt9bd2daf2015-04-17 09:30:06 -07001882 if (!fUseDistanceFields) {
1883 // Bitmap Text
1884 if (fMaskFormat != that->fMaskFormat) {
1885 return false;
1886 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001887
joshualitt9bd2daf2015-04-17 09:30:06 -07001888 // TODO we can often batch across LCD text if we have dual source blending and don't
1889 // have to use the blend constant
1890 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1891 return false;
1892 }
1893
1894 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1895 return false;
1896 }
1897 } else {
1898 // Distance Fields
1899 SkASSERT(this->fMaskFormat == that->fMaskFormat &&
1900 this->fMaskFormat == kA8_GrMaskFormat);
1901
1902 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1903 return false;
1904 }
1905
1906 if (fFilteredColor != that->fFilteredColor) {
1907 return false;
1908 }
1909
1910 if (fUseLCDText != that->fUseLCDText) {
1911 return false;
1912 }
1913
1914 if (fUseBGR != that->fUseBGR) {
1915 return false;
1916 }
1917
joshualitt9bd2daf2015-04-17 09:30:06 -07001918 // TODO see note above
1919 if (fUseLCDText && this->color() != that->color()) {
1920 return false;
1921 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001922 }
1923
1924 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001925
1926 // copy that->geoData(). We do this manually for performance reasons
1927 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1928 int otherInstanceCount = that->instanceCount();
1929 int allocSize = otherInstanceCount + fInstanceCount;
1930 if (allocSize > fAllocatedCount) {
1931 while (allocSize > fAllocatedCount) {
1932 fAllocatedCount = fAllocatedCount << 1;
1933 }
1934 fGeoData.realloc(fAllocatedCount);
1935 }
1936
1937 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1938 otherInstanceCount * sizeof(Geometry));
1939 int total = fInstanceCount + otherInstanceCount;
1940 for (int i = fInstanceCount; i < total; i++) {
1941 fGeoData[i].fBlob->ref();
1942 }
1943 fInstanceCount = total;
joshualitt99c7c072015-05-01 13:43:30 -07001944
1945 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001946 return true;
1947 }
1948
joshualitt9bd2daf2015-04-17 09:30:06 -07001949 // TODO just use class params
1950 // TODO trying to figure out why lcd is so whack
1951 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1952 GrColor color, GrTexture* texture) {
1953 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
1954
1955 // set up any flags
1956 uint32_t flags = 0;
1957 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1958 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
1959 flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
1960 kRectToRect_DistanceFieldEffectFlag : 0;
1961 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1962
1963 // see if we need to create a new effect
1964 if (fUseLCDText) {
1965 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1966
1967 float redCorrection =
1968 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1969 float greenCorrection =
1970 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1971 float blueCorrection =
1972 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1973 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1974 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1975 greenCorrection,
1976 blueCorrection);
1977
1978 return GrDistanceFieldLCDTextGeoProc::Create(color,
1979 viewMatrix,
1980 texture,
1981 params,
1982 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001983 flags,
1984 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001985 } else {
1986 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07001987#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07001988 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07001989 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
1990 return GrDistanceFieldA8TextGeoProc::Create(color,
1991 viewMatrix,
1992 texture,
1993 params,
1994 correction,
joshualittb8c241a2015-05-19 08:23:30 -07001995 flags,
1996 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001997#else
1998 return GrDistanceFieldA8TextGeoProc::Create(color,
1999 viewMatrix,
2000 texture,
2001 params,
joshualittb8c241a2015-05-19 08:23:30 -07002002 flags,
2003 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002004#endif
2005 }
2006
2007 }
2008
joshualitt1d89e8d2015-04-01 12:40:54 -07002009 struct BatchTracker {
2010 GrColor fColor;
2011 SkMatrix fViewMatrix;
2012 bool fUsesLocalCoords;
2013 bool fColorIgnored;
2014 bool fCoverageIgnored;
2015 int fNumGlyphs;
2016 };
2017
2018 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002019 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2020 int fInstanceCount;
2021 int fAllocatedCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07002022 GrMaskFormat fMaskFormat;
2023 GrPixelConfig fPixelConfig;
2024 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002025
2026 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002027 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002028 SkColor fFilteredColor;
2029 bool fUseDistanceFields;
2030 bool fUseLCDText;
2031 bool fUseBGR;
joshualitt1d89e8d2015-04-01 12:40:54 -07002032};
2033
robertphillips2334fb62015-06-17 05:43:33 -07002034void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002035 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002036 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2037 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2038 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002039
joshualitt9a27e632015-04-06 10:53:36 -07002040 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2041 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002042
joshualitt9a27e632015-04-06 10:53:36 -07002043 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002044
joshualitt9a27e632015-04-06 10:53:36 -07002045 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2046 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002047 }
2048
robertphillipsfcf78292015-06-19 11:49:52 -07002049 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002050
2051 switch (it.positioning()) {
2052 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002053 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002054 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002055 textLen, x + offset.x(), y + offset.y(), clipBounds);
2056 break;
2057 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002058 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002059 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002060 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2061 clipBounds);
2062 break;
2063 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002064 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002065 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002066 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2067 break;
2068 }
2069}
2070
joshualitt79dfb2b2015-05-11 08:58:08 -07002071
2072inline BitmapTextBatch*
2073GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
2074 int glyphCount, int run, int subRun,
2075 GrColor color, SkScalar transX, SkScalar transY,
2076 const SkPaint& skPaint) {
2077 GrMaskFormat format = info.fMaskFormat;
2078 GrColor subRunColor;
2079 if (kARGB_GrMaskFormat == format) {
2080 uint8_t paintAlpha = skPaint.getAlpha();
2081 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2082 } else {
2083 subRunColor = color;
2084 }
2085
2086 BitmapTextBatch* batch;
2087 if (info.fDrawAsDistanceFields) {
2088 SkColor filteredColor;
2089 SkColorFilter* colorFilter = skPaint.getColorFilter();
2090 if (colorFilter) {
2091 filteredColor = colorFilter->filterColor(skPaint.getColor());
2092 } else {
2093 filteredColor = skPaint.getColor();
2094 }
robertphillipsfcf78292015-06-19 11:49:52 -07002095 bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
joshualitt79dfb2b2015-05-11 08:58:08 -07002096 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
2097 fDistanceAdjustTable, filteredColor,
robertphillips9fc82752015-06-19 04:46:45 -07002098 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002099 } else {
2100 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
2101 }
2102 BitmapTextBatch::Geometry& geometry = batch->geometry();
2103 geometry.fBlob = SkRef(cacheBlob);
2104 geometry.fRun = run;
2105 geometry.fSubRun = subRun;
2106 geometry.fColor = subRunColor;
2107 geometry.fTransX = transX;
2108 geometry.fTransY = transY;
2109 batch->init();
2110
2111 return batch;
2112}
2113
robertphillips2334fb62015-06-17 05:43:33 -07002114inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt9a27e632015-04-06 10:53:36 -07002115 BitmapTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002116 SkScalar transX, SkScalar transY,
2117 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002118 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2119 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2120 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2121 if (0 == glyphCount) {
2122 continue;
2123 }
2124
joshualitt79dfb2b2015-05-11 08:58:08 -07002125 SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2126 subRun, color, transX, transY,
2127 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002128 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002129 }
2130}
2131
robertphillips2334fb62015-06-17 05:43:33 -07002132inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002133 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002134 SkScalar transX, SkScalar transY,
2135 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002136 if (!cacheBlob->fBigGlyphs.count()) {
2137 return;
2138 }
2139
2140 SkMatrix pathMatrix;
2141 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2142 SkDebugf("could not invert viewmatrix\n");
2143 return;
2144 }
2145
joshualitt9a27e632015-04-06 10:53:36 -07002146 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07002147 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002148 bigGlyph.fVx += transX;
2149 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002150 SkMatrix translate = cacheBlob->fViewMatrix;
2151 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2152
robertphillips2334fb62015-06-17 05:43:33 -07002153 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002154 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002155 }
2156}
joshualitt9a27e632015-04-06 10:53:36 -07002157
robertphillips2334fb62015-06-17 05:43:33 -07002158void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt9a27e632015-04-06 10:53:36 -07002159 BitmapTextBlob* cacheBlob,
2160 GrRenderTarget* rt,
2161 const SkPaint& skPaint,
2162 const GrPaint& grPaint,
2163 SkDrawFilter* drawFilter,
2164 const GrClip& clip,
2165 const SkMatrix& viewMatrix,
2166 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002167 SkScalar x, SkScalar y,
2168 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002169 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2170 // it as paths
joshualitt7b670db2015-07-09 13:25:02 -07002171 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002172
2173 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002174
2175 SkTextBlob::RunIterator it(blob);
2176 for (int run = 0; !it.done(); it.next(), run++) {
2177 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002178 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002179 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002180 continue;
2181 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002182 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002183 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002184 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002185 }
2186
2187 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002188 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002189}
2190
robertphillips2334fb62015-06-17 05:43:33 -07002191void GrAtlasTextContext::flush(BitmapTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002192 GrRenderTarget* rt,
2193 const SkPaint& skPaint,
2194 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002195 const GrClip& clip,
2196 const SkIRect& clipBounds) {
joshualitt7b670db2015-07-09 13:25:02 -07002197 GrPipelineBuilder pipelineBuilder(grPaint, rt, clip);
joshualitt9a27e632015-04-06 10:53:36 -07002198
2199 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002200 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillips2334fb62015-06-17 05:43:33 -07002201 this->flushRun(&pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002202 }
2203
2204 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002205 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002206}
joshualitt79dfb2b2015-05-11 08:58:08 -07002207
2208///////////////////////////////////////////////////////////////////////////////////////////////////
2209
2210#ifdef GR_TEST_UTILS
2211
joshualitt6c891102015-05-13 08:51:49 -07002212BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002213 static uint32_t gContextID = SK_InvalidGenID;
2214 static GrAtlasTextContext* gTextContext = NULL;
robertphillipsfcf78292015-06-19 11:49:52 -07002215 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt79dfb2b2015-05-11 08:58:08 -07002216
2217 if (context->uniqueID() != gContextID) {
2218 gContextID = context->uniqueID();
2219 SkDELETE(gTextContext);
robertphillips2334fb62015-06-17 05:43:33 -07002220
joshualitt79dfb2b2015-05-11 08:58:08 -07002221 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2222 // because we don't really want to have a gpu device here.
2223 // We enable distance fields by twiddling a knob on the paint
robertphillipsfcf78292015-06-19 11:49:52 -07002224 GrDrawContext* drawContext = context->drawContext(&gSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -07002225
robertphillipsfcf78292015-06-19 11:49:52 -07002226 gTextContext = GrAtlasTextContext::Create(context, drawContext, gSurfaceProps);
joshualitt79dfb2b2015-05-11 08:58:08 -07002227 }
2228
2229 // create dummy render target
2230 GrSurfaceDesc desc;
2231 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2232 desc.fWidth = 1024;
2233 desc.fHeight = 1024;
2234 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002235 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002236 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2237 SkASSERT(texture);
2238 SkASSERT(NULL != texture->asRenderTarget());
2239 GrRenderTarget* rt = texture->asRenderTarget();
2240
2241 // Setup dummy SkPaint / GrPaint
2242 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002243 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002244 SkPaint skPaint;
joshualitt79dfb2b2015-05-11 08:58:08 -07002245 skPaint.setColor(color);
2246 skPaint.setLCDRenderText(random->nextBool());
2247 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2248 skPaint.setSubpixelText(random->nextBool());
2249
2250 GrPaint grPaint;
2251 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2252 SkFAIL("couldn't convert paint\n");
2253 }
2254
2255 const char* text = "The quick brown fox jumps over the lazy dog.";
2256 int textLen = (int)strlen(text);
2257
2258 // Setup clip
2259 GrClip clip;
2260 SkIRect noClip = SkIRect::MakeLargest();
2261
2262 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2263 // intend to test the batch with this unit test, that is okay.
2264 SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
2265 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2266 static_cast<size_t>(textLen), 0, 0, noClip));
2267
2268 SkScalar transX = static_cast<SkScalar>(random->nextU());
2269 SkScalar transY = static_cast<SkScalar>(random->nextU());
2270 const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
2271 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2272}
2273
2274#endif