• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
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 
LoadFromMemory(jpeg_decompress_struct & cinfo,uint32_t loadFlags,Info & info)97     static LibBaseImagePtr LoadFromMemory(jpeg_decompress_struct &cinfo, uint32_t loadFlags, Info &info)
98     {
99         LibBaseImagePtr imageBytes = nullptr;
100 
101         HandleJPEGColorType(cinfo, loadFlags, info);
102 
103         size_t imgSize = cinfo.output_width * cinfo.output_height * static_cast<uint32_t>(cinfo.output_components);
104         if (imgSize < 1 || imgSize >= IMG_SIZE_LIMIT_2GB) {
105             CORE_LOG_E("imgSize more than limit!");
106             return imageBytes;
107         }
108 
109         int row_stride = static_cast<int>(cinfo.output_width) * cinfo.output_components;
110         int pos = 0;
111         bool needVerticalFlip = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT) != 0;
112         if (info.is16bpc) {
113 #ifdef LIBJPEG_SUPPORT_12_BIT
114             J12SAMPARRAY rowPointers12 = (J12SAMPARRAY)(*cinfo.mem->alloc_sarray)(
115                 (j_common_ptr)&cinfo, JPOOL_IMAGE, static_cast<uint32_t>(row_stride), 1);
116             uint16_t *buff = (uint16_t *)malloc(imgSize * sizeof(uint16_t));
117             while (cinfo.output_scanline < cinfo.output_height) {
118                 jpeg12_read_scanlines(&cinfo, rowPointers12, 1);
119                 HandleLittleEndian(rowPointers12, row_stride);
120                 if (needVerticalFlip) {
121                     VerticalFlipRow(
122                         rowPointers12[0], cinfo.output_width, static_cast<uint32_t>(cinfo.output_components));
123                 }
124                 for (int k = 0; k < row_stride; k++) {
125                     buff[pos++] = rowPointers12[0][k];
126                 }
127             }
128 #else
129             uint16_t *buff = nullptr;
130             CORE_LOG_E("libjpeg do not support 12/16 bit in current version");
131 #endif
132             imageBytes = {buff, FreeLibBaseImageBytes};
133         } else {
134             JSAMPARRAY rowPointers8 =
135                 (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, static_cast<uint32_t>(row_stride), 1);
136             uint8_t *buff = (uint8_t *)malloc(imgSize * sizeof(uint8_t));
137             while (cinfo.output_scanline < cinfo.output_height) {
138                 jpeg_read_scanlines(&cinfo, rowPointers8, 1);
139                 if (needVerticalFlip) {
140                     VerticalFlipRow(
141                         rowPointers8[0], cinfo.output_width, static_cast<uint32_t>(cinfo.output_components));
142                 }
143                 for (int k = 0; k < row_stride; k++) {
144                     buff[pos++] = rowPointers8[0][k];
145                 }
146             }
147             imageBytes = {buff, FreeLibBaseImageBytes};
148         }
149         return imageBytes;
150     }
151 
152     // Actual jpeg loading implementation.
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)153     static ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags)
154     {
155         CORE_LOG_D("ImageLoaderManager Load jpeg start");
156         if (imageFileBytes.empty()) {
157             return ImageLoaderManager::ResultFailure("Input data must not be null.");
158         }
159 
160         struct jpeg_decompress_struct cinfo;
161         struct MyErrorMgr jerr;
162 
163         cinfo.err = jpeg_std_error(&jerr.pub);
164         jerr.pub.error_exit = MyErrorExit;
165         if (setjmp(jerr.setjmpBuffer)) {
166             jpeg_destroy_decompress(&cinfo);
167             return ImageLoaderManager::ResultFailure("jpeg_jmpbuf to fail");
168         }
169         jpeg_create_decompress(&cinfo);
170 
171         // Load the image info without decoding the image data
172         // (Just to check what the image format is so we can convert if necessary).
173         Info info;
174         jpeg_mem_src(&cinfo, imageFileBytes.data(), imageFileBytes.size());
175         jpeg_read_header(&cinfo, TRUE);
176         info.width = cinfo.image_width;
177         info.height = cinfo.image_height;
178         info.componentCount = static_cast<uint32_t>(cinfo.num_components);
179         info.is16bpc = cinfo.data_precision > 8;
180 
181         // Not supporting hdr images via libjpeg.
182         // LibJPEG cannot check hdr
183 
184         LibBaseImagePtr imageBytes = nullptr;
185         if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
186             imageBytes = LoadFromMemory(cinfo, loadFlags, info);
187             if (imageBytes == nullptr) {
188                 jpeg_finish_decompress(&cinfo);
189                 jpeg_destroy_decompress(&cinfo);
190                 return ImageLoaderManager::ResultFailure("jpeg LoadFromMemory fail");
191             }
192         } else {
193             imageBytes = {nullptr, FreeLibBaseImageBytes};
194         }
195         jpeg_finish_decompress(&cinfo);
196         jpeg_destroy_decompress(&cinfo);
197 
198         // Success. Populate the image info and image data object.
199         return CreateImage(
200             CORE_NS::move(imageBytes), info.width, info.height, info.componentCount, loadFlags, info.is16bpc);
201     }
202 
203 protected:
Destroy()204     void Destroy() override
205     {
206         delete this;
207     }
208 
209 private:
210     ImageDesc imageDesc_;
211     SubImageDesc imageBuffer_;
212 
213     LibBaseImagePtr imageBytes_;
214     size_t imageBytesLength_ = 0;
215 };
216 
217 class ImageLoaderLibJPEGImage final : public ImageLoaderManager::IImageLoader {
218 public:
219     // Inherited via ImageManager::IImageLoader
Load(IFile & file,uint32_t loadFlags) const220     ImageLoaderManager::LoadResult Load(IFile &file, uint32_t loadFlags) const override
221     {
222         const uint64_t byteLength = file.GetLength();
223         // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
224         if (byteLength > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
225             return ImageLoaderManager::ResultFailure("File too big to read.");
226         }
227 
228         // Read the file to a buffer.
229         unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(static_cast<size_t>(byteLength));
230         const uint64_t read = file.Read(buffer.get(), byteLength);
231         if (read != byteLength) {
232             return ImageLoaderManager::ResultFailure("Reading file failed.");
233         }
234 
235         return LibJPEGImage::Load(array_view<const uint8_t>(buffer.get(), static_cast<size_t>(byteLength)), loadFlags);
236     }
237 
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags) const238     ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) const override
239     {
240         // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
241         // Not writing a test for this :)
242         if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
243             return ImageLoaderManager::ResultFailure("Data too big to read.");
244         }
245 
246         return LibJPEGImage::Load(imageFileBytes, loadFlags);
247     }
248 
CanLoad(array_view<const uint8_t> imageFileBytes) const249     bool CanLoad(array_view<const uint8_t> imageFileBytes) const override
250     {
251         // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
252         // Not writing a test for this :)
253         size_t maxFileSize = static_cast<size_t>(std::numeric_limits<int>::max());
254         size_t jpegHeaderSize = 10;
255         if (imageFileBytes.size() > maxFileSize || imageFileBytes.size() < jpegHeaderSize) {
256             return false;
257         }
258 
259         // Check for JPEG / JFIF / Exif / ICC_PROFILE tag
260         bool isJFIF = (imageFileBytes[3] == 0xe0 && imageFileBytes[6] == 'J' && imageFileBytes[7] == 'F' &&
261                        imageFileBytes[8] == 'I' && imageFileBytes[9] == 'F');  // JFIF
262         bool isExif = (imageFileBytes[3] == 0xe1 && imageFileBytes[6] == 'E' && imageFileBytes[7] == 'x' &&
263                        imageFileBytes[8] == 'i' && imageFileBytes[9] == 'f');  // Exif
264         bool isICC = (imageFileBytes[3] == 0xe2 && imageFileBytes[6] == 'I' && imageFileBytes[7] == 'C' &&
265                       imageFileBytes[8] == 'C' && imageFileBytes[9] == '_');  // ICC_PROFILE
266         if (imageFileBytes[0] == 0xff && imageFileBytes[1] == 0xd8 && imageFileBytes[2] == 0xff &&
267             (isJFIF || isExif || isICC)) {
268             return true;
269         }
270 
271         return false;
272     }
273 
274     // No animation support
LoadAnimatedImage(IFile & file,uint32_t loadFlags)275     ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(IFile &file, uint32_t loadFlags) override
276     {
277         return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
278     }
279 
LoadAnimatedImage(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)280     ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(
281         array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) override
282     {
283         return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
284     }
285 
286 protected:
287     ~ImageLoaderLibJPEGImage() = default;
Destroy()288     void Destroy() override
289     {
290         delete this;
291     }
292 };
293 }  // namespace
CreateImageLoaderLibJPEGImage()294 IImageLoaderManager::IImageLoader::Ptr CreateImageLoaderLibJPEGImage()
295 {
296     return ImageLoaderManager::IImageLoader::Ptr{new ImageLoaderLibJPEGImage()};
297 }
298 CORE_END_NAMESPACE()
299