blob: a606dd5c649128cab4f2a651ea0dad5d7d3e5455 [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) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000024 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000025 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000026 GrSamplerState::Filter filter;
27 if (fExtMatrix.isIdentity()) {
28 filter = GrSamplerState::kNearest_Filter;
29 } else {
30 filter = GrSamplerState::kBilinear_Filter;
31 }
bsalomon@google.com1e266f82011-12-12 16:11:33 +000032 drawState->sampler(kGlyphMaskStage)->reset(
33 GrSamplerState::kRepeat_WrapMode,filter);
reed@google.comac10a2d2010-12-22 21:39:39 +000034
35 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000036 GrAssert(fCurrTexture);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000037 drawState->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000038
bsalomon@google.com669fdc42011-04-05 17:08:27 +000039 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000040 if (kOne_BlendCoeff != fGrPaint.fSrcBlendCoeff ||
41 kISA_BlendCoeff != fGrPaint.fDstBlendCoeff ||
42 fGrPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000043 GrPrintf("LCD Text will not draw correctly.\n");
44 }
45 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000046 drawState->setBlendConstant(fGrPaint.fColor);
bsalomon@google.com6b67e212011-12-09 16:10:24 +000047 drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000048 // don't modulate by the paint's color in the frag since we're
49 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000050 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000051 } else {
52 // set back to normal in case we took LCD path previously.
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000053 drawState->setBlendFunc(fGrPaint.fSrcBlendCoeff, fGrPaint.fDstBlendCoeff);
54 drawState->setColor(fGrPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000055 }
56
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000057 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com934c5702012-03-20 21:17:58 +000058 int nGlyphs = fCurrVertex / 4;
59 fDrawTarget->drawIndexedInstances(kTriangles_PrimitiveType,
60 nGlyphs,
61 4, 6);
reed@google.comac10a2d2010-12-22 21:39:39 +000062 fVertices = NULL;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000063 this->INHERITED::reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000064 }
65}
66
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000067GrDefaultTextContext::GrDefaultTextContext() {
68}
reed@google.comac10a2d2010-12-22 21:39:39 +000069
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000070GrDefaultTextContext::~GrDefaultTextContext() {
71}
72
73void GrDefaultTextContext::init(GrContext* context,
74 const GrPaint& paint,
75 const GrMatrix* extMatrix) {
76 this->INHERITED::init(context, paint, extMatrix);
77
78 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000079
80 if (NULL != extMatrix) {
81 fExtMatrix = *extMatrix;
82 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000083 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000084 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000085 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000086 if (!fExtMatrix.isIdentity()) {
87 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000088 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000089 if (fExtMatrix.invert(&inverse)) {
90 inverse.mapRect(&r);
91 r.roundOut(&fClipRect);
92 }
93 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000094 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +000095 }
bsalomon@google.comd302f142011-03-03 13:54:13 +000096 } else {
97 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +000098 }
99
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000100 // save the context's original matrix off and restore in destructor
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000101 // getTextTarget should be called after that
bsalomon@google.com5782d712011-01-21 21:03:59 +0000102 fOrigViewMatrix = fContext->getMatrix();
103 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000104
bsalomon@google.com39149582011-06-13 21:55:32 +0000105 /*
106 We need to call preConcatMatrix with our viewmatrix's inverse, for each
107 texture and mask in the paint. However, computing the inverse can be
108 expensive, and its possible we may not have any textures or masks, so these
109 two loops are written such that we only compute the inverse (once) if we
110 need it. We do this on our copy of the paint rather than directly on the
111 draw target because we re-provide the paint to the context when we have
112 to flush our glyphs or draw a glyph as a path midstream.
113 */
114 bool invVMComputed = false;
115 GrMatrix invVM;
116 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000117 if (NULL != fGrPaint.getTexture(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000118 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
119 invVMComputed = true;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000120 fGrPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000121 }
122 }
123 }
124 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000125 if (NULL != fGrPaint.getMask(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000126 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
127 invVMComputed = true;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000128 fGrPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000129 }
130 }
131 }
132
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000133 // this has been already done in the baseclass, but we need to repeat
134 // due to new matrix
135 fDrawTarget = fContext->getTextTarget(fGrPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000136
reed@google.comac10a2d2010-12-22 21:39:39 +0000137 fVertices = NULL;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000138
139 fVertexLayout =
140 GrDrawTarget::kTextFormat_VertexLayoutBit |
141 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
142
143 int stageMask = paint.getActiveStageMask();
144 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000145 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
146 if ((1 << i) & stageMask) {
147 fVertexLayout |=
148 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
149 GrAssert(i != kGlyphMaskStage);
150 }
151 }
152 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000153}
154
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000155void GrDefaultTextContext::finish() {
156 this->flush();
157
158 fStrike = NULL;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000159 fContext->setMatrix(fOrigViewMatrix);
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000160
161 this->INHERITED::finish();
reed@google.comac10a2d2010-12-22 21:39:39 +0000162}
163
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000164void GrDefaultTextContext::flush() {
165 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +0000166 this->flushGlyphs();
167}
168
169static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
170 int stride) {
171 v[0 * stride].setI(l, t);
172 v[1 * stride].setI(l, b);
173 v[2 * stride].setI(r, b);
174 v[3 * stride].setI(r, t);
175}
176
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000177void GrDefaultTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000178 GrFixed vx, GrFixed vy,
179 GrFontScaler* scaler) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000180 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +0000181 if (NULL == fStrike) {
182 fStrike = fContext->getFontCache()->getStrike(scaler);
183 }
184
185 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
186 if (NULL == glyph || glyph->fBounds.isEmpty()) {
187 return;
188 }
189
190 vx += GrIntToFixed(glyph->fBounds.fLeft);
191 vy += GrIntToFixed(glyph->fBounds.fTop);
192
193 // keep them as ints until we've done the clip-test
194 GrFixed width = glyph->fBounds.width();
195 GrFixed height = glyph->fBounds.height();
196
197 // check if we clipped out
198 if (true || NULL == glyph->fAtlas) {
199 int x = vx >> 16;
200 int y = vy >> 16;
201 if (fClipRect.quickReject(x, y, x + width, y + height)) {
202// Gr_clz(3); // so we can set a break-point in the debugger
203 return;
204 }
205 }
206
207 if (NULL == glyph->fAtlas) {
208 if (fStrike->getGlyphAtlas(glyph, scaler)) {
209 goto HAS_ATLAS;
210 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000211
212 // before we purge the cache, we must flush any accumulated draws
213 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000214 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000215
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 // try to purge
217 fContext->getFontCache()->purgeExceptFor(fStrike);
218 if (fStrike->getGlyphAtlas(glyph, scaler)) {
219 goto HAS_ATLAS;
220 }
221
reed@google.comac10a2d2010-12-22 21:39:39 +0000222 if (NULL == glyph->fPath) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 GrPath* path = new GrPath;
224 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
225 // flag the glyph as being dead?
226 delete path;
227 return;
228 }
229 glyph->fPath = path;
230 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000231
reed@google.comac10a2d2010-12-22 21:39:39 +0000232 GrPoint translate;
233 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
234 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000235 fContext->drawPath(fGrPaint, *glyph->fPath, kWinding_PathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000236 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000237 return;
238 }
239
240HAS_ATLAS:
241 GrAssert(glyph->fAtlas);
242
243 // now promote them to fixed
244 width = GrIntToFixed(width);
245 height = GrIntToFixed(height);
246
247 GrTexture* texture = glyph->fAtlas->texture();
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000248 this->prepareForGlyph(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000249
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000250 this->setupVertexBuff(GrTCast<void**>(&fVertices),
251 fVertexLayout);
reed@google.comac10a2d2010-12-22 21:39:39 +0000252
253 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
254 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
255
256#if GR_GL_TEXT_TEXTURE_NORMALIZED
257 int x = vx >> 16;
258 int y = vy >> 16;
259 int w = width >> 16;
260 int h = height >> 16;
261
262 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
263 setRectFan(&fVertices[2*fCurrVertex+1],
264 texture->normalizeFixedX(tx),
265 texture->normalizeFixedY(ty),
266 texture->normalizeFixedX(tx + width),
267 texture->normalizeFixedY(ty + height),
268 2);
269#else
270 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
271 2 * sizeof(GrGpuTextVertex));
272 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
273 texture->normalizeFixedY(ty),
274 texture->normalizeFixedX(tx + width),
275 texture->normalizeFixedY(ty + height),
276 2 * sizeof(GrGpuTextVertex));
277#endif
278 fCurrVertex += 4;
279}