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
27namespace OHOS {
28class UIScreenshotListener : public IWindowsManager::ScreenshotListener {
29public:
30    UIScreenshotListener() : filePath_(nullptr) {}
31
32    virtual ~UIScreenshotListener()
33    {
34        if (filePath_ != nullptr) {
35            UIFree(reinterpret_cast<void*>(filePath_));
36            filePath_ = nullptr;
37        }
38    }
39
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
89    void SetFilePath(char* path)
90    {
91        if (filePath_ != nullptr) {
92            UIFree(reinterpret_cast<void*>(filePath_));
93        }
94        filePath_ = path;
95    }
96
97private:
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
102    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
130UIScreenshot::~UIScreenshot()
131{
132    if (screenshotListener_ != nullptr) {
133        delete screenshotListener_;
134        screenshotListener_ = nullptr;
135    }
136}
137
138UIScreenshot* UIScreenshot::GetInstance()
139{
140    static UIScreenshot instance;
141    return &instance;
142}
143
144bool 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