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->get()); 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