blob: 23b16dd3ae59339a6fc457842e1b0298e7f0f2ff [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2010 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@google.comac10a2d2010-12-22 21:39:39 +00006 */
7
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
tomhudson@google.com375ff852012-06-29 18:37:57 +000010#include "GrTextContext.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000011#include "GrAtlas.h"
12#include "GrContext.h"
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000013#include "GrDrawTarget.h"
14#include "GrFontScaler.h"
15#include "GrGpuVertex.h"
tomhudson@google.com375ff852012-06-29 18:37:57 +000016#include "GrIndexBuffer.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000017#include "GrTextStrike.h"
18#include "GrTextStrike_impl.h"
tomhudson@google.com375ff852012-06-29 18:37:57 +000019#include "SkPath.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000020
tomhudson@google.com375ff852012-06-29 18:37:57 +000021enum {
22 kGlyphMaskStage = GrPaint::kTotalStages,
23};
24
25void GrTextContext::flushGlyphs() {
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000026 if (NULL == fDrawTarget) {
27 return;
28 }
29 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000030 if (fCurrVertex > 0) {
reed@google.comac10a2d2010-12-22 21:39:39 +000031 // setup our sampler state for our text texture/atlas
bsalomon@google.comb8670992012-07-25 21:27:09 +000032 drawState->sampler(kGlyphMaskStage)->reset(SkShader::kRepeat_TileMode,
bsalomon@google.comeb9568a2012-07-26 14:12:23 +000033 !fExtMatrix.isIdentity());
reed@google.comac10a2d2010-12-22 21:39:39 +000034
35 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000036 GrAssert(fCurrTexture);
tomhudson@google.com1e8f0162012-07-20 16:25:18 +000037 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000038
bsalomon@google.com669fdc42011-04-05 17:08:27 +000039 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000040 if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
41 kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
42 fPaint.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
tomhudson@google.com375ff852012-06-29 18:37:57 +000046 drawState->setBlendConstant(fPaint.fColor);
bsalomon@google.com47059542012-06-06 20:51:20 +000047 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
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.
tomhudson@google.com375ff852012-06-29 18:37:57 +000053 drawState->setBlendFunc(fPaint.fSrcBlendCoeff,
54 fPaint.fDstBlendCoeff);
55 drawState->setColor(fPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000056 }
57
bsalomon@google.com934c5702012-03-20 21:17:58 +000058 int nGlyphs = fCurrVertex / 4;
tomhudson@google.com375ff852012-06-29 18:37:57 +000059 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com47059542012-06-06 20:51:20 +000060 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
bsalomon@google.com934c5702012-03-20 21:17:58 +000061 nGlyphs,
62 4, 6);
tomhudson@google.com375ff852012-06-29 18:37:57 +000063 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000064 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +000065 fMaxVertices = 0;
66 fCurrVertex = 0;
67 GrSafeSetNull(fCurrTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +000068 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000069 drawState->disableStages();
70 fDrawTarget = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000071}
72
tomhudson@google.com375ff852012-06-29 18:37:57 +000073GrTextContext::GrTextContext(GrContext* context,
74 const GrPaint& paint,
75 const GrMatrix* extMatrix) : fPaint(paint) {
76 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000077 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000078
tomhudson@google.com375ff852012-06-29 18:37:57 +000079 fCurrTexture = NULL;
80 fCurrVertex = 0;
81
reed@google.comac10a2d2010-12-22 21:39:39 +000082 if (NULL != extMatrix) {
83 fExtMatrix = *extMatrix;
84 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000085 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000086 }
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000087
robertphillips@google.combeb1af72012-07-26 18:52:16 +000088 const GrClipData* clipData = context->getClip();
89
robertphillips@google.com641f8b12012-07-31 19:15:58 +000090 GrRect devConservativeBound;
91 clipData->fClipStack->getConservativeBounds(
92 -clipData->fOrigin.fX,
93 -clipData->fOrigin.fY,
94 context->getRenderTarget()->width(),
95 context->getRenderTarget()->height(),
96 &devConservativeBound);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000097
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000098 if (!fExtMatrix.isIdentity()) {
99 GrMatrix inverse;
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +0000100 if (fExtMatrix.invert(&inverse)) {
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000101 inverse.mapRect(&devConservativeBound);
reed@google.comac10a2d2010-12-22 21:39:39 +0000102 }
103 }
104
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000105 devConservativeBound.roundOut(&fClipRect);
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000106
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000107 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000108 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000109 fOrigViewMatrix = fContext->getMatrix();
110 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000111
bsalomon@google.com39149582011-06-13 21:55:32 +0000112 /*
113 We need to call preConcatMatrix with our viewmatrix's inverse, for each
114 texture and mask in the paint. However, computing the inverse can be
115 expensive, and its possible we may not have any textures or masks, so these
116 two loops are written such that we only compute the inverse (once) if we
117 need it. We do this on our copy of the paint rather than directly on the
118 draw target because we re-provide the paint to the context when we have
119 to flush our glyphs or draw a glyph as a path midstream.
120 */
121 bool invVMComputed = false;
122 GrMatrix invVM;
123 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000124 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000125 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
126 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000127 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000128 }
129 }
130 }
131 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000132 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000133 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
134 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000135 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000136 }
137 }
138 }
139
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000140 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000141
reed@google.comac10a2d2010-12-22 21:39:39 +0000142 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000143 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000144
145 fVertexLayout =
146 GrDrawTarget::kTextFormat_VertexLayoutBit |
147 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000148}
149
tomhudson@google.com375ff852012-06-29 18:37:57 +0000150GrTextContext::~GrTextContext() {
151 this->flushGlyphs();
152 if (fDrawTarget) {
153 fDrawTarget->drawState()->disableStages();
154 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000155 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000156}
157
tomhudson@google.com375ff852012-06-29 18:37:57 +0000158void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000159 this->flushGlyphs();
160}
161
162static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
163 int stride) {
164 v[0 * stride].setI(l, t);
165 v[1 * stride].setI(l, b);
166 v[2 * stride].setI(r, b);
167 v[3 * stride].setI(r, t);
168}
169
tomhudson@google.com375ff852012-06-29 18:37:57 +0000170void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000171 GrFixed vx, GrFixed vy,
172 GrFontScaler* scaler) {
173 if (NULL == fStrike) {
174 fStrike = fContext->getFontCache()->getStrike(scaler);
175 }
176
177 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
178 if (NULL == glyph || glyph->fBounds.isEmpty()) {
179 return;
180 }
181
182 vx += GrIntToFixed(glyph->fBounds.fLeft);
183 vy += GrIntToFixed(glyph->fBounds.fTop);
184
185 // keep them as ints until we've done the clip-test
186 GrFixed width = glyph->fBounds.width();
187 GrFixed height = glyph->fBounds.height();
188
189 // check if we clipped out
190 if (true || NULL == glyph->fAtlas) {
191 int x = vx >> 16;
192 int y = vy >> 16;
193 if (fClipRect.quickReject(x, y, x + width, y + height)) {
194// Gr_clz(3); // so we can set a break-point in the debugger
195 return;
196 }
197 }
198
199 if (NULL == glyph->fAtlas) {
200 if (fStrike->getGlyphAtlas(glyph, scaler)) {
201 goto HAS_ATLAS;
202 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000203
204 // before we purge the cache, we must flush any accumulated draws
205 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000206 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000207
reed@google.comac10a2d2010-12-22 21:39:39 +0000208 // try to purge
209 fContext->getFontCache()->purgeExceptFor(fStrike);
210 if (fStrike->getGlyphAtlas(glyph, scaler)) {
211 goto HAS_ATLAS;
212 }
213
reed@google.comac10a2d2010-12-22 21:39:39 +0000214 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000215 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
217 // flag the glyph as being dead?
218 delete path;
219 return;
220 }
221 glyph->fPath = path;
222 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000223
reed@google.comac10a2d2010-12-22 21:39:39 +0000224 GrPoint translate;
225 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
226 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000227 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000228 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000229 return;
230 }
231
232HAS_ATLAS:
233 GrAssert(glyph->fAtlas);
234
235 // now promote them to fixed
236 width = GrIntToFixed(width);
237 height = GrIntToFixed(height);
238
239 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000240 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000241
tomhudson@google.com375ff852012-06-29 18:37:57 +0000242 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
243 this->flushGlyphs();
244 fCurrTexture = texture;
245 fCurrTexture->ref();
246 }
247
248 if (NULL == fVertices) {
249 // If we need to reserve vertices allow the draw target to suggest
250 // a number of verts to reserve and whether to perform a flush.
251 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000252 bool flush = (NULL != fDrawTarget) &&
253 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000254 &fMaxVertices,
255 NULL);
256 if (flush) {
257 this->flushGlyphs();
258 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000259 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000260 fDrawTarget = fContext->getTextTarget(fPaint);
261 fMaxVertices = kDefaultRequestedVerts;
262 // ignore return, no point in flushing again.
263 fDrawTarget->geometryHints(fVertexLayout,
264 &fMaxVertices,
265 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000266
267 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
268 if (fMaxVertices < kMinRequestedVerts) {
269 fMaxVertices = kDefaultRequestedVerts;
270 } else if (fMaxVertices > maxQuadVertices) {
271 // don't exceed the limit of the index buffer
272 fMaxVertices = maxQuadVertices;
273 }
274 bool success = fDrawTarget->reserveVertexAndIndexSpace(
275 fVertexLayout,
276 fMaxVertices,
277 0,
278 GrTCast<void**>(&fVertices),
279 NULL);
280 GrAlwaysAssert(success);
281 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000282
283 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
284 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
285
bsalomon@google.com2717d562012-05-07 19:10:52 +0000286#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000287 int x = vx >> 16;
288 int y = vy >> 16;
289 int w = width >> 16;
290 int h = height >> 16;
291
292 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
293 setRectFan(&fVertices[2*fCurrVertex+1],
294 texture->normalizeFixedX(tx),
295 texture->normalizeFixedY(ty),
296 texture->normalizeFixedX(tx + width),
297 texture->normalizeFixedY(ty + height),
298 2);
299#else
300 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
301 2 * sizeof(GrGpuTextVertex));
302 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
303 texture->normalizeFixedY(ty),
304 texture->normalizeFixedX(tx + width),
305 texture->normalizeFixedY(ty + height),
306 2 * sizeof(GrGpuTextVertex));
307#endif
308 fCurrVertex += 4;
309}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000310