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 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. 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<int> 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 (int 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 60 void SkRecordPartialDraw(const SkRecord& record, SkCanvas* canvas, 61 SkPicture const* const drawablePicts[], int drawableCount, 62 int start, int 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, nullptr, drawableCount, &initialCTM); 68 for (int i = start; i < stop; i++) { 69 record.visit<void>(i, draw); 70 } 71 } 72 73 namespace SkRecords { 74 75 // NoOps draw nothing. 76 template <> void Draw::draw(const NoOp&) {} 77 78 #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; } 79 DRAW(Restore, restore()); 80 DRAW(Save, save()); 81 DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.backdrop, r.saveLayerFlags))); 82 DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); 83 DRAW(Concat, concat(r.matrix)); 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 DRAW(DrawBitmap, drawBitmap(r.bitmap.shallowCopy(), r.left, r.top, r.paint)); 91 DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap.shallowCopy(), r.center, r.dst, r.paint)); 92 DRAW(DrawBitmapRect, 93 legacy_drawBitmapRect(r.bitmap.shallowCopy(), r.src, r.dst, r.paint, 94 SkCanvas::kStrict_SrcRectConstraint)); 95 DRAW(DrawBitmapRectFast, 96 legacy_drawBitmapRect(r.bitmap.shallowCopy(), r.src, r.dst, r.paint, 97 SkCanvas::kFast_SrcRectConstraint)); 98 DRAW(DrawBitmapRectFixedSize, 99 legacy_drawBitmapRect(r.bitmap.shallowCopy(), &r.src, r.dst, &r.paint, r.constraint)); 100 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); 101 DRAW(DrawImage, drawImage(r.image, r.left, r.top, r.paint)); 102 DRAW(DrawImageRect, legacy_drawImageRect(r.image, r.src, r.dst, r.paint, r.constraint)); 103 DRAW(DrawImageNine, drawImageNine(r.image, r.center, r.dst, r.paint)); 104 DRAW(DrawOval, drawOval(r.oval, r.paint)); 105 DRAW(DrawPaint, drawPaint(r.paint)); 106 DRAW(DrawPath, drawPath(r.path, r.paint)); 107 DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.xmode, r.paint)); 108 DRAW(DrawPicture, drawPicture(r.picture, &r.matrix, r.paint)); 109 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); 110 DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); 111 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); 112 DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); 113 DRAW(DrawRect, drawRect(r.rect, r.paint)); 114 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); 115 DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); 116 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint)); 117 DRAW(DrawAtlas, drawAtlas(r.atlas, r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint)); 118 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, 119 r.xmode, r.indices, r.indexCount, r.paint)); 120 #undef DRAW 121 122 template <> void Draw::draw(const DrawDrawable& r) { 123 SkASSERT(r.index >= 0); 124 SkASSERT(r.index < fDrawableCount); 125 if (fDrawables) { 126 SkASSERT(nullptr == fDrawablePicts); 127 fCanvas->drawDrawable(fDrawables[r.index], r.matrix); 128 } else { 129 fCanvas->drawPicture(fDrawablePicts[r.index], r.matrix, nullptr); 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. 151 class FillBounds : SkNoncopyable { 152 public: 153 FillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[]) 154 : fNumRecords(record.count()) 155 , fCullRect(cullRect) 156 , fBounds(bounds) { 157 fCTM = SkMatrix::I(); 158 fCurrentClipBounds = fCullRect; 159 } 160 161 void cleanUp() { 162 // If we have any lingering unpaired Saves, simulate restores to make 163 // sure all ops in those Save blocks have their bounds calculated. 164 while (!fSaveStack.isEmpty()) { 165 this->popSaveBlock(); 166 } 167 168 // Any control ops not part of any Save/Restore block draw everywhere. 169 while (!fControlIndices.isEmpty()) { 170 this->popControl(fCullRect); 171 } 172 } 173 174 void setCurrentOp(int currentOp) { fCurrentOp = currentOp; } 175 176 177 template <typename T> void operator()(const T& op) { 178 this->updateCTM(op); 179 this->updateClipBounds(op); 180 this->trackBounds(op); 181 } 182 183 // In this file, SkRect are in local coordinates, Bounds are translated back to identity space. 184 typedef SkRect Bounds; 185 186 int currentOp() const { return fCurrentOp; } 187 const SkMatrix& ctm() const { return fCTM; } 188 const Bounds& getBounds(int index) const { return fBounds[index]; } 189 190 // Adjust rect for all paints that may affect its geometry, then map it to identity space. 191 Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const { 192 // Inverted rectangles really confuse our BBHs. 193 rect.sort(); 194 195 // Adjust the rect for its own paint. 196 if (!AdjustForPaint(paint, &rect)) { 197 // The paint could do anything to our bounds. The only safe answer is the current clip. 198 return fCurrentClipBounds; 199 } 200 201 // Adjust rect for all the paints from the SaveLayers we're inside. 202 if (!this->adjustForSaveLayerPaints(&rect)) { 203 // Same deal as above. 204 return fCurrentClipBounds; 205 } 206 207 // Map the rect back to identity space. 208 fCTM.mapRect(&rect); 209 210 // Nothing can draw outside the current clip. 211 if (!rect.intersect(fCurrentClipBounds)) { 212 return Bounds::MakeEmpty(); 213 } 214 215 return rect; 216 } 217 218 private: 219 struct SaveBounds { 220 int controlOps; // Number of control ops in this Save block, including the Save. 221 Bounds bounds; // Bounds of everything in the block. 222 const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block. 223 SkMatrix ctm; 224 }; 225 226 // Only Restore, SetMatrix, and Concat change the CTM. 227 template <typename T> void updateCTM(const T&) {} 228 void updateCTM(const Restore& op) { fCTM = op.matrix; } 229 void updateCTM(const SetMatrix& op) { fCTM = op.matrix; } 230 void updateCTM(const Concat& op) { fCTM.preConcat(op.matrix); } 231 232 // Most ops don't change the clip. 233 template <typename T> void updateClipBounds(const T&) {} 234 235 // Clip{Path,RRect,Rect,Region} obviously change the clip. They all know their bounds already. 236 void updateClipBounds(const ClipPath& op) { this->updateClipBoundsForClipOp(op.devBounds); } 237 void updateClipBounds(const ClipRRect& op) { this->updateClipBoundsForClipOp(op.devBounds); } 238 void updateClipBounds(const ClipRect& op) { this->updateClipBoundsForClipOp(op.devBounds); } 239 void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipOp(op.devBounds); } 240 241 // The bounds of clip ops need to be adjusted for the paints of saveLayers they're inside. 242 void updateClipBoundsForClipOp(const SkIRect& devBounds) { 243 Bounds clip = SkRect::Make(devBounds); 244 // We don't call adjustAndMap() because as its last step it would intersect the adjusted 245 // clip bounds with the previous clip, exactly what we can't do when the clip grows. 246 if (this->adjustForSaveLayerPaints(&clip)) { 247 fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty(); 248 } else { 249 fCurrentClipBounds = fCullRect; 250 } 251 } 252 253 // Restore holds the devBounds for the clip after the {save,saveLayer}/restore block completes. 254 void updateClipBounds(const Restore& op) { 255 // This is just like the clip ops above, but we need to skip the effects (if any) of our 256 // paired saveLayer (if it is one); it has not yet been popped off the save stack. Our 257 // devBounds reflect the state of the world after the saveLayer/restore block is done, 258 // so they are not affected by the saveLayer's paint. 259 const int kSavesToIgnore = 1; 260 Bounds clip = SkRect::Make(op.devBounds); 261 if (this->adjustForSaveLayerPaints(&clip, kSavesToIgnore)) { 262 fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty(); 263 } else { 264 fCurrentClipBounds = fCullRect; 265 } 266 } 267 268 // We also take advantage of SaveLayer bounds when present to further cut the clip down. 269 void updateClipBounds(const SaveLayer& op) { 270 if (op.bounds) { 271 // adjustAndMap() intersects these layer bounds with the previous clip for us. 272 fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint); 273 } 274 } 275 276 // The bounds of these ops must be calculated when we hit the Restore 277 // from the bounds of the ops in the same Save block. 278 void trackBounds(const Save&) { this->pushSaveBlock(nullptr); } 279 void trackBounds(const SaveLayer& op) { this->pushSaveBlock(op.paint); } 280 void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); } 281 282 void trackBounds(const SetMatrix&) { this->pushControl(); } 283 void trackBounds(const Concat&) { this->pushControl(); } 284 void trackBounds(const ClipRect&) { this->pushControl(); } 285 void trackBounds(const ClipRRect&) { this->pushControl(); } 286 void trackBounds(const ClipPath&) { this->pushControl(); } 287 void trackBounds(const ClipRegion&) { this->pushControl(); } 288 289 // For all other ops, we can calculate and store the bounds directly now. 290 template <typename T> void trackBounds(const T& op) { 291 fBounds[fCurrentOp] = this->bounds(op); 292 this->updateSaveBounds(fBounds[fCurrentOp]); 293 } 294 295 void pushSaveBlock(const SkPaint* paint) { 296 // Starting a new Save block. Push a new entry to represent that. 297 SaveBounds sb; 298 sb.controlOps = 0; 299 // If the paint affects transparent black, the bound shouldn't be smaller 300 // than the current clip bounds. 301 sb.bounds = 302 PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty(); 303 sb.paint = paint; 304 sb.ctm = this->fCTM; 305 306 fSaveStack.push(sb); 307 this->pushControl(); 308 } 309 310 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) { 311 if (paint) { 312 // FIXME: this is very conservative 313 if (paint->getImageFilter() || paint->getColorFilter()) { 314 return true; 315 } 316 317 // Unusual Xfermodes require us to process a saved layer 318 // even with operations outisde the clip. 319 // For example, DstIn is used by masking layers. 320 // https://code.google.com/p/skia/issues/detail?id=1291 321 // https://crbug.com/401593 322 SkXfermode* xfermode = paint->getXfermode(); 323 SkXfermode::Mode mode; 324 // SrcOver is ok, and is also the common case with a nullptr xfermode. 325 // So we should make that the fast path and bypass the mode extraction 326 // and test. 327 if (xfermode && xfermode->asMode(&mode)) { 328 switch (mode) { 329 // For each of the following transfer modes, if the source 330 // alpha is zero (our transparent black), the resulting 331 // blended alpha is not necessarily equal to the original 332 // destination alpha. 333 case SkXfermode::kClear_Mode: 334 case SkXfermode::kSrc_Mode: 335 case SkXfermode::kSrcIn_Mode: 336 case SkXfermode::kDstIn_Mode: 337 case SkXfermode::kSrcOut_Mode: 338 case SkXfermode::kDstATop_Mode: 339 case SkXfermode::kModulate_Mode: 340 return true; 341 break; 342 default: 343 break; 344 } 345 } 346 } 347 return false; 348 } 349 350 Bounds popSaveBlock() { 351 // We're done the Save block. Apply the block's bounds to all control ops inside it. 352 SaveBounds sb; 353 fSaveStack.pop(&sb); 354 355 while (sb.controlOps --> 0) { 356 this->popControl(sb.bounds); 357 } 358 359 // This whole Save block may be part another Save block. 360 this->updateSaveBounds(sb.bounds); 361 362 // If called from a real Restore (not a phony one for balance), it'll need the bounds. 363 return sb.bounds; 364 } 365 366 void pushControl() { 367 fControlIndices.push(fCurrentOp); 368 if (!fSaveStack.isEmpty()) { 369 fSaveStack.top().controlOps++; 370 } 371 } 372 373 void popControl(const Bounds& bounds) { 374 fBounds[fControlIndices.top()] = bounds; 375 fControlIndices.pop(); 376 } 377 378 void updateSaveBounds(const Bounds& bounds) { 379 // If we're in a Save block, expand its bounds to cover these bounds too. 380 if (!fSaveStack.isEmpty()) { 381 fSaveStack.top().bounds.join(bounds); 382 } 383 } 384 385 // FIXME: this method could use better bounds 386 Bounds bounds(const DrawText&) const { return fCurrentClipBounds; } 387 388 Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } 389 Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOps don't draw. 390 391 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); } 392 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); } 393 Bounds bounds(const DrawRRect& op) const { 394 return this->adjustAndMap(op.rrect.rect(), &op.paint); 395 } 396 Bounds bounds(const DrawDRRect& op) const { 397 return this->adjustAndMap(op.outer.rect(), &op.paint); 398 } 399 Bounds bounds(const DrawImage& op) const { 400 const SkImage* image = op.image; 401 SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height()); 402 403 return this->adjustAndMap(rect, op.paint); 404 } 405 Bounds bounds(const DrawImageRect& op) const { 406 return this->adjustAndMap(op.dst, op.paint); 407 } 408 Bounds bounds(const DrawImageNine& op) const { 409 return this->adjustAndMap(op.dst, op.paint); 410 } 411 Bounds bounds(const DrawBitmapRect& op) const { 412 return this->adjustAndMap(op.dst, op.paint); 413 } 414 Bounds bounds(const DrawBitmapRectFast& op) const { 415 return this->adjustAndMap(op.dst, op.paint); 416 } 417 Bounds bounds(const DrawBitmapRectFixedSize& op) const { 418 return this->adjustAndMap(op.dst, &op.paint); 419 } 420 Bounds bounds(const DrawBitmapNine& op) const { 421 return this->adjustAndMap(op.dst, op.paint); 422 } 423 Bounds bounds(const DrawBitmap& op) const { 424 return this->adjustAndMap( 425 SkRect::MakeXYWH(op.left, op.top, op.bitmap.width(), op.bitmap.height()), 426 op.paint); 427 } 428 429 Bounds bounds(const DrawPath& op) const { 430 return op.path.isInverseFillType() ? fCurrentClipBounds 431 : this->adjustAndMap(op.path.getBounds(), &op.paint); 432 } 433 Bounds bounds(const DrawPoints& op) const { 434 SkRect dst; 435 dst.set(op.pts, op.count); 436 437 // Pad the bounding box a little to make sure hairline points' bounds aren't empty. 438 SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f); 439 dst.outset(stroke/2, stroke/2); 440 441 return this->adjustAndMap(dst, &op.paint); 442 } 443 Bounds bounds(const DrawPatch& op) const { 444 SkRect dst; 445 dst.set(op.cubics, SkPatchUtils::kNumCtrlPts); 446 return this->adjustAndMap(dst, &op.paint); 447 } 448 Bounds bounds(const DrawVertices& op) const { 449 SkRect dst; 450 dst.set(op.vertices, op.vertexCount); 451 return this->adjustAndMap(dst, &op.paint); 452 } 453 454 Bounds bounds(const DrawAtlas& op) const { 455 if (op.cull) { 456 return this->adjustAndMap(*op.cull, op.paint); 457 } else { 458 return fCurrentClipBounds; 459 } 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, nullptr); 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 SkMatrix inverse; 560 if (!fSaveStack[i].ctm.invert(&inverse)) { 561 return false; 562 } 563 inverse.mapRect(rect); 564 if (!AdjustForPaint(fSaveStack[i].paint, rect)) { 565 return false; 566 } 567 fSaveStack[i].ctm.mapRect(rect); 568 } 569 return true; 570 } 571 572 const int fNumRecords; 573 574 // We do not guarantee anything for operations outside of the cull rect 575 const SkRect fCullRect; 576 577 // Conservative identity-space bounds for each op in the SkRecord. 578 Bounds* fBounds; 579 580 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() 581 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative 582 // identity-space bounds of the current clip (fCurrentClipBounds). 583 int fCurrentOp; 584 SkMatrix fCTM; 585 Bounds fCurrentClipBounds; 586 587 // Used to track the bounds of Save/Restore blocks and the control ops inside them. 588 SkTDArray<SaveBounds> fSaveStack; 589 SkTDArray<int> fControlIndices; 590 }; 591 592 // SkRecord visitor to gather saveLayer/restore information. 593 class CollectLayers : SkNoncopyable { 594 public: 595 CollectLayers(const SkRect& cullRect, const SkRecord& record, SkRect bounds[], 596 const SkBigPicture::SnapshotArray* pictList, SkLayerInfo* accelData) 597 : fSaveLayersInStack(0) 598 , fAccelData(accelData) 599 , fPictList(pictList) 600 , fFillBounds(cullRect, record, bounds) 601 {} 602 603 void cleanUp() { 604 // fFillBounds must perform its cleanUp first so that all the bounding 605 // boxes associated with unbalanced restores are updated (prior to 606 // fetching their bound in popSaveLayerInfo). 607 fFillBounds.cleanUp(); 608 while (!fSaveLayerStack.isEmpty()) { 609 this->popSaveLayerInfo(); 610 } 611 } 612 613 void setCurrentOp(int currentOp) { fFillBounds.setCurrentOp(currentOp); } 614 615 616 template <typename T> void operator()(const T& op) { 617 fFillBounds(op); 618 this->trackSaveLayers(op); 619 } 620 621 private: 622 struct SaveLayerInfo { 623 SaveLayerInfo() { } 624 SaveLayerInfo(int opIndex, bool isSaveLayer, const SkRect* bounds, const SkPaint* paint) 625 : fStartIndex(opIndex) 626 , fIsSaveLayer(isSaveLayer) 627 , fHasNestedSaveLayer(false) 628 , fBounds(bounds ? *bounds : SkRect::MakeEmpty()) 629 , fPaint(paint) { 630 } 631 632 int fStartIndex; 633 bool fIsSaveLayer; 634 bool fHasNestedSaveLayer; 635 SkRect fBounds; 636 const SkPaint* fPaint; 637 }; 638 639 template <typename T> void trackSaveLayers(const T& op) { 640 /* most ops aren't involved in saveLayers */ 641 } 642 void trackSaveLayers(const Save& s) { this->pushSaveLayerInfo(false, nullptr, nullptr); } 643 void trackSaveLayers(const SaveLayer& sl) { this->pushSaveLayerInfo(true, sl.bounds, sl.paint); } 644 void trackSaveLayers(const Restore& r) { this->popSaveLayerInfo(); } 645 646 void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) { 647 // For sub-pictures, we wrap their layer information within the parent 648 // picture's rendering hierarchy 649 const SkLayerInfo* childData = nullptr; 650 if (const SkBigPicture* bp = picture->asSkBigPicture()) { 651 childData = static_cast<const SkLayerInfo*>(bp->accelData()); 652 } 653 if (!childData) { 654 // If the child layer hasn't been generated with saveLayer data we 655 // assume the worst (i.e., that it does contain layers which nest 656 // inside existing layers). Layers within sub-pictures that don't 657 // have saveLayer data cannot be hoisted. 658 // TODO: could the analysis data be use to fine tune this? 659 this->updateStackForSaveLayer(); 660 return; 661 } 662 663 for (int i = 0; i < childData->numBlocks(); ++i) { 664 const SkLayerInfo::BlockInfo& src = childData->block(i); 665 666 FillBounds::Bounds newBound = fFillBounds.adjustAndMap(src.fBounds, paint); 667 if (newBound.isEmpty()) { 668 continue; 669 } 670 671 this->updateStackForSaveLayer(); 672 673 SkLayerInfo::BlockInfo& dst = fAccelData->addBlock(); 674 675 // If src.fPicture is nullptr the layer is in dp.picture; otherwise 676 // it belongs to a sub-picture. 677 dst.fPicture = src.fPicture ? src.fPicture : picture; 678 dst.fPicture->ref(); 679 dst.fBounds = newBound; 680 dst.fSrcBounds = src.fSrcBounds; 681 dst.fLocalMat = src.fLocalMat; 682 dst.fPreMat = src.fPreMat; 683 dst.fPreMat.postConcat(fFillBounds.ctm()); 684 if (src.fPaint) { 685 dst.fPaint = new SkPaint(*src.fPaint); 686 } 687 dst.fSaveLayerOpID = src.fSaveLayerOpID; 688 dst.fRestoreOpID = src.fRestoreOpID; 689 dst.fHasNestedLayers = src.fHasNestedLayers; 690 dst.fIsNested = fSaveLayersInStack > 0 || src.fIsNested; 691 692 // Store 'saveLayer ops from enclosing picture' + drawPict op + 'ops from sub-picture' 693 dst.fKeySize = fSaveLayerOpStack.count() + src.fKeySize + 1; 694 dst.fKey = new int[dst.fKeySize]; 695 sk_careful_memcpy(dst.fKey, fSaveLayerOpStack.begin(), 696 fSaveLayerOpStack.count() * sizeof(int)); 697 dst.fKey[fSaveLayerOpStack.count()] = fFillBounds.currentOp(); 698 memcpy(&dst.fKey[fSaveLayerOpStack.count()+1], src.fKey, src.fKeySize * sizeof(int)); 699 } 700 } 701 702 void trackSaveLayers(const DrawPicture& dp) { 703 this->trackSaveLayersForPicture(dp.picture, dp.paint); 704 } 705 706 void trackSaveLayers(const DrawDrawable& dp) { 707 SkASSERT(fPictList); 708 SkASSERT(dp.index >= 0 && dp.index < fPictList->count()); 709 const SkPaint* paint = nullptr; // drawables don't get a side-car paint 710 this->trackSaveLayersForPicture(fPictList->begin()[dp.index], paint); 711 } 712 713 // Inform all the saveLayers already on the stack that they now have a 714 // nested saveLayer inside them 715 void updateStackForSaveLayer() { 716 for (int index = fSaveLayerStack.count() - 1; index >= 0; --index) { 717 if (fSaveLayerStack[index].fHasNestedSaveLayer) { 718 break; 719 } 720 fSaveLayerStack[index].fHasNestedSaveLayer = true; 721 if (fSaveLayerStack[index].fIsSaveLayer) { 722 break; 723 } 724 } 725 } 726 727 void pushSaveLayerInfo(bool isSaveLayer, const SkRect* bounds, const SkPaint* paint) { 728 if (isSaveLayer) { 729 this->updateStackForSaveLayer(); 730 ++fSaveLayersInStack; 731 fSaveLayerOpStack.push(fFillBounds.currentOp()); 732 } 733 734 fSaveLayerStack.push(SaveLayerInfo(fFillBounds.currentOp(), isSaveLayer, bounds, paint)); 735 } 736 737 void popSaveLayerInfo() { 738 if (fSaveLayerStack.count() <= 0) { 739 SkASSERT(false); 740 return; 741 } 742 743 SkASSERT(fSaveLayersInStack == fSaveLayerOpStack.count()); 744 745 SaveLayerInfo sli; 746 fSaveLayerStack.pop(&sli); 747 748 if (!sli.fIsSaveLayer) { 749 return; 750 } 751 752 --fSaveLayersInStack; 753 754 SkLayerInfo::BlockInfo& block = fAccelData->addBlock(); 755 756 SkASSERT(nullptr == block.fPicture); // This layer is in the top-most picture 757 758 block.fBounds = fFillBounds.getBounds(sli.fStartIndex); 759 block.fLocalMat = fFillBounds.ctm(); 760 block.fPreMat = SkMatrix::I(); 761 if (sli.fPaint) { 762 block.fPaint = new SkPaint(*sli.fPaint); 763 } 764 765 block.fSrcBounds = sli.fBounds; 766 block.fSaveLayerOpID = sli.fStartIndex; 767 block.fRestoreOpID = fFillBounds.currentOp(); 768 block.fHasNestedLayers = sli.fHasNestedSaveLayer; 769 block.fIsNested = fSaveLayersInStack > 0; 770 771 block.fKeySize = fSaveLayerOpStack.count(); 772 block.fKey = new int[block.fKeySize]; 773 memcpy(block.fKey, fSaveLayerOpStack.begin(), block.fKeySize * sizeof(int)); 774 775 fSaveLayerOpStack.pop(); 776 } 777 778 // Used to collect saveLayer information for layer hoisting 779 int fSaveLayersInStack; 780 SkTDArray<SaveLayerInfo> fSaveLayerStack; 781 // The op code indices of all the currently active saveLayers 782 SkTDArray<int> fSaveLayerOpStack; 783 SkLayerInfo* fAccelData; 784 const SkBigPicture::SnapshotArray* fPictList; 785 786 SkRecords::FillBounds fFillBounds; 787 }; 788 789 } // namespace SkRecords 790 791 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[]) { 792 SkRecords::FillBounds visitor(cullRect, record, bounds); 793 for (int curOp = 0; curOp < record.count(); curOp++) { 794 visitor.setCurrentOp(curOp); 795 record.visit<void>(curOp, visitor); 796 } 797 visitor.cleanUp(); 798 } 799 800 void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record, SkRect bounds[], 801 const SkBigPicture::SnapshotArray* pictList, SkLayerInfo* data) { 802 SkRecords::CollectLayers visitor(cullRect, record, bounds, pictList, data); 803 for (int curOp = 0; curOp < record.count(); curOp++) { 804 visitor.setCurrentOp(curOp); 805 record.visit<void>(curOp, visitor); 806 } 807 visitor.cleanUp(); 808 } 809 810