aboutsummaryrefslogtreecommitdiff
path: root/image/jpeg.cpp
diff options
context:
space:
mode:
authorMatthew Hoops2014-02-27 21:27:23 -0500
committerMatthew Hoops2014-02-28 00:27:28 -0500
commit740b6e8fbdece44ae2a5295cb0549a53b6dc6ae7 (patch)
treed1f414522b8effe91dc2e4c75443ca18a30fc0ef /image/jpeg.cpp
parentcbf085287c74e0994fb18fb0830ccdb340b5b0ac (diff)
downloadscummvm-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.cpp266
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