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