1 /*
2  * Copyright (C) 2023-2024 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_libpng.h"
17 #if defined(USE_LIB_PNG_JPEG) && (USE_LIB_PNG_JPEG == 1)
18 #include "png.h"
19 #include "pngstruct.h"
20 #include "pnginfo.h"
21 #include <core/log.h>
22 #include "base/containers/string.h"
23 
24 #include "image/loaders/image_loader_common.h"
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 
ArrayPNGReader(png_structp png_ptr,png_bytep png_data,png_size_t length)38 void ArrayPNGReader(png_structp png_ptr, png_bytep png_data, png_size_t length)
39 {
40     ArrayLoader<uint8_t> &loader = *static_cast<ArrayLoader<uint8_t> *>(png_get_io_ptr(png_ptr));
41     loader.ArrayRead(reinterpret_cast<uint8_t *>(png_data), static_cast<uint64_t>(length));
42 }
43 
HandlePNGColorType(png_structp png_ptr,png_infop info_ptr,uint32_t loadFlags)44 void HandlePNGColorType(png_structp png_ptr, png_infop info_ptr, uint32_t loadFlags)
45 {
46     if (png_ptr == nullptr || info_ptr == nullptr) {
47         CORE_LOG_E("pass nullptr to HandlePNGColorType");
48         return;
49     }
50     if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY && info_ptr->bit_depth < 8) {
51         png_set_expand_gray_1_2_4_to_8(png_ptr);  // force to 8bit
52     } else if (info_ptr->bit_depth == 16) {
53         png_set_strip_16(png_ptr);  // force to 8bit
54     }
55     bool forceGray = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT) != 0;
56     if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
57         png_set_palette_to_rgb(png_ptr);  // force color index to RGB
58         if (forceGray) {
59             png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);  // RGB->Gray from opencv
60             png_set_strip_alpha(png_ptr);
61         }
62     }
63     // Also convert 2 channel (grayscale + alpha) to 4 because r + a in not supported.
64     else if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
65         // Force grayscale if requested.
66         if (forceGray) {
67             png_set_strip_alpha(png_ptr);
68         } else {
69             png_set_gray_to_rgb(png_ptr);
70         }
71     }
72     // Convert 3 channels to 4 because 3 channel textures are not always supported.
73     else if (info_ptr->color_type == PNG_COLOR_TYPE_RGB) {
74         // Force grayscale if requested.
75         if (forceGray) {
76             png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);  // RGB->Gray from opencv
77         } else {
78             png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
79         }
80     } else if (info_ptr->color_type == PNG_COLOR_TYPE_RGBA) {
81         // Force grayscale if requested.
82         if (forceGray) {
83             png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);  // RGB->Gray from opencv
84             png_set_strip_alpha(png_ptr);
85         }
86     }
87     png_read_update_info(png_ptr, info_ptr);
88 }
89 
90 class LibPNGImage final : public LibBaseImage {
91 public:
LibPNGImage()92     LibPNGImage() : LibBaseImage()
93     {}
94 
LoadFromMemory(png_structp png_ptr,png_infop info_ptr,uint32_t loadFlags,Info & info)95     static LibBaseImagePtr LoadFromMemory(png_structp png_ptr, png_infop info_ptr, uint32_t loadFlags, Info &info)
96     {
97         LibBaseImagePtr imageBytes = nullptr;
98         if (png_ptr == nullptr || info_ptr == nullptr) {
99             CORE_LOG_E("pass nullptr to LoadFromMemory");
100             return imageBytes;
101         }
102         HandlePNGColorType(png_ptr, info_ptr, loadFlags);
103 
104         info.width = info_ptr->width;
105         info.height = info_ptr->height;
106         info.componentCount = info_ptr->channels;
107         info.is16bpc = info_ptr->bit_depth == 16;
108 
109         size_t imgSize = info.width * info.height * info.componentCount;
110         if (imgSize < 1 || imgSize >= IMG_SIZE_LIMIT_2GB) {
111             CORE_LOG_E("imgSize more than limit!");
112             return imageBytes;
113         }
114 
115         RowPointers<uint8_t> rp(info.width, info.height, info.componentCount, sizeof(uint8_t));
116         if (!rp.allocSucc) {
117             CORE_LOG_E("RowPointers allocate fail");
118             return imageBytes;
119         }
120 
121         png_bytep buff = static_cast<png_bytep>(malloc(imgSize * sizeof(uint8_t)));
122         if (buff == nullptr) {
123             CORE_LOG_E("malloc fail return null");
124             return imageBytes;
125         }
126         imageBytes = {buff, FreeLibBaseImageBytes};
127 
128         png_read_image(png_ptr, rp.rowPointers);
129         png_read_end(png_ptr, info_ptr);
130         // Flip vertically if requested.
131         if (imageBytes && (loadFlags & IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT) != 0) {
132             VerticalFlipRowPointers(rp.rowPointers, info.height, info.width, info.componentCount);
133         }
134 
135         uint32_t pos = 0;
136         for (uint32_t y = 0; y < info.height; ++y) {
137             for (uint32_t x = 0; x < info.componentCount * info.width; x += info.componentCount) {
138                 for (uint32_t k = 0; k < info.componentCount; k++) {
139                     buff[pos++] = rp.rowPointers[y][x + k];
140                 }
141             }
142         }
143         return imageBytes;
144     }
145 
146     // Actual png loading implementation.
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)147     static ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags)
148     {
149         CORE_LOG_D("ImageLoaderManager Load png start");
150         if (imageFileBytes.empty()) {
151             return ImageLoaderManager::ResultFailure("Input data must not be null.");
152         }
153 
154         png_structp png_ptr;
155         png_infop info_ptr;
156 
157         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
158         if (!png_ptr) {
159             png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
160             return ImageLoaderManager::ResultFailure("Loading png_ptr failed");
161         }
162         info_ptr = png_create_info_struct(png_ptr);
163         if (!info_ptr) {
164             png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
165             return ImageLoaderManager::ResultFailure("Loading info_ptr failed");
166         }
167         if (setjmp(png_jmpbuf(png_ptr))) {
168             /* If we get here, we had a problem reading the file. */
169             png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
170             return ImageLoaderManager::ResultFailure("png_jmpbuf to fail");
171         }
172 
173         // Load the image info without decoding the image data
174         // (Just to check what the image format is so we can convert if necessary).
175         Info info;
176         ArrayLoader<uint8_t> aloader(imageFileBytes);
177         png_set_read_fn(png_ptr, &aloader, ArrayPNGReader);
178         png_read_info(png_ptr, info_ptr);
179         info.width = info_ptr->width;
180         info.height = info_ptr->height;
181         info.componentCount = info_ptr->channels;
182         info.is16bpc = info_ptr->bit_depth == 16;
183 
184         // Not supporting hdr images via pnglib.
185         // libpng cannot check hdr
186 
187         LibBaseImagePtr imageBytes = nullptr;
188         if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
189             imageBytes = LoadFromMemory(png_ptr, info_ptr, loadFlags, info);
190             if (imageBytes == nullptr) {
191                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
192                 return ImageLoaderManager::ResultFailure("png LoadFromMemory fail");
193             }
194         } else {
195             imageBytes = {nullptr, FreeLibBaseImageBytes};
196         }
197         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
198 
199         // Success. Populate the image info and image data object.
200         return CreateImage(
201             CORE_NS::move(imageBytes), info.width, info.height, info.componentCount, loadFlags, info.is16bpc);
202     }
203 
204 protected:
Destroy()205     void Destroy() override
206     {
207         delete this;
208     }
209 
210 private:
211     ImageDesc imageDesc_;
212     SubImageDesc imageBuffer_;
213 
214     LibBaseImagePtr imageBytes_;
215     size_t imageBytesLength_ = 0;
216 };
217 
218 class ImageLoaderLibPNGImage final : public ImageLoaderManager::IImageLoader {
219 public:
220     // Inherited via ImageManager::IImageLoader
Load(IFile & file,uint32_t loadFlags) const221     ImageLoaderManager::LoadResult Load(IFile &file, uint32_t loadFlags) const override
222     {
223         const uint64_t byteLength = file.GetLength();
224         // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
225         if (byteLength > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
226             return ImageLoaderManager::ResultFailure("File too big to read.");
227         }
228 
229         // Read the file to a buffer.
230         unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(static_cast<size_t>(byteLength));
231         const uint64_t read = file.Read(buffer.get(), byteLength);
232         if (read != byteLength) {
233             return ImageLoaderManager::ResultFailure("Reading file failed.");
234         }
235 
236         return LibPNGImage::Load(array_view<const uint8_t>(buffer.get(), static_cast<size_t>(byteLength)), loadFlags);
237     }
238 
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags) const239     ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) const override
240     {
241         // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
242         // Not writing a test for this :)
243         if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
244             return ImageLoaderManager::ResultFailure("Data too big to read.");
245         }
246 
247         return LibPNGImage::Load(imageFileBytes, loadFlags);
248     }
249 
CanLoad(array_view<const uint8_t> imageFileBytes) const250     bool CanLoad(array_view<const uint8_t> imageFileBytes) const override
251     {
252         // uses int for file sizes. Don't even try to read a file if the size does not fit to int.
253         // Not writing a test for this :)
254         size_t maxFileSize = static_cast<size_t>(std::numeric_limits<int>::max());
255         size_t pngHeaderSize = 8;
256         if (imageFileBytes.size() > maxFileSize || imageFileBytes.size() < pngHeaderSize) {
257             return false;
258         }
259 
260         // Check for PNG
261         if (imageFileBytes[0] == 137 && imageFileBytes[1] == 80 && imageFileBytes[2] == 78 && imageFileBytes[3] == 71 &&
262             imageFileBytes[4] == 13 && imageFileBytes[5] == 10 && imageFileBytes[6] == 26 && imageFileBytes[7] == 10) {
263             return true;
264         }
265 
266         return false;
267     }
268 
269     // No animation support
LoadAnimatedImage(IFile & file,uint32_t loadFlags)270     ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(IFile &file, uint32_t loadFlags) override
271     {
272         return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
273     }
274 
LoadAnimatedImage(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)275     ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(
276         array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) override
277     {
278         return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
279     }
280 
GetSupportedTypes() const281     BASE_NS::vector<IImageLoaderManager::ImageType> GetSupportedTypes() const override
282     {
283         return BASE_NS::vector<IImageLoaderManager::ImageType>(std::begin(PNG_IMAGE_TYPES), std::end(PNG_IMAGE_TYPES));
284     }
285 protected:
286     ~ImageLoaderLibPNGImage() = default;
Destroy()287     void Destroy() override
288     {
289         delete this;
290     }
291 };
292 }  // namespace
CreateImageLoaderLibPNGImage(PluginToken)293 IImageLoaderManager::IImageLoader::Ptr CreateImageLoaderLibPNGImage(PluginToken)
294 {
295     return ImageLoaderManager::IImageLoader::Ptr{new ImageLoaderLibPNGImage()};
296 }
297 CORE_END_NAMESPACE()
298 #endif // defined(USE_LIB_PNG_JPEG) && (USE_LIB_PNG_JPEG == 1)