Add a means of extracting active operations from SkPicture
authorrobertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 17 Mar 2014 14:56:13 +0000 (14:56 +0000)
committerrobertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 17 Mar 2014 14:56:13 +0000 (14:56 +0000)
https://codereview.chromium.org/195793010/

git-svn-id: http://skia.googlecode.com/svn/trunk@13831 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkPicture.h
src/core/SkPicture.cpp
src/core/SkPicturePlayback.cpp
src/core/SkPicturePlayback.h

index 3718d3a..d600ab9 100644 (file)
@@ -308,11 +308,39 @@ protected:
     // SkBBoxHierarchy implementation
     virtual SkBBoxHierarchy* createBBoxHierarchy() const;
 private:
+    // An OperationList encapsulates a set of operation offsets into the picture byte
+    // stream along with the CTMs needed for those operation.
+    class OperationList : public SkNoncopyable {
+    public:
+        // If valid returns false then there is no optimization data
+        // present. All the draw operations need to be issued.
+        virtual bool valid() const { return false; }
+
+        // The following three entry points should only be accessed if
+        // 'valid' returns true.
+        virtual int numOps() const { SkASSERT(false); return 0; };
+        // The offset in the picture of the operation to execute.
+        virtual uint32_t offset(int index) const { SkASSERT(false); return 0; };
+        // The CTM that must be installed for the operation to behave correctly
+        virtual const SkMatrix& matrix(int index) const { SkASSERT(false); return SkMatrix::I(); }
+
+        static const OperationList& InvalidList();
+
+    private:
+        typedef SkNoncopyable INHERITED;
+    };
+
+    /** PRIVATE / EXPERIMENTAL -- do not call
+        Return the operations required to render the content inside 'queryRect'.
+    */
+    const OperationList& EXPERIMENTAL_getActiveOps(const SkIRect& queryRect);
+
     void createHeader(SkPictInfo* info) const;
     static bool IsValidPictInfo(const SkPictInfo& info);
 
     friend class SkFlatPicture;
     friend class SkPicturePlayback;
+    friend class SkGpuDevice;
 
     typedef SkRefCnt INHERITED;
 };
index 1285ff1..b3dd6a4 100644 (file)
@@ -264,6 +264,19 @@ void SkPicture::endRecording() {
     SkASSERT(NULL == fRecord);
 }
 
