1 /*
2 * Copyright (c) 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 <cstdio>
16 #include <cmath>
17 #include <unistd.h>
18
19
20 #include "image_compress.h"
21
22 #include "app_log_wrapper.h"
23 #include "securec.h"
24 #include "image_source.h"
25 #include "image_packer.h"
26 #include "media_errors.h"
27
28 namespace OHOS {
29 namespace AppExecFwk {
30 namespace {
31 constexpr size_t FORMAT_LENGTH = 8;
32 const std::string JPEG_FORMAT = "image/jpeg";
33 const std::string PNG_FORMAT = "image/png";
34 const std::string WEBP_FORMAT = "image/webp";
35 const std::string BUNDLE_PATH = "/data/app/el1/bundle";
36 constexpr int32_t QUALITY = 20;
37 constexpr int32_t MUNBER_ONE = 1;
38 constexpr int64_t BUFFER_SIZE = 2 * 1024 * 1024;
39 constexpr int32_t FILE_MAX_SIZE = 10240;
40 constexpr int32_t FILE_COMPRESS_SIZE = 4096;
41 constexpr int32_t WEBP_COMPRESS_SIZE = 128;
42 constexpr uint8_t JPEG_DATA_ZERO = 0xFF;
43 constexpr uint8_t JPEG_DATA_ONE = 0xD8;
44 constexpr uint8_t JPEG_DATA_TWO = 0xFF;
45 constexpr uint8_t PNG_DATA_ZERO = 0x89;
46 constexpr uint8_t PNG_DATA_ONE = 0x50;
47 constexpr uint8_t PNG_DATA_TWO = 0x4E;
48 constexpr uint8_t PNG_DATA_THREE = 0x47;
49 constexpr int32_t INDEX_ZERO = 0;
50 constexpr int32_t INDEX_ONE = 1;
51 constexpr int32_t INDEX_TWO = 2;
52 constexpr int32_t INDEX_THREE = 3;
53 constexpr int32_t EMPTY_FILE_SIZE = 0;
54 constexpr double FILE_SIZE_ERR = -1.0;
55 }
IsPathValid(const std::string & srcPath)56 bool ImageCompress::IsPathValid(const std::string &srcPath)
57 {
58 if (srcPath.find(BUNDLE_PATH) != std::string::npos) {
59 return access(srcPath.c_str(), R_OK) == 0;
60 } else {
61 return false;
62 }
63 }
64
IsImageNeedCompressBySize(size_t fileSize)65 bool ImageCompress::IsImageNeedCompressBySize(size_t fileSize)
66 {
67 return fileSize > FILE_MAX_SIZE;
68 }
69
CalculateRatio(size_t fileSize,const std::string & imageType)70 double ImageCompress::CalculateRatio(size_t fileSize, const std::string &imageType)
71 {
72 if (fileSize == EMPTY_FILE_SIZE) {
73 return FILE_SIZE_ERR;
74 }
75 if (imageType == WEBP_FORMAT) {
76 return sqrt(static_cast<double>(WEBP_COMPRESS_SIZE) / fileSize);
77 }
78 return sqrt(static_cast<double>(FILE_COMPRESS_SIZE) / fileSize);
79 }
80
GetImageType(const std::unique_ptr<uint8_t[]> & fileData,size_t fileLength)81 ImageType ImageCompress::GetImageType(const std::unique_ptr<uint8_t[]> &fileData, size_t fileLength)
82 {
83 if (fileLength < FORMAT_LENGTH) {
84 return ImageType::WORNG_TYPE;
85 }
86 const uint8_t* data = fileData.get();
87 if (data[INDEX_ZERO] == JPEG_DATA_ZERO && data[INDEX_ONE] == JPEG_DATA_ONE
88 && data[INDEX_TWO] == JPEG_DATA_TWO) {
89 return ImageType::JPEG;
90 } else if (data[INDEX_ZERO] == PNG_DATA_ZERO && data[INDEX_ONE] == PNG_DATA_ONE &&
91 data[INDEX_TWO] == PNG_DATA_TWO && data[INDEX_THREE] == PNG_DATA_THREE) {
92 return ImageType::PNG;
93 } else {
94 return ImageType::WORNG_TYPE;
95 }
96 }
97
GetImageTypeString(const std::unique_ptr<uint8_t[]> & fileData,size_t fileLength,std::string & imageType)98 bool ImageCompress::GetImageTypeString(const std::unique_ptr<uint8_t[]> &fileData,
99 size_t fileLength, std::string &imageType)
100 {
101 ImageType type = GetImageType(fileData, fileLength);
102 if (type == ImageType::WORNG_TYPE) {
103 APP_LOGE("input wrong type image!");
104 return false;
105 }
106 imageType = type == ImageType::JPEG ? JPEG_FORMAT : PNG_FORMAT;
107 return true;
108 }
109
GetImageFileInfo(const std::string & srcFile,std::unique_ptr<uint8_t[]> & fileContent,int64_t & fileLength)110 bool ImageCompress::GetImageFileInfo(const std::string &srcFile,
111 std::unique_ptr<uint8_t[]> &fileContent, int64_t &fileLength)
112 {
113 if (!IsPathValid(srcFile)) {
114 APP_LOGE("%{public}s is unavailable", srcFile.c_str());
115 return false;
116 }
117 FILE* file = fopen(srcFile.c_str(), "rb");
118 if (!file) {
119 APP_LOGE("ImageCompress: GetImageTypeByFile %{public}s is unavailable", srcFile.c_str());
120 return false;
121 }
122 if (fseek(file, 0L, SEEK_END) != 0) {
123 fclose(file);
124 return false;
125 }
126 fileLength = ftell(file);
127 rewind(file);
128 fileContent = std::make_unique<uint8_t[]>(fileLength);
129 if (!fread(fileContent.get(), sizeof(uint8_t), fileLength, file)) {
130 APP_LOGE("read file failed!");
131 fclose(file);
132 return false;
133 }
134 if (fclose(file) != 0) {
135 APP_LOGE("close file failed!");
136 return false;
137 }
138 return true;
139 }
140
CompressImageByContent(const std::unique_ptr<uint8_t[]> & fileData,size_t fileSize,std::unique_ptr<uint8_t[]> & compressedData,int64_t & compressedSize,std::string & imageType)141 bool ImageCompress::CompressImageByContent(const std::unique_ptr<uint8_t[]> &fileData, size_t fileSize,
142 std::unique_ptr<uint8_t[]> &compressedData, int64_t &compressedSize, std::string &imageType)
143 {
144 ImageType type = GetImageType(fileData, fileSize);
145 if (type == ImageType::WORNG_TYPE) {
146 APP_LOGE("input wrong image!");
147 return false;
148 }
149 imageType = type == ImageType::JPEG ? JPEG_FORMAT : WEBP_FORMAT;
150 uint32_t errorCode = 0;
151 Media::SourceOptions options;
152 std::unique_ptr<Media::ImageSource> imageSourcePtr =
153 Media::ImageSource::CreateImageSource(fileData.get(), fileSize, options, errorCode);
154 // do compress
155 Media::DecodeOptions decodeOptions;
156 uint32_t pixMapError = 0;
157 std::unique_ptr<Media::PixelMap> pixMap = imageSourcePtr->CreatePixelMap(decodeOptions, pixMapError);
158 if (pixMap == nullptr || pixMapError != Media::SUCCESS) {
159 APP_LOGE("CreatePixelMap failed!");
160 return false;
161 }
162 double ratio = CalculateRatio(fileSize, imageType);
163 if (ratio == FILE_SIZE_ERR) {
164 APP_LOGE("CalculateRatio failed: ratio is %{public}f", ratio);
165 return false;
166 }
167 APP_LOGD("ratio is %{public}f", ratio);
168 pixMap->scale(ratio, ratio);
169 Media::ImagePacker imagePacker;
170 Media::PackOption packOption;
171 packOption.format = imageType;
172 packOption.quality = QUALITY;
173 packOption.numberHint = MUNBER_ONE;
174 uint8_t *resultBuffer = reinterpret_cast<uint8_t *>(malloc(BUFFER_SIZE));
175 if (resultBuffer == nullptr) {
176 APP_LOGE("image packer malloc buffer failed.");
177 return 0;
178 }
179 imagePacker.StartPacking(resultBuffer, BUFFER_SIZE, packOption);
180 imagePacker.AddImage(*pixMap);
181 imagePacker.FinalizePacking(compressedSize);
182 compressedData = std::make_unique<uint8_t[]>(compressedSize);
183 APP_LOGD("compressedSize is %{public}d", static_cast<int32_t>(compressedSize));
184 uint8_t *result = compressedData.get();
185 if (memcpy_s(result, compressedSize, resultBuffer, compressedSize) != EOK) {
186 free(resultBuffer);
187 APP_LOGE("memcpy_s to compressedData failed!");
188 return false;
189 }
190 free(resultBuffer);
191 return true;
192 }
193 }
194 }