blob: e2c81c7700e75804278d390860924ed486bf6d17 [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.com7d34d2e2011-01-24 17:41:47 +000028static const int TEXT_STAGE = 1;
29
30static const GrVertexLayout BASE_VLAYOUT =
31 GrDrawTarget::kTextFormat_VertexLayoutBit |
32 GrDrawTarget::StageTexCoordVertexLayoutBit(TEXT_STAGE,0);
reed@google.comac10a2d2010-12-22 21:39:39 +000033
34void GrTextContext::flushGlyphs() {
35 if (fCurrVertex > 0) {
36 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
37
38 // setup our sampler state for our text texture/atlas
39
40 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
41 GrSamplerState::kRepeat_WrapMode,
reed@google.comac10a2d2010-12-22 21:39:39 +000042 !fExtMatrix.isIdentity());
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000043 fDrawTarget->setSamplerState(TEXT_STAGE, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000044
45 GrAssert(GrIsALIGN4(fCurrVertex));
46 int nIndices = fCurrVertex + (fCurrVertex >> 1);
47 GrAssert(fCurrTexture);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000048 fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000049
50 if (!GrTexture::PixelConfigIsAlphaOnly(fCurrTexture->config())) {
51 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
52 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
53 NULL != fPaint.getTexture()) {
54 GrPrintf("LCD Text will not draw correctly.\n");
55 }
56 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
57 fDrawTarget->setBlendConstant(fPaint.fColor);
58 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
59 // don't modulate by the paint's color in the frag since we're
60 // already doing it via the blend const.
61 fDrawTarget->setColor(0xffffffff);
62 } else {
63 // set back to normal in case we took LCD path previously.
64 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
65 fDrawTarget->setColor(fPaint.fColor);
66 }
67
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000068 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000069
bsalomon@google.comffca4002011-02-22 20:34:01 +000070 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000071 0, 0, fCurrVertex, nIndices);
reed@google.comac10a2d2010-12-22 21:39:39 +000072 fDrawTarget->releaseReservedGeometry();
73 fVertices = NULL;
74 fMaxVertices = 0;
75 fCurrVertex = 0;
76 fCurrTexture->unref();
77 fCurrTexture = NULL;
78 }
79}
80
bsalomon@google.com5782d712011-01-21 21:03:59 +000081GrTextContext::GrTextContext(GrContext* context,
82 const GrPaint& paint,
83 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000084 fContext = context;
85 fStrike = NULL;
86
87 fCurrTexture = NULL;
88 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000089
90 if (NULL != extMatrix) {
91 fExtMatrix = *extMatrix;
92 } else {
93 fExtMatrix = GrMatrix::I();
94 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000095 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000096 if (!fExtMatrix.isIdentity()) {
97 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000098 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000099 if (fExtMatrix.invert(&inverse)) {
100 inverse.mapRect(&r);
101 r.roundOut(&fClipRect);
102 }
103 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000104 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000105 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000106 } else {
107 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000108 }
109
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000110 // save the context's original matrix off and restore in destructor
111 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000112 fOrigViewMatrix = fContext->getMatrix();
113 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000114
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000115 fVertexLayout = BASE_VLAYOUT;
116 if (NULL != paint.getTexture()) {
117 fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
118 GrMatrix inverseViewMatrix;
119 if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000120 fPaint.fSampler.preConcatMatrix(inverseViewMatrix);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000121 }
122 }
123
reed@google.comac10a2d2010-12-22 21:39:39 +0000124 fVertices = NULL;
125 fMaxVertices = 0;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000126 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000127}
128
129GrTextContext::~GrTextContext() {
130 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000131 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000132}
133
134void GrTextContext::flush() {
135 this->flushGlyphs();
136}
137
138static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
139 int stride) {
140 v[0 * stride].setI(l, t);
141 v[1 * stride].setI(l, b);
142 v[2 * stride].setI(r, b);
143 v[3 * stride].setI(r, t);
144}
145
146void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
147 GrFixed vx, GrFixed vy,
148 GrFontScaler* scaler) {
149 if (NULL == fStrike) {
150 fStrike = fContext->getFontCache()->getStrike(scaler);
151 }
152
153 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
154 if (NULL == glyph || glyph->fBounds.isEmpty()) {
155 return;
156 }
157
158 vx += GrIntToFixed(glyph->fBounds.fLeft);
159 vy += GrIntToFixed(glyph->fBounds.fTop);
160
161 // keep them as ints until we've done the clip-test
162 GrFixed width = glyph->fBounds.width();
163 GrFixed height = glyph->fBounds.height();
164
165 // check if we clipped out
166 if (true || NULL == glyph->fAtlas) {
167 int x = vx >> 16;
168 int y = vy >> 16;
169 if (fClipRect.quickReject(x, y, x + width, y + height)) {
170// Gr_clz(3); // so we can set a break-point in the debugger
171 return;
172 }
173 }
174
175 if (NULL == glyph->fAtlas) {
176 if (fStrike->getGlyphAtlas(glyph, scaler)) {
177 goto HAS_ATLAS;
178 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000179
180 // before we purge the cache, we must flush any accumulated draws
181 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000182 fContext->flushText();
183
reed@google.comac10a2d2010-12-22 21:39:39 +0000184 // try to purge
185 fContext->getFontCache()->purgeExceptFor(fStrike);
186 if (fStrike->getGlyphAtlas(glyph, scaler)) {
187 goto HAS_ATLAS;
188 }
189
reed@google.comac10a2d2010-12-22 21:39:39 +0000190 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000191 GrPath* path = new GrPath;
192 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
193 // flag the glyph as being dead?
194 delete path;
195 return;
196 }
197 glyph->fPath = path;
198 }
199 GrPath::Iter iter(*glyph->fPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000200 GrPoint translate;
201 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
202 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000203 fContext->drawPath(fPaint, &iter, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000204 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000205 return;
206 }
207
208HAS_ATLAS:
209 GrAssert(glyph->fAtlas);
210
211 // now promote them to fixed
212 width = GrIntToFixed(width);
213 height = GrIntToFixed(height);
214
215 GrTexture* texture = glyph->fAtlas->texture();
216 GrAssert(texture);
217
218 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
219 this->flushGlyphs();
220 fCurrTexture = texture;
221 fCurrTexture->ref();
222 }
223
224 if (NULL == fVertices) {
225 // If we need to reserve vertices allow the draw target to suggest
226 // a number of verts to reserve and whether to perform a flush.
227 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000228 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000229 &fMaxVertices,
230 NULL);
231 if (flush) {
232 this->flushGlyphs();
233 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000234 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000235 fMaxVertices = kDefaultRequestedVerts;
236 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000237 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000238 &fMaxVertices,
239 NULL);
240 }
241
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000242 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 if (fMaxVertices < kMinRequestedVerts) {
244 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000245 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000246 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000247 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000248 }
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000249 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000250 fMaxVertices, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000251 GrTCast<void**>(&fVertices),
reed@google.comac10a2d2010-12-22 21:39:39 +0000252 NULL);
253 GrAlwaysAssert(success);
254 }
255
256 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
257 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
258
259#if GR_GL_TEXT_TEXTURE_NORMALIZED
260 int x = vx >> 16;
261 int y = vy >> 16;
262 int w = width >> 16;
263 int h = height >> 16;
264
265 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
266 setRectFan(&fVertices[2*fCurrVertex+1],
267 texture->normalizeFixedX(tx),
268 texture->normalizeFixedY(ty),
269 texture->normalizeFixedX(tx + width),
270 texture->normalizeFixedY(ty + height),
271 2);
272#else
273 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
274 2 * sizeof(GrGpuTextVertex));
275 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
276 texture->normalizeFixedY(ty),
277 texture->normalizeFixedX(tx + width),
278 texture->normalizeFixedY(ty + height),
279 2 * sizeof(GrGpuTextVertex));
280#endif
281 fCurrVertex += 4;
282}
283
284