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 }
IsPathValid(const std::string & srcPath)54 bool ImageCompress::IsPathValid(const std::string &srcPath)
55 {
56 if (srcPath.find(BUNDLE_PATH) != std::string::npos) {
57 return access(srcPath.c_str(), R_OK) == 0;
58 } else {
59 return false;
60 }
61 }
62
IsImageNeedCompressBySize(size_t fileSize)63 bool ImageCompress::IsImageNeedCompressBySize(size_t fileSize)
64 {
65 return fileSize > FILE_MAX_SIZE;
66 }
67
CalculateRatio(size_t fileSize,const std::string & imageType)68 double ImageCompress::CalculateRatio(size_t fileSize, const std::string &imageType)
69 {
70 if (imageType == WEBP_FORMAT) {
71 return sqrt(static_cast<double>(WEBP_COMPRESS_SIZE) / fileSize);
72 }
73 return sqrt(static_cast<double>(FILE_COMPRESS_SIZE) / fileSize);
74 }
75
GetImageType(const std::unique_ptr<uint8_t[]> & fileData,size_t fileLength)76 ImageType ImageCompress::GetImageType(const std::unique_ptr<uint8_t[]> &fileData, size_t fileLength)
77 {
78 if (fileLength < FORMAT_LENGTH) {
79 return ImageType::WORNG_TYPE;
80 }
81 const uint8_t* data = fileData.get();
82 if (data[INDEX_ZERO] == JPEG_DATA_ZERO && data[INDEX_ONE] == JPEG_DATA_ONE
83 && data[INDEX_TWO] == JPEG_DATA_TWO) {
84 return ImageType::JPEG;
85 } else if (data[INDEX_ZERO] == PNG_DATA_ZERO && data[INDEX_ONE] == PNG_DATA_ONE &&
86 data[INDEX_TWO] == PNG_DATA_TWO && data[INDEX_THREE] == PNG_DATA_THREE) {
87 return ImageType::PNG;
88 } else {
89 return ImageType::WORNG_TYPE;
90 }
91 }
92
GetImageTypeString(const std::unique_ptr<uint8_t[]> & fileData,size_t fileLength,std::string & imageType)93 bool ImageCompress::GetImageTypeString(const std::unique_ptr<uint8_t[]> &fileData,
94 size_t fileLength, std::string &imageType)
95 {
96 ImageType type = GetImageType(fileData, fileLength);
97 if (type == ImageType::WORNG_TYPE) {
98 APP_LOGE("input wrong type image!");
99 return false;
100 }
101 imageType = type == ImageType::JPEG ? JPEG_FORMAT : PNG_FORMAT;
102 return true;
103 }
104
GetImageFileInfo(const std::string & srcFile,std::unique_ptr<uint8_t[]> & fileContent,int64_t & fileLength)105 bool ImageCompress::GetImageFileInfo(const std::string &srcFile,
106 std::unique_ptr<uint8_t[]> &fileContent, int64_t &fileLength)
107 {
108 if (!IsPathValid(srcFile)) {
109 APP_LOGE("%{public}s is unavailable", srcFile.c_str());
110 return false;
111 }
112 FILE* file = fopen(srcFile.c_str(), "rb");
113 if (!file) {
114 APP_LOGE("ImageCompress: GetImageTypeByFile %{public}s is unavailable", srcFile.c_str());
115 return false;
116 }
117 if (fseek(file, 0L, SEEK_END) != 0) {
118 fclose(file);
119 return false;
120 }
121 fileLength = ftell(file);
122 rewind(file);
123 fileContent = std::make_unique<uint8_t[]>(fileLength);
124 if (!fread(fileContent.get(), sizeof(uint8_t), fileLength, file)) {
125 APP_LOGE("read file failed!");
126 fclose(file);
127 return false;
128 }
129 if (fclose(file) != 0) {
130 APP_LOGE("close file failed!");
131 return false;
132 }
133 return true;
134 }
135
CompressImageByContent(const std::unique_ptr<uint8_t[]> & fileData,size_t fileSize,std::unique_ptr<uint8_t[]> & compressedData,int64_t & compressedSize,std::string & imageType)136 bool ImageCompress::CompressImageByContent(const std::unique_ptr<uint8_t[]> &fileData, size_t fileSize,
137 std::unique_ptr<uint8_t[]> &compressedData, int64_t &compressedSize, std::string &imageType)
138 {
139 ImageType type = GetImageType(fileData, fileSize);
140 if (type == ImageType::WORNG_TYPE) {
141 APP_LOGE("input wrong image!");
142 return false;
143 }
144 imageType = type == ImageType::JPEG ? JPEG_FORMAT : WEBP_FORMAT;
145 uint32_t errorCode = 0;
146 Media::SourceOptions options;
147 std::unique_ptr<Media::ImageSource> imageSourcePtr =
148 Media::ImageSource::CreateImageSource(fileData.get(), fileSize, options, errorCode);
149 // do compress
150 Media::DecodeOptions decodeOptions;
151 uint32_t pixMapError = 0;
152 std::unique_ptr<Media::PixelMap> pixMap = imageSourcePtr->CreatePixelMap(decodeOptions, pixMapError);
153 if (pixMap == nullptr || pixMapError != Media::SUCCESS) {
154 APP_LOGE("CreatePixelMap failed!");
155 return false;
156 }
157 double ratio = CalculateRatio(fileSize, imageType);
158 APP_LOGE("ratio is %{public}f", ratio);
159 pixMap->scale(ratio, ratio);
160 Media::ImagePacker imagePacker;
161 Media::PackOption packOption;
162 packOption.format = imageType;
163 packOption.quality = QUALITY;
164 packOption.numberHint = MUNBER_ONE;
165 uint8_t *resultBuffer = reinterpret_cast<uint8_t *>(malloc(BUFFER_SIZE));
166 if (resultBuffer == nullptr) {
167 APP_LOGE("image packer malloc buffer failed.");
168 return 0;
169 }
170 imagePacker.StartPacking(resultBuffer, BUFFER_SIZE, packOption);
171 imagePacker.AddImage(*pixMap);
172 imagePacker.FinalizePacking(compressedSize);
173 compressedData = std::make_unique<uint8_t[]>(compressedSize);
174 APP_LOGD("compressedSize is %{public}d", static_cast<int32_t>(compressedSize));
175 uint8_t *result = compressedData.get();
176 if (memcpy_s(result, compressedSize, resultBuffer, compressedSize) != EOK) {
177 free(resultBuffer);
178 APP_LOGE("memcpy_s to compressedData failed!");
179 return false;
180 }
181 free(resultBuffer);
182 return true;
183 }
184 }
185 }