• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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_png.h"
17 
18 #include <functional>
19 #include <limits>
20 #include <memory>
21 
22 #include <base/math/mathf.h>
23 #include <core/io/intf_file_manager.h>
24 #include <core/log.h>
25 #include <core/namespace.h>
26 
27 #include "png.h"
28 
29 using namespace BASE_NS;
30 using namespace CORE_NS;
31 
32 namespace PNGPlugin {
33 namespace {
34 constexpr uint32_t MAX_IMAGE_EXTENT { 32767U };
35 constexpr int IMG_SIZE_LIMIT_2GB = std::numeric_limits<int>::max();
36 uint8_t g_sRgbPremultiplyLookup[256u * 256u] = { 0 };
37 
InitializeSRGBTable()38 void InitializeSRGBTable()
39 {
40     // Generate lookup table to premultiply sRGB encoded image in linear space and reencoding it to sRGB
41     // Formulas from https://en.wikipedia.org/wiki/SRGB
42     for (uint32_t a = 0; a < 256u; a++) {
43         const float alpha = static_cast<float>(a) / 255.f;
44         for (uint32_t sRGB = 0; sRGB < 256u; sRGB++) {
45             float color = static_cast<float>(sRGB) / 255.f;
46             if (color <= 0.04045f) {
47                 color *= (1.f / 12.92f);
48             } else {
49                 color = pow((color + 0.055f) * (1.f / 1.055f), 2.4f);
50             }
51             float premultiplied = color * alpha;
52             if (premultiplied <= 0.0031308f) {
53                 premultiplied *= 12.92f;
54             } else {
55                 premultiplied = 1.055f * pow(premultiplied, 1.f / 2.4f) - 0.055f;
56             }
57             g_sRgbPremultiplyLookup[a * 256u + sRGB] = static_cast<uint8_t>(round(premultiplied * 255.f));
58         }
59     }
60 }
61 
PremultiplyAlpha(uint8_t * imageBytes,uint32_t width,uint32_t height,uint32_t channelCount,uint32_t bytesPerChannel,bool linear)62 bool PremultiplyAlpha(
63     uint8_t* imageBytes, uint32_t width, uint32_t height, uint32_t channelCount, uint32_t bytesPerChannel, bool linear)
64 {
65     // Only need to process images with color and alpha data. I.e. RGBA or grayscale + alpha.
66     if (channelCount != 4u && channelCount != 2u) {
67         return true;
68     }
69 
70     const uint32_t pixelCount = width * height;
71 
72     if (bytesPerChannel == 1) {
73         if (linear) {
74             uint8_t* img = imageBytes;
75             for (uint32_t i = 0; i < pixelCount; i++) {
76                 // We know the alpha value is always last.
77                 uint32_t alpha = img[channelCount - 1];
78                 for (uint32_t j = 0; j < channelCount - 1; j++) {
79                     *img = static_cast<uint8_t>(*img * alpha / 0xff);
80                     img++;
81                 }
82                 img++; // Skip over the alpha value.
83             }
84         } else {
85             if (g_sRgbPremultiplyLookup[256u * 256u - 1] == 0) {
86                 InitializeSRGBTable();
87             }
88             uint8_t* img = imageBytes;
89             for (uint32_t i = 0; i < pixelCount; i++) {
90                 uint8_t* p = &g_sRgbPremultiplyLookup[img[channelCount - 1] * 256u];
91                 for (uint32_t j = 0; j < channelCount - 1; j++) {
92                     *img = p[*img];
93                     img++;
94                 }
95                 img++;
96             }
97         }
98     } else if (bytesPerChannel == 2u) {
99         // Same for 16 bits per channel images.
100         uint16_t* img = reinterpret_cast<uint16_t*>(imageBytes);
101         for (uint32_t i = 0; i < pixelCount; i++) {
102             uint32_t alpha = img[channelCount - 1];
103             for (uint32_t j = 0; j < channelCount - 1; j++) {
104                 *img = static_cast<uint16_t>(*img * alpha / 0xffff);
105                 img++;
106             }
107             img++;
108         }
109     } else {
110         CORE_LOG_E("Format not supported.");
111         return false;
112     }
113     return true;
114 }
115 
ResultFailure(const string_view error)116 IImageLoaderManager::LoadResult ResultFailure(const string_view error)
117 {
118     IImageLoaderManager::LoadResult result {
119         false,  // success
120         "",     // error[128];
121         nullptr // image;
122     };
123 
124     // Copy the error string
125     const auto count = Math::min(error.size(), sizeof(result.error) - 1);
126     error.copy(result.error, count);
127     result.error[count] = '\0';
128 
129     return result;
130 }
131 
ResultSuccess(IImageContainer::Ptr image)132 IImageLoaderManager::LoadResult ResultSuccess(IImageContainer::Ptr image)
133 {
134     return IImageLoaderManager::LoadResult {
135         true,                // success
136         "",                  // error[128];
137         BASE_NS::move(image) // image;
138     };
139 }
140 
ResultFailureAnimated(const string_view error)141 IImageLoaderManager::LoadAnimatedResult ResultFailureAnimated(const string_view error)
142 {
143     IImageLoaderManager::LoadAnimatedResult result {
144         false,  // success
145         "",     // error[128];
146         nullptr // image;
147     };
148 
149     // Copy the error string
150     const auto count = Math::min(error.size(), sizeof(result.error) - 1);
151     error.copy(result.error, count);
152     result.error[count] = '\0';
153 
154     return result;
155 }
156 } // namespace
157 
158 class PNGImage final : public IImageContainer {
159 public:
PNGImage()160     PNGImage() : IImageContainer(), imageDesc_(), imageBuffer_() {}
161 
162     ~PNGImage() override = default;
163 
164     using Ptr = BASE_NS::unique_ptr<PNGImage, Deleter>;
165 
GetImageDesc() const166     const ImageDesc& GetImageDesc() const override
167     {
168         return imageDesc_;
169     }
170 
GetData() const171     array_view<const uint8_t> GetData() const override
172     {
173         return array_view<const uint8_t>(imageBytes_.get(), imageBytesLength_);
174     }
175 
GetBufferImageCopies() const176     array_view<const SubImageDesc> GetBufferImageCopies() const override
177     {
178         return array_view<const SubImageDesc>(&imageBuffer_, 1);
179     }
180 
ResolveFormat(uint32_t loadFlags,uint32_t componentCount,bool is16bpc)181     static constexpr Format ResolveFormat(uint32_t loadFlags, uint32_t componentCount, bool is16bpc) noexcept
182     {
183         Format format {};
184         const bool forceLinear = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT) != 0;
185 
186         switch (componentCount) {
187             case 1u:
188                 format = is16bpc ? Format::BASE_FORMAT_R16_UNORM
189                                  : (forceLinear ? Format::BASE_FORMAT_R8_UNORM : Format::BASE_FORMAT_R8_SRGB);
190                 break;
191             case 2u:
192                 format = is16bpc ? Format::BASE_FORMAT_R16G16_UNORM
193                                  : (forceLinear ? Format::BASE_FORMAT_R8G8_UNORM : Format::BASE_FORMAT_R8G8_SRGB);
194                 break;
195             case 3u:
196                 format = is16bpc ? Format::BASE_FORMAT_R16G16B16_UNORM
197                                  : (forceLinear ? Format::BASE_FORMAT_R8G8B8_UNORM : Format::BASE_FORMAT_R8G8B8_SRGB);
198                 break;
199             case 4u:
200                 format = is16bpc
201                              ? Format::BASE_FORMAT_R16G16B16A16_UNORM
202                              : (forceLinear ? Format::BASE_FORMAT_R8G8B8A8_UNORM : Format::BASE_FORMAT_R8G8B8A8_SRGB);
203                 break;
204             default:
205                 format = Format::BASE_FORMAT_UNDEFINED;
206                 break;
207         }
208 
209         return format;
210     }
211 
ResolveImageDesc(Format format,uint32_t imageWidth,uint32_t imageHeight,uint32_t bitsPerPixel,bool generateMips,bool isPremultiplied)212     constexpr static ImageDesc ResolveImageDesc(Format format, uint32_t imageWidth, uint32_t imageHeight,
213         uint32_t bitsPerPixel, bool generateMips, bool isPremultiplied) noexcept
214     {
215         uint32_t mipCount = 1;
216 
217         // 1D images not supported with WebP loader
218         constexpr ImageType imageType = ImageType::TYPE_2D;
219         constexpr ImageViewType imageViewType = ImageViewType::VIEW_TYPE_2D;
220 
221         uint32_t imageFlags = isPremultiplied ? ImageFlags::FLAGS_PREMULTIPLIED_ALPHA_BIT : 0;
222 
223         if (generateMips) {
224             imageFlags |= ImageFlags::FLAGS_REQUESTING_MIPMAPS_BIT;
225 
226             uint32_t mipsize = (imageWidth > imageHeight) ? imageWidth : imageHeight;
227             mipCount = 0;
228             while (mipsize > 0) {
229                 mipCount++;
230                 mipsize >>= 1;
231             }
232         }
233 
234         return ImageDesc {
235             imageFlags,    // imageFlags
236             1,             // blockPixelWidth
237             1,             // blockPixelHeight
238             1,             // blockPixelDepth
239             bitsPerPixel,  // bitsPerBlock
240             imageType,     // imageType
241             imageViewType, // imageViewType
242             format,        // format
243             imageWidth,    // width
244             imageHeight,   // height
245             1,             // depth
246             mipCount,      // mipCount
247             1,             // layerCount
248         };
249     }
250 
CreateImage(BASE_NS::unique_ptr<uint8_t[]> imageBytes,uint32_t imageWidth,uint32_t imageHeight,uint32_t componentCount,uint32_t loadFlags,bool is16bpc)251     static IImageLoaderManager::LoadResult CreateImage(BASE_NS::unique_ptr<uint8_t[]> imageBytes, uint32_t imageWidth,
252         uint32_t imageHeight, uint32_t componentCount, uint32_t loadFlags, bool is16bpc) noexcept
253     {
254         auto image = PNGImage::Ptr(new PNGImage);
255         if (!image) {
256             return ResultFailure("Loading image failed.");
257         }
258         const uint32_t bytesPerComponent = is16bpc ? 2u : 1u;
259 
260         // Premultiply alpha if requested.
261         bool isPremultiplied = false;
262         if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
263             if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_PREMULTIPLY_ALPHA) != 0) {
264                 const bool forceLinear = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT) != 0;
265                 isPremultiplied = PremultiplyAlpha(static_cast<uint8_t*>(imageBytes.get()), imageWidth, imageHeight,
266                     componentCount, bytesPerComponent, forceLinear);
267             }
268         }
269 
270         const Format format = ResolveFormat(loadFlags, componentCount, is16bpc);
271         const uint32_t bitsPerPixel = bytesPerComponent * componentCount * 8u;
272         const bool generateMips = (loadFlags & IImageLoaderManager::IMAGE_LOADER_GENERATE_MIPS) != 0;
273         image->imageDesc_ =
274             ResolveImageDesc(format, imageWidth, imageHeight, bitsPerPixel, generateMips, isPremultiplied);
275         image->imageBytes_ = BASE_NS::move(imageBytes);
276         image->imageBytesLength_ = imageWidth * imageHeight * componentCount * bytesPerComponent;
277 
278         image->imageBuffer_ = SubImageDesc {
279             0,           // uint32_t bufferOffset
280             imageWidth,  // uint32_t bufferRowLength
281             imageHeight, // uint32_t bufferImageHeight
282 
283             0, // uint32_t mipLevel
284             1, // uint32_t layerCount
285 
286             static_cast<uint32_t>(imageWidth),
287             static_cast<uint32_t>(imageHeight),
288             1,
289         };
290 
291         return ResultSuccess(BASE_NS::move(image));
292     }
293 
UpdateColorType(png_structp pngPtr,png_infop infoPtr,uint32_t loadFlags)294     static void UpdateColorType(png_structp pngPtr, png_infop infoPtr, uint32_t loadFlags) noexcept
295     {
296         const auto colorType = png_get_color_type(pngPtr, infoPtr);
297         const auto bitDepth = png_get_bit_depth(pngPtr, infoPtr);
298         const bool forceGray = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT) ==
299                                IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT;
300 
301         // convert gray scale to RGB if needed
302         if ((colorType & (PNG_COLOR_MASK_PALETTE | PNG_COLOR_MASK_COLOR)) == 0) {
303             if (bitDepth < 8) { // 8: depth
304                 png_set_expand_gray_1_2_4_to_8(pngPtr);
305             } else if (!forceGray) {
306                 png_set_gray_to_rgb(pngPtr);
307             }
308         }
309         // convert palette to RGB
310         if (colorType & PNG_COLOR_MASK_PALETTE) {
311             png_set_palette_to_rgb(pngPtr);
312         }
313         // limit RGB to 8 bit
314         if (colorType & PNG_COLOR_MASK_COLOR) {
315             if (bitDepth > 8) { // 8: depth
316                 png_set_scale_16(pngPtr);
317             }
318         }
319         if (forceGray) {
320             // conversion to gray scale requested
321             if (colorType & PNG_COLOR_MASK_COLOR) {
322                 png_set_rgb_to_gray(pngPtr, 1, 0.299, 0.587); // 0.299、0.587: Specifications
323             }
324             if (colorType & PNG_COLOR_MASK_ALPHA) {
325                 png_set_strip_alpha(pngPtr);
326             }
327         } else if ((colorType & PNG_COLOR_MASK_ALPHA) == 0) {
328             png_set_add_alpha(pngPtr, 0xff, PNG_FILLER_AFTER);
329         }
330         png_read_update_info(pngPtr, infoPtr);
331     }
332 
333     // callback for fetching data from imageFileBytes to libpng
ReadData(png_structp pngPtr,png_bytep outBytes,png_size_t byteCountToRead)334     static void ReadData(png_structp pngPtr, png_bytep outBytes, png_size_t byteCountToRead) noexcept
335     {
336         png_voidp ioPtr = png_get_io_ptr(pngPtr);
337         if (!ioPtr) {
338             png_error(pngPtr, "Failed to read data.");
339             return;
340         }
341         array_view<const uint8_t>& imageFileBytes = *(array_view<const uint8_t>*)ioPtr;
342         // copy either the requested amount or what's left.
343         const auto read = BASE_NS::Math::min(imageFileBytes.size(), byteCountToRead);
344         BASE_NS::CloneData(outBytes, byteCountToRead, imageFileBytes.data(), read);
345         // move the start of imageFileBytes past the data which was copied.
346         imageFileBytes = array_view(imageFileBytes.data() + read, imageFileBytes.size() - read);
347         if (read != byteCountToRead) {
348             png_error(pngPtr, "Not enough data.");
349             return;
350         }
351     }
352 
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)353     static IImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) noexcept
354     {
355         if (imageFileBytes.empty()) {
356             return ResultFailure("Input data must not be null.");
357         }
358 
359         png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
360         if (!png) {
361             png_destroy_read_struct(&png, nullptr, nullptr);
362             return ResultFailure("Initialization failed.");
363         }
364         png_infop info = png_create_info_struct(png);
365         if (!info) {
366             png_destroy_read_struct(&png, nullptr, nullptr);
367             return ResultFailure("Initialization failed.");
368         }
369 
370         // libpng uses longjmp for error handling. execution will jump to this if statement on error. it's not
371         // guaranteed that stack is cleaned up with longjmp, so have the big buffer pointer and pointers to start of
372         // each row available here, and we can call reset() to release memory if something fails.
373         BASE_NS::unique_ptr<uint8_t[]> image;
374         BASE_NS::unique_ptr<png_byte* []> rows;
375         if (setjmp(png_jmpbuf(png))) {
376             rows.reset();
377             image.reset();
378             png_destroy_read_struct(&png, &info, nullptr);
379             return ResultFailure("Decoding failed.");
380         }
381 
382         // libpng will now call ReadData when it needs data.
383         png_set_read_fn(png, &imageFileBytes, ReadData);
384 
385         // first ask for the image information.
386         png_read_info(png, info);
387         auto width = png_get_image_width(png, info);
388         auto height = png_get_image_height(png, info);
389         auto channels = png_get_channels(png, info);
390         auto is16bpc = png_get_bit_depth(png, info) == 16;
391 
392         if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) !=
393             IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) {
394             // ask libpng to do possible conversions according to loadFlags.
395             UpdateColorType(png, info, loadFlags);
396 
397             // update the image information in case some conversions will be done.
398             width = png_get_image_width(png, info);
399             height = png_get_image_height(png, info);
400             channels = png_get_channels(png, info);
401             is16bpc = png_get_bit_depth(png, info) == 16; // 16: index
402             if (channels <= 0 || channels > 4) { // 0: invalid channel num, 4: RGBA
403                 png_destroy_read_struct(&png, &info, nullptr);
404                 return ResultFailure("Invalid number of color channels.");
405             }
406 
407             const size_t imageSize = width * height * channels;
408             if ((width > MAX_IMAGE_EXTENT) || (height > MAX_IMAGE_EXTENT) || (imageSize > IMG_SIZE_LIMIT_2GB)) {
409                 png_destroy_read_struct(&png, &info, nullptr);
410                 return ResultFailure("Image too large.");
411             }
412             // allocate space for the whole image and and array of row pointers. libpng writes data to each row pointer.
413             // alternative would be to use a different api which writes only one row and feed it the correct address
414             // every time.
415             image = BASE_NS::make_unique<uint8_t[]>(imageSize);
416             rows = BASE_NS::make_unique<png_byte* []>(height);
417             // fill rows depending on should there be a vertical flip or not.
418             auto row = rows.get();
419             if (loadFlags & IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT) {
420                 for (auto i = 0U; i < height; ++i) {
421                     *row++ = image.get() + ((height - 1) - i) * (width * channels);
422                 }
423             } else {
424                 for (auto i = 0U; i < height; ++i) {
425                     *row++ = image.get() + i * (width * channels);
426                 }
427             }
428 
429             // ask libpng to devoce the whole image.
430             png_read_image(png, rows.get());
431             png_read_end(png, info);
432         }
433         png_destroy_read_struct(&png, &info, nullptr);
434 
435         // Success. Populate the image info and image data object.
436         return CreateImage(BASE_NS::move(image), width, height, channels, loadFlags, is16bpc);
437     }
438 
439 protected:
Destroy()440     void Destroy() override
441     {
442         delete this;
443     }
444 
445 private:
446     ImageDesc imageDesc_;
447     SubImageDesc imageBuffer_;
448 
449     BASE_NS::unique_ptr<uint8_t[]> imageBytes_;
450     size_t imageBytesLength_ = 0;
451 };
452 
453 class ImageLoaderPng final : public IImageLoaderManager::IImageLoader {
454 public:
455     // Inherited via ImageManager::IImageLoader
Load(IFile & file,uint32_t loadFlags) const456     IImageLoaderManager::LoadResult Load(IFile& file, uint32_t loadFlags) const override
457     {
458         const uint64_t byteLength = file.GetLength();
459         if (byteLength > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
460             return ResultFailure("File too big.");
461         }
462 
463         // Read the file to a buffer.
464         unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(static_cast<size_t>(byteLength));
465         const uint64_t read = file.Read(buffer.get(), byteLength);
466         if (read != byteLength) {
467             return ResultFailure("Reading file failed.");
468         }
469 
470         return PNGImage::Load(array_view<const uint8_t>(buffer.get(), static_cast<size_t>(byteLength)), loadFlags);
471     }
472 
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags) const473     IImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) const override
474     {
475         if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
476             return ResultFailure("File too big.");
477         }
478         return PNGImage::Load(imageFileBytes, loadFlags);
479     }
480 
CanLoad(array_view<const uint8_t> imageFileBytes) const481     bool CanLoad(array_view<const uint8_t> imageFileBytes) const override
482     {
483         // Check for PNG
484         return png_sig_cmp(imageFileBytes.data(), 0, imageFileBytes.size()) == 0;
485     }
486 
LoadAnimatedImage(IFile & file,uint32_t loadFlags)487     IImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(IFile& file, uint32_t loadFlags) override
488     {
489         return ResultFailureAnimated("");
490     }
491 
LoadAnimatedImage(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)492     IImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(
493         array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) override
494     {
495         return ResultFailureAnimated("");
496     }
497 
GetSupportedTypes() const498     vector<IImageLoaderManager::ImageType> GetSupportedTypes() const override
499     {
500         return vector<IImageLoaderManager::ImageType>(std::begin(IMAGE_TYPES), std::end(IMAGE_TYPES));
501     }
502 
503 protected:
504     ~ImageLoaderPng() = default;
Destroy()505     void Destroy() override
506     {
507         delete this;
508     }
509 };
510 
CreateImageLoaderPng(CORE_NS::PluginToken)511 IImageLoaderManager::IImageLoader::Ptr CreateImageLoaderPng(CORE_NS::PluginToken)
512 {
513     return IImageLoaderManager::IImageLoader::Ptr { new ImageLoaderPng() };
514 }
515 
516 } // namespace PNGPlugin
517