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 "GrOpFlushState.h" 11#include "GrTextUtils.h" 12 13#include "SkDistanceFieldGen.h" 14#include "SkGlyphCache.h" 15 16#include "ops/GrAtlasTextOp.h" 17 18//////////////////////////////////////////////////////////////////////////////////////////////////// 19// A large template to handle regenerating the vertices of a textblob with as few branches as 20// possible 21template <bool regenPos, bool regenCol, bool regenTexCoords> 22inline 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 140template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs> 141void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target, GrAtlasGlyphCache* fontCache, 142 GrBlobRegenHelper* helper, Run* run, Run::SubRunInfo* info, 143 SkAutoGlyphCache* lazyCache, int glyphCount, size_t vertexStride, 144 GrColor color, SkScalar transX, SkScalar transY) const { 145 SkASSERT(lazyCache); 146 static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs"); 147 GrAtlasTextStrike* strike = nullptr; 148 if (regenTexCoords) { 149 info->resetBulkUseToken(); 150 151 const SkDescriptor* desc = (run->fOverrideDescriptor && !info->drawAsDistanceFields()) 152 ? run->fOverrideDescriptor->getDesc() 153 : run->fDescriptor.getDesc(); 154 155 if (!*lazyCache || (*lazyCache)->getDescriptor() != *desc) { 156 SkScalerContextEffects effects; 157 effects.fPathEffect = run->fPathEffect.get(); 158 effects.fRasterizer = run->fRasterizer.get(); 159 effects.fMaskFilter = run->fMaskFilter.get(); 160 lazyCache->reset(SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc)); 161 } 162 163 if (regenGlyphs) { 164 strike = fontCache->getStrike(lazyCache->get()); 165 } else { 166 strike = info->strike(); 167 } 168 } 169 170 bool brokenRun = false; 171 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { 172 GrGlyph* glyph = nullptr; 173 int log2Width = 0, log2Height = 0; 174 if (regenTexCoords) { 175 size_t glyphOffset = glyphIdx + info->glyphStartIndex(); 176 177 if (regenGlyphs) { 178 // Get the id from the old glyph, and use the new strike to lookup 179 // the glyph. 180 GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID; 181 fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), lazyCache->get()); 182 SkASSERT(id == fGlyphs[glyphOffset]->fPackedID); 183 } 184 glyph = fGlyphs[glyphOffset]; 185 SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat()); 186 187 if (!fontCache->hasGlyph(glyph) && 188 !strike->addGlyphToAtlas(target, glyph, lazyCache->get(), info->maskFormat())) { 189 helper->flush(); 190 brokenRun = glyphIdx > 0; 191 192 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target, 193 glyph, 194 lazyCache->get(), 195 info->maskFormat()); 196 SkASSERT(success); 197 } 198 fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph, 199 target->nextDrawToken()); 200 log2Width = fontCache->log2Width(info->maskFormat()); 201 log2Height = fontCache->log2Height(info->maskFormat()); 202 } 203 204 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices); 205 vertex += info->vertexStartIndex(); 206 vertex += vertexStride * glyphIdx * GrAtlasTextOp::kVerticesPerGlyph; 207 regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride, 208 info->drawAsDistanceFields(), transX, 209 transY, log2Width, log2Height, color); 210 helper->incGlyphCount(); 211 } 212 213 // We may have changed the color so update it here 214 info->setColor(color); 215 if (regenTexCoords) { 216 if (regenGlyphs) { 217 info->setStrike(strike); 218 } 219 info->setAtlasGeneration(brokenRun ? GrDrawOpAtlas::kInvalidAtlasGeneration 220 : fontCache->atlasGeneration(info->maskFormat())); 221 } 222} 223 224enum RegenMask { 225 kNoRegen = 0x0, 226 kRegenPos = 0x1, 227 kRegenCol = 0x2, 228 kRegenTex = 0x4, 229 kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs 230 231 // combinations 232 kRegenPosCol = kRegenPos | kRegenCol, 233 kRegenPosTex = kRegenPos | kRegenTex, 234 kRegenPosTexGlyph = kRegenPos | kRegenGlyph, 235 kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex, 236 kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph, 237 kRegenColTex = kRegenCol | kRegenTex, 238 kRegenColTexGlyph = kRegenCol | kRegenGlyph, 239}; 240 241#define REGEN_ARGS target, fontCache, helper, &run, &info, lazyCache, \ 242 *glyphCount, vertexStride, color, transX, transY 243 244void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target, 245 GrAtlasGlyphCache* fontCache, 246 GrBlobRegenHelper* helper, 247 int runIndex, int subRunIndex, SkAutoGlyphCache* lazyCache, 248 size_t vertexStride, const SkMatrix& viewMatrix, 249 SkScalar x, SkScalar y, GrColor color, 250 void** vertices, size_t* byteCount, int* glyphCount) { 251 Run& run = fRuns[runIndex]; 252 Run::SubRunInfo& info = run.fSubRunInfo[subRunIndex]; 253 254 uint64_t currentAtlasGen = fontCache->atlasGeneration(info.maskFormat()); 255 256 // Compute translation if any 257 SkScalar transX, transY; 258 info.computeTranslation(viewMatrix, x, y, &transX, &transY); 259 260 // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for 261 // generating its texture coords, we have to track whether or not the strike has 262 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is 263 // otherwise we have to get the new strike, and use that to get the correct glyphs. 264 // Because we do not have the packed ids, and thus can't look up our glyphs in the 265 // new strike, we instead keep our ref to the old strike and use the packed ids from 266 // it. These ids will still be valid as long as we hold the ref. When we are done 267 // updating our cache of the GrGlyph*s, we drop our ref on the old strike 268 bool regenerateGlyphs = info.strike()->isAbandoned(); 269 bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen || 270 regenerateGlyphs; 271 bool regenerateColors = kARGB_GrMaskFormat != info.maskFormat() && 272 info.color() != color; 273 bool regeneratePositions = transX != 0.f || transY != 0.f; 274 *glyphCount = info.glyphCount(); 275 276 uint32_t regenMaskBits = kNoRegen; 277 regenMaskBits |= regeneratePositions ? kRegenPos : 0; 278 regenMaskBits |= regenerateColors ? kRegenCol : 0; 279 regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0; 280 regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0; 281 RegenMask regenMask = (RegenMask)regenMaskBits; 282 283 switch (regenMask) { 284 case kRegenPos: 285 this->regenInOp<true, false, false, false>(REGEN_ARGS); 286 break; 287 case kRegenCol: 288 this->regenInOp<false, true, false, false>(REGEN_ARGS); 289 break; 290 case kRegenTex: 291 this->regenInOp<false, false, true, false>(REGEN_ARGS); 292 break; 293 case kRegenGlyph: 294 this->regenInOp<false, false, true, true>(REGEN_ARGS); 295 break; 296 297 // combinations 298 case kRegenPosCol: 299 this->regenInOp<true, true, false, false>(REGEN_ARGS); 300 break; 301 case kRegenPosTex: 302 this->regenInOp<true, false, true, false>(REGEN_ARGS); 303 break; 304 case kRegenPosTexGlyph: 305 this->regenInOp<true, false, true, true>(REGEN_ARGS); 306 break; 307 case kRegenPosColTex: 308 this->regenInOp<true, true, true, false>(REGEN_ARGS); 309 break; 310 case kRegenPosColTexGlyph: 311 this->regenInOp<true, true, true, true>(REGEN_ARGS); 312 break; 313 case kRegenColTex: 314 this->regenInOp<false, true, true, false>(REGEN_ARGS); 315 break; 316 case kRegenColTexGlyph: 317 this->regenInOp<false, true, true, true>(REGEN_ARGS); 318 break; 319 case kNoRegen: 320 helper->incGlyphCount(*glyphCount); 321 322 // set use tokens for all of the glyphs in our subrun. This is only valid if we 323 // have a valid atlas generation 324 fontCache->setUseTokenBulk(*info.bulkUseToken(), target->nextDrawToken(), 325 info.maskFormat()); 326 break; 327 } 328 329 *byteCount = info.byteCount(); 330 *vertices = fVertices + info.vertexStartIndex(); 331} 332