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 "GrAtlasTextBlob.h" 9 10 #include "GrBatchFlushState.h" 11 #include "GrTextUtils.h" 12 13 #include "SkDistanceFieldGen.h" 14 #include "SkGlyphCache.h" 15 16 #include "batches/GrAtlasTextBatch.h" 17 18 //////////////////////////////////////////////////////////////////////////////////////////////////// 19 // A large template to handle regenerating the vertices of a textblob with as few branches as 20 // possible 21 template <bool regenPos, bool regenCol, bool regenTexCoords> 22 inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexStride, 23 bool useDistanceFields, SkScalar transX, SkScalar transY, 24 int32_t log2Width, int32_t log2Height, 25 GrColor color) { 26 int u0, v0, u1, v1; 27 if (regenTexCoords) { 28 SkASSERT(glyph); 29 int width = glyph->fBounds.width(); 30 int height = glyph->fBounds.height(); 31 32 if (useDistanceFields) { 33 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset; 34 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset; 35 u1 = u0 + width - 2 * SK_DistanceFieldInset; 36 v1 = v0 + height - 2 * SK_DistanceFieldInset; 37 } else { 38 u0 = glyph->fAtlasLocation.fX; 39 v0 = glyph->fAtlasLocation.fY; 40 u1 = u0 + width; 41 v1 = v0 + height; 42 } 43 44 // normalize 45 u0 *= 65535; 46 u0 >>= log2Width; 47 u1 *= 65535; 48 u1 >>= log2Width; 49 v0 *= 65535; 50 v0 >>= log2Height; 51 v1 *= 65535; 52 v1 >>= log2Height; 53 SkASSERT(u0 >= 0 && u0 <= 65535); 54 SkASSERT(u1 >= 0 && u1 <= 65535); 55 SkASSERT(v0 >= 0 && v0 <= 65535); 56 SkASSERT(v1 >= 0 && v1 <= 65535); 57 } 58 59 // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color 60 // vertices, hence vertexStride - sizeof(SkIPoint16) 61 intptr_t colorOffset = sizeof(SkPoint); 62 intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16); 63 64 // V0 65 if (regenPos) { 66 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); 67 point->fX += transX; 68 point->fY += transY; 69 } 70 71 if (regenCol) { 72 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); 73 *vcolor = color; 74 } 75 76 if (regenTexCoords) { 77 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset); 78 textureCoords[0] = (uint16_t) u0; 79 textureCoords[1] = (uint16_t) v0; 80 } 81 vertex += vertexStride; 82 83 // V1 84 if (regenPos) { 85 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); 86 point->fX += transX; 87 point->fY += transY; 88 } 89 90 if (regenCol) { 91 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); 92 *vcolor = color; 93 } 94 95 if (regenTexCoords) { 96 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset); 97 textureCoords[0] = (uint16_t)u0; 98 textureCoords[1] = (uint16_t)v1; 99 } 100 vertex += vertexStride; 101 102 // V2 103 if (regenPos) { 104 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); 105 point->fX += transX; 106 point->fY += transY; 107 } 108 109 if (regenCol) { 110 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); 111 *vcolor = color; 112 } 113 114 if (regenTexCoords) { 115 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset); 116 textureCoords[0] = (uint16_t)u1; 117 textureCoords[1] = (uint16_t)v1; 118 } 119 vertex += vertexStride; 120 121 // V3 122 if (regenPos) { 123 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); 124 point->fX += transX; 125 point->fY += transY; 126 } 127 128 if (regenCol) { 129 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset); 130 *vcolor = color; 131 } 132 133 if (regenTexCoords) { 134 uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset); 135 textureCoords[0] = (uint16_t)u1; 136 textureCoords[1] = (uint16_t)v0; 137 } 138 } 139 140 template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs> 141 void GrAtlasTextBlob::regenInBatch(GrDrawBatch::Target* target, 142 GrBatchFontCache* fontCache, 143 GrBlobRegenHelper *helper, 144 Run* run, 145 Run::SubRunInfo* info, SkGlyphCache** cache, 146 SkTypeface** typeface, GrFontScaler** scaler, 147 const SkDescriptor** desc, 148 int glyphCount, size_t vertexStride, 149 GrColor color, SkScalar transX, 150 SkScalar transY) const { 151 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs"); 152 GrBatchTextStrike* strike = nullptr; 153 if (regenTexCoords) { 154 info->resetBulkUseToken(); 155 156 // We can reuse if we have a valid strike and our descriptors / typeface are the 157 // same. The override descriptor is only for the non distance field text within 158 // a run 159 const SkDescriptor* newDesc = (run->fOverrideDescriptor && !info->drawAsDistanceFields()) ? 160 run->fOverrideDescriptor->getDesc() : 161 run->fDescriptor.getDesc(); 162 if (!*cache || !SkTypeface::Equal(*typeface, run->fTypeface) || 163 !((*desc)->equals(*newDesc))) { 164 if (*cache) { 165 SkGlyphCache::AttachCache(*cache); 166 } 167 *desc = newDesc; 168 *cache = SkGlyphCache::DetachCache(run->fTypeface, *desc); 169 *scaler = GrTextUtils::GetGrFontScaler(*cache); 170 *typeface = run->fTypeface; 171 } 172 173 if (regenGlyphs) { 174 strike = fontCache->getStrike(*scaler); 175 } else { 176 strike = info->strike(); 177 } 178 } 179 180 bool brokenRun = false; 181 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { 182 GrGlyph* glyph = nullptr; 183 int log2Width = 0, log2Height = 0; 184 if (regenTexCoords) { 185 size_t glyphOffset = glyphIdx + info->glyphStartIndex(); 186 187 if (regenGlyphs) { 188 // Get the id from the old glyph, and use the new strike to lookup 189 // the glyph. 190 GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID; 191 fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), *scaler); 192 SkASSERT(id == fGlyphs[glyphOffset]->fPackedID); 193 } 194 glyph = fGlyphs[glyphOffset]; 195 SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat()); 196 197 if (!fontCache->hasGlyph(glyph) && 198 !strike->addGlyphToAtlas(target, glyph, *scaler, info->maskFormat())) { 199 helper->flush(); 200 brokenRun = glyphIdx > 0; 201 202 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target, 203 glyph, 204 *scaler, 205 info->maskFormat()); 206 SkASSERT(success); 207 } 208 fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph, 209 target->currentToken()); 210 log2Width = fontCache->log2Width(info->maskFormat()); 211 log2Height = fontCache->log2Height(info->maskFormat()); 212 } 213 214 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices); 215 vertex += info->vertexStartIndex(); 216 vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph; 217 regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride, 218 info->drawAsDistanceFields(), transX, 219 transY, log2Width, log2Height, color); 220 helper->incGlyphCount(); 221 } 222 223 // We may have changed the color so update it here 224 info->setColor(color); 225 if (regenTexCoords) { 226 if (regenGlyphs) { 227 info->setStrike(strike); 228 } 229 info->setAtlasGeneration(brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration : 230 fontCache->atlasGeneration(info->maskFormat())); 231 } 232 } 233 234 enum RegenMask { 235 kNoRegen = 0x0, 236 kRegenPos = 0x1, 237 kRegenCol = 0x2, 238 kRegenTex = 0x4, 239 kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs 240 241 // combinations 242 kRegenPosCol = kRegenPos | kRegenCol, 243 kRegenPosTex = kRegenPos | kRegenTex, 244 kRegenPosTexGlyph = kRegenPos | kRegenGlyph, 245 kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex, 246 kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph, 247 kRegenColTex = kRegenCol | kRegenTex, 248 kRegenColTexGlyph = kRegenCol | kRegenGlyph, 249 }; 250 251 #define REGEN_ARGS target, fontCache, helper, &run, &info, cache, typeface, scaler, desc, \ 252 *glyphCount, vertexStride, color, transX, transY 253 254 void GrAtlasTextBlob::regenInBatch(GrDrawBatch::Target* target, 255 GrBatchFontCache* fontCache, 256 GrBlobRegenHelper *helper, 257 int runIndex, int subRunIndex, SkGlyphCache** cache, 258 SkTypeface** typeface, GrFontScaler** scaler, 259 const SkDescriptor** desc, size_t vertexStride, 260 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, 261 GrColor color, 262 void** vertices, size_t* byteCount, int* glyphCount) { 263 Run& run = fRuns[runIndex]; 264 Run::SubRunInfo& info = run.fSubRunInfo[subRunIndex]; 265 266 uint64_t currentAtlasGen = fontCache->atlasGeneration(info.maskFormat()); 267 268 // Compute translation if any 269 SkScalar transX, transY; 270 info.computeTranslation(viewMatrix, x, y, &transX, &transY); 271 272 // Because the GrBatchFontCache may evict the strike a blob depends on using for 273 // generating its texture coords, we have to track whether or not the strike has 274 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is 275 // otherwise we have to get the new strike, and use that to get the correct glyphs. 276 // Because we do not have the packed ids, and thus can't look up our glyphs in the 277 // new strike, we instead keep our ref to the old strike and use the packed ids from 278 // it. These ids will still be valid as long as we hold the ref. When we are done 279 // updating our cache of the GrGlyph*s, we drop our ref on the old strike 280 bool regenerateGlyphs = info.strike()->isAbandoned(); 281 bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen || 282 regenerateGlyphs; 283 bool regenerateColors = kARGB_GrMaskFormat != info.maskFormat() && 284 info.color() != color; 285 bool regeneratePositions = transX != 0.f || transY != 0.f; 286 *glyphCount = info.glyphCount(); 287 288 uint32_t regenMaskBits = kNoRegen; 289 regenMaskBits |= regeneratePositions ? kRegenPos : 0; 290 regenMaskBits |= regenerateColors ? kRegenCol : 0; 291 regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0; 292 regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0; 293 RegenMask regenMask = (RegenMask)regenMaskBits; 294 295 switch (regenMask) { 296 case kRegenPos: this->regenInBatch<true, false, false, false>(REGEN_ARGS); break; 297 case kRegenCol: this->regenInBatch<false, true, false, false>(REGEN_ARGS); break; 298 case kRegenTex: this->regenInBatch<false, false, true, false>(REGEN_ARGS); break; 299 case kRegenGlyph: this->regenInBatch<false, false, true, true>(REGEN_ARGS); break; 300 301 // combinations 302 case kRegenPosCol: this->regenInBatch<true, true, false, false>(REGEN_ARGS); break; 303 case kRegenPosTex: this->regenInBatch<true, false, true, false>(REGEN_ARGS); break; 304 case kRegenPosTexGlyph: this->regenInBatch<true, false, true, true>(REGEN_ARGS); break; 305 case kRegenPosColTex: this->regenInBatch<true, true, true, false>(REGEN_ARGS); break; 306 case kRegenPosColTexGlyph: this->regenInBatch<true, true, true, true>(REGEN_ARGS); break; 307 case kRegenColTex: this->regenInBatch<false, true, true, false>(REGEN_ARGS); break; 308 case kRegenColTexGlyph: this->regenInBatch<false, true, true, true>(REGEN_ARGS); break; 309 case kNoRegen: 310 helper->incGlyphCount(*glyphCount); 311 312 // set use tokens for all of the glyphs in our subrun. This is only valid if we 313 // have a valid atlas generation 314 fontCache->setUseTokenBulk(*info.bulkUseToken(), target->currentToken(), 315 info.maskFormat()); 316 break; 317 } 318 319 *byteCount = info.byteCount(); 320 *vertices = fVertices + info.vertexStartIndex(); 321 } 322