blob: 0f244a2e8108bdd2bbd26f71609e929455bc231b [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);
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000048 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000049
50 fDrawTarget->drawIndexed(GrDrawTarget::kTriangles_PrimitiveType,
51 0, 0, fCurrVertex, nIndices);
reed@google.comac10a2d2010-12-22 21:39:39 +000052 fDrawTarget->releaseReservedGeometry();
53 fVertices = NULL;
54 fMaxVertices = 0;
55 fCurrVertex = 0;
56 fCurrTexture->unref();
57 fCurrTexture = NULL;
58 }
59}
60
bsalomon@google.com5782d712011-01-21 21:03:59 +000061GrTextContext::GrTextContext(GrContext* context,
62 const GrPaint& paint,
63 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000064 fContext = context;
65 fStrike = NULL;
66
67 fCurrTexture = NULL;
68 fCurrVertex = 0;
69 fClipRect = context->getClip().getBounds();
70
71 if (NULL != extMatrix) {
72 fExtMatrix = *extMatrix;
73 } else {
74 fExtMatrix = GrMatrix::I();
75 }
76 if (!fExtMatrix.isIdentity()) {
77 GrMatrix inverse;
78 GrRect r;
79 r.set(fClipRect);
80 if (fExtMatrix.invert(&inverse)) {
81 inverse.mapRect(&r);
82 r.roundOut(&fClipRect);
83 }
84 }
85
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000086 // save the context's original matrix off and restore in destructor
87 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +000088 fOrigViewMatrix = fContext->getMatrix();
89 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +000090
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000091 fVertexLayout = BASE_VLAYOUT;
92 if (NULL != paint.getTexture()) {
93 fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
94 GrMatrix inverseViewMatrix;
95 if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +000096 fPaint.fSampler.preConcatMatrix(inverseViewMatrix);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000097 }
98 }
99
reed@google.comac10a2d2010-12-22 21:39:39 +0000100 fVertices = NULL;
101 fMaxVertices = 0;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000102 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000103}
104
105GrTextContext::~GrTextContext() {
106 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000107 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000108}
109
110void GrTextContext::flush() {
111 this->flushGlyphs();
112}
113
114static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
115 int stride) {
116 v[0 * stride].setI(l, t);
117 v[1 * stride].setI(l, b);
118 v[2 * stride].setI(r, b);
119 v[3 * stride].setI(r, t);
120}
121
122void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
123 GrFixed vx, GrFixed vy,
124 GrFontScaler* scaler) {
125 if (NULL == fStrike) {
126 fStrike = fContext->getFontCache()->getStrike(scaler);
127 }
128
129 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
130 if (NULL == glyph || glyph->fBounds.isEmpty()) {
131 return;
132 }
133
134 vx += GrIntToFixed(glyph->fBounds.fLeft);
135 vy += GrIntToFixed(glyph->fBounds.fTop);
136
137 // keep them as ints until we've done the clip-test
138 GrFixed width = glyph->fBounds.width();
139 GrFixed height = glyph->fBounds.height();
140
141 // check if we clipped out
142 if (true || NULL == glyph->fAtlas) {
143 int x = vx >> 16;
144 int y = vy >> 16;
145 if (fClipRect.quickReject(x, y, x + width, y + height)) {
146// Gr_clz(3); // so we can set a break-point in the debugger
147 return;
148 }
149 }
150
151 if (NULL == glyph->fAtlas) {
152 if (fStrike->getGlyphAtlas(glyph, scaler)) {
153 goto HAS_ATLAS;
154 }
reed@google.com313e4032010-12-22 22:16:59 +0000155 // must do this to flush inorder buffering before we purge
156 fContext->flushText();
157
reed@google.comac10a2d2010-12-22 21:39:39 +0000158 // try to purge
159 fContext->getFontCache()->purgeExceptFor(fStrike);
160 if (fStrike->getGlyphAtlas(glyph, scaler)) {
161 goto HAS_ATLAS;
162 }
163
164 // Draw as a path, so we flush any accumulated glyphs first
165 this->flushGlyphs();
166
167 if (NULL == glyph->fPath) {
168
169 GrPath* path = new GrPath;
170 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
171 // flag the glyph as being dead?
172 delete path;
173 return;
174 }
175 glyph->fPath = path;
176 }
177 GrPath::Iter iter(*glyph->fPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000178 GrPoint translate;
179 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
180 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.com5782d712011-01-21 21:03:59 +0000181 fContext->drawPath(fPaint, &iter, GrContext::kWinding_PathFill,
182 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000183 return;
184 }
185
186HAS_ATLAS:
187 GrAssert(glyph->fAtlas);
188
189 // now promote them to fixed
190 width = GrIntToFixed(width);
191 height = GrIntToFixed(height);
192
193 GrTexture* texture = glyph->fAtlas->texture();
194 GrAssert(texture);
195
196 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
197 this->flushGlyphs();
198 fCurrTexture = texture;
199 fCurrTexture->ref();
200 }
201
202 if (NULL == fVertices) {
203 // If we need to reserve vertices allow the draw target to suggest
204 // a number of verts to reserve and whether to perform a flush.
205 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000206 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000207 &fMaxVertices,
208 NULL);
209 if (flush) {
210 this->flushGlyphs();
211 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000212 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000213 fMaxVertices = kDefaultRequestedVerts;
214 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000215 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 &fMaxVertices,
217 NULL);
218 }
219
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000220 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 if (fMaxVertices < kMinRequestedVerts) {
222 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000223 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000224 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000225 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000226 }
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000227 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000228 fMaxVertices, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000229 GrTCast<void**>(&fVertices),
reed@google.comac10a2d2010-12-22 21:39:39 +0000230 NULL);
231 GrAlwaysAssert(success);
232 }
233
234 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
235 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
236
237#if GR_GL_TEXT_TEXTURE_NORMALIZED
238 int x = vx >> 16;
239 int y = vy >> 16;
240 int w = width >> 16;
241 int h = height >> 16;
242
243 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
244 setRectFan(&fVertices[2*fCurrVertex+1],
245 texture->normalizeFixedX(tx),
246 texture->normalizeFixedY(ty),
247 texture->normalizeFixedX(tx + width),
248 texture->normalizeFixedY(ty + height),
249 2);
250#else
251 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
252 2 * sizeof(GrGpuTextVertex));
253 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
254 texture->normalizeFixedY(ty),
255 texture->normalizeFixedX(tx + width),
256 texture->normalizeFixedY(ty + height),
257 2 * sizeof(GrGpuTextVertex));
258#endif
259 fCurrVertex += 4;
260}
261
262