00001 #ifndef ICEIMAGECOMPRESSION_C 00002 #define ICEIMAGECOMPRESSION_C 00003 00004 #include "Ice/IceImageCompressor.H" 00005 #include <vector> 00006 00007 //The output buffer size... I believe this should be as large as possible 00008 //so that empty_output_buffer will never need to be called as an intermediary 00009 //step, and we can just rely on term_destination to write all of our data 00010 #define OUTPUT_BUF_SIZE 100000 00011 00012 namespace IIC_NS 00013 { 00014 typedef struct { 00015 //The public fields common to destination managers 00016 struct jpeg_destination_mgr pub; 00017 00018 //The input buffer pointer 00019 JOCTET* in_buffer; 00020 00021 //The destination queue of 00022 std::vector<unsigned char>* out_buffer; 00023 00024 } buff_dest_mgr; 00025 00026 //////////////////////////////////////////////////////////////// 00027 // Initialize destination --- called by jpeg_start_compress 00028 // before any data is actually written. 00029 //////////////////////////////////////////////////////////////// 00030 METHODDEF(void) init_destination(j_compress_ptr cinfo) 00031 { 00032 //Get the pointer to our cinfo's destination manager 00033 buff_dest_mgr* dest = (buff_dest_mgr*) cinfo->dest; 00034 00035 //Allocate the input buffer --- it will be released when done with image 00036 dest->in_buffer = (JOCTET *) 00037 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 00038 OUTPUT_BUF_SIZE * sizeof(JOCTET)); 00039 00040 //Reset the input buffer 00041 dest->pub.next_output_byte = dest->in_buffer; 00042 dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; 00043 } 00044 00045 //////////////////////////////////////////////////////////////// 00046 // Empty the output buffer --- called whenever buffer fills up. 00047 //////////////////////////////////////////////////////////////// 00048 METHODDEF(boolean) empty_output_buffer (j_compress_ptr cinfo) 00049 { 00050 //Get the pointer to our cinfo's destination manager 00051 buff_dest_mgr* dest = (buff_dest_mgr*) cinfo->dest; 00052 00053 //Copy the rest of the input buffer onto the end of our output buffer 00054 dest->out_buffer->insert(dest->out_buffer->end(), 00055 dest->in_buffer, 00056 dest->in_buffer+OUTPUT_BUF_SIZE); 00057 00058 //Reset the input buffer 00059 dest->pub.next_output_byte = dest->in_buffer; 00060 dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; 00061 00062 return TRUE; 00063 } 00064 00065 //////////////////////////////////////////////////////////////// 00066 // Terminate destination --- called by jpeg_finish_compress 00067 // after all data has been written. Usually needs to flush buffer. 00068 //////////////////////////////////////////////////////////////// 00069 METHODDEF(void) term_destination (j_compress_ptr cinfo) 00070 { 00071 //Get the pointer to our cinfo's destination manager 00072 buff_dest_mgr* dest = (buff_dest_mgr*) cinfo->dest; 00073 00074 //Calculate the number of bytes left to be written 00075 size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; 00076 00077 //Copy the rest of the input buffer onto the end of our output buffer 00078 dest->out_buffer->insert(dest->out_buffer->end(), 00079 dest->in_buffer, 00080 dest->in_buffer+datacount); 00081 } 00082 } 00083 00084 00085 IceImageCompressor::IceImageCompressor() 00086 { 00087 //Initialize our jpeg error handler to the default 00088 //(spit out non-fatal error messages on cerr, and 00089 // exit() on fatal errors) 00090 cinfo.err = jpeg_std_error(&jerr); 00091 00092 //Create our jpeg compression object 00093 jpeg_create_compress(&cinfo); 00094 } 00095 00096 std::vector<unsigned char> IceImageCompressor::CompressImage(Image<PixRGB<byte> > input) 00097 { 00098 std::vector<unsigned char> jpeg_buffer; 00099 00100 //Initialize our data destination 00101 InitImageIceDest(&jpeg_buffer); 00102 00103 //Set our image and compression parameters 00104 cinfo.image_width = input.getWidth(); 00105 cinfo.image_height = input.getHeight(); 00106 cinfo.input_components = 3; 00107 cinfo.in_color_space = JCS_RGB; 00108 jpeg_set_defaults(&cinfo); 00109 00110 //Begin the compression 00111 jpeg_start_compress(&cinfo, TRUE); 00112 00113 //Get a pointer to the start of the image's raw data array. This assumes that 00114 //all of the image data is layed out as R G B R G B, with each element as a 00115 //byte in contiguous memory. This should be a valid assumption for any 00116 //Image<PixRGB<byte> > 00117 JSAMPLE* p_start = reinterpret_cast<JSAMPLE*>(input.getArrayPtr()); 00118 00119 //Pass a pointer to each row of the image to the jpeg compressor. It would 00120 //be nice to do this all in one shot, but libjpeg segfaults when I try. 00121 for(int row_idx=0; row_idx<input.getHeight(); row_idx++) 00122 { 00123 JSAMPLE* p = (p_start + (row_idx * input.getWidth()*3) ); 00124 jpeg_write_scanlines(&cinfo, &p, 1); 00125 } 00126 00127 //Clean up the compression, and finish writing all bytes to the output buffer 00128 jpeg_finish_compress(&cinfo); 00129 00130 return jpeg_buffer; 00131 } 00132 00133 00134 void IceImageCompressor::InitImageIceDest(std::vector<unsigned char>* output_buffer) 00135 { 00136 IIC_NS::buff_dest_mgr* dest; 00137 00138 if(cinfo.dest == NULL) 00139 { 00140 //Allocate some memory space for our destination manager. 00141 cinfo.dest = (struct jpeg_destination_mgr *) 00142 (*(cinfo.mem->alloc_small)) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, sizeof(IIC_NS::buff_dest_mgr)); 00143 } 00144 00145 //Initialize our destination manager by filling in all of the appropriate 00146 //function pointers, and assigning the output buffer. 00147 dest = (IIC_NS::buff_dest_mgr*) cinfo.dest; 00148 dest->pub.init_destination = IIC_NS::init_destination; 00149 dest->pub.empty_output_buffer = IIC_NS::empty_output_buffer; 00150 dest->pub.term_destination = IIC_NS::term_destination; 00151 dest->out_buffer = output_buffer; 00152 } 00153 00154 #endif 00155