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