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 "common/util.h"
00034 #include "graphics/pixelformat.h"
00035 
00036 #ifdef USE_JPEG
00037 // The original release of libjpeg v6b did not contain any extern "C" in case
00038 // its header files are included in a C++ environment. To avoid any linking
00039 // issues we need to add it on our own.
00040 extern "C" {
00041 #include <jpeglib.h>
00042 #include <jerror.h>
00043 }
00044 #endif
00045 
00046 namespace Image {
00047 
00048 JPEGDecoder::JPEGDecoder() :
00049         _surface(),
00050         _colorSpace(kColorSpaceRGB),
00051         _requestedPixelFormat(getByteOrderRgbPixelFormat()) {
00052 }
00053 
00054 JPEGDecoder::~JPEGDecoder() {
00055     destroy();
00056 }
00057 
00058 Graphics::PixelFormat JPEGDecoder::getByteOrderRgbPixelFormat() const {
00059 #ifdef SCUMM_BIG_ENDIAN
00060     return Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
00061 #else
00062     return Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0);
00063 #endif
00064 }
00065 
00066 const Graphics::Surface *JPEGDecoder::getSurface() const {
00067     return &_surface;
00068 }
00069 
00070 void JPEGDecoder::destroy() {
00071     _surface.free();
00072 }
00073 
00074 const Graphics::Surface *JPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
00075     if (!loadStream(stream))
00076         return 0;
00077 
00078     return getSurface();
00079 }
00080 
00081 Graphics::PixelFormat JPEGDecoder::getPixelFormat() const {
00082     return _surface.format;
00083 }
00084 
00085 #ifdef USE_JPEG
00086 namespace {
00087 
00088 #define JPEG_BUFFER_SIZE 4096
00089 
00090 struct StreamSource : public jpeg_source_mgr {
00091     Common::SeekableReadStream *stream;
00092     bool startOfFile;
00093     JOCTET buffer[JPEG_BUFFER_SIZE];
00094 };
00095 
00096 void initSource(j_decompress_ptr cinfo) {
00097     StreamSource *source = (StreamSource *)cinfo->src;
00098     source->startOfFile = true;
00099 }
00100 
00101 boolean fillInputBuffer(j_decompress_ptr cinfo) {
00102     StreamSource *source = (StreamSource *)cinfo->src;
00103 
00104     uint32 bufferSize = source->stream->read((byte *)source->buffer, sizeof(source->buffer));
00105     if (bufferSize == 0) {
00106         if (source->startOfFile) {
00107             // An empty file is a fatal error
00108             ERREXIT(cinfo, JERR_INPUT_EMPTY);
00109         } else {
00110             // Otherwise we insert an EOF marker
00111             WARNMS(cinfo, JWRN_JPEG_EOF);
00112             source->buffer[0] = (JOCTET)0xFF;
00113             source->buffer[1] = (JOCTET)JPEG_EOI;
00114             bufferSize = 2;
00115         }
00116     }
00117 
00118     source->next_input_byte = source->buffer;
00119     source->bytes_in_buffer = bufferSize;
00120     source->startOfFile = false;
00121 
00122     return TRUE;
00123 }
00124 
00125 void skipInputData(j_decompress_ptr cinfo, long numBytes) {
00126     StreamSource *source = (StreamSource *)cinfo->src;
00127 
00128     if (numBytes > 0) {
00129         if (numBytes > (long)source->bytes_in_buffer) {
00130             // In case we need to skip more bytes than there are in the buffer
00131             // we will skip the remaining data and fill the buffer again
00132             numBytes -= (long)source->bytes_in_buffer;
00133 
00134             // Skip the remaining bytes
00135             source->stream->skip(numBytes);
00136 
00137             // Fill up the buffer again
00138             (*source->fill_input_buffer)(cinfo);
00139         } else {
00140             source->next_input_byte += (size_t)numBytes;
00141             source->bytes_in_buffer -= (size_t)numBytes;
00142         }
00143 
00144     }
00145 }
00146 
00147 void termSource(j_decompress_ptr cinfo) {
00148 }
00149 
00150 void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
00151     StreamSource *source;
00152 
00153     // Initialize the source in case it has not been done yet.
00154     if (cinfo->src == NULL) {
00155         cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(StreamSource));
00156     }
00157 
00158     source = (StreamSource *)cinfo->src;
00159     source->init_source       = &initSource;
00160     source->fill_input_buffer = &fillInputBuffer;
00161     source->skip_input_data   = &skipInputData;
00162     source->resync_to_restart = &jpeg_resync_to_restart;
00163     source->term_source       = &termSource;
00164     source->bytes_in_buffer   = 0;
00165     source->next_input_byte   = NULL;
00166 
00167     source->stream = stream;
00168 }
00169 
00170 void errorExit(j_common_ptr cinfo) {
00171     char buffer[JMSG_LENGTH_MAX];
00172     (*cinfo->err->format_message)(cinfo, buffer);
00173     // This function is not allowed to return to the caller, thus we simply
00174     // error out with our error handling here.
00175     error("%s", buffer);
00176 }
00177 
00178 void outputMessage(j_common_ptr cinfo) {
00179     char buffer[JMSG_LENGTH_MAX];
00180     (*cinfo->err->format_message)(cinfo, buffer);
00181     // Is using debug here a good idea? Or do we want to ignore all libjpeg
00182     // messages?
00183     debug(3, "libjpeg: %s", buffer);
00184 }
00185 
00186 J_COLOR_SPACE fromScummvmPixelFormat(const Graphics::PixelFormat &format) {
00187 #if defined(JCS_EXTENSIONS) or defined(JCS_ALPHA_EXTENSIONS)
00188     struct PixelFormatMapping {
00189         Graphics::PixelFormat pixelFormat;
00190         J_COLOR_SPACE bigEndianColorSpace;
00191         J_COLOR_SPACE littleEndianColorSpace;
00192     };
00193 
00194     static const PixelFormatMapping mappings[] = {
00195 #ifdef JCS_EXTENSIONS
00196         { Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16,  8,  0), JCS_EXT_RGBX, JCS_EXT_XBGR },
00197         { Graphics::PixelFormat(4, 8, 8, 8, 0,  0,  8, 16, 24), JCS_EXT_XBGR, JCS_EXT_RGBX },
00198         { Graphics::PixelFormat(4, 8, 8, 8, 0, 16,  8,  0, 24), JCS_EXT_XRGB, JCS_EXT_BGRX },
00199         { Graphics::PixelFormat(4, 8, 8, 8, 0,  8, 16, 24,  0), JCS_EXT_BGRX, JCS_EXT_XRGB },
00200         { Graphics::PixelFormat(3, 8, 8, 8, 0, 16,  8,  0,  0), JCS_EXT_RGB,  JCS_EXT_BGR  },
00201         { Graphics::PixelFormat(3, 8, 8, 8, 0,  0,  8, 16,  0), JCS_EXT_BGR,  JCS_EXT_RGB  }
00202 #endif
00203 #if defined(JCS_EXTENSIONS) and defined(JCS_ALPHA_EXTENSIONS)
00204         ,
00205 #endif
00206 #ifdef JCS_ALPHA_EXTENSIONS
00207         { Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16,  8,  0), JCS_EXT_RGBA, JCS_EXT_ABGR },
00208         { Graphics::PixelFormat(4, 8, 8, 8, 8,  0,  8, 16, 24), JCS_EXT_ABGR, JCS_EXT_RGBA },
00209         { Graphics::PixelFormat(4, 8, 8, 8, 8, 16,  8,  0, 24), JCS_EXT_ARGB, JCS_EXT_BGRA },
00210         { Graphics::PixelFormat(4, 8, 8, 8, 8,  8, 16, 24,  0), JCS_EXT_BGRA, JCS_EXT_ARGB }
00211 #endif
00212     };
00213 
00214     for (uint i = 0; i < ARRAYSIZE(mappings); i++) {
00215         if (mappings[i].pixelFormat == format) {
00216 #ifdef SCUMM_BIG_ENDIAN
00217             return mappings[i].bigEndianColorSpace;
00218 #else
00219             return mappings[i].littleEndianColorSpace;
00220 #endif
00221         }
00222     }
00223 #endif
00224 
00225     return JCS_UNKNOWN;
00226 }
00227 
00228 } // End of anonymous namespace
00229 #endif
00230 
00231 bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
00232 #ifdef USE_JPEG
00233     // Reset member variables from previous decodings
00234     destroy();
00235 
00236     jpeg_decompress_struct cinfo;
00237     jpeg_error_mgr jerr;
00238 
00239     // Initialize error handling callbacks
00240     cinfo.err = jpeg_std_error(&jerr);
00241     cinfo.err->error_exit = &errorExit;
00242     cinfo.err->output_message = &outputMessage;
00243 
00244     // Initialize the decompression structure
00245     jpeg_create_decompress(&cinfo);
00246 
00247     // Initialize our buffer handling
00248     jpeg_scummvm_src(&cinfo, &stream);
00249 
00250     // Read the file header
00251     jpeg_read_header(&cinfo, TRUE);
00252 
00253     // We can request YUV output because Groovie requires it
00254     switch (_colorSpace) {
00255     case kColorSpaceRGB: {
00256         J_COLOR_SPACE colorSpace = fromScummvmPixelFormat(_requestedPixelFormat);
00257 
00258         if (colorSpace == JCS_UNKNOWN) {
00259             // When libjpeg-turbo is not available or an unhandled pixel
00260             // format was requested, ask libjpeg to decode to byte order RGB
00261             // as it's always available.
00262             colorSpace = JCS_RGB;
00263         }
00264 
00265         cinfo.out_color_space = colorSpace;
00266         break;
00267     }
00268     case kColorSpaceYUV:
00269         cinfo.out_color_space = JCS_YCbCr;
00270         break;
00271     }
00272 
00273     // Actually start decompressing the image
00274     jpeg_start_decompress(&cinfo);
00275 
00276     // Allocate buffers for the output data
00277     switch (_colorSpace) {
00278     case kColorSpaceRGB: {
00279         Graphics::PixelFormat outputPixelFormat;
00280         if (cinfo.out_color_space == JCS_RGB) {
00281             outputPixelFormat = getByteOrderRgbPixelFormat();
00282         } else {
00283             outputPixelFormat = _requestedPixelFormat;
00284         }
00285         _surface.create(cinfo.output_width, cinfo.output_height, outputPixelFormat);
00286         break;
00287     }
00288     case kColorSpaceYUV:
00289         // We use YUV with 3 bytes per pixel otherwise.
00290         // This is pretty ugly since our PixelFormat cannot express YUV...
00291         _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
00292         break;
00293     }
00294 
00295     // Allocate buffer for one scanline
00296     JDIMENSION pitch = cinfo.output_width * _surface.format.bytesPerPixel;
00297     assert(_surface.pitch >= pitch);
00298     JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
00299 
00300     // Go through the image data scanline by scanline
00301     while (cinfo.output_scanline < cinfo.output_height) {
00302         byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
00303 
00304         jpeg_read_scanlines(&cinfo, buffer, 1);
00305 
00306         memcpy(dst, buffer[0], pitch);
00307     }
00308 
00309     // We are done with decompressing, thus free all the data
00310     jpeg_finish_decompress(&cinfo);
00311     jpeg_destroy_decompress(&cinfo);
00312 
00313     if (_colorSpace == kColorSpaceRGB && _surface.format != _requestedPixelFormat) {
00314         _surface.convertToInPlace(_requestedPixelFormat); // Slow path
00315     }
00316 
00317     return true;
00318 #else
00319     return false;
00320 #endif
00321 }
00322 
00323 } // End of Graphics namespace


Generated on Sat May 18 2019 05:01:06 for ResidualVM by doxygen 1.7.1
curved edge   curved edge