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