Index: src/core/SkRecordDraw.cpp |
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp |
index c9e029b8db67f11a59c9844a3128f5042931dedf..46241d72aca40cffe9239472b9c0b5cc72829057 100644 |
--- a/src/core/SkRecordDraw.cpp |
+++ b/src/core/SkRecordDraw.cpp |
@@ -16,10 +16,16 @@ void SkRecordDraw(const SkRecord& record, |
if (NULL != bbh) { |
// Draw only ops that affect pixels in the canvas's current clip. |
- SkIRect devBounds; |
- canvas->getClipDeviceBounds(&devBounds); |
+ SkIRect query; |
+#if 1 // TODO: Why is this the right way to make the query? I'd think it'd be the else branch. |
+ SkRect clipBounds; |
+ canvas->getClipBounds(&clipBounds); |
+ clipBounds.roundOut(&query); |
+#else |
+ canvas->getClipDeviceBounds(&query); |
+#endif |
SkTDArray<void*> ops; |
- bbh->search(devBounds, &ops); |
+ bbh->search(query, &ops); |
// FIXME: QuadTree doesn't send these back in the order we inserted them. :( |
// Also remove the sort in SkPictureData::getActiveOps()? |
@@ -117,7 +123,9 @@ public: |
FillBounds(const SkRecord& record, SkBBoxHierarchy* bbh) : fBounds(record.count()) { |
// Calculate bounds for all ops. This won't go quite in order, so we'll need |
// to store the bounds separately then feed them in to the BBH later in order. |
+ const SkIRect largest = SkIRect::MakeLargest(); |
fCTM.setIdentity(); |
+ fCurrentClipBounds = largest; |
for (fCurrentOp = 0; fCurrentOp < record.count(); fCurrentOp++) { |
record.visit<void>(fCurrentOp, *this); |
} |
@@ -130,7 +138,7 @@ public: |
// Any control ops not part of any Save/Restore block draw everywhere. |
while (!fControlIndices.isEmpty()) { |
- this->popControl(SkIRect::MakeLargest()); |
+ this->popControl(largest); |
} |
// Finally feed all stored bounds into the BBH. They'll be returned in this order. |
@@ -143,28 +151,44 @@ public: |
bbh->flushDeferredInserts(); |
} |
- template <typename T> void operator()(const T& r) { |
- this->updateCTM(r); |
- this->trackBounds(r); |
+ template <typename T> void operator()(const T& op) { |
+ this->updateCTM(op); |
+ this->updateClipBounds(op); |
+ this->trackBounds(op); |
} |
private: |
struct SaveBounds { |
- int controlOps; // Number of control ops in this Save block, including the Save. |
- SkIRect bounds; // Bounds of everything in the block. |
+ int controlOps; // Number of control ops in this Save block, including the Save. |
+ SkIRect bounds; // Bounds of everything in the block. |
+ const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block. |
}; |
template <typename T> void updateCTM(const T&) { /* most ops don't change the CTM */ } |
- void updateCTM(const Restore& r) { fCTM = r.matrix; } |
- void updateCTM(const SetMatrix& r) { fCTM = r.matrix; } |
- void updateCTM(const Concat& r) { fCTM.preConcat(r.matrix); } |
+ void updateCTM(const Restore& op) { fCTM = op.matrix; } |
+ void updateCTM(const SetMatrix& op) { fCTM = op.matrix; } |
+ void updateCTM(const Concat& op) { fCTM.preConcat(op.matrix); } |
+ |
+ template <typename T> void updateClipBounds(const T&) { /* most ops don't change the clip */ } |
+ // Each of these devBounds fields is the state of the device bounds after the op. |
+ // So Restore's devBounds are those bounds saved by its paired Save or SaveLayer. |
+ void updateClipBounds(const Restore& op) { fCurrentClipBounds = op.devBounds; } |
+ void updateClipBounds(const ClipPath& op) { fCurrentClipBounds = op.devBounds; } |
+ void updateClipBounds(const ClipRRect& op) { fCurrentClipBounds = op.devBounds; } |
+ void updateClipBounds(const ClipRect& op) { fCurrentClipBounds = op.devBounds; } |
+ void updateClipBounds(const ClipRegion& op) { fCurrentClipBounds = op.devBounds; } |
+ void updateClipBounds(const SaveLayer& op) { |
+ if (op.bounds) { |
+ fCurrentClipBounds.intersect(this->adjustAndMap(*op.bounds, op.paint)); |
+ } |
+ } |
// The bounds of these ops must be calculated when we hit the Restore |
// from the bounds of the ops in the same Save block. |
- void trackBounds(const Save&) { this->pushSaveBlock(); } |
+ void trackBounds(const Save&) { this->pushSaveBlock(NULL); } |
// TODO: bounds of SaveLayer may be more complicated? |
- void trackBounds(const SaveLayer&) { this->pushSaveBlock(); } |
- void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); } |
+ void trackBounds(const SaveLayer& op) { this->pushSaveBlock(op.paint); } |
+ void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); } |
void trackBounds(const Concat&) { this->pushControl(); } |
void trackBounds(const SetMatrix&) { this->pushControl(); } |
@@ -179,12 +203,9 @@ private: |
this->updateSaveBounds(fBounds[fCurrentOp]); |
} |
- // TODO: remove this trivially-safe default when done bounding all ops |
- template <typename T> SkIRect bounds(const T&) { return SkIRect::MakeLargest(); } |
- |
- void pushSaveBlock() { |
+ void pushSaveBlock(const SkPaint* paint) { |
// Starting a new Save block. Push a new entry to represent that. |
- SaveBounds sb = { 0, SkIRect::MakeEmpty() }; |
+ SaveBounds sb = { 0, SkIRect::MakeEmpty(), paint }; |
fSaveStack.push(sb); |
this->pushControl(); |
} |
@@ -223,11 +244,54 @@ private: |
} |
} |
- SkIRect bounds(const NoOp&) { return SkIRect::MakeEmpty(); } // NoOps don't draw anywhere. |
+ // TODO: Remove this default when done bounding all ops. |
+ template <typename T> SkIRect bounds(const T&) { return fCurrentClipBounds; } |
+ SkIRect bounds(const Clear&) { return SkIRect::MakeLargest(); } // Ignores the clip |
+ SkIRect bounds(const NoOp&) { return SkIRect::MakeEmpty(); } // NoOps don't draw anywhere. |
+ |
+ // Adjust rect for all paints that may affect its geometry, then map it to device space. |
+ SkIRect adjustAndMap(SkRect rect, const SkPaint* paint) { |
+ // Adjust rect for its own paint. |
+ if (paint) { |
+ if (paint->canComputeFastBounds()) { |
+ rect = paint->computeFastBounds(rect, &rect); |
+ } else { |
+ // The paint could do anything. The only safe answer is the current clip. |
+ return fCurrentClipBounds; |
+ } |
+ } |
- SkAutoTMalloc<SkIRect> fBounds; // One for each op in the record. |
- SkMatrix fCTM; |
+ // Adjust rect for all the paints from the SaveLayers we're inside. |
+ // For SaveLayers, only image filters will affect the bounds. |
+ for (int i = fSaveStack.count() - 1; i >= 0; i--) { |
+ if (fSaveStack[i].paint && fSaveStack[i].paint->getImageFilter()) { |
+ if (paint->canComputeFastBounds()) { |
+ rect = fSaveStack[i].paint->computeFastBounds(rect, &rect); |
+ } else { |
+ // Same deal as above. |
+ return fCurrentClipBounds; |
+ } |
+ } |
+ } |
+ |
+ // Map the rect back to device space. |
+ fCTM.mapRect(&rect); |
+ SkIRect devRect; |
+ rect.roundOut(&devRect); |
+ return devRect; |
+ } |
+ |
+ // Conservative device bounds for each op in the SkRecord. |
+ SkAutoTMalloc<SkIRect> fBounds; |
+ |
+ // We walk fCurrentOp through the SkRecord, as we go using updateCTM() |
+ // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative |
+ // device bounds of the current clip (fCurrentClipBounds). |
unsigned fCurrentOp; |
+ SkMatrix fCTM; |
+ SkIRect fCurrentClipBounds; |
+ |
+ // Used to track the bounds of Save/Restore blocks and the control ops inside them. |
SkTDArray<SaveBounds> fSaveStack; |
SkTDArray<unsigned> fControlIndices; |
}; |