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)