blob: 36e6646307445344c4d8bfe8a8f1ca5ef1a753b0 [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();
91
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000092 if (!fExtMatrix.isIdentity()) {
93 GrMatrix inverse;
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000094 if (fExtMatrix.invert(&inverse)) {
robertphillips@google.combeb1af72012-07-26 18:52:16 +000095 inverse.mapRect(&conservativeBound);
reed@google.comac10a2d2010-12-22 21:39:39 +000096 }
97 }
98
robertphillips@google.combeb1af72012-07-26 18:52:16 +000099 conservativeBound.roundOut(&fClipRect);
100
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000101 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000102 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000103 fOrigViewMatrix = fContext->getMatrix();
104 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000105
bsalomon@google.com39149582011-06-13 21:55:32 +0000106 /*
107 We need to call preConcatMatrix with our viewmatrix's inverse, for each
108 texture and mask in the paint. However, computing the inverse can be
109 expensive, and its possible we may not have any textures or masks, so these
110 two loops are written such that we only compute the inverse (once) if we
111 need it. We do this on our copy of the paint rather than directly on the
112 draw target because we re-provide the paint to the context when we have
113 to flush our glyphs or draw a glyph as a path midstream.
114 */
115 bool invVMComputed = false;
116 GrMatrix invVM;
117 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000118 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000119 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
120 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000121 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000122 }
123 }
124 }
125 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000126 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000127 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
128 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000129 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000130 }
131 }
132 }
133
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000134 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000135
reed@google.comac10a2d2010-12-22 21:39:39 +0000136 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000137 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000138
139 fVertexLayout =
140 GrDrawTarget::kTextFormat_VertexLayoutBit |
141 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000142}
143
tomhudson@google.com375ff852012-06-29 18:37:57 +0000144GrTextContext::~GrTextContext() {
145 this->flushGlyphs();
146 if (fDrawTarget) {
147 fDrawTarget->drawState()->disableStages();
148 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000149 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000150}
151
tomhudson@google.com375ff852012-06-29 18:37:57 +0000152void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000153 this->flushGlyphs();
154}
155
156static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
157 int stride) {
158 v[0 * stride].setI(l, t);
159 v[1 * stride].setI(l, b);
160 v[2 * stride].setI(r, b);
161 v[3 * stride].setI(r, t);
162}
163
tomhudson@google.com375ff852012-06-29 18:37:57 +0000164void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000165 GrFixed vx, GrFixed vy,
166 GrFontScaler* scaler) {
167 if (NULL == fStrike) {
168 fStrike = fContext->getFontCache()->getStrike(scaler);
169 }
170
171 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
172 if (NULL == glyph || glyph->fBounds.isEmpty()) {
173 return;
174 }
175
176 vx += GrIntToFixed(glyph->fBounds.fLeft);
177 vy += GrIntToFixed(glyph->fBounds.fTop);
178
179 // keep them as ints until we've done the clip-test
180 GrFixed width = glyph->fBounds.width();
181 GrFixed height = glyph->fBounds.height();
182
183 // check if we clipped out
184 if (true || NULL == glyph->fAtlas) {
185 int x = vx >> 16;
186 int y = vy >> 16;
187 if (fClipRect.quickReject(x, y, x + width, y + height)) {
188// Gr_clz(3); // so we can set a break-point in the debugger
189 return;
190 }
191 }
192
193 if (NULL == glyph->fAtlas) {
194 if (fStrike->getGlyphAtlas(glyph, scaler)) {
195 goto HAS_ATLAS;
196 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000197
198 // before we purge the cache, we must flush any accumulated draws
199 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000200 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000201
reed@google.comac10a2d2010-12-22 21:39:39 +0000202 // try to purge
203 fContext->getFontCache()->purgeExceptFor(fStrike);
204 if (fStrike->getGlyphAtlas(glyph, scaler)) {
205 goto HAS_ATLAS;
206 }
207
reed@google.comac10a2d2010-12-22 21:39:39 +0000208 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000209 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000210 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
211 // flag the glyph as being dead?
212 delete path;
213 return;
214 }
215 glyph->fPath = path;
216 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000217
reed@google.comac10a2d2010-12-22 21:39:39 +0000218 GrPoint translate;
219 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
220 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000221 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000222 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 return;
224 }
225
226HAS_ATLAS:
227 GrAssert(glyph->fAtlas);
228
229 // now promote them to fixed
230 width = GrIntToFixed(width);
231 height = GrIntToFixed(height);
232
233 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000234 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000235
tomhudson@google.com375ff852012-06-29 18:37:57 +0000236 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
237 this->flushGlyphs();
238 fCurrTexture = texture;
239 fCurrTexture->ref();
240 }
241
242 if (NULL == fVertices) {
243 // If we need to reserve vertices allow the draw target to suggest
244 // a number of verts to reserve and whether to perform a flush.
245 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000246 bool flush = (NULL != fDrawTarget) &&
247 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000248 &fMaxVertices,
249 NULL);
250 if (flush) {
251 this->flushGlyphs();
252 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000253 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000254 fDrawTarget = fContext->getTextTarget(fPaint);
255 fMaxVertices = kDefaultRequestedVerts;
256 // ignore return, no point in flushing again.
257 fDrawTarget->geometryHints(fVertexLayout,
258 &fMaxVertices,
259 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000260
261 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
262 if (fMaxVertices < kMinRequestedVerts) {
263 fMaxVertices = kDefaultRequestedVerts;
264 } else if (fMaxVertices > maxQuadVertices) {
265 // don't exceed the limit of the index buffer
266 fMaxVertices = maxQuadVertices;
267 }
268 bool success = fDrawTarget->reserveVertexAndIndexSpace(
269 fVertexLayout,
270 fMaxVertices,
271 0,
272 GrTCast<void**>(&fVertices),
273 NULL);
274 GrAlwaysAssert(success);
275 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000276
277 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
278 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
279
bsalomon@google.com2717d562012-05-07 19:10:52 +0000280#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000281 int x = vx >> 16;
282 int y = vy >> 16;
283 int w = width >> 16;
284 int h = height >> 16;
285
286 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
287 setRectFan(&fVertices[2*fCurrVertex+1],
288 texture->normalizeFixedX(tx),
289 texture->normalizeFixedY(ty),
290 texture->normalizeFixedX(tx + width),
291 texture->normalizeFixedY(ty + height),
292 2);
293#else
294 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
295 2 * sizeof(GrGpuTextVertex));
296 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
297 texture->normalizeFixedY(ty),
298 texture->normalizeFixedX(tx + width),
299 texture->normalizeFixedY(ty + height),
300 2 * sizeof(GrGpuTextVertex));
301#endif
302 fCurrVertex += 4;
303}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000304