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