Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Unified Diff: src/core/SkRecordDraw.cpp

Issue 475473002: Start filling BBH in SkRecordDraw. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: missed these Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698