Index: src/core/SkRecordDraw.cpp |
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp |
index 8be9e005f6c3846b363c23851eb4a9d93cce4e37..688d0b695ead0da67ca3928f4e07ac2f718fa312 100644 |
--- a/src/core/SkRecordDraw.cpp |
+++ b/src/core/SkRecordDraw.cpp |
@@ -15,17 +15,12 @@ void SkRecordDraw(const SkRecord& record, |
SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); |
if (NULL != bbh) { |
- SkASSERT(bbh->getCount() == SkToInt(record.count())); |
- |
// Draw only ops that affect pixels in the canvas's current clip. |
SkIRect devBounds; |
canvas->getClipDeviceBounds(&devBounds); |
SkTDArray<void*> ops; |
bbh->search(devBounds, &ops); |
- // Until we start filling in real bounds, we should get every op back. |
- SkASSERT(ops.count() == SkToInt(record.count())); |
- |
// FIXME: QuadTree doesn't send these back in the order we inserted them. :( |
// Also remove the sort in SkPictureData::getActiveOps()? |
if (ops.count() > 0) { |
@@ -100,33 +95,137 @@ DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co |
// This is an SkRecord visitor that fills an SkBBoxHierarchy. |
+// |
+// The interesting part here is how to calculate bounds for ops which don't |
+// have intrinsic bounds. What is the bounds of a Save or a Translate? |
+// |
+// We answer this by thinking about a particular definition of bounds: if I |
+// don't execute this op, pixels in this rectangle might draw incorrectly. So |
+// the bounds of a Save, a Translate, a Restore, etc. are the union of the |
+// bounds of Draw* ops that they might have an effect on. For any given |
+// Save/Restore block, the bounds of the Save, the Restore, and any other |
+// non-drawing ("control") ops inside are exactly the union of the bounds of |
+// the drawing ops inside that block. |
+// |
+// To implement this, we keep a stack of active Save blocks. As we consume ops |
+// inside the Save/Restore block, drawing ops are unioned with the bounds of |
+// the block, and control ops are stashed away for later. When we finish the |
+// block with a Restore, our bounds are complete, and we go back and fill them |
+// in for all the control ops we stashed away. |
class FillBounds : SkNoncopyable { |
public: |
- explicit FillBounds(SkBBoxHierarchy* bbh) : fBBH(bbh), fIndex(0) {} |
- ~FillBounds() { fBBH->flushDeferredInserts(); } |
+ 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. |
+ for (fCurrentOp = 0; fCurrentOp < record.count(); fCurrentOp++) { |
+ record.visit<void>(fCurrentOp, *this); |
+ } |
+ |
+ // If we have any lingering unpaired Saves, simulate restores to make |
+ // sure all ops in those Save blocks have their bounds calculated. |
+ while (!fSaveStack.isEmpty()) { |
+ this->popSaveBlock(); |
+ } |
+ |
+ // Any control ops not part of any Save/Restore block draw everywhere. |
+ while (!fControlIndices.isEmpty()) { |
+ this->popControl(SkIRect::MakeLargest()); |
+ } |
- uintptr_t index() const { return fIndex; } |
- void next() { ++fIndex; } |
+ // Finally feed all stored bounds into the BBH. They'll be returned in this order. |
+ SkASSERT(NULL != bbh); |
+ for (uintptr_t i = 0; i < record.count(); i++) { |
+ if (!fBounds[i].isEmpty()) { |
+ bbh->insert((void*)i, fBounds[i], true/*ok to defer*/); |
+ } |
+ } |
+ bbh->flushDeferredInserts(); |
+ } |
template <typename T> void operator()(const T& r) { |
- // MakeLargest() is a trivially safe default for ops that haven't been bounded yet. |
- this->insert(this->index(), SkIRect::MakeLargest()); |
+ this->trackBounds(r); |
} |
private: |
- void insert(uintptr_t opIndex, const SkIRect& bounds) { |
- fBBH->insert((void*)opIndex, bounds, true/*ok to defer*/); |
+ struct SaveBounds { |
+ int controlOps; // Number of control ops in this Save block, including the Save. |
+ SkIRect bounds; // Bounds of everything in the block. |
+ }; |
+ |
+ // 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(); } |
+ // 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 Concat&) { this->pushControl(); } |
+ void trackBounds(const SetMatrix&) { this->pushControl(); } |
+ void trackBounds(const ClipRect&) { this->pushControl(); } |
+ void trackBounds(const ClipRRect&) { this->pushControl(); } |
+ void trackBounds(const ClipPath&) { this->pushControl(); } |
+ void trackBounds(const ClipRegion&) { this->pushControl(); } |
+ |
+ // For all other ops, we can calculate and store the bounds directly now. |
+ template <typename T> void trackBounds(const T& op) { |
+ fBounds[fCurrentOp] = this->bounds(op); |
+ 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() { |
+ // Starting a new Save block. Push a new entry to represent that. |
+ SaveBounds sb = { 0, SkIRect::MakeEmpty() }; |
+ fSaveStack.push(sb); |
+ this->pushControl(); |
+ } |
+ |
+ SkIRect popSaveBlock() { |
+ // We're done the Save block. Apply the block's bounds to all control ops inside it. |
+ SaveBounds sb; |
+ fSaveStack.pop(&sb); |
+ while (sb.controlOps --> 0) { |
+ this->popControl(sb.bounds); |
+ } |
+ |
+ // This whole Save block may be part another Save block. |
+ this->updateSaveBounds(sb.bounds); |
+ |
+ // If called from a real Restore (not a phony one for balance), it'll need the bounds. |
+ return sb.bounds; |
+ } |
+ |
+ void pushControl() { |
+ fControlIndices.push(fCurrentOp); |
+ if (!fSaveStack.isEmpty()) { |
+ fSaveStack.top().controlOps++; |
+ } |
} |
- SkBBoxHierarchy* fBBH; // Unowned. The BBH is guaranteed to be ref'd for our lifetime. |
- uintptr_t fIndex; |
+ void popControl(const SkIRect& bounds) { |
+ fBounds[fControlIndices.top()] = bounds; |
+ fControlIndices.pop(); |
+ } |
+ |
+ void updateSaveBounds(const SkIRect& bounds) { |
+ // If we're in a Save block, expand its bounds to cover these bounds too. |
+ if (!fSaveStack.isEmpty()) { |
+ fSaveStack.top().bounds.join(bounds); |
+ } |
+ } |
+ |
+ SkIRect bounds(const NoOp&) { return SkIRect::MakeEmpty(); } // NoOps don't draw anywhere. |
+ |
+ SkAutoTMalloc<SkIRect> fBounds; // One for each op in the record. |
+ unsigned fCurrentOp; |
+ SkTDArray<SaveBounds> fSaveStack; |
+ SkTDArray<unsigned> fControlIndices; |
}; |
} // namespace SkRecords |
void SkRecordFillBounds(const SkRecord& record, SkBBoxHierarchy* bbh) { |
- SkASSERT(NULL != bbh); |
- for(SkRecords::FillBounds fb(bbh); fb.index() < record.count(); fb.next()) { |
- record.visit<void>(fb.index(), fb); |
- } |
+ SkRecords::FillBounds(record, bbh); |
} |