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
16 #include "image_loader_jpg.h"
17
18 #include <csetjmp>
19 #include <functional>
20 #include <jpeglib.h>
21 #include <limits>
22 #include <memory>
23
24 #include <base/math/mathf.h>
25 #include <core/io/intf_file_manager.h>
26 #include <core/log.h>
27 #include <core/namespace.h>
28
29 using namespace BASE_NS;
30 using namespace CORE_NS;
31
32 namespace JPGPlugin {
33 namespace {
34 constexpr uint32_t MAX_IMAGE_EXTENT { 32767U };
35 constexpr int IMG_SIZE_LIMIT_2GB = std::numeric_limits<int>::max();
36
37 uint8_t g_sRgbPremultiplyLookup[256u * 256u] = { 0 };
38
InitializeSRGBTable()39 void InitializeSRGBTable()
40 {
41 // Generate lookup table to premultiply sRGB encoded image in linear space and reencoding it to sRGB
42 // Formulas from https://en.wikipedia.org/wiki/SRGB
43 for (uint32_t a = 0; a < 256u; a++) {
44 const float alpha = static_cast<float>(a) / 255.f;
45 for (uint32_t sRGB = 0; sRGB < 256u; sRGB++) {
46 float color = static_cast<float>(sRGB) / 255.f;
47 if (color <= 0.04045f) {
48 color *= (1.f / 12.92f);
49 } else {
50 color = pow((color + 0.055f) * (1.f / 1.055f), 2.4f);
51 }
52 float premultiplied = color * alpha;
53 if (premultiplied <= 0.0031308f) {
54 premultiplied *= 12.92f;
55 } else {
56 premultiplied = 1.055f * pow(premultiplied, 1.f / 2.4f) - 0.055f;
57 }
58 g_sRgbPremultiplyLookup[a * 256u + sRGB] = static_cast<uint8_t>(round(premultiplied * 255.f));
59 }
60 }
61 }
62
PremultiplyAlpha(uint8_t * imageBytes,uint32_t width,uint32_t height,uint32_t channelCount,uint32_t bytesPerChannel,bool linear)63 bool PremultiplyAlpha(
64 uint8_t* imageBytes, uint32_t width, uint32_t height, uint32_t channelCount, uint32_t bytesPerChannel, bool linear)
65 {
66 // Only need to process images with color and alpha data. I.e. RGBA or grayscale + alpha.
67 if (channelCount != 4u && channelCount != 2u) {
68 return true;
69 }
70
71 const uint32_t pixelCount = width * height;
72
73 if (bytesPerChannel == 1) {
74 if (linear) {
75 uint8_t* img = imageBytes;
76 for (uint32_t i = 0; i < pixelCount; i++) {
77 // We know the alpha value is always last.
78 uint32_t alpha = img[channelCount - 1];
79 for (uint32_t j = 0; j < channelCount - 1; j++) {
80 *img = static_cast<uint8_t>(*img * alpha / 0xff);
81 img++;
82 }
83 img++; // Skip over the alpha value.
84 }
85 } else {
86 if (g_sRgbPremultiplyLookup[256u * 256u - 1] == 0) {
87 InitializeSRGBTable();
88 }
89 uint8_t* img = imageBytes;
90 for (uint32_t i = 0; i < pixelCount; i++) {
91 uint8_t* p = &g_sRgbPremultiplyLookup[img[channelCount - 1] * 256u];
92 for (uint32_t j = 0; j < channelCount - 1; j++) {
93 *img = p[*img];
94 img++;
95 }
96 img++;
97 }
98 }
99 } else if (bytesPerChannel == 2u) {
100 // Same for 16 bits per channel images.
101 uint16_t* img = reinterpret_cast<uint16_t*>(imageBytes);
102 for (uint32_t i = 0; i < pixelCount; i++) {
103 uint32_t alpha = img[channelCount - 1];
104 for (uint32_t j = 0; j < channelCount - 1; j++) {
105 *img = static_cast<uint16_t>(*img * alpha / 0xffff);
106 img++;
107 }
108 img++;
109 }
110 } else {
111 CORE_LOG_E("Format not supported.");
112 return false;
113 }
114 return true;
115 }
116
ResultFailure(const string_view error)117 IImageLoaderManager::LoadResult ResultFailure(const string_view error)
118 {
119 IImageLoaderManager::LoadResult result {
120 false, // success
121 "", // error[128];
122 nullptr // image;
123 };
124
125 // Copy the error string
126 const auto count = Math::min(error.size(), sizeof(result.error) - 1);
127 error.copy(result.error, count);
128 result.error[count] = '\0';
129
130 return result;
131 }
132
ResultSuccess(IImageContainer::Ptr image)133 IImageLoaderManager::LoadResult ResultSuccess(IImageContainer::Ptr image)
134 {
135 return IImageLoaderManager::LoadResult {
136 true, // success
137 "", // error[128];
138 BASE_NS::move(image) // image;
139 };
140 }
141
ResultFailureAnimated(const string_view error)142 IImageLoaderManager::LoadAnimatedResult ResultFailureAnimated(const string_view error)
143 {
144 IImageLoaderManager::LoadAnimatedResult result {
145 false, // success
146 "", // error[128];
147 nullptr // image;
148 };
149
150 // Copy the error string
151 const auto count = Math::min(error.size(), sizeof(result.error) - 1);
152 error.copy(result.error, count);
153 result.error[count] = '\0';
154
155 return result;
156 }
157 } // namespace
158
159 class JPGImage final : public IImageContainer {
160 public:
JPGImage()161 JPGImage() : IImageContainer(), imageDesc_(), imageBuffer_() {}
162
163 ~JPGImage() override = default;
164
165 using Ptr = BASE_NS::unique_ptr<JPGImage, Deleter>;
166
GetImageDesc() const167 const ImageDesc& GetImageDesc() const override
168 {
169 return imageDesc_;
170 }
171
GetData() const172 array_view<const uint8_t> GetData() const override
173 {
174 return array_view<const uint8_t>(imageBytes_.get(), imageBytesLength_);
175 }
176
GetBufferImageCopies() const177 array_view<const SubImageDesc> GetBufferImageCopies() const override
178 {
179 return array_view<const SubImageDesc>(&imageBuffer_, 1);
180 }
181
ResolveFormat(uint32_t loadFlags,uint32_t componentCount,bool is16bpc)182 static constexpr Format ResolveFormat(uint32_t loadFlags, uint32_t componentCount, bool is16bpc) noexcept
183 {
184 Format format {};
185 const bool forceLinear = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT) != 0;
186
187 switch (componentCount) {
188 case 1u:
189 format = is16bpc ? Format::BASE_FORMAT_R16_UNORM
190 : (forceLinear ? Format::BASE_FORMAT_R8_UNORM : Format::BASE_FORMAT_R8_SRGB);
191 break;
192 case 2u:
193 format = is16bpc ? Format::BASE_FORMAT_R16G16_UNORM
194 : (forceLinear ? Format::BASE_FORMAT_R8G8_UNORM : Format::BASE_FORMAT_R8G8_SRGB);
195 break;
196 case 3u:
197 format = is16bpc ? Format::BASE_FORMAT_R16G16B16_UNORM
198 : (forceLinear ? Format::BASE_FORMAT_R8G8B8_UNORM : Format::BASE_FORMAT_R8G8B8_SRGB);
199 break;
200 case 4u:
201 format = is16bpc
202 ? Format::BASE_FORMAT_R16G16B16A16_UNORM
203 : (forceLinear ? Format::BASE_FORMAT_R8G8B8A8_UNORM : Format::BASE_FORMAT_R8G8B8A8_SRGB);
204 break;
205 default:
206 format = Format::BASE_FORMAT_UNDEFINED;
207 break;
208 }
209
210 return format;
211 }
212
ResolveImageDesc(Format format,uint32_t imageWidth,uint32_t imageHeight,uint32_t bitsPerPixel,bool generateMips,bool isPremultiplied)213 constexpr static ImageDesc ResolveImageDesc(Format format, uint32_t imageWidth, uint32_t imageHeight,
214 uint32_t bitsPerPixel, bool generateMips, bool isPremultiplied) noexcept
215 {
216 uint32_t mipCount = 1;
217
218 // 1D images not supported with WebP loader
219 constexpr ImageType imageType = ImageType::TYPE_2D;
220 constexpr ImageViewType imageViewType = ImageViewType::VIEW_TYPE_2D;
221
222 uint32_t imageFlags = isPremultiplied ? ImageFlags::FLAGS_PREMULTIPLIED_ALPHA_BIT : 0;
223
224 if (generateMips) {
225 imageFlags |= ImageFlags::FLAGS_REQUESTING_MIPMAPS_BIT;
226
227 uint32_t mipsize = (imageWidth > imageHeight) ? imageWidth : imageHeight;
228 mipCount = 0;
229 while (mipsize > 0) {
230 mipCount++;
231 mipsize >>= 1;
232 }
233 }
234
235 return ImageDesc {
236 imageFlags, // imageFlags
237 1, // blockPixelWidth
238 1, // blockPixelHeight
239 1, // blockPixelDepth
240 bitsPerPixel, // bitsPerBlock
241 imageType, // imageType
242 imageViewType, // imageViewType
243 format, // format
244 imageWidth, // width
245 imageHeight, // height
246 1, // depth
247 mipCount, // mipCount
248 1, // layerCount
249 };
250 }
251
CreateImage(BASE_NS::unique_ptr<uint8_t[]> imageBytes,uint32_t imageWidth,uint32_t imageHeight,uint32_t componentCount,uint32_t loadFlags,bool is16bpc)252 static IImageLoaderManager::LoadResult CreateImage(BASE_NS::unique_ptr<uint8_t[]> imageBytes, uint32_t imageWidth,
253 uint32_t imageHeight, uint32_t componentCount, uint32_t loadFlags, bool is16bpc) noexcept
254 {
255 auto image = JPGImage::Ptr(new JPGImage);
256 if (!image) {
257 return ResultFailure("Loading image failed.");
258 }
259 const uint32_t bytesPerComponent = is16bpc ? 2u : 1u;
260
261 // Premultiply alpha if requested.
262 bool isPremultiplied = false;
263 if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
264 if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_PREMULTIPLY_ALPHA) != 0) {
265 const bool forceLinear = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT) != 0;
266 isPremultiplied = PremultiplyAlpha(static_cast<uint8_t*>(imageBytes.get()), imageWidth, imageHeight,
267 componentCount, bytesPerComponent, forceLinear);
268 }
269 }
270
271 const Format format = ResolveFormat(loadFlags, componentCount, is16bpc);
272 const uint32_t bitsPerPixel = bytesPerComponent * componentCount * 8u;
273 const bool generateMips = (loadFlags & IImageLoaderManager::IMAGE_LOADER_GENERATE_MIPS) != 0;
274 image->imageDesc_ =
275 ResolveImageDesc(format, imageWidth, imageHeight, bitsPerPixel, generateMips, isPremultiplied);
276 image->imageBytes_ = BASE_NS::move(imageBytes);
277 image->imageBytesLength_ = imageWidth * imageHeight * componentCount * bytesPerComponent;
278
279 image->imageBuffer_ = SubImageDesc {
280 0, // uint32_t bufferOffset
281 imageWidth, // uint32_t bufferRowLength
282 imageHeight, // uint32_t bufferImageHeight
283
284 0, // uint32_t mipLevel
285 1, // uint32_t layerCount
286
287 static_cast<uint32_t>(imageWidth),
288 static_cast<uint32_t>(imageHeight),
289 1,
290 };
291
292 return ResultSuccess(BASE_NS::move(image));
293 }
294
ErrorExit(j_common_ptr cinfo)295 static void ErrorExit(j_common_ptr cinfo) noexcept
296 {
297 if (!cinfo) {
298 CORE_LOG_I("No info");
299 return;
300 }
301 if (cinfo->err && cinfo->err->output_message) {
302 (*cinfo->err->output_message)(cinfo);
303 }
304 jmp_buf* jmpBuffer = static_cast<jmp_buf*>(cinfo->client_data);
305 longjmp(*jmpBuffer, 1);
306 }
307
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)308 static IImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) noexcept
309 {
310 if (imageFileBytes.empty()) {
311 return ResultFailure("Input data must not be null.");
312 }
313 jmp_buf jmpBuffer;
314 struct jpeg_decompress_struct cinfo;
315 struct jpeg_error_mgr jerr;
316 // initialize the error handler and override the exit handler which calls exit(). instead we'll call longjmp to
317 // return here for cleanup.
318 cinfo.err = jpeg_std_error(&jerr);
319 jerr.error_exit = ErrorExit;
320 cinfo.client_data = &jmpBuffer;
321
322 BASE_NS::unique_ptr<uint8_t[]> image;
323 BASE_NS::unique_ptr<uint8_t* []> rows;
324 if (setjmp(jmpBuffer)) {
325 jpeg_destroy_decompress(&cinfo);
326 rows.reset();
327 image.reset();
328 return ResultFailure("Failed to load.");
329 }
330
331 // initialize the decompress struct
332 jpeg_create_decompress(&cinfo);
333 jpeg_mem_src(&cinfo, imageFileBytes.data(), static_cast<unsigned long>(imageFileBytes.size()));
334
335 // first ask for the image information.
336 auto ret = jpeg_read_header(&cinfo, TRUE);
337 if (ret != JPEG_HEADER_OK) {
338 jpeg_destroy_decompress(&cinfo);
339 return ResultFailure("Failed to load.");
340 }
341
342 auto width = cinfo.image_width;
343 auto height = cinfo.image_height;
344 auto channels = static_cast<uint32_t>(cinfo.num_components);
345 auto is16bpc = cinfo.data_precision > 8;
346
347 if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) !=
348 IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) {
349 // ask libjpg to do possible conversions according to loadFlags.
350 if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT) ==
351 IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT) {
352 cinfo.out_color_space = JCS_GRAYSCALE;
353 } else if (cinfo.num_components == 2 || cinfo.num_components == 3) { // 2: index 3: index
354 cinfo.out_color_space = JCS_EXT_RGBA;
355 }
356 if (!jpeg_start_decompress(&cinfo)) {
357 jpeg_abort_decompress(&cinfo);
358 }
359
360 // update the image information in case some conversions will be done.
361 width = cinfo.output_width;
362 height = cinfo.output_height;
363 channels = static_cast<uint32_t>(cinfo.output_components);
364 is16bpc = cinfo.data_precision > 8; // 8: index
365 if (channels <= 0 || channels > 4) { // 0: invalid channel num, 4: RGBA
366 jpeg_destroy_decompress(&cinfo);
367 return ResultFailure("Invalid number of color channels.");
368 }
369
370 const size_t imageSize = width * height * channels;
371 if ((width > MAX_IMAGE_EXTENT) || (height > MAX_IMAGE_EXTENT) || (imageSize > IMG_SIZE_LIMIT_2GB)) {
372 jpeg_destroy_decompress(&cinfo);
373 return ResultFailure("Image too large.");
374 }
375 // allocate space for the whole image and and array of row pointers. libjpg writes data to each row pointer.
376 // alternative would be to use a different api which writes only one row and feed it the correct address
377 // every time.
378 image = BASE_NS::make_unique<uint8_t[]>(imageSize);
379 rows = BASE_NS::make_unique<uint8_t* []>(height);
380 // fill rows depending on should there be a vertical flip or not.
381 auto row = rows.get();
382 if (loadFlags & IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT) {
383 for (auto i = 0U; i < height; ++i) {
384 *row++ = image.get() + ((height - 1) - i) * (width * channels);
385 }
386 } else {
387 for (auto i = 0U; i < height; ++i) {
388 *row++ = image.get() + i * (width * channels);
389 }
390 }
391 while (cinfo.output_scanline < cinfo.output_height) {
392 jpeg_read_scanlines(
393 &cinfo, rows.get() + cinfo.output_scanline, cinfo.output_height - cinfo.output_scanline);
394 }
395 jpeg_finish_decompress(&cinfo);
396 }
397 jpeg_destroy_decompress(&cinfo);
398
399 // Success. Populate the image info and image data object.
400 return CreateImage(BASE_NS::move(image), width, height, channels, loadFlags, is16bpc);
401 }
402
403 protected:
Destroy()404 void Destroy() override
405 {
406 delete this;
407 }
408
409 private:
410 ImageDesc imageDesc_;
411 SubImageDesc imageBuffer_;
412
413 BASE_NS::unique_ptr<uint8_t[]> imageBytes_;
414 size_t imageBytesLength_ = 0;
415 };
416
417 class ImageLoaderJPG final : public IImageLoaderManager::IImageLoader {
418 public:
419 // Inherited via ImageManager::IImageLoader
Load(IFile & file,uint32_t loadFlags) const420 IImageLoaderManager::LoadResult Load(IFile& file, uint32_t loadFlags) const override
421 {
422 const uint64_t byteLength = file.GetLength();
423 if (byteLength > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
424 return ResultFailure("File too big.");
425 }
426
427 // Read the file to a buffer.
428 unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(static_cast<size_t>(byteLength));
429 const uint64_t read = file.Read(buffer.get(), byteLength);
430 if (read != byteLength) {
431 return ResultFailure("Reading file failed.");
432 }
433
434 return JPGImage::Load(array_view<const uint8_t>(buffer.get(), static_cast<size_t>(byteLength)), loadFlags);
435 }
436
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags) const437 IImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) const override
438 {
439 if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
440 return ResultFailure("File too big.");
441 }
442 return JPGImage::Load(imageFileBytes, loadFlags);
443 }
444
CanLoad(array_view<const uint8_t> imageFileBytes) const445 bool CanLoad(array_view<const uint8_t> imageFileBytes) const override
446 {
447 // Check for JPEG / JFIF / Exif / ICC_PROFILE tag
448 // 10:size
449 if ((imageFileBytes.size() >= 10) && imageFileBytes[0] == 0xff && imageFileBytes[1] == 0xd8 &&
450 imageFileBytes[2] == 0xff && // 2 : idx
451 ((imageFileBytes[3] == 0xe0 && imageFileBytes[6] == 'J' && imageFileBytes[7] == 'F' && // 3,6,7: idx
452 imageFileBytes[8] == 'I' && imageFileBytes[9] == 'F') || // 8,9 :idx JFIF
453 (imageFileBytes[3] == 0xe1 && imageFileBytes[6] == 'E' && imageFileBytes[7] == 'x' && // 3,6,7 : idx
454 imageFileBytes[8] == 'i' && imageFileBytes[9] == 'f') || // 8,9 : idx Exif
455 (imageFileBytes[3] == 0xe2 && imageFileBytes[6] == 'I' && imageFileBytes[7] == 'C' && // 3,6,7 : idx
456 imageFileBytes[8] == 'C' && imageFileBytes[9] == '_'))) { // 8,9 : idx ICC_PROFILE
457 return true;
458 }
459
460 return false;
461 }
462
LoadAnimatedImage(IFile & file,uint32_t loadFlags)463 IImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(IFile& file, uint32_t loadFlags) override
464 {
465 return ResultFailureAnimated("");
466 }
467
LoadAnimatedImage(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)468 IImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(
469 array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) override
470 {
471 return ResultFailureAnimated("");
472 }
473
GetSupportedTypes() const474 vector<IImageLoaderManager::ImageType> GetSupportedTypes() const override
475 {
476 return vector<IImageLoaderManager::ImageType>(std::begin(IMAGE_TYPES), std::end(IMAGE_TYPES));
477 }
478
479 protected:
480 ~ImageLoaderJPG() = default;
Destroy()481 void Destroy() override
482 {
483 delete this;
484 }
485 };
486
CreateImageLoaderJPG(CORE_NS::PluginToken)487 IImageLoaderManager::IImageLoader::Ptr CreateImageLoaderJPG(CORE_NS::PluginToken)
488 {
489 return IImageLoaderManager::IImageLoader::Ptr { new ImageLoaderJPG() };
490 }
491
492 } // namespace JPGPlugin
493