blob: dee3be7635a4d0acb69ea504317c113572c15266 [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"
12#include "GrDefaultGeoProcFactory.h"
13#include "GrDrawTarget.h"
14#include "GrFontScaler.h"
15#include "GrIndexBuffer.h"
16#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070017#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070018#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070019#include "GrVertexBuffer.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070020
21#include "SkAutoKern.h"
22#include "SkColorPriv.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070023#include "SkColorFilter.h"
24#include "SkDistanceFieldGen.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070025#include "SkDraw.h"
26#include "SkDrawFilter.h"
27#include "SkDrawProcs.h"
28#include "SkGlyphCache.h"
29#include "SkGpuDevice.h"
30#include "SkGr.h"
31#include "SkPath.h"
32#include "SkRTConf.h"
33#include "SkStrokeRec.h"
34#include "SkTextBlob.h"
35#include "SkTextMapStateProc.h"
36
37#include "effects/GrBitmapTextGeoProc.h"
joshualitt9bd2daf2015-04-17 09:30:06 -070038#include "effects/GrDistanceFieldGeoProc.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070039
40namespace {
41static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
42
43// position + local coord
44static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
45
46static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
47
joshualitt9bd2daf2015-04-17 09:30:06 -070048static const int kMinDFFontSize = 18;
49static const int kSmallDFFontSize = 32;
50static const int kSmallDFFontLimit = 32;
51static const int kMediumDFFontSize = 72;
52static const int kMediumDFFontLimit = 72;
53static const int kLargeDFFontSize = 162;
joshualitta7c63892015-04-21 13:24:37 -070054static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
joshualitt9bd2daf2015-04-17 09:30:06 -070055
56SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
57static const int kDistanceAdjustLumShift = 5;
58
joshualitt1d89e8d2015-04-01 12:40:54 -070059static const int kVerticesPerGlyph = 4;
60static const int kIndicesPerGlyph = 6;
61
62static size_t get_vertex_stride(GrMaskFormat maskFormat) {
63 switch (maskFormat) {
64 case kA8_GrMaskFormat:
65 return kGrayTextVASize;
66 case kARGB_GrMaskFormat:
67 return kColorTextVASize;
68 default:
69 return kLCDTextVASize;
70 }
71}
72
joshualitt9bd2daf2015-04-17 09:30:06 -070073static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
74 SkASSERT(maskFormat == kA8_GrMaskFormat);
75 if (useLCDText) {
76 return kLCDTextVASize;
77 } else {
78 return kGrayTextVASize;
79 }
80}
81
82static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
83 unsigned r = SkColorGetR(c);
84 unsigned g = SkColorGetG(c);
85 unsigned b = SkColorGetB(c);
86 return GrColorPackRGBA(r, g, b, 0xff);
87}
88
joshualitt1d89e8d2015-04-01 12:40:54 -070089};
90
91// TODO
joshualitt9bd2daf2015-04-17 09:30:06 -070092// Distance field text in textblobs
joshualitt1d89e8d2015-04-01 12:40:54 -070093
joshualittdbd35932015-04-02 09:19:04 -070094GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
95 SkGpuDevice* gpuDevice,
joshualitt9bd2daf2015-04-17 09:30:06 -070096 const SkDeviceProperties& properties,
97 bool enableDistanceFields)
98 : INHERITED(context, gpuDevice, properties)
99 , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
joshualittb7133be2015-04-08 09:08:31 -0700100 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
101 // vertexStride
102 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
103 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -0700104 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -0700105 fCache = context->getTextBlobCache();
joshualitt9bd2daf2015-04-17 09:30:06 -0700106
107#if SK_FORCE_DISTANCE_FIELD_TEXT
108 fEnableDFRendering = true;
109#else
110 fEnableDFRendering = enableDistanceFields;
111#endif
112}
113
114void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
115
116 // This is used for an approximation of the mask gamma hack, used by raster and bitmap
117 // text. The mask gamma hack is based off of guessing what the blend color is going to
118 // be, and adjusting the mask so that when run through the linear blend will
119 // produce the value closest to the desired result. However, in practice this means
120 // that the 'adjusted' mask is just increasing or decreasing the coverage of
121 // the mask depending on what it is thought it will blit against. For black (on
122 // assumed white) this means that coverages are decreased (on a curve). For white (on
123 // assumed black) this means that coverages are increased (on a a curve). At
124 // middle (perceptual) gray (which could be blit against anything) the coverages
125 // remain the same.
126 //
127 // The idea here is that instead of determining the initial (real) coverage and
128 // then adjusting that coverage, we determine an adjusted coverage directly by
129 // essentially manipulating the geometry (in this case, the distance to the glyph
130 // edge). So for black (on assumed white) this thins a bit; for white (on
131 // assumed black) this fake bolds the geometry a bit.
132 //
133 // The distance adjustment is calculated by determining the actual coverage value which
134 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
135 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
136 // actual edge. So by subtracting this distance adjustment and computing without the
137 // the coverage adjustment we should get 0.5 coverage at the same point.
138 //
139 // This has several implications:
140 // For non-gray lcd smoothed text, each subpixel essentially is using a
141 // slightly different geometry.
142 //
143 // For black (on assumed white) this may not cover some pixels which were
144 // previously covered; however those pixels would have been only slightly
145 // covered and that slight coverage would have been decreased anyway. Also, some pixels
146 // which were previously fully covered may no longer be fully covered.
147 //
148 // For white (on assumed black) this may cover some pixels which weren't
149 // previously covered at all.
150
151 int width, height;
152 size_t size;
153
154#ifdef SK_GAMMA_CONTRAST
155 SkScalar contrast = SK_GAMMA_CONTRAST;
156#else
157 SkScalar contrast = 0.5f;
158#endif
159 SkScalar paintGamma = gamma;
160 SkScalar deviceGamma = gamma;
161
162 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
163 &width, &height);
164
165 SkASSERT(kExpectedDistanceAdjustTableSize == height);
166 fTable = SkNEW_ARRAY(SkScalar, height);
167
168 SkAutoTArray<uint8_t> data((int)size);
169 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
170
171 // find the inverse points where we cross 0.5
172 // binsearch might be better, but we only need to do this once on creation
173 for (int row = 0; row < height; ++row) {
174 uint8_t* rowPtr = data.get() + row*width;
175 for (int col = 0; col < width - 1; ++col) {
176 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
177 // compute point where a mask value will give us a result of 0.5
178 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
179 float borderAlpha = (col + interp) / 255.f;
180
181 // compute t value for that alpha
182 // this is an approximate inverse for smoothstep()
183 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
184
185 // compute distance which gives us that t value
186 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
187 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
188
189 fTable[row] = d;
190 break;
191 }
192 }
193 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700194}
195
joshualittdbd35932015-04-02 09:19:04 -0700196GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
197 SkGpuDevice* gpuDevice,
joshualitt9bd2daf2015-04-17 09:30:06 -0700198 const SkDeviceProperties& props,
199 bool enableDistanceFields) {
200 return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
joshualitt1d89e8d2015-04-01 12:40:54 -0700201}
202
joshualittdbd35932015-04-02 09:19:04 -0700203bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
204 const GrClip&,
205 const GrPaint&,
206 const SkPaint& skPaint,
207 const SkMatrix& viewMatrix) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700208 return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
209 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700210}
211
joshualitt9e36c1a2015-04-14 12:17:27 -0700212GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
213 GrColor canonicalColor = paint.computeLuminanceColor();
214 if (lcd) {
215 // This is the correct computation, but there are tons of cases where LCD can be overridden.
216 // For now we just regenerate if any run in a textblob has LCD.
217 // TODO figure out where all of these overrides are and see if we can incorporate that logic
218 // at a higher level *OR* use sRGB
219 SkASSERT(false);
220 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
221 } else {
222 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
223 // gamma corrected masks anyways, nor color
224 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
225 SkColorGetG(canonicalColor),
226 SkColorGetB(canonicalColor));
227 // reduce to our finite number of bits
228 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
229 }
230 return canonicalColor;
231}
232
233// TODO if this function ever shows up in profiling, then we can compute this value when the
234// textblob is being built and cache it. However, for the time being textblobs mostly only have 1
235// run so this is not a big deal to compute here.
236bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
237 SkTextBlob::RunIterator it(blob);
238 for (; !it.done(); it.next()) {
239 if (it.isLCD()) {
240 return true;
241 }
242 }
243 return false;
244}
245
joshualitt2a0e9f32015-04-13 06:12:21 -0700246bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
247 const BitmapTextBlob& blob, const SkPaint& paint,
joshualitt53b5f442015-04-13 06:33:59 -0700248 const SkMaskFilter::BlurRec& blurRec,
joshualittdbd35932015-04-02 09:19:04 -0700249 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700250 // If we have LCD text then our canonical color will be set to transparent, in this case we have
251 // to regenerate the blob on any color change
252 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700253 return true;
254 }
255
256 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
257 return true;
258 }
259
260 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
261 return true;
262 }
263
joshualitt53b5f442015-04-13 06:33:59 -0700264 // We only cache one masked version
265 if (blob.fKey.fHasBlur &&
266 (blob.fBlurRec.fSigma != blurRec.fSigma ||
267 blob.fBlurRec.fStyle != blurRec.fStyle ||
268 blob.fBlurRec.fQuality != blurRec.fQuality)) {
269 return true;
270 }
271
272 // Similarly, we only cache one version for each style
273 if (blob.fKey.fStyle != SkPaint::kFill_Style &&
274 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
275 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
276 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
277 return true;
278 }
279
joshualittfcfb9fc2015-04-21 07:35:10 -0700280 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
281 // for mixed blobs if this becomes an issue.
282 if (blob.hasBitmap() && blob.hasDistanceField()) {
joshualitt473ffa12015-04-22 18:23:15 -0700283 // Identical viewmatrices and we can reuse in all cases
284 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
285 return false;
286 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700287 return true;
288 }
289
joshualittfcfb9fc2015-04-21 07:35:10 -0700290 if (blob.hasBitmap()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700291 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
292 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
293 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
294 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
295 return true;
296 }
297
joshualittfcfb9fc2015-04-21 07:35:10 -0700298 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
299 // but only for integer translations.
300 // This cool bit of math will determine the necessary translation to apply to the already
301 // generated vertex coordinates to move them to the correct position
302 SkScalar transX = viewMatrix.getTranslateX() +
303 viewMatrix.getScaleX() * (x - blob.fX) +
304 viewMatrix.getSkewX() * (y - blob.fY) -
305 blob.fViewMatrix.getTranslateX();
306 SkScalar transY = viewMatrix.getTranslateY() +
307 viewMatrix.getSkewY() * (x - blob.fX) +
308 viewMatrix.getScaleY() * (y - blob.fY) -
309 blob.fViewMatrix.getTranslateY();
joshualittf0c000d2015-04-27 09:36:55 -0700310 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700311 return true;
312 }
313
joshualittfcfb9fc2015-04-21 07:35:10 -0700314 (*outTransX) = transX;
315 (*outTransY) = transY;
joshualitta7c63892015-04-21 13:24:37 -0700316 } else if (blob.hasDistanceField()) {
joshualitt64c99cc2015-04-21 09:43:03 -0700317 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
318 // distance field being generated, so we have to regenerate in those cases
319 SkScalar newMaxScale = viewMatrix.getMaxScale();
320 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
321 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
322 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
323 return true;
324 }
325
326 (*outTransX) = x - blob.fX;
327 (*outTransY) = y - blob.fY;
joshualittfcfb9fc2015-04-21 07:35:10 -0700328 }
joshualitta7c63892015-04-21 13:24:37 -0700329 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
330 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
331 // the blob anyways at flush time, so no need to regenerate explicitly
joshualittfcfb9fc2015-04-21 07:35:10 -0700332
joshualitt2a0e9f32015-04-13 06:12:21 -0700333 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700334}
335
336
joshualittdbd35932015-04-02 09:19:04 -0700337inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
338 const SkPaint& skPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -0700339 const SkMatrix* viewMatrix,
340 bool noGamma) {
341 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
joshualitt1d89e8d2015-04-01 12:40:54 -0700342 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
343 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
344}
345
joshualittdbd35932015-04-02 09:19:04 -0700346void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
347 const SkPaint& skPaint, const SkMatrix& viewMatrix,
348 const SkTextBlob* blob, SkScalar x, SkScalar y,
349 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualitt9b8e79e2015-04-24 09:57:12 -0700350 // If we have been abandoned, then don't draw
351 if (!fContext->getTextTarget()) {
352 return;
353 }
354
joshualitt2a0e9f32015-04-13 06:12:21 -0700355 SkAutoTUnref<BitmapTextBlob> cacheBlob;
joshualitt53b5f442015-04-13 06:33:59 -0700356 SkMaskFilter::BlurRec blurRec;
357 BitmapTextBlob::Key key;
358 // It might be worth caching these things, but its not clear at this time
359 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
360 const SkMaskFilter* mf = skPaint.getMaskFilter();
joshualitt2a0e9f32015-04-13 06:12:21 -0700361 bool canCache = !(skPaint.getPathEffect() ||
joshualitt53b5f442015-04-13 06:33:59 -0700362 (mf && !mf->asABlur(&blurRec)) ||
joshualitt2a0e9f32015-04-13 06:12:21 -0700363 drawFilter);
364
365 if (canCache) {
joshualitt9e36c1a2015-04-14 12:17:27 -0700366 bool hasLCD = HasLCD(blob);
367 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
368 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
369 // ensure we always match the same key
370 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
371 ComputeCanonicalColor(skPaint, hasLCD);
372
joshualitt53b5f442015-04-13 06:33:59 -0700373 key.fUniqueID = blob->uniqueID();
374 key.fStyle = skPaint.getStyle();
375 key.fHasBlur = SkToBool(mf);
joshualitt9e36c1a2015-04-14 12:17:27 -0700376 key.fCanonicalColor = canonicalColor;
joshualitt53b5f442015-04-13 06:33:59 -0700377 cacheBlob.reset(SkSafeRef(fCache->find(key)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700378 }
379
joshualitt1d89e8d2015-04-01 12:40:54 -0700380 SkIRect clipRect;
381 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
382
joshualitt2a0e9f32015-04-13 06:12:21 -0700383 SkScalar transX = 0.f;
384 SkScalar transY = 0.f;
385
joshualitt9e36c1a2015-04-14 12:17:27 -0700386 // Though for the time being runs in the textblob can override the paint, they only touch font
387 // info.
388 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700389 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
390 return;
391 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700392
joshualittb7133be2015-04-08 09:08:31 -0700393 if (cacheBlob) {
joshualitt53b5f442015-04-13 06:33:59 -0700394 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700395 // We have to remake the blob because changes may invalidate our masks.
396 // TODO we could probably get away reuse most of the time if the pointer is unique,
397 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700398 fCache->remove(cacheBlob);
joshualitt53b5f442015-04-13 06:33:59 -0700399 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
400 kGrayTextVASize)));
joshualitt9e36c1a2015-04-14 12:17:27 -0700401 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700402 drawFilter, clipRect, rt, clip, grPaint);
joshualittb7133be2015-04-08 09:08:31 -0700403 } else {
joshualitt9e36c1a2015-04-14 12:17:27 -0700404 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
405 // offsets
joshualitt2a0e9f32015-04-13 06:12:21 -0700406 cacheBlob->fViewMatrix = viewMatrix;
407 cacheBlob->fX = x;
408 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700409 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700410 }
411 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700412 if (canCache) {
joshualitt53b5f442015-04-13 06:33:59 -0700413 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
414 kGrayTextVASize)));
joshualitt2a0e9f32015-04-13 06:12:21 -0700415 } else {
416 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
417 }
joshualitt9e36c1a2015-04-14 12:17:27 -0700418 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700419 drawFilter, clipRect, rt, clip, grPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -0700420 }
421
joshualitt9e36c1a2015-04-14 12:17:27 -0700422 cacheBlob->fPaintColor = skPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -0700423 this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700424 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700425}
426
joshualitt9bd2daf2015-04-17 09:30:06 -0700427inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
428 const SkMatrix& viewMatrix) {
429 // TODO: support perspective (need getMaxScale replacement)
430 if (viewMatrix.hasPerspective()) {
431 return false;
432 }
433
434 SkScalar maxScale = viewMatrix.getMaxScale();
435 SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
436 // Hinted text looks far better at small resolutions
437 // Scaling up beyond 2x yields undesireable artifacts
joshualitta7c63892015-04-21 13:24:37 -0700438 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700439 return false;
440 }
441
442 if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
443 scaledTextSize < kLargeDFFontSize) {
444 return false;
445 }
446
447 // rasterizers and mask filters modify alpha, which doesn't
448 // translate well to distance
449 if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
450 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
451 return false;
452 }
453
454 // TODO: add some stroking support
455 if (skPaint.getStyle() != SkPaint::kFill_Style) {
456 return false;
457 }
458
459 return true;
460}
461
joshualittdbd35932015-04-02 09:19:04 -0700462void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
joshualitt9e36c1a2015-04-14 12:17:27 -0700463 const SkPaint& skPaint, GrColor color,
464 const SkMatrix& viewMatrix,
joshualittdbd35932015-04-02 09:19:04 -0700465 const SkTextBlob* blob, SkScalar x, SkScalar y,
joshualittfcfb9fc2015-04-21 07:35:10 -0700466 SkDrawFilter* drawFilter, const SkIRect& clipRect,
467 GrRenderTarget* rt, const GrClip& clip,
468 const GrPaint& paint) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700469 cacheBlob->fViewMatrix = viewMatrix;
470 cacheBlob->fX = x;
471 cacheBlob->fY = y;
joshualitt1d89e8d2015-04-01 12:40:54 -0700472
473 // Regenerate textblob
474 SkPaint runPaint = skPaint;
475 SkTextBlob::RunIterator it(blob);
476 for (int run = 0; !it.done(); it.next(), run++) {
477 int glyphCount = it.glyphCount();
478 size_t textLen = glyphCount * sizeof(uint16_t);
479 const SkPoint& offset = it.offset();
480 // applyFontToPaint() always overwrites the exact same attributes,
481 // so it is safe to not re-seed the paint for this reason.
482 it.applyFontToPaint(&runPaint);
483
484 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
485 // A false return from filter() means we should abort the current draw.
486 runPaint = skPaint;
487 continue;
488 }
489
490 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
491
joshualitt1d89e8d2015-04-01 12:40:54 -0700492 // setup vertex / glyphIndex for the new run
493 if (run > 0) {
494 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
495 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
496
497 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
498 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
499
500 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
501 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
502 }
503
joshualittfcfb9fc2015-04-21 07:35:10 -0700504 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
505 cacheBlob->setHasDistanceField();
506 SkPaint dfPaint = runPaint;
507 SkScalar textRatio;
joshualitt64c99cc2015-04-21 09:43:03 -0700508 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
joshualittfcfb9fc2015-04-21 07:35:10 -0700509 Run& runIdx = cacheBlob->fRuns[run];
510 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
511 subRun.fUseLCDText = runPaint.isLCDRenderText();
512 subRun.fDrawAsDistanceFields = true;
joshualitt9a27e632015-04-06 10:53:36 -0700513
joshualittfcfb9fc2015-04-21 07:35:10 -0700514 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
515
516 SkTDArray<char> fallbackTxt;
517 SkTDArray<SkScalar> fallbackPos;
518 SkPoint dfOffset;
519 int scalarsPerPosition = 2;
520 switch (it.positioning()) {
521 case SkTextBlob::kDefault_Positioning: {
522 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
523 (const char *)it.glyphs(), textLen,
524 x + offset.x(), y + offset.y(), clipRect, textRatio,
525 &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
526 break;
527 }
528 case SkTextBlob::kHorizontal_Positioning: {
529 scalarsPerPosition = 1;
530 dfOffset = SkPoint::Make(x, y + offset.y());
531 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
532 (const char*)it.glyphs(), textLen, it.pos(),
533 scalarsPerPosition, dfOffset, clipRect, textRatio,
534 &fallbackTxt, &fallbackPos);
535 break;
536 }
537 case SkTextBlob::kFull_Positioning: {
538 dfOffset = SkPoint::Make(x, y);
539 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
540 (const char*)it.glyphs(), textLen, it.pos(),
541 scalarsPerPosition, dfOffset, clipRect, textRatio,
542 &fallbackTxt, &fallbackPos);
543 break;
544 }
545 }
546 if (fallbackTxt.count()) {
547 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
548 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
549 clipRect);
550 }
551
552 SkGlyphCache::AttachCache(cache);
553 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
554 cacheBlob->fRuns[run].fDrawAsPaths = true;
555 } else {
556 cacheBlob->setHasBitmap();
557 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
558 false);
559 switch (it.positioning()) {
560 case SkTextBlob::kDefault_Positioning:
561 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
562 (const char *)it.glyphs(), textLen,
563 x + offset.x(), y + offset.y(), clipRect);
564 break;
565 case SkTextBlob::kHorizontal_Positioning:
566 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
567 (const char*)it.glyphs(), textLen, it.pos(), 1,
568 SkPoint::Make(x, y + offset.y()), clipRect);
569 break;
570 case SkTextBlob::kFull_Positioning:
571 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
572 (const char*)it.glyphs(), textLen, it.pos(), 2,
573 SkPoint::Make(x, y), clipRect);
574 break;
575 }
576 SkGlyphCache::AttachCache(cache);
joshualitt1d89e8d2015-04-01 12:40:54 -0700577 }
578
579 if (drawFilter) {
580 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
581 runPaint = skPaint;
582 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700583 }
584}
585
joshualitt64c99cc2015-04-21 09:43:03 -0700586inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
587 SkPaint* skPaint,
588 SkScalar* textRatio,
joshualitt9bd2daf2015-04-17 09:30:06 -0700589 const SkMatrix& viewMatrix) {
590 // getMaxScale doesn't support perspective, so neither do we at the moment
591 SkASSERT(!viewMatrix.hasPerspective());
592 SkScalar maxScale = viewMatrix.getMaxScale();
593 SkScalar textSize = skPaint->getTextSize();
594 SkScalar scaledTextSize = textSize;
595 // if we have non-unity scale, we need to choose our base text size
596 // based on the SkPaint's text size multiplied by the max scale factor
597 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
598 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
599 scaledTextSize *= maxScale;
600 }
601
joshualitt64c99cc2015-04-21 09:43:03 -0700602 // We have three sizes of distance field text, and within each size 'bucket' there is a floor
603 // and ceiling. A scale outside of this range would require regenerating the distance fields
604 SkScalar dfMaskScaleFloor;
605 SkScalar dfMaskScaleCeil;
joshualitt9bd2daf2015-04-17 09:30:06 -0700606 if (scaledTextSize <= kSmallDFFontLimit) {
joshualitt64c99cc2015-04-21 09:43:03 -0700607 dfMaskScaleFloor = kMinDFFontSize;
joshualitta7c63892015-04-21 13:24:37 -0700608 dfMaskScaleCeil = kSmallDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700609 *textRatio = textSize / kSmallDFFontSize;
610 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
611 } else if (scaledTextSize <= kMediumDFFontLimit) {
joshualitta7c63892015-04-21 13:24:37 -0700612 dfMaskScaleFloor = kSmallDFFontLimit;
613 dfMaskScaleCeil = kMediumDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700614 *textRatio = textSize / kMediumDFFontSize;
615 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
616 } else {
joshualitta7c63892015-04-21 13:24:37 -0700617 dfMaskScaleFloor = kMediumDFFontLimit;
618 dfMaskScaleCeil = kLargeDFFontLimit;
joshualitt9bd2daf2015-04-17 09:30:06 -0700619 *textRatio = textSize / kLargeDFFontSize;
620 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
621 }
622
joshualitt64c99cc2015-04-21 09:43:03 -0700623 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
624 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
625 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
626 // tolerate before we'd have to move to a large mip size. When we actually test these values
627 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
628 // against these values to decide if we can reuse or not(ie, will a given scale change our mip
629 // level)
joshualitta7c63892015-04-21 13:24:37 -0700630 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
joshualitt64c99cc2015-04-21 09:43:03 -0700631 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
632 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
633
joshualitt9bd2daf2015-04-17 09:30:06 -0700634 skPaint->setLCDRenderText(false);
635 skPaint->setAutohinted(false);
636 skPaint->setHinting(SkPaint::kNormal_Hinting);
637 skPaint->setSubpixelText(true);
638}
639
joshualittfec19e12015-04-17 10:32:32 -0700640inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
joshualittfcfb9fc2015-04-21 07:35:10 -0700641 int runIndex,
joshualittfec19e12015-04-17 10:32:32 -0700642 GrRenderTarget* rt, const GrClip& clip,
joshualitt9bd2daf2015-04-17 09:30:06 -0700643 const GrPaint& paint,
644 const SkPaint& skPaint,
645 const SkMatrix& viewMatrix,
646 const SkTDArray<char>& fallbackTxt,
647 const SkTDArray<SkScalar>& fallbackPos,
648 int scalarsPerPosition,
649 const SkPoint& offset,
650 const SkIRect& clipRect) {
joshualittfec19e12015-04-17 10:32:32 -0700651 SkASSERT(fallbackTxt.count());
joshualittfcfb9fc2015-04-21 07:35:10 -0700652 blob->setHasBitmap();
653 Run& run = blob->fRuns[runIndex];
joshualitt97202d22015-04-22 13:47:02 -0700654 // Push back a new subrun to fill and set the override descriptor
655 run.push_back();
656 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
657 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
joshualittfec19e12015-04-17 10:32:32 -0700658 &fDeviceProperties, &viewMatrix, false);
659 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
joshualitt97202d22015-04-22 13:47:02 -0700660 run.fOverrideDescriptor->getDesc());
joshualittfcfb9fc2015-04-21 07:35:10 -0700661 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
joshualitt9bd2daf2015-04-17 09:30:06 -0700662 fallbackTxt.begin(), fallbackTxt.count(),
663 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
664 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700665}
666
667inline GrAtlasTextContext::BitmapTextBlob*
668GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
669 const SkMatrix& viewMatrix, SkGlyphCache** cache,
670 SkPaint* dfPaint, SkScalar* textRatio) {
671 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
672
673 *dfPaint = origPaint;
joshualitt64c99cc2015-04-21 09:43:03 -0700674 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
joshualitt9bd2daf2015-04-17 09:30:06 -0700675 blob->fViewMatrix = viewMatrix;
joshualittfcfb9fc2015-04-21 07:35:10 -0700676 Run& run = blob->fRuns[0];
677 PerSubRunInfo& subRun = run.fSubRunInfo.back();
678 subRun.fUseLCDText = origPaint.isLCDRenderText();
679 subRun.fDrawAsDistanceFields = true;
joshualitt9bd2daf2015-04-17 09:30:06 -0700680
681 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
682 return blob;
683}
684
joshualittdbd35932015-04-02 09:19:04 -0700685void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
686 const GrPaint& paint, const SkPaint& skPaint,
687 const SkMatrix& viewMatrix,
688 const char text[], size_t byteLength,
689 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700690 int glyphCount = skPaint.countText(text, byteLength);
joshualitt1d89e8d2015-04-01 12:40:54 -0700691 SkIRect clipRect;
692 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
693
694 // setup cache
joshualitt9bd2daf2015-04-17 09:30:06 -0700695 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
696 SkPaint dfPaint;
697 SkScalar textRatio;
698 SkGlyphCache* cache;
699 SkAutoTUnref<BitmapTextBlob> blob(this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache,
700 &dfPaint, &textRatio));
joshualitt1d89e8d2015-04-01 12:40:54 -0700701
joshualitt9bd2daf2015-04-17 09:30:06 -0700702 SkTDArray<char> fallbackTxt;
703 SkTDArray<SkScalar> fallbackPos;
704 SkPoint offset;
705 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
706 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
707 &offset, skPaint);
708 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700709 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700710 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700711 fallbackPos, 2, offset, clipRect);
712 }
joshualittfec19e12015-04-17 10:32:32 -0700713 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
joshualitt9bd2daf2015-04-17 09:30:06 -0700714 } else {
715 SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
716 blob->fViewMatrix = viewMatrix;
717
718 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
719 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
720 byteLength, x, y, clipRect);
721 SkGlyphCache::AttachCache(cache);
722 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
723 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700724}
725
joshualitt9bd2daf2015-04-17 09:30:06 -0700726void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
727 const GrPaint& paint, const SkPaint& skPaint,
728 const SkMatrix& viewMatrix,
729 const char text[], size_t byteLength,
730 const SkScalar pos[], int scalarsPerPosition,
731 const SkPoint& offset, const SkIRect& regionClipBounds) {
732 int glyphCount = skPaint.countText(text, byteLength);
733
734 SkIRect clipRect;
735 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
736
737 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
738 SkPaint dfPaint;
739 SkScalar textRatio;
740 SkGlyphCache* cache;
741 SkAutoTUnref<BitmapTextBlob> blob(this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache,
742 &dfPaint, &textRatio));
743
744 SkTDArray<char> fallbackTxt;
745 SkTDArray<SkScalar> fallbackPos;
746 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
747 byteLength, pos, scalarsPerPosition, offset, clipRect,
748 textRatio, &fallbackTxt, &fallbackPos);
749 SkGlyphCache::AttachCache(cache);
joshualitt9bd2daf2015-04-17 09:30:06 -0700750 if (fallbackTxt.count()) {
joshualittfcfb9fc2015-04-21 07:35:10 -0700751 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
joshualitt9bd2daf2015-04-17 09:30:06 -0700752 fallbackPos, scalarsPerPosition, offset, clipRect);
753 }
joshualittfec19e12015-04-17 10:32:32 -0700754 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
joshualitt9bd2daf2015-04-17 09:30:06 -0700755 } else {
756 SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
757 blob->fViewMatrix = viewMatrix;
758 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
759 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
760 byteLength, pos, scalarsPerPosition, offset, clipRect);
761 SkGlyphCache::AttachCache(cache);
762 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
763 }
764}
765
766void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
767 SkGlyphCache* cache, const SkPaint& skPaint,
768 GrColor color,
769 const SkMatrix& viewMatrix,
770 const char text[], size_t byteLength,
771 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700772 SkASSERT(byteLength == 0 || text != NULL);
773
774 // nothing to draw
775 if (text == NULL || byteLength == 0) {
776 return;
777 }
778
779 fCurrStrike = NULL;
780 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
781
782 // Get GrFontScaler from cache
783 GrFontScaler* fontScaler = GetGrFontScaler(cache);
784
785 // transform our starting point
786 {
787 SkPoint loc;
788 viewMatrix.mapXY(x, y, &loc);
789 x = loc.fX;
790 y = loc.fY;
791 }
792
793 // need to measure first
794 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
795 SkVector stopVector;
796 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
797
798 SkScalar stopX = stopVector.fX;
799 SkScalar stopY = stopVector.fY;
800
801 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
802 stopX = SkScalarHalf(stopX);
803 stopY = SkScalarHalf(stopY);
804 }
805 x -= stopX;
806 y -= stopY;
807 }
808
809 const char* stop = text + byteLength;
810
811 SkAutoKern autokern;
812
813 SkFixed fxMask = ~0;
814 SkFixed fyMask = ~0;
815 SkScalar halfSampleX, halfSampleY;
816 if (cache->isSubpixel()) {
817 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
818 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
819 if (kX_SkAxisAlignment == baseline) {
820 fyMask = 0;
821 halfSampleY = SK_ScalarHalf;
822 } else if (kY_SkAxisAlignment == baseline) {
823 fxMask = 0;
824 halfSampleX = SK_ScalarHalf;
825 }
826 } else {
827 halfSampleX = halfSampleY = SK_ScalarHalf;
828 }
829
830 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
831 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
832
833 while (text < stop) {
834 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
835
836 fx += autokern.adjust(glyph);
837
838 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700839 this->bmpAppendGlyph(blob,
840 runIndex,
841 GrGlyph::Pack(glyph.getGlyphID(),
842 glyph.getSubXFixed(),
843 glyph.getSubYFixed(),
844 GrGlyph::kCoverage_MaskStyle),
845 Sk48Dot16FloorToInt(fx),
846 Sk48Dot16FloorToInt(fy),
847 color,
848 fontScaler,
849 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700850 }
851
852 fx += glyph.fAdvanceX;
853 fy += glyph.fAdvanceY;
854 }
855}
856
joshualitt9bd2daf2015-04-17 09:30:06 -0700857void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
858 SkGlyphCache* cache, const SkPaint& skPaint,
859 GrColor color,
860 const SkMatrix& viewMatrix,
861 const char text[], size_t byteLength,
862 const SkScalar pos[], int scalarsPerPosition,
863 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700864 SkASSERT(byteLength == 0 || text != NULL);
865 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
866
867 // nothing to draw
868 if (text == NULL || byteLength == 0) {
869 return;
870 }
871
872 fCurrStrike = NULL;
873 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
874
875 // Get GrFontScaler from cache
876 GrFontScaler* fontScaler = GetGrFontScaler(cache);
877
878 const char* stop = text + byteLength;
879 SkTextAlignProc alignProc(skPaint.getTextAlign());
880 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
881
882 if (cache->isSubpixel()) {
883 // maybe we should skip the rounding if linearText is set
884 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
885
886 SkFixed fxMask = ~0;
887 SkFixed fyMask = ~0;
888 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
889 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
890 if (kX_SkAxisAlignment == baseline) {
891 fyMask = 0;
892 halfSampleY = SK_ScalarHalf;
893 } else if (kY_SkAxisAlignment == baseline) {
894 fxMask = 0;
895 halfSampleX = SK_ScalarHalf;
896 }
897
898 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
899 while (text < stop) {
900 SkPoint tmsLoc;
901 tmsProc(pos, &tmsLoc);
902 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
903 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
904
905 const SkGlyph& glyph = glyphCacheProc(cache, &text,
906 fx & fxMask, fy & fyMask);
907
908 if (glyph.fWidth) {
joshualitt9bd2daf2015-04-17 09:30:06 -0700909 this->bmpAppendGlyph(blob,
910 runIndex,
911 GrGlyph::Pack(glyph.getGlyphID(),
912 glyph.getSubXFixed(),
913 glyph.getSubYFixed(),
914 GrGlyph::kCoverage_MaskStyle),
915 Sk48Dot16FloorToInt(fx),
916 Sk48Dot16FloorToInt(fy),
917 color,
918 fontScaler,
919 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700920 }
921 pos += scalarsPerPosition;
922 }
923 } else {
924 while (text < stop) {
925 const char* currentText = text;
926 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
927
928 if (metricGlyph.fWidth) {
929 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
930 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
931 SkPoint tmsLoc;
932 tmsProc(pos, &tmsLoc);
933 SkPoint alignLoc;
934 alignProc(tmsLoc, metricGlyph, &alignLoc);
935
936 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
937 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
938
939 // have to call again, now that we've been "aligned"
940 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
941 fx & fxMask, fy & fyMask);
942 // the assumption is that the metrics haven't changed
943 SkASSERT(prevAdvX == glyph.fAdvanceX);
944 SkASSERT(prevAdvY == glyph.fAdvanceY);
945 SkASSERT(glyph.fWidth);
946
joshualitt9bd2daf2015-04-17 09:30:06 -0700947 this->bmpAppendGlyph(blob,
948 runIndex,
949 GrGlyph::Pack(glyph.getGlyphID(),
950 glyph.getSubXFixed(),
951 glyph.getSubYFixed(),
952 GrGlyph::kCoverage_MaskStyle),
953 Sk48Dot16FloorToInt(fx),
954 Sk48Dot16FloorToInt(fy),
955 color,
956 fontScaler,
957 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700958 }
959 pos += scalarsPerPosition;
960 }
961 }
962 } else { // not subpixel
963
964 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
965 while (text < stop) {
966 // the last 2 parameters are ignored
967 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
968
969 if (glyph.fWidth) {
970 SkPoint tmsLoc;
971 tmsProc(pos, &tmsLoc);
972
973 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
974 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -0700975 this->bmpAppendGlyph(blob,
976 runIndex,
977 GrGlyph::Pack(glyph.getGlyphID(),
978 glyph.getSubXFixed(),
979 glyph.getSubYFixed(),
980 GrGlyph::kCoverage_MaskStyle),
981 Sk48Dot16FloorToInt(fx),
982 Sk48Dot16FloorToInt(fy),
983 color,
984 fontScaler,
985 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -0700986 }
987 pos += scalarsPerPosition;
988 }
989 } else {
990 while (text < stop) {
991 // the last 2 parameters are ignored
992 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
993
994 if (glyph.fWidth) {
995 SkPoint tmsLoc;
996 tmsProc(pos, &tmsLoc);
997
998 SkPoint alignLoc;
999 alignProc(tmsLoc, glyph, &alignLoc);
1000
1001 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
1002 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
joshualitt9bd2daf2015-04-17 09:30:06 -07001003 this->bmpAppendGlyph(blob,
1004 runIndex,
1005 GrGlyph::Pack(glyph.getGlyphID(),
1006 glyph.getSubXFixed(),
1007 glyph.getSubYFixed(),
1008 GrGlyph::kCoverage_MaskStyle),
1009 Sk48Dot16FloorToInt(fx),
1010 Sk48Dot16FloorToInt(fy),
1011 color,
1012 fontScaler,
1013 clipRect);
joshualitt1d89e8d2015-04-01 12:40:54 -07001014 }
1015 pos += scalarsPerPosition;
1016 }
1017 }
1018 }
1019}
1020
joshualitt9bd2daf2015-04-17 09:30:06 -07001021
1022void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
1023 SkGlyphCache* cache, const SkPaint& skPaint,
1024 GrColor color,
1025 const SkMatrix& viewMatrix,
1026 const char text[], size_t byteLength,
1027 SkScalar x, SkScalar y, const SkIRect& clipRect,
1028 SkScalar textRatio,
1029 SkTDArray<char>* fallbackTxt,
1030 SkTDArray<SkScalar>* fallbackPos,
1031 SkPoint* offset,
1032 const SkPaint& origPaint) {
1033 SkASSERT(byteLength == 0 || text != NULL);
1034
1035 // nothing to draw
1036 if (text == NULL || byteLength == 0) {
1037 return;
1038 }
1039
1040 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
1041 SkAutoDescriptor desc;
1042 origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
1043 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
1044 desc.getDesc());
1045
1046 SkTArray<SkScalar> positions;
1047
1048 const char* textPtr = text;
1049 SkFixed stopX = 0;
1050 SkFixed stopY = 0;
1051 SkFixed origin = 0;
1052 switch (origPaint.getTextAlign()) {
1053 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
1054 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
1055 case SkPaint::kLeft_Align: origin = 0; break;
1056 }
1057
1058 SkAutoKern autokern;
1059 const char* stop = text + byteLength;
1060 while (textPtr < stop) {
1061 // don't need x, y here, since all subpixel variants will have the
1062 // same advance
1063 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
1064
1065 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
1066 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
1067
1068 SkFixed height = glyph.fAdvanceY;
1069 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
1070
1071 stopX += width;
1072 stopY += height;
1073 }
1074 SkASSERT(textPtr == stop);
1075
1076 // now adjust starting point depending on alignment
1077 SkScalar alignX = SkFixedToScalar(stopX);
1078 SkScalar alignY = SkFixedToScalar(stopY);
1079 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
1080 alignX = SkScalarHalf(alignX);
1081 alignY = SkScalarHalf(alignY);
1082 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
1083 alignX = 0;
1084 alignY = 0;
1085 }
1086 x -= alignX;
1087 y -= alignY;
1088 *offset = SkPoint::Make(x, y);
1089
1090 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
1091 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
1092 fallbackPos);
1093 SkGlyphCache::AttachCache(origPaintCache);
1094}
1095
1096void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
1097 SkGlyphCache* cache, const SkPaint& skPaint,
1098 GrColor color,
1099 const SkMatrix& viewMatrix,
1100 const char text[], size_t byteLength,
1101 const SkScalar pos[], int scalarsPerPosition,
1102 const SkPoint& offset, const SkIRect& clipRect,
1103 SkScalar textRatio,
1104 SkTDArray<char>* fallbackTxt,
1105 SkTDArray<SkScalar>* fallbackPos) {
1106
1107 SkASSERT(byteLength == 0 || text != NULL);
1108 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1109
1110 // nothing to draw
1111 if (text == NULL || byteLength == 0) {
1112 return;
1113 }
1114
1115 fCurrStrike = NULL;
1116
1117 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
1118 GrFontScaler* fontScaler = GetGrFontScaler(cache);
1119
1120 const char* stop = text + byteLength;
1121
1122 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
1123 while (text < stop) {
1124 const char* lastText = text;
1125 // the last 2 parameters are ignored
1126 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1127
1128 if (glyph.fWidth) {
1129 SkScalar x = offset.x() + pos[0];
1130 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1131
1132 if (!this->dfAppendGlyph(blob,
1133 runIndex,
1134 GrGlyph::Pack(glyph.getGlyphID(),
1135 glyph.getSubXFixed(),
1136 glyph.getSubYFixed(),
1137 GrGlyph::kDistance_MaskStyle),
1138 x, y, color, fontScaler, clipRect,
1139 textRatio, viewMatrix)) {
1140 // couldn't append, send to fallback
1141 fallbackTxt->append(SkToInt(text-lastText), lastText);
1142 *fallbackPos->append() = pos[0];
1143 if (2 == scalarsPerPosition) {
1144 *fallbackPos->append() = pos[1];
1145 }
1146 }
1147 }
1148 pos += scalarsPerPosition;
1149 }
1150 } else {
1151 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
1152 : SK_Scalar1;
1153 while (text < stop) {
1154 const char* lastText = text;
1155 // the last 2 parameters are ignored
1156 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1157
1158 if (glyph.fWidth) {
1159 SkScalar x = offset.x() + pos[0];
1160 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
1161
1162 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
1163 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
1164
1165 if (!this->dfAppendGlyph(blob,
1166 runIndex,
1167 GrGlyph::Pack(glyph.getGlyphID(),
1168 glyph.getSubXFixed(),
1169 glyph.getSubYFixed(),
1170 GrGlyph::kDistance_MaskStyle),
1171 x - advanceX, y - advanceY, color,
1172 fontScaler,
1173 clipRect,
1174 textRatio,
1175 viewMatrix)) {
1176 // couldn't append, send to fallback
1177 fallbackTxt->append(SkToInt(text-lastText), lastText);
1178 *fallbackPos->append() = pos[0];
1179 if (2 == scalarsPerPosition) {
1180 *fallbackPos->append() = pos[1];
1181 }
1182 }
1183 }
1184 pos += scalarsPerPosition;
1185 }
1186 }
1187}
1188
1189void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
1190 GrGlyph::PackedID packed,
1191 int vx, int vy, GrColor color, GrFontScaler* scaler,
1192 const SkIRect& clipRect) {
joshualittae32c102015-04-21 09:37:57 -07001193 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001194 if (!fCurrStrike) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001195 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001196 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt1d89e8d2015-04-01 12:40:54 -07001197 }
1198
1199 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001200 if (!glyph) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001201 return;
1202 }
1203
1204 int x = vx + glyph->fBounds.fLeft;
1205 int y = vy + glyph->fBounds.fTop;
1206
1207 // keep them as ints until we've done the clip-test
1208 int width = glyph->fBounds.width();
1209 int height = glyph->fBounds.height();
1210
joshualitt2a0e9f32015-04-13 06:12:21 -07001211#if 0
1212 // Not checking the clip bounds might introduce a performance regression. However, its not
1213 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
1214 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
1215 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
1216 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -07001217 // check if we clipped out
1218 if (clipRect.quickReject(x, y, x + width, y + height)) {
1219 return;
1220 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001221#endif
joshualitt1d89e8d2015-04-01 12:40:54 -07001222
1223 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001224 if (glyph->fTooLargeForAtlas) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001225 this->appendGlyphPath(blob, glyph, scaler, vx, vy);
joshualitt1d89e8d2015-04-01 12:40:54 -07001226 return;
1227 }
1228
joshualitt1d89e8d2015-04-01 12:40:54 -07001229 GrMaskFormat format = glyph->fMaskFormat;
1230
1231 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1232 if (run.fInitialized && subRun->fMaskFormat != format) {
joshualittfec19e12015-04-17 10:32:32 -07001233 subRun = &run.fSubRunInfo.push_back();
joshualitt1d89e8d2015-04-01 12:40:54 -07001234 }
1235
1236 run.fInitialized = true;
joshualitt1d89e8d2015-04-01 12:40:54 -07001237
1238 size_t vertexStride = get_vertex_stride(format);
1239
1240 SkRect r;
1241 r.fLeft = SkIntToScalar(x);
1242 r.fTop = SkIntToScalar(y);
1243 r.fRight = r.fLeft + SkIntToScalar(width);
1244 r.fBottom = r.fTop + SkIntToScalar(height);
joshualitt9bd2daf2015-04-17 09:30:06 -07001245 subRun->fMaskFormat = format;
1246 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
joshualittae32c102015-04-21 09:37:57 -07001247 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001248}
joshualitt1d89e8d2015-04-01 12:40:54 -07001249
joshualitt9bd2daf2015-04-17 09:30:06 -07001250bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
1251 GrGlyph::PackedID packed,
1252 SkScalar sx, SkScalar sy, GrColor color,
1253 GrFontScaler* scaler,
1254 const SkIRect& clipRect,
1255 SkScalar textRatio, const SkMatrix& viewMatrix) {
joshualittae32c102015-04-21 09:37:57 -07001256 Run& run = blob->fRuns[runIndex];
joshualitt9bd2daf2015-04-17 09:30:06 -07001257 if (!fCurrStrike) {
1258 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
joshualittae32c102015-04-21 09:37:57 -07001259 run.fStrike.reset(SkRef(fCurrStrike));
joshualitt9bd2daf2015-04-17 09:30:06 -07001260 }
1261
1262 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
joshualitt010db532015-04-21 10:07:26 -07001263 if (!glyph) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001264 return true;
1265 }
1266
1267 // fallback to color glyph support
1268 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
1269 return false;
1270 }
1271
1272 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
1273 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
1274 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
1275 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
1276
1277 SkScalar scale = textRatio;
1278 dx *= scale;
1279 dy *= scale;
1280 width *= scale;
1281 height *= scale;
1282 sx += dx;
1283 sy += dy;
1284 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
1285
1286#if 0
1287 // check if we clipped out
1288 SkRect dstRect;
1289 viewMatrix.mapRect(&dstRect, glyphRect);
1290 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
1291 SkScalarTruncToInt(dstRect.top()),
1292 SkScalarTruncToInt(dstRect.right()),
1293 SkScalarTruncToInt(dstRect.bottom()))) {
1294 return true;
1295 }
1296#endif
1297
1298 // TODO combine with the above
1299 // If the glyph is too large we fall back to paths
joshualitt010db532015-04-21 10:07:26 -07001300 if (glyph->fTooLargeForAtlas) {
joshualitt9bd2daf2015-04-17 09:30:06 -07001301 this->appendGlyphPath(blob, glyph, scaler, SkScalarRoundToInt(sx - dx),
1302 SkScalarRoundToInt(sy - dy));
1303 return true;
1304 }
1305
joshualitt9bd2daf2015-04-17 09:30:06 -07001306 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
1307 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
1308 subRun->fMaskFormat = kA8_GrMaskFormat;
1309
1310 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
1311
1312 bool useColorVerts = !subRun->fUseLCDText;
1313 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
joshualittae32c102015-04-21 09:37:57 -07001314 glyph);
joshualitt9bd2daf2015-04-17 09:30:06 -07001315 return true;
1316}
1317
1318inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
1319 GrFontScaler* scaler, int x, int y) {
1320 if (NULL == glyph->fPath) {
1321 SkPath* path = SkNEW(SkPath);
1322 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
1323 // flag the glyph as being dead?
1324 SkDELETE(path);
1325 return;
1326 }
1327 glyph->fPath = path;
1328 }
1329 SkASSERT(glyph->fPath);
1330 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
1331}
1332
1333inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
1334 Run::SubRunInfo* subRun,
1335 const SkRect& positions, GrColor color,
1336 size_t vertexStride, bool useVertexColor,
joshualittae32c102015-04-21 09:37:57 -07001337 GrGlyph* glyph) {
1338 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
joshualitt9bd2daf2015-04-17 09:30:06 -07001339 run->fVertexBounds.joinNonEmptyArg(positions);
1340 run->fColor = color;
joshualitt1d89e8d2015-04-01 12:40:54 -07001341
1342 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
1343
joshualitt9bd2daf2015-04-17 09:30:06 -07001344 if (useVertexColor) {
joshualitt010db532015-04-21 10:07:26 -07001345 // V0
1346 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1347 position->set(positions.fLeft, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001348 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
1349 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001350 vertex += vertexStride;
joshualitt9bd2daf2015-04-17 09:30:06 -07001351
joshualitt010db532015-04-21 10:07:26 -07001352 // V1
1353 position = reinterpret_cast<SkPoint*>(vertex);
1354 position->set(positions.fLeft, positions.fBottom);
1355 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001356 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001357 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001358
joshualitt010db532015-04-21 10:07:26 -07001359 // V2
1360 position = reinterpret_cast<SkPoint*>(vertex);
1361 position->set(positions.fRight, positions.fBottom);
1362 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001363 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001364 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -07001365
joshualitt010db532015-04-21 10:07:26 -07001366 // V3
1367 position = reinterpret_cast<SkPoint*>(vertex);
1368 position->set(positions.fRight, positions.fTop);
1369 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
joshualitt1d89e8d2015-04-01 12:40:54 -07001370 *colorPtr = color;
joshualitt010db532015-04-21 10:07:26 -07001371 } else {
1372 // V0
1373 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
1374 position->set(positions.fLeft, positions.fTop);
1375 vertex += vertexStride;
1376
1377 // V1
1378 position = reinterpret_cast<SkPoint*>(vertex);
1379 position->set(positions.fLeft, positions.fBottom);
1380 vertex += vertexStride;
1381
1382 // V2
1383 position = reinterpret_cast<SkPoint*>(vertex);
1384 position->set(positions.fRight, positions.fBottom);
1385 vertex += vertexStride;
1386
1387 // V3
1388 position = reinterpret_cast<SkPoint*>(vertex);
1389 position->set(positions.fRight, positions.fTop);
joshualitt1d89e8d2015-04-01 12:40:54 -07001390 }
1391
1392 subRun->fGlyphEndIndex++;
1393 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
1394}
1395
1396class BitmapTextBatch : public GrBatch {
1397public:
joshualitt9bd2daf2015-04-17 09:30:06 -07001398 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
joshualittdbd35932015-04-02 09:19:04 -07001399 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001400 typedef Blob::Run Run;
1401 typedef Run::SubRunInfo TextInfo;
1402 struct Geometry {
joshualittad802c62015-04-15 05:31:57 -07001403 Blob* fBlob;
joshualitt1d89e8d2015-04-01 12:40:54 -07001404 int fRun;
1405 int fSubRun;
1406 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001407 SkScalar fTransX;
1408 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -07001409 };
1410
joshualittad802c62015-04-15 05:31:57 -07001411 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1412 GrBatchFontCache* fontCache) {
1413 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -07001414 }
1415
joshualitt9bd2daf2015-04-17 09:30:06 -07001416 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
1417 GrBatchFontCache* fontCache,
1418 DistanceAdjustTable* distanceAdjustTable,
1419 SkColor filteredColor, bool useLCDText,
1420 bool useBGR, float gamma) {
1421 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
1422 filteredColor, useLCDText, useBGR, gamma));
1423 }
1424
joshualitt1d89e8d2015-04-01 12:40:54 -07001425 const char* name() const override { return "BitmapTextBatch"; }
1426
1427 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1428 if (kARGB_GrMaskFormat == fMaskFormat) {
1429 out->setUnknownFourComponents();
1430 } else {
1431 out->setKnownFourComponents(fBatch.fColor);
1432 }
1433 }
1434
1435 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt9bd2daf2015-04-17 09:30:06 -07001436 if (!fUseDistanceFields) {
1437 // Bitmap Text
1438 if (kARGB_GrMaskFormat != fMaskFormat) {
1439 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
1440 out->setUnknownSingleComponent();
1441 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
1442 out->setUnknownOpaqueFourComponents();
1443 out->setUsingLCDCoverage();
1444 } else {
1445 out->setUnknownFourComponents();
1446 out->setUsingLCDCoverage();
1447 }
1448 } else {
1449 out->setKnownSingleComponent(0xff);
1450 }
1451 } else {
1452 // Distance fields
1453 if (!fUseLCDText) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001454 out->setUnknownSingleComponent();
joshualitt1d89e8d2015-04-01 12:40:54 -07001455 } else {
1456 out->setUnknownFourComponents();
1457 out->setUsingLCDCoverage();
1458 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001459 }
1460 }
1461
1462 void initBatchTracker(const GrPipelineInfo& init) override {
1463 // Handle any color overrides
1464 if (init.fColorIgnored) {
1465 fBatch.fColor = GrColor_ILLEGAL;
1466 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1467 fBatch.fColor = init.fOverrideColor;
1468 }
1469
1470 // setup batch properties
1471 fBatch.fColorIgnored = init.fColorIgnored;
1472 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1473 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1474 }
1475
1476 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1477 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
1478 // TODO actually only invert if we don't have RGBA
1479 SkMatrix localMatrix;
1480 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
1481 SkDebugf("Cannot invert viewmatrix\n");
1482 return;
1483 }
1484
joshualitt62db8ba2015-04-09 08:22:37 -07001485 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
1486 if (!texture) {
1487 SkDebugf("Could not allocate backing texture for atlas\n");
1488 return;
1489 }
1490
joshualitt9bd2daf2015-04-17 09:30:06 -07001491 SkAutoTUnref<const GrGeometryProcessor> gp;
1492 if (fUseDistanceFields) {
1493 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
1494 texture));
1495 } else {
1496 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitteef5b3e2015-04-03 08:07:26 -07001497
joshualitt9bd2daf2015-04-17 09:30:06 -07001498 // This will be ignored in the non A8 case
1499 bool opaqueVertexColors = GrColorIsOpaque(this->color());
1500 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
1501 texture,
1502 params,
1503 fMaskFormat,
1504 opaqueVertexColors,
1505 localMatrix));
1506 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001507
1508 size_t vertexStride = gp->getVertexStride();
joshualitt9bd2daf2015-04-17 09:30:06 -07001509 SkASSERT(vertexStride == (fUseDistanceFields ?
1510 get_vertex_stride_df(fMaskFormat, fUseLCDText) :
1511 get_vertex_stride(fMaskFormat)));
joshualitt1d89e8d2015-04-01 12:40:54 -07001512
1513 this->initDraw(batchTarget, gp, pipeline);
1514
1515 int glyphCount = this->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001516 int instanceCount = fInstanceCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07001517 const GrVertexBuffer* vertexBuffer;
1518 int firstVertex;
1519
1520 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1521 glyphCount * kVerticesPerGlyph,
1522 &vertexBuffer,
1523 &firstVertex);
1524 if (!vertices) {
1525 SkDebugf("Could not allocate vertices\n");
1526 return;
1527 }
1528
1529 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
1530
1531 // setup drawinfo
1532 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1533 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1534
1535 GrDrawTarget::DrawInfo drawInfo;
1536 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1537 drawInfo.setStartVertex(0);
1538 drawInfo.setStartIndex(0);
1539 drawInfo.setVerticesPerInstance(kVerticesPerGlyph);
1540 drawInfo.setIndicesPerInstance(kIndicesPerGlyph);
1541 drawInfo.adjustStartVertex(firstVertex);
1542 drawInfo.setVertexBuffer(vertexBuffer);
1543 drawInfo.setIndexBuffer(quadIndexBuffer);
1544
joshualitt25ba7ea2015-04-21 07:49:49 -07001545 // We cache some values to avoid going to the glyphcache for the same fontScaler twice
1546 // in a row
1547 const SkDescriptor* desc = NULL;
1548 SkGlyphCache* cache = NULL;
1549 GrFontScaler* scaler = NULL;
joshualitt25ba7ea2015-04-21 07:49:49 -07001550 SkTypeface* typeface = NULL;
1551
joshualitt1d89e8d2015-04-01 12:40:54 -07001552 int instancesToFlush = 0;
1553 for (int i = 0; i < instanceCount; i++) {
1554 Geometry& args = fGeoData[i];
1555 Blob* blob = args.fBlob;
1556 Run& run = blob->fRuns[args.fRun];
1557 TextInfo& info = run.fSubRunInfo[args.fSubRun];
1558
1559 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
1560 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
joshualitt9bd2daf2015-04-17 09:30:06 -07001561 bool regenerateColors;
1562 if (fUseDistanceFields) {
joshualittfcfb9fc2015-04-21 07:35:10 -07001563 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
joshualitt9bd2daf2015-04-17 09:30:06 -07001564 } else {
1565 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
1566 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001567 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -07001568 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1569
1570 // We regenerate both texture coords and colors in the blob itself, and update the
1571 // atlas generation. If we don't end up purging any unused plots, we can avoid
1572 // regenerating the coords. We could take a finer grained approach to updating texture
1573 // coords but its not clear if the extra bookkeeping would offset any gains.
1574 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
1575 // or coords as needed. One final note, if we have to break a run for an atlas eviction
1576 // then we can't really trust the atlas has all of the correct data. Atlas evictions
1577 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -07001578 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001579 // first regenerate texture coordinates / colors if need be
joshualitt1d89e8d2015-04-01 12:40:54 -07001580 bool brokenRun = false;
joshualittae32c102015-04-21 09:37:57 -07001581
1582 // Because the GrBatchFontCache may evict the strike a blob depends on using for
1583 // generating its texture coords, we have to track whether or not the strike has
1584 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
1585 // otherwise we have to get the new strike, and use that to get the correct glyphs.
1586 // Because we do not have the packed ids, and thus can't look up our glyphs in the
1587 // new strike, we instead keep our ref to the old strike and use the packed ids from
1588 // it. These ids will still be valid as long as we hold the ref. When we are done
1589 // updating our cache of the GrGlyph*s, we drop our ref on the old strike
1590 bool regenerateGlyphs = false;
1591 GrBatchTextStrike* strike = NULL;
joshualitt1d89e8d2015-04-01 12:40:54 -07001592 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -07001593 info.fBulkUseToken.reset();
joshualitt25ba7ea2015-04-21 07:49:49 -07001594
1595 // We can reuse if we have a valid strike and our descriptors / typeface are the
1596 // same
joshualitt97202d22015-04-22 13:47:02 -07001597 const SkDescriptor* newDesc = run.fOverrideDescriptor ?
1598 run.fOverrideDescriptor->getDesc() :
joshualitt25ba7ea2015-04-21 07:49:49 -07001599 run.fDescriptor.getDesc();
1600 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
1601 !(desc->equals(*newDesc))) {
1602 if (cache) {
1603 SkGlyphCache::AttachCache(cache);
1604 }
1605 desc = newDesc;
1606 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
1607 scaler = GrTextContext::GetGrFontScaler(cache);
joshualittae32c102015-04-21 09:37:57 -07001608 strike = run.fStrike;
joshualitt25ba7ea2015-04-21 07:49:49 -07001609 typeface = run.fTypeface;
1610 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001611
joshualittae32c102015-04-21 09:37:57 -07001612 if (run.fStrike->isAbandoned()) {
1613 regenerateGlyphs = true;
1614 strike = fFontCache->getStrike(scaler);
1615 } else {
1616 strike = run.fStrike;
1617 }
1618 }
1619
1620 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001621 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001622 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
1623 GrGlyph* glyph;
1624 if (regenerateGlyphs) {
1625 // Get the id from the old glyph, and use the new strike to lookup
1626 // the glyph.
1627 glyph = blob->fGlyphs[glyphOffset];
1628 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
1629 scaler);
1630 }
1631 glyph = blob->fGlyphs[glyphOffset];
joshualitt1d89e8d2015-04-01 12:40:54 -07001632 SkASSERT(glyph);
1633
1634 if (!fFontCache->hasGlyph(glyph) &&
1635 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
1636 this->flush(batchTarget, &drawInfo, instancesToFlush,
1637 maxInstancesPerDraw);
1638 this->initDraw(batchTarget, gp, pipeline);
1639 instancesToFlush = 0;
1640 brokenRun = glyphIdx > 0;
1641
joshualittae32c102015-04-21 09:37:57 -07001642 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
1643 glyph,
joshualitt1d89e8d2015-04-01 12:40:54 -07001644 scaler);
1645 SkASSERT(success);
1646 }
joshualittb4c507e2015-04-08 08:07:59 -07001647 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
1648 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -07001649
1650 // Texture coords are the last vertex attribute so we get a pointer to the
1651 // first one and then map with stride in regenerateTextureCoords
1652 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1653 vertex += info.fVertexStartIndex;
1654 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1655 vertex += vertexStride - sizeof(SkIPoint16);
1656
1657 this->regenerateTextureCoords(glyph, vertex, vertexStride);
1658 }
1659
1660 if (regenerateColors) {
1661 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1662 vertex += info.fVertexStartIndex;
1663 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
1664 this->regenerateColors(vertex, vertexStride, args.fColor);
1665 }
1666
joshualitt2a0e9f32015-04-13 06:12:21 -07001667 if (regeneratePositions) {
1668 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
1669 vertex += info.fVertexStartIndex;
1670 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
1671 SkScalar transX = args.fTransX;
1672 SkScalar transY = args.fTransY;
1673 this->regeneratePositions(vertex, vertexStride, transX, transY);
1674 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001675 instancesToFlush++;
1676 }
1677
joshualitt2a0e9f32015-04-13 06:12:21 -07001678 // We my have changed the color so update it here
1679 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -07001680 if (regenerateTextureCoords) {
joshualittae32c102015-04-21 09:37:57 -07001681 if (regenerateGlyphs) {
1682 run.fStrike.reset(SkRef(strike));
1683 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001684 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
1685 fFontCache->atlasGeneration(fMaskFormat);
1686 }
1687 } else {
1688 instancesToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -07001689
1690 // set use tokens for all of the glyphs in our subrun. This is only valid if we
1691 // have a valid atlas generation
1692 fFontCache->setUseTokenBulk(info.fBulkUseToken,
1693 batchTarget->currentToken(),
1694 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -07001695 }
1696
1697 // now copy all vertices
1698 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
1699 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
1700
1701 currVertex += byteCount;
1702 }
joshualitt25ba7ea2015-04-21 07:49:49 -07001703 // Make sure to attach the last cache if applicable
1704 if (cache) {
1705 SkGlyphCache::AttachCache(cache);
1706 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001707 this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDraw);
1708 }
1709
joshualittad802c62015-04-15 05:31:57 -07001710 // The minimum number of Geometry we will try to allocate.
1711 static const int kMinAllocated = 32;
1712
1713 // Total number of Geometry this Batch owns
1714 int instanceCount() const { return fInstanceCount; }
1715 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
1716
1717 // to avoid even the initial copy of the struct, we have a getter for the first item which
1718 // is used to seed the batch with its initial geometry. After seeding, the client should call
1719 // init() so the Batch can initialize itself
1720 Geometry& geometry() { return fGeoData[0]; }
1721 void init() {
1722 fBatch.fColor = fGeoData[0].fColor;
1723 fBatch.fViewMatrix = fGeoData[0].fBlob->fViewMatrix;
1724 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001725
1726private:
joshualitt9bd2daf2015-04-17 09:30:06 -07001727 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
joshualitt1d89e8d2015-04-01 12:40:54 -07001728 : fMaskFormat(maskFormat)
1729 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
joshualitt9bd2daf2015-04-17 09:30:06 -07001730 , fFontCache(fontCache)
1731 , fUseDistanceFields(false) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001732 this->initClassID<BitmapTextBatch>();
joshualitt1d89e8d2015-04-01 12:40:54 -07001733 fBatch.fNumGlyphs = glyphCount;
joshualittad802c62015-04-15 05:31:57 -07001734 fInstanceCount = 1;
1735 fAllocatedCount = kMinAllocated;
1736 }
1737
joshualitt9bd2daf2015-04-17 09:30:06 -07001738 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
1739 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
1740 bool useLCDText, bool useBGR, float gamma)
1741 : fMaskFormat(maskFormat)
1742 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
1743 , fFontCache(fontCache)
1744 , fDistanceAdjustTable(SkRef(distanceAdjustTable))
1745 , fFilteredColor(filteredColor)
1746 , fUseDistanceFields(true)
1747 , fUseLCDText(useLCDText)
1748 , fUseBGR(useBGR)
1749 , fGamma(gamma) {
1750 this->initClassID<BitmapTextBatch>();
1751 fBatch.fNumGlyphs = glyphCount;
1752 fInstanceCount = 1;
1753 fAllocatedCount = kMinAllocated;
1754 SkASSERT(fMaskFormat == kA8_GrMaskFormat);
1755 }
1756
joshualittad802c62015-04-15 05:31:57 -07001757 ~BitmapTextBatch() {
1758 for (int i = 0; i < fInstanceCount; i++) {
1759 fGeoData[i].fBlob->unref();
1760 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001761 }
1762
1763 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
1764 int width = glyph->fBounds.width();
1765 int height = glyph->fBounds.height();
joshualitt1d89e8d2015-04-01 12:40:54 -07001766
joshualitt9bd2daf2015-04-17 09:30:06 -07001767 int u0, v0, u1, v1;
1768 if (fUseDistanceFields) {
1769 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
1770 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
1771 u1 = u0 + width - 2 * SK_DistanceFieldInset;
1772 v1 = v0 + height - 2 * SK_DistanceFieldInset;
1773 } else {
1774 u0 = glyph->fAtlasLocation.fX;
1775 v0 = glyph->fAtlasLocation.fY;
1776 u1 = u0 + width;
1777 v1 = v0 + height;
1778 }
1779
joshualitt1d89e8d2015-04-01 12:40:54 -07001780 SkIPoint16* textureCoords;
1781 // V0
1782 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1783 textureCoords->set(u0, v0);
1784 vertex += vertexStride;
1785
1786 // V1
1787 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1788 textureCoords->set(u0, v1);
1789 vertex += vertexStride;
1790
1791 // V2
1792 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1793 textureCoords->set(u1, v1);
1794 vertex += vertexStride;
1795
1796 // V3
1797 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
1798 textureCoords->set(u1, v0);
1799 }
1800
1801 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1802 for (int i = 0; i < kVerticesPerGlyph; i++) {
1803 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1804 *vcolor = color;
1805 vertex += vertexStride;
1806 }
1807 }
1808
joshualitt2a0e9f32015-04-13 06:12:21 -07001809 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1810 SkScalar transY) {
1811 for (int i = 0; i < kVerticesPerGlyph; i++) {
1812 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1813 point->fX += transX;
1814 point->fY += transY;
1815 vertex += vertexStride;
1816 }
1817 }
1818
joshualitt1d89e8d2015-04-01 12:40:54 -07001819 void initDraw(GrBatchTarget* batchTarget,
1820 const GrGeometryProcessor* gp,
1821 const GrPipeline* pipeline) {
1822 batchTarget->initDraw(gp, pipeline);
1823
1824 // TODO remove this when batch is everywhere
1825 GrPipelineInfo init;
1826 init.fColorIgnored = fBatch.fColorIgnored;
1827 init.fOverrideColor = GrColor_ILLEGAL;
1828 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1829 init.fUsesLocalCoords = this->usesLocalCoords();
1830 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1831 }
1832
1833 void flush(GrBatchTarget* batchTarget,
1834 GrDrawTarget::DrawInfo* drawInfo,
1835 int instanceCount,
1836 int maxInstancesPerDraw) {
1837 while (instanceCount) {
1838 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1839 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verticesPerInstance());
1840 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indicesPerInstance());
1841
1842 batchTarget->draw(*drawInfo);
1843
1844 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexCount());
1845 instanceCount -= drawInfo->instanceCount();
1846 }
1847 }
1848
1849 GrColor color() const { return fBatch.fColor; }
1850 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1851 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1852 int numGlyphs() const { return fBatch.fNumGlyphs; }
1853
1854 bool onCombineIfPossible(GrBatch* t) override {
1855 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1856
joshualitt9bd2daf2015-04-17 09:30:06 -07001857 if (fUseDistanceFields != that->fUseDistanceFields) {
joshualitt1d89e8d2015-04-01 12:40:54 -07001858 return false;
1859 }
1860
joshualitt9bd2daf2015-04-17 09:30:06 -07001861 if (!fUseDistanceFields) {
1862 // Bitmap Text
1863 if (fMaskFormat != that->fMaskFormat) {
1864 return false;
1865 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001866
joshualitt9bd2daf2015-04-17 09:30:06 -07001867 // TODO we can often batch across LCD text if we have dual source blending and don't
1868 // have to use the blend constant
1869 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1870 return false;
1871 }
1872
1873 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1874 return false;
1875 }
1876 } else {
1877 // Distance Fields
1878 SkASSERT(this->fMaskFormat == that->fMaskFormat &&
1879 this->fMaskFormat == kA8_GrMaskFormat);
1880
1881 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1882 return false;
1883 }
1884
1885 if (fFilteredColor != that->fFilteredColor) {
1886 return false;
1887 }
1888
1889 if (fUseLCDText != that->fUseLCDText) {
1890 return false;
1891 }
1892
1893 if (fUseBGR != that->fUseBGR) {
1894 return false;
1895 }
1896
1897 if (fGamma != that->fGamma) {
1898 return false;
1899 }
1900
1901 // TODO see note above
1902 if (fUseLCDText && this->color() != that->color()) {
1903 return false;
1904 }
joshualitt1d89e8d2015-04-01 12:40:54 -07001905 }
1906
1907 fBatch.fNumGlyphs += that->numGlyphs();
joshualittad802c62015-04-15 05:31:57 -07001908
1909 // copy that->geoData(). We do this manually for performance reasons
1910 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
1911 int otherInstanceCount = that->instanceCount();
1912 int allocSize = otherInstanceCount + fInstanceCount;
1913 if (allocSize > fAllocatedCount) {
1914 while (allocSize > fAllocatedCount) {
1915 fAllocatedCount = fAllocatedCount << 1;
1916 }
1917 fGeoData.realloc(fAllocatedCount);
1918 }
1919
1920 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
1921 otherInstanceCount * sizeof(Geometry));
1922 int total = fInstanceCount + otherInstanceCount;
1923 for (int i = fInstanceCount; i < total; i++) {
1924 fGeoData[i].fBlob->ref();
1925 }
1926 fInstanceCount = total;
joshualitt1d89e8d2015-04-01 12:40:54 -07001927 return true;
1928 }
1929
joshualitt9bd2daf2015-04-17 09:30:06 -07001930 // TODO just use class params
1931 // TODO trying to figure out why lcd is so whack
1932 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
1933 GrColor color, GrTexture* texture) {
1934 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
1935
1936 // set up any flags
1937 uint32_t flags = 0;
1938 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1939 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
1940 flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
1941 kRectToRect_DistanceFieldEffectFlag : 0;
1942 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
1943
1944 // see if we need to create a new effect
1945 if (fUseLCDText) {
1946 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
1947
1948 float redCorrection =
1949 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
1950 float greenCorrection =
1951 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
1952 float blueCorrection =
1953 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
1954 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
1955 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
1956 greenCorrection,
1957 blueCorrection);
1958
1959 return GrDistanceFieldLCDTextGeoProc::Create(color,
1960 viewMatrix,
1961 texture,
1962 params,
1963 widthAdjust,
1964 flags);
1965 } else {
1966 flags |= kColorAttr_DistanceFieldEffectFlag;
1967 bool opaque = GrColorIsOpaque(color);
1968#ifdef SK_GAMMA_APPLY_TO_A8
1969 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
1970 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
1971 return GrDistanceFieldA8TextGeoProc::Create(color,
1972 viewMatrix,
1973 texture,
1974 params,
1975 correction,
1976 flags,
1977 opaque);
1978#else
1979 return GrDistanceFieldA8TextGeoProc::Create(color,
1980 viewMatrix,
1981 texture,
1982 params,
1983 flags,
1984 opaque);
1985#endif
1986 }
1987
1988 }
1989
joshualitt1d89e8d2015-04-01 12:40:54 -07001990 struct BatchTracker {
1991 GrColor fColor;
1992 SkMatrix fViewMatrix;
1993 bool fUsesLocalCoords;
1994 bool fColorIgnored;
1995 bool fCoverageIgnored;
1996 int fNumGlyphs;
1997 };
1998
1999 BatchTracker fBatch;
joshualittad802c62015-04-15 05:31:57 -07002000 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
2001 int fInstanceCount;
2002 int fAllocatedCount;
joshualitt1d89e8d2015-04-01 12:40:54 -07002003 GrMaskFormat fMaskFormat;
2004 GrPixelConfig fPixelConfig;
2005 GrBatchFontCache* fFontCache;
joshualitt9bd2daf2015-04-17 09:30:06 -07002006
2007 // Distance field properties
2008 SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
2009 SkColor fFilteredColor;
2010 bool fUseDistanceFields;
2011 bool fUseLCDText;
2012 bool fUseBGR;
2013 float fGamma;
joshualitt1d89e8d2015-04-01 12:40:54 -07002014};
2015
joshualitt9a27e632015-04-06 10:53:36 -07002016void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
2017 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
2018 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
2019 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07002020
joshualitt9a27e632015-04-06 10:53:36 -07002021 size_t textLen = it.glyphCount() * sizeof(uint16_t);
2022 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07002023
joshualitt9a27e632015-04-06 10:53:36 -07002024 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07002025
joshualitt9a27e632015-04-06 10:53:36 -07002026 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
2027 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07002028 }
2029
joshualitt9a27e632015-04-06 10:53:36 -07002030 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
2031
2032 switch (it.positioning()) {
2033 case SkTextBlob::kDefault_Positioning:
2034 this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
2035 textLen, x + offset.x(), y + offset.y(), clipBounds);
2036 break;
2037 case SkTextBlob::kHorizontal_Positioning:
2038 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
2039 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
2040 clipBounds);
2041 break;
2042 case SkTextBlob::kFull_Positioning:
2043 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
2044 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
2045 break;
2046 }
2047}
2048
2049inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
2050 BitmapTextBlob* cacheBlob, int run, GrColor color,
joshualitt9bd2daf2015-04-17 09:30:06 -07002051 SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
joshualitt9a27e632015-04-06 10:53:36 -07002052 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
2053 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
2054 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
2055 if (0 == glyphCount) {
2056 continue;
2057 }
2058
2059 GrMaskFormat format = info.fMaskFormat;
joshualitt9bd2daf2015-04-17 09:30:06 -07002060 GrColor subRunColor;
2061 if (kARGB_GrMaskFormat == format) {
2062 uint8_t paintAlpha = skPaint.getAlpha();
2063 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
2064 } else {
2065 subRunColor = color;
2066 }
joshualitt9a27e632015-04-06 10:53:36 -07002067
joshualitt9bd2daf2015-04-17 09:30:06 -07002068 SkAutoTUnref<BitmapTextBatch> batch;
2069 if (info.fDrawAsDistanceFields) {
2070 SkColor filteredColor;
2071 SkColorFilter* colorFilter = skPaint.getColorFilter();
2072 if (colorFilter) {
2073 filteredColor = colorFilter->filterColor(skPaint.getColor());
2074 } else {
2075 filteredColor = skPaint.getColor();
2076 }
2077 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
2078 float gamma = fDeviceProperties.gamma();
2079 batch.reset(BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
2080 fDistanceAdjustTable, filteredColor,
2081 info.fUseLCDText, useBGR,
2082 gamma));
2083 } else {
2084 batch.reset(BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache()));
2085 }
joshualittad802c62015-04-15 05:31:57 -07002086 BitmapTextBatch::Geometry& geometry = batch->geometry();
2087 geometry.fBlob = SkRef(cacheBlob);
joshualitt9a27e632015-04-06 10:53:36 -07002088 geometry.fRun = run;
2089 geometry.fSubRun = subRun;
2090 geometry.fColor = subRunColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07002091 geometry.fTransX = transX;
2092 geometry.fTransY = transY;
joshualittad802c62015-04-15 05:31:57 -07002093 batch->init();
joshualitt9a27e632015-04-06 10:53:36 -07002094
2095 target->drawBatch(pipelineBuilder, batch, &cacheBlob->fRuns[run].fVertexBounds);
2096 }
2097}
2098
2099inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
joshualitt2a0e9f32015-04-13 06:12:21 -07002100 const GrPaint& grPaint, const GrClip& clip,
2101 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002102 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07002103 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
2104 bigGlyph.fVx += SkScalarTruncToInt(transX);
2105 bigGlyph.fVy += SkScalarTruncToInt(transY);
joshualitt1d89e8d2015-04-01 12:40:54 -07002106 SkMatrix translate;
joshualitt2a0e9f32015-04-13 06:12:21 -07002107 translate.setTranslate(SkIntToScalar(bigGlyph.fVx),
2108 SkIntToScalar(bigGlyph.fVy));
joshualitt1d89e8d2015-04-01 12:40:54 -07002109 SkPath tmpPath(bigGlyph.fPath);
2110 tmpPath.transform(translate);
2111 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt9a27e632015-04-06 10:53:36 -07002112 fContext->drawPath(rt, clip, grPaint, SkMatrix::I(), tmpPath, strokeInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07002113 }
2114}
joshualitt9a27e632015-04-06 10:53:36 -07002115
2116void GrAtlasTextContext::flush(GrDrawTarget* target,
2117 const SkTextBlob* blob,
2118 BitmapTextBlob* cacheBlob,
2119 GrRenderTarget* rt,
2120 const SkPaint& skPaint,
2121 const GrPaint& grPaint,
2122 SkDrawFilter* drawFilter,
2123 const GrClip& clip,
2124 const SkMatrix& viewMatrix,
2125 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07002126 SkScalar x, SkScalar y,
2127 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07002128 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
2129 // it as paths
2130 GrPipelineBuilder pipelineBuilder;
2131 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2132
2133 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002134
2135 SkTextBlob::RunIterator it(blob);
2136 for (int run = 0; !it.done(); it.next(), run++) {
2137 if (cacheBlob->fRuns[run].fDrawAsPaths) {
2138 this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
2139 continue;
2140 }
joshualitt2a0e9f32015-04-13 06:12:21 -07002141 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
joshualitt9bd2daf2015-04-17 09:30:06 -07002142 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002143 }
2144
2145 // Now flush big glyphs
joshualitt2a0e9f32015-04-13 06:12:21 -07002146 this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, transX, transY);
joshualitt9a27e632015-04-06 10:53:36 -07002147}
2148
2149void GrAtlasTextContext::flush(GrDrawTarget* target,
2150 BitmapTextBlob* cacheBlob,
2151 GrRenderTarget* rt,
2152 const SkPaint& skPaint,
2153 const GrPaint& grPaint,
joshualitt9bd2daf2015-04-17 09:30:06 -07002154 const GrClip& clip) {
joshualitt9a27e632015-04-06 10:53:36 -07002155 GrPipelineBuilder pipelineBuilder;
2156 pipelineBuilder.setFromPaint(grPaint, rt, clip);
2157
2158 GrColor color = grPaint.getColor();
joshualitt9a27e632015-04-06 10:53:36 -07002159 for (int run = 0; run < cacheBlob->fRunCount; run++) {
joshualitt9bd2daf2015-04-17 09:30:06 -07002160 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
joshualitt9a27e632015-04-06 10:53:36 -07002161 }
2162
2163 // Now flush big glyphs
joshualitt2a0e9f32015-04-13 06:12:21 -07002164 this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, 0, 0);
joshualitt9a27e632015-04-06 10:53:36 -07002165}