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 "GrPipelineBuilder.h" 12 #include "GrRenderTargetContext.h" 13 #include "GrTextUtils.h" 14 #include "SkColorFilter.h" 15 #include "SkDrawFilter.h" 16 #include "SkGlyphCache.h" 17 #include "SkTextBlobRunIterator.h" 18 #include "ops/GrAtlasTextOp.h" 19 20 sk_sp<GrAtlasTextBlob> GrAtlasTextBlob::Make(GrMemoryPool* pool, int glyphCount, int runCount) { 21 // We allocate size for the GrAtlasTextBlob itself, plus size for the vertices array, 22 // and size for the glyphIds array. 23 size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize; 24 size_t size = sizeof(GrAtlasTextBlob) + 25 verticesCount + 26 glyphCount * sizeof(GrGlyph**) + 27 sizeof(GrAtlasTextBlob::Run) * runCount; 28 29 void* allocation = pool->allocate(size); 30 if (CACHE_SANITY_CHECK) { 31 sk_bzero(allocation, size); 32 } 33 34 sk_sp<GrAtlasTextBlob> cacheBlob(new (allocation) GrAtlasTextBlob); 35 cacheBlob->fSize = size; 36 37 // setup offsets for vertices / glyphs 38 cacheBlob->fVertices = sizeof(GrAtlasTextBlob) + 39 reinterpret_cast<unsigned char*>(cacheBlob.get()); 40 cacheBlob->fGlyphs = reinterpret_cast<GrGlyph**>(cacheBlob->fVertices + verticesCount); 41 cacheBlob->fRuns = reinterpret_cast<GrAtlasTextBlob::Run*>(cacheBlob->fGlyphs + glyphCount); 42 43 // Initialize runs 44 for (int i = 0; i < runCount; i++) { 45 new (&cacheBlob->fRuns[i]) GrAtlasTextBlob::Run; 46 } 47 cacheBlob->fRunCount = runCount; 48 cacheBlob->fPool = pool; 49 return cacheBlob; 50 } 51 52 SkGlyphCache* GrAtlasTextBlob::setupCache(int runIndex, 53 const SkSurfaceProps& props, 54 uint32_t scalerContextFlags, 55 const SkPaint& skPaint, 56 const SkMatrix* viewMatrix) { 57 GrAtlasTextBlob::Run* run = &fRuns[runIndex]; 58 59 // if we have an override descriptor for the run, then we should use that 60 SkAutoDescriptor* desc = run->fOverrideDescriptor.get() ? run->fOverrideDescriptor.get() : 61 &run->fDescriptor; 62 SkScalerContextEffects effects; 63 skPaint.getScalerContextDescriptor(&effects, desc, props, scalerContextFlags, viewMatrix); 64 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); 65 run->fPathEffect = sk_ref_sp(effects.fPathEffect); 66 run->fRasterizer = sk_ref_sp(effects.fRasterizer); 67 run->fMaskFilter = sk_ref_sp(effects.fMaskFilter); 68 return SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc->getDesc()); 69 } 70 71 void GrAtlasTextBlob::appendGlyph(int runIndex, 72 const SkRect& positions, 73 GrColor color, 74 GrAtlasTextStrike* strike, 75 GrGlyph* glyph, 76 SkGlyphCache* cache, const SkGlyph& skGlyph, 77 SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) { 78 if (positions.isEmpty()) { 79 return; 80 } 81 82 // If the glyph is too large we fall back to paths 83 if (glyph->fTooLargeForAtlas) { 84 this->appendLargeGlyph(glyph, cache, skGlyph, x, y, scale, treatAsBMP); 85 return; 86 } 87 88 Run& run = fRuns[runIndex]; 89 GrMaskFormat format = glyph->fMaskFormat; 90 91 Run::SubRunInfo* subRun = &run.fSubRunInfo.back(); 92 if (run.fInitialized && subRun->maskFormat() != format) { 93 subRun = &run.push_back(); 94 subRun->setStrike(strike); 95 } else if (!run.fInitialized) { 96 subRun->setStrike(strike); 97 } 98 99 run.fInitialized = true; 100 101 size_t vertexStride = GetVertexStride(format); 102 103 subRun->setMaskFormat(format); 104 105 subRun->joinGlyphBounds(positions); 106 subRun->setColor(color); 107 108 intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex()); 109 110 if (kARGB_GrMaskFormat != glyph->fMaskFormat) { 111 // V0 112 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); 113 position->set(positions.fLeft, positions.fTop); 114 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 115 *colorPtr = color; 116 vertex += vertexStride; 117 118 // V1 119 position = reinterpret_cast<SkPoint*>(vertex); 120 position->set(positions.fLeft, positions.fBottom); 121 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 122 *colorPtr = color; 123 vertex += vertexStride; 124 125 // V2 126 position = reinterpret_cast<SkPoint*>(vertex); 127 position->set(positions.fRight, positions.fBottom); 128 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 129 *colorPtr = color; 130 vertex += vertexStride; 131 132 // V3 133 position = reinterpret_cast<SkPoint*>(vertex); 134 position->set(positions.fRight, positions.fTop); 135 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 136 *colorPtr = color; 137 } else { 138 // V0 139 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); 140 position->set(positions.fLeft, positions.fTop); 141 vertex += vertexStride; 142 143 // V1 144 position = reinterpret_cast<SkPoint*>(vertex); 145 position->set(positions.fLeft, positions.fBottom); 146 vertex += vertexStride; 147 148 // V2 149 position = reinterpret_cast<SkPoint*>(vertex); 150 position->set(positions.fRight, positions.fBottom); 151 vertex += vertexStride; 152 153 // V3 154 position = reinterpret_cast<SkPoint*>(vertex); 155 position->set(positions.fRight, positions.fTop); 156 } 157 subRun->appendVertices(vertexStride); 158 fGlyphs[subRun->glyphEndIndex()] = glyph; 159 subRun->glyphAppended(); 160 } 161 162 void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, SkGlyphCache* cache, const SkGlyph& skGlyph, 163 SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) { 164 if (nullptr == glyph->fPath) { 165 const SkPath* glyphPath = cache->findPath(skGlyph); 166 if (!glyphPath) { 167 return; 168 } 169 170 glyph->fPath = new SkPath(*glyphPath); 171 } 172 fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, treatAsBMP)); 173 } 174 175 bool GrAtlasTextBlob::mustRegenerate(const GrTextUtils::Paint& paint, 176 const SkMaskFilter::BlurRec& blurRec, 177 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { 178 // If we have LCD text then our canonical color will be set to transparent, in this case we have 179 // to regenerate the blob on any color change 180 // We use the grPaint to get any color filter effects 181 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT && 182 fFilteredPaintColor != paint.filteredSkColor()) { 183 return true; 184 } 185 186 if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { 187 return true; 188 } 189 190 if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) { 191 return true; 192 } 193 194 // We only cache one masked version 195 if (fKey.fHasBlur && 196 (fBlurRec.fSigma != blurRec.fSigma || 197 fBlurRec.fStyle != blurRec.fStyle || 198 fBlurRec.fQuality != blurRec.fQuality)) { 199 return true; 200 } 201 202 // Similarly, we only cache one version for each style 203 if (fKey.fStyle != SkPaint::kFill_Style && 204 (fStrokeInfo.fFrameWidth != paint.skPaint().getStrokeWidth() || 205 fStrokeInfo.fMiterLimit != paint.skPaint().getStrokeMiter() || 206 fStrokeInfo.fJoin != paint.skPaint().getStrokeJoin())) { 207 return true; 208 } 209 210 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls 211 // for mixed blobs if this becomes an issue. 212 if (this->hasBitmap() && this->hasDistanceField()) { 213 // Identical viewmatrices and we can reuse in all cases 214 if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) { 215 return false; 216 } 217 return true; 218 } 219 220 if (this->hasBitmap()) { 221 if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() || 222 fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() || 223 fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() || 224 fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) { 225 return true; 226 } 227 228 // We can update the positions in the cachedtextblobs without regenerating the whole blob, 229 // but only for integer translations. 230 // This cool bit of math will determine the necessary translation to apply to the already 231 // generated vertex coordinates to move them to the correct position 232 SkScalar transX = viewMatrix.getTranslateX() + 233 viewMatrix.getScaleX() * (x - fInitialX) + 234 viewMatrix.getSkewX() * (y - fInitialY) - 235 fInitialViewMatrix.getTranslateX(); 236 SkScalar transY = viewMatrix.getTranslateY() + 237 viewMatrix.getSkewY() * (x - fInitialX) + 238 viewMatrix.getScaleY() * (y - fInitialY) - 239 fInitialViewMatrix.getTranslateY(); 240 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) { 241 return true; 242 } 243 } else if (this->hasDistanceField()) { 244 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different 245 // distance field being generated, so we have to regenerate in those cases 246 SkScalar newMaxScale = viewMatrix.getMaxScale(); 247 SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale(); 248 SkScalar scaleAdjust = newMaxScale / oldMaxScale; 249 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) { 250 return true; 251 } 252 } 253 254 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case 255 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate 256 // the blob anyways at flush time, so no need to regenerate explicitly 257 return false; 258 } 259 260 inline std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::makeOp( 261 const Run::SubRunInfo& info, int glyphCount, int run, int subRun, 262 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint, 263 const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, 264 bool useGammaCorrectDistanceTable, GrAtlasGlyphCache* cache) { 265 GrMaskFormat format = info.maskFormat(); 266 267 std::unique_ptr<GrAtlasTextOp> op; 268 if (info.drawAsDistanceFields()) { 269 SkColor filteredColor = paint.filteredSkColor(); 270 bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry()); 271 op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable, 272 useGammaCorrectDistanceTable, filteredColor, 273 info.hasUseLCDText(), useBGR); 274 } else { 275 op = GrAtlasTextOp::MakeBitmap(format, glyphCount, cache); 276 } 277 GrAtlasTextOp::Geometry& geometry = op->geometry(); 278 geometry.fViewMatrix = viewMatrix; 279 geometry.fBlob = SkRef(this); 280 geometry.fRun = run; 281 geometry.fSubRun = subRun; 282 geometry.fColor = 283 info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor(); 284 geometry.fX = x; 285 geometry.fY = y; 286 op->init(); 287 288 return std::move(op); 289 } 290 291 inline void GrAtlasTextBlob::flushRun(GrRenderTargetContext* rtc, const GrClip& clip, int run, 292 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, 293 const GrTextUtils::Paint& paint, const SkSurfaceProps& props, 294 const GrDistanceFieldAdjustTable* distanceAdjustTable, 295 GrAtlasGlyphCache* cache) { 296 int lastRun = fRuns[run].fSubRunInfo.count() - 1; 297 for (int subRun = 0; subRun <= lastRun; subRun++) { 298 const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun]; 299 GrPaint grPaint; 300 if (!paint.toGrPaint(info.maskFormat(), rtc, viewMatrix, &grPaint)) { 301 continue; 302 } 303 int glyphCount = info.glyphCount(); 304 if (0 == glyphCount) { 305 continue; 306 } 307 308 std::unique_ptr<GrMeshDrawOp> op(this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, 309 y, paint, props, distanceAdjustTable, 310 rtc->isGammaCorrect(), cache)); 311 GrPipelineBuilder pipelineBuilder(std::move(grPaint), GrAAType::kNone); 312 313 rtc->addMeshDrawOp(pipelineBuilder, clip, std::move(op)); 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<GrMeshDrawOp> 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 const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun]; 433 return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props, 434 distanceAdjustTable, false, cache); 435 } 436 437 void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) { 438 SkASSERT_RELEASE(l.fSize == r.fSize); 439 SkASSERT_RELEASE(l.fPool == r.fPool); 440 441 SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma); 442 SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle); 443 SkASSERT_RELEASE(l.fBlurRec.fQuality == r.fBlurRec.fQuality); 444 445 SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth); 446 SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit); 447 SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin); 448 449 SkASSERT_RELEASE(l.fBigGlyphs.count() == r.fBigGlyphs.count()); 450 for (int i = 0; i < l.fBigGlyphs.count(); i++) { 451 const BigGlyph& lBigGlyph = l.fBigGlyphs[i]; 452 const BigGlyph& rBigGlyph = r.fBigGlyphs[i]; 453 454 SkASSERT_RELEASE(lBigGlyph.fPath == rBigGlyph.fPath); 455 // We can't assert that these have the same translations 456 } 457 458 SkASSERT_RELEASE(l.fKey == r.fKey); 459 //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical 460 SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale); 461 SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale); 462 SkASSERT_RELEASE(l.fTextType == r.fTextType); 463 464 SkASSERT_RELEASE(l.fRunCount == r.fRunCount); 465 for (int i = 0; i < l.fRunCount; i++) { 466 const Run& lRun = l.fRuns[i]; 467 const Run& rRun = r.fRuns[i]; 468 469 if (lRun.fTypeface.get()) { 470 SkASSERT_RELEASE(rRun.fTypeface.get()); 471 SkASSERT_RELEASE(SkTypeface::Equal(lRun.fTypeface.get(), rRun.fTypeface.get())); 472 } else { 473 SkASSERT_RELEASE(!rRun.fTypeface.get()); 474 } 475 476 477 SkASSERT_RELEASE(lRun.fDescriptor.getDesc()); 478 SkASSERT_RELEASE(rRun.fDescriptor.getDesc()); 479 SkASSERT_RELEASE(*lRun.fDescriptor.getDesc() == *rRun.fDescriptor.getDesc()); 480 481 if (lRun.fOverrideDescriptor.get()) { 482 SkASSERT_RELEASE(lRun.fOverrideDescriptor->getDesc()); 483 SkASSERT_RELEASE(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc()); 484 SkASSERT_RELEASE(*lRun.fOverrideDescriptor->getDesc() == 485 *rRun.fOverrideDescriptor->getDesc()); 486 } else { 487 SkASSERT_RELEASE(!rRun.fOverrideDescriptor.get()); 488 } 489 490 // color can be changed 491 //SkASSERT(lRun.fColor == rRun.fColor); 492 SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized); 493 SkASSERT_RELEASE(lRun.fDrawAsPaths == rRun.fDrawAsPaths); 494 495 SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count()); 496 for(int j = 0; j < lRun.fSubRunInfo.count(); j++) { 497 const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j]; 498 const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j]; 499 500 // TODO we can do this check, but we have to apply the VM to the old vertex bounds 501 //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds()); 502 503 if (lSubRun.strike()) { 504 SkASSERT_RELEASE(rSubRun.strike()); 505 SkASSERT_RELEASE(GrAtlasTextStrike::GetKey(*lSubRun.strike()) == 506 GrAtlasTextStrike::GetKey(*rSubRun.strike())); 507 508 } else { 509 SkASSERT_RELEASE(!rSubRun.strike()); 510 } 511 512 SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex()); 513 SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex()); 514 SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex()); 515 SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex()); 516 SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat()); 517 SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields()); 518 SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText()); 519 } 520 } 521 } 522 523 void GrAtlasTextBlob::Run::SubRunInfo::computeTranslation(const SkMatrix& viewMatrix, 524 SkScalar x, SkScalar y, SkScalar* transX, 525 SkScalar* transY) { 526 calculate_translation(!this->drawAsDistanceFields(), viewMatrix, x, y, 527 fCurrentViewMatrix, fX, fY, transX, transY); 528 fCurrentViewMatrix = viewMatrix; 529 fX = x; 530 fY = y; 531 } 532