blob: 2230af23462fcc84a5a96cd162aad25d82c8b601 [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.com313e4032010-12-22 22:16:59 +0000179 // must do this to flush inorder buffering before we purge
180 fContext->flushText();
181
reed@google.comac10a2d2010-12-22 21:39:39 +0000182 // try to purge
183 fContext->getFontCache()->purgeExceptFor(fStrike);
184 if (fStrike->getGlyphAtlas(glyph, scaler)) {
185 goto HAS_ATLAS;
186 }
187
188 // Draw as a path, so we flush any accumulated glyphs first
189 this->flushGlyphs();
190
191 if (NULL == glyph->fPath) {
192
193 GrPath* path = new GrPath;
194 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
195 // flag the glyph as being dead?
196 delete path;
197 return;
198 }
199 glyph->fPath = path;
200 }
201 GrPath::Iter iter(*glyph->fPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000202 GrPoint translate;
203 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
204 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000205 fContext->drawPath(fPaint, &iter, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000206 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000207 return;
208 }
209
210HAS_ATLAS:
211 GrAssert(glyph->fAtlas);
212
213 // now promote them to fixed
214 width = GrIntToFixed(width);
215 height = GrIntToFixed(height);
216
217 GrTexture* texture = glyph->fAtlas->texture();
218 GrAssert(texture);
219
220 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
221 this->flushGlyphs();
222 fCurrTexture = texture;
223 fCurrTexture->ref();
224 }
225
226 if (NULL == fVertices) {
227 // If we need to reserve vertices allow the draw target to suggest
228 // a number of verts to reserve and whether to perform a flush.
229 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000230 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 &fMaxVertices,
232 NULL);
233 if (flush) {
234 this->flushGlyphs();
235 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000236 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000237 fMaxVertices = kDefaultRequestedVerts;
238 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000239 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000240 &fMaxVertices,
241 NULL);
242 }
243
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000244 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
reed@google.comac10a2d2010-12-22 21:39:39 +0000245 if (fMaxVertices < kMinRequestedVerts) {
246 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000247 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000248 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000249 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000250 }
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000251 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000252 fMaxVertices, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000253 GrTCast<void**>(&fVertices),
reed@google.comac10a2d2010-12-22 21:39:39 +0000254 NULL);
255 GrAlwaysAssert(success);
256 }
257
258 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
259 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
260
261#if GR_GL_TEXT_TEXTURE_NORMALIZED
262 int x = vx >> 16;
263 int y = vy >> 16;
264 int w = width >> 16;
265 int h = height >> 16;
266
267 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
268 setRectFan(&fVertices[2*fCurrVertex+1],
269 texture->normalizeFixedX(tx),
270 texture->normalizeFixedY(ty),
271 texture->normalizeFixedX(tx + width),
272 texture->normalizeFixedY(ty + height),
273 2);
274#else
275 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
276 2 * sizeof(GrGpuTextVertex));
277 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
278 texture->normalizeFixedY(ty),
279 texture->normalizeFixedX(tx + width),
280 texture->normalizeFixedY(ty + height),
281 2 * sizeof(GrGpuTextVertex));
282#endif
283 fCurrVertex += 4;
284}
285
286