~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

1 by Brian Sidebotham
Initial import into Bazaar
1
/////////////////////////////////////////////////////////////////////////////
2
// Name:        src/common/imagjpeg.cpp
3
// Purpose:     wxImage JPEG handler
4
// Author:      Vaclav Slavik
5
// RCS-ID:      $Id: imagjpeg.cpp 69759 2011-11-14 13:35:48Z VZ $
6
// Copyright:   (c) Vaclav Slavik
7
// Licence:     wxWindows licence
8
/////////////////////////////////////////////////////////////////////////////
9
10
// For compilers that support precompilation, includes "wx.h".
11
#include "wx/wxprec.h"
12
13
#ifdef __BORLANDC__
14
    #pragma hdrstop
15
#endif
16
17
#if wxUSE_IMAGE && wxUSE_LIBJPEG
18
19
#include "wx/imagjpeg.h"
20
#include "wx/versioninfo.h"
21
22
#ifndef WX_PRECOMP
23
    #include "wx/log.h"
24
    #include "wx/app.h"
25
    #include "wx/intl.h"
26
    #include "wx/bitmap.h"
27
    #include "wx/module.h"
28
#endif
29
30
// A hack based on one from tif_jpeg.c to overcome the problem on Windows
31
// of rpcndr.h defining boolean with a different type to the jpeg headers.
32
//
33
// This hack is only necessary for an external jpeg library, the builtin one
34
// usually used on Windows doesn't use the type boolean, so always works.
35
//
36
#ifdef wxHACK_BOOLEAN
37
    #define HAVE_BOOLEAN
38
    #define boolean wxHACK_BOOLEAN
39
#endif
40
41
extern "C"
42
{
43
    #if defined(__WXMSW__)
44
        #define XMD_H
45
    #endif
46
    #include "jpeglib.h"
47
}
48
49
#ifndef HAVE_WXJPEG_BOOLEAN
50
typedef boolean wxjpeg_boolean;
51
#endif
52
53
#include "wx/filefn.h"
54
#include "wx/wfstream.h"
55
56
// For memcpy
57
#include <string.h>
58
// For JPEG library error handling
59
#include <setjmp.h>
60
61
// ----------------------------------------------------------------------------
62
// types
63
// ----------------------------------------------------------------------------
64
65
// the standard definition of METHODDEF(type) from jmorecfg.h is "static type"
66
// which means that we can't declare the method functions as extern "C" - the
67
// compiler (rightfully) complains about the multiple storage classes in
68
// declaration
69
//
70
// so we only add extern "C" when using our own, modified, jmorecfg.h - and use
71
// whatever we have in the system headers if this is what we use hoping that it
72
// should be ok (can't do anything else)
73
#ifdef JPEG_METHOD_LINKAGE
74
    #define CPP_METHODDEF(type) extern "C" METHODDEF(type)
75
#else // not using our jmorecfg.h header
76
    #define CPP_METHODDEF(type) METHODDEF(type)
