Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPBitmapFormat.h"
25 : #include "SPLog.h"
26 : #include "SPFilesystem.h"
27 : #include "png.h"
28 :
29 : namespace STAPPLER_VERSIONIZED stappler::bitmap::png {
30 :
31 : struct ReadState {
32 : const uint8_t *data = nullptr;
33 : size_t offset = 0;
34 : };
35 :
36 8554 : static bool isPng(const uint8_t * data, size_t dataLen) {
37 8554 : if (dataLen <= 8) {
38 0 : return false;
39 : }
40 :
41 : static const unsigned char PNG_SIGNATURE[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
42 8554 : return memcmp(PNG_SIGNATURE, data, sizeof(PNG_SIGNATURE)) == 0;
43 : }
44 :
45 1819 : static bool getPngImageSize(const io::Producer &file, StackBuffer<512> &data, uint32_t &width, uint32_t &height) {
46 1819 : if (isPng(data.data(), data.size()) && data.size() >= 24) {
47 644 : auto reader = BytesViewNetwork(data.data() + 16, 8);
48 :
49 644 : width = reader.readUnsigned32();
50 644 : height = reader.readUnsigned32();
51 :
52 644 : return true;
53 : }
54 1175 : return false;
55 : }
56 :
57 55712 : static void readDynamicData(png_structp pngPtr, png_bytep data, png_size_t length) {
58 55712 : auto state = (ReadState *)png_get_io_ptr(pngPtr);
59 55712 : memcpy(data, state->data + state->offset, length);
60 55712 : state->offset += length;
61 55712 : }
62 :
63 : struct PngReadStruct {
64 2061 : ~PngReadStruct() {
65 2061 : if (png_ptr || info_ptr) {
66 2061 : png_destroy_read_struct(png_ptr ? &png_ptr : nullptr, info_ptr ? &info_ptr : nullptr, NULL);
67 2061 : png_ptr = nullptr;
68 2061 : info_ptr = nullptr;
69 : }
70 2061 : }
71 :
72 2061 : PngReadStruct() { }
73 :
74 2061 : bool init(const uint8_t *inputData, size_t size) {
75 2061 : png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
76 2061 : if (png_ptr == NULL) {
77 0 : log::error("libpng", "fail to create read struct");
78 0 : return false;
79 : }
80 :
81 2061 : info_ptr = png_create_info_struct(png_ptr);
82 2061 : if (info_ptr == NULL) {
83 0 : log::error("libpng", "fail to create info struct");
84 0 : png_destroy_read_struct(&png_ptr, NULL, NULL);
85 0 : return false;
86 : }
87 :
88 2061 : if (setjmp(png_jmpbuf(png_ptr))) {
89 0 : log::error("libpng", "error in processing (setjmp return)");
90 0 : return false;
91 : }
92 :
93 2061 : state.data = inputData;
94 2061 : state.offset = 0;
95 2061 : png_set_read_fn(png_ptr,(png_voidp)&state, readDynamicData);
96 :
97 : #ifdef PNG_ARM_NEON_API_SUPPORTED
98 : png_set_option(png_ptr, PNG_ARM_NEON, PNG_OPTION_ON);
99 : #endif
100 2061 : png_read_info(png_ptr, info_ptr);
101 2061 : return true;
102 : }
103 :
104 2061 : bool info(ImageInfo &info) {
105 2061 : if (setjmp(png_jmpbuf(png_ptr))) {
106 0 : log::error("libpng", "error in processing (setjmp return)");
107 0 : return false;
108 : }
109 :
110 2061 : info.width = png_get_image_width(png_ptr, info_ptr);
111 2061 : info.height = png_get_image_height(png_ptr, info_ptr);
112 :
113 2061 : png_byte bitdepth = png_get_bit_depth(png_ptr, info_ptr);
114 2061 : png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
115 :
116 2061 : if (color_type == PNG_COLOR_TYPE_PALETTE) {
117 0 : png_set_palette_to_rgb(png_ptr);
118 : }
119 2061 : if (color_type == PNG_COLOR_TYPE_GRAY && bitdepth < 8) {
120 0 : bitdepth = 8;
121 0 : png_set_expand_gray_1_2_4_to_8(png_ptr);
122 : }
123 2061 : if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
124 0 : png_set_tRNS_to_alpha(png_ptr);
125 : }
126 2061 : if (bitdepth == 16) {
127 0 : png_set_strip_16(png_ptr);
128 : }
129 2061 : if (bitdepth < 8) {
130 0 : png_set_packing(png_ptr);
131 : }
132 :
133 2061 : png_read_update_info(png_ptr, info_ptr);
134 2061 : bitdepth = png_get_bit_depth(png_ptr, info_ptr);
135 2061 : color_type = png_get_color_type(png_ptr, info_ptr);
136 2061 : auto rowbytes = png_get_rowbytes(png_ptr, info_ptr);
137 :
138 2061 : if (color_type == PNG_COLOR_TYPE_GRAY) {
139 354 : info.color = (info.color == PixelFormat::A8?PixelFormat::A8:PixelFormat::I8);
140 1707 : } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
141 354 : info.color = PixelFormat::IA88;
142 1353 : } else if (color_type == PNG_COLOR_TYPE_RGB) {
143 329 : info.color = PixelFormat::RGB888;
144 1024 : } else if (color_type == PNG_COLOR_TYPE_RGBA) {
145 1024 : info.color = PixelFormat::RGBA8888;
146 : } else {
147 0 : info.width = 0;
148 0 : info.height = 0;
149 0 : info.stride = 0;
150 0 : log::format(log::Error, "libpng", "unsupported color type: %u", (unsigned int)color_type);
151 0 : return false;
152 : }
153 :
154 2061 : info.stride = (uint32_t)rowbytes;
155 :
156 2061 : if (info.color == PixelFormat::I8 || info.color == PixelFormat::RGB888) {
157 683 : info.alpha = AlphaFormat::Opaque;
158 : } else {
159 1378 : info.alpha = AlphaFormat::Unpremultiplied;
160 : }
161 :
162 2061 : return true;
163 : }
164 :
165 1303 : bool load(BitmapWriter &outputData) {
166 1303 : if (!info(outputData)) {
167 0 : return false;
168 : }
169 :
170 1303 : if (setjmp(png_jmpbuf(png_ptr))) {
171 0 : log::error("libpng", "error in processing (setjmp return)");
172 0 : return false;
173 : }
174 :
175 1303 : if (outputData.getStride) {
176 75 : auto rowbytes = png_get_rowbytes(png_ptr, info_ptr);
177 75 : outputData.stride = max((uint32_t)outputData.getStride(outputData.target, outputData.color, outputData.width), (uint32_t)rowbytes);
178 : }
179 :
180 1303 : png_bytep row_pointers[outputData.height];
181 :
182 1303 : auto dataLen = outputData.stride * outputData.height;
183 :
184 1303 : outputData.resize(outputData.target, dataLen);
185 :
186 401156 : for (uint32_t i = 0; i < outputData.height; ++i) {
187 399853 : row_pointers[i] = outputData.getData(outputData.target, i * outputData.stride);
188 : }
189 :
190 1303 : png_read_image(png_ptr, row_pointers);
191 1303 : png_read_end(png_ptr, nullptr);
192 1303 : return true;
193 1303 : }
194 :
195 : ReadState state;
196 : png_structp png_ptr = nullptr;
197 : png_infop info_ptr = nullptr;
198 : };
199 :
200 : struct PngWriteStruct {
201 : int bit_depth = 8;
202 : png_structp png_ptr = nullptr;
203 : png_infop info_ptr = nullptr;
204 : bool valid = false;
205 :
206 : FILE *fp = nullptr;
207 : BitmapWriter *out = nullptr;
208 :
209 1426 : ~PngWriteStruct() {
210 1426 : if (png_ptr != nullptr) {
211 1426 : png_destroy_write_struct(&png_ptr, &info_ptr);
212 : }
213 :
214 1426 : if (fp) {
215 1150 : fclose(fp);
216 : }
217 1426 : }
218 :
219 1426 : PngWriteStruct() {
220 1426 : png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
221 1426 : if (png_ptr == nullptr) {
222 0 : log::error("libpng", "fail to create write struct");
223 0 : return;
224 : }
225 :
226 1426 : info_ptr = png_create_info_struct (png_ptr);
227 1426 : if (info_ptr == nullptr) {
228 0 : log::error("libpng", "fail to create info struct");
229 0 : return;
230 : }
231 : }
232 :
233 276 : PngWriteStruct(BitmapWriter *v) : PngWriteStruct() {
234 276 : out = v;
235 276 : valid = true;
236 276 : }
237 :
238 1150 : PngWriteStruct(StringView filename) : PngWriteStruct() {
239 1150 : fp = filesystem::native::fopen_fn(filename, "wb");
240 1150 : if (!fp) {
241 0 : log::error("libpng", "fail to open file '%s' to write png data", filename.data());
242 0 : valid = false;
243 0 : return;
244 : }
245 :
246 1150 : valid = true;
247 0 : }
248 :
249 10794 : static void writePngFn(png_structp png_ptr, png_bytep data, png_size_t length) {
250 10794 : auto out = (BitmapWriter *)png_get_io_ptr(png_ptr);
251 10794 : out->push(out->target, data, length);
252 10794 : }
253 :
254 1426 : bool write(const uint8_t *data, BitmapWriter &state, bool invert = false) {
255 1426 : if (!valid) {
256 0 : return false;
257 : }
258 :
259 1426 : if (!fp && !out) {
260 0 : return false;
261 : }
262 :
263 1426 : if (setjmp (png_jmpbuf (png_ptr))) {
264 0 : log::error("libpng", "error in processing (setjmp return)");
265 0 : return false;
266 : }
267 :
268 1426 : if (state.stride == 0) {
269 0 : state.stride = getBytesPerPixel(state.color) * state.width;
270 : }
271 :
272 1426 : int color_type = 0;
273 1426 : switch (state.color) {
274 44 : case PixelFormat::A8:
275 : case PixelFormat::I8:
276 44 : color_type = PNG_COLOR_TYPE_GRAY;
277 44 : break;
278 44 : case PixelFormat::IA88:
279 44 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
280 44 : break;
281 919 : case PixelFormat::RGB888:
282 919 : color_type = PNG_COLOR_TYPE_RGB;
283 919 : break;
284 419 : case PixelFormat::RGBA8888:
285 419 : color_type = PNG_COLOR_TYPE_RGBA;
286 419 : break;
287 0 : default:
288 0 : return false;
289 : }
290 :
291 : /* Set image attributes. */
292 1426 : png_set_IHDR (png_ptr, info_ptr, state.width, state.height, bit_depth,
293 : color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
294 :
295 : /* Initialize rows of PNG. */
296 1426 : png_byte *row_pointers[state.height];
297 193152 : for (size_t i = 0; i < state.height; ++i) {
298 191726 : row_pointers[i] = (png_byte *)data + (invert ? state.stride * (state.height - i - 1) : state.stride * i);
299 : }
300 :
301 1426 : if (fp) {
302 1150 : png_init_io (png_ptr, fp);
303 : } else {
304 276 : png_set_write_fn(png_ptr, out, &writePngFn, nullptr);
305 : }
306 :
307 1426 : png_set_rows (png_ptr, info_ptr, row_pointers);
308 1426 : png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
309 1426 : return true;
310 : }
311 : };
312 :
313 758 : static bool infoPng(const uint8_t *inputData, size_t size, ImageInfo &outputData) {
314 758 : PngReadStruct pngStruct;
315 1516 : return pngStruct.init(inputData, size) && pngStruct.info(outputData);
316 758 : }
317 :
318 1303 : static bool loadPng(const uint8_t *inputData, size_t size, BitmapWriter &outputData) {
319 1303 : PngReadStruct pngStruct;
320 2606 : return pngStruct.init(inputData, size) && pngStruct.load(outputData);
321 1303 : }
322 :
323 1150 : static bool savePng(StringView filename, const uint8_t *data, BitmapWriter &state, bool invert) {
324 1150 : PngWriteStruct s(filename);
325 2300 : return s.write(data, state, invert);
326 1150 : }
327 :
328 276 : static bool writePng(const uint8_t *data, BitmapWriter &state, bool invert) {
329 276 : PngWriteStruct s(&state);
330 552 : return s.write(data, state, invert);
331 276 : }
332 :
333 : }
|