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)