root/OpenSceneGraph/trunk/src/osgPlugins/jpeg/ReaderWriterJPEG.cpp @ 13041

Revision 13041, 32.2 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/Image>
2#include <osg/Notify>
3#include <osg/Geode>
4#include <osg/GL>
5
6#include <osgDB/Registry>
7#include <osgDB/FileNameUtils>
8#include <osgDB/FileUtils>
9
10#include <sstream>
11
12#if defined(_MSC_VER) && defined(OSG_DISABLE_MSVC_WARNINGS)
13    // disable "structure was padded due to __declspec(align())
14    #pragma warning( disable : 4324 )
15#endif
16
17/****************************************************************************
18 *
19 * Follows is code extracted from the simage library.  Original Authors:
20 *
21 *      Systems in Motion,
22 *      <URL:http://www.sim.no>
23 *
24 *      Peder Blekken <pederb@sim.no>
25 *      Morten Eriksen <mortene@sim.no>
26 *      Marius Bugge Monsen <mariusbu@sim.no>
27 *
28 * The original COPYING notice
29 *
30 *      All files in this library are public domain, except simage_rgb.cpp which is
31 *      Copyright (c) Mark J Kilgard <mjk@nvidia.com>. I will contact Mark
32 *      very soon to hear if this source also can become public domain.
33 *
34 *      Please send patches for bugs and new features to: <pederb@sim.no>.
35 *
36 *      Peder Blekken
37 *
38 *
39 * Ported into the OSG as a plugin, Robert Osfield Decemeber 2000.
40 * Note, reference above to license of simage_rgb is not relevent to the OSG
41 * as the OSG does not use it.  Also for patches, bugs and new features
42 * please send them direct to the OSG dev team rather than address above.
43 *
44 **********************************************************************/
45
46/*
47 * Based on example code found in the libjpeg archive
48 *
49 */
50
51#include <stdio.h>
52
53extern "C"
54{
55    #include <jpeglib.h>
56    #include "jerror.h"
57}
58
59#include <setjmp.h>
60#include <string.h>
61#include <assert.h>
62#include <stdlib.h>
63
64#if defined(_MSC_VER) && defined(OSG_DISABLE_MSVC_WARNINGS)
65    #pragma warning( disable : 4611 )
66#endif
67
68namespace osgDBJPEG
69{
70
71#define ERR_NO_ERROR 0
72#define ERR_OPEN     1
73#define ERR_MEM      2
74#define ERR_JPEGLIB  3
75
76static int jpegerror = ERR_NO_ERROR;
77
78/* Some versions of jmorecfg.h define boolean, some don't...
79   Those that do also define HAVE_BOOLEAN, so we can guard using that. */
80#ifndef HAVE_BOOLEAN
81  typedef int boolean;
82  #define FALSE 0
83  #define TRUE 1
84#endif
85
86/* CODE FOR READING/WRITING JPEG FROM STREAMS
87 *  This code was taken directly from jdatasrc.c and jdatadst.c (libjpeg source)
88 *  and modified to use a std::istream/ostream* instead of a FILE*
89 */
90
91/* Expanded data source object for stdio input */
92
93typedef struct {
94    struct jpeg_source_mgr pub;    /* public fields */
95    std::istream * infile;        /* source stream */
96    JOCTET * buffer;              /* start of buffer */
97    boolean start_of_file;        /* have we gotten any data yet? */
98} stream_source_mgr;
99
100typedef stream_source_mgr * stream_src_ptr;
101
102#define INPUT_BUF_SIZE  4096    /* choose an efficiently fread'able size */
103
104/*
105 * Initialize source --- called by jpeg_read_header
106 * before any data is actually read.
107 */
108
109void init_source (j_decompress_ptr cinfo)
110{
111  stream_src_ptr src = (stream_src_ptr) cinfo->src;
112
113  /* We reset the empty-input-file flag for each image,
114   * but we don't clear the input buffer.
115   * This is correct behavior for reading a series of images from one source.
116   */
117  src->start_of_file = TRUE;
118}
119
120
121/*
122 * Fill the input buffer --- called whenever buffer is emptied.
123 *
124 * In typical applications, this should read fresh data into the buffer
125 * (ignoring the current state of next_input_byte & bytes_in_buffer),
126 * reset the pointer & count to the start of the buffer, and return TRUE
127 * indicating that the buffer has been reloaded.  It is not necessary to
128 * fill the buffer entirely, only to obtain at least one more byte.
129 *
130 * There is no such thing as an EOF return.  If the end of the file has been
131 * reached, the routine has a choice of ERREXIT() or inserting fake data into
132 * the buffer.  In most cases, generating a warning message and inserting a
133 * fake EOI marker is the best course of action --- this will allow the
134 * decompressor to output however much of the image is there.  However,
135 * the resulting error message is misleading if the real problem is an empty
136 * input file, so we handle that case specially.
137 *
138 * In applications that need to be able to suspend compression due to input
139 * not being available yet, a FALSE return indicates that no more data can be
140 * obtained right now, but more may be forthcoming later.  In this situation,
141 * the decompressor will return to its caller (with an indication of the
142 * number of scanlines it has read, if any).  The application should resume
143 * decompression after it has loaded more data into the input buffer.  Note
144 * that there are substantial restrictions on the use of suspension --- see
145 * the documentation.
146 *
147 * When suspending, the decompressor will back up to a convenient restart point
148 * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
149 * indicate where the restart point will be if the current call returns FALSE.
150 * Data beyond this point must be rescanned after resumption, so move it to
151 * the front of the buffer rather than discarding it.
152 */
153
154boolean fill_input_buffer (j_decompress_ptr cinfo)
155{
156  stream_src_ptr src = (stream_src_ptr) cinfo->src;
157  size_t nbytes;
158
159  src->infile->read((char*)src->buffer,INPUT_BUF_SIZE);
160  nbytes = src->infile->gcount();
161
162  if (nbytes <= 0) {
163    if (src->start_of_file)    /* Treat empty input file as fatal error */
164      ERREXIT(cinfo, JERR_INPUT_EMPTY);
165    WARNMS(cinfo, JWRN_JPEG_EOF);
166    /* Insert a fake EOI marker */
167    src->buffer[0] = (JOCTET) 0xFF;
168    src->buffer[1] = (JOCTET) JPEG_EOI;
169    nbytes = 2;
170  }
171
172  src->pub.next_input_byte = src->buffer;
173  src->pub.bytes_in_buffer = nbytes;
174  src->start_of_file = FALSE;
175
176  return TRUE;
177}
178
179
180/*
181 * Skip data --- used to skip over a potentially large amount of
182 * uninteresting data (such as an APPn marker).
183 *
184 * Writers of suspendable-input applications must note that skip_input_data
185 * is not granted the right to give a suspension return.  If the skip extends
186 * beyond the data currently in the buffer, the buffer can be marked empty so
187 * that the next read will cause a fill_input_buffer call that can suspend.
188 * Arranging for additional bytes to be discarded before reloading the input
189 * buffer is the application writer's problem.
190 */
191
192void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
193{
194  stream_src_ptr src = (stream_src_ptr) cinfo->src;
195
196  /* Just a dumb implementation for now.  Could use fseek() except
197   * it doesn't work on pipes.  Not clear that being smart is worth
198   * any trouble anyway --- large skips are infrequent.
199   */
200  if (num_bytes > 0) {
201    while (num_bytes > (long) src->pub.bytes_in_buffer) {
202      num_bytes -= (long) src->pub.bytes_in_buffer;
203      (void) fill_input_buffer(cinfo);
204      /* note we assume that fill_input_buffer will never return FALSE,
205       * so suspension need not be handled.
206       */
207    }
208    src->pub.next_input_byte += (size_t) num_bytes;
209    src->pub.bytes_in_buffer -= (size_t) num_bytes;
210  }
211}
212
213
214/*
215 * An additional method that can be provided by data source modules is the
216 * resync_to_restart method for error recovery in the presence of RST markers.
217 * For the moment, this source module just uses the default resync method
218 * provided by the JPEG library.  That method assumes that no backtracking
219 * is possible.
220 */
221
222
223/*
224 * Terminate source --- called by jpeg_finish_decompress
225 * after all data has been read.  Often a no-op.
226 *
227 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
228 * application must deal with any cleanup that should happen even
229 * for error exit.
230 */
231void term_source (j_decompress_ptr /*cinfo*/)
232{
233  /* no work necessary here */
234}
235
236void jpeg_istream_src(j_decompress_ptr cinfo, std::istream *infile)
237{
238    stream_src_ptr src;
239
240    /* The source object and input buffer are made permanent so that a series
241     * of JPEG images can be read from the same file by calling jpeg_stdio_src
242     * only before the first one.  (If we discarded the buffer at the end of
243     * one image, we'd likely lose the start of the next one.)
244     * This makes it unsafe to use this manager and a different source
245     * manager serially with the same JPEG object.  Caveat programmer.
246     */
247    if (cinfo->src == NULL) {    /* first time for this JPEG object? */
248        cinfo->src = (struct jpeg_source_mgr *)
249            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(stream_source_mgr));
250        src = (stream_src_ptr) cinfo->src;
251        src->buffer = (JOCTET *)
252            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,INPUT_BUF_SIZE * sizeof(JOCTET));
253    }
254
255    src = (stream_src_ptr) cinfo->src;
256    src->pub.init_source = init_source;
257    src->pub.fill_input_buffer = fill_input_buffer;
258    src->pub.skip_input_data = skip_input_data;
259    src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
260    src->pub.term_source = term_source;
261    src->infile = infile;
262    src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
263    src->pub.next_input_byte = NULL; /* until buffer loaded */
264}
265
266/* Expanded data destination object for stdio output */
267
268typedef struct {
269  struct jpeg_destination_mgr pub; /* public fields */
270
271  std::ostream * outfile;    /* target stream */
272  JOCTET * buffer;          /* start of buffer */
273} stream_destination_mgr;
274
275typedef stream_destination_mgr * stream_dest_ptr;
276
277#define OUTPUT_BUF_SIZE  4096    /* choose an efficiently fwrite'able size */
278
279
280/*
281 * Initialize destination --- called by jpeg_start_compress
282 * before any data is actually written.
283 */
284
285void init_destination (j_compress_ptr cinfo)
286{
287  stream_dest_ptr dest = (stream_dest_ptr) cinfo->dest;
288
289  /* Allocate the output buffer --- it will be released when done with image */
290  dest->buffer = (JOCTET *)
291      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof(JOCTET));
292
293  dest->pub.next_output_byte = dest->buffer;
294  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
295}
296
297
298/*
299 * Empty the output buffer --- called whenever buffer fills up.
300 *
301 * In typical applications, this should write the entire output buffer
302 * (ignoring the current state of next_output_byte & free_in_buffer),
303 * reset the pointer & count to the start of the buffer, and return TRUE
304 * indicating that the buffer has been dumped.
305 *
306 * In applications that need to be able to suspend compression due to output
307 * overrun, a FALSE return indicates that the buffer cannot be emptied now.
308 * In this situation, the compressor will return to its caller (possibly with
309 * an indication that it has not accepted all the supplied scanlines).  The
310 * application should resume compression after it has made more room in the
311 * output buffer.  Note that there are substantial restrictions on the use of
312 * suspension --- see the documentation.
313 *
314 * When suspending, the compressor will back up to a convenient restart point
315 * (typically the start of the current MCU). next_output_byte & free_in_buffer
316 * indicate where the restart point will be if the current call returns FALSE.
317 * Data beyond this point will be regenerated after resumption, so do not
318 * write it out when emptying the buffer externally.
319 */
320
321boolean empty_output_buffer (j_compress_ptr cinfo)
322{
323  stream_dest_ptr dest = (stream_dest_ptr) cinfo->dest;
324
325  dest->outfile->write((const char*)dest->buffer,OUTPUT_BUF_SIZE);
326  if (dest->outfile->bad())
327    ERREXIT(cinfo, JERR_FILE_WRITE);
328
329  dest->pub.next_output_byte = dest->buffer;
330  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
331
332  return TRUE;
333}
334
335
336/*
337 * Terminate destination --- called by jpeg_finish_compress
338 * after all data has been written.  Usually needs to flush buffer.
339 *
340 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
341 * application must deal with any cleanup that should happen even
342 * for error exit.
343 */
344
345void term_destination (j_compress_ptr cinfo)
346{
347  stream_dest_ptr dest = (stream_dest_ptr) cinfo->dest;
348  size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
349
350  /* Write any data remaining in the buffer */
351  if (datacount > 0) {
352    dest->outfile->write((const char*)dest->buffer,datacount);
353    if (dest->outfile->bad())
354      ERREXIT(cinfo, JERR_FILE_WRITE);
355  }
356  dest->outfile->flush();
357  /* Make sure we wrote the output file OK */
358  if (dest->outfile->bad())
359    ERREXIT(cinfo, JERR_FILE_WRITE);
360}
361
362
363/*
364 * Prepare for output to a stdio stream.
365 * The caller must have already opened the stream, and is responsible
366 * for closing it after finishing compression.
367 */
368
369void jpeg_stream_dest (j_compress_ptr cinfo, std::ostream * outfile)
370{
371    stream_dest_ptr dest;
372
373    /* The destination object is made permanent so that multiple JPEG images
374     * can be written to the same file without re-executing jpeg_stdio_dest.
375     * This makes it dangerous to use this manager and a different destination
376     * manager serially with the same JPEG object, because their private object
377     * sizes may be different.  Caveat programmer.
378     */
379    if (cinfo->dest == NULL) {    /* first time for this JPEG object? */
380        cinfo->dest = (struct jpeg_destination_mgr *)
381            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(stream_destination_mgr));
382    }
383
384    dest = (stream_dest_ptr) cinfo->dest;
385    dest->pub.init_destination = init_destination;
386    dest->pub.empty_output_buffer = empty_output_buffer;
387    dest->pub.term_destination = term_destination;
388    dest->outfile = outfile;
389}
390
391/* END OF READ/WRITE STREAM CODE */
392
393int
394simage_jpeg_error(char * buffer, int buflen)
395{
396    switch (jpegerror)
397    {
398        case ERR_OPEN:
399            strncpy(buffer, "JPEG loader: Error opening file", buflen);
400            break;
401        case ERR_MEM:
402            strncpy(buffer, "JPEG loader: Out of memory error", buflen);
403            break;
404        case ERR_JPEGLIB:
405            strncpy(buffer, "JPEG loader: Illegal jpeg file", buflen);
406            break;
407    }
408    return jpegerror;
409}
410
411
412struct my_error_mgr
413{
414    struct jpeg_error_mgr pub;   /* "public" fields */
415
416    jmp_buf setjmp_buffer;       /* for return to caller */
417};
418
419typedef struct my_error_mgr * my_error_ptr;
420
421static void
422my_error_exit (j_common_ptr cinfo)
423{
424    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
425    my_error_ptr myerr = (my_error_ptr) cinfo->err;
426
427    /* Always display the message. */
428    /* We could postpone this until after returning, if we chose. */
429    /*(*cinfo->err->output_message) (cinfo);*/
430
431    /* FIXME: get error messahe from jpeglib */
432
433    /* Return control to the setjmp point */
434    longjmp(myerr->setjmp_buffer, 1);
435}
436
437static void
438my_output_message (j_common_ptr cinfo)
439{
440  char buffer[JMSG_LENGTH_MAX];
441
442  /* Create the message */
443  (*cinfo->err->format_message) (cinfo, buffer);
444
445  OSG_WARN<<buffer<<std::endl;
446}
447
448
449int
450simage_jpeg_identify(const char *,
451const unsigned char *header,
452int headerlen)
453{
454    static unsigned char jpgcmp[] = {'J', 'F', 'I', 'F' };
455    if (headerlen < 4) return 0;
456    if (memcmp((const void*)&header[6],
457        (const void*)jpgcmp, 4) == 0) return 1;
458    return 0;
459}
460
461
462static unsigned char*
463copyScanline(unsigned char *currPtr, unsigned char *from, int cnt)
464{
465    memcpy((void*)currPtr, (void*)from, cnt);
466    currPtr -= cnt;
467    return currPtr;
468}
469
470unsigned char *
471simage_jpeg_load(std::istream& fin,
472int *width_ret,
473int *height_ret,
474int *numComponents_ret)
475{
476    int width;
477    int height;
478    unsigned char *currPtr;
479    int format;
480    /* This struct contains the JPEG decompression parameters and pointers to
481     * working space (which is allocated as needed by the JPEG library).
482     */
483    struct jpeg_decompress_struct cinfo;
484    /* We use our private extension JPEG error handler.
485     * Note that this struct must live as long as the main JPEG parameter
486     * struct, to avoid dangling-pointer problems.
487     */
488    struct my_error_mgr jerr;
489    /* More stuff */
490    //FILE * infile;               /* source file */
491    JSAMPARRAY rowbuffer;        /* Output row buffer */
492    int row_stride;              /* physical row width in output buffer */
493
494    jpegerror = ERR_NO_ERROR;
495
496    /* In this example we want to open the input file before doing anything else,
497     * so that the setjmp() error recovery below can assume the file is open.
498     * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
499     * requires it in order to read binary files.
500     */
501
502    /*if ((infile = fopen(filename, "rb")) == NULL)
503    {
504        jpegerror = ERR_OPEN;
505        return NULL;
506    }*/
507
508    /* Step 1: allocate and initialize JPEG decompression object */
509
510    /* We set up the normal JPEG error routines, then override error_exit. */
511    cinfo.err = jpeg_std_error(&jerr.pub);
512    jerr.pub.error_exit = my_error_exit;
513    jerr.pub.output_message = my_output_message;
514    /* Establish the setjmp return context for my_error_exit to use. */
515    if (setjmp(jerr.setjmp_buffer))
516    {
517        /* If we get here, the JPEG code has signaled an error.
518         * We need to clean up the JPEG object, close the input file, and return.
519         */
520        jpegerror = ERR_JPEGLIB;
521        jpeg_destroy_decompress(&cinfo);
522        //fclose(infile);
523        //if (buffer) delete [] buffer;
524        return NULL;
525    }
526
527    // used to be before setjump above, but have moved to after to avoid compile warnings.
528    unsigned char *buffer = NULL;
529
530    /* Now we can initialize the JPEG decompression object. */
531    jpeg_create_decompress(&cinfo);
532
533    /* Step 2: specify data source (eg, a file) */
534
535    //jpeg_stdio_src(&cinfo, infile);
536    jpeg_istream_src(&cinfo,&fin);
537
538    /* Step 3: read file parameters with jpeg_read_header() */
539
540    (void) jpeg_read_header(&cinfo, TRUE);
541    /* We can ignore the return value from jpeg_read_header since
542     *   (a) suspension is not possible with the stdio data source, and
543     *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
544     * See libjpeg.doc for more info.
545     */
546
547    /* Step 4: set parameters for decompression */
548    /* In this example, we don't need to change any of the defaults set by
549     * jpeg_read_header(), so we do nothing here.
550     */
551
552    /* Step 5: Start decompressor */
553    if (cinfo.jpeg_color_space == JCS_GRAYSCALE)
554    {
555        format = 1;
556        cinfo.out_color_space = JCS_GRAYSCALE;
557    }
558    else                         /* use rgb */
559    {
560        format = 3;
561        cinfo.out_color_space = JCS_RGB;
562    }
563
564    (void) jpeg_start_decompress(&cinfo);
565    /* We can ignore the return value since suspension is not possible
566     * with the stdio data source.
567     */
568
569    /* We may need to do some setup of our own at this point before reading
570     * the data.  After jpeg_start_decompress() we have the correct scaled
571     * output image dimensions available, as well as the output colormap
572     * if we asked for color quantization.
573     * In this example, we need to make an output work buffer of the right size.
574     */
575    /* JSAMPLEs per row in output buffer */
576    row_stride = cinfo.output_width * cinfo.output_components;
577    /* Make a one-row-high sample array that will go away when done with image */
578    rowbuffer = (*cinfo.mem->alloc_sarray)
579        ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
580    width = cinfo.output_width;
581    height = cinfo.output_height;
582    buffer = currPtr = new unsigned char [width*height*cinfo.output_components];
583
584    /* Step 6: while (scan lines remain to be read) */
585    /*           jpeg_read_scanlines(...); */
586
587    /* Here we use the library's state variable cinfo.output_scanline as the
588     * loop counter, so that we don't have to keep track ourselves.
589     */
590
591    /* flip image upside down */
592    if (buffer)
593    {
594        currPtr = buffer + row_stride * (cinfo.output_height-1);
595
596        while (cinfo.output_scanline < cinfo.output_height)
597        {
598            /* jpeg_read_scanlines expects an array of pointers to scanlines.
599             * Here the array is only one element long, but you could ask for
600             * more than one scanline at a time if that's more convenient.
601             */
602            (void) jpeg_read_scanlines(&cinfo, rowbuffer, 1);
603            /* Assume put_scanline_someplace wants a pointer and sample count. */
604            currPtr = copyScanline(currPtr, rowbuffer[0], row_stride);
605        }
606    }
607    /* Step 7: Finish decompression */
608
609    (void) jpeg_finish_decompress(&cinfo);
610    /* We can ignore the return value since suspension is not possible
611     * with the stdio data source.
612     */
613
614    /* Step 8: Release JPEG decompression object */
615
616    /* This is an important step since it will release a good deal of memory. */
617    jpeg_destroy_decompress(&cinfo);
618
619    /* After finish_decompress, we can close the input file.
620     * Here we postpone it until after no more JPEG errors are possible,
621     * so as to simplify the setjmp error logic above.  (Actually, I don't
622     * think that jpeg_destroy can do an error exit, but why assume anything...)
623     */
624    //fclose(infile);
625
626    /* At this point you may want to check to see whether any corrupt-data
627     * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
628     */
629
630    /* And we're done! */
631    if (buffer)
632    {
633        *width_ret = width;
634        *height_ret = height;
635        *numComponents_ret = format;
636    }
637    else
638    {
639        jpegerror = ERR_MEM;
640    }
641    return buffer;
642}
643} // namespace osgDBJPEG
644
645class ReaderWriterJPEG : public osgDB::ReaderWriter
646{
647
648        WriteResult::WriteStatus write_JPEG_file (std::ostream &fout, const osg::Image &img, int quality = 100) const
649        {
650            if (!img.isDataContiguous())
651            {
652                OSG_WARN<<"Warning: Writing of image data, that is non contiguous, is not supported by JPEG plugin."<<std::endl;
653                return WriteResult::ERROR_IN_WRITING_FILE;
654            }
655
656            int image_width = img.s();
657            int image_height = img.t();
658            if ( (image_width == 0) || (image_height == 0) )
659            {
660                OSG_DEBUG << "ReaderWriterJPEG::write_JPEG_file - Error no size" << std::endl;
661                return WriteResult::ERROR_IN_WRITING_FILE;
662            }
663
664            J_COLOR_SPACE image_color_space = JCS_RGB;
665            int image_components = 3;
666            // Only cater for gray, alpha and RGB for now
667            switch(img.getPixelFormat()) {
668              case(GL_LUMINANCE):
669              case(GL_ALPHA): {
670                  image_color_space = JCS_GRAYSCALE;
671                  image_components = 1;
672                  break;
673              }
674              case(GL_RGB): {
675                  image_color_space = JCS_RGB;
676                  image_components = 3;
677                  break;
678              }
679              default:
680              {
681                  OSG_DEBUG << "ReaderWriterJPEG::write_JPEG_file - Error pixel format non supported" << std::endl;
682                return WriteResult::ERROR_IN_WRITING_FILE; break;
683              }
684            }
685
686            JSAMPLE* image_buffer = (JSAMPLE*)(img.data());
687
688            /* This struct contains the JPEG compression parameters and pointers to
689            * working space (which is allocated as needed by the JPEG library).
690            * It is possible to have several such structures, representing multiple
691            * compression/decompression processes, in existence at once.  We refer
692            * to any one struct (and its associated working data) as a "JPEG object".
693            */
694            struct jpeg_compress_struct cinfo;
695            /* This struct represents a JPEG error handler.  It is declared separately
696            * because applications often want to supply a specialized error handler
697            * (see the second half of this file for an example).  But here we just
698            * take the easy way out and use the standard error handler, which will
699            * print a message on stderr and call exit() if compression fails.
700            * Note that this struct must live as long as the main JPEG parameter
701            * struct, to avoid dangling-pointer problems.
702            */
703            struct jpeg_error_mgr jerr;
704            /* More stuff */
705            //FILE * outfile;        /* target file */
706            JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
707            int row_stride;        /* physical row width in image buffer */
708
709            /* Step 1: allocate and initialize JPEG compression object */
710
711            /* We have to set up the error handler first, in case the initialization
712            * step fails.  (Unlikely, but it could happen if you are out of memory.)
713            * This routine fills in the contents of struct jerr, and returns jerr's
714            * address which we place into the link field in cinfo.
715            */
716            cinfo.err = jpeg_std_error(&jerr);
717            /* Now we can initialize the JPEG compression object. */
718            jpeg_create_compress(&cinfo);
719
720            /* Step 2: specify data destination (eg, a file) */
721            /* Note: steps 2 and 3 can be done in either order. */
722
723            /* Here we use the library-supplied code to send compressed data to a
724            * stdio stream.  You can also write your own code to do something else.
725            * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
726            * requires it in order to write binary files.
727            */
728            /*if (!(outfile = fopen(filename, "wb")))
729            {
730                return WriteResult::ERROR_IN_WRITING_FILE;
731            }*/
732
733            //jpeg_stdio_dest(&cinfo, outfile);
734            osgDBJPEG::jpeg_stream_dest(&cinfo, &fout);
735
736            /* Step 3: set parameters for compression */
737
738            /* First we supply a description of the input image.
739            * Four fields of the cinfo struct must be filled in:
740            */
741            cinfo.image_width = image_width;     /* image width and height, in pixels */
742            cinfo.image_height = image_height;
743            cinfo.input_components = image_components;        /* # of color components per pixel */
744            cinfo.in_color_space = image_color_space;     /* colorspace of input image */
745            /* Now use the library's routine to set default compression parameters.
746            * (You must set at least cinfo.in_color_space before calling this,
747            * since the defaults depend on the source color space.)
748            */
749            jpeg_set_defaults(&cinfo);
750            /* Now you can set any non-default parameters you wish to.
751            * Here we just illustrate the use of quality (quantization table) scaling:
752            */
753            jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
754
755            /* Step 4: Start compressor */
756
757            /* TRUE ensures that we will write a complete interchange-JPEG file.
758            * Pass TRUE unless you are very sure of what you're doing.
759            */
760            jpeg_start_compress(&cinfo, TRUE);
761
762            /* Step 5: while (scan lines remain to be written) */
763            /*           jpeg_write_scanlines(...); */
764
765            /* Here we use the library's state variable cinfo.next_scanline as the
766            * loop counter, so that we don't have to keep track ourselves.
767            * To keep things simple, we pass one scanline per call; you can pass
768            * more if you wish, though.
769            */
770            row_stride = image_width * image_components;    /* JSAMPLEs per row in image_buffer */
771
772            while (cinfo.next_scanline < cinfo.image_height)
773            {
774                /* jpeg_write_scanlines expects an array of pointers to scanlines.
775                * Here the array is only one element long, but you could pass
776                * more than one scanline at a time if that's more convenient.
777                */
778                row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
779                (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
780            }
781
782            /* Step 6: Finish compression */
783
784            jpeg_finish_compress(&cinfo);
785            /* After finish_compress, we can close the output file. */
786            //fclose(outfile);
787
788            /* Step 7: release JPEG compression object */
789
790            /* This is an important step since it will release a good deal of memory. */
791            jpeg_destroy_compress(&cinfo);
792
793            /* And we're done! */
794            return WriteResult::FILE_SAVED;
795        }
796        int getQuality(const osgDB::ReaderWriter::Options *options) const {
797            if(options) {
798                std::istringstream iss(options->getOptionString());
799                std::string opt;
800                while (iss >> opt) {
801                    if(opt=="JPEG_QUALITY") {
802                        int quality;
803                        iss >> quality;
804                        return quality;
805                    }
806                }
807            }
808
809            return 100;
810        }
811    public:
812
813        ReaderWriterJPEG()
814        {
815            supportsExtension("jpeg","JPEG image format");
816            supportsExtension("jpg","JPEG image format");
817        }
818
819        virtual const char* className() const { return "JPEG Image Reader/Writer"; }
820
821        ReadResult readJPGStream(std::istream& fin) const
822        {
823            unsigned char *imageData = NULL;
824            int width_ret;
825            int height_ret;
826            int numComponents_ret;
827
828            imageData = osgDBJPEG::simage_jpeg_load(fin,&width_ret,&height_ret,&numComponents_ret);
829
830            if (imageData==NULL) return ReadResult::ERROR_IN_READING_FILE;
831
832            int s = width_ret;
833            int t = height_ret;
834            int r = 1;
835
836            //int internalFormat = numComponents_ret;
837            int internalFormat =
838                numComponents_ret == 1 ? GL_LUMINANCE :
839                numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
840                numComponents_ret == 3 ? GL_RGB :
841                numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;
842
843            unsigned int pixelFormat =
844                numComponents_ret == 1 ? GL_LUMINANCE :
845                numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
846                numComponents_ret == 3 ? GL_RGB :
847                numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;
848
849            unsigned int dataType = GL_UNSIGNED_BYTE;
850
851            osg::Image* pOsgImage = new osg::Image;
852            pOsgImage->setImage(s,t,r,
853                internalFormat,
854                pixelFormat,
855                dataType,
856                imageData,
857                osg::Image::USE_NEW_DELETE);
858
859            return pOsgImage;
860        }
861
862        virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
863        {
864            return readImage(fin, options);
865        }
866
867        virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
868        {
869            return readImage(file, options);
870        }
871
872        virtual ReadResult readImage(std::istream& fin,const osgDB::ReaderWriter::Options* =NULL) const
873        {
874            return readJPGStream(fin);
875        }
876
877        virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
878        {
879            std::string ext = osgDB::getLowerCaseFileExtension(file);
880            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
881
882            std::string fileName = osgDB::findDataFile( file, options );
883            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
884
885            osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
886            if(!istream) return ReadResult::ERROR_IN_READING_FILE;
887            ReadResult rr = readJPGStream(istream);
888            if(rr.validImage()) rr.getImage()->setFileName(file);
889            return rr;
890        }
891
892        virtual WriteResult writeImage(const osg::Image& img,std::ostream& fout,const osgDB::ReaderWriter::Options *options) const
893        {
894            osg::ref_ptr<osg::Image> tmp_img = new osg::Image(img);
895            tmp_img->flipVertical();
896            WriteResult::WriteStatus ws = write_JPEG_file(fout, *(tmp_img.get()), getQuality(options));
897            return ws;
898        }
899
900        virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options *options) const
901        {
902            std::string ext = osgDB::getFileExtension(fileName);
903            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
904
905            osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
906            if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
907
908            return writeImage(img,fout,options);
909        }
910};
911
912// now register with Registry to instantiate the above
913// reader/writer.
914REGISTER_OSGPLUGIN(jpeg, ReaderWriterJPEG)
Note: See TracBrowser for help on using the browser.