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