blob: 06cc2a125f11839c37f2e8720acb4186d9d0b8f6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac10a2d2010-12-22 21:39:39 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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.
reed@google.comac10a2d2010-12-22 21:39:39 +00007 */
8
9
epoger@google.comec3ed6a2011-07-28 14:26:00 +000010
reed@google.comac10a2d2010-12-22 21:39:39 +000011#include "GrTextContext.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000012#include "GrAtlas.h"
13#include "GrContext.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000014#include "GrTextStrike.h"
15#include "GrTextStrike_impl.h"
16#include "GrFontScaler.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000017#include "GrIndexBuffer.h"
18#include "GrGpuVertex.h"
bsalomon@google.comffca4002011-02-22 20:34:01 +000019#include "GrDrawTarget.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000020
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000021enum {
22 kGlyphMaskStage = GrPaint::kTotalStages,
23};
reed@google.comac10a2d2010-12-22 21:39:39 +000024
25void GrTextContext::flushGlyphs() {
26 if (fCurrVertex > 0) {
27 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
28
29 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000030 GrSamplerState::Filter filter;
31 if (fExtMatrix.isIdentity()) {
32 filter = GrSamplerState::kNearest_Filter;
33 } else {
34 filter = GrSamplerState::kBilinear_Filter;
35 }
reed@google.comac10a2d2010-12-22 21:39:39 +000036 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
37 GrSamplerState::kRepeat_WrapMode,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000038 filter);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000039 fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000040
41 GrAssert(GrIsALIGN4(fCurrVertex));
42 int nIndices = fCurrVertex + (fCurrVertex >> 1);
43 GrAssert(fCurrTexture);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000044 fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000045
bsalomon@google.com669fdc42011-04-05 17:08:27 +000046 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000047 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
48 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000049 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000050 GrPrintf("LCD Text will not draw correctly.\n");
51 }
52 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
53 fDrawTarget->setBlendConstant(fPaint.fColor);
54 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
55 // don't modulate by the paint's color in the frag since we're
56 // already doing it via the blend const.
57 fDrawTarget->setColor(0xffffffff);
58 } else {
59 // set back to normal in case we took LCD path previously.
60 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
61 fDrawTarget->setColor(fPaint.fColor);
62 }
63
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000064 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000065
bsalomon@google.comffca4002011-02-22 20:34:01 +000066 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000067 0, 0, fCurrVertex, nIndices);
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000068 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000069 fVertices = NULL;
70 fMaxVertices = 0;
71 fCurrVertex = 0;
72 fCurrTexture->unref();
73 fCurrTexture = NULL;
74 }
75}
76
bsalomon@google.com5782d712011-01-21 21:03:59 +000077GrTextContext::GrTextContext(GrContext* context,
78 const GrPaint& paint,
79 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000080 fContext = context;
81 fStrike = NULL;
82
83 fCurrTexture = NULL;
84 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000085
86 if (NULL != extMatrix) {
87 fExtMatrix = *extMatrix;
88 } else {
89 fExtMatrix = GrMatrix::I();
90 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000091 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000092 if (!fExtMatrix.isIdentity()) {
93 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000094 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000095 if (fExtMatrix.invert(&inverse)) {
96 inverse.mapRect(&r);
97 r.roundOut(&fClipRect);
98 }
99 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000100 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000101 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000102 } else {
103 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000104 }
105
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000106 // save the context's original matrix off and restore in destructor
107 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000108 fOrigViewMatrix = fContext->getMatrix();
109 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000110
bsalomon@google.com39149582011-06-13 21:55:32 +0000111 /*
112 We need to call preConcatMatrix with our viewmatrix's inverse, for each
113 texture and mask in the paint. However, computing the inverse can be
114 expensive, and its possible we may not have any textures or masks, so these
115 two loops are written such that we only compute the inverse (once) if we
116 need it. We do this on our copy of the paint rather than directly on the
117 draw target because we re-provide the paint to the context when we have
118 to flush our glyphs or draw a glyph as a path midstream.
119 */
120 bool invVMComputed = false;
121 GrMatrix invVM;
122 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
123 if (NULL != fPaint.getTexture(t)) {
124 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
125 invVMComputed = true;
126 fPaint.getTextureSampler(t)->preConcatMatrix(invVM);
127 }
128 }
129 }
130 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
131 if (NULL != fPaint.getMask(m)) {
132 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
133 invVMComputed = true;
134 fPaint.getMaskSampler(m)->preConcatMatrix(invVM);
135 }
136 }
137 }
138
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000139 fDrawTarget = fContext->getTextTarget(fPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000140
reed@google.comac10a2d2010-12-22 21:39:39 +0000141 fVertices = NULL;
142 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000143
144 fVertexLayout =
145 GrDrawTarget::kTextFormat_VertexLayoutBit |
146 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
147
148 int stageMask = paint.getActiveStageMask();
149 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000150 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
151 if ((1 << i) & stageMask) {
152 fVertexLayout |=
153 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
154 GrAssert(i != kGlyphMaskStage);
155 }
156 }
157 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000158}
159
160GrTextContext::~GrTextContext() {
161 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000162 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000163}
164
165void GrTextContext::flush() {
166 this->flushGlyphs();
167}
168
169static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
170 int stride) {
171 v[0 * stride].setI(l, t);
172 v[1 * stride].setI(l, b);
173 v[2 * stride].setI(r, b);
174 v[3 * stride].setI(r, t);
175}
176
177void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
178 GrFixed vx, GrFixed vy,
179 GrFontScaler* scaler) {
180 if (NULL == fStrike) {
181 fStrike = fContext->getFontCache()->getStrike(scaler);
182 }
183
184 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
185 if (NULL == glyph || glyph->fBounds.isEmpty()) {
186 return;
187 }
188
189 vx += GrIntToFixed(glyph->fBounds.fLeft);
190 vy += GrIntToFixed(glyph->fBounds.fTop);
191
192 // keep them as ints until we've done the clip-test
193 GrFixed width = glyph->fBounds.width();
194 GrFixed height = glyph->fBounds.height();
195
196 // check if we clipped out
197 if (true || NULL == glyph->fAtlas) {
198 int x = vx >> 16;
199 int y = vy >> 16;
200 if (fClipRect.quickReject(x, y, x + width, y + height)) {
201// Gr_clz(3); // so we can set a break-point in the debugger
202 return;
203 }
204 }
205
206 if (NULL == glyph->fAtlas) {
207 if (fStrike->getGlyphAtlas(glyph, scaler)) {
208 goto HAS_ATLAS;
209 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000210
211 // before we purge the cache, we must flush any accumulated draws
212 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000213 fContext->flushText();
214
reed@google.comac10a2d2010-12-22 21:39:39 +0000215 // try to purge
216 fContext->getFontCache()->purgeExceptFor(fStrike);
217 if (fStrike->getGlyphAtlas(glyph, scaler)) {
218 goto HAS_ATLAS;
219 }
220
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000222 GrPath* path = new GrPath;
223 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
224 // flag the glyph as being dead?
225 delete path;
226 return;
227 }
228 glyph->fPath = path;
229 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000230
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 GrPoint translate;
232 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
233 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
reed@google.com07f3ee12011-05-16 17:21:57 +0000234 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000235 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000236 return;
237 }
238
239HAS_ATLAS:
240 GrAssert(glyph->fAtlas);
241
242 // now promote them to fixed
243 width = GrIntToFixed(width);
244 height = GrIntToFixed(height);
245
246 GrTexture* texture = glyph->fAtlas->texture();
247 GrAssert(texture);
248
249 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
250 this->flushGlyphs();
251 fCurrTexture = texture;
252 fCurrTexture->ref();
253 }
254
255 if (NULL == fVertices) {
256 // If we need to reserve vertices allow the draw target to suggest
257 // a number of verts to reserve and whether to perform a flush.
258 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000259 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000260 &fMaxVertices,
261 NULL);
262 if (flush) {
263 this->flushGlyphs();
264 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000265 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000266 fMaxVertices = kDefaultRequestedVerts;
267 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000268 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000269 &fMaxVertices,
270 NULL);
271 }
272
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000273 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
reed@google.comac10a2d2010-12-22 21:39:39 +0000274 if (fMaxVertices < kMinRequestedVerts) {
275 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000276 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000277 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000278 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000279 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000280 bool success = fDrawTarget->reserveVertexSpace(fVertexLayout,
281 fMaxVertices,
282 GrTCast<void**>(&fVertices));
reed@google.comac10a2d2010-12-22 21:39:39 +0000283 GrAlwaysAssert(success);
284 }
285
286 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
287 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
288
289#if GR_GL_TEXT_TEXTURE_NORMALIZED
290 int x = vx >> 16;
291 int y = vy >> 16;
292 int w = width >> 16;
293 int h = height >> 16;
294
295 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
296 setRectFan(&fVertices[2*fCurrVertex+1],
297 texture->normalizeFixedX(tx),
298 texture->normalizeFixedY(ty),
299 texture->normalizeFixedX(tx + width),
300 texture->normalizeFixedY(ty + height),
301 2);
302#else
303 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
304 2 * sizeof(GrGpuTextVertex));
305 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
306 texture->normalizeFixedY(ty),
307 texture->normalizeFixedX(tx + width),
308 texture->normalizeFixedY(ty + height),
309 2 * sizeof(GrGpuTextVertex));
310#endif
311 fCurrVertex += 4;
312}
313
314