1 /* 2 * Copyright 2014 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 "SkRecordDraw.h" 9 #include "SkImage.h" 10 #include "SkPatchUtils.h" 11 12 void SkRecordDraw(const SkRecord& record, 13 SkCanvas* canvas, 14 SkPicture const* const drawablePicts[], 15 SkDrawable* const drawables[], 16 int drawableCount, 17 const SkBBoxHierarchy* bbh, 18 SkPicture::AbortCallback* callback) { 19 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); 20 21 if (bbh) { 22 // Draw only ops that affect pixels in the canvas's current clip. 23 // The SkRecord and BBH were recorded in identity space. This canvas 24 // is not necessarily in that same space. getLocalClipBounds() returns us 25 // this canvas' clip bounds transformed back into identity space, which 26 // lets us query the BBH. 27 SkRect query = canvas->getLocalClipBounds(); 28 29 SkTDArray<int> ops; 30 bbh->search(query, &ops); 31 32 SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); 33 for (int i = 0; i < ops.count(); i++) { 34 if (callback && callback->abort()) { 35 return; 36 } 37 // This visit call uses the SkRecords::Draw::operator() to call 38 // methods on the |canvas|, wrapped by methods defined with the 39 // DRAW() macro. 40 record.visit(ops[i], draw); 41 } 42 } else { 43 // Draw all ops. 44 SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); 45 for (int i = 0; i < record.count(); i++) { 46 if (callback && callback->abort()) { 47 return; 48 } 49 // This visit call uses the SkRecords::Draw::operator() to call 50 // methods on the |canvas|, wrapped by methods defined with the 51 // DRAW() macro. 52 record.visit(i, draw); 53 } 54 } 55 } 56 57 void SkRecordPartialDraw(const SkRecord& record, SkCanvas* canvas, 58 SkPicture const* const drawablePicts[], int drawableCount, 59 int start, int stop, 60 const SkMatrix& initialCTM) { 61 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); 62 63 stop = SkTMin(stop, record.count()); 64 SkRecords::Draw draw(canvas, drawablePicts, nullptr, drawableCount, &initialCTM); 65 for (int i = start; i < stop; i++) { 66 record.visit(i, draw); 67 } 68 } 69 70 namespace SkRecords { 71 72 // NoOps draw nothing. 73 template <> void Draw::draw(const NoOp&) {} 74 75 #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; } 76 DRAW(Flush, flush()); 77 DRAW(Restore, restore()); 78 DRAW(Save, save()); 79 DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, 80 r.paint, 81 r.backdrop.get(), 82 r.clipMask.get(), 83 r.clipMatrix, 84 r.saveLayerFlags))); 85 DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); 86 DRAW(Concat, concat(r.matrix)); 87 DRAW(Translate, translate(r.dx, r.dy)); 88 89 DRAW(ClipPath, clipPath(r.path, r.opAA.op(), r.opAA.aa())); 90 DRAW(ClipRRect, clipRRect(r.rrect, r.opAA.op(), r.opAA.aa())); 91 DRAW(ClipRect, clipRect(r.rect, r.opAA.op(), r.opAA.aa())); 92 DRAW(ClipRegion, clipRegion(r.region, r.op)); 93 94 DRAW(DrawArc, drawArc(r.oval, r.startAngle, r.sweepAngle, r.useCenter, r.paint)); 95 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); 96 DRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.paint)); 97 98 template <> void Draw::draw(const DrawImageLattice& r) { 99 SkCanvas::Lattice lattice; 100 lattice.fXCount = r.xCount; 101 lattice.fXDivs = r.xDivs; 102 lattice.fYCount = r.yCount; 103 lattice.fYDivs = r.yDivs; 104 lattice.fRectTypes = (0 == r.flagCount) ? nullptr : r.flags; 105 lattice.fColors = (0 == r.flagCount) ? nullptr : r.colors; 106 lattice.fBounds = &r.src; 107 fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.paint); 108 } 109 110 DRAW(DrawImageRect, legacy_drawImageRect(r.image.get(), r.src, r.dst, r.paint, r.constraint)); 111 DRAW(DrawImageNine, drawImageNine(r.image.get(), r.center, r.dst, r.paint)); 112 DRAW(DrawOval, drawOval(r.oval, r.paint)); 113 DRAW(DrawPaint, drawPaint(r.paint)); 114 DRAW(DrawPath, drawPath(r.path, r.paint)); 115 DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint)); 116 DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint)); 117 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); 118 DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); 119 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); 120 DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); 121 DRAW(DrawRect, drawRect(r.rect, r.paint)); 122 DRAW(DrawRegion, drawRegion(r.region, r.paint)); 123 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); 124 DRAW(DrawTextBlob, drawTextBlob(r.blob.get(), r.x, r.y, r.paint)); 125 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint)); 126 DRAW(DrawTextRSXform, drawTextRSXform(r.text, r.byteLength, r.xforms, r.cull, r.paint)); 127 DRAW(DrawAtlas, drawAtlas(r.atlas.get(), 128 r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint)); 129 DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint)); 130 DRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec)); 131 DRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get())); 132 #undef DRAW 133 134 template <> void Draw::draw(const DrawDrawable& r) { 135 SkASSERT(r.index >= 0); 136 SkASSERT(r.index < fDrawableCount); 137 if (fDrawables) { 138 SkASSERT(nullptr == fDrawablePicts); 139 fCanvas->drawDrawable(fDrawables[r.index], r.matrix); 140 } else { 141 fCanvas->drawPicture(fDrawablePicts[r.index], r.matrix, nullptr); 142 } 143 } 144 145 // This is an SkRecord visitor that fills an SkBBoxHierarchy. 146 // 147 // The interesting part here is how to calculate bounds for ops which don't 148 // have intrinsic bounds. What is the bounds of a Save or a Translate? 149 // 150 // We answer this by thinking about a particular definition of bounds: if I 151 // don't execute this op, pixels in this rectangle might draw incorrectly. So 152 // the bounds of a Save, a Translate, a Restore, etc. are the union of the 153 // bounds of Draw* ops that they might have an effect on. For any given 154 // Save/Restore block, the bounds of the Save, the Restore, and any other 155 // non-drawing ("control") ops inside are exactly the union of the bounds of 156 // the drawing ops inside that block. 157 // 158 // To implement this, we keep a stack of active Save blocks. As we consume ops 159 // inside the Save/Restore block, drawing ops are unioned with the bounds of 160 // the block, and control ops are stashed away for later. When we finish the 161 // block with a Restore, our bounds are complete, and we go back and fill them 162 // in for all the control ops we stashed away. 163 class FillBounds : SkNoncopyable { 164 public: 165 FillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[]) 166 : fNumRecords(record.count()) 167 , fCullRect(cullRect) 168 , fBounds(bounds) { 169 fCTM = SkMatrix::I(); 170 fCurrentClipBounds = fCullRect; 171 } 172 173 void cleanUp() { 174 // If we have any lingering unpaired Saves, simulate restores to make 175 // sure all ops in those Save blocks have their bounds calculated. 176 while (!fSaveStack.isEmpty()) { 177 this->popSaveBlock(); 178 } 179 180 // Any control ops not part of any Save/Restore block draw everywhere. 181 while (!fControlIndices.isEmpty()) { 182 this->popControl(fCullRect); 183 } 184 } 185 186 void setCurrentOp(int currentOp) { fCurrentOp = currentOp; } 187 188 189 template <typename T> void operator()(const T& op) { 190 this->updateCTM(op); 191 this->updateClipBounds(op); 192 this->trackBounds(op); 193 } 194 195 // In this file, SkRect are in local coordinates, Bounds are translated back to identity space. 196 typedef SkRect Bounds; 197 198 int currentOp() const { return fCurrentOp; } 199 const SkMatrix& ctm() const { return fCTM; } 200 const Bounds& getBounds(int index) const { return fBounds[index]; } 201 202 // Adjust rect for all paints that may affect its geometry, then map it to identity space. 203 Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const { 204 // Inverted rectangles really confuse our BBHs. 205 rect.sort(); 206 207 // Adjust the rect for its own paint. 208 if (!AdjustForPaint(paint, &rect)) { 209 // The paint could do anything to our bounds. The only safe answer is the current clip. 210 return fCurrentClipBounds; 211 } 212 213 // Adjust rect for all the paints from the SaveLayers we're inside. 214 if (!this->adjustForSaveLayerPaints(&rect)) { 215 // Same deal as above. 216 return fCurrentClipBounds; 217 } 218 219 // Map the rect back to identity space. 220 fCTM.mapRect(&rect); 221 222 // Nothing can draw outside the current clip. 223 if (!rect.intersect(fCurrentClipBounds)) { 224 return Bounds::MakeEmpty(); 225 } 226 227 return rect; 228 } 229 230 private: 231 struct SaveBounds { 232 int controlOps; // Number of control ops in this Save block, including the Save. 233 Bounds bounds; // Bounds of everything in the block. 234 const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block. 235 SkMatrix ctm; 236 }; 237 238 // Only Restore, SetMatrix, Concat, and Translate change the CTM. 239 template <typename T> void updateCTM(const T&) {} 240 void updateCTM(const Restore& op) { fCTM = op.matrix; } 241 void updateCTM(const SetMatrix& op) { fCTM = op.matrix; } 242 void updateCTM(const Concat& op) { fCTM.preConcat(op.matrix); } 243 void updateCTM(const Translate& op) { fCTM.preTranslate(op.dx, op.dy); } 244 245 // Most ops don't change the clip. 246 template <typename T> void updateClipBounds(const T&) {} 247 248 // Clip{Path,RRect,Rect,Region} obviously change the clip. They all know their bounds already. 249 void updateClipBounds(const ClipPath& op) { this->updateClipBoundsForClipOp(op.devBounds); } 250 void updateClipBounds(const ClipRRect& op) { this->updateClipBoundsForClipOp(op.devBounds); } 251 void updateClipBounds(const ClipRect& op) { this->updateClipBoundsForClipOp(op.devBounds); } 252 void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipOp(op.devBounds); } 253 254 // The bounds of clip ops need to be adjusted for the paints of saveLayers they're inside. 255 void updateClipBoundsForClipOp(const SkIRect& devBounds) { 256 Bounds clip = SkRect::Make(devBounds); 257 // We don't call adjustAndMap() because as its last step it would intersect the adjusted 258 // clip bounds with the previous clip, exactly what we can't do when the clip grows. 259 if (this->adjustForSaveLayerPaints(&clip)) { 260 fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty(); 261 } else { 262 fCurrentClipBounds = fCullRect; 263 } 264 } 265 266 // Restore holds the devBounds for the clip after the {save,saveLayer}/restore block completes. 267 void updateClipBounds(const Restore& op) { 268 // This is just like the clip ops above, but we need to skip the effects (if any) of our 269 // paired saveLayer (if it is one); it has not yet been popped off the save stack. Our 270 // devBounds reflect the state of the world after the saveLayer/restore block is done, 271 // so they are not affected by the saveLayer's paint. 272 const int kSavesToIgnore = 1; 273 Bounds clip = SkRect::Make(op.devBounds); 274 if (this->adjustForSaveLayerPaints(&clip, kSavesToIgnore)) { 275 fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty(); 276 } else { 277 fCurrentClipBounds = fCullRect; 278 } 279 } 280 281 // We also take advantage of SaveLayer bounds when present to further cut the clip down. 282 void updateClipBounds(const SaveLayer& op) { 283 if (op.bounds) { 284 // adjustAndMap() intersects these layer bounds with the previous clip for us. 285 fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint); 286 } 287 } 288 289 // The bounds of these ops must be calculated when we hit the Restore 290 // from the bounds of the ops in the same Save block. 291 void trackBounds(const Save&) { this->pushSaveBlock(nullptr); } 292 void trackBounds(const SaveLayer& op) { this->pushSaveBlock(op.paint); } 293 void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); } 294 295 void trackBounds(const SetMatrix&) { this->pushControl(); } 296 void trackBounds(const Concat&) { this->pushControl(); } 297 void trackBounds(const Translate&) { this->pushControl(); } 298 void trackBounds(const ClipRect&) { this->pushControl(); } 299 void trackBounds(const ClipRRect&) { this->pushControl(); } 300 void trackBounds(const ClipPath&) { this->pushControl(); } 301 void trackBounds(const ClipRegion&) { this->pushControl(); } 302 303 304 // For all other ops, we can calculate and store the bounds directly now. 305 template <typename T> void trackBounds(const T& op) { 306 fBounds[fCurrentOp] = this->bounds(op); 307 this->updateSaveBounds(fBounds[fCurrentOp]); 308 } 309 310 void pushSaveBlock(const SkPaint* paint) { 311 // Starting a new Save block. Push a new entry to represent that. 312 SaveBounds sb; 313 sb.controlOps = 0; 314 // If the paint affects transparent black, the bound shouldn't be smaller 315 // than the current clip bounds. 316 sb.bounds = 317 PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty(); 318 sb.paint = paint; 319 sb.ctm = this->fCTM; 320 321 fSaveStack.push(sb); 322 this->pushControl(); 323 } 324 325 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) { 326 if (paint) { 327 // FIXME: this is very conservative 328 if (paint->getImageFilter() || paint->getColorFilter()) { 329 return true; 330 } 331 332 // Unusual blendmodes require us to process a saved layer 333 // even with operations outisde the clip. 334 // For example, DstIn is used by masking layers. 335 // https://code.google.com/p/skia/issues/detail?id=1291 336 // https://crbug.com/401593 337 switch (paint->getBlendMode()) { 338 // For each of the following transfer modes, if the source 339 // alpha is zero (our transparent black), the resulting 340 // blended alpha is not necessarily equal to the original 341 // destination alpha. 342 case SkBlendMode::kClear: 343 case SkBlendMode::kSrc: 344 case SkBlendMode::kSrcIn: 345 case SkBlendMode::kDstIn: 346 case SkBlendMode::kSrcOut: 347 case SkBlendMode::kDstATop: 348 case SkBlendMode::kModulate: 349 return true; 350 break; 351 default: 352 break; 353 } 354 } 355 return false; 356 } 357 358 Bounds popSaveBlock() { 359 // We're done the Save block. Apply the block's bounds to all control ops inside it. 360 SaveBounds sb; 361 fSaveStack.pop(&sb); 362 363 while (sb.controlOps --> 0) { 364 this->popControl(sb.bounds); 365 } 366 367 // This whole Save block may be part another Save block. 368 this->updateSaveBounds(sb.bounds); 369 370 // If called from a real Restore (not a phony one for balance), it'll need the bounds. 371 return sb.bounds; 372 } 373 374 void pushControl() { 375 fControlIndices.push(fCurrentOp); 376 if (!fSaveStack.isEmpty()) { 377 fSaveStack.top().controlOps++; 378 } 379 } 380 381 void popControl(const Bounds& bounds) { 382 fBounds[fControlIndices.top()] = bounds; 383 fControlIndices.pop(); 384 } 385 386 void updateSaveBounds(const Bounds& bounds) { 387 // If we're in a Save block, expand its bounds to cover these bounds too. 388 if (!fSaveStack.isEmpty()) { 389 fSaveStack.top().bounds.join(bounds); 390 } 391 } 392 393 Bounds bounds(const Flush&) const { return fCurrentClipBounds; } 394 395 // FIXME: this method could use better bounds 396 Bounds bounds(const DrawText&) const { return fCurrentClipBounds; } 397 398 Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } 399 Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOps don't draw. 400 401 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); } 402 Bounds bounds(const DrawRegion& op) const { 403 SkRect rect = SkRect::Make(op.region.getBounds()); 404 return this->adjustAndMap(rect, &op.paint); 405 } 406 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); } 407 // Tighter arc bounds? 408 Bounds bounds(const DrawArc& op) const { return this->adjustAndMap(op.oval, &op.paint); } 409 Bounds bounds(const DrawRRect& op) const { 410 return this->adjustAndMap(op.rrect.rect(), &op.paint); 411 } 412 Bounds bounds(const DrawDRRect& op) const { 413 return this->adjustAndMap(op.outer.rect(), &op.paint); 414 } 415 Bounds bounds(const DrawImage& op) const { 416 const SkImage* image = op.image.get(); 417 SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height()); 418 419 return this->adjustAndMap(rect, op.paint); 420 } 421 Bounds bounds(const DrawImageLattice& op) const { 422 return this->adjustAndMap(op.dst, op.paint); 423 } 424 Bounds bounds(const DrawImageRect& op) const { 425 return this->adjustAndMap(op.dst, op.paint); 426 } 427 Bounds bounds(const DrawImageNine& op) const { 428 return this->adjustAndMap(op.dst, op.paint); 429 } 430 Bounds bounds(const DrawPath& op) const { 431 return op.path.isInverseFillType() ? fCurrentClipBounds 432 : this->adjustAndMap(op.path.getBounds(), &op.paint); 433 } 434 Bounds bounds(const DrawPoints& op) const { 435 SkRect dst; 436 dst.set(op.pts, op.count); 437 438 // Pad the bounding box a little to make sure hairline points' bounds aren't empty. 439 SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f); 440 dst.outset(stroke/2, stroke/2); 441 442 return this->adjustAndMap(dst, &op.paint); 443 } 444 Bounds bounds(const DrawPatch& op) const { 445 SkRect dst; 446 dst.set(op.cubics, SkPatchUtils::kNumCtrlPts); 447 return this->adjustAndMap(dst, &op.paint); 448 } 449 Bounds bounds(const DrawVertices& op) const { 450 return this->adjustAndMap(op.vertices->bounds(), &op.paint); 451 } 452 453 Bounds bounds(const DrawAtlas& op) const { 454 if (op.cull) { 455 // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct" 456 // for the paint (by the caller)? 457 return this->adjustAndMap(*op.cull, op.paint); 458 } else { 459 return fCurrentClipBounds; 460 } 461 } 462 463 Bounds bounds(const DrawShadowRec& op) const { 464 SkRect bounds; 465 SkDrawShadowMetrics::GetLocalBounds(op.path, op.rec, fCTM, &bounds); 466 return this->adjustAndMap(bounds, nullptr); 467 } 468 469 Bounds bounds(const DrawPicture& op) const { 470 SkRect dst = op.picture->cullRect(); 471 op.matrix.mapRect(&dst); 472 return this->adjustAndMap(dst, op.paint); 473 } 474 475 Bounds bounds(const DrawPosText& op) const { 476 const int N = op.paint.countText(op.text, op.byteLength); 477 if (N == 0) { 478 return Bounds::MakeEmpty(); 479 } 480 481 SkRect dst; 482 dst.set(op.pos, N); 483 AdjustTextForFontMetrics(&dst, op.paint); 484 return this->adjustAndMap(dst, &op.paint); 485 } 486 Bounds bounds(const DrawPosTextH& op) const { 487 const int N = op.paint.countText(op.text, op.byteLength); 488 if (N == 0) { 489 return Bounds::MakeEmpty(); 490 } 491 492 SkScalar left = op.xpos[0], right = op.xpos[0]; 493 for (int i = 1; i < N; i++) { 494 left = SkMinScalar(left, op.xpos[i]); 495 right = SkMaxScalar(right, op.xpos[i]); 496 } 497 SkRect dst = { left, op.y, right, op.y }; 498 AdjustTextForFontMetrics(&dst, op.paint); 499 return this->adjustAndMap(dst, &op.paint); 500 } 501 Bounds bounds(const DrawTextOnPath& op) const { 502 SkRect dst = op.path.getBounds(); 503 504 // Pad all sides by the maximum padding in any direction we'd normally apply. 505 SkRect pad = { 0, 0, 0, 0}; 506 AdjustTextForFontMetrics(&pad, op.paint); 507 508 // That maximum padding happens to always be the right pad today. 509 SkASSERT(pad.fLeft == -pad.fRight); 510 SkASSERT(pad.fTop == -pad.fBottom); 511 SkASSERT(pad.fRight > pad.fBottom); 512 dst.outset(pad.fRight, pad.fRight); 513 514 return this->adjustAndMap(dst, &op.paint); 515 } 516 517 Bounds bounds(const DrawTextRSXform& op) const { 518 if (op.cull) { 519 return this->adjustAndMap(*op.cull, nullptr); 520 } else { 521 return fCurrentClipBounds; 522 } 523 } 524 525 Bounds bounds(const DrawTextBlob& op) const { 526 SkRect dst = op.blob->bounds(); 527 dst.offset(op.x, op.y); 528 return this->adjustAndMap(dst, &op.paint); 529 } 530 531 Bounds bounds(const DrawDrawable& op) const { 532 return this->adjustAndMap(op.worstCaseBounds, nullptr); 533 } 534 535 Bounds bounds(const DrawAnnotation& op) const { 536 return this->adjustAndMap(op.rect, nullptr); 537 } 538 539 static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint) { 540 #ifdef SK_DEBUG 541 SkRect correct = *rect; 542 #endif 543 // crbug.com/373785 ~~> xPad = 4x yPad 544 // crbug.com/424824 ~~> bump yPad from 2x text size to 2.5x 545 const SkScalar yPad = 2.5f * paint.getTextSize(), 546 xPad = 4.0f * yPad; 547 rect->outset(xPad, yPad); 548 #ifdef SK_DEBUG 549 SkPaint::FontMetrics metrics; 550 paint.getFontMetrics(&metrics); 551 correct.fLeft += metrics.fXMin; 552 correct.fTop += metrics.fTop; 553 correct.fRight += metrics.fXMax; 554 correct.fBottom += metrics.fBottom; 555 // See skia:2862 for why we ignore small text sizes. 556 SkASSERTF(paint.getTextSize() < 0.001f || rect->contains(correct), 557 "%f %f %f %f vs. %f %f %f %f\n", 558 -xPad, -yPad, +xPad, +yPad, 559 metrics.fXMin, metrics.fTop, metrics.fXMax, metrics.fBottom); 560 #endif 561 } 562 563 // Returns true if rect was meaningfully adjusted for the effects of paint, 564 // false if the paint could affect the rect in unknown ways. 565 static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) { 566 if (paint) { 567 if (paint->canComputeFastBounds()) { 568 *rect = paint->computeFastBounds(*rect, rect); 569 return true; 570 } 571 return false; 572 } 573 return true; 574 } 575 576 bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const { 577 for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) { 578 SkMatrix inverse; 579 if (!fSaveStack[i].ctm.invert(&inverse)) { 580 return false; 581 } 582 inverse.mapRect(rect); 583 if (!AdjustForPaint(fSaveStack[i].paint, rect)) { 584 return false; 585 } 586 fSaveStack[i].ctm.mapRect(rect); 587 } 588 return true; 589 } 590 591 const int fNumRecords; 592 593 // We do not guarantee anything for operations outside of the cull rect 594 const SkRect fCullRect; 595 596 // Conservative identity-space bounds for each op in the SkRecord. 597 Bounds* fBounds; 598 599 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() 600 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative 601 // identity-space bounds of the current clip (fCurrentClipBounds). 602 int fCurrentOp; 603 SkMatrix fCTM; 604 Bounds fCurrentClipBounds; 605 606 // Used to track the bounds of Save/Restore blocks and the control ops inside them. 607 SkTDArray<SaveBounds> fSaveStack; 608 SkTDArray<int> fControlIndices; 609 }; 610 611 } // namespace SkRecords 612 613 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[]) { 614 SkRecords::FillBounds visitor(cullRect, record, bounds); 615 for (int curOp = 0; curOp < record.count(); curOp++) { 616 visitor.setCurrentOp(curOp); 617 record.visit(curOp, visitor); 618 } 619 visitor.cleanUp(); 620 } 621 622