// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_jpeg #define tools_jpeg #include //FILE,size_t //#define TOOLS_JPEG_OUT_ERR extern "C" { //Needed on Windows. #ifdef TOOLS_USE_NATIVE_JPEG #include #else #include //enforce ourex jpeg #endif } #include #include //memcpy #include namespace tools { namespace jpeg { inline bool write_file(FILE* a_file,int a_quality,unsigned char* a_buffer,unsigned int a_width,unsigned int a_height,unsigned int a_bpp) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err = ::jpeg_std_error(&jerr); ::jpeg_create_compress(&cinfo); ::jpeg_stdio_dest(&cinfo,a_file); cinfo.image_width = a_width; cinfo.image_height = a_height; cinfo.input_components = a_bpp; // # of color components per pixel. cinfo.in_color_space = JCS_RGB; // colorspace of input image. ::jpeg_set_defaults(&cinfo); ::jpeg_set_quality(&cinfo,a_quality,TRUE); ::jpeg_start_compress(&cinfo,TRUE); int row_stride = a_width * cinfo.input_components; while (cinfo.next_scanline < cinfo.image_height) { JSAMPROW row_pointer = a_buffer+(cinfo.image_height-1-cinfo.next_scanline) * row_stride; ::jpeg_write_scanlines(&cinfo, &row_pointer, 1); } ::jpeg_finish_compress(&cinfo); ::jpeg_destroy_compress(&cinfo); return true; } inline bool write_file_100(FILE* a_file,unsigned char* a_buffer,unsigned int a_width,unsigned int a_height,unsigned int a_bpp) { return tools::jpeg::write_file(a_file,100,a_buffer,a_width,a_height,a_bpp); } struct error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct error_mgr* error_ptr; extern "C" { inline void exlib_jpeg_error_exit(j_common_ptr cinfo){ error_ptr myerr = (error_ptr) cinfo->err; //(*cinfo->err->output_message) (cinfo); //printf("debug : error_exit\n"); //NSLog(@"debug : jpeg::error_exit"); ::longjmp(myerr->setjmp_buffer, 1); } } inline bool infos_file(FILE* a_file,unsigned int& a_width,unsigned int& a_height,unsigned int& a_bpp) { //NOTE : G.Barrand : not sure that in case of error, we cleanup properly ! struct jpeg_decompress_struct cinfo; struct error_mgr jerr; cinfo.err = ::jpeg_std_error(&jerr.pub); jerr.pub.error_exit = exlib_jpeg_error_exit; if(::setjmp(jerr.setjmp_buffer)) { //printf("debug : out of setjmp\n"); a_width = 0; a_height = 0; a_bpp = 0; return false; } ::jpeg_create_decompress(&cinfo); ::jpeg_stdio_src(&cinfo,a_file); ::jpeg_read_header(&cinfo, TRUE); ::jpeg_start_decompress(&cinfo); a_width = cinfo.output_width; a_height = cinfo.output_height; a_bpp = cinfo.output_components; ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); return true; } inline unsigned char* read_file(FILE* a_file,unsigned int& a_width,unsigned int& a_height,unsigned int& a_bpp) { // the returned buffer must be delete with "delete []". //NOTE : G.Barrand : not sure that in case of error, we cleanup properly ! struct jpeg_decompress_struct cinfo; struct error_mgr jerr; cinfo.err = ::jpeg_std_error(&jerr.pub); jerr.pub.error_exit = exlib_jpeg_error_exit; if(::setjmp(jerr.setjmp_buffer)) { #ifdef TOOLS_JPEG_OUT_ERR ::printf("tools::jpeg::read_file : out of setjmp\n"); #endif //NOTE : how to be sure to delete the below new if having been done ? a_width = 0; a_height = 0; a_bpp = 0; return 0; } ::jpeg_create_decompress(&cinfo); ::jpeg_stdio_src(&cinfo,a_file); ::jpeg_read_header(&cinfo, TRUE); ::jpeg_start_decompress(&cinfo); int row_stride = cinfo.output_width * cinfo.output_components; if((!cinfo.output_width)|| (!cinfo.output_height)|| (cinfo.output_components<=0)) { #ifdef TOOLS_JPEG_OUT_ERR ::printf("tools::jpeg::read_file : bad size, w %u h %u cpnts %d\n", cinfo.output_width,cinfo.output_height,cinfo.output_components); #endif ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); a_width = 0; a_height = 0; a_bpp = 0; return 0; } a_width = cinfo.output_width; a_height = cinfo.output_height; a_bpp = cinfo.output_components; unsigned char* image_buffer = new unsigned char[row_stride * cinfo.output_height]; if(!image_buffer) { #ifdef TOOLS_JPEG_OUT_ERR ::printf("tools::jpeg::read_file : alloc of %d bytes failed\n", row_stride * cinfo.output_height); #endif ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); a_width = 0; a_height = 0; a_bpp = 0; return 0; } //FIXME : iPad : the below crashes on LHCb_artist_16384_4096_80.jpg. {JSAMPROW buffer[1]; unsigned char* pos = image_buffer + row_stride * cinfo.output_height; while (cinfo.output_scanline #include #include namespace tools { namespace jpeg { inline bool is(const std::string& a_file){ FILE* file = ::fopen(a_file.c_str(),"rb"); if(!file) return false; //if file does not exist, then it is not a jpeg ! unsigned char head[4]; size_t nitem = ::fread(head,1,4,file); ::fclose(file); if(nitem!=4) return false; if(head[0]!=255) return false; if(head[1]!=216) return false; if(head[2]!=255) return false; //if(head[3]!=224) return false; //LRI.jpg is 225 ! return true; } inline bool infos(std::ostream& a_out,const std::string& a_file,unsigned int& a_width,unsigned int& a_height,unsigned int& a_bpp) { if(!is(a_file)) { a_out << "tools::jpeg::infos :" << " file " << a_file << " is not jpeg." << std::endl; a_width = 0; a_height = 0; a_bpp = 0; return false; } FILE* file = ::fopen(a_file.c_str(),"rb"); if(!file) { a_out << "tools::jpeg::infos :" << " can't open " << a_file << std::endl; a_width = 0; a_height = 0; a_bpp = 0; return false; } unsigned int w,h,bpp; bool status = infos_file(file,w,h,bpp); ::fclose(file); if(!status) { a_out << "tools::jpeg::infos :" << " problem reading " << a_file << std::endl; a_width = 0; a_height = 0; a_bpp = 0; return false; } a_width = w; a_height = h; a_bpp = bpp; return true; } inline unsigned char* read(std::ostream& a_out,const std::string& a_file,unsigned int& a_width,unsigned int& a_height,unsigned int& a_bpp) { if(!is(a_file)) { a_out << "tools::jpeg::read :" << " file " << a_file << " is not jpeg." << std::endl; a_width = 0; a_height = 0; a_bpp = 0; return 0; } FILE* file = ::fopen(a_file.c_str(),"rb"); if(!file) { a_out << "tools::jpeg::read :" << " can't open " << a_file << std::endl; a_width = 0; a_height = 0; a_bpp = 0; return 0; } unsigned int w,h,bpp; unsigned char* buffer = read_file(file,w,h,bpp); ::fclose(file); if(!buffer) { a_out << "tools::jpeg::read :" << " problem reading " << a_file << std::endl; a_width = 0; a_height = 0; a_bpp = 0; return 0; } a_width = w; a_height = h; a_bpp = bpp; return buffer; } inline unsigned char* read_part(std::ostream& a_out, const std::string& a_file, unsigned int a_sx,unsigned int a_sy, unsigned int a_sw,unsigned int a_sh, unsigned int& a_rw,unsigned int& a_rh, unsigned int& a_rbpp) { if(!is(a_file)) { a_out << "tools::jpeg::read_part :" << " file " << a_file << " is not jpeg." << std::endl; a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } FILE* file = ::fopen(a_file.c_str(),"rb"); if(!file) { a_out << "tools::jpeg::read_part :" << " can't open " << a_file << std::endl; a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } struct jpeg_decompress_struct cinfo; struct error_mgr jerr; cinfo.err = ::jpeg_std_error(&jerr.pub); jerr.pub.error_exit = exlib_jpeg_error_exit; if(::setjmp(jerr.setjmp_buffer)) { //printf("debug : out of setjmp\n"); ::fclose(file); //NOTE : how to be sure to delete the below news if having been done ? a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } ::jpeg_create_decompress(&cinfo); ::jpeg_stdio_src(&cinfo,file); ::jpeg_read_header(&cinfo,TRUE); ::jpeg_start_decompress(&cinfo); int row_stride = cinfo.output_width * cinfo.output_components; unsigned int w = cinfo.output_width; unsigned int h = cinfo.output_height; if((a_sx>=w)||(a_sy>=h)){ ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); ::fclose(file); a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } // 012345 a_rw = tools::mn(w-a_sx,a_sw); a_rh = tools::mn(h-a_sy,a_sh); a_rbpp = cinfo.output_components; if(!a_rw||!a_rh||!a_rbpp){ ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); ::fclose(file); a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } unsigned char* image_buffer = new unsigned char[a_rw*a_rh*a_rbpp]; if(!image_buffer) { ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); ::fclose(file); a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } unsigned char* line = new unsigned char[row_stride]; if(!line) { ::jpeg_abort_decompress(&cinfo); ::jpeg_destroy_decompress(&cinfo); ::fclose(file); delete [] image_buffer; a_rw = 0; a_rh = 0; a_rbpp = 0; return 0; } //FIXME : iPad : the below crashes on LHCb_artist_16384_4096_80.jpg. unsigned int hbeg = a_sy; unsigned int hend = a_sy+a_rh; {JSAMPROW buffer[1]; buffer[0] = line; unsigned char* pos = image_buffer+a_rh*a_rw*a_rbpp; while(cinfo.output_scanline namespace tools { namespace jpeg { inline bool to_xpm(std::ostream& a_out,const std::string& a_file,const std::string& a_name,bool a_verbose = false) { return tools::xpm::to_xpm(a_out,a_file,a_name,read,a_verbose); } }} #include #include namespace tools { namespace jpeg { inline bool concatenate(std::ostream& a_out, const std::vector& a_files, unsigned int a_cols,unsigned int a_rows, unsigned int a_bw,unsigned int a_bh, unsigned char a_bc, const std::string& a_file,int a_quality) { unsigned int wa,ha,bppa; unsigned char* ba = tools::image::concatenate (a_out,a_files,a_cols,a_rows,a_bw,a_bh,a_bc,read,wa,ha,bppa); if(!ba) { a_out << "tools::jpeg::concatenate :" << " failed to concatenate all buffers." << std::endl; delete [] ba; return false; } FILE* file = ::fopen(a_file.c_str(),"wb"); if(!file) { a_out << "tools::jpeg::concatenate :" << " can't open " << file << " for writing." << std::endl; delete [] ba; return false; } if(!write_file(file,a_quality,ba,wa,ha,bppa)) { a_out << "tools::jpeg::concatenate :" << " can't write " << a_file << "." << std::endl; delete [] ba; ::fclose(file); return false; } delete [] ba; ::fclose(file); return true; } class reader : public virtual tools::image::ireader { public: virtual tools::image::ireader* copy() const {return new reader(*this);} virtual bool is(const std::string& a_file) const { return tools::jpeg::is(a_file); } virtual bool infos(std::ostream& a_out,const std::string& a_file, unsigned int& a_width,unsigned int& a_height, unsigned int& a_bpp) const { return tools::jpeg::infos(a_out,a_file,a_width,a_height,a_bpp); } virtual unsigned char* read(std::ostream& a_out,const std::string& a_file, unsigned int& a_width,unsigned int& a_height, unsigned int& a_bpp) const { return tools::jpeg::read(a_out,a_file,a_width,a_height,a_bpp); } virtual unsigned char* read_part(std::ostream& a_out, const std::string& a_file, unsigned int a_sx,unsigned int a_sy, unsigned int a_sw,unsigned int a_sh, unsigned int& a_rw,unsigned int& a_rh, unsigned int& a_rbpp) const { return tools::jpeg::read_part(a_out,a_file, a_sx,a_sy, a_sw,a_sh, a_rw,a_rh, a_rbpp); } public: reader(){} virtual ~reader(){} public: reader(const reader&):ireader(){} reader& operator=(const reader&){return *this;} }; }} //#include // //namespace tools { //namespace jpeg { // //class jpeg_data : public tools::img_byte { //public: // jpeg_data(std::ostream& a_out) // :tools::img_byte(3) // ,m_out(a_out) // ,m_FILE(0) // ,m_row_stride(0) // ,m_pos(0) // {} // virtual ~jpeg_data(){ // if(m_FILE) ::fclose(m_FILE); // } //protected: //don't copy // jpeg_data(const jpeg_data& a_from) // :tools::img_byte(a_from) // ,m_out(a_from.m_out) // {} // jpeg_data& operator=(const jpeg_data& a_from){ // tools::img_byte::operator=(a_from); // return *this; // } //public: // bool open(const std::string& a_file) { // if(m_FILE) return false; // // if(!tools::jpeg::is(a_file)) { // m_out << "tools::jpeg::jpeg_data::open :" // << " file " << a_file << " is not jpeg." // << std::endl; // return false; // } // m_FILE = ::fopen(a_file.c_str(),"rb"); // if(!m_FILE) { // m_out << "tools::jpeg::jpeg_data::open :" // << " can't open " << a_file // << std::endl; // return false; // } // // m_cinfo.err = ::jpeg_std_error(&m_jerr.pub); // m_jerr.pub.error_exit = exlib_jpeg_exlib_jpeg_exlib_jpeg_error_exit; // if(::setjmp(m_jerr.setjmp_buffer)) { // m_out << "tools::jpeg::jpeg_data::open :" // << " setjmp failed." << a_file // << std::endl; // ::fclose(m_FILE); // m_FILE = 0; // return false; // } // // ::jpeg_create_decompress(&m_cinfo); // ::jpeg_stdio_src(&m_cinfo,m_FILE); // ::jpeg_read_header(&m_cinfo,TRUE); // // ::jpeg_start_decompress(&m_cinfo); // // if(m_cinfo.output_components!=3) { // m_out << "tools::jpeg::jpeg_data::open :" // << " file " << a_file << " dont't have three components." // << std::endl; // ::fclose(m_FILE); // m_FILE = 0; // return false; // } // // m_row_stride = m_cinfo.output_width * m_cinfo.output_components; // // unsigned char* buffer = // new unsigned char[m_row_stride * m_cinfo.output_height]; // if(!buffer) { // m_out << "tools::jpeg::jpeg_data::open :" // << " can't alloc " << m_row_stride * m_cinfo.output_height // << std::endl; // ::fclose(m_FILE); // m_FILE = 0; // return false; // } // // tools::img_byte::set(m_cinfo.output_width,m_cinfo.output_height, // buffer,true); // m_pos = buffer + m_row_stride * m_cinfo.output_height; // return true; // } // bool read_row() { // if(!m_FILE) return false; // //::printf("debug : read_row %d\n",m_cinfo.output_scanline); // if(m_cinfo.output_scanline