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