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