1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxImage JPEG handler 
   4 // Author:      Vaclav Slavik 
   6 // Copyright:   (c) Vaclav Slavik 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  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(__WATCOMC__) && __WATCOMC__ < 1200)) 
  46 #include "wx/filefn.h" 
  47 #include "wx/wfstream.h" 
  49 #include "wx/module.h" 
  53 // For JPEG library error handling 
  60 // ---------------------------------------------------------------------------- 
  62 // ---------------------------------------------------------------------------- 
  64 // the standard definition of METHODDEF(type) from jmorecfg.h is "static type" 
  65 // which means that we can't declare the method functions as extern "C" - the 
  66 // compiler (rightfully) complains about the multiple storage classes in 
  69 // so we only add extern "C" when using our own, modified, jmorecfg.h - and use 
  70 // whatever we have in the system headers if this is what we use hoping that it 
  71 // should be ok (can't do anything else) 
  72 #ifdef JPEG_METHOD_LINKAGE 
  73     #define CPP_METHODDEF(type) extern "C" METHODDEF(type) 
  74 #else // not using our jmorecfg.h header 
  75     #define CPP_METHODDEF(type) METHODDEF(type) 
  78 //----------------------------------------------------------------------------- 
  80 //----------------------------------------------------------------------------- 
  82 IMPLEMENT_DYNAMIC_CLASS(wxJPEGHandler
,wxImageHandler
) 
  86 //------------- JPEG Data Source Manager 
  88 #define JPEG_IO_BUFFER_SIZE   2048 
  91     struct jpeg_source_mgr pub
;   /* public fields */ 
  93     JOCTET
* buffer
;               /* start of buffer */ 
  94     wxInputStream 
*stream
; 
  97 typedef my_source_mgr 
* my_src_ptr
; 
  99 CPP_METHODDEF(void) my_init_source ( j_decompress_ptr 
WXUNUSED(cinfo
) ) 
 103 CPP_METHODDEF(boolean
) my_fill_input_buffer ( j_decompress_ptr cinfo 
) 
 105     my_src_ptr src 
= (my_src_ptr
) cinfo
->src
; 
 107     src
->pub
.next_input_byte 
= src
->buffer
; 
 108     src
->pub
.bytes_in_buffer 
= src
->stream
->Read(src
->buffer
, JPEG_IO_BUFFER_SIZE
).LastRead(); 
 110     if (src
->pub
.bytes_in_buffer 
== 0) // check for end-of-stream 
 112         // Insert a fake EOI marker 
 113         src
->buffer
[0] = 0xFF; 
 114         src
->buffer
[1] = JPEG_EOI
; 
 115         src
->pub
.bytes_in_buffer 
= 2; 
 120 CPP_METHODDEF(void) my_skip_input_data ( j_decompress_ptr cinfo
, long num_bytes 
) 
 124         my_src_ptr src 
= (my_src_ptr
) cinfo
->src
; 
 126         while (num_bytes 
> (long)src
->pub
.bytes_in_buffer
) 
 128             num_bytes 
-= (long) src
->pub
.bytes_in_buffer
; 
 129             src
->pub
.fill_input_buffer(cinfo
); 
 131         src
->pub
.next_input_byte 
+= (size_t) num_bytes
; 
 132         src
->pub
.bytes_in_buffer 
-= (size_t) num_bytes
; 
 136 CPP_METHODDEF(void) my_term_source ( j_decompress_ptr cinfo 
) 
 138     my_src_ptr src 
= (my_src_ptr
) cinfo
->src
; 
 140     if (src
->pub
.bytes_in_buffer 
> 0) 
 141         src
->stream
->SeekI(-(long)src
->pub
.bytes_in_buffer
, wxFromCurrent
); 
 142     delete[] src
