Home | History | Annotate | Download | only in text
      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 
      8 #include "GrAtlasTextBlob.h"
      9 #include "GrBlurUtils.h"
     10 #include "GrContext.h"
     11 #include "GrPipelineBuilder.h"
     12 #include "GrRenderTargetContext.h"
     13 #include "GrTextUtils.h"
     14 #include "SkColorFilter.h"
     15 #include "SkDrawFilter.h"
     16 #include "SkGlyphCache.h"
     17 #include "SkTextBlobRunIterator.h"
     18 #include "ops/GrAtlasTextOp.h"
     19 
     20 sk_sp<GrAtlasTextBlob> GrAtlasTextBlob::Make(GrMemoryPool* pool, int glyphCount, int runCount) {
     21     // We allocate size for the GrAtlasTextBlob itself, plus size for the vertices array,
     22     // and size for the glyphIds array.
     23     size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize;
     24     size_t size = sizeof(GrAtlasTextBlob) +
     25                   verticesCount +
     26                   glyphCount * sizeof(GrGlyph**) +
     27                   sizeof(GrAtlasTextBlob::Run) * runCount;
     28 
     29     void* allocation = pool->allocate(size);
     30     if (CACHE_SANITY_CHECK) {
     31         sk_bzero(allocation, size);
     32     }
     33 
     34     sk_sp<GrAtlasTextBlob> cacheBlob(new (allocation) GrAtlasTextBlob);
     35     cacheBlob->fSize = size;
     36 
     37     // setup offsets for vertices / glyphs
     38     cacheBlob->fVertices = sizeof(GrAtlasTextBlob) +
     39                            reinterpret_cast<unsigned char*>(cacheBlob.get());
     40     cacheBlob->fGlyphs = reinterpret_cast<GrGlyph**>(cacheBlob->fVertices + verticesCount);
     41     cacheBlob->fRuns = reinterpret_cast<GrAtlasTextBlob::Run*>(cacheBlob->fGlyphs + glyphCount);
     42 
     43     // Initialize runs
     44     for (int i = 0; i < runCount; i++) {
     45         new (&cacheBlob->fRuns[i]) GrAtlasTextBlob::Run;
     46     }
     47     cacheBlob->fRunCount = runCount;
     48     cacheBlob->fPool = pool;
     49     return cacheBlob;
     50 }
     51 
     52 SkGlyphCache* GrAtlasTextBlob::setupCache(int runIndex,
     53                                           const SkSurfaceProps& props,
     54                                           uint32_t scalerContextFlags,
     55                                           const SkPaint& skPaint,
     56                                           const SkMatrix* viewMatrix) {
     57     GrAtlasTextBlob::Run* run = &fRuns[runIndex];
     58 
     59     // if we have an override descriptor for the run, then we should use that
     60     SkAutoDescriptor* desc = run->fOverrideDescriptor.get() ? run->fOverrideDescriptor.get() :
     61                                                               &run->fDescriptor;
     62     SkScalerContextEffects effects;
     63     skPaint.getScalerContextDescriptor(&effects, desc, props, scalerContextFlags, viewMatrix);
     64     run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
     65     run->fPathEffect = sk_ref_sp(effects.fPathEffect);
     66     run->fRasterizer = sk_ref_sp(effects.fRasterizer);
     67     run->fMaskFilter = sk_ref_sp(effects.fMaskFilter);
     68     return SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc->getDesc());
     69 }
     70 
     71 void GrAtlasTextBlob::appendGlyph(int runIndex,
     72                                   const SkRect& positions,
     73                                   GrColor color,
     74                                   GrAtlasTextStrike* strike,
     75                                   GrGlyph* glyph,
     76                                   SkGlyphCache* cache, const SkGlyph& skGlyph,
     77                                   SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
     78     if (positions.isEmpty()) {
     79         return;
     80     }
     81 
     82     // If the glyph is too large we fall back to paths
     83     if (glyph->fTooLargeForAtlas) {
     84         this->appendLargeGlyph(glyph, cache, skGlyph, x, y, scale, treatAsBMP);
     85         return;
     86     }
     87 
     88     Run& run = fRuns[runIndex];
     89     GrMaskFormat format = glyph->fMaskFormat;
     90 
     91     Run::SubRunInfo* subRun = &run.fSubRunInfo.back();
     92     if (run.fInitialized && subRun->maskFormat() != format) {
     93         subRun = &run.push_back();
     94         subRun->setStrike(strike);
     95     } else if (!run.fInitialized) {
     96         subRun->setStrike(strike);
     97     }
     98 
     99     run.fInitialized = true;
    100 
    101     size_t vertexStride = GetVertexStride(format);
    102 
    103     subRun->setMaskFormat(format);
    104 
    105     subRun->joinGlyphBounds(positions);
    106     subRun->setColor(color);
    107 
    108     intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex());
    109 
    110     if (kARGB_GrMaskFormat != glyph->fMaskFormat) {
    111         // V0
    112         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
    113         position->set(positions.fLeft, positions.fTop);
    114         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
    115         *colorPtr = color;
    116         vertex += vertexStride;
    117 
    118         // V1
    119         position = reinterpret_cast<SkPoint*>(vertex);
    120         position->set(positions.fLeft, positions.fBottom);
    121         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
    122         *colorPtr = color;
    123         vertex += vertexStride;
    124 
    125         // V2
    126         position = reinterpret_cast<SkPoint*>(vertex);
    127         position->set(positions.fRight, positions.fBottom);
    128         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
    129         *colorPtr = color;
    130         vertex += vertexStride;
    131 
    132         // V3
    133         position = reinterpret_cast<SkPoint*>(vertex);
    134         position->set(positions.fRight, positions.fTop);
    135         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
    136         *colorPtr = color;
    137     } else {
    138         // V0
    139         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
    140         position->set(positions.fLeft, positions.fTop);
    141         vertex += vertexStride;
    142 
    143         // V1
    144         position = reinterpret_cast<SkPoint*>(vertex);
    145         position->set(positions.fLeft, positions.fBottom);
    146         vertex += vertexStride;
    147 
    148         // V2
    149         position = reinterpret_cast<SkPoint*>(vertex);
    150         position->set(positions.fRight, positions.fBottom);
    151         vertex += vertexStride;
    152 
    153         // V3
    154         position = reinterpret_cast<SkPoint*>(vertex);
    155         position->set(positions.fRight, positions.fTop);
    156     }
    157     subRun->appendVertices(vertexStride);
    158     fGlyphs[subRun->glyphEndIndex()] = glyph;
    159     subRun->glyphAppended();
    160 }
    161 
    162 void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, SkGlyphCache* cache, const SkGlyph& skGlyph,
    163                                        SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
    164     if (nullptr == glyph->fPath) {
    165         const SkPath* glyphPath = cache->findPath(skGlyph);
    166         if (!glyphPath) {
    167             return;
    168         }
    169 
    170         glyph->fPath = new SkPath(*glyphPath);
    171     }
    172     fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, treatAsBMP));
    173 }
    174 
    175 bool GrAtlasTextBlob::mustRegenerate(const GrTextUtils::Paint& paint,
    176                                      const SkMaskFilter::BlurRec& blurRec,
    177                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    178     // If we have LCD text then our canonical color will be set to transparent, in this case we have
    179     // to regenerate the blob on any color change
    180     // We use the grPaint to get any color filter effects
    181     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
    182         fFilteredPaintColor != paint.filteredSkColor()) {
    183         return true;
    184     }
    185 
    186     if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
    187         return true;
    188     }
    189 
    190     if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
    191         return true;
    192     }
    193 
    194     // We only cache one masked version
    195     if (fKey.fHasBlur &&
    196         (fBlurRec.fSigma != blurRec.fSigma ||
    197          fBlurRec.fStyle != blurRec.fStyle ||
    198          fBlurRec.fQuality != blurRec.fQuality)) {
    199         return true;
    200     }
    201 
    202     // Similarly, we only cache one version for each style
    203     if (fKey.fStyle != SkPaint::kFill_Style &&
    204         (fStrokeInfo.fFrameWidth != paint.skPaint().getStrokeWidth() ||
    205          fStrokeInfo.fMiterLimit != paint.skPaint().getStrokeMiter() ||
    206          fStrokeInfo.fJoin != paint.skPaint().getStrokeJoin())) {
    207         return true;
    208     }
    209 
    210     // Mixed blobs must be regenerated.  We could probably figure out a way to do integer scrolls
    211     // for mixed blobs if this becomes an issue.
    212     if (this->hasBitmap() && this->hasDistanceField()) {
    213         // Identical viewmatrices and we can reuse in all cases
    214         if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) {
    215             return false;
    216         }
    217         return true;
    218     }
    219 
    220     if (this->hasBitmap()) {
    221         if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
    222             fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
    223             fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
    224             fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
    225             return true;
    226         }
    227 
    228         // We can update the positions in the cachedtextblobs without regenerating the whole blob,
    229         // but only for integer translations.
    230         // This cool bit of math will determine the necessary translation to apply to the already
    231         // generated vertex coordinates to move them to the correct position
    232         SkScalar transX = viewMatrix.getTranslateX() +
    233                           viewMatrix.getScaleX() * (x - fInitialX) +
    234                           viewMatrix.getSkewX() * (y - fInitialY) -
    235                           fInitialViewMatrix.getTranslateX();
    236         SkScalar transY = viewMatrix.getTranslateY() +
    237                           viewMatrix.getSkewY() * (x - fInitialX) +
    238                           viewMatrix.getScaleY() * (y - fInitialY) -
    239                           fInitialViewMatrix.getTranslateY();
    240         if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
    241             return true;
    242         }
    243     } else if (this->hasDistanceField()) {
    244         // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
    245         // distance field being generated, so we have to regenerate in those cases
    246         SkScalar newMaxScale = viewMatrix.getMaxScale();
    247         SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale();
    248         SkScalar scaleAdjust = newMaxScale / oldMaxScale;
    249         if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
    250             return true;
    251         }
    252     }
    253 
    254     // It is possible that a blob has neither distanceField nor bitmaptext.  This is in the case
    255     // when all of the runs inside the blob are drawn as paths.  In this case, we always regenerate
    256     // the blob anyways at flush time, so no need to regenerate explicitly
    257     return false;
    258 }
    259 
    260 inline std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::makeOp(
    261         const Run::SubRunInfo& info, int glyphCount, int run, int subRun,
    262         const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint,
    263         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
    264         bool useGammaCorrectDistanceTable, GrAtlasGlyphCache* cache) {
    265     GrMaskFormat format = info.maskFormat();
    266 
    267     std::unique_ptr<GrAtlasTextOp> op;
    268     if (info.drawAsDistanceFields()) {
    269         SkColor filteredColor = paint.filteredSkColor();
    270         bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
    271         op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable,
    272                                               useGammaCorrectDistanceTable, filteredColor,
    273                                               info.hasUseLCDText(), useBGR);
    274     } else {
    275         op = GrAtlasTextOp::MakeBitmap(format, glyphCount, cache);
    276     }
    277     GrAtlasTextOp::Geometry& geometry = op->geometry();
    278     geometry.fViewMatrix = viewMatrix;
    279     geometry.fBlob = SkRef(this);
    280     geometry.fRun = run;
    281     geometry.fSubRun = subRun;
    282     geometry.fColor =
    283             info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor();
    284     geometry.fX = x;
    285     geometry.fY = y;
    286     op->init();
    287 
    288     return std::move(op);
    289 }
    290 
    291 inline void GrAtlasTextBlob::flushRun(GrRenderTargetContext* rtc, const GrClip& clip, int run,
    292                                       const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    293                                       const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
    294                                       const GrDistanceFieldAdjustTable* distanceAdjustTable,
    295                                       GrAtlasGlyphCache* cache) {
    296     int lastRun = fRuns[run].fSubRunInfo.count() - 1;
    297     for (int subRun = 0; subRun <= lastRun; subRun++) {
    298         const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
    299         GrPaint grPaint;
    300         if (!paint.toGrPaint(info.maskFormat(), rtc, viewMatrix, &grPaint)) {
    301             continue;
    302         }
    303         int glyphCount = info.glyphCount();
    304         if (0 == glyphCount) {
    305             continue;
    306         }
    307 
    308         std::unique_ptr<GrMeshDrawOp> op(this->makeOp(info, glyphCount, run, subRun, viewMatrix, x,
    309                                                       y, paint, props, distanceAdjustTable,
    310                                                       rtc->isGammaCorrect(), cache));
    311         GrPipelineBuilder pipelineBuilder(std::move(grPaint), GrAAType::kNone);
    312 
    313         rtc->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
    314     }
    315 }
    316 
    317 static void calculate_translation(bool applyVM,
    318                                   const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
    319                                   const SkMatrix& currentViewMatrix, SkScalar currentX,
    320                                   SkScalar currentY, SkScalar* transX, SkScalar* transY) {
    321     if (applyVM) {
    322         *transX = newViewMatrix.getTranslateX() +
    323                   newViewMatrix.getScaleX() * (newX - currentX) +
    324                   newViewMatrix.getSkewX() * (newY - currentY) -
    325                   currentViewMatrix.getTranslateX();
    326 
    327         *transY = newViewMatrix.getTranslateY() +
    328                   newViewMatrix.getSkewY() * (newX - currentX) +
    329                   newViewMatrix.getScaleY() * (newY - currentY) -
    330                   currentViewMatrix.getTranslateY();
    331     } else {
    332         *transX = newX - currentX;
    333         *transY = newY - currentY;
    334     }
    335 }
    336 
    337 void GrAtlasTextBlob::flushBigGlyphs(GrContext* context, GrRenderTargetContext* rtc,
    338                                      const GrClip& clip, const SkPaint& paint,
    339                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    340                                      const SkIRect& clipBounds) {
    341     SkScalar transX, transY;
    342     for (int i = 0; i < fBigGlyphs.count(); i++) {
    343         GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i];
    344         calculate_translation(bigGlyph.fTreatAsBMP, viewMatrix, x, y,
    345                               fInitialViewMatrix, fInitialX, fInitialY, &transX, &transY);
    346         SkMatrix ctm;
    347         ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
    348         ctm.postTranslate(bigGlyph.fX + transX, bigGlyph.fY + transY);
    349         if (!bigGlyph.fTreatAsBMP) {
    350             ctm.postConcat(viewMatrix);
    351         }
    352 
    353         GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, bigGlyph.fPath, paint, ctm, nullptr,
    354                                             clipBounds, false);
    355     }
    356 }
    357 
    358 void GrAtlasTextBlob::flushRunAsPaths(GrContext* context, GrRenderTargetContext* rtc,
    359                                       const SkSurfaceProps& props, const SkTextBlobRunIterator& it,
    360                                       const GrClip& clip, const GrTextUtils::Paint& paint,
    361                                       SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
    362                                       const SkIRect& clipBounds, SkScalar x, SkScalar y) {
    363     size_t textLen = it.glyphCount() * sizeof(uint16_t);
    364     const SkPoint& offset = it.offset();
    365 
    366     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
    367     if (!runPaint.modifyForRun(it)) {
    368         return;
    369     }
    370 
    371     switch (it.positioning()) {
    372         case SkTextBlob::kDefault_Positioning:
    373             GrTextUtils::DrawTextAsPath(context, rtc, clip, runPaint, viewMatrix,
    374                                         (const char*)it.glyphs(), textLen, x + offset.x(),
    375                                         y + offset.y(), clipBounds);
    376             break;
    377         case SkTextBlob::kHorizontal_Positioning:
    378             GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
    379                                            (const char*)it.glyphs(), textLen, it.pos(), 1,
    380                                            SkPoint::Make(x, y + offset.y()), clipBounds);
    381             break;
    382         case SkTextBlob::kFull_Positioning:
    383             GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
    384                                            (const char*)it.glyphs(), textLen, it.pos(), 2,
    385                                            SkPoint::Make(x, y), clipBounds);
    386             break;
    387     }
    388 }
    389 
    390 void GrAtlasTextBlob::flushCached(GrContext* context, GrRenderTargetContext* rtc,
    391                                   const SkTextBlob* blob, const SkSurfaceProps& props,
    392                                   const GrDistanceFieldAdjustTable* distanceAdjustTable,
    393                                   const GrTextUtils::Paint& paint, SkDrawFilter* drawFilter,
    394                                   const GrClip& clip, const SkMatrix& viewMatrix,
    395                                   const SkIRect& clipBounds, SkScalar x, SkScalar y) {
    396     // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
    397     // it as paths
    398     SkTextBlobRunIterator it(blob);
    399     for (int run = 0; !it.done(); it.next(), run++) {
    400         if (fRuns[run].fDrawAsPaths) {
    401             this->flushRunAsPaths(context, rtc, props, it, clip, paint, drawFilter, viewMatrix,
    402                                   clipBounds, x, y);
    403             continue;
    404         }
    405         this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
    406                        context->getAtlasGlyphCache());
    407     }
    408 
    409     // Now flush big glyphs
    410     this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
    411 }
    412 
    413 void GrAtlasTextBlob::flushThrowaway(GrContext* context, GrRenderTargetContext* rtc,
    414                                      const SkSurfaceProps& props,
    415                                      const GrDistanceFieldAdjustTable* distanceAdjustTable,
    416                                      const GrTextUtils::Paint& paint, const GrClip& clip,
    417                                      const SkMatrix& viewMatrix, const SkIRect& clipBounds,
    418                                      SkScalar x, SkScalar y) {
    419     for (int run = 0; run < fRunCount; run++) {
    420         this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
    421                        context->getAtlasGlyphCache());
    422     }
    423 
    424     // Now flush big glyphs
    425     this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
    426 }
    427 
    428 std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::test_makeOp(
    429         int glyphCount, int run, int subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    430         const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
    431         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache) {
    432     const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
    433     return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
    434                         distanceAdjustTable, false, cache);
    435 }
    436 
    437 void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
    438     SkASSERT_RELEASE(l.fSize == r.fSize);
    439     SkASSERT_RELEASE(l.fPool == r.fPool);
    440 
    441     SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
    442     SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
    443     SkASSERT_RELEASE(l.fBlurRec.fQuality == r.fBlurRec.fQuality);
    444 
    445     SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
    446     SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
    447     SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
    448 
    449     SkASSERT_RELEASE(l.fBigGlyphs.count() == r.fBigGlyphs.count());
    450     for (int i = 0; i < l.fBigGlyphs.count(); i++) {
    451         const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
    452         const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
    453 
    454         SkASSERT_RELEASE(lBigGlyph.fPath == rBigGlyph.fPath);
    455         // We can't assert that these have the same translations
    456     }
    457 
    458     SkASSERT_RELEASE(l.fKey == r.fKey);
    459     //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical
    460     SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale);
    461     SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
    462     SkASSERT_RELEASE(l.fTextType == r.fTextType);
    463 
    464     SkASSERT_RELEASE(l.fRunCount == r.fRunCount);
    465     for (int i = 0; i < l.fRunCount; i++) {
    466         const Run& lRun = l.fRuns[i];
    467         const Run& rRun = r.fRuns[i];
    468 
    469         if (lRun.fTypeface.get()) {
    470             SkASSERT_RELEASE(rRun.fTypeface.get());
    471             SkASSERT_RELEASE(SkTypeface::Equal(lRun.fTypeface.get(), rRun.fTypeface.get()));
    472         } else {
    473             SkASSERT_RELEASE(!rRun.fTypeface.get());
    474         }
    475 
    476 
    477         SkASSERT_RELEASE(lRun.fDescriptor.getDesc());
    478         SkASSERT_RELEASE(rRun.fDescriptor.getDesc());
    479         SkASSERT_RELEASE(*lRun.fDescriptor.getDesc() == *rRun.fDescriptor.getDesc());
    480 
    481         if (lRun.fOverrideDescriptor.get()) {
    482             SkASSERT_RELEASE(lRun.fOverrideDescriptor->getDesc());
    483             SkASSERT_RELEASE(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());
    484             SkASSERT_RELEASE(*lRun.fOverrideDescriptor->getDesc() ==
    485                              *rRun.fOverrideDescriptor->getDesc());
    486         } else {
    487             SkASSERT_RELEASE(!rRun.fOverrideDescriptor.get());
    488         }
    489 
    490         // color can be changed
    491         //SkASSERT(lRun.fColor == rRun.fColor);
    492         SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
    493         SkASSERT_RELEASE(lRun.fDrawAsPaths == rRun.fDrawAsPaths);
    494 
    495         SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
    496         for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
    497             const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j];
    498             const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j];
    499 
    500             // TODO we can do this check, but we have to apply the VM to the old vertex bounds
    501             //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds());
    502 
    503             if (lSubRun.strike()) {
    504                 SkASSERT_RELEASE(rSubRun.strike());
    505                 SkASSERT_RELEASE(GrAtlasTextStrike::GetKey(*lSubRun.strike()) ==
    506                                  GrAtlasTextStrike::GetKey(*rSubRun.strike()));
    507 
    508             } else {
    509                 SkASSERT_RELEASE(!rSubRun.strike());
    510             }
    511 
    512             SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
    513             SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex());
    514             SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
    515             SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex());
    516             SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
    517             SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
    518             SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
    519         }
    520     }
    521 }
    522 
    523 void GrAtlasTextBlob::Run::SubRunInfo::computeTranslation(const SkMatrix& viewMatrix,
    524                                                           SkScalar x, SkScalar y, SkScalar* transX,
    525                                                           SkScalar* transY) {
    526     calculate_translation(!this->drawAsDistanceFields(), viewMatrix, x, y,
    527                           fCurrentViewMatrix, fX, fY, transX, transY);
    528     fCurrentViewMatrix = viewMatrix;
    529     fX = x;
    530     fY = y;
    531 }
    532