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 <memory>
9
10#include "include/core/SkData.h"
11#include "include/core/SkDrawable.h"
12#include "include/core/SkPictureRecorder.h"
13#include "include/core/SkTypes.h"
14#include "src/core/SkBigPicture.h"
15#include "src/core/SkMiniRecorder.h"
16#include "src/core/SkRecord.h"
17#include "src/core/SkRecordDraw.h"
18#include "src/core/SkRecordOpts.h"
19#include "src/core/SkRecordedDrawable.h"
20#include "src/core/SkRecorder.h"
21
22SkPictureRecorder::SkPictureRecorder() {
23 fActivelyRecording = false;
24 fMiniRecorder = std::make_unique<SkMiniRecorder>();
25 fRecorder = std::make_unique<SkRecorder>(nullptr, SkRect::MakeEmpty(), fMiniRecorder.get());
26}
27
28SkPictureRecorder::~SkPictureRecorder() {}
29
30SkCanvas* SkPictureRecorder::beginRecording(const SkRect& userCullRect,
31 sk_sp<SkBBoxHierarchy> bbh,
32 uint32_t recordFlags /* = 0 */) {
33 const SkRect cullRect = userCullRect.isEmpty() ? SkRect::MakeEmpty() : userCullRect;
34
35 fCullRect = cullRect;
36 fFlags = recordFlags;
37 fBBH = std::move(bbh);
38
39 if (!fRecord) {
40 fRecord.reset(new SkRecord);
41 }
42 SkRecorder::DrawPictureMode dpm = (recordFlags & kPlaybackDrawPicture_RecordFlag)
43 ? SkRecorder::Playback_DrawPictureMode
44 : SkRecorder::Record_DrawPictureMode;
45 fRecorder->reset(fRecord.get(), cullRect, dpm, fMiniRecorder.get());
46 fActivelyRecording = true;
47 return this->getRecordingCanvas();
48}
49
50SkCanvas* SkPictureRecorder::beginRecording(const SkRect& bounds,
51 SkBBHFactory* factory,
52 uint32_t flags) {
53 return this->beginRecording(bounds, factory ? (*factory)() : nullptr, flags);
54}
55
56SkCanvas* SkPictureRecorder::getRecordingCanvas() {
57 return fActivelyRecording ? fRecorder.get() : nullptr;
58}
59
60sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPicture(uint32_t finishFlags) {
61 fActivelyRecording = false;
62 fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
63
64 if (fRecord->count() == 0) {
65 auto pic = fMiniRecorder->detachAsPicture(fBBH ? nullptr : &fCullRect);
66 if (fBBH) {
67 SkRect bounds = pic->cullRect(); // actually the computed bounds, not fCullRect.
68 SkBBoxHierarchy::Metadata meta;
69 meta.isDraw = true; // All mini-recorder pictures are single draws.
70 fBBH->insert(&bounds, &meta, 1);
71 }
72 fBBH.reset(nullptr);
73 return pic;
74 }
75
76 // TODO: delay as much of this work until just before first playback?
77 SkRecordOptimize(fRecord.get());
78
79 SkDrawableList* drawableList = fRecorder->getDrawableList();
80 std::unique_ptr<SkBigPicture::SnapshotArray> pictList{
81 drawableList ? drawableList->newDrawableSnapshot() : nullptr
82 };
83
84 if (fBBH.get()) {
85 SkAutoTMalloc<SkRect> bounds(fRecord->count());
86 SkAutoTMalloc<SkBBoxHierarchy::Metadata> meta(fRecord->count());
87 SkRecordFillBounds(fCullRect, *fRecord, bounds, meta);
88
89 fBBH->insert(bounds, meta, fRecord->count());
90
91 // Now that we've calculated content bounds, we can update fCullRect, often trimming it.
92 SkRect bbhBound = SkRect::MakeEmpty();
93 for (int i = 0; i < fRecord->count(); i++) {
94 bbhBound.join(bounds[i]);
95 }
96 SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound))
97 || (bbhBound.isEmpty() && fCullRect.isEmpty()));
98 fCullRect = bbhBound;
99 }
100
101 size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures();
102 for (int i = 0; pictList && i < pictList->count(); i++) {
103 subPictureBytes += pictList->begin()[i]->approximateBytesUsed();
104 }
105 return sk_make_sp<SkBigPicture>(fCullRect,
106 std::move(fRecord),
107 std::move(pictList),
108 std::move(fBBH),
109 subPictureBytes);
110}
111
112sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPictureWithCull(const SkRect& cullRect,
113 uint32_t finishFlags) {
114 fCullRect = cullRect;
115 return this->finishRecordingAsPicture(finishFlags);
116}
117
118
119void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
120 if (nullptr == canvas) {
121 return;
122 }
123
124 int drawableCount = 0;
125 SkDrawable* const* drawables = nullptr;
126 SkDrawableList* drawableList = fRecorder->getDrawableList();
127 if (drawableList) {
128 drawableCount = drawableList->count();
129 drawables = drawableList->begin();
130 }
131 SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, nullptr/*bbh*/, nullptr/*callback*/);
132}
133
134sk_sp<SkDrawable> SkPictureRecorder::finishRecordingAsDrawable(uint32_t finishFlags) {
135 fActivelyRecording = false;
136 fRecorder->flushMiniRecorder();
137 fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
138
139 SkRecordOptimize(fRecord.get());
140
141 if (fBBH.get()) {
142 SkAutoTMalloc<SkRect> bounds(fRecord->count());
143 SkAutoTMalloc<SkBBoxHierarchy::Metadata> meta(fRecord->count());
144 SkRecordFillBounds(fCullRect, *fRecord, bounds, meta);
145 fBBH->insert(bounds, meta, fRecord->count());
146 }
147
148 sk_sp<SkDrawable> drawable =
149 sk_make_sp<SkRecordedDrawable>(std::move(fRecord), std::move(fBBH),
150 fRecorder->detachDrawableList(), fCullRect);
151
152 return drawable;
153}
154