*/
#include "GrRecordReplaceDraw.h"
+#include "SkCanvasPriv.h"
#include "SkImage.h"
#include "SkRecordDraw.h"
-
-GrReplacements::ReplacementInfo* GrReplacements::push() {
- SkDEBUGCODE(this->validate());
- return fReplacements.push();
+#include "SkRecords.h"
+
+GrReplacements::ReplacementInfo* GrReplacements::newReplacement(uint32_t pictureID,
+ unsigned int start,
+ const SkMatrix& ctm) {
+ ReplacementInfo* replacement = SkNEW_ARGS(ReplacementInfo, (pictureID, start, ctm));
+ fReplacementHash.add(replacement);
+ return replacement;
}
void GrReplacements::freeAll() {
- for (int i = 0; i < fReplacements.count(); ++i) {
- fReplacements[i].fImage->unref();
- SkDELETE(fReplacements[i].fPaint);
- }
- fReplacements.reset();
-}
+ SkTDynamicHash<ReplacementInfo, ReplacementInfo::Key>::Iter iter(&fReplacementHash);
-#ifdef SK_DEBUG
-void GrReplacements::validate() const {
- // Check that the ranges are monotonically increasing and non-overlapping
- if (fReplacements.count() > 0) {
- SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop);
-
- for (int i = 1; i < fReplacements.count(); ++i) {
- SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop);
- SkASSERT(fReplacements[i - 1].fStop < fReplacements[i].fStart);
- }
+ for (; !iter.done(); ++iter) {
+ ReplacementInfo* replacement = &(*iter);
+ SkDELETE(replacement);
}
+
+ fReplacementHash.reset();
}
-#endif
-
-const GrReplacements::ReplacementInfo*
-GrReplacements::lookupByStart(size_t start, int* searchStart) const {
- SkDEBUGCODE(this->validate());
- for (int i = *searchStart; i < fReplacements.count(); ++i) {
- if (start == fReplacements[i].fStart) {
- *searchStart = i + 1;
- return &fReplacements[i];
- } else if (start < fReplacements[i].fStart) {
- return NULL; // the ranges are monotonically increasing and non-overlapping
- }
- }
- return NULL;
+const GrReplacements::ReplacementInfo* GrReplacements::lookupByStart(uint32_t pictureID,
+ size_t start,
+ const SkMatrix& ctm) const {
+ return fReplacementHash.find(ReplacementInfo::Key(pictureID, start, ctm));
}
static inline void draw_replacement_bitmap(const GrReplacements::ReplacementInfo* ri,
canvas->restore();
}
-void GrRecordReplaceDraw(const SkPicture* picture,
- SkCanvas* canvas,
- const GrReplacements* replacements,
- const SkMatrix& initialMatrix,
- SkDrawPictureCallback* callback) {
- SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
-
- const SkBBoxHierarchy* bbh = picture->fBBH.get();
- const SkRecord* record = picture->fRecord.get();
- if (NULL == record) {
- return;
+// Used by GrRecordReplaceDraw. It intercepts nested drawPicture calls and
+// also draws them with replaced layers.
+class ReplaceDraw : public SkRecords::Draw {
+public:
+ ReplaceDraw(SkCanvas* canvas,
+ const SkPicture* picture,
+ const GrReplacements* replacements,
+ const SkMatrix& initialMatrix,
+ SkDrawPictureCallback* callback)
+ : INHERITED(canvas)
+ , fCanvas(canvas)
+ , fPicture(picture)
+ , fReplacements(replacements)
+ , fInitialMatrix(initialMatrix)
+ , fCallback(callback)
+ , fIndex(0) {
}
- SkRecords::Draw draw(canvas);
- const GrReplacements::ReplacementInfo* ri = NULL;
- int searchStart = 0;
-
- if (bbh) {
- // Draw only ops that affect pixels in the canvas's current clip.
- // The SkRecord and BBH were recorded in identity space. This canvas
- // is not necessarily in that same space. getClipBounds() returns us
- // this canvas' clip bounds transformed back into identity space, which
- // lets us query the BBH.
- SkRect query = { 0, 0, 0, 0 };
- (void)canvas->getClipBounds(&query);
-
- SkTDArray<void*> ops;
- bbh->search(query, &ops);
-
- for (int i = 0; i < ops.count(); i++) {
- if (callback && callback->abortDrawing()) {
- return;
+ void draw() {
+ const SkBBoxHierarchy* bbh = fPicture->fBBH.get();
+ const SkRecord* record = fPicture->fRecord.get();
+ if (NULL == record) {
+ return;
+ }
+
+ fOps.rewind();
+
+ if (bbh) {
+ // Draw only ops that affect pixels in the canvas's current clip.
+ // The SkRecord and BBH were recorded in identity space. This canvas
+ // is not necessarily in that same space. getClipBounds() returns us
+ // this canvas' clip bounds transformed back into identity space, which
+ // lets us query the BBH.
+ SkRect query = { 0, 0, 0, 0 };
+ (void)fCanvas->getClipBounds(&query);
+
+ bbh->search(query, &fOps);
+
+ for (fIndex = 0; fIndex < fOps.count(); ++fIndex) {
+ if (fCallback && fCallback->abortDrawing()) {
+ return;
+ }
+
+ record->visit<void>((uintptr_t)fOps[fIndex], *this);
}
- ri = replacements->lookupByStart((uintptr_t)ops[i], &searchStart);
- if (ri) {
- draw_replacement_bitmap(ri, canvas, initialMatrix);
- while ((uintptr_t)ops[i] < ri->fStop) {
- ++i;
+ } else {
+ for (fIndex = 0; fIndex < (int) record->count(); ++fIndex) {
+ if (fCallback && fCallback->abortDrawing()) {
+ return;
}
- SkASSERT((uintptr_t)ops[i] == ri->fStop);
- continue;
+
+ record->visit<void>(fIndex, *this);
}
+ }
+ }
+
+ // Same as Draw for all ops except DrawPicture and SaveLayer.
+ template <typename T> void operator()(const T& r) {
+ this->INHERITED::operator()(r);
+ }
+ void operator()(const SkRecords::DrawPicture& dp) {
+ SkAutoCanvasMatrixPaint acmp(fCanvas, dp.matrix, dp.paint, dp.picture->cullRect());
- record->visit<void>((uintptr_t)ops[i], draw);
+ // Draw sub-pictures with the same replacement list but a different picture
+ ReplaceDraw draw(fCanvas, dp.picture, fReplacements, fInitialMatrix, fCallback);
+
+ draw.draw();
+ }
+ void operator()(const SkRecords::SaveLayer& sl) {
+
+ // For a saveLayer command, check if it can be replaced by a drawBitmap
+ // call and, if so, draw it and then update the current op index accordingly.
+ size_t startOffset;
+ if (fOps.count()) {
+ startOffset = (uintptr_t)fOps[fIndex];
+ } else {
+ startOffset = fIndex;
}
- } else {
- for (unsigned int i = 0; i < record->count(); ++i) {
- if (callback && callback->abortDrawing()) {
- return;
- }
- ri = replacements->lookupByStart(i, &searchStart);
- if (ri) {
- draw_replacement_bitmap(ri, canvas, initialMatrix);
- i = ri->fStop;
- continue;
- }
- record->visit<void>(i, draw);
+ const GrReplacements::ReplacementInfo* ri = fReplacements->lookupByStart(
+ fPicture->uniqueID(),
+ startOffset,
+ fCanvas->getTotalMatrix());
+
+ if (ri) {
+ draw_replacement_bitmap(ri, fCanvas, fInitialMatrix);
+
+ if (fPicture->fBBH.get()) {
+ while ((uintptr_t)fOps[fIndex] < ri->fStop) {
+ ++fIndex;
+ }
+ SkASSERT((uintptr_t)fOps[fIndex] == ri->fStop);
+ } else {
+ fIndex = ri->fStop;
+ }
+ return;
}
+
+ // This is a fail for layer hoisting
+ this->INHERITED::operator()(sl);
}
+
+private:
+ SkCanvas* fCanvas;
+ const SkPicture* fPicture;
+ const GrReplacements* fReplacements;
+ const SkMatrix fInitialMatrix;
+ SkDrawPictureCallback* fCallback;
+
+ SkTDArray<void*> fOps;
+ int fIndex;
+
+ typedef Draw INHERITED;
+};
+
+void GrRecordReplaceDraw(const SkPicture* picture,
+ SkCanvas* canvas,
+ const GrReplacements* replacements,
+ const SkMatrix& initialMatrix,
+ SkDrawPictureCallback* callback) {
+ SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
+
+ ReplaceDraw draw(canvas, picture, replacements, initialMatrix, callback);
+
+ draw.draw();
}
#ifndef GrRecordReplaceDraw_DEFINED
#define GrRecordReplaceDraw_DEFINED
+#include "SkChecksum.h"
#include "SkDrawPictureCallback.h"
+#include "SkImage.h"
#include "SkRect.h"
-#include "SkTDArray.h"
+#include "SkTDynamicHash.h"
class SkBBoxHierarchy;
class SkBitmap;
class GrReplacements {
public:
// All the operations between fStart and fStop (inclusive) will be replaced with
- // a single drawBitmap call using fPos, fBM and fPaint.
- struct ReplacementInfo {
- unsigned fStart;
+ // a single drawBitmap call using fPos, fImage and fPaint.
+ class ReplacementInfo {
+ public:
+ struct Key {
+ Key(uint32_t pictureID, unsigned int start, const SkMatrix& ctm)
+ : fPictureID(pictureID)
+ , fStart(start)
+ , fCTM(ctm) {
+ fCTM.getType(); // force initialization of type so hashes match
+
+ // Key needs to be tightly packed.
+ GR_STATIC_ASSERT(sizeof(Key) == sizeof(uint32_t) + // picture ID
+ sizeof(int) + // start
+ 9 * sizeof(SkScalar) // 3x3 from CTM
+ +sizeof(uint32_t)); // matrix's type
+ }
+
+ bool operator==(const Key& other) const {
+ return fPictureID == other.fPictureID &&
+ fStart == other.fStart &&
+ fCTM.cheapEqualTo(other.fCTM); // TODO: should be fuzzy
+ }
+
+ uint32_t pictureID() const { return fPictureID; }
+ unsigned int start() const { return fStart; }
+
+ private:
+ const uint32_t fPictureID;
+ const unsigned int fStart;
+ const SkMatrix fCTM;
+ };
+
+ static const Key& GetKey(const ReplacementInfo& layer) { return layer.fKey; }
+ static uint32_t Hash(const Key& key) {
+ return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
+ }
+
+ ReplacementInfo(uint32_t pictureID, unsigned int start, const SkMatrix& ctm)
+ : fKey(pictureID, start, ctm)
+ , fImage(NULL)
+ , fPaint(NULL) {
+ }
+ ~ReplacementInfo() { fImage->unref(); SkDELETE(fPaint); }
+
+ unsigned int start() const { return fKey.start(); }
+
+ const Key fKey;
unsigned fStop;
SkIPoint fPos;
SkImage* fImage; // Owns a ref
~GrReplacements() { this->freeAll(); }
- // Add a new replacement range. The replacement ranges should be
- // sorted in increasing order and non-overlapping (esp. no nested
- // saveLayers).
- ReplacementInfo* push();
+ // Add a new replacement range.
+ ReplacementInfo* newReplacement(uint32_t pictureID, unsigned int start, const SkMatrix& ctm);
- // look up a replacement range by its start offset.
- // lastLookedUp is an in/out parameter that is used to speed up the search.
- // It should be initialized to 0 on the first call and then passed back in
- // unmodified on subsequent calls.
- const ReplacementInfo* lookupByStart(size_t start, int* lastLookedUp) const;
+ // look up a replacement range by its pictureID, start offset and the CTM
+ // TODO: also need to add clip to lookup
+ const ReplacementInfo* lookupByStart(uint32_t pictureID, size_t start,
+ const SkMatrix& ctm) const;
private:
- SkTDArray<ReplacementInfo> fReplacements;
+ SkTDynamicHash<ReplacementInfo, ReplacementInfo::Key> fReplacementHash;
void freeAll();
-
-#ifdef SK_DEBUG
- void validate() const;
-#endif
};
// Draw an SkPicture into an SkCanvas replacing saveLayer/restore blocks with
void GrRecordReplaceDraw(const SkPicture*,
SkCanvas*,
const GrReplacements*,
- const SkMatrix&,
+ const SkMatrix& initialMatrix,
SkDrawPictureCallback*);
#endif // GrRecordReplaceDraw_DEFINED