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