1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxImage JPEG handler 
   4 // Author:      Vaclav Slavik 
   6 // Copyright:   (c) Vaclav Slavik 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  11 #pragma implementation "imagjpeg.h" 
  14 // For compilers that support precompilation, includes "wx.h". 
  15 #include "wx/wxprec.h" 
  23 #if wxUSE_IMAGE && wxUSE_LIBJPEG 
  25 #include "wx/imagjpeg.h" 
  26 #include "wx/bitmap.h" 
  31 // NB: Some compilers define boolean type in Windows headers 
  32 //     (e.g. Watcom C++, but not Open Watcom). 
  33 //     This causes a conflict with jmorecfg.h header from libjpeg, so we have 
  34 //     to make sure libjpeg won't try to define boolean itself. This is done by 
  35 //     defining HAVE_BOOLEAN. 
  36 #if defined(__WXMSW__) && (defined(__MWERKS__) || defined(__DIGITALMARS__) || (defined(__WATCOMC__) && __WATCOMC__ < 1200)) 
  38     #include "wx/msw/wrapwin.h" 
  43     #if defined(__WXMSW__) 
  49 #include "wx/filefn.h" 
  50 #include "wx/wfstream.h" 
  52 #include "wx/module.h" 
  56 // For JPEG library error handling 
  63 // ---------------------------------------------------------------------------- 
  65 // ---------------------------------------------------------------------------- 
  67 // the standard definition of METHODDEF(type) from jmorecfg.h is "static type" 
  68 // which means that we can't declare the method functions as extern "C" - the 
  69 // compiler (rightfully) complains about the multiple storage classes in 
  72 // so we only add extern "C" when using our own, modified, jmorecfg.h - and use 
  73 // whatever we have in the system headers if this is what we use hoping that it 
  74 // should be ok (can't do anything else) 
  75 #ifdef JPEG_METHOD_LINKAGE 
  76     #define CPP_METHODDEF(type) extern "C" METHODDEF(type) 
  77 #else // not using our jmorecfg.h header 
  78     #define CPP_METHODDEF(type) METHODDEF(type) 
  81 //----------------------------------------------------------------------------- 
  83 //----------------------------------------------------------------------------- 
  85 IMPLEMENT_DYNAMIC_CLASS(wxJPEGHandler
,wxImageHandler
) 
  89 //------------- JPEG Data Source Manager 
  91 #define JPEG_IO_BUFFER_SIZE   2048 
  94     struct jpeg_source_mgr pub
;   /* public fields */ 
  96     JOCTET
* buffer
;               /* start of buffer */ 
  97     wxInputStream 
*stream
; 
 100 typedef wx_source_mgr 
* wx_src_ptr
; 
 102 CPP_METHODDEF(void) wx_init_source ( j_decompress_ptr 
WXUNUSED(cinfo
) ) 
 106 CPP_METHODDEF(boolean
) wx_fill_input_buffer ( j_decompress_ptr cinfo 
) 
 108     wx_src_ptr src 
= (wx_src_ptr
) cinfo
->src
; 
 110     src
->pub
.next_input_byte 
= src
->buffer
; 
 111     src
->pub
.bytes_in_buffer 
= src
->stream
->Read(src
->buffer
, JPEG_IO_BUFFER_SIZE
).LastRead(); 
 113     if (src
->pub
.bytes_in_buffer 
== 0) // check for end-of-stream 
 115         // Insert a fake EOI marker 
 116         src
->buffer
[0] = 0xFF; 
 117         src
->buffer
[1] = JPEG_EOI
; 
 118         src
->pub
.bytes_in_buffer 
= 2; 
 123 CPP_METHODDEF(void) wx_skip_input_data ( j_decompress_ptr cinfo
, long num_bytes 
) 
 127         wx_src_ptr src 
= (wx_src_ptr
) cinfo
->src
; 
 129         while (num_bytes 
> (long)src
->pub
.bytes_in_buffer
) 
 131             num_bytes 
-= (long) src
->pub
.bytes_in_buffer
; 
 132             src
->pub
.fill_input_buffer(cinfo
); 
 134         src
->pub
.next_input_byte 
+= (size_t) num_bytes
; 
 135         src
