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 #include "Shared/Config.h"
00011 #ifndef LOADFILE_NO_MMAP
00012 #  include <fcntl.h>
00013 #  include <errno.h>
00014 #  include <sys/types.h>
00015 #  include <sys/mman.h>
00016 #  include <sys/stat.h>
00017 #endif
00018 
00019 #include <png.h>
00020 extern "C" {
00021 #include <jpeglib.h>
00022 }
00023 using namespace std; 
00024 
00025 namespace image_util {
00026   
00027   bool loadFile(const std::string& file, char*& buf, size_t& size) {
00028     struct stat statbuf;
00029     if(stat(file.c_str(),&statbuf)!=0) {
00030       perror("image_util::loadFile");
00031       return false;
00032     }
00033 #ifdef LOADFILE_NO_MMAP
00034     FILE * infile= fopen(file.c_str(), "rb");
00035     if (infile==NULL) {
00036       int err=errno;
00037       cerr << "image_util::loadFile(): Could not open '" << file << "' (fopen:" << strerror(err) << ")" << endl;
00038       return false;
00039     }
00040     char* inbuf=new char[statbuf.st_size];
00041     if(!fread(inbuf,statbuf.st_size,1,infile)) {
00042       int err=errno;
00043       cerr << "image_util::loadFile(): Could not load '" << file << "' (fread:" << strerror(err) << ")" << endl;
00044       if(fclose(infile))
00045         perror("Warning: image_util::loadFile(), on fclose");
00046       delete [] inbuf;
00047       return false;
00048     }
00049     if(fclose(infile))
00050       perror("Warning: image_util::loadFile(), on fclose");
00051 #else /*use mmap to load file into memory*/
00052     int infd=open(file.c_str(),O_RDONLY,0666);
00053     if(infd<0) {
00054       int err=errno;
00055       cerr << "image_util::loadFile(): Could not open '" << file << "' (open:" << strerror(err) << ")" << endl;
00056       return false;
00057     }
00058     void* inbuf=mmap(NULL,statbuf.st_size,PROT_READ,MAP_FILE|MAP_PRIVATE,infd,0);
00059     if (inbuf == MAP_FAILED) { /* MAP_FAILED generally defined as ((void*)-1) */
00060       int err=errno;
00061       cerr << "image_util::loadFile(): Could not load '" << file << "' (mmap:" << strerror(err) << ")" << endl;
00062       if(close(infd)<0)
00063         perror("Warning: image_util::loadFile(), on closing temporary file descriptor from open");
00064       return false;
00065     }
00066     if(close(infd)<0)
00067       perror("Warning: image_util::loadFile(), on closing temporary file descriptor from open");
00068 #endif
00069     buf=static_cast<char*>(inbuf);
00070     size=statbuf.st_size;
00071     return true;
00072   }
00073 
00074 #ifdef LOADFILE_NO_MMAP
00075   void releaseFile(char* buf, size_t /*size*/) {
00076     delete [] buf;
00077   }
00078 #else
00079   void releaseFile(char* buf, size_t size) {
00080     if(munmap(buf,size))
00081       perror("Warning: image_util::releaseFile(), on munmap");
00082   }
00083 #endif
00084 
00085   /// @cond INTERNAL
00086   
00087   //! provides error handling in the case of a jpeg error, inspired by libpng's handling
00088   struct my_error_mgr {
00089     //! 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")
00090     struct jpeg_error_mgr pub;
00091     char buffer[JMSG_LENGTH_MAX]; //!< message indicating reason for error
00092     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...)
00093   };
00094   //! called on error from libjpeg, @a cinfo should be an instance of my_error_mgr
00095   static void my_error_exit (j_common_ptr cinfo)
00096   {
00097     /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
00098     my_error_mgr * myerr = (my_error_mgr *) cinfo->err;
00099     (*cinfo->err->format_message) (cinfo, myerr->buffer);
00100     /* Return control to the setjmp point */
00101     longjmp(myerr->setjmp_buffer, 1);
00102   }
00103 
00104   //! common implementation for variants of decodeJPEG() to call after they've set up the decompression structure
00105   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) {
00106     jpeg_read_header(&cinfo, true);
00107     cinfo.out_color_space=JCS_YCbCr;
00108     jpeg_calc_output_dimensions(&cinfo);
00109     width=cinfo.output_width;
00110     height=cinfo.output_height;
00111     channels=cinfo.output_components;
00112     if(cinfo.output_width==0 || cinfo.output_height==0 || cinfo.output_components==0) {
00113       jpeg_destroy_decompress(&cinfo);
00114       return true;
00115     }
00116     if(outbuf==NULL) {
00117       outbufSize=width*height*channels;
00118       outbuf=new char[outbufSize];
00119     } else if(width*height*channels>outbufSize) {
00120       // not enough output space
00121       jpeg_destroy_decompress(&cinfo);
00122       return false;
00123     }
00124     
00125     // setup image destination
00126     unsigned int remain=cinfo.output_height;
00127     unsigned int row_stride = cinfo.output_width * cinfo.output_components;
00128     JSAMPROW rows[remain];
00129     rows[0]=(JSAMPLE*)outbuf;
00130     if(rows[0]==NULL) {
00131       jpeg_destroy_decompress(&cinfo);
00132       return false;
00133     }
00134     for(unsigned int i=1; i<remain; i++)
00135       rows[i]=rows[i-1]+row_stride;
00136     JSAMPROW* curpos=rows;
00137     //JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
00138     
00139     // read image
00140     jpeg_start_decompress(&cinfo);
00141     while (remain>0) {
00142       unsigned int used=jpeg_read_scanlines(&cinfo, curpos, remain);
00143       curpos+=used;
00144       remain-=used;
00145     }
00146     jpeg_finish_decompress(&cinfo);
00147     if(jerr.pub.num_warnings>0) {
00148       if(filename.size()>0)
00149         cerr << "Warning: image_util JPEG decompression of '" << filename << "' had warnings" << endl;
00150       else
00151         cerr << "Warning: image_util JPEG decompression had warnings" << endl;
00152       jerr.pub.num_warnings=0;
00153     }
00154     
00155     // teardown
00156     jpeg_destroy_decompress(&cinfo);
00157     return true;
00158   }
00159   
00160   /// @endcond
00161   
00162   bool decodeJPEG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00163     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00164     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00165     cinfo.err = jpeg_std_error(&jerr.pub);
00166     jerr.pub.error_exit = my_error_exit;
00167     /* Establish the setjmp return context for my_error_exit to use. */
00168     if (setjmp(jerr.setjmp_buffer)) {
00169       /* If we get here, the JPEG code has signaled an error.
00170       * We need to clean up the JPEG object, close the input file, and return.
00171       */
00172       jpeg_destroy_decompress(&cinfo);
00173       cerr << "JPEG header check failed: " << jerr.buffer << endl;
00174       return false;
00175     }
00176     
00177     jpeg_create_decompress(&cinfo);
00178     
00179     // get image header info
00180     jpeg_mem_src(&cinfo, (JOCTET*)inbuf, inbufSize);
00181     jpeg_read_header(&cinfo, true);
00182     cinfo.out_color_space=JCS_YCbCr;
00183     jpeg_calc_output_dimensions(&cinfo);
00184     width=cinfo.output_width;
00185     height=cinfo.output_height;
00186     channels=cinfo.output_components;
00187     jpeg_destroy_decompress(&cinfo);
00188     return true;
00189   }
00190 
00191   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/*=""*/) {
00192     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00193     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00194     cinfo.err = jpeg_std_error(&jerr.pub);
00195     jerr.pub.error_exit = my_error_exit;
00196     /* Establish the setjmp return context for my_error_exit to use. */
00197     if (setjmp(jerr.setjmp_buffer)) {
00198       /* If we get here, the JPEG code has signaled an error.
00199       * We need to clean up the JPEG object, close the input file, and return.
00200       */
00201       jpeg_destroy_decompress(&cinfo);
00202       cerr << "JPEG decompression failed: " << jerr.buffer << endl;
00203       return false;
00204     }
00205 
00206     jpeg_create_decompress(&cinfo);
00207     
00208     // get image header info
00209     jpeg_mem_src(&cinfo, (JOCTET*)inbuf, inbufSize);
00210     return decodeJPEG(cinfo,jerr,width,height,channels,outbuf,outbufSize,filename);
00211   }
00212 
00213   bool decodeJPEG(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize, const std::string& filename) {
00214     jpeg_decompress_struct cinfo; //!< used to interface with libjpeg - holds compression parameters and state
00215     my_error_mgr jerr;          //!< used to interface with libjpeg - gives us access to error information
00216     cinfo.err = jpeg_std_error(&jerr.pub);
00217     jerr.pub.error_exit = my_error_exit;
00218     /* Establish the setjmp return context for my_error_exit to use. */
00219     if (setjmp(jerr.setjmp_buffer)) {
00220       /* If we get here, the JPEG code has signaled an error.
00221       * We need to clean up the JPEG object, close the input file, and return.
00222       */
00223       jpeg_destroy_decompress(&cinfo);
00224       cerr << "JPEG stream decompression failed: " << jerr.buffer << endl;
00225       return false;
00226     }
00227 
00228     jpeg_create_decompress(&cinfo);
00229     
00230     // get image header info
00231     jpeg_istream_src(&cinfo, inStream);
00232     return decodeJPEG(cinfo,jerr,width,height,channels,outbuf,outbufSize,filename);
00233   }
00234 
00235 
00236   /// @cond INTERNAL
00237   
00238   //! stores progress of user_read_png_data() between calls
00239   struct png_read_mem_status {
00240     png_bytep buf; //!< pointer to the beginning of the read buffer, the current position within the buffer is indicated by #offset
00241     size_t bufsize; //!< size of the read buffer pointed to by #buf
00242     size_t offset; //!< current read position within #buf
00243   };
00244   //! user callback function for reading a png from user parameters stored in @a png_ptr into @a data
00245   static void user_read_png_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00246     png_read_mem_status* status=(png_read_mem_status*)(png_get_io_ptr(png_ptr));
00247     size_t endoff=status->offset+length;
00248     if(endoff<=status->bufsize) {
00249       memcpy(data,status->buf+status->offset,length);
00250       status->offset=endoff;
00251     } else {
00252       png_error(png_ptr, "Read Error - ran out of file");
00253     }
00254   }
00255   
00256   //! user callback function for reading a png from user parameters stored in @a png_ptr into @a data
00257   static void user_read_png_istream(png_structp png_ptr, png_bytep data, png_size_t length) {
00258     std::istream* inStream=(std::istream*)(png_get_io_ptr(png_ptr));
00259     if(!inStream->read((char*)data,length))
00260       png_error(png_ptr, "Read Error - stream closed early");
00261   }
00262   
00263   //! stores progress of user_write_png_data() between calls
00264   struct png_write_mem_status {
00265     png_bytep buf;  //!< beginning of buffer being writen into (doesn't move with progress)
00266     size_t bufsize; //!< total size of buffer
00267     size_t offset;  //!< position within buffer, i.e. amount of buffer written so far
00268   };
00269   //! user callback function for writing a png at @a data into user parameters stored in @a png_ptr
00270   void user_write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00271     png_write_mem_status* status=(png_write_mem_status*)(png_get_io_ptr(png_ptr));
00272     size_t endoff=status->offset+length;
00273     if(endoff<=status->bufsize) {
00274       memcpy(status->buf+status->offset,data,length);
00275       status->offset=endoff;
00276     } else {
00277       png_error(png_ptr, "Write Error - ran out of file");
00278     }
00279   }
00280   //! user callback function for flushing results of user_write_png_data() (this is a no-op)
00281   void user_flush_png_data(png_structp /*png_ptr*/) {}
00282   
00283   //! Common implementation for decodePNG() implementations
00284   bool decodePNG(png_structp png_ptr, png_infop info_ptr, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00285     // get image header info
00286     png_read_info(png_ptr, info_ptr);
00287     width=png_get_image_width(png_ptr, info_ptr);
00288     height=png_get_image_height(png_ptr, info_ptr);
00289     channels=3;
00290     if(width==0 || height==0)
00291       return true;
00292     if(outbuf==NULL) {
00293       outbufSize=width*height*channels;
00294       outbuf=new char[outbufSize];
00295     } else if(width*height*channels>outbufSize) {
00296       png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00297       return false;
00298     }
00299     
00300     // setup image destination
00301     size_t bit_depth=png_get_bit_depth(png_ptr, info_ptr);
00302     if(bit_depth == 16)
00303       png_set_strip_16(png_ptr);
00304     if (bit_depth < 8)
00305       png_set_packing(png_ptr);
00306     
00307     size_t color_type=png_get_color_type(png_ptr, info_ptr);
00308     if (color_type & PNG_COLOR_MASK_ALPHA)
00309       png_set_strip_alpha(png_ptr);
00310     if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
00311       png_set_gray_to_rgb(png_ptr);
00312     
00313     png_color_16 my_background;
00314     my_background.index=0;
00315     my_background.red=1<<15;
00316     my_background.green=1<<15;
00317     my_background.blue=1<<15;
00318     my_background.gray=1<<15;
00319     png_color_16p image_background=NULL;
00320     if (png_get_bKGD(png_ptr, info_ptr, &image_background))
00321       png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00322     else
00323       png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00324     png_read_update_info(png_ptr, info_ptr);
00325     
00326     // read image
00327     size_t rowbytes=png_get_rowbytes(png_ptr, info_ptr);
00328     /*png_bytep row_pointers[height];
00329     row_pointers[0]=reinterpret_cast<png_bytep>(data->Base()+FULL_HEADER_SIZE);
00330     for(unsigned int h=1; h<height; ++h)
00331       row_pointers[h]=row_pointers[h-1]+rowbytes;
00332     png_read_image(png_ptr, row_pointers);*/
00333     png_bytep row=reinterpret_cast<png_bytep>(outbuf);
00334     for(unsigned int h=0; h<height; ++h) {
00335       if(row+rowbytes>reinterpret_cast<png_bytep>(outbuf+outbufSize)) {
00336         cerr << "image_util::decodePNG ran out of PNG buffer space" << endl;
00337         break;
00338       }
00339       png_read_row(png_ptr, row, NULL);
00340       row+=rowbytes;
00341     }
00342     png_read_end(png_ptr, NULL);
00343     
00344     // teardown
00345     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00346     return true;
00347   }
00348 
00349   /// @endcond
00350   
00351   bool decodePNG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00352     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00353     if (!png_ptr)
00354       return false;
00355     png_infop info_ptr = png_create_info_struct(png_ptr);
00356     if (!info_ptr) {
00357       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00358       return false;
00359     }
00360     if (setjmp(png_jmpbuf(png_ptr))) {
00361       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00362       return false;
00363     }
00364     
00365     png_read_mem_status read_status;
00366     read_status.buf=(png_bytep)inbuf;
00367     read_status.bufsize=inbufSize;
00368     read_status.offset=0;
00369     png_set_read_fn(png_ptr, &read_status, user_read_png_data);
00370     
00371     // get image header info
00372     png_read_info(png_ptr, info_ptr);
00373     width=png_get_image_width(png_ptr, info_ptr);
00374     height=png_get_image_height(png_ptr, info_ptr);
00375     channels=3;
00376     png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00377     return true;
00378   }
00379 
00380   bool decodePNG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00381     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00382     if (!png_ptr)
00383       return false;
00384     png_infop info_ptr = png_create_info_struct(png_ptr);
00385     if (!info_ptr) {
00386       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00387       return false;
00388     }
00389     if (setjmp(png_jmpbuf(png_ptr))) {
00390       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00391       return false;
00392     }
00393     
00394     png_read_mem_status read_status;
00395     read_status.buf=(png_bytep)inbuf;
00396     read_status.bufsize=inbufSize;
00397     read_status.offset=0;
00398     png_set_read_fn(png_ptr, &read_status, user_read_png_data);
00399     return decodePNG(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize);
00400   }
00401 
00402   bool decodePNG(std::istream& inStream, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
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_set_read_fn(png_ptr, &inStream, user_read_png_istream);
00417     return decodePNG(png_ptr, info_ptr, width, height, channels, outbuf, outbufSize);
00418   }
00419 
00420   
00421   
00422   bool decodeImage(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00423     if(!png_sig_cmp((png_byte*)inbuf, 0, 8)) {
00424       return decodePNG(inbuf,inbufSize,width,height,channels);
00425     } else {
00426       return decodeJPEG(inbuf,inbufSize,width,height,channels);
00427     }
00428   }
00429   bool decodeImage(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00430     if(!png_sig_cmp((png_byte*)inbuf, 0, 8)) {
00431       return decodePNG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00432     } else {
00433       return decodeJPEG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00434     }
00435   }
00436 
00437 
00438 
00439   bool loadJPEG(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00440     char* inbuf;
00441     size_t inbufSize;
00442     if(!loadFile(file, inbuf, inbufSize))
00443       return false;
00444     bool res=decodeJPEG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00445     releaseFile(inbuf,inbufSize);
00446     return res;
00447   }
00448   bool loadPNG(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00449     char* inbuf;
00450     size_t inbufSize;
00451     if(!loadFile(file, inbuf, inbufSize))
00452       return false;
00453     bool res=decodePNG(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00454     releaseFile(inbuf,inbufSize);
00455     return res;
00456   }
00457   bool loadImage(const std::string& file, size_t& width, size_t& height, size_t& channels, char*& outbuf, size_t& outbufSize) {
00458     char* inbuf;
00459     size_t inbufSize;
00460     if(!loadFile(file, inbuf, inbufSize))
00461       return false;
00462     bool res=decodeImage(inbuf,inbufSize,width,height,channels,outbuf,outbufSize);
00463     releaseFile(inbuf,inbufSize);
00464     return res;
00465   }
00466 
00467 
00468 
00469   size_t encodeJPEG(char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels, int quality) {
00470     jpeg_compress_struct cinfo;
00471     jpeg_error_mgr jerr;
00472     // We set the err object before we create the compress...  the idea
00473     // is if the creation fails, we can still get the error as to why it failed.
00474     cinfo.err = jpeg_std_error(&jerr);
00475     jpeg_create_compress(&cinfo);
00476     size_t ans=encodeJPEG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,quality,cinfo);
00477     jpeg_destroy_compress(&cinfo);
00478     return ans;
00479   }
00480   size_t encodeJPEG(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) {
00481     return encodeJPEG(inbuf,inbufSize,width,height,inbufChannels,outbuf,outbufSize,outbufChannels,quality,1,1,cinfo);
00482   }
00483   size_t encodeJPEG(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) {
00484     try {
00485       if(quality>100)
00486         quality=100;
00487       if(quality<0)
00488         quality=0;
00489       // check destination buffer
00490       if(outbuf==NULL) {
00491         outbufSize=width*height*outbufChannels*(quality+50)/200;
00492         outbuf=new char[outbufSize];
00493       }
00494       
00495       //pass the destination buffer and buffer size here
00496       jpeg_mem_dest(&cinfo, reinterpret_cast<JOCTET*>(outbuf), outbufSize);
00497       
00498       // mode setup
00499       cinfo.image_width = width;
00500       cinfo.image_height = height;
00501       cinfo.input_components = outbufChannels;
00502       if(outbufChannels==1) {
00503         cinfo.in_color_space = JCS_GRAYSCALE;
00504       } else if(outbufChannels==3) {
00505         cinfo.in_color_space = JCS_YCbCr;
00506       } else {
00507         cerr << "image_util JPEG compression failed - don't know how to compress into " << outbufChannels << " channels" << endl;
00508         return 0;
00509       }
00510       
00511       // parameter setup
00512       jpeg_set_defaults(&cinfo);
00513       jpeg_set_quality(&cinfo, quality, false); /* limit to baseline-JPEG values */
00514       cinfo.dct_method=config->vision.jpeg_dct_method;
00515       if(cinfo.in_color_space==JCS_GRAYSCALE && inbufChannels!=1) {
00516         //special case, need to remove interleaved channels as we compress (single channel, grayscale)
00517         jpeg_start_compress(&cinfo, true);
00518         unsigned int row_stride = width*inbufChannels;  /* JSAMPLEs per row in image_buffer */
00519         JSAMPROW row_pointer[1] = { new JSAMPLE[width] };
00520         while (cinfo.next_scanline < cinfo.image_height) {
00521           if(inbufSize<row_stride) {
00522             cerr << "image_util ran out of input buffer during JPEG grayscale compression" << endl;
00523             break;
00524           }
00525           for(unsigned int x=0; x<width; x++) // copy over a row into temporary space
00526             row_pointer[0][x] = inbuf[x*inbufChannels];
00527           jpeg_write_scanlines(&cinfo, row_pointer, 1); // pass that row to libjpeg
00528           inbuf+=row_stride;
00529           inbufSize-=row_stride;
00530         }
00531         jpeg_finish_compress(&cinfo);
00532         delete [] row_pointer[0];
00533         
00534       } else  {
00535         if(cinfo.in_color_space==JCS_YCbCr) {
00536           unsigned int ysamp=1;
00537           unsigned int uvsamp=1;
00538           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"
00539           if(yskip>uvskip) {
00540             uvsamp=yskip-uvskip+1;
00541             if(uvsamp>maxsamp)
00542               uvsamp=maxsamp;
00543           } else {
00544             ysamp=uvskip-yskip+1;
00545             if(ysamp>maxsamp)
00546               ysamp=maxsamp;
00547           }
00548           cinfo.comp_info[0].h_samp_factor=ysamp;
00549           cinfo.comp_info[0].v_samp_factor=ysamp;
00550           cinfo.comp_info[1].h_samp_factor=uvsamp;
00551           cinfo.comp_info[1].v_samp_factor=uvsamp;
00552           cinfo.comp_info[2].h_samp_factor=uvsamp;
00553           cinfo.comp_info[2].v_samp_factor=uvsamp;
00554         }
00555         
00556         // compression
00557         jpeg_start_compress(&cinfo, true);
00558         unsigned int row_stride = width*inbufChannels;  /* JSAMPLEs per row in image_buffer */
00559         JSAMPROW row_pointer[1] = { const_cast<JSAMPROW>(reinterpret_cast<JSAMPLE*>(inbuf)) };
00560         while (cinfo.next_scanline < cinfo.image_height) {
00561           if(inbufSize<row_stride) {
00562             cerr << "image_util ran out of input buffer during JPEG compression" << endl;
00563             break;
00564           }
00565           jpeg_write_scanlines(&cinfo, row_pointer, 1);
00566           row_pointer[0]+=row_stride;
00567           inbufSize-=row_stride;
00568         }
00569         jpeg_finish_compress(&cinfo);
00570       }
00571       
00572       // results
00573       return jpeg_mem_size(&cinfo);
00574     } catch(const std::exception& ex) {
00575       std::cerr << "image_util Exception while compressing JPEG: " << ex.what() << std::endl; //really, can only be bad_alloc
00576       jpeg_finish_compress(&cinfo);
00577     }
00578     return 0;
00579   }
00580 
00581   size_t encodePNG(char* inbuf, size_t inbufSize, size_t width, size_t height, size_t inbufChannels, char*& outbuf, size_t& outbufSize, size_t outbufChannels) {
00582     // check destination buffer
00583     if(outbuf==NULL) {
00584       outbufSize=width*height*outbufChannels;
00585       outbuf=new char[outbufSize];
00586     }
00587     
00588     // setup state structures
00589     png_structp  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00590     if (!png_ptr) {
00591       cerr << "image_util::encodePNG(): png_create_write_struct failed" << endl;
00592       return 0;
00593     }
00594     png_infop  info_ptr = png_create_info_struct(png_ptr);
00595     if (!info_ptr) {
00596       png_destroy_write_struct(&png_ptr, NULL);
00597       cerr << "image_util::encodePNG(): png_create_info_struct failed" << endl;
00598       return 0;
00599     }
00600     
00601     png_write_mem_status write_status;
00602     write_status.buf=reinterpret_cast<png_byte*>(outbuf);
00603     write_status.bufsize=outbufSize;
00604     write_status.offset=0;
00605     png_set_write_fn(png_ptr, &write_status, user_write_png_data, user_flush_png_data);
00606     
00607     if(setjmp(png_jmpbuf(png_ptr))) {
00608       cerr << "An error occurred during PNG compression" << endl;
00609       png_destroy_write_struct(&png_ptr, &info_ptr);
00610       return 0;
00611     }
00612     
00613     // configure compression
00614     int bit_depth=8;
00615     int color_type;
00616     if(outbufChannels==3)
00617       color_type=PNG_COLOR_TYPE_RGB;
00618     else if(outbufChannels==1)
00619       color_type=PNG_COLOR_TYPE_GRAY;
00620     else {
00621       cerr << "image_util PNG compression failed - don't know how to compress into " << outbufChannels << " channels" << endl;
00622       return 0;
00623     }
00624     png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00625     png_write_info(png_ptr, info_ptr);
00626     png_bytep row=reinterpret_cast<png_bytep>(inbuf);
00627     const unsigned int inc=inbufChannels;
00628 #ifdef DEBUG
00629     if(color_type==PNG_COLOR_TYPE_RGB && inc!=3 || color_type==PNG_COLOR_TYPE_GRAY && inc!=1) {
00630       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;
00631       png_write_end(png_ptr, NULL);
00632       return 0;
00633     }
00634 #endif
00635     
00636     // do compression
00637     unsigned int row_stride = width*inc;
00638     png_bytep endp=reinterpret_cast<png_bytep>(row+inbufSize);
00639     for(unsigned int h=0; h<height; ++h) {
00640       if(row+row_stride>endp) {
00641         cerr << "image_util ran out of src image -- bad height?" << endl;
00642         break;
00643       }
00644       png_write_row(png_ptr, row);
00645       row+=row_stride;
00646     }
00647     png_write_end(png_ptr, NULL);
00648     png_destroy_write_struct(&png_ptr, &info_ptr);
00649     
00650     // return results
00651     return write_status.offset;
00652   }
00653 
00654 } // image_util namespace
00655 
00656 /*! @file
00657  * @brief 
00658  * @author Ethan Tira-Thompson (ejt) (Creator)
00659  *
00660  * $Author: ejt $
00661  * $Name: tekkotsu-4_0 $
00662  * $Revision: 1.14 $
00663  * $State: Exp $
00664  * $Date: 2007/11/14 02:29:03 $
00665  */

Tekkotsu v4.0
Generated Thu Nov 22 00:54:53 2007 by Doxygen 1.5.4