Upstream version 8.37.180.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 "SkColorFilter.h"
11 #include "GrDrawTarget.h"
12 #include "GrDrawTargetCaps.h"
13 #include "GrFontScaler.h"
14 #include "SkGlyphCache.h"
15 #include "GrGpu.h"
16 #include "GrIndexBuffer.h"
17 #include "GrStrokeInfo.h"
18 #include "GrTextStrike.h"
19 #include "GrTextStrike_impl.h"
20 #include "SkDistanceFieldGen.h"
21 #include "SkDraw.h"
22 #include "SkGpuDevice.h"
23 #include "SkPath.h"
24 #include "SkRTConf.h"
25 #include "SkStrokeRec.h"
26 #include "effects/GrDistanceFieldTextureEffect.h"
27
28 static const int kGlyphCoordsAttributeIndex = 1;
29
30 static const int kSmallDFFontSize = 32;
31 static const int kSmallDFFontLimit = 32;
32 static const int kMediumDFFontSize = 64;
33 static const int kMediumDFFontLimit = 64;
34 static const int kLargeDFFontSize = 128;
35
36 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
37                 "Dump the contents of the font cache before every purge.");
38
39 GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
40                                                        const SkDeviceProperties& properties,
41                                                        bool enable)
42                                                     : GrTextContext(context, properties) {
43 #if SK_FORCE_DISTANCEFIELD_FONTS
44     fEnableDFRendering = true;
45 #else
46     fEnableDFRendering = enable;
47 #endif
48     fStrike = NULL;
49     fGammaTexture = NULL;
50
51     fCurrTexture = NULL;
52     fCurrVertex = 0;
53
54     fVertices = NULL;
55     fMaxVertices = 0;
56 }
57
58 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
59     this->flushGlyphs();
60     SkSafeSetNull(fGammaTexture);
61 }
62
63 bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
64     if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
65         return false;
66     }
67
68     // rasterizers and mask filters modify alpha, which doesn't
69     // translate well to distance
70     if (paint.getRasterizer() || paint.getMaskFilter() ||
71         !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
72         return false;
73     }
74
75     // TODO: add some stroking support
76     if (paint.getStyle() != SkPaint::kFill_Style) {
77         return false;
78     }
79
80     // TODO: choose an appropriate maximum scale for distance fields and
81     //       enable perspective
82     if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
83         return false;
84     }
85
86     // distance fields cannot represent color fonts
87     SkScalerContext::Rec    rec;
88     SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
89     return rec.getFormat() != SkMask::kARGB32_Format;
90 }
91
92 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
93     unsigned r = SkColorGetR(c);
94     unsigned g = SkColorGetG(c);
95     unsigned b = SkColorGetB(c);
96     return GrColorPackRGBA(r, g, b, 0xff);
97 }
98
99 void GrDistanceFieldTextContext::flushGlyphs() {
100     if (NULL == fDrawTarget) {
101         return;
102     }
103
104     GrDrawState* drawState = fDrawTarget->drawState();
105     GrDrawState::AutoRestoreEffects are(drawState);
106     drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
107
108     if (fCurrVertex > 0) {
109         // setup our sampler state for our text texture/atlas
110         SkASSERT(SkIsAlign4(fCurrVertex));
111         SkASSERT(fCurrTexture);
112         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
113         GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
114
115         // Effects could be stored with one of the cache objects (atlas?)
116         SkColor filteredColor;
117         SkColorFilter* colorFilter = fSkPaint.getColorFilter();
118         if (NULL != colorFilter) {
119             filteredColor = colorFilter->filterColor(fSkPaint.getColor());
120         } else {
121             filteredColor = fSkPaint.getColor();
122         }
123         if (fUseLCDText) {
124             GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
125             bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
126                                                             fDeviceProperties.fGeometry.getLayout();
127             drawState->addCoverageEffect(GrDistanceFieldLCDTextureEffect::Create(
128                                                             fCurrTexture,
129                                                             params,
130                                                             fGammaTexture,
131                                                             gammaParams,
132                                                             colorNoPreMul,
133                                                             fContext->getMatrix().rectStaysRect() &&
134                                                             fContext->getMatrix().isSimilarity(),
135                                                             useBGR),
136                                          kGlyphCoordsAttributeIndex)->unref();
137
138             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
139                 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
140                 fPaint.numColorStages()) {
141                 GrPrintf("LCD Text will not draw correctly.\n");
142             }
143             // We don't use the GrPaint's color in this case because it's been premultiplied by
144             // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
145             // the mask texture color. The end result is that we get
146             //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
147             int a = SkColorGetA(fSkPaint.getColor());
148             // paintAlpha
149             drawState->setColor(SkColorSetARGB(a, a, a, a));
150             // paintColor
151             drawState->setBlendConstant(colorNoPreMul);
152             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
153         } else {
154 #ifdef SK_GAMMA_APPLY_TO_A8
155             U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
156                                                                 filteredColor);
157             drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
158                                                               fCurrTexture, params,
159                                                               fGammaTexture, gammaParams,
160                                                               lum/255.f,
161                                                               fContext->getMatrix().isSimilarity()),
162                                          kGlyphCoordsAttributeIndex)->unref();
163 #else
164             drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
165                                                               fCurrTexture, params,
166                                                               fContext->getMatrix().isSimilarity()),
167                                          kGlyphCoordsAttributeIndex)->unref();
168 #endif
169             // set back to normal in case we took LCD path previously.
170             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
171             drawState->setColor(fPaint.getColor());
172         }
173
174         int nGlyphs = fCurrVertex / 4;
175         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
176         fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
177                                           nGlyphs,
178                                           4, 6);
179         fDrawTarget->resetVertexSource();
180         fVertices = NULL;
181         fMaxVertices = 0;
182         fCurrVertex = 0;
183         SkSafeSetNull(fCurrTexture);
184     }
185 }
186
187 namespace {
188
189 // position + texture coord
190 extern const GrVertexAttrib gTextVertexAttribs[] = {
191     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
192     {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}
193 };
194
195 };
196
197 void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
198                                                  SkFixed vx, SkFixed vy,
199                                                  GrFontScaler* scaler) {
200     if (NULL == fDrawTarget) {
201         return;
202     }
203     if (NULL == fStrike) {
204         fStrike = fContext->getFontCache()->getStrike(scaler, true);
205     }
206
207     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
208     if (NULL == glyph || glyph->fBounds.isEmpty()) {
209         return;
210     }
211
212     SkScalar sx = SkFixedToScalar(vx);
213     SkScalar sy = SkFixedToScalar(vy);
214 /*
215     // not valid, need to find a different solution for this
216     vx += SkIntToFixed(glyph->fBounds.fLeft);
217     vy += SkIntToFixed(glyph->fBounds.fTop);
218
219     // keep them as ints until we've done the clip-test
220     GrFixed width = glyph->fBounds.width();
221     GrFixed height = glyph->fBounds.height();
222
223     // check if we clipped out
224     if (true || NULL == glyph->fPlot) {
225         int x = vx >> 16;
226         int y = vy >> 16;
227         if (fClipRect.quickReject(x, y, x + width, y + height)) {
228 //            SkCLZ(3);    // so we can set a break-point in the debugger
229             return;
230         }
231     }
232 */
233     if (NULL == glyph->fPlot) {
234         if (fStrike->addGlyphToAtlas(glyph, scaler)) {
235             goto HAS_ATLAS;
236         }
237
238         // try to clear out an unused plot before we flush
239         if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
240             fStrike->addGlyphToAtlas(glyph, scaler)) {
241             goto HAS_ATLAS;
242         }
243
244         if (c_DumpFontCache) {
245 #ifdef SK_DEVELOPER
246             fContext->getFontCache()->dump();
247 #endif
248         }
249
250         // before we purge the cache, we must flush any accumulated draws
251         this->flushGlyphs();
252         fContext->flush();
253
254         // we should have an unused plot now
255         if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
256             fStrike->addGlyphToAtlas(glyph, scaler)) {
257             goto HAS_ATLAS;
258         }
259
260         if (NULL == glyph->fPath) {
261             SkPath* path = SkNEW(SkPath);
262             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
263                 // flag the glyph as being dead?
264                 delete path;
265                 return;
266             }
267             glyph->fPath = path;
268         }
269
270         GrContext::AutoMatrix am;
271         SkMatrix ctm;
272         ctm.setScale(fTextRatio, fTextRatio);
273         ctm.postTranslate(sx, sy);
274         GrPaint tmpPaint(fPaint);
275         am.setPreConcat(fContext, ctm, &tmpPaint);
276         GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
277         fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
278         return;
279     }
280
281 HAS_ATLAS:
282     SkASSERT(glyph->fPlot);
283     GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
284     glyph->fPlot->setDrawToken(drawToken);
285
286     GrTexture* texture = glyph->fPlot->texture();
287     SkASSERT(texture);
288
289     if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
290         this->flushGlyphs();
291         fCurrTexture = texture;
292         fCurrTexture->ref();
293     }
294
295     if (NULL == fVertices) {
296        // If we need to reserve vertices allow the draw target to suggest
297         // a number of verts to reserve and whether to perform a flush.
298         fMaxVertices = kMinRequestedVerts;
299         fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
300             SK_ARRAY_COUNT(gTextVertexAttribs));
301         bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
302         if (flush) {
303             this->flushGlyphs();
304             fContext->flush();
305             fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
306                 SK_ARRAY_COUNT(gTextVertexAttribs));
307         }
308         fMaxVertices = kDefaultRequestedVerts;
309         // ignore return, no point in flushing again.
310         fDrawTarget->geometryHints(&fMaxVertices, NULL);
311
312         int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
313         if (fMaxVertices < kMinRequestedVerts) {
314             fMaxVertices = kDefaultRequestedVerts;
315         } else if (fMaxVertices > maxQuadVertices) {
316             // don't exceed the limit of the index buffer
317             fMaxVertices = maxQuadVertices;
318         }
319         bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
320                                                                0,
321                                                                GrTCast<void**>(&fVertices),
322                                                                NULL);
323         GrAlwaysAssert(success);
324         SkASSERT(2*sizeof(SkPoint) == fDrawTarget->getDrawState().getVertexSize());
325     }
326
327     SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
328     SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
329     SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
330     SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
331
332     SkScalar scale = fTextRatio;
333     dx *= scale;
334     dy *= scale;
335     sx += dx;
336     sy += dy;
337     width *= scale;
338     height *= scale;
339
340     SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
341     SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
342     SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
343     SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
344
345     static const size_t kVertexSize = 2 * sizeof(SkPoint);
346     fVertices[2*fCurrVertex].setRectFan(sx,
347                                         sy,
348                                         sx + width,
349                                         sy + height,
350                                         kVertexSize);
351     fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
352                                           SkFixedToFloat(texture->normalizeFixedY(ty)),
353                                           SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
354                                           SkFixedToFloat(texture->normalizeFixedY(ty + th)),
355                                           kVertexSize);
356     fCurrVertex += 4;
357 }
358
359 inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
360     GrTextContext::init(paint, skPaint);
361
362     fStrike = NULL;
363
364     fCurrTexture = NULL;
365     fCurrVertex = 0;
366
367     fVertices = NULL;
368     fMaxVertices = 0;
369
370     if (fSkPaint.getTextSize() <= kSmallDFFontLimit) {
371         fTextRatio = fSkPaint.getTextSize()/kSmallDFFontSize;
372         fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
373     } else if (fSkPaint.getTextSize() <= kMediumDFFontLimit) {
374         fTextRatio = fSkPaint.getTextSize()/kMediumDFFontSize;
375         fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
376     } else {
377         fTextRatio = fSkPaint.getTextSize()/kLargeDFFontSize;
378         fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
379     }
380
381     fUseLCDText = fSkPaint.isLCDRenderText();
382
383     fSkPaint.setLCDRenderText(false);
384     fSkPaint.setAutohinted(false);
385     fSkPaint.setHinting(SkPaint::kNormal_Hinting);
386     fSkPaint.setSubpixelText(true);
387
388 }
389
390 inline void GrDistanceFieldTextContext::finish() {
391     flushGlyphs();
392
393     GrTextContext::finish();
394 }
395
396 static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
397                                 const SkDeviceProperties& deviceProperties,
398                                 GrTexture** gammaTexture) {
399     if (NULL == *gammaTexture) {
400         int width, height;
401         size_t size;
402
403 #ifdef SK_GAMMA_CONTRAST
404         SkScalar contrast = SK_GAMMA_CONTRAST;
405 #else
406         SkScalar contrast = 0.5f;
407 #endif
408         SkScalar paintGamma = deviceProperties.fGamma;
409         SkScalar deviceGamma = deviceProperties.fGamma;
410
411         size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
412                                                 &width, &height);
413
414         SkAutoTArray<uint8_t> data((int)size);
415         SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
416
417         // TODO: Update this to use the cache rather than directly creating a texture.
418         GrTextureDesc desc;
419         desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
420         desc.fWidth = width;
421         desc.fHeight = height;
422         desc.fConfig = kAlpha_8_GrPixelConfig;
423
424         *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
425         if (NULL == *gammaTexture) {
426             return;
427         }
428
429         context->writeTexturePixels(*gammaTexture,
430                                     0, 0, width, height,
431                                     (*gammaTexture)->config(), data.get(), 0,
432                                     GrContext::kDontFlush_PixelOpsFlag);
433     }
434 }
435
436 void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
437                                           const char text[], size_t byteLength,
438                                           SkScalar x, SkScalar y) {
439     SkASSERT(byteLength == 0 || text != NULL);
440
441     // nothing to draw or can't draw
442     if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
443         || fSkPaint.getRasterizer()) {
444         return;
445     }
446
447     this->init(paint, skPaint);
448
449     SkScalar sizeRatio = fTextRatio;
450
451     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
452
453     SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
454     SkGlyphCache*              cache = autoCache.getCache();
455     GrFontScaler*              fontScaler = GetGrFontScaler(cache);
456
457     setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
458
459     // need to measure first
460     // TODO - generate positions and pre-load cache as well?
461     const char* stop = text + byteLength;
462     if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
463         SkFixed    stopX = 0;
464         SkFixed    stopY = 0;
465
466         const char* textPtr = text;
467         while (textPtr < stop) {
468             // don't need x, y here, since all subpixel variants will have the
469             // same advance
470             const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
471
472             stopX += glyph.fAdvanceX;
473             stopY += glyph.fAdvanceY;
474         }
475         SkASSERT(textPtr == stop);
476
477         SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
478         SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
479
480         if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
481             alignX = SkScalarHalf(alignX);
482             alignY = SkScalarHalf(alignY);
483         }
484
485         x -= alignX;
486         y -= alignY;
487     }
488
489     SkFixed fx = SkScalarToFixed(x);
490     SkFixed fy = SkScalarToFixed(y);
491     SkFixed fixedScale = SkScalarToFixed(sizeRatio);
492     while (text < stop) {
493         const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
494
495         if (glyph.fWidth) {
496             this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
497                                                 glyph.getSubXFixed(),
498                                                 glyph.getSubYFixed()),
499                                   fx,
500                                   fy,
501                                   fontScaler);
502         }
503
504         fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
505         fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
506     }
507
508     this->finish();
509 }
510
511 void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
512                                              const char text[], size_t byteLength,
513                                              const SkScalar pos[], SkScalar constY,
514                                              int scalarsPerPosition) {
515
516     SkASSERT(byteLength == 0 || text != NULL);
517     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
518
519     // nothing to draw
520     if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
521         return;
522     }
523
524     this->init(paint, skPaint);
525
526     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
527
528     SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
529     SkGlyphCache*              cache = autoCache.getCache();
530     GrFontScaler*              fontScaler = GetGrFontScaler(cache);
531
532     setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
533
534     const char*        stop = text + byteLength;
535
536     if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
537         while (text < stop) {
538             // the last 2 parameters are ignored
539             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
540
541             if (glyph.fWidth) {
542                 SkScalar x = pos[0];
543                 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
544
545                 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
546                                                     glyph.getSubXFixed(),
547                                                     glyph.getSubYFixed()),
548                                       SkScalarToFixed(x),
549                                       SkScalarToFixed(y),
550                                       fontScaler);
551             }
552             pos += scalarsPerPosition;
553         }
554     } else {
555         int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
556         while (text < stop) {
557             // the last 2 parameters are ignored
558             const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
559
560             if (glyph.fWidth) {
561                 SkScalar x = pos[0];
562                 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
563
564                 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
565                                                     glyph.getSubXFixed(),
566                                                     glyph.getSubYFixed()),
567                                       SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
568                                       SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
569                                       fontScaler);
570             }
571             pos += scalarsPerPosition;
572         }
573     }
574
575     this->finish();
576 }