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