blob: 684672152b233e1dba35bb8bed4503296a00cbcb [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.com0e354aa2012-10-08 20:44:25 +000037 GrTextureParams params(SkShader::kRepeat_TileMode, false);
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +000038 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
bsalomon@google.com0e354aa2012-10-08 20:44:25 +000073GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000074 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000075 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000076
tomhudson@google.com375ff852012-06-29 18:37:57 +000077 fCurrTexture = NULL;
78 fCurrVertex = 0;
79
robertphillips@google.combeb1af72012-07-26 18:52:16 +000080 const GrClipData* clipData = context->getClip();
81
robertphillips@google.com641f8b12012-07-31 19:15:58 +000082 GrRect devConservativeBound;
83 clipData->fClipStack->getConservativeBounds(
84 -clipData->fOrigin.fX,
85 -clipData->fOrigin.fY,
86 context->getRenderTarget()->width(),
87 context->getRenderTarget()->height(),
88 &devConservativeBound);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000089
robertphillips@google.com641f8b12012-07-31 19:15:58 +000090 devConservativeBound.roundOut(&fClipRect);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000091
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000092 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +000093 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +000094 fOrigViewMatrix = fContext->getMatrix();
bsalomon@google.com0e354aa2012-10-08 20:44:25 +000095 fContext->setIdentityMatrix();
reed@google.comac10a2d2010-12-22 21:39:39 +000096
bsalomon@google.com39149582011-06-13 21:55:32 +000097 /*
98 We need to call preConcatMatrix with our viewmatrix's inverse, for each
rmistry@google.comd6176b02012-08-23 18:14:13 +000099 texture and mask in the paint. However, computing the inverse can be
bsalomon@google.com39149582011-06-13 21:55:32 +0000100 expensive, and its possible we may not have any textures or masks, so these
101 two loops are written such that we only compute the inverse (once) if we
rmistry@google.comd6176b02012-08-23 18:14:13 +0000102 need it. We do this on our copy of the paint rather than directly on the
bsalomon@google.com39149582011-06-13 21:55:32 +0000103 draw target because we re-provide the paint to the context when we have
104 to flush our glyphs or draw a glyph as a path midstream.
105 */
106 bool invVMComputed = false;
107 GrMatrix invVM;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000108 for (int t = 0; t < GrPaint::kMaxColorStages; ++t) {
109 if (fPaint.isColorStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000110 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
111 invVMComputed = true;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000112 fPaint.colorSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000113 }
114 }
115 }
bsalomon@google.com88becf42012-10-05 14:54:42 +0000116 for (int m = 0; m < GrPaint::kMaxCoverageStages; ++m) {
117 if (fPaint.isCoverageStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000118 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
119 invVMComputed = true;
bsalomon@google.com88becf42012-10-05 14:54:42 +0000120 fPaint.coverageSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000121 }
122 }
123 }
124
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000125 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000126
reed@google.comac10a2d2010-12-22 21:39:39 +0000127 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000128 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000129
rmistry@google.comd6176b02012-08-23 18:14:13 +0000130 fVertexLayout =
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000131 GrDrawTarget::kTextFormat_VertexLayoutBit |
132 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000133}
134
tomhudson@google.com375ff852012-06-29 18:37:57 +0000135GrTextContext::~GrTextContext() {
136 this->flushGlyphs();
137 if (fDrawTarget) {
138 fDrawTarget->drawState()->disableStages();
139 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000140 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000141}
142
tomhudson@google.com375ff852012-06-29 18:37:57 +0000143void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000144 this->flushGlyphs();
145}
146
147static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
148 int stride) {
149 v[0 * stride].setI(l, t);
150 v[1 * stride].setI(l, b);
151 v[2 * stride].setI(r, b);
152 v[3 * stride].setI(r, t);
153}
154
tomhudson@google.com375ff852012-06-29 18:37:57 +0000155void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000156 GrFixed vx, GrFixed vy,
157 GrFontScaler* scaler) {
158 if (NULL == fStrike) {
159 fStrike = fContext->getFontCache()->getStrike(scaler);
160 }
161
162 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
163 if (NULL == glyph || glyph->fBounds.isEmpty()) {
164 return;
165 }
166
167 vx += GrIntToFixed(glyph->fBounds.fLeft);
168 vy += GrIntToFixed(glyph->fBounds.fTop);
169
170 // keep them as ints until we've done the clip-test
171 GrFixed width = glyph->fBounds.width();
172 GrFixed height = glyph->fBounds.height();
173
174 // check if we clipped out
175 if (true || NULL == glyph->fAtlas) {
176 int x = vx >> 16;
177 int y = vy >> 16;
178 if (fClipRect.quickReject(x, y, x + width, y + height)) {
179// Gr_clz(3); // so we can set a break-point in the debugger
180 return;
181 }
182 }
183
184 if (NULL == glyph->fAtlas) {
185 if (fStrike->getGlyphAtlas(glyph, scaler)) {
186 goto HAS_ATLAS;
187 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000188
189 // before we purge the cache, we must flush any accumulated draws
190 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000191 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000192
reed@google.comac10a2d2010-12-22 21:39:39 +0000193 // try to purge
194 fContext->getFontCache()->purgeExceptFor(fStrike);
195 if (fStrike->getGlyphAtlas(glyph, scaler)) {
196 goto HAS_ATLAS;
197 }
198
reed@google.comac10a2d2010-12-22 21:39:39 +0000199 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000200 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000201 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
202 // flag the glyph as being dead?
203 delete path;
204 return;
205 }
206 glyph->fPath = path;
207 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000208
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000209 GrContext::AutoMatrix am;
bsalomon@google.com0f11e1a2012-10-08 14:48:36 +0000210 GrMatrix translate;
211 translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
212 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000213 GrPaint tmpPaint(fPaint);
214 am.setPreConcat(fContext, translate, &tmpPaint);
215 fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill);
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 return;
217 }
218
219HAS_ATLAS:
220 GrAssert(glyph->fAtlas);
221
222 // now promote them to fixed
223 width = GrIntToFixed(width);
224 height = GrIntToFixed(height);
225
226 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000227 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000228
tomhudson@google.com375ff852012-06-29 18:37:57 +0000229 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
230 this->flushGlyphs();
231 fCurrTexture = texture;
232 fCurrTexture->ref();
233 }
234
235 if (NULL == fVertices) {
236 // If we need to reserve vertices allow the draw target to suggest
237 // a number of verts to reserve and whether to perform a flush.
238 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000239 bool flush = (NULL != fDrawTarget) &&
240 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000241 &fMaxVertices,
242 NULL);
243 if (flush) {
244 this->flushGlyphs();
245 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000246 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000247 fDrawTarget = fContext->getTextTarget(fPaint);
248 fMaxVertices = kDefaultRequestedVerts;
249 // ignore return, no point in flushing again.
250 fDrawTarget->geometryHints(fVertexLayout,
251 &fMaxVertices,
252 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000253
254 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
255 if (fMaxVertices < kMinRequestedVerts) {
256 fMaxVertices = kDefaultRequestedVerts;
257 } else if (fMaxVertices > maxQuadVertices) {
258 // don't exceed the limit of the index buffer
259 fMaxVertices = maxQuadVertices;
260 }
261 bool success = fDrawTarget->reserveVertexAndIndexSpace(
rmistry@google.comd6176b02012-08-23 18:14:13 +0000262 fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000263 fMaxVertices,
264 0,
265 GrTCast<void**>(&fVertices),
266 NULL);
267 GrAlwaysAssert(success);
268 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000269
270 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
271 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
272
bsalomon@google.com2717d562012-05-07 19:10:52 +0000273#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000274 int x = vx >> 16;
275 int y = vy >> 16;
276 int w = width >> 16;
277 int h = height >> 16;
278
279 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
280 setRectFan(&fVertices[2*fCurrVertex+1],
281 texture->normalizeFixedX(tx),
282 texture->normalizeFixedY(ty),
283 texture->normalizeFixedX(tx + width),
284 texture->normalizeFixedY(ty + height),
285 2);
286#else
287 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
288 2 * sizeof(GrGpuTextVertex));
289 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
290 texture->normalizeFixedY(ty),
291 texture->normalizeFixedX(tx + width),
292 texture->normalizeFixedY(ty + height),
293 2 * sizeof(GrGpuTextVertex));
294#endif
295 fCurrVertex += 4;
296}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000297