wave-jpeg.cpp

Go to the documentation of this file.
00001 /*
00002  * wave-jpeg.cpp
00003  *
00004  * Copyright (C) 2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Reading jpeg files.  See wave-jpeg.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "wave-jpeg.h"                  // always include our own header first!
00036 
00037 // libjpeg headers
00038 #include <setjmp.h>
00039 #include <jpeglib.h>
00040 
00041 #include "common/wave_ex.h"
00042 #include "nstream/nstream.h"
00043 #include "perf/perf.h"
00044 #include "util/file.h"
00045 #include "util/token_stream.h"
00046 
00047 
00048 
00049 namespace media {
00050 
00051 
00052 // constants
00053 
00054 static const int s_bufferSize           = 4096;
00055 
00056 struct source_context_t {
00057         // constructor, manipulators
00058         source_context_t(void) throw() { this->clear(); }
00059         void clear(void) throw() {
00060                         pstream = NULL;
00061                 }
00062 
00063         struct jpeg_source_mgr          pub;
00064         std::istream *                  pstream;
00065         char                            buffer[s_bufferSize];
00066 };
00067 
00068 
00069 
00070 
00071 ////////////////////////////////////////////////////////////////////////////////
00072 //
00073 //      static helper methods
00074 //
00075 ////////////////////////////////////////////////////////////////////////////////
00076 
00077 static void
00078 init_source
00079 (
00080 IN struct jpeg_decompress_struct * pds
00081 )
00082 {
00083         pds = pds;      // unused
00084         // nothing to do!
00085 }
00086 
00087 
00088 #ifdef WIN32
00089 typedef boolean boolean_t;
00090 #else   // WIN32
00091 typedef int boolean_t;
00092 #endif  // WIN32
00093 
00094 static boolean_t
00095 fill_input_buffer
00096 (
00097 IN struct jpeg_decompress_struct * pds
00098 )
00099 {
00100         ASSERT(pds, "null");
00101         source_context_t * psc = (source_context_t *) pds->src;
00102         ASSERT(psc, "null source object");
00103         ASSERT(psc->pstream, "null stream");
00104 
00105         // I still find it hard to believe there isn't a transactional
00106         //   read command for std::istream()!  Calling read() and then
00107         //   gcount() is bogus...
00108         psc->pstream->read(psc->buffer, s_bufferSize);
00109         int nRead = psc->pstream->gcount();
00110 
00111         // we take a cue from the SDL source: if the stream ends early,
00112         //   fake an end of image.  I'm not sure if this works, but it
00113         //   may make loading of partial images more graceful.
00114         if (nRead <= 0) {
00115                 psc->buffer[0] = 0xFF;
00116                 psc->buffer[1] = JPEG_EOI;
00117                 nRead = 2;
00118         }
00119 
00120         // update public data
00121         psc->pub.next_input_byte = (JOCTET *) psc->buffer;
00122         psc->pub.bytes_in_buffer = nRead;
00123 
00124         return TRUE;
00125 }
00126 
00127 
00128 
00129 static void
00130 skip_input_data
00131 (
00132 IN struct jpeg_decompress_struct * pds,
00133 IN long bytes
00134 )
00135 {
00136         ASSERT(pds, "null");
00137         source_context_t * psc = (source_context_t *) pds->src;
00138         ASSERT(psc, "null source");
00139 
00140         if (bytes < 1)
00141                 return;         // nothing to do...
00142 
00143         while (bytes > (long) psc->pub.bytes_in_buffer) {
00144                 bytes -= psc->pub.bytes_in_buffer;
00145                 psc->pub.fill_input_buffer(pds);
00146         }
00147 
00148         psc->pub.next_input_byte += bytes;
00149         psc->pub.bytes_in_buffer -= bytes;
00150 }
00151 
00152 
00153 
00154 static void
00155 term_source
00156 (
00157 IN struct jpeg_decompress_struct * pds
00158 )
00159 {
00160         pds = pds;      // unused
00161         // nothing to do...
00162 }
00163 
00164 
00165 
00166 static void
00167 setJpegSourceStream
00168 (
00169 IN struct jpeg_decompress_struct& cinfo,
00170 IN std::istream& stream
00171 )
00172 {
00173         ASSERT_THROW(stream.good(), "bad?");
00174 
00175         // Here we are setting our own source for jpeg data.  Basically we
00176         // are wrapping the std::istream so the jpeg libraries can pull data.
00177         // Look for this sort of code out in the Internet for other examples.
00178         if (!cinfo.src) {
00179                 cinfo.src = (struct jpeg_source_mgr *)
00180                     (*cinfo.mem->alloc_small)((j_common_ptr) &cinfo,
00181                         JPOOL_PERMANENT,
00182                         sizeof(source_context_t));
00183                 ASSERT(cinfo.src, "out of memory");
00184 
00185                 source_context_t * psc = (source_context_t *) cinfo.src;
00186                 psc->clear();
00187         }
00188 
00189         // set up other data
00190         source_context_t * psc = (source_context_t *) cinfo.src;
00191         ASSERT(psc, "null");
00192         psc->pub.init_source = init_source;
00193         psc->pub.fill_input_buffer = fill_input_buffer;
00194         psc->pub.skip_input_data = skip_input_data;
00195         psc->pub.resync_to_restart = jpeg_resync_to_restart;    // use default
00196         psc->pub.term_source = term_source;
00197         psc->pub.next_input_byte = (JOCTET *) psc->buffer;
00198         psc->pub.bytes_in_buffer = 0;   // needs to be filled
00199 
00200         psc->pstream = &stream;
00201 }
00202 
00203 
00204 
00205 struct my_error_mgr {
00206         struct jpeg_error_mgr   errmgr;
00207         jmp_buf                 setjmp_buffer;
00208 };
00209 
00210 
00211 
00212 static void
00213 my_error_exit
00214 (
00215 IN j_common_ptr cinfo
00216 )
00217 {
00218         my_error_mgr * err = (my_error_mgr *) cinfo->err;
00219         longjmp(err->setjmp_buffer, 1);
00220 }
00221 
00222 
00223 
00224 ////////////////////////////////////////////////////////////////////////////////
00225 //
00226 //      public API
00227 //
00228 ////////////////////////////////////////////////////////////////////////////////
00229 
00230 void
00231 readJpegFromStream
00232 (
00233 IO std::istream& stream,
00234 OUT image_t& image
00235 )
00236 {
00237         perf::Timer timer("readJpegFromStream");
00238         ASSERT_THROW(stream.good(), "bad?");
00239         image.clear();
00240 
00241         struct jpeg_decompress_struct cinfo;
00242         struct my_error_mgr jerr;
00243         JSAMPROW rowptr[1];
00244 
00245         // this is where we go if there's an error
00246         cinfo.err = jpeg_std_error(&jerr.errmgr);
00247         jerr.errmgr.error_exit = my_error_exit;
00248         cinfo.err->output_message = NULL;
00249         if (setjmp(jerr.setjmp_buffer)) {
00250                 jpeg_destroy_decompress(&cinfo);
00251                 image.clear();
00252                 WAVE_EX(wex);
00253                 wex << "Failed to read jpeg from stream";
00254         }
00255 
00256         // initialize
00257         jpeg_create_decompress(&cinfo);
00258         setJpegSourceStream(cinfo, stream);     // use stream as data source
00259         jpeg_read_header(&cinfo, TRUE);
00260         cinfo.out_color_space = JCS_RGB;        // we want RGB data
00261         cinfo.quantize_colors = FALSE;
00262         jpeg_calc_output_dimensions(&cinfo);
00263 
00264         // allocate space for raw data
00265         image.width = cinfo.output_width;
00266         image.height = cinfo.output_height;
00267         image.bytes_per_pixel = 3;
00268         image.bit_depth = 24;
00269         image.color_format = eColorFormat_RGB;
00270         image.allocate();
00271         if (!image.data) {
00272                 jpeg_destroy_decompress(&cinfo);
00273                 WAVE_EX(wex);
00274                 wex << "Out of memory";
00275         }
00276 
00277         // okay, start decompressing...
00278         jpeg_start_decompress(&cinfo);
00279         while (cinfo.output_scanline < cinfo.output_height) {
00280                 rowptr[0] = (JSAMPROW) (image.data +
00281                     (cinfo.output_scanline * cinfo.output_width));
00282                 jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
00283         }
00284 
00285         // all done--cleanup!
00286         jpeg_finish_decompress(&cinfo);
00287         jpeg_destroy_decompress(&cinfo);
00288 }
00289 
00290 
00291 
00292 class JpegImageLoader : public ImageLoader {
00293 public:
00294         ~JpegImageLoader(void) throw() { }
00295 
00296         // media::ImageLoader class interface methods --------------------------
00297         const char * getLoaderName(void) const throw() { return "JPEG"; }
00298 
00299         bool canLoadImage(IN nstream::Manager * mgr,
00300                                 IN const char * name) const {
00301                         mgr = mgr;              // unused
00302                         ASSERT(name, "null");
00303 
00304                         const char * ext = GetExtension(name);
00305                         return (!strcasecmp(ext, "jpg") ||
00306                                 !strcasecmp(ext, "jpeg"));
00307                 }
00308 
00309         void load(IN nstream::Stream * stream,
00310                                 OUT image_t& image) const {
00311                         ASSERT(stream, "null");
00312                         std::istream& input = stream->getStream();
00313                         ASSERT_THROW(input.good(), "bad?");
00314 
00315                         readJpegFromStream(input, image);
00316                 }
00317 };
00318 
00319 
00320 
00321 smart_ptr<ImageLoader>
00322 createJpegImageLoader
00323 (
00324 void
00325 )
00326 {
00327         smart_ptr<ImageLoader> local = new JpegImageLoader;
00328         ASSERT_THROW(local, "out of memory");
00329 
00330         return local;
00331 }
00332 
00333 
00334 
00335 };      // media namespace
00336