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 "SkLayerInfo.h" 9#include "SkRecordDraw.h" 10#include "SkPatchUtils.h" 11 12void 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. getClipBounds() returns us 25 // this canvas' clip bounds transformed back into identity space, which 26 // lets us query the BBH. 27 SkRect query; 28 if (!canvas->getClipBounds(&query)) { 29 query.setEmpty(); 30 } 31 32 SkTDArray<unsigned> ops; 33 bbh->search(query, &ops); 34 35 SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); 36 for (int i = 0; i < ops.count(); i++) { 37 if (callback && callback->abort()) { 38 return; 39 } 40 // This visit call uses the SkRecords::Draw::operator() to call 41 // methods on the |canvas|, wrapped by methods defined with the 42 // DRAW() macro. 43 record.visit<void>(ops[i], draw); 44 } 45 } else { 46 // Draw all ops. 47 SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); 48 for (unsigned i = 0; i < record.count(); i++) { 49 if (callback && callback->abort()) { 50 return; 51 } 52 // This visit call uses the SkRecords::Draw::operator() to call 53 // methods on the |canvas|, wrapped by methods defined with the 54 // DRAW() macro. 55 record.visit<void>(i, draw); 56 } 57 } 58} 59 60void SkRecordPartialDraw(const SkRecord& record, SkCanvas* canvas, 61 SkPicture const* const drawablePicts[], int drawableCount, 62 unsigned start, unsigned stop, 63 const SkMatrix& initialCTM) { 64 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); 65 66 stop = SkTMin(stop, record.count()); 67 SkRecords::Draw draw(canvas, drawablePicts, NULL, drawableCount, &initialCTM); 68 for (unsigned i = start; i < stop; i++) { 69 record.visit<void>(i, draw); 70 } 71} 72 73namespace SkRecords { 74 75// NoOps draw nothing. 76template <> void Draw::draw(const NoOp&) {} 77 78#define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; } 79DRAW(Restore, restore()); 80DRAW(Save, save()); 81DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); 82DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); 83 84DRAW(ClipPath, clipPath(r.path, r.opAA.op, r.opAA.aa)); 85DRAW(ClipRRect, clipRRect(r.rrect, r.opAA.op, r.opAA.aa)); 86DRAW(ClipRect, clipRect(r.rect, r.opAA.op, r.opAA.aa)); 87DRAW(ClipRegion, clipRegion(r.region, r.op)); 88 89DRAW(BeginCommentGroup, beginCommentGroup(r.description)); 90DRAW(AddComment, addComment(r.key, r.value)); 91DRAW(EndCommentGroup, endCommentGroup()); 92 93DRAW(DrawBitmap, drawBitmap(r.bitmap.shallowCopy(), r.left, r.top, r.paint)); 94DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap.shallowCopy(), r.center, r.dst, r.paint)); 95DRAW(DrawBitmapRectToRect, 96 drawBitmapRectToRect(r.bitmap.shallowCopy(), r.src, r.dst, r.paint, 97 SkCanvas::kNone_DrawBitmapRectFlag)); 98DRAW(DrawBitmapRectToRectBleed, 99 drawBitmapRectToRect(r.bitmap.shallowCopy(), r.src, r.dst, r.paint, 100 SkCanvas::kBleed_DrawBitmapRectFlag)); 101DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); 102DRAW(DrawImage, drawImage(r.image, r.left, r.top, r.paint)); 103DRAW(DrawImageRect, drawImageRect(r.image, r.src, r.dst, r.paint)); 104DRAW(DrawOval, drawOval(r.oval, r.paint)); 105DRAW(DrawPaint, drawPaint(r.paint)); 106DRAW(DrawPath, drawPath(r.path, r.paint)); 107DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.xmode, r.paint)); 108DRAW(DrawPicture, drawPicture(r.picture, &r.matrix, r.paint)); 109DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); 110DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); 111DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); 112DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); 113DRAW(DrawRect, drawRect(r.rect, r.paint)); 114DRAW(DrawSprite, drawSprite(r.bitmap.shallowCopy(), r.left, r.top, r.paint)); 115DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); 116DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); 117DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint)); 118DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, 119 r.xmode.get(), r.indices, r.indexCount, r.paint)); 120#undef DRAW 121 122template <> void Draw::draw(const DrawDrawable& r) { 123 SkASSERT(r.index >= 0); 124 SkASSERT(r.index < fDrawableCount); 125 if (fDrawables) { 126 SkASSERT(NULL == fDrawablePicts); 127 fCanvas->drawDrawable(fDrawables[r.index]); 128 } else { 129 fCanvas->drawPicture(fDrawablePicts[r.index]); 130 } 131} 132 133// This is an SkRecord visitor that fills an SkBBoxHierarchy. 134// 135// The interesting part here is how to calculate bounds for ops which don't 136// have intrinsic bounds. What is the bounds of a Save or a Translate? 137// 138// We answer this by thinking about a particular definition of bounds: if I 139// don't execute this op, pixels in this rectangle might draw incorrectly. So 140// the bounds of a Save, a Translate, a Restore, etc. are the union of the 141// bounds of Draw* ops that they might have an effect on. For any given 142// Save/Restore block, the bounds of the Save, the Restore, and any other 143// non-drawing ("control") ops inside are exactly the union of the bounds of 144// the drawing ops inside that block. 145// 146// To implement this, we keep a stack of active Save blocks. As we consume ops 147// inside the Save/Restore block, drawing ops are unioned with the bounds of 148// the block, and control ops are stashed away for later. When we finish the 149// block with a Restore, our bounds are complete, and we go back and fill them 150// in for all the control ops we stashed away. 151class FillBounds : SkNoncopyable { 152public: 153 FillBounds(const SkRect& cullRect, const SkRecord& record) 154 : fNumRecords(record.count()) 155 , fCullRect(cullRect) 156 , fBounds(record.count()) 157 { 158 // Calculate bounds for all ops. This won't go quite in order, so we'll need 159 // to store the bounds separately then feed them in to the BBH later in order. 160 fCTM = &SkMatrix::I(); 161 fCurrentClipBounds = fCullRect; 162 } 163 164 void setCurrentOp(unsigned currentOp) { fCurrentOp = currentOp; } 165 166 void cleanUp(SkBBoxHierarchy* bbh) { 167 // If we have any lingering unpaired Saves, simulate restores to make 168 // sure all ops in those Save blocks have their bounds calculated. 169 while (!fSaveStack.isEmpty()) { 170 this->popSaveBlock(); 171 } 172 173 // Any control ops not part of any Save/Restore block draw everywhere. 174 while (!fControlIndices.isEmpty()) { 175 this->popControl(fCullRect); 176 } 177 178 // Finally feed all stored bounds into the BBH. They'll be returned in this order. 179 if (bbh) { 180 bbh->insert(fBounds.get(), fNumRecords); 181 } 182 } 183 184 template <typename T> void operator()(const T& op) { 185 this->updateCTM(op); 186 this->updateClipBounds(op); 187 this->trackBounds(op); 188 } 189 190 // In this file, SkRect are in local coordinates, Bounds are translated back to identity space. 191 typedef SkRect Bounds; 192 193 unsigned currentOp() const { return fCurrentOp; } 194 const SkMatrix& ctm() const { return *fCTM; } 195 const Bounds& getBounds(unsigned index) const { return fBounds[index]; } 196 197 // Adjust rect for all paints that may affect its geometry, then map it to identity space. 198 Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const { 199 // Inverted rectangles really confuse our BBHs. 200 rect.sort(); 201 202 // Adjust the rect for its own paint. 203 if (!AdjustForPaint(paint, &rect)) { 204 // The paint could do anything to our bounds. The only safe answer is the current clip. 205 return fCurrentClipBounds; 206 } 207 208 // Adjust rect for all the paints from the SaveLayers we're inside. 209 if (!this->adjustForSaveLayerPaints(&rect)) { 210 // Same deal as above. 211 return fCurrentClipBounds; 212 } 213 214 // Map the rect back to identity space. 215 fCTM->mapRect(&rect); 216 217 // Nothing can draw outside the current clip. 218 if (!rect.intersect(fCurrentClipBounds)) { 219 return Bounds::MakeEmpty(); 220 } 221 222 return rect; 223 } 224 225private: 226 struct SaveBounds { 227 int controlOps; // Number of control ops in this Save block, including the Save. 228 Bounds bounds; // Bounds of everything in the block. 229 const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block. 230 }; 231 232 // Only Restore and SetMatrix change the CTM. 233 template <typename T> void updateCTM(const T&) {} 234 void updateCTM(const Restore& op) { fCTM = &op.matrix; } 235 void updateCTM(const SetMatrix& op) { fCTM = &op.matrix; } 236 237 // Most ops don't change the clip. 238 template <typename T> void updateClipBounds(const T&) {} 239 240 // Clip{Path,RRect,Rect,Region} obviously change the clip. They all know their bounds already. 241 void updateClipBounds(const ClipPath& op) { this->updateClipBoundsForClipOp(op.devBounds); } 242 void updateClipBounds(const ClipRRect& op) { this->updateClipBoundsForClipOp(op.devBounds); } 243 void updateClipBounds(const ClipRect& op) { this->updateClipBoundsForClipOp(op.devBounds); } 244 void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipOp(op.devBounds); } 245 246 // The bounds of clip ops need to be adjusted for the paints of saveLayers they're inside. 247 void updateClipBoundsForClipOp(const SkIRect& devBounds) { 248 Bounds clip = SkRect::Make(devBounds); 249 // We don't call adjustAndMap() because as its last step it would intersect the adjusted 250 // clip bounds with the previous clip, exactly what we can't do when the clip grows. 251 if (this->adjustForSaveLayerPaints(&clip)) { 252 fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty(); 253 } else { 254 fCurrentClipBounds = fCullRect; 255 } 256 } 257 258 // Restore holds the devBounds for the clip after the {save,saveLayer}/restore block completes. 259 void updateClipBounds(const Restore& op) { 260 // This is just like the clip ops above, but we need to skip the effects (if any) of our 261 // paired saveLayer (if it is one); it has not yet been popped off the save stack. Our 262 // devBounds reflect the state of the world after the saveLayer/restore block is done, 263 // so they are not affected by the saveLayer's paint. 264 const int kSavesToIgnore = 1; 265 Bounds clip = SkRect::Make(op.devBounds); 266 if (this->adjustForSaveLayerPaints(&clip, kSavesToIgnore)) { 267 fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty(); 268 } else { 269 fCurrentClipBounds = fCullRect; 270 } 271 } 272 273 // We also take advantage of SaveLayer bounds when present to further cut the clip down. 274 void updateClipBounds(const SaveLayer& op) { 275 if (op.bounds) { 276 // adjustAndMap() intersects these layer bounds with the previous clip for us. 277 fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint); 278 } 279 } 280 281 // The bounds of these ops must be calculated when we hit the Restore 282 // from the bounds of the ops in the same Save block. 283 void trackBounds(const Save&) { this->pushSaveBlock(NULL); } 284 void trackBounds(const SaveLayer& op) { this->pushSaveBlock(op.paint); } 285 void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); } 286 287 void trackBounds(const SetMatrix&) { this->pushControl(); } 288 void trackBounds(const ClipRect&) { this->pushControl(); } 289 void trackBounds(const ClipRRect&) { this->pushControl(); } 290 void trackBounds(const ClipPath&) { this->pushControl(); } 291 void trackBounds(const ClipRegion&) { this->pushControl(); } 292 void trackBounds(const BeginCommentGroup&) { this->pushControl(); } 293 void trackBounds(const AddComment&) { this->pushControl(); } 294 void trackBounds(const EndCommentGroup&) { this->pushControl(); } 295 296 // For all other ops, we can calculate and store the bounds directly now. 297 template <typename T> void trackBounds(const T& op) { 298 fBounds[fCurrentOp] = this->bounds(op); 299 this->updateSaveBounds(fBounds[fCurrentOp]); 300 } 301 302 void pushSaveBlock(const SkPaint* paint) { 303 // Starting a new Save block. Push a new entry to represent that. 304 SaveBounds sb; 305 sb.controlOps = 0; 306 // If the paint affects transparent black, the bound shouldn't be smaller 307 // than the current clip bounds. 308 sb.bounds = 309 PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty(); 310 sb.paint = paint; 311 312 fSaveStack.push(sb); 313 this->pushControl(); 314 } 315 316 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) { 317 if (paint) { 318 // FIXME: this is very conservative 319 if (paint->getImageFilter() || paint->getColorFilter()) { 320 return true; 321 } 322 323 // Unusual Xfermodes require us to process a saved layer 324 // even with operations outisde the clip. 325 // For example, DstIn is used by masking layers. 326 // https://code.google.com/p/skia/issues/detail?id=1291 327 // https://crbug.com/401593 328 SkXfermode* xfermode = paint->getXfermode(); 329 SkXfermode::Mode mode; 330 // SrcOver is ok, and is also the common case with a NULL xfermode. 331 // So we should make that the fast path and bypass the mode extraction 332 // and test. 333 if (xfermode && xfermode->asMode(&mode)) { 334 switch (mode) { 335 // For each of the following transfer modes, if the source 336 // alpha is zero (our transparent black), the resulting 337 // blended alpha is not necessarily equal to the original 338 // destination alpha. 339 case SkXfermode::kClear_Mode: 340 case SkXfermode::kSrc_Mode: 341 case SkXfermode::kSrcIn_Mode: 342 case SkXfermode::kDstIn_Mode: 343 case SkXfermode::kSrcOut_Mode: 344 case SkXfermode::kDstATop_Mode: 345 case SkXfermode::kModulate_Mode: 346 return true; 347 break; 348 default: 349 break; 350 } 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 DrawSprite& op) const { // Ignores the matrix, but respects the clip. 398 SkRect rect = Bounds::MakeXYWH(op.left, op.top, op.bitmap.width(), op.bitmap.height()); 399 if (!rect.intersect(fCurrentClipBounds)) { 400 return Bounds::MakeEmpty(); 401 } 402 return rect; 403 } 404 405 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); } 406 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); } 407 Bounds bounds(const DrawRRect& op) const { 408 return this->adjustAndMap(op.rrect.rect(), &op.paint); 409 } 410 Bounds bounds(const DrawDRRect& op) const { 411 return this->adjustAndMap(op.outer.rect(), &op.paint); 412 } 413 Bounds bounds(const DrawImage& op) const { 414 const SkImage* image = op.image; 415 SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height()); 416 417 return this->adjustAndMap(rect, op.paint); 418 } 419 Bounds bounds(const DrawImageRect& op) const { 420 return this->adjustAndMap(op.dst, op.paint); 421 } 422 Bounds bounds(const DrawBitmapRectToRect& op) const { 423 return this->adjustAndMap(op.dst, op.paint); 424 } 425 Bounds bounds(const DrawBitmapRectToRectBleed& op) const { 426 return this->adjustAndMap(op.dst, op.paint); 427 } 428 Bounds bounds(const DrawBitmapNine& op) const { 429 return this->adjustAndMap(op.dst, op.paint); 430 } 431 Bounds bounds(const DrawBitmap& op) const { 432 return this->adjustAndMap( 433 SkRect::MakeXYWH(op.left, op.top, op.bitmap.width(), op.bitmap.height()), 434 op.paint); 435 } 436 437 Bounds bounds(const DrawPath& op) const { 438 return op.path.isInverseFillType() ? fCurrentClipBounds 439 : this->adjustAndMap(op.path.getBounds(), &op.paint); 440 } 441 Bounds bounds(const DrawPoints& op) const { 442 SkRect dst; 443 dst.set(op.pts, op.count); 444 445 // Pad the bounding box a little to make sure hairline points' bounds aren't empty. 446 SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f); 447 dst.outset(stroke/2, stroke/2); 448 449 return this->adjustAndMap(dst, &op.paint); 450 } 451 Bounds bounds(const DrawPatch& op) const { 452 SkRect dst; 453 dst.set(op.cubics, SkPatchUtils::kNumCtrlPts); 454 return this->adjustAndMap(dst, &op.paint); 455 } 456 Bounds bounds(const DrawVertices& op) const { 457 SkRect dst; 458 dst.set(op.vertices, op.vertexCount); 459 return this->adjustAndMap(dst, &op.paint); 460 } 461 462 Bounds bounds(const DrawPicture& op) const { 463 SkRect dst = op.picture->cullRect(); 464 op.matrix.mapRect(&dst); 465 return this->adjustAndMap(dst, op.paint); 466 } 467 468 Bounds bounds(const DrawPosText& op) const { 469 const int N = op.paint.countText(op.text, op.byteLength); 470 if (N == 0) { 471 return Bounds::MakeEmpty(); 472 } 473 474 SkRect dst; 475 dst.set(op.pos, N); 476 AdjustTextForFontMetrics(&dst, op.paint); 477 return this->adjustAndMap(dst, &op.paint); 478 } 479 Bounds bounds(const DrawPosTextH& op) const { 480 const int N = op.paint.countText(op.text, op.byteLength); 481 if (N == 0) { 482 return Bounds::MakeEmpty(); 483 } 484 485 SkScalar left = op.xpos[0], right = op.xpos[0]; 486 for (int i = 1; i < N; i++) { 487 left = SkMinScalar(left, op.xpos[i]); 488 right = SkMaxScalar(right, op.xpos[i]); 489 } 490 SkRect dst = { left, op.y, right, op.y }; 491 AdjustTextForFontMetrics(&dst, op.paint); 492 return this->adjustAndMap(dst, &op.paint); 493 } 494 Bounds bounds(const DrawTextOnPath& op) const { 495 SkRect dst = op.path.getBounds(); 496 497 // Pad all sides by the maximum padding in any direction we'd normally apply. 498 SkRect pad = { 0, 0, 0, 0}; 499 AdjustTextForFontMetrics(&pad, op.paint); 500 501 // That maximum padding happens to always be the right pad today. 502 SkASSERT(pad.fLeft == -pad.fRight); 503 SkASSERT(pad.fTop == -pad.fBottom); 504 SkASSERT(pad.fRight > pad.fBottom); 505 dst.outset(pad.fRight, pad.fRight); 506 507 return this->adjustAndMap(dst, &op.paint); 508 } 509 510 Bounds bounds(const DrawTextBlob& op) const { 511 SkRect dst = op.blob->bounds(); 512 dst.offset(op.x, op.y); 513 return this->adjustAndMap(dst, &op.paint); 514 } 515 516 Bounds bounds(const DrawDrawable& op) const { 517 return this->adjustAndMap(op.worstCaseBounds, NULL); 518 } 519 520 static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint) { 521#ifdef SK_DEBUG 522 SkRect correct = *rect; 523#endif 524 // crbug.com/373785 ~~> xPad = 4x yPad 525 // crbug.com/424824 ~~> bump yPad from 2x text size to 2.5x 526 const SkScalar yPad = 2.5f * paint.getTextSize(), 527 xPad = 4.0f * yPad; 528 rect->outset(xPad, yPad); 529#ifdef SK_DEBUG 530 SkPaint::FontMetrics metrics; 531 paint.getFontMetrics(&metrics); 532 correct.fLeft += metrics.fXMin; 533 correct.fTop += metrics.fTop; 534 correct.fRight += metrics.fXMax; 535 correct.fBottom += metrics.fBottom; 536 // See skia:2862 for why we ignore small text sizes. 537 SkASSERTF(paint.getTextSize() < 0.001f || rect->contains(correct), 538 "%f %f %f %f vs. %f %f %f %f\n", 539 -xPad, -yPad, +xPad, +yPad, 540 metrics.fXMin, metrics.fTop, metrics.fXMax, metrics.fBottom); 541#endif 542 } 543 544 // Returns true if rect was meaningfully adjusted for the effects of paint, 545 // false if the paint could affect the rect in unknown ways. 546 static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) { 547 if (paint) { 548 if (paint->canComputeFastBounds()) { 549 *rect = paint->computeFastBounds(*rect, rect); 550 return true; 551 } 552 return false; 553 } 554 return true; 555 } 556 557 bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const { 558 for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) { 559 if (!AdjustForPaint(fSaveStack[i].paint, rect)) { 560 return false; 561 } 562 } 563 return true; 564 } 565 566 const unsigned fNumRecords; 567 568 // We do not guarantee anything for operations outside of the cull rect 569 const SkRect fCullRect; 570 571 // Conservative identity-space bounds for each op in the SkRecord. 572 SkAutoTMalloc<Bounds> fBounds; 573 574 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() 575 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative 576 // identity-space bounds of the current clip (fCurrentClipBounds). 577 unsigned fCurrentOp; 578 const SkMatrix* fCTM; 579 Bounds fCurrentClipBounds; 580 581 // Used to track the bounds of Save/Restore blocks and the control ops inside them. 582 SkTDArray<SaveBounds> fSaveStack; 583 SkTDArray<unsigned> fControlIndices; 584}; 585 586// SkRecord visitor to gather saveLayer/restore information. 587class CollectLayers : SkNoncopyable { 588public: 589 CollectLayers(const SkRect& cullRect, const SkRecord& record, 590 const SkPicture::SnapshotArray* pictList, SkLayerInfo* accelData) 591 : fSaveLayersInStack(0) 592 , fAccelData(accelData) 593 , fPictList(pictList) 594 , fFillBounds(cullRect, record) 595 {} 596 597 void setCurrentOp(unsigned currentOp) { fFillBounds.setCurrentOp(currentOp); } 598 599 void cleanUp(SkBBoxHierarchy* bbh) { 600 // fFillBounds must perform its cleanUp first so that all the bounding 601 // boxes associated with unbalanced restores are updated (prior to 602 // fetching their bound in popSaveLayerInfo). 603 fFillBounds.cleanUp(bbh); 604 605 while (!fSaveLayerStack.isEmpty()) { 606 this->popSaveLayerInfo(); 607 } 608 } 609 610 template <typename T> void operator()(const T& op) { 611 fFillBounds(op); 612 this->trackSaveLayers(op); 613 } 614 615private: 616 struct SaveLayerInfo { 617 SaveLayerInfo() { } 618 SaveLayerInfo(int opIndex, bool isSaveLayer, const SkRect* bounds, const SkPaint* paint) 619 : fStartIndex(opIndex) 620 , fIsSaveLayer(isSaveLayer) 621 , fHasNestedSaveLayer(false) 622 , fBounds(bounds ? *bounds : SkRect::MakeEmpty()) 623 , fPaint(paint) { 624 } 625 626 int fStartIndex; 627 bool fIsSaveLayer; 628 bool fHasNestedSaveLayer; 629 SkRect fBounds; 630 const SkPaint* fPaint; 631 }; 632 633 template <typename T> void trackSaveLayers(const T& op) { 634 /* most ops aren't involved in saveLayers */ 635 } 636 void trackSaveLayers(const Save& s) { this->pushSaveLayerInfo(false, NULL, NULL); } 637 void trackSaveLayers(const SaveLayer& sl) { this->pushSaveLayerInfo(true, sl.bounds, sl.paint); } 638 void trackSaveLayers(const Restore& r) { this->popSaveLayerInfo(); } 639 640 void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) { 641 // For sub-pictures, we wrap their layer information within the parent 642 // picture's rendering hierarchy 643 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); 644 645 const SkLayerInfo* childData = 646 static_cast<const SkLayerInfo*>(picture->EXPERIMENTAL_getAccelData(key)); 647 if (!childData) { 648 // If the child layer hasn't been generated with saveLayer data we 649 // assume the worst (i.e., that it does contain layers which nest 650 // inside existing layers). Layers within sub-pictures that don't 651 // have saveLayer data cannot be hoisted. 652 // TODO: could the analysis data be use to fine tune this? 653 this->updateStackForSaveLayer(); 654 return; 655 } 656 657 for (int i = 0; i < childData->numBlocks(); ++i) { 658 const SkLayerInfo::BlockInfo& src = childData->block(i); 659 660 FillBounds::Bounds newBound = fFillBounds.adjustAndMap(src.fBounds, paint); 661 if (newBound.isEmpty()) { 662 continue; 663 } 664 665 this->updateStackForSaveLayer(); 666 667 SkLayerInfo::BlockInfo& dst = fAccelData->addBlock(); 668 669 // If src.fPicture is NULL the layer is in dp.picture; otherwise 670 // it belongs to a sub-picture. 671 dst.fPicture = src.fPicture ? src.fPicture : picture; 672 dst.fPicture->ref(); 673 dst.fBounds = newBound; 674 dst.fSrcBounds = src.fSrcBounds; 675 dst.fLocalMat = src.fLocalMat; 676 dst.fPreMat = src.fPreMat; 677 dst.fPreMat.postConcat(fFillBounds.ctm()); 678 if (src.fPaint) { 679 dst.fPaint = SkNEW_ARGS(SkPaint, (*src.fPaint)); 680 } 681 dst.fSaveLayerOpID = src.fSaveLayerOpID; 682 dst.fRestoreOpID = src.fRestoreOpID; 683 dst.fHasNestedLayers = src.fHasNestedLayers; 684 dst.fIsNested = fSaveLayersInStack > 0 || src.fIsNested; 685 686 // Store 'saveLayer ops from enclosing picture' + drawPict op + 'ops from sub-picture' 687 dst.fKeySize = fSaveLayerOpStack.count() + src.fKeySize + 1; 688 dst.fKey = SkNEW_ARRAY(unsigned, dst.fKeySize); 689 memcpy(dst.fKey, fSaveLayerOpStack.begin(), fSaveLayerOpStack.count() * sizeof(unsigned)); 690 dst.fKey[fSaveLayerOpStack.count()] = fFillBounds.currentOp(); 691 memcpy(&dst.fKey[fSaveLayerOpStack.count()+1], src.fKey, src.fKeySize * sizeof(unsigned)); 692 } 693 } 694 695 void trackSaveLayers(const DrawPicture& dp) { 696 this->trackSaveLayersForPicture(dp.picture, dp.paint); 697 } 698 699 void trackSaveLayers(const DrawDrawable& dp) { 700 SkASSERT(fPictList); 701 SkASSERT(dp.index >= 0 && dp.index < fPictList->count()); 702 const SkPaint* paint = NULL; // drawables don't get a side-car paint 703 this->trackSaveLayersForPicture(fPictList->begin()[dp.index], paint); 704 } 705 706 // Inform all the saveLayers already on the stack that they now have a 707 // nested saveLayer inside them 708 void updateStackForSaveLayer() { 709 for (int index = fSaveLayerStack.count() - 1; index >= 0; --index) { 710 if (fSaveLayerStack[index].fHasNestedSaveLayer) { 711 break; 712 } 713 fSaveLayerStack[index].fHasNestedSaveLayer = true; 714 if (fSaveLayerStack[index].fIsSaveLayer) { 715 break; 716 } 717 } 718 } 719 720 void pushSaveLayerInfo(bool isSaveLayer, const SkRect* bounds, const SkPaint* paint) { 721 if (isSaveLayer) { 722 this->updateStackForSaveLayer(); 723 ++fSaveLayersInStack; 724 fSaveLayerOpStack.push(fFillBounds.currentOp()); 725 } 726 727 fSaveLayerStack.push(SaveLayerInfo(fFillBounds.currentOp(), isSaveLayer, bounds, paint)); 728 } 729 730 void popSaveLayerInfo() { 731 if (fSaveLayerStack.count() <= 0) { 732 SkASSERT(false); 733 return; 734 } 735 736 SkASSERT(fSaveLayersInStack == fSaveLayerOpStack.count()); 737 738 SaveLayerInfo sli; 739 fSaveLayerStack.pop(&sli); 740 741 if (!sli.fIsSaveLayer) { 742 return; 743 } 744 745 --fSaveLayersInStack; 746 747 SkLayerInfo::BlockInfo& block = fAccelData->addBlock(); 748 749 SkASSERT(NULL == block.fPicture); // This layer is in the top-most picture 750 751 block.fBounds = fFillBounds.getBounds(sli.fStartIndex); 752 block.fLocalMat = fFillBounds.ctm(); 753 block.fPreMat = SkMatrix::I(); 754 if (sli.fPaint) { 755 block.fPaint = SkNEW_ARGS(SkPaint, (*sli.fPaint)); 756 } 757 758 block.fSrcBounds = sli.fBounds; 759 block.fSaveLayerOpID = sli.fStartIndex; 760 block.fRestoreOpID = fFillBounds.currentOp(); 761 block.fHasNestedLayers = sli.fHasNestedSaveLayer; 762 block.fIsNested = fSaveLayersInStack > 0; 763 764 block.fKeySize = fSaveLayerOpStack.count(); 765 block.fKey = SkNEW_ARRAY(unsigned, block.fKeySize); 766 memcpy(block.fKey, fSaveLayerOpStack.begin(), block.fKeySize * sizeof(unsigned)); 767 768 fSaveLayerOpStack.pop(); 769 } 770 771 // Used to collect saveLayer information for layer hoisting 772 int fSaveLayersInStack; 773 SkTDArray<SaveLayerInfo> fSaveLayerStack; 774 // The op code indices of all the currently active saveLayers 775 SkTDArray<unsigned> fSaveLayerOpStack; 776 SkLayerInfo* fAccelData; 777 const SkPicture::SnapshotArray* fPictList; 778 779 SkRecords::FillBounds fFillBounds; 780}; 781 782} // namespace SkRecords 783 784void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHierarchy* bbh) { 785 SkRecords::FillBounds visitor(cullRect, record); 786 787 for (unsigned curOp = 0; curOp < record.count(); curOp++) { 788 visitor.setCurrentOp(curOp); 789 record.visit<void>(curOp, visitor); 790 } 791 792 visitor.cleanUp(bbh); 793} 794 795void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record, 796 const SkPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh, 797 SkLayerInfo* data) { 798 SkRecords::CollectLayers visitor(cullRect, record, pictList, data); 799 800 for (unsigned curOp = 0; curOp < record.count(); curOp++) { 801 visitor.setCurrentOp(curOp); 802 record.visit<void>(curOp, visitor); 803 } 804 805 visitor.cleanUp(bbh); 806} 807 808