blob: 87be8cad373cebfabd3c91becdd1706b8f186a70 [file] [log] [blame]
jvanverth@google.comd830d132013-11-11 20:54:09 +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 "GrDistanceFieldTextContext.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/GrDistanceFieldTextureEffect.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
25
26GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
27 const GrPaint& paint,
28 SkColor color,
29 SkScalar textRatio)
30 : GrTextContext(context, paint)
31 , fTextRatio(textRatio) {
32 fSkPaintColor = color;
33
34 fStrike = NULL;
35
36 fCurrTexture = NULL;
37 fCurrVertex = 0;
38
39 fVertices = NULL;
40 fMaxVertices = 0;
41}
42
43GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
44 this->flushGlyphs();
45}
46
47static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
48 unsigned r = SkColorGetR(c);
49 unsigned g = SkColorGetG(c);
50 unsigned b = SkColorGetB(c);
51 return GrColorPackRGBA(r, g, b, 0xff);
52}
53
54void GrDistanceFieldTextContext::flushGlyphs() {
55 if (NULL == fDrawTarget) {
56 return;
57 }
58
59 GrDrawState* drawState = fDrawTarget->drawState();
60 GrDrawState::AutoRestoreEffects are(drawState);
61 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
62
63 if (fCurrVertex > 0) {
64 // setup our sampler state for our text texture/atlas
65 SkASSERT(GrIsALIGN4(fCurrVertex));
66 SkASSERT(fCurrTexture);
67 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
68
69 // This effect could be stored with one of the cache objects (atlas?)
70 drawState->addCoverageEffect(
71 GrDistanceFieldTextureEffect::Create(fCurrTexture, params),
72 kGlyphCoordsAttributeIndex)->unref();
73
74 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
75 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
76 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
77 fPaint.numColorStages()) {
78 GrPrintf("LCD Text will not draw correctly.\n");
79 }
80 // 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
82 // the mask texture color. The end result is that we get
83 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
84 int a = SkColorGetA(fSkPaintColor);
85 // paintAlpha
86 drawState->setColor(SkColorSetARGB(a, a, a, a));
87 // paintColor
88 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
89 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
90 } 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);
101 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 GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
120 GrFixed vx, GrFixed vy,
121 GrFontScaler* scaler) {
122 if (NULL == fDrawTarget) {
123 return;
124 }
125 if (NULL == fStrike) {
126 fStrike = fContext->getFontCache()->getStrike(scaler, true);
127 }
128
129 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
130 if (NULL == glyph || glyph->fBounds.isEmpty()) {
131 return;
132 }
133
134 SkScalar sx = SkFixedToScalar(vx);
135 SkScalar sy = SkFixedToScalar(vy);
136/*
137 // not valid, need to find a different solution for this
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(sx, sy);
196 GrPaint tmpPaint(fPaint);
197 am.setPreConcat(fContext, translate, &tmpPaint);
198 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
199 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
200 return;
201 }
202
203HAS_ATLAS:
204 SkASSERT(glyph->fPlot);
205 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
206 glyph->fPlot->setDrawToken(drawToken);
207
208 GrTexture* texture = glyph->fPlot->texture();
209 SkASSERT(texture);
210
211 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
212 this->flushGlyphs();
213 fCurrTexture = texture;
214 fCurrTexture->ref();
215 }
216
217 if (NULL == fVertices) {
218 // If we need to reserve vertices allow the draw target to suggest
219 // a number of verts to reserve and whether to perform a flush.
220 fMaxVertices = kMinRequestedVerts;
221 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
222 SK_ARRAY_COUNT(gTextVertexAttribs));
223 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
224 if (flush) {
225 this->flushGlyphs();
226 fContext->flush();
227 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
228 SK_ARRAY_COUNT(gTextVertexAttribs));
229 }
230 fMaxVertices = kDefaultRequestedVerts;
231 // ignore return, no point in flushing again.
232 fDrawTarget->geometryHints(&fMaxVertices, NULL);
233
234 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
235 if (fMaxVertices < kMinRequestedVerts) {
236 fMaxVertices = kDefaultRequestedVerts;
237 } else if (fMaxVertices > maxQuadVertices) {
238 // don't exceed the limit of the index buffer
239 fMaxVertices = maxQuadVertices;
240 }
241 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
242 0,
243 GrTCast<void**>(&fVertices),
244 NULL);
245 GrAlwaysAssert(success);
246 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
247 }
248
249 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
250 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
251 SkScalar width = SkIntToScalar(glyph->fBounds.width());
252 SkScalar height = SkIntToScalar(glyph->fBounds.height());
253
254 SkScalar scale = fTextRatio;
255 dx *= scale;
256 dy *= scale;
257 sx += dx;
258 sy += dy;
259 width *= scale;
260 height *= scale;
261
262 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
263 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
264 GrFixed tw = SkIntToFixed(glyph->fBounds.width());
265 GrFixed th = SkIntToFixed(glyph->fBounds.height());
266
267 fVertices[2*fCurrVertex].setRectFan(sx,
268 sy,
269 sx + width,
270 sy + height,
271 2 * sizeof(SkPoint));
272 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
273 SkFixedToFloat(texture->normalizeFixedY(ty)),
274 SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
275 SkFixedToFloat(texture->normalizeFixedY(ty + th)),
276 2 * sizeof(SkPoint));
277 fCurrVertex += 4;
278}