blob: 684b7f749f492a4981c2772f38faf7fefa3655d2 [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"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000020#include "SkStrokeRec.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000021
tomhudson@google.com375ff852012-06-29 18:37:57 +000022enum {
23 kGlyphMaskStage = GrPaint::kTotalStages,
24};
25
26void GrTextContext::flushGlyphs() {
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000027 if (NULL == fDrawTarget) {
28 return;
29 }
30 GrDrawState* drawState = fDrawTarget->drawState();
reed@google.comac10a2d2010-12-22 21:39:39 +000031 if (fCurrVertex > 0) {
reed@google.comac10a2d2010-12-22 21:39:39 +000032 // setup our sampler state for our text texture/atlas
reed@google.comac10a2d2010-12-22 21:39:39 +000033 GrAssert(GrIsALIGN4(fCurrVertex));
reed@google.comac10a2d2010-12-22 21:39:39 +000034 GrAssert(fCurrTexture);
bsalomon@google.com0e354aa2012-10-08 20:44:25 +000035 GrTextureParams params(SkShader::kRepeat_TileMode, false);
bsalomon@google.comb9086a02012-11-01 18:02:54 +000036 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, SkMatrix::I(), params);
bsalomon@google.com080773c2011-03-15 19:09:25 +000037
bsalomon@google.com669fdc42011-04-05 17:08:27 +000038 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
bsalomon@google.comc7448ce2012-10-05 19:04:13 +000039 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
40 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
bsalomon@google.com88becf42012-10-05 14:54:42 +000041 fPaint.hasColorStage()) {
bsalomon@google.com080773c2011-03-15 19:09:25 +000042 GrPrintf("LCD Text will not draw correctly.\n");
43 }
44 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
bsalomon@google.comc7448ce2012-10-05 19:04:13 +000045 drawState->setBlendConstant(fPaint.getColor());
bsalomon@google.com47059542012-06-06 20:51:20 +000046 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000047 // don't modulate by the paint's color in the frag since we're
48 // already doing it via the blend const.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000049 drawState->setColor(0xffffffff);
bsalomon@google.com080773c2011-03-15 19:09:25 +000050 } else {
51 // set back to normal in case we took LCD path previously.
bsalomon@google.comc7448ce2012-10-05 19:04:13 +000052 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
53 drawState->setColor(fPaint.getColor());
bsalomon@google.com080773c2011-03-15 19:09:25 +000054 }
55
bsalomon@google.com934c5702012-03-20 21:17:58 +000056 int nGlyphs = fCurrVertex / 4;
tomhudson@google.com375ff852012-06-29 18:37:57 +000057 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
bsalomon@google.com47059542012-06-06 20:51:20 +000058 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
bsalomon@google.com934c5702012-03-20 21:17:58 +000059 nGlyphs,
60 4, 6);
tomhudson@google.com375ff852012-06-29 18:37:57 +000061 fDrawTarget->resetVertexSource();
reed@google.comac10a2d2010-12-22 21:39:39 +000062 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +000063 fMaxVertices = 0;
64 fCurrVertex = 0;
65 GrSafeSetNull(fCurrTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +000066 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000067 drawState->disableStages();
68 fDrawTarget = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000069}
70
bsalomon@google.com0e354aa2012-10-08 20:44:25 +000071GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
tomhudson@google.com375ff852012-06-29 18:37:57 +000072 fContext = context;
bsalomon@google.comf4a9c822012-03-16 14:02:46 +000073 fStrike = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000074
tomhudson@google.com375ff852012-06-29 18:37:57 +000075 fCurrTexture = NULL;
76 fCurrVertex = 0;
77
robertphillips@google.combeb1af72012-07-26 18:52:16 +000078 const GrClipData* clipData = context->getClip();
79
robertphillips@google.com641f8b12012-07-31 19:15:58 +000080 GrRect devConservativeBound;
81 clipData->fClipStack->getConservativeBounds(
82 -clipData->fOrigin.fX,
83 -clipData->fOrigin.fY,
84 context->getRenderTarget()->width(),
85 context->getRenderTarget()->height(),
86 &devConservativeBound);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000087
robertphillips@google.com641f8b12012-07-31 19:15:58 +000088 devConservativeBound.roundOut(&fClipRect);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000089
bsalomon@google.com858804d2012-10-15 14:25:50 +000090 fAutoMatrix.setIdentity(fContext, &fPaint);
bsalomon@google.com39149582011-06-13 21:55:32 +000091
tomhudson@google.comcb325ce2012-07-11 14:41:19 +000092 fDrawTarget = NULL;
bsalomon@google.com7d34d2e2011-01-24 17:41:47 +000093
reed@google.comac10a2d2010-12-22 21:39:39 +000094 fVertices = NULL;
tomhudson@google.com375ff852012-06-29 18:37:57 +000095 fMaxVertices = 0;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000096
rmistry@google.comd6176b02012-08-23 18:14:13 +000097 fVertexLayout =
jvanverth@google.comcc782382013-01-28 20:39:48 +000098 GrDrawState::kTextFormat_VertexLayoutBit |
99 GrDrawState::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000100}
101
tomhudson@google.com375ff852012-06-29 18:37:57 +0000102GrTextContext::~GrTextContext() {
103 this->flushGlyphs();
104 if (fDrawTarget) {
105 fDrawTarget->drawState()->disableStages();
106 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000107}
108
tomhudson@google.com375ff852012-06-29 18:37:57 +0000109void GrTextContext::flush() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000110 this->flushGlyphs();
111}
112
113static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
114 int stride) {
115 v[0 * stride].setI(l, t);
116 v[1 * stride].setI(l, b);
117 v[2 * stride].setI(r, b);
118 v[3 * stride].setI(r, t);
119}
120
tomhudson@google.com375ff852012-06-29 18:37:57 +0000121void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
reed@google.comac10a2d2010-12-22 21:39:39 +0000122 GrFixed vx, GrFixed vy,
123 GrFontScaler* scaler) {
124 if (NULL == fStrike) {
125 fStrike = fContext->getFontCache()->getStrike(scaler);
126 }
127
128 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
129 if (NULL == glyph || glyph->fBounds.isEmpty()) {
130 return;
131 }
132
bsalomon@google.com81712882012-11-01 17:12:34 +0000133 vx += SkIntToFixed(glyph->fBounds.fLeft);
134 vy += SkIntToFixed(glyph->fBounds.fTop);
reed@google.comac10a2d2010-12-22 21:39:39 +0000135
136 // keep them as ints until we've done the clip-test
137 GrFixed width = glyph->fBounds.width();
138 GrFixed height = glyph->fBounds.height();
139
140 // check if we clipped out
141 if (true || NULL == glyph->fAtlas) {
142 int x = vx >> 16;
143 int y = vy >> 16;
144 if (fClipRect.quickReject(x, y, x + width, y + height)) {
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000145// SkCLZ(3); // so we can set a break-point in the debugger
reed@google.comac10a2d2010-12-22 21:39:39 +0000146 return;
147 }
148 }
149
150 if (NULL == glyph->fAtlas) {
151 if (fStrike->getGlyphAtlas(glyph, scaler)) {
152 goto HAS_ATLAS;
153 }
reed@google.com0ebe81a2011-04-04 20:06:59 +0000154
155 // before we purge the cache, we must flush any accumulated draws
156 this->flushGlyphs();
bsalomon@google.com193395c2012-03-30 17:35:12 +0000157 fContext->flush();
reed@google.com313e4032010-12-22 22:16:59 +0000158
reed@google.comac10a2d2010-12-22 21:39:39 +0000159 // try to purge
160 fContext->getFontCache()->purgeExceptFor(fStrike);
161 if (fStrike->getGlyphAtlas(glyph, scaler)) {
162 goto HAS_ATLAS;
163 }
164
reed@google.comac10a2d2010-12-22 21:39:39 +0000165 if (NULL == glyph->fPath) {
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000166 SkPath* path = SkNEW(SkPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000167 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
168 // flag the glyph as being dead?
169 delete path;
170 return;
171 }
172 glyph->fPath = path;
173 }
reed@google.com07f3ee12011-05-16 17:21:57 +0000174
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000175 GrContext::AutoMatrix am;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000176 SkMatrix translate;
bsalomon@google.com81712882012-11-01 17:12:34 +0000177 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
178 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000179 GrPaint tmpPaint(fPaint);
180 am.setPreConcat(fContext, translate, &tmpPaint);
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000181 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
182 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
reed@google.comac10a2d2010-12-22 21:39:39 +0000183 return;
184 }
185
186HAS_ATLAS:
187 GrAssert(glyph->fAtlas);
188
189 // now promote them to fixed
bsalomon@google.com81712882012-11-01 17:12:34 +0000190 width = SkIntToFixed(width);
191 height = SkIntToFixed(height);
reed@google.comac10a2d2010-12-22 21:39:39 +0000192
193 GrTexture* texture = glyph->fAtlas->texture();
tomhudson@google.com375ff852012-06-29 18:37:57 +0000194 GrAssert(texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000195
tomhudson@google.com375ff852012-06-29 18:37:57 +0000196 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
197 this->flushGlyphs();
198 fCurrTexture = texture;
199 fCurrTexture->ref();
200 }
201
202 if (NULL == fVertices) {
203 // If we need to reserve vertices allow the draw target to suggest
204 // a number of verts to reserve and whether to perform a flush.
205 fMaxVertices = kMinRequestedVerts;
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000206 bool flush = false;
207 fDrawTarget = fContext->getTextTarget(fPaint);
208 if (NULL != fDrawTarget) {
209 fDrawTarget->drawState()->setVertexLayout(fVertexLayout);
210 flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
211 }
tomhudson@google.com375ff852012-06-29 18:37:57 +0000212 if (flush) {
213 this->flushGlyphs();
214 fContext->flush();
bsalomon@google.comf3a7bc72013-02-05 21:32:06 +0000215 // flushGlyphs() will reset fDrawTarget to NULL.
216 fDrawTarget = fContext->getTextTarget(fPaint);
217 fDrawTarget->drawState()->setVertexLayout(fVertexLayout);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000218 }
tomhudson@google.comcb325ce2012-07-11 14:41:19 +0000219 fMaxVertices = kDefaultRequestedVerts;
220 // ignore return, no point in flushing again.
bsalomon@google.comf3a7bc72013-02-05 21:32:06 +0000221 fDrawTarget->geometryHints(&fMaxVertices, NULL);
tomhudson@google.com375ff852012-06-29 18:37:57 +0000222
223 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
224 if (fMaxVertices < kMinRequestedVerts) {
225 fMaxVertices = kDefaultRequestedVerts;
226 } else if (fMaxVertices > maxQuadVertices) {
227 // don't exceed the limit of the index buffer
228 fMaxVertices = maxQuadVertices;
229 }
230 bool success = fDrawTarget->reserveVertexAndIndexSpace(
tomhudson@google.com375ff852012-06-29 18:37:57 +0000231 fMaxVertices,
232 0,
233 GrTCast<void**>(&fVertices),
234 NULL);
235 GrAlwaysAssert(success);
236 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000237
bsalomon@google.com81712882012-11-01 17:12:34 +0000238 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
239 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
reed@google.comac10a2d2010-12-22 21:39:39 +0000240
bsalomon@google.com2717d562012-05-07 19:10:52 +0000241#if GR_TEXT_SCALAR_IS_USHORT
reed@google.comac10a2d2010-12-22 21:39:39 +0000242 int x = vx >> 16;
243 int y = vy >> 16;
244 int w = width >> 16;
245 int h = height >> 16;
246
247 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
248 setRectFan(&fVertices[2*fCurrVertex+1],
249 texture->normalizeFixedX(tx),
250 texture->normalizeFixedY(ty),
251 texture->normalizeFixedX(tx + width),
252 texture->normalizeFixedY(ty + height),
253 2);
254#else
255 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
256 2 * sizeof(GrGpuTextVertex));
257 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
258 texture->normalizeFixedY(ty),
259 texture->normalizeFixedX(tx + width),
260 texture->normalizeFixedY(ty + height),
261 2 * sizeof(GrGpuTextVertex));
262#endif
263 fCurrVertex += 4;
264}