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