blob: d389ed65d29f3745b6cab2211809c5441e9de432 [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) {
126 fStrike = fContext->getFontCache()->getStrike(scaler);
127 }
128
129 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
130 if (NULL == glyph || glyph->fBounds.isEmpty()) {
131 return;
132 }
133
134 vx += SkIntToFixed(glyph->fBounds.fLeft);
135 vy += SkIntToFixed(glyph->fBounds.fTop);
136
137 // keep them as ints until we've done the clip-test
138 GrFixed width = glyph->fBounds.width();
139 GrFixed height = glyph->fBounds.height();
140
141 // check if we clipped out
142 if (true || NULL == glyph->fPlot) {
143 int x = vx >> 16;
144 int y = vy >> 16;
145 if (fClipRect.quickReject(x, y, x + width, y + height)) {
146// SkCLZ(3); // so we can set a break-point in the debugger
147 return;
148 }
149 }
150
151 if (NULL == glyph->fPlot) {
152 if (fStrike->getGlyphAtlas(glyph, scaler)) {
153 goto HAS_ATLAS;
154 }
155
156 // try to clear out an unused plot before we flush
157 fContext->getFontCache()->freePlotExceptFor(fStrike);
158 if (fStrike->getGlyphAtlas(glyph, scaler)) {
159 goto HAS_ATLAS;
160 }
161
162 if (c_DumpFontCache) {
163#ifdef SK_DEVELOPER
164 fContext->getFontCache()->dump();
165#endif
166 }
167
168 // before we purge the cache, we must flush any accumulated draws
169 this->flushGlyphs();
170 fContext->flush();
171
172 // try to purge
173 fContext->getFontCache()->purgeExceptFor(fStrike);
174 // need to use new flush count here
175 if (fStrike->getGlyphAtlas(glyph, scaler)) {
176 goto HAS_ATLAS;
177 }
178
179 if (NULL == glyph->fPath) {
180 SkPath* path = SkNEW(SkPath);
181 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
182 // flag the glyph as being dead?
183 delete path;
184 return;
185 }
186 glyph->fPath = path;
187 }
188
189 GrContext::AutoMatrix am;
190 SkMatrix translate;
191 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
192 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
193 GrPaint tmpPaint(fPaint);
194 am.setPreConcat(fContext, translate, &tmpPaint);
195 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
196 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
197 return;
198 }
199
200HAS_ATLAS:
201 SkASSERT(glyph->fPlot);
202 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
203 glyph->fPlot->setDrawToken(drawToken);
204
205 // now promote them to fixed (TODO: Rethink using fixed pt).
206 width = SkIntToFixed(width);
207 height = SkIntToFixed(height);
208
209 GrTexture* texture = glyph->fPlot->texture();
210 SkASSERT(texture);
211
212 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
213 this->flushGlyphs();
214 fCurrTexture = texture;
215 fCurrTexture->ref();
216 }
217
218 if (NULL == fVertices) {
219 // If we need to reserve vertices allow the draw target to suggest
220 // a number of verts to reserve and whether to perform a flush.
221 fMaxVertices = kMinRequestedVerts;
222 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
223 SK_ARRAY_COUNT(gTextVertexAttribs));
224 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
225 if (flush) {
226 this->flushGlyphs();
227 fContext->flush();
228 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
229 SK_ARRAY_COUNT(gTextVertexAttribs));
230 }
231 fMaxVertices = kDefaultRequestedVerts;
232 // ignore return, no point in flushing again.
233 fDrawTarget->geometryHints(&fMaxVertices, NULL);
234
235 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
236 if (fMaxVertices < kMinRequestedVerts) {
237 fMaxVertices = kDefaultRequestedVerts;
238 } else if (fMaxVertices > maxQuadVertices) {
239 // don't exceed the limit of the index buffer
240 fMaxVertices = maxQuadVertices;
241 }
242 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
243 0,
244 GrTCast<void**>(&fVertices),
245 NULL);
246 GrAlwaysAssert(success);
247 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
248 }
249
250 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
251 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
252
253 fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
254 SkFixedToFloat(vy),
255 SkFixedToFloat(vx + width),
256 SkFixedToFloat(vy + height),
257 2 * sizeof(SkPoint));
258 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
259 SkFixedToFloat(texture->normalizeFixedY(ty)),
260 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
261 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
262 2 * sizeof(SkPoint));
263 fCurrVertex += 4;
264}