blob: 576a5bdeae245f555c8a35f373dd44ebd4f8a04f [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
robertphillips@google.com7b112892012-07-31 15:18:21 +000073namespace {
74
75// 'rect' enters in canvas coordinates and leaves in device coordinates
76void canvas_to_device(SkRect* rect, const SkIPoint& origin) {
77 GrAssert(NULL != rect);
78
79 rect->fLeft -= SkIntToScalar(origin.fX);
80 rect->fTop -= SkIntToScalar(origin.fY);
81 rect->fRight -= SkIntToScalar(origin.fX);
82 rect->fBottom -= SkIntToScalar(origin.fY);
83}
84
85};
86
tomhudson@google.com375ff852012-06-29 18:37:57 +000087GrTextContext::GrTextContext(GrContext* context,
88 const GrPaint& paint,
89 const GrMatrix* extMatrix) : fPaint(paint) {
90 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000091 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000092
tomhudson@google.com375ff852012-06-29 18:37:57 +000093 fCurrTexture = NULL;
94 fCurrVertex = 0;
95
reed@google.comac10a2d2010-12-22 21:39:39 +000096 if (NULL != extMatrix) {
97 fExtMatrix = *extMatrix;
98 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000099 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +0000100 }
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +0000101
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000102 const GrClipData* clipData = context->getClip();
103
104 GrRect conservativeBound = clipData->fClipStack->getConservativeBounds();
robertphillips@google.com7b112892012-07-31 15:18:21 +0000105 canvas_to_device(&conservativeBound, clipData->fOrigin);
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000106
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +0000107 if (!fExtMatrix.isIdentity()) {
108 GrMatrix inverse;
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +0000109 if (fExtMatrix.invert(&inverse)) {
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000110 inverse.mapRect(&conservativeBound);
reed@google.comac10a2d2010-12-22 21:39:39 +0000111 }
112 }
113
robertphillips@google.combeb1af72012-07-26 18:52:16 +0000114 conservativeBound.roundOut(&fClipRect);
115
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000116 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000117 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000118 fOrigViewMatrix = fContext->getMatrix();
119 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000120
bsalomon@google.com39149582011-06-13 21:55:32 +0000121 /*
122 We need to call preConcatMatrix with our viewmatrix's inverse, for each
123 texture and mask in the paint. However, computing the inverse can be
124 expensive, and its possible we may not have any textures or masks, so these
125 two loops are written such that we only compute the inverse (once) if we
126 need it. We do this on our copy of the paint rather than directly on the
127 draw target because we re-provide the paint to the context when we have
128 to flush our glyphs or draw a glyph as a path midstream.
129 */
130 bool invVMComputed = false;
131 GrMatrix invVM;
132 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000133 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000134 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
135 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000136 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000137 }
138 }
139 }
140 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000141 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000142 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
143 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000144 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000145 }
146 }
147 }
148
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000149 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000150
reed@google.comac10a2d2010-12-22 21:39:39 +0000151 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000152 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000153
154 fVertexLayout =
155 GrDrawTarget::kTextFormat_VertexLayoutBit |
156 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000157}
158
tomhudson@google.com375ff852012-06-29 18:37:57 +0000159GrTextContext::~GrTextContext() {
160 this->flushGlyphs();
161 if (fDrawTarget) {
162 fDrawTarget->drawState()->disableStages();
163 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000164 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000165}
166
tomhudson@google.com375ff852012-06-29 18:37:57 +0000167void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000168 this->flushGlyphs();
169}
170
171static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
172 int stride) {
173 v[0 * stride].setI(l, t);
174 v[1 * stride].setI(l, b);
175 v[2 * stride].setI(r, b);
176 v[3 * stride].setI(r, t);
177}
178
tomhudson@google.com375ff852012-06-29 18:37:57 +0000179void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000180 GrFixed vx, GrFixed vy,
181 GrFontScaler* scaler) {
182 if (NULL == fStrike) {
183 fStrike = fContext->getFontCache()->getStrike(scaler);
184 }
185
186 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
187 if (NULL == glyph || glyph->fBounds.isEmpty()) {
188 return;
189 }
190
191 vx += GrIntToFixed(glyph->fBounds.fLeft);
192 vy += GrIntToFixed(glyph->fBounds.fTop);
193
194 // keep them as ints until we've done the clip-test
195 GrFixed width = glyph->fBounds.width();
196 GrFixed height = glyph->fBounds.height();
197
198 // check if we clipped out
199 if (true || NULL == glyph->fAtlas) {
200 int x = vx >> 16;
201 int y = vy >> 16;
202 if (fClipRect.quickReject(x, y, x + width, y + height)) {
203// Gr_clz(3); // so we can set a break-point in the debugger
204 return;
205 }
206 }
207
208 if (NULL == glyph->fAtlas) {
209 if (fStrike->getGlyphAtlas(glyph, scaler)) {
210 goto HAS_ATLAS;
211 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000212
213 // before we purge the cache, we must flush any accumulated draws
214 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000215 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000216
reed@google.comac10a2d2010-12-22 21:39:39 +0000217 // try to purge
218 fContext->getFontCache()->purgeExceptFor(fStrike);
219 if (fStrike->getGlyphAtlas(glyph, scaler)) {
220 goto HAS_ATLAS;
221 }
222
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000224 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000225 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
226 // flag the glyph as being dead?
227 delete path;
228 return;
229 }
230 glyph->fPath = path;
231 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000232
reed@google.comac10a2d2010-12-22 21:39:39 +0000233 GrPoint translate;
234 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
235 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000236 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000237 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000238 return;
239 }
240
241HAS_ATLAS:
242 GrAssert(glyph->fAtlas);
243
244 // now promote them to fixed
245 width = GrIntToFixed(width);
246 height = GrIntToFixed(height);
247
248 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000249 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000250
tomhudson@google.com375ff852012-06-29 18:37:57 +0000251 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
252 this->flushGlyphs();
253 fCurrTexture = texture;
254 fCurrTexture->ref();
255 }
256
257 if (NULL == fVertices) {
258 // If we need to reserve vertices allow the draw target to suggest
259 // a number of verts to reserve and whether to perform a flush.
260 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000261 bool flush = (NULL != fDrawTarget) &&
262 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000263 &fMaxVertices,
264 NULL);
265 if (flush) {
266 this->flushGlyphs();
267 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000268 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000269 fDrawTarget = fContext->getTextTarget(fPaint);
270 fMaxVertices = kDefaultRequestedVerts;
271 // ignore return, no point in flushing again.
272 fDrawTarget->geometryHints(fVertexLayout,
273 &fMaxVertices,
274 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000275
276 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
277 if (fMaxVertices < kMinRequestedVerts) {
278 fMaxVertices = kDefaultRequestedVerts;
279 } else if (fMaxVertices > maxQuadVertices) {
280 // don't exceed the limit of the index buffer
281 fMaxVertices = maxQuadVertices;
282 }
283 bool success = fDrawTarget->reserveVertexAndIndexSpace(
284 fVertexLayout,
285 fMaxVertices,
286 0,
287 GrTCast<void**>(&fVertices),
288 NULL);
289 GrAlwaysAssert(success);
290 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000291
292 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
293 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
294
bsalomon@google.com2717d562012-05-07 19:10:52 +0000295#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000296 int x = vx >> 16;
297 int y = vy >> 16;
298 int w = width >> 16;
299 int h = height >> 16;
300
301 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
302 setRectFan(&fVertices[2*fCurrVertex+1],
303 texture->normalizeFixedX(tx),
304 texture->normalizeFixedY(ty),
305 texture->normalizeFixedX(tx + width),
306 texture->normalizeFixedY(ty + height),
307 2);
308#else
309 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
310 2 * sizeof(GrGpuTextVertex));
311 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
312 texture->normalizeFixedY(ty),
313 texture->normalizeFixedX(tx + width),
314 texture->normalizeFixedY(ty + height),
315 2 * sizeof(GrGpuTextVertex));
316#endif
317 fCurrVertex += 4;
318}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000319