blob: 5efb5b94250f2f9d9dcd171bc578769e13c49979 [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"
18#include "GrTexturePriv.h"
19
20#include "SkAutoKern.h"
21#include "SkColorPriv.h"
22#include "SkDraw.h"
23#include "SkDrawFilter.h"
24#include "SkDrawProcs.h"
25#include "SkGlyphCache.h"
26#include "SkGpuDevice.h"
27#include "SkGr.h"
28#include "SkPath.h"
29#include "SkRTConf.h"
30#include "SkStrokeRec.h"
31#include "SkTextBlob.h"
32#include "SkTextMapStateProc.h"
33
34#include "effects/GrBitmapTextGeoProc.h"
35#include "effects/GrSimpleTextureEffect.h"
36
37namespace {
38static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
39
40// position + local coord
41static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
42
43static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
44
45static const int kVerticesPerGlyph = 4;
46static const int kIndicesPerGlyph = 6;
47
48static size_t get_vertex_stride(GrMaskFormat maskFormat) {
49 switch (maskFormat) {
50 case kA8_GrMaskFormat:
51 return kGrayTextVASize;
52 case kARGB_GrMaskFormat:
53 return kColorTextVASize;
54 default:
55 return kLCDTextVASize;
56 }
57}
58
59};
60
61// TODO
62// More tests
63// move to SkCache
64// handle textblobs where the whole run is larger than the cache size
65// TODO implement micro speedy hash map for fast refing of glyphs
66
67GrBitmapTextContextB::GrBitmapTextContextB(GrContext* context,
68 SkGpuDevice* gpuDevice,
69 const SkDeviceProperties& properties)
70 : INHERITED(context, gpuDevice, properties) {
71 fCurrStrike = NULL;
72}
73
74void GrBitmapTextContextB::ClearCacheEntry(uint32_t key, BitmapTextBlob** blob) {
75 (*blob)->unref();
76}
77
78GrBitmapTextContextB::~GrBitmapTextContextB() {
79 fCache.foreach(&GrBitmapTextContextB::ClearCacheEntry);
80}
81
82GrBitmapTextContextB* GrBitmapTextContextB::Create(GrContext* context,
83 SkGpuDevice* gpuDevice,
84 const SkDeviceProperties& props) {
85 return SkNEW_ARGS(GrBitmapTextContextB, (context, gpuDevice, props));
86}
87
88bool GrBitmapTextContextB::canDraw(const GrRenderTarget*,
89 const GrClip&,
90 const GrPaint&,
91 const SkPaint& skPaint,
92 const SkMatrix& viewMatrix) {
93 return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
94}
95
96inline void GrBitmapTextContextB::init(GrRenderTarget* rt, const GrClip& clip,
97 const GrPaint& paint, const SkPaint& skPaint,
98 const SkIRect& regionClipBounds) {
99 INHERITED::init(rt, clip, paint, skPaint, regionClipBounds);
100
101 fCurrStrike = NULL;
102}
103
104bool GrBitmapTextContextB::MustRegenerateBlob(const BitmapTextBlob& blob, const SkPaint& paint,
105 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
106 // We always regenerate blobs with patheffects or mask filters we could cache these
107 // TODO find some way to cache the maskfilter / patheffects on the textblob
108 return !blob.fViewMatrix.cheapEqualTo(viewMatrix) || blob.fX != x || blob.fY != y ||
109 paint.getMaskFilter() || paint.getPathEffect() || paint.getStyle() != blob.fStyle;
110}
111
112
113inline SkGlyphCache* GrBitmapTextContextB::setupCache(BitmapTextBlob::Run* run,
114 const SkPaint& skPaint,
115 const SkMatrix& viewMatrix) {
116 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, &viewMatrix, false);
117 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
118 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
119}
120
121inline void GrBitmapTextContextB::BlobGlyphCount(int* glyphCount, int* runCount,
122 const SkTextBlob* blob) {
123 SkTextBlob::RunIterator itCounter(blob);
124 for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
125 *glyphCount += itCounter.glyphCount();
126 }
127}
128
129GrBitmapTextContextB::BitmapTextBlob* GrBitmapTextContextB::CreateBlob(int glyphCount,
130 int runCount) {
131 // We allocate size for the BitmapTextBlob itself, plus size for the vertices array,
132 // and size for the glyphIds array.
133 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
134 vertex_attribute_changed);
135 size_t verticesCount = glyphCount * kVerticesPerGlyph * kGrayTextVASize;
136 size_t length = sizeof(BitmapTextBlob) +
137 verticesCount +
138 glyphCount * sizeof(GrGlyph::PackedID) +
139 sizeof(BitmapTextBlob::Run) * runCount;
140
141 BitmapTextBlob* cacheBlob = SkNEW_PLACEMENT(sk_malloc_throw(length), BitmapTextBlob);
142
143 // setup offsets for vertices / glyphs
144 cacheBlob->fVertices = sizeof(BitmapTextBlob) + reinterpret_cast<unsigned char*>(cacheBlob);
145 cacheBlob->fGlyphIDs =
146 reinterpret_cast<GrGlyph::PackedID*>(cacheBlob->fVertices + verticesCount);
147 cacheBlob->fRuns = reinterpret_cast<BitmapTextBlob::Run*>(cacheBlob->fGlyphIDs + glyphCount);
148
149 // Initialize runs
150 for (int i = 0; i < runCount; i++) {
151 SkNEW_PLACEMENT(&cacheBlob->fRuns[i], BitmapTextBlob::Run);
152 }
153 cacheBlob->fRunCount = runCount;
154 return cacheBlob;
155}
156
157void GrBitmapTextContextB::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
158 const SkPaint& skPaint, const SkMatrix& viewMatrix,
159 const SkTextBlob* blob, SkScalar x, SkScalar y,
160 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
161 BitmapTextBlob* cacheBlob;
162 BitmapTextBlob** foundBlob = fCache.find(blob->uniqueID());
163 SkIRect clipRect;
164 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
165
166 if (foundBlob) {
167 cacheBlob = *foundBlob;
168 if (MustRegenerateBlob(*cacheBlob, skPaint, viewMatrix, x, y)) {
169 // We have to remake the blob because changes may invalidate our masks.
170 // TODO we could probably get away reuse most of the time if the pointer is unique,
171 // but we'd have to clear the subrun information
172 cacheBlob->unref();
173 int glyphCount = 0;
174 int runCount = 0;
175 BlobGlyphCount(&glyphCount, &runCount, blob);
176 cacheBlob = CreateBlob(glyphCount, runCount);
177 fCache.set(blob->uniqueID(), cacheBlob);
178 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter,
179 clipRect);
180 }
181 } else {
182 int glyphCount = 0;
183 int runCount = 0;
184 BlobGlyphCount(&glyphCount, &runCount, blob);
185 cacheBlob = CreateBlob(glyphCount, runCount);
186 fCache.set(blob->uniqueID(), cacheBlob);
187 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter, clipRect);
188 }
189
190 // Though for the time being runs in the textblob can override the paint, they only touch font
191 // info.
192 GrPaint grPaint;
193 SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint);
194
195 this->flush(fContext->getTextTarget(), cacheBlob, rt, grPaint, clip, viewMatrix,
196 fSkPaint.getAlpha());
197}
198
199void GrBitmapTextContextB::regenerateTextBlob(BitmapTextBlob* cacheBlob,
200 const SkPaint& skPaint, const SkMatrix& viewMatrix,
201 const SkTextBlob* blob, SkScalar x, SkScalar y,
202 SkDrawFilter* drawFilter, const SkIRect& clipRect) {
203 cacheBlob->fViewMatrix = viewMatrix;
204 cacheBlob->fX = x;
205 cacheBlob->fY = y;
206 cacheBlob->fStyle = skPaint.getStyle();
207
208 // Regenerate textblob
209 SkPaint runPaint = skPaint;
210 SkTextBlob::RunIterator it(blob);
211 for (int run = 0; !it.done(); it.next(), run++) {
212 int glyphCount = it.glyphCount();
213 size_t textLen = glyphCount * sizeof(uint16_t);
214 const SkPoint& offset = it.offset();
215 // applyFontToPaint() always overwrites the exact same attributes,
216 // so it is safe to not re-seed the paint for this reason.
217 it.applyFontToPaint(&runPaint);
218
219 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
220 // A false return from filter() means we should abort the current draw.
221 runPaint = skPaint;
222 continue;
223 }
224
225 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
226
227 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, viewMatrix);
228
229 // setup vertex / glyphIndex for the new run
230 if (run > 0) {
231 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
232 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
233
234 newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
235 newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
236
237 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
238 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
239 }
240
241 switch (it.positioning()) {
242 case SkTextBlob::kDefault_Positioning:
243 this->internalDrawText(cacheBlob, run, cache, runPaint, viewMatrix,
244 (const char *)it.glyphs(), textLen,
245 x + offset.x(), y + offset.y(), clipRect);
246 break;
247 case SkTextBlob::kHorizontal_Positioning:
248 this->internalDrawPosText(cacheBlob, run, cache, runPaint, viewMatrix,
249 (const char*)it.glyphs(), textLen, it.pos(), 1,
250 SkPoint::Make(x, y + offset.y()), clipRect);
251 break;
252 case SkTextBlob::kFull_Positioning:
253 this->internalDrawPosText(cacheBlob, run, cache, runPaint, viewMatrix,
254 (const char*)it.glyphs(), textLen, it.pos(), 2,
255 SkPoint::Make(x, y), clipRect);
256 break;
257 }
258
259 if (drawFilter) {
260 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
261 runPaint = skPaint;
262 }
263
264 SkGlyphCache::AttachCache(cache);
265 }
266}
267
268void GrBitmapTextContextB::onDrawText(GrRenderTarget* rt, const GrClip& clip,
269 const GrPaint& paint, const SkPaint& skPaint,
270 const SkMatrix& viewMatrix,
271 const char text[], size_t byteLength,
272 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
273 int glyphCount = skPaint.countText(text, byteLength);
274 SkAutoTUnref<BitmapTextBlob> blob(CreateBlob(glyphCount, 1));
275 blob->fViewMatrix = viewMatrix;
276 blob->fX = x;
277 blob->fY = y;
278 blob->fStyle = skPaint.getStyle();
279
280 SkIRect clipRect;
281 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
282
283 // setup cache
284 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
285 this->internalDrawText(blob, 0, cache, skPaint, viewMatrix, text, byteLength, x, y, clipRect);
286 SkGlyphCache::AttachCache(cache);
287
288 this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, skPaint.getAlpha());
289}
290
291void GrBitmapTextContextB::internalDrawText(BitmapTextBlob* blob, int runIndex,
292 SkGlyphCache* cache, const SkPaint& skPaint,
293 const SkMatrix& viewMatrix,
294 const char text[], size_t byteLength,
295 SkScalar x, SkScalar y, const SkIRect& clipRect) {
296 SkASSERT(byteLength == 0 || text != NULL);
297
298 // nothing to draw
299 if (text == NULL || byteLength == 0) {
300 return;
301 }
302
303 fCurrStrike = NULL;
304 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
305
306 // Get GrFontScaler from cache
307 GrFontScaler* fontScaler = GetGrFontScaler(cache);
308
309 // transform our starting point
310 {
311 SkPoint loc;
312 viewMatrix.mapXY(x, y, &loc);
313 x = loc.fX;
314 y = loc.fY;
315 }
316
317 // need to measure first
318 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
319 SkVector stopVector;
320 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
321
322 SkScalar stopX = stopVector.fX;
323 SkScalar stopY = stopVector.fY;
324
325 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
326 stopX = SkScalarHalf(stopX);
327 stopY = SkScalarHalf(stopY);
328 }
329 x -= stopX;
330 y -= stopY;
331 }
332
333 const char* stop = text + byteLength;
334
335 SkAutoKern autokern;
336
337 SkFixed fxMask = ~0;
338 SkFixed fyMask = ~0;
339 SkScalar halfSampleX, halfSampleY;
340 if (cache->isSubpixel()) {
341 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
342 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
343 if (kX_SkAxisAlignment == baseline) {
344 fyMask = 0;
345 halfSampleY = SK_ScalarHalf;
346 } else if (kY_SkAxisAlignment == baseline) {
347 fxMask = 0;
348 halfSampleX = SK_ScalarHalf;
349 }
350 } else {
351 halfSampleX = halfSampleY = SK_ScalarHalf;
352 }
353
354 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
355 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
356
357 while (text < stop) {
358 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
359
360 fx += autokern.adjust(glyph);
361
362 if (glyph.fWidth) {
363 this->appendGlyph(blob,
364 runIndex,
365 GrGlyph::Pack(glyph.getGlyphID(),
366 glyph.getSubXFixed(),
367 glyph.getSubYFixed(),
368 GrGlyph::kCoverage_MaskStyle),
369 Sk48Dot16FloorToInt(fx),
370 Sk48Dot16FloorToInt(fy),
371 fontScaler,
372 clipRect);
373 }
374
375 fx += glyph.fAdvanceX;
376 fy += glyph.fAdvanceY;
377 }
378}
379
380void GrBitmapTextContextB::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
381 const GrPaint& paint, const SkPaint& skPaint,
382 const SkMatrix& viewMatrix,
383 const char text[], size_t byteLength,
384 const SkScalar pos[], int scalarsPerPosition,
385 const SkPoint& offset, const SkIRect& regionClipBounds) {
386 int glyphCount = skPaint.countText(text, byteLength);
387 SkAutoTUnref<BitmapTextBlob> blob(CreateBlob(glyphCount, 1));
388 blob->fStyle = skPaint.getStyle();
389 blob->fViewMatrix = viewMatrix;
390
391 SkIRect clipRect;
392 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
393
394 // setup cache
395 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
396 this->internalDrawPosText(blob, 0, cache, skPaint, viewMatrix, text, byteLength, pos,
397 scalarsPerPosition, offset, clipRect);
398 SkGlyphCache::AttachCache(cache);
399
400 this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, fSkPaint.getAlpha());
401}
402
403void GrBitmapTextContextB::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
404 SkGlyphCache* cache, const SkPaint& skPaint,
405 const SkMatrix& viewMatrix,
406 const char text[], size_t byteLength,
407 const SkScalar pos[], int scalarsPerPosition,
408 const SkPoint& offset, const SkIRect& clipRect) {
409 SkASSERT(byteLength == 0 || text != NULL);
410 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
411
412 // nothing to draw
413 if (text == NULL || byteLength == 0) {
414 return;
415 }
416
417 fCurrStrike = NULL;
418 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
419
420 // Get GrFontScaler from cache
421 GrFontScaler* fontScaler = GetGrFontScaler(cache);
422
423 const char* stop = text + byteLength;
424 SkTextAlignProc alignProc(skPaint.getTextAlign());
425 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
426
427 if (cache->isSubpixel()) {
428 // maybe we should skip the rounding if linearText is set
429 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
430
431 SkFixed fxMask = ~0;
432 SkFixed fyMask = ~0;
433 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
434 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
435 if (kX_SkAxisAlignment == baseline) {
436 fyMask = 0;
437 halfSampleY = SK_ScalarHalf;
438 } else if (kY_SkAxisAlignment == baseline) {
439 fxMask = 0;
440 halfSampleX = SK_ScalarHalf;
441 }
442
443 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
444 while (text < stop) {
445 SkPoint tmsLoc;
446 tmsProc(pos, &tmsLoc);
447 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
448 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
449
450 const SkGlyph& glyph = glyphCacheProc(cache, &text,
451 fx & fxMask, fy & fyMask);
452
453 if (glyph.fWidth) {
454 this->appendGlyph(blob,
455 runIndex,
456 GrGlyph::Pack(glyph.getGlyphID(),
457 glyph.getSubXFixed(),
458 glyph.getSubYFixed(),
459 GrGlyph::kCoverage_MaskStyle),
460 Sk48Dot16FloorToInt(fx),
461 Sk48Dot16FloorToInt(fy),
462 fontScaler,
463 clipRect);
464 }
465 pos += scalarsPerPosition;
466 }
467 } else {
468 while (text < stop) {
469 const char* currentText = text;
470 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
471
472 if (metricGlyph.fWidth) {
473 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
474 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
475 SkPoint tmsLoc;
476 tmsProc(pos, &tmsLoc);
477 SkPoint alignLoc;
478 alignProc(tmsLoc, metricGlyph, &alignLoc);
479
480 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
481 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
482
483 // have to call again, now that we've been "aligned"
484 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
485 fx & fxMask, fy & fyMask);
486 // the assumption is that the metrics haven't changed
487 SkASSERT(prevAdvX == glyph.fAdvanceX);
488 SkASSERT(prevAdvY == glyph.fAdvanceY);
489 SkASSERT(glyph.fWidth);
490
491 this->appendGlyph(blob,
492 runIndex,
493 GrGlyph::Pack(glyph.getGlyphID(),
494 glyph.getSubXFixed(),
495 glyph.getSubYFixed(),
496 GrGlyph::kCoverage_MaskStyle),
497 Sk48Dot16FloorToInt(fx),
498 Sk48Dot16FloorToInt(fy),
499 fontScaler,
500 clipRect);
501 }
502 pos += scalarsPerPosition;
503 }
504 }
505 } else { // not subpixel
506
507 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
508 while (text < stop) {
509 // the last 2 parameters are ignored
510 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
511
512 if (glyph.fWidth) {
513 SkPoint tmsLoc;
514 tmsProc(pos, &tmsLoc);
515
516 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
517 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
518 this->appendGlyph(blob,
519 runIndex,
520 GrGlyph::Pack(glyph.getGlyphID(),
521 glyph.getSubXFixed(),
522 glyph.getSubYFixed(),
523 GrGlyph::kCoverage_MaskStyle),
524 Sk48Dot16FloorToInt(fx),
525 Sk48Dot16FloorToInt(fy),
526 fontScaler,
527 clipRect);
528 }
529 pos += scalarsPerPosition;
530 }
531 } else {
532 while (text < stop) {
533 // the last 2 parameters are ignored
534 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
535
536 if (glyph.fWidth) {
537 SkPoint tmsLoc;
538 tmsProc(pos, &tmsLoc);
539
540 SkPoint alignLoc;
541 alignProc(tmsLoc, glyph, &alignLoc);
542
543 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
544 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
545 this->appendGlyph(blob,
546 runIndex,
547 GrGlyph::Pack(glyph.getGlyphID(),
548 glyph.getSubXFixed(),
549 glyph.getSubYFixed(),
550 GrGlyph::kCoverage_MaskStyle),
551 Sk48Dot16FloorToInt(fx),
552 Sk48Dot16FloorToInt(fy),
553 fontScaler,
554 clipRect);
555 }
556 pos += scalarsPerPosition;
557 }
558 }
559 }
560}
561
562void GrBitmapTextContextB::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph::PackedID packed,
563 int vx, int vy, GrFontScaler* scaler,
564 const SkIRect& clipRect) {
565 if (NULL == fCurrStrike) {
566 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
567 }
568
569 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
570 if (NULL == glyph || glyph->fBounds.isEmpty()) {
571 return;
572 }
573
574 int x = vx + glyph->fBounds.fLeft;
575 int y = vy + glyph->fBounds.fTop;
576
577 // keep them as ints until we've done the clip-test
578 int width = glyph->fBounds.width();
579 int height = glyph->fBounds.height();
580
581 // check if we clipped out
582 if (clipRect.quickReject(x, y, x + width, y + height)) {
583 return;
584 }
585
586 // If the glyph is too large we fall back to paths
587 if (fCurrStrike->glyphTooLargeForAtlas(glyph)) {
588 if (NULL == glyph->fPath) {
589 SkPath* path = SkNEW(SkPath);
590 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
591 // flag the glyph as being dead?
592 SkDELETE(path);
593 return;
594 }
595 glyph->fPath = path;
596 }
597 SkASSERT(glyph->fPath);
598 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, vx, vy));
599 return;
600 }
601
602 Run& run = blob->fRuns[runIndex];
603
604 GrMaskFormat format = glyph->fMaskFormat;
605
606 PerSubRunInfo* subRun = &run.fSubRunInfo.back();
607 if (run.fInitialized && subRun->fMaskFormat != format) {
608 PerSubRunInfo* newSubRun = &run.fSubRunInfo.push_back();
609 newSubRun->fGlyphStartIndex = subRun->fGlyphEndIndex;
610 newSubRun->fGlyphEndIndex = subRun->fGlyphEndIndex;
611
612 newSubRun->fVertexStartIndex = subRun->fVertexEndIndex;
613 newSubRun->fVertexEndIndex = subRun->fVertexEndIndex;
614
615 subRun = newSubRun;
616 }
617
618 run.fInitialized = true;
619 subRun->fMaskFormat = format;
620 blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed;
621
622 size_t vertexStride = get_vertex_stride(format);
623
624 SkRect r;
625 r.fLeft = SkIntToScalar(x);
626 r.fTop = SkIntToScalar(y);
627 r.fRight = r.fLeft + SkIntToScalar(width);
628 r.fBottom = r.fTop + SkIntToScalar(height);
629
630 run.fVertexBounds.joinNonEmptyArg(r);
631 GrColor color = fPaint.getColor();
632 run.fColor = color;
633
634 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
635
636 // V0
637 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
638 position->set(r.fLeft, r.fTop);
639 if (kA8_GrMaskFormat == format) {
640 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
641 *colorPtr = color;
642 }
643 vertex += vertexStride;
644
645 // V1
646 position = reinterpret_cast<SkPoint*>(vertex);
647 position->set(r.fLeft, r.fBottom);
648 if (kA8_GrMaskFormat == format) {
649 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
650 *colorPtr = color;
651 }
652 vertex += vertexStride;
653
654 // V2
655 position = reinterpret_cast<SkPoint*>(vertex);
656 position->set(r.fRight, r.fBottom);
657 if (kA8_GrMaskFormat == format) {
658 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
659 *colorPtr = color;
660 }
661 vertex += vertexStride;
662
663 // V3
664 position = reinterpret_cast<SkPoint*>(vertex);
665 position->set(r.fRight, r.fTop);
666 if (kA8_GrMaskFormat == format) {
667 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
668 *colorPtr = color;
669 }
670
671 subRun->fGlyphEndIndex++;
672 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
673}
674
675class BitmapTextBatch : public GrBatch {
676public:
677 typedef GrBitmapTextContextB::BitmapTextBlob Blob;
678 typedef Blob::Run Run;
679 typedef Run::SubRunInfo TextInfo;
680 struct Geometry {
681 Geometry() {}
682 Geometry(const Geometry& geometry)
683 : fBlob(SkRef(geometry.fBlob.get()))
684 , fRun(geometry.fRun)
685 , fSubRun(geometry.fSubRun)
686 , fColor(geometry.fColor) {}
687 SkAutoTUnref<Blob> fBlob;
688 int fRun;
689 int fSubRun;
690 GrColor fColor;
691 };
692
693 static GrBatch* Create(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat,
694 int glyphCount, GrBatchFontCache* fontCache) {
695 return SkNEW_ARGS(BitmapTextBatch, (geometry, color, maskFormat, glyphCount, fontCache));
696 }
697
698 const char* name() const override { return "BitmapTextBatch"; }
699
700 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
701 if (kARGB_GrMaskFormat == fMaskFormat) {
702 out->setUnknownFourComponents();
703 } else {
704 out->setKnownFourComponents(fBatch.fColor);
705 }
706 }
707
708 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
709 if (kARGB_GrMaskFormat != fMaskFormat) {
710 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
711 out->setUnknownSingleComponent();
712 } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
713 out->setUnknownOpaqueFourComponents();
714 out->setUsingLCDCoverage();
715 } else {
716 out->setUnknownFourComponents();
717 out->setUsingLCDCoverage();
718 }
719 } else {
720 out->setKnownSingleComponent(0xff);
721 }
722 }
723
724 void initBatchTracker(const GrPipelineInfo& init) override {
725 // Handle any color overrides
726 if (init.fColorIgnored) {
727 fBatch.fColor = GrColor_ILLEGAL;
728 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
729 fBatch.fColor = init.fOverrideColor;
730 }
731
732 // setup batch properties
733 fBatch.fColorIgnored = init.fColorIgnored;
734 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
735 fBatch.fCoverageIgnored = init.fCoverageIgnored;
736 }
737
738 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
739 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
740 // TODO actually only invert if we don't have RGBA
741 SkMatrix localMatrix;
742 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
743 SkDebugf("Cannot invert viewmatrix\n");
744 return;
745 }
746
747 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
748 // This will be ignored in the non A8 case
749 bool opaqueVertexColors = GrColorIsOpaque(this->color());
750 SkAutoTUnref<const GrGeometryProcessor> gp(
751 GrBitmapTextGeoProc::Create(this->color(),
752 fFontCache->getTexture(fMaskFormat),
753 params,
754 fMaskFormat,
755 opaqueVertexColors,
756 localMatrix));
757
758 size_t vertexStride = gp->getVertexStride();
759 SkASSERT(vertexStride == get_vertex_stride(fMaskFormat));
760
761 this->initDraw(batchTarget, gp, pipeline);
762
763 int glyphCount = this->numGlyphs();
764 int instanceCount = fGeoData.count();
765 const GrVertexBuffer* vertexBuffer;
766 int firstVertex;
767
768 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
769 glyphCount * kVerticesPerGlyph,
770 &vertexBuffer,
771 &firstVertex);
772 if (!vertices) {
773 SkDebugf("Could not allocate vertices\n");
774 return;
775 }
776
777 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
778
779 // setup drawinfo
780 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
781 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
782
783 GrDrawTarget::DrawInfo drawInfo;
784 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
785 drawInfo.setStartVertex(0);
786 drawInfo.setStartIndex(0);
787 drawInfo.setVerticesPerInstance(kVerticesPerGlyph);
788 drawInfo.setIndicesPerInstance(kIndicesPerGlyph);
789 drawInfo.adjustStartVertex(firstVertex);
790 drawInfo.setVertexBuffer(vertexBuffer);
791 drawInfo.setIndexBuffer(quadIndexBuffer);
792
793 int instancesToFlush = 0;
794 for (int i = 0; i < instanceCount; i++) {
795 Geometry& args = fGeoData[i];
796 Blob* blob = args.fBlob;
797 Run& run = blob->fRuns[args.fRun];
798 TextInfo& info = run.fSubRunInfo[args.fSubRun];
799
800 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
801 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
802 bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
803 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
804
805 // We regenerate both texture coords and colors in the blob itself, and update the
806 // atlas generation. If we don't end up purging any unused plots, we can avoid
807 // regenerating the coords. We could take a finer grained approach to updating texture
808 // coords but its not clear if the extra bookkeeping would offset any gains.
809 // To avoid looping over the glyphs twice, we do one loop and conditionally update color
810 // or coords as needed. One final note, if we have to break a run for an atlas eviction
811 // then we can't really trust the atlas has all of the correct data. Atlas evictions
812 // should be pretty rare, so we just always regenerate in those cases
813 if (regenerateTextureCoords || regenerateColors) {
814 // first regenerate texture coordinates / colors if need be
815 const SkDescriptor* desc = NULL;
816 SkGlyphCache* cache = NULL;
817 GrFontScaler* scaler = NULL;
818 GrBatchTextStrike* strike = NULL;
819 bool brokenRun = false;
820 if (regenerateTextureCoords) {
821 desc = run.fDescriptor.getDesc();
822 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
823 scaler = GrTextContext::GetGrFontScaler(cache);
824 strike = fFontCache->getStrike(scaler);
825 }
826 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
827 GrGlyph::PackedID glyphID = blob->fGlyphIDs[glyphIdx + info.fGlyphStartIndex];
828
829 if (regenerateTextureCoords) {
830 // Upload the glyph only if needed
831 GrGlyph* glyph = strike->getGlyph(glyphID, scaler);
832 SkASSERT(glyph);
833
834 if (!fFontCache->hasGlyph(glyph) &&
835 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
836 this->flush(batchTarget, &drawInfo, instancesToFlush,
837 maxInstancesPerDraw);
838 this->initDraw(batchTarget, gp, pipeline);
839 instancesToFlush = 0;
840 brokenRun = glyphIdx > 0;
841
842 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget, glyph,
843 scaler);
844 SkASSERT(success);
845 }
846
847 fFontCache->setGlyphRefToken(glyph, batchTarget->currentToken());
848
849 // Texture coords are the last vertex attribute so we get a pointer to the
850 // first one and then map with stride in regenerateTextureCoords
851 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
852 vertex += info.fVertexStartIndex;
853 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
854 vertex += vertexStride - sizeof(SkIPoint16);
855
856 this->regenerateTextureCoords(glyph, vertex, vertexStride);
857 }
858
859 if (regenerateColors) {
860 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
861 vertex += info.fVertexStartIndex;
862 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
863 this->regenerateColors(vertex, vertexStride, args.fColor);
864 }
865
866 instancesToFlush++;
867 }
868
869 if (regenerateTextureCoords) {
870 SkGlyphCache::AttachCache(cache);
871 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
872 fFontCache->atlasGeneration(fMaskFormat);
873 }
874 } else {
875 instancesToFlush += glyphCount;
876 }
877
878 // now copy all vertices
879 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
880 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
881
882 currVertex += byteCount;
883 }
884
885 this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDraw);
886 }
887
888 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
889
890private:
891 BitmapTextBatch(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat,
892 int glyphCount, GrBatchFontCache* fontCache)
893 : fMaskFormat(maskFormat)
894 , fPixelConfig(fontCache->getPixelConfig(maskFormat))
895 , fFontCache(fontCache) {
896 this->initClassID<BitmapTextBatch>();
897 fGeoData.push_back(geometry);
898 fBatch.fColor = color;
899 fBatch.fViewMatrix = geometry.fBlob->fViewMatrix;
900 fBatch.fNumGlyphs = glyphCount;
901 }
902
903 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
904 int width = glyph->fBounds.width();
905 int height = glyph->fBounds.height();
906 int u0 = glyph->fAtlasLocation.fX;
907 int v0 = glyph->fAtlasLocation.fY;
908 int u1 = u0 + width;
909 int v1 = v0 + height;
910
911 // we assume texture coords are the last vertex attribute, this is a bit fragile.
912 // TODO pass in this offset or something
913 SkIPoint16* textureCoords;
914 // V0
915 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
916 textureCoords->set(u0, v0);
917 vertex += vertexStride;
918
919 // V1
920 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
921 textureCoords->set(u0, v1);
922 vertex += vertexStride;
923
924 // V2
925 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
926 textureCoords->set(u1, v1);
927 vertex += vertexStride;
928
929 // V3
930 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
931 textureCoords->set(u1, v0);
932 }
933
934 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
935 for (int i = 0; i < kVerticesPerGlyph; i++) {
936 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
937 *vcolor = color;
938 vertex += vertexStride;
939 }
940 }
941
942 void initDraw(GrBatchTarget* batchTarget,
943 const GrGeometryProcessor* gp,
944 const GrPipeline* pipeline) {
945 batchTarget->initDraw(gp, pipeline);
946
947 // TODO remove this when batch is everywhere
948 GrPipelineInfo init;
949 init.fColorIgnored = fBatch.fColorIgnored;
950 init.fOverrideColor = GrColor_ILLEGAL;
951 init.fCoverageIgnored = fBatch.fCoverageIgnored;
952 init.fUsesLocalCoords = this->usesLocalCoords();
953 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
954 }
955
956 void flush(GrBatchTarget* batchTarget,
957 GrDrawTarget::DrawInfo* drawInfo,
958 int instanceCount,
959 int maxInstancesPerDraw) {
960 while (instanceCount) {
961 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
962 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verticesPerInstance());
963 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indicesPerInstance());
964
965 batchTarget->draw(*drawInfo);
966
967 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexCount());
968 instanceCount -= drawInfo->instanceCount();
969 }
970 }
971
972 GrColor color() const { return fBatch.fColor; }
973 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
974 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
975 int numGlyphs() const { return fBatch.fNumGlyphs; }
976
977 bool onCombineIfPossible(GrBatch* t) override {
978 BitmapTextBatch* that = t->cast<BitmapTextBatch>();
979
980 if (this->fMaskFormat != that->fMaskFormat) {
981 return false;
982 }
983
984 if (this->fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
985 return false;
986 }
987
988 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
989 return false;
990 }
991
992 fBatch.fNumGlyphs += that->numGlyphs();
993 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
994 return true;
995 }
996
997 struct BatchTracker {
998 GrColor fColor;
999 SkMatrix fViewMatrix;
1000 bool fUsesLocalCoords;
1001 bool fColorIgnored;
1002 bool fCoverageIgnored;
1003 int fNumGlyphs;
1004 };
1005
1006 BatchTracker fBatch;
1007 SkSTArray<1, Geometry, true> fGeoData;
1008 GrMaskFormat fMaskFormat;
1009 GrPixelConfig fPixelConfig;
1010 GrBatchFontCache* fFontCache;
1011};
1012
1013void GrBitmapTextContextB::flush(GrDrawTarget* target, BitmapTextBlob* blob, GrRenderTarget* rt,
1014 const GrPaint& paint, const GrClip& clip,
1015 const SkMatrix& viewMatrix, int paintAlpha) {
1016 GrPipelineBuilder pipelineBuilder;
1017 pipelineBuilder.setFromPaint(paint, rt, clip);
1018
1019 GrColor color = paint.getColor();
1020 for (uint32_t run = 0; run < blob->fRunCount; run++) {
1021 for (int subRun = 0; subRun < blob->fRuns[run].fSubRunInfo.count(); subRun++) {
1022 PerSubRunInfo& info = blob->fRuns[run].fSubRunInfo[subRun];
1023 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
1024 if (0 == glyphCount) {
1025 continue;
1026 }
1027
1028 GrMaskFormat format = info.fMaskFormat;
1029 if (kARGB_GrMaskFormat == format) {
1030 color = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
1031 }
1032
1033 BitmapTextBatch::Geometry geometry;
1034 geometry.fBlob.reset(SkRef(blob));
1035 geometry.fRun = run;
1036 geometry.fSubRun = subRun;
1037 geometry.fColor = color;
1038 SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, color, format, glyphCount,
1039 fContext->getBatchFontCache()));
1040
1041 target->drawBatch(&pipelineBuilder, batch, &blob->fRuns[run].fVertexBounds);
1042 }
1043 }
1044
1045 // Now flush big glyphs
1046 for (int i = 0; i < blob->fBigGlyphs.count(); i++) {
1047 BitmapTextBlob::BigGlyph& bigGlyph = blob->fBigGlyphs[i];
1048 SkMatrix translate;
1049 translate.setTranslate(SkIntToScalar(bigGlyph.fVx), SkIntToScalar(bigGlyph.fVy));
1050 SkPath tmpPath(bigGlyph.fPath);
1051 tmpPath.transform(translate);
1052 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
1053 fContext->drawPath(rt, clip, paint, SkMatrix::I(), tmpPath, strokeInfo);
1054 }
1055}