LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/text - GrAtlasTextContext.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 178 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 13 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015 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             : #include "GrAtlasTextContext.h"
       8             : #include "GrContext.h"
       9             : #include "GrRenderTargetContext.h"
      10             : #include "GrTextBlobCache.h"
      11             : #include "SkDraw.h"
      12             : #include "SkDrawFilter.h"
      13             : #include "SkGr.h"
      14             : #include "ops/GrMeshDrawOp.h"
      15             : 
      16           0 : GrAtlasTextContext::GrAtlasTextContext()
      17           0 :     : fDistanceAdjustTable(new GrDistanceFieldAdjustTable) {
      18           0 : }
      19             : 
      20           0 : GrAtlasTextContext* GrAtlasTextContext::Create() {
      21           0 :     return new GrAtlasTextContext();
      22             : }
      23             : 
      24           0 : bool GrAtlasTextContext::canDraw(const SkPaint& skPaint,
      25             :                                  const SkMatrix& viewMatrix,
      26             :                                  const SkSurfaceProps& props,
      27             :                                  const GrShaderCaps& shaderCaps) {
      28           0 :     return GrTextUtils::CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) ||
      29           0 :            !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
      30             : }
      31             : 
      32           0 : SkColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
      33           0 :     SkColor canonicalColor = paint.computeLuminanceColor();
      34           0 :     if (lcd) {
      35             :         // This is the correct computation, but there are tons of cases where LCD can be overridden.
      36             :         // For now we just regenerate if any run in a textblob has LCD.
      37             :         // TODO figure out where all of these overrides are and see if we can incorporate that logic
      38             :         // at a higher level *OR* use sRGB
      39           0 :         SkASSERT(false);
      40             :         //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
      41             :     } else {
      42             :         // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
      43             :         // gamma corrected masks anyways, nor color
      44           0 :         U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
      45           0 :                                        SkColorGetG(canonicalColor),
      46           0 :                                        SkColorGetB(canonicalColor));
      47             :         // reduce to our finite number of bits
      48           0 :         canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
      49             :     }
      50           0 :     return canonicalColor;
      51             : }
      52             : 
      53           0 : uint32_t GrAtlasTextContext::ComputeScalerContextFlags(GrRenderTargetContext* rtc) {
      54             :     // If we're doing gamma-correct rendering, then we can disable the gamma hacks.
      55             :     // Otherwise, leave them on. In either case, we still want the contrast boost:
      56           0 :     if (rtc->isGammaCorrect()) {
      57           0 :         return SkPaint::kBoostContrast_ScalerContextFlag;
      58             :     } else {
      59           0 :         return SkPaint::kFakeGammaAndBoostContrast_ScalerContextFlags;
      60             :     }
      61             : }
      62             : 
      63             : // TODO if this function ever shows up in profiling, then we can compute this value when the
      64             : // textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
      65             : // run so this is not a big deal to compute here.
      66           0 : bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
      67           0 :     SkTextBlobRunIterator it(blob);
      68           0 :     for (; !it.done(); it.next()) {
      69           0 :         if (it.isLCD()) {
      70           0 :             return true;
      71             :         }
      72             :     }
      73           0 :     return false;
      74             : }
      75             : 
      76           0 : void GrAtlasTextContext::drawTextBlob(GrContext* context, GrRenderTargetContext* rtc,
      77             :                                       const GrClip& clip, const SkPaint& skPaint,
      78             :                                       const SkMatrix& viewMatrix,
      79             :                                       const SkSurfaceProps& props, const SkTextBlob* blob,
      80             :                                       SkScalar x, SkScalar y,
      81             :                                       SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
      82             :     // If we have been abandoned, then don't draw
      83           0 :     if (context->abandoned()) {
      84           0 :         return;
      85             :     }
      86             : 
      87           0 :     sk_sp<GrAtlasTextBlob> cacheBlob;
      88             :     SkMaskFilter::BlurRec blurRec;
      89           0 :     GrAtlasTextBlob::Key key;
      90             :     // It might be worth caching these things, but its not clear at this time
      91             :     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
      92           0 :     const SkMaskFilter* mf = skPaint.getMaskFilter();
      93           0 :     bool canCache = !(skPaint.getPathEffect() ||
      94           0 :                       (mf && !mf->asABlur(&blurRec)) ||
      95           0 :                       drawFilter);
      96           0 :     uint32_t scalerContextFlags = ComputeScalerContextFlags(rtc);
      97             : 
      98           0 :     GrTextBlobCache* cache = context->getTextBlobCache();
      99           0 :     if (canCache) {
     100           0 :         bool hasLCD = HasLCD(blob);
     101             : 
     102             :         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
     103           0 :         SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
     104           0 :                                                  kUnknown_SkPixelGeometry;
     105             : 
     106             :         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
     107             :         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
     108             :         // ensure we always match the same key
     109           0 :         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
     110           0 :                                           ComputeCanonicalColor(skPaint, hasLCD);
     111             : 
     112           0 :         key.fPixelGeometry = pixelGeometry;
     113           0 :         key.fUniqueID = blob->uniqueID();
     114           0 :         key.fStyle = skPaint.getStyle();
     115           0 :         key.fHasBlur = SkToBool(mf);
     116           0 :         key.fCanonicalColor = canonicalColor;
     117           0 :         key.fScalerContextFlags = scalerContextFlags;
     118           0 :         cacheBlob = cache->find(key);
     119             :     }
     120             : 
     121           0 :     GrTextUtils::Paint paint(&skPaint);
     122           0 :     if (cacheBlob) {
     123           0 :         if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
     124             :             // We have to remake the blob because changes may invalidate our masks.
     125             :             // TODO we could probably get away reuse most of the time if the pointer is unique,
     126             :             // but we'd have to clear the subrun information
     127           0 :             cache->remove(cacheBlob.get());
     128           0 :             cacheBlob = cache->makeCachedBlob(blob, key, blurRec, skPaint);
     129           0 :             RegenerateTextBlob(cacheBlob.get(), context->getAtlasGlyphCache(),
     130           0 :                                *context->caps()->shaderCaps(), paint, scalerContextFlags,
     131           0 :                                viewMatrix, props, blob, x, y, drawFilter);
     132             :         } else {
     133           0 :             cache->makeMRU(cacheBlob.get());
     134             : 
     135             :             if (CACHE_SANITY_CHECK) {
     136             :                 int glyphCount = 0;
     137             :                 int runCount = 0;
     138             :                 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
     139             :                 sk_sp<GrAtlasTextBlob> sanityBlob(cache->makeBlob(glyphCount, runCount));
     140             :                 sanityBlob->setupKey(key, blurRec, skPaint);
     141             :                 RegenerateTextBlob(sanityBlob.get(), context->getAtlasGlyphCache(),
     142             :                                    *context->caps()->shaderCaps(), paint, scalerContextFlags,
     143             :                                    viewMatrix, props, blob, x, y, drawFilter);
     144             :                 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
     145             :             }
     146             :         }
     147             :     } else {
     148           0 :         if (canCache) {
     149           0 :             cacheBlob = cache->makeCachedBlob(blob, key, blurRec, skPaint);
     150             :         } else {
     151           0 :             cacheBlob = cache->makeBlob(blob);
     152             :         }
     153           0 :         RegenerateTextBlob(cacheBlob.get(), context->getAtlasGlyphCache(),
     154           0 :                            *context->caps()->shaderCaps(), paint, scalerContextFlags, viewMatrix,
     155           0 :                            props, blob, x, y, drawFilter);
     156             :     }
     157             : 
     158           0 :     cacheBlob->flushCached(context, rtc, blob, props, fDistanceAdjustTable.get(), paint, drawFilter,
     159           0 :                            clip, viewMatrix, clipBounds, x, y);
     160             : }
     161             : 
     162           0 : void GrAtlasTextContext::RegenerateTextBlob(GrAtlasTextBlob* cacheBlob,
     163             :                                             GrAtlasGlyphCache* fontCache,
     164             :                                             const GrShaderCaps& shaderCaps,
     165             :                                             const GrTextUtils::Paint& paint,
     166             :                                             uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
     167             :                                             const SkSurfaceProps& props, const SkTextBlob* blob,
     168             :                                             SkScalar x, SkScalar y, SkDrawFilter* drawFilter) {
     169           0 :     cacheBlob->initReusableBlob(paint.filteredSkColor(), viewMatrix, x, y);
     170             : 
     171             :     // Regenerate textblob
     172           0 :     SkTextBlobRunIterator it(blob);
     173           0 :     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
     174           0 :     for (int run = 0; !it.done(); it.next(), run++) {
     175           0 :         int glyphCount = it.glyphCount();
     176           0 :         size_t textLen = glyphCount * sizeof(uint16_t);
     177           0 :         const SkPoint& offset = it.offset();
     178           0 :         cacheBlob->push_back_run(run);
     179           0 :         if (!runPaint.modifyForRun(it)) {
     180           0 :             continue;
     181             :         }
     182           0 :         if (GrTextUtils::CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
     183           0 :             switch (it.positioning()) {
     184             :                 case SkTextBlob::kDefault_Positioning: {
     185           0 :                     GrTextUtils::DrawDFText(cacheBlob, run, fontCache, props, runPaint,
     186             :                                             scalerContextFlags, viewMatrix,
     187           0 :                                             (const char*)it.glyphs(), textLen, x + offset.x(),
     188           0 :                                             y + offset.y());
     189           0 :                     break;
     190             :                 }
     191             :                 case SkTextBlob::kHorizontal_Positioning: {
     192           0 :                     SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
     193           0 :                     GrTextUtils::DrawDFPosText(
     194             :                             cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
     195           0 :                             viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, dfOffset);
     196           0 :                     break;
     197             :                 }
     198             :                 case SkTextBlob::kFull_Positioning: {
     199           0 :                     SkPoint dfOffset = SkPoint::Make(x, y);
     200           0 :                     GrTextUtils::DrawDFPosText(
     201             :                             cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
     202           0 :                             viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, dfOffset);
     203           0 :                     break;
     204             :                 }
     205             :             }
     206           0 :         } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
     207           0 :             cacheBlob->setRunDrawAsPaths(run);
     208             :         } else {
     209           0 :             switch (it.positioning()) {
     210             :                 case SkTextBlob::kDefault_Positioning:
     211           0 :                     GrTextUtils::DrawBmpText(cacheBlob, run, fontCache, props, runPaint,
     212             :                                              scalerContextFlags, viewMatrix,
     213           0 :                                              (const char*)it.glyphs(), textLen, x + offset.x(),
     214           0 :                                              y + offset.y());
     215           0 :                     break;
     216             :                 case SkTextBlob::kHorizontal_Positioning:
     217           0 :                     GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint,
     218             :                                                 scalerContextFlags, viewMatrix,
     219           0 :                                                 (const char*)it.glyphs(), textLen, it.pos(), 1,
     220           0 :                                                 SkPoint::Make(x, y + offset.y()));
     221           0 :                     break;
     222             :                 case SkTextBlob::kFull_Positioning:
     223           0 :                     GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint,
     224             :                                                 scalerContextFlags, viewMatrix,
     225           0 :                                                 (const char*)it.glyphs(), textLen, it.pos(), 2,
     226           0 :                                                 SkPoint::Make(x, y));
     227           0 :                     break;
     228             :             }
     229             :         }
     230             :     }
     231           0 : }
     232             : 
     233             : inline sk_sp<GrAtlasTextBlob>
     234           0 : GrAtlasTextContext::MakeDrawTextBlob(GrTextBlobCache* blobCache,
     235             :                                      GrAtlasGlyphCache* fontCache,
     236             :                                      const GrShaderCaps& shaderCaps,
     237             :                                      const GrTextUtils::Paint& paint,
     238             :                                      uint32_t scalerContextFlags,
     239             :                                      const SkMatrix& viewMatrix,
     240             :                                      const SkSurfaceProps& props,
     241             :                                      const char text[], size_t byteLength,
     242             :                                      SkScalar x, SkScalar y) {
     243           0 :     int glyphCount = paint.skPaint().countText(text, byteLength);
     244             : 
     245           0 :     sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
     246           0 :     blob->initThrowawayBlob(viewMatrix, x, y);
     247             : 
     248           0 :     if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
     249           0 :         GrTextUtils::DrawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
     250           0 :                                 viewMatrix, text, byteLength, x, y);
     251             :     } else {
     252           0 :         GrTextUtils::DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
     253           0 :                                  viewMatrix, text, byteLength, x, y);
     254             :     }
     255           0 :     return blob;
     256             : }
     257             : 
     258             : inline sk_sp<GrAtlasTextBlob>
     259           0 : GrAtlasTextContext::MakeDrawPosTextBlob(GrTextBlobCache* blobCache,
     260             :                                         GrAtlasGlyphCache* fontCache,
     261             :                                         const GrShaderCaps& shaderCaps,
     262             :                                         const GrTextUtils::Paint& paint,
     263             :                                         uint32_t scalerContextFlags,
     264             :                                         const SkMatrix& viewMatrix,
     265             :                                         const SkSurfaceProps& props,
     266             :                                         const char text[], size_t byteLength,
     267             :                                         const SkScalar pos[], int scalarsPerPosition, const
     268             :                                         SkPoint& offset) {
     269           0 :     int glyphCount = paint.skPaint().countText(text, byteLength);
     270             : 
     271           0 :     sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
     272           0 :     blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
     273             : 
     274           0 :     if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
     275           0 :         GrTextUtils::DrawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
     276           0 :                                    viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
     277             :     } else {
     278           0 :         GrTextUtils::DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
     279           0 :                                     viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
     280             :     }
     281           0 :     return blob;
     282             : }
     283             : 
     284           0 : void GrAtlasTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
     285             :                                   const GrClip& clip, const SkPaint& skPaint,
     286             :                                   const SkMatrix& viewMatrix, const SkSurfaceProps& props,
     287             :                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
     288             :                                   const SkIRect& regionClipBounds) {
     289           0 :     if (context->abandoned()) {
     290           0 :         return;
     291             :     }
     292           0 :     GrTextUtils::Paint paint(&skPaint);
     293           0 :     if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
     294             :         sk_sp<GrAtlasTextBlob> blob(
     295             :             MakeDrawTextBlob(context->getTextBlobCache(), context->getAtlasGlyphCache(),
     296           0 :                              *context->caps()->shaderCaps(),
     297             :                              paint, ComputeScalerContextFlags(rtc),
     298             :                              viewMatrix, props,
     299           0 :                              text, byteLength, x, y));
     300           0 :         blob->flushThrowaway(context, rtc, props, fDistanceAdjustTable.get(), paint, clip,
     301           0 :                              viewMatrix, regionClipBounds, x, y);
     302           0 :         return;
     303             :     }
     304             : 
     305             :     // fall back to drawing as a path
     306           0 :     GrTextUtils::DrawTextAsPath(context, rtc, clip, paint, viewMatrix, text, byteLength, x, y,
     307           0 :                                 regionClipBounds);
     308             : }
     309             : 
     310           0 : void GrAtlasTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
     311             :                                      const GrClip& clip, const SkPaint& skPaint,
     312             :                                      const SkMatrix& viewMatrix, const SkSurfaceProps& props,
     313             :                                      const char text[], size_t byteLength, const SkScalar pos[],
     314             :                                      int scalarsPerPosition, const SkPoint& offset,
     315             :                                      const SkIRect& regionClipBounds) {
     316           0 :     GrTextUtils::Paint paint(&skPaint);
     317           0 :     if (context->abandoned()) {
     318           0 :         return;
     319           0 :     } else if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
     320             :         sk_sp<GrAtlasTextBlob> blob(
     321             :             MakeDrawPosTextBlob(context->getTextBlobCache(), context->getAtlasGlyphCache(),
     322           0 :                                 *context->caps()->shaderCaps(),
     323             :                                 paint, ComputeScalerContextFlags(rtc),
     324             :                                 viewMatrix, props,
     325             :                                 text, byteLength,
     326             :                                 pos, scalarsPerPosition,
     327           0 :                                 offset));
     328           0 :         blob->flushThrowaway(context, rtc, props, fDistanceAdjustTable.get(), paint, clip,
     329           0 :                              viewMatrix, regionClipBounds, offset.fX, offset.fY);
     330           0 :         return;
     331             :     }
     332             : 
     333             :     // fall back to drawing as a path
     334           0 :     GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, paint, viewMatrix, text, byteLength,
     335           0 :                                    pos, scalarsPerPosition, offset, regionClipBounds);
     336             : }
     337             : 
     338             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     339             : 
     340             : #if GR_TEST_UTILS
     341             : 
     342           0 : DRAW_OP_TEST_DEFINE(TextBlobOp) {
     343             :     static uint32_t gContextID = SK_InvalidGenID;
     344             :     static GrAtlasTextContext* gTextContext = nullptr;
     345           0 :     static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
     346             : 
     347           0 :     if (context->uniqueID() != gContextID) {
     348           0 :         gContextID = context->uniqueID();
     349           0 :         delete gTextContext;
     350             : 
     351           0 :         gTextContext = GrAtlasTextContext::Create();
     352             :     }
     353             : 
     354             :     // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
     355             :     sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
     356           0 :         SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
     357             : 
     358           0 :     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     359           0 :     SkPaint skPaint;
     360           0 :     skPaint.setColor(random->nextU());
     361           0 :     skPaint.setLCDRenderText(random->nextBool());
     362           0 :     skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
     363           0 :     skPaint.setSubpixelText(random->nextBool());
     364             : 
     365           0 :     const char* text = "The quick brown fox jumps over the lazy dog.";
     366           0 :     int textLen = (int)strlen(text);
     367             : 
     368             :     // create some random x/y offsets, including negative offsets
     369             :     static const int kMaxTrans = 1024;
     370           0 :     int xPos = (random->nextU() % 2) * 2 - 1;
     371           0 :     int yPos = (random->nextU() % 2) * 2 - 1;
     372           0 :     int xInt = (random->nextU() % kMaxTrans) * xPos;
     373           0 :     int yInt = (random->nextU() % kMaxTrans) * yPos;
     374           0 :     SkScalar x = SkIntToScalar(xInt);
     375           0 :     SkScalar y = SkIntToScalar(yInt);
     376             : 
     377           0 :     GrTextUtils::Paint paint(&skPaint);
     378             :     // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
     379             :     // test the text op with this unit test, that is okay.
     380             :     sk_sp<GrAtlasTextBlob> blob(GrAtlasTextContext::MakeDrawTextBlob(
     381             :             context->getTextBlobCache(), context->getAtlasGlyphCache(),
     382           0 :             *context->caps()->shaderCaps(), paint,
     383             :             GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, gSurfaceProps, text,
     384           0 :             static_cast<size_t>(textLen), x, y));
     385             : 
     386             :     return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, paint, gSurfaceProps,
     387           0 :                              gTextContext->dfAdjustTable(), context->getAtlasGlyphCache());
     388             : }
     389             : 
     390             : #endif

Generated by: LCOV version 1.13