Visual Servoing Platform  version 3.6.1 under development (2025-03-14)
vpImageIoLibpng.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 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 
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 
52 BEGIN_VISP_NAMESPACE
60 void 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 
153 void 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 
259 void 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 = 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 
435 void 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 = 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 
591 END_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.
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:544
Type * bitmap
points toward the bitmap
Definition: vpImage.h:135
unsigned int getHeight() const
Definition: vpImage.h:181
@ alpha_default
Definition: vpRGBa.h:72