blob: 5c8b9e50d31a94f180c79f24219e29f1a95b70a7 [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 {
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +000022
tomhudson@google.com375ff852012-06-29 18:37:57 +000023 kGlyphMaskStage = GrPaint::kTotalStages,
24};
25
26void GrTextContext::flushGlyphs() {
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000027 if (NULL == fDrawTarget) {
28 return;
29 }
30 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000031 if (fCurrVertex > 0) {
reed@google.comac10a2d2010-12-22 21:39:39 +000032 // setup our sampler state for our text texture/atlas
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +000033 drawState->sampler(kGlyphMaskStage)->reset();
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.com1ce49fc2012-09-18 14:14:49 +000037 GrTextureParams params(SkShader::kRepeat_TileMode, !fExtMatrix.isIdentity());
38 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, params);
bsalomon@google.com080773c2011-03-15 19:09:25 +000039
bsalomon@google.com669fdc42011-04-05 17:08:27 +000040 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000041 if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
42 kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
bsalomon@google.com88becf42012-10-05 14:54:42 +000043 fPaint.hasColorStage()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000044 GrPrintf("LCD Text will not draw correctly.\n");
45 }
46 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
tomhudson@google.com375ff852012-06-29 18:37:57 +000047 drawState->setBlendConstant(fPaint.fColor);
bsalomon@google.com47059542012-06-06 20:51:20 +000048 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000049 // don't modulate by the paint's color in the frag since we're
50 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000051 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000052 } else {
53 // set back to normal in case we took LCD path previously.
tomhudson@google.com375ff852012-06-29 18:37:57 +000054 drawState->setBlendFunc(fPaint.fSrcBlendCoeff,
55 fPaint.fDstBlendCoeff);
56 drawState->setColor(fPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000057 }
58
bsalomon@google.com934c5702012-03-20 21:17:58 +000059 int nGlyphs = fCurrVertex / 4;
tomhudson@google.com375ff852012-06-29 18:37:57 +000060 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com47059542012-06-06 20:51:20 +000061 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
bsalomon@google.com934c5702012-03-20 21:17:58 +000062 nGlyphs,
63 4, 6);
tomhudson@google.com375ff852012-06-29 18:37:57 +000064 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000065 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +000066 fMaxVertices = 0;
67 fCurrVertex = 0;
68 GrSafeSetNull(fCurrTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +000069 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000070 drawState->disableStages();
71 fDrawTarget = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000072}
73
tomhudson@google.com375ff852012-06-29 18:37:57 +000074GrTextContext::GrTextContext(GrContext* context,
75 const GrPaint& paint,
76 const GrMatrix* extMatrix) : fPaint(paint) {
77 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000078 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000079
tomhudson@google.com375ff852012-06-29 18:37:57 +000080 fCurrTexture = NULL;
81 fCurrVertex = 0;
82
reed@google.comac10a2d2010-12-22 21:39:39 +000083 if (NULL != extMatrix) {
84 fExtMatrix = *extMatrix;
85 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000086 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000087 }
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000088
robertphillips@google.combeb1af72012-07-26 18:52:16 +000089 const GrClipData* clipData = context->getClip();
90
robertphillips@google.com641f8b12012-07-31 19:15:58 +000091 GrRect devConservativeBound;
92 clipData->fClipStack->getConservativeBounds(
93 -clipData->fOrigin.fX,
94 -clipData->fOrigin.fY,
95 context->getRenderTarget()->width(),
96 context->getRenderTarget()->height(),
97 &devConservativeBound);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000098
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000099 if (!fExtMatrix.isIdentity()) {
100 GrMatrix inverse;
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +0000101 if (fExtMatrix.invert(&inverse)) {
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000102 inverse.mapRect(&devConservativeBound);
reed@google.comac10a2d2010-12-22 21:39:39 +0000103 }
104 }
105
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000106 devConservativeBound.roundOut(&fClipRect);
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000107
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000108 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000109 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000110 fOrigViewMatrix = fContext->getMatrix();
111 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000112
bsalomon@google.com39149582011-06-13 21:55:32 +0000113 /*
114 We need to call preConcatMatrix with our viewmatrix's inverse, for each
rmistry@google.comd6176b02012-08-23 18:14:13 +0000115 texture and mask in the paint. However, computing the inverse can be
bsalomon@google.com39149582011-06-13 21:55:32 +0000116 expensive, and its possible we may not have any textures or masks, so these
117 two loops are written such that we only compute the inverse (once) if we
rmistry@google.comd6176b02012-08-23 18:14:13 +0000118 need it. We do this on our copy of the paint rather than directly on the
bsalomon@google.com39149582011-06-13 21:55:32 +0000119 draw target because we re-provide the paint to the context when we have
120 to flush our glyphs or draw a glyph as a path midstream.
121 */
122 bool invVMComputed = false;
123 GrMatrix invVM;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000124 for (int t = 0; t < GrPaint::kMaxColorStages; ++t) {
125 if (fPaint.isColorStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000126 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
127 invVMComputed = true;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000128 fPaint.colorSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000129 }
130 }
131 }
bsalomon@google.com88becf42012-10-05 14:54:42 +0000132 for (int m = 0; m < GrPaint::kMaxCoverageStages; ++m) {
133 if (fPaint.isCoverageStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000134 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
135 invVMComputed = true;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000136 fPaint.coverageSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000137 }
138 }
139 }
140
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000141 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000142
reed@google.comac10a2d2010-12-22 21:39:39 +0000143 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000144 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000145
rmistry@google.comd6176b02012-08-23 18:14:13 +0000146 fVertexLayout =
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000147 GrDrawTarget::kTextFormat_VertexLayoutBit |
148 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000149}
150
tomhudson@google.com375ff852012-06-29 18:37:57 +0000151GrTextContext::~GrTextContext() {
152 this->flushGlyphs();
153 if (fDrawTarget) {
154 fDrawTarget->drawState()->disableStages();
155 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000156 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000157}
158
tomhudson@google.com375ff852012-06-29 18:37:57 +0000159void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000160 this->flushGlyphs();
161}
162
163static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
164 int stride) {
165 v[0 * stride].setI(l, t);
166 v[1 * stride].setI(l, b);
167 v[2 * stride].setI(r, b);
168 v[3 * stride].setI(r, t);
169}
170
tomhudson@google.com375ff852012-06-29 18:37:57 +0000171void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000172 GrFixed vx, GrFixed vy,
173 GrFontScaler* scaler) {
174 if (NULL == fStrike) {
175 fStrike = fContext->getFontCache()->getStrike(scaler);
176 }
177
178 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
179 if (NULL == glyph || glyph->fBounds.isEmpty()) {
180 return;
181 }
182
183 vx += GrIntToFixed(glyph->fBounds.fLeft);
184 vy += GrIntToFixed(glyph->fBounds.fTop);
185
186 // keep them as ints until we've done the clip-test
187 GrFixed width = glyph->fBounds.width();
188 GrFixed height = glyph->fBounds.height();
189
190 // check if we clipped out
191 if (true || NULL == glyph->fAtlas) {
192 int x = vx >> 16;
193 int y = vy >> 16;
194 if (fClipRect.quickReject(x, y, x + width, y + height)) {
195// Gr_clz(3); // so we can set a break-point in the debugger
196 return;
197 }
198 }
199
200 if (NULL == glyph->fAtlas) {
201 if (fStrike->getGlyphAtlas(glyph, scaler)) {
202 goto HAS_ATLAS;
203 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000204
205 // before we purge the cache, we must flush any accumulated draws
206 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000207 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000208
reed@google.comac10a2d2010-12-22 21:39:39 +0000209 // try to purge
210 fContext->getFontCache()->purgeExceptFor(fStrike);
211 if (fStrike->getGlyphAtlas(glyph, scaler)) {
212 goto HAS_ATLAS;
213 }
214
reed@google.comac10a2d2010-12-22 21:39:39 +0000215 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000216 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000217 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
218 // flag the glyph as being dead?
219 delete path;
220 return;
221 }
222 glyph->fPath = path;
223 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000224
reed@google.comac10a2d2010-12-22 21:39:39 +0000225 GrPoint translate;
226 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
227 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000228 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000229 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000230 return;
231 }
232
233HAS_ATLAS:
234 GrAssert(glyph->fAtlas);
235
236 // now promote them to fixed
237 width = GrIntToFixed(width);
238 height = GrIntToFixed(height);
239
240 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000241 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000242
tomhudson@google.com375ff852012-06-29 18:37:57 +0000243 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
244 this->flushGlyphs();
245 fCurrTexture = texture;
246 fCurrTexture->ref();
247 }
248
249 if (NULL == fVertices) {
250 // If we need to reserve vertices allow the draw target to suggest
251 // a number of verts to reserve and whether to perform a flush.
252 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000253 bool flush = (NULL != fDrawTarget) &&
254 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000255 &fMaxVertices,
256 NULL);
257 if (flush) {
258 this->flushGlyphs();
259 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000260 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000261 fDrawTarget = fContext->getTextTarget(fPaint);
262 fMaxVertices = kDefaultRequestedVerts;
263 // ignore return, no point in flushing again.
264 fDrawTarget->geometryHints(fVertexLayout,
265 &fMaxVertices,
266 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000267
268 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
269 if (fMaxVertices < kMinRequestedVerts) {
270 fMaxVertices = kDefaultRequestedVerts;
271 } else if (fMaxVertices > maxQuadVertices) {
272 // don't exceed the limit of the index buffer
273 fMaxVertices = maxQuadVertices;
274 }
275 bool success = fDrawTarget->reserveVertexAndIndexSpace(
rmistry@google.comd6176b02012-08-23 18:14:13 +0000276 fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000277 fMaxVertices,
278 0,
279 GrTCast<void**>(&fVertices),
280 NULL);
281 GrAlwaysAssert(success);
282 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000283
284 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
285 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
286
bsalomon@google.com2717d562012-05-07 19:10:52 +0000287#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000288 int x = vx >> 16;
289 int y = vy >> 16;
290 int w = width >> 16;
291 int h = height >> 16;
292
293 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
294 setRectFan(&fVertices[2*fCurrVertex+1],
295 texture->normalizeFixedX(tx),
296 texture->normalizeFixedY(ty),
297 texture->normalizeFixedX(tx + width),
298 texture->normalizeFixedY(ty + height),
299 2);
300#else
301 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
302 2 * sizeof(GrGpuTextVertex));
303 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
304 texture->normalizeFixedY(ty),
305 texture->normalizeFixedX(tx + width),
306 texture->normalizeFixedY(ty + height),
307 2 * sizeof(GrGpuTextVertex));
308#endif
309 fCurrVertex += 4;
310}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000311