blob: 93b7e2cf956c8ca523c54c8f9b8c577b1a8a0ffc [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac10a2d2010-12-22 21:39:39 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@google.comac10a2d2010-12-22 21:39:39 +00007 */
8
9
epoger@google.comec3ed6a2011-07-28 14:26:00 +000010
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000011#include "GrAtlas.h"
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000012#include "GrDefaultTextContext.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000013#include "GrContext.h"
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000014#include "GrDrawTarget.h"
15#include "GrFontScaler.h"
16#include "GrGpuVertex.h"
17#include "GrTemplates.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000018#include "GrTextStrike.h"
19#include "GrTextStrike_impl.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000020
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000021void GrDefaultTextContext::flushGlyphs() {
22 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +000023 if (fCurrVertex > 0) {
24 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000025 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000026 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000027 GrSamplerState::Filter filter;
28 if (fExtMatrix.isIdentity()) {
29 filter = GrSamplerState::kNearest_Filter;
30 } else {
31 filter = GrSamplerState::kBilinear_Filter;
32 }
bsalomon@google.com1e266f82011-12-12 16:11:33 +000033 drawState->sampler(kGlyphMaskStage)->reset(
34 GrSamplerState::kRepeat_WrapMode,filter);
reed@google.comac10a2d2010-12-22 21:39:39 +000035
36 GrAssert(GrIsALIGN4(fCurrVertex));
37 int nIndices = fCurrVertex + (fCurrVertex >> 1);
38 GrAssert(fCurrTexture);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000039 drawState->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000040
bsalomon@google.com669fdc42011-04-05 17:08:27 +000041 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000042 if (kOne_BlendCoeff != fGrPaint.fSrcBlendCoeff ||
43 kISA_BlendCoeff != fGrPaint.fDstBlendCoeff ||
44 fGrPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000045 GrPrintf("LCD Text will not draw correctly.\n");
46 }
47 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000048 drawState->setBlendConstant(fGrPaint.fColor);
bsalomon@google.com6b67e212011-12-09 16:10:24 +000049 drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000050 // don't modulate by the paint's color in the frag since we're
51 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000052 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000053 } else {
54 // set back to normal in case we took LCD path previously.
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000055 drawState->setBlendFunc(fGrPaint.fSrcBlendCoeff, fGrPaint.fDstBlendCoeff);
56 drawState->setColor(fGrPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000057 }
58
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000059 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
reed@google.comac10a2d2010-12-22 21:39:39 +000060
bsalomon@google.comffca4002011-02-22 20:34:01 +000061 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +000062 0, 0, fCurrVertex, nIndices);
reed@google.comac10a2d2010-12-22 21:39:39 +000063 fVertices = NULL;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000064 this->INHERITED::reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000065 }
66}
67
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000068GrDefaultTextContext::GrDefaultTextContext() {
69}
reed@google.comac10a2d2010-12-22 21:39:39 +000070
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000071GrDefaultTextContext::~GrDefaultTextContext() {
72}
73
74void GrDefaultTextContext::init(GrContext* context,
75 const GrPaint& paint,
76 const GrMatrix* extMatrix) {
77 this->INHERITED::init(context, paint, extMatrix);
78
79 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000080
81 if (NULL != extMatrix) {
82 fExtMatrix = *extMatrix;
83 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000084 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000085 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000086 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000087 if (!fExtMatrix.isIdentity()) {
88 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000089 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000090 if (fExtMatrix.invert(&inverse)) {
91 inverse.mapRect(&r);
92 r.roundOut(&fClipRect);
93 }
94 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000095 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +000096 }
bsalomon@google.comd302f142011-03-03 13:54:13 +000097 } else {
98 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +000099 }
100
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000101 // save the context's original matrix off and restore in destructor
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000102 // getTextTarget should be called after that
bsalomon@google.com5782d712011-01-21 21:03:59 +0000103 fOrigViewMatrix = fContext->getMatrix();
104 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000105
bsalomon@google.com39149582011-06-13 21:55:32 +0000106 /*
107 We need to call preConcatMatrix with our viewmatrix's inverse, for each
108 texture and mask in the paint. However, computing the inverse can be
109 expensive, and its possible we may not have any textures or masks, so these
110 two loops are written such that we only compute the inverse (once) if we
111 need it. We do this on our copy of the paint rather than directly on the
112 draw target because we re-provide the paint to the context when we have
113 to flush our glyphs or draw a glyph as a path midstream.
114 */
115 bool invVMComputed = false;
116 GrMatrix invVM;
117 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000118 if (NULL != fGrPaint.getTexture(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000119 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
120 invVMComputed = true;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000121 fGrPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000122 }
123 }
124 }
125 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000126 if (NULL != fGrPaint.getMask(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000127 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
128 invVMComputed = true;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000129 fGrPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000130 }
131 }
132 }
133
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000134 // this has been already done in the baseclass, but we need to repeat
135 // due to new matrix
136 fDrawTarget = fContext->getTextTarget(fGrPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000137
reed@google.comac10a2d2010-12-22 21:39:39 +0000138 fVertices = NULL;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000139
140 fVertexLayout =
141 GrDrawTarget::kTextFormat_VertexLayoutBit |
142 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
143
144 int stageMask = paint.getActiveStageMask();
145 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000146 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
147 if ((1 << i) & stageMask) {
148 fVertexLayout |=
149 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
150 GrAssert(i != kGlyphMaskStage);
151 }
152 }
153 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000154}
155
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000156void GrDefaultTextContext::finish() {
157 this->flush();
158
159 fStrike = NULL;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000160 fContext->setMatrix(fOrigViewMatrix);
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000161
162 this->INHERITED::finish();
reed@google.comac10a2d2010-12-22 21:39:39 +0000163}
164
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000165void GrDefaultTextContext::flush() {
166 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +0000167 this->flushGlyphs();
168}
169
170static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
171 int stride) {
172 v[0 * stride].setI(l, t);
173 v[1 * stride].setI(l, b);
174 v[2 * stride].setI(r, b);
175 v[3 * stride].setI(r, t);
176}
177
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000178void GrDefaultTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000179 GrFixed vx, GrFixed vy,
180 GrFontScaler* scaler) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000181 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +0000182 if (NULL == fStrike) {
183 fStrike = fContext->getFontCache()->getStrike(scaler);
184 }
185
186 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
187 if (NULL == glyph || glyph->fBounds.isEmpty()) {
188 return;
189 }
190
191 vx += GrIntToFixed(glyph->fBounds.fLeft);
192 vy += GrIntToFixed(glyph->fBounds.fTop);
193
194 // keep them as ints until we've done the clip-test
195 GrFixed width = glyph->fBounds.width();
196 GrFixed height = glyph->fBounds.height();
197
198 // check if we clipped out
199 if (true || NULL == glyph->fAtlas) {
200 int x = vx >> 16;
201 int y = vy >> 16;
202 if (fClipRect.quickReject(x, y, x + width, y + height)) {
203// Gr_clz(3); // so we can set a break-point in the debugger
204 return;
205 }
206 }
207
208 if (NULL == glyph->fAtlas) {
209 if (fStrike->getGlyphAtlas(glyph, scaler)) {
210 goto HAS_ATLAS;
211 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000212
213 // before we purge the cache, we must flush any accumulated draws
214 this->flushGlyphs();
reed@google.com313e4032010-12-22 22:16:59 +0000215 fContext->flushText();
216
reed@google.comac10a2d2010-12-22 21:39:39 +0000217 // try to purge
218 fContext->getFontCache()->purgeExceptFor(fStrike);
219 if (fStrike->getGlyphAtlas(glyph, scaler)) {
220 goto HAS_ATLAS;
221 }
222
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000224 GrPath* path = new GrPath;
225 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
226 // flag the glyph as being dead?
227 delete path;
228 return;
229 }
230 glyph->fPath = path;
231 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000232
reed@google.comac10a2d2010-12-22 21:39:39 +0000233 GrPoint translate;
234 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
235 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000236 fContext->drawPath(fGrPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000237 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000238 return;
239 }
240
241HAS_ATLAS:
242 GrAssert(glyph->fAtlas);
243
244 // now promote them to fixed
245 width = GrIntToFixed(width);
246 height = GrIntToFixed(height);
247
248 GrTexture* texture = glyph->fAtlas->texture();
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000249 this->prepareForGlyph(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000250
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000251 this->setupVertexBuff(GrTCast<void**>(&fVertices),
252 fVertexLayout);
reed@google.comac10a2d2010-12-22 21:39:39 +0000253
254 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
255 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
256
257#if GR_GL_TEXT_TEXTURE_NORMALIZED
258 int x = vx >> 16;
259 int y = vy >> 16;
260 int w = width >> 16;
261 int h = height >> 16;
262
263 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
264 setRectFan(&fVertices[2*fCurrVertex+1],
265 texture->normalizeFixedX(tx),
266 texture->normalizeFixedY(ty),
267 texture->normalizeFixedX(tx + width),
268 texture->normalizeFixedY(ty + height),
269 2);
270#else
271 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
272 2 * sizeof(GrGpuTextVertex));
273 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
274 texture->normalizeFixedY(ty),
275 texture->normalizeFixedX(tx + width),
276 texture->normalizeFixedY(ty + height),
277 2 * sizeof(GrGpuTextVertex));
278#endif
279 fCurrVertex += 4;
280}