blob: d642f13d84fa302173ef2c85e11bb458b241c15b [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() {
reed@google.comac10a2d2010-12-22 21:39:39 +000026 if (fCurrVertex > 0) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000027 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000028 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000029 GrSamplerState::Filter filter;
30 if (fExtMatrix.isIdentity()) {
31 filter = GrSamplerState::kNearest_Filter;
32 } else {
33 filter = GrSamplerState::kBilinear_Filter;
34 }
bsalomon@google.com1e266f82011-12-12 16:11:33 +000035 drawState->sampler(kGlyphMaskStage)->reset(
36 GrSamplerState::kRepeat_WrapMode,filter);
reed@google.comac10a2d2010-12-22 21:39:39 +000037
38 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000039 GrAssert(fCurrTexture);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000040 drawState->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000041
bsalomon@google.com669fdc42011-04-05 17:08:27 +000042 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000043 if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
44 kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
45 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000046 GrPrintf("LCD Text will not draw correctly.\n");
47 }
48 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
tomhudson@google.com375ff852012-06-29 18:37:57 +000049 drawState->setBlendConstant(fPaint.fColor);
bsalomon@google.com47059542012-06-06 20:51:20 +000050 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000051 // don't modulate by the paint's color in the frag since we're
52 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000053 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000054 } else {
55 // set back to normal in case we took LCD path previously.
tomhudson@google.com375ff852012-06-29 18:37:57 +000056 drawState->setBlendFunc(fPaint.fSrcBlendCoeff,
57 fPaint.fDstBlendCoeff);
58 drawState->setColor(fPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000059 }
60
bsalomon@google.com934c5702012-03-20 21:17:58 +000061 int nGlyphs = fCurrVertex / 4;
tomhudson@google.com375ff852012-06-29 18:37:57 +000062 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com47059542012-06-06 20:51:20 +000063 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
bsalomon@google.com934c5702012-03-20 21:17:58 +000064 nGlyphs,
65 4, 6);
tomhudson@google.com375ff852012-06-29 18:37:57 +000066 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000067 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +000068 fMaxVertices = 0;
69 fCurrVertex = 0;
70 GrSafeSetNull(fCurrTexture);
robertphillips@google.com972265d2012-06-13 18:49:30 +000071 drawState->setTexture(kGlyphMaskStage, NULL);
reed@google.comac10a2d2010-12-22 21:39:39 +000072 }
73}
74
tomhudson@google.com375ff852012-06-29 18:37:57 +000075GrTextContext::GrTextContext(GrContext* context,
76 const GrPaint& paint,
77 const GrMatrix* extMatrix) : fPaint(paint) {
78 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000079 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000080
tomhudson@google.com375ff852012-06-29 18:37:57 +000081 fCurrTexture = NULL;
82 fCurrVertex = 0;
83
reed@google.comac10a2d2010-12-22 21:39:39 +000084 if (NULL != extMatrix) {
85 fExtMatrix = *extMatrix;
86 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000087 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000088 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000089 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000090 if (!fExtMatrix.isIdentity()) {
91 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000092 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000093 if (fExtMatrix.invert(&inverse)) {
94 inverse.mapRect(&r);
95 r.roundOut(&fClipRect);
96 }
97 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000098 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +000099 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000100 } else {
101 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +0000102 }
103
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000104 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000105 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000106 fOrigViewMatrix = fContext->getMatrix();
107 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000108
bsalomon@google.com39149582011-06-13 21:55:32 +0000109 /*
110 We need to call preConcatMatrix with our viewmatrix's inverse, for each
111 texture and mask in the paint. However, computing the inverse can be
112 expensive, and its possible we may not have any textures or masks, so these
113 two loops are written such that we only compute the inverse (once) if we
114 need it. We do this on our copy of the paint rather than directly on the
115 draw target because we re-provide the paint to the context when we have
116 to flush our glyphs or draw a glyph as a path midstream.
117 */
118 bool invVMComputed = false;
119 GrMatrix invVM;
120 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000121 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000122 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
123 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000124 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000125 }
126 }
127 }
128 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000129 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000130 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
131 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000132 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000133 }
134 }
135 }
136
tomhudson@google.com375ff852012-06-29 18:37:57 +0000137 fDrawTarget = fContext->getTextTarget(fPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000138
reed@google.comac10a2d2010-12-22 21:39:39 +0000139 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000140 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000141
142 fVertexLayout =
143 GrDrawTarget::kTextFormat_VertexLayoutBit |
144 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000145}
146
tomhudson@google.com375ff852012-06-29 18:37:57 +0000147GrTextContext::~GrTextContext() {
148 this->flushGlyphs();
149 if (fDrawTarget) {
150 fDrawTarget->drawState()->disableStages();
151 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000152 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000153}
154
tomhudson@google.com375ff852012-06-29 18:37:57 +0000155void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000156 this->flushGlyphs();
157}
158
159static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
160 int stride) {
161 v[0 * stride].setI(l, t);
162 v[1 * stride].setI(l, b);
163 v[2 * stride].setI(r, b);
164 v[3 * stride].setI(r, t);
165}
166
tomhudson@google.com375ff852012-06-29 18:37:57 +0000167void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000168 GrFixed vx, GrFixed vy,
169 GrFontScaler* scaler) {
170 if (NULL == fStrike) {
171 fStrike = fContext->getFontCache()->getStrike(scaler);
172 }
173
174 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
175 if (NULL == glyph || glyph->fBounds.isEmpty()) {
176 return;
177 }
178
179 vx += GrIntToFixed(glyph->fBounds.fLeft);
180 vy += GrIntToFixed(glyph->fBounds.fTop);
181
182 // keep them as ints until we've done the clip-test
183 GrFixed width = glyph->fBounds.width();
184 GrFixed height = glyph->fBounds.height();
185
186 // check if we clipped out
187 if (true || NULL == glyph->fAtlas) {
188 int x = vx >> 16;
189 int y = vy >> 16;
190 if (fClipRect.quickReject(x, y, x + width, y + height)) {
191// Gr_clz(3); // so we can set a break-point in the debugger
192 return;
193 }
194 }
195
196 if (NULL == glyph->fAtlas) {
197 if (fStrike->getGlyphAtlas(glyph, scaler)) {
198 goto HAS_ATLAS;
199 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000200
201 // before we purge the cache, we must flush any accumulated draws
202 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000203 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000204
reed@google.comac10a2d2010-12-22 21:39:39 +0000205 // try to purge
206 fContext->getFontCache()->purgeExceptFor(fStrike);
207 if (fStrike->getGlyphAtlas(glyph, scaler)) {
208 goto HAS_ATLAS;
209 }
210
reed@google.comac10a2d2010-12-22 21:39:39 +0000211 if (NULL == glyph->fPath) {
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000212 SkPath* path = new SkPath;
reed@google.comac10a2d2010-12-22 21:39:39 +0000213 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
214 // flag the glyph as being dead?
215 delete path;
216 return;
217 }
218 glyph->fPath = path;
219 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000220
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 GrPoint translate;
222 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
223 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000224 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000225 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000226 return;
227 }
228
229HAS_ATLAS:
230 GrAssert(glyph->fAtlas);
231
232 // now promote them to fixed
233 width = GrIntToFixed(width);
234 height = GrIntToFixed(height);
235
236 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000237 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000238
tomhudson@google.com375ff852012-06-29 18:37:57 +0000239 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
240 this->flushGlyphs();
241 fCurrTexture = texture;
242 fCurrTexture->ref();
243 }
244
245 if (NULL == fVertices) {
246 // If we need to reserve vertices allow the draw target to suggest
247 // a number of verts to reserve and whether to perform a flush.
248 fMaxVertices = kMinRequestedVerts;
249 bool flush = fDrawTarget->geometryHints(fVertexLayout,
250 &fMaxVertices,
251 NULL);
252 if (flush) {
253 this->flushGlyphs();
254 fContext->flush();
255 fDrawTarget = fContext->getTextTarget(fPaint);
256 fMaxVertices = kDefaultRequestedVerts;
257 // ignore return, no point in flushing again.
258 fDrawTarget->geometryHints(fVertexLayout,
259 &fMaxVertices,
260 NULL);
261 }
262
263 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
264 if (fMaxVertices < kMinRequestedVerts) {
265 fMaxVertices = kDefaultRequestedVerts;
266 } else if (fMaxVertices > maxQuadVertices) {
267 // don't exceed the limit of the index buffer
268 fMaxVertices = maxQuadVertices;
269 }
270 bool success = fDrawTarget->reserveVertexAndIndexSpace(
271 fVertexLayout,
272 fMaxVertices,
273 0,
274 GrTCast<void**>(&fVertices),
275 NULL);
276 GrAlwaysAssert(success);
277 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000278
279 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
280 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
281
bsalomon@google.com2717d562012-05-07 19:10:52 +0000282#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000283 int x = vx >> 16;
284 int y = vy >> 16;
285 int w = width >> 16;
286 int h = height >> 16;
287
288 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
289 setRectFan(&fVertices[2*fCurrVertex+1],
290 texture->normalizeFixedX(tx),
291 texture->normalizeFixedY(ty),
292 texture->normalizeFixedX(tx + width),
293 texture->normalizeFixedY(ty + height),
294 2);
295#else
296 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
297 2 * sizeof(GrGpuTextVertex));
298 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
299 texture->normalizeFixedY(ty),
300 texture->normalizeFixedX(tx + width),
301 texture->normalizeFixedY(ty + height),
302 2 * sizeof(GrGpuTextVertex));
303#endif
304 fCurrVertex += 4;
305}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000306