->pub
.bytes_in_buffer 
-= (size_t) num_bytes
; 
 139 CPP_METHODDEF(void) wx_term_source ( j_decompress_ptr cinfo 
) 
 141     wx_src_ptr src 
= (wx_src_ptr
) cinfo
->src
; 
 143     if (src
->pub
.bytes_in_buffer 
> 0) 
 144         src
->stream
->SeekI(-(long)src
->pub
.bytes_in_buffer
, wxFromCurrent
); 
 145     delete[] src
->buffer
; 
 149 // JPEG error manager: 
 151 struct wx_error_mgr 
{ 
 152   struct jpeg_error_mgr pub
;    /* "public" fields */ 
 154   jmp_buf setjmp_buffer
;    /* for return to caller */ 
 157 typedef struct wx_error_mgr 
* wx_error_ptr
; 
 160  * Here's the routine that will replace the standard error_exit method: 
 163 CPP_METHODDEF(void) wx_error_exit (j_common_ptr cinfo
) 
 165   /* cinfo->err really points to a wx_error_mgr struct, so coerce pointer */ 
 166   wx_error_ptr myerr 
= (wx_error_ptr
) cinfo
->err
; 
 168   /* Always display the message. */ 
 169   /* We could postpone this until after returning, if we chose. */ 
 170   if (cinfo
->err
->output_message
) (*cinfo
->err
->output_message
) (cinfo
); 
 172   /* Return control to the setjmp point */ 
 173   longjmp(myerr
->setjmp_buffer
, 1); 
 176 void wx_jpeg_io_src( j_decompress_ptr cinfo
, wxInputStream
& infile 
) 
 180     if (cinfo
->src 
== NULL
) {    /* first time for this JPEG object? */ 
 181         cinfo
->src 
= (struct jpeg_source_mgr 
*) 
 182             (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
, 
 183             sizeof(wx_source_mgr
)); 
 185     src 
= (wx_src_ptr
) cinfo
->src
; 
 186     src
->pub
.bytes_in_buffer 
= 0; /* forces fill_input_buffer on first read */ 
 187     src
->buffer 
= new JOCTET
[JPEG_IO_BUFFER_SIZE
]; 
 188     src
->pub
.next_input_byte 
= NULL
; /* until buffer loaded */ 
 189     src
->stream 
= &infile
; 
 191     src
->pub
.init_source 
= wx_init_source
; 
 192     src
->pub
.fill_input_buffer 
= wx_fill_input_buffer
; 
 193     src
->pub
.skip_input_data 
= wx_skip_input_data
; 
 194     src
->pub
.resync_to_restart 
= jpeg_resync_to_restart
; /* use default method */ 
 195     src
->pub
.term_source 
= wx_term_source
; 
 199 // temporarily disable the warning C4611 (interaction between '_setjmp' and 
 200 // C++ object destruction is non-portable) - I don't see any dtors here 
 202     #pragma warning(disable:4611) 
 205 bool wxJPEGHandler::LoadFile( wxImage 
*image
, wxInputStream
& stream
, bool verbose
, int WXUNUSED(index
) ) 
 207     struct jpeg_decompress_struct cinfo
; 
 208     struct wx_error_mgr jerr
; 
 214     cinfo
.err 
= jpeg_std_error( &jerr
.pub 
); 
 215     jerr
.pub
.error_exit 
= wx_error_exit
; 
 217     if (!verbose
) cinfo
.err
->output_message
=NULL
; 
 219     /* Establish the setjmp return context for wx_error_exit to use. */ 
 220     if (setjmp(jerr
.setjmp_buffer
)) { 
 221       /* If we get here, the JPEG code has signaled an error. 
 222        * We need to clean up the JPEG object, close the input file, and return. 
 225         wxLogError(_("JPEG: Couldn't load - file is probably corrupted.")); 
 226       (cinfo
.src
->term_source
)(&cinfo
); 
 227       jpeg_destroy_decompress(&cinfo
); 
 228       if (image
->Ok()) image
->Destroy(); 
 232     jpeg_create_decompress( &cinfo 
); 
 233     wx_jpeg_io_src( &cinfo
, stream 
); 
 234     jpeg_read_header( &cinfo
, TRUE 
); 
 235     cinfo
.out_color_space 
= JCS_RGB
; 
 236     jpeg_start_decompress( &cinfo 
); 
 238     image
->Create( cinfo
.image_width
, cinfo
.image_height 
); 
 240         jpeg_finish_decompress( &cinfo 
); 
 241         jpeg_destroy_decompress( &cinfo 
); 
 244     image
->SetMask( false ); 
 245     ptr 
= image
->GetData(); 
 246     stride 
= cinfo
.output_width 
* 3; 
 247     tempbuf 
= (*cinfo
.mem
->alloc_sarray
) 
 248         ((j_common_ptr
) &cinfo
, JPOOL_IMAGE
, stride
, 1 ); 
 250     while ( cinfo
.output_scanline 
< cinfo
.output_height 
) { 
 251         jpeg_read_scanlines( &cinfo
, tempbuf
, 1 ); 
 252         memcpy( ptr
, tempbuf
[0], stride 
); 
 255     jpeg_finish_decompress( &cinfo 
); 
 256     jpeg_destroy_decompress( &cinfo 
); 
 261     struct jpeg_destination_mgr pub
; 
 263     wxOutputStream 
*stream
; 
 265 } wx_destination_mgr
; 
 267 typedef wx_destination_mgr 
* wx_dest_ptr
; 
 269 #define OUTPUT_BUF_SIZE  4096    /* choose an efficiently fwrite'able size */ 
 271 CPP_METHODDEF(void) wx_init_destination (j_compress_ptr cinfo
) 
 273     wx_dest_ptr dest 
= (wx_dest_ptr
) cinfo
->dest
; 
 275     /* Allocate the output buffer --- it will be released when done with image */ 
 276     dest
->buffer 
= (JOCTET 
*) 
 277         (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_IMAGE
, 
 278         OUTPUT_BUF_SIZE 
* sizeof(JOCTET
)); 
 279     dest
->pub
.next_output_byte 
= dest
->buffer
; 
 280     dest
->pub
.free_in_buffer 
= OUTPUT_BUF_SIZE
; 
 283 CPP_METHODDEF(boolean
) wx_empty_output_buffer (j_compress_ptr cinfo
) 
 285     wx_dest_ptr dest 
= (wx_dest_ptr
) cinfo
->dest
; 
 287     dest
->stream
->Write(dest
->buffer
, OUTPUT_BUF_SIZE
); 
 288     dest
->pub
.next_output_byte 
= dest
->buffer
; 
 289     dest
->pub
.free_in_buffer 
= OUTPUT_BUF_SIZE
; 
 293 CPP_METHODDEF(void) wx_term_destination (j_compress_ptr cinfo
) 
 295     wx_dest_ptr dest 
= (wx_dest_ptr
) cinfo
->dest
; 
 296     size_t datacount 
= OUTPUT_BUF_SIZE 
- dest
->pub
.free_in_buffer
; 
 297     /* Write any data remaining in the buffer */ 
 299         dest
->stream
->Write(dest
->buffer
, datacount
); 
 302 GLOBAL(void) wx_jpeg_io_dest (j_compress_ptr cinfo
, wxOutputStream
& outfile
) 
 306     if (cinfo
->dest 
== NULL
) {    /* first time for this JPEG object? */ 
 307         cinfo
->dest 
= (struct jpeg_destination_mgr 
*) 
 308             (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
, 
 309             sizeof(wx_destination_mgr
)); 
 312     dest 
= (wx_dest_ptr
) cinfo
->dest
; 
 313     dest
->pub
.init_destination 
= wx_init_destination
; 
 314     dest
->pub
.empty_output_buffer 
= wx_empty_output_buffer
; 
 315     dest
->pub
.term_destination 
= wx_term_destination
; 
 316     dest
->stream 
= &outfile
; 
 319 bool wxJPEGHandler::SaveFile( wxImage 
*image
, wxOutputStream
& stream
, bool verbose 
) 
 321     struct jpeg_compress_struct cinfo
; 
 322     struct wx_error_mgr jerr
; 
 323     JSAMPROW row_pointer
[1];    /* pointer to JSAMPLE row[s] */ 
 324     JSAMPLE 
*image_buffer
; 
 325     int stride
;                /* physical row width in image buffer */ 
 327     cinfo
.err 
= jpeg_std_error(&jerr
.pub
); 
 328     jerr
.pub
.error_exit 
= wx_error_exit
; 
 330     if (!verbose
) cinfo
.err
->output_message
=NULL
; 
 332     /* Establish the setjmp return context for wx_error_exit to use. */ 
 333     if (setjmp(jerr
.setjmp_buffer
)) 
 335         /* If we get here, the JPEG code has signaled an error. 
 336          * We need to clean up the JPEG object, close the input file, and return. 
 339             wxLogError(_("JPEG: Couldn't save image.")); 
 340          jpeg_destroy_compress(&cinfo
); 
 344     jpeg_create_compress(&cinfo
); 
 345     wx_jpeg_io_dest(&cinfo
, stream
); 
 347     cinfo
.image_width 
= image
->GetWidth(); 
 348     cinfo
.image_height 
= image
->GetHeight(); 
 349     cinfo
.input_components 
= 3; 
 350     cinfo
.in_color_space 
= JCS_RGB
; 
 351     jpeg_set_defaults(&cinfo
); 
 353     // TODO: 3rd parameter is force_baseline, what value should this be? 
 354     // Code says: "If force_baseline is TRUE, the computed quantization table entries 
 355     // are limited to 1..255 for JPEG baseline compatibility." 
 356     // 'Quality' is a number between 0 (terrible) and 100 (very good). 
 357     // The default (in jcparam.c, jpeg_set_defaults) is 75, 
 358     // and force_baseline is TRUE. 
 359     if (image
->HasOption(wxIMAGE_OPTION_QUALITY
)) 
 360         jpeg_set_quality(&cinfo
, image
->GetOptionInt(wxIMAGE_OPTION_QUALITY
), TRUE
); 
 362     // set the resolution fields in the output file 
 365     if ( image
->HasOption(wxIMAGE_OPTION_RESOLUTIONX
) && 
 366          image
->HasOption(wxIMAGE_OPTION_RESOLUTIONY
) ) 
 368         resX 
= image
->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONX
); 
 369         resY 
= image
->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONY
); 
 371     else if ( image
->HasOption(wxIMAGE_OPTION_RESOLUTION
) ) 
 374         resY 
= image
->GetOptionInt(wxIMAGE_OPTION_RESOLUTION
); 
 384         cinfo
.X_density 
= resX
; 
 385         cinfo
.Y_density 
= resY
; 
 388     // sets the resolution unit field in the output file 
 389     // wxIMAGE_RESOLUTION_INCHES for inches 
 390     // wxIMAGE_RESOLUTION_CM for centimeters 
 391     if ( image
->HasOption(wxIMAGE_OPTION_RESOLUTIONUNIT
) ) 
 393         cinfo
.density_unit 
= (UINT8
)image
->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONUNIT
); 
 396     jpeg_start_compress(&cinfo
, TRUE
); 
 398     stride 
= cinfo
.image_width 
* 3;    /* JSAMPLEs per row in image_buffer */ 
 399     image_buffer 
= image
->GetData(); 
 400     while (cinfo
.next_scanline 
< cinfo
.image_height
) { 
 401         row_pointer
[0] = &image_buffer
[cinfo
.next_scanline 
* stride
]; 
 402         jpeg_write_scanlines( &cinfo
, row_pointer
, 1 ); 
 404     jpeg_finish_compress(&cinfo
); 
 405     jpeg_destroy_compress(&cinfo
); 
 411     #pragma warning(default:4611) 
 414 bool wxJPEGHandler::DoCanRead( wxInputStream
& stream 
) 
 416     unsigned char hdr
[2]; 
 418     if ( !stream
.Read(hdr
, WXSIZEOF(hdr
)) ) 
 421     return hdr
[0] == 0xFF && hdr
[1] == 0xD8; 
 424 #endif   // wxUSE_STREAMS 
 426 #endif   // wxUSE_LIBJPEG