1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "skia_recording.h"
17
18 #include <iostream>
19
20 #include "parameters.h"
21
22 namespace OHOS::Rosen {
GetCaptureEnabled() const23 bool SkiaRecording::GetCaptureEnabled() const
24 {
25 return captureEnabled_;
26 }
27
InitConfigsFromParam()28 void SkiaRecording::InitConfigsFromParam()
29 {
30 captureEnabled_ = std::stoi(system::GetParameter("skpcapture.enabled", "0"));
31 if (captureEnabled_) {
32 captureFrameNum_ = std::stoi(system::GetParameter("skpcapture.frameNum", "0"));
33 if (captureFrameNum_ < 1) {
34 captureMode_ = SkiaCaptureMode::NONE;
35 } else if (captureFrameNum_ == 1) {
36 captureMode_ = SkiaCaptureMode::SINGLE_FRAME;
37 } else {
38 captureMode_ = SkiaCaptureMode::MULTI_FRAME;
39 }
40 captureFileName_ = system::GetParameter("skpcapture.path", "");
41 std::string fileExtension = ".skp";
42 if (captureFileName_.rfind(fileExtension) == (captureFileName_.size() - fileExtension.size())) {
43 captureMode_ = SkiaCaptureMode::SINGLE_FRAME;
44 }
45 }
46 }
47
SetupMultiFrame()48 bool SkiaRecording::SetupMultiFrame()
49 {
50 std::cout<< "Set up multi-frame capture, the frame number is " << captureFrameNum_ << std::endl;
51 auto stream = std::make_unique<SkFILEWStream>(captureFileName_.c_str());
52 if (!stream->isValid()) {
53 std::cout<< "Could not open " << captureFileName_ << " for writing." << std::endl;
54 captureFrameNum_ = 0;
55 captureMode_ = SkiaCaptureMode::NONE;
56 return false;
57 }
58 openMultiPicStream_ = std::move(stream);
59 serialContext_ = std::make_unique<SkSharingSerialContext>();
60 SkSerialProcs procs;
61 procs.fImageProc = SkSharingSerialContext::serializeImage;
62 procs.fImageCtx = serialContext_.get();
63 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
64 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
65 };
66 // SkDocuments don't take owership of the streams they write.
67 // we need to keep it until after multiPic_.close()
68 // procs is passed as a pointer, but just as a method of having an optional default.
69 // procs doesn't need to outlive this Make call.
70 #ifdef NEW_SKIA
71 multiPic_ = SkMakeMultiPictureDocument(openMultiPicStream_.get(), &procs,
72 [sharingCtx = serialContext_.get()](const SkPicture* pic) {
73 SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
74 });
75 #else
76 multiPic_ = SkMakeMultiPictureDocument(openMultiPicStream_.get(), &procs);
77 #endif
78 return true;
79 }
80
BeginCapture(SkCanvas * canvas,int width,int height)81 SkCanvas* SkiaRecording::BeginCapture(SkCanvas* canvas, int width, int height)
82 {
83 if (canvas == nullptr) {
84 return nullptr;
85 }
86 static bool isFirstFrame = true;
87 if (isFirstFrame && captureMode_ == SkiaCaptureMode::MULTI_FRAME) {
88 isFirstFrame = false;
89 if (!SetupMultiFrame()) {
90 return nullptr;
91 }
92 }
93 // Create a canvas pointer, fill it depending on what kind of capture is requested (if any)
94 switch (captureMode_) {
95 case SkiaCaptureMode::SINGLE_FRAME:
96 recorder_ = std::make_unique<SkPictureRecorder>();
97 pictureCanvas_ = recorder_->beginRecording(width, height);
98 break;
99 case SkiaCaptureMode::MULTI_FRAME:
100 // If a multi frame recording is active, initialize recording for a single frame of a
101 // multi frame file.
102 pictureCanvas_ = multiPic_->beginPage(width, height);
103 break;
104 case SkiaCaptureMode::NONE:
105 // Returning here in the non-capture case means we can count on pictureCanvas being
106 // non-null below.
107 return nullptr;
108 }
109
110 // Setting up an nway canvas is common to any kind of capture.
111 if (nwayCanvas_ == nullptr) {
112 nwayCanvas_ = std::make_unique<SkNWayCanvas>(width, height);
113 }
114 nwayCanvas_->addCanvas(canvas);
115 nwayCanvas_->addCanvas(pictureCanvas_);
116
117 return nwayCanvas_.get();
118 }
119
EndCapture()120 void SkiaRecording::EndCapture()
121 {
122 if (captureMode_ == SkiaCaptureMode::NONE) {
123 return;
124 }
125 nwayCanvas_->removeAll();
126
127 if (captureFrameNum_ > 0 && captureMode_ == SkiaCaptureMode::MULTI_FRAME) {
128 if (!multiPic_) {
129 std::cout << "multiPic_ is nullptr" << std::endl;
130 return;
131 }
132 multiPic_->endPage();
133 captureFrameNum_--;
134 if (captureFrameNum_ == 0) {
135 captureEnabled_ = false;
136 captureMode_ = SkiaCaptureMode::NONE;
137 // Pass multiPic_ and openMultiPicStream_ to a background thread, which will handle
138 // the heavyweight serialization work and destroy them. openMultiPicStream_ is released
139 // to a bare pointer because keeping it in a smart pointer makes the lambda
140 // non-copyable. The lambda is only called once, so this is safe.
141 SkFILEWStream* stream = openMultiPicStream_.release();
142 multiPic_->close();
143 delete stream;
144 std::cout << "Multi frame SKP complete." << std::endl;
145 }
146 } else if (!recorder_) {
147 std::cout << "recorder_ is nullptr" << std::endl;
148 } else {
149 sk_sp<SkPicture> picture = recorder_->finishRecordingAsPicture();
150 if (picture->approximateOpCount() > 0) {
151 // single frame skp to file
152 SkSerialProcs procs;
153 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
154 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
155 };
156 auto data = picture->serialize(&procs);
157 static int tmpId = 0;
158 std::string fileName = captureFileName_;
159 std::string fileExtension = ".skp";
160 if (captureFileName_.rfind(fileExtension) == (captureFileName_.size() - fileExtension.size())) {
161 fileName.insert(fileName.size() - fileExtension.size(), "_" + std::to_string(tmpId++));
162 } else {
163 fileName = fileName + "_" + std::to_string(tmpId++) + fileExtension;
164 }
165 SavePicture(data, fileName);
166 if (--captureFrameNum_ == 0) {
167 captureMode_ = SkiaCaptureMode::NONE;
168 }
169 }
170 recorder_.reset();
171 }
172 }
173
SavePicture(const sk_sp<SkData> & data,const std::string & filename)174 void SkiaRecording::SavePicture(const sk_sp<SkData>& data, const std::string& filename)
175 {
176 SkFILEWStream stream(filename.c_str());
177 if (stream.isValid()) {
178 stream.write(data->data(), data->size());
179 stream.flush();
180 std::cout << "SKP Captured Drawing Output (" << stream.bytesWritten() << " bytes) for frame. " <<
181 filename << std::endl;
182 } else {
183 std::cout << "Save picture failed. " << filename << " is not valid for frame." << filename << std::endl;
184 }
185 }
186 }