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 10 #include "GrBlurUtils.h" 11 #include "GrContext.h" 12 #include "GrDrawContext.h" 13 #include "GrTextUtils.h" 14 #include "SkColorFilter.h" 15 #include "SkDrawFilter.h" 16 #include "SkGlyphCache.h" 17 #include "SkTextBlobRunIterator.h" 18 #include "batches/GrAtlasTextBatch.h" 19 20 GrAtlasTextBlob* GrAtlasTextBlob::Create(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 GrAtlasTextBlob* cacheBlob = new (allocation) GrAtlasTextBlob; 35 cacheBlob->fSize = size; 36 37 // setup offsets for vertices / glyphs 38 cacheBlob->fVertices = sizeof(GrAtlasTextBlob) + reinterpret_cast<unsigned char*>(cacheBlob); 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 52 SkGlyphCache* GrAtlasTextBlob::setupCache(int runIndex, 53 const SkSurfaceProps& props, 54 SkPaint::FakeGamma fakeGamma, 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 skPaint.getScalerContextDescriptor(desc, props, fakeGamma, viewMatrix); 63 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); 64 return SkGlyphCache::DetachCache(run->fTypeface, desc->getDesc()); 65 } 66 67 void GrAtlasTextBlob::appendGlyph(int runIndex, 68 const SkRect& positions, 69 GrColor color, 70 GrBatchTextStrike* strike, 71 GrGlyph* glyph, 72 GrFontScaler* scaler, const SkGlyph& skGlyph, 73 SkScalar x, SkScalar y, SkScalar scale, bool applyVM) { 74 75 // If the glyph is too large we fall back to paths 76 if (glyph->fTooLargeForAtlas) { 77 this->appendLargeGlyph(glyph, scaler, skGlyph, x, y, scale, applyVM); 78 return; 79 } 80 81 Run& run = fRuns[runIndex]; 82 GrMaskFormat format = glyph->fMaskFormat; 83 84 Run::SubRunInfo* subRun = &run.fSubRunInfo.back(); 85 if (run.fInitialized && subRun->maskFormat() != format) { 86 subRun = &run.push_back(); 87 subRun->setStrike(strike); 88 } else if (!run.fInitialized) { 89 subRun->setStrike(strike); 90 } 91 92 run.fInitialized = true; 93 94 size_t vertexStride = GetVertexStride(format); 95 96 subRun->setMaskFormat(format); 97 98 subRun->joinGlyphBounds(positions); 99 subRun->setColor(color); 100 101 intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex()); 102 103 if (kARGB_GrMaskFormat != glyph->fMaskFormat) { 104 // V0 105 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); 106 position->set(positions.fLeft, positions.fTop); 107 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 108 *colorPtr = color; 109 vertex += vertexStride; 110 111 // V1 112 position = reinterpret_cast<SkPoint*>(vertex); 113 position->set(positions.fLeft, positions.fBottom); 114 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 115 *colorPtr = color; 116 vertex += vertexStride; 117 118 // V2 119 position = reinterpret_cast<SkPoint*>(vertex); 120 position->set(positions.fRight, positions.fBottom); 121 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 122 *colorPtr = color; 123 vertex += vertexStride; 124 125 // V3 126 position = reinterpret_cast<SkPoint*>(vertex); 127 position->set(positions.fRight, positions.fTop); 128 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 129 *colorPtr = color; 130 } else { 131 // V0 132 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); 133 position->set(positions.fLeft, positions.fTop); 134 vertex += vertexStride; 135 136 // V1 137 position = reinterpret_cast<SkPoint*>(vertex); 138 position->set(positions.fLeft, positions.fBottom); 139 vertex += vertexStride; 140 141 // V2 142 position = reinterpret_cast<SkPoint*>(vertex); 143 position->set(positions.fRight, positions.fBottom); 144 vertex += vertexStride; 145 146 // V3 147 position = reinterpret_cast<SkPoint*>(vertex); 148 position->set(positions.fRight, positions.fTop); 149 } 150 subRun->appendVertices(vertexStride); 151 fGlyphs[subRun->glyphEndIndex()] = glyph; 152 subRun->glyphAppended(); 153 } 154 155 void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, GrFontScaler* scaler, const SkGlyph& skGlyph, 156 SkScalar x, SkScalar y, SkScalar scale, bool applyVM) { 157 if (nullptr == glyph->fPath) { 158 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph); 159 if (!glyphPath) { 160 return; 161 } 162 163 glyph->fPath = new SkPath(*glyphPath); 164 } 165 fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM)); 166 } 167 168 bool GrAtlasTextBlob::mustRegenerate(const SkPaint& paint, 169 GrColor color, const SkMaskFilter::BlurRec& blurRec, 170 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { 171 // If we have LCD text then our canonical color will be set to transparent, in this case we have 172 // to regenerate the blob on any color change 173 // We use the grPaint to get any color filter effects 174 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT && 175 fPaintColor != color) { 176 return true; 177 } 178 179 if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { 180 return true; 181 } 182 183 if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) { 184 return true; 185 } 186 187 // We only cache one masked version 188 if (fKey.fHasBlur && 189 (fBlurRec.fSigma != blurRec.fSigma || 190 fBlurRec.fStyle != blurRec.fStyle || 191 fBlurRec.fQuality != blurRec.fQuality)) { 192 return true; 193 } 194 195 // Similarly, we only cache one version for each style 196 if (fKey.fStyle != SkPaint::kFill_Style && 197 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() || 198 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() || 199 fStrokeInfo.fJoin != paint.getStrokeJoin())) { 200 return true; 201 } 202 203 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls 204 // for mixed blobs if this becomes an issue. 205 if (this->hasBitmap() && this->hasDistanceField()) { 206 // Identical viewmatrices and we can reuse in all cases 207 if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) { 208 return false; 209 } 210 return true; 211 } 212 213 if (this->hasBitmap()) { 214 if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() || 215 fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() || 216 fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() || 217 fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) { 218 return true; 219 } 220 221 // We can update the positions in the cachedtextblobs without regenerating the whole blob, 222 // but only for integer translations. 223 // This cool bit of math will determine the necessary translation to apply to the already 224 // generated vertex coordinates to move them to the correct position 225 SkScalar transX = viewMatrix.getTranslateX() + 226 viewMatrix.getScaleX() * (x - fInitialX) + 227 viewMatrix.getSkewX() * (y - fInitialY) - 228 fInitialViewMatrix.getTranslateX(); 229 SkScalar transY = viewMatrix.getTranslateY() + 230 viewMatrix.getSkewY() * (x - fInitialX) + 231 viewMatrix.getScaleY() * (y - fInitialY) - 232 fInitialViewMatrix.getTranslateY(); 233 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) { 234 return true; 235 } 236 } else if (this->hasDistanceField()) { 237 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different 238 // distance field being generated, so we have to regenerate in those cases 239 SkScalar newMaxScale = viewMatrix.getMaxScale(); 240 SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale(); 241 SkScalar scaleAdjust = newMaxScale / oldMaxScale; 242 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) { 243 return true; 244 } 245 } 246 247 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case 248 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate 249 // the blob anyways at flush time, so no need to regenerate explicitly 250 return false; 251 } 252 253 inline GrDrawBatch* GrAtlasTextBlob::createBatch( 254 const Run::SubRunInfo& info, 255 int glyphCount, int run, int subRun, 256 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, 257 GrColor color, 258 const SkPaint& skPaint, const SkSurfaceProps& props, 259 const GrDistanceFieldAdjustTable* distanceAdjustTable, 260 GrBatchFontCache* cache) { 261 GrMaskFormat format = info.maskFormat(); 262 GrColor subRunColor; 263 if (kARGB_GrMaskFormat == format) { 264 uint8_t paintAlpha = skPaint.getAlpha(); 265 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha); 266 } else { 267 subRunColor = color; 268 } 269 270 GrAtlasTextBatch* batch; 271 if (info.drawAsDistanceFields()) { 272 SkColor filteredColor; 273 SkColorFilter* colorFilter = skPaint.getColorFilter(); 274 if (colorFilter) { 275 filteredColor = colorFilter->filterColor(skPaint.getColor()); 276 } else { 277 filteredColor = skPaint.getColor(); 278 } 279 bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry()); 280 batch = GrAtlasTextBatch::CreateDistanceField(glyphCount, cache, 281 distanceAdjustTable, filteredColor, 282 info.hasUseLCDText(), useBGR); 283 } else { 284 batch = GrAtlasTextBatch::CreateBitmap(format, glyphCount, cache); 285 } 286 GrAtlasTextBatch::Geometry& geometry = batch->geometry(); 287 geometry.fViewMatrix = viewMatrix; 288 geometry.fBlob = SkRef(this); 289 geometry.fRun = run; 290 geometry.fSubRun = subRun; 291 geometry.fColor = subRunColor; 292 geometry.fX = x; 293 geometry.fY = y; 294 batch->init(); 295 296 return batch; 297 } 298 299 inline 300 void GrAtlasTextBlob::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder, 301 int run, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, 302 GrColor color, 303 const SkPaint& skPaint, const SkSurfaceProps& props, 304 const GrDistanceFieldAdjustTable* distanceAdjustTable, 305 GrBatchFontCache* cache) { 306 for (int subRun = 0; subRun < fRuns[run].fSubRunInfo.count(); subRun++) { 307 const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun]; 308 int glyphCount = info.glyphCount(); 309 if (0 == glyphCount) { 310 continue; 311 } 312 313 SkAutoTUnref<GrDrawBatch> batch(this->createBatch(info, glyphCount, run, 314 subRun, viewMatrix, x, y, color, 315 skPaint, props, 316 distanceAdjustTable, cache)); 317 dc->drawBatch(pipelineBuilder, batch); 318 } 319 } 320 321 static void calculate_translation(bool applyVM, 322 const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY, 323 const SkMatrix& currentViewMatrix, SkScalar currentX, 324 SkScalar currentY, SkScalar* transX, SkScalar* transY) { 325 if (applyVM) { 326 *transX = newViewMatrix.getTranslateX() + 327 newViewMatrix.getScaleX() * (newX - currentX) + 328 newViewMatrix.getSkewX() * (newY - currentY) - 329 currentViewMatrix.getTranslateX(); 330 331 *transY = newViewMatrix.getTranslateY() + 332 newViewMatrix.getSkewY() * (newX - currentX) + 333 newViewMatrix.getScaleY() * (newY - currentY) - 334 currentViewMatrix.getTranslateY(); 335 } else { 336 *transX = newX - currentX; 337 *transY = newY - currentY; 338 } 339 } 340 341 342 void GrAtlasTextBlob::flushBigGlyphs(GrContext* context, GrDrawContext* dc, 343 const GrClip& clip, const SkPaint& skPaint, 344 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, 345 const SkIRect& clipBounds) { 346 SkScalar transX, transY; 347 for (int i = 0; i < fBigGlyphs.count(); i++) { 348 GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i]; 349 calculate_translation(bigGlyph.fApplyVM, viewMatrix, x, y, 350 fInitialViewMatrix, fInitialX, fInitialY, &transX, &transY); 351 SkMatrix ctm; 352 ctm.setScale(bigGlyph.fScale, bigGlyph.fScale); 353 ctm.postTranslate(bigGlyph.fX + transX, bigGlyph.fY + transY); 354 if (bigGlyph.fApplyVM) { 355 ctm.postConcat(viewMatrix); 356 } 357 358 GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, bigGlyph.fPath, 359 skPaint, ctm, nullptr, clipBounds, false); 360 } 361 } 362 363 void GrAtlasTextBlob::flushRunAsPaths(GrContext* context, GrDrawContext* dc, 364 const SkSurfaceProps& props, 365 const SkTextBlobRunIterator& it, 366 const GrClip& clip, const SkPaint& skPaint, 367 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix, 368 const SkIRect& clipBounds, SkScalar x, SkScalar y) { 369 SkPaint runPaint = skPaint; 370 371 size_t textLen = it.glyphCount() * sizeof(uint16_t); 372 const SkPoint& offset = it.offset(); 373 374 it.applyFontToPaint(&runPaint); 375 376 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { 377 return; 378 } 379 380 runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint)); 381 382 switch (it.positioning()) { 383 case SkTextBlob::kDefault_Positioning: 384 GrTextUtils::DrawTextAsPath(context, dc, clip, runPaint, viewMatrix, 385 (const char *)it.glyphs(), 386 textLen, x + offset.x(), y + offset.y(), clipBounds); 387 break; 388 case SkTextBlob::kHorizontal_Positioning: 389 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, runPaint, viewMatrix, 390 (const char*)it.glyphs(), 391 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), 392 clipBounds); 393 break; 394 case SkTextBlob::kFull_Positioning: 395 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, runPaint, viewMatrix, 396 (const char*)it.glyphs(), 397 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); 398 break; 399 } 400 } 401 402 void GrAtlasTextBlob::flushCached(GrContext* context, 403 GrDrawContext* dc, 404 const SkTextBlob* blob, 405 const SkSurfaceProps& props, 406 const GrDistanceFieldAdjustTable* distanceAdjustTable, 407 const SkPaint& skPaint, 408 const GrPaint& grPaint, 409 SkDrawFilter* drawFilter, 410 const GrClip& clip, 411 const SkMatrix& viewMatrix, 412 const SkIRect& clipBounds, 413 SkScalar x, SkScalar y) { 414 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush 415 // it as paths 416 GrPipelineBuilder pipelineBuilder(grPaint, dc->accessRenderTarget(), clip); 417 418 GrColor color = grPaint.getColor(); 419 420 SkTextBlobRunIterator it(blob); 421 for (int run = 0; !it.done(); it.next(), run++) { 422 if (fRuns[run].fDrawAsPaths) { 423 this->flushRunAsPaths(context, dc, props, it, clip, skPaint, 424 drawFilter, viewMatrix, clipBounds, x, y); 425 continue; 426 } 427 this->flushRun(dc, &pipelineBuilder, run, viewMatrix, x, y, color, skPaint, props, 428 distanceAdjustTable, context->getBatchFontCache()); 429 } 430 431 // Now flush big glyphs 432 this->flushBigGlyphs(context, dc, clip, skPaint, viewMatrix, x, y, clipBounds); 433 } 434 435 void GrAtlasTextBlob::flushThrowaway(GrContext* context, 436 GrDrawContext* dc, 437 const SkSurfaceProps& props, 438 const GrDistanceFieldAdjustTable* distanceAdjustTable, 439 const SkPaint& skPaint, 440 const GrPaint& grPaint, 441 const GrClip& clip, 442 const SkMatrix& viewMatrix, 443 const SkIRect& clipBounds, 444 SkScalar x, SkScalar y) { 445 GrPipelineBuilder pipelineBuilder(grPaint, dc->accessRenderTarget(), clip); 446 447 GrColor color = grPaint.getColor(); 448 for (int run = 0; run < fRunCount; run++) { 449 this->flushRun(dc, &pipelineBuilder, run, viewMatrix, x, y, color, skPaint, props, 450 distanceAdjustTable, context->getBatchFontCache()); 451 } 452 453 // Now flush big glyphs 454 this->flushBigGlyphs(context, dc, clip, skPaint, viewMatrix, x, y, clipBounds); 455 } 456 457 GrDrawBatch* GrAtlasTextBlob::test_createBatch( 458 int glyphCount, int run, int subRun, 459 const SkMatrix& viewMatrix, SkScalar x, SkScalar y, 460 GrColor color, 461 const SkPaint& skPaint, const SkSurfaceProps& props, 462 const GrDistanceFieldAdjustTable* distanceAdjustTable, 463 GrBatchFontCache* cache) { 464 const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun]; 465 return this->createBatch(info, glyphCount, run, subRun, viewMatrix, x, y, color, skPaint, 466 props, distanceAdjustTable, cache); 467 } 468 469 void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) { 470 SkASSERT_RELEASE(l.fSize == r.fSize); 471 SkASSERT_RELEASE(l.fPool == r.fPool); 472 473 SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma); 474 SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle); 475 SkASSERT_RELEASE(l.fBlurRec.fQuality == r.fBlurRec.fQuality); 476 477 SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth); 478 SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit); 479 SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin); 480 481 SkASSERT_RELEASE(l.fBigGlyphs.count() == r.fBigGlyphs.count()); 482 for (int i = 0; i < l.fBigGlyphs.count(); i++) { 483 const BigGlyph& lBigGlyph = l.fBigGlyphs[i]; 484 const BigGlyph& rBigGlyph = r.fBigGlyphs[i]; 485 486 SkASSERT_RELEASE(lBigGlyph.fPath == rBigGlyph.fPath); 487 // We can't assert that these have the same translations 488 } 489 490 SkASSERT_RELEASE(l.fKey == r.fKey); 491 //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical 492 SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale); 493 SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale); 494 SkASSERT_RELEASE(l.fTextType == r.fTextType); 495 496 SkASSERT_RELEASE(l.fRunCount == r.fRunCount); 497 for (int i = 0; i < l.fRunCount; i++) { 498 const Run& lRun = l.fRuns[i]; 499 const Run& rRun = r.fRuns[i]; 500 501 if (lRun.fTypeface.get()) { 502 SkASSERT_RELEASE(rRun.fTypeface.get()); 503 SkASSERT_RELEASE(SkTypeface::Equal(lRun.fTypeface, rRun.fTypeface)); 504 } else { 505 SkASSERT_RELEASE(!rRun.fTypeface.get()); 506 } 507 508 509 SkASSERT_RELEASE(lRun.fDescriptor.getDesc()); 510 SkASSERT_RELEASE(rRun.fDescriptor.getDesc()); 511 SkASSERT_RELEASE(lRun.fDescriptor.getDesc()->equals(*rRun.fDescriptor.getDesc())); 512 513 if (lRun.fOverrideDescriptor.get()) { 514 SkASSERT_RELEASE(lRun.fOverrideDescriptor->getDesc()); 515 SkASSERT_RELEASE(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc()); 516 SkASSERT_RELEASE(lRun.fOverrideDescriptor->getDesc()->equals( 517 *rRun.fOverrideDescriptor->getDesc())); 518 } else { 519 SkASSERT_RELEASE(!rRun.fOverrideDescriptor.get()); 520 } 521 522 // color can be changed 523 //SkASSERT(lRun.fColor == rRun.fColor); 524 SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized); 525 SkASSERT_RELEASE(lRun.fDrawAsPaths == rRun.fDrawAsPaths); 526 527 SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count()); 528 for(int j = 0; j < lRun.fSubRunInfo.count(); j++) { 529 const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j]; 530 const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j]; 531 532 // TODO we can do this check, but we have to apply the VM to the old vertex bounds 533 //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds()); 534 535 if (lSubRun.strike()) { 536 SkASSERT_RELEASE(rSubRun.strike()); 537 SkASSERT_RELEASE(GrBatchTextStrike::GetKey(*lSubRun.strike()) == 538 GrBatchTextStrike::GetKey(*rSubRun.strike())); 539 540 } else { 541 SkASSERT_RELEASE(!rSubRun.strike()); 542 } 543 544 SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex()); 545 SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex()); 546 SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex()); 547 SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex()); 548 SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat()); 549 SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields()); 550 SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText()); 551 } 552 } 553 } 554 555 void GrAtlasTextBlob::Run::SubRunInfo::computeTranslation(const SkMatrix& viewMatrix, 556 SkScalar x, SkScalar y, SkScalar* transX, 557 SkScalar* transY) { 558 calculate_translation(!this->drawAsDistanceFields(), viewMatrix, x, y, 559 fCurrentViewMatrix, fX, fY, transX, transY); 560 fCurrentViewMatrix = viewMatrix; 561 fX = x; 562 fY = y; 563 } 564