blob: ab04bb48f58d528482a0072a789c77f5f030c3a8 [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.comb8670992012-07-25 21:27:09 +000032 drawState->sampler(kGlyphMaskStage)->reset(SkShader::kRepeat_TileMode,
bsalomon@google.comeb9568a2012-07-26 14:12:23 +000033 !fExtMatrix.isIdentity());
reed@google.comac10a2d2010-12-22 21:39:39 +000034
35 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000036 GrAssert(fCurrTexture);
tomhudson@google.com1e8f0162012-07-20 16:25:18 +000037 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000038
bsalomon@google.com669fdc42011-04-05 17:08:27 +000039 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000040 if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
41 kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
42 fPaint.hasTexture()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000043 GrPrintf("LCD Text will not draw correctly.\n");
44 }
45 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
tomhudson@google.com375ff852012-06-29 18:37:57 +000046 drawState->setBlendConstant(fPaint.fColor);
bsalomon@google.com47059542012-06-06 20:51:20 +000047 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000048 // don't modulate by the paint's color in the frag since we're
49 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000050 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000051 } else {
52 // set back to normal in case we took LCD path previously.
tomhudson@google.com375ff852012-06-29 18:37:57 +000053 drawState->setBlendFunc(fPaint.fSrcBlendCoeff,
54 fPaint.fDstBlendCoeff);
55 drawState->setColor(fPaint.fColor);
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
90 GrRect conservativeBound = clipData->fClipStack->getConservativeBounds();
robertphillips@google.comf8d904a2012-07-31 12:18:16 +000091 conservativeBound.offset(SkIntToScalar(-clipData->fOrigin.fX),
92 SkIntToScalar(-clipData->fOrigin.fY));
robertphillips@google.combeb1af72012-07-26 18:52:16 +000093
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000094 if (!fExtMatrix.isIdentity()) {
95 GrMatrix inverse;
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000096 if (fExtMatrix.invert(&inverse)) {
robertphillips@google.combeb1af72012-07-26 18:52:16 +000097 inverse.mapRect(&conservativeBound);
reed@google.comac10a2d2010-12-22 21:39:39 +000098 }
99 }
100
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000101 conservativeBound.roundOut(&fClipRect);
102
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000103 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000104 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000105 fOrigViewMatrix = fContext->getMatrix();
106 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000107
bsalomon@google.com39149582011-06-13 21:55:32 +0000108 /*
109 We need to call preConcatMatrix with our viewmatrix's inverse, for each
110 texture and mask in the paint. However, computing the inverse can be
111 expensive, and its possible we may not have any textures or masks, so these
112 two loops are written such that we only compute the inverse (once) if we
113 need it. We do this on our copy of the paint rather than directly on the
114 draw target because we re-provide the paint to the context when we have
115 to flush our glyphs or draw a glyph as a path midstream.
116 */
117 bool invVMComputed = false;
118 GrMatrix invVM;
119 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000120 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000121 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
122 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000123 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000124 }
125 }
126 }
127 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000128 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000129 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
130 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000131 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000132 }
133 }
134 }
135
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000136 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000137
reed@google.comac10a2d2010-12-22 21:39:39 +0000138 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000139 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000140
141 fVertexLayout =
142 GrDrawTarget::kTextFormat_VertexLayoutBit |
143 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000144}
145
tomhudson@google.com375ff852012-06-29 18:37:57 +0000146GrTextContext::~GrTextContext() {
147 this->flushGlyphs();
148 if (fDrawTarget) {
149 fDrawTarget->drawState()->disableStages();
150 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000151 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000152}
153
tomhudson@google.com375ff852012-06-29 18:37:57 +0000154void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000155 this->flushGlyphs();
156}
157
158static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
159 int stride) {
160 v[0 * stride].setI(l, t);
161 v[1 * stride].setI(l, b);
162 v[2 * stride].setI(r, b);
163 v[3 * stride].setI(r, t);
164}
165
tomhudson@google.com375ff852012-06-29 18:37:57 +0000166void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000167 GrFixed vx, GrFixed vy,
168 GrFontScaler* scaler) {
169 if (NULL == fStrike) {
170 fStrike = fContext->getFontCache()->getStrike(scaler);
171 }
172
173 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
174 if (NULL == glyph || glyph->fBounds.isEmpty()) {
175 return;
176 }
177
178 vx += GrIntToFixed(glyph->fBounds.fLeft);
179 vy += GrIntToFixed(glyph->fBounds.fTop);
180
181 // keep them as ints until we've done the clip-test
182 GrFixed width = glyph->fBounds.width();
183 GrFixed height = glyph->fBounds.height();
184
185 // check if we clipped out
186 if (true || NULL == glyph->fAtlas) {
187 int x = vx >> 16;
188 int y = vy >> 16;
189 if (fClipRect.quickReject(x, y, x + width, y + height)) {
190// Gr_clz(3); // so we can set a break-point in the debugger
191 return;
192 }
193 }
194
195 if (NULL == glyph->fAtlas) {
196 if (fStrike->getGlyphAtlas(glyph, scaler)) {
197 goto HAS_ATLAS;
198 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000199
200 // before we purge the cache, we must flush any accumulated draws
201 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000202 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000203
reed@google.comac10a2d2010-12-22 21:39:39 +0000204 // try to purge
205 fContext->getFontCache()->purgeExceptFor(fStrike);
206 if (fStrike->getGlyphAtlas(glyph, scaler)) {
207 goto HAS_ATLAS;
208 }
209
reed@google.comac10a2d2010-12-22 21:39:39 +0000210 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000211 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000212 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
213 // flag the glyph as being dead?
214 delete path;
215 return;
216 }
217 glyph->fPath = path;
218 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000219
reed@google.comac10a2d2010-12-22 21:39:39 +0000220 GrPoint translate;
221 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
222 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000223 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000224 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000225 return;
226 }
227
228HAS_ATLAS:
229 GrAssert(glyph->fAtlas);
230
231 // now promote them to fixed
232 width = GrIntToFixed(width);
233 height = GrIntToFixed(height);
234
235 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000236 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000237
tomhudson@google.com375ff852012-06-29 18:37:57 +0000238 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
239 this->flushGlyphs();
240 fCurrTexture = texture;
241 fCurrTexture->ref();
242 }
243
244 if (NULL == fVertices) {
245 // If we need to reserve vertices allow the draw target to suggest
246 // a number of verts to reserve and whether to perform a flush.
247 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000248 bool flush = (NULL != fDrawTarget) &&
249 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000250 &fMaxVertices,
251 NULL);
252 if (flush) {
253 this->flushGlyphs();
254 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000255 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000256 fDrawTarget = fContext->getTextTarget(fPaint);
257 fMaxVertices = kDefaultRequestedVerts;
258 // ignore return, no point in flushing again.
259 fDrawTarget->geometryHints(fVertexLayout,
260 &fMaxVertices,
261 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000262
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