1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkBigPicture.h"
9 #include "SkData.h"
10 #include "SkDrawable.h"
11 #include "SkLayerInfo.h"
12 #include "SkPictureRecorder.h"
13 #include "SkPictureUtils.h"
14 #include "SkRecord.h"
15 #include "SkRecordDraw.h"
16 #include "SkRecordOpts.h"
17 #include "SkRecorder.h"
18 #include "SkTypes.h"
19 
SkPictureRecorder()20 SkPictureRecorder::SkPictureRecorder() {
21     fActivelyRecording = false;
22     fRecorder.reset(new SkRecorder(nullptr, SkRect::MakeWH(0, 0), &fMiniRecorder));
23 }
24 
~SkPictureRecorder()25 SkPictureRecorder::~SkPictureRecorder() {}
26 
beginRecording(const SkRect & cullRect,SkBBHFactory * bbhFactory,uint32_t recordFlags)27 SkCanvas* SkPictureRecorder::beginRecording(const SkRect& cullRect,
28                                             SkBBHFactory* bbhFactory /* = nullptr */,
29                                             uint32_t recordFlags /* = 0 */) {
30     fCullRect = cullRect;
31     fFlags = recordFlags;
32 
33     if (bbhFactory) {
34         fBBH.reset((*bbhFactory)(cullRect));
35         SkASSERT(fBBH.get());
36     }
37 
38     if (!fRecord) {
39         fRecord.reset(new SkRecord);
40     }
41     SkRecorder::DrawPictureMode dpm = (recordFlags & kPlaybackDrawPicture_RecordFlag)
42         ? SkRecorder::Playback_DrawPictureMode
43         : SkRecorder::Record_DrawPictureMode;
44     fRecorder->reset(fRecord.get(), cullRect, dpm, &fMiniRecorder);
45     fActivelyRecording = true;
46     return this->getRecordingCanvas();
47 }
48 
getRecordingCanvas()49 SkCanvas* SkPictureRecorder::getRecordingCanvas() {
50     return fActivelyRecording ? fRecorder.get() : nullptr;
51 }
52 
endRecordingAsPicture()53 SkPicture* SkPictureRecorder::endRecordingAsPicture() {
54     fActivelyRecording = false;
55     fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.
56 
57     if (fRecord->count() == 0) {
58         return fMiniRecorder.detachAsPicture(fCullRect);
59     }
60 
61     // TODO: delay as much of this work until just before first playback?
62     SkRecordOptimize(fRecord);
63 
64     SkAutoTUnref<SkLayerInfo> saveLayerData;
65 
66     if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) {
67         saveLayerData.reset(new SkLayerInfo);
68     }
69 
70     SkDrawableList* drawableList = fRecorder->getDrawableList();
71     SkBigPicture::SnapshotArray* pictList =
72         drawableList ? drawableList->newDrawableSnapshot() : nullptr;
73 
74     if (fBBH.get()) {
75         SkAutoTMalloc<SkRect> bounds(fRecord->count());
76         if (saveLayerData) {
77             SkRecordComputeLayers(fCullRect, *fRecord, bounds, pictList, saveLayerData);
78         } else {
79             SkRecordFillBounds(fCullRect, *fRecord, bounds);
80         }
81         fBBH->insert(bounds, fRecord->count());
82 
83         // Now that we've calculated content bounds, we can update fCullRect, often trimming it.
84         // TODO: get updated fCullRect from bounds instead of forcing the BBH to return it?
85         SkRect bbhBound = fBBH->getRootBound();
86         SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound))
87             || (bbhBound.isEmpty() && fCullRect.isEmpty()));
88         fCullRect = bbhBound;
89     }
90 
91     size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures();
92     for (int i = 0; pictList && i < pictList->count(); i++) {
93         subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
94     }
95     return new SkBigPicture(fCullRect, fRecord.detach(), pictList, fBBH.detach(),
96                             saveLayerData.detach(), subPictureBytes);
97 }
98 
endRecordingAsPicture(const SkRect & cullRect)99 SkPicture* SkPictureRecorder::endRecordingAsPicture(const SkRect& cullRect) {
100     fCullRect = cullRect;
101     return this->endRecordingAsPicture();
102 }
103 
104 
partialReplay(SkCanvas * canvas) const105 void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
106     if (nullptr == canvas) {
107         return;
108     }
109 
110     int drawableCount = 0;
111     SkDrawable* const* drawables = nullptr;
112     SkDrawableList* drawableList = fRecorder->getDrawableList();
113     if (drawableList) {
114         drawableCount = drawableList->count();
115         drawables = drawableList->begin();
116     }
117     SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, nullptr/*bbh*/, nullptr/*callback*/);
118 }
119 
120 ///////////////////////////////////////////////////////////////////////////////////////////////////
121 
122 class SkRecordedDrawable : public SkDrawable {
123     SkAutoTUnref<SkRecord>          fRecord;
124     SkAutoTUnref<SkBBoxHierarchy>   fBBH;
125     SkAutoTDelete<SkDrawableList>   fDrawableList;
126     const SkRect                    fBounds;
127     const bool                      fDoSaveLayerInfo;
128 
129 public:
SkRecordedDrawable(SkRecord * record,SkBBoxHierarchy * bbh,SkDrawableList * drawableList,const SkRect & bounds,bool doSaveLayerInfo)130     SkRecordedDrawable(SkRecord* record, SkBBoxHierarchy* bbh, SkDrawableList* drawableList,
131                        const SkRect& bounds, bool doSaveLayerInfo)
132         : fRecord(SkRef(record))
133         , fBBH(SkSafeRef(bbh))
134         , fDrawableList(drawableList)   // we take ownership
135         , fBounds(bounds)
136         , fDoSaveLayerInfo(doSaveLayerInfo)
137     {}
138 
139 protected:
onGetBounds()140     SkRect onGetBounds() override { return fBounds; }
141 
onDraw(SkCanvas * canvas)142     void onDraw(SkCanvas* canvas) override {
143         SkDrawable* const* drawables = nullptr;
144         int drawableCount = 0;
145         if (fDrawableList) {
146             drawables = fDrawableList->begin();
147             drawableCount = fDrawableList->count();
148         }
149         SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, fBBH, nullptr/*callback*/);
150     }
151 
onNewPictureSnapshot()152     SkPicture* onNewPictureSnapshot() override {
153         SkBigPicture::SnapshotArray* pictList = nullptr;
154         if (fDrawableList) {
155             // TODO: should we plumb-down the BBHFactory and recordFlags from our host
156             //       PictureRecorder?
157             pictList = fDrawableList->newDrawableSnapshot();
158         }
159 
160         SkAutoTUnref<SkLayerInfo> saveLayerData;
161         if (fBBH && fDoSaveLayerInfo) {
162             // TODO: can we avoid work by not allocating / filling these bounds?
163             SkAutoTMalloc<SkRect> scratchBounds(fRecord->count());
164             saveLayerData.reset(new SkLayerInfo);
165 
166             SkRecordComputeLayers(fBounds, *fRecord, scratchBounds, pictList, saveLayerData);
167         }
168 
169         size_t subPictureBytes = 0;
170         for (int i = 0; pictList && i < pictList->count(); i++) {
171             subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
172         }
173         // SkBigPicture will take ownership of a ref on both fRecord and fBBH.
174         // We're not willing to give up our ownership, so we must ref them for SkPicture.
175         return new SkBigPicture(fBounds, SkRef(fRecord.get()), pictList, SkSafeRef(fBBH.get()),
176                                 saveLayerData.detach(), subPictureBytes);
177     }
178 };
179 
endRecordingAsDrawable()180 SkDrawable* SkPictureRecorder::endRecordingAsDrawable() {
181     fActivelyRecording = false;
182     fRecorder->flushMiniRecorder();
183     fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.
184 
185     // TODO: delay as much of this work until just before first playback?
186     SkRecordOptimize(fRecord);
187 
188     if (fBBH.get()) {
189         SkAutoTMalloc<SkRect> bounds(fRecord->count());
190         SkRecordFillBounds(fCullRect, *fRecord, bounds);
191         fBBH->insert(bounds, fRecord->count());
192     }
193 
194     SkDrawable* drawable =
195             new SkRecordedDrawable(fRecord, fBBH, fRecorder->detachDrawableList(), fCullRect,
196                                    SkToBool(fFlags & kComputeSaveLayerInfo_RecordFlag));
197 
198     // release our refs now, so only the drawable will be the owner.
199     fRecord.reset(nullptr);
200     fBBH.reset(nullptr);
201 
202     return drawable;
203 }
204