Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright 2016 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 "GrAtlasManager.h"
      9 #include "GrTextBlob.h"
     10 #include "GrTextTarget.h"
     11 #include "SkDistanceFieldGen.h"
     12 #include "ops/GrAtlasTextOp.h"
     13 
     14 enum RegenMask {
     15     kNoRegen    = 0x0,
     16     kRegenPos   = 0x1,
     17     kRegenCol   = 0x2,
     18     kRegenTex   = 0x4,
     19     kRegenGlyph = 0x8,
     20 };
     21 
     22 ////////////////////////////////////////////////////////////////////////////////////////////////////
     23 
     24 static void regen_positions(char* vertex, size_t vertexStride, SkScalar transX, SkScalar transY) {
     25     SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
     26     for (int i = 0; i < 4; ++i) {
     27         point->fX += transX;
     28         point->fY += transY;
     29         point = SkTAddOffset<SkPoint>(point, vertexStride);
     30     }
     31 }
     32 
     33 static void regen_colors(char* vertex, size_t vertexStride, GrColor color) {
     34     // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
     35     // vertices, hence vertexStride - sizeof(SkIPoint16)
     36     size_t colorOffset = vertexStride - sizeof(SkIPoint16) - sizeof(GrColor);
     37     GrColor* vcolor = reinterpret_cast<GrColor*>(vertex + colorOffset);
     38     for (int i = 0; i < 4; ++i) {
     39         *vcolor = color;
     40         vcolor = SkTAddOffset<GrColor>(vcolor, vertexStride);
     41     }
     42 }
     43 
     44 static void regen_texcoords(char* vertex, size_t vertexStride, const GrGlyph* glyph,
     45                             bool useDistanceFields) {
     46     // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
     47     // vertices, hence vertexStride - sizeof(SkIPoint16)
     48     size_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
     49 
     50     uint16_t u0, v0, u1, v1;
     51     SkASSERT(glyph);
     52     int width = glyph->fBounds.width();
     53     int height = glyph->fBounds.height();
     54 
     55     if (useDistanceFields) {
     56         u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
     57         v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
     58         u1 = u0 + width - 2 * SK_DistanceFieldInset;
     59         v1 = v0 + height - 2 * SK_DistanceFieldInset;
     60     } else {
     61         u0 = glyph->fAtlasLocation.fX;
     62         v0 = glyph->fAtlasLocation.fY;
     63         u1 = u0 + width;
     64         v1 = v0 + height;
     65     }
     66     // We pack the 2bit page index in the low bit of the u and v texture coords
     67     uint32_t pageIndex = glyph->pageIndex();
     68     SkASSERT(pageIndex < 4);
     69     uint16_t uBit = (pageIndex >> 1) & 0x1;
     70     uint16_t vBit = pageIndex & 0x1;
     71     u0 <<= 1;
     72     u0 |= uBit;
     73     v0 <<= 1;
     74     v0 |= vBit;
     75     u1 <<= 1;
     76     u1 |= uBit;
     77     v1 <<= 1;
     78     v1 |= vBit;
     79 
     80     uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
     81     textureCoords[0] = u0;
     82     textureCoords[1] = v0;
     83     textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
     84     textureCoords[0] = u0;
     85     textureCoords[1] = v1;
     86     textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
     87     textureCoords[0] = u1;
     88     textureCoords[1] = v0;
     89     textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
     90     textureCoords[0] = u1;
     91     textureCoords[1] = v1;
     92 
     93 #ifdef DISPLAY_PAGE_INDEX
     94     // Enable this to visualize the page from which each glyph is being drawn.
     95     // Green Red Magenta Cyan -> 0 1 2 3; Black -> error
     96     GrColor hackColor;
     97     switch (pageIndex) {
     98         case 0:
     99             hackColor = GrColorPackRGBA(0, 255, 0, 255);
    100             break;
    101         case 1:
    102             hackColor = GrColorPackRGBA(255, 0, 0, 255);;
    103             break;
    104         case 2:
    105             hackColor = GrColorPackRGBA(255, 0, 255, 255);
    106             break;
    107         case 3:
    108             hackColor = GrColorPackRGBA(0, 255, 255, 255);
    109             break;
    110         default:
    111             hackColor = GrColorPackRGBA(0, 0, 0, 255);
    112             break;
    113     }
    114     regen_colors(vertex, vertexStride, hackColor);
    115 #endif
    116 }
    117 
    118 GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
    119                                                  GrTextBlob* blob,
    120                                                  int runIdx, int subRunIdx,
    121                                                  const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    122                                                  GrColor color,
    123                                                  GrDeferredUploadTarget* uploadTarget,
    124                                                  GrStrikeCache* glyphCache,
    125                                                  GrAtlasManager* fullAtlasManager,
    126                                                  SkExclusiveStrikePtr* lazyCache)
    127         : fResourceProvider(resourceProvider)
    128         , fViewMatrix(viewMatrix)
    129         , fBlob(blob)
    130         , fUploadTarget(uploadTarget)
    131         , fGlyphCache(glyphCache)
    132         , fFullAtlasManager(fullAtlasManager)
    133         , fLazyCache(lazyCache)
    134         , fRun(&blob->fRuns[runIdx])
    135         , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
    136         , fColor(color) {
    137     // Compute translation if any
    138     fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
    139 
    140     // Because the GrStrikeCache may evict the strike a blob depends on using for
    141     // generating its texture coords, we have to track whether or not the strike has
    142     // been abandoned.  If it hasn't been abandoned, then we can use the GrGlyph*s as is
    143     // otherwise we have to get the new strike, and use that to get the correct glyphs.
    144     // Because we do not have the packed ids, and thus can't look up our glyphs in the
    145     // new strike, we instead keep our ref to the old strike and use the packed ids from
    146     // it.  These ids will still be valid as long as we hold the ref.  When we are done
    147     // updating our cache of the GrGlyph*s, we drop our ref on the old strike
    148     if (fSubRun->strike()->isAbandoned()) {
    149         fRegenFlags |= kRegenGlyph;
    150         fRegenFlags |= kRegenTex;
    151     }
    152     if (kARGB_GrMaskFormat != fSubRun->maskFormat() && fSubRun->color() != color) {
    153         fRegenFlags |= kRegenCol;
    154     }
    155     if (0.f != fTransX || 0.f != fTransY) {
    156         fRegenFlags |= kRegenPos;
    157     }
    158 }
    159 
    160 bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Result* result,
    161                                             bool regenPos, bool regenCol, bool regenTexCoords,
    162                                             bool regenGlyphs) {
    163     SkASSERT(!regenGlyphs || regenTexCoords);
    164     sk_sp<GrTextStrike> strike;
    165     if (regenTexCoords) {
    166         fSubRun->resetBulkUseToken();
    167 
    168         const SkDescriptor* desc = fSubRun->desc();
    169 
    170         if (!*fLazyCache || (*fLazyCache)->getDescriptor() != *desc) {
    171             SkScalerContextEffects effects;
    172             effects.fPathEffect = fRun->fPathEffect.get();
    173             effects.fMaskFilter = fRun->fMaskFilter.get();
    174             *fLazyCache =
    175                 SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *fRun->fTypeface);
    176         }
    177 
    178         if (regenGlyphs) {
    179             strike = fGlyphCache->getStrike((*fLazyCache)->getDescriptor());
    180         } else {
    181             strike = fSubRun->refStrike();
    182         }
    183     }
    184 
    185     bool hasW = fSubRun->hasWCoord();
    186     auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
    187     char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
    188                        fCurrGlyph * kVerticesPerGlyph * vertexStride;
    189     result->fFirstVertex = currVertex;
    190 
    191     for (int glyphIdx = fCurrGlyph; glyphIdx < (int)fSubRun->glyphCount(); glyphIdx++) {
    192         GrGlyph* glyph = nullptr;
    193         if (regenTexCoords) {
    194             size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
    195 
    196             if (regenGlyphs) {
    197                 // Get the id from the old glyph, and use the new strike to lookup
    198                 // the glyph.
    199                 SkPackedGlyphID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
    200                 fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyCache->get());
    201                 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
    202             }
    203             glyph = fBlob->fGlyphs[glyphOffset];
    204             SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
    205 
    206             if (!fFullAtlasManager->hasGlyph(glyph)) {
    207                 GrDrawOpAtlas::ErrorCode code;
    208                 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
    209                                               fFullAtlasManager, glyph,
    210                                               fLazyCache->get(), fSubRun->maskFormat(),
    211                                               fSubRun->needsTransform());
    212                 if (GrDrawOpAtlas::ErrorCode::kError == code) {
    213                     // Something horrible has happened - drop the op
    214                     return false;
    215                 }
    216                 else if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
    217                     fBrokenRun = glyphIdx > 0;
    218                     result->fFinished = false;
    219                     return true;
    220                 }
    221             }
    222             auto tokenTracker = fUploadTarget->tokenTracker();
    223             fFullAtlasManager->addGlyphToBulkAndSetUseToken(fSubRun->bulkUseToken(), glyph,
    224                                                             tokenTracker->nextDrawToken());
    225         }
    226 
    227         if (regenPos) {
    228             regen_positions(currVertex, vertexStride, fTransX, fTransY);
    229         }
    230         if (regenCol) {
    231             regen_colors(currVertex, vertexStride, fColor);
    232         }
    233         if (regenTexCoords) {
    234             regen_texcoords(currVertex, vertexStride, glyph, fSubRun->drawAsDistanceFields());
    235         }
    236 
    237         currVertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
    238         ++result->fGlyphsRegenerated;
    239         ++fCurrGlyph;
    240     }
    241 
    242     // We may have changed the color so update it here
    243     fSubRun->setColor(fColor);
    244     if (regenTexCoords) {
    245         if (regenGlyphs) {
    246             fSubRun->setStrike(std::move(strike));
    247         }
    248         fSubRun->setAtlasGeneration(fBrokenRun
    249                                     ? GrDrawOpAtlas::kInvalidAtlasGeneration
    250                                     : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
    251     } else {
    252         // For the non-texCoords case we need to ensure that we update the associated use tokens
    253         fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
    254                                            fUploadTarget->tokenTracker()->nextDrawToken(),
    255                                            fSubRun->maskFormat());
    256     }
    257     return true;
    258 }
    259 
    260 bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Result* result) {
    261     uint64_t currentAtlasGen = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
    262     // If regenerate() is called multiple times then the atlas gen may have changed. So we check
    263     // this each time.
    264     if (fSubRun->atlasGeneration() != currentAtlasGen) {
    265         fRegenFlags |= kRegenTex;
    266     }
    267 
    268     if (fRegenFlags) {
    269         return this->doRegen(result,
    270                              fRegenFlags & kRegenPos,
    271                              fRegenFlags & kRegenCol,
    272                              fRegenFlags & kRegenTex,
    273                              fRegenFlags & kRegenGlyph);
    274     } else {
    275         bool hasW = fSubRun->hasWCoord();
    276         auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
    277         result->fFinished = true;
    278         result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
    279         result->fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
    280                                fCurrGlyph * kVerticesPerGlyph * vertexStride;
    281         fCurrGlyph = fSubRun->glyphCount();
    282 
    283         // set use tokens for all of the glyphs in our subrun.  This is only valid if we
    284         // have a valid atlas generation
    285         fFullAtlasManager->setUseTokenBulk(*fSubRun->bulkUseToken(),
    286                                            fUploadTarget->tokenTracker()->nextDrawToken(),
    287                                            fSubRun->maskFormat());
    288         return true;
    289     }
    290     SK_ABORT("Should not get here");
    291     return false;
    292 }
    293