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