Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpImageIoLibpng.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Libpng backend for PNG image I/O operations.
32 */
33
38
39#include "vpImageIoBackend.h"
40#include <visp3/core/vpImageConvert.h>
41
42#if defined(VISP_HAVE_PNG)
43#include <png.h>
44#endif
45
46//--------------------------------------------------------------------------
47// PNG
48//--------------------------------------------------------------------------
49
50#if defined(VISP_HAVE_PNG)
51
60void writePNGLibpng(const vpImage<unsigned char> &I, const std::string &filename)
61{
62 FILE *file;
63
64 // Test the filename
65 if (filename.empty()) {
66 throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty"));
67 }
68
69 file = fopen(filename.c_str(), "wb");
70
71 if (file == nullptr) {
72 throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str()));
73 }
74
75 /* create a png info struct */
76 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
77 if (!png_ptr) {
78 fclose(file);
79 throw(vpImageException(vpImageException::ioError, "PNG write error"));
80 }
81
82 png_infop info_ptr = png_create_info_struct(png_ptr);
83 if (!info_ptr) {
84 fclose(file);
85 png_destroy_write_struct(&png_ptr, nullptr);
86 throw(vpImageException(vpImageException::ioError, "PNG write error"));
87 }
88
89 /* initialize the setjmp for returning properly after a libpng error occurred
90 */
91 if (setjmp(png_jmpbuf(png_ptr))) {
92 fclose(file);
93 png_destroy_write_struct(&png_ptr, &info_ptr);
94 throw(vpImageException(vpImageException::ioError, "PNG write error"));
95 }
96
97 /* setup libpng for using standard C fwrite() function with our FILE pointer
98 */
99 png_init_io(png_ptr, file);
100
101 unsigned int width = I.getWidth();
102 unsigned int height = I.getHeight();
103 int bit_depth = 8;
104 int color_type = PNG_COLOR_TYPE_GRAY;
105 /* set some useful information from header */
106
107 if (setjmp(png_jmpbuf(png_ptr))) {
108 fclose(file);
109 png_destroy_write_struct(&png_ptr, &info_ptr);
110 throw(vpImageException(vpImageException::ioError, "PNG write error"));
111 }
112
113 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
114 PNG_FILTER_TYPE_BASE);
115
116 png_write_info(png_ptr, info_ptr);
117
118 png_bytep *row_ptrs = new png_bytep[height];
119 for (unsigned int i = 0; i < height; ++i)
120 row_ptrs[i] = new png_byte[width];
121
122 unsigned char *input = (unsigned char *)I.bitmap;
123
124 for (unsigned int i = 0; i < height; ++i) {
125 png_byte *row = row_ptrs[i];
126 for (unsigned int j = 0; j < width; ++j) {
127 row[j] = *(input);
128 input++;
129 }
130 }
131
132 png_write_image(png_ptr, row_ptrs);
133
134 png_write_end(png_ptr, nullptr);
135
136 for (unsigned int j = 0; j < height; ++j)
137 delete[] row_ptrs[j];
138
139 delete[] row_ptrs;
140
141 png_destroy_write_struct(&png_ptr, &info_ptr);
142
143 fclose(file);
144}
145
153void writePNGLibpng(const vpImage<vpRGBa> &I, const std::string &filename)
154{
155 FILE *file;
156
157 // Test the filename
158 if (filename.empty()) {
159 throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty"));
160 }
161
162 file = fopen(filename.c_str(), "wb");
163
164 if (file == nullptr) {
165 throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str()));
166 }
167
168 /* create a png info struct */
169 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
170 if (!png_ptr) {
171 fclose(file);
172 throw(vpImageException(vpImageException::ioError, "PNG write error"));
173 }
174
175 png_infop info_ptr = png_create_info_struct(png_ptr);
176 if (!info_ptr) {
177 fclose(file);
178 png_destroy_write_struct(&png_ptr, nullptr);
179 throw(vpImageException(vpImageException::ioError, "PNG write error"));
180 }
181
182 /* initialize the setjmp for returning properly after a libpng error occurred
183 */
184 if (setjmp(png_jmpbuf(png_ptr))) {
185 fclose(file);
186 png_destroy_write_struct(&png_ptr, &info_ptr);
187 throw(vpImageException(vpImageException::ioError, "PNG write error"));
188 }
189
190 /* setup libpng for using standard C fwrite() function with our FILE pointer
191 */
192 png_init_io(png_ptr, file);
193
194 unsigned int width = I.getWidth();
195 unsigned int height = I.getHeight();
196 int bit_depth = 8;
197 int color_type = PNG_COLOR_TYPE_RGB;
198 /* set some useful information from header */
199
200 if (setjmp(png_jmpbuf(png_ptr))) {
201 fclose(file);
202 png_destroy_write_struct(&png_ptr, &info_ptr);
203 throw(vpImageException(vpImageException::ioError, "PNG write error"));
204 }
205
206 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
207 PNG_FILTER_TYPE_BASE);
208
209 png_write_info(png_ptr, info_ptr);
210
211 png_bytep *row_ptrs = new png_bytep[height];
212 for (unsigned int i = 0; i < height; ++i)
213 row_ptrs[i] = new png_byte[3 * width];
214
215 unsigned char *input = (unsigned char *)I.bitmap;
216
217 for (unsigned int i = 0; i < height; ++i) {
218 png_byte *row = row_ptrs[i];
219 for (unsigned int j = 0; j < width; ++j) {
220 row[3 * j] = *(input);
221 input++;
222 row[3 * j + 1] = *(input);
223 input++;
224 row[3 * j + 2] = *(input);
225 input++;
226 input++;
227 }
228 }
229
230 png_write_image(png_ptr, row_ptrs);
231
232 png_write_end(png_ptr, nullptr);
233
234 for (unsigned int j = 0; j < height; ++j)
235 delete[] row_ptrs[j];
236
237 delete[] row_ptrs;
238
239 png_destroy_write_struct(&png_ptr, &info_ptr);
240
241 fclose(file);
242}
243
259void readPNGLibpng(vpImage<unsigned char> &I, const std::string &filename)
260{
261 FILE *file;
262 png_byte magic[8];
263 // Test the filename
264 if (filename.empty()) {
265 throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty"));
266 }
267
268 file = fopen(filename.c_str(), "rb");
269
270 if (file == nullptr) {
271 throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str()));
272 }
273
274 /* read magic number */
275 if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) {
276 fclose(file);
277 throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str()));
278 }
279
280 /* check for valid magic number */
281 if (png_sig_cmp(magic, 0, sizeof(magic))) {
282 fclose(file);
283 throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image",
284 filename.c_str()));
285 }
286
287 /* create a png read struct */
288 // printf("version %s\n", PNG_LIBPNG_VER_STRING);
289 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
290 if (png_ptr == nullptr) {
291 fprintf(stderr, "error: can't create a png read structure!\n");
292 fclose(file);
293 throw(vpImageException(vpImageException::ioError, "error reading png file"));
294 }
295
296 /* create a png info struct */
297 png_infop info_ptr = png_create_info_struct(png_ptr);
298 if (info_ptr == nullptr) {
299 fprintf(stderr, "error: can't create a png info structure!\n");
300 fclose(file);
301 png_destroy_read_struct(&png_ptr, nullptr, nullptr);
302 throw(vpImageException(vpImageException::ioError, "error reading png file"));
303 }
304
305 /* initialize the setjmp for returning properly after a libpng error occurred
306 */
307 if (setjmp(png_jmpbuf(png_ptr))) {
308 fclose(file);
309 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
310 throw(vpImageException(vpImageException::ioError, "PNG read error"));
311 }
312
313 /* setup libpng for using standard C fread() function with our FILE pointer
314 */
315 png_init_io(png_ptr, file);
316
317 /* tell libpng that we have already read the magic number */
318 png_set_sig_bytes(png_ptr, sizeof(magic));
319
320 /* read png info */
321 png_read_info(png_ptr, info_ptr);
322
323 unsigned int width = png_get_image_width(png_ptr, info_ptr);
324 unsigned int height = png_get_image_height(png_ptr, info_ptr);
325
326 unsigned int bit_depth, channels, color_type;
327 /* get some useful information from header */
328 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
329 channels = png_get_channels(png_ptr, info_ptr);
330 color_type = png_get_color_type(png_ptr, info_ptr);
331
332 /* convert index color images to RGB images */
333 if (color_type == PNG_COLOR_TYPE_PALETTE)
334 png_set_palette_to_rgb(png_ptr);
335
336 /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */
337 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
338 png_set_expand(png_ptr);
339
340 // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
341 // png_set_tRNS_to_alpha (png_ptr);
342
343 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
344 png_set_strip_alpha(png_ptr);
345
346 if (bit_depth == 16)
347 png_set_strip_16(png_ptr);
348 else if (bit_depth < 8)
349 png_set_packing(png_ptr);
350
351 /* update info structure to apply transformations */
352 png_read_update_info(png_ptr, info_ptr);
353
354 channels = png_get_channels(png_ptr, info_ptr);
355
356 if ((width != I.getWidth()) || (height != I.getHeight()))
357 I.resize(height, width);
358
359 png_bytep *rowPtrs = new png_bytep[height];
360
361 unsigned int stride = static_cast<unsigned int>(png_get_rowbytes(png_ptr, info_ptr));
362 unsigned char *data = new unsigned char[stride * height];
363
364 for (unsigned int i = 0; i < height; ++i)
365 rowPtrs[i] = (png_bytep)data + (i * stride);
366
367 png_read_image(png_ptr, rowPtrs);
368
369 vpImage<vpRGBa> Ic(height, width);
370 unsigned char *output;
371
372 switch (channels) {
373 case 1:
374 output = (unsigned char *)I.bitmap;
375 for (unsigned int i = 0; i < width * height; ++i) {
376 *(output++) = data[i];
377 }
378 break;
379
380 case 2:
381 output = (unsigned char *)I.bitmap;
382 for (unsigned int i = 0; i < width * height; ++i) {
383 *(output++) = data[i * 2];
384 }
385 break;
386
387 case 3:
388 output = (unsigned char *)Ic.bitmap;
389 for (unsigned int i = 0; i < width * height; ++i) {
390 *(output++) = data[i * 3];
391 *(output++) = data[i * 3 + 1];
392 *(output++) = data[i * 3 + 2];
393 *(output++) = vpRGBa::alpha_default;
394 }
396 break;
397
398 case 4:
399 output = (unsigned char *)Ic.bitmap;
400 for (unsigned int i = 0; i < width * height; ++i) {
401 *(output++) = data[i * 4];
402 *(output++) = data[i * 4 + 1];
403 *(output++) = data[i * 4 + 2];
404 *(output++) = data[i * 4 + 3];
405 }
407 break;
408 }
409
410 delete[](png_bytep) rowPtrs;
411 delete[] data;
412 png_read_end(png_ptr, nullptr);
413 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
414 fclose(file);
415}
416
435void readPNGLibpng(vpImage<vpRGBa> &I, const std::string &filename)
436{
437 FILE *file;
438 png_byte magic[8];
439
440 // Test the filename
441 if (filename.empty()) {
442 throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty"));
443 }
444
445 file = fopen(filename.c_str(), "rb");
446
447 if (file == nullptr) {
448 throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str()));
449 }
450
451 /* read magic number */
452 if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) {
453 fclose(file);
454 throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str()));
455 }
456
457 /* check for valid magic number */
458 if (png_sig_cmp(magic, 0, sizeof(magic))) {
459 fclose(file);
460 throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image",
461 filename.c_str()));
462 }
463
464 /* create a png read struct */
465 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
466 if (!png_ptr) {
467 fclose(file);
468 throw(vpImageException(vpImageException::ioError, "PNG read error"));
469 }
470
471 /* create a png info struct */
472 png_infop info_ptr = png_create_info_struct(png_ptr);
473 if (!info_ptr) {
474 fclose(file);
475 png_destroy_read_struct(&png_ptr, nullptr, nullptr);
476 throw(vpImageException(vpImageException::ioError, "PNG read error"));
477 }
478
479 /* initialize the setjmp for returning properly after a libpng error occurred
480 */
481 if (setjmp(png_jmpbuf(png_ptr))) {
482 fclose(file);
483 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
484 throw(vpImageException(vpImageException::ioError, "PNG read error"));
485 }
486
487 /* setup libpng for using standard C fread() function with our FILE pointer
488 */
489 png_init_io(png_ptr, file);
490
491 /* tell libpng that we have already read the magic number */
492 png_set_sig_bytes(png_ptr, sizeof(magic));
493
494 /* read png info */
495 png_read_info(png_ptr, info_ptr);
496
497 unsigned int width = png_get_image_width(png_ptr, info_ptr);
498 unsigned int height = png_get_image_height(png_ptr, info_ptr);
499
500 unsigned int bit_depth, channels, color_type;
501 /* get some useful information from header */
502 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
503 channels = png_get_channels(png_ptr, info_ptr);
504 color_type = png_get_color_type(png_ptr, info_ptr);
505
506 /* convert index color images to RGB images */
507 if (color_type == PNG_COLOR_TYPE_PALETTE)
508 png_set_palette_to_rgb(png_ptr);
509
510 /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */
511 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
512 png_set_expand(png_ptr);
513
514 // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
515 // png_set_tRNS_to_alpha (png_ptr);
516
517 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
518 png_set_strip_alpha(png_ptr);
519
520 if (bit_depth == 16)
521 png_set_strip_16(png_ptr);
522 else if (bit_depth < 8)
523 png_set_packing(png_ptr);
524
525 /* update info structure to apply transformations */
526 png_read_update_info(png_ptr, info_ptr);
527
528 channels = png_get_channels(png_ptr, info_ptr);
529
530 if ((width != I.getWidth()) || (height != I.getHeight()))
531 I.resize(height, width);
532
533 png_bytep *rowPtrs = new png_bytep[height];
534
535 unsigned int stride = static_cast<unsigned int>(png_get_rowbytes(png_ptr, info_ptr));
536 unsigned char *data = new unsigned char[stride * height];
537
538 for (unsigned int i = 0; i < height; ++i)
539 rowPtrs[i] = (png_bytep)data + (i * stride);
540
541 png_read_image(png_ptr, rowPtrs);
542
543 vpImage<unsigned char> Ig(height, width);
544 unsigned char *output;
545
546 switch (channels) {
547 case 1:
548 output = (unsigned char *)Ig.bitmap;
549 for (unsigned int i = 0; i < width * height; ++i) {
550 *(output++) = data[i];
551 }
553 break;
554
555 case 2:
556 output = (unsigned char *)Ig.bitmap;
557 for (unsigned int i = 0; i < width * height; ++i) {
558 *(output++) = data[i * 2];
559 }
561 break;
562
563 case 3:
564 output = (unsigned char *)I.bitmap;
565 for (unsigned int i = 0; i < width * height; ++i) {
566 *(output++) = data[i * 3];
567 *(output++) = data[i * 3 + 1];
568 *(output++) = data[i * 3 + 2];
569 *(output++) = vpRGBa::alpha_default;
570 }
571 break;
572
573 case 4:
574 output = (unsigned char *)I.bitmap;
575 for (unsigned int i = 0; i < width * height; ++i) {
576 *(output++) = data[i * 4];
577 *(output++) = data[i * 4 + 1];
578 *(output++) = data[i * 4 + 2];
579 *(output++) = data[i * 4 + 3];
580 }
581 break;
582 }
583
584 delete[](png_bytep) rowPtrs;
585 delete[] data;
586 png_read_end(png_ptr, nullptr);
587 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
588 fclose(file);
589}
590
591END_VISP_NAMESPACE
592
593#endif
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Error that can be emitted by the vpImage class and its derivatives.
@ ioError
Image io error.
Definition of the vpImage class member functions.
Definition vpImage.h:131
@ alpha_default
Definition vpRGBa.h:76