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