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