blob: e97bc8f3a2c555decf84f8bd75019cca2b0bcbe3 [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())) {
bsalomon@google.comc7448ce2012-10-05 19:04:13 +000041 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
42 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
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
bsalomon@google.comc7448ce2012-10-05 19:04:13 +000047 drawState->setBlendConstant(fPaint.getColor());
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.
bsalomon@google.comc7448ce2012-10-05 19:04:13 +000054 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
55 drawState->setColor(fPaint.getColor());
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
rmistry@google.comd6176b02012-08-23 18:14:13 +0000114 texture and mask in the paint. However, computing the inverse can be
bsalomon@google.com39149582011-06-13 21:55:32 +0000115 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
rmistry@google.comd6176b02012-08-23 18:14:13 +0000117 need it. We do this on our copy of the paint rather than directly on the
bsalomon@google.com39149582011-06-13 21:55:32 +0000118 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;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000123 for (int t = 0; t < GrPaint::kMaxColorStages; ++t) {
124 if (fPaint.isColorStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000125 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
126 invVMComputed = true;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000127 fPaint.colorSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000128 }
129 }
130 }
bsalomon@google.com88becf42012-10-05 14:54:42 +0000131 for (int m = 0; m < GrPaint::kMaxCoverageStages; ++m) {
132 if (fPaint.isCoverageStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000133 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
134 invVMComputed = true;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000135 fPaint.coverageSampler(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
rmistry@google.comd6176b02012-08-23 18:14:13 +0000145 fVertexLayout =
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000146 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
bsalomon@google.com0f11e1a2012-10-08 14:48:36 +0000224 GrContext::AutoMatrix am(fContext, GrContext::AutoMatrix::kPreserve_InitialMatrix);
225 GrMatrix translate;
226 translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
227 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
228 fContext->concatMatrix(translate);
229 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill);
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