/src/libpng/contrib/oss-fuzz/libpng_read_fuzzer.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // libpng_read_fuzzer.cc |
2 | | // Copyright 2017-2018 Glenn Randers-Pehrson |
3 | | // Copyright 2015 The Chromium Authors. All rights reserved. |
4 | | // Use of this source code is governed by a BSD-style license that may |
5 | | // be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE |
6 | | |
7 | | // The modifications in 2017 by Glenn Randers-Pehrson include |
8 | | // 1. addition of a PNG_CLEANUP macro, |
9 | | // 2. setting the option to ignore ADLER32 checksums, |
10 | | // 3. adding "#include <string.h>" which is needed on some platforms |
11 | | // to provide memcpy(). |
12 | | // 4. adding read_end_info() and creating an end_info structure. |
13 | | // 5. adding calls to png_set_*() transforms commonly used by browsers. |
14 | | |
15 | | #include <stddef.h> |
16 | | #include <stdint.h> |
17 | | #include <stdlib.h> |
18 | | #include <string.h> |
19 | | |
20 | | #include <vector> |
21 | | |
22 | | #define PNG_INTERNAL |
23 | | #include "png.h" |
24 | | |
25 | | #define PNG_CLEANUP \ |
26 | 26.0k | if(png_handler.png_ptr) \ |
27 | 26.0k | { \ |
28 | 26.0k | if (png_handler.row_ptr) \ |
29 | 26.0k | png_free(png_handler.png_ptr, png_handler.row_ptr); \ |
30 | 26.0k | if (png_handler.end_info_ptr) \ |
31 | 26.0k | png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ |
32 | 26.0k | &png_handler.end_info_ptr); \ |
33 | 26.0k | else if (png_handler.info_ptr) \ |
34 | 0 | png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ |
35 | 0 | nullptr); \ |
36 | 0 | else \ |
37 | 0 | png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ |
38 | 26.0k | png_handler.png_ptr = nullptr; \ |
39 | 26.0k | png_handler.row_ptr = nullptr; \ |
40 | 26.0k | png_handler.info_ptr = nullptr; \ |
41 | 26.0k | png_handler.end_info_ptr = nullptr; \ |
42 | 26.0k | } |
43 | | |
44 | | struct BufState { |
45 | | const uint8_t* data; |
46 | | size_t bytes_left; |
47 | | }; |
48 | | |
49 | | struct PngObjectHandler { |
50 | | png_infop info_ptr = nullptr; |
51 | | png_structp png_ptr = nullptr; |
52 | | png_infop end_info_ptr = nullptr; |
53 | | png_voidp row_ptr = nullptr; |
54 | | BufState* buf_state = nullptr; |
55 | | |
56 | 26.0k | ~PngObjectHandler() { |
57 | 26.0k | if (row_ptr) |
58 | 0 | png_free(png_ptr, row_ptr); |
59 | 26.0k | if (end_info_ptr) |
60 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); |
61 | 26.0k | else if (info_ptr) |
62 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); |
63 | 26.0k | else |
64 | 26.0k | png_destroy_read_struct(&png_ptr, nullptr, nullptr); |
65 | 26.0k | delete buf_state; |
66 | 26.0k | } |
67 | | }; |
68 | | |
69 | 561k | void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { |
70 | 561k | BufState* buf_state = static_cast<BufState*>(png_get_io_ptr(png_ptr)); |
71 | 561k | if (length > buf_state->bytes_left) { |
72 | 4.67k | png_error(png_ptr, "read error"); |
73 | 4.67k | } |
74 | 556k | memcpy(data, buf_state->data, length); |
75 | 556k | buf_state->bytes_left -= length; |
76 | 556k | buf_state->data += length; |
77 | 556k | } |
78 | | |
79 | 178k | void* limited_malloc(png_structp, png_alloc_size_t size) { |
80 | | // libpng may allocate large amounts of memory that the fuzzer reports as |
81 | | // an error. In order to silence these errors, make libpng fail when trying |
82 | | // to allocate a large amount. This allocator used to be in the Chromium |
83 | | // version of this fuzzer. |
84 | | // This number is chosen to match the default png_user_chunk_malloc_max. |
85 | 178k | if (size > 8000000) |
86 | 2 | return nullptr; |
87 | | |
88 | 178k | return malloc(size); |
89 | 178k | } |
90 | | |
91 | 256k | void default_free(png_structp, png_voidp ptr) { |
92 | 256k | return free(ptr); |
93 | 256k | } |
94 | | |
95 | | static const int kPngHeaderSize = 8; |
96 | | |
97 | | // Entry point for LibFuzzer. |
98 | | // Roughly follows the libpng book example: |
99 | | // http://www.libpng.org/pub/png/book/chapter13.html |
100 | 26.0k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
101 | 26.0k | if (size < kPngHeaderSize) { |
102 | 4 | return 0; |
103 | 4 | } |
104 | | |
105 | 26.0k | std::vector<unsigned char> v(data, data + size); |
106 | 26.0k | if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { |
107 | | // not a PNG. |
108 | 72 | return 0; |
109 | 72 | } |
110 | | |
111 | 26.0k | PngObjectHandler png_handler; |
112 | 26.0k | png_handler.png_ptr = nullptr; |
113 | 26.0k | png_handler.row_ptr = nullptr; |
114 | 26.0k | png_handler.info_ptr = nullptr; |
115 | 26.0k | png_handler.end_info_ptr = nullptr; |
116 | | |
117 | 26.0k | png_handler.png_ptr = png_create_read_struct |
118 | 26.0k | (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); |
119 | 26.0k | if (!png_handler.png_ptr) { |
120 | 0 | return 0; |
121 | 0 | } |
122 | | |
123 | 26.0k | png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); |
124 | 26.0k | if (!png_handler.info_ptr) { |
125 | 0 | PNG_CLEANUP |
126 | 0 | return 0; |
127 | 0 | } |
128 | | |
129 | 26.0k | png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); |
130 | 26.0k | if (!png_handler.end_info_ptr) { |
131 | 0 | PNG_CLEANUP |
132 | 0 | return 0; |
133 | 0 | } |
134 | | |
135 | | // Use a custom allocator that fails for large allocations to avoid OOM. |
136 | 26.0k | png_set_mem_fn(png_handler.png_ptr, nullptr, limited_malloc, default_free); |
137 | | |
138 | 26.0k | png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); |
139 | | #ifdef PNG_IGNORE_ADLER32 |
140 | | png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); |
141 | | #endif |
142 | | |
143 | | // Setting up reading from buffer. |
144 | 26.0k | png_handler.buf_state = new BufState(); |
145 | 26.0k | png_handler.buf_state->data = data + kPngHeaderSize; |
146 | 26.0k | png_handler.buf_state->bytes_left = size - kPngHeaderSize; |
147 | 26.0k | png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); |
148 | 26.0k | png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); |
149 | | |
150 | 26.0k | if (setjmp(png_jmpbuf(png_handler.png_ptr))) { |
151 | 14.8k | PNG_CLEANUP |
152 | 14.8k | return 0; |
153 | 14.8k | } |
154 | | |
155 | | // Reading. |
156 | 11.1k | png_read_info(png_handler.png_ptr, png_handler.info_ptr); |
157 | | |
158 | | // reset error handler to put png_deleter into scope. |
159 | 11.1k | if (setjmp(png_jmpbuf(png_handler.png_ptr))) { |
160 | 2.76k | PNG_CLEANUP |
161 | 2.76k | return 0; |
162 | 2.76k | } |
163 | | |
164 | 8.42k | png_uint_32 width, height; |
165 | 8.42k | int bit_depth, color_type, interlace_type, compression_type; |
166 | 8.42k | int filter_type; |
167 | | |
168 | 8.42k | if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, |
169 | 8.42k | &height, &bit_depth, &color_type, &interlace_type, |
170 | 8.42k | &compression_type, &filter_type)) { |
171 | 0 | PNG_CLEANUP |
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | // This is going to be too slow. |
176 | 11.1k | if (width && height > 100000000 / width) { |
177 | 21 | PNG_CLEANUP |
178 | 21 | return 0; |
179 | 21 | } |
180 | | |
181 | | // Set several transforms that browsers typically use: |
182 | 8.40k | png_set_gray_to_rgb(png_handler.png_ptr); |
183 | 8.40k | png_set_expand(png_handler.png_ptr); |
184 | 8.40k | png_set_packing(png_handler.png_ptr); |
185 | 8.40k | png_set_scale_16(png_handler.png_ptr); |
186 | 8.40k | png_set_tRNS_to_alpha(png_handler.png_ptr); |
187 | | |
188 | 8.40k | int passes = png_set_interlace_handling(png_handler.png_ptr); |
189 | | |
190 | 8.40k | png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); |
191 | | |
192 | 8.40k | png_handler.row_ptr = png_malloc( |
193 | 8.40k | png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, |
194 | 8.40k | png_handler.info_ptr)); |
195 | | |
196 | 49.3k | for (int pass = 0; pass < passes; ++pass) { |
197 | 853k | for (png_uint_32 y = 0; y < height; ++y) { |
198 | 812k | png_read_row(png_handler.png_ptr, |
199 | 812k | static_cast<png_bytep>(png_handler.row_ptr), nullptr); |
200 | 812k | } |
201 | 40.9k | } |
202 | | |
203 | 8.40k | png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); |
204 | | |
205 | 8.40k | PNG_CLEANUP |
206 | | |
207 | 8.40k | #ifdef PNG_SIMPLIFIED_READ_SUPPORTED |
208 | | // Simplified READ API |
209 | 8.40k | png_image image; |
210 | 8.40k | memset(&image, 0, (sizeof image)); |
211 | 8.40k | image.version = PNG_IMAGE_VERSION; |
212 | | |
213 | 8.40k | if (!png_image_begin_read_from_memory(&image, data, size)) { |
214 | 11 | return 0; |
215 | 11 | } |
216 | | |
217 | 8.39k | image.format = PNG_FORMAT_RGBA; |
218 | 8.39k | std::vector<png_byte> buffer(PNG_IMAGE_SIZE(image)); |
219 | 8.39k | png_image_finish_read(&image, NULL, buffer.data(), 0, NULL); |
220 | 8.39k | #endif |
221 | | |
222 | 8.39k | return 0; |
223 | 8.40k | } Line | Count | Source | 100 | 18.3k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { | 101 | 18.3k | if (size < kPngHeaderSize) { | 102 | 0 | return 0; | 103 | 0 | } | 104 | | | 105 | 18.3k | std::vector<unsigned char> v(data, data + size); | 106 | 18.3k | if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { | 107 | | // not a PNG. | 108 | 0 | return 0; | 109 | 0 | } | 110 | | | 111 | 18.3k | PngObjectHandler png_handler; | 112 | 18.3k | png_handler.png_ptr = nullptr; | 113 | 18.3k | png_handler.row_ptr = nullptr; | 114 | 18.3k | png_handler.info_ptr = nullptr; | 115 | 18.3k | png_handler.end_info_ptr = nullptr; | 116 | | | 117 | 18.3k | png_handler.png_ptr = png_create_read_struct | 118 | 18.3k | (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | 119 | 18.3k | if (!png_handler.png_ptr) { | 120 | 0 | return 0; | 121 | 0 | } | 122 | | | 123 | 18.3k | png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); | 124 | 18.3k | if (!png_handler.info_ptr) { | 125 | 0 | PNG_CLEANUP | 126 | 0 | return 0; | 127 | 0 | } | 128 | | | 129 | 18.3k | png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); | 130 | 18.3k | if (!png_handler.end_info_ptr) { | 131 | 0 | PNG_CLEANUP | 132 | 0 | return 0; | 133 | 0 | } | 134 | | | 135 | | // Use a custom allocator that fails for large allocations to avoid OOM. | 136 | 18.3k | png_set_mem_fn(png_handler.png_ptr, nullptr, limited_malloc, default_free); | 137 | | | 138 | 18.3k | png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); | 139 | | #ifdef PNG_IGNORE_ADLER32 | 140 | | png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); | 141 | | #endif | 142 | | | 143 | | // Setting up reading from buffer. | 144 | 18.3k | png_handler.buf_state = new BufState(); | 145 | 18.3k | png_handler.buf_state->data = data + kPngHeaderSize; | 146 | 18.3k | png_handler.buf_state->bytes_left = size - kPngHeaderSize; | 147 | 18.3k | png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); | 148 | 18.3k | png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); | 149 | | | 150 | 18.3k | if (setjmp(png_jmpbuf(png_handler.png_ptr))) { | 151 | 10.2k | PNG_CLEANUP | 152 | 10.2k | return 0; | 153 | 10.2k | } | 154 | | | 155 | | // Reading. | 156 | 8.07k | png_read_info(png_handler.png_ptr, png_handler.info_ptr); | 157 | | | 158 | | // reset error handler to put png_deleter into scope. | 159 | 8.07k | if (setjmp(png_jmpbuf(png_handler.png_ptr))) { | 160 | 1.65k | PNG_CLEANUP | 161 | 1.65k | return 0; | 162 | 1.65k | } | 163 | | | 164 | 6.42k | png_uint_32 width, height; | 165 | 6.42k | int bit_depth, color_type, interlace_type, compression_type; | 166 | 6.42k | int filter_type; | 167 | | | 168 | 6.42k | if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, | 169 | 6.42k | &height, &bit_depth, &color_type, &interlace_type, | 170 | 6.42k | &compression_type, &filter_type)) { | 171 | 0 | PNG_CLEANUP | 172 | 0 | return 0; | 173 | 0 | } | 174 | | | 175 | | // This is going to be too slow. | 176 | 8.07k | if (width && height > 100000000 / width) { | 177 | 0 | PNG_CLEANUP | 178 | 0 | return 0; | 179 | 0 | } | 180 | | | 181 | | // Set several transforms that browsers typically use: | 182 | 6.42k | png_set_gray_to_rgb(png_handler.png_ptr); | 183 | 6.42k | png_set_expand(png_handler.png_ptr); | 184 | 6.42k | png_set_packing(png_handler.png_ptr); | 185 | 6.42k | png_set_scale_16(png_handler.png_ptr); | 186 | 6.42k | png_set_tRNS_to_alpha(png_handler.png_ptr); | 187 | | | 188 | 6.42k | int passes = png_set_interlace_handling(png_handler.png_ptr); | 189 | | | 190 | 6.42k | png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); | 191 | | | 192 | 6.42k | png_handler.row_ptr = png_malloc( | 193 | 6.42k | png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, | 194 | 6.42k | png_handler.info_ptr)); | 195 | | | 196 | 39.4k | for (int pass = 0; pass < passes; ++pass) { | 197 | 239k | for (png_uint_32 y = 0; y < height; ++y) { | 198 | 206k | png_read_row(png_handler.png_ptr, | 199 | 206k | static_cast<png_bytep>(png_handler.row_ptr), nullptr); | 200 | 206k | } | 201 | 33.0k | } | 202 | | | 203 | 6.42k | png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); | 204 | | | 205 | 6.42k | PNG_CLEANUP | 206 | | | 207 | 6.42k | #ifdef PNG_SIMPLIFIED_READ_SUPPORTED | 208 | | // Simplified READ API | 209 | 6.42k | png_image image; | 210 | 6.42k | memset(&image, 0, (sizeof image)); | 211 | 6.42k | image.version = PNG_IMAGE_VERSION; | 212 | | | 213 | 6.42k | if (!png_image_begin_read_from_memory(&image, data, size)) { | 214 | 0 | return 0; | 215 | 0 | } | 216 | | | 217 | 6.42k | image.format = PNG_FORMAT_RGBA; | 218 | 6.42k | std::vector<png_byte> buffer(PNG_IMAGE_SIZE(image)); | 219 | 6.42k | png_image_finish_read(&image, NULL, buffer.data(), 0, NULL); | 220 | 6.42k | #endif | 221 | | | 222 | 6.42k | return 0; | 223 | 6.42k | } |
Line | Count | Source | 100 | 7.73k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { | 101 | 7.73k | if (size < kPngHeaderSize) { | 102 | 4 | return 0; | 103 | 4 | } | 104 | | | 105 | 7.72k | std::vector<unsigned char> v(data, data + size); | 106 | 7.72k | if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { | 107 | | // not a PNG. | 108 | 72 | return 0; | 109 | 72 | } | 110 | | | 111 | 7.65k | PngObjectHandler png_handler; | 112 | 7.65k | png_handler.png_ptr = nullptr; | 113 | 7.65k | png_handler.row_ptr = nullptr; | 114 | 7.65k | png_handler.info_ptr = nullptr; | 115 | 7.65k | png_handler.end_info_ptr = nullptr; | 116 | | | 117 | 7.65k | png_handler.png_ptr = png_create_read_struct | 118 | 7.65k | (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | 119 | 7.65k | if (!png_handler.png_ptr) { | 120 | 0 | return 0; | 121 | 0 | } | 122 | | | 123 | 7.65k | png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); | 124 | 7.65k | if (!png_handler.info_ptr) { | 125 | 0 | PNG_CLEANUP | 126 | 0 | return 0; | 127 | 0 | } | 128 | | | 129 | 7.65k | png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); | 130 | 7.65k | if (!png_handler.end_info_ptr) { | 131 | 0 | PNG_CLEANUP | 132 | 0 | return 0; | 133 | 0 | } | 134 | | | 135 | | // Use a custom allocator that fails for large allocations to avoid OOM. | 136 | 7.65k | png_set_mem_fn(png_handler.png_ptr, nullptr, limited_malloc, default_free); | 137 | | | 138 | 7.65k | png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); | 139 | | #ifdef PNG_IGNORE_ADLER32 | 140 | | png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); | 141 | | #endif | 142 | | | 143 | | // Setting up reading from buffer. | 144 | 7.65k | png_handler.buf_state = new BufState(); | 145 | 7.65k | png_handler.buf_state->data = data + kPngHeaderSize; | 146 | 7.65k | png_handler.buf_state->bytes_left = size - kPngHeaderSize; | 147 | 7.65k | png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); | 148 | 7.65k | png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); | 149 | | | 150 | 7.65k | if (setjmp(png_jmpbuf(png_handler.png_ptr))) { | 151 | 4.53k | PNG_CLEANUP | 152 | 4.53k | return 0; | 153 | 4.53k | } | 154 | | | 155 | | // Reading. | 156 | 3.11k | png_read_info(png_handler.png_ptr, png_handler.info_ptr); | 157 | | | 158 | | // reset error handler to put png_deleter into scope. | 159 | 3.11k | if (setjmp(png_jmpbuf(png_handler.png_ptr))) { | 160 | 1.11k | PNG_CLEANUP | 161 | 1.11k | return 0; | 162 | 1.11k | } | 163 | | | 164 | 2.00k | png_uint_32 width, height; | 165 | 2.00k | int bit_depth, color_type, interlace_type, compression_type; | 166 | 2.00k | int filter_type; | 167 | | | 168 | 2.00k | if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, | 169 | 2.00k | &height, &bit_depth, &color_type, &interlace_type, | 170 | 2.00k | &compression_type, &filter_type)) { | 171 | 0 | PNG_CLEANUP | 172 | 0 | return 0; | 173 | 0 | } | 174 | | | 175 | | // This is going to be too slow. | 176 | 3.11k | if (width && height > 100000000 / width) { | 177 | 21 | PNG_CLEANUP | 178 | 21 | return 0; | 179 | 21 | } | 180 | | | 181 | | // Set several transforms that browsers typically use: | 182 | 1.98k | png_set_gray_to_rgb(png_handler.png_ptr); | 183 | 1.98k | png_set_expand(png_handler.png_ptr); | 184 | 1.98k | png_set_packing(png_handler.png_ptr); | 185 | 1.98k | png_set_scale_16(png_handler.png_ptr); | 186 | 1.98k | png_set_tRNS_to_alpha(png_handler.png_ptr); | 187 | | | 188 | 1.98k | int passes = png_set_interlace_handling(png_handler.png_ptr); | 189 | | | 190 | 1.98k | png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); | 191 | | | 192 | 1.98k | png_handler.row_ptr = png_malloc( | 193 | 1.98k | png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, | 194 | 1.98k | png_handler.info_ptr)); | 195 | | | 196 | 9.88k | for (int pass = 0; pass < passes; ++pass) { | 197 | 614k | for (png_uint_32 y = 0; y < height; ++y) { | 198 | 606k | png_read_row(png_handler.png_ptr, | 199 | 606k | static_cast<png_bytep>(png_handler.row_ptr), nullptr); | 200 | 606k | } | 201 | 7.90k | } | 202 | | | 203 | 1.98k | png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); | 204 | | | 205 | 1.98k | PNG_CLEANUP | 206 | | | 207 | 1.98k | #ifdef PNG_SIMPLIFIED_READ_SUPPORTED | 208 | | // Simplified READ API | 209 | 1.98k | png_image image; | 210 | 1.98k | memset(&image, 0, (sizeof image)); | 211 | 1.98k | image.version = PNG_IMAGE_VERSION; | 212 | | | 213 | 1.98k | if (!png_image_begin_read_from_memory(&image, data, size)) { | 214 | 11 | return 0; | 215 | 11 | } | 216 | | | 217 | 1.97k | image.format = PNG_FORMAT_RGBA; | 218 | 1.97k | std::vector<png_byte> buffer(PNG_IMAGE_SIZE(image)); | 219 | 1.97k | png_image_finish_read(&image, NULL, buffer.data(), 0, NULL); | 220 | 1.97k | #endif | 221 | | | 222 | 1.97k | return 0; | 223 | 1.98k | } |
|