ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

jpeg.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // libjpeg uses forbidden symbols in its header. Thus, we need to allow them
00024 // here.
00025 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00026 
00027 #include "image/jpeg.h"
00028 
00029 #include "common/debug.h"
00030 #include "common/endian.h"
00031 #include "common/stream.h"
00032 #include "common/textconsole.h"
00033 #include "graphics/pixelformat.h"
00034 
00035 #ifdef USE_JPEG
00036 // The original release of libjpeg v6b did not contain any extern "C" in case
00037 // its header files are included in a C++ environment. To avoid any linking
00038 // issues we need to add it on our own.
00039 extern "C" {
00040 #include <jpeglib.h>
00041 #include <jerror.h>
00042 }
00043 #endif
00044 
00045 namespace Image {
00046 
00047 JPEGDecoder::JPEGDecoder() : _surface(), _colorSpace(kColorSpaceRGBA) {
00048 }
00049 
00050 JPEGDecoder::~JPEGDecoder() {
00051     destroy();
00052 }
00053 
00054 const Graphics::Surface *JPEGDecoder::getSurface() const {
00055     return &_surface;
00056 }
00057 
00058 void JPEGDecoder::destroy() {
00059     _surface.free();
00060 }
00061 
00062 const Graphics::Surface *JPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
00063     if (!loadStream(stream))
00064         return 0;
00065 
00066     return getSurface();
00067 }
00068 
00069 Graphics::PixelFormat JPEGDecoder::getPixelFormat() const {
00070     return _surface.format;
00071 }
00072 
00073 #ifdef USE_JPEG
00074 namespace {
00075 
00076 #define JPEG_BUFFER_SIZE 4096
00077 
00078 struct StreamSource : public jpeg_source_mgr {
00079     Common::SeekableReadStream *stream;
00080     bool startOfFile;
00081     JOCTET buffer[JPEG_BUFFER_SIZE];
00082 };
00083 
00084 void initSource(j_decompress_ptr cinfo) {
00085     StreamSource *source = (StreamSource *)cinfo->src;
00086     source->startOfFile = true;
00087 }
00088 
00089 boolean fillInputBuffer(j_decompress_ptr cinfo) {
00090     StreamSource *source = (StreamSource *)cinfo->src;
00091 
00092     uint32 bufferSize = source->stream->read((byte *)source->buffer, sizeof(source->buffer));
00093     if (bufferSize == 0) {
00094         if (source->startOfFile) {
00095             // An empty file is a fatal error
00096             ERREXIT(cinfo, JERR_INPUT_EMPTY);
00097         } else {
00098             // Otherwise we insert an EOF marker
00099             WARNMS(cinfo, JWRN_JPEG_EOF);
00100             source->buffer[0] = (JOCTET)0xFF;
00101             source->buffer[1] = (JOCTET)JPEG_EOI;
00102             bufferSize = 2;
00103         }
00104     }
00105 
00106     source->next_input_byte = source->buffer;
00107     source->bytes_in_buffer = bufferSize;
00108     source->startOfFile = false;
00109 
00110     return TRUE;
00111 }
00112 
00113 void skipInputData(j_decompress_ptr cinfo, long numBytes) {
00114     StreamSource *source = (StreamSource *)cinfo->src;
00115 
00116     if (numBytes > 0) {
00117         if (numBytes > (long)source->bytes_in_buffer) {
00118             // In case we need to skip more bytes than there are in the buffer
00119             // we will skip the remaining data and fill the buffer again
00120             numBytes -= (long)source->bytes_in_buffer;
00121 
00122             // Skip the remaining bytes
00123             source->stream->skip(numBytes);
00124 
00125             // Fill up the buffer again
00126             (*source->fill_input_buffer)(cinfo);
00127         } else {
00128             source->next_input_byte += (size_t)numBytes;
00129             source->bytes_in_buffer -= (size_t)numBytes;
00130         }
00131 
00132     }
00133 }
00134 
00135 void termSource(j_decompress_ptr cinfo) {
00136 }
00137 
00138 void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
00139     StreamSource *source;
00140 
00141     // Initialize the source in case it has not been done yet.
00142     if (cinfo->src == NULL) {
00143         cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(StreamSource));
00144     }
00145 
00146     source = (StreamSource *)cinfo->src;
00147     source->init_source       = &initSource;
00148     source->fill_input_buffer = &fillInputBuffer;
00149     source->skip_input_data   = &skipInputData;
00150     source->resync_to_restart = &jpeg_resync_to_restart;
00151     source->term_source       = &termSource;
00152     source->bytes_in_buffer   = 0;
00153     source->next_input_byte   = NULL;
00154 
00155     source->stream = stream;
00156 }
00157 
00158 void errorExit(j_common_ptr cinfo) {
00159     char buffer[JMSG_LENGTH_MAX];
00160     (*cinfo->err->format_message)(cinfo, buffer);
00161     // This function is not allowed to return to the caller, thus we simply
00162     // error out with our error handling here.
00163     error("%s", buffer);
00164 }
00165 
00166 void outputMessage(j_common_ptr cinfo) {
00167     char buffer[JMSG_LENGTH_MAX];
00168     (*cinfo->err->format_message)(cinfo, buffer);
00169     // Is using debug here a good idea? Or do we want to ignore all libjpeg
00170     // messages?
00171     debug(3, "libjpeg: %s", buffer);
00172 }
00173 
00174 } // End of anonymous namespace
00175 #endif
00176 
00177 bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
00178 #ifdef USE_JPEG
00179     // Reset member variables from previous decodings
00180     destroy();
00181 
00182     jpeg_decompress_struct cinfo;
00183     jpeg_error_mgr jerr;
00184 
00185     // Initialize error handling callbacks
00186     cinfo.err = jpeg_std_error(&jerr);
00187     cinfo.err->error_exit = &errorExit;
00188     cinfo.err->output_message = &outputMessage;
00189 
00190     // Initialize the decompression structure
00191     jpeg_create_decompress(&cinfo);
00192 
00193     // Initialize our buffer handling
00194     jpeg_scummvm_src(&cinfo, &stream);
00195 
00196     // Read the file header
00197     jpeg_read_header(&cinfo, TRUE);
00198 
00199     // We can request YUV output because Groovie requires it
00200     switch (_colorSpace) {
00201     case kColorSpaceRGBA:
00202         cinfo.out_color_space = JCS_RGB;
00203         break;
00204 
00205     case kColorSpaceYUV:
00206         cinfo.out_color_space = JCS_YCbCr;
00207         break;
00208     }
00209 
00210     // Actually start decompressing the image
00211     jpeg_start_decompress(&cinfo);
00212 
00213     // Allocate buffers for the output data
00214     switch (_colorSpace) {
00215     case kColorSpaceRGBA:
00216         // We use RGBA8888 in this scenario
00217         _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
00218         break;
00219 
00220     case kColorSpaceYUV:
00221         // We use YUV with 3 bytes per pixel otherwise.
00222         // This is pretty ugly since our PixelFormat cannot express YUV...
00223         _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
00224         break;
00225     }
00226 
00227     // Allocate buffer for one scanline
00228     assert(cinfo.output_components == 3);
00229     JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
00230     assert(_surface.pitch >= pitch);
00231     JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
00232 
00233     // Go through the image data scanline by scanline
00234     while (cinfo.output_scanline < cinfo.output_height) {
00235         byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
00236 
00237         jpeg_read_scanlines(&cinfo, buffer, 1);
00238 
00239         const byte *src = buffer[0];
00240         switch (_colorSpace) {
00241         case kColorSpaceRGBA: {
00242             for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
00243                 byte r = *src++;
00244                 byte g = *src++;
00245                 byte b = *src++;
00246                 // We need to insert a alpha value of 255 (opaque) here.
00247 #ifdef SCUMM_BIG_ENDIAN
00248                 *dst++ = r;
00249                 *dst++ = g;
00250                 *dst++ = b;
00251                 *dst++ = 0xFF;
00252 #else
00253                 *dst++ = 0xFF;
00254                 *dst++ = b;
00255                 *dst++ = g;
00256                 *dst++ = r;
00257 #endif
00258             }
00259             } break;
00260 
00261         case kColorSpaceYUV:
00262             memcpy(dst, src, pitch);
00263             break;
00264         }
00265     }
00266 
00267     // We are done with decompressing, thus free all the data
00268     jpeg_finish_decompress(&cinfo);
00269     jpeg_destroy_decompress(&cinfo);
00270 
00271     return true;
00272 #else
00273     return false;
00274 #endif
00275 }
00276 
00277 } // End of Graphics namespace


Generated on Sat Feb 16 2019 05:00:55 for ResidualVM by doxygen 1.7.1
curved edge   curved edge