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       png_read_row(png_ptr, row, NULL);
00391       row+=rowbytes;
00392     }
00393     png_read_end(png_ptr, NULL);
00394     
00395     // teardown
00396     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00397     return true;
00398   }
00399 
00400   /// @endcond
00401   
00402   bool decodePNG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00403     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00404     if (!png_ptr)
00405       return false;
00406     png_infop info_ptr = png_create_info_struct(png_ptr);
00407     if (!info_ptr) {
00408       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00409       return false;
00410     }
00411     if (setjmp(png_jmpbuf(png_ptr))) {
00412       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00413       return false;
00414     }
00415     
00416     png_read_mem_status read_status;
00417     read_status.buf=(png_bytep)inbuf;
00418     read_status.bufsize=inbufSize;
00419     read_status.offset=0;
00420     png_set_read_fn(png_ptr, &read_status, user_read_png_data);
00421     
00422     // get image header info
00423     png_read_info(png_ptr, info_ptr);
00424     width=png_get_image_width(png_ptr, info_ptr);
00425     height=png_get_image_height(png_ptr, info_ptr);
00426     channels=3;
00427     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00428     return true;
00429   }
00430   
00431   bool decodePNG(std::istream& inStream, size_t& width, size_t& height, size_t& channels) {
00432     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00433     if (!png_ptr)
00434       return false;
00435     png_infop info_ptr = png_create_info_struct(png_ptr);
00436     if (!info_ptr) {
00437       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00438       return false;
00439     }
00440     if (setjmp(png_jmpbuf(png_ptr))) {
00441       png_istream_revert(png_ptr);
00442       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00443       return false;
00444     }
00445     
00446     png_read_stream_status status;
00447     status.is = &inStream;
00448     status.n = 0;
00449     png_set_read_fn(png_ptr, &status, user_read_png_istream);
00450     
00451     // get image header info
00452     png_read_info(png_ptr, info_ptr);
00453     width=png_get_image_width(png_ptr, info_ptr);
00454     height=png_get_image_height(png_ptr, info_ptr);
00455     channels=3;
00456     bool reverted = png_istream_revert(png_ptr);
00457     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00458     return reverted;
00459   }
00460   
00461   bool decodePNG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00462     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00463     if (!png_ptr)
00464       return false;
00465     png_infop info_ptr = png_create_info_struct(png_ptr);
00466     if (!info_ptr) {
00467       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00468       return false;
00469     }
00470     if (setjmp(png_jmpbuf(png_ptr))) {
00471       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00472       return false;
00473     }
00474     
00475     png_read_mem_status read_status;
00476     read_status.buf=(png_bytep)inbuf;
00477     read_status.bufsize=inbufSize;
00478     read_status.offset=0;
00479     png_set_read_fn(png_ptr, &read_status, user_read_png_data);
00480     return decodePNG(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize);
00481   }
00482 
00483   bool decodePNG(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00484     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00485     if (!png_ptr)
00486       return false;
00487     png_infop info_ptr = png_create_info_struct(png_ptr);
00488     if (!info_ptr) {
00489       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00490       return false;
00491     }
00492     if (setjmp(png_jmpbuf(png_ptr))) {
00493       png_istream_revert(png_ptr);
00494       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00495       return false;
00496     }
00497     
00498     png_read_stream_status status;
00499     status.is = &inStream;
00500     status.n = 0;
00501     png_set_read_fn(png_ptr, &status, user_read_png_istream);
00502     return decodePNG(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize, png_istream_revert);
00503   }
00504 
00505   
00506   
00507   const unsigned int TEST_HEADER_LEN=8;
00508   bool decodeImage(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00509     if(!png_sig_cmp((png_byte*)inbuf, 0, TEST_HEADER_LEN)) {
00510       return decodePNG(inbuf,inbufSize,width,height,channels);
00511     } else {
00512       return decodeJPEG(inbuf,inbufSize,width,height,channels);
00513     }
00514   }
00515   bool decodeImage(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00516     if(!png_sig_cmp((png_byte*)inbuf, 0, TEST_HEADER_LEN)) {
00517       return decodePNG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00518     } else {
00519       return decodeJPEG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00520     }
00521   }
00522   bool decodeImage(std::istream& inStream, size_t& width, size_t& height, size_t& channels) {
00523     char header[TEST_HEADER_LEN];
00524     inStream.read(header,TEST_HEADER_LEN);
00525     size_t cnt = (size_t)inStream.gcount();
00526     if(cnt==0)
00527       return false;
00528     if(!inStream.seekg(-cnt,ios_base::cur))
00529       return false;
00530     bool png = !png_sig_cmp((png_byte*)header, 0, cnt);
00531     if(png)
00532       return false; //return decodePNG(inStream, width, height, channels);
00533     else
00534       return decodeJPEG(inStream, width, height, channels);
00535   }
00536   bool decodeImage(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00537     char header[TEST_HEADER_LEN];
00538     inStream.read(header,TEST_HEADER_LEN);
00539     size_t cnt = (size_t)inStream.gcount();
00540     if(cnt==0)
00541       return false;
00542     if(!inStream.seekg(-cnt,ios_base::cur))
00543       return false;
00544     bool png = !png_sig_cmp((png_byte*)header, 0, cnt);
00545     if(png)
00546       return decodePNG(inStream, width, height, channels, outbuf, outbufSize);
00547     else
00548       return decodeJPEG(inStream, width, height, channels, outbuf, outbufSize);
00549   }
00550 
00551 
00552 
00553   bool loadJPEG(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00554     char* inbuf;
00555     size_t inbufSize;
00556     if(!loadFile(file, inbuf, inbufSize))
00557       return false;
00558     bool res=decodeJPEG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00559     releaseFile(inbuf,inbufSize);
00560     return res;
00561   }
00562   bool loadPNG(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00563     char* inbuf;
00564     size_t inbufSize;
00565     if(!loadFile(file, inbuf, inbufSize))
00566       return false;
00567     bool res=decodePNG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00568     releaseFile(inbuf,inbufSize);
00569     return res;
00570   }
00571   bool loadImage(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00572     char* inbuf;
00573     size_t inbufSize;
00574     if(!loadFile(file, inbuf, inbufSize))
00575       return false;
00576     bool res=decodeImage(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00577     releaseFile(inbuf,inbufSize);
00578     return res;
00579   }
00580 
00581 
00582 
00583   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) {
00584     jpeg_compress_struct cinfo;
00585     jpeg_error_mgr jerr;
00586     // We set the err object before we create the compress...  the idea
00587     // is if the creation fails, we can still get the error as to why it failed.
00588     cinfo.err = jpeg_std_error(&jerr);
00589     jpeg_create_compress(&cinfo);
00590     size_t ans=encodeJPEG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,quality,cinfo);
00591     jpeg_destroy_compress(&cinfo);
00592     return ans;
00593   }
00594   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) {
00595     return encodeJPEG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,quality,1,1,cinfo);
00596   }
00597   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) {
00598     try {
00599       if(quality>100)
00600         quality=100;
00601       if(quality<0)
00602         quality=0;
00603       // check destination buffer
00604       if(outbuf==NULL) {
00605         outbufSize=width*height*outbufChannels*(quality+50)/200+768;
00606         outbuf=new char[outbufSize];
00607       }
00608       
00609       //pass the destination buffer and buffer size here
00610       tk_jpeg_mem_dest(&cinfo, reinterpret_cast<JOCTET*>(outbuf), outbufSize);
00611       
00612       // mode setup
00613       cinfo.image_width = width;
00614       cinfo.image_height = height;
00615       cinfo.input_components = inbufChannels;
00616       if(inbufChannels==1) {
00617         cinfo.in_color_space = JCS_GRAYSCALE;
00618       } else if(inbufChannels==3) {
00619         cinfo.in_color_space = JCS_YCbCr;
00620       } else if(inbufChannels==-3U) {
00621         // ugly hack to request internal conversion from RGB
00622         cinfo.input_components = inbufChannels = 3;
00623         cinfo.in_color_space = JCS_RGB;
00624       } else {
00625         cerr << "image_util JPEG compression failed - don't know how to compress into " << outbufChannels << " channels" << endl;
00626         return 0;
00627       }
00628       
00629       // parameter setup
00630       jpeg_set_defaults(&cinfo);
00631       jpeg_set_quality(&cinfo, quality, false); /* limit to baseline-JPEG values */
00632 #ifndef NO_TEKKOTSU_CONFIG
00633       cinfo.dct_method=config->vision.jpeg_dct_method;
00634 #endif
00635       if(cinfo.in_color_space==JCS_GRAYSCALE && inbufChannels!=1) {
00636         //special case, need to remove interleaved channels as we compress (single channel, grayscale)
00637         jpeg_start_compress(&cinfo, true);
00638         unsigned int row_stride = width*inbufChannels;  /* JSAMPLEs per row in image_buffer */
00639         JSAMPROW row_pointer[1] = { new JSAMPLE[width] };
00640         while (cinfo.next_scanline < cinfo.image_height) {
00641           if(inbufSize<row_stride) {
00642             cerr << "image_util ran out of input buffer during JPEG grayscale compression" << endl;
00643             break;
00644           }
00645           for(unsigned int x=0; x<width; x++) // copy over a row into temporary space
00646             row_pointer[0][x] = inbuf[x*inbufChannels];
00647           jpeg_write_scanlines(&cinfo, row_pointer, 1); // pass that row to libjpeg
00648           inbuf+=row_stride;
00649           inbufSize-=row_stride;
00650         }
00651         jpeg_finish_compress(&cinfo);
00652         delete [] row_pointer[0];
00653         
00654       } else  {
00655         if(cinfo.in_color_space==JCS_YCbCr) {
00656           unsigned int ysamp=1;
00657           unsigned int uvsamp=1;
00658           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"
00659           if(yskip>uvskip) {
00660             uvsamp=yskip-uvskip+1;
00661             if(uvsamp>maxsamp)
00662               uvsamp=maxsamp;
00663           } else {
00664             ysamp=uvskip-yskip+1;
00665             if(ysamp>maxsamp)
00666               ysamp=maxsamp;
00667           }
00668           cinfo.comp_info[0].h_samp_factor=ysamp;
00669           cinfo.comp_info[0].v_samp_factor=ysamp;
00670           cinfo.comp_info[1].h_samp_factor=uvsamp;
00671           cinfo.comp_info[1].v_samp_factor=uvsamp;
00672           cinfo.comp_info[2].h_samp_factor=uvsamp;
00673           cinfo.comp_info[2].v_samp_factor=uvsamp;
00674         }
00675         
00676         // compression
00677         jpeg_start_compress(&cinfo, true);
00678         unsigned int row_stride = width*inbufChannels;  /* JSAMPLEs per row in image_buffer */
00679         JSAMPROW row_pointer[1] = { const_cast<JSAMPROW>(reinterpret_cast<const JSAMPLE*>(inbuf)) };
00680         while (cinfo.next_scanline < cinfo.image_height) {
00681           if(inbufSize<row_stride) {
00682             cerr << "image_util ran out of input buffer during JPEG compression" << endl;
00683             break;
00684           }
00685           jpeg_write_scanlines(&cinfo, row_pointer, 1);
00686           row_pointer[0]+=row_stride;
00687           inbufSize-=row_stride;
00688         }
00689         jpeg_finish_compress(&cinfo);
00690       }
00691       
00692       // results
00693       return tk_jpeg_mem_size(&cinfo);
00694     } catch(const std::exception& ex) {
00695       std::cerr << "image_util Exception while compressing JPEG: " << ex.what() << std::endl; //really, can only be bad_alloc
00696       jpeg_finish_compress(&cinfo);
00697     }
00698     return 0;
00699   }
00700 
00701   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) {
00702     return encodePNG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,Z_DEFAULT_COMPRESSION);
00703   }
00704   
00705   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) {
00706     if(compressionLevel!=Z_DEFAULT_COMPRESSION) {
00707       if(compressionLevel<Z_NO_COMPRESSION)
00708         compressionLevel=Z_NO_COMPRESSION;
00709       if(compressionLevel>Z_BEST_COMPRESSION)
00710         compressionLevel=Z_BEST_COMPRESSION;
00711     }
00712     
00713     // check destination buffer
00714     if(outbuf==NULL) {
00715       outbufSize=width*height*outbufChannels + 256;
00716       if(compressionLevel==Z_NO_COMPRESSION)
00717         outbufSize=static_cast<size_t>(outbufSize*1.005+50); // with no compression, size will grow (plus constant header)
00718       else
00719         outbufSize=static_cast<size_t>(outbufSize*2.0/3.0+1);
00720       outbuf=new char[outbufSize];
00721     }
00722     
00723     // setup state structures
00724     png_structp  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00725     if (!png_ptr) {
00726       cerr << "image_util::encodePNG(): png_create_write_struct failed" << endl;
00727       return 0;
00728     }
00729     png_infop  info_ptr = png_create_info_struct(png_ptr);
00730     if (!info_ptr) {
00731       png_destroy_write_struct(&png_ptr, NULL);
00732       cerr << "image_util::encodePNG(): png_create_info_struct failed" << endl;
00733       return 0;
00734     }
00735     
00736     png_write_mem_status write_status;
00737     write_status.buf=reinterpret_cast<png_byte*>(outbuf);
00738     write_status.bufsize=outbufSize;
00739     write_status.offset=0;
00740     png_set_write_fn(png_ptr, &write_status, user_write_png_data, user_flush_png_data);
00741     
00742     if(setjmp(png_jmpbuf(png_ptr))) {
00743       cerr << "An error occurred during PNG compression" << endl;
00744       png_destroy_write_struct(&png_ptr, &info_ptr);
00745       return 0;
00746     }
00747     
00748     // configure compression
00749     int bit_depth=8;
00750     int color_type;
00751     if(outbufChannels==3)
00752       color_type=PNG_COLOR_TYPE_RGB;
00753     else if(outbufChannels==1)
00754       color_type=PNG_COLOR_TYPE_GRAY;
00755     else {
00756       cerr << "image_util PNG compression failed - don't know how to compress into " << outbufChannels << " channels" << endl;
00757       return 0;
00758     }
00759     png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00760     png_set_compression_level(png_ptr,compressionLevel);
00761     png_write_info(png_ptr, info_ptr);
00762     png_byte* row=const_cast<png_byte*>(reinterpret_cast<const png_byte*>(inbuf));
00763     const unsigned int inc=inbufChannels;
00764 #ifdef DEBUG
00765     if( (color_type==PNG_COLOR_TYPE_RGB && inc!=3) || (color_type==PNG_COLOR_TYPE_GRAY && inc!=1) ) {
00766       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;
00767       png_write_end(png_ptr, NULL);
00768       return 0;
00769     }
00770 #endif
00771     
00772     // do compression
00773     unsigned int row_stride = width*inc;
00774     const png_byte* endp=reinterpret_cast<const png_byte*>(row+inbufSize);
00775     for(unsigned int h=0; h<height; ++h) {
00776       if(row+row_stride>endp) {
00777         cerr << "image_util ran out of src image -- bad height?" << endl;
00778         break;
00779       }
00780       png_write_row(png_ptr, row);
00781       row+=row_stride;
00782     }
00783     png_write_end(png_ptr, NULL);
00784     png_destroy_write_struct(&png_ptr, &info_ptr);
00785     
00786     // return results
00787     return write_status.offset;
00788   }
00789 
00790 } // image_util namespace
00791 
00792 /*! @file
00793  * @brief 
00794  * @author Ethan Tira-Thompson (ejt) (Creator)
00795  */

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:42 2012 by Doxygen 1.6.3