77
#endif
78
79
//-----------------------------------------------------------------------------
80
// wxJPEGHandler
81
//-----------------------------------------------------------------------------
82
83
IMPLEMENT_DYNAMIC_CLASS(wxJPEGHandler,wxImageHandler)
84
85
#if wxUSE_STREAMS
86
87
//------------- JPEG Data Source Manager
88
89
#define JPEG_IO_BUFFER_SIZE   2048
90
91
typedef struct {
92
    struct jpeg_source_mgr pub;   /* public fields */
93
94
    JOCTET* buffer;               /* start of buffer */
95
    wxInputStream *stream;
96
} wx_source_mgr;
97
98
typedef wx_source_mgr * wx_src_ptr;
99
100
extern "C"
101
{
102
103
CPP_METHODDEF(void) wx_init_source ( j_decompress_ptr WXUNUSED(cinfo) )
104
{
105
}
106
107
CPP_METHODDEF(wxjpeg_boolean) wx_fill_input_buffer ( j_decompress_ptr cinfo )
108
{
109
    wx_src_ptr src = (wx_src_ptr) cinfo->src;
110
111
    src->pub.next_input_byte = src->buffer;
112
    src->pub.bytes_in_buffer = src->stream->Read(src->buffer, JPEG_IO_BUFFER_SIZE).LastRead();
113
114
    if (src->pub.bytes_in_buffer == 0) // check for end-of-stream
115
    {
116
        // Insert a fake EOI marker
117
        src->buffer[0] = 0xFF;
118
        src->buffer[1] = JPEG_EOI;
119
        src->pub.bytes_in_buffer = 2;
120
    }
121
    return TRUE;
122
}
123
124
CPP_METHODDEF(void) wx_skip_input_data ( j_decompress_ptr cinfo, long num_bytes )
125
{
126
    if (num_bytes > 0)
127
    {
128
        wx_src_ptr src = (wx_src_ptr) cinfo->src;
129
130
        while (num_bytes > (long)src->pub.bytes_in_buffer)
131
        {
132
            num_bytes -= (long) src->pub.bytes_in_buffer;
133
            src->pub.fill_input_buffer(cinfo);
134
        }
135
        src->pub.next_input_byte += (size_t) num_bytes;
136
        src->pub.bytes_in_buffer -= (size_t) num_bytes;
137
    }
138
}
139
140
CPP_METHODDEF(void) wx_term_source ( j_decompress_ptr cinfo )
141
{
142
    wx_src_ptr src = (wx_src_ptr) cinfo->src;
143
144
    if (src->pub.bytes_in_buffer > 0)
145
        src->stream->SeekI(-(long)src->pub.bytes_in_buffer, wxFromCurrent);
146
    delete[] src->buffer;
147
}
148
149
150
// JPEG error manager:
151
152
struct wx_error_mgr : public jpeg_error_mgr
153
{
154
  jmp_buf setjmp_buffer;    /* for return to caller */
155
};
156
157
/*
158
 * Here's the routine that will replace the standard error_exit method:
159
 */
160
161
CPP_METHODDEF(void) wx_error_exit (j_common_ptr cinfo)
162
{
163
  /* cinfo->err really points to a wx_error_mgr struct, so coerce pointer */
164
  wx_error_mgr * const jerr = (wx_error_mgr *) cinfo->err;
165
166
  /* Always display the message. */
167
  /* We could postpone this until after returning, if we chose. */
168
  (*cinfo->err->output_message) (cinfo);
169
170
  /* Return control to the setjmp point */
171
  longjmp(jerr->setjmp_buffer, 1);
172
}
173
174
/*
175
 * This will replace the standard output_message method when the user
176
 * wants us to be silent (verbose==false). We must have such method instead of
177
 * simply using NULL for cinfo->err->output_message because it's called
178
 * unconditionally from within libjpeg when there's "garbage input".
179
 */
180
CPP_METHODDEF(void) wx_ignore_message (j_common_ptr WXUNUSED(cinfo))
181
{
182
}
183
184
void wx_jpeg_io_src( j_decompress_ptr cinfo, wxInputStream& infile )
185
{
186
    wx_src_ptr src;
187
188
    if (cinfo->src == NULL) {    /* first time for this JPEG object? */
189
        cinfo->src = (struct jpeg_source_mgr *)
190
            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
191
            sizeof(wx_source_mgr));
192
    }
193
    src = (wx_src_ptr) cinfo->src;
194
    src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
195
    src->buffer = new JOCTET[JPEG_IO_BUFFER_SIZE];
196
    src->pub.next_input_byte = NULL; /* until buffer loaded */
197
    src->stream = &infile;
198
199
    src->pub.init_source = wx_init_source;
200
    src->pub.fill_input_buffer = wx_fill_input_buffer;
201
    src->pub.skip_input_data = wx_skip_input_data;
202
    src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
203
    src->pub.term_source = wx_term_source;
204
}
205
206
} // extern "C"
207
208
static inline void wx_cmyk_to_rgb(unsigned char* rgb, const unsigned char* cmyk)
209
{
210
    register int k = 255 - cmyk[3];
211
    register int k2 = cmyk[3];
212
    register int c;
213
214
    c = k + k2 * (255 - cmyk[0]) / 255;
215
    rgb[0] = (unsigned char)((c > 255) ? 0 : (255 - c));
216
217
    c = k + k2 * (255 - cmyk[1]) / 255;
218
    rgb[1] = (unsigned char)((c > 255) ? 0 : (255 - c));
219
220
    c = k + k2 * (255 - cmyk[2]) / 255;
221
    rgb[2] = (unsigned char)((c > 255) ? 0 : (255 - c));
222
}
223
224
// temporarily disable the warning C4611 (interaction between '_setjmp' and
225
// C++ object destruction is non-portable) - I don't see any dtors here
226
#ifdef __VISUALC__
227
    #pragma warning(disable:4611)
