blob: 042e0307d9830024429dd99185624a36fdb66d03 [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.com0f11e1a2012-10-08 14:48:36 +0000209 GrContext::AutoMatrix am(fContext, GrContext::AutoMatrix::kPreserve_InitialMatrix);
210 GrMatrix translate;
211 translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
212 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
213 fContext->concatMatrix(translate);
214 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill);
reed@google.comac10a2d2010-12-22 21:39:39 +0000215 return;
216 }
217
218HAS_ATLAS:
219 GrAssert(glyph->fAtlas);
220
221 // now promote them to fixed
222 width = GrIntToFixed(width);
223 height = GrIntToFixed(height);
224
225 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000226 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000227
tomhudson@google.com375ff852012-06-29 18:37:57 +0000228 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
229 this->flushGlyphs();
230 fCurrTexture = texture;
231 fCurrTexture->ref();
232 }
233
234 if (NULL == fVertices) {
235 // If we need to reserve vertices allow the draw target to suggest
236 // a number of verts to reserve and whether to perform a flush.
237 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000238 bool flush = (NULL != fDrawTarget) &&
239 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000240 &fMaxVertices,
241 NULL);
242 if (flush) {
243 this->flushGlyphs();
244 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000245 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000246 fDrawTarget = fContext->getTextTarget(fPaint);
247 fMaxVertices = kDefaultRequestedVerts;
248 // ignore return, no point in flushing again.
249 fDrawTarget->geometryHints(fVertexLayout,
250 &fMaxVertices,
251 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000252
253 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
254 if (fMaxVertices < kMinRequestedVerts) {
255 fMaxVertices = kDefaultRequestedVerts;
256 } else if (fMaxVertices > maxQuadVertices) {
257 // don't exceed the limit of the index buffer
258 fMaxVertices = maxQuadVertices;
259 }
260 bool success = fDrawTarget->reserveVertexAndIndexSpace(
rmistry@google.comd6176b02012-08-23 18:14:13 +0000261 fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000262 fMaxVertices,
263 0,
264 GrTCast<void**>(&fVertices),
265 NULL);
266 GrAlwaysAssert(success);
267 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000268
269 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
270 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
271
bsalomon@google.com2717d562012-05-07 19:10:52 +0000272#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000273 int x = vx >> 16;
274 int y = vy >> 16;
275 int w = width >> 16;
276 int h = height >> 16;
277
278 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
279 setRectFan(&fVertices[2*fCurrVertex+1],
280 texture->normalizeFixedX(tx),
281 texture->normalizeFixedY(ty),
282 texture->normalizeFixedX(tx + width),
283 texture->normalizeFixedY(ty + height),
284 2);
285#else
286 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
287 2 * sizeof(GrGpuTextVertex));
288 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
289 texture->normalizeFixedY(ty),
290 texture->normalizeFixedX(tx + width),
291 texture->normalizeFixedY(ty + height),
292 2 * sizeof(GrGpuTextVertex));
293#endif
294 fCurrVertex += 4;
295}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000296