• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("debug.graphic.skpcapture.enabled", "0"));
31     if (captureEnabled_) {
32         captureFrameNum_ = std::stoi(system::GetParameter("debug.graphic.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("debug.graphic.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     SkCanvas* pictureCanvas = nullptr;
95     switch (captureMode_) {
96         case SkiaCaptureMode::SINGLE_FRAME:
97             recorder_ = std::make_unique<SkPictureRecorder>();
98             pictureCanvas = recorder_->beginRecording(width, height);
99             break;
100         case SkiaCaptureMode::MULTI_FRAME:
101             // If a multi frame recording is active, initialize recording for a single frame of a
102             // multi frame file.
103             pictureCanvas = multiPic_->beginPage(width, height);
104             break;
105         case SkiaCaptureMode::NONE:
106             // Returning here in the non-capture case means we can count on pictureCanvas being
107             // non-null below.
108             return nullptr;
109     }
110 
111     // Setting up an nway canvas is common to any kind of capture.
112     nwayCanvas_ = std::make_unique<SkNWayCanvas>(width, height);
113     nwayCanvas_->addCanvas(canvas);
114     nwayCanvas_->addCanvas(pictureCanvas);
115 
116     return nwayCanvas_.get();
117 }
118 
EndCapture()119 void SkiaRecording::EndCapture()
120 {
121     if (captureMode_ == SkiaCaptureMode::NONE) {
122         return;
123     }
124     nwayCanvas_.reset();
125 
126     if (captureFrameNum_ > 0 && captureMode_ == SkiaCaptureMode::MULTI_FRAME) {
127         if (!multiPic_) {
128             std::cout << "multiPic_ is nullptr" << std::endl;
129             return;
130         }
131         multiPic_->endPage();
132         captureFrameNum_--;
133         if (captureFrameNum_ == 0) {
134             captureEnabled_ = false;
135             captureMode_ = SkiaCaptureMode::NONE;
136             // Pass multiPic_ and openMultiPicStream_ to a background thread, which will handle
137             // the heavyweight serialization work and destroy them. openMultiPicStream_ is released
138             // to a bare pointer because keeping it in a smart pointer makes the lambda
139             // non-copyable. The lambda is only called once, so this is safe.
140             SkFILEWStream* stream = openMultiPicStream_.release();
141             multiPic_->close();
142             delete stream;
143             std::cout << "Multi frame SKP complete." << std::endl;
144         }
145     } else if (!recorder_) {
146         std::cout << "recorder_ is nullptr" << std::endl;
147     } else {
148         sk_sp<SkPicture> picture = recorder_->finishRecordingAsPicture();
149         if (picture->approximateOpCount() > 0) {
150             // single frame skp to file
151             SkSerialProcs procs;
152             procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
153                 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
154             };
155             auto data = picture->serialize(&procs);
156             static int tmpId = 0;
157             std::string fileName = captureFileName_;
158             std::string fileExtension = ".skp";
159             if (captureFileName_.rfind(fileExtension) == (captureFileName_.size() - fileExtension.size())) {
160                 fileName.insert(fileName.size() - fileExtension.size(), "_" + std::to_string(tmpId++));
161             } else {
162                 fileName = fileName + "_" + std::to_string(tmpId++) + fileExtension;
163             }
164             SavePicture(data, fileName);
165             if (--captureFrameNum_ == 0) {
166                 captureMode_ = SkiaCaptureMode::NONE;
167             }
168         }
169         recorder_.reset();
170     }
171 }
172 
SavePicture(const sk_sp<SkData> & data,const std::string & filename)173 void SkiaRecording::SavePicture(const sk_sp<SkData>& data, const std::string& filename)
174 {
175     SkFILEWStream stream(filename.c_str());
176     if (stream.isValid()) {
177         stream.write(data->data(), data->size());
178         stream.flush();
179         std::cout << "SKP Captured Drawing Output (" << stream.bytesWritten() << " bytes) for frame. " <<
180             filename << std::endl;
181     } else {
182         std::cout << "Save picture failed. " << filename << " is not valid for frame." << filename << std::endl;
183     }
184 }
185 }