blob: ef9848741ec779480ec165563156b26e15fe87f8 [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);
bsalomon@google.com0fec61d2011-12-08 15:53:53 +000028 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000029
30 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000031 GrSamplerState::Filter filter;
32 if (fExtMatrix.isIdentity()) {
33 filter = GrSamplerState::kNearest_Filter;
34 } else {
35 filter = GrSamplerState::kBilinear_Filter;
36 }
reed@google.comac10a2d2010-12-22 21:39:39 +000037 GrSamplerState sampler(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.com0fec61d2011-12-08 15:53:53 +000045 drawState->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000046
bsalomon@google.com669fdc42011-04-05 17:08:27 +000047 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000048 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
49 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000050 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000051 GrPrintf("LCD Text will not draw correctly.\n");
52 }
53 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
bsalomon@google.com0fec61d2011-12-08 15:53:53 +000054 drawState->setBlendConstant(fPaint.fColor);
55 drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000056 // don't modulate by the paint's color in the frag since we're
57 // already doing it via the blend const.
bsalomon@google.com0fec61d2011-12-08 15:53:53 +000058 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000059 } else {
60 // set back to normal in case we took LCD path previously.
bsalomon@google.com0fec61d2011-12-08 15:53:53 +000061 drawState->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
62 drawState->setColor(fPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000063 }
64
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000065 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000066
bsalomon@google.comffca4002011-02-22 20:34:01 +000067 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000068 0, 0, fCurrVertex, nIndices);
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000069 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000070 fVertices = NULL;
71 fMaxVertices = 0;
72 fCurrVertex = 0;
73 fCurrTexture->unref();
74 fCurrTexture = NULL;
75 }
76}
77
bsalomon@google.com5782d712011-01-21 21:03:59 +000078GrTextContext::GrTextContext(GrContext* context,
79 const GrPaint& paint,
80 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000081 fContext = context;
82 fStrike = NULL;
83
84 fCurrTexture = NULL;
85 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000086
87 if (NULL != extMatrix) {
88 fExtMatrix = *extMatrix;
89 } else {
90 fExtMatrix = GrMatrix::I();
91 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000092 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000093 if (!fExtMatrix.isIdentity()) {
94 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000095 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000096 if (fExtMatrix.invert(&inverse)) {
97 inverse.mapRect(&r);
98 r.roundOut(&fClipRect);
99 }
100 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000101 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000102 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000103 } else {
104 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000105 }
106
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000107 // save the context's original matrix off and restore in destructor
108 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000109 fOrigViewMatrix = fContext->getMatrix();
110 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000111
bsalomon@google.com39149582011-06-13 21:55:32 +0000112 /*
113 We need to call preConcatMatrix with our viewmatrix's inverse, for each
114 texture and mask in the paint. However, computing the inverse can be
115 expensive, and its possible we may not have any textures or masks, so these
116 two loops are written such that we only compute the inverse (once) if we
117 need it. We do this on our copy of the paint rather than directly on the
118 draw target because we re-provide the paint to the context when we have
119 to flush our glyphs or draw a glyph as a path midstream.
120 */
121 bool invVMComputed = false;
122 GrMatrix invVM;
123 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
124 if (NULL != fPaint.getTexture(t)) {
125 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
126 invVMComputed = true;
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000127 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000128 }
129 }
130 }
131 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
132 if (NULL != fPaint.getMask(m)) {
133 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
134 invVMComputed = true;
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000135 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000136 }
137 }
138 }
139
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000140 fDrawTarget = fContext->getTextTarget(fPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000141
reed@google.comac10a2d2010-12-22 21:39:39 +0000142 fVertices = NULL;
143 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000144
145 fVertexLayout =
146 GrDrawTarget::kTextFormat_VertexLayoutBit |
147 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
148
149 int stageMask = paint.getActiveStageMask();
150 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000151 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
152 if ((1 << i) & stageMask) {
153 fVertexLayout |=
154 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
155 GrAssert(i != kGlyphMaskStage);
156 }
157 }
158 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000159}
160
161GrTextContext::~GrTextContext() {
162 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000163 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000164}
165
166void GrTextContext::flush() {
167 this->flushGlyphs();
168}
169
170static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
171 int stride) {
172 v[0 * stride].setI(l, t);
173 v[1 * stride].setI(l, b);
174 v[2 * stride].setI(r, b);
175 v[3 * stride].setI(r, t);
176}
177
178void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
179 GrFixed vx, GrFixed vy,
180 GrFontScaler* scaler) {
181 if (NULL == fStrike) {
182 fStrike = fContext->getFontCache()->getStrike(scaler);
183 }
184
185 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
186 if (NULL == glyph || glyph->fBounds.isEmpty()) {
187 return;
188 }
189
190 vx += GrIntToFixed(glyph->fBounds.fLeft);
191 vy += GrIntToFixed(glyph->fBounds.fTop);
192
193 // keep them as ints until we've done the clip-test
194 GrFixed width = glyph->fBounds.width();
195 GrFixed height = glyph->fBounds.height();
196
197 // check if we clipped out
198 if (true || NULL == glyph->fAtlas) {
199 int x = vx >> 16;
200 int y = vy >> 16;
201 if (fClipRect.quickReject(x, y, x + width, y + height)) {
202// Gr_clz(3); // so we can set a break-point in the debugger
203 return;
204 }
205 }
206
207 if (NULL == glyph->fAtlas) {
208 if (fStrike->getGlyphAtlas(glyph, scaler)) {
209 goto HAS_ATLAS;
210 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000211
212 // before we purge the cache, we must flush any accumulated draws
213 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000214 fContext->flushText();
215
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 // try to purge
217 fContext->getFontCache()->purgeExceptFor(fStrike);
218 if (fStrike->getGlyphAtlas(glyph, scaler)) {
219 goto HAS_ATLAS;
220 }
221
reed@google.comac10a2d2010-12-22 21:39:39 +0000222 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 GrPath* path = new GrPath;
224 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
225 // flag the glyph as being dead?
226 delete path;
227 return;
228 }
229 glyph->fPath = path;
230 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000231
reed@google.comac10a2d2010-12-22 21:39:39 +0000232 GrPoint translate;
233 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
234 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
reed@google.com07f3ee12011-05-16 17:21:57 +0000235 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000236 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000237 return;
238 }
239
240HAS_ATLAS:
241 GrAssert(glyph->fAtlas);
242
243 // now promote them to fixed
244 width = GrIntToFixed(width);
245 height = GrIntToFixed(height);
246
247 GrTexture* texture = glyph->fAtlas->texture();
248 GrAssert(texture);
249
250 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
251 this->flushGlyphs();
252 fCurrTexture = texture;
253 fCurrTexture->ref();
254 }
255
256 if (NULL == fVertices) {
257 // If we need to reserve vertices allow the draw target to suggest
258 // a number of verts to reserve and whether to perform a flush.
259 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000260 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000261 &fMaxVertices,
262 NULL);
263 if (flush) {
264 this->flushGlyphs();
265 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000266 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000267 fMaxVertices = kDefaultRequestedVerts;
268 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000269 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000270 &fMaxVertices,
271 NULL);
272 }
273
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000274 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
reed@google.comac10a2d2010-12-22 21:39:39 +0000275 if (fMaxVertices < kMinRequestedVerts) {
276 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000277 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000278 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000279 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000280 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000281 bool success = fDrawTarget->reserveVertexSpace(fVertexLayout,
282 fMaxVertices,
283 GrTCast<void**>(&fVertices));
reed@google.comac10a2d2010-12-22 21:39:39 +0000284 GrAlwaysAssert(success);
285 }
286
287 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
288 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
289
290#if GR_GL_TEXT_TEXTURE_NORMALIZED
291 int x = vx >> 16;
292 int y = vy >> 16;
293 int w = width >> 16;
294 int h = height >> 16;
295
296 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
297 setRectFan(&fVertices[2*fCurrVertex+1],
298 texture->normalizeFixedX(tx),
299 texture->normalizeFixedY(ty),
300 texture->normalizeFixedX(tx + width),
301 texture->normalizeFixedY(ty + height),
302 2);
303#else
304 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
305 2 * sizeof(GrGpuTextVertex));
306 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
307 texture->normalizeFixedY(ty),
308 texture->normalizeFixedX(tx + width),
309 texture->normalizeFixedY(ty + height),
310 2 * sizeof(GrGpuTextVertex));
311#endif
312 fCurrVertex += 4;
313}
314
315