blob: 7252d76ca2acb65a1876a2a3ae8cbf8c2d34d108 [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;
joshualitta7c63892015-04-21 13:24:37 -070058static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
joshualitt9bd2daf2015-04-17 09:30:06 -070059
60SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
61static const int kDistanceAdjustLumShift = 5;
62
joshualitt1d89e8d2015-04-01 12:40:54 -070063static const int kVerticesPerGlyph = 4;
64static const int kIndicesPerGlyph = 6;
65
66static size_t get_vertex_stride(GrMaskFormat maskFormat) {
67 switch (maskFormat) {
68 case kA8_GrMaskFormat:
69 return kGrayTextVASize;
70 case kARGB_GrMaskFormat:
71 return kColorTextVASize;
72 default:
73 return kLCDTextVASize;
74 }
75}
76
joshualitt9bd2daf2015-04-17 09:30:06 -070077static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
78 SkASSERT(maskFormat == kA8_GrMaskFormat);
79 if (useLCDText) {
80 return kLCDTextVASize;
81 } else {
82 return kGrayTextVASize;
83 }
84}
85
86static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
87 unsigned r = SkColorGetR(c);
88 unsigned g = SkColorGetG(c);
89 unsigned b = SkColorGetB(c);
90 return GrColorPackRGBA(r, g, b, 0xff);
91}
92
joshualitt1d89e8d2015-04-01 12:40:54 -070093};
94
95// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -070096// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -070097
joshualittdbd35932015-04-02 09:19:04 -070098GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -070099 GrDrawContext* drawContext,
robertphillipsbcd7ab52015-06-18 05:27:18 -0700100 const SkDeviceProperties& properties)
robertphillips2334fb62015-06-17 05:43:33 -0700101 : INHERITED(context, drawContext, properties)
robertphillips9fc82752015-06-19 04:46:45 -0700102 , fDistanceAdjustTable(SkNEW(DistanceAdjustTable)) {
joshualittb7133be2015-04-08 09:08:31 -0700103 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
104 // vertexStride
105 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
106 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700107 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700108 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700109}
110
robertphillips9fc82752015-06-19 04:46:45 -0700111void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() {
joshualitt9bd2daf2015-04-17 09:30:06 -0700112
113 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
114 // text. The mask gamma hack is based off of guessing what the blend color is going to
115 // be, and adjusting the mask so that when run through the linear blend will
116 // produce the value closest to the desired result. However, in practice this means
117 // that the 'adjusted' mask is just increasing or decreasing the coverage of
118 // the mask depending on what it is thought it will blit against. For black (on
119 // assumed white) this means that coverages are decreased (on a curve). For white (on
120 // assumed black) this means that coverages are increased (on a a curve). At
121 // middle (perceptual) gray (which could be blit against anything) the coverages
122 // remain the same.
123 //
124 // The idea here is that instead of determining the initial (real) coverage and
125 // then adjusting that coverage, we determine an adjusted coverage directly by
126 // essentially manipulating the geometry (in this case, the distance to the glyph
127 // edge). So for black (on assumed white) this thins a bit; for white (on
128 // assumed black) this fake bolds the geometry a bit.
129 //
130 // The distance adjustment is calculated by determining the actual coverage value which
131 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
132 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
133 // actual edge. So by subtracting this distance adjustment and computing without the
134 // the coverage adjustment we should get 0.5 coverage at the same point.
135 //
136 // This has several implications:
137 // For non-gray lcd smoothed text, each subpixel essentially is using a
138 // slightly different geometry.
139 //
140 // For black (on assumed white) this may not cover some pixels which were
141 // previously covered; however those pixels would have been only slightly
142 // covered and that slight coverage would have been decreased anyway. Also, some pixels
143 // which were previously fully covered may no longer be fully covered.
144 //
145 // For white (on assumed black) this may cover some pixels which weren't
146 // previously covered at all.
147
148 int width, height;
149 size_t size;
150
151#ifdef SK_GAMMA_CONTRAST
152 SkScalar contrast = SK_GAMMA_CONTRAST;
153#else
154 SkScalar contrast = 0.5f;
155#endif
robertphillips9fc82752015-06-19 04:46:45 -0700156 SkScalar paintGamma = SK_GAMMA_EXPONENT;
157 SkScalar deviceGamma = SK_GAMMA_EXPONENT;
joshualitt9bd2daf2015-04-17 09:30:06 -0700158
159 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
160 &width, &height);
161
162 SkASSERT(kExpectedDistanceAdjustTableSize == height);
163 fTable = SkNEW_ARRAY(SkScalar, height);
164
165 SkAutoTArray<uint8_t> data((int)size);
166 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
167
168 // find the inverse points where we cross 0.5
169 // binsearch might be better, but we only need to do this once on creation
170 for (int row = 0; row < height; ++row) {
171 uint8_t* rowPtr = data.get() + row*width;
172 for (int col = 0; col < width - 1; ++col) {
173 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
174 // compute point where a mask value will give us a result of 0.5
175 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
176 float borderAlpha = (col + interp) / 255.f;
177
178 // compute t value for that alpha
179 // this is an approximate inverse for smoothstep()
180 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
181
182 // compute distance which gives us that t value
183 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
184 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
185
186 fTable[row] = d;
187 break;
188 }
189 }
190 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700191}
192
joshualittdbd35932015-04-02 09:19:04 -0700193GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
robertphillips2334fb62015-06-17 05:43:33 -0700194 GrDrawContext* drawContext,
robertphillipsbcd7ab52015-06-18 05:27:18 -0700195 const SkDeviceProperties& props) {
196 return SkNEW_ARGS(GrAtlasTextContext, (context, drawContext, props));
joshualitt1d89e8d2015-04-01 12:40:54 -0700197}
198
joshualittdbd35932015-04-02 09:19:04 -0700199bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
200 const GrClip&,
201 const GrPaint&,
202 const SkPaint& skPaint,
203 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700204 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
205 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700206}
207
joshualitt9e36c1a2015-04-14 12:17:27 -0700208GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
209 GrColor canonicalColor = paint.computeLuminanceColor();
210 if (lcd) {
211 // This is the correct computation, but there are tons of cases where LCD can be overridden.
212 // For now we just regenerate if any run in a textblob has LCD.
213 // TODO figure out where all of these overrides are and see if we can incorporate that logic
214 // at a higher level *OR* use sRGB
215 SkASSERT(false);
216 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
217 } else {
218 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
219 // gamma corrected masks anyways, nor color
220 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
221 SkColorGetG(canonicalColor),
222 SkColorGetB(canonicalColor));
223 // reduce to our finite number of bits
224 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
225 }
226 return canonicalColor;
227}
228
229// TODO if this function ever shows up in profiling, then we can compute this value when the
230// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
231// run so this is not a big deal to compute here.
232bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
233 SkTextBlob::RunIterator it(blob);
234 for (; !it.done(); it.next()) {
235 if (it.isLCD()) {
236 return true;
237 }
238 }
239 return false;
240}
241
joshualitt2a0e9f32015-04-13 06:12:21 -0700242bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
243 const BitmapTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700244 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700245 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700246 // If we have LCD text then our canonical color will be set to transparent, in this case we have
247 // to regenerate the blob on any color change
248 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700249 return true;
250 }
251
252 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
253 return true;
254 }
255
256 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
257 return true;
258 }
259
joshualitt53b5f442015-04-13 06:33:59 -0700260 // We only cache one masked version
261 if (blob.fKey.fHasBlur &&
262 (blob.fBlurRec.fSigma != blurRec.fSigma ||
263 blob.fBlurRec.fStyle != blurRec.fStyle ||
264 blob.fBlurRec.fQuality != blurRec.fQuality)) {
265 return true;
266 }
267
268 // Similarly, we only cache one version for each style
269 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
270 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
271 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
272 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
273 return true;
274 }
275
joshualittfcfb9fc2015-04-21 07:35:10 -0700276 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
277 // for mixed blobs if this becomes an issue.
278 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700279 // Identical viewmatrices and we can reuse in all cases
280 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
281 return false;
282 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700283 return true;
284 }
285
joshualittfcfb9fc2015-04-21 07:35:10 -0700286 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700287 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
288 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
289 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
290 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
291 return true;
292 }
293
joshualittfcfb9fc2015-04-21 07:35:10 -0700294 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
295 // but only for integer translations.
296 // This cool bit of math will determine the necessary translation to apply to the already
297 // generated vertex coordinates to move them to the correct position
298 SkScalar transX = viewMatrix.getTranslateX() +
299 viewMatrix.getScaleX() * (x - blob.fX) +
300 viewMatrix.getSkewX() * (y - blob.fY) -
301 blob.fViewMatrix.getTranslateX();
302 SkScalar transY = viewMatrix.getTranslateY() +
303 viewMatrix.getSkewY() * (x - blob.fX) +
304 viewMatrix.getScaleY() * (y - blob.fY) -
305 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700306 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700307 return true;
308 }
309
joshualittfcfb9fc2015-04-21 07:35:10 -0700310 (*outTransX) = transX;
311 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700312 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700313 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
314 // distance field being generated, so we have to regenerate in those cases
315 SkScalar newMaxScale = viewMatrix.getMaxScale();
316 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
317 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
318 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
319 return true;
320 }
321
322 (*outTransX) = x - blob.fX;
323 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700324 }
joshualitta7c63892015-04-21 13:24:37 -0700325 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
326 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
327 // the blob anyways at flush time, so no need to regenerate explicitly
joshualittfcfb9fc2015-04-21 07:35:10 -0700328
joshualitt2a0e9f32015-04-13 06:12:21 -0700329 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700330}
331
332
joshualittdbd35932015-04-02 09:19:04 -0700333inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
334 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700335 const SkMatrix* viewMatrix,
336 bool noGamma) {
337 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700338 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
339 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
340}
341
robertphillips9c240a12015-05-28 07:45:59 -0700342void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700343 const GrClip& clip, const SkPaint& skPaint,
344 const SkMatrix& viewMatrix, const SkTextBlob* blob,
345 SkScalar x, SkScalar y,
joshualittdbd35932015-04-02 09:19:04 -0700346 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700347 // If we have been abandoned, then don't draw
robertphillipsea461502015-05-26 11:38:03 -0700348 if (fContext->abandoned()) {
349 return;
350 }
351
joshualitt2a0e9f32015-04-13 06:12:21 -0700352 SkAutoTUnref<BitmapTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700353 SkMaskFilter::BlurRec blurRec;
354 BitmapTextBlob::Key key;
355 // It might be worth caching these things, but its not clear at this time
356 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
357 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700358 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700359 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700360 drawFilter);
361
362 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700363 bool hasLCD = HasLCD(blob);
joshualitte4cee1f2015-05-11 13:04:28 -0700364
365 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
366 SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() :
367 kUnknown_SkPixelGeometry;
368
joshualitt9e36c1a2015-04-14 12:17:27 -0700369 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
370 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
371 // ensure we always match the same key
372 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
373 ComputeCanonicalColor(skPaint, hasLCD);
374
joshualitte4cee1f2015-05-11 13:04:28 -0700375 key.fPixelGeometry = pixelGeometry;
joshualitt53b5f442015-04-13 06:33:59 -0700376 key.fUniqueID = blob->uniqueID();
377 key.fStyle = skPaint.getStyle();
378 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700379 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700380 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700381 }
382
joshualitt1d89e8d2015-04-01 12:40:54 -0700383 SkIRect clipRect;
384 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
385
joshualitt2a0e9f32015-04-13 06:12:21 -0700386 SkScalar transX = 0.f;
387 SkScalar transY = 0.f;
388
joshualitt9e36c1a2015-04-14 12:17:27 -0700389 // Though for the time being runs in the textblob can override the paint, they only touch font
390 // info.
391 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700392 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
393 return;
394 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700395
joshualittb7133be2015-04-08 09:08:31 -0700396 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700397 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700398 // We have to remake the blob because changes may invalidate our masks.
399 // TODO we could probably get away reuse most of the time if the pointer is unique,
400 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700401 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700402 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
403 kGrayTextVASize)));
robertphillips9c240a12015-05-28 07:45:59 -0700404 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700405 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700406 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700407 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
408 // offsets
joshualitt2a0e9f32015-04-13 06:12:21 -0700409 cacheBlob->fViewMatrix = viewMatrix;
410 cacheBlob->fX = x;
411 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700412 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700413 }
414 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700415 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700416 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
417 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700418 } else {
419 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
420 }
robertphillips9c240a12015-05-28 07:45:59 -0700421 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -0700422 blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700423 }
424
joshualitt9e36c1a2015-04-14 12:17:27 -0700425 cacheBlob->fPaintColor = skPaint.getColor();
robertphillips2334fb62015-06-17 05:43:33 -0700426 this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700427 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700428}
429
joshualitt9bd2daf2015-04-17 09:30:06 -0700430inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
431 const SkMatrix& viewMatrix) {
432 // TODO: support perspective (need getMaxScale replacement)
433 if (viewMatrix.hasPerspective()) {
434 return false;
435 }
436
437 SkScalar maxScale = viewMatrix.getMaxScale();
438 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
439 // Hinted text looks far better at small resolutions
440 // Scaling up beyond 2x yields undesireable artifacts
joshualitta7c63892015-04-21 13:24:37 -0700441 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700442 return false;
443 }
444
robertphillipsbcd7ab52015-06-18 05:27:18 -0700445 bool useDFT = fDeviceProperties.useDFT();
446#if SK_FORCE_DISTANCE_FIELD_TEXT
447 useDFT = true;
448#endif
449
450 if (!useDFT && !skPaint.isDistanceFieldTextTEMP() && scaledTextSize < kLargeDFFontSize) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700451 return false;
452 }
453
454 // rasterizers and mask filters modify alpha, which doesn't
455 // translate well to distance
456 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
bsalomon76228632015-05-29 08:02:10 -0700457 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700458 return false;
459 }
460
461 // TODO: add some stroking support
462 if (skPaint.getStyle() != SkPaint::kFill_Style) {
463 return false;
464 }
465
466 return true;
467}
468
robertphillips9c240a12015-05-28 07:45:59 -0700469void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700470 const SkPaint& skPaint, GrColor color,
471 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700472 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700473 SkDrawFilter* drawFilter, const SkIRect& clipRect,
474 GrRenderTarget* rt, const GrClip& clip,
475 const GrPaint& paint) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700476 cacheBlob->fViewMatrix = viewMatrix;
477 cacheBlob->fX = x;
478 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700479
480 // Regenerate textblob
481 SkPaint runPaint = skPaint;
482 SkTextBlob::RunIterator it(blob);
483 for (int run = 0; !it.done(); it.next(), run++) {
484 int glyphCount = it.glyphCount();
485 size_t textLen = glyphCount * sizeof(uint16_t);
486 const SkPoint& offset = it.offset();
487 // applyFontToPaint() always overwrites the exact same attributes,
488 // so it is safe to not re-seed the paint for this reason.
489 it.applyFontToPaint(&runPaint);
490
491 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
492 // A false return from filter() means we should abort the current draw.
493 runPaint = skPaint;
494 continue;
495 }
496
robertphillips9c240a12015-05-28 07:45:59 -0700497 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
joshualitt1d89e8d2015-04-01 12:40:54 -0700498
joshualitt1d89e8d2015-04-01 12:40:54 -0700499 // setup vertex / glyphIndex for the new run
500 if (run > 0) {
501 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
502 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
503
504 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
505 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
506
507 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
508 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
509 }
510
joshualittfcfb9fc2015-04-21 07:35:10 -0700511 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
512 cacheBlob->setHasDistanceField();
513 SkPaint dfPaint = runPaint;
514 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700515 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700516 Run& runIdx = cacheBlob->fRuns[run];
517 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
518 subRun.fUseLCDText = runPaint.isLCDRenderText();
519 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700520
joshualittfcfb9fc2015-04-21 07:35:10 -0700521 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
522
523 SkTDArray<char> fallbackTxt;
524 SkTDArray<SkScalar> fallbackPos;
525 SkPoint dfOffset;
526 int scalarsPerPosition = 2;
527 switch (it.positioning()) {
528 case SkTextBlob::kDefault_Positioning: {
529 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
530 (const char *)it.glyphs(), textLen,
531 x + offset.x(), y + offset.y(), clipRect, textRatio,
532 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
533 break;
534 }
535 case SkTextBlob::kHorizontal_Positioning: {
536 scalarsPerPosition = 1;
537 dfOffset = SkPoint::Make(x, y + offset.y());
538 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
539 (const char*)it.glyphs(), textLen, it.pos(),
540 scalarsPerPosition, dfOffset, clipRect, textRatio,
541 &fallbackTxt, &fallbackPos);
542 break;
543 }
544 case SkTextBlob::kFull_Positioning: {
545 dfOffset = SkPoint::Make(x, y);
546 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
547 (const char*)it.glyphs(), textLen, it.pos(),
548 scalarsPerPosition, dfOffset, clipRect, textRatio,
549 &fallbackTxt, &fallbackPos);
550 break;
551 }
552 }
553 if (fallbackTxt.count()) {
554 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
555 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
556 clipRect);
557 }
558
559 SkGlyphCache::AttachCache(cache);
560 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
561 cacheBlob->fRuns[run].fDrawAsPaths = true;
562 } else {
563 cacheBlob->setHasBitmap();
564 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
565 false);
566 switch (it.positioning()) {
567 case SkTextBlob::kDefault_Positioning:
568 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
569 (const char *)it.glyphs(), textLen,
570 x + offset.x(), y + offset.y(), clipRect);
571 break;
572 case SkTextBlob::kHorizontal_Positioning:
573 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
574 (const char*)it.glyphs(), textLen, it.pos(), 1,
575 SkPoint::Make(x, y + offset.y()), clipRect);
576 break;
577 case SkTextBlob::kFull_Positioning:
578 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
579 (const char*)it.glyphs(), textLen, it.pos(), 2,
580 SkPoint::Make(x, y), clipRect);
581 break;
582 }
583 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700584 }
585
586 if (drawFilter) {
587 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
588 runPaint = skPaint;
589 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700590 }
591}
592
joshualitt64c99cc2015-04-21 09:43:03 -0700593inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
594 SkPaint* skPaint,
595 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700596 const SkMatrix& viewMatrix) {
597 // getMaxScale doesn't support perspective, so neither do we at the moment
598 SkASSERT(!viewMatrix.hasPerspective());
599 SkScalar maxScale = viewMatrix.getMaxScale();
600 SkScalar textSize = skPaint->getTextSize();
601 SkScalar scaledTextSize = textSize;
602 // if we have non-unity scale, we need to choose our base text size
603 // based on the SkPaint's text size multiplied by the max scale factor
604 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
605 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
606 scaledTextSize *= maxScale;
607 }
608
joshualitt64c99cc2015-04-21 09:43:03 -0700609 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
610 // and ceiling. A scale outside of this range would require regenerating the distance fields
611 SkScalar dfMaskScaleFloor;
612 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700613 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700614 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700615 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700616 *textRatio = textSize / kSmallDFFontSize;
617 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
618 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700619 dfMaskScaleFloor = kSmallDFFontLimit;
620 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700621 *textRatio = textSize / kMediumDFFontSize;
622 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
623 } else {
joshualitta7c63892015-04-21 13:24:37 -0700624 dfMaskScaleFloor = kMediumDFFontLimit;
625 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700626 *textRatio = textSize / kLargeDFFontSize;
627 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
628 }
629
joshualitt64c99cc2015-04-21 09:43:03 -0700630 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
631 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
632 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
633 // tolerate before we'd have to move to a large mip size. When we actually test these values
634 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
635 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
636 // level)
joshualitta7c63892015-04-21 13:24:37 -0700637 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700638 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
639 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
640
joshualitt9bd2daf2015-04-17 09:30:06 -0700641 skPaint->setLCDRenderText(false);
642 skPaint->setAutohinted(false);
643 skPaint->setHinting(SkPaint::kNormal_Hinting);
644 skPaint->setSubpixelText(true);
645}
646
joshualittfec19e12015-04-17 10:32:32 -0700647inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700648 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700649 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700650 const GrPaint& paint,
651 const SkPaint& skPaint,
652 const SkMatrix& viewMatrix,
653 const SkTDArray<char>& fallbackTxt,
654 const SkTDArray<SkScalar>& fallbackPos,
655 int scalarsPerPosition,
656 const SkPoint& offset,
657 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700658 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700659 blob->setHasBitmap();
660 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700661 // Push back a new subrun to fill and set the override descriptor
662 run.push_back();
663 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
664 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
joshualittfec19e12015-04-17 10:32:32 -0700665 &fDeviceProperties, &viewMatrix, false);
666 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700667 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700668 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700669 fallbackTxt.begin(), fallbackTxt.count(),
670 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
671 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700672}
673
674inline GrAtlasTextContext::BitmapTextBlob*
675GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
676 const SkMatrix& viewMatrix, SkGlyphCache** cache,
677 SkPaint* dfPaint, SkScalar* textRatio) {
678 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
679
680 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700681 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700682 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700683 Run& run = blob->fRuns[0];
684 PerSubRunInfo& subRun = run.fSubRunInfo.back();
685 subRun.fUseLCDText = origPaint.isLCDRenderText();
686 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700687
688 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
689 return blob;
690}
691
joshualitt79dfb2b2015-05-11 08:58:08 -0700692inline GrAtlasTextContext::BitmapTextBlob*
693GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
694 const GrPaint& paint, const SkPaint& skPaint,
695 const SkMatrix& viewMatrix,
696 const char text[], size_t byteLength,
697 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700698 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700699 SkIRect clipRect;
700 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
701
joshualitt79dfb2b2015-05-11 08:58:08 -0700702 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700703 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
704 SkPaint dfPaint;
705 SkScalar textRatio;
706 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700707 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt1d89e8d2015-04-01 12:40:54 -0700708
joshualitt9bd2daf2015-04-17 09:30:06 -0700709 SkTDArray<char> fallbackTxt;
710 SkTDArray<SkScalar> fallbackPos;
711 SkPoint offset;
712 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
713 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
714 &offset, skPaint);
715 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700716 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700717 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700718 fallbackPos, 2, offset, clipRect);
719 }
720 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700721 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700722 blob->fViewMatrix = viewMatrix;
723
724 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
725 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
726 byteLength, x, y, clipRect);
727 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700728 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700729 return blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700730}
731
joshualitt79dfb2b2015-05-11 08:58:08 -0700732inline GrAtlasTextContext::BitmapTextBlob*
733GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
734 const GrPaint& paint, const SkPaint& skPaint,
735 const SkMatrix& viewMatrix,
736 const char text[], size_t byteLength,
737 const SkScalar pos[], int scalarsPerPosition,
738 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700739 int glyphCount = skPaint.countText(text, byteLength);
740
741 SkIRect clipRect;
742 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
743
joshualitt79dfb2b2015-05-11 08:58:08 -0700744 BitmapTextBlob* blob;
joshualitt9bd2daf2015-04-17 09:30:06 -0700745 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
746 SkPaint dfPaint;
747 SkScalar textRatio;
748 SkGlyphCache* cache;
joshualitt79dfb2b2015-05-11 08:58:08 -0700749 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
joshualitt9bd2daf2015-04-17 09:30:06 -0700750
751 SkTDArray<char> fallbackTxt;
752 SkTDArray<SkScalar> fallbackPos;
753 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
754 byteLength, pos, scalarsPerPosition, offset, clipRect,
755 textRatio, &fallbackTxt, &fallbackPos);
756 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700757 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700758 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700759 fallbackPos, scalarsPerPosition, offset, clipRect);
760 }
761 } else {
joshualitt79dfb2b2015-05-11 08:58:08 -0700762 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
joshualitt9bd2daf2015-04-17 09:30:06 -0700763 blob->fViewMatrix = viewMatrix;
764 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
765 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
766 byteLength, pos, scalarsPerPosition, offset, clipRect);
767 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700768 }
joshualitt79dfb2b2015-05-11 08:58:08 -0700769 return blob;
770}
771
robertphillips2334fb62015-06-17 05:43:33 -0700772void GrAtlasTextContext::onDrawText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700773 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700774 const GrPaint& paint, const SkPaint& skPaint,
775 const SkMatrix& viewMatrix,
776 const char text[], size_t byteLength,
777 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -0700778 SkAutoTUnref<BitmapTextBlob> blob(
779 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
780 text, byteLength, x, y, regionClipBounds));
781 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt79dfb2b2015-05-11 08:58:08 -0700782}
783
robertphillips2334fb62015-06-17 05:43:33 -0700784void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700785 const GrClip& clip,
joshualitt79dfb2b2015-05-11 08:58:08 -0700786 const GrPaint& paint, const SkPaint& skPaint,
787 const SkMatrix& viewMatrix,
788 const char text[], size_t byteLength,
789 const SkScalar pos[], int scalarsPerPosition,
790 const SkPoint& offset, const SkIRect& regionClipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -0700791 SkAutoTUnref<BitmapTextBlob> blob(
792 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
793 text, byteLength,
794 pos, scalarsPerPosition,
795 offset, regionClipBounds));
joshualitt79dfb2b2015-05-11 08:58:08 -0700796
robertphillips2334fb62015-06-17 05:43:33 -0700797 this->flush(blob, rt, skPaint, paint, clip, regionClipBounds);
joshualitt9bd2daf2015-04-17 09:30:06 -0700798}
799
800void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
801 SkGlyphCache* cache, const SkPaint& skPaint,
802 GrColor color,
803 const SkMatrix& viewMatrix,
804 const char text[], size_t byteLength,
805 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700806 SkASSERT(byteLength == 0 || text != NULL);
807
808 // nothing to draw
809 if (text == NULL || byteLength == 0) {
810 return;
811 }
812
813 fCurrStrike = NULL;
814 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
815
816 // Get GrFontScaler from cache
817 GrFontScaler* fontScaler = GetGrFontScaler(cache);
818
819 // transform our starting point
820 {
821 SkPoint loc;
822 viewMatrix.mapXY(x, y, &loc);
823 x = loc.fX;
824 y = loc.fY;
825 }
826
827 // need to measure first
828 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
829 SkVector stopVector;
830 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
831
832 SkScalar stopX = stopVector.fX;
833 SkScalar stopY = stopVector.fY;
834
835 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
836 stopX = SkScalarHalf(stopX);
837 stopY = SkScalarHalf(stopY);
838 }
839 x -= stopX;
840 y -= stopY;
841 }
842
843 const char* stop = text + byteLength;
844
845 SkAutoKern autokern;
846
847 SkFixed fxMask = ~0;
848 SkFixed fyMask = ~0;
849 SkScalar halfSampleX, halfSampleY;
850 if (cache->isSubpixel()) {
851 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
852 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
853 if (kX_SkAxisAlignment == baseline) {
854 fyMask = 0;
855 halfSampleY = SK_ScalarHalf;
856 } else if (kY_SkAxisAlignment == baseline) {
857 fxMask = 0;
858 halfSampleX = SK_ScalarHalf;
859 }
860 } else {
861 halfSampleX = halfSampleY = SK_ScalarHalf;
862 }
863
864 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
865 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
866
867 while (text < stop) {
868 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
869
870 fx += autokern.adjust(glyph);
871
872 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700873 this->bmpAppendGlyph(blob,
874 runIndex,
875 GrGlyph::Pack(glyph.getGlyphID(),
876 glyph.getSubXFixed(),
877 glyph.getSubYFixed(),
878 GrGlyph::kCoverage_MaskStyle),
879 Sk48Dot16FloorToInt(fx),
880 Sk48Dot16FloorToInt(fy),
881 color,
882 fontScaler,
883 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700884 }
885
886 fx += glyph.fAdvanceX;
887 fy += glyph.fAdvanceY;
888 }
889}
890
joshualitt9bd2daf2015-04-17 09:30:06 -0700891void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
892 SkGlyphCache* cache, const SkPaint& skPaint,
893 GrColor color,
894 const SkMatrix& viewMatrix,
895 const char text[], size_t byteLength,
896 const SkScalar pos[], int scalarsPerPosition,
897 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700898 SkASSERT(byteLength == 0 || text != NULL);
899 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
900
901 // nothing to draw
902 if (text == NULL || byteLength == 0) {
903 return;
904 }
905
906 fCurrStrike = NULL;
907 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
908
909 // Get GrFontScaler from cache
910 GrFontScaler* fontScaler = GetGrFontScaler(cache);
911
912 const char* stop = text + byteLength;
913 SkTextAlignProc alignProc(skPaint.getTextAlign());
914 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
915
916 if (cache->isSubpixel()) {
917 // maybe we should skip the rounding if linearText is set
918 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
919
920 SkFixed fxMask = ~0;
921 SkFixed fyMask = ~0;
922 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
923 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
924 if (kX_SkAxisAlignment == baseline) {
925 fyMask = 0;
926 halfSampleY = SK_ScalarHalf;
927 } else if (kY_SkAxisAlignment == baseline) {
928 fxMask = 0;
929 halfSampleX = SK_ScalarHalf;
930 }
931
932 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
933 while (text < stop) {
934 SkPoint tmsLoc;
935 tmsProc(pos, &tmsLoc);
936 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
937 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
938
939 const SkGlyph& glyph = glyphCacheProc(cache, &text,
940 fx & fxMask, fy & fyMask);
941
942 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700943 this->bmpAppendGlyph(blob,
944 runIndex,
945 GrGlyph::Pack(glyph.getGlyphID(),
946 glyph.getSubXFixed(),
947 glyph.getSubYFixed(),
948 GrGlyph::kCoverage_MaskStyle),
949 Sk48Dot16FloorToInt(fx),
950 Sk48Dot16FloorToInt(fy),
951 color,
952 fontScaler,
953 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700954 }
955 pos += scalarsPerPosition;
956 }
957 } else {
958 while (text < stop) {
959 const char* currentText = text;
960 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
961
962 if (metricGlyph.fWidth) {
963 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
964 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
965 SkPoint tmsLoc;
966 tmsProc(pos, &tmsLoc);
967 SkPoint alignLoc;
968 alignProc(tmsLoc, metricGlyph, &alignLoc);
969
970 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
971 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
972
973 // have to call again, now that we've been "aligned"
974 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
975 fx & fxMask, fy & fyMask);
976 // the assumption is that the metrics haven't changed
977 SkASSERT(prevAdvX == glyph.fAdvanceX);
978 SkASSERT(prevAdvY == glyph.fAdvanceY);
979 SkASSERT(glyph.fWidth);
980
joshualitt9bd2daf2015-04-17 09:30:06 -0700981 this->bmpAppendGlyph(blob,
982 runIndex,
983 GrGlyph::Pack(glyph.getGlyphID(),
984 glyph.getSubXFixed(),
985 glyph.getSubYFixed(),
986 GrGlyph::kCoverage_MaskStyle),
987 Sk48Dot16FloorToInt(fx),
988 Sk48Dot16FloorToInt(fy),
989 color,
990 fontScaler,
991 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700992 }
993 pos += scalarsPerPosition;
994 }
995 }
996 } else { // not subpixel
997
998 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
999 while (text < stop) {
1000 // the last 2 parameters are ignored
1001 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1002
1003 if (glyph.fWidth) {
1004 SkPoint tmsLoc;
1005 tmsProc(pos, &tmsLoc);
1006
1007 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
1008 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001009 this->bmpAppendGlyph(blob,
1010 runIndex,
1011 GrGlyph::Pack(glyph.getGlyphID(),
1012 glyph.getSubXFixed(),
1013 glyph.getSubYFixed(),
1014 GrGlyph::kCoverage_MaskStyle),
1015 Sk48Dot16FloorToInt(fx),
1016 Sk48Dot16FloorToInt(fy),
1017 color,
1018 fontScaler,
1019 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001020 }
1021 pos += scalarsPerPosition;
1022 }
1023 } else {
1024 while (text < stop) {
1025 // the last 2 parameters are ignored
1026 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1027
1028 if (glyph.fWidth) {
1029 SkPoint tmsLoc;
1030 tmsProc(pos, &tmsLoc);
1031
1032 SkPoint alignLoc;
1033 alignProc(tmsLoc, glyph, &alignLoc);
1034
1035 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1036 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001037 this->bmpAppendGlyph(blob,
1038 runIndex,
1039 GrGlyph::Pack(glyph.getGlyphID(),
1040 glyph.getSubXFixed(),
1041 glyph.getSubYFixed(),
1042 GrGlyph::kCoverage_MaskStyle),
1043 Sk48Dot16FloorToInt(fx),
1044 Sk48Dot16FloorToInt(fy),
1045 color,
1046 fontScaler,
1047 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001048 }
1049 pos += scalarsPerPosition;
1050 }
1051 }
1052 }
1053}
1054
joshualitt9bd2daf2015-04-17 09:30:06 -07001055
1056void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1057 SkGlyphCache* cache, const SkPaint& skPaint,
1058 GrColor color,
1059 const SkMatrix& viewMatrix,
1060 const char text[], size_t byteLength,
1061 SkScalar x, SkScalar y, const SkIRect& clipRect,
1062 SkScalar textRatio,
1063 SkTDArray<char>* fallbackTxt,
1064 SkTDArray<SkScalar>* fallbackPos,
1065 SkPoint* offset,
1066 const SkPaint& origPaint) {
1067 SkASSERT(byteLength == 0 || text != NULL);
1068
1069 // nothing to draw
1070 if (text == NULL || byteLength == 0) {
1071 return;
1072 }
1073
1074 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1075 SkAutoDescriptor desc;
1076 origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
1077 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1078 desc.getDesc());
1079
1080 SkTArray<SkScalar> positions;
1081
1082 const char* textPtr = text;
1083 SkFixed stopX = 0;
1084 SkFixed stopY = 0;
1085 SkFixed origin = 0;
1086 switch (origPaint.getTextAlign()) {
1087 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1088 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1089 case SkPaint::kLeft_Align: origin = 0; break;
1090 }
1091
1092 SkAutoKern autokern;
1093 const char* stop = text + byteLength;
1094 while (textPtr < stop) {
1095 // don't need x, y here, since all subpixel variants will have the
1096 // same advance
1097 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1098
1099 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1100 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1101
1102 SkFixed height = glyph.fAdvanceY;
1103 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1104
1105 stopX += width;
1106 stopY += height;
1107 }
1108 SkASSERT(textPtr == stop);
1109
1110 // now adjust starting point depending on alignment
1111 SkScalar alignX = SkFixedToScalar(stopX);
1112 SkScalar alignY = SkFixedToScalar(stopY);
1113 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1114 alignX = SkScalarHalf(alignX);
1115 alignY = SkScalarHalf(alignY);
1116 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1117 alignX = 0;
1118 alignY = 0;
1119 }
1120 x -= alignX;
1121 y -= alignY;
1122 *offset = SkPoint::Make(x, y);
1123
1124 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1125 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1126 fallbackPos);
1127 SkGlyphCache::AttachCache(origPaintCache);
1128}
1129
1130void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1131 SkGlyphCache* cache, const SkPaint& skPaint,
1132 GrColor color,
1133 const SkMatrix& viewMatrix,
1134 const char text[], size_t byteLength,
1135 const SkScalar pos[], int scalarsPerPosition,
1136 const SkPoint& offset, const SkIRect& clipRect,
1137 SkScalar textRatio,
1138 SkTDArray<char>* fallbackTxt,
1139 SkTDArray<SkScalar>* fallbackPos) {
1140
1141 SkASSERT(byteLength == 0 || text != NULL);
1142 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1143
1144 // nothing to draw
1145 if (text == NULL || byteLength == 0) {
1146 return;
1147 }
1148
1149 fCurrStrike = NULL;
1150
1151 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1152 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1153
1154 const char* stop = text + byteLength;
1155
1156 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1157 while (text < stop) {
1158 const char* lastText = text;
1159 // the last 2 parameters are ignored
1160 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1161
1162 if (glyph.fWidth) {
1163 SkScalar x = offset.x() + pos[0];
1164 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1165
1166 if (!this->dfAppendGlyph(blob,
1167 runIndex,
1168 GrGlyph::Pack(glyph.getGlyphID(),
1169 glyph.getSubXFixed(),
1170 glyph.getSubYFixed(),
1171 GrGlyph::kDistance_MaskStyle),
1172 x, y, color, fontScaler, clipRect,
1173 textRatio, viewMatrix)) {
1174 // couldn't append, send to fallback
1175 fallbackTxt->append(SkToInt(text-lastText), lastText);
1176 *fallbackPos->append() = pos[0];
1177 if (2 == scalarsPerPosition) {
1178 *fallbackPos->append() = pos[1];
1179 }
1180 }
1181 }
1182 pos += scalarsPerPosition;
1183 }
1184 } else {
1185 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1186 : SK_Scalar1;
1187 while (text < stop) {
1188 const char* lastText = text;
1189 // the last 2 parameters are ignored
1190 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1191
1192 if (glyph.fWidth) {
1193 SkScalar x = offset.x() + pos[0];
1194 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1195
1196 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1197 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1198
1199 if (!this->dfAppendGlyph(blob,
1200 runIndex,
1201 GrGlyph::Pack(glyph.getGlyphID(),
1202 glyph.getSubXFixed(),
1203 glyph.getSubYFixed(),
1204 GrGlyph::kDistance_MaskStyle),
1205 x - advanceX, y - advanceY, color,
1206 fontScaler,
1207 clipRect,
1208 textRatio,
1209 viewMatrix)) {
1210 // couldn't append, send to fallback
1211 fallbackTxt->append(SkToInt(text-lastText), lastText);
1212 *fallbackPos->append() = pos[0];
1213 if (2 == scalarsPerPosition) {
1214 *fallbackPos->append() = pos[1];
1215 }
1216 }
1217 }
1218 pos += scalarsPerPosition;
1219 }
1220 }
1221}
1222
1223void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1224 GrGlyph::PackedID packed,
1225 int vx, int vy, GrColor color, GrFontScaler* scaler,
1226 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001227 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001228 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001229 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001230 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001231 }
1232
1233 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001234 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001235 return;
1236 }
1237
1238 int x = vx + glyph->fBounds.fLeft;
1239 int y = vy + glyph->fBounds.fTop;
1240
1241 // keep them as ints until we've done the clip-test
1242 int width = glyph->fBounds.width();
1243 int height = glyph->fBounds.height();
1244
joshualitt2a0e9f32015-04-13 06:12:21 -07001245#if 0
1246 // Not checking the clip bounds might introduce a performance regression. However, its not
1247 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1248 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1249 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1250 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001251 // check if we clipped out
1252 if (clipRect.quickReject(x, y, x + width, y + height)) {
1253 return;
1254 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001255#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001256
1257 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001258 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001259 this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001260 return;
1261 }
1262
joshualitt1d89e8d2015-04-01 12:40:54 -07001263 GrMaskFormat format = glyph->fMaskFormat;
1264
1265 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1266 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001267 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001268 }
1269
1270 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001271
1272 size_t vertexStride = get_vertex_stride(format);
1273
1274 SkRect r;
1275 r.fLeft = SkIntToScalar(x);
1276 r.fTop = SkIntToScalar(y);
1277 r.fRight = r.fLeft + SkIntToScalar(width);
1278 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001279 subRun->fMaskFormat = format;
1280 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001281 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001282}
joshualitt1d89e8d2015-04-01 12:40:54 -07001283
joshualitt9bd2daf2015-04-17 09:30:06 -07001284bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1285 GrGlyph::PackedID packed,
1286 SkScalar sx, SkScalar sy, GrColor color,
1287 GrFontScaler* scaler,
1288 const SkIRect& clipRect,
1289 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001290 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001291 if (!fCurrStrike) {
1292 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001293 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001294 }
1295
1296 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001297 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001298 return true;
1299 }
1300
1301 // fallback to color glyph support
1302 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1303 return false;
1304 }
1305
1306 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1307 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1308 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1309 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1310
1311 SkScalar scale = textRatio;
1312 dx *= scale;
1313 dy *= scale;
1314 width *= scale;
1315 height *= scale;
1316 sx += dx;
1317 sy += dy;
1318 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1319
1320#if 0
1321 // check if we clipped out
1322 SkRect dstRect;
1323 viewMatrix.mapRect(&dstRect, glyphRect);
1324 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1325 SkScalarTruncToInt(dstRect.top()),
1326 SkScalarTruncToInt(dstRect.right()),
1327 SkScalarTruncToInt(dstRect.bottom()))) {
1328 return true;
1329 }
1330#endif
1331
1332 // TODO combine with the above
1333 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001334 if (glyph->fTooLargeForAtlas) {
joshualitt19e4c022015-05-13 11:23:03 -07001335 this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
joshualitt9bd2daf2015-04-17 09:30:06 -07001336 return true;
1337 }
1338
joshualitt9bd2daf2015-04-17 09:30:06 -07001339 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1340 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1341 subRun->fMaskFormat = kA8_GrMaskFormat;
1342
1343 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1344
1345 bool useColorVerts = !subRun->fUseLCDText;
1346 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001347 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001348 return true;
1349}
1350
1351inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
joshualitt19e4c022015-05-13 11:23:03 -07001352 GrFontScaler* scaler, SkScalar x, SkScalar y) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001353 if (NULL == glyph->fPath) {
1354 SkPath* path = SkNEW(SkPath);
1355 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1356 // flag the glyph as being dead?
1357 SkDELETE(path);
1358 return;
1359 }
1360 glyph->fPath = path;
1361 }
1362 SkASSERT(glyph->fPath);
1363 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1364}
1365
1366inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1367 Run::SubRunInfo* subRun,
1368 const SkRect& positions, GrColor color,
1369 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001370 GrGlyph* glyph) {
1371 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001372 run->fVertexBounds.joinNonEmptyArg(positions);
1373 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001374
1375 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1376
joshualitt9bd2daf2015-04-17 09:30:06 -07001377 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001378 // V0
1379 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1380 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001381 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1382 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001383 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001384
joshualitt010db532015-04-21 10:07:26 -07001385 // V1
1386 position = reinterpret_cast<SkPoint*>(vertex);
1387 position->set(positions.fLeft, positions.fBottom);
1388 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001389 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001390 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001391
joshualitt010db532015-04-21 10:07:26 -07001392 // V2
1393 position = reinterpret_cast<SkPoint*>(vertex);
1394 position->set(positions.fRight, positions.fBottom);
1395 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001396 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001397 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001398
joshualitt010db532015-04-21 10:07:26 -07001399 // V3
1400 position = reinterpret_cast<SkPoint*>(vertex);
1401 position->set(positions.fRight, positions.fTop);
1402 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001403 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001404 } else {
1405 // V0
1406 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1407 position->set(positions.fLeft, positions.fTop);
1408 vertex += vertexStride;
1409
1410 // V1
1411 position = reinterpret_cast<SkPoint*>(vertex);
1412 position->set(positions.fLeft, positions.fBottom);
1413 vertex += vertexStride;
1414
1415 // V2
1416 position = reinterpret_cast<SkPoint*>(vertex);
1417 position->set(positions.fRight, positions.fBottom);
1418 vertex += vertexStride;
1419
1420 // V3
1421 position = reinterpret_cast<SkPoint*>(vertex);
1422 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001423 }
1424
1425 subRun->fGlyphEndIndex++;
1426 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1427}
1428
1429class BitmapTextBatch : public GrBatch {
1430public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001431 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualittdbd35932015-04-02 09:19:04 -07001432 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001433 typedef Blob::Run Run;
1434 typedef Run::SubRunInfo TextInfo;
1435 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001436 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001437 int fRun;
1438 int fSubRun;
1439 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001440 SkScalar fTransX;
1441 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001442 };
1443
joshualittad802c62015-04-15 05:31:57 -07001444 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1445 GrBatchFontCache* fontCache) {
1446 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -07001447 }
1448
joshualitt9bd2daf2015-04-17 09:30:06 -07001449 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1450 GrBatchFontCache* fontCache,
1451 DistanceAdjustTable* distanceAdjustTable,
1452 SkColor filteredColor, bool useLCDText,
robertphillips9fc82752015-06-19 04:46:45 -07001453 bool useBGR) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001454 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
robertphillips9fc82752015-06-19 04:46:45 -07001455 filteredColor, useLCDText, useBGR));
joshualitt9bd2daf2015-04-17 09:30:06 -07001456 }
1457
joshualitt1d89e8d2015-04-01 12:40:54 -07001458 const char* name() const override { return "BitmapTextBatch"; }
1459
1460 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1461 if (kARGB_GrMaskFormat == fMaskFormat) {
1462 out->setUnknownFourComponents();
1463 } else {
1464 out->setKnownFourComponents(fBatch.fColor);
1465 }
1466 }
1467
1468 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt9bd2daf2015-04-17 09:30:06 -07001469 if (!fUseDistanceFields) {
1470 // Bitmap Text
1471 if (kARGB_GrMaskFormat != fMaskFormat) {
1472 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
1473 out->setUnknownSingleComponent();
1474 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
1475 out->setUnknownOpaqueFourComponents();
1476 out->setUsingLCDCoverage();
1477 } else {
1478 out->setUnknownFourComponents();
1479 out->setUsingLCDCoverage();
1480 }
1481 } else {
1482 out->setKnownSingleComponent(0xff);
1483 }
1484 } else {
1485 // Distance fields
1486 if (!fUseLCDText) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001487 out->setUnknownSingleComponent();
joshualitt1d89e8d2015-04-01 12:40:54 -07001488 } else {
1489 out->setUnknownFourComponents();
1490 out->setUsingLCDCoverage();
1491 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001492 }
1493 }
1494
1495 void initBatchTracker(const GrPipelineInfo& init) override {
1496 // Handle any color overrides
1497 if (init.fColorIgnored) {
1498 fBatch.fColor = GrColor_ILLEGAL;
1499 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1500 fBatch.fColor = init.fOverrideColor;
1501 }
1502
1503 // setup batch properties
1504 fBatch.fColorIgnored = init.fColorIgnored;
1505 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1506 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1507 }
1508
bsalomonb5238a72015-05-05 07:49:49 -07001509 struct FlushInfo {
1510 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
1511 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
1512 int fGlyphsToFlush;
1513 int fVertexOffset;
1514 };
1515
joshualitt1d89e8d2015-04-01 12:40:54 -07001516 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1517 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1518 // TODO actually only invert if we don't have RGBA
1519 SkMatrix localMatrix;
1520 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1521 SkDebugf("Cannot invert viewmatrix\n");
1522 return;
1523 }
1524
joshualitt62db8ba2015-04-09 08:22:37 -07001525 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
1526 if (!texture) {
1527 SkDebugf("Could not allocate backing texture for atlas\n");
1528 return;
1529 }
1530
joshualitt9bd2daf2015-04-17 09:30:06 -07001531 SkAutoTUnref<const GrGeometryProcessor> gp;
1532 if (fUseDistanceFields) {
1533 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1534 texture));
1535 } else {
1536 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt9bd2daf2015-04-17 09:30:06 -07001537 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1538 texture,
1539 params,
1540 fMaskFormat,
joshualittb8c241a2015-05-19 08:23:30 -07001541 localMatrix,
1542 this->usesLocalCoords()));
joshualitt9bd2daf2015-04-17 09:30:06 -07001543 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001544
bsalomonb5238a72015-05-05 07:49:49 -07001545 FlushInfo flushInfo;
1546 flushInfo.fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001547 size_t vertexStride = gp->getVertexStride();
joshualitt9bd2daf2015-04-17 09:30:06 -07001548 SkASSERT(vertexStride == (fUseDistanceFields ?
1549 get_vertex_stride_df(fMaskFormat, fUseLCDText) :
1550 get_vertex_stride(fMaskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001551
joshualittb8c241a2015-05-19 08:23:30 -07001552 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001553
1554 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001555 int instanceCount = fInstanceCount;
bsalomon8415abe2015-05-04 11:41:41 -07001556 const GrVertexBuffer* vertexBuffer;
bsalomonb5238a72015-05-05 07:49:49 -07001557
robertphillipse40d3972015-05-07 09:51:43 -07001558 void* vertices = batchTarget->makeVertSpace(vertexStride,
1559 glyphCount * kVerticesPerGlyph,
1560 &vertexBuffer,
1561 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -07001562 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
1563 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
1564 if (!vertices || !flushInfo.fVertexBuffer) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001565 SkDebugf("Could not allocate vertices\n");
1566 return;
1567 }
1568
1569 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1570
joshualitt25ba7ea2015-04-21 07:49:49 -07001571 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1572 // in a row
1573 const SkDescriptor* desc = NULL;
1574 SkGlyphCache* cache = NULL;
1575 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001576 SkTypeface* typeface = NULL;
1577
joshualitt1d89e8d2015-04-01 12:40:54 -07001578 for (int i = 0; i < instanceCount; i++) {
1579 Geometry& args = fGeoData[i];
1580 Blob* blob = args.fBlob;
1581 Run& run = blob->fRuns[args.fRun];
1582 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1583
1584 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
joshualitt7a9c45c2015-05-26 12:32:23 -07001585 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
1586 run.fStrike->isAbandoned();
joshualitt9bd2daf2015-04-17 09:30:06 -07001587 bool regenerateColors;
1588 if (fUseDistanceFields) {
joshualittfcfb9fc2015-04-21 07:35:10 -07001589 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001590 } else {
1591 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
1592 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001593 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001594 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1595
1596 // We regenerate both texture coords and colors in the blob itself, and update the
1597 // atlas generation. If we don't end up purging any unused plots, we can avoid
1598 // regenerating the coords. We could take a finer grained approach to updating texture
1599 // coords but its not clear if the extra bookkeeping would offset any gains.
1600 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1601 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1602 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1603 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001604 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001605 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001606 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001607
1608 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1609 // generating its texture coords, we have to track whether or not the strike has
1610 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1611 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1612 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1613 // new strike, we instead keep our ref to the old strike and use the packed ids from
1614 // it. These ids will still be valid as long as we hold the ref. When we are done
1615 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1616 bool regenerateGlyphs = false;
1617 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001618 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001619 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001620
1621 // We can reuse if we have a valid strike and our descriptors / typeface are the
1622 // same
joshualitt97202d22015-04-22 13:47:02 -07001623 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1624 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001625 run.fDescriptor.getDesc();
1626 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1627 !(desc->equals(*newDesc))) {
1628 if (cache) {
1629 SkGlyphCache::AttachCache(cache);
1630 }
1631 desc = newDesc;
1632 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1633 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001634 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001635 typeface = run.fTypeface;
1636 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001637
joshualittae32c102015-04-21 09:37:57 -07001638 if (run.fStrike->isAbandoned()) {
1639 regenerateGlyphs = true;
1640 strike = fFontCache->getStrike(scaler);
1641 } else {
1642 strike = run.fStrike;
1643 }
1644 }
1645
1646 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001647 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001648 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1649 GrGlyph* glyph;
1650 if (regenerateGlyphs) {
1651 // Get the id from the old glyph, and use the new strike to lookup
1652 // the glyph.
1653 glyph = blob->fGlyphs[glyphOffset];
1654 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1655 scaler);
1656 }
1657 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001658 SkASSERT(glyph);
1659
1660 if (!fFontCache->hasGlyph(glyph) &&
1661 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
bsalomonb5238a72015-05-05 07:49:49 -07001662 this->flush(batchTarget, &flushInfo);
joshualittb8c241a2015-05-19 08:23:30 -07001663 batchTarget->initDraw(gp, pipeline);
joshualitt1d89e8d2015-04-01 12:40:54 -07001664 brokenRun = glyphIdx > 0;
1665
joshualittae32c102015-04-21 09:37:57 -07001666 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1667 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001668 scaler);
1669 SkASSERT(success);
1670 }
joshualittb4c507e2015-04-08 08:07:59 -07001671 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1672 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001673
1674 // Texture coords are the last vertex attribute so we get a pointer to the
1675 // first one and then map with stride in regenerateTextureCoords
1676 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1677 vertex += info.fVertexStartIndex;
1678 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1679 vertex += vertexStride - sizeof(SkIPoint16);
1680
1681 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1682 }
1683
1684 if (regenerateColors) {
1685 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1686 vertex += info.fVertexStartIndex;
1687 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1688 this->regenerateColors(vertex, vertexStride, args.fColor);
1689 }
1690
joshualitt2a0e9f32015-04-13 06:12:21 -07001691 if (regeneratePositions) {
1692 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1693 vertex += info.fVertexStartIndex;
1694 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1695 SkScalar transX = args.fTransX;
1696 SkScalar transY = args.fTransY;
1697 this->regeneratePositions(vertex, vertexStride, transX, transY);
1698 }
bsalomonb5238a72015-05-05 07:49:49 -07001699 flushInfo.fGlyphsToFlush++;
joshualitt1d89e8d2015-04-01 12:40:54 -07001700 }
1701
joshualitt2a0e9f32015-04-13 06:12:21 -07001702 // We my have changed the color so update it here
1703 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001704 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001705 if (regenerateGlyphs) {
1706 run.fStrike.reset(SkRef(strike));
1707 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001708 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
1709 fFontCache->atlasGeneration(fMaskFormat);
1710 }
1711 } else {
bsalomonb5238a72015-05-05 07:49:49 -07001712 flushInfo.fGlyphsToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001713
1714 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1715 // have a valid atlas generation
1716 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1717 batchTarget->currentToken(),
1718 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001719 }
1720
1721 // now copy all vertices
1722 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1723 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1724
1725 currVertex += byteCount;
1726 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001727 // Make sure to attach the last cache if applicable
1728 if (cache) {
1729 SkGlyphCache::AttachCache(cache);
1730 }
bsalomonb5238a72015-05-05 07:49:49 -07001731 this->flush(batchTarget, &flushInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001732 }
1733
joshualittad802c62015-04-15 05:31:57 -07001734 // The minimum number of Geometry we will try to allocate.
1735 static const int kMinAllocated = 32;
1736
1737 // Total number of Geometry this Batch owns
1738 int instanceCount() const { return fInstanceCount; }
1739 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1740
1741 // to avoid even the initial copy of the struct, we have a getter for the first item which
1742 // is used to seed the batch with its initial geometry. After seeding, the client should call
1743 // init() so the Batch can initialize itself
1744 Geometry& geometry() { return fGeoData[0]; }
1745 void init() {
joshualitt444987f2015-05-06 06:46:01 -07001746 const Geometry& geo = fGeoData[0];
1747 fBatch.fColor = geo.fColor;
1748 fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
1749
1750 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
1751 // into device space
1752 const Run& run = geo.fBlob->fRuns[geo.fRun];
1753 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
1754 SkRect bounds = run.fVertexBounds;
1755 fBatch.fViewMatrix.mapRect(&bounds);
1756 this->setBounds(bounds);
1757 } else {
1758 this->setBounds(run.fVertexBounds);
1759 }
joshualittad802c62015-04-15 05:31:57 -07001760 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001761
1762private:
joshualitt9bd2daf2015-04-17 09:30:06 -07001763 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
joshualitt1d89e8d2015-04-01 12:40:54 -07001764 : fMaskFormat(maskFormat)
1765 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
joshualitt9bd2daf2015-04-17 09:30:06 -07001766 , fFontCache(fontCache)
1767 , fUseDistanceFields(false) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001768 this->initClassID<BitmapTextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001769 fBatch.fNumGlyphs = glyphCount;
joshualittad802c62015-04-15 05:31:57 -07001770 fInstanceCount = 1;
1771 fAllocatedCount = kMinAllocated;
1772 }
1773
joshualitt9bd2daf2015-04-17 09:30:06 -07001774 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
1775 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
robertphillips9fc82752015-06-19 04:46:45 -07001776 bool useLCDText, bool useBGR)
joshualitt9bd2daf2015-04-17 09:30:06 -07001777 : fMaskFormat(maskFormat)
1778 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
1779 , fFontCache(fontCache)
1780 , fDistanceAdjustTable(SkRef(distanceAdjustTable))
1781 , fFilteredColor(filteredColor)
1782 , fUseDistanceFields(true)
1783 , fUseLCDText(useLCDText)
robertphillips9fc82752015-06-19 04:46:45 -07001784 , fUseBGR(useBGR) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001785 this->initClassID<BitmapTextBatch>();
1786 fBatch.fNumGlyphs = glyphCount;
1787 fInstanceCount = 1;
1788 fAllocatedCount = kMinAllocated;
1789 SkASSERT(fMaskFormat == kA8_GrMaskFormat);
1790 }
1791
joshualittad802c62015-04-15 05:31:57 -07001792 ~BitmapTextBatch() {
1793 for (int i = 0; i < fInstanceCount; i++) {
1794 fGeoData[i].fBlob->unref();
1795 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001796 }
1797
1798 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1799 int width = glyph->fBounds.width();
1800 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001801
joshualitt9bd2daf2015-04-17 09:30:06 -07001802 int u0, v0, u1, v1;
1803 if (fUseDistanceFields) {
1804 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1805 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1806 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1807 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1808 } else {
1809 u0 = glyph->fAtlasLocation.fX;
1810 v0 = glyph->fAtlasLocation.fY;
1811 u1 = u0 + width;
1812 v1 = v0 + height;
1813 }
1814
joshualitt1d89e8d2015-04-01 12:40:54 -07001815 SkIPoint16* textureCoords;
1816 // V0
1817 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1818 textureCoords->set(u0, v0);
1819 vertex += vertexStride;
1820
1821 // V1
1822 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1823 textureCoords->set(u0, v1);
1824 vertex += vertexStride;
1825
1826 // V2
1827 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1828 textureCoords->set(u1, v1);
1829 vertex += vertexStride;
1830
1831 // V3
1832 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1833 textureCoords->set(u1, v0);
1834 }
1835
1836 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1837 for (int i = 0; i < kVerticesPerGlyph; i++) {
1838 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1839 *vcolor = color;
1840 vertex += vertexStride;
1841 }
1842 }
1843
joshualitt2a0e9f32015-04-13 06:12:21 -07001844 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1845 SkScalar transY) {
1846 for (int i = 0; i < kVerticesPerGlyph; i++) {
1847 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1848 point->fX += transX;
1849 point->fY += transY;
1850 vertex += vertexStride;
1851 }
1852 }
1853
bsalomonb5238a72015-05-05 07:49:49 -07001854 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -07001855 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -07001856 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -07001857 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001858 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
bsalomone64eb572015-05-07 11:35:55 -07001859 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
bsalomonb5238a72015-05-05 07:49:49 -07001860 maxGlyphsPerDraw);
bsalomone64eb572015-05-07 11:35:55 -07001861 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -07001862 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
1863 flushInfo->fGlyphsToFlush = 0;
joshualitt1d89e8d2015-04-01 12:40:54 -07001864 }
1865
1866 GrColor color() const { return fBatch.fColor; }
1867 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1868 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1869 int numGlyphs() const { return fBatch.fNumGlyphs; }
1870
1871 bool onCombineIfPossible(GrBatch* t) override {
1872 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1873
joshualitt9bd2daf2015-04-17 09:30:06 -07001874 if (fUseDistanceFields != that->fUseDistanceFields) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001875 return false;
1876 }
1877
joshualitt9bd2daf2015-04-17 09:30:06 -07001878 if (!fUseDistanceFields) {
1879 // Bitmap Text
1880 if (fMaskFormat != that->fMaskFormat) {
1881 return false;
1882 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001883
joshualitt9bd2daf2015-04-17 09:30:06 -07001884 // TODO we can often batch across LCD text if we have dual source blending and don't
1885 // have to use the blend constant
1886 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1887 return false;
1888 }
1889
1890 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1891 return false;
1892 }
1893 } else {
1894 // Distance Fields
1895 SkASSERT(this->fMaskFormat == that->fMaskFormat &&
1896 this->fMaskFormat == kA8_GrMaskFormat);
1897
1898 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1899 return false;
1900 }
1901
1902 if (fFilteredColor != that->fFilteredColor) {
1903 return false;
1904 }
1905
1906 if (fUseLCDText != that->fUseLCDText) {
1907 return false;
1908 }
1909
1910 if (fUseBGR != that->fUseBGR) {
1911 return false;
1912 }
1913
joshualitt9bd2daf2015-04-17 09:30:06 -07001914 // TODO see note above
1915 if (fUseLCDText && this->color() != that->color()) {
1916 return false;
1917 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001918 }
1919
1920 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001921
1922 // copy that->geoData(). We do this manually for performance reasons
1923 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1924 int otherInstanceCount = that->instanceCount();
1925 int allocSize = otherInstanceCount + fInstanceCount;
1926 if (allocSize > fAllocatedCount) {
1927 while (allocSize > fAllocatedCount) {
1928 fAllocatedCount = fAllocatedCount << 1;
1929 }
1930 fGeoData.realloc(fAllocatedCount);
1931 }
1932
1933 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1934 otherInstanceCount * sizeof(Geometry));
1935 int total = fInstanceCount + otherInstanceCount;
1936 for (int i = fInstanceCount; i < total; i++) {
1937 fGeoData[i].fBlob->ref();
1938 }
1939 fInstanceCount = total;
joshualitt99c7c072015-05-01 13:43:30 -07001940
1941 this->joinBounds(that->bounds());
joshualitt1d89e8d2015-04-01 12:40:54 -07001942 return true;
1943 }
1944
joshualitt9bd2daf2015-04-17 09:30:06 -07001945 // TODO just use class params
1946 // TODO trying to figure out why lcd is so whack
1947 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1948 GrColor color, GrTexture* texture) {
1949 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
1950
1951 // set up any flags
1952 uint32_t flags = 0;
1953 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1954 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
1955 flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
1956 kRectToRect_DistanceFieldEffectFlag : 0;
1957 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1958
1959 // see if we need to create a new effect
1960 if (fUseLCDText) {
1961 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1962
1963 float redCorrection =
1964 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1965 float greenCorrection =
1966 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1967 float blueCorrection =
1968 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1969 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1970 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1971 greenCorrection,
1972 blueCorrection);
1973
1974 return GrDistanceFieldLCDTextGeoProc::Create(color,
1975 viewMatrix,
1976 texture,
1977 params,
1978 widthAdjust,
joshualittb8c241a2015-05-19 08:23:30 -07001979 flags,
1980 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001981 } else {
1982 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt9bd2daf2015-04-17 09:30:06 -07001983#ifdef SK_GAMMA_APPLY_TO_A8
robertphillips9fc82752015-06-19 04:46:45 -07001984 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
joshualitt9bd2daf2015-04-17 09:30:06 -07001985 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
1986 return GrDistanceFieldA8TextGeoProc::Create(color,
1987 viewMatrix,
1988 texture,
1989 params,
1990 correction,
joshualittb8c241a2015-05-19 08:23:30 -07001991 flags,
1992 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07001993#else
1994 return GrDistanceFieldA8TextGeoProc::Create(color,
1995 viewMatrix,
1996 texture,
1997 params,
joshualittb8c241a2015-05-19 08:23:30 -07001998 flags,
1999 this->usesLocalCoords());
joshualitt9bd2daf2015-04-17 09:30:06 -07002000#endif
2001 }
2002
2003 }
2004
joshualitt1d89e8d2015-04-01 12:40:54 -07002005 struct BatchTracker {
2006 GrColor fColor;
2007 SkMatrix fViewMatrix;
2008 bool fUsesLocalCoords;
2009 bool fColorIgnored;
2010 bool fCoverageIgnored;
2011 int fNumGlyphs;
2012 };
2013
2014 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002015 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2016 int fInstanceCount;
2017 int fAllocatedCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07002018 GrMaskFormat fMaskFormat;
2019 GrPixelConfig fPixelConfig;
2020 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002021
2022 // Distance field properties
robertphillips9fc82752015-06-19 04:46:45 -07002023 SkAutoTUnref<const DistanceAdjustTable> fDistanceAdjustTable;
joshualitt9bd2daf2015-04-17 09:30:06 -07002024 SkColor fFilteredColor;
2025 bool fUseDistanceFields;
2026 bool fUseLCDText;
2027 bool fUseBGR;
joshualitt1d89e8d2015-04-01 12:40:54 -07002028};
2029
robertphillips2334fb62015-06-17 05:43:33 -07002030void GrAtlasTextContext::flushRunAsPaths(GrRenderTarget* rt, const SkTextBlob::RunIterator& it,
robertphillipsccb1b572015-05-27 11:02:55 -07002031 const GrClip& clip, const SkPaint& skPaint,
joshualitt9a27e632015-04-06 10:53:36 -07002032 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2033 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2034 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002035
joshualitt9a27e632015-04-06 10:53:36 -07002036 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2037 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002038
joshualitt9a27e632015-04-06 10:53:36 -07002039 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002040
joshualitt9a27e632015-04-06 10:53:36 -07002041 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2042 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002043 }
2044
robertphillips9c240a12015-05-28 07:45:59 -07002045 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
joshualitt9a27e632015-04-06 10:53:36 -07002046
2047 switch (it.positioning()) {
2048 case SkTextBlob::kDefault_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002049 this->drawTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002050 (const char *)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002051 textLen, x + offset.x(), y + offset.y(), clipBounds);
2052 break;
2053 case SkTextBlob::kHorizontal_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002054 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002055 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002056 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2057 clipBounds);
2058 break;
2059 case SkTextBlob::kFull_Positioning:
robertphillips2334fb62015-06-17 05:43:33 -07002060 this->drawPosTextAsPath(rt, clip, runPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -07002061 (const char*)it.glyphs(),
joshualitt9a27e632015-04-06 10:53:36 -07002062 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2063 break;
2064 }
2065}
2066
joshualitt79dfb2b2015-05-11 08:58:08 -07002067
2068inline BitmapTextBatch*
2069GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
2070 int glyphCount, int run, int subRun,
2071 GrColor color, SkScalar transX, SkScalar transY,
2072 const SkPaint& skPaint) {
2073 GrMaskFormat format = info.fMaskFormat;
2074 GrColor subRunColor;
2075 if (kARGB_GrMaskFormat == format) {
2076 uint8_t paintAlpha = skPaint.getAlpha();
2077 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2078 } else {
2079 subRunColor = color;
2080 }
2081
2082 BitmapTextBatch* batch;
2083 if (info.fDrawAsDistanceFields) {
2084 SkColor filteredColor;
2085 SkColorFilter* colorFilter = skPaint.getColorFilter();
2086 if (colorFilter) {
2087 filteredColor = colorFilter->filterColor(skPaint.getColor());
2088 } else {
2089 filteredColor = skPaint.getColor();
2090 }
2091 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
joshualitt79dfb2b2015-05-11 08:58:08 -07002092 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
2093 fDistanceAdjustTable, filteredColor,
robertphillips9fc82752015-06-19 04:46:45 -07002094 info.fUseLCDText, useBGR);
joshualitt79dfb2b2015-05-11 08:58:08 -07002095 } else {
2096 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
2097 }
2098 BitmapTextBatch::Geometry& geometry = batch->geometry();
2099 geometry.fBlob = SkRef(cacheBlob);
2100 geometry.fRun = run;
2101 geometry.fSubRun = subRun;
2102 geometry.fColor = subRunColor;
2103 geometry.fTransX = transX;
2104 geometry.fTransY = transY;
2105 batch->init();
2106
2107 return batch;
2108}
2109
robertphillips2334fb62015-06-17 05:43:33 -07002110inline void GrAtlasTextContext::flushRun(GrPipelineBuilder* pipelineBuilder,
joshualitt9a27e632015-04-06 10:53:36 -07002111 BitmapTextBlob* cacheBlob, int run, GrColor color,
robertphillipsea461502015-05-26 11:38:03 -07002112 SkScalar transX, SkScalar transY,
2113 const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002114 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2115 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2116 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2117 if (0 == glyphCount) {
2118 continue;
2119 }
2120
joshualitt79dfb2b2015-05-11 08:58:08 -07002121 SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
2122 subRun, color, transX, transY,
2123 skPaint));
robertphillips2334fb62015-06-17 05:43:33 -07002124 fDrawContext->drawBatch(pipelineBuilder, batch);
joshualitt9a27e632015-04-06 10:53:36 -07002125 }
2126}
2127
robertphillips2334fb62015-06-17 05:43:33 -07002128inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -07002129 const GrClip& clip, const SkPaint& skPaint,
joshualitt1107e902015-05-11 14:52:11 -07002130 SkScalar transX, SkScalar transY,
2131 const SkIRect& clipBounds) {
joshualittfc072562015-05-13 12:15:06 -07002132 if (!cacheBlob->fBigGlyphs.count()) {
2133 return;
2134 }
2135
2136 SkMatrix pathMatrix;
2137 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
2138 SkDebugf("could not invert viewmatrix\n");
2139 return;
2140 }
2141
joshualitt9a27e632015-04-06 10:53:36 -07002142 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07002143 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
joshualitt19e4c022015-05-13 11:23:03 -07002144 bigGlyph.fVx += transX;
2145 bigGlyph.fVy += transY;
joshualittfc072562015-05-13 12:15:06 -07002146 SkMatrix translate = cacheBlob->fViewMatrix;
2147 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
2148
robertphillips2334fb62015-06-17 05:43:33 -07002149 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, bigGlyph.fPath,
robertphillipsccb1b572015-05-27 11:02:55 -07002150 skPaint, translate, &pathMatrix, clipBounds, false);
joshualitt1d89e8d2015-04-01 12:40:54 -07002151 }
2152}
joshualitt9a27e632015-04-06 10:53:36 -07002153
robertphillips2334fb62015-06-17 05:43:33 -07002154void GrAtlasTextContext::flush(const SkTextBlob* blob,
joshualitt9a27e632015-04-06 10:53:36 -07002155 BitmapTextBlob* cacheBlob,
2156 GrRenderTarget* rt,
2157 const SkPaint& skPaint,
2158 const GrPaint& grPaint,
2159 SkDrawFilter* drawFilter,
2160 const GrClip& clip,
2161 const SkMatrix& viewMatrix,
2162 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002163 SkScalar x, SkScalar y,
2164 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002165 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2166 // it as paths
2167 GrPipelineBuilder pipelineBuilder;
2168 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2169
2170 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002171
2172 SkTextBlob::RunIterator it(blob);
2173 for (int run = 0; !it.done(); it.next(), run++) {
2174 if (cacheBlob->fRuns[run].fDrawAsPaths) {
robertphillips2334fb62015-06-17 05:43:33 -07002175 this->flushRunAsPaths(rt, it, clip, skPaint,
robertphillipsccb1b572015-05-27 11:02:55 -07002176 drawFilter, viewMatrix, clipBounds, x, y);
joshualitt9a27e632015-04-06 10:53:36 -07002177 continue;
2178 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002179 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
robertphillips2334fb62015-06-17 05:43:33 -07002180 this->flushRun(&pipelineBuilder, cacheBlob, run, color,
robertphillipsea461502015-05-26 11:38:03 -07002181 transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002182 }
2183
2184 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002185 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, transX, transY, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002186}
2187
robertphillips2334fb62015-06-17 05:43:33 -07002188void GrAtlasTextContext::flush(BitmapTextBlob* cacheBlob,
joshualitt9a27e632015-04-06 10:53:36 -07002189 GrRenderTarget* rt,
2190 const SkPaint& skPaint,
2191 const GrPaint& grPaint,
joshualitt1107e902015-05-11 14:52:11 -07002192 const GrClip& clip,
2193 const SkIRect& clipBounds) {
joshualitt9a27e632015-04-06 10:53:36 -07002194 GrPipelineBuilder pipelineBuilder;
2195 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2196
2197 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002198 for (int run = 0; run < cacheBlob->fRunCount; run++) {
robertphillips2334fb62015-06-17 05:43:33 -07002199 this->flushRun(&pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002200 }
2201
2202 // Now flush big glyphs
robertphillips2334fb62015-06-17 05:43:33 -07002203 this->flushBigGlyphs(cacheBlob, rt, clip, skPaint, 0, 0, clipBounds);
joshualitt9a27e632015-04-06 10:53:36 -07002204}
joshualitt79dfb2b2015-05-11 08:58:08 -07002205
2206///////////////////////////////////////////////////////////////////////////////////////////////////
2207
2208#ifdef GR_TEST_UTILS
2209
joshualitt6c891102015-05-13 08:51:49 -07002210BATCH_TEST_DEFINE(TextBlobBatch) {
joshualitt79dfb2b2015-05-11 08:58:08 -07002211 static uint32_t gContextID = SK_InvalidGenID;
2212 static GrAtlasTextContext* gTextContext = NULL;
robertphillips2334fb62015-06-17 05:43:33 -07002213 static SkDeviceProperties gDevProperties;
joshualitt79dfb2b2015-05-11 08:58:08 -07002214
2215 if (context->uniqueID() != gContextID) {
2216 gContextID = context->uniqueID();
2217 SkDELETE(gTextContext);
robertphillips2334fb62015-06-17 05:43:33 -07002218
joshualitt79dfb2b2015-05-11 08:58:08 -07002219 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly
2220 // because we don't really want to have a gpu device here.
2221 // We enable distance fields by twiddling a knob on the paint
robertphillipsbcd7ab52015-06-18 05:27:18 -07002222 GrDrawContext* drawContext = context->drawContext(&gDevProperties);
robertphillips2334fb62015-06-17 05:43:33 -07002223
robertphillipsbcd7ab52015-06-18 05:27:18 -07002224 gTextContext = GrAtlasTextContext::Create(context, drawContext, gDevProperties);
joshualitt79dfb2b2015-05-11 08:58:08 -07002225 }
2226
2227 // create dummy render target
2228 GrSurfaceDesc desc;
2229 desc.fFlags = kRenderTarget_GrSurfaceFlag;
2230 desc.fWidth = 1024;
2231 desc.fHeight = 1024;
2232 desc.fConfig = kRGBA_8888_GrPixelConfig;
joshualitt15732062015-05-13 12:15:14 -07002233 desc.fSampleCnt = 0;
joshualitt79dfb2b2015-05-11 08:58:08 -07002234 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
2235 SkASSERT(texture);
2236 SkASSERT(NULL != texture->asRenderTarget());
2237 GrRenderTarget* rt = texture->asRenderTarget();
2238
2239 // Setup dummy SkPaint / GrPaint
2240 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002241 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt79dfb2b2015-05-11 08:58:08 -07002242 SkPaint skPaint;
2243 skPaint.setDistanceFieldTextTEMP(random->nextBool());
2244 skPaint.setColor(color);
2245 skPaint.setLCDRenderText(random->nextBool());
2246 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
2247 skPaint.setSubpixelText(random->nextBool());
2248
2249 GrPaint grPaint;
2250 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
2251 SkFAIL("couldn't convert paint\n");
2252 }
2253
2254 const char* text = "The quick brown fox jumps over the lazy dog.";
2255 int textLen = (int)strlen(text);
2256
2257 // Setup clip
2258 GrClip clip;
2259 SkIRect noClip = SkIRect::MakeLargest();
2260
2261 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only
2262 // intend to test the batch with this unit test, that is okay.
2263 SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
2264 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
2265 static_cast<size_t>(textLen), 0, 0, noClip));
2266
2267 SkScalar transX = static_cast<SkScalar>(random->nextU());
2268 SkScalar transY = static_cast<SkScalar>(random->nextU());
2269 const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
2270 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
2271}
2272
2273#endif