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 "GrStrokeInfo.h"
14 #include "GrTextStrike.h"
15 #include "GrTextStrike_impl.h"
16 #include "SkColorPriv.h"
19 #include "SkStrokeRec.h"
20 #include "effects/GrCustomCoordsTextureEffect.h"
22 #include "SkAutoKern.h"
24 #include "SkDrawProcs.h"
25 #include "SkGlyphCache.h"
26 #include "SkGpuDevice.h"
28 #include "SkTextMapStateProc.h"
30 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
31 "Dump the contents of the font cache before every purge.");
34 // position + texture coord
35 extern const GrVertexAttrib gTextVertexAttribs[] = {
36 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
37 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
40 static const size_t kTextVASize = 2 * sizeof(SkPoint);
42 // position + color + texture coord
43 extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
44 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
45 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
46 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
49 static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
53 GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
54 const SkDeviceProperties& properties)
55 : GrTextContext(context, properties) {
60 fEffectTextureUniqueID = SK_InvalidUniqueID;
65 fVertexBounds.setLargestInverted();
68 GrBitmapTextContext::~GrBitmapTextContext() {
72 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
73 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
76 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
77 unsigned r = SkColorGetR(c);
78 unsigned g = SkColorGetG(c);
79 unsigned b = SkColorGetB(c);
80 return GrColorPackRGBA(r, g, b, 0xff);
83 void GrBitmapTextContext::flushGlyphs() {
84 if (NULL == fDrawTarget) {
88 GrDrawState* drawState = fDrawTarget->drawState();
89 GrDrawState::AutoRestoreEffects are(drawState);
90 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
92 if (fCurrVertex > 0) {
93 // setup our sampler state for our text texture/atlas
94 SkASSERT(SkIsAlign4(fCurrVertex));
95 SkASSERT(fCurrTexture);
96 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
98 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
100 if (textureUniqueID != fEffectTextureUniqueID) {
101 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
103 fEffectTextureUniqueID = textureUniqueID;
106 // This effect could be stored with one of the cache objects (atlas?)
107 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
109 switch (fStrike->getMaskFormat()) {
111 case kARGB_GrMaskFormat:
112 SkASSERT(!drawState->hasColorVertexAttribute());
113 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
114 drawState->setColor(0xffffffff);
117 case kA888_GrMaskFormat:
118 case kA565_GrMaskFormat: {
119 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
120 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
121 fPaint.numColorStages()) {
122 GrPrintf("LCD Text will not draw correctly.\n");
124 SkASSERT(!drawState->hasColorVertexAttribute());
125 // We don't use the GrPaint's color in this case because it's been premultiplied by
126 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
127 // the mask texture color. The end result is that we get
128 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
129 int a = SkColorGetA(fSkPaint.getColor());
131 drawState->setColor(SkColorSetARGB(a, a, a, a));
133 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
134 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
138 case kA8_GrMaskFormat:
139 // set back to normal in case we took LCD path previously.
140 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
141 // We're using per-vertex color.
142 SkASSERT(drawState->hasColorVertexAttribute());
145 SkFAIL("Unexepected mask format.");
147 int nGlyphs = fCurrVertex / 4;
148 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
149 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
151 4, 6, &fVertexBounds);
153 fDrawTarget->resetVertexSource();
157 fVertexBounds.setLargestInverted();
158 SkSafeSetNull(fCurrTexture);
162 inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
163 GrTextContext::init(paint, skPaint);
174 inline void GrBitmapTextContext::finish() {
177 GrTextContext::finish();
180 void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
181 const char text[], size_t byteLength,
182 SkScalar x, SkScalar y) {
183 SkASSERT(byteLength == 0 || text != NULL);
186 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
190 this->init(paint, skPaint);
192 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
194 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
195 SkGlyphCache* cache = autoCache.getCache();
196 GrFontScaler* fontScaler = GetGrFontScaler(cache);
198 // transform our starting point
201 fContext->getMatrix().mapXY(x, y, &loc);
206 // need to measure first
207 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
210 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
212 SkScalar stopX = stop.fX;
213 SkScalar stopY = stop.fY;
215 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
216 stopX = SkScalarHalf(stopX);
217 stopY = SkScalarHalf(stopY);
223 const char* stop = text + byteLength;
229 SkFixed halfSampleX, halfSampleY;
230 if (cache->isSubpixel()) {
231 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
232 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
233 if (kX_SkAxisAlignment == baseline) {
235 halfSampleY = SK_FixedHalf;
236 } else if (kY_SkAxisAlignment == baseline) {
238 halfSampleX = SK_FixedHalf;
241 halfSampleX = halfSampleY = SK_FixedHalf;
244 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
245 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
247 GrContext::AutoMatrix autoMatrix;
248 autoMatrix.setIdentity(fContext, &fPaint);
250 while (text < stop) {
251 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
253 fx += autokern.adjust(glyph);
256 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
257 glyph.getSubXFixed(),
258 glyph.getSubYFixed()),
259 SkFixedFloorToFixed(fx),
260 SkFixedFloorToFixed(fy),
264 fx += glyph.fAdvanceX;
265 fy += glyph.fAdvanceY;
271 void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
272 const char text[], size_t byteLength,
273 const SkScalar pos[], SkScalar constY,
274 int scalarsPerPosition) {
275 SkASSERT(byteLength == 0 || text != NULL);
276 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
279 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
283 this->init(paint, skPaint);
285 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
287 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
288 SkGlyphCache* cache = autoCache.getCache();
289 GrFontScaler* fontScaler = GetGrFontScaler(cache);
291 // store original matrix before we reset, so we can use it to transform positions
292 SkMatrix ctm = fContext->getMatrix();
293 GrContext::AutoMatrix autoMatrix;
294 autoMatrix.setIdentity(fContext, &fPaint);
296 const char* stop = text + byteLength;
297 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
298 SkTextMapStateProc tmsProc(ctm, constY, scalarsPerPosition);
299 SkFixed halfSampleX = 0, halfSampleY = 0;
301 if (cache->isSubpixel()) {
302 // maybe we should skip the rounding if linearText is set
303 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
307 if (kX_SkAxisAlignment == baseline) {
309 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
310 halfSampleY = SK_FixedHalf;
312 } else if (kY_SkAxisAlignment == baseline) {
314 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
315 halfSampleX = SK_FixedHalf;
319 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
320 while (text < stop) {
322 tmsProc(pos, &tmsLoc);
323 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
324 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
326 const SkGlyph& glyph = glyphCacheProc(cache, &text,
327 fx & fxMask, fy & fyMask);
330 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
331 glyph.getSubXFixed(),
332 glyph.getSubYFixed()),
333 SkFixedFloorToFixed(fx),
334 SkFixedFloorToFixed(fy),
337 pos += scalarsPerPosition;
340 while (text < stop) {
341 const char* currentText = text;
342 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
344 if (metricGlyph.fWidth) {
345 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
346 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
348 tmsProc(pos, &tmsLoc);
350 alignProc(tmsLoc, metricGlyph, &fixedLoc);
352 SkFixed fx = fixedLoc.fX + halfSampleX;
353 SkFixed fy = fixedLoc.fY + halfSampleY;
355 // have to call again, now that we've been "aligned"
356 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText,
357 fx & fxMask, fy & fyMask);
358 // the assumption is that the metrics haven't changed
359 SkASSERT(prevAdvX == glyph.fAdvanceX);
360 SkASSERT(prevAdvY == glyph.fAdvanceY);
361 SkASSERT(glyph.fWidth);
363 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
364 glyph.getSubXFixed(),
365 glyph.getSubYFixed()),
366 SkFixedFloorToFixed(fx),
367 SkFixedFloorToFixed(fy),
370 pos += scalarsPerPosition;
373 } else { // not subpixel
375 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
376 while (text < stop) {
377 // the last 2 parameters are ignored
378 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
382 tmsProc(pos, &tmsLoc);
384 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
385 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
386 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
387 glyph.getSubXFixed(),
388 glyph.getSubYFixed()),
389 SkFixedFloorToFixed(fx),
390 SkFixedFloorToFixed(fy),
393 pos += scalarsPerPosition;
396 while (text < stop) {
397 // the last 2 parameters are ignored
398 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
402 tmsProc(pos, &tmsLoc);
405 alignProc(tmsLoc, glyph, &fixedLoc);
407 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
408 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
409 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
410 glyph.getSubXFixed(),
411 glyph.getSubYFixed()),
412 SkFixedFloorToFixed(fx),
413 SkFixedFloorToFixed(fy),
416 pos += scalarsPerPosition;
424 void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
425 SkFixed vx, SkFixed vy,
426 GrFontScaler* scaler) {
427 if (NULL == fDrawTarget) {
431 if (NULL == fStrike) {
432 fStrike = fContext->getFontCache()->getStrike(scaler, false);
435 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
436 if (NULL == glyph || glyph->fBounds.isEmpty()) {
440 vx += SkIntToFixed(glyph->fBounds.fLeft);
441 vy += SkIntToFixed(glyph->fBounds.fTop);
443 // keep them as ints until we've done the clip-test
444 SkFixed width = glyph->fBounds.width();
445 SkFixed height = glyph->fBounds.height();
447 // check if we clipped out
448 if (true || NULL == glyph->fPlot) {
451 if (fClipRect.quickReject(x, y, x + width, y + height)) {
452 // SkCLZ(3); // so we can set a break-point in the debugger
457 if (NULL == glyph->fPlot) {
458 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
459 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
463 // try to clear out an unused plot before we flush
464 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
465 fStrike->addGlyphToAtlas(glyph, scaler)) {
469 if (c_DumpFontCache) {
471 fContext->getFontCache()->dump();
475 // flush any accumulated draws to allow us to free up a plot
479 // we should have an unused plot now
480 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
481 fStrike->addGlyphToAtlas(glyph, scaler)) {
486 if (NULL == glyph->fPath) {
487 SkPath* path = SkNEW(SkPath);
488 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
489 // flag the glyph as being dead?
496 GrContext::AutoMatrix am;
498 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
499 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
500 GrPaint tmpPaint(fPaint);
501 am.setPreConcat(fContext, translate, &tmpPaint);
502 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
503 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
508 SkASSERT(glyph->fPlot);
509 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
510 glyph->fPlot->setDrawToken(drawToken);
512 // now promote them to fixed (TODO: Rethink using fixed pt).
513 width = SkIntToFixed(width);
514 height = SkIntToFixed(height);
516 GrTexture* texture = glyph->fPlot->texture();
519 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
521 fCurrTexture = texture;
525 bool useColorVerts = kA8_GrMaskFormat == fStrike->getMaskFormat();
527 if (NULL == fVertices) {
528 // If we need to reserve vertices allow the draw target to suggest
529 // a number of verts to reserve and whether to perform a flush.
530 fMaxVertices = kMinRequestedVerts;
532 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
533 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
535 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
536 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
538 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
543 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
544 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
546 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
547 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
550 fMaxVertices = kDefaultRequestedVerts;
551 // ignore return, no point in flushing again.
552 fDrawTarget->geometryHints(&fMaxVertices, NULL);
554 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
555 if (fMaxVertices < kMinRequestedVerts) {
556 fMaxVertices = kDefaultRequestedVerts;
557 } else if (fMaxVertices > maxQuadVertices) {
558 // don't exceed the limit of the index buffer
559 fMaxVertices = maxQuadVertices;
561 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
565 GrAlwaysAssert(success);
568 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
569 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
572 r.fLeft = SkFixedToFloat(vx);
573 r.fTop = SkFixedToFloat(vy);
574 r.fRight = SkFixedToFloat(vx + width);
575 r.fBottom = SkFixedToFloat(vy + height);
577 fVertexBounds.growToInclude(r);
579 size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
580 (2 * sizeof(SkPoint));
582 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
584 SkPoint* positions = reinterpret_cast<SkPoint*>(
585 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
586 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
588 // The texture coords are last in both the with and without color vertex layouts.
589 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
590 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
591 textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
592 SkFixedToFloat(texture->normalizeFixedY(ty)),
593 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
594 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
597 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
598 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
600 // color comes after position.
601 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
602 for (int i = 0; i < 4; ++i) {
603 *colors = fPaint.getColor();
604 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);