diff options
author | Matthew Hoops | 2014-02-27 21:27:23 -0500 |
---|---|---|
committer | Matthew Hoops | 2014-02-28 00:27:28 -0500 |
commit | 740b6e8fbdece44ae2a5295cb0549a53b6dc6ae7 (patch) | |
tree | d1f414522b8effe91dc2e4c75443ca18a30fc0ef /image/jpeg.cpp | |
parent | cbf085287c74e0994fb18fb0830ccdb340b5b0ac (diff) | |
download | scummvm-rg350-740b6e8fbdece44ae2a5295cb0549a53b6dc6ae7.tar.gz scummvm-rg350-740b6e8fbdece44ae2a5295cb0549a53b6dc6ae7.tar.bz2 scummvm-rg350-740b6e8fbdece44ae2a5295cb0549a53b6dc6ae7.zip |
IMAGE: Move all ImageDecoders to image/
Diffstat (limited to 'image/jpeg.cpp')
-rw-r--r-- | image/jpeg.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/image/jpeg.cpp b/image/jpeg.cpp new file mode 100644 index 0000000000..9d4b0a7cfe --- /dev/null +++ b/image/jpeg.cpp @@ -0,0 +1,266 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// libjpeg uses forbidden symbols in its header. Thus, we need to allow them +// here. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "image/jpeg.h" + +#include "common/debug.h" +#include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "graphics/pixelformat.h" + +#ifdef USE_JPEG +// The original release of libjpeg v6b did not contain any extern "C" in case +// its header files are included in a C++ environment. To avoid any linking +// issues we need to add it on our own. +extern "C" { +#include <jpeglib.h> +#include <jerror.h> +} +#endif + +namespace Image { + +JPEGDecoder::JPEGDecoder() : ImageDecoder(), _surface(), _colorSpace(kColorSpaceRGBA) { +} + +JPEGDecoder::~JPEGDecoder() { + destroy(); +} + +const Graphics::Surface *JPEGDecoder::getSurface() const { + return &_surface; +} + +void JPEGDecoder::destroy() { + _surface.free(); +} + +#ifdef USE_JPEG +namespace { + +#define JPEG_BUFFER_SIZE 4096 + +struct StreamSource : public jpeg_source_mgr { + Common::SeekableReadStream *stream; + bool startOfFile; + JOCTET buffer[JPEG_BUFFER_SIZE]; +}; + +void initSource(j_decompress_ptr cinfo) { + StreamSource *source = (StreamSource *)cinfo->src; + source->startOfFile = true; +} + +boolean fillInputBuffer(j_decompress_ptr cinfo) { + StreamSource *source = (StreamSource *)cinfo->src; + + uint32 bufferSize = source->stream->read((byte *)source->buffer, sizeof(source->buffer)); + if (bufferSize == 0) { + if (source->startOfFile) { + // An empty file is a fatal error + ERREXIT(cinfo, JERR_INPUT_EMPTY); + } else { + // Otherwise we insert an EOF marker + WARNMS(cinfo, JWRN_JPEG_EOF); + source->buffer[0] = (JOCTET)0xFF; + source->buffer[1] = (JOCTET)JPEG_EOI; + bufferSize = 2; + } + } + + source->next_input_byte = source->buffer; + source->bytes_in_buffer = bufferSize; + source->startOfFile = false; + + return TRUE; +} + +void skipInputData(j_decompress_ptr cinfo, long numBytes) { + StreamSource *source = (StreamSource *)cinfo->src; + + if (numBytes > 0) { + if (numBytes > (long)source->bytes_in_buffer) { + // In case we need to skip more bytes than there are in the buffer + // we will skip the remaining data and fill the buffer again + numBytes -= (long)source->bytes_in_buffer; + + // Skip the remaining bytes + source->stream->skip(numBytes); + + // Fill up the buffer again + (*source->fill_input_buffer)(cinfo); + } else { + source->next_input_byte += (size_t)numBytes; + source->bytes_in_buffer -= (size_t)numBytes; + } + + } +} + +void termSource(j_decompress_ptr cinfo) { +} + +void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) { + StreamSource *source; + + // Initialize the source in case it has not been done yet. + if (cinfo->src == NULL) { + cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(StreamSource)); + } + + source = (StreamSource *)cinfo->src; + source->init_source = &initSource; + source->fill_input_buffer = &fillInputBuffer; + source->skip_input_data = &skipInputData; + source->resync_to_restart = &jpeg_resync_to_restart; + source->term_source = &termSource; + source->bytes_in_buffer = 0; + source->next_input_byte = NULL; + + source->stream = stream; +} + +void errorExit(j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + // This function is not allowed to return to the caller, thus we simply + // error out with our error handling here. + error("%s", buffer); +} + +void outputMessage(j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + // Is using debug here a good idea? Or do we want to ignore all libjpeg + // messages? + debug(3, "libjpeg: %s", buffer); +} + +} // End of anonymous namespace +#endif + +bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) { +#ifdef USE_JPEG + // Reset member variables from previous decodings + destroy(); + + jpeg_decompress_struct cinfo; + jpeg_error_mgr jerr; + + // Initialize error handling callbacks + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = &errorExit; + cinfo.err->output_message = &outputMessage; + + // Initialize the decompression structure + jpeg_create_decompress(&cinfo); + + // Initialize our buffer handling + jpeg_scummvm_src(&cinfo, &stream); + + // Read the file header + jpeg_read_header(&cinfo, TRUE); + + // We can request YUV output because Groovie requires it + switch (_colorSpace) { + case kColorSpaceRGBA: + cinfo.out_color_space = JCS_RGB; + break; + + case kColorSpaceYUV: + cinfo.out_color_space = JCS_YCbCr; + break; + } + + // Actually start decompressing the image + jpeg_start_decompress(&cinfo); + + // Allocate buffers for the output data + switch (_colorSpace) { + case kColorSpaceRGBA: + // We use RGBA8888 in this scenario + _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0)); + break; + + case kColorSpaceYUV: + // We use YUV with 3 bytes per pixel otherwise. + // This is pretty ugly since our PixelFormat cannot express YUV... + _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0)); + break; + } + + // Allocate buffer for one scanline + assert(cinfo.output_components == 3); + JDIMENSION pitch = cinfo.output_width * cinfo.output_components; + assert(_surface.pitch >= pitch); + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1); + + // Go through the image data scanline by scanline + while (cinfo.output_scanline < cinfo.output_height) { + byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline); + + jpeg_read_scanlines(&cinfo, buffer, 1); + + const byte *src = buffer[0]; + switch (_colorSpace) { + case kColorSpaceRGBA: { + for (int remaining = cinfo.output_width; remaining > 0; --remaining) { + byte r = *src++; + byte g = *src++; + byte b = *src++; + // We need to insert a alpha value of 255 (opaque) here. +#ifdef SCUMM_BIG_ENDIAN + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = 0xFF; +#else + *dst++ = 0xFF; + *dst++ = b; + *dst++ = g; + *dst++ = r; +#endif + } + } break; + + case kColorSpaceYUV: + memcpy(dst, src, pitch); + break; + } + } + + // We are done with decompressing, thus free all the data + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return true; +#else + return false; +#endif +} + +} // End of Graphics namespace |