blob: dc04fcd2799c3995451b47979e04d851fcc7c839 [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() {
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000026 if (NULL == fDrawTarget) {
27 return;
28 }
29 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000030 if (fCurrVertex > 0) {
reed@google.comac10a2d2010-12-22 21:39:39 +000031 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000032 GrSamplerState::Filter filter;
33 if (fExtMatrix.isIdentity()) {
34 filter = GrSamplerState::kNearest_Filter;
35 } else {
36 filter = GrSamplerState::kBilinear_Filter;
37 }
bsalomon@google.com1e266f82011-12-12 16:11:33 +000038 drawState->sampler(kGlyphMaskStage)->reset(
39 GrSamplerState::kRepeat_WrapMode,filter);
reed@google.comac10a2d2010-12-22 21:39:39 +000040
41 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000042 GrAssert(fCurrTexture);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000043 drawState->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000044
bsalomon@google.com669fdc42011-04-05 17:08:27 +000045 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000046 if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
47 kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
48 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000049 GrPrintf("LCD Text will not draw correctly.\n");
50 }
51 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
tomhudson@google.com375ff852012-06-29 18:37:57 +000052 drawState->setBlendConstant(fPaint.fColor);
bsalomon@google.com47059542012-06-06 20:51:20 +000053 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000054 // don't modulate by the paint's color in the frag since we're
55 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000056 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000057 } else {
58 // set back to normal in case we took LCD path previously.
tomhudson@google.com375ff852012-06-29 18:37:57 +000059 drawState->setBlendFunc(fPaint.fSrcBlendCoeff,
60 fPaint.fDstBlendCoeff);
61 drawState->setColor(fPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000062 }
63
bsalomon@google.com934c5702012-03-20 21:17:58 +000064 int nGlyphs = fCurrVertex / 4;
tomhudson@google.com375ff852012-06-29 18:37:57 +000065 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com47059542012-06-06 20:51:20 +000066 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
bsalomon@google.com934c5702012-03-20 21:17:58 +000067 nGlyphs,
68 4, 6);
tomhudson@google.com375ff852012-06-29 18:37:57 +000069 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000070 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +000071 fMaxVertices = 0;
72 fCurrVertex = 0;
73 GrSafeSetNull(fCurrTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +000074 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000075 drawState->disableStages();
76 fDrawTarget = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000077}
78
tomhudson@google.com375ff852012-06-29 18:37:57 +000079GrTextContext::GrTextContext(GrContext* context,
80 const GrPaint& paint,
81 const GrMatrix* extMatrix) : fPaint(paint) {
82 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000083 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000084
tomhudson@google.com375ff852012-06-29 18:37:57 +000085 fCurrTexture = NULL;
86 fCurrVertex = 0;
87
reed@google.comac10a2d2010-12-22 21:39:39 +000088 if (NULL != extMatrix) {
89 fExtMatrix = *extMatrix;
90 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000091 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000092 }
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000093
94 if (!fExtMatrix.isIdentity()) {
95 GrMatrix inverse;
96 GrRect r = context->getClip().getConservativeBounds();
97 if (fExtMatrix.invert(&inverse)) {
98 inverse.mapRect(&r);
99 r.roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000100 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000101 } else {
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +0000102 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +0000103 }
104
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000105 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000106 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000107 fOrigViewMatrix = fContext->getMatrix();
108 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000109
bsalomon@google.com39149582011-06-13 21:55:32 +0000110 /*
111 We need to call preConcatMatrix with our viewmatrix's inverse, for each
112 texture and mask in the paint. However, computing the inverse can be
113 expensive, and its possible we may not have any textures or masks, so these
114 two loops are written such that we only compute the inverse (once) if we
115 need it. We do this on our copy of the paint rather than directly on the
116 draw target because we re-provide the paint to the context when we have
117 to flush our glyphs or draw a glyph as a path midstream.
118 */
119 bool invVMComputed = false;
120 GrMatrix invVM;
121 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000122 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000123 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
124 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000125 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000126 }
127 }
128 }
129 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000130 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000131 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
132 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000133 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000134 }
135 }
136 }
137
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000138 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000139
reed@google.comac10a2d2010-12-22 21:39:39 +0000140 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000141 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000142
143 fVertexLayout =
144 GrDrawTarget::kTextFormat_VertexLayoutBit |
145 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000146}
147
tomhudson@google.com375ff852012-06-29 18:37:57 +0000148GrTextContext::~GrTextContext() {
149 this->flushGlyphs();
150 if (fDrawTarget) {
151 fDrawTarget->drawState()->disableStages();
152 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000153 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000154}
155
tomhudson@google.com375ff852012-06-29 18:37:57 +0000156void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000157 this->flushGlyphs();
158}
159
160static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
161 int stride) {
162 v[0 * stride].setI(l, t);
163 v[1 * stride].setI(l, b);
164 v[2 * stride].setI(r, b);
165 v[3 * stride].setI(r, t);
166}
167
tomhudson@google.com375ff852012-06-29 18:37:57 +0000168void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000169 GrFixed vx, GrFixed vy,
170 GrFontScaler* scaler) {
171 if (NULL == fStrike) {
172 fStrike = fContext->getFontCache()->getStrike(scaler);
173 }
174
175 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
176 if (NULL == glyph || glyph->fBounds.isEmpty()) {
177 return;
178 }
179
180 vx += GrIntToFixed(glyph->fBounds.fLeft);
181 vy += GrIntToFixed(glyph->fBounds.fTop);
182
183 // keep them as ints until we've done the clip-test
184 GrFixed width = glyph->fBounds.width();
185 GrFixed height = glyph->fBounds.height();
186
187 // check if we clipped out
188 if (true || NULL == glyph->fAtlas) {
189 int x = vx >> 16;
190 int y = vy >> 16;
191 if (fClipRect.quickReject(x, y, x + width, y + height)) {
192// Gr_clz(3); // so we can set a break-point in the debugger
193 return;
194 }
195 }
196
197 if (NULL == glyph->fAtlas) {
198 if (fStrike->getGlyphAtlas(glyph, scaler)) {
199 goto HAS_ATLAS;
200 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000201
202 // before we purge the cache, we must flush any accumulated draws
203 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000204 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000205
reed@google.comac10a2d2010-12-22 21:39:39 +0000206 // try to purge
207 fContext->getFontCache()->purgeExceptFor(fStrike);
208 if (fStrike->getGlyphAtlas(glyph, scaler)) {
209 goto HAS_ATLAS;
210 }
211
reed@google.comac10a2d2010-12-22 21:39:39 +0000212 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000213 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000214 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
215 // flag the glyph as being dead?
216 delete path;
217 return;
218 }
219 glyph->fPath = path;
220 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000221
reed@google.comac10a2d2010-12-22 21:39:39 +0000222 GrPoint translate;
223 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
224 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000225 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000226 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000227 return;
228 }
229
230HAS_ATLAS:
231 GrAssert(glyph->fAtlas);
232
233 // now promote them to fixed
234 width = GrIntToFixed(width);
235 height = GrIntToFixed(height);
236
237 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000238 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000239
tomhudson@google.com375ff852012-06-29 18:37:57 +0000240 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
241 this->flushGlyphs();
242 fCurrTexture = texture;
243 fCurrTexture->ref();
244 }
245
246 if (NULL == fVertices) {
247 // If we need to reserve vertices allow the draw target to suggest
248 // a number of verts to reserve and whether to perform a flush.
249 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000250 bool flush = (NULL != fDrawTarget) &&
251 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000252 &fMaxVertices,
253 NULL);
254 if (flush) {
255 this->flushGlyphs();
256 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000257 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000258 fDrawTarget = fContext->getTextTarget(fPaint);
259 fMaxVertices = kDefaultRequestedVerts;
260 // ignore return, no point in flushing again.
261 fDrawTarget->geometryHints(fVertexLayout,
262 &fMaxVertices,
263 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000264
265 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
266 if (fMaxVertices < kMinRequestedVerts) {
267 fMaxVertices = kDefaultRequestedVerts;
268 } else if (fMaxVertices > maxQuadVertices) {
269 // don't exceed the limit of the index buffer
270 fMaxVertices = maxQuadVertices;
271 }
272 bool success = fDrawTarget->reserveVertexAndIndexSpace(
273 fVertexLayout,
274 fMaxVertices,
275 0,
276 GrTCast<void**>(&fVertices),
277 NULL);
278 GrAlwaysAssert(success);
279 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000280
281 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
282 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
283
bsalomon@google.com2717d562012-05-07 19:10:52 +0000284#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000285 int x = vx >> 16;
286 int y = vy >> 16;
287 int w = width >> 16;
288 int h = height >> 16;
289
290 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
291 setRectFan(&fVertices[2*fCurrVertex+1],
292 texture->normalizeFixedX(tx),
293 texture->normalizeFixedY(ty),
294 texture->normalizeFixedX(tx + width),
295 texture->normalizeFixedY(ty + height),
296 2);
297#else
298 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
299 2 * sizeof(GrGpuTextVertex));
300 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
301 texture->normalizeFixedY(ty),
302 texture->normalizeFixedX(tx + width),
303 texture->normalizeFixedY(ty + height),
304 2 * sizeof(GrGpuTextVertex));
305#endif
306 fCurrVertex += 4;
307}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000308