Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

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

Generated on Thu Jan 10 14:40:00 2008 for core/vil by  doxygen 1.4.4