2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrBitmapTextContext.h"
10 #include "GrDrawTarget.h"
11 #include "GrFontScaler.h"
12 #include "GrIndexBuffer.h"
13 #include "GrTextStrike.h"
14 #include "GrTextStrike_impl.h"
15 #include "SkColorPriv.h"
18 #include "SkStrokeRec.h"
19 #include "effects/GrCustomCoordsTextureEffect.h"
21 #include "SkAutoKern.h"
23 #include "SkGlyphCache.h"
24 #include "SkGpuDevice.h"
27 static const int kGlyphCoordsAttributeIndex = 1;
29 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
30 "Dump the contents of the font cache before every purge.");
32 GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
33 const SkDeviceProperties& properties)
34 : GrTextContext(context, properties) {
43 fVertexBounds.setLargestInverted();
46 GrBitmapTextContext::~GrBitmapTextContext() {
50 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
51 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
54 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
55 unsigned r = SkColorGetR(c);
56 unsigned g = SkColorGetG(c);
57 unsigned b = SkColorGetB(c);
58 return GrColorPackRGBA(r, g, b, 0xff);
61 void GrBitmapTextContext::flushGlyphs() {
62 if (NULL == fDrawTarget) {
66 GrDrawState* drawState = fDrawTarget->drawState();
67 GrDrawState::AutoRestoreEffects are(drawState);
68 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
70 if (fCurrVertex > 0) {
71 // setup our sampler state for our text texture/atlas
72 SkASSERT(GrIsALIGN4(fCurrVertex));
73 SkASSERT(fCurrTexture);
74 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
76 // This effect could be stored with one of the cache objects (atlas?)
77 drawState->addCoverageEffect(
78 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
79 kGlyphCoordsAttributeIndex)->unref();
81 if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
82 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
83 drawState->setColor(0xffffffff);
84 } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
85 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
86 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
87 fPaint.numColorStages()) {
88 GrPrintf("LCD Text will not draw correctly.\n");
90 // We don't use the GrPaint's color in this case because it's been premultiplied by
91 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
92 // the mask texture color. The end result is that we get
93 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
94 int a = SkColorGetA(fSkPaint.getColor());
96 drawState->setColor(SkColorSetARGB(a, a, a, a));
98 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
99 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
101 // set back to normal in case we took LCD path previously.
102 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
103 drawState->setColor(fPaint.getColor());
106 int nGlyphs = fCurrVertex / 4;
107 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
108 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
110 4, 6, &fVertexBounds);
112 fDrawTarget->resetVertexSource();
116 fVertexBounds.setLargestInverted();
117 SkSafeSetNull(fCurrTexture);
121 inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
122 GrTextContext::init(paint, skPaint);
133 inline void GrBitmapTextContext::finish() {
136 GrTextContext::finish();
139 void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
140 const char text[], size_t byteLength,
141 SkScalar x, SkScalar y) {
142 SkASSERT(byteLength == 0 || text != NULL);
145 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
149 this->init(paint, skPaint);
151 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
153 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
154 SkGlyphCache* cache = autoCache.getCache();
155 GrFontScaler* fontScaler = GetGrFontScaler(cache);
157 // transform our starting point
160 fContext->getMatrix().mapXY(x, y, &loc);
165 // need to measure first
166 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
169 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
171 SkScalar stopX = stop.fX;
172 SkScalar stopY = stop.fY;
174 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
175 stopX = SkScalarHalf(stopX);
176 stopY = SkScalarHalf(stopY);
182 const char* stop = text + byteLength;
188 SkFixed halfSampleX, halfSampleY;
189 if (cache->isSubpixel()) {
190 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
191 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
192 if (kX_SkAxisAlignment == baseline) {
194 halfSampleY = SK_FixedHalf;
195 } else if (kY_SkAxisAlignment == baseline) {
197 halfSampleX = SK_FixedHalf;
200 halfSampleX = halfSampleY = SK_FixedHalf;
203 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
204 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
206 GrContext::AutoMatrix autoMatrix;
207 autoMatrix.setIdentity(fContext, &fPaint);
209 while (text < stop) {
210 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
212 fx += autokern.adjust(glyph);
215 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
216 glyph.getSubXFixed(),
217 glyph.getSubYFixed()),
218 SkFixedFloorToFixed(fx),
219 SkFixedFloorToFixed(fy),
223 fx += glyph.fAdvanceX;
224 fy += glyph.fAdvanceY;
230 ///////////////////////////////////////////////////////////////////////////////
231 // Copied from SkDraw
233 // last parameter is interpreted as SkFixed [x, y]
234 // return the fixed position, which may be rounded or not by the caller
235 // e.g. subpixel doesn't round
236 typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
238 static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
239 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
242 static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
243 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
244 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
247 static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
248 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
249 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
252 static AlignProc pick_align_proc(SkPaint::Align align) {
253 static const AlignProc gProcs[] = {
254 leftAlignProc, centerAlignProc, rightAlignProc
257 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
259 return gProcs[align];
262 class BitmapTextMapState {
264 mutable SkPoint fLoc;
266 BitmapTextMapState(const SkMatrix& matrix, SkScalar y)
267 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
269 typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]);
271 Proc pickProc(int scalarsPerPosition);
274 const SkMatrix& fMatrix;
275 SkMatrix::MapXYProc fProc;
276 SkScalar fY; // ignored by MapXYProc
277 // these are only used by Only... procs
278 SkScalar fScaleX, fTransX, fTransformedY;
280 static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) {
281 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
284 static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) {
285 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
288 static void MapOnlyScaleXProc(const BitmapTextMapState& state,
289 const SkScalar pos[]) {
290 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
291 state.fTransformedY);
294 static void MapOnlyTransXProc(const BitmapTextMapState& state,
295 const SkScalar pos[]) {
296 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
300 BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) {
301 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
303 if (1 == scalarsPerPosition) {
304 unsigned mtype = fMatrix.getType();
305 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
308 fScaleX = fMatrix.getScaleX();
309 fTransX = fMatrix.getTranslateX();
310 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
311 fMatrix.getTranslateY();
312 return (mtype & SkMatrix::kScale_Mask) ?
313 MapOnlyScaleXProc : MapOnlyTransXProc;
320 ///////////////////////////////////////////////////////////////////////////////
322 void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
323 const char text[], size_t byteLength,
324 const SkScalar pos[], SkScalar constY,
325 int scalarsPerPosition) {
326 SkASSERT(byteLength == 0 || text != NULL);
327 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
330 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
334 this->init(paint, skPaint);
336 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
338 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
339 SkGlyphCache* cache = autoCache.getCache();
340 GrFontScaler* fontScaler = GetGrFontScaler(cache);
342 // store original matrix before we reset, so we can use it to transform positions
343 SkMatrix ctm = fContext->getMatrix();
344 GrContext::AutoMatrix autoMatrix;
345 autoMatrix.setIdentity(fContext, &fPaint);
347 const char* stop = text + byteLength;
348 AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign());
349 BitmapTextMapState tms(ctm, constY);
350 BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
351 SkFixed halfSampleX = 0, halfSampleY = 0;
353 if (cache->isSubpixel()) {
354 // maybe we should skip the rounding if linearText is set
355 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
359 if (kX_SkAxisAlignment == baseline) {
361 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
362 halfSampleY = SK_FixedHalf;
364 } else if (kY_SkAxisAlignment == baseline) {
366 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
367 halfSampleX = SK_FixedHalf;
371 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
372 while (text < stop) {
374 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX;
375 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY;
377 const SkGlyph& glyph = glyphCacheProc(cache, &text,
378 fx & fxMask, fy & fyMask);
381 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
382 glyph.getSubXFixed(),
383 glyph.getSubYFixed()),
384 SkFixedFloorToFixed(fx),
385 SkFixedFloorToFixed(fy),
388 pos += scalarsPerPosition;
391 while (text < stop) {
392 const char* currentText = text;
393 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
395 if (metricGlyph.fWidth) {
396 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
397 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
401 alignProc(tms.fLoc, metricGlyph, &fixedLoc);
403 SkFixed fx = fixedLoc.fX + halfSampleX;
404 SkFixed fy = fixedLoc.fY + halfSampleY;
406 // have to call again, now that we've been "aligned"
407 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText,
408 fx & fxMask, fy & fyMask);
409 // the assumption is that the metrics haven't changed
410 SkASSERT(prevAdvX == glyph.fAdvanceX);
411 SkASSERT(prevAdvY == glyph.fAdvanceY);
412 SkASSERT(glyph.fWidth);
414 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
415 glyph.getSubXFixed(),
416 glyph.getSubYFixed()),
417 SkFixedFloorToFixed(fx),
418 SkFixedFloorToFixed(fy),
421 pos += scalarsPerPosition;
424 } else { // not subpixel
426 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
427 while (text < stop) {
428 // the last 2 parameters are ignored
429 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
434 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
435 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
436 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
437 glyph.getSubXFixed(),
438 glyph.getSubYFixed()),
439 SkFixedFloorToFixed(fx),
440 SkFixedFloorToFixed(fy),
443 pos += scalarsPerPosition;
446 while (text < stop) {
447 // the last 2 parameters are ignored
448 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
454 alignProc(tms.fLoc, glyph, &fixedLoc);
456 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
457 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
458 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
459 glyph.getSubXFixed(),
460 glyph.getSubYFixed()),
461 SkFixedFloorToFixed(fx),
462 SkFixedFloorToFixed(fy),
465 pos += scalarsPerPosition;
475 // position + texture coord
476 extern const GrVertexAttrib gTextVertexAttribs[] = {
477 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
478 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
483 void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
484 GrFixed vx, GrFixed vy,
485 GrFontScaler* scaler) {
486 if (NULL == fDrawTarget) {
490 if (NULL == fStrike) {
491 #if SK_DISTANCEFIELD_FONTS
492 fStrike = fContext->getFontCache()->getStrike(scaler, false);
494 fStrike = fContext->getFontCache()->getStrike(scaler);
498 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
499 if (NULL == glyph || glyph->fBounds.isEmpty()) {
503 vx += SkIntToFixed(glyph->fBounds.fLeft);
504 vy += SkIntToFixed(glyph->fBounds.fTop);
506 // keep them as ints until we've done the clip-test
507 GrFixed width = glyph->fBounds.width();
508 GrFixed height = glyph->fBounds.height();
510 // check if we clipped out
511 if (true || NULL == glyph->fPlot) {
514 if (fClipRect.quickReject(x, y, x + width, y + height)) {
515 // SkCLZ(3); // so we can set a break-point in the debugger
520 if (NULL == glyph->fPlot) {
521 if (fStrike->getGlyphAtlas(glyph, scaler)) {
525 // try to clear out an unused plot before we flush
526 fContext->getFontCache()->freePlotExceptFor(fStrike);
527 if (fStrike->getGlyphAtlas(glyph, scaler)) {
531 if (c_DumpFontCache) {
533 fContext->getFontCache()->dump();
537 // before we purge the cache, we must flush any accumulated draws
542 fContext->getFontCache()->purgeExceptFor(fStrike);
543 // need to use new flush count here
544 if (fStrike->getGlyphAtlas(glyph, scaler)) {
548 if (NULL == glyph->fPath) {
549 SkPath* path = SkNEW(SkPath);
550 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
551 // flag the glyph as being dead?
558 GrContext::AutoMatrix am;
560 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
561 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
562 GrPaint tmpPaint(fPaint);
563 am.setPreConcat(fContext, translate, &tmpPaint);
564 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
565 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
570 SkASSERT(glyph->fPlot);
571 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
572 glyph->fPlot->setDrawToken(drawToken);
574 // now promote them to fixed (TODO: Rethink using fixed pt).
575 width = SkIntToFixed(width);
576 height = SkIntToFixed(height);
578 GrTexture* texture = glyph->fPlot->texture();
581 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
583 fCurrTexture = texture;
587 if (NULL == fVertices) {
588 // If we need to reserve vertices allow the draw target to suggest
589 // a number of verts to reserve and whether to perform a flush.
590 fMaxVertices = kMinRequestedVerts;
591 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
592 SK_ARRAY_COUNT(gTextVertexAttribs));
593 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
597 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
598 SK_ARRAY_COUNT(gTextVertexAttribs));
600 fMaxVertices = kDefaultRequestedVerts;
601 // ignore return, no point in flushing again.
602 fDrawTarget->geometryHints(&fMaxVertices, NULL);
604 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
605 if (fMaxVertices < kMinRequestedVerts) {
606 fMaxVertices = kDefaultRequestedVerts;
607 } else if (fMaxVertices > maxQuadVertices) {
608 // don't exceed the limit of the index buffer
609 fMaxVertices = maxQuadVertices;
611 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
613 GrTCast<void**>(&fVertices),
615 GrAlwaysAssert(success);
616 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
619 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
620 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
623 r.fLeft = SkFixedToFloat(vx);
624 r.fTop = SkFixedToFloat(vy);
625 r.fRight = SkFixedToFloat(vx + width);
626 r.fBottom = SkFixedToFloat(vy + height);
628 fVertexBounds.growToInclude(r);
630 fVertices[2*fCurrVertex].setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom,
631 2 * sizeof(SkPoint));
632 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
633 SkFixedToFloat(texture->normalizeFixedY(ty)),
634 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
635 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
636 2 * sizeof(SkPoint));