228
#endif /* VC++ */
229
230
bool wxJPEGHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
231
{
232
    wxCHECK_MSG( image, false, "NULL image pointer" );
233
234
    struct jpeg_decompress_struct cinfo;
235
    wx_error_mgr jerr;
236
    unsigned char *ptr;
237
238
    // save this before calling Destroy()
239
    const unsigned maxWidth = image->GetOptionInt(wxIMAGE_OPTION_MAX_WIDTH),
240
                   maxHeight = image->GetOptionInt(wxIMAGE_OPTION_MAX_HEIGHT);
241
    image->Destroy();
242
243
    cinfo.err = jpeg_std_error( &jerr );
244
    jerr.error_exit = wx_error_exit;
245
246
    if (!verbose)
247
        cinfo.err->output_message = wx_ignore_message;
248
249
    /* Establish the setjmp return context for wx_error_exit to use. */
250
    if (setjmp(jerr.setjmp_buffer)) {
251
      /* If we get here, the JPEG code has signaled an error.
252
       * We need to clean up the JPEG object, close the input file, and return.
253
       */
254
      if (verbose)
255
      {
256
        wxLogError(_("JPEG: Couldn't load - file is probably corrupted."));
257
      }
258
      (cinfo.src->term_source)(&cinfo);
259
      jpeg_destroy_decompress(&cinfo);
260
      if (image->IsOk()) image->Destroy();
261
      return false;
262
    }
263
264
    jpeg_create_decompress( &cinfo );
265
    wx_jpeg_io_src( &cinfo, stream );
266
    jpeg_read_header( &cinfo, TRUE );
267
268
    int bytesPerPixel;
269
    if ((cinfo.out_color_space == JCS_CMYK) || (cinfo.out_color_space == JCS_YCCK))
270
    {
271
        cinfo.out_color_space = JCS_CMYK;
272
        bytesPerPixel = 4;
273
    }
274
    else // all the rest is treated as RGB
275
    {
276
        cinfo.out_color_space = JCS_RGB;
277
        bytesPerPixel = 3;
278
    }
279
280
    // scale the picture to fit in the specified max size if necessary
281
    if ( maxWidth > 0 || maxHeight > 0 )
282
    {
283
        unsigned& scale = cinfo.scale_denom;
284
        while ( (maxWidth && (cinfo.image_width / scale > maxWidth)) ||
285
                    (maxHeight && (cinfo.image_height / scale > maxHeight)) )
286
        {
287
            scale *= 2;
288
        }
289
    }
290
291
    jpeg_start_decompress( &cinfo );
292
293
    image->Create( cinfo.output_width, cinfo.output_height );
294
    if (!image->IsOk()) {
295
        jpeg_finish_decompress( &cinfo );
296
        jpeg_destroy_decompress( &cinfo );
297
        return false;
298
    }
299
    image->SetMask( false );
300
    ptr = image->GetData();
301
302
    unsigned stride = cinfo.output_width * bytesPerPixel;
303
    JSAMPARRAY tempbuf = (*cinfo.mem->alloc_sarray)
304
                            ((j_common_ptr) &cinfo, JPOOL_IMAGE, stride, 1 );
305
306
    while ( cinfo.output_scanline < cinfo.output_height )
307
    {
308
        jpeg_read_scanlines( &cinfo, tempbuf, 1 );
309
        if (cinfo.out_color_space == JCS_RGB)
310
        {
311
            memcpy( ptr, tempbuf[0], stride );
312
            ptr += stride;
313
        }
314
        else // CMYK
315
        {
316
            const unsigned char* inptr = (const unsigned char*) tempbuf[0];
317
            for (size_t i = 0; i < cinfo.output_width; i++)
318
            {
319
                wx_cmyk_to_rgb(ptr, inptr);
320
                ptr += 3;
321
                inptr += 4;
322
            }
323
        }
324
    }
325
326
    // set up resolution if available: it's part of optional JFIF APP0 chunk
327
    if ( cinfo.saw_JFIF_marker )
328
    {
329
        image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, cinfo.X_density);
330
        image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, cinfo.Y_density);
