Home | History | Annotate | Download | only in gpu
      1 
      2 /*
      3  * Copyright 2010 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 
     11 #include "GrTextContext.h"
     12 #include "GrAtlas.h"
     13 #include "GrContext.h"
     14 #include "GrTextStrike.h"
     15 #include "GrTextStrike_impl.h"
     16 #include "GrFontScaler.h"
     17 #include "GrIndexBuffer.h"
     18 #include "GrGpuVertex.h"
     19 #include "GrDrawTarget.h"
     20 
     21 enum {
     22     kGlyphMaskStage = GrPaint::kTotalStages,
     23 };
     24 
     25 void GrTextContext::flushGlyphs() {
     26     if (fCurrVertex > 0) {
     27         GrDrawTarget::AutoStateRestore asr(fDrawTarget);
     28         GrDrawState* drawState = fDrawTarget->drawState();
     29         // setup our sampler state for our text texture/atlas
     30         GrSamplerState::Filter filter;
     31         if (fExtMatrix.isIdentity()) {
     32             filter = GrSamplerState::kNearest_Filter;
     33         } else {
     34             filter = GrSamplerState::kBilinear_Filter;
     35         }
     36         drawState->sampler(kGlyphMaskStage)->reset(
     37             GrSamplerState::kRepeat_WrapMode,filter);
     38 
     39         GrAssert(GrIsALIGN4(fCurrVertex));
     40         int nIndices = fCurrVertex + (fCurrVertex >> 1);
     41         GrAssert(fCurrTexture);
     42         drawState->setTexture(kGlyphMaskStage, fCurrTexture);
     43 
     44         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
     45             if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
     46                 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
     47                 fPaint.hasTexture()) {
     48                 GrPrintf("LCD Text will not draw correctly.\n");
     49             }
     50             // setup blend so that we get mask * paintColor + (1-mask)*dstColor
     51             drawState->setBlendConstant(fPaint.fColor);
     52             drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
     53             // don't modulate by the paint's color in the frag since we're
     54             // already doing it via the blend const.
     55             drawState->setColor(0xffffffff);
     56         } else {
     57             // set back to normal in case we took LCD path previously.
     58             drawState->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
     59             drawState->setColor(fPaint.fColor);
     60         }
     61 
     62         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
     63 
     64         fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
     65                                  0, 0, fCurrVertex, nIndices);
     66         fDrawTarget->resetVertexSource();
     67         fVertices = NULL;
     68         fMaxVertices = 0;
     69         fCurrVertex = 0;
     70         fCurrTexture->unref();
     71         fCurrTexture = NULL;
     72     }
     73 }
     74 
     75 GrTextContext::GrTextContext(GrContext* context,
     76                              const GrPaint& paint,
     77                              const GrMatrix* extMatrix) : fPaint(paint) {
     78     fContext = context;
     79     fStrike = NULL;
     80 
     81     fCurrTexture = NULL;
     82     fCurrVertex = 0;
     83 
     84     if (NULL != extMatrix) {
     85         fExtMatrix = *extMatrix;
     86     } else {
     87         fExtMatrix = GrMatrix::I();
     88     }
     89     if (context->getClip().hasConservativeBounds()) {
     90         if (!fExtMatrix.isIdentity()) {
     91             GrMatrix inverse;
     92             GrRect r = context->getClip().getConservativeBounds();
     93             if (fExtMatrix.invert(&inverse)) {
     94                 inverse.mapRect(&r);
     95                 r.roundOut(&fClipRect);
     96             }
     97         } else {
     98             context->getClip().getConservativeBounds().roundOut(&fClipRect);
     99         }
    100     } else {
    101         fClipRect.setLargest();
    102     }
    103 
    104     // save the context's original matrix off and restore in destructor
    105     // this must be done before getTextTarget.
    106     fOrigViewMatrix = fContext->getMatrix();
    107     fContext->setMatrix(fExtMatrix);
    108 
    109     /*
    110      We need to call preConcatMatrix with our viewmatrix's inverse, for each
    111      texture and mask in the paint. However, computing the inverse can be
    112      expensive, and its possible we may not have any textures or masks, so these
    113      two loops are written such that we only compute the inverse (once) if we
    114      need it. We do this on our copy of the paint rather than directly on the
    115      draw target because we re-provide the paint to the context when we have
    116      to flush our glyphs or draw a glyph as a path midstream.
    117     */
    118     bool invVMComputed = false;
    119     GrMatrix invVM;
    120     for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
    121         if (NULL != fPaint.getTexture(t)) {
    122             if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
    123                 invVMComputed = true;
    124                 fPaint.textureSampler(t)->preConcatMatrix(invVM);
    125             }
    126         }
    127     }
    128     for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
    129         if (NULL != fPaint.getMask(m)) {
    130             if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
    131                 invVMComputed = true;
    132                 fPaint.maskSampler(m)->preConcatMatrix(invVM);
    133             }
    134         }
    135     }
    136 
    137     fDrawTarget = fContext->getTextTarget(fPaint);
    138 
    139     fVertices = NULL;
    140     fMaxVertices = 0;
    141 
    142     fVertexLayout =
    143         GrDrawTarget::kTextFormat_VertexLayoutBit |
    144         GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
    145 
    146     int stageMask = paint.getActiveStageMask();
    147     if (stageMask) {
    148         for (int i = 0; i < GrPaint::kTotalStages; ++i) {
    149             if ((1 << i) & stageMask) {
    150                 fVertexLayout |=
    151                     GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
    152                 GrAssert(i != kGlyphMaskStage);
    153             }
    154         }
    155     }
    156 }
    157 
    158 GrTextContext::~GrTextContext() {
    159     this->flushGlyphs();
    160     fContext->setMatrix(fOrigViewMatrix);
    161 }
    162 
    163 void GrTextContext::flush() {
    164     this->flushGlyphs();
    165 }
    166 
    167 static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
    168                               int stride) {
    169     v[0 * stride].setI(l, t);
    170     v[1 * stride].setI(l, b);
    171     v[2 * stride].setI(r, b);
    172     v[3 * stride].setI(r, t);
    173 }
    174 
    175 void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
    176                                     GrFixed vx, GrFixed vy,
    177                                     GrFontScaler* scaler) {
    178     if (NULL == fStrike) {
    179         fStrike = fContext->getFontCache()->getStrike(scaler);
    180     }
    181 
    182     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
    183     if (NULL == glyph || glyph->fBounds.isEmpty()) {
    184         return;
    185     }
    186 
    187     vx += GrIntToFixed(glyph->fBounds.fLeft);
    188     vy += GrIntToFixed(glyph->fBounds.fTop);
    189 
    190     // keep them as ints until we've done the clip-test
    191     GrFixed width = glyph->fBounds.width();
    192     GrFixed height = glyph->fBounds.height();
    193 
    194     // check if we clipped out
    195     if (true || NULL == glyph->fAtlas) {
    196         int x = vx >> 16;
    197         int y = vy >> 16;
    198         if (fClipRect.quickReject(x, y, x + width, y + height)) {
    199 //            Gr_clz(3);    // so we can set a break-point in the debugger
    200             return;
    201         }
    202     }
    203 
    204     if (NULL == glyph->fAtlas) {
    205         if (fStrike->getGlyphAtlas(glyph, scaler)) {
    206             goto HAS_ATLAS;
    207         }
    208 
    209         // before we purge the cache, we must flush any accumulated draws
    210         this->flushGlyphs();
    211         fContext->flushText();
    212 
    213         // try to purge
    214         fContext->getFontCache()->purgeExceptFor(fStrike);
    215         if (fStrike->getGlyphAtlas(glyph, scaler)) {
    216             goto HAS_ATLAS;
    217         }
    218 
    219         if (NULL == glyph->fPath) {
    220             GrPath* path = new GrPath;
    221             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
    222                 // flag the glyph as being dead?
    223                 delete path;
    224                 return;
    225             }
    226             glyph->fPath = path;
    227         }
    228 
    229         GrPoint translate;
    230         translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
    231                       GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
    232         fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
    233                            &translate);
    234         return;
    235     }
    236 
    237 HAS_ATLAS:
    238     GrAssert(glyph->fAtlas);
    239 
    240     // now promote them to fixed
    241     width = GrIntToFixed(width);
    242     height = GrIntToFixed(height);
    243 
    244     GrTexture* texture = glyph->fAtlas->texture();
    245     GrAssert(texture);
    246 
    247     if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
    248         this->flushGlyphs();
    249         fCurrTexture = texture;
    250         fCurrTexture->ref();
    251     }
    252 
    253     if (NULL == fVertices) {
    254         // If we need to reserve vertices allow the draw target to suggest
    255         // a number of verts to reserve and whether to perform a flush.
    256         fMaxVertices = kMinRequestedVerts;
    257         bool flush = fDrawTarget->geometryHints(fVertexLayout,
    258                                                &fMaxVertices,
    259                                                NULL);
    260         if (flush) {
    261             this->flushGlyphs();
    262             fContext->flushText();
    263             fDrawTarget = fContext->getTextTarget(fPaint);
    264             fMaxVertices = kDefaultRequestedVerts;
    265             // ignore return, no point in flushing again.
    266             fDrawTarget->geometryHints(fVertexLayout,
    267                                        &fMaxVertices,
    268                                        NULL);
    269         }
    270 
    271         int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
    272         if (fMaxVertices < kMinRequestedVerts) {
    273             fMaxVertices = kDefaultRequestedVerts;
    274         } else if (fMaxVertices > maxQuadVertices) {
    275             // don't exceed the limit of the index buffer
    276             fMaxVertices = maxQuadVertices;
    277         }
    278         bool success = fDrawTarget->reserveVertexSpace(fVertexLayout,
    279                                                    fMaxVertices,
    280                                                    GrTCast<void**>(&fVertices));
    281         GrAlwaysAssert(success);
    282     }
    283 
    284     GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
    285     GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
    286 
    287 #if GR_GL_TEXT_TEXTURE_NORMALIZED
    288     int x = vx >> 16;
    289     int y = vy >> 16;
    290     int w = width >> 16;
    291     int h = height >> 16;
    292 
    293     setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
    294     setRectFan(&fVertices[2*fCurrVertex+1],
    295                texture->normalizeFixedX(tx),
    296                texture->normalizeFixedY(ty),
    297                texture->normalizeFixedX(tx + width),
    298                texture->normalizeFixedY(ty + height),
    299                2);
    300 #else
    301     fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
    302                                         2 * sizeof(GrGpuTextVertex));
    303     fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
    304                                           texture->normalizeFixedY(ty),
    305                                           texture->normalizeFixedX(tx + width),
    306                                           texture->normalizeFixedY(ty + height),
    307                                           2 * sizeof(GrGpuTextVertex));
    308 #endif
    309     fCurrVertex += 4;
    310 }
    311 
    312 
    313