• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
flushGlyphs()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 
GrTextContext(GrContext * context,const GrPaint & paint,const GrMatrix * extMatrix)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 
~GrTextContext()158 GrTextContext::~GrTextContext() {
159     this->flushGlyphs();
160     fContext->setMatrix(fOrigViewMatrix);
161 }
162 
flush()163 void GrTextContext::flush() {
164     this->flushGlyphs();
165 }
166 
setRectFan(GrGpuTextVertex v[4],int l,int t,int r,int b,int stride)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 
drawPackedGlyph(GrGlyph::PackedID packed,GrFixed vx,GrFixed vy,GrFontScaler * scaler)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