blob: 7a99f093e5149108163a402a2aa9ee7788dc1f47 [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"
15#include "SkPath.h"
16#include "SkRTConf.h"
17#include "SkStrokeRec.h"
18#include "effects/GrCustomCoordsTextureEffect.h"
19
20static const int kGlyphCoordsAttributeIndex = 1;
21
22SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
23 "Dump the contents of the font cache before every purge.");
24
25GrBitmapTextContext::GrBitmapTextContext(GrContext* context, const GrPaint& paint) :
26 GrTextContext(context, paint) {
27 fAutoMatrix.setIdentity(fContext, &fPaint);
28
29 fStrike = NULL;
30
31 fCurrTexture = NULL;
32 fCurrVertex = 0;
33
34 fVertices = NULL;
35 fMaxVertices = 0;
36}
37
38GrBitmapTextContext::~GrBitmapTextContext() {
39 this->flushGlyphs();
40}
41
42void GrBitmapTextContext::flushGlyphs() {
43 if (NULL == fDrawTarget) {
44 return;
45 }
46
47 GrDrawState* drawState = fDrawTarget->drawState();
48 GrDrawState::AutoRestoreEffects are(drawState);
49 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
50
51 if (fCurrVertex > 0) {
52 // setup our sampler state for our text texture/atlas
53 SkASSERT(GrIsALIGN4(fCurrVertex));
54 SkASSERT(fCurrTexture);
55 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
56
57 // This effect could be stored with one of the cache objects (atlas?)
58 drawState->addCoverageEffect(
59 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
60 kGlyphCoordsAttributeIndex)->unref();
61
62 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
63 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
64 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
65 fPaint.numColorStages()) {
66 GrPrintf("LCD Text will not draw correctly.\n");
67 }
68 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
69 drawState->setBlendConstant(fPaint.getColor());
70 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
71 // don't modulate by the paint's color in the frag since we're
72 // already doing it via the blend const.
73 drawState->setColor(0xffffffff);
74 } else {
75 // set back to normal in case we took LCD path previously.
76 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
77 drawState->setColor(fPaint.getColor());
78 }
79
80 int nGlyphs = fCurrVertex / 4;
81 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
82 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
83 nGlyphs,
84 4, 6);
85 fDrawTarget->resetVertexSource();
86 fVertices = NULL;
87 fMaxVertices = 0;
88 fCurrVertex = 0;
89 SkSafeSetNull(fCurrTexture);
90 }
91}
92
93namespace {
94
95// position + texture coord
96extern const GrVertexAttrib gTextVertexAttribs[] = {
97 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
98 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
99};
100
101};
102
103void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
104 GrFixed vx, GrFixed vy,
105 GrFontScaler* scaler) {
106 if (NULL == fDrawTarget) {
107 return;
108 }
109 if (NULL == fStrike) {
110 fStrike = fContext->getFontCache()->getStrike(scaler);
111 }
112
113 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
114 if (NULL == glyph || glyph->fBounds.isEmpty()) {
115 return;
116 }
117
118 vx += SkIntToFixed(glyph->fBounds.fLeft);
119 vy += SkIntToFixed(glyph->fBounds.fTop);
120
121 // keep them as ints until we've done the clip-test
122 GrFixed width = glyph->fBounds.width();
123 GrFixed height = glyph->fBounds.height();
124
125 // check if we clipped out
126 if (true || NULL == glyph->fPlot) {
127 int x = vx >> 16;
128 int y = vy >> 16;
129 if (fClipRect.quickReject(x, y, x + width, y + height)) {
130// SkCLZ(3); // so we can set a break-point in the debugger
131 return;
132 }
133 }
134
135 if (NULL == glyph->fPlot) {
136 if (fStrike->getGlyphAtlas(glyph, scaler)) {
137 goto HAS_ATLAS;
138 }
139
140 // try to clear out an unused plot before we flush
141 fContext->getFontCache()->freePlotExceptFor(fStrike);
142 if (fStrike->getGlyphAtlas(glyph, scaler)) {
143 goto HAS_ATLAS;
144 }
145
146 if (c_DumpFontCache) {
147#ifdef SK_DEVELOPER
148 fContext->getFontCache()->dump();
149#endif
150 }
151
152 // before we purge the cache, we must flush any accumulated draws
153 this->flushGlyphs();
154 fContext->flush();
155
156 // try to purge
157 fContext->getFontCache()->purgeExceptFor(fStrike);
158 // need to use new flush count here
159 if (fStrike->getGlyphAtlas(glyph, scaler)) {
160 goto HAS_ATLAS;
161 }
162
163 if (NULL == glyph->fPath) {
164 SkPath* path = SkNEW(SkPath);
165 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
166 // flag the glyph as being dead?
167 delete path;
168 return;
169 }
170 glyph->fPath = path;
171 }
172
173 GrContext::AutoMatrix am;
174 SkMatrix translate;
175 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
176 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
177 GrPaint tmpPaint(fPaint);
178 am.setPreConcat(fContext, translate, &tmpPaint);
179 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
180 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
181 return;
182 }
183
184HAS_ATLAS:
185 SkASSERT(glyph->fPlot);
186 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
187 glyph->fPlot->setDrawToken(drawToken);
188
189 // now promote them to fixed (TODO: Rethink using fixed pt).
190 width = SkIntToFixed(width);
191 height = SkIntToFixed(height);
192
193 GrTexture* texture = glyph->fPlot->texture();
194 SkASSERT(texture);
195
196 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;
206 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
207 SK_ARRAY_COUNT(gTextVertexAttribs));
208 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
209 if (flush) {
210 this->flushGlyphs();
211 fContext->flush();
212 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
213 SK_ARRAY_COUNT(gTextVertexAttribs));
214 }
215 fMaxVertices = kDefaultRequestedVerts;
216 // ignore return, no point in flushing again.
217 fDrawTarget->geometryHints(&fMaxVertices, NULL);
218
219 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
220 if (fMaxVertices < kMinRequestedVerts) {
221 fMaxVertices = kDefaultRequestedVerts;
222 } else if (fMaxVertices > maxQuadVertices) {
223 // don't exceed the limit of the index buffer
224 fMaxVertices = maxQuadVertices;
225 }
226 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
227 0,
228 GrTCast<void**>(&fVertices),
229 NULL);
230 GrAlwaysAssert(success);
231 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
232 }
233
234 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
235 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
236
237 fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
238 SkFixedToFloat(vy),
239 SkFixedToFloat(vx + width),
240 SkFixedToFloat(vy + height),
241 2 * sizeof(SkPoint));
242 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
243 SkFixedToFloat(texture->normalizeFixedY(ty)),
244 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
245 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
246 2 * sizeof(SkPoint));
247 fCurrVertex += 4;
248}