blob: 4b78d4a8c7d90842949239acef1e8102afd3e38c [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,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000037 filter);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000038 fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000039
40 GrAssert(GrIsALIGN4(fCurrVertex));
41 int nIndices = fCurrVertex + (fCurrVertex >> 1);
42 GrAssert(fCurrTexture);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000043 fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000044
bsalomon@google.com669fdc42011-04-05 17:08:27 +000045 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000046 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
47 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000048 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000049 GrPrintf("LCD Text will not draw correctly.\n");
50 }
51 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
bsalomon@google.com3d0835b2011-12-08 16:12:03 +000052 fDrawTarget->setBlendConstant(fPaint.fColor);
53 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000054 // don't modulate by the paint's color in the frag since we're
55 // already doing it via the blend const.
bsalomon@google.com3d0835b2011-12-08 16:12:03 +000056 fDrawTarget->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000057 } else {
58 // set back to normal in case we took LCD path previously.
bsalomon@google.com3d0835b2011-12-08 16:12:03 +000059 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
60 fDrawTarget->setColor(fPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000061 }
62
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000063 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000064
bsalomon@google.comffca4002011-02-22 20:34:01 +000065 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000066 0, 0, fCurrVertex, nIndices);
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000067 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000068 fVertices = NULL;
69 fMaxVertices = 0;
70 fCurrVertex = 0;
71 fCurrTexture->unref();
72 fCurrTexture = NULL;
73 }
74}
75
bsalomon@google.com5782d712011-01-21 21:03:59 +000076GrTextContext::GrTextContext(GrContext* context,
77 const GrPaint& paint,
78 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000079 fContext = context;
80 fStrike = NULL;
81
82 fCurrTexture = NULL;
83 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000084
85 if (NULL != extMatrix) {
86 fExtMatrix = *extMatrix;
87 } else {
88 fExtMatrix = GrMatrix::I();
89 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000090 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000091 if (!fExtMatrix.isIdentity()) {
92 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000093 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000094 if (fExtMatrix.invert(&inverse)) {
95 inverse.mapRect(&r);
96 r.roundOut(&fClipRect);
97 }
98 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000099 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000100 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000101 } else {
102 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000103 }
104
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000105 // save the context's original matrix off and restore in destructor
106 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000107 fOrigViewMatrix = fContext->getMatrix();
108 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000109
bsalomon@google.com39149582011-06-13 21:55:32 +0000110 /*
111 We need to call preConcatMatrix with our viewmatrix's inverse, for each
112 texture and mask in the paint. However, computing the inverse can be
113 expensive, and its possible we may not have any textures or masks, so these
114 two loops are written such that we only compute the inverse (once) if we
115 need it. We do this on our copy of the paint rather than directly on the
116 draw target because we re-provide the paint to the context when we have
117 to flush our glyphs or draw a glyph as a path midstream.
118 */
119 bool invVMComputed = false;
120 GrMatrix invVM;
121 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
122 if (NULL != fPaint.getTexture(t)) {
123 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
124 invVMComputed = true;
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000125 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000126 }
127 }
128 }
129 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
130 if (NULL != fPaint.getMask(m)) {
131 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
132 invVMComputed = true;
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000133 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000134 }
135 }
136 }
137
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000138 fDrawTarget = fContext->getTextTarget(fPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000139
reed@google.comac10a2d2010-12-22 21:39:39 +0000140 fVertices = NULL;
141 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000142
143 fVertexLayout =
144 GrDrawTarget::kTextFormat_VertexLayoutBit |
145 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
146
147 int stageMask = paint.getActiveStageMask();
148 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000149 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
150 if ((1 << i) & stageMask) {
151 fVertexLayout |=
152 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
153 GrAssert(i != kGlyphMaskStage);
154 }
155 }
156 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000157}
158
159GrTextContext::~GrTextContext() {
160 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000161 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000162}
163
164void GrTextContext::flush() {
165 this->flushGlyphs();
166}
167
168static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
169 int stride) {
170 v[0 * stride].setI(l, t);
171 v[1 * stride].setI(l, b);
172 v[2 * stride].setI(r, b);
173 v[3 * stride].setI(r, t);
174}
175
176void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
177 GrFixed vx, GrFixed vy,
178 GrFontScaler* scaler) {
179 if (NULL == fStrike) {
180 fStrike = fContext->getFontCache()->getStrike(scaler);
181 }
182
183 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
184 if (NULL == glyph || glyph->fBounds.isEmpty()) {
185 return;
186 }
187
188 vx += GrIntToFixed(glyph->fBounds.fLeft);
189 vy += GrIntToFixed(glyph->fBounds.fTop);
190
191 // keep them as ints until we've done the clip-test
192 GrFixed width = glyph->fBounds.width();
193 GrFixed height = glyph->fBounds.height();
194
195 // check if we clipped out
196 if (true || NULL == glyph->fAtlas) {
197 int x = vx >> 16;
198 int y = vy >> 16;
199 if (fClipRect.quickReject(x, y, x + width, y + height)) {
200// Gr_clz(3); // so we can set a break-point in the debugger
201 return;
202 }
203 }
204
205 if (NULL == glyph->fAtlas) {
206 if (fStrike->getGlyphAtlas(glyph, scaler)) {
207 goto HAS_ATLAS;
208 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000209
210 // before we purge the cache, we must flush any accumulated draws
211 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000212 fContext->flushText();
213
reed@google.comac10a2d2010-12-22 21:39:39 +0000214 // try to purge
215 fContext->getFontCache()->purgeExceptFor(fStrike);
216 if (fStrike->getGlyphAtlas(glyph, scaler)) {
217 goto HAS_ATLAS;
218 }
219
reed@google.comac10a2d2010-12-22 21:39:39 +0000220 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 GrPath* path = new GrPath;
222 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
223 // flag the glyph as being dead?
224 delete path;
225 return;
226 }
227 glyph->fPath = path;
228 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000229
reed@google.comac10a2d2010-12-22 21:39:39 +0000230 GrPoint translate;
231 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
232 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
reed@google.com07f3ee12011-05-16 17:21:57 +0000233 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000234 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000235 return;
236 }
237
238HAS_ATLAS:
239 GrAssert(glyph->fAtlas);
240
241 // now promote them to fixed
242 width = GrIntToFixed(width);
243 height = GrIntToFixed(height);
244
245 GrTexture* texture = glyph->fAtlas->texture();
246 GrAssert(texture);
247
248 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
249 this->flushGlyphs();
250 fCurrTexture = texture;
251 fCurrTexture->ref();
252 }
253
254 if (NULL == fVertices) {
255 // If we need to reserve vertices allow the draw target to suggest
256 // a number of verts to reserve and whether to perform a flush.
257 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000258 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000259 &fMaxVertices,
260 NULL);
261 if (flush) {
262 this->flushGlyphs();
263 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000264 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000265 fMaxVertices = kDefaultRequestedVerts;
266 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000267 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000268 &fMaxVertices,
269 NULL);
270 }
271
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000272 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
reed@google.comac10a2d2010-12-22 21:39:39 +0000273 if (fMaxVertices < kMinRequestedVerts) {
274 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000275 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000276 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000277 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000278 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000279 bool success = fDrawTarget->reserveVertexSpace(fVertexLayout,
280 fMaxVertices,
281 GrTCast<void**>(&fVertices));
reed@google.comac10a2d2010-12-22 21:39:39 +0000282 GrAlwaysAssert(success);
283 }
284
285 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
286 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
287
288#if GR_GL_TEXT_TEXTURE_NORMALIZED
289 int x = vx >> 16;
290 int y = vy >> 16;
291 int w = width >> 16;
292 int h = height >> 16;
293
294 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
295 setRectFan(&fVertices[2*fCurrVertex+1],
296 texture->normalizeFixedX(tx),
297 texture->normalizeFixedY(ty),
298 texture->normalizeFixedX(tx + width),
299 texture->normalizeFixedY(ty + height),
300 2);
301#else
302 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
303 2 * sizeof(GrGpuTextVertex));
304 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
305 texture->normalizeFixedY(ty),
306 texture->normalizeFixedX(tx + width),
307 texture->normalizeFixedY(ty + height),
308 2 * sizeof(GrGpuTextVertex));
309#endif
310 fCurrVertex += 4;
311}
312
313