blob: 433eda148a5b993c7303765cb4bfbc34e1d88cb1 [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
73 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
74 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
75 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
76 fPaint.numColorStages()) {
77 GrPrintf("LCD Text will not draw correctly.\n");
78 }
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000079 // We don't use the GrPaint's color in this case because it's been premultiplied by
80 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
skia.committer@gmail.com70402c32013-10-29 07:01:50 +000081 // the mask texture color. The end result is that we get
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000082 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
83 int a = SkColorGetA(fSkPaintColor);
84 // paintAlpha
85 drawState->setColor(SkColorSetARGB(a, a, a, a));
86 // paintColor
87 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000088 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000089 } else {
90 // set back to normal in case we took LCD path previously.
91 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
92 drawState->setColor(fPaint.getColor());
93 }
94
95 int nGlyphs = fCurrVertex / 4;
96 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
97 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
98 nGlyphs,
99 4, 6);
commit-bot@chromium.org42a89572013-10-28 15:13:50 +0000100
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000101 fDrawTarget->resetVertexSource();
102 fVertices = NULL;
103 fMaxVertices = 0;
104 fCurrVertex = 0;
105 SkSafeSetNull(fCurrTexture);
106 }
107}
108
109namespace {
110
111// position + texture coord
112extern const GrVertexAttrib gTextVertexAttribs[] = {
113 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
114 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
115};
116
117};
118
119void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
120 GrFixed vx, GrFixed vy,
121 GrFontScaler* scaler) {
122 if (NULL == fDrawTarget) {
123 return;
124 }
125 if (NULL == fStrike) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000126#if SK_DISTANCEFIELD_FONTS
127 fStrike = fContext->getFontCache()->getStrike(scaler, true);
128#else
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000129 fStrike = fContext->getFontCache()->getStrike(scaler);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000130#endif
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000131 }
132
133 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
134 if (NULL == glyph || glyph->fBounds.isEmpty()) {
135 return;
136 }
137
138 vx += SkIntToFixed(glyph->fBounds.fLeft);
139 vy += SkIntToFixed(glyph->fBounds.fTop);
140
141 // keep them as ints until we've done the clip-test
142 GrFixed width = glyph->fBounds.width();
143 GrFixed height = glyph->fBounds.height();
144
145 // check if we clipped out
146 if (true || NULL == glyph->fPlot) {
147 int x = vx >> 16;
148 int y = vy >> 16;
149 if (fClipRect.quickReject(x, y, x + width, y + height)) {
150// SkCLZ(3); // so we can set a break-point in the debugger
151 return;
152 }
153 }
154
155 if (NULL == glyph->fPlot) {
156 if (fStrike->getGlyphAtlas(glyph, scaler)) {
157 goto HAS_ATLAS;
158 }
159
160 // try to clear out an unused plot before we flush
161 fContext->getFontCache()->freePlotExceptFor(fStrike);
162 if (fStrike->getGlyphAtlas(glyph, scaler)) {
163 goto HAS_ATLAS;
164 }
165
166 if (c_DumpFontCache) {
167#ifdef SK_DEVELOPER
168 fContext->getFontCache()->dump();
169#endif
170 }
171
172 // before we purge the cache, we must flush any accumulated draws
173 this->flushGlyphs();
174 fContext->flush();
175
176 // try to purge
177 fContext->getFontCache()->purgeExceptFor(fStrike);
178 // need to use new flush count here
179 if (fStrike->getGlyphAtlas(glyph, scaler)) {
180 goto HAS_ATLAS;
181 }
182
183 if (NULL == glyph->fPath) {
184 SkPath* path = SkNEW(SkPath);
185 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
186 // flag the glyph as being dead?
187 delete path;
188 return;
189 }
190 glyph->fPath = path;
191 }
192
193 GrContext::AutoMatrix am;
194 SkMatrix translate;
195 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
196 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
197 GrPaint tmpPaint(fPaint);
198 am.setPreConcat(fContext, translate, &tmpPaint);
199 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
200 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
201 return;
202 }
203
204HAS_ATLAS:
205 SkASSERT(glyph->fPlot);
206 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
207 glyph->fPlot->setDrawToken(drawToken);
208
209 // now promote them to fixed (TODO: Rethink using fixed pt).
210 width = SkIntToFixed(width);
211 height = SkIntToFixed(height);
212
213 GrTexture* texture = glyph->fPlot->texture();
214 SkASSERT(texture);
215
216 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
217 this->flushGlyphs();
218 fCurrTexture = texture;
219 fCurrTexture->ref();
220 }
221
222 if (NULL == fVertices) {
223 // If we need to reserve vertices allow the draw target to suggest
224 // a number of verts to reserve and whether to perform a flush.
225 fMaxVertices = kMinRequestedVerts;
226 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
227 SK_ARRAY_COUNT(gTextVertexAttribs));
228 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
229 if (flush) {
230 this->flushGlyphs();
231 fContext->flush();
232 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
233 SK_ARRAY_COUNT(gTextVertexAttribs));
234 }
235 fMaxVertices = kDefaultRequestedVerts;
236 // ignore return, no point in flushing again.
237 fDrawTarget->geometryHints(&fMaxVertices, NULL);
238
239 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
240 if (fMaxVertices < kMinRequestedVerts) {
241 fMaxVertices = kDefaultRequestedVerts;
242 } else if (fMaxVertices > maxQuadVertices) {
243 // don't exceed the limit of the index buffer
244 fMaxVertices = maxQuadVertices;
245 }
246 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
247 0,
248 GrTCast<void**>(&fVertices),
249 NULL);
250 GrAlwaysAssert(success);
251 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
252 }
253
254 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
255 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
256
257 fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
258 SkFixedToFloat(vy),
259 SkFixedToFloat(vx + width),
260 SkFixedToFloat(vy + height),
261 2 * sizeof(SkPoint));
262 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
263 SkFixedToFloat(texture->normalizeFixedY(ty)),
264 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
265 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
266 2 * sizeof(SkPoint));
267 fCurrVertex += 4;
268}