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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2014 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 "GrStencilAndCoverTextContext.h"
       9             : #include "GrAtlasTextContext.h"
      10             : #include "GrContext.h"
      11             : #include "GrPath.h"
      12             : #include "GrPathRange.h"
      13             : #include "GrPipelineBuilder.h"
      14             : #include "GrRenderTargetContext.h"
      15             : #include "GrResourceProvider.h"
      16             : #include "GrTextUtils.h"
      17             : #include "SkAutoKern.h"
      18             : #include "SkDraw.h"
      19             : #include "SkDrawFilter.h"
      20             : #include "SkDrawProcs.h"
      21             : #include "SkGlyphCache.h"
      22             : #include "SkGr.h"
      23             : #include "SkPath.h"
      24             : #include "SkTextBlobRunIterator.h"
      25             : #include "SkTextFormatParams.h"
      26             : #include "SkTextMapStateProc.h"
      27             : 
      28             : #include "ops/GrDrawPathOp.h"
      29             : 
      30           0 : template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
      31           0 :     SkASSERT(*val);
      32           0 :     delete *val;
      33           0 : }
      34             : 
      35           0 : template<typename T> static void delete_hash_table_entry(T* val) {
      36           0 :     SkASSERT(*val);
      37           0 :     delete *val;
      38           0 : }
      39             : 
      40           0 : GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
      41             :     : fFallbackTextContext(fallbackTextContext)
      42           0 :     , fCacheSize(0) {
      43           0 : }
      44             : 
      45             : GrStencilAndCoverTextContext*
      46           0 : GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
      47           0 :     return new GrStencilAndCoverTextContext(fallbackTextContext);;
      48             : }
      49             : 
      50           0 : GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
      51           0 :     fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
      52           0 :     fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
      53           0 : }
      54             : 
      55           0 : bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
      56           0 :     if (skPaint.getRasterizer()) {
      57           0 :         return false;
      58             :     }
      59           0 :     if (skPaint.getMaskFilter()) {
      60           0 :         return false;
      61             :     }
      62           0 :     if (SkPathEffect* pe = skPaint.getPathEffect()) {
      63           0 :         if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
      64           0 :             return false;
      65             :         }
      66             :     }
      67             :     // No hairlines. They would require new paths with customized strokes for every new draw matrix.
      68           0 :     return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
      69             : }
      70             : 
      71           0 : void GrStencilAndCoverTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
      72             :                                             const GrClip& clip, const SkPaint& skPaint,
      73             :                                             const SkMatrix& viewMatrix, const SkSurfaceProps& props,
      74             :                                             const char text[], size_t byteLength, SkScalar x,
      75             :                                             SkScalar y, const SkIRect& clipBounds) {
      76           0 :     if (context->abandoned()) {
      77           0 :         return;
      78           0 :     } else if (this->canDraw(skPaint, viewMatrix)) {
      79           0 :         if (skPaint.getTextSize() > 0) {
      80           0 :             TextRun run(skPaint);
      81           0 :             run.setText(text, byteLength, x, y);
      82           0 :             run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
      83           0 :                      skPaint);
      84             :         }
      85           0 :         return;
      86           0 :     } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
      87           0 :                                              *context->caps()->shaderCaps())) {
      88           0 :         fFallbackTextContext->drawText(context, rtc, clip, skPaint, viewMatrix, props, text,
      89           0 :                                        byteLength, x, y, clipBounds);
      90           0 :         return;
      91             :     }
      92             : 
      93             :     // fall back to drawing as a path
      94             :     GrTextUtils::DrawTextAsPath(context, rtc, clip, skPaint, viewMatrix, text, byteLength, x, y,
      95           0 :                                 clipBounds);
      96             : }
      97             : 
      98           0 : void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
      99             :                                                const GrClip& clip, const SkPaint& skPaint,
     100             :                                                const SkMatrix& viewMatrix,
     101             :                                                const SkSurfaceProps& props, const char text[],
     102             :                                                size_t byteLength, const SkScalar pos[],
     103             :                                                int scalarsPerPosition, const SkPoint& offset,
     104             :                                                const SkIRect& clipBounds) {
     105           0 :     if (context->abandoned()) {
     106           0 :         return;
     107           0 :     } else if (this->canDraw(skPaint, viewMatrix)) {
     108           0 :         if (skPaint.getTextSize() > 0) {
     109           0 :             TextRun run(skPaint);
     110           0 :             run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
     111           0 :             run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
     112           0 :                      skPaint);
     113             :         }
     114           0 :         return;
     115           0 :     } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
     116           0 :                                              *context->caps()->shaderCaps())) {
     117           0 :         fFallbackTextContext->drawPosText(context, rtc, clip, skPaint, viewMatrix, props, text,
     118           0 :                                           byteLength, pos, scalarsPerPosition, offset, clipBounds);
     119           0 :         return;
     120             :     }
     121             : 
     122             :     // fall back to drawing as a path
     123             :     GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, skPaint, viewMatrix, text,
     124           0 :                                    byteLength, pos, scalarsPerPosition, offset, clipBounds);
     125             : }
     126             : 
     127           0 : void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
     128             :                                                         GrRenderTargetContext* rtc,
     129             :                                                         const GrClip& clip,
     130             :                                                         const SkPaint& skPaint,
     131             :                                                         const SkMatrix& viewMatrix,
     132             :                                                         const SkSurfaceProps& props,
     133             :                                                         const SkTextBlob* blob,
     134             :                                                         SkScalar x, SkScalar y,
     135             :                                                         SkDrawFilter* drawFilter,
     136             :                                                         const SkIRect& clipBounds) {
     137           0 :     GrTextUtils::Paint paint(&skPaint);
     138           0 :     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
     139           0 :     SkTextBlobRunIterator it(blob);
     140           0 :     for (;!it.done(); it.next()) {
     141           0 :         if (!runPaint.modifyForRun(it)) {
     142           0 :             continue;
     143             :         }
     144           0 :         size_t textLen = it.glyphCount() * sizeof(uint16_t);
     145           0 :         const SkPoint& offset = it.offset();
     146             : 
     147           0 :         switch (it.positioning()) {
     148             :             case SkTextBlob::kDefault_Positioning:
     149           0 :                 this->drawText(context, rtc, clip, runPaint, viewMatrix, props,
     150           0 :                                (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y(),
     151           0 :                                clipBounds);
     152           0 :                 break;
     153             :             case SkTextBlob::kHorizontal_Positioning:
     154           0 :                 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
     155           0 :                                   (const char*)it.glyphs(), textLen, it.pos(), 1,
     156           0 :                                   SkPoint::Make(x, y + offset.y()), clipBounds);
     157           0 :                 break;
     158             :             case SkTextBlob::kFull_Positioning:
     159           0 :                 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
     160           0 :                                   (const char*)it.glyphs(), textLen, it.pos(), 2,
     161           0 :                                   SkPoint::Make(x, y), clipBounds);
     162           0 :                 break;
     163             :         }
     164             :     }
     165           0 : }
     166             : 
     167           0 : void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrRenderTargetContext* rtc,
     168             :                                                 const GrClip& clip, const SkPaint& skPaint,
     169             :                                                 const SkMatrix& viewMatrix,
     170             :                                                 const SkSurfaceProps& props,
     171             :                                                 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
     172             :                                                 SkDrawFilter* drawFilter,
     173             :                                                 const SkIRect& clipBounds) {
     174           0 :     if (context->abandoned()) {
     175           0 :         return;
     176             :     }
     177             : 
     178           0 :     if (!this->internalCanDraw(skPaint)) {
     179           0 :         fFallbackTextContext->drawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob,
     180           0 :                                            x, y, drawFilter, clipBounds);
     181           0 :         return;
     182             :     }
     183             : 
     184           0 :     if (drawFilter || skPaint.getPathEffect()) {
     185             :         // This draw can't be cached.
     186             :         this->uncachedDrawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob, x, y,
     187           0 :                                    drawFilter, clipBounds);
     188           0 :         return;
     189             :     }
     190             : 
     191           0 :     const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
     192             : 
     193           0 :     TextBlob::Iter iter(blob);
     194           0 :     for (TextRun *run = iter.get(), *nextRun; run; run = nextRun) {
     195           0 :         nextRun = iter.next();
     196           0 :         run->draw(context, rtc, clip, viewMatrix, props, x, y, clipBounds, fFallbackTextContext,
     197           0 :                   skPaint);
     198           0 :         run->releaseGlyphCache();
     199             :     }
     200             : }
     201             : 
     202           0 : static inline int style_key_cnt(const GrStyle& style) {
     203           0 :     int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
     204             :     // We should be able to make a key because we filtered out arbitrary path effects.
     205           0 :     SkASSERT(cnt > 0);
     206           0 :     return cnt;
     207             : }
     208             : 
     209           0 : static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
     210             :     // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
     211           0 :     GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
     212           0 : }
     213             : 
     214             : const GrStencilAndCoverTextContext::TextBlob&
     215           0 : GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
     216             :                                                    const SkPaint& skPaint) {
     217             :     // The font-related parameters are baked into the text blob and will override this skPaint, so
     218             :     // the only remaining properties that can affect a TextBlob are the ones related to stroke.
     219           0 :     if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
     220           0 :         if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
     221           0 :             fLRUList.remove(*found);
     222           0 :             fLRUList.addToTail(*found);
     223           0 :             return **found;
     224             :         }
     225           0 :         TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
     226           0 :         this->purgeToFit(*blob);
     227           0 :         fBlobIdCache.set(skBlob->uniqueID(), blob);
     228           0 :         fLRUList.addToTail(blob);
     229           0 :         fCacheSize += blob->cpuMemorySize();
     230           0 :         return *blob;
     231             :     } else {
     232           0 :         GrStyle style(skPaint);
     233           0 :         SkSTArray<4, uint32_t, true> key;
     234           0 :         key.reset(1 + style_key_cnt(style));
     235           0 :         key[0] = skBlob->uniqueID();
     236           0 :         write_style_key(&key[1], style);
     237           0 :         if (TextBlob** found = fBlobKeyCache.find(key)) {
     238           0 :             fLRUList.remove(*found);
     239           0 :             fLRUList.addToTail(*found);
     240           0 :             return **found;
     241             :         }
     242           0 :         TextBlob* blob = new TextBlob(key, skBlob, skPaint);
     243           0 :         this->purgeToFit(*blob);
     244           0 :         fBlobKeyCache.set(blob);
     245           0 :         fLRUList.addToTail(blob);
     246           0 :         fCacheSize += blob->cpuMemorySize();
     247           0 :         return *blob;
     248             :     }
     249             : }
     250             : 
     251           0 : void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
     252             :     static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
     253             : 
     254           0 :     size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
     255           0 :     while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
     256           0 :         TextBlob* lru = fLRUList.head();
     257           0 :         if (1 == lru->key().count()) {
     258             :             // 1-length keys are unterstood to be the blob id.
     259           0 :             fBlobIdCache.remove(lru->key()[0]);
     260             :         } else {
     261           0 :             fBlobKeyCache.remove(lru->key());
     262             :         }
     263           0 :         fLRUList.remove(lru);
     264           0 :         fCacheSize -= lru->cpuMemorySize();
     265           0 :         delete lru;
     266             :     }
     267           0 : }
     268             : 
     269             : ////////////////////////////////////////////////////////////////////////////////////////////////////
     270             : 
     271           0 : void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
     272             :                                                   const SkPaint& skPaint) {
     273           0 :     fCpuMemorySize = sizeof(TextBlob);
     274           0 :     SkPaint runPaint(skPaint);
     275           0 :     for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
     276           0 :         iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
     277           0 :         if (runPaint.getTextSize() <= 0) {
     278           0 :             continue;
     279             :         }
     280           0 :         TextRun* run = this->addToTail(runPaint);
     281             : 
     282           0 :         const char* text = reinterpret_cast<const char*>(iter.glyphs());
     283           0 :         size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
     284           0 :         const SkPoint& runOffset = iter.offset();
     285             : 
     286           0 :         switch (iter.positioning()) {
     287             :             case SkTextBlob::kDefault_Positioning:
     288           0 :                 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
     289           0 :                 break;
     290             :             case SkTextBlob::kHorizontal_Positioning:
     291           0 :                 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
     292           0 :                 break;
     293             :             case SkTextBlob::kFull_Positioning:
     294           0 :                 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
     295           0 :                 break;
     296             :         }
     297             : 
     298           0 :         fCpuMemorySize += run->computeSizeInCache();
     299             :     }
     300           0 : }
     301             : 
     302             : ////////////////////////////////////////////////////////////////////////////////////////////////////
     303             : 
     304           0 : class GrStencilAndCoverTextContext::FallbackBlobBuilder {
     305             : public:
     306           0 :     FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
     307             : 
     308           0 :     bool isInitialized() const { return fBuilder != nullptr; }
     309             : 
     310             :     void init(const SkPaint& font, SkScalar textRatio);
     311             : 
     312             :     void appendGlyph(uint16_t glyphId, const SkPoint& pos);
     313             : 
     314             :     sk_sp<SkTextBlob> makeIfNeeded(int* count);
     315             : 
     316             : private:
     317             :     enum { kWriteBufferSize = 1024 };
     318             : 
     319             :     void flush();
     320             : 
     321             :     std::unique_ptr<SkTextBlobBuilder> fBuilder;
     322             :     SkPaint                            fFont;
     323             :     int                                fBuffIdx;
     324             :     int                                fCount;
     325             :     uint16_t                           fGlyphIds[kWriteBufferSize];
     326             :     SkPoint                            fPositions[kWriteBufferSize];
     327             : };
     328             : 
     329             : ////////////////////////////////////////////////////////////////////////////////////////////////////
     330             : 
     331           0 : GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
     332             :     : fStyle(fontAndStroke)
     333             :     , fFont(fontAndStroke)
     334             :     , fTotalGlyphCount(0)
     335             :     , fFallbackGlyphCount(0)
     336             :     , fDetachedGlyphCache(nullptr)
     337           0 :     , fLastDrawnGlyphsID(SK_InvalidUniqueID) {
     338           0 :     SkASSERT(fFont.getTextSize() > 0);
     339           0 :     SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
     340           0 :     SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
     341             : 
     342             :     // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
     343             :     // rendering API for stroking).
     344           0 :     fFont.setStyle(SkPaint::kFill_Style);
     345             : 
     346           0 :     if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
     347           0 :         const SkStrokeRec& stroke = fStyle.strokeRec();
     348             :         // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
     349           0 :         SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
     350             :                                                     kStdFakeBoldInterpKeys,
     351             :                                                     kStdFakeBoldInterpValues,
     352           0 :                                                     kStdFakeBoldInterpLength);
     353           0 :         SkScalar extra = fFont.getTextSize() * fakeBoldScale;
     354             : 
     355           0 :         SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
     356           0 :         strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
     357           0 :                                  true /*strokeAndFill*/);
     358           0 :         fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
     359           0 :         fFont.setFakeBoldText(false);
     360             :     }
     361             : 
     362           0 :     if (!fFont.getPathEffect() && !fStyle.isDashed()) {
     363           0 :         const SkStrokeRec& stroke = fStyle.strokeRec();
     364             :         // We can draw the glyphs from canonically sized paths.
     365           0 :         fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
     366           0 :         fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
     367             : 
     368             :         // Compensate for the glyphs being scaled by fTextRatio.
     369           0 :         if (!fStyle.isSimpleFill()) {
     370           0 :             SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
     371           0 :             strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
     372           0 :                                      SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
     373           0 :             fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
     374             :         }
     375             : 
     376           0 :         fFont.setLinearText(true);
     377           0 :         fFont.setLCDRenderText(false);
     378           0 :         fFont.setAutohinted(false);
     379           0 :         fFont.setHinting(SkPaint::kNo_Hinting);
     380           0 :         fFont.setSubpixelText(true);
     381           0 :         fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
     382             : 
     383           0 :         fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
     384           0 :                               0 == fFont.getTextSkewX() &&
     385           0 :                               !fFont.isFakeBoldText() &&
     386           0 :                               !fFont.isVerticalText();
     387             :     } else {
     388           0 :         fTextRatio = fTextInverseRatio = 1.0f;
     389           0 :         fUsingRawGlyphPaths = false;
     390             :     }
     391             : 
     392             :     // Generate the key that will be used to cache the GPU glyph path objects.
     393           0 :     if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
     394           0 :         static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
     395             : 
     396           0 :         const SkTypeface* typeface = fFont.getTypeface();
     397           0 :         GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
     398           0 :         reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
     399             :     } else {
     400           0 :         static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
     401             : 
     402           0 :         int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
     403             :         // Key should be valid since we opted out of drawing arbitrary path effects.
     404           0 :         SkASSERT(styleDataCount >= 0);
     405           0 :         if (fUsingRawGlyphPaths) {
     406           0 :             const SkTypeface* typeface = fFont.getTypeface();
     407           0 :             GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
     408           0 :             reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
     409           0 :             reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
     410           0 :             if (styleDataCount) {
     411           0 :                 write_style_key(&builder[2], fStyle);
     412             :             }
     413             :         } else {
     414           0 :             SkGlyphCache* glyphCache = this->getGlyphCache();
     415           0 :             const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
     416           0 :             const SkDescriptor* desc = &glyphCache->getDescriptor();
     417           0 :             int descDataCount = (desc->getLength() + 3) / 4;
     418             :             GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
     419           0 :                                          2 + styleDataCount + descDataCount);
     420           0 :             reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
     421           0 :             reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
     422           0 :             if (styleDataCount) {
     423           0 :                 write_style_key(&builder[2], fStyle);
     424             :             }
     425           0 :             memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
     426             :         }
     427             :     }
     428           0 : }
     429             : 
     430           0 : GrStencilAndCoverTextContext::TextRun::~TextRun() {
     431           0 :     this->releaseGlyphCache();
     432           0 : }
     433             : 
     434           0 : void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
     435             :                                                     SkScalar x, SkScalar y) {
     436           0 :     SkASSERT(byteLength == 0 || text != nullptr);
     437             : 
     438           0 :     SkGlyphCache* glyphCache = this->getGlyphCache();
     439           0 :     SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
     440           0 :                                                                         fFont.isDevKernText(),
     441           0 :                                                                         true);
     442             : 
     443           0 :     fTotalGlyphCount = fFont.countText(text, byteLength);
     444           0 :     fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
     445           0 :                                             fTotalGlyphCount));
     446             : 
     447           0 :     const char* stop = text + byteLength;
     448             : 
     449             :     // Measure first if needed.
     450           0 :     if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
     451           0 :         SkScalar   stopX = 0;
     452           0 :         SkScalar   stopY = 0;
     453             : 
     454           0 :         const char* textPtr = text;
     455           0 :         while (textPtr < stop) {
     456             :             // We don't need x, y here, since all subpixel variants will have the
     457             :             // same advance.
     458           0 :             const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
     459             : 
     460           0 :             stopX += SkFloatToScalar(glyph.fAdvanceX);
     461           0 :             stopY += SkFloatToScalar(glyph.fAdvanceY);
     462             :         }
     463           0 :         SkASSERT(textPtr == stop);
     464             : 
     465           0 :         SkScalar alignX = stopX * fTextRatio;
     466           0 :         SkScalar alignY = stopY * fTextRatio;
     467             : 
     468           0 :         if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
     469           0 :             alignX = SkScalarHalf(alignX);
     470           0 :             alignY = SkScalarHalf(alignY);
     471             :         }
     472             : 
     473           0 :         x -= alignX;
     474           0 :         y -= alignY;
     475             :     }
     476             : 
     477           0 :     SkAutoKern autokern;
     478             : 
     479           0 :     FallbackBlobBuilder fallback;
     480           0 :     while (text < stop) {
     481           0 :         const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
     482           0 :         x += autokern.adjust(glyph) * fTextRatio;
     483           0 :         if (glyph.fWidth) {
     484           0 :             this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
     485             :         }
     486             : 
     487           0 :         x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
     488           0 :         y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
     489             :     }
     490             : 
     491           0 :     fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
     492           0 : }
     493             : 
     494           0 : void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
     495             :                                                        const SkScalar pos[], int scalarsPerPosition,
     496             :                                                        const SkPoint& offset) {
     497           0 :     SkASSERT(byteLength == 0 || text != nullptr);
     498           0 :     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
     499             : 
     500           0 :     SkGlyphCache* glyphCache = this->getGlyphCache();
     501           0 :     SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
     502           0 :                                                                         fFont.isDevKernText(),
     503           0 :                                                                         true);
     504             : 
     505           0 :     fTotalGlyphCount = fFont.countText(text, byteLength);
     506           0 :     fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
     507           0 :                                             fTotalGlyphCount));
     508             : 
     509           0 :     const char* stop = text + byteLength;
     510             : 
     511           0 :     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
     512           0 :     SkTextAlignProc alignProc(fFont.getTextAlign());
     513           0 :     FallbackBlobBuilder fallback;
     514           0 :     while (text < stop) {
     515           0 :         const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
     516           0 :         if (glyph.fWidth) {
     517             :             SkPoint tmsLoc;
     518           0 :             tmsProc(pos, &tmsLoc);
     519             :             SkPoint loc;
     520           0 :             alignProc(tmsLoc, glyph, &loc);
     521             : 
     522           0 :             this->appendGlyph(glyph, loc, &fallback);
     523             :         }
     524           0 :         pos += scalarsPerPosition;
     525             :     }
     526             : 
     527           0 :     fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
     528           0 : }
     529             : 
     530           0 : GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(
     531             :                                                     GrResourceProvider* resourceProvider) const {
     532             :     GrPathRange* glyphs = static_cast<GrPathRange*>(
     533           0 :             resourceProvider->findAndRefResourceByUniqueKey(fGlyphPathsKey));
     534           0 :     if (nullptr == glyphs) {
     535           0 :         if (fUsingRawGlyphPaths) {
     536           0 :             SkScalerContextEffects noeffects;
     537           0 :             glyphs = resourceProvider->createGlyphs(fFont.getTypeface(), noeffects,
     538           0 :                                                     nullptr, fStyle);
     539             :         } else {
     540           0 :             SkGlyphCache* cache = this->getGlyphCache();
     541           0 :             glyphs = resourceProvider->createGlyphs(cache->getScalerContext()->getTypeface(),
     542           0 :                                                     cache->getScalerContext()->getEffects(),
     543           0 :                                                     &cache->getDescriptor(),
     544           0 :                                                     fStyle);
     545             :         }
     546           0 :         resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
     547             :     }
     548           0 :     return glyphs;
     549             : }
     550             : 
     551           0 : inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
     552             :                                                                const SkPoint& pos,
     553             :                                                                FallbackBlobBuilder* fallback) {
     554             :     // Stick the glyphs we can't draw into the fallback text blob.
     555           0 :     if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
     556           0 :         if (!fallback->isInitialized()) {
     557           0 :             fallback->init(fFont, fTextRatio);
     558             :         }
     559           0 :         fallback->appendGlyph(glyph.getGlyphID(), pos);
     560             :     } else {
     561           0 :         fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
     562           0 :                               fTextInverseRatio * pos.y());
     563             :     }
     564           0 : }
     565             : 
     566           0 : void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
     567             :                                                  GrRenderTargetContext* renderTargetContext,
     568             :                                                  const GrClip& clip, const SkMatrix& viewMatrix,
     569             :                                                  const SkSurfaceProps& props, SkScalar x,
     570             :                                                  SkScalar y, const SkIRect& clipBounds,
     571             :                                                  GrAtlasTextContext* fallbackTextContext,
     572             :                                                  const SkPaint& originalSkPaint) const {
     573           0 :     SkASSERT(fInstanceData);
     574             : 
     575           0 :     if (fInstanceData->count()) {
     576             :         static constexpr GrUserStencilSettings kCoverPass(
     577             :             GrUserStencilSettings::StaticInit<
     578             :                 0x0000,
     579             :                 GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
     580             :                 0xffff,
     581             :                 GrUserStencilOp::kZero,
     582             :                 GrUserStencilOp::kKeep,
     583             :                 0xffff>()
     584             :         );
     585             : 
     586           0 :         sk_sp<GrPathRange> glyphs(this->createGlyphs(ctx->resourceProvider()));
     587           0 :         if (fLastDrawnGlyphsID != glyphs->uniqueID()) {
     588             :             // Either this is the first draw or the glyphs object was purged since last draw.
     589           0 :             glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
     590           0 :             fLastDrawnGlyphsID = glyphs->uniqueID();
     591             :         }
     592             : 
     593           0 :         GrPaint grPaint;
     594           0 :         if (!SkPaintToGrPaint(ctx, renderTargetContext, originalSkPaint, viewMatrix, &grPaint)) {
     595           0 :             return;
     596             :         }
     597             : 
     598             :         // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
     599             :         // the entire dst. Realistically this is a moot point, because any context that supports
     600             :         // NV_path_rendering will also support NV_blend_equation_advanced.
     601             :         // For clipping we'll just skip any optimizations based on the bounds. This does, however,
     602             :         // hurt GrOp combining.
     603             :         const SkRect bounds = SkRect::MakeIWH(renderTargetContext->width(),
     604           0 :                                               renderTargetContext->height());
     605             : 
     606             :         // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
     607             :         GrAAType aaType;
     608           0 :         if (this->aa() == GrAA::kYes) {
     609           0 :             SkASSERT(renderTargetContext->isStencilBufferMultisampled());
     610           0 :             aaType = renderTargetContext->isUnifiedMultisampled() ? GrAAType::kMSAA
     611             :                                                                   : GrAAType::kMixedSamples;
     612             :         } else {
     613           0 :             aaType = GrAAType::kNone;
     614             :         }
     615             : 
     616             :         std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
     617           0 :                 viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
     618           0 :                 std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
     619           0 :                 fInstanceData.get(), bounds);
     620             : 
     621           0 :         renderTargetContext->addDrawOp(clip, std::move(op));
     622             :     }
     623             : 
     624           0 :     if (fFallbackTextBlob) {
     625           0 :         SkPaint fallbackSkPaint(originalSkPaint);
     626           0 :         fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
     627           0 :         if (!fStyle.isSimpleFill()) {
     628           0 :             fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
     629             :         }
     630             : 
     631             :         fallbackTextContext->drawTextBlob(ctx, renderTargetContext, clip, fallbackSkPaint,
     632           0 :                                           viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr,
     633           0 :                                           clipBounds);
     634             :     }
     635             : }
     636             : 
     637           0 : SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
     638           0 :     if (!fDetachedGlyphCache) {
     639           0 :         fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
     640             :                                                 nullptr);
     641             :     }
     642           0 :     return fDetachedGlyphCache;
     643             : }
     644             : 
     645             : 
     646           0 : void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
     647           0 :     if (fDetachedGlyphCache) {
     648           0 :         SkGlyphCache::AttachCache(fDetachedGlyphCache);
     649           0 :         fDetachedGlyphCache = nullptr;
     650             :     }
     651           0 : }
     652             : 
     653           0 : size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
     654           0 :     size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
     655             :     // The instance data always reserves enough space for every glyph.
     656           0 :     size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
     657           0 :     if (fInstanceData) {
     658           0 :         size += sizeof(InstanceData);
     659             :     }
     660           0 :     if (fFallbackTextBlob) {
     661           0 :         size += sizeof(SkTextBlob);
     662             :     }
     663           0 :     return size;
     664             : }
     665             : 
     666             : ////////////////////////////////////////////////////////////////////////////////////////////////////
     667             : 
     668           0 : void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
     669             :                                                              SkScalar textRatio) {
     670           0 :     SkASSERT(!this->isInitialized());
     671           0 :     fBuilder.reset(new SkTextBlobBuilder);
     672           0 :     fFont = font;
     673           0 :     fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
     674           0 :     fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
     675             :     // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
     676             :     // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
     677           0 :     fFont.setSubpixelText(false);
     678           0 :     fFont.setTextSize(fFont.getTextSize() * textRatio);
     679           0 :     fBuffIdx = 0;
     680           0 : }
     681             : 
     682           0 : void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
     683             :                                                                     const SkPoint& pos) {
     684           0 :     SkASSERT(this->isInitialized());
     685           0 :     if (fBuffIdx >= kWriteBufferSize) {
     686           0 :         this->flush();
     687             :     }
     688           0 :     fGlyphIds[fBuffIdx] = glyphId;
     689           0 :     fPositions[fBuffIdx] = pos;
     690           0 :     fBuffIdx++;
     691           0 :     fCount++;
     692           0 : }
     693             : 
     694           0 : void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
     695           0 :     SkASSERT(this->isInitialized());
     696           0 :     SkASSERT(fBuffIdx <= kWriteBufferSize);
     697           0 :     if (!fBuffIdx) {
     698           0 :         return;
     699             :     }
     700             :     // This will automatically merge with previous runs since we use the same font.
     701           0 :     const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
     702           0 :     memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
     703           0 :     memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
     704           0 :     fBuffIdx = 0;
     705             : }
     706             : 
     707           0 : sk_sp<SkTextBlob> GrStencilAndCoverTextContext::FallbackBlobBuilder::makeIfNeeded(int *count) {
     708           0 :     *count = fCount;
     709           0 :     if (fCount) {
     710           0 :         this->flush();
     711           0 :         return fBuilder->make();
     712             :     }
     713           0 :     return nullptr;
     714             : }

Generated by: LCOV version 1.13