blob: c6f34e2c53086e1affac24dbab31b351a89a008b [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac10a2d2010-12-22 21:39:39 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@google.comac10a2d2010-12-22 21:39:39 +00007 */
8
9
epoger@google.comec3ed6a2011-07-28 14:26:00 +000010
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000011#include "GrAtlas.h"
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000012#include "GrDefaultTextContext.h"
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000013#include "GrContext.h"
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000014#include "GrDrawTarget.h"
15#include "GrFontScaler.h"
16#include "GrGpuVertex.h"
17#include "GrTemplates.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000018#include "GrTextStrike.h"
19#include "GrTextStrike_impl.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000020
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000021void GrDefaultTextContext::flushGlyphs() {
22 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +000023 if (fCurrVertex > 0) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000024 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000025 // setup our sampler state for our text texture/atlas
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000026 GrSamplerState::Filter filter;
27 if (fExtMatrix.isIdentity()) {
28 filter = GrSamplerState::kNearest_Filter;
29 } else {
30 filter = GrSamplerState::kBilinear_Filter;
31 }
bsalomon@google.com1e266f82011-12-12 16:11:33 +000032 drawState->sampler(kGlyphMaskStage)->reset(
33 GrSamplerState::kRepeat_WrapMode,filter);
reed@google.comac10a2d2010-12-22 21:39:39 +000034
35 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000036 GrAssert(fCurrTexture);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000037 drawState->setTexture(kGlyphMaskStage, fCurrTexture);
bsalomon@google.com080773c2011-03-15 19:09:25 +000038
bsalomon@google.com669fdc42011-04-05 17:08:27 +000039 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.com47059542012-06-06 20:51:20 +000040 if (kOne_GrBlendCoeff != fGrPaint.fSrcBlendCoeff ||
41 kISA_GrBlendCoeff != fGrPaint.fDstBlendCoeff ||
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000042 fGrPaint.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
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000046 drawState->setBlendConstant(fGrPaint.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.
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000053 drawState->setBlendFunc(fGrPaint.fSrcBlendCoeff, fGrPaint.fDstBlendCoeff);
54 drawState->setColor(fGrPaint.fColor);
bsalomon@google.com080773c2011-03-15 19:09:25 +000055 }
56
bsalomon@google.com86afc2a2011-02-16 16:12:19 +000057 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com934c5702012-03-20 21:17:58 +000058 int nGlyphs = fCurrVertex / 4;
bsalomon@google.com47059542012-06-06 20:51:20 +000059 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
bsalomon@google.com934c5702012-03-20 21:17:58 +000060 nGlyphs,
61 4, 6);
reed@google.comac10a2d2010-12-22 21:39:39 +000062 fVertices = NULL;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000063 this->INHERITED::reset();
robertphillips@google.com972265d2012-06-13 18:49:30 +000064 drawState->setTexture(kGlyphMaskStage, NULL);
reed@google.comac10a2d2010-12-22 21:39:39 +000065 }
66}
67
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000068GrDefaultTextContext::GrDefaultTextContext() {
69}
reed@google.comac10a2d2010-12-22 21:39:39 +000070
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000071GrDefaultTextContext::~GrDefaultTextContext() {
72}
73
74void GrDefaultTextContext::init(GrContext* context,
75 const GrPaint& paint,
76 const GrMatrix* extMatrix) {
77 this->INHERITED::init(context, paint, extMatrix);
78
79 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000080
81 if (NULL != extMatrix) {
82 fExtMatrix = *extMatrix;
83 } else {
bsalomon@google.com022a3e12012-03-16 15:41:40 +000084 fExtMatrix.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +000085 }
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000086 if (context->getClip().hasConservativeBounds()) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000087 if (!fExtMatrix.isIdentity()) {
88 GrMatrix inverse;
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000089 GrRect r = context->getClip().getConservativeBounds();
bsalomon@google.comd302f142011-03-03 13:54:13 +000090 if (fExtMatrix.invert(&inverse)) {
91 inverse.mapRect(&r);
92 r.roundOut(&fClipRect);
93 }
94 } else {
bsalomon@google.com0b50b2e2011-03-08 21:07:21 +000095 context->getClip().getConservativeBounds().roundOut(&fClipRect);
reed@google.comac10a2d2010-12-22 21:39:39 +000096 }
bsalomon@google.comd302f142011-03-03 13:54:13 +000097 } else {
98 fClipRect.setLargest();
reed@google.comac10a2d2010-12-22 21:39:39 +000099 }
100
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000101 // save the context's original matrix off and restore in destructor
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000102 // getTextTarget should be called after that
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) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000118 if (NULL != fGrPaint.getTexture(t)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000119 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
120 invVMComputed = true;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000121 fGrPaint.textureSampler(t)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000122 }
123 }
124 }
125 for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000126 if (NULL != fGrPaint.getMask(m)) {
bsalomon@google.com39149582011-06-13 21:55:32 +0000127 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
128 invVMComputed = true;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000129 fGrPaint.maskSampler(m)->preConcatMatrix(invVM);
bsalomon@google.com39149582011-06-13 21:55:32 +0000130 }
131 }
132 }
133
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000134 fDrawTarget = fContext->getTextTarget(fGrPaint);
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +0000135
reed@google.comac10a2d2010-12-22 21:39:39 +0000136 fVertices = NULL;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000137
138 fVertexLayout =
139 GrDrawTarget::kTextFormat_VertexLayoutBit |
140 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
141
142 int stageMask = paint.getActiveStageMask();
143 if (stageMask) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000144 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
145 if ((1 << i) & stageMask) {
146 fVertexLayout |=
147 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
148 GrAssert(i != kGlyphMaskStage);
149 }
150 }
151 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000152}
153
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000154void GrDefaultTextContext::finish() {
155 this->flush();
156
157 fStrike = NULL;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000158 fContext->setMatrix(fOrigViewMatrix);
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000159
160 this->INHERITED::finish();
reed@google.comac10a2d2010-12-22 21:39:39 +0000161}
162
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000163void GrDefaultTextContext::flush() {
164 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +0000165 this->flushGlyphs();
166}
167
168static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
169 int stride) {
170 v[0 * stride].setI(l, t);
171 v[1 * stride].setI(l, b);
172 v[2 * stride].setI(r, b);
173 v[3 * stride].setI(r, t);
174}
175
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000176void GrDefaultTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000177 GrFixed vx, GrFixed vy,
178 GrFontScaler* scaler) {
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000179 GrAssert(this->isValid());
reed@google.comac10a2d2010-12-22 21:39:39 +0000180 if (NULL == fStrike) {
181 fStrike = fContext->getFontCache()->getStrike(scaler);
182 }
183
184 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
185 if (NULL == glyph || glyph->fBounds.isEmpty()) {
186 return;
187 }
188
189 vx += GrIntToFixed(glyph->fBounds.fLeft);
190 vy += GrIntToFixed(glyph->fBounds.fTop);
191
192 // keep them as ints until we've done the clip-test
193 GrFixed width = glyph->fBounds.width();
194 GrFixed height = glyph->fBounds.height();
195
196 // check if we clipped out
197 if (true || NULL == glyph->fAtlas) {
198 int x = vx >> 16;
199 int y = vy >> 16;
200 if (fClipRect.quickReject(x, y, x + width, y + height)) {
201// Gr_clz(3); // so we can set a break-point in the debugger
202 return;
203 }
204 }
205
206 if (NULL == glyph->fAtlas) {
207 if (fStrike->getGlyphAtlas(glyph, scaler)) {
208 goto HAS_ATLAS;
209 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000210
211 // before we purge the cache, we must flush any accumulated draws
212 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000213 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000214
reed@google.comac10a2d2010-12-22 21:39:39 +0000215 // try to purge
216 fContext->getFontCache()->purgeExceptFor(fStrike);
217 if (fStrike->getGlyphAtlas(glyph, scaler)) {
218 goto HAS_ATLAS;
219 }
220
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 if (NULL == glyph->fPath) {
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000222 SkPath* path = new SkPath;
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
224 // flag the glyph as being dead?
225 delete path;
226 return;
227 }
228 glyph->fPath = path;
229 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000230
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 GrPoint translate;
232 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
233 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.com47059542012-06-06 20:51:20 +0000234 fContext->drawPath(fGrPaint, *glyph->fPath, kWinding_GrPathFill,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000235 &translate);
reed@google.comac10a2d2010-12-22 21:39:39 +0000236 return;
237 }
238
239HAS_ATLAS:
240 GrAssert(glyph->fAtlas);
241
242 // now promote them to fixed
243 width = GrIntToFixed(width);
244 height = GrIntToFixed(height);
245
246 GrTexture* texture = glyph->fAtlas->texture();
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000247 this->prepareForGlyph(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000248
bsalomon@google.comf4a9c822012-03-16 14:02:46 +0000249 this->setupVertexBuff(GrTCast<void**>(&fVertices),
250 fVertexLayout);
reed@google.comac10a2d2010-12-22 21:39:39 +0000251
252 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
253 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
254
bsalomon@google.com2717d562012-05-07 19:10:52 +0000255#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000256 int x = vx >> 16;
257 int y = vy >> 16;
258 int w = width >> 16;
259 int h = height >> 16;
260
261 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
262 setRectFan(&fVertices[2*fCurrVertex+1],
263 texture->normalizeFixedX(tx),
264 texture->normalizeFixedY(ty),
265 texture->normalizeFixedX(tx + width),
266 texture->normalizeFixedY(ty + height),
267 2);
268#else
269 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
270 2 * sizeof(GrGpuTextVertex));
271 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
272 texture->normalizeFixedY(ty),
273 texture->normalizeFixedX(tx + width),
274 texture->normalizeFixedY(ty + height),
275 2 * sizeof(GrGpuTextVertex));
276#endif
277 fCurrVertex += 4;
278}