1 /*
2 * Copyright (C) 2023-2024 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 "image/loaders/image_loader_libjpeg.h"
17 #if defined(USE_LIB_PNG_JPEG) && (USE_LIB_PNG_JPEG == 1)
18 #include <csetjmp>
19 #include "jpeglib.h"
20 #include "jerror.h"
21
22 #include "image/loaders/image_loader_common.h"
23
24 #define LIBJPEG_SUPPORT_8_BIT
25
26 CORE_BEGIN_NAMESPACE()
27 namespace {
28 using BASE_NS::array_view;
29 using BASE_NS::Format;
30 using BASE_NS::make_unique;
31 using BASE_NS::move;
32 using BASE_NS::string;
33 using BASE_NS::string_view;
34 using BASE_NS::unique_ptr;
35
36 constexpr size_t IMG_SIZE_LIMIT_2GB = static_cast<size_t>(std::numeric_limits<int>::max());
37
38 struct MyErrorMgr {
39 struct jpeg_error_mgr pub;
40 jmp_buf setjmpBuffer;
41 };
42
MyErrorExit(j_common_ptr cinfo)43 void MyErrorExit(j_common_ptr cinfo)
44 {
45 if (cinfo == nullptr) {
46 CORE_LOG_E("cinfo is nullptr in MyErrorExit");
47 return;
48 }
49 MyErrorMgr *myerr = reinterpret_cast<MyErrorMgr *>(cinfo->err);
50 if (cinfo->err->output_message == nullptr) {
51 CORE_LOG_E("cinfo err output_message is nullptr in MyErrorExit");
52 return;
53 }
54 (*cinfo->err->output_message)(cinfo);
55 longjmp(myerr->setjmpBuffer, 1);
56 }
57
58 #ifdef LIBJPEG_SUPPORT_12_BIT
HandleLittleEndian(J12SAMPARRAY rowPointers12,int len)59 void HandleLittleEndian(J12SAMPARRAY rowPointers12, int len)
60 {
61 int littleEndian = 1;
62 char *ptr = reinterpret_cast<char *>(&littleEndian);
63 if (*ptr == 1) {
64 /* Swap MSB and LSB in each sample */
65 for (int col = 0; col < len; col++) {
66 rowPointers12[0][col] = ((rowPointers12[0][col] & 0xFF) << 8) | ((rowPointers12[0][col] >> 8) & 0xFF);
67 }
68 }
69 }
70 #endif
71
HandleJPEGColorType(jpeg_decompress_struct & cinfo,uint32_t loadFlags,LibBaseImage::Info & info)72 void HandleJPEGColorType(jpeg_decompress_struct &cinfo, uint32_t loadFlags, LibBaseImage::Info &info)
73 {
74 // Convert 3 channels to 4 because 3 channel textures are not always supported.
75 // Also convert 2 channel (grayscale + alpha) to 4 because r + a in not supported.
76 if (cinfo.num_components == 2 || cinfo.num_components == 3) {
77 cinfo.out_color_space = JCS_EXT_RGBA;
78 }
79 bool forceGray = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT) != 0;
80 if (forceGray) {
81 cinfo.out_color_space = JCS_GRAYSCALE;
82 }
83
84 jpeg_start_decompress(&cinfo);
85
86 info.width = cinfo.output_width;
87 info.height = cinfo.output_height;
88 info.componentCount = static_cast<uint32_t>(cinfo.output_components);
89 info.is16bpc = cinfo.data_precision > 8;
90 }
91
92 class LibJPEGImage final : public LibBaseImage {
93 public:
LibJPEGImage()94 LibJPEGImage() : LibBaseImage()
95 {}
96
ReadBuffer16Bit(jpeg_decompress_struct & cinfo,uint16_t * buff,int row_stride,bool needVerticalFlip)97 static void ReadBuffer16Bit(jpeg_decompress_struct &cinfo, uint16_t *buff, int row_stride, bool needVerticalFlip)
98 {
99 #ifdef LIBJPEG_SUPPORT_12_BIT
100 J12SAMPARRAY rowPointers12 = static_cast<J12SAMPARRAY>((*cinfo.mem->alloc_sarray)(
101 reinterpret_cast<j_common_ptr>(&cinfo), JPOOL_IMAGE, static_cast<uint32_t>(row_stride), 1));
102 if (rowPointers12 == nullptr) {
103 CORE_LOG_E("rowPointers12 is null");
104 return;
105 }
106 int pos = 0;
107 while (cinfo.output_scanline < cinfo.output_height) {
108 jpeg12_read_scanlines(&cinfo, rowPointers12, 1);
109 HandleLittleEndian(rowPointers12, row_stride);
110 if (needVerticalFlip) {
111 VerticalFlipRow(rowPointers12[0], cinfo.output_width, static_cast<uint32_t>(cinfo.output_components));
112 }
113 for (int k = 0; k < row_stride; k++) {
114 buff[pos++] = rowPointers12[0][k];
115 }
116 }
117 #endif
118 }
ReadBuffer8Bit(jpeg_decompress_struct & cinfo,uint8_t * buff,int row_stride,bool needVerticalFlip)119 static void ReadBuffer8Bit(jpeg_decompress_struct &cinfo, uint8_t *buff, int row_stride, bool needVerticalFlip)
120 {
121 JSAMPARRAY rowPointers8 = (*cinfo.mem->alloc_sarray)(
122 reinterpret_cast<j_common_ptr>(&cinfo), JPOOL_IMAGE, static_cast<uint32_t>(row_stride), 1);
123 if (rowPointers8 == nullptr) {
124 CORE_LOG_E("rowPointers8 is null");
125 return;
126 }
127 int pos = 0;
128 while (cinfo.output_scanline < cinfo.output_height) {
129 jpeg_read_scanlines(&cinfo, rowPointers8, 1);
130 if (needVerticalFlip) {
131 VerticalFlipRow(rowPointers8[0], cinfo.output_width, static_cast<uint32_t>(cinfo.output_components));
132 }
133 for (int k = 0; k < row_stride; k++) {
134 buff[pos++] = rowPointers8[0][k];
135 }
136 }
137 }
138
LoadFromMemory(jpeg_decompress_struct & cinfo,uint32_t loadFlags,Info & info)139 static LibBaseImagePtr LoadFromMemory(jpeg_decompress_struct &cinfo, uint32_t loadFlags, Info &info)
140 {
141 LibBaseImagePtr imageBytes = nullptr;
142
143 HandleJPEGColorType(cinfo, loadFlags, info);
144
145 size_t imgSize = cinfo.output_width * cinfo.output_height * static_cast<uint32_t>(cinfo.output_components);
146 if (imgSize < 1 || imgSize >= IMG_SIZE_LIMIT_2GB) {
147 CORE_LOG_E("imgSize more than limit!");
148 return imageBytes;
149 }
150
151 int row_stride = static_cast<int>(cinfo.output_width) * cinfo.output_components;
152 bool needVerticalFlip = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT) != 0;
153 if (info.is16bpc) {
154 #ifdef LIBJPEG_SUPPORT_12_BIT
155 uint16_t *buff = static_cast<uint16_t *>(malloc(imgSize * sizeof(uint16_t)));
156 if (buff == nullptr) {
157 CORE_LOG_E("malloc fail return null");
158 return imageBytes;
159 }
160 ReadBuffer16Bit(cinfo, buff, row_stride, needVerticalFlip);
161 #else
162 uint16_t *buff = nullptr;
163 CORE_LOG_E("libjpeg do not support 12/16 bit in current version");
164 #endif
165 imageBytes = {buff, FreeLibBaseImageBytes};
166 } else {
167 uint8_t *buff = static_cast<uint8_t *>(malloc(imgSize * sizeof(uint8_t)));
168 if (buff == nullptr) {
169 CORE_LOG_E("malloc fail return null");
170 return imageBytes;
171 }
172 ReadBuffer8Bit(cinfo, buff, row_stride, needVerticalFlip);
173 imageBytes = {buff, FreeLibBaseImageBytes};
174 }
175 return imageBytes;
176 }
177
178 // Actual jpeg loading implementation.
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)179 static ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags)
180 {
181 CORE_LOG_D("ImageLoaderManager Load jpeg start");
182 if (imageFileBytes.empty()) {
183 return ImageLoaderManager::ResultFailure("Input data must not be null.");
184 }
185
186 struct jpeg_decompress_struct cinfo;
187 struct MyErrorMgr jerr;
188
189 cinfo.err = jpeg_std_error(&jerr.pub);
190 jerr.pub.error_exit = MyErrorExit;
191 if (setjmp(jerr.setjmpBuffer)) {
192 jpeg_destroy_decompress(&cinfo);
193 return ImageLoaderManager::ResultFailure("jpeg_jmpbuf to fail");
194 }
195 jpeg_create_decompress(&cinfo);
196
197 // Load the image info without decoding the image data
198 // (Just to check what the image format is so we can convert if necessary).
199 Info info;
200 jpeg_mem_src(&cinfo, imageFileBytes.data(), imageFileBytes.size());
201 jpeg_read_header(&cinfo, TRUE);
202 info.width = cinfo.image_width;
203 info.height = cinfo.image_height;
204 info.componentCount = static_cast<uint32_t>(cinfo.num_components);
205 info.is16bpc = cinfo.data_precision > 8;
206
207 // Not supporting hdr images via libjpeg.
208 // LibJPEG cannot check hdr
209
210 LibBaseImagePtr imageBytes = nullptr;
211 if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
212 imageBytes = LoadFromMemory(cinfo, loadFlags, info);
213 if (imageBytes == nullptr) {
214 jpeg_finish_decompress(&cinfo);
215 jpeg_destroy_decompress(&cinfo);
216 return ImageLoaderManager::ResultFailure("jpeg LoadFromMemory fail");
217 }
218 } else {
219 imageBytes = {nullptr, FreeLibBaseImageBytes};
220 }
221 jpeg_finish_decompress(&cinfo);
222 jpeg_destroy_decompress(&cinfo);
223
224 // Success. Populate the image info and image data object.
225 return CreateImage(
226 CORE_NS::move(imageBytes), info.width, info.height, info.componentCount, loadFlags, info.is16bpc);
227 }
228
229 protected:
Destroy()230 void Destroy() override
231 {
232 delete this;
233 }
234
235 private:
236 ImageDesc imageDesc_;
237 SubImageDesc imageBuffer_;
238
239 LibBaseImagePtr imageBytes_;
240 size_t imageBytesLength_ = 0;
241 };
242
243 class ImageLoaderLibJPEGImage final : public ImageLoaderManager::IImageLoader {
244 public:
245 // Inherited via ImageManager::IImageLoader
Load(IFile & file,uint32_t loadFlags) const246 ImageLoaderManager::LoadResult Load(IFile &file, uint32_t loadFlags) const override
247 {
248 const uint64_t byteLength = file.GetLength();
249 // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
250 if (byteLength > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
251 return ImageLoaderManager::ResultFailure("File too big to read.");
252 }
253
254 // Read the file to a buffer.
255 unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(static_cast<size_t>(byteLength));
256 const uint64_t read = file.Read(buffer.get(), byteLength);
257 if (read != byteLength) {
258 return ImageLoaderManager::ResultFailure("Reading file failed.");
259 }
260
261 return LibJPEGImage::Load(array_view<const uint8_t>(buffer.get(), static_cast<size_t>(byteLength)), loadFlags);
262 }
263
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags) const264 ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) const override
265 {
266 // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
267 // Not writing a test for this :)
268 if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
269 return ImageLoaderManager::ResultFailure("Data too big to read.");
270 }
271
272 return LibJPEGImage::Load(imageFileBytes, loadFlags);
273 }
274
CanLoad(array_view<const uint8_t> imageFileBytes) const275 bool CanLoad(array_view<const uint8_t> imageFileBytes) const override
276 {
277 // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
278 // Not writing a test for this :)
279 size_t maxFileSize = static_cast<size_t>(std::numeric_limits<int>::max());
280 size_t jpegHeaderSize = 10;
281 if (imageFileBytes.size() > maxFileSize || imageFileBytes.size() < jpegHeaderSize) {
282 return false;
283 }
284
285 // Check for JPEG / JFIF / Exif / ICC_PROFILE tag
286 bool isJFIF = (imageFileBytes[3] == 0xe0 && imageFileBytes[6] == 'J' && imageFileBytes[7] == 'F' &&
287 imageFileBytes[8] == 'I' && imageFileBytes[9] == 'F'); // JFIF
288 bool isExif = (imageFileBytes[3] == 0xe1 && imageFileBytes[6] == 'E' && imageFileBytes[7] == 'x' &&
289 imageFileBytes[8] == 'i' && imageFileBytes[9] == 'f'); // Exif
290 bool isICC = (imageFileBytes[3] == 0xe2 && imageFileBytes[6] == 'I' && imageFileBytes[7] == 'C' &&
291 imageFileBytes[8] == 'C' && imageFileBytes[9] == '_'); // ICC_PROFILE
292 if (imageFileBytes[0] == 0xff && imageFileBytes[1] == 0xd8 && imageFileBytes[2] == 0xff &&
293 (isJFIF || isExif || isICC)) {
294 return true;
295 }
296
297 return false;
298 }
299
300 // No animation support
LoadAnimatedImage(IFile & file,uint32_t loadFlags)301 ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(IFile &file, uint32_t loadFlags) override
302 {
303 return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
304 }
305
LoadAnimatedImage(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)306 ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(
307 array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) override
308 {
309 return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
310 }
311
GetSupportedTypes() const312 BASE_NS::vector<IImageLoaderManager::ImageType> GetSupportedTypes() const override
313 {
314 return BASE_NS::vector<IImageLoaderManager::ImageType>(std::begin(JPEG_IMAGE_TYPES),
315 std::end(JPEG_IMAGE_TYPES));
316 }
317
318 protected:
319 ~ImageLoaderLibJPEGImage() = default;
Destroy()320 void Destroy() override
321 {
322 delete this;
323 }
324 };
325 } // namespace
CreateImageLoaderLibJPEGImage(PluginToken)326 IImageLoaderManager::IImageLoader::Ptr CreateImageLoaderLibJPEGImage(PluginToken)
327 {
328 return ImageLoaderManager::IImageLoader::Ptr{new ImageLoaderLibJPEGImage()};
329 }
330 CORE_END_NAMESPACE()
331 #endif // defined(USE_LIB_PNG_JPEG) && (USE_LIB_PNG_JPEG == 1)