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