blob: 3a1bc31fa41f8cce9f8032a4b0e6f8bedadb7303 [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
9#include "GrAtlas.h"
10#include "GrBatch.h"
11#include "GrBatchFontCache.h"
12#include "GrBatchTarget.h"
13#include "GrDefaultGeoProcFactory.h"
14#include "GrDrawTarget.h"
15#include "GrFontScaler.h"
16#include "GrIndexBuffer.h"
17#include "GrStrokeInfo.h"
joshualittb7133be2015-04-08 09:08:31 -070018#include "GrTextBlobCache.h"
joshualitt1d89e8d2015-04-01 12:40:54 -070019#include "GrTexturePriv.h"
20
21#include "SkAutoKern.h"
22#include "SkColorPriv.h"
23#include "SkDraw.h"
24#include "SkDrawFilter.h"
25#include "SkDrawProcs.h"
26#include "SkGlyphCache.h"
27#include "SkGpuDevice.h"
28#include "SkGr.h"
29#include "SkPath.h"
30#include "SkRTConf.h"
31#include "SkStrokeRec.h"
32#include "SkTextBlob.h"
33#include "SkTextMapStateProc.h"
34
35#include "effects/GrBitmapTextGeoProc.h"
36#include "effects/GrSimpleTextureEffect.h"
37
38namespace {
39static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
40
41// position + local coord
42static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
43
44static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
45
46static const int kVerticesPerGlyph = 4;
47static const int kIndicesPerGlyph = 6;
48
49static size_t get_vertex_stride(GrMaskFormat maskFormat) {
50 switch (maskFormat) {
51 case kA8_GrMaskFormat:
52 return kGrayTextVASize;
53 case kARGB_GrMaskFormat:
54 return kColorTextVASize;
55 default:
56 return kLCDTextVASize;
57 }
58}
59
60};
61
62// TODO
63// More tests
64// move to SkCache
65// handle textblobs where the whole run is larger than the cache size
66// TODO implement micro speedy hash map for fast refing of glyphs
67
joshualittdbd35932015-04-02 09:19:04 -070068GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
69 SkGpuDevice* gpuDevice,
70 const SkDeviceProperties& properties)
joshualittb7133be2015-04-08 09:08:31 -070071 : INHERITED(context, gpuDevice, properties) {
72 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
73 // vertexStride
74 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
75 vertex_attribute_changed);
joshualitt1d89e8d2015-04-01 12:40:54 -070076 fCurrStrike = NULL;
joshualittb7133be2015-04-08 09:08:31 -070077 fCache = context->getTextBlobCache();
joshualitt1d89e8d2015-04-01 12:40:54 -070078}
79
joshualittdbd35932015-04-02 09:19:04 -070080GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
81 SkGpuDevice* gpuDevice,
82 const SkDeviceProperties& props) {
83 return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props));
joshualitt1d89e8d2015-04-01 12:40:54 -070084}
85
joshualittdbd35932015-04-02 09:19:04 -070086bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
87 const GrClip&,
88 const GrPaint&,
89 const SkPaint& skPaint,
90 const SkMatrix& viewMatrix) {
joshualitt1d89e8d2015-04-01 12:40:54 -070091 return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
92}
93
joshualitt2a0e9f32015-04-13 06:12:21 -070094bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
95 const BitmapTextBlob& blob, const SkPaint& paint,
joshualittdbd35932015-04-02 09:19:04 -070096 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
joshualitt2a0e9f32015-04-13 06:12:21 -070097 // Color can affect the mask
98 // TODO we can adjust the color within specific gamma slots
99 if (blob.fColor != paint.getColor()) {
100 return true;
101 }
102
103 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
104 return true;
105 }
106
107 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
108 return true;
109 }
110
111 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
112 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
113 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
114 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
115 return true;
116 }
117
118 // We can update the positions in the cachedtextblobs without regenerating the whole blob, but
119 // only for integer translations.
120 // This cool bit of math will determine the necessary translation to apply to the already
121 // generated vertex coordinates to move them to the correct position
122 SkScalar transX = viewMatrix.getTranslateX() +
123 viewMatrix.getScaleX() * (x - blob.fX) +
124 viewMatrix.getSkewX() * (y - blob.fY) -
125 blob.fViewMatrix.getTranslateX();
126 SkScalar transY = viewMatrix.getTranslateY() +
127 viewMatrix.getSkewY() * (x - blob.fX) +
128 viewMatrix.getScaleY() * (y - blob.fY) -
129 blob.fViewMatrix.getTranslateY();
130 if (SkScalarFraction(transX) > SK_ScalarNearlyZero ||
131 SkScalarFraction(transY) > SK_ScalarNearlyZero) {
132 return true;
133 }
134
135#ifdef SK_DEBUG
136 static const SkScalar kMinDiscernableTranslation = 0.0625;
137 // As a safeguard when debugging, we store the total error across all translations and print if
138 // the error becomes discernable. This is pretty unlikely to occur given the tight bounds above
139 // on translation
140 blob.fTotalXError += SkScalarAbs(SkScalarFraction(transX));
141 blob.fTotalYError += SkScalarAbs(SkScalarFraction(transY));
142 if (blob.fTotalXError > kMinDiscernableTranslation ||
143 blob.fTotalYError > kMinDiscernableTranslation) {
144 SkDebugf("Exceeding error threshold for bitmap text translation");
145 }
146#endif
147 (*outTransX) = transX;
148 (*outTransY) = transY;
149 return false;
joshualitt1d89e8d2015-04-01 12:40:54 -0700150}
151
152
joshualittdbd35932015-04-02 09:19:04 -0700153inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
154 const SkPaint& skPaint,
155 const SkMatrix& viewMatrix) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700156 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, &viewMatrix, false);
157 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
158 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
159}
160
joshualittdbd35932015-04-02 09:19:04 -0700161void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
162 const SkPaint& skPaint, const SkMatrix& viewMatrix,
163 const SkTextBlob* blob, SkScalar x, SkScalar y,
164 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
joshualittb7133be2015-04-08 09:08:31 -0700165 uint32_t uniqueID = blob->uniqueID();
joshualitt2a0e9f32015-04-13 06:12:21 -0700166 SkAutoTUnref<BitmapTextBlob> cacheBlob;
167 // TODO start caching these, mix bits into the key
168 bool canCache = !(skPaint.getPathEffect() ||
169 skPaint.getMaskFilter() ||
170 skPaint.getColorFilter() ||
171 skPaint.getStyle() != SkPaint::kFill_Style ||
172 drawFilter);
173
174 if (canCache) {
175 cacheBlob.reset(SkSafeRef(fCache->find(uniqueID)));
176 }
177
joshualitt1d89e8d2015-04-01 12:40:54 -0700178 SkIRect clipRect;
179 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
180
joshualitt2a0e9f32015-04-13 06:12:21 -0700181 SkScalar transX = 0.f;
182 SkScalar transY = 0.f;
183
joshualittb7133be2015-04-08 09:08:31 -0700184 if (cacheBlob) {
joshualitt2a0e9f32015-04-13 06:12:21 -0700185 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, viewMatrix, x, y)) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700186 // We have to remake the blob because changes may invalidate our masks.
187 // TODO we could probably get away reuse most of the time if the pointer is unique,
188 // but we'd have to clear the subrun information
joshualittb7133be2015-04-08 09:08:31 -0700189 fCache->remove(cacheBlob);
joshualitt2a0e9f32015-04-13 06:12:21 -0700190 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, kGrayTextVASize)));
joshualitt1d89e8d2015-04-01 12:40:54 -0700191 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter,
192 clipRect);
joshualittb7133be2015-04-08 09:08:31 -0700193 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700194 // If we can reuse the blob, then make sure we update the blob's viewmatrix and x/y
195 // offsets to reflect the results of any translations we may apply in generateGeometry
196 cacheBlob->fViewMatrix = viewMatrix;
197 cacheBlob->fX = x;
198 cacheBlob->fY = y;
joshualittb7133be2015-04-08 09:08:31 -0700199 fCache->makeMRU(cacheBlob);
joshualitt1d89e8d2015-04-01 12:40:54 -0700200 }
201 } else {
joshualitt2a0e9f32015-04-13 06:12:21 -0700202 if (canCache) {
203 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, kGrayTextVASize)));
204 } else {
205 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
206 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700207 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter, clipRect);
208 }
209
210 // Though for the time being runs in the textblob can override the paint, they only touch font
211 // info.
212 GrPaint grPaint;
213 SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint);
214
joshualitt9a27e632015-04-06 10:53:36 -0700215 this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
joshualitt2a0e9f32015-04-13 06:12:21 -0700216 clip, viewMatrix, clipBounds, x, y, transX, transY);
joshualitt1d89e8d2015-04-01 12:40:54 -0700217}
218
joshualittdbd35932015-04-02 09:19:04 -0700219void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
220 const SkPaint& skPaint, const SkMatrix& viewMatrix,
221 const SkTextBlob* blob, SkScalar x, SkScalar y,
222 SkDrawFilter* drawFilter, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700223 cacheBlob->fViewMatrix = viewMatrix;
224 cacheBlob->fX = x;
225 cacheBlob->fY = y;
joshualitt2a0e9f32015-04-13 06:12:21 -0700226 cacheBlob->fColor = skPaint.getColor();
joshualitt1d89e8d2015-04-01 12:40:54 -0700227 cacheBlob->fStyle = skPaint.getStyle();
228
229 // Regenerate textblob
230 SkPaint runPaint = skPaint;
231 SkTextBlob::RunIterator it(blob);
232 for (int run = 0; !it.done(); it.next(), run++) {
233 int glyphCount = it.glyphCount();
234 size_t textLen = glyphCount * sizeof(uint16_t);
235 const SkPoint& offset = it.offset();
236 // applyFontToPaint() always overwrites the exact same attributes,
237 // so it is safe to not re-seed the paint for this reason.
238 it.applyFontToPaint(&runPaint);
239
240 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
241 // A false return from filter() means we should abort the current draw.
242 runPaint = skPaint;
243 continue;
244 }
245
246 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
247
248 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, viewMatrix);
249
250 // setup vertex / glyphIndex for the new run
251 if (run > 0) {
252 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
253 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
254
255 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
256 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
257
258 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
259 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
260 }
261
joshualitt2a0e9f32015-04-13 06:12:21 -0700262 if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
joshualitt9a27e632015-04-06 10:53:36 -0700263 cacheBlob->fRuns[run].fDrawAsPaths = true;
264 continue;
265 }
266 cacheBlob->fRuns[run].fDrawAsPaths = false;
267
joshualitt1d89e8d2015-04-01 12:40:54 -0700268 switch (it.positioning()) {
269 case SkTextBlob::kDefault_Positioning:
270 this->internalDrawText(cacheBlob, run, cache, runPaint, viewMatrix,
271 (const char *)it.glyphs(), textLen,
272 x + offset.x(), y + offset.y(), clipRect);
273 break;
274 case SkTextBlob::kHorizontal_Positioning:
275 this->internalDrawPosText(cacheBlob, run, cache, runPaint, viewMatrix,
276 (const char*)it.glyphs(), textLen, it.pos(), 1,
277 SkPoint::Make(x, y + offset.y()), clipRect);
278 break;
279 case SkTextBlob::kFull_Positioning:
280 this->internalDrawPosText(cacheBlob, run, cache, runPaint, viewMatrix,
281 (const char*)it.glyphs(), textLen, it.pos(), 2,
282 SkPoint::Make(x, y), clipRect);
283 break;
284 }
285
286 if (drawFilter) {
287 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
288 runPaint = skPaint;
289 }
290
291 SkGlyphCache::AttachCache(cache);
292 }
293}
294
joshualittdbd35932015-04-02 09:19:04 -0700295void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
296 const GrPaint& paint, const SkPaint& skPaint,
297 const SkMatrix& viewMatrix,
298 const char text[], size_t byteLength,
299 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700300 int glyphCount = skPaint.countText(text, byteLength);
joshualittb7133be2015-04-08 09:08:31 -0700301 SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
joshualitt1d89e8d2015-04-01 12:40:54 -0700302 blob->fViewMatrix = viewMatrix;
303 blob->fX = x;
304 blob->fY = y;
305 blob->fStyle = skPaint.getStyle();
306
307 SkIRect clipRect;
308 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
309
310 // setup cache
311 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
312 this->internalDrawText(blob, 0, cache, skPaint, viewMatrix, text, byteLength, x, y, clipRect);
313 SkGlyphCache::AttachCache(cache);
314
joshualitt9a27e632015-04-06 10:53:36 -0700315 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700316}
317
joshualittdbd35932015-04-02 09:19:04 -0700318void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
319 SkGlyphCache* cache, const SkPaint& skPaint,
320 const SkMatrix& viewMatrix,
321 const char text[], size_t byteLength,
322 SkScalar x, SkScalar y, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700323 SkASSERT(byteLength == 0 || text != NULL);
324
325 // nothing to draw
326 if (text == NULL || byteLength == 0) {
327 return;
328 }
329
330 fCurrStrike = NULL;
331 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
332
333 // Get GrFontScaler from cache
334 GrFontScaler* fontScaler = GetGrFontScaler(cache);
335
336 // transform our starting point
337 {
338 SkPoint loc;
339 viewMatrix.mapXY(x, y, &loc);
340 x = loc.fX;
341 y = loc.fY;
342 }
343
344 // need to measure first
345 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
346 SkVector stopVector;
347 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
348
349 SkScalar stopX = stopVector.fX;
350 SkScalar stopY = stopVector.fY;
351
352 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
353 stopX = SkScalarHalf(stopX);
354 stopY = SkScalarHalf(stopY);
355 }
356 x -= stopX;
357 y -= stopY;
358 }
359
360 const char* stop = text + byteLength;
361
362 SkAutoKern autokern;
363
364 SkFixed fxMask = ~0;
365 SkFixed fyMask = ~0;
366 SkScalar halfSampleX, halfSampleY;
367 if (cache->isSubpixel()) {
368 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
369 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
370 if (kX_SkAxisAlignment == baseline) {
371 fyMask = 0;
372 halfSampleY = SK_ScalarHalf;
373 } else if (kY_SkAxisAlignment == baseline) {
374 fxMask = 0;
375 halfSampleX = SK_ScalarHalf;
376 }
377 } else {
378 halfSampleX = halfSampleY = SK_ScalarHalf;
379 }
380
381 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
382 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
383
384 while (text < stop) {
385 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
386
387 fx += autokern.adjust(glyph);
388
389 if (glyph.fWidth) {
390 this->appendGlyph(blob,
391 runIndex,
392 GrGlyph::Pack(glyph.getGlyphID(),
393 glyph.getSubXFixed(),
394 glyph.getSubYFixed(),
395 GrGlyph::kCoverage_MaskStyle),
396 Sk48Dot16FloorToInt(fx),
397 Sk48Dot16FloorToInt(fy),
joshualitteef5b3e2015-04-03 08:07:26 -0700398 skPaint.getColor(),
joshualitt1d89e8d2015-04-01 12:40:54 -0700399 fontScaler,
400 clipRect);
401 }
402
403 fx += glyph.fAdvanceX;
404 fy += glyph.fAdvanceY;
405 }
406}
407
joshualittdbd35932015-04-02 09:19:04 -0700408void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
409 const GrPaint& paint, const SkPaint& skPaint,
410 const SkMatrix& viewMatrix,
411 const char text[], size_t byteLength,
412 const SkScalar pos[], int scalarsPerPosition,
413 const SkPoint& offset, const SkIRect& regionClipBounds) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700414 int glyphCount = skPaint.countText(text, byteLength);
joshualittb7133be2015-04-08 09:08:31 -0700415 SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
joshualitt1d89e8d2015-04-01 12:40:54 -0700416 blob->fStyle = skPaint.getStyle();
417 blob->fViewMatrix = viewMatrix;
418
419 SkIRect clipRect;
420 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
421
422 // setup cache
423 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
424 this->internalDrawPosText(blob, 0, cache, skPaint, viewMatrix, text, byteLength, pos,
425 scalarsPerPosition, offset, clipRect);
426 SkGlyphCache::AttachCache(cache);
427
joshualitt9a27e632015-04-06 10:53:36 -0700428 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
joshualitt1d89e8d2015-04-01 12:40:54 -0700429}
430
joshualittdbd35932015-04-02 09:19:04 -0700431void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
432 SkGlyphCache* cache, const SkPaint& skPaint,
433 const SkMatrix& viewMatrix,
434 const char text[], size_t byteLength,
435 const SkScalar pos[], int scalarsPerPosition,
436 const SkPoint& offset, const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700437 SkASSERT(byteLength == 0 || text != NULL);
438 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
439
440 // nothing to draw
441 if (text == NULL || byteLength == 0) {
442 return;
443 }
444
445 fCurrStrike = NULL;
446 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
447
448 // Get GrFontScaler from cache
449 GrFontScaler* fontScaler = GetGrFontScaler(cache);
450
451 const char* stop = text + byteLength;
452 SkTextAlignProc alignProc(skPaint.getTextAlign());
453 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
454
455 if (cache->isSubpixel()) {
456 // maybe we should skip the rounding if linearText is set
457 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
458
459 SkFixed fxMask = ~0;
460 SkFixed fyMask = ~0;
461 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
462 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
463 if (kX_SkAxisAlignment == baseline) {
464 fyMask = 0;
465 halfSampleY = SK_ScalarHalf;
466 } else if (kY_SkAxisAlignment == baseline) {
467 fxMask = 0;
468 halfSampleX = SK_ScalarHalf;
469 }
470
471 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
472 while (text < stop) {
473 SkPoint tmsLoc;
474 tmsProc(pos, &tmsLoc);
475 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
476 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
477
478 const SkGlyph& glyph = glyphCacheProc(cache, &text,
479 fx & fxMask, fy & fyMask);
480
481 if (glyph.fWidth) {
482 this->appendGlyph(blob,
483 runIndex,
484 GrGlyph::Pack(glyph.getGlyphID(),
485 glyph.getSubXFixed(),
486 glyph.getSubYFixed(),
487 GrGlyph::kCoverage_MaskStyle),
488 Sk48Dot16FloorToInt(fx),
489 Sk48Dot16FloorToInt(fy),
joshualitteef5b3e2015-04-03 08:07:26 -0700490 skPaint.getColor(),
joshualitt1d89e8d2015-04-01 12:40:54 -0700491 fontScaler,
492 clipRect);
493 }
494 pos += scalarsPerPosition;
495 }
496 } else {
497 while (text < stop) {
498 const char* currentText = text;
499 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
500
501 if (metricGlyph.fWidth) {
502 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
503 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
504 SkPoint tmsLoc;
505 tmsProc(pos, &tmsLoc);
506 SkPoint alignLoc;
507 alignProc(tmsLoc, metricGlyph, &alignLoc);
508
509 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
510 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
511
512 // have to call again, now that we've been "aligned"
513 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
514 fx & fxMask, fy & fyMask);
515 // the assumption is that the metrics haven't changed
516 SkASSERT(prevAdvX == glyph.fAdvanceX);
517 SkASSERT(prevAdvY == glyph.fAdvanceY);
518 SkASSERT(glyph.fWidth);
519
520 this->appendGlyph(blob,
521 runIndex,
522 GrGlyph::Pack(glyph.getGlyphID(),
523 glyph.getSubXFixed(),
524 glyph.getSubYFixed(),
525 GrGlyph::kCoverage_MaskStyle),
526 Sk48Dot16FloorToInt(fx),
527 Sk48Dot16FloorToInt(fy),
joshualitteef5b3e2015-04-03 08:07:26 -0700528 skPaint.getColor(),
joshualitt1d89e8d2015-04-01 12:40:54 -0700529 fontScaler,
530 clipRect);
531 }
532 pos += scalarsPerPosition;
533 }
534 }
535 } else { // not subpixel
536
537 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
538 while (text < stop) {
539 // the last 2 parameters are ignored
540 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
541
542 if (glyph.fWidth) {
543 SkPoint tmsLoc;
544 tmsProc(pos, &tmsLoc);
545
546 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
547 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
548 this->appendGlyph(blob,
549 runIndex,
550 GrGlyph::Pack(glyph.getGlyphID(),
551 glyph.getSubXFixed(),
552 glyph.getSubYFixed(),
553 GrGlyph::kCoverage_MaskStyle),
554 Sk48Dot16FloorToInt(fx),
555 Sk48Dot16FloorToInt(fy),
joshualitteef5b3e2015-04-03 08:07:26 -0700556 skPaint.getColor(),
joshualitt1d89e8d2015-04-01 12:40:54 -0700557 fontScaler,
558 clipRect);
559 }
560 pos += scalarsPerPosition;
561 }
562 } else {
563 while (text < stop) {
564 // the last 2 parameters are ignored
565 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
566
567 if (glyph.fWidth) {
568 SkPoint tmsLoc;
569 tmsProc(pos, &tmsLoc);
570
571 SkPoint alignLoc;
572 alignProc(tmsLoc, glyph, &alignLoc);
573
574 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
575 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
576 this->appendGlyph(blob,
577 runIndex,
578 GrGlyph::Pack(glyph.getGlyphID(),
579 glyph.getSubXFixed(),
580 glyph.getSubYFixed(),
581 GrGlyph::kCoverage_MaskStyle),
582 Sk48Dot16FloorToInt(fx),
583 Sk48Dot16FloorToInt(fy),
joshualitteef5b3e2015-04-03 08:07:26 -0700584 skPaint.getColor(),
joshualitt1d89e8d2015-04-01 12:40:54 -0700585 fontScaler,
586 clipRect);
587 }
588 pos += scalarsPerPosition;
589 }
590 }
591 }
592}
593
joshualittdbd35932015-04-02 09:19:04 -0700594void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph::PackedID packed,
joshualitteef5b3e2015-04-03 08:07:26 -0700595 int vx, int vy, GrColor color, GrFontScaler* scaler,
joshualittdbd35932015-04-02 09:19:04 -0700596 const SkIRect& clipRect) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700597 if (NULL == fCurrStrike) {
598 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
599 }
600
601 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
602 if (NULL == glyph || glyph->fBounds.isEmpty()) {
603 return;
604 }
605
606 int x = vx + glyph->fBounds.fLeft;
607 int y = vy + glyph->fBounds.fTop;
608
609 // keep them as ints until we've done the clip-test
610 int width = glyph->fBounds.width();
611 int height = glyph->fBounds.height();
612
joshualitt2a0e9f32015-04-13 06:12:21 -0700613#if 0
614 // Not checking the clip bounds might introduce a performance regression. However, its not
615 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable
616 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
617 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
618 // TODO verify this
joshualitt1d89e8d2015-04-01 12:40:54 -0700619 // check if we clipped out
620 if (clipRect.quickReject(x, y, x + width, y + height)) {
621 return;
622 }
joshualitt2a0e9f32015-04-13 06:12:21 -0700623#endif
joshualitt1d89e8d2015-04-01 12:40:54 -0700624
625 // If the glyph is too large we fall back to paths
626 if (fCurrStrike->glyphTooLargeForAtlas(glyph)) {
627 if (NULL == glyph->fPath) {
628 SkPath* path = SkNEW(SkPath);
629 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
630 // flag the glyph as being dead?
631 SkDELETE(path);
632 return;
633 }
634 glyph->fPath = path;
635 }
636 SkASSERT(glyph->fPath);
637 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, vx, vy));
638 return;
639 }
640
641 Run& run = blob->fRuns[runIndex];
642
643 GrMaskFormat format = glyph->fMaskFormat;
644
645 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
646 if (run.fInitialized && subRun->fMaskFormat != format) {
647 PerSubRunInfo* newSubRun = &run.fSubRunInfo.push_back();
648 newSubRun->fGlyphStartIndex = subRun->fGlyphEndIndex;
649 newSubRun->fGlyphEndIndex = subRun->fGlyphEndIndex;
650
651 newSubRun->fVertexStartIndex = subRun->fVertexEndIndex;
652 newSubRun->fVertexEndIndex = subRun->fVertexEndIndex;
653
654 subRun = newSubRun;
655 }
656
657 run.fInitialized = true;
658 subRun->fMaskFormat = format;
659 blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed;
660
661 size_t vertexStride = get_vertex_stride(format);
662
663 SkRect r;
664 r.fLeft = SkIntToScalar(x);
665 r.fTop = SkIntToScalar(y);
666 r.fRight = r.fLeft + SkIntToScalar(width);
667 r.fBottom = r.fTop + SkIntToScalar(height);
668
669 run.fVertexBounds.joinNonEmptyArg(r);
joshualitt1d89e8d2015-04-01 12:40:54 -0700670 run.fColor = color;
671
672 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
673
674 // V0
675 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
676 position->set(r.fLeft, r.fTop);
677 if (kA8_GrMaskFormat == format) {
678 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
679 *colorPtr = color;
680 }
681 vertex += vertexStride;
joshualitt1d89e8d2015-04-01 12:40:54 -0700682 // V1
683 position = reinterpret_cast<SkPoint*>(vertex);
684 position->set(r.fLeft, r.fBottom);
685 if (kA8_GrMaskFormat == format) {
686 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
687 *colorPtr = color;
688 }
689 vertex += vertexStride;
690
691 // V2
692 position = reinterpret_cast<SkPoint*>(vertex);
693 position->set(r.fRight, r.fBottom);
694 if (kA8_GrMaskFormat == format) {
695 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
696 *colorPtr = color;
697 }
698 vertex += vertexStride;
699
700 // V3
701 position = reinterpret_cast<SkPoint*>(vertex);
702 position->set(r.fRight, r.fTop);
703 if (kA8_GrMaskFormat == format) {
704 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
705 *colorPtr = color;
706 }
707
708 subRun->fGlyphEndIndex++;
709 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
710}
711
712class BitmapTextBatch : public GrBatch {
713public:
joshualittdbd35932015-04-02 09:19:04 -0700714 typedef GrAtlasTextContext::BitmapTextBlob Blob;
joshualitt1d89e8d2015-04-01 12:40:54 -0700715 typedef Blob::Run Run;
716 typedef Run::SubRunInfo TextInfo;
717 struct Geometry {
718 Geometry() {}
719 Geometry(const Geometry& geometry)
720 : fBlob(SkRef(geometry.fBlob.get()))
721 , fRun(geometry.fRun)
722 , fSubRun(geometry.fSubRun)
joshualitt2a0e9f32015-04-13 06:12:21 -0700723 , fColor(geometry.fColor)
724 , fTransX(geometry.fTransX)
725 , fTransY(geometry.fTransY) {}
joshualitt1d89e8d2015-04-01 12:40:54 -0700726 SkAutoTUnref<Blob> fBlob;
727 int fRun;
728 int fSubRun;
729 GrColor fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -0700730 SkScalar fTransX;
731 SkScalar fTransY;
joshualitt1d89e8d2015-04-01 12:40:54 -0700732 };
733
joshualitt9a27e632015-04-06 10:53:36 -0700734 static GrBatch* Create(const Geometry& geometry, GrMaskFormat maskFormat,
joshualitt1d89e8d2015-04-01 12:40:54 -0700735 int glyphCount, GrBatchFontCache* fontCache) {
joshualitt9a27e632015-04-06 10:53:36 -0700736 return SkNEW_ARGS(BitmapTextBatch, (geometry, maskFormat, glyphCount, fontCache));
joshualitt1d89e8d2015-04-01 12:40:54 -0700737 }
738
739 const char* name() const override { return "BitmapTextBatch"; }
740
741 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
742 if (kARGB_GrMaskFormat == fMaskFormat) {
743 out->setUnknownFourComponents();
744 } else {
745 out->setKnownFourComponents(fBatch.fColor);
746 }
747 }
748
749 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
750 if (kARGB_GrMaskFormat != fMaskFormat) {
751 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
752 out->setUnknownSingleComponent();
753 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
754 out->setUnknownOpaqueFourComponents();
755 out->setUsingLCDCoverage();
756 } else {
757 out->setUnknownFourComponents();
758 out->setUsingLCDCoverage();
759 }
760 } else {
761 out->setKnownSingleComponent(0xff);
762 }
763 }
764
765 void initBatchTracker(const GrPipelineInfo& init) override {
766 // Handle any color overrides
767 if (init.fColorIgnored) {
768 fBatch.fColor = GrColor_ILLEGAL;
769 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
770 fBatch.fColor = init.fOverrideColor;
771 }
772
773 // setup batch properties
774 fBatch.fColorIgnored = init.fColorIgnored;
775 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
776 fBatch.fCoverageIgnored = init.fCoverageIgnored;
777 }
778
779 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
780 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
781 // TODO actually only invert if we don't have RGBA
782 SkMatrix localMatrix;
783 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
784 SkDebugf("Cannot invert viewmatrix\n");
785 return;
786 }
787
joshualitt62db8ba2015-04-09 08:22:37 -0700788 GrTexture* texture = fFontCache->getTexture(fMaskFormat);
789 if (!texture) {
790 SkDebugf("Could not allocate backing texture for atlas\n");
791 return;
792 }
793
joshualitt1d89e8d2015-04-01 12:40:54 -0700794 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitteef5b3e2015-04-03 08:07:26 -0700795
joshualitt1d89e8d2015-04-01 12:40:54 -0700796 // This will be ignored in the non A8 case
797 bool opaqueVertexColors = GrColorIsOpaque(this->color());
798 SkAutoTUnref<const GrGeometryProcessor> gp(
799 GrBitmapTextGeoProc::Create(this->color(),
joshualitt62db8ba2015-04-09 08:22:37 -0700800 texture,
joshualitt1d89e8d2015-04-01 12:40:54 -0700801 params,
802 fMaskFormat,
803 opaqueVertexColors,
804 localMatrix));
805
806 size_t vertexStride = gp->getVertexStride();
807 SkASSERT(vertexStride == get_vertex_stride(fMaskFormat));
808
809 this->initDraw(batchTarget, gp, pipeline);
810
811 int glyphCount = this->numGlyphs();
812 int instanceCount = fGeoData.count();
813 const GrVertexBuffer* vertexBuffer;
814 int firstVertex;
815
816 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
817 glyphCount * kVerticesPerGlyph,
818 &vertexBuffer,
819 &firstVertex);
820 if (!vertices) {
821 SkDebugf("Could not allocate vertices\n");
822 return;
823 }
824
825 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
826
827 // setup drawinfo
828 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
829 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
830
831 GrDrawTarget::DrawInfo drawInfo;
832 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
833 drawInfo.setStartVertex(0);
834 drawInfo.setStartIndex(0);
835 drawInfo.setVerticesPerInstance(kVerticesPerGlyph);
836 drawInfo.setIndicesPerInstance(kIndicesPerGlyph);
837 drawInfo.adjustStartVertex(firstVertex);
838 drawInfo.setVertexBuffer(vertexBuffer);
839 drawInfo.setIndexBuffer(quadIndexBuffer);
840
841 int instancesToFlush = 0;
842 for (int i = 0; i < instanceCount; i++) {
843 Geometry& args = fGeoData[i];
844 Blob* blob = args.fBlob;
845 Run& run = blob->fRuns[args.fRun];
846 TextInfo& info = run.fSubRunInfo[args.fSubRun];
847
848 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
849 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
850 bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
joshualitt2a0e9f32015-04-13 06:12:21 -0700851 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
joshualitt1d89e8d2015-04-01 12:40:54 -0700852 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
853
854 // We regenerate both texture coords and colors in the blob itself, and update the
855 // atlas generation. If we don't end up purging any unused plots, we can avoid
856 // regenerating the coords. We could take a finer grained approach to updating texture
857 // coords but its not clear if the extra bookkeeping would offset any gains.
858 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
859 // or coords as needed. One final note, if we have to break a run for an atlas eviction
860 // then we can't really trust the atlas has all of the correct data. Atlas evictions
861 // should be pretty rare, so we just always regenerate in those cases
joshualitt2a0e9f32015-04-13 06:12:21 -0700862 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
joshualitt1d89e8d2015-04-01 12:40:54 -0700863 // first regenerate texture coordinates / colors if need be
864 const SkDescriptor* desc = NULL;
865 SkGlyphCache* cache = NULL;
866 GrFontScaler* scaler = NULL;
867 GrBatchTextStrike* strike = NULL;
868 bool brokenRun = false;
869 if (regenerateTextureCoords) {
joshualittb4c507e2015-04-08 08:07:59 -0700870 info.fBulkUseToken.reset();
joshualitt1d89e8d2015-04-01 12:40:54 -0700871 desc = run.fDescriptor.getDesc();
872 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
873 scaler = GrTextContext::GetGrFontScaler(cache);
874 strike = fFontCache->getStrike(scaler);
875 }
876 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
877 GrGlyph::PackedID glyphID = blob->fGlyphIDs[glyphIdx + info.fGlyphStartIndex];
878
879 if (regenerateTextureCoords) {
880 // Upload the glyph only if needed
881 GrGlyph* glyph = strike->getGlyph(glyphID, scaler);
882 SkASSERT(glyph);
883
884 if (!fFontCache->hasGlyph(glyph) &&
885 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
886 this->flush(batchTarget, &drawInfo, instancesToFlush,
887 maxInstancesPerDraw);
888 this->initDraw(batchTarget, gp, pipeline);
889 instancesToFlush = 0;
890 brokenRun = glyphIdx > 0;
891
892 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget, glyph,
893 scaler);
894 SkASSERT(success);
895 }
joshualittb4c507e2015-04-08 08:07:59 -0700896 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
897 batchTarget->currentToken());
joshualitt1d89e8d2015-04-01 12:40:54 -0700898
899 // Texture coords are the last vertex attribute so we get a pointer to the
900 // first one and then map with stride in regenerateTextureCoords
901 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
902 vertex += info.fVertexStartIndex;
903 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
904 vertex += vertexStride - sizeof(SkIPoint16);
905
906 this->regenerateTextureCoords(glyph, vertex, vertexStride);
907 }
908
909 if (regenerateColors) {
910 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
911 vertex += info.fVertexStartIndex;
912 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
913 this->regenerateColors(vertex, vertexStride, args.fColor);
914 }
915
joshualitt2a0e9f32015-04-13 06:12:21 -0700916 if (regeneratePositions) {
917 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
918 vertex += info.fVertexStartIndex;
919 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
920 SkScalar transX = args.fTransX;
921 SkScalar transY = args.fTransY;
922 this->regeneratePositions(vertex, vertexStride, transX, transY);
923 }
joshualitt1d89e8d2015-04-01 12:40:54 -0700924 instancesToFlush++;
925 }
926
joshualitt2a0e9f32015-04-13 06:12:21 -0700927 // We my have changed the color so update it here
928 run.fColor = args.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -0700929 if (regenerateTextureCoords) {
930 SkGlyphCache::AttachCache(cache);
931 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
932 fFontCache->atlasGeneration(fMaskFormat);
933 }
934 } else {
935 instancesToFlush += glyphCount;
joshualittb4c507e2015-04-08 08:07:59 -0700936
937 // set use tokens for all of the glyphs in our subrun. This is only valid if we
938 // have a valid atlas generation
939 fFontCache->setUseTokenBulk(info.fBulkUseToken,
940 batchTarget->currentToken(),
941 fMaskFormat);
joshualitt1d89e8d2015-04-01 12:40:54 -0700942 }
943
944 // now copy all vertices
945 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
946 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
947
948 currVertex += byteCount;
949 }
950
951 this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDraw);
952 }
953
954 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
955
956private:
joshualitt9a27e632015-04-06 10:53:36 -0700957 BitmapTextBatch(const Geometry& geometry, GrMaskFormat maskFormat,
joshualitt1d89e8d2015-04-01 12:40:54 -0700958 int glyphCount, GrBatchFontCache* fontCache)
959 : fMaskFormat(maskFormat)
960 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
961 , fFontCache(fontCache) {
962 this->initClassID<BitmapTextBatch>();
963 fGeoData.push_back(geometry);
joshualitt9a27e632015-04-06 10:53:36 -0700964 fBatch.fColor = geometry.fColor;
joshualitt1d89e8d2015-04-01 12:40:54 -0700965 fBatch.fViewMatrix = geometry.fBlob->fViewMatrix;
966 fBatch.fNumGlyphs = glyphCount;
967 }
968
969 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
970 int width = glyph->fBounds.width();
971 int height = glyph->fBounds.height();
972 int u0 = glyph->fAtlasLocation.fX;
973 int v0 = glyph->fAtlasLocation.fY;
974 int u1 = u0 + width;
975 int v1 = v0 + height;
976
977 // we assume texture coords are the last vertex attribute, this is a bit fragile.
978 // TODO pass in this offset or something
979 SkIPoint16* textureCoords;
980 // V0
981 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
982 textureCoords->set(u0, v0);
983 vertex += vertexStride;
984
985 // V1
986 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
987 textureCoords->set(u0, v1);
988 vertex += vertexStride;
989
990 // V2
991 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
992 textureCoords->set(u1, v1);
993 vertex += vertexStride;
994
995 // V3
996 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
997 textureCoords->set(u1, v0);
998 }
999
1000 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
1001 for (int i = 0; i < kVerticesPerGlyph; i++) {
1002 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
1003 *vcolor = color;
1004 vertex += vertexStride;
1005 }
1006 }
1007
joshualitt2a0e9f32015-04-13 06:12:21 -07001008 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
1009 SkScalar transY) {
1010 for (int i = 0; i < kVerticesPerGlyph; i++) {
1011 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
1012 point->fX += transX;
1013 point->fY += transY;
1014 vertex += vertexStride;
1015 }
1016 }
1017
joshualitt1d89e8d2015-04-01 12:40:54 -07001018 void initDraw(GrBatchTarget* batchTarget,
1019 const GrGeometryProcessor* gp,
1020 const GrPipeline* pipeline) {
1021 batchTarget->initDraw(gp, pipeline);
1022
1023 // TODO remove this when batch is everywhere
1024 GrPipelineInfo init;
1025 init.fColorIgnored = fBatch.fColorIgnored;
1026 init.fOverrideColor = GrColor_ILLEGAL;
1027 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1028 init.fUsesLocalCoords = this->usesLocalCoords();
1029 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1030 }
1031
1032 void flush(GrBatchTarget* batchTarget,
1033 GrDrawTarget::DrawInfo* drawInfo,
1034 int instanceCount,
1035 int maxInstancesPerDraw) {
1036 while (instanceCount) {
1037 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1038 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verticesPerInstance());
1039 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indicesPerInstance());
1040
1041 batchTarget->draw(*drawInfo);
1042
1043 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexCount());
1044 instanceCount -= drawInfo->instanceCount();
1045 }
1046 }
1047
1048 GrColor color() const { return fBatch.fColor; }
1049 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
1050 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1051 int numGlyphs() const { return fBatch.fNumGlyphs; }
1052
1053 bool onCombineIfPossible(GrBatch* t) override {
1054 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
1055
1056 if (this->fMaskFormat != that->fMaskFormat) {
1057 return false;
1058 }
1059
1060 if (this->fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
1061 return false;
1062 }
1063
1064 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1065 return false;
1066 }
1067
1068 fBatch.fNumGlyphs += that->numGlyphs();
1069 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1070 return true;
1071 }
1072
1073 struct BatchTracker {
1074 GrColor fColor;
1075 SkMatrix fViewMatrix;
1076 bool fUsesLocalCoords;
1077 bool fColorIgnored;
1078 bool fCoverageIgnored;
1079 int fNumGlyphs;
1080 };
1081
1082 BatchTracker fBatch;
1083 SkSTArray<1, Geometry, true> fGeoData;
1084 GrMaskFormat fMaskFormat;
1085 GrPixelConfig fPixelConfig;
1086 GrBatchFontCache* fFontCache;
1087};
1088
joshualitt9a27e632015-04-06 10:53:36 -07001089void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
1090 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
1091 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
1092 SkPaint runPaint = skPaint;
joshualitt1d89e8d2015-04-01 12:40:54 -07001093
joshualitt9a27e632015-04-06 10:53:36 -07001094 size_t textLen = it.glyphCount() * sizeof(uint16_t);
1095 const SkPoint& offset = it.offset();
joshualitt1d89e8d2015-04-01 12:40:54 -07001096
joshualitt9a27e632015-04-06 10:53:36 -07001097 it.applyFontToPaint(&runPaint);
joshualitt1d89e8d2015-04-01 12:40:54 -07001098
joshualitt9a27e632015-04-06 10:53:36 -07001099 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
1100 return;
joshualitt1d89e8d2015-04-01 12:40:54 -07001101 }
1102
joshualitt9a27e632015-04-06 10:53:36 -07001103 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
1104
1105 switch (it.positioning()) {
1106 case SkTextBlob::kDefault_Positioning:
1107 this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
1108 textLen, x + offset.x(), y + offset.y(), clipBounds);
1109 break;
1110 case SkTextBlob::kHorizontal_Positioning:
1111 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
1112 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
1113 clipBounds);
1114 break;
1115 case SkTextBlob::kFull_Positioning:
1116 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
1117 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
1118 break;
1119 }
1120}
1121
1122inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
1123 BitmapTextBlob* cacheBlob, int run, GrColor color,
joshualitt2a0e9f32015-04-13 06:12:21 -07001124 uint8_t paintAlpha, SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07001125 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
1126 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
1127 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1128 if (0 == glyphCount) {
1129 continue;
1130 }
1131
1132 GrMaskFormat format = info.fMaskFormat;
1133 GrColor subRunColor = kARGB_GrMaskFormat == format ?
1134 SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) :
1135 color;
1136
1137 BitmapTextBatch::Geometry geometry;
1138 geometry.fBlob.reset(SkRef(cacheBlob));
1139 geometry.fRun = run;
1140 geometry.fSubRun = subRun;
1141 geometry.fColor = subRunColor;
joshualitt2a0e9f32015-04-13 06:12:21 -07001142 geometry.fTransX = transX;
1143 geometry.fTransY = transY;
joshualitt9a27e632015-04-06 10:53:36 -07001144 SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, format, glyphCount,
1145 fContext->getBatchFontCache()));
1146
1147 target->drawBatch(pipelineBuilder, batch, &cacheBlob->fRuns[run].fVertexBounds);
1148 }
1149}
1150
1151inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
joshualitt2a0e9f32015-04-13 06:12:21 -07001152 const GrPaint& grPaint, const GrClip& clip,
1153 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07001154 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07001155 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
1156 bigGlyph.fVx += SkScalarTruncToInt(transX);
1157 bigGlyph.fVy += SkScalarTruncToInt(transY);
joshualitt1d89e8d2015-04-01 12:40:54 -07001158 SkMatrix translate;
joshualitt2a0e9f32015-04-13 06:12:21 -07001159 translate.setTranslate(SkIntToScalar(bigGlyph.fVx),
1160 SkIntToScalar(bigGlyph.fVy));
joshualitt1d89e8d2015-04-01 12:40:54 -07001161 SkPath tmpPath(bigGlyph.fPath);
1162 tmpPath.transform(translate);
1163 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt9a27e632015-04-06 10:53:36 -07001164 fContext->drawPath(rt, clip, grPaint, SkMatrix::I(), tmpPath, strokeInfo);
joshualitt1d89e8d2015-04-01 12:40:54 -07001165 }
1166}
joshualitt9a27e632015-04-06 10:53:36 -07001167
1168void GrAtlasTextContext::flush(GrDrawTarget* target,
1169 const SkTextBlob* blob,
1170 BitmapTextBlob* cacheBlob,
1171 GrRenderTarget* rt,
1172 const SkPaint& skPaint,
1173 const GrPaint& grPaint,
1174 SkDrawFilter* drawFilter,
1175 const GrClip& clip,
1176 const SkMatrix& viewMatrix,
1177 const SkIRect& clipBounds,
joshualitt2a0e9f32015-04-13 06:12:21 -07001178 SkScalar x, SkScalar y,
1179 SkScalar transX, SkScalar transY) {
joshualitt9a27e632015-04-06 10:53:36 -07001180 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
1181 // it as paths
1182 GrPipelineBuilder pipelineBuilder;
1183 pipelineBuilder.setFromPaint(grPaint, rt, clip);
1184
1185 GrColor color = grPaint.getColor();
1186 uint8_t paintAlpha = skPaint.getAlpha();
1187
1188 SkTextBlob::RunIterator it(blob);
1189 for (int run = 0; !it.done(); it.next(), run++) {
1190 if (cacheBlob->fRuns[run].fDrawAsPaths) {
1191 this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
1192 continue;
1193 }
joshualitt2a0e9f32015-04-13 06:12:21 -07001194 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
1195 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, transX, transY);
joshualitt9a27e632015-04-06 10:53:36 -07001196 }
1197
1198 // Now flush big glyphs
joshualitt2a0e9f32015-04-13 06:12:21 -07001199 this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, transX, transY);
joshualitt9a27e632015-04-06 10:53:36 -07001200}
1201
1202void GrAtlasTextContext::flush(GrDrawTarget* target,
1203 BitmapTextBlob* cacheBlob,
1204 GrRenderTarget* rt,
1205 const SkPaint& skPaint,
1206 const GrPaint& grPaint,
1207 const GrClip& clip,
1208 const SkMatrix& viewMatrix) {
1209 GrPipelineBuilder pipelineBuilder;
1210 pipelineBuilder.setFromPaint(grPaint, rt, clip);
1211
1212 GrColor color = grPaint.getColor();
1213 uint8_t paintAlpha = skPaint.getAlpha();
1214 for (int run = 0; run < cacheBlob->fRunCount; run++) {
joshualitt2a0e9f32015-04-13 06:12:21 -07001215 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, 0, 0);
joshualitt9a27e632015-04-06 10:53:36 -07001216 }
1217
1218 // Now flush big glyphs
joshualitt2a0e9f32015-04-13 06:12:21 -07001219 this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, 0, 0);
joshualitt9a27e632015-04-06 10:53:36 -07001220}