blob: 7341005b86ffbe09c8a96668cd667e920fd2b68f [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"
reed@google.comac10a2d2010-12-22 21:39:39 +000026
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000027static const int TEXT_STAGE = 1;
28
29static const GrVertexLayout BASE_VLAYOUT =
30 GrDrawTarget::kTextFormat_VertexLayoutBit |
31 GrDrawTarget::StageTexCoordVertexLayoutBit(TEXT_STAGE,0);
reed@google.comac10a2d2010-12-22 21:39:39 +000032
33void GrTextContext::flushGlyphs() {
34 if (fCurrVertex > 0) {
35 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
36
37 // setup our sampler state for our text texture/atlas
38
39 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
40 GrSamplerState::kRepeat_WrapMode,
reed@google.comac10a2d2010-12-22 21:39:39 +000041 !fExtMatrix.isIdentity());
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000042 fDrawTarget->setSamplerState(TEXT_STAGE, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000043
44 GrAssert(GrIsALIGN4(fCurrVertex));
45 int nIndices = fCurrVertex + (fCurrVertex >> 1);
46 GrAssert(fCurrTexture);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000047 fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture);
48 fDrawTarget->setTextureMatrix(TEXT_STAGE, GrMatrix::I());
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000049 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000050
51 fDrawTarget->drawIndexed(GrDrawTarget::kTriangles_PrimitiveType,
52 0, 0, fCurrVertex, nIndices);
reed@google.comac10a2d2010-12-22 21:39:39 +000053 fDrawTarget->releaseReservedGeometry();
54 fVertices = NULL;
55 fMaxVertices = 0;
56 fCurrVertex = 0;
57 fCurrTexture->unref();
58 fCurrTexture = NULL;
59 }
60}
61
bsalomon@google.com5782d712011-01-21 21:03:59 +000062GrTextContext::GrTextContext(GrContext* context,
63 const GrPaint& paint,
64 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000065 fContext = context;
66 fStrike = NULL;
67
68 fCurrTexture = NULL;
69 fCurrVertex = 0;
70 fClipRect = context->getClip().getBounds();
71
72 if (NULL != extMatrix) {
73 fExtMatrix = *extMatrix;
74 } else {
75 fExtMatrix = GrMatrix::I();
76 }
77 if (!fExtMatrix.isIdentity()) {
78 GrMatrix inverse;
79 GrRect r;
80 r.set(fClipRect);
81 if (fExtMatrix.invert(&inverse)) {
82 inverse.mapRect(&r);
83 r.roundOut(&fClipRect);
84 }
85 }
86
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000087 // save the context's original matrix off and restore in destructor
88 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +000089 fOrigViewMatrix = fContext->getMatrix();
90 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +000091
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000092 fVertexLayout = BASE_VLAYOUT;
93 if (NULL != paint.getTexture()) {
94 fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
95 GrMatrix inverseViewMatrix;
96 if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
97 fPaint.fTextureMatrix.preConcat(inverseViewMatrix);
98 }
99 }
100
reed@google.comac10a2d2010-12-22 21:39:39 +0000101 fVertices = NULL;
102 fMaxVertices = 0;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000103 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000104}
105
106GrTextContext::~GrTextContext() {
107 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000108 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000109}
110
111void GrTextContext::flush() {
112 this->flushGlyphs();
113}
114
115static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
116 int stride) {
117 v[0 * stride].setI(l, t);
118 v[1 * stride].setI(l, b);
119 v[2 * stride].setI(r, b);
120 v[3 * stride].setI(r, t);
121}
122
123void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
124 GrFixed vx, GrFixed vy,
125 GrFontScaler* scaler) {
126 if (NULL == fStrike) {
127 fStrike = fContext->getFontCache()->getStrike(scaler);
128 }
129
130 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
131 if (NULL == glyph || glyph->fBounds.isEmpty()) {
132 return;
133 }
134
135 vx += GrIntToFixed(glyph->fBounds.fLeft);
136 vy += GrIntToFixed(glyph->fBounds.fTop);
137
138 // keep them as ints until we've done the clip-test
139 GrFixed width = glyph->fBounds.width();
140 GrFixed height = glyph->fBounds.height();
141
142 // check if we clipped out
143 if (true || NULL == glyph->fAtlas) {
144 int x = vx >> 16;
145 int y = vy >> 16;
146 if (fClipRect.quickReject(x, y, x + width, y + height)) {
147// Gr_clz(3); // so we can set a break-point in the debugger
148 return;
149 }
150 }
151
152 if (NULL == glyph->fAtlas) {
153 if (fStrike->getGlyphAtlas(glyph, scaler)) {
154 goto HAS_ATLAS;
155 }
reed@google.com313e4032010-12-22 22:16:59 +0000156 // must do this to flush inorder buffering before we purge
157 fContext->flushText();
158
reed@google.comac10a2d2010-12-22 21:39:39 +0000159 // try to purge
160 fContext->getFontCache()->purgeExceptFor(fStrike);
161 if (fStrike->getGlyphAtlas(glyph, scaler)) {
162 goto HAS_ATLAS;
163 }
164
165 // Draw as a path, so we flush any accumulated glyphs first
166 this->flushGlyphs();
167
168 if (NULL == glyph->fPath) {
169
170 GrPath* path = new GrPath;
171 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
172 // flag the glyph as being dead?
173 delete path;
174 return;
175 }
176 glyph->fPath = path;
177 }
178 GrPath::Iter iter(*glyph->fPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000179 GrPoint translate;
180 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
181 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.com5782d712011-01-21 21:03:59 +0000182 fContext->drawPath(fPaint, &iter, GrContext::kWinding_PathFill,
183 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000184 return;
185 }
186
187HAS_ATLAS:
188 GrAssert(glyph->fAtlas);
189
190 // now promote them to fixed
191 width = GrIntToFixed(width);
192 height = GrIntToFixed(height);
193
194 GrTexture* texture = glyph->fAtlas->texture();
195 GrAssert(texture);
196
197 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
198 this->flushGlyphs();
199 fCurrTexture = texture;
200 fCurrTexture->ref();
201 }
202
203 if (NULL == fVertices) {
204 // If we need to reserve vertices allow the draw target to suggest
205 // a number of verts to reserve and whether to perform a flush.
206 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000207 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000208 &fMaxVertices,
209 NULL);
210 if (flush) {
211 this->flushGlyphs();
212 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000213 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000214 fMaxVertices = kDefaultRequestedVerts;
215 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000216 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000217 &fMaxVertices,
218 NULL);
219 }
220
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000221 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
reed@google.comac10a2d2010-12-22 21:39:39 +0000222 if (fMaxVertices < kMinRequestedVerts) {
223 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000224 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000225 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000226 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000227 }
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000228 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000229 fMaxVertices, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000230 GrTCast<void**>(&fVertices),
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 NULL);
232 GrAlwaysAssert(success);
233 }
234
235 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
236 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
237
238#if GR_GL_TEXT_TEXTURE_NORMALIZED
239 int x = vx >> 16;
240 int y = vy >> 16;
241 int w = width >> 16;
242 int h = height >> 16;
243
244 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
245 setRectFan(&fVertices[2*fCurrVertex+1],
246 texture->normalizeFixedX(tx),
247 texture->normalizeFixedY(ty),
248 texture->normalizeFixedX(tx + width),
249 texture->normalizeFixedY(ty + height),
250 2);
251#else
252 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
253 2 * sizeof(GrGpuTextVertex));
254 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
255 texture->normalizeFixedY(ty),
256 texture->normalizeFixedX(tx + width),
257 texture->normalizeFixedY(ty + height),
258 2 * sizeof(GrGpuTextVertex));
259#endif
260 fCurrVertex += 4;
261}
262
263