blob: 9d69a97f4fa4de8cb4561907bc9022fd0b73027d [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
2 Copyright 2010 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17
reed@google.comac10a2d2010-12-22 21:39:39 +000018#include "GrTextContext.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000019#include "GrAtlas.h"
20#include "GrContext.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000021#include "GrTextStrike.h"
22#include "GrTextStrike_impl.h"
23#include "GrFontScaler.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000024#include "GrIndexBuffer.h"
25#include "GrGpuVertex.h"
bsalomon@google.comffca4002011-02-22 20:34:01 +000026#include "GrDrawTarget.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000027
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000028enum {
29 kGlyphMaskStage = GrPaint::kTotalStages,
30};
reed@google.comac10a2d2010-12-22 21:39:39 +000031
32void GrTextContext::flushGlyphs() {
33 if (fCurrVertex > 0) {
34 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
35
36 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000037 GrSamplerState::Filter filter;
38 if (fExtMatrix.isIdentity()) {
39 filter = GrSamplerState::kNearest_Filter;
40 } else {
41 filter = GrSamplerState::kBilinear_Filter;
42 }
reed@google.comac10a2d2010-12-22 21:39:39 +000043 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
44 GrSamplerState::kRepeat_WrapMode,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000045 filter);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000046 fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000047
48 GrAssert(GrIsALIGN4(fCurrVertex));
49 int nIndices = fCurrVertex + (fCurrVertex >> 1);
50 GrAssert(fCurrTexture);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000051 fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000052
bsalomon@google.com669fdc42011-04-05 17:08:27 +000053 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000054 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
55 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000056 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000057 GrPrintf("LCD Text will not draw correctly.\n");
58 }
59 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
60 fDrawTarget->setBlendConstant(fPaint.fColor);
61 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
62 // don't modulate by the paint's color in the frag since we're
63 // already doing it via the blend const.
64 fDrawTarget->setColor(0xffffffff);
65 } else {
66 // set back to normal in case we took LCD path previously.
67 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
68 fDrawTarget->setColor(fPaint.fColor);
69 }
70
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000071 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000072
bsalomon@google.comffca4002011-02-22 20:34:01 +000073 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000074 0, 0, fCurrVertex, nIndices);
bsalomon@google.com25fb21f2011-06-21 18:17:25 +000075 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000076 fVertices = NULL;
77 fMaxVertices = 0;
78 fCurrVertex = 0;
79 fCurrTexture->unref();
80 fCurrTexture = NULL;
81 }
82}
83
bsalomon@google.com5782d712011-01-21 21:03:59 +000084GrTextContext::GrTextContext(GrContext* context,
85 const GrPaint& paint,
86 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000087 fContext = context;
88 fStrike = NULL;
89
90 fCurrTexture = NULL;
91 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000092
93 if (NULL != extMatrix) {
94 fExtMatrix = *extMatrix;
95 } else {
96 fExtMatrix = GrMatrix::I();
97 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000098 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000099 if (!fExtMatrix.isIdentity()) {
100 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000101 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000102 if (fExtMatrix.invert(&inverse)) {
103 inverse.mapRect(&r);
104 r.roundOut(&fClipRect);
105 }
106 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000107 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000108 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000109 } else {
110 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000111 }
112
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000113 // save the context's original matrix off and restore in destructor
114 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000115 fOrigViewMatrix = fContext->getMatrix();
116 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000117
bsalomon@google.com39149582011-06-13 21:55:32 +0000118 /*
119 We need to call preConcatMatrix with our viewmatrix's inverse, for each
120 texture and mask in the paint. However, computing the inverse can be
121 expensive, and its possible we may not have any textures or masks, so these
122 two loops are written such that we only compute the inverse (once) if we
123 need it. We do this on our copy of the paint rather than directly on the
124 draw target because we re-provide the paint to the context when we have
125 to flush our glyphs or draw a glyph as a path midstream.
126 */
127 bool invVMComputed = false;
128 GrMatrix invVM;
129 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
130 if (NULL != fPaint.getTexture(t)) {
131 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
132 invVMComputed = true;
133 fPaint.getTextureSampler(t)->preConcatMatrix(invVM);
134 }
135 }
136 }
137 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
138 if (NULL != fPaint.getMask(m)) {
139 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
140 invVMComputed = true;
141 fPaint.getMaskSampler(m)->preConcatMatrix(invVM);
142 }
143 }
144 }
145
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000146 fDrawTarget = fContext->getTextTarget(fPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000147
reed@google.comac10a2d2010-12-22 21:39:39 +0000148 fVertices = NULL;
149 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000150
151 fVertexLayout =
152 GrDrawTarget::kTextFormat_VertexLayoutBit |
153 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
154
155 int stageMask = paint.getActiveStageMask();
156 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000157 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
158 if ((1 << i) & stageMask) {
159 fVertexLayout |=
160 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
161 GrAssert(i != kGlyphMaskStage);
162 }
163 }
164 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000165}
166
167GrTextContext::~GrTextContext() {
168 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000169 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000170}
171
172void GrTextContext::flush() {
173 this->flushGlyphs();
174}
175
176static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
177 int stride) {
178 v[0 * stride].setI(l, t);
179 v[1 * stride].setI(l, b);
180 v[2 * stride].setI(r, b);
181 v[3 * stride].setI(r, t);
182}
183
184void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
185 GrFixed vx, GrFixed vy,
186 GrFontScaler* scaler) {
187 if (NULL == fStrike) {
188 fStrike = fContext->getFontCache()->getStrike(scaler);
189 }
190
191 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
192 if (NULL == glyph || glyph->fBounds.isEmpty()) {
193 return;
194 }
195
196 vx += GrIntToFixed(glyph->fBounds.fLeft);
197 vy += GrIntToFixed(glyph->fBounds.fTop);
198
199 // keep them as ints until we've done the clip-test
200 GrFixed width = glyph->fBounds.width();
201 GrFixed height = glyph->fBounds.height();
202
203 // check if we clipped out
204 if (true || NULL == glyph->fAtlas) {
205 int x = vx >> 16;
206 int y = vy >> 16;
207 if (fClipRect.quickReject(x, y, x + width, y + height)) {
208// Gr_clz(3); // so we can set a break-point in the debugger
209 return;
210 }
211 }
212
213 if (NULL == glyph->fAtlas) {
214 if (fStrike->getGlyphAtlas(glyph, scaler)) {
215 goto HAS_ATLAS;
216 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000217
218 // before we purge the cache, we must flush any accumulated draws
219 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000220 fContext->flushText();
221
reed@google.comac10a2d2010-12-22 21:39:39 +0000222 // try to purge
223 fContext->getFontCache()->purgeExceptFor(fStrike);
224 if (fStrike->getGlyphAtlas(glyph, scaler)) {
225 goto HAS_ATLAS;
226 }
227
reed@google.comac10a2d2010-12-22 21:39:39 +0000228 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000229 GrPath* path = new GrPath;
230 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
231 // flag the glyph as being dead?
232 delete path;
233 return;
234 }
235 glyph->fPath = path;
236 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000237
reed@google.comac10a2d2010-12-22 21:39:39 +0000238 GrPoint translate;
239 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
240 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
reed@google.com07f3ee12011-05-16 17:21:57 +0000241 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000242 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 return;
244 }
245
246HAS_ATLAS:
247 GrAssert(glyph->fAtlas);
248
249 // now promote them to fixed
250 width = GrIntToFixed(width);
251 height = GrIntToFixed(height);
252
253 GrTexture* texture = glyph->fAtlas->texture();
254 GrAssert(texture);
255
256 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
257 this->flushGlyphs();
258 fCurrTexture = texture;
259 fCurrTexture->ref();
260 }
261
262 if (NULL == fVertices) {
263 // If we need to reserve vertices allow the draw target to suggest
264 // a number of verts to reserve and whether to perform a flush.
265 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000266 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000267 &fMaxVertices,
268 NULL);
269 if (flush) {
270 this->flushGlyphs();
271 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000272 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000273 fMaxVertices = kDefaultRequestedVerts;
274 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000275 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000276 &fMaxVertices,
277 NULL);
278 }
279
bsalomon@google.comcee661a2011-07-26 12:32:36 +0000280 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
reed@google.comac10a2d2010-12-22 21:39:39 +0000281 if (fMaxVertices < kMinRequestedVerts) {
282 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000283 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000284 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000285 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000286 }
bsalomon@google.com25fb21f2011-06-21 18:17:25 +0000287 bool success = fDrawTarget->reserveVertexSpace(fVertexLayout,
288 fMaxVertices,
289 GrTCast<void**>(&fVertices));
reed@google.comac10a2d2010-12-22 21:39:39 +0000290 GrAlwaysAssert(success);
291 }
292
293 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
294 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
295
296#if GR_GL_TEXT_TEXTURE_NORMALIZED
297 int x = vx >> 16;
298 int y = vy >> 16;
299 int w = width >> 16;
300 int h = height >> 16;
301
302 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
303 setRectFan(&fVertices[2*fCurrVertex+1],
304 texture->normalizeFixedX(tx),
305 texture->normalizeFixedY(ty),
306 texture->normalizeFixedX(tx + width),
307 texture->normalizeFixedY(ty + height),
308 2);
309#else
310 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
311 2 * sizeof(GrGpuTextVertex));
312 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
313 texture->normalizeFixedY(ty),
314 texture->normalizeFixedX(tx + width),
315 texture->normalizeFixedY(ty + height),
316 2 * sizeof(GrGpuTextVertex));
317#endif
318 fCurrVertex += 4;
319}
320
321