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