1 /*
2  * Copyright (c) 2021-2022 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 #include "snapshot_utils.h"
16 
17 #include <cerrno>
18 #include <climits>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <ctime>
22 #include <getopt.h>
23 #include <hitrace_meter.h>
24 #include <image_type.h>
25 #include <iostream>
26 #include <ostream>
27 #include <csetjmp>
28 #include <pixel_map.h>
29 #include <securec.h>
30 #include <string>
31 #include <sys/time.h>
32 
33 #include "image_packer.h"
34 #include "jpeglib.h"
35 
36 using namespace OHOS::Rosen;
37 
38 namespace OHOS {
39 constexpr int MAX_TIME_STR_LEN = 40;
40 constexpr int YEAR_SINCE = 1900;
41 constexpr int32_t RGB565_PIXEL_BYTES = 2;
42 constexpr int32_t RGB888_PIXEL_BYTES = 3;
43 constexpr int32_t RGBA8888_PIXEL_BYTES = 4;
44 constexpr uint8_t B_INDEX = 0;
45 constexpr uint8_t G_INDEX = 1;
46 constexpr uint8_t R_INDEX = 2;
47 constexpr uint8_t SHIFT_2_BIT = 2;
48 constexpr uint8_t SHIFT_3_BIT = 3;
49 constexpr uint8_t SHIFT_5_BIT = 5;
50 constexpr uint8_t SHIFT_8_BIT = 8;
51 constexpr uint8_t SHIFT_11_BIT = 11;
52 constexpr uint8_t SHIFT_16_BIT = 16;
53 
54 constexpr uint16_t RGB565_MASK_BLUE = 0xF800;
55 constexpr uint16_t RGB565_MASK_GREEN = 0x07E0;
56 constexpr uint16_t RGB565_MASK_RED = 0x001F;
57 constexpr uint32_t RGBA8888_MASK_BLUE = 0x000000FF;
58 constexpr uint32_t RGBA8888_MASK_GREEN = 0x0000FF00;
59 constexpr uint32_t RGBA8888_MASK_RED = 0x00FF0000;
60 
61 constexpr uint8_t PNG_PACKER_QUALITY = 100;
62 constexpr uint8_t PACKER_QUALITY = 75;
63 constexpr uint32_t PACKER_SUCCESS = 0;
64 struct MissionErrorMgr : public jpeg_error_mgr {
65     jmp_buf environment;
66 };
67 
mission_error_exit(j_common_ptr cinfo)68 void mission_error_exit(j_common_ptr cinfo)
69 {
70     if (cinfo == nullptr || cinfo->err == nullptr) {
71         std::cout << __func__ << ": param is invalid." << std::endl;
72         return;
73     }
74     auto err = reinterpret_cast<MissionErrorMgr*>(cinfo->err);
75     longjmp(err->environment, 1);
76 }
77 
78 const char *VALID_SNAPSHOT_PATH = "/data/local/tmp";
79 const char *DEFAULT_SNAPSHOT_PREFIX = "/snapshot";
80 const char *VALID_SNAPSHOT_SUFFIX = ".jpeg";
81 const char *VALID_SNAPSHOT_PNG_SUFFIX = ".png";
82 
PrintUsage(const std::string& cmdLine)83 void SnapShotUtils::PrintUsage(const std::string& cmdLine)
84 {
85     std::cout << "usage: " << cmdLine.c_str() <<
86         " [-i displayId] [-f output_file] [-w width] [-h height] [-t type] [-m]" << std::endl;
87 }
88 
GenerateFileName(std::string fileType, int offset)89 std::string SnapShotUtils::GenerateFileName(std::string fileType, int offset)
90 {
91     timeval tv;
92     std::string fileName = VALID_SNAPSHOT_PATH;
93 
94     fileName += DEFAULT_SNAPSHOT_PREFIX;
95     if (gettimeofday(&tv, nullptr) == 0) {
96         tv.tv_sec += offset; // add offset second
97         struct tm *tmVal = localtime(&tv.tv_sec);
98         if (tmVal != nullptr) {
99             char timeStr[MAX_TIME_STR_LEN] = { 0 };
100             snprintf_s(timeStr, sizeof(timeStr), sizeof(timeStr) - 1,
101                 "_%04d-%02d-%02d_%02d-%02d-%02d",
102                 tmVal->tm_year + YEAR_SINCE, tmVal->tm_mon + 1, tmVal->tm_mday,
103                 tmVal->tm_hour, tmVal->tm_min, tmVal->tm_sec);
104             fileName += timeStr;
105         }
106     }
107     fileName += (fileType == "png") ? VALID_SNAPSHOT_PNG_SUFFIX : VALID_SNAPSHOT_SUFFIX;
108     return fileName;
109 }
110 
CheckFileNameValid(const std::string& fileName, std::string fileType)111 bool SnapShotUtils::CheckFileNameValid(const std::string& fileName, std::string fileType)
112 {
113     std::cout << "fileType: " << fileType << std::endl;
114     size_t fileMinLength = (fileType == "png") ? strlen(VALID_SNAPSHOT_PNG_SUFFIX) : strlen(VALID_SNAPSHOT_SUFFIX);
115     if (fileName.length() <= fileMinLength) {
116         std::cout << "error: fileName " << fileName.c_str() << " invalid, file length too short!" << std::endl;
117         return false;
118     }
119     // check file path
120     std::string fileDir = fileName;
121     auto pos = fileDir.find_last_of("/");
122     if (pos != std::string::npos) {
123         fileDir.erase(pos + 1);
124     } else {
125         fileDir = "."; // current work dir
126     }
127     char resolvedPath[PATH_MAX] = { 0 };
128     char *realPath = realpath(fileDir.c_str(), resolvedPath);
129     if (realPath == nullptr) {
130         std::cout << "error: fileName " << fileName.c_str() << " invalid, realpath nullptr!" << std::endl;
131         return false;
132     }
133     if (strncmp(realPath, VALID_SNAPSHOT_PATH, strlen(VALID_SNAPSHOT_PATH)) != 0) {
134         std::cout << "error: fileName " << fileName.c_str() << " invalid, realpath "
135             << realPath << " must dump at dir: " << VALID_SNAPSHOT_PATH << std::endl;
136         return false;
137     }
138 
139     // check file suffix
140     const char *fileNameSuffix = fileName.c_str() + (fileName.length() - fileMinLength);
141     const char *fileSuffix = (fileType == "png") ? VALID_SNAPSHOT_PNG_SUFFIX : VALID_SNAPSHOT_SUFFIX;
142     if (strncmp(fileNameSuffix, fileSuffix, fileMinLength) == 0) {
143         return true; // valid suffix
144     }
145     std::cout << "error: fileName " << fileName.c_str() << " invalid, suffix must be " << fileSuffix << std::endl;
146     return false;
147 }
148 
CheckWHValid(int32_t param)149 bool SnapShotUtils::CheckWHValid(int32_t param)
150 {
151     return (param > 0) && (param <= DisplayManager::MAX_RESOLUTION_SIZE_SCREENSHOT);
152 }
153 
CheckWidthAndHeightValid(int32_t w, int32_t h)154 bool SnapShotUtils::CheckWidthAndHeightValid(int32_t w, int32_t h)
155 {
156     return CheckWHValid(w) && CheckWHValid(h);
157 }
158 
CheckParamValid(const WriteToJpegParam& param)159 bool SnapShotUtils::CheckParamValid(const WriteToJpegParam& param)
160 {
161     switch (param.format) {
162         case Media::PixelFormat::RGBA_8888:
163             if (param.stride != param.width * RGBA8888_PIXEL_BYTES) {
164                 return false;
165             }
166             break;
167         case Media::PixelFormat::RGB_565:
168             if (param.stride != param.width * RGB565_PIXEL_BYTES) {
169                 return false;
170             }
171             break;
172         case Media::PixelFormat::RGB_888:
173             if (param.stride != param.width * RGB888_PIXEL_BYTES) {
174                 return false;
175             }
176             break;
177         default:
178             std::cout << __func__ << ": unsupported pixel format: " <<
179                 static_cast<uint32_t>(param.format) << std::endl;
180             return false;
181     }
182     if (!CheckWidthAndHeightValid(param.width, param.height)) {
183         return false;
184     }
185     if (param.data == nullptr) {
186         return false;
187     }
188     return true;
189 }
190 
RGBA8888ToRGB888(const uint8_t* rgba8888Buf, uint8_t* rgb888Buf, int32_t size)191 bool SnapShotUtils::RGBA8888ToRGB888(const uint8_t* rgba8888Buf, uint8_t* rgb888Buf, int32_t size)
192 {
193     if (rgba8888Buf == nullptr || rgb888Buf == nullptr || size <= 0) {
194         std::cout << __func__ << ": params are invalid." << std::endl;
195         return false;
196     }
197     const uint32_t* rgba8888 = reinterpret_cast<const uint32_t*>(rgba8888Buf);
198     for (int32_t i = 0; i < size; i++) {
199         rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] = (rgba8888[i] & RGBA8888_MASK_RED) >> SHIFT_16_BIT;
200         rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] = (rgba8888[i] & RGBA8888_MASK_GREEN) >> SHIFT_8_BIT;
201         rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] = rgba8888[i] & RGBA8888_MASK_BLUE;
202     }
203     return true;
204 }
205 
RGB565ToRGB888(const uint8_t* rgb565Buf, uint8_t* rgb888Buf, int32_t size)206 bool SnapShotUtils::RGB565ToRGB888(const uint8_t* rgb565Buf, uint8_t* rgb888Buf, int32_t size)
207 {
208     if (rgb565Buf == nullptr || rgb888Buf == nullptr || size <= 0) {
209         std::cout << __func__ << ": params are invalid." << std::endl;
210         return false;
211     }
212     const uint16_t* rgb565 = reinterpret_cast<const uint16_t*>(rgb565Buf);
213     for (int32_t i = 0; i < size; i++) {
214         rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] = (rgb565[i] & RGB565_MASK_RED);
215         rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] = (rgb565[i] & RGB565_MASK_GREEN) >> SHIFT_5_BIT;
216         rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] = (rgb565[i] & RGB565_MASK_BLUE) >> SHIFT_11_BIT;
217         rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] <<= SHIFT_3_BIT;
218         rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] <<= SHIFT_2_BIT;
219         rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] <<= SHIFT_3_BIT;
220     }
221     return true;
222 }
223 
224 // The method will NOT release file.
WriteRgb888ToJpeg(FILE* file, uint32_t width, uint32_t height, const uint8_t* data)225 bool SnapShotUtils::WriteRgb888ToJpeg(FILE* file, uint32_t width, uint32_t height, const uint8_t* data)
226 {
227     if (data == nullptr) {
228         std::cout << "error: data error, nullptr!" << std::endl;
229         return false;
230     }
231 
232     if (file == nullptr) {
233         std::cout << "error: file is null" << std::endl;
234         return false;
235     }
236 
237     struct jpeg_compress_struct jpeg;
238     struct MissionErrorMgr jerr;
239     jpeg.err = jpeg_std_error(&jerr);
240     jerr.error_exit = mission_error_exit;
241     if (setjmp(jerr.environment)) {
242         jpeg_destroy_compress(&jpeg);
243         std::cout << "error: lib jpeg exit with error!" << std::endl;
244         return false;
245     }
246 
247     jpeg_create_compress(&jpeg);
248     jpeg.image_width = width;
249     jpeg.image_height = height;
250     jpeg.input_components = RGB888_PIXEL_BYTES;
251     jpeg.in_color_space = JCS_RGB;
252     jpeg_set_defaults(&jpeg);
253 
254     constexpr int32_t quality = 75;
255     jpeg_set_quality(&jpeg, quality, TRUE);
256 
257     jpeg_stdio_dest(&jpeg, file);
258     jpeg_start_compress(&jpeg, TRUE);
259     JSAMPROW rowPointer[1];
260     for (uint32_t i = 0; i < jpeg.image_height; i++) {
261         rowPointer[0] = const_cast<uint8_t *>(data + i * jpeg.image_width * RGB888_PIXEL_BYTES);
262         (void)jpeg_write_scanlines(&jpeg, rowPointer, 1);
263     }
264 
265     jpeg_finish_compress(&jpeg);
266     jpeg_destroy_compress(&jpeg);
267     return true;
268 }
269 
WriteToJpeg(const std::string& fileName, const WriteToJpegParam& param)270 bool SnapShotUtils::WriteToJpeg(const std::string& fileName, const WriteToJpegParam& param)
271 {
272     bool ret = false;
273     if (!CheckFileNameValid(fileName)) {
274         return ret;
275     }
276     if (!CheckParamValid(param)) {
277         std::cout << "error: invalid param." << std::endl;
278         return ret;
279     }
280     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "snapshot:WriteToJpeg(%s)", fileName.c_str());
281 
282     FILE *file = fopen(fileName.c_str(), "wb");
283     if (file == nullptr) {
284         std::cout << "error: open file [" << fileName.c_str() << "] error, " << errno << "!" << std::endl;
285         return ret;
286     }
287     std::cout << "snapshot: pixel format is: " << static_cast<uint32_t>(param.format) << std::endl;
288     if (param.format == Media::PixelFormat::RGBA_8888) {
289         int32_t rgb888Size = param.stride * param.height * RGB888_PIXEL_BYTES / RGBA8888_PIXEL_BYTES;
290         uint8_t *rgb888 = new uint8_t[rgb888Size];
291         ret = RGBA8888ToRGB888(param.data, rgb888, rgb888Size / RGB888_PIXEL_BYTES);
292         if (ret) {
293             std::cout << "snapshot: convert rgba8888 to rgb888 successfully." << std::endl;
294             ret = WriteRgb888ToJpeg(file, param.width, param.height, rgb888);
295         }
296         delete[] rgb888;
297     } else if (param.format == Media::PixelFormat::RGB_565) {
298         int32_t rgb888Size = param.stride * param.height * RGB888_PIXEL_BYTES / RGB565_PIXEL_BYTES;
299         uint8_t *rgb888 = new uint8_t[rgb888Size];
300         ret = RGB565ToRGB888(param.data, rgb888, rgb888Size / RGB888_PIXEL_BYTES);
301         if (ret) {
302             std::cout << "snapshot: convert rgb565 to rgb888 successfully." << std::endl;
303             ret = WriteRgb888ToJpeg(file, param.width, param.height, rgb888);
304         }
305         delete[] rgb888;
306     } else if (param.format == Media::PixelFormat::RGB_888) {
307         ret = WriteRgb888ToJpeg(file, param.width, param.height, param.data);
308     } else {
309         std::cout << "snapshot: invalid pixel format." << std::endl;
310     }
311     if (fclose(file) != 0) {
312         std::cout << "error: close file failed!" << std::endl;
313         ret = false;
314     }
315     return ret;
316 }
317 
WriteToJpeg(int fd, const WriteToJpegParam& param)318 bool SnapShotUtils::WriteToJpeg(int fd, const WriteToJpegParam& param)
319 {
320     bool ret = false;
321     if (!CheckParamValid(param)) {
322         std::cout << "error: invalid param." << std::endl;
323         return ret;
324     }
325 
326     FILE *file = fdopen(fd, "wb");
327     if (file == nullptr) {
328         return ret;
329     }
330     std::cout << "snapshot: pixel format is: " << static_cast<uint32_t>(param.format) << std::endl;
331     if (param.format == Media::PixelFormat::RGBA_8888) {
332         int32_t rgb888Size = param.stride * param.height * RGB888_PIXEL_BYTES / RGBA8888_PIXEL_BYTES;
333         uint8_t *rgb888 = new uint8_t[rgb888Size];
334         ret = RGBA8888ToRGB888(param.data, rgb888, rgb888Size / RGB888_PIXEL_BYTES);
335         if (ret) {
336             std::cout << "snapshot: convert rgba8888 to rgb888 successfully." << std::endl;
337             ret = WriteRgb888ToJpeg(file, param.width, param.height, rgb888);
338         }
339         delete[] rgb888;
340     } else if (param.format == Media::PixelFormat::RGB_565) {
341         int32_t rgb888Size = param.stride * param.height * RGB888_PIXEL_BYTES / RGB565_PIXEL_BYTES;
342         uint8_t *rgb888 = new uint8_t[rgb888Size];
343         ret = RGB565ToRGB888(param.data, rgb888, rgb888Size / RGB888_PIXEL_BYTES);
344         if (ret) {
345             std::cout << "snapshot: convert rgb565 to rgb888 successfully." << std::endl;
346             ret = WriteRgb888ToJpeg(file, param.width, param.height, rgb888);
347         }
348         delete[] rgb888;
349     } else if (param.format == Media::PixelFormat::RGB_888) {
350         ret = WriteRgb888ToJpeg(file, param.width, param.height, param.data);
351     } else {
352         std::cout << "snapshot: invalid pixel format." << std::endl;
353     }
354     if (fclose(file) != 0) {
355         std::cout << "error: close file failed!" << std::endl;
356         ret = false;
357     }
358     return ret;
359 }
360 
SaveSnapShot(const std::string& fileName, Media::PixelMap& pixelMap, std::string fileType)361 bool SnapShotUtils::SaveSnapShot(const std::string& fileName, Media::PixelMap& pixelMap, std::string fileType)
362 {
363     OHOS::Media::ImagePacker imagePacker;
364     OHOS::Media::PackOption option;
365     option.format = (fileType == "png") ? "image/png" : "image/jpeg";
366     option.quality = (fileType == "png") ? PNG_PACKER_QUALITY : PACKER_QUALITY;
367     option.numberHint = 1;
368     std::set<std::string> formats;
369     auto ret = imagePacker.GetSupportedFormats(formats);
370     if (ret) {
371         std::cout << "error: get supported formats error" << std::endl;
372         return false;
373     }
374 
375     imagePacker.StartPacking(fileName, option);
376     imagePacker.AddImage(pixelMap);
377     int64_t packedSize = 0;
378     uint32_t res = imagePacker.FinalizePacking(packedSize);
379     if (res != PACKER_SUCCESS) {
380         std::cout << "error:FinalizePacking error" << std::endl;
381         return false;
382     }
383     return true;
384 }
385 
WriteToJpegWithPixelMap(const std::string& fileName, Media::PixelMap& pixelMap)386 bool SnapShotUtils::WriteToJpegWithPixelMap(const std::string& fileName, Media::PixelMap& pixelMap)
387 {
388     if (pixelMap.GetAllocatorType() == Media::AllocatorType::DMA_ALLOC) {
389         return SaveSnapShot(fileName, pixelMap);
390     }
391     WriteToJpegParam param;
392     param.width = static_cast<uint32_t>(pixelMap.GetWidth());
393     param.height = static_cast<uint32_t>(pixelMap.GetHeight());
394     param.data = pixelMap.GetPixels();
395     param.stride = static_cast<uint32_t>(pixelMap.GetRowBytes());
396     param.format = pixelMap.GetPixelFormat();
397     return SnapShotUtils::WriteToJpeg(fileName, param);
398 }
399 
WriteToJpegWithPixelMap(int fd, Media::PixelMap& pixelMap)400 bool SnapShotUtils::WriteToJpegWithPixelMap(int fd, Media::PixelMap& pixelMap)
401 {
402     WriteToJpegParam param;
403     param.width = static_cast<uint32_t>(pixelMap.GetWidth());
404     param.height = static_cast<uint32_t>(pixelMap.GetHeight());
405     param.data = pixelMap.GetPixels();
406     param.stride = static_cast<uint32_t>(pixelMap.GetRowBytes());
407     param.format = pixelMap.GetPixelFormat();
408     return SnapShotUtils::WriteToJpeg(fd, param);
409 }
410 
ProcessDisplayId(Rosen::DisplayId& displayId, bool isDisplayIdSet)411 bool SnapShotUtils::ProcessDisplayId(Rosen::DisplayId& displayId, bool isDisplayIdSet)
412 {
413     if (!isDisplayIdSet) {
414         displayId = DisplayManager::GetInstance().GetDefaultDisplayId();
415     } else {
416         bool validFlag = false;
417         auto displayIds = DisplayManager::GetInstance().GetAllDisplayIds();
418         for (auto id : displayIds) {
419             if (displayId == id) {
420                 validFlag = true;
421                 break;
422             }
423         }
424         if (!validFlag) {
425             std::cout << "error: displayId " << static_cast<int64_t>(displayId) << " invalid!" << std::endl;
426             std::cout << "tips: supported displayIds:" << std::endl;
427             for (auto dispId : displayIds) {
428                 std::cout << "\t" << dispId << std::endl;
429             }
430             return false;
431         }
432     }
433     return true;
434 }
435 
ProcessArgs(int argc, char* const argv[], CmdArguments& cmdArguments)436 bool SnapShotUtils::ProcessArgs(int argc, char* const argv[], CmdArguments& cmdArguments)
437 {
438     int opt = 0;
439     const struct option longOption[] = {
440         { "id", required_argument, nullptr, 'i' },
441         { "width", required_argument, nullptr, 'w' },
442         { "height", required_argument, nullptr, 'h' },
443         { "file", required_argument, nullptr, 'f' },
444         { "type", required_argument, nullptr, 't' },
445         { "help", required_argument, nullptr, 'm' },
446         { nullptr, 0, nullptr, 0 }
447     };
448     while ((opt = getopt_long(argc, argv, "i:w:h:f:t:m", longOption, nullptr)) != -1) {
449         switch (opt) {
450             case 'i': // display id
451                 cmdArguments.displayId = static_cast<DisplayId>(atoll(optarg));
452                 cmdArguments.isDisplayIdSet = true;
453                 break;
454             case 'w': // output width
455                 cmdArguments.width = atoi(optarg);
456                 cmdArguments.isWidthSet = true;
457                 break;
458             case 'h': // output height
459                 cmdArguments.height = atoi(optarg);
460                 cmdArguments.isHeightSet = true;
461                 break;
462             case 'f': // output file name
463                 cmdArguments.fileName = optarg;
464                 break;
465             case 't': // output file type
466                 cmdArguments.fileType = optarg;
467                 break;
468             case 'm': // help
469             default:
470                 SnapShotUtils::PrintUsage(argv[0]);
471                 return false;
472         }
473     }
474 
475     if (!ProcessDisplayId(cmdArguments.displayId, cmdArguments.isDisplayIdSet)) {
476         return false;
477     }
478 
479     if (cmdArguments.fileName == "") {
480         cmdArguments.fileName = GenerateFileName(cmdArguments.fileType);
481         std::cout << "process: set filename to " << cmdArguments.fileName.c_str() << std::endl;
482     }
483 
484     // check fileName
485     if (!SnapShotUtils::CheckFileNameValid(cmdArguments.fileName, cmdArguments.fileType)) {
486         std::cout << "error: filename " << cmdArguments.fileName.c_str() << " invalid!" << std::endl;
487         return false;
488     }
489     return true;
490 }
491 }
492