+const SkPicture::OperationList& SkPicture::OperationList::InvalidList() {
+    static OperationList gInvalid;
+    return gInvalid;
+}
+
+const SkPicture::OperationList& SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) {
+    this->endRecording();
+    if (NULL != fPlayback) {
+        return fPlayback->getActiveOps(queryRect);
+    }
+    return OperationList::InvalidList();
+}
+
 void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) {
     this->endRecording();
     if (NULL != fPlayback) {
index d61e616..c989061 100644 (file)
@@ -69,7 +69,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record, bool deepCop
 
     record.validate(record.writeStream().bytesWritten(), 0);
     const SkWriter32& writer = record.writeStream();
-    init();
+    this->init();
     SkASSERT(!fOpData);
     if (writer.bytesWritten() == 0) {
         fOpData = SkData::NewEmpty();
@@ -261,6 +261,7 @@ void SkPicturePlayback::init() {
     fFactoryPlayback = NULL;
     fBoundingHierarchy = NULL;
     fStateTree = NULL;
+    fCachedActiveOps = NULL;
 }
 
 SkPicturePlayback::~SkPicturePlayback() {
@@ -271,6 +272,8 @@ SkPicturePlayback::~SkPicturePlayback() {
     SkSafeUnref(fBoundingHierarchy);
     SkSafeUnref(fStateTree);
 
+    SkDELETE(fCachedActiveOps);
+
     for (int i = 0; i < fPictureCount; i++) {
         fPictureRefs[i]->unref();
     }
@@ -754,8 +757,13 @@ static DrawType read_op_and_size(SkReader32* reader, uint32_t* size) {
 // The activeOps parameter is actually "const SkTDArray<SkPictureStateTree::Draw*>&".
 // It represents the operations about to be drawn, as generated by some spatial
 // subdivision helper class. It should already be in 'fOffset' sorted order.
-void SkPicturePlayback::preLoadBitmaps(const SkTDArray<void*>& activeOps) {
-    if (0 == activeOps.count() || NULL == fBitmapUseOffsets) {
+void SkPicturePlayback::preLoadBitmaps(const SkTDArray<void*>* activeOps) {
+    if ((NULL != activeOps && 0 == activeOps->count()) || NULL == fBitmapUseOffsets) {
+        return;
+    }
+
+    if (NULL == activeOps) {
+        // going to need everything
         return;
     }
 
@@ -766,10 +774,10 @@ void SkPicturePlayback::preLoadBitmaps(const SkTDArray<void*>& activeOps) {
         needToCheck.get()[i] = true;
     }
 
-    uint32_t max = ((SkPictureStateTree::Draw*)activeOps[activeOps.count()-1])->fOffset;
+    uint32_t max = ((SkPictureStateTree::Draw*)(*activeOps)[(*activeOps).count()-1])->fOffset;
 
-    for (int i = 0; i < activeOps.count(); ++i) {
-        SkPictureStateTree::Draw* draw = (SkPictureStateTree::Draw*) activeOps[i];
+    for (int i = 0; i < activeOps->count(); ++i) {
+        SkPictureStateTree::Draw* draw = (SkPictureStateTree::Draw*) (*activeOps)[i];
 
         for (int j = 0; j < fBitmapUseOffsets->numIDs(); ++j) {
             if (!needToCheck.get()[j]) {
@@ -795,6 +803,41 @@ void SkPicturePlayback::preLoadBitmaps(const SkTDArray<void*>& activeOps) {
     }
 }
 
+uint32_t SkPicturePlayback::CachedOperationList::offset(int index) const {
+    SkASSERT(index < fOps.count());
+    return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
+}
+
+const SkMatrix& SkPicturePlayback::CachedOperationList::matrix(int index) const {
+    SkASSERT(index < fOps.count());
+    return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
+}
+
+const SkPicture::OperationList& SkPicturePlayback::getActiveOps(const SkIRect& query) {
+    if (NULL == fStateTree || NULL == fBoundingHierarchy) {
+        return SkPicture::OperationList::InvalidList();
+    }
+
+    if (NULL == fCachedActiveOps) {
+        fCachedActiveOps = SkNEW(CachedOperationList);
+    }
+
+    if (query == fCachedActiveOps->fCacheQueryRect) {
+        return *fCachedActiveOps;
+    }
+
+    fCachedActiveOps->fOps.rewind();
+
+    fBoundingHierarchy->search(query, &(fCachedActiveOps->fOps));
+
+    SkTQSort<SkPictureStateTree::Draw>(
+        reinterpret_cast<SkPictureStateTree::Draw**>(fCachedActiveOps->fOps.begin()),
+        reinterpret_cast<SkPictureStateTree::Draw**>(fCachedActiveOps->fOps.end()-1));
+
+    fCachedActiveOps->fCacheQueryRect = query;
+    return *fCachedActiveOps;
+}
+
 void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback) {
 #ifdef ENABLE_TIME_DRAW
     SkAutoTime  at("SkPicture::draw", 50);
@@ -815,26 +858,29 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
 
     SkReader32 reader(fOpData->bytes(), fOpData->size());
     TextContainer text;
-    SkTDArray<void*> activeOps;
+    const SkTDArray<void*>* activeOps = NULL;
 
     if (NULL != fStateTree && NULL != fBoundingHierarchy) {
         SkRect clipBounds;
         if (canvas.getClipBounds(&clipBounds)) {
             SkIRect query;
             clipBounds.roundOut(&query);
-            fBoundingHierarchy->search(query, &activeOps);
-            if (activeOps.count() == 0) {
-                return;
+
+            const SkPicture::OperationList& activeOpsList = this->getActiveOps(query);
+            if (activeOpsList.valid()) {
+                if (0 == activeOpsList.numOps()) {
+                    return;     // nothing to draw
+                }
+                
+                // Since the opList is valid we know it is our derived class
+                activeOps = &((const CachedOperationList&)activeOpsList).fOps;
             }
-            SkTQSort<SkPictureStateTree::Draw>(
-                reinterpret_cast<SkPictureStateTree::Draw**>(activeOps.begin()),
-                reinterpret_cast<SkPictureStateTree::Draw**>(activeOps.end()-1));
         }
     }
 
-    SkPictureStateTree::Iterator it = (NULL == fStateTree) ?
+    SkPictureStateTree::Iterator it = (NULL == activeOps) ?
         SkPictureStateTree::Iterator() :
-        fStateTree->getIterator(activeOps, &canvas);
+        fStateTree->getIterator(*activeOps, &canvas);
 
     if (it.isValid()) {
         uint32_t skipTo = it.draw();
index 512f24a..1162420 100644 (file)
@@ -85,6 +85,8 @@ public:
 
     virtual ~SkPicturePlayback();
 
+    const SkPicture::OperationList& getActiveOps(const SkIRect& queryRect);
+
     void draw(SkCanvas& canvas, SkDrawPictureCallback*);
 
     void serialize(SkWStream*, SkPicture::EncodeBitmap) const;
@@ -109,7 +111,7 @@ protected:
     virtual void postDraw(int opIndex);
 #endif
 
-    void preLoadBitmaps(const SkTDArray<void*>& results);
+    void preLoadBitmaps(const SkTDArray<void*>* results);
 
 private:
     class TextContainer {
@@ -237,6 +239,29 @@ private:
     SkBBoxHierarchy* fBoundingHierarchy;
     SkPictureStateTree* fStateTree;
 
+    class CachedOperationList : public SkPicture::OperationList {
+    public:
+        CachedOperationList() {
+            fCacheQueryRect.setEmpty();
+        }
+
+        virtual bool valid() const { return true; }
+        virtual int numOps() const SK_OVERRIDE { return fOps.count(); }
+        virtual uint32_t offset(int index) const SK_OVERRIDE;
+        virtual const SkMatrix& matrix(int index) const SK_OVERRIDE;
+
+        // The query rect for which the cached active ops are valid
+        SkIRect          fCacheQueryRect;
+
+        // The operations which are active within 'fCachedQueryRect'
+        SkTDArray<void*> fOps;
+
+    private:
+        typedef SkPicture::OperationList INHERITED;
+    };
+
+    CachedOperationList* fCachedActiveOps;
+
     SkTypefacePlayback fTFPlayback;
     SkFactoryPlayback* fFactoryPlayback;
 #ifdef SK_BUILD_FOR_ANDROID