1 /*
2  * Copyright (c) 2020-2021 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 "dfx/ui_screenshot.h"
17 #if ENABLE_DEBUG
18 #include "iwindows_manager.h"
19 #include "common/screen.h"
20 #include "draw/draw_utils.h"
21 #include "gfx_utils/color.h"
22 #include "gfx_utils/file.h"
23 #include "gfx_utils/graphic_log.h"
24 #include "gfx_utils/image_info.h"
25 #include "securec.h"
26 
27 namespace OHOS {
28 class UIScreenshotListener : public IWindowsManager::ScreenshotListener {
29 public:
UIScreenshotListener()30     UIScreenshotListener() : filePath_(nullptr) {}
31 
~UIScreenshotListener()32     virtual ~UIScreenshotListener()
33     {
34         if (filePath_ != nullptr) {
35             UIFree(reinterpret_cast<void*>(filePath_));
36             filePath_ = nullptr;
37         }
38     }
39 
OnScreenshotEnd(uint8_t * virAddr,uint32_t width,uint32_t height,ImagePixelFormat format,uint32_t stride)40     void OnScreenshotEnd(uint8_t* virAddr, uint32_t width, uint32_t height,
41                          ImagePixelFormat format, uint32_t stride) override
42     {
43         if ((virAddr == nullptr) || ((format != IMAGE_PIXEL_FORMAT_ARGB1555) &&
44             (format != IMAGE_PIXEL_FORMAT_ARGB8888)) || (width == 0) || (height == 0)) {
45             return;
46         }
47 
48         ImageHeader header = {0};
49         header.colorMode = ARGB8888;
50         header.width = width;
51         header.height = height;
52 
53         unlink(filePath_);
54         int32_t fd = open(filePath_, O_RDWR | O_CREAT, DEFAULT_FILE_PERMISSION);
55         UIFree(reinterpret_cast<void*>(filePath_));
56         filePath_ = nullptr;
57         if (fd < 0) {
58             GRAPHIC_LOGE("UIScreenshotListener::OnScreenshotEnd open file failed Err!\n");
59             return;
60         }
61 
62         if (write(fd, &header, sizeof(ImageHeader)) != sizeof(ImageHeader)) {
63             GRAPHIC_LOGE("UIScreenshotListener::OnScreenshotEnd write image header failed Err!\n");
64             close(fd);
65             return;
66         }
67 
68         uint32_t row = MAX_MALLOC_SIZE / width;
69         row = (row == 0) ? 1 : row;
70         uint32_t size = row * width * sizeof(uint32_t);
71         uint32_t* argb8888Addr = static_cast<uint32_t*>(UIMalloc(size));
72         if (argb8888Addr == nullptr) {
73             GRAPHIC_LOGE("UIScreenshotListener::OnScreenshotEnd memory allocation failed Err!");
74             close(fd);
75             return;
76         }
77 
78         while (height >= row) {
79             WriteBlockToFile(fd, argb8888Addr, virAddr, row, width, format, stride);
80             height -= row;
81         }
82         if (height != 0) {
83             WriteBlockToFile(fd, argb8888Addr, virAddr, height, width, format, stride);
84         }
85         UIFree(reinterpret_cast<void*>(argb8888Addr));
86         close(fd);
87     }
88 
SetFilePath(char * path)89     void SetFilePath(char* path)
90     {
91         if (filePath_ != nullptr) {
92             UIFree(reinterpret_cast<void*>(filePath_));
93         }
94         filePath_ = path;
95     }
96 
97 private:
98     static constexpr uint8_t DEFAULT_COLOR_SIZE = 4;
99     static constexpr uint16_t MAX_MALLOC_SIZE = 2048; // unit: 4 bytes
100     char* filePath_;
101 
WriteBlockToFile(int32_t fd,uint32_t * buffer,uint8_t * & startAddr,uint32_t row,uint32_t width,ImagePixelFormat format,uint32_t stride) const102     bool WriteBlockToFile(int32_t fd, uint32_t* buffer, uint8_t*& startAddr, uint32_t row,
103                           uint32_t width, ImagePixelFormat format, uint32_t stride) const
104     {
105         uint32_t* argb8888Addr = buffer;
106         for (uint32_t r = 0; r < row; ++r) {
107             if (format == IMAGE_PIXEL_FORMAT_ARGB1555) {
108                 uint16_t* temp = reinterpret_cast<uint16_t*>(startAddr);
109                 for (uint32_t i = 0; i < width; ++i) {
110                     buffer[i] = PixelFormatUtils::ARGB1555ToARGB8888(*temp++);
111                 }
112             } else if (format == IMAGE_PIXEL_FORMAT_ARGB8888) {
113                 if (memcpy_s(buffer, width * DEFAULT_COLOR_SIZE, startAddr, width * DEFAULT_COLOR_SIZE) != EOK) {
114                     GRAPHIC_LOGE("memcpy_s error!");
115                 }
116             }
117             startAddr += stride;
118             buffer += width;
119         }
120 
121         uint32_t blockSize = row * width * sizeof(uint32_t);
122         if (static_cast<uint32_t>(write(fd, argb8888Addr, blockSize)) != blockSize) {
123             GRAPHIC_LOGE("UIScreenshotListener::WriteBlockToFile wrong amount of written data Err!");
124             return false;
125         }
126         return true;
127     }
128 };
129 
~UIScreenshot()130 UIScreenshot::~UIScreenshot()
131 {
132     if (screenshotListener_ != nullptr) {
133         delete screenshotListener_;
134         screenshotListener_ = nullptr;
135     }
136 }
137 
GetInstance()138 UIScreenshot* UIScreenshot::GetInstance()
139 {
140     static UIScreenshot instance;
141     return &instance;
142 }
143 
ScreenshotToFile(const char * path)144 bool UIScreenshot::ScreenshotToFile(const char* path)
145 {
146     IWindowsManager* manager = IWindowsManager::GetInstance();
147     if (screenshotListener_ == nullptr) {
148         screenshotListener_ = new UIScreenshotListener();
149         if (screenshotListener_ == nullptr) {
150             GRAPHIC_LOGE("UIScreenshot::ScreenshotToFile register screenshot listener failed Err!\n");
151             return false;
152         }
153         manager->SetScreenshotListener(screenshotListener_);
154     }
155 
156     const char* srcPath = (path == nullptr) ? DEFAULT_SCREENSHOT_PATH : path;
157     uint32_t pathLength = strlen(srcPath);
158     char* destPath = static_cast<char*>(UIMalloc(pathLength + 1));
159     if (destPath == nullptr) {
160         return false;
161     }
162 
163     if (memcpy_s(destPath, pathLength + 1, srcPath, pathLength) != EOK) {
164         UIFree(reinterpret_cast<void*>(destPath));
165         return false;
166     }
167     destPath[pathLength] = '\0';
168     screenshotListener_->SetFilePath(destPath);
169     manager->Screenshot();
170     return true;
171 }
172 } // namespace OHOS
173 #endif // ENABLE_DEBUG
174