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