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 "GrTexturePriv.h"
15 #include "GrTextStrike.h"
16 #include "GrTextStrike_impl.h"
17 #include "effects/GrCustomCoordsTextureEffect.h"
18 #include "effects/GrSimpleTextureEffect.h"
20 #include "SkAutoKern.h"
21 #include "SkColorPriv.h"
23 #include "SkDrawProcs.h"
24 #include "SkGlyphCache.h"
25 #include "SkGpuDevice.h"
29 #include "SkStrokeRec.h"
30 #include "SkTextMapStateProc.h"
32 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
33 "Dump the contents of the font cache before every purge.");
36 // position + texture coord
37 extern const GrVertexAttrib gLCDVertexAttribs[] = {
38 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
39 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
42 static const size_t kLCDTextVASize = 2 * sizeof(SkPoint);
44 // position + local coord
45 extern const GrVertexAttrib gColorVertexAttribs[] = {
46 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
47 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kLocalCoord_GrVertexAttribBinding}
50 static const size_t kColorTextVASize = 2 * sizeof(SkPoint);
52 // position + color + texture coord
53 extern const GrVertexAttrib gGrayVertexAttribs[] = {
54 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
55 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
56 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
59 static const size_t kGrayTextVASize = 2 * sizeof(SkPoint) + sizeof(GrColor);
61 static const int kVerticesPerGlyph = 4;
62 static const int kIndicesPerGlyph = 6;
65 GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
66 const SkDeviceProperties& properties)
67 : GrTextContext(context, properties) {
71 fEffectTextureUniqueID = SK_InvalidUniqueID;
75 fAllocVertexCount = 0;
76 fTotalVertexCount = 0;
78 fVertexBounds.setLargestInverted();
81 GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
82 const SkDeviceProperties& props) {
83 return SkNEW_ARGS(GrBitmapTextContext, (context, props));
86 GrBitmapTextContext::~GrBitmapTextContext() {
90 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
91 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
94 inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
95 GrTextContext::init(paint, skPaint);
103 fAllocVertexCount = 0;
104 fTotalVertexCount = 0;
107 void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
108 const char text[], size_t byteLength,
109 SkScalar x, SkScalar y) {
110 SkASSERT(byteLength == 0 || text != NULL);
113 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
117 this->init(paint, skPaint);
119 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
121 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
122 SkGlyphCache* cache = autoCache.getCache();
123 GrFontScaler* fontScaler = GetGrFontScaler(cache);
125 // transform our starting point
128 fContext->getMatrix().mapXY(x, y, &loc);
133 // need to measure first
135 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
137 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
139 SkScalar stopX = stopVector.fX;
140 SkScalar stopY = stopVector.fY;
142 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
143 stopX = SkScalarHalf(stopX);
144 stopY = SkScalarHalf(stopY);
149 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
151 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
153 const char* stop = text + byteLength;
159 SkFixed halfSampleX, halfSampleY;
160 if (cache->isSubpixel()) {
161 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
162 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
163 if (kX_SkAxisAlignment == baseline) {
165 halfSampleY = SK_FixedHalf;
166 } else if (kY_SkAxisAlignment == baseline) {
168 halfSampleX = SK_FixedHalf;
171 halfSampleX = halfSampleY = SK_FixedHalf;
174 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
175 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
177 GrContext::AutoMatrix autoMatrix;
178 autoMatrix.setIdentity(fContext, &fPaint);
180 while (text < stop) {
181 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
183 fx += autokern.adjust(glyph);
186 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
187 glyph.getSubXFixed(),
188 glyph.getSubYFixed()),
189 SkFixedFloorToFixed(fx),
190 SkFixedFloorToFixed(fy),
194 fx += glyph.fAdvanceX;
195 fy += glyph.fAdvanceY;
201 void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
202 const char text[], size_t byteLength,
203 const SkScalar pos[], int scalarsPerPosition,
204 const SkPoint& offset) {
205 SkASSERT(byteLength == 0 || text != NULL);
206 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
209 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
213 this->init(paint, skPaint);
215 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
217 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
218 SkGlyphCache* cache = autoCache.getCache();
219 GrFontScaler* fontScaler = GetGrFontScaler(cache);
221 // store original matrix before we reset, so we can use it to transform positions
222 SkMatrix ctm = fContext->getMatrix();
223 GrContext::AutoMatrix autoMatrix;
224 autoMatrix.setIdentity(fContext, &fPaint);
226 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
227 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
229 const char* stop = text + byteLength;
230 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
231 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
232 SkFixed halfSampleX = 0, halfSampleY = 0;
234 if (cache->isSubpixel()) {
235 // maybe we should skip the rounding if linearText is set
236 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
240 if (kX_SkAxisAlignment == baseline) {
242 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
243 halfSampleY = SK_FixedHalf;
245 } else if (kY_SkAxisAlignment == baseline) {
247 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
248 halfSampleX = SK_FixedHalf;
252 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
253 while (text < stop) {
255 tmsProc(pos, &tmsLoc);
256 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
257 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
259 const SkGlyph& glyph = glyphCacheProc(cache, &text,
260 fx & fxMask, fy & fyMask);
263 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
264 glyph.getSubXFixed(),
265 glyph.getSubYFixed()),
266 SkFixedFloorToFixed(fx),
267 SkFixedFloorToFixed(fy),
270 pos += scalarsPerPosition;
273 while (text < stop) {
274 const char* currentText = text;
275 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
277 if (metricGlyph.fWidth) {
278 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
279 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
281 tmsProc(pos, &tmsLoc);
283 alignProc(tmsLoc, metricGlyph, &fixedLoc);
285 SkFixed fx = fixedLoc.fX + halfSampleX;
286 SkFixed fy = fixedLoc.fY + halfSampleY;
288 // have to call again, now that we've been "aligned"
289 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText,
290 fx & fxMask, fy & fyMask);
291 // the assumption is that the metrics haven't changed
292 SkASSERT(prevAdvX == glyph.fAdvanceX);
293 SkASSERT(prevAdvY == glyph.fAdvanceY);
294 SkASSERT(glyph.fWidth);
296 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
297 glyph.getSubXFixed(),
298 glyph.getSubYFixed()),
299 SkFixedFloorToFixed(fx),
300 SkFixedFloorToFixed(fy),
303 pos += scalarsPerPosition;
306 } else { // not subpixel
308 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
309 while (text < stop) {
310 // the last 2 parameters are ignored
311 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
315 tmsProc(pos, &tmsLoc);
317 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
318 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
319 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
320 glyph.getSubXFixed(),
321 glyph.getSubYFixed()),
322 SkFixedFloorToFixed(fx),
323 SkFixedFloorToFixed(fy),
326 pos += scalarsPerPosition;
329 while (text < stop) {
330 // the last 2 parameters are ignored
331 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
335 tmsProc(pos, &tmsLoc);
338 alignProc(tmsLoc, glyph, &fixedLoc);
340 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
341 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
342 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
343 glyph.getSubXFixed(),
344 glyph.getSubYFixed()),
345 SkFixedFloorToFixed(fx),
346 SkFixedFloorToFixed(fy),
349 pos += scalarsPerPosition;
357 static void* alloc_vertices(GrDrawTarget* drawTarget, int numVertices, GrMaskFormat maskFormat) {
358 if (numVertices <= 0) {
363 if (kA8_GrMaskFormat == maskFormat) {
364 drawTarget->drawState()->setVertexAttribs<gGrayVertexAttribs>(
365 SK_ARRAY_COUNT(gGrayVertexAttribs), kGrayTextVASize);
366 } else if (kARGB_GrMaskFormat == maskFormat) {
367 drawTarget->drawState()->setVertexAttribs<gColorVertexAttribs>(
368 SK_ARRAY_COUNT(gColorVertexAttribs), kColorTextVASize);
370 drawTarget->drawState()->setVertexAttribs<gLCDVertexAttribs>(
371 SK_ARRAY_COUNT(gLCDVertexAttribs), kLCDTextVASize);
373 void* vertices = NULL;
374 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
378 GrAlwaysAssert(success);
382 void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
383 SkFixed vx, SkFixed vy,
384 GrFontScaler* scaler) {
385 if (NULL == fDrawTarget) {
389 if (NULL == fStrike) {
390 fStrike = fContext->getFontCache()->getStrike(scaler, false);
393 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
394 if (NULL == glyph || glyph->fBounds.isEmpty()) {
398 vx += SkIntToFixed(glyph->fBounds.fLeft);
399 vy += SkIntToFixed(glyph->fBounds.fTop);
401 // keep them as ints until we've done the clip-test
402 SkFixed width = glyph->fBounds.width();
403 SkFixed height = glyph->fBounds.height();
405 // check if we clipped out
406 if (true || NULL == glyph->fPlot) {
409 if (fClipRect.quickReject(x, y, x + width, y + height)) {
410 // SkCLZ(3); // so we can set a break-point in the debugger
415 if (NULL == glyph->fPlot) {
416 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
417 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
421 // try to clear out an unused plot before we flush
422 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
423 fStrike->addGlyphToAtlas(glyph, scaler)) {
427 if (c_DumpFontCache) {
429 fContext->getFontCache()->dump();
433 // flush any accumulated draws to allow us to free up a plot
437 // we should have an unused plot now
438 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
439 fStrike->addGlyphToAtlas(glyph, scaler)) {
444 if (NULL == glyph->fPath) {
445 SkPath* path = SkNEW(SkPath);
446 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
447 // flag the glyph as being dead?
454 // flush any accumulated draws before drawing this glyph as a path.
457 GrContext::AutoMatrix am;
459 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
460 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
461 GrPaint tmpPaint(fPaint);
462 am.setPreConcat(fContext, translate, &tmpPaint);
463 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
464 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
466 // remove this glyph from the vertices we need to allocate
467 fTotalVertexCount -= kVerticesPerGlyph;
472 SkASSERT(glyph->fPlot);
473 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
474 glyph->fPlot->setDrawToken(drawToken);
476 // now promote them to fixed (TODO: Rethink using fixed pt).
477 width = SkIntToFixed(width);
478 height = SkIntToFixed(height);
480 // the current texture/maskformat must match what the glyph needs
481 GrTexture* texture = glyph->fPlot->texture();
484 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
486 fCurrTexture = texture;
488 fCurrMaskFormat = glyph->fMaskFormat;
491 if (NULL == fVertices) {
492 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
493 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
494 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
497 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
498 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
501 r.fLeft = SkFixedToFloat(vx);
502 r.fTop = SkFixedToFloat(vy);
503 r.fRight = SkFixedToFloat(vx + width);
504 r.fBottom = SkFixedToFloat(vy + height);
506 fVertexBounds.joinNonEmptyArg(r);
509 switch (fCurrMaskFormat) {
510 case kA8_GrMaskFormat:
511 vertSize = kGrayTextVASize;
513 case kARGB_GrMaskFormat:
514 vertSize = kColorTextVASize;
516 vertSize = kLCDTextVASize;
519 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
521 SkPoint* positions = reinterpret_cast<SkPoint*>(
522 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
523 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
525 // The texture coords are last in both the with and without color vertex layouts.
526 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
527 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
528 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
529 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
530 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
531 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
533 if (kA8_GrMaskFormat == fCurrMaskFormat) {
534 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
535 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
537 // color comes after position.
538 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
539 for (int i = 0; i < 4; ++i) {
540 *colors = fPaint.getColor();
541 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
547 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
548 unsigned r = SkColorGetR(c);
549 unsigned g = SkColorGetG(c);
550 unsigned b = SkColorGetB(c);
551 return GrColorPackRGBA(r, g, b, 0xff);
554 void GrBitmapTextContext::flush() {
555 if (NULL == fDrawTarget) {
559 GrDrawState* drawState = fDrawTarget->drawState();
560 GrDrawState::AutoRestoreEffects are(drawState);
561 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
563 if (fCurrVertex > 0) {
564 // setup our sampler state for our text texture/atlas
565 SkASSERT(SkIsAlign4(fCurrVertex));
566 SkASSERT(fCurrTexture);
567 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
569 // This effect could be stored with one of the cache objects (atlas?)
570 if (kARGB_GrMaskFormat == fCurrMaskFormat) {
571 GrFragmentProcessor* fragProcessor = GrSimpleTextureEffect::Create(fCurrTexture,
574 drawState->addColorProcessor(fragProcessor)->unref();
576 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
577 if (textureUniqueID != fEffectTextureUniqueID) {
578 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
580 fEffectTextureUniqueID = textureUniqueID;
583 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
587 switch (fCurrMaskFormat) {
589 case kARGB_GrMaskFormat:
590 SkASSERT(!drawState->hasColorVertexAttribute());
591 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
592 drawState->setAlpha(fSkPaint.getAlpha());
595 case kA888_GrMaskFormat:
596 case kA565_GrMaskFormat: {
597 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
598 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
599 fPaint.numColorStages()) {
600 SkDebugf("LCD Text will not draw correctly.\n");
602 SkASSERT(!drawState->hasColorVertexAttribute());
603 // We don't use the GrPaint's color in this case because it's been premultiplied by
604 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
605 // the mask texture color. The end result is that we get
606 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
607 int a = SkColorGetA(fSkPaint.getColor());
609 drawState->setColor(SkColorSetARGB(a, a, a, a));
611 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
612 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
616 case kA8_GrMaskFormat:
617 // set back to normal in case we took LCD path previously.
618 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
619 // We're using per-vertex color.
620 SkASSERT(drawState->hasColorVertexAttribute());
623 SkFAIL("Unexpected mask format.");
625 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
626 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
627 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
629 kVerticesPerGlyph, kIndicesPerGlyph, &fVertexBounds);
631 fDrawTarget->resetVertexSource();
633 fAllocVertexCount = 0;
634 // reset to be those that are left
635 fTotalVertexCount -= fCurrVertex;
637 fVertexBounds.setLargestInverted();
638 SkSafeSetNull(fCurrTexture);
642 inline void GrBitmapTextContext::finish() {
644 fTotalVertexCount = 0;
646 GrTextContext::finish();