331
332
        // we use the same values for this option as libjpeg so we don't need
333
        // any conversion here
334
        image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, cinfo.density_unit);
335
    }
336
337
    if ( cinfo.image_width != cinfo.output_width || cinfo.image_height != cinfo.output_height )
338
    {
339
        // save the original image size
340
        image->SetOption(wxIMAGE_OPTION_ORIGINAL_WIDTH, cinfo.image_width);
341
        image->SetOption(wxIMAGE_OPTION_ORIGINAL_HEIGHT, cinfo.image_height);
342
    }
343
344
    jpeg_finish_decompress( &cinfo );
345
    jpeg_destroy_decompress( &cinfo );
346
    return true;
347
}
348
349
typedef struct {
350
    struct jpeg_destination_mgr pub;
351
352
    wxOutputStream *stream;
353
    JOCTET * buffer;
354
} wx_destination_mgr;
355
356
typedef wx_destination_mgr * wx_dest_ptr;
357
358
#define OUTPUT_BUF_SIZE  4096    /* choose an efficiently fwrite'able size */
359
360
extern "C"
361
{
362
363
CPP_METHODDEF(void) wx_init_destination (j_compress_ptr cinfo)
364
{
365
    wx_dest_ptr dest = (wx_dest_ptr) cinfo->dest;
366
367
    /* Allocate the output buffer --- it will be released when done with image */
368
    dest->buffer = (JOCTET *)
369
        (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
370
        OUTPUT_BUF_SIZE * sizeof(JOCTET));
371
    dest->pub.next_output_byte = dest->buffer;
372
    dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
373
}
374
375
CPP_METHODDEF(wxjpeg_boolean) wx_empty_output_buffer (j_compress_ptr cinfo)
376
{
377
    wx_dest_ptr dest = (wx_dest_ptr) cinfo->dest;
378
379
    dest->stream->Write(dest->buffer, OUTPUT_BUF_SIZE);
380
    dest->pub.next_output_byte = dest->buffer;
381
    dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
382
    return TRUE;
383
}
384
385
CPP_METHODDEF(void) wx_term_destination (j_compress_ptr cinfo)
386
{
387
    wx_dest_ptr dest = (wx_dest_ptr) cinfo->dest;
388
    size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
389
    /* Write any data remaining in the buffer */
390
    if (datacount > 0)
391
        dest->stream->Write(dest->buffer, datacount);
392
}
393
394
GLOBAL(void) wx_jpeg_io_dest (j_compress_ptr cinfo, wxOutputStream& outfile)
395
{
396
    wx_dest_ptr dest;
397
398
    if (cinfo->dest == NULL) {    /* first time for this JPEG object? */
399
        cinfo->dest = (struct jpeg_destination_mgr *)
400
            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
401
            sizeof(wx_destination_mgr));
402
    }
403
404
    dest = (wx_dest_ptr) cinfo->dest;
405
    dest->pub.init_destination = wx_init_destination;
406
    dest->pub.empty_output_buffer = wx_empty_output_buffer;
407
    dest->pub.term_destination = wx_term_destination;
408
    dest->stream = &outfile;
409
}
410
411
} // extern "C"
412
413
bool wxJPEGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
414
{
415
    struct jpeg_compress_struct cinfo;
416
    wx_error_mgr jerr;
417
    JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
418
    JSAMPLE *image_buffer;
419
    int stride;                /* physical row width in image buffer */
420
421
    cinfo.err = jpeg_std_error(&jerr);
422
    jerr.error_exit = wx_error_exit;
423
424
    if (!verbose)
425
        cinfo.err->output_message = wx_ignore_message;
426
427
    /* Establish the setjmp return context for wx_error_exit to use. */
428
    if (setjmp(jerr.setjmp_buffer))
429
    {
430
        /* If we get here, the JPEG code has signaled an error.
431
         * We need to clean up the JPEG object, close the input file, and return.
432
         */
433
         if (verbose)
434
         {
435
            wxLogError(_("JPEG: Couldn't save image."));
436
         }
437
         jpeg_destroy_compress(&cinfo);
438
         return false;
439
    }
440
441
    jpeg_create_compress(&cinfo);
442
    wx_jpeg_io_dest(&cinfo, stream);
443
444
    cinfo.image_width = image->GetWidth();
445
    cinfo.image_height = image->GetHeight();
446
    cinfo.input_components = 3;
447
    cinfo.in_color_space = JCS_RGB;
448
    jpeg_set_defaults(&cinfo);
449
450
    // TODO: 3rd parameter is force_baseline, what value should this be?
451
    // Code says: "If force_baseline is TRUE, the computed quantization table entries
452
    // are limited to 1..255 for JPEG baseline compatibility."
453
    // 'Quality' is a number between 0 (terrible) and 100 (very good).
454
    // The default (in jcparam.c, jpeg_set_defaults) is 75,
455
    // and force_baseline is TRUE.
456
    if (image->HasOption(wxIMAGE_OPTION_QUALITY))
457
        jpeg_set_quality(&cinfo, image->GetOptionInt(wxIMAGE_OPTION_QUALITY), TRUE);
458
459
    // set the resolution fields in the output file
460
    int resX, resY;
461
    wxImageResolution res = GetResolutionFromOptions(*image, &resX, &resY);
462
    if ( res != wxIMAGE_RESOLUTION_NONE )
463
    {
464
        cinfo.X_density = resX;
465
        cinfo.Y_density = resY;
466
467
        // it so happens that wxIMAGE_RESOLUTION_INCHES/CM values are the same
468
        // ones as used by libjpeg, so we can assign them directly
469
        cinfo.density_unit = res;
470
    }
471
472
    jpeg_start_compress(&cinfo, TRUE);
473
474
    stride = cinfo.image_width * 3;    /* JSAMPLEs per row in image_buffer */
475
    image_buffer = image->GetData();
476
    while (cinfo.next_scanline < cinfo.image_height) {
477
        row_pointer[0] = &image_buffer[cinfo.next_scanline * stride];
478
        jpeg_write_scanlines( &cinfo, row_pointer, 1 );
479
    }
480
    jpeg_finish_compress(&cinfo);
481
    jpeg_destroy_compress(&cinfo);
482
483
    return true;
484
}
485
486
#ifdef __VISUALC__
487
    #pragma warning(default:4611)
488
#endif /* VC++ */
489
490
bool wxJPEGHandler::DoCanRead( wxInputStream& stream )
491
{
492
    unsigned char hdr[2];
493
494
    if ( !stream.Read(hdr, WXSIZEOF(hdr)) )     // it's ok to modify the stream position here
495
        return false;
496
497
    return hdr[0] == 0xFF && hdr[1] == 0xD8;
498
}
499
500
#endif   // wxUSE_STREAMS
501
502
/*static*/ wxVersionInfo wxJPEGHandler::GetLibraryVersionInfo()
503
{
504
    return wxVersionInfo("libjpeg", JPEG_LIB_VERSION/10, JPEG_LIB_VERSION%10);
505
}
506
507
#endif   // wxUSE_LIBJPEG