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 
00089 static int
00090 fill_input_buffer
00091 (
00092 IN struct jpeg_decompress_struct * pds
00093 )
00094 {
00095         ASSERT(pds, "null");
00096         source_context_t * psc = (source_context_t *) pds->src;
00097         ASSERT(psc, "null source object");
00098         ASSERT(psc->pstream, "null stream");
00099 
00100         // I still find it hard to believe there isn't a transactional
00101         //   read command for std::istream()!  Calling read() and then
00102         //   gcount() is bogus...
00103         psc->pstream->read(psc->buffer, s_bufferSize);
00104         int nRead = psc->pstream->gcount();
00105 
00106         // we take a cue from the SDL source: if the stream ends early,
00107         //   fake an end of image.  I'm not sure if this works, but it
00108         //   may make loading of partial images more graceful.
00109         if (nRead <= 0) {
00110                 psc->buffer[0] = 0xFF;
00111                 psc->buffer[1] = JPEG_EOI;
00112                 nRead = 2;
00113         }
00114 
00115         // update public data
00116         psc->pub.next_input_byte = (JOCTET *) psc->buffer;
00117         psc->pub.bytes_in_buffer = nRead;
00118 
00119         return TRUE;
00120 }
00121 
00122 
00123 
00124 static void
00125 skip_input_data
00126 (
00127 IN struct jpeg_decompress_struct * pds,
00128 IN long bytes
00129 )
00130 {
00131         ASSERT(pds, "null");
00132         source_context_t * psc = (source_context_t *) pds->src;
00133         ASSERT(psc, "null source");
00134 
00135         if (bytes < 1)
00136                 return;         // nothing to do...
00137 
00138         while (bytes > (long) psc->pub.bytes_in_buffer) {
00139                 bytes -= psc->pub.bytes_in_buffer;
00140                 psc->pub.fill_input_buffer(pds);
00141         }
00142 
00143         psc->pub.next_input_byte += bytes;
00144         psc->pub.bytes_in_buffer -= bytes;
00145 }
00146 
00147 
00148 
00149 static void
00150 term_source
00151 (
00152 IN struct jpeg_decompress_struct * pds
00153 )
00154 {
00155         pds = pds;      // unused
00156         // nothing to do...
00157 }
00158 
00159 
00160 
00161 static void
00162 setJpegSourceStream
00163 (
00164 IN struct jpeg_decompress_struct& cinfo,
00165 IN std::istream& stream
00166 )
00167 {
00168         ASSERT_THROW(stream.good(), "bad?");
00169 
00170         // Here we are setting our own source for jpeg data.  Basically we
00171         // are wrapping the std::istream so the jpeg libraries can pull data.
00172         // Look for this sort of code out in the Internet for other examples.
00173         if (!cinfo.src) {
00174                 cinfo.src = (struct jpeg_source_mgr *)
00175                     (*cinfo.mem->alloc_small)((j_common_ptr) &cinfo,
00176                         JPOOL_PERMANENT,
00177                         sizeof(source_context_t));
00178                 ASSERT(cinfo.src, "out of memory");
00179 
00180                 source_context_t * psc = (source_context_t *) cinfo.src;
00181                 psc->clear();
00182         }
00183 
00184         // set up other data
00185         source_context_t * psc = (source_context_t *) cinfo.src;
00186         ASSERT(psc, "null");
00187         psc->pub.init_source = init_source;
00188         psc->pub.fill_input_buffer = fill_input_buffer;
00189         psc->pub.skip_input_data = skip_input_data;
00190         psc->pub.resync_to_restart = jpeg_resync_to_restart;    // use default
00191         psc->pub.term_source = term_source;
00192         psc->pub.next_input_byte = (JOCTET *) psc->buffer;
00193         psc->pub.bytes_in_buffer = 0;   // needs to be filled
00194 
00195         psc->pstream = &stream;
00196 }
00197 
00198 
00199 
00200 struct my_error_mgr {
00201         struct jpeg_error_mgr   errmgr;
00202         jmp_buf                 setjmp_buffer;
00203 };
00204 
00205 
00206 
00207 static void
00208 my_error_exit
00209 (
00210 IN j_common_ptr cinfo
00211 )
00212 {
00213         my_error_mgr * err = (my_error_mgr *) cinfo->err;
00214         longjmp(err->setjmp_buffer, 1);
00215 }
00216 
00217 
00218 
00219 ////////////////////////////////////////////////////////////////////////////////
00220 //
00221 //      public API
00222 //
00223 ////////////////////////////////////////////////////////////////////////////////
00224 
00225 void
00226 readJpegFromStream
00227 (
00228 IO std::istream& stream,
00229 OUT image_t& image
00230 )
00231 {
00232         perf::Timer timer("readJpegFromStream");
00233         ASSERT_THROW(stream.good(), "bad?");
00234         image.clear();
00235 
00236         struct jpeg_decompress_struct cinfo;
00237         struct my_error_mgr jerr;
00238         JSAMPROW rowptr[1];
00239 
00240         // this is where we go if there's an error
00241         cinfo.err = jpeg_std_error(&jerr.errmgr);
00242         jerr.errmgr.error_exit = my_error_exit;
00243         cinfo.err->output_message = NULL;
00244         if (setjmp(jerr.setjmp_buffer)) {
00245                 jpeg_destroy_decompress(&cinfo);
00246                 image.clear();
00247                 WAVE_EX(wex);
00248                 wex << "Failed to read jpeg from stream";
00249         }
00250 
00251         // initialize
00252         jpeg_create_decompress(&cinfo);
00253         setJpegSourceStream(cinfo, stream);     // use stream as data source
00254         jpeg_read_header(&cinfo, TRUE);
00255         cinfo.out_color_space = JCS_RGB;        // we want RGB data
00256         cinfo.quantize_colors = FALSE;
00257         jpeg_calc_output_dimensions(&cinfo);
00258 
00259         // allocate space for raw data
00260         image.width = cinfo.output_width;
00261         image.height = cinfo.output_height;
00262         image.bytes_per_pixel = 3;
00263         image.bit_depth = 24;
00264         image.allocate();
00265         if (!image.data) {
00266                 jpeg_destroy_decompress(&cinfo);
00267                 WAVE_EX(wex);
00268                 wex << "Out of memory";
00269         }
00270 
00271         // okay, start decompressing...
00272         jpeg_start_decompress(&cinfo);
00273         while (cinfo.output_scanline < cinfo.output_height) {
00274                 rowptr[0] = (JSAMPROW) (image.data +
00275                     (cinfo.output_scanline * cinfo.output_width));
00276                 jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
00277         }
00278 
00279         // all done--cleanup!
00280         jpeg_finish_decompress(&cinfo);
00281         jpeg_destroy_decompress(&cinfo);
00282 }
00283 
00284 
00285 
00286 class JpegImageLoader : public ImageLoader {
00287 public:
00288         ~JpegImageLoader(void) throw() { }
00289 
00290         // media::ImageLoader class interface methods --------------------------
00291         const char * getLoaderName(void) const throw() { return "JPEG"; }
00292 
00293         bool canLoadImage(IN nstream::Manager * mgr,
00294                                 IN const char * name) const {
00295                         mgr = mgr;              // unused
00296                         ASSERT(name, "null");
00297 
00298                         const char * ext = GetExtension(name);
00299                         return (!strcasecmp(ext, "jpg") ||
00300                                 !strcasecmp(ext, "jpeg"));
00301                 }
00302 
00303         void load(IN nstream::Stream * stream,
00304                                 OUT image_t& image) const {
00305                         ASSERT(stream, "null");
00306                         std::istream& input = stream->getStream();
00307                         ASSERT_THROW(input.good(), "bad?");
00308 
00309                         readJpegFromStream(input, image);
00310                 }
00311 };
00312 
00313 
00314 
00315 smart_ptr<ImageLoader>
00316 createJpegImageLoader
00317 (
00318 void
00319 )
00320 {
00321         smart_ptr<ImageLoader> local = new JpegImageLoader;
00322         ASSERT_THROW(local, "out of memory");
00323 
00324         return local;
00325 }
00326 
00327 
00328 
00329 };      // media namespace
00330