blob: a43c4a28746be3551b07d9dd9e68ca97df4293ed [file] [log] [blame]
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +00001/*
2 * Copyright 2013 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.
6 */
7
8#include "GrBitmapTextContext.h"
9#include "GrAtlas.h"
10#include "GrDrawTarget.h"
11#include "GrFontScaler.h"
12#include "GrIndexBuffer.h"
13#include "GrTextStrike.h"
14#include "GrTextStrike_impl.h"
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000015#include "SkColorPriv.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000016#include "SkPath.h"
17#include "SkRTConf.h"
18#include "SkStrokeRec.h"
19#include "effects/GrCustomCoordsTextureEffect.h"
20
21static const int kGlyphCoordsAttributeIndex = 1;
22
23SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
24 "Dump the contents of the font cache before every purge.");
25
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000026GrBitmapTextContext::GrBitmapTextContext(GrContext* context, const GrPaint& paint,
27 SkColor color) :
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000028 GrTextContext(context, paint) {
29 fAutoMatrix.setIdentity(fContext, &fPaint);
30
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000031 fSkPaintColor = color;
32
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000033 fStrike = NULL;
34
35 fCurrTexture = NULL;
36 fCurrVertex = 0;
37
38 fVertices = NULL;
39 fMaxVertices = 0;
40}
41
42GrBitmapTextContext::~GrBitmapTextContext() {
43 this->flushGlyphs();
44}
45
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000046static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
47 unsigned r = SkColorGetR(c);
48 unsigned g = SkColorGetG(c);
49 unsigned b = SkColorGetB(c);
50 return GrColorPackRGBA(r, g, b, 0xff);
51}
52
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000053void GrBitmapTextContext::flushGlyphs() {
54 if (NULL == fDrawTarget) {
55 return;
56 }
57
58 GrDrawState* drawState = fDrawTarget->drawState();
59 GrDrawState::AutoRestoreEffects are(drawState);
60 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
61
62 if (fCurrVertex > 0) {
63 // setup our sampler state for our text texture/atlas
64 SkASSERT(GrIsALIGN4(fCurrVertex));
65 SkASSERT(fCurrTexture);
66 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
67
68 // This effect could be stored with one of the cache objects (atlas?)
69 drawState->addCoverageEffect(
70 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
71 kGlyphCoordsAttributeIndex)->unref();
72
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000073 if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
74 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
75 drawState->setColor(0xffffffff);
76 } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000077 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
78 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
79 fPaint.numColorStages()) {
80 GrPrintf("LCD Text will not draw correctly.\n");
81 }
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000082 // We don't use the GrPaint's color in this case because it's been premultiplied by
83 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
skia.committer@gmail.com70402c32013-10-29 07:01:50 +000084 // the mask texture color. The end result is that we get
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000085 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
86 int a = SkColorGetA(fSkPaintColor);
87 // paintAlpha
88 drawState->setColor(SkColorSetARGB(a, a, a, a));
89 // paintColor
90 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000091 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000092 } else {
93 // set back to normal in case we took LCD path previously.
94 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
95 drawState->setColor(fPaint.getColor());
96 }
97
98 int nGlyphs = fCurrVertex / 4;
99 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
100 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
101 nGlyphs,
102 4, 6);
commit-bot@chromium.org42a89572013-10-28 15:13:50 +0000103
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000104 fDrawTarget->resetVertexSource();
105 fVertices = NULL;
106 fMaxVertices = 0;
107 fCurrVertex = 0;
108 SkSafeSetNull(fCurrTexture);
109 }
110}
111
112namespace {
113
114// position + texture coord
115extern const GrVertexAttrib gTextVertexAttribs[] = {
116 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
117 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
118};
119
120};
121
122void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
123 GrFixed vx, GrFixed vy,
124 GrFontScaler* scaler) {
125 if (NULL == fDrawTarget) {
126 return;
127 }
128 if (NULL == fStrike) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000129#if SK_DISTANCEFIELD_FONTS
commit-bot@chromium.org75a22952013-11-21 15:09:33 +0000130 fStrike = fContext->getFontCache()->getStrike(scaler, false);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000131#else
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000132 fStrike = fContext->getFontCache()->getStrike(scaler);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000133#endif
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000134 }
135
136 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
137 if (NULL == glyph || glyph->fBounds.isEmpty()) {
138 return;
139 }
140
141 vx += SkIntToFixed(glyph->fBounds.fLeft);
142 vy += SkIntToFixed(glyph->fBounds.fTop);
143
144 // keep them as ints until we've done the clip-test
145 GrFixed width = glyph->fBounds.width();
146 GrFixed height = glyph->fBounds.height();
147
148 // check if we clipped out
149 if (true || NULL == glyph->fPlot) {
150 int x = vx >> 16;
151 int y = vy >> 16;
152 if (fClipRect.quickReject(x, y, x + width, y + height)) {
153// SkCLZ(3); // so we can set a break-point in the debugger
154 return;
155 }
156 }
157
158 if (NULL == glyph->fPlot) {
159 if (fStrike->getGlyphAtlas(glyph, scaler)) {
160 goto HAS_ATLAS;
161 }
162
163 // try to clear out an unused plot before we flush
164 fContext->getFontCache()->freePlotExceptFor(fStrike);
165 if (fStrike->getGlyphAtlas(glyph, scaler)) {
166 goto HAS_ATLAS;
167 }
168
169 if (c_DumpFontCache) {
170#ifdef SK_DEVELOPER
171 fContext->getFontCache()->dump();
172#endif
173 }
174
175 // before we purge the cache, we must flush any accumulated draws
176 this->flushGlyphs();
177 fContext->flush();
178
179 // try to purge
180 fContext->getFontCache()->purgeExceptFor(fStrike);
181 // need to use new flush count here
182 if (fStrike->getGlyphAtlas(glyph, scaler)) {
183 goto HAS_ATLAS;
184 }
185
186 if (NULL == glyph->fPath) {
187 SkPath* path = SkNEW(SkPath);
188 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
189 // flag the glyph as being dead?
190 delete path;
191 return;
192 }
193 glyph->fPath = path;
194 }
195
196 GrContext::AutoMatrix am;
197 SkMatrix translate;
198 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
199 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
200 GrPaint tmpPaint(fPaint);
201 am.setPreConcat(fContext, translate, &tmpPaint);
202 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
203 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
204 return;
205 }
206
207HAS_ATLAS:
208 SkASSERT(glyph->fPlot);
209 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
210 glyph->fPlot->setDrawToken(drawToken);
211
212 // now promote them to fixed (TODO: Rethink using fixed pt).
213 width = SkIntToFixed(width);
214 height = SkIntToFixed(height);
215
216 GrTexture* texture = glyph->fPlot->texture();
217 SkASSERT(texture);
218
219 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
220 this->flushGlyphs();
221 fCurrTexture = texture;
222 fCurrTexture->ref();
223 }
224
225 if (NULL == fVertices) {
226 // If we need to reserve vertices allow the draw target to suggest
227 // a number of verts to reserve and whether to perform a flush.
228 fMaxVertices = kMinRequestedVerts;
229 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
230 SK_ARRAY_COUNT(gTextVertexAttribs));
231 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
232 if (flush) {
233 this->flushGlyphs();
234 fContext->flush();
235 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
236 SK_ARRAY_COUNT(gTextVertexAttribs));
237 }
238 fMaxVertices = kDefaultRequestedVerts;
239 // ignore return, no point in flushing again.
240 fDrawTarget->geometryHints(&fMaxVertices, NULL);
241
242 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
243 if (fMaxVertices < kMinRequestedVerts) {
244 fMaxVertices = kDefaultRequestedVerts;
245 } else if (fMaxVertices > maxQuadVertices) {
246 // don't exceed the limit of the index buffer
247 fMaxVertices = maxQuadVertices;
248 }
249 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
250 0,
251 GrTCast<void**>(&fVertices),
252 NULL);
253 GrAlwaysAssert(success);
254 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
255 }
256
257 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
258 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
259
260 fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
261 SkFixedToFloat(vy),
262 SkFixedToFloat(vx + width),
263 SkFixedToFloat(vy + height),
264 2 * sizeof(SkPoint));
265 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
266 SkFixedToFloat(texture->normalizeFixedY(ty)),
267 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
268 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
269 2 * sizeof(SkPoint));
270 fCurrVertex += 4;
271}