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

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

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • 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.