core/vil/file_formats/vil_png.cxx
Go to the documentation of this file.
00001 // This is core/vil/file_formats/vil_png.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // http://www.libpng.org/pub/png/libpng.html
00008 
00009 #include "vil_png.h"
00010 
00011 #include <vcl_cassert.h>
00012 #include <vcl_cstring.h>
00013 #include <vcl_iostream.h>
00014 #include <vcl_algorithm.h>
00015 
00016 #include <vil/vil_stream.h>
00017 #include <vil/vil_image_view.h>
00018 #include <vil/vil_property.h>
00019 #include <vil/vil_exception.h>
00020 
00021 #include <png.h>
00022 #if (PNG_LIBPNG_VER_MAJOR == 0)
00023 extern "You need a later libpng. You should rerun CMake, after setting VXL_FORCE_V3P_PNG to ON."
00024 #endif
00025 #include <vcl_cstdlib.h> // for vcl_exit()
00026 
00027 #include <vxl_config.h>
00028 
00029 // Constants
00030 #define SIG_CHECK_SIZE 4
00031 
00032 char const* vil_png_format_tag = "png";
00033 
00034 // Functions
00035 static bool problem(char const* msg)
00036 {
00037   vcl_cerr << "[vil_png: PROBLEM " <<msg << ']';
00038   return false;
00039 }
00040 
00041 vil_image_resource_sptr vil_png_file_format::make_input_image(vil_stream* is)
00042 {
00043   // Attempt to read header
00044   png_byte sig_buf [SIG_CHECK_SIZE];
00045   if (is->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00046     problem("Initial header fread");
00047     return 0;
00048   }
00049 
00050   if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0)
00051     return 0;
00052 
00053   return new vil_png_image(is);
00054 }
00055 
00056 vil_image_resource_sptr vil_png_file_format::make_output_image(vil_stream* vs,
00057                                                                unsigned nx,
00058                                                                unsigned ny,
00059                                                                unsigned nplanes,
00060                                                                enum vil_pixel_format format)
00061 {
00062   if (format != VIL_PIXEL_FORMAT_BYTE &&
00063       format != VIL_PIXEL_FORMAT_UINT_16)
00064   // FIXME || format != VIL_PIXEL_FORMAT_BOOL
00065 
00066   {
00067     vcl_cout<<"ERROR! vil_png_file_format::make_output_image()\n"
00068             <<"Pixel format should be byte, but is "<<format<<" instead.\n";
00069     return 0;
00070   }
00071 
00072   return new vil_png_image(vs, nx, ny, nplanes, format);
00073 }
00074 
00075 char const* vil_png_file_format::tag() const
00076 {
00077   return vil_png_format_tag;
00078 }
00079 
00080 /////////////////////////////////////////////////////////////////////////////
00081 
00082 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
00083 {
00084   vil_stream* f = static_cast<vil_stream*>(png_get_io_ptr(png_ptr));
00085   f->read(data, length);
00086 }
00087 
00088 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
00089 {
00090   vil_stream* f = static_cast<vil_stream*>(png_get_io_ptr(png_ptr));
00091   f->write(data, length);
00092 }
00093 
00094 static void user_flush_data(png_structp /*png_ptr*/)
00095 {
00096   // IOFile* f = (IOFile*)png_get_io_ptr(png_ptr);
00097   // urk.  how to flush?
00098 }
00099 
00100 struct vil_jmpbuf_wrapper
00101 {
00102   jmp_buf jmpbuf;
00103 };
00104 static vil_jmpbuf_wrapper pngtopnm_jmpbuf_struct;
00105 static bool jmpbuf_ok = false;
00106 
00107 // Must be  a macro - setjmp needs its stack frame to live
00108 #define png_setjmp_on(ACTION) \
00109   do {\
00110     jmpbuf_ok = true;\
00111     if (setjmp (pngtopnm_jmpbuf_struct.jmpbuf) != 0) {\
00112       problem("png_setjmp_on");\
00113       ACTION;\
00114     }\
00115   } while (false);
00116 #define png_setjmp_off() (jmpbuf_ok = false)
00117 
00118 // this function, aside from the extra step of retrieving the "error
00119 // pointer" (below) and the fact that it exists within the application
00120 // rather than within libpng, is essentially identical to libpng's
00121 // default error handler.  The second point is critical:  since both
00122 // setjmp() and longjmp() are called from the same code, they are
00123 // guaranteed to have compatible notions of how big a jmp_buf is,
00124 // regardless of whether _BSD_SOURCE or anything else has (or has not)
00125 // been defined.
00126 //
00127 static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
00128 {
00129   vcl_cerr << "vil_png:  fatal libpng error: " << msg << '\n';
00130 
00131   if (!jmpbuf_ok) {
00132     // Someone called the error handler when the setjmp was wrong
00133     vcl_cerr << "vil_png: jmpbuf is pretty far from ok.  returning\n";
00134     // vcl_abort();
00135     return;
00136   }
00137 
00138   vil_jmpbuf_wrapper  *jmpbuf_ptr = static_cast<vil_jmpbuf_wrapper*>(png_get_error_ptr(png_ptr));
00139   if (jmpbuf_ptr == NULL) {         // we are completely hosed now
00140     vcl_cerr << "pnmtopng:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n";
00141     vcl_exit(99);
00142   }
00143 
00144   longjmp(jmpbuf_ptr->jmpbuf, 1);
00145 }
00146 
00147 struct vil_png_structures
00148 {
00149   bool reading_;
00150   png_struct *png_ptr;
00151   png_info *info_ptr;
00152   png_byte **rows;
00153   int channels;
00154   bool ok;
00155 
00156   vil_png_structures(bool reading)
00157   {
00158     reading_ = reading;
00159     png_ptr = 0;
00160     info_ptr = 0;
00161     rows = 0;
00162     channels = 0;
00163     ok = false;
00164 
00165     png_setjmp_on(return);
00166 
00167     if (reading)
00168       png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00169     else
00170       png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00171 
00172     if (!png_ptr) {
00173       problem("cannot allocate LIBPNG structure");
00174       return;
00175     }
00176 
00177     info_ptr = png_create_info_struct (png_ptr);
00178     if (!info_ptr) {
00179       png_destroy_read_struct(&png_ptr, NULL, NULL);
00180       problem("cannot allocate LIBPNG structures");
00181       return;
00182     }
00183 
00184     ok = true;
00185 
00186     // Now jmpbuf is broken, hope noone calls png....
00187     png_setjmp_off();
00188   }
00189 
00190   bool alloc_image()
00191   {
00192     rows = new png_byte* [info_ptr->height];
00193     if (rows == 0)
00194       return ok = problem("couldn't allocate space for image");
00195 
00196     unsigned long linesize;
00197     if (png_get_bit_depth(png_ptr, info_ptr) == 16)
00198       linesize = 2 * info_ptr->width;
00199     else
00200       linesize = info_ptr->width;
00201 
00202     if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA)
00203       linesize *= 2;
00204     else
00205       if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)
00206         linesize *= 3;
00207       else
00208         if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA)
00209           linesize *= 4;
00210 
00211     unsigned int height = png_get_image_height(png_ptr,info_ptr);
00212     // Alloc the whole thing at once
00213     rows[0] = new png_byte[linesize * height];
00214     if (!rows[0])
00215       return ok = problem("couldn't allocate space for image");
00216 
00217     // Re-point rows.
00218     for (unsigned int y = 1; y < height; ++y)
00219       rows[y] = rows[0] + y * linesize;
00220 
00221     return true;
00222   }
00223 
00224   png_byte** get_rows()
00225   {
00226     if (reading_) {
00227       if (!rows) {
00228         if (alloc_image()) {
00229           png_setjmp_on(return 0);
00230           png_read_image (png_ptr, rows);
00231           png_read_end (png_ptr, info_ptr);
00232           png_setjmp_off();
00233         }
00234       }
00235     } else {
00236       assert(rows != 0);
00237     }
00238 
00239     return rows;
00240   }
00241 
00242   ~vil_png_structures()
00243   {
00244     png_setjmp_on(goto del);
00245     if (reading_) {
00246       // Reading - just delete
00247       png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
00248 
00249     } else {
00250       // Writing - save the rows
00251       png_write_image(png_ptr, rows);
00252       png_write_end(png_ptr, info_ptr);
00253 
00254       png_destroy_write_struct (&png_ptr, &info_ptr);
00255     }
00256     png_setjmp_off();
00257 
00258    del:
00259     if (rows) {
00260       delete [] rows[0];
00261       delete [] rows;
00262     }
00263   }
00264 };
00265 
00266 
00267 /////////////////////////////////////////////////////////////////////////////
00268 
00269 vil_png_image::vil_png_image(vil_stream* is)
00270 : vs_(is),
00271   p_(new vil_png_structures(true))
00272 {
00273   vs_->ref();
00274   read_header();
00275 }
00276 
00277 bool vil_png_image::get_property(char const *key, void * value) const
00278 {
00279   if (vcl_strcmp(vil_property_quantisation_depth, key)==0)
00280   {
00281     if (value)
00282       *static_cast<unsigned int*>(value) = bits_per_component_;
00283     return true;
00284   }
00285 
00286   return false;
00287 }
00288 
00289 vil_png_image::vil_png_image(vil_stream *s,
00290                              unsigned nx,
00291                              unsigned ny,
00292                              unsigned nplanes,
00293                              enum vil_pixel_format format)
00294 : vs_(s),
00295   width_(nx),
00296   height_(ny),
00297   components_(nplanes),
00298   format_(format),
00299   p_(new vil_png_structures(false))
00300 {
00301   if (format == VIL_PIXEL_FORMAT_BOOL) bits_per_component_ = 1;
00302   else bits_per_component_ = vil_pixel_format_sizeof_components(format) * 8;
00303 
00304   vs_->ref();
00305   write_header();
00306   assert(format == VIL_PIXEL_FORMAT_BYTE ||
00307          format == VIL_PIXEL_FORMAT_UINT_16);
00308   // FIXME || format == VIL_PIXEL_FORMAT_BOOL
00309 }
00310 
00311 vil_png_image::~vil_png_image()
00312 {
00313   delete p_;
00314   vs_->unref();
00315 }
00316 
00317 
00318 char const* vil_png_image::file_format() const
00319 {
00320   return vil_png_format_tag;
00321 }
00322 
00323 bool vil_png_image::read_header()
00324 {
00325   if (!p_->ok)
00326     return false;
00327 
00328   png_setjmp_on(return false);
00329 
00330   vs_->seek(0L);
00331   png_byte sig_buf [SIG_CHECK_SIZE];
00332   if (vs_->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00333     png_setjmp_off();
00334     return problem("Initial header fread");
00335   }
00336 
00337   if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0) {
00338     png_setjmp_off();
00339     return problem("png_sig_cmp");
00340   }
00341 
00342   png_set_read_fn(p_->png_ptr, vs_, user_read_data);
00343   png_set_sig_bytes (p_->png_ptr, SIG_CHECK_SIZE);
00344   png_read_info (p_->png_ptr, p_->info_ptr);
00345 
00346   if (png_get_bit_depth(p_->png_ptr, p_->info_ptr) < 8)
00347     png_set_packing (p_->png_ptr);
00348 
00349 #if 0
00350  int maxval;
00351  if (p->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { maxval = 255; }
00352  else { maxval = (1l << p->info_ptr->bit_depth) - 1; }
00353 #endif
00354 
00355   p_->channels = png_get_channels(p_->png_ptr, p_->info_ptr);
00356 
00357 #if VXL_LITTLE_ENDIAN
00358   // PNG stores data MSB
00359   if ( png_get_bit_depth(p_->png_ptr, p_->info_ptr) > 8 )
00360     png_set_swap(p_->png_ptr);
00361 #endif
00362 
00363   this->width_ = png_get_image_width(p_->png_ptr, p_->info_ptr);
00364   this->height_ = png_get_image_height(p_->png_ptr, p_->info_ptr);
00365   this->components_ = png_get_channels(p_->png_ptr, p_->info_ptr);
00366   this->bits_per_component_ = png_get_bit_depth(p_->png_ptr, p_->info_ptr);
00367 
00368   png_color_8p sig_bit;
00369   if (png_get_sBIT(p_->png_ptr, p_->info_ptr, &sig_bit)) {
00370     png_byte max_bits = sig_bit->red;
00371     max_bits = vcl_max( max_bits, sig_bit->green );
00372     max_bits = vcl_max( max_bits, sig_bit->blue );
00373     max_bits = vcl_max( max_bits, sig_bit->gray );
00374     max_bits = vcl_max( max_bits, sig_bit->alpha );
00375     this->bits_per_component_ = max_bits;
00376 
00377     png_set_shift(p_->png_ptr, sig_bit);
00378   }
00379 
00380   if (this->bits_per_component_ == 1) format_ = VIL_PIXEL_FORMAT_BOOL;
00381   else if (this->bits_per_component_ <=8) format_=VIL_PIXEL_FORMAT_BYTE;
00382   else format_ = VIL_PIXEL_FORMAT_UINT_16;
00383 
00384 
00385   // if (p->info_ptr->valid & PNG_INFO_bKGD) problem("LAZY AWF! PNG_INFO_bKGD");
00386   png_setjmp_off();
00387   return true;
00388 }
00389 
00390 bool vil_png_image::write_header()
00391 {
00392   if (!p_->ok)
00393     return false;
00394 
00395   png_setjmp_on( return false );
00396 
00397   vs_->seek(0L);
00398 
00399   png_set_write_fn(p_->png_ptr, vs_, user_write_data, user_flush_data);
00400 
00401   int color_type;
00402   if (components_ == 4)
00403     color_type = PNG_COLOR_TYPE_RGB_ALPHA;
00404   else if (components_ == 3)
00405     color_type = PNG_COLOR_TYPE_RGB;
00406   else if (components_ == 2)
00407     color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
00408   else
00409     color_type = PNG_COLOR_TYPE_GRAY;
00410 
00411   png_set_IHDR(p_->png_ptr, p_->info_ptr, width_, height_, bits_per_component_, color_type,
00412                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00413 
00414   png_write_info(p_->png_ptr, p_->info_ptr);
00415 
00416 #if VXL_LITTLE_ENDIAN
00417   // PNG stores data MSB
00418   if ( bits_per_component_ > 8 )
00419     png_set_swap(p_->png_ptr);
00420 #endif
00421 
00422   // Make memory image
00423   p_->channels = components_;
00424   p_->alloc_image();
00425 
00426   png_setjmp_off();
00427 
00428   return true;
00429 }
00430 
00431 vil_image_view_base_sptr vil_png_image::get_copy_view(unsigned x0,
00432                                                       unsigned nx,
00433                                                       unsigned y0,
00434                                                       unsigned ny) const
00435 {
00436   if (!p_->ok)
00437     return 0;
00438 
00439   // PNG lib wants everything in memory - the first get_rows reads the whole image.
00440   png_byte** rows = p_->get_rows();
00441   if (!rows) return 0;
00442 
00443   int bit_depth = ((bits_per_component_ + 7)/8)*8;
00444   int bytes_per_pixel = (bit_depth * p_->channels + 7) / 8;
00445   int bytes_per_row_dst = nx*nplanes() * vil_pixel_format_sizeof_components(format_);
00446 
00447   vil_memory_chunk_sptr chunk = new vil_memory_chunk(ny*bytes_per_row_dst, format_);
00448 
00449   if ((bit_depth==16) &&
00450       (nx == png_get_image_width(p_->png_ptr, p_->info_ptr)))
00451   {
00452     assert(x0 == 0);
00453 
00454     vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00455     return new vil_image_view<vxl_uint_16>(chunk, reinterpret_cast<vxl_uint_16*>(chunk->data()),
00456       nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00457   }
00458   if ((bit_depth ==8) &&
00459       (nx == png_get_image_width(p_->png_ptr, p_->info_ptr)))
00460   {
00461     assert(x0 == 0);
00462 
00463     vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00464     return new vil_image_view<vxl_byte>(chunk, reinterpret_cast<vxl_byte*>(chunk->data()),
00465       nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00466   }
00467   else if (bit_depth==16)
00468   {
00469     png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00470     for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00471       vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00472     return new vil_image_view<vxl_uint_16>(chunk, reinterpret_cast<vxl_uint_16*>(chunk->data()),
00473       nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00474   }
00475   else if (bit_depth==8)
00476   {
00477     png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00478     for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00479       vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00480     return new vil_image_view<vxl_byte>(chunk, reinterpret_cast<vxl_byte*>(chunk->data()),
00481       nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00482   }
00483   // FIXME Can't handle pixel depths of 1 2 or 4 pixels yet
00484   else return 0;
00485 }
00486 
00487 bool vil_png_image::put_view(const vil_image_view_base &view,
00488                              unsigned x0, unsigned y0)
00489 {
00490   if (!view_fits(view, x0, y0))
00491   {
00492     vil_exception_warning(vil_exception_out_of_bounds("vil_png_image::put_view"));
00493     return false;
00494   }
00495 
00496   if (!p_->ok) return false;
00497 
00498   // PNG lib wants everything in memory - the writing isn't done till this image is deleted.
00499 
00500   png_byte** rows = p_->get_rows();
00501   if (!rows) return false;
00502 
00503   // int bytes_per_pixel = bit_depth  * p_->channels / 8;
00504   // int bytes_per_row_dst = view.ni()*bytes_per_pixel;
00505   if (bits_per_component_ == 8)
00506   {
00507     if (view.pixel_format() != VIL_PIXEL_FORMAT_BYTE) return false;
00508     const vil_image_view<vxl_byte> &view2 = static_cast<const vil_image_view<vxl_byte>&>(view);
00509     if (nplanes()==1)
00510     {
00511       for (unsigned y = 0; y < view.nj(); ++y)
00512         for (unsigned x=0; x < view.ni(); ++x)
00513           rows[y0+y][x0+x] = view2(x,y);
00514     }
00515     else if (nplanes()==2)
00516     {
00517       for (unsigned y = 0; y < view.nj(); ++y)
00518         for (unsigned x=0; x < view.ni(); ++x)
00519         {
00520           rows[y0+y][(x0+x)*2] = view2(x,y,0);
00521           rows[y0+y][(x0+x)*2+1] = view2(x,y,1);
00522         }
00523     }
00524     else if (nplanes()==3)
00525     {
00526       for (unsigned y = 0; y < view.nj(); ++y)
00527         for (unsigned x=0; x < view.ni(); ++x)
00528         {
00529           rows[y0+y][(x0+x)*3] = view2(x,y,0);
00530           rows[y0+y][(x0+x)*3+1] = view2(x,y,1);
00531           rows[y0+y][(x0+x)*3+2] = view2(x,y,2);
00532         }
00533     }
00534     else
00535     {
00536       assert(nplanes() == 4);
00537       for (unsigned y = 0; y < view.nj(); ++y)
00538         for (unsigned x=0; x < view.ni(); ++x)
00539         {
00540           rows[y0+y][(x0+x)*4] = view2(x,y,0);
00541           rows[y0+y][(x0+x)*4+1] = view2(x,y,1);
00542           rows[y0+y][(x0+x)*4+2] = view2(x,y,2);
00543           rows[y0+y][(x0+x)*4+3] = view2(x,y,3);
00544         }
00545     }
00546   }
00547   else if (bits_per_component_ == 16)
00548   {
00549     if (view.pixel_format() != VIL_PIXEL_FORMAT_UINT_16) return false;
00550     const vil_image_view<vxl_uint_16> &view2 = static_cast<const vil_image_view<vxl_uint_16>&>(view);
00551     if (nplanes()==1)
00552     {
00553       for (unsigned y = 0; y < view.nj(); ++y)
00554         for (unsigned x=0; x < view.ni(); ++x)
00555           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*2]) = view2(x,y);
00556     }
00557     else if (nplanes() == 3)
00558     {
00559       for (unsigned y = 0; y < view.nj(); ++y)
00560         for (unsigned x=0; x < view.ni(); ++x)
00561         {
00562           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6]) = view2(x,y,0);
00563           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6+2]) = view2(x,y,1);
00564           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6+4]) = view2(x,y,2);
00565         }
00566     }
00567     else
00568     {
00569       assert(nplanes() == 4);
00570       for (unsigned y = 0; y < view.nj(); ++y)
00571         for (unsigned x=0; x < view.ni(); ++x)
00572         {
00573           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8]) = view2(x,y,0);
00574           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+2]) = view2(x,y,1);
00575           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+4]) = view2(x,y,2);
00576           *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+6]) = view2(x,y,3);
00577         }
00578     }
00579   }
00580   // FIXME else if (bit_depth=1)
00581   else return false;
00582 
00583   return true;
00584 }