Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrDistanceFieldTextContext.cpp
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 "GrDistanceFieldTextContext.h"
9 #include "GrAtlas.h"
10 #include "GrDrawTarget.h"
11 #include "GrFontScaler.h"
12 #include "SkGlyphCache.h"
13 #include "GrIndexBuffer.h"
14 #include "GrTextStrike.h"
15 #include "GrTextStrike_impl.h"
16 #include "SkDraw.h"
17 #include "SkGpuDevice.h"
18 #include "SkPath.h"
19 #include "SkRTConf.h"
20 #include "SkStrokeRec.h"
21 #include "effects/GrDistanceFieldTextureEffect.h"
22
23 static const int kGlyphCoordsAttributeIndex = 1;
24
25 static const int kBaseDFFontSize = 32;
26
27 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
28                 "Dump the contents of the font cache before every purge.");
29
30 GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
31                                                        const SkDeviceProperties& properties)
32                                                     : GrTextContext(context, properties) {
33     fStrike = NULL;
34
35     fCurrTexture = NULL;
36     fCurrVertex = 0;
37
38     fVertices = NULL;
39     fMaxVertices = 0;
40 }
41
42 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
43     this->flushGlyphs();
44 }
45
46 bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
47     return !paint.getRasterizer() && !paint.getMaskFilter() &&
48            paint.getStyle() == SkPaint::kFill_Style &&
49            !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
50 }
51
52 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
53     unsigned r = SkColorGetR(c);
54     unsigned g = SkColorGetG(c);
55     unsigned b = SkColorGetB(c);
56     return GrColorPackRGBA(r, g, b, 0xff);
57 }
58
59 void GrDistanceFieldTextContext::flushGlyphs() {
60     if (NULL == fDrawTarget) {
61         return;
62     }
63
64     GrDrawState* drawState = fDrawTarget->drawState();
65     GrDrawState::AutoRestoreEffects are(drawState);
66     drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
67
68     if (fCurrVertex > 0) {
69         // setup our sampler state for our text texture/atlas
70         SkASSERT(GrIsALIGN4(fCurrVertex));
71         SkASSERT(fCurrTexture);
72         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
73
74         // This effect could be stored with one of the cache objects (atlas?)
75         drawState->addCoverageEffect(
76                                 GrDistanceFieldTextureEffect::Create(fCurrTexture, params),
77                                 kGlyphCoordsAttributeIndex)->unref();
78
79         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
80             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
81                 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
82                 fPaint.numColorStages()) {
83                 GrPrintf("LCD Text will not draw correctly.\n");
84             }
85             // We don't use the GrPaint's color in this case because it's been premultiplied by
86             // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
87             // the mask texture color. The end result is that we get
88             //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
89             int a = SkColorGetA(fSkPaint.getColor());
90             // paintAlpha
91             drawState->setColor(SkColorSetARGB(a, a, a, a));
92             // paintColor
93             drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
94             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
95         } else {
96             // set back to normal in case we took LCD path previously.
97             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
98             drawState->setColor(fPaint.getColor());
99         }
100
101         int nGlyphs = fCurrVertex / 4;
102         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
103         fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
104                                           nGlyphs,
105                                           4, 6);
106         fDrawTarget->resetVertexSource();
107         fVertices = NULL;
108         fMaxVertices = 0;
109         fCurrVertex = 0;
110         SkSafeSetNull(fCurrTexture);
111     }
112 }
113
114 namespace {
115
116 // position + texture coord
117 extern const GrVertexAttrib gTextVertexAttribs[] = {
118     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
119     {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
120 };
121
122 };
123
124 void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
125                                                  GrFixed vx, GrFixed vy,
126                                                  GrFontScaler* scaler) {
127     if (NULL == fDrawTarget) {
128         return;
129     }
130     if (NULL == fStrike) {
131         fStrike = fContext->getFontCache()->getStrike(scaler, true);
132     }
133
134     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
135     if (NULL == glyph || glyph->fBounds.isEmpty()) {
136         return;
137     }
138
139     SkScalar sx = SkFixedToScalar(vx);
140     SkScalar sy = SkFixedToScalar(vy);
141 /*
142     // not valid, need to find a different solution for this
143     vx += SkIntToFixed(glyph->fBounds.fLeft);
144     vy += SkIntToFixed(glyph->fBounds.fTop);
145
146     // keep them as ints until we've done the clip-test
147     GrFixed width = glyph->fBounds.width();
148     GrFixed height = glyph->fBounds.height();
149
150     // check if we clipped out
151     if (true || NULL == glyph->fPlot) {
152         int x = vx >> 16;
153         int y = vy >> 16;
154         if (fClipRect.quickReject(x, y, x + width, y + height)) {
155 //            SkCLZ(3);    // so we can set a break-point in the debugger
156             return;
157         }
158     }
159 */
160     if (NULL == glyph->fPlot) {
161         if (fStrike->getGlyphAtlas(glyph, scaler)) {
162             goto HAS_ATLAS;
163         }
164
165         // try to clear out an unused plot before we flush
166         fContext->getFontCache()->freePlotExceptFor(fStrike);
167         if (fStrike->getGlyphAtlas(glyph, scaler)) {
168             goto HAS_ATLAS;
169         }
170
171         if (c_DumpFontCache) {
172 #ifdef SK_DEVELOPER
173             fContext->getFontCache()->dump();
174 #endif
175         }
176
177         // before we purge the cache, we must flush any accumulated draws
178         this->flushGlyphs();
179         fContext->flush();
180
181         // try to purge
182         fContext->getFontCache()->purgeExceptFor(fStrike);
183         // need to use new flush count here
184         if (fStrike->getGlyphAtlas(glyph, scaler)) {
185             goto HAS_ATLAS;
186         }
187
188         if (NULL == glyph->fPath) {
189             SkPath* path = SkNEW(SkPath);
190             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
191                 // flag the glyph as being dead?
192                 delete path;
193                 return;
194             }
195             glyph->fPath = path;
196         }
197
198         GrContext::AutoMatrix am;
199         SkMatrix translate;
200         translate.setTranslate(sx, sy);
201         GrPaint tmpPaint(fPaint);
202         am.setPreConcat(fContext, translate, &tmpPaint);
203         SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
204         fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
205         return;
206     }
207
208 HAS_ATLAS:
209     SkASSERT(glyph->fPlot);
210     GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
211     glyph->fPlot->setDrawToken(drawToken);
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     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
255     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
256     SkScalar width = SkIntToScalar(glyph->fBounds.width());
257     SkScalar height = SkIntToScalar(glyph->fBounds.height());
258
259     SkScalar scale = fTextRatio;
260     dx *= scale;
261     dy *= scale;
262     sx += dx;
263     sy += dy;
264     width *= scale;
265     height *= scale;
266
267     GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
268     GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
269     GrFixed tw = SkIntToFixed(glyph->fBounds.width());
270     GrFixed th = SkIntToFixed(glyph->fBounds.height());
271
272     fVertices[2*fCurrVertex].setRectFan(sx,
273                                         sy,
274                                         sx + width,
275                                         sy + height,
276                                         2 * sizeof(SkPoint));
277     fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
278                                           SkFixedToFloat(texture->normalizeFixedY(ty)),
279                                           SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
280                                           SkFixedToFloat(texture->normalizeFixedY(ty + th)),
281                                           2 * sizeof(SkPoint));
282     fCurrVertex += 4;
283 }
284
285 inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
286     GrTextContext::init(paint, skPaint);
287
288     fStrike = NULL;
289
290     fCurrTexture = NULL;
291     fCurrVertex = 0;
292
293     fVertices = NULL;
294     fMaxVertices = 0;
295
296     fTextRatio = fSkPaint.getTextSize()/kBaseDFFontSize;
297
298     fSkPaint.setTextSize(SkIntToScalar(kBaseDFFontSize));
299     fSkPaint.setLCDRenderText(false);
300     fSkPaint.setAutohinted(false);
301     fSkPaint.setSubpixelText(false);
302 }
303
304 inline void GrDistanceFieldTextContext::finish() {
305     flushGlyphs();
306
307     GrTextContext::finish();
308 }
309
310 void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
311                                           const char text[], size_t byteLength,
312                                           SkScalar x, SkScalar y) {
313     SkASSERT(byteLength == 0 || text != NULL);
314
315     // nothing to draw or can't draw
316     if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
317         || fSkPaint.getRasterizer()) {
318         return;
319     }
320
321     this->init(paint, skPaint);
322
323     SkScalar sizeRatio = fTextRatio;
324
325     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
326
327     SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, NULL);
328     SkGlyphCache*       cache = autoCache.getCache();
329     GrFontScaler*       fontScaler = GetGrFontScaler(cache);
330
331     // need to measure first
332     // TODO - generate positions and pre-load cache as well?
333     const char* stop = text + byteLength;
334     if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
335         SkFixed    stopX = 0;
336         SkFixed    stopY = 0;
337
338         const char* textPtr = text;
339         while (textPtr < stop) {
340             // don't need x, y here, since all subpixel variants will have the
341             // same advance
342             const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
343
344             stopX += glyph.fAdvanceX;
345             stopY += glyph.fAdvanceY;
346         }
347         SkASSERT(textPtr == stop);
348
349         SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
350         SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
351
352         if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
353             alignX = SkScalarHalf(alignX);
354             alignY = SkScalarHalf(alignY);
355         }
356
357         x -= alignX;
358         y -= alignY;
359     }
360
361     SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf;
362     SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf;
363     SkFixed fixedScale = SkScalarToFixed(sizeRatio);
364     while (text < stop) {
365         const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy);
366
367         if (glyph.fWidth) {
368             this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
369                                                 glyph.getSubXFixed(),
370                                                 glyph.getSubYFixed()),
371                                   SkFixedFloorToFixed(fx),
372                                   SkFixedFloorToFixed(fy),
373                                   fontScaler);
374         }
375
376         fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
377         fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
378     }
379
380     this->finish();
381 }
382
383 void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
384                                              const char text[], size_t byteLength,
385                                              const SkScalar pos[], SkScalar constY,
386                                              int scalarsPerPosition) {
387
388     SkASSERT(byteLength == 0 || text != NULL);
389     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
390
391     // nothing to draw
392     if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
393         return;
394     }
395
396     this->init(paint, skPaint);
397
398     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
399
400     SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, NULL);
401     SkGlyphCache*       cache = autoCache.getCache();
402     GrFontScaler*       fontScaler = GetGrFontScaler(cache);
403
404     const char*        stop = text + byteLength;
405
406     if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
407         while (text < stop) {
408             // the last 2 parameters are ignored
409             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
410
411             if (glyph.fWidth) {
412                 SkScalar x = pos[0];
413                 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
414
415                 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
416                                                     glyph.getSubXFixed(),
417                                                     glyph.getSubYFixed()),
418                                       SkScalarToFixed(x) + SK_FixedHalf, //d1g.fHalfSampleX,
419                                       SkScalarToFixed(y) + SK_FixedHalf, //d1g.fHalfSampleY,
420                                       fontScaler);
421             }
422             pos += scalarsPerPosition;
423         }
424     } else {
425         int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
426         while (text < stop) {
427             // the last 2 parameters are ignored
428             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
429
430             if (glyph.fWidth) {
431                 SkScalar x = pos[0];
432                 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
433
434                 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
435                                                     glyph.getSubXFixed(),
436                                                     glyph.getSubYFixed()),
437                                       SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift)
438                                         + SK_FixedHalf, //d1g.fHalfSampleX,
439                                       SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift)
440                                         + SK_FixedHalf, //d1g.fHalfSampleY,
441                                       fontScaler);
442             }
443             pos += scalarsPerPosition;
444         }
445     }
446
447     this->finish();
448 }