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

« back to all changes in this revision

Viewing changes to src/common/imagjpeg.cpp

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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