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
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) {
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 ) {
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
00086
00087
00088 struct my_error_mgr {
00089
00090 struct jpeg_error_mgr pub;
00091 char buffer[JMSG_LENGTH_MAX];
00092 jmp_buf setjmp_buffer;
00093 };
00094
00095 static void my_error_exit (j_common_ptr cinfo)
00096 {
00097
00098 my_error_mgr * myerr = (my_error_mgr *) cinfo->err;
00099 (*cinfo->err->format_message) (cinfo, myerr->buffer);
00100
00101 longjmp(myerr->setjmp_buffer, 1);
00102 }
00103
00104
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
00121 jpeg_destroy_decompress(&cinfo);
00122 return false;
00123 }
00124
00125
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
00138
00139
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
00156 jpeg_destroy_decompress(&cinfo);
00157 return true;
00158 }
00159
00160
00161
00162 bool decodeJPEG(char* inbuf, size_t inbufSize, size_t& width, size_t& height, size_t& channels) {
00163 jpeg_decompress_struct cinfo;
00164 my_error_mgr jerr;
00165 cinfo.err = jpeg_std_error(&jerr.pub);
00166 jerr.pub.error_exit = my_error_exit;
00167
00168 if (setjmp(jerr.setjmp_buffer)) {
00169
00170
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
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;
00193 my_error_mgr jerr;
00194 cinfo.err = jpeg_std_error(&jerr.pub);
00195 jerr.pub.error_exit = my_error_exit;
00196
00197 if (setjmp(jerr.setjmp_buffer)) {
00198
00199
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
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;
00215 my_error_mgr jerr;
00216 cinfo.err = jpeg_std_error(&jerr.pub);
00217 jerr.pub.error_exit = my_error_exit;
00218
00219 if (setjmp(jerr.setjmp_buffer)) {
00220
00221
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
00231 jpeg_istream_src(&cinfo, inStream);
00232 return decodeJPEG(cinfo,jerr,width,height,channels,outbuf,outbufSize,filename);
00233 }
00234
00235
00236
00237
00238
00239 struct png_read_mem_status {
00240 png_bytep buf;
00241 size_t bufsize;
00242 size_t offset;
00243 };
00244
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
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
00264 struct png_write_mem_status {
00265 png_bytep buf;
00266 size_t bufsize;
00267 size_t offset;
00268 };
00269
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
00281 void user_flush_png_data(png_structp ) {}
00282
00283
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
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
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
00327 size_t rowbytes=png_get_rowbytes(png_ptr, info_ptr);
00328
00329
00330
00331
00332
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
00345 png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
00346 return true;
00347 }
00348
00349
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
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
00473
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
00490 if(outbuf==NULL) {
00491 outbufSize=width*height*outbufChannels*(quality+50)/200;
00492 outbuf=new char[outbufSize];
00493 }
00494
00495
00496 jpeg_mem_dest(&cinfo, reinterpret_cast<JOCTET*>(outbuf), outbufSize);
00497
00498
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
00512 jpeg_set_defaults(&cinfo);
00513 jpeg_set_quality(&cinfo, quality, false);
00514 cinfo.dct_method=config->vision.jpeg_dct_method;
00515 if(cinfo.in_color_space==JCS_GRAYSCALE && inbufChannels!=1) {
00516
00517 jpeg_start_compress(&cinfo, true);
00518 unsigned int row_stride = width*inbufChannels;
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++)
00526 row_pointer[0][x] = inbuf[x*inbufChannels];
00527 jpeg_write_scanlines(&cinfo, row_pointer, 1);
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;
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
00557 jpeg_start_compress(&cinfo, true);
00558 unsigned int row_stride = width*inbufChannels;
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
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;
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
00583 if(outbuf==NULL) {
00584 outbufSize=width*height*outbufChannels;
00585 outbuf=new char[outbufSize];
00586 }
00587
00588
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
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
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
00651 return write_status.offset;
00652 }
00653
00654 }
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665