Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

ImageUtil.cc

Go to the documentation of this file.
00001 #include "ImageUtil.h"
00002 
00003 #include <sys/types.h>
00004 #include <sys/stat.h>
00005 #include <errno.h>
00006 #include <iostream>
00007 #include "Shared/jpeg-6b/jpeg_mem_dest.h"
00008 #include "Shared/jpeg-6b/jpeg_mem_src.h"
00009 #include "Shared/jpeg-6b/jpeg_istream_src.h"
00010 #ifndef NO_TEKKOTSU_CONFIG
00011 #  include "Shared/Config.h"
00012 #endif
00013 #ifndef LOADFILE_NO_MMAP
00014 #  include <fcntl.h>
00015 #  include <errno.h>
00016 #  include <sys/types.h>
00017 #  include <sys/mman.h>
00018 #  include <sys/stat.h>
00019 #endif
00020 
00021 #include <png.h>
00022 #include <zlib.h>
00023 extern "C" {
00024 #include <jpeglib.h>
00025 }
00026 using namespace std; 
00027 
00028 namespace image_util {
00029   
00030   bool loadFile(const std::string& file, char*& buf, size_t& size) {
00031     struct stat statbuf;
00032     if(stat(file.c_str(),&statbuf)!=0) {
00033       perror("image_util::loadFile");
00034       return false;
00035     }
00036 #ifdef LOADFILE_NO_MMAP
00037     FILE * infile= fopen(file.c_str(), "rb");
00038     if (infile==NULL) {
00039       int err=errno;
00040       cerr << "image_util::loadFile(): Could not open '" << file << "' (fopen:" << strerror(err) << ")" << endl;
00041       return false;
00042     }
00043     char* inbuf=new char[statbuf.st_size];
00044     if(!fread(inbuf,statbuf.st_size,1,infile)) {
00045       int err=errno;
00046       cerr << "image_util::loadFile(): Could not load '" << file << "' (fread:" << strerror(err) << ")" << endl;
00047       if(fclose(infile))
00048         perror("Warning: image_util::loadFile(), on fclose");
00049       delete [] inbuf;
00050       return false;
00051     }
00052     if(fclose(infile))
00053       perror("Warning: image_util::loadFile(), on fclose");
00054 #else /*use mmap to load file into memory*/
00055     int infd=open(file.c_str(),O_RDONLY,0666);
00056     if(infd<0) {
00057       int err=errno;
00058       cerr << "image_util::loadFile(): Could not open '" << file << "' (open:" << strerror(err) << ")" << endl;
00059       return false;
00060     }
00061     void* inbuf=mmap(NULL,(size_t)statbuf.st_size,PROT_READ,MAP_FILE|MAP_PRIVATE,infd,0);
00062     if (inbuf == MAP_FAILED) { /* MAP_FAILED generally defined as ((void*)-1) */
00063       int err=errno;
00064       cerr << "image_util::loadFile(): Could not load '" << file << "' (mmap:" << strerror(err) << ")" << endl;
00065       if(close(infd)<0)
00066         perror("Warning: image_util::loadFile(), on closing temporary file descriptor from open");
00067       return false;
00068     }
00069     if(close(infd)<0)
00070       perror("Warning: image_util::loadFile(), on closing temporary file descriptor from open");
00071 #endif
00072     buf=static_cast<char*>(inbuf);
00073     size=(size_t)statbuf.st_size;
00074     return true;
00075   }
00076 
00077 #ifdef LOADFILE_NO_MMAP
00078   void releaseFile(char* buf, size_t /*size*/) {
00079     delete [] buf;
00080   }
00081 #else
00082   void releaseFile(char* buf, size_t size) {
00083     if(munmap(buf,size))
00084       perror("Warning: image_util::releaseFile(), on munmap");
00085   }
00086 #endif
00087 
00088   /// @cond INTERNAL
00089   
00090   //! provides error handling in the case of a jpeg error, inspired by libpng's handling
00091   struct my_error_mgr {
00092     //! fields used for jpeg error handling (this MUST BE FIRST MEMBER so libjpeg can treat my_error_mgr as a jpeg_error_mgr struct — C-style "inheritance")
00093     struct jpeg_error_mgr pub;
00094     char buffer[JMSG_LENGTH_MAX]; //!< message indicating reason for error
00095     jmp_buf setjmp_buffer;  //!< a jump buffer to trigger on error, similar to exception except not as safe (use a C library, this is what you get...)
00096   };
00097   //! called on error from libjpeg, @a cinfo should be an instance of my_error_mgr
00098   static void my_error_exit (j_common_ptr cinfo)
00099   {
00100     /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
00101     my_error_mgr * myerr = (my_error_mgr *) cinfo->err;
00102     (*cinfo->err->format_message) (cinfo, myerr->buffer);
00103     /* Return control to the setjmp point */
00104     longjmp(myerr->setjmp_buffer, 1);
00105   }
00106   
00107   typedef int (*cinfo_cleanup_f)(jpeg_decompress_struct* cinfo);
00108 
00109   //! common implementation for variants of decodeJPEG() to call after they've set up the decompression structure
00110   bool decodeJPEG(jpeg_decompress_struct& cinfo, my_error_mgr& jerr, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize, const std::string& filename, cinfo_cleanup_f cinfo_cleanup=NULL) {
00111     jpeg_read_header(&cinfo, true);
00112     cinfo.out_color_space=JCS_YCbCr;
00113     jpeg_calc_output_dimensions(&cinfo);
00114     width=cinfo.output_width;
00115     height=cinfo.output_height;
00116     channels=cinfo.output_components;
00117     if(cinfo.output_width==0 || cinfo.output_height==0 || cinfo.output_components==0) {
00118       jpeg_destroy_decompress(&cinfo);
00119       return true;
00120     }
00121     if(outbuf==NULL) {
00122       outbufSize=width*height*channels;
00123       outbuf=new char[outbufSize];
00124     } else if(width*height*channels>outbufSize) {
00125       // not enough output space
00126       if(cinfo_cleanup!=NULL)
00127         (*cinfo_cleanup)(&cinfo);
00128       jpeg_destroy_decompress(&cinfo);
00129       return false;
00130     }
00131     
00132     // setup image destination
00133     unsigned int remain=cinfo.output_height;
00134     unsigned int row_stride = cinfo.output_width * cinfo.output_components;
00135     JSAMPROW rows[remain];
00136     rows[0]=(JSAMPLE*)outbuf;
00137     for(unsigned int i=1; i<remain; i++)
00138       rows[i]=rows[i-1]+row_stride;
00139     JSAMPROW* curpos=rows;
00140     //JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
00141     
00142     // read image
00143     jpeg_start_decompress(&cinfo);
00144     while (remain>0) {
00145       unsigned int used=jpeg_read_scanlines(&cinfo, curpos, remain);
00146       curpos+=used;
00147       remain-=used;
00148     }
00149     jpeg_finish_decompress(&cinfo);
00150     if(jerr.pub.num_warnings>0) {
00151       if(filename.size()>0)
00152         cerr << "Warning: image_util JPEG decompression of '" << filename << "' had warnings" << endl;
00153       else
00154         cerr << "Warning: image_util JPEG decompression had warnings" << endl;
00155       jerr.pub.num_warnings=0;
00156     }
00157     
00158     // teardown
00159     jpeg_destroy_decompress(&cinfo);
00160     return true;
00161   }
00162   
00163   /// @endcond
00164   
00165   bool decodeJPEG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00166     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00167     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00168     cinfo.err = jpeg_std_error(&jerr.pub);
00169     jerr.pub.error_exit = my_error_exit;
00170     /* Establish the setjmp return context for my_error_exit to use. */
00171     if (setjmp(jerr.setjmp_buffer)) {
00172       /* If we get here, the JPEG code has signaled an error.
00173        * We need to clean up the JPEG object, close the input file, and return.
00174        */
00175       jpeg_destroy_decompress(&cinfo);
00176       cerr << "JPEG header check failed: " << jerr.buffer << endl;
00177       return false;
00178     }
00179     
00180     jpeg_create_decompress(&cinfo);
00181     
00182     // get image header info
00183     tk_jpeg_mem_src(&cinfo, (JOCTET*)inbuf, inbufSize);
00184     jpeg_read_header(&cinfo, true);
00185     cinfo.out_color_space=JCS_YCbCr;
00186     jpeg_calc_output_dimensions(&cinfo);
00187     width=cinfo.output_width;
00188     height=cinfo.output_height;
00189     channels=cinfo.output_components;
00190     jpeg_destroy_decompress(&cinfo);
00191     return true;
00192   }
00193   
00194   bool decodeJPEG(std::istream& inStream, size_t& width, size_t& height, size_t& channels) {
00195     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00196     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00197     cinfo.err = jpeg_std_error(&jerr.pub);
00198     jerr.pub.error_exit = my_error_exit;
00199     /* Establish the setjmp return context for my_error_exit to use. */
00200     if (setjmp(jerr.setjmp_buffer)) {
00201       /* If we get here, the JPEG code has signaled an error.
00202        * We need to clean up the JPEG object, close the input file, and return.
00203        */
00204       jpeg_istream_revert(&cinfo);
00205       jpeg_destroy_decompress(&cinfo);
00206       cerr << "JPEG stream header check failed: " << jerr.buffer << endl;
00207       return false;
00208     }
00209     
00210     jpeg_create_decompress(&cinfo);
00211     
00212     // get image header info
00213     jpeg_istream_src(&cinfo, inStream);
00214     jpeg_read_header(&cinfo, true);
00215     cinfo.out_color_space=JCS_YCbCr;
00216     jpeg_calc_output_dimensions(&cinfo);
00217     width=cinfo.output_width;
00218     height=cinfo.output_height;
00219     channels=cinfo.output_components;
00220     bool reverted = jpeg_istream_revert(&cinfo);
00221     jpeg_destroy_decompress(&cinfo);
00222     return reverted;
00223   }
00224   
00225   bool decodeJPEG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize, const std::string& filename/*=""*/) {
00226     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00227     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00228     cinfo.err = jpeg_std_error(&jerr.pub);
00229     jerr.pub.error_exit = my_error_exit;
00230     /* Establish the setjmp return context for my_error_exit to use. */
00231     if (setjmp(jerr.setjmp_buffer)) {
00232       /* If we get here, the JPEG code has signaled an error.
00233       * We need to clean up the JPEG object, close the input file, and return.
00234       */
00235       jpeg_destroy_decompress(&cinfo);
00236       cerr << "JPEG decompression failed: " << jerr.buffer << endl;
00237       return false;
00238     }
00239 
00240     jpeg_create_decompress(&cinfo);
00241     
00242     // get image header info
00243     tk_jpeg_mem_src(&cinfo, (JOCTET*)inbuf, inbufSize);
00244     return decodeJPEG(cinfo,jerr,width,height,channels,outbuf,outbufSize,filename);
00245   }
00246 
00247   bool decodeJPEG(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize, const std::string& filename) {
00248     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00249     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00250     cinfo.err = jpeg_std_error(&jerr.pub);
00251     jerr.pub.error_exit = my_error_exit;
00252     /* Establish the setjmp return context for my_error_exit to use. */
00253     if (setjmp(jerr.setjmp_buffer)) {
00254       /* If we get here, the JPEG code has signaled an error.
00255       * We need to clean up the JPEG object, close the input file, and return.
00256       */
00257       jpeg_istream_revert(&cinfo);
00258       jpeg_destroy_decompress(&cinfo);
00259       cerr << "JPEG stream decompression failed: " << jerr.buffer << endl;
00260       return false;
00261     }
00262 
00263     jpeg_create_decompress(&cinfo);
00264     
00265     // get image header info
00266     jpeg_istream_src(&cinfo, inStream);
00267     return decodeJPEG(cinfo,jerr,width,height,channels,outbuf,outbufSize,filename,&jpeg_istream_revert);
00268   }
00269 
00270 
00271   /// @cond INTERNAL
00272   
00273   //! stores progress of user_read_png_data() between calls
00274   struct png_read_mem_status {
00275     png_bytep buf; //!< pointer to the beginning of the read buffer, the current position within the buffer is indicated by #offset
00276     size_t bufsize; //!< size of the read buffer pointed to by #buf
00277     size_t offset; //!< current read position within #buf
00278   };
00279   //! user callback function for reading a png from user parameters stored in @a png_ptr into @a data
00280   static void user_read_png_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00281     png_read_mem_status* status=(png_read_mem_status*)(png_get_io_ptr(png_ptr));
00282     size_t endoff=status->offset+length;
00283     if(endoff<=status->bufsize) {
00284       memcpy(data,status->buf+status->offset,length);
00285       status->offset=endoff;
00286     } else {
00287       png_error(png_ptr, "Read Error - ran out of file");
00288     }
00289   }
00290   
00291   struct png_read_stream_status {
00292     std::istream* is;
00293     std::streamoff n;
00294   };
00295   
00296   //! user callback function for reading a png from user parameters stored in @a png_ptr into @a data
00297   static void user_read_png_istream(png_structp png_ptr, png_bytep data, png_size_t length) {
00298     png_read_stream_status* status=(png_read_stream_status*)(png_get_io_ptr(png_ptr));
00299     if(!status->is->read((char*)data,length))
00300       png_error(png_ptr, "Read Error - stream closed early");
00301     status->n+=length;
00302   }
00303   
00304   //! stores progress of user_write_png_data() between calls
00305   struct png_write_mem_status {
00306     png_bytep buf;  //!< beginning of buffer being writen into (doesn't move with progress)
00307     size_t bufsize; //!< total size of buffer
00308     size_t offset;  //!< position within buffer, i.e. amount of buffer written so far
00309   };
00310   //! user callback function for writing a png at @a data into user parameters stored in @a png_ptr
00311   void user_write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00312     png_write_mem_status* status=(png_write_mem_status*)(png_get_io_ptr(png_ptr));
00313     size_t endoff=status->offset+length;
00314     if(endoff<=status->bufsize) {
00315       memcpy(status->buf+status->offset,data,length);
00316       status->offset=endoff;
00317     } else {
00318       png_error(png_ptr, "Write Error - ran out of file");
00319     }
00320   }
00321   //! user callback function for flushing results of user_write_png_data() (this is a no-op)
00322   void user_flush_png_data(png_structp /*png_ptr*/) {}
00323   
00324   typedef int (*png_cleanup_f)(png_structp png_ptr);
00325   int png_istream_revert(png_structp png_ptr) {
00326     png_read_stream_status* status=(png_read_stream_status*)(png_get_io_ptr(png_ptr));
00327     status->is->seekg(-status->n,ios_base::cur);
00328     status->n=0;
00329     return status->is->good();
00330   }
00331 
00332   //! Common implementation for decodePNG() implementations
00333   bool decodePNG(png_structp png_ptr, png_infop info_ptr, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize, png_cleanup_f png_cleanup=NULL) {
00334     // get image header info
00335     png_read_info(png_ptr, info_ptr);
00336     width=png_get_image_width(png_ptr, info_ptr);
00337     height=png_get_image_height(png_ptr, info_ptr);
00338     channels=3;
00339     if(width==0 || height==0)
00340       return true;
00341     if(outbuf==NULL) {
00342       outbufSize=width*height*channels;
00343       outbuf=new char[outbufSize];
00344     } else if(width*height*channels>outbufSize) {
00345       if(png_cleanup)
00346         (*png_cleanup)(png_ptr);
00347       png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00348       return false;
00349     }
00350     
00351     // setup image destination
00352     size_t bit_depth=png_get_bit_depth(png_ptr, info_ptr);
00353     if(bit_depth == 16)
00354       png_set_strip_16(png_ptr);
00355     if (bit_depth < 8)
00356       png_set_packing(png_ptr);
00357     
00358     size_t color_type=png_get_color_type(png_ptr, info_ptr);
00359     if (color_type & PNG_COLOR_MASK_ALPHA)
00360       png_set_strip_alpha(png_ptr);
00361     if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
00362       png_set_gray_to_rgb(png_ptr);
00363     
00364     png_color_16 my_background;
00365     my_background.index=0;
00366     my_background.red=1<<15;
00367     my_background.green=1<<15;
00368     my_background.blue=1<<15;
00369     my_background.gray=1<<15;
00370     png_color_16p image_background=NULL;
00371     if (png_get_bKGD(png_ptr, info_ptr, &image_background))
00372       png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00373     else
00374       png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00375     png_read_update_info(png_ptr, info_ptr);
00376     
00377     // read image
00378     size_t rowbytes=png_get_rowbytes(png_ptr, info_ptr);
00379     /*png_bytep row_pointers[height];
00380     row_pointers[0]=reinterpret_cast<png_bytep>(data->Base()+FULL_HEADER_SIZE);
00381     for(unsigned int h=1; h<height; ++h)
00382       row_pointers[h]=row_pointers[h-1]+rowbytes;
00383     png_read_image(png_ptr, row_pointers);*/
00384     png_bytep row=reinterpret_cast<png_bytep>(outbuf);
00385     for(unsigned int h=0; h<height; ++h) {
00386       if(row+rowbytes>reinterpret_cast<png_bytep>(outbuf+outbufSize)) {
00387         cerr << "image_util::decodePNG ran out of PNG buffer space" << endl;
00388         break;
00389       }
00390 
00391       png_read_row(png_ptr, row, NULL);
00392       row+=rowbytes;
00393     }
00394     png_read_end(png_ptr, NULL);
00395     
00396     // teardown
00397     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00398     return true;
00399   }
00400 
00401   /// @endcond
00402   
00403   bool decodePNG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00404     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00405     if (!png_ptr)
00406       return false;
00407     png_infop info_ptr = png_create_info_struct(png_ptr);
00408     if (!info_ptr) {
00409       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00410       return false;
00411     }
00412     if (setjmp(png_jmpbuf(png_ptr))) {
00413       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00414       return false;
00415     }
00416     
00417     png_read_mem_status read_status;
00418     read_status.buf=(png_bytep)inbuf;
00419     read_status.bufsize=inbufSize;
00420     read_status.offset=0;
00421     png_set_read_fn(png_ptr, &read_status, user_read_png_data);
00422     
00423     // get image header info
00424     png_read_info(png_ptr, info_ptr);
00425     width=png_get_image_width(png_ptr, info_ptr);
00426     height=png_get_image_height(png_ptr, info_ptr);
00427     channels=3;
00428     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00429     return true;
00430   }
00431   
00432   bool decodePNG(std::istream& inStream, size_t& width, size_t& height, size_t& channels) {
00433     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00434     if (!png_ptr)
00435       return false;
00436     png_infop info_ptr = png_create_info_struct(png_ptr);
00437     if (!info_ptr) {
00438       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00439       return false;
00440     }
00441     if (setjmp(png_jmpbuf(png_ptr))) {
00442       png_istream_revert(png_ptr);
00443       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00444       return false;
00445     }
00446     
00447     png_read_stream_status status;
00448     status.is = &inStream;
00449     status.n = 0;
00450     png_set_read_fn(png_ptr, &status, user_read_png_istream);
00451     
00452     // get image header info
00453     png_read_info(png_ptr, info_ptr);
00454     width=png_get_image_width(png_ptr, info_ptr);
00455     height=png_get_image_height(png_ptr, info_ptr);
00456     channels=3;
00457     bool reverted = png_istream_revert(png_ptr);
00458     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00459     return reverted;
00460   }
00461   
00462   bool decodePNG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00463     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00464     if (!png_ptr)
00465       return false;
00466     png_infop info_ptr = png_create_info_struct(png_ptr);
00467     if (!info_ptr) {
00468       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00469       return false;
00470     }
00471     if (setjmp(png_jmpbuf(png_ptr))) {
00472       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00473       return false;
00474     }
00475     
00476     png_read_mem_status read_status;
00477     read_status.buf=(png_bytep)inbuf;
00478     read_status.bufsize=inbufSize;
00479     read_status.offset=0;
00480     png_set_read_fn(png_ptr, &read_status, user_read_png_data);
00481     return decodePNG(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize);
00482   }
00483 
00484   bool decodePNGToDepth(png_structp png_ptr, png_infop info_ptr, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize, png_cleanup_f png_cleanup=NULL) {
00485     // get image header info
00486     png_read_info(png_ptr, info_ptr);
00487     width=png_get_image_width(png_ptr, info_ptr);
00488     height=png_get_image_height(png_ptr, info_ptr);
00489     channels=2;
00490     if(width==0 || height==0)
00491       return true;
00492     if(outbuf==NULL) {
00493       outbufSize=width*height*channels;
00494       outbuf=new char[outbufSize];
00495     } else if(width*height*channels>outbufSize) {
00496       if(png_cleanup)
00497         (*png_cleanup)(png_ptr);
00498       png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00499       return false;
00500     }
00501     
00502     // setup image destination
00503     size_t bit_depth=png_get_bit_depth(png_ptr, info_ptr);
00504     if(bit_depth == 16)
00505       png_set_strip_16(png_ptr);
00506     if (bit_depth < 8)
00507       png_set_packing(png_ptr);
00508     
00509     size_t color_type=png_get_color_type(png_ptr, info_ptr);
00510     if (color_type & PNG_COLOR_MASK_ALPHA)
00511       png_set_strip_alpha(png_ptr);
00512     if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
00513       png_set_gray_to_rgb(png_ptr);
00514     
00515     png_color_16 my_background;
00516     my_background.index=0;
00517     my_background.red=1<<15;
00518     my_background.green=1<<15;
00519     my_background.blue=1<<15;
00520     my_background.gray=1<<15;
00521     png_color_16p image_background=NULL;
00522     if (png_get_bKGD(png_ptr, info_ptr, &image_background))
00523       png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00524     else
00525       png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00526     png_read_update_info(png_ptr, info_ptr);
00527 
00528     // read image row by row
00529     size_t rowbytes=png_get_rowbytes(png_ptr, info_ptr);
00530 
00531     png_bytep row = (png_bytep) (new char*[rowbytes]);
00532 
00533     for (unsigned int i = 0; i < rowbytes; ++i) {
00534       row[i] = 0;
00535     }
00536 
00537     // Cast the output buffer pointer to an unsigned short to do some pointer
00538     // arithmetic
00539     unsigned short* outputBuf = (unsigned short*) outbuf;
00540 
00541     for(unsigned int h=0; h<height; ++h) {
00542       // Read in a row from the image
00543       png_read_row(png_ptr, row, NULL);
00544       // compute the depth from the pixels
00545       for (unsigned int w = 0; w < width; ++w) {
00546         // Just need the intensity channel
00547         int y = row[0 + 3 * w];
00548         //int u = row[1 + 3 * w];
00549         //int v = row[2 + 3 * w];
00550 
00551         // Assumed bounds on y-channel are 16-220
00552         double d = (y - 16) / 220.0 * 10000;
00553                                 unsigned short depth = d;
00554                                 if (depth > 10000) {
00555           depth = 10000;
00556         }
00557                                 if (depth < 800) {
00558                                   depth = 0;
00559                                 }
00560         *outputBuf++ = depth;
00561       }
00562     }
00563                 //cout << "(" << min_y << ", " << max_y << ")" << endl;
00564 
00565     png_read_end(png_ptr, NULL);
00566     
00567     // teardown
00568     delete(row);
00569     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00570     return true;
00571   } 
00572 
00573   bool decodePNGToDepth(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00574     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00575     if (!png_ptr)
00576       return false;
00577     png_infop info_ptr = png_create_info_struct(png_ptr);
00578     if (!info_ptr) {
00579       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00580       return false;
00581     }
00582     if (setjmp(png_jmpbuf(png_ptr))) {
00583       png_istream_revert(png_ptr);
00584       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00585       return false;
00586     }
00587     
00588     png_read_stream_status status;
00589     status.is = &inStream;
00590     status.n = 0;
00591     png_set_read_fn(png_ptr, &status, user_read_png_istream);
00592     return decodePNGToDepth(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize, png_istream_revert);
00593   }
00594 
00595   bool decodePNG(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00596     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00597     if (!png_ptr)
00598       return false;
00599     png_infop info_ptr = png_create_info_struct(png_ptr);
00600     if (!info_ptr) {
00601       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00602       return false;
00603     }
00604     if (setjmp(png_jmpbuf(png_ptr))) {
00605       png_istream_revert(png_ptr);
00606       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00607       return false;
00608     }
00609     
00610     png_read_stream_status status;
00611     status.is = &inStream;
00612     status.n = 0;
00613     png_set_read_fn(png_ptr, &status, user_read_png_istream);
00614     return decodePNG(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize, png_istream_revert);
00615   }
00616 
00617   
00618   
00619   const unsigned int TEST_HEADER_LEN=8;
00620   bool decodeImage(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00621     if(!png_sig_cmp((png_byte*)inbuf, 0, TEST_HEADER_LEN)) {
00622       return decodePNG(inbuf,inbufSize,width,height,channels);
00623     } else {
00624       return decodeJPEG(inbuf,inbufSize,width,height,channels);
00625     }
00626   }
00627   bool decodeImage(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00628     if(!png_sig_cmp((png_byte*)inbuf, 0, TEST_HEADER_LEN)) {
00629       return decodePNG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00630     } else {
00631       return decodeJPEG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00632     }
00633   }
00634   bool decodeImage(std::istream& inStream, size_t& width, size_t& height, size_t& channels) {
00635     char header[TEST_HEADER_LEN];
00636     inStream.read(header,TEST_HEADER_LEN);
00637     size_t cnt = (size_t)inStream.gcount();
00638     if(cnt==0)
00639       return false;
00640     if(!inStream.seekg(-cnt,ios_base::cur))
00641       return false;
00642     bool png = !png_sig_cmp((png_byte*)header, 0, cnt);
00643     if(png)
00644       return false; //return decodePNG(inStream, width, height, channels);
00645     else
00646       return decodeJPEG(inStream, width, height, channels);
00647   }
00648   bool decodeImage(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00649     char header[TEST_HEADER_LEN];
00650     inStream.read(header,TEST_HEADER_LEN);
00651     size_t cnt = (size_t)inStream.gcount();
00652     if(cnt==0)
00653       return false;
00654     if(!inStream.seekg(-cnt,ios_base::cur))
00655       return false;
00656     bool png = !png_sig_cmp((png_byte*)header, 0, cnt);
00657     if(png)
00658       return decodePNG(inStream, width, height, channels, outbuf, outbufSize);
00659     else
00660       return decodeJPEG(inStream, width, height, channels, outbuf, outbufSize);
00661   }
00662 
00663 
00664 
00665   bool loadJPEG(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00666     char* inbuf;
00667     size_t inbufSize;
00668     if(!loadFile(file, inbuf, inbufSize))
00669       return false;
00670     bool res=decodeJPEG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00671     releaseFile(inbuf,inbufSize);
00672     return res;
00673   }
00674   bool loadPNG(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00675     char* inbuf;
00676     size_t inbufSize;
00677     if(!loadFile(file, inbuf, inbufSize))
00678       return false;
00679     bool res=decodePNG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00680     releaseFile(inbuf,inbufSize);
00681     return res;
00682   }
00683   bool loadImage(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00684     char* inbuf;
00685     size_t inbufSize;
00686     if(!loadFile(file, inbuf, inbufSize))
00687       return false;
00688     bool res=decodeImage(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00689     releaseFile(inbuf,inbufSize);
00690     return res;
00691   }
00692 
00693 
00694 
00695   size_t encodeJPEG(const char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels, int quality) {
00696     jpeg_compress_struct cinfo;
00697     jpeg_error_mgr jerr;
00698     // We set the err object before we create the compress...  the idea
00699     // is if the creation fails, we can still get the error as to why it failed.
00700     cinfo.err = jpeg_std_error(&jerr);
00701     jpeg_create_compress(&cinfo);
00702     size_t ans=encodeJPEG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,quality,cinfo);
00703     jpeg_destroy_compress(&cinfo);
00704     return ans;
00705   }
00706   size_t encodeJPEG(const char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels, int quality, jpeg_compress_struct& cinfo) {
00707     return encodeJPEG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,quality,1,1,cinfo);
00708   }
00709   size_t encodeJPEG(const char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels, int quality, unsigned int yskip, unsigned int uvskip, jpeg_compress_struct& cinfo) {
00710     try {
00711       if(quality>100)
00712         quality=100;
00713       if(quality<0)
00714         quality=0;
00715       // check destination buffer
00716       if(outbuf==NULL) {
00717         outbufSize=width*height*outbufChannels*(quality+50)/200+768;
00718         outbuf=new char[outbufSize];
00719       }
00720       
00721       //pass the destination buffer and buffer size here
00722       tk_jpeg_mem_dest(&cinfo, reinterpret_cast<JOCTET*>(outbuf), outbufSize);
00723       
00724       // mode setup
00725       cinfo.image_width = width;
00726       cinfo.image_height = height;
00727       cinfo.input_components = inbufChannels;
00728       if(inbufChannels==1) {
00729         cinfo.in_color_space = JCS_GRAYSCALE;
00730       } else if(inbufChannels==3) {
00731         cinfo.in_color_space = JCS_YCbCr;
00732       } else if(inbufChannels==-3U) {
00733         // ugly hack to request internal conversion from RGB
00734         cinfo.input_components = inbufChannels = 3;
00735         cinfo.in_color_space = JCS_RGB;
00736       } else {
00737         cerr << "image_util JPEG compression failed - don't know how to compress into " << outbufChannels << " channels" << endl;
00738         return 0;
00739       }
00740       
00741       // parameter setup
00742       jpeg_set_defaults(&cinfo);
00743       jpeg_set_quality(&cinfo, quality, false); /* limit to baseline-JPEG values */
00744 #ifndef NO_TEKKOTSU_CONFIG
00745       cinfo.dct_method=config->vision.jpeg_dct_method;
00746 #endif
00747       if(cinfo.in_color_space==JCS_GRAYSCALE && inbufChannels!=1) {
00748         //special case, need to remove interleaved channels as we compress (single channel, grayscale)
00749         jpeg_start_compress(&cinfo, true);
00750         unsigned int row_stride = width*inbufChannels;  /* JSAMPLEs per row in image_buffer */
00751         JSAMPROW row_pointer[1] = { new JSAMPLE[width] };
00752         while (cinfo.next_scanline < cinfo.image_height) {
00753           if(inbufSize<row_stride) {
00754             cerr << "image_util ran out of input buffer during JPEG grayscale compression" << endl;
00755             break;
00756           }
00757           for(unsigned int x=0; x<width; x++) // copy over a row into temporary space
00758             row_pointer[0][x] = inbuf[x*inbufChannels];
00759           jpeg_write_scanlines(&cinfo, row_pointer, 1); // pass that row to libjpeg
00760           inbuf+=row_stride;
00761           inbufSize-=row_stride;
00762         }
00763         jpeg_finish_compress(&cinfo);
00764         delete [] row_pointer[0];
00765         
00766       } else  {
00767         if(cinfo.in_color_space==JCS_YCbCr) {
00768           unsigned int ysamp=1;
00769           unsigned int uvsamp=1;
00770           const unsigned int maxsamp=2;  // according to jpeg docs, this should be able to go up to 4, but I get error: "Sampling factors too large for interleaved scan"
00771           if(yskip>uvskip) {
00772             uvsamp=yskip-uvskip+1;
00773             if(uvsamp>maxsamp)
00774               uvsamp=maxsamp;
00775           } else {
00776             ysamp=uvskip-yskip+1;
00777             if(ysamp>maxsamp)
00778               ysamp=maxsamp;
00779           }
00780           cinfo.comp_info[0].h_samp_factor=ysamp;
00781           cinfo.comp_info[0].v_samp_factor=ysamp;
00782           cinfo.comp_info[1].h_samp_factor=uvsamp;
00783           cinfo.comp_info[1].v_samp_factor=uvsamp;
00784           cinfo.comp_info[2].h_samp_factor=uvsamp;
00785           cinfo.comp_info[2].v_samp_factor=uvsamp;
00786         }
00787         
00788         // compression
00789         jpeg_start_compress(&cinfo, true);
00790         unsigned int row_stride = width*inbufChannels;  /* JSAMPLEs per row in image_buffer */
00791         JSAMPROW row_pointer[1] = { const_cast<JSAMPROW>(reinterpret_cast<const JSAMPLE*>(inbuf)) };
00792         while (cinfo.next_scanline < cinfo.image_height) {
00793           if(inbufSize<row_stride) {
00794             cerr << "image_util ran out of input buffer during JPEG compression" << endl;
00795             break;
00796           }
00797           jpeg_write_scanlines(&cinfo, row_pointer, 1);
00798           row_pointer[0]+=row_stride;
00799           inbufSize-=row_stride;
00800         }
00801         jpeg_finish_compress(&cinfo);
00802       }
00803       
00804       // results
00805       return tk_jpeg_mem_size(&cinfo);
00806     } catch(const std::exception& ex) {
00807       std::cerr << "image_util Exception while compressing JPEG: " << ex.what() << std::endl; //really, can only be bad_alloc
00808       jpeg_finish_compress(&cinfo);
00809     }
00810     return 0;
00811   }
00812 
00813   size_t encodePNG(const char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels) {
00814     return encodePNG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,Z_DEFAULT_COMPRESSION);
00815   }
00816   
00817   size_t encodePNG(const char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels, int compressionLevel) {
00818     if(compressionLevel!=Z_DEFAULT_COMPRESSION) {
00819       if(compressionLevel<Z_NO_COMPRESSION)
00820         compressionLevel=Z_NO_COMPRESSION;
00821       if(compressionLevel>Z_BEST_COMPRESSION)
00822         compressionLevel=Z_BEST_COMPRESSION;
00823     }
00824     
00825     // check destination buffer
00826     if(outbuf==NULL) {
00827       outbufSize=width*height*outbufChannels + 256;
00828       if(compressionLevel==Z_NO_COMPRESSION)
00829         outbufSize=static_cast<size_t>(outbufSize*1.005+50); // with no compression, size will grow (plus constant header)
00830       else
00831         outbufSize=static_cast<size_t>(outbufSize*2.0/3.0+1);
00832       outbuf=new char[outbufSize];
00833     }
00834     
00835     // setup state structures
00836     png_structp  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00837     if (!png_ptr) {
00838       cerr << "image_util::encodePNG(): png_create_write_struct failed" << endl;
00839       return 0;
00840     }
00841     png_infop  info_ptr = png_create_info_struct(png_ptr);
00842     if (!info_ptr) {
00843       png_destroy_write_struct(&png_ptr, NULL);
00844       cerr << "image_util::encodePNG(): png_create_info_struct failed" << endl;
00845       return 0;
00846     }
00847     
00848     png_write_mem_status write_status;
00849     write_status.buf=reinterpret_cast<png_byte*>(outbuf);
00850     write_status.bufsize=outbufSize;
00851     write_status.offset=0;
00852     png_set_write_fn(png_ptr, &write_status, user_write_png_data, user_flush_png_data);
00853     
00854     if(setjmp(png_jmpbuf(png_ptr))) {
00855       cerr << "An error occurred during PNG compression" << endl;
00856       png_destroy_write_struct(&png_ptr, &info_ptr);
00857       return 0;
00858     }
00859     
00860     // configure compression
00861     int bit_depth=8;
00862     int color_type;
00863     if(outbufChannels==3)
00864       color_type=PNG_COLOR_TYPE_RGB;
00865     else if(outbufChannels==1)
00866       color_type=PNG_COLOR_TYPE_GRAY;
00867     else {
00868       cerr << "image_util PNG compression failed - don't know how to compress into " << outbufChannels << " channels" << endl;
00869       return 0;
00870     }
00871     png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00872     png_set_compression_level(png_ptr,compressionLevel);
00873     png_write_info(png_ptr, info_ptr);
00874     png_byte* row=const_cast<png_byte*>(reinterpret_cast<const png_byte*>(inbuf));
00875     const unsigned int inc=inbufChannels;
00876 #ifdef DEBUG
00877     if( (color_type==PNG_COLOR_TYPE_RGB && inc!=3) || (color_type==PNG_COLOR_TYPE_GRAY && inc!=1) ) {
00878       cerr << "image_util::encodePNG() only supports color mode from sources with interleaving of 3 samples (increment==3), or grayscale from \"pure\" sources (increment==1)" << endl;
00879       png_write_end(png_ptr, NULL);
00880       return 0;
00881     }
00882 #endif
00883     
00884     // do compression
00885     unsigned int row_stride = width*inc;
00886     const png_byte* endp=reinterpret_cast<const png_byte*>(row+inbufSize);
00887     for(unsigned int h=0; h<height; ++h) {
00888       if(row+row_stride>endp) {
00889         cerr << "image_util ran out of src image -- bad height?" << endl;
00890         break;
00891       }
00892       png_write_row(png_ptr, row);
00893       row+=row_stride;
00894     }
00895     png_write_end(png_ptr, NULL);
00896     png_destroy_write_struct(&png_ptr, &info_ptr);
00897     
00898     // return results
00899     return write_status.offset;
00900   }
00901 
00902 } // image_util namespace
00903 
00904 /*! @file
00905  * @brief 
00906  * @author Ethan Tira-Thompson (ejt) (Creator)
00907  */

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:42 2016 by Doxygen 1.6.3