core/vil1/file_formats/vil1_png.cxx

Go to the documentation of this file.
00001 // This is core/vil1/file_formats/vil1_png.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // http://www.mirror.ac.uk/sites/ftp.cdrom.com/pub/png/libpng.html
00008 
00009 #include "vil1_png.h"
00010 
00011 #include <vcl_cassert.h>
00012 #include <vcl_cstring.h>
00013 #include <vcl_iostream.h>
00014 
00015 #include <vil1/vil1_stream.h>
00016 #include <vil1/vil1_image_impl.h>
00017 #include <vil1/vil1_image.h>
00018 #include <vil1/vil1_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* vil1_png_format_tag = "png";
00032 
00033 // Functions
00034 static bool problem(char const* msg)
00035 {
00036   vcl_cerr << "[vil1_png: PROBLEM " <<msg << ']';
00037   return false;
00038 }
00039 
00040 vil1_image_impl* vil1_png_file_format::make_input_image(vil1_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 vil1_png_generic_image(is);
00053 }
00054 
00055 vil1_image_impl* vil1_png_file_format::make_output_image(vil1_stream* is, int planes,
00056                                                          int width,
00057                                                          int height,
00058                                                          int components,
00059                                                          int bits_per_component,
00060                                                          vil1_component_format format)
00061 {
00062   return new vil1_png_generic_image(is, planes, width, height, components, bits_per_component, format);
00063 }
00064 
00065 char const* vil1_png_file_format::tag() const
00066 {
00067   return vil1_png_format_tag;
00068 }
00069 
00070 /////////////////////////////////////////////////////////////////////////////
00071 
00072 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
00073 {
00074   vil1_stream* f = (vil1_stream*)png_get_io_ptr(png_ptr);
00075   f->read(data, length);
00076 }
00077 
00078 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
00079 {
00080   vil1_stream* f = (vil1_stream*)png_get_io_ptr(png_ptr);
00081   f->write(data, length);
00082 }
00083 
00084 static void user_flush_data(png_structp /*png_ptr*/)
00085 {
00086   vcl_cerr << "vil1_png_generic_image::user_flush_data() NYI\n";
00087 #if 0 // NYI
00088   IOFile* f = (IOFile*)png_get_io_ptr(png_ptr);
00089   // urk.  how to flush?
00090 #endif
00091 }
00092 
00093 struct vil1_jmpbuf_wrapper
00094 {
00095   jmp_buf jmpbuf;
00096 };
00097 static vil1_jmpbuf_wrapper pngtopnm_jmpbuf_struct;
00098 static bool jmpbuf_ok = false;
00099 
00100 // Must be  a macro - setjmp needs its stack frame to live
00101 #define png_setjmp_on(ACTION) \
00102   do {\
00103     jmpbuf_ok = true;\
00104     if (setjmp (pngtopnm_jmpbuf_struct.jmpbuf) != 0) {\
00105       problem("png_setjmp_on");\
00106       ACTION;\
00107     }\
00108   } while (false);
00109 #define png_setjmp_off() (jmpbuf_ok = false)
00110 
00111 // this function, aside from the extra step of retrieving the "error
00112 // pointer" (below) and the fact that it exists within the application
00113 // rather than within libpng, is essentially identical to libpng's
00114 // default error handler.  The second point is critical:  since both
00115 // setjmp() and longjmp() are called from the same code, they are
00116 // guaranteed to have compatible notions of how big a jmp_buf is,
00117 // regardless of whether _BSD_SOURCE or anything else has (or has not)
00118 // been defined.
00119 //
00120 static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
00121 {
00122   vcl_cerr << "vil1_png:  fatal libpng error: " << msg << '\n';
00123 
00124   if (!jmpbuf_ok) {
00125     // Someone called the error handler when the setjmp was wrong
00126     vcl_cerr << "vil1_png: jmpbuf is pretty far from ok.  returning\n";
00127     // vcl_abort();
00128     return;
00129   }
00130 
00131   vil1_jmpbuf_wrapper  *jmpbuf_ptr = (vil1_jmpbuf_wrapper*) png_get_error_ptr(png_ptr);
00132   if (jmpbuf_ptr == NULL) {         // we are completely hosed now
00133     vcl_cerr << "pnmtopng:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n";
00134     vcl_exit(99);
00135   }
00136 
00137   longjmp(jmpbuf_ptr->jmpbuf, 1);
00138 }
00139 
00140 struct vil1_png_structures
00141 {
00142   bool reading_;
00143   png_struct *png_ptr;
00144   png_info *info_ptr;
00145   png_byte **rows;
00146   int channels;
00147   bool ok;
00148 
00149   vil1_png_structures(bool reading)
00150   {
00151     reading_ = reading;
00152     png_ptr = 0;
00153     info_ptr = 0;
00154     rows = 0;
00155     channels = 0;
00156     ok = false;
00157 
00158     png_setjmp_on(return);
00159 
00160     if (reading)
00161       png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00162     else
00163       png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00164 
00165     if (!png_ptr) {
00166       problem("cannot allocate LIBPNG structure");
00167       return;
00168     }
00169 
00170     info_ptr = png_create_info_struct (png_ptr);
00171     if (!info_ptr) {
00172       problem("cannot allocate LIBPNG structures");
00173       return;
00174     }
00175 
00176     ok = true;
00177 
00178     // Now jmpbuf is broken, hope noone calls png....
00179     png_setjmp_off();
00180   }
00181 
00182   bool alloc_image()
00183   {
00184     rows = new png_byte* [info_ptr->height];
00185     if (rows == 0)
00186       return ok = problem("couldn't allocate space for image");
00187 
00188     unsigned long linesize;
00189     if (png_get_bit_depth( png_ptr, info_ptr ) == 16)
00190       linesize = 2 * info_ptr->width;
00191     else
00192       linesize = info_ptr->width;
00193 
00194     if (png_get_color_type( png_ptr, info_ptr ) == PNG_COLOR_TYPE_GRAY_ALPHA)
00195       linesize *= 2;
00196     else
00197       if (png_get_color_type( png_ptr, info_ptr ) == PNG_COLOR_TYPE_RGB)
00198         linesize *= 3;
00199       else
00200         if (png_get_color_type( png_ptr, info_ptr ) == PNG_COLOR_TYPE_RGB_ALPHA)
00201           linesize *= 4;
00202 
00203     unsigned int height = png_get_image_height(png_ptr,info_ptr);
00204     // Alloc the whole thing at once
00205     rows[0] = new png_byte[linesize * height];
00206     if (!rows[0])
00207       return ok = problem("couldn't allocate space for image");
00208 
00209     // Re-point rows.
00210     for (unsigned int y = 1; y < height; ++y)
00211       rows[y] = rows[0] + y * linesize;
00212 
00213     return true;
00214   }
00215 
00216   png_byte** get_rows()
00217   {
00218     if (reading_) {
00219       if (!rows) {
00220         if (alloc_image()) {
00221           png_setjmp_on(return 0);
00222           png_read_image (png_ptr, rows);
00223           png_read_end (png_ptr, info_ptr);
00224           png_setjmp_off();
00225         }
00226       }
00227     } else {
00228       assert(rows != 0);
00229     }
00230 
00231     return rows;
00232   }
00233 
00234   ~vil1_png_structures()
00235   {
00236     png_setjmp_on(goto del);
00237     if (reading_) {
00238       // Reading - just delete
00239       png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
00240 
00241     } else {
00242       // Writing - save the rows
00243       png_write_image(png_ptr, rows);
00244       png_write_end(png_ptr, info_ptr);
00245 
00246       png_destroy_write_struct (&png_ptr, &info_ptr);
00247     }
00248     png_setjmp_off();
00249 
00250    del:
00251     if (rows) {
00252       delete [] rows[0];
00253       delete [] rows;
00254     }
00255   }
00256 };
00257 
00258 
00259 /////////////////////////////////////////////////////////////////////////////
00260 
00261 vil1_png_generic_image::vil1_png_generic_image(vil1_stream* is)
00262 : vs_(is),
00263   p(new vil1_png_structures(true))
00264 {
00265   vs_->ref();
00266   read_header();
00267 }
00268 
00269 bool vil1_png_generic_image::get_property(char const *tag, void *prop) const
00270 {
00271   if (0==vcl_strcmp(tag, vil1_property_top_row_first))
00272     return prop ? (*(bool*)prop) = true : true;
00273 
00274   if (0==vcl_strcmp(tag, vil1_property_left_first))
00275     return prop ? (*(bool*)prop) = true : true;
00276 
00277   return false;
00278 }
00279 
00280 vil1_png_generic_image::vil1_png_generic_image(vil1_stream* is, int /*planes*/,
00281                                                int width,
00282                                                int height,
00283                                                int components,
00284                                                int bits_per_component,
00285                                                vil1_component_format /*format*/)
00286 : vs_(is),
00287   width_(width),
00288   height_(height),
00289   components_(components),
00290   bits_per_component_(bits_per_component),
00291   p(new vil1_png_structures(false))
00292 {
00293   vs_->ref();
00294   write_header();
00295 }
00296 
00297 vil1_png_generic_image::~vil1_png_generic_image()
00298 {
00299   delete p;
00300   vs_->unref();
00301 }
00302 
00303 char const* vil1_png_generic_image::file_format() const
00304 {
00305   return vil1_png_format_tag;
00306 }
00307 
00308 bool vil1_png_generic_image::read_header()
00309 {
00310   if (!p->ok)
00311     return false;
00312 
00313   png_setjmp_on(return false);
00314 
00315   vs_->seek(0L);
00316   png_byte sig_buf [SIG_CHECK_SIZE];
00317   if (vs_->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00318     png_setjmp_off();
00319     return problem("Initial header fread");
00320   }
00321 
00322   if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0) {
00323     png_setjmp_off();
00324     return problem("png_sig_cmp");
00325   }
00326 
00327   png_set_read_fn(p->png_ptr, vs_, user_read_data);
00328   png_set_sig_bytes (p->png_ptr, SIG_CHECK_SIZE);
00329   png_read_info (p->png_ptr, p->info_ptr);
00330 
00331   if (png_get_bit_depth( p->png_ptr, p->info_ptr ) < 8)
00332     png_set_packing (p->png_ptr);
00333 
00334 #if 0
00335  int maxval;
00336  if (p->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { maxval = 255; }
00337  else { maxval = (1l << p->info_ptr->bit_depth) - 1; }
00338 #endif
00339 
00340   p->channels = png_get_channels(p->png_ptr, p->info_ptr);
00341 
00342 #if VXL_LITTLE_ENDIAN
00343   // PNG stores data MSB
00344   if ( png_get_bit_depth( p->png_ptr, p->info_ptr ) > 8 )
00345     png_set_swap( p->png_ptr );
00346 #endif
00347 
00348   this->width_ = png_get_image_width( p->png_ptr, p->info_ptr );
00349   this->height_ = png_get_image_height( p->png_ptr, p->info_ptr );
00350   this->components_ = png_get_channels( p->png_ptr, p->info_ptr );
00351   this->bits_per_component_ = png_get_bit_depth( p->png_ptr, p->info_ptr );
00352 
00353   if (png_get_valid( p->png_ptr, p->info_ptr, PNG_INFO_sBIT )) problem("LAZY AWF! PNG_INFO_sBIT");
00354 
00355   // if (p->info_ptr->valid & PNG_INFO_bKGD) problem("LAZY AWF! PNG_INFO_bKGD");
00356   png_setjmp_off();
00357   return true;
00358 }
00359 
00360 bool vil1_png_generic_image::write_header()
00361 {
00362   if (!p->ok)
00363     return false;
00364 
00365   png_setjmp_on( return false );
00366 
00367   vs_->seek(0L);
00368 
00369   png_set_write_fn(p->png_ptr, vs_, user_write_data, user_flush_data);
00370 
00371   int color_type;
00372   if (components_ == 3)
00373     color_type = PNG_COLOR_TYPE_RGB;
00374   else
00375     color_type = PNG_COLOR_TYPE_GRAY;
00376 
00377   png_set_IHDR(p->png_ptr, p->info_ptr, width_, height_, bits_per_component_, color_type,
00378                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00379 
00380   png_write_info(p->png_ptr, p->info_ptr);
00381 
00382 #if VXL_LITTLE_ENDIAN
00383   // PNG stores data MSB
00384   if ( bits_per_component_ > 8 )
00385     png_set_swap( p->png_ptr );
00386 #endif
00387 
00388   // Make memory image
00389   p->channels = components_;
00390   p->alloc_image();
00391 
00392   png_setjmp_off();
00393 
00394   return true;
00395 }
00396 
00397 bool vil1_png_generic_image::get_section(void* buf, int x0, int y0, int xs, int ys) const
00398 {
00399   if (!p->ok)
00400     return false;
00401 
00402   // PNG lib wants everything in memory - the first get_rows reads the whole image.
00403   png_byte** rows = p->get_rows();
00404   if (!rows) return 0;
00405 
00406   int bytes_per_pixel = png_get_bit_depth(p->png_ptr,p->info_ptr) * p->channels / 8;
00407   int bytes_per_row_dst = xs*bytes_per_pixel;
00408   if ((unsigned int)xs == png_get_image_width(p->png_ptr, p->info_ptr)) {
00409     assert(x0 == 0);
00410     vcl_memcpy(buf, rows[y0], ys * bytes_per_row_dst);
00411   }
00412   else {
00413     png_byte* dst = (png_byte*)buf;
00414     for (int y = 0; y < ys; ++y, dst += bytes_per_row_dst)
00415       vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], xs*bytes_per_pixel);
00416   }
00417 
00418   return true;
00419 }
00420 
00421 bool vil1_png_generic_image::put_section(void const* buf, int x0, int y0, int xs, int ys)
00422 {
00423   if (!p->ok)
00424     return false;
00425 
00426   // PNG lib wants everything in memory - the writing isn't done till this image is deleted.
00427 
00428   png_byte** rows = p->get_rows();
00429   if (!rows) return false;
00430 
00431   int bytes_per_pixel = png_get_bit_depth(p->png_ptr,p->info_ptr) * p->channels / 8;
00432   int bytes_per_row_dst = xs*bytes_per_pixel;
00433   if ((unsigned int)xs == png_get_image_width(p->png_ptr, p->info_ptr)) {
00434     assert(x0 == 0);
00435     vcl_memcpy(rows[y0], buf, ys * bytes_per_row_dst);
00436   }
00437   else {
00438     const png_byte* dst = (const png_byte*)buf;
00439     for (int y = 0; y < ys; ++y, dst += bytes_per_row_dst)
00440       vcl_memcpy(&rows[y0+y][x0*bytes_per_pixel], dst, xs*bytes_per_pixel);
00441   }
00442 
00443   return true;
00444 }
00445 
00446 vil1_image vil1_png_generic_image::get_plane(unsigned int plane) const
00447 {
00448   assert(plane == 0);
00449   return const_cast<vil1_png_generic_image*>(this);
00450 }

Generated on Sat Nov 22 05:08:28 2008 for core/vil1 by  doxygen 1.5.1