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