blob: 2aacfa25ad919f90bb0501584dcbb51d5ab603ac [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.com26c2d0a2011-05-17 20:15:30 +000028enum {
29 kGlyphMaskStage = GrPaint::kTotalStages,
30};
reed@google.comac10a2d2010-12-22 21:39:39 +000031
32void GrTextContext::flushGlyphs() {
33 if (fCurrVertex > 0) {
34 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
35
36 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000037 GrSamplerState::Filter filter;
38 if (fExtMatrix.isIdentity()) {
39 filter = GrSamplerState::kNearest_Filter;
40 } else {
41 filter = GrSamplerState::kBilinear_Filter;
42 }
reed@google.comac10a2d2010-12-22 21:39:39 +000043 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
44 GrSamplerState::kRepeat_WrapMode,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000045 filter);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000046 fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +000047
48 GrAssert(GrIsALIGN4(fCurrVertex));
49 int nIndices = fCurrVertex + (fCurrVertex >> 1);
50 GrAssert(fCurrTexture);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000051 fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000052
bsalomon@google.com669fdc42011-04-05 17:08:27 +000053 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000054 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
55 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000056 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000057 GrPrintf("LCD Text will not draw correctly.\n");
58 }
59 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
60 fDrawTarget->setBlendConstant(fPaint.fColor);
61 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
62 // don't modulate by the paint's color in the frag since we're
63 // already doing it via the blend const.
64 fDrawTarget->setColor(0xffffffff);
65 } else {
66 // set back to normal in case we took LCD path previously.
67 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
68 fDrawTarget->setColor(fPaint.fColor);
69 }
70
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000071 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000072
bsalomon@google.comffca4002011-02-22 20:34:01 +000073 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000074 0, 0, fCurrVertex, nIndices);
reed@google.comac10a2d2010-12-22 21:39:39 +000075 fDrawTarget->releaseReservedGeometry();
76 fVertices = NULL;
77 fMaxVertices = 0;
78 fCurrVertex = 0;
79 fCurrTexture->unref();
80 fCurrTexture = NULL;
81 }
82}
83
bsalomon@google.com5782d712011-01-21 21:03:59 +000084GrTextContext::GrTextContext(GrContext* context,
85 const GrPaint& paint,
86 const GrMatrix* extMatrix) : fPaint(paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +000087 fContext = context;
88 fStrike = NULL;
89
90 fCurrTexture = NULL;
91 fCurrVertex = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000092
93 if (NULL != extMatrix) {
94 fExtMatrix = *extMatrix;
95 } else {
96 fExtMatrix = GrMatrix::I();
97 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000098 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000099 if (!fExtMatrix.isIdentity()) {
100 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000101 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000102 if (fExtMatrix.invert(&inverse)) {
103 inverse.mapRect(&r);
104 r.roundOut(&fClipRect);
105 }
106 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +0000107 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000108 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000109 } else {
110 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000111 }
112
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000113 // save the context's original matrix off and restore in destructor
114 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000115 fOrigViewMatrix = fContext->getMatrix();
116 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000117
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000118 fDrawTarget = fContext->getTextTarget(fPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000119
reed@google.comac10a2d2010-12-22 21:39:39 +0000120 fVertices = NULL;
121 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000122
123 fVertexLayout =
124 GrDrawTarget::kTextFormat_VertexLayoutBit |
125 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
126
127 int stageMask = paint.getActiveStageMask();
128 if (stageMask) {
129 GrMatrix inverseViewMatrix;
130 if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
131 fDrawTarget->preConcatSamplerMatrices(stageMask,
132 inverseViewMatrix);
133 }
134 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
135 if ((1 << i) & stageMask) {
136 fVertexLayout |=
137 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
138 GrAssert(i != kGlyphMaskStage);
139 }
140 }
141 }
142
reed@google.comac10a2d2010-12-22 21:39:39 +0000143}
144
145GrTextContext::~GrTextContext() {
146 this->flushGlyphs();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000147 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000148}
149
150void GrTextContext::flush() {
151 this->flushGlyphs();
152}
153
154static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
155 int stride) {
156 v[0 * stride].setI(l, t);
157 v[1 * stride].setI(l, b);
158 v[2 * stride].setI(r, b);
159 v[3 * stride].setI(r, t);
160}
161
162void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
163 GrFixed vx, GrFixed vy,
164 GrFontScaler* scaler) {
165 if (NULL == fStrike) {
166 fStrike = fContext->getFontCache()->getStrike(scaler);
167 }
168
169 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
170 if (NULL == glyph || glyph->fBounds.isEmpty()) {
171 return;
172 }
173
174 vx += GrIntToFixed(glyph->fBounds.fLeft);
175 vy += GrIntToFixed(glyph->fBounds.fTop);
176
177 // keep them as ints until we've done the clip-test
178 GrFixed width = glyph->fBounds.width();
179 GrFixed height = glyph->fBounds.height();
180
181 // check if we clipped out
182 if (true || NULL == glyph->fAtlas) {
183 int x = vx >> 16;
184 int y = vy >> 16;
185 if (fClipRect.quickReject(x, y, x + width, y + height)) {
186// Gr_clz(3); // so we can set a break-point in the debugger
187 return;
188 }
189 }
190
191 if (NULL == glyph->fAtlas) {
192 if (fStrike->getGlyphAtlas(glyph, scaler)) {
193 goto HAS_ATLAS;
194 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000195
196 // before we purge the cache, we must flush any accumulated draws
197 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000198 fContext->flushText();
199
reed@google.comac10a2d2010-12-22 21:39:39 +0000200 // try to purge
201 fContext->getFontCache()->purgeExceptFor(fStrike);
202 if (fStrike->getGlyphAtlas(glyph, scaler)) {
203 goto HAS_ATLAS;
204 }
205
reed@google.comac10a2d2010-12-22 21:39:39 +0000206 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000207 GrPath* path = new GrPath;
208 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
209 // flag the glyph as being dead?
210 delete path;
211 return;
212 }
213 glyph->fPath = path;
214 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000215
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 GrPoint translate;
217 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
218 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
reed@google.com07f3ee12011-05-16 17:21:57 +0000219 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000220 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 return;
222 }
223
224HAS_ATLAS:
225 GrAssert(glyph->fAtlas);
226
227 // now promote them to fixed
228 width = GrIntToFixed(width);
229 height = GrIntToFixed(height);
230
231 GrTexture* texture = glyph->fAtlas->texture();
232 GrAssert(texture);
233
234 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
235 this->flushGlyphs();
236 fCurrTexture = texture;
237 fCurrTexture->ref();
238 }
239
240 if (NULL == fVertices) {
241 // If we need to reserve vertices allow the draw target to suggest
242 // a number of verts to reserve and whether to perform a flush.
243 fMaxVertices = kMinRequestedVerts;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000244 bool flush = fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000245 &fMaxVertices,
246 NULL);
247 if (flush) {
248 this->flushGlyphs();
249 fContext->flushText();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000250 fDrawTarget = fContext->getTextTarget(fPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000251 fMaxVertices = kDefaultRequestedVerts;
252 // ignore return, no point in flushing again.
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000253 fDrawTarget->geometryHints(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000254 &fMaxVertices,
255 NULL);
256 }
257
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000258 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
reed@google.comac10a2d2010-12-22 21:39:39 +0000259 if (fMaxVertices < kMinRequestedVerts) {
260 fMaxVertices = kDefaultRequestedVerts;
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000261 } else if (fMaxVertices > maxQuadVertices) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000262 // don't exceed the limit of the index buffer
bsalomon@google.com86afc2a2011-02-16 16:12:19 +0000263 fMaxVertices = maxQuadVertices;
reed@google.comac10a2d2010-12-22 21:39:39 +0000264 }
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000265 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
reed@google.comac10a2d2010-12-22 21:39:39 +0000266 fMaxVertices, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000267 GrTCast<void**>(&fVertices),
reed@google.comac10a2d2010-12-22 21:39:39 +0000268 NULL);
269 GrAlwaysAssert(success);
270 }
271
272 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
273 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
274
275#if GR_GL_TEXT_TEXTURE_NORMALIZED
276 int x = vx >> 16;
277 int y = vy >> 16;
278 int w = width >> 16;
279 int h = height >> 16;
280
281 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
282 setRectFan(&fVertices[2*fCurrVertex+1],
283 texture->normalizeFixedX(tx),
284 texture->normalizeFixedY(ty),
285 texture->normalizeFixedX(tx + width),
286 texture->normalizeFixedY(ty + height),
287 2);
288#else
289 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
290 2 * sizeof(GrGpuTextVertex));
291 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
292 texture->normalizeFixedY(ty),
293 texture->normalizeFixedX(tx + width),
294 texture->normalizeFixedY(ty + height),
295 2 * sizeof(GrGpuTextVertex));
296#endif
297 fCurrVertex += 4;
298}
299
300