blob: 9e8e40a9bd7d30a51ee2bb914178634751cf5f8b [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
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000039 GrSamplerState::Filter filter;
40 if (fExtMatrix.isIdentity()) {
41 filter = GrSamplerState::kNearest_Filter;
42 } else {
43 filter = GrSamplerState::kBilinear_Filter;
44 }
reed@google.comac10a2d2010-12-22 21:39:39 +000045 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
46 GrSamplerState::kRepeat_WrapMode,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000047 filter);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000048 fDrawTarget->setSamplerState(TEXT_STAGE, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000049
50 GrAssert(GrIsALIGN4(fCurrVertex));
51 int nIndices = fCurrVertex + (fCurrVertex >> 1);
52 GrAssert(fCurrTexture);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000053 fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000054
bsalomon@google.com669fdc42011-04-05 17:08:27 +000055 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000056 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
57 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
58 NULL != fPaint.getTexture()) {
59 GrPrintf("LCD Text will not draw correctly.\n");
60 }
61 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
62 fDrawTarget->setBlendConstant(fPaint.fColor);
63 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
64 // don't modulate by the paint's color in the frag since we're
65 // already doing it via the blend const.
66 fDrawTarget->setColor(0xffffffff);
67 } else {
68 // set back to normal in case we took LCD path previously.
69 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
70 fDrawTarget->setColor(fPaint.fColor);
71 }
72
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000073 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000074
bsalomon@google.comffca4002011-02-22 20:34:01 +000075 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000076 0, 0, fCurrVertex, nIndices);
reed@google.comac10a2d2010-12-22 21:39:39 +000077 fDrawTarget->releaseReservedGeometry();
78 fVertices = NULL;
79 fMaxVertices = 0;
80 fCurrVertex = 0;
81 fCurrTexture->unref();
82 fCurrTexture = NULL;
83 }
84}
85
bsalomon@google.com5782d712011-01-21 21:03:59 +000086GrTextContext::GrTextContext(GrContext* context,
87 const GrPaint& paint,
88 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000089 fContext = context;
90 fStrike = NULL;
91
92 fCurrTexture = NULL;
93 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000094
95 if (NULL != extMatrix) {
96 fExtMatrix = *extMatrix;
97 } else {
98 fExtMatrix = GrMatrix::I();
99 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000100 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000101 if (!fExtMatrix.isIdentity()) {
102 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000103 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000104 if (fExtMatrix.invert(&inverse)) {
105 inverse.mapRect(&r);
106 r.roundOut(&fClipRect);
107 }
108 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000109 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000110 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000111 } else {
112 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000113 }
114
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000115 // save the context's original matrix off and restore in destructor
116 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000117 fOrigViewMatrix = fContext->getMatrix();
118 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000119
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000120 fVertexLayout = BASE_VLAYOUT;
121 if (NULL != paint.getTexture()) {
122 fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
123 GrMatrix inverseViewMatrix;
124 if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000125 fPaint.fSampler.preConcatMatrix(inverseViewMatrix);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000126 }
127 }
128
reed@google.comac10a2d2010-12-22 21:39:39 +0000129 fVertices = NULL;
130 fMaxVertices = 0;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000131 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000132}
133
134GrTextContext::~GrTextContext() {
135 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000136 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000137}
138
139void GrTextContext::flush() {
140 this->flushGlyphs();
141}
142
143static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
144 int stride) {
145 v[0 * stride].setI(l, t);
146 v[1 * stride].setI(l, b);
147 v[2 * stride].setI(r, b);
148 v[3 * stride].setI(r, t);
149}
150
151void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
152 GrFixed vx, GrFixed vy,
153 GrFontScaler* scaler) {
154 if (NULL == fStrike) {
155 fStrike = fContext->getFontCache()->getStrike(scaler);
156 }
157
158 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
159 if (NULL == glyph || glyph->fBounds.isEmpty()) {
160 return;
161 }
162
163 vx += GrIntToFixed(glyph->fBounds.fLeft);
164 vy += GrIntToFixed(glyph->fBounds.fTop);
165
166 // keep them as ints until we've done the clip-test
167 GrFixed width = glyph->fBounds.width();
168 GrFixed height = glyph->fBounds.height();
169
170 // check if we clipped out
171 if (true || NULL == glyph->fAtlas) {
172 int x = vx >> 16;
173 int y = vy >> 16;
174 if (fClipRect.quickReject(x, y, x + width, y + height)) {
175// Gr_clz(3); // so we can set a break-point in the debugger
176 return;
177 }
178 }
179
180 if (NULL == glyph->fAtlas) {
181 if (fStrike->getGlyphAtlas(glyph, scaler)) {
182 goto HAS_ATLAS;
183 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000184
185 // before we purge the cache, we must flush any accumulated draws
186 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000187 fContext->flushText();
188
reed@google.comac10a2d2010-12-22 21:39:39 +0000189 // try to purge
190 fContext->getFontCache()->purgeExceptFor(fStrike);
191 if (fStrike->getGlyphAtlas(glyph, scaler)) {
192 goto HAS_ATLAS;
193 }
194
reed@google.comac10a2d2010-12-22 21:39:39 +0000195 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000196 GrPath* path = new GrPath;
197 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
198 // flag the glyph as being dead?
199 delete path;
200 return;
201 }
202 glyph->fPath = path;
203 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000204
reed@google.comac10a2d2010-12-22 21:39:39 +0000205 GrPoint translate;
206 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
207 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
reed@google.com07f3ee12011-05-16 17:21:57 +0000208 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000209 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000210 return;
211 }
212
213HAS_ATLAS:
214 GrAssert(glyph->fAtlas);
215
216 // now promote them to fixed
217 width = GrIntToFixed(width);
218 height = GrIntToFixed(height);
219
220 GrTexture* texture = glyph->fAtlas->texture();
221 GrAssert(texture);
222
223 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
224 this->flushGlyphs();
225 fCurrTexture = texture;
226 fCurrTexture->ref();
227 }
228
229 if (NULL == fVertices) {
230 // If we need to reserve vertices allow the draw target to suggest
231 // a number of verts to reserve and whether to perform a flush.
232 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000233 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000234 &fMaxVertices,
235 NULL);
236 if (flush) {
237 this->flushGlyphs();
238 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000239 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000240 fMaxVertices = kDefaultRequestedVerts;
241 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000242 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 &fMaxVertices,
244 NULL);
245 }
246
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000247 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
reed@google.comac10a2d2010-12-22 21:39:39 +0000248 if (fMaxVertices < kMinRequestedVerts) {
249 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000250 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000251 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000252 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000253 }
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000254 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000255 fMaxVertices, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000256 GrTCast<void**>(&fVertices),
reed@google.comac10a2d2010-12-22 21:39:39 +0000257 NULL);
258 GrAlwaysAssert(success);
259 }
260
261 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
262 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
263
264#if GR_GL_TEXT_TEXTURE_NORMALIZED
265 int x = vx >> 16;
266 int y = vy >> 16;
267 int w = width >> 16;
268 int h = height >> 16;
269
270 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
271 setRectFan(&fVertices[2*fCurrVertex+1],
272 texture->normalizeFixedX(tx),
273 texture->normalizeFixedY(ty),
274 texture->normalizeFixedX(tx + width),
275 texture->normalizeFixedY(ty + height),
276 2);
277#else
278 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
279 2 * sizeof(GrGpuTextVertex));
280 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
281 texture->normalizeFixedY(ty),
282 texture->normalizeFixedX(tx + width),
283 texture->normalizeFixedY(ty + height),
284 2 * sizeof(GrGpuTextVertex));
285#endif
286 fCurrVertex += 4;
287}
288
289