->buffer
; 
 146 // JPEG error manager: 
 148 struct my_error_mgr 
{ 
 149   struct jpeg_error_mgr pub
;    /* "public" fields */ 
 151   jmp_buf setjmp_buffer
;    /* for return to caller */ 
 154 typedef struct my_error_mgr 
* my_error_ptr
; 
 157  * Here's the routine that will replace the standard error_exit method: 
 160 CPP_METHODDEF(void) my_error_exit (j_common_ptr cinfo
) 
 162   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ 
 163   my_error_ptr myerr 
= (my_error_ptr
) cinfo
->err
; 
 165   /* Always display the message. */ 
 166   /* We could postpone this until after returning, if we chose. */ 
 167   if (cinfo
->err
->output_message
) (*cinfo
->err
->output_message
) (cinfo
); 
 169   /* Return control to the setjmp point */ 
 170   longjmp(myerr
->setjmp_buffer
, 1); 
 173 void jpeg_wxio_src( j_decompress_ptr cinfo
, wxInputStream
& infile 
) 
 177     if (cinfo
->src 
== NULL
) {    /* first time for this JPEG object? */ 
 178         cinfo
->src 
= (struct jpeg_source_mgr 
*) 
 179             (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
, 
 180             sizeof(my_source_mgr
)); 
 181         src 
= (my_src_ptr
) cinfo
->src
; 
 183     src 
= (my_src_ptr
) cinfo
->src
; 
 184     src
->pub
.bytes_in_buffer 
= 0; /* forces fill_input_buffer on first read */ 
 185     src
->buffer 
= new JOCTET
[JPEG_IO_BUFFER_SIZE
]; 
 186     src
->pub
.next_input_byte 
= NULL
; /* until buffer loaded */ 
 187     src
->stream 
= &infile
; 
 189     src
->pub
.init_source 
= my_init_source
; 
 190     src
->pub
.fill_input_buffer 
= my_fill_input_buffer
; 
 191     src
->pub
.skip_input_data 
= my_skip_input_data
; 
 192     src
->pub
.resync_to_restart 
= jpeg_resync_to_restart
; /* use default method */ 
 193     src
->pub
.term_source 
= my_term_source
; 
 197 // temporarily disable the warning C4611 (interaction between '_setjmp' and 
 198 // C++ object destruction is non-portable) - I don't see any dtors here 
 200     #pragma warning(disable:4611) 
 203 bool wxJPEGHandler::LoadFile( wxImage 
*image
, wxInputStream
& stream
, bool verbose
, int WXUNUSED(index
) ) 
 205     struct jpeg_decompress_struct cinfo
; 
 206     struct my_error_mgr jerr
; 
 212     cinfo
.err 
= jpeg_std_error( &jerr
.pub 
); 
 213     jerr
.pub
.error_exit 
= my_error_exit
; 
 215     if (!verbose
) cinfo
.err
->output_message
=NULL
; 
 217     /* Establish the setjmp return context for my_error_exit to use. */ 
 218     if (setjmp(jerr
.setjmp_buffer
)) { 
 219       /* If we get here, the JPEG code has signaled an error. 
 220        * We need to clean up the JPEG object, close the input file, and return. 
 223         wxLogError(_("JPEG: Couldn't load - file is probably corrupted.")); 
 224       (cinfo
.src
->term_source
)(&cinfo
); 
 225       jpeg_destroy_decompress(&cinfo
); 
 226       if (image
->Ok()) image
->Destroy(); 
 230     jpeg_create_decompress( &cinfo 
); 
 231     jpeg_wxio_src( &cinfo
, stream 
); 
 232     jpeg_read_header( &cinfo
, TRUE 
); 
 233     cinfo
.out_color_space 
= JCS_RGB
; 
 234     jpeg_start_decompress( &cinfo 
); 
 236     image
->Create( cinfo
.image_width
, cinfo
.image_height 
); 
 238         jpeg_finish_decompress( &cinfo 
); 
 239         jpeg_destroy_decompress( &cinfo 
); 
 242     image
->SetMask( FALSE 
); 
 243     ptr 
= image
->GetData(); 
 244     stride 
= cinfo
.output_width 
* 3; 
 245     tempbuf 
= (*cinfo
.mem
->alloc_sarray
) 
 246         ((j_common_ptr
) &cinfo
, JPOOL_IMAGE
, stride
, 1 ); 
 248     while ( cinfo
.output_scanline 
< cinfo
.output_height 
) { 
 249         jpeg_read_scanlines( &cinfo
, tempbuf
, 1 ); 
 250         memcpy( ptr
, tempbuf
[0], stride 
); 
 253     jpeg_finish_decompress( &cinfo 
); 
 254     jpeg_destroy_decompress( &cinfo 
); 
 259     struct jpeg_destination_mgr pub
; 
 261     wxOutputStream 
*stream
; 
 263 } my_destination_mgr
; 
 265 typedef my_destination_mgr 
* my_dest_ptr
; 
 267 #define OUTPUT_BUF_SIZE  4096    /* choose an efficiently fwrite'able size */ 
 269 CPP_METHODDEF(void) init_destination (j_compress_ptr cinfo
) 
 271     my_dest_ptr dest 
= (my_dest_ptr
) cinfo
->dest
; 
 273     /* Allocate the output buffer --- it will be released when done with image */ 
 274     dest
->buffer 
= (JOCTET 
*) 
 275         (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_IMAGE
, 
 276         OUTPUT_BUF_SIZE 
* sizeof(JOCTET
)); 
 277     dest
->pub
.next_output_byte 
= dest
->buffer
; 
 278     dest
->pub
.free_in_buffer 
= OUTPUT_BUF_SIZE
; 
 281 CPP_METHODDEF(boolean
) empty_output_buffer (j_compress_ptr cinfo
) 
 283     my_dest_ptr dest 
= (my_dest_ptr
) cinfo
->dest
; 
 285     dest
->stream
->Write(dest
->buffer
, OUTPUT_BUF_SIZE
); 
 286     dest
->pub
.next_output_byte 
= dest
->buffer
; 
 287     dest
->pub
.free_in_buffer 
= OUTPUT_BUF_SIZE
; 
 291 CPP_METHODDEF(void) term_destination (j_compress_ptr cinfo
) 
 293     my_dest_ptr dest 
= (my_dest_ptr
) cinfo
->dest
; 
 294     size_t datacount 
= OUTPUT_BUF_SIZE 
- dest
->pub
.free_in_buffer
; 
 295     /* Write any data remaining in the buffer */ 
 297         dest
->stream
->Write(dest
->buffer
, datacount
); 
 300 GLOBAL(void) jpeg_wxio_dest (j_compress_ptr cinfo
, wxOutputStream
& outfile
) 
 304     if (cinfo
->dest 
== NULL
) {    /* first time for this JPEG object? */ 
 305         cinfo
->dest 
= (struct jpeg_destination_mgr 
*) 
 306             (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
, 
 307             sizeof(my_destination_mgr
)); 
 310     dest 
= (my_dest_ptr
) cinfo
->dest
; 
 311     dest
->pub
.init_destination 
= init_destination
; 
 312     dest
->pub
.empty_output_buffer 
= empty_output_buffer
; 
 313     dest
->pub
.term_destination 
= term_destination
; 
 314     dest
->stream 
= &outfile
; 
 317 bool wxJPEGHandler::SaveFile( wxImage 
*image
, wxOutputStream
& stream
, bool verbose 
) 
 319     struct jpeg_compress_struct cinfo
; 
 320     struct my_error_mgr jerr
; 
 321     JSAMPROW row_pointer
[1];    /* pointer to JSAMPLE row[s] */ 
 322     JSAMPLE 
*image_buffer
; 
 323     int stride
;                /* physical row width in image buffer */ 
 325     cinfo
.err 
= jpeg_std_error(&jerr
.pub
); 
 326     jerr
.pub
.error_exit 
= my_error_exit
; 
 328     if (!verbose
) cinfo
.err
->output_message
=NULL
; 
 330     /* Establish the setjmp return context for my_error_exit to use. */ 
 331     if (setjmp(jerr
.setjmp_buffer
)) 
 333         /* If we get here, the JPEG code has signaled an error. 
 334          * We need to clean up the JPEG object, close the input file, and return. 
 337             wxLogError(_("JPEG: Couldn't save image.")); 
 338          jpeg_destroy_compress(&cinfo
); 
 342     jpeg_create_compress(&cinfo
); 
 343     jpeg_wxio_dest(&cinfo
, stream
); 
 345     cinfo
.image_width 
= image
->GetWidth(); 
 346     cinfo
.image_height 
= image
->GetHeight(); 
 347     cinfo
.input_components 
= 3; 
 348     cinfo
.in_color_space 
= JCS_RGB
; 
 349     jpeg_set_defaults(&cinfo
); 
 351     // TODO: 3rd parameter is force_baseline, what value should this be? 
 352     // Code says: "If force_baseline is TRUE, the computed quantization table entries 
 353     // are limited to 1..255 for JPEG baseline compatibility." 
 354     // 'Quality' is a number between 0 (terrible) and 100 (very good). 
 355     // The default (in jcparam.c, jpeg_set_defaults) is 75, 
 356     // and force_baseline is TRUE. 
 357     if (image
->HasOption(wxT("quality"))) 
 358         jpeg_set_quality(&cinfo
, image
->GetOptionInt(wxT("quality")), TRUE
); 
 360     jpeg_start_compress(&cinfo
, TRUE
); 
 362     stride 
= cinfo
.image_width 
* 3;    /* JSAMPLEs per row in image_buffer */ 
 363     image_buffer 
= image
->GetData(); 
 364     while (cinfo
.next_scanline 
< cinfo
.image_height
) { 
 365         row_pointer
[0] = &image_buffer
[cinfo
.next_scanline 
* stride
]; 
 366         jpeg_write_scanlines( &cinfo
, row_pointer
, 1 ); 
 368     jpeg_finish_compress(&cinfo
); 
 369     jpeg_destroy_compress(&cinfo
); 
 375     #pragma warning(default:4611) 
 378 bool wxJPEGHandler::DoCanRead( wxInputStream
& stream 
) 
 380     unsigned char hdr
[2]; 
 382     if ( !stream
.Read(hdr
, WXSIZEOF(hdr
)) ) 
 385     return hdr
[0] == 0xFF && hdr
[1] == 0xD8; 
 388 #endif   // wxUSE_STREAMS 
 390 #endif   // wxUSE_LIBJPEG