• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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