blob: 925fdb32f485adb065d8759e290d9bd602552b48 [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,
33 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
88 if (!fExtMatrix.isIdentity()) {
89 GrMatrix inverse;
90 GrRect r = context->getClip().getConservativeBounds();
91 if (fExtMatrix.invert(&inverse)) {
92 inverse.mapRect(&r);
93 r.roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +000094 }
bsalomon@google.comd302f142011-03-03 13:54:13 +000095 } else {
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000096 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +000097 }
98
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000099 // save the context's original matrix off and restore in destructor
tomhudson@google.com375ff852012-06-29 18:37:57 +0000100 // this must be done before getTextTarget.
bsalomon@google.com5782d712011-01-21 21:03:59 +0000101 fOrigViewMatrix = fContext->getMatrix();
102 fContext->setMatrix(fExtMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000103
bsalomon@google.com39149582011-06-13 21:55:32 +0000104 /*
105 We need to call preConcatMatrix with our viewmatrix's inverse, for each
106 texture and mask in the paint. However, computing the inverse can be
107 expensive, and its possible we may not have any textures or masks, so these
108 two loops are written such that we only compute the inverse (once) if we
109 need it. We do this on our copy of the paint rather than directly on the
110 draw target because we re-provide the paint to the context when we have
111 to flush our glyphs or draw a glyph as a path midstream.
112 */
113 bool invVMComputed = false;
114 GrMatrix invVM;
115 for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000116 if (fPaint.isTextureStageEnabled(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000117 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
118 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000119 fPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000120 }
121 }
122 }
123 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
tomhudson@google.com375ff852012-06-29 18:37:57 +0000124 if (fPaint.isMaskStageEnabled(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000125 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
126 invVMComputed = true;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000127 fPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000128 }
129 }
130 }
131
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000132 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000133
reed@google.comac10a2d2010-12-22 21:39:39 +0000134 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +0000135 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000136
137 fVertexLayout =
138 GrDrawTarget::kTextFormat_VertexLayoutBit |
139 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000140}
141
tomhudson@google.com375ff852012-06-29 18:37:57 +0000142GrTextContext::~GrTextContext() {
143 this->flushGlyphs();
144 if (fDrawTarget) {
145 fDrawTarget->drawState()->disableStages();
146 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000147 fContext->setMatrix(fOrigViewMatrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000148}
149
tomhudson@google.com375ff852012-06-29 18:37:57 +0000150void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000151 this->flushGlyphs();
152}
153
154static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
155 int stride) {
156 v[0 * stride].setI(l, t);
157 v[1 * stride].setI(l, b);
158 v[2 * stride].setI(r, b);
159 v[3 * stride].setI(r, t);
160}
161
tomhudson@google.com375ff852012-06-29 18:37:57 +0000162void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000163 GrFixed vx, GrFixed vy,
164 GrFontScaler* scaler) {
165 if (NULL == fStrike) {
166 fStrike = fContext->getFontCache()->getStrike(scaler);
167 }
168
169 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
170 if (NULL == glyph || glyph->fBounds.isEmpty()) {
171 return;
172 }
173
174 vx += GrIntToFixed(glyph->fBounds.fLeft);
175 vy += GrIntToFixed(glyph->fBounds.fTop);
176
177 // keep them as ints until we've done the clip-test
178 GrFixed width = glyph->fBounds.width();
179 GrFixed height = glyph->fBounds.height();
180
181 // check if we clipped out
182 if (true || NULL == glyph->fAtlas) {
183 int x = vx >> 16;
184 int y = vy >> 16;
185 if (fClipRect.quickReject(x, y, x + width, y + height)) {
186// Gr_clz(3); // so we can set a break-point in the debugger
187 return;
188 }
189 }
190
191 if (NULL == glyph->fAtlas) {
192 if (fStrike->getGlyphAtlas(glyph, scaler)) {
193 goto HAS_ATLAS;
194 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000195
196 // before we purge the cache, we must flush any accumulated draws
197 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000198 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000199
reed@google.comac10a2d2010-12-22 21:39:39 +0000200 // try to purge
201 fContext->getFontCache()->purgeExceptFor(fStrike);
202 if (fStrike->getGlyphAtlas(glyph, scaler)) {
203 goto HAS_ATLAS;
204 }
205
reed@google.comac10a2d2010-12-22 21:39:39 +0000206 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000207 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000208 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
209 // flag the glyph as being dead?
210 delete path;
211 return;
212 }
213 glyph->fPath = path;
214 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000215
reed@google.comac10a2d2010-12-22 21:39:39 +0000216 GrPoint translate;
217 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
218 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
tomhudson@google.com375ff852012-06-29 18:37:57 +0000219 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000220 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 return;
222 }
223
224HAS_ATLAS:
225 GrAssert(glyph->fAtlas);
226
227 // now promote them to fixed
228 width = GrIntToFixed(width);
229 height = GrIntToFixed(height);
230
231 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000232 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000233
tomhudson@google.com375ff852012-06-29 18:37:57 +0000234 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
235 this->flushGlyphs();
236 fCurrTexture = texture;
237 fCurrTexture->ref();
238 }
239
240 if (NULL == fVertices) {
241 // If we need to reserve vertices allow the draw target to suggest
242 // a number of verts to reserve and whether to perform a flush.
243 fMaxVertices = kMinRequestedVerts;
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000244 bool flush = (NULL != fDrawTarget) &&
245 fDrawTarget->geometryHints(fVertexLayout,
tomhudson@google.com375ff852012-06-29 18:37:57 +0000246 &fMaxVertices,
247 NULL);
248 if (flush) {
249 this->flushGlyphs();
250 fContext->flush();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000251 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000252 fDrawTarget = fContext->getTextTarget(fPaint);
253 fMaxVertices = kDefaultRequestedVerts;
254 // ignore return, no point in flushing again.
255 fDrawTarget->geometryHints(fVertexLayout,
256 &fMaxVertices,
257 NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000258
259 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
260 if (fMaxVertices < kMinRequestedVerts) {
261 fMaxVertices = kDefaultRequestedVerts;
262 } else if (fMaxVertices > maxQuadVertices) {
263 // don't exceed the limit of the index buffer
264 fMaxVertices = maxQuadVertices;
265 }
266 bool success = fDrawTarget->reserveVertexAndIndexSpace(
267 fVertexLayout,
268 fMaxVertices,
269 0,
270 GrTCast<void**>(&fVertices),
271 NULL);
272 GrAlwaysAssert(success);
273 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000274
275 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
276 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
277
bsalomon@google.com2717d562012-05-07 19:10:52 +0000278#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000279 int x = vx >> 16;
280 int y = vy >> 16;
281 int w = width >> 16;
282 int h = height >> 16;
283
284 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
285 setRectFan(&fVertices[2*fCurrVertex+1],
286 texture->normalizeFixedX(tx),
287 texture->normalizeFixedY(ty),
288 texture->normalizeFixedX(tx + width),
289 texture->normalizeFixedY(ty + height),
290 2);
291#else
292 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
293 2 * sizeof(GrGpuTextVertex));
294 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
295 texture->normalizeFixedY(ty),
296 texture->normalizeFixedX(tx + width),
297 texture->normalizeFixedY(ty + height),
298 2 * sizeof(GrGpuTextVertex));
299#endif
300 fCurrVertex += 4;
301}
tomhudson@google.com375ff852012-06-29 18:37:57 +0000302