Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2013 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 
      8 #include "GrBitmapTextContext.h"
      9 #include "GrAtlas.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"
     16 #include "SkPath.h"
     17 #include "SkRTConf.h"
     18 #include "SkStrokeRec.h"
     19 #include "effects/GrCustomCoordsTextureEffect.h"
     20 
     21 static const int kGlyphCoordsAttributeIndex = 1;
     22 
     23 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
     24                 "Dump the contents of the font cache before every purge.");
     25 
     26 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, const GrPaint& paint,
     27                                          SkColor color) :
     28                                          GrTextContext(context, paint) {
     29     fAutoMatrix.setIdentity(fContext, &fPaint);
     30 
     31     fSkPaintColor = color;
     32 
     33     fStrike = NULL;
     34 
     35     fCurrTexture = NULL;
     36     fCurrVertex = 0;
     37 
     38     fVertices = NULL;
     39     fMaxVertices = 0;
     40 }
     41 
     42 GrBitmapTextContext::~GrBitmapTextContext() {
     43     this->flushGlyphs();
     44 }
     45 
     46 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
     47     unsigned r = SkColorGetR(c);
     48     unsigned g = SkColorGetG(c);
     49     unsigned b = SkColorGetB(c);
     50     return GrColorPackRGBA(r, g, b, 0xff);
     51 }
     52 
     53 void GrBitmapTextContext::flushGlyphs() {
     54     if (NULL == fDrawTarget) {
     55         return;
     56     }
     57 
     58     GrDrawState* drawState = fDrawTarget->drawState();
     59     GrDrawState::AutoRestoreEffects are(drawState);
     60     drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
     61 
     62     if (fCurrVertex > 0) {
     63         // setup our sampler state for our text texture/atlas
     64         SkASSERT(GrIsALIGN4(fCurrVertex));
     65         SkASSERT(fCurrTexture);
     66         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
     67 
     68         // This effect could be stored with one of the cache objects (atlas?)
     69         drawState->addCoverageEffect(
     70                                 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
     71                                 kGlyphCoordsAttributeIndex)->unref();
     72 
     73         if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
     74             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
     75             drawState->setColor(0xffffffff);
     76         } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
     77             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
     78                 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
     79                 fPaint.numColorStages()) {
     80                 GrPrintf("LCD Text will not draw correctly.\n");
     81             }
     82             // We don't use the GrPaint's color in this case because it's been premultiplied by
     83             // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
     84             // the mask texture color. The end result is that we get
     85             //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
     86             int a = SkColorGetA(fSkPaintColor);
     87             // paintAlpha
     88             drawState->setColor(SkColorSetARGB(a, a, a, a));
     89             // paintColor
     90             drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
     91             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
     92         } else {
     93             // set back to normal in case we took LCD path previously.
     94             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
     95             drawState->setColor(fPaint.getColor());
     96         }
     97 
     98         int nGlyphs = fCurrVertex / 4;
     99         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
    100         fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
    101                                           nGlyphs,
    102                                           4, 6);
    103 
    104         fDrawTarget->resetVertexSource();
    105         fVertices = NULL;
    106         fMaxVertices = 0;
    107         fCurrVertex = 0;
    108         SkSafeSetNull(fCurrTexture);
    109     }
    110 }
    111 
    112 namespace {
    113 
    114 // position + texture coord
    115 extern const GrVertexAttrib gTextVertexAttribs[] = {
    116     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
    117     {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
    118 };
    119 
    120 };
    121 
    122 void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
    123                                           GrFixed vx, GrFixed vy,
    124                                           GrFontScaler* scaler) {
    125     if (NULL == fDrawTarget) {
    126         return;
    127     }
    128     if (NULL == fStrike) {
    129 #if SK_DISTANCEFIELD_FONTS
    130         fStrike = fContext->getFontCache()->getStrike(scaler, false);
    131 #else
    132         fStrike = fContext->getFontCache()->getStrike(scaler);
    133 #endif
    134     }
    135 
    136     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
    137     if (NULL == glyph || glyph->fBounds.isEmpty()) {
    138         return;
    139     }
    140 
    141     vx += SkIntToFixed(glyph->fBounds.fLeft);
    142     vy += SkIntToFixed(glyph->fBounds.fTop);
    143 
    144     // keep them as ints until we've done the clip-test
    145     GrFixed width = glyph->fBounds.width();
    146     GrFixed height = glyph->fBounds.height();
    147 
    148     // check if we clipped out
    149     if (true || NULL == glyph->fPlot) {
    150         int x = vx >> 16;
    151         int y = vy >> 16;
    152         if (fClipRect.quickReject(x, y, x + width, y + height)) {
    153 //            SkCLZ(3);    // so we can set a break-point in the debugger
    154             return;
    155         }
    156     }
    157 
    158     if (NULL == glyph->fPlot) {
    159         if (fStrike->getGlyphAtlas(glyph, scaler)) {
    160             goto HAS_ATLAS;
    161         }
    162 
    163         // try to clear out an unused plot before we flush
    164         fContext->getFontCache()->freePlotExceptFor(fStrike);
    165         if (fStrike->getGlyphAtlas(glyph, scaler)) {
    166             goto HAS_ATLAS;
    167         }
    168 
    169         if (c_DumpFontCache) {
    170 #ifdef SK_DEVELOPER
    171             fContext->getFontCache()->dump();
    172 #endif
    173         }
    174 
    175         // before we purge the cache, we must flush any accumulated draws
    176         this->flushGlyphs();
    177         fContext->flush();
    178 
    179         // try to purge
    180         fContext->getFontCache()->purgeExceptFor(fStrike);
    181         // need to use new flush count here
    182         if (fStrike->getGlyphAtlas(glyph, scaler)) {
    183             goto HAS_ATLAS;
    184         }
    185 
    186         if (NULL == glyph->fPath) {
    187             SkPath* path = SkNEW(SkPath);
    188             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
    189                 // flag the glyph as being dead?
    190                 delete path;
    191                 return;
    192             }
    193             glyph->fPath = path;
    194         }
    195 
    196         GrContext::AutoMatrix am;
    197         SkMatrix translate;
    198         translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
    199                                SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
    200         GrPaint tmpPaint(fPaint);
    201         am.setPreConcat(fContext, translate, &tmpPaint);
    202         SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
    203         fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
    204         return;
    205     }
    206 
    207 HAS_ATLAS:
    208     SkASSERT(glyph->fPlot);
    209     GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
    210     glyph->fPlot->setDrawToken(drawToken);
    211 
    212     // now promote them to fixed (TODO: Rethink using fixed pt).
    213     width = SkIntToFixed(width);
    214     height = SkIntToFixed(height);
    215 
    216     GrTexture* texture = glyph->fPlot->texture();
    217     SkASSERT(texture);
    218 
    219     if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
    220         this->flushGlyphs();
    221         fCurrTexture = texture;
    222         fCurrTexture->ref();
    223     }
    224 
    225     if (NULL == fVertices) {
    226        // If we need to reserve vertices allow the draw target to suggest
    227         // a number of verts to reserve and whether to perform a flush.
    228         fMaxVertices = kMinRequestedVerts;
    229         fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
    230             SK_ARRAY_COUNT(gTextVertexAttribs));
    231         bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
    232         if (flush) {
    233             this->flushGlyphs();
    234             fContext->flush();
    235             fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
    236                 SK_ARRAY_COUNT(gTextVertexAttribs));
    237         }
    238         fMaxVertices = kDefaultRequestedVerts;
    239         // ignore return, no point in flushing again.
    240         fDrawTarget->geometryHints(&fMaxVertices, NULL);
    241 
    242         int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
    243         if (fMaxVertices < kMinRequestedVerts) {
    244             fMaxVertices = kDefaultRequestedVerts;
    245         } else if (fMaxVertices > maxQuadVertices) {
    246             // don't exceed the limit of the index buffer
    247             fMaxVertices = maxQuadVertices;
    248         }
    249         bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
    250                                                                0,
    251                                                                GrTCast<void**>(&fVertices),
    252                                                                NULL);
    253         GrAlwaysAssert(success);
    254         SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
    255     }
    256 
    257     GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
    258     GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
    259 
    260     fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
    261                                         SkFixedToFloat(vy),
    262                                         SkFixedToFloat(vx + width),
    263                                         SkFixedToFloat(vy + height),
    264                                         2 * sizeof(SkPoint));
    265     fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
    266                                           SkFixedToFloat(texture->normalizeFixedY(ty)),
    267                                           SkFixedToFloat(texture->normalizeFixedX(tx + width)),
    268                                           SkFixedToFloat(texture->normalizeFixedY(ty + height)),
    269                                           2 * sizeof(SkPoint));
    270     fCurrVertex += 4;
    271 }
    272