[Scummvm-cvs-logs] scummvm master -> 46a69c89f47451ec2a69cf66025624399ca1c2a2

lordhoto lordhoto at gmail.com
Tue Sep 17 02:11:24 CEST 2013


This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
ac66cc9219 GRAPHICS: Implement JPEGDecoder based on libjpeg.
4809294b43 GRAPHICS: Make JPEGDecoder request RGB output from libjpeg by default.
4063de4070 GRAPHICS: Add some paranoia asserts in JPEGDecoder.
46a69c89f4 Merge pull request #376 from lordhoto/libjpeg


Commit: ac66cc921904387518f2e0b2a14670e9598defe4
    https://github.com/scummvm/scummvm/commit/ac66cc921904387518f2e0b2a14670e9598defe4
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2013-09-16T06:55:36-07:00

Commit Message:
GRAPHICS: Implement JPEGDecoder based on libjpeg.

Changed paths:
    configure
    engines/configure.engines
    engines/groovie/roq.cpp
    graphics/decoders/jpeg.cpp
    graphics/decoders/jpeg.h



diff --git a/configure b/configure
index 1da7d7f..d4ee1e5 100755
--- a/configure
+++ b/configure
@@ -115,6 +115,7 @@ _timidity=auto
 _zlib=auto
 _mpeg2=auto
 _sparkle=auto
+_jpeg=auto
 _png=auto
 _theoradec=auto
 _faad=auto
@@ -188,6 +189,7 @@ add_feature faad "libfaad" "_faad"
 add_feature flac "FLAC" "_flac"
 add_feature freetype2 "FreeType2" "_freetype2"
 add_feature mad "MAD" "_mad"
+add_feature jpeg "JPEG" "_jpeg"
 add_feature png "PNG" "_png"
 add_feature theoradec "libtheoradec" "_theoradec"
 add_feature vorbis "Vorbis file support" "_vorbis _tremor"
@@ -936,6 +938,9 @@ Optional Libraries:
   --with-opengl-prefix=DIR Prefix where OpenGL (ES) is installed (optional)
   --disable-opengl         disable OpenGL (ES) support [autodetect]
 
+  --with-jpeg-prefix=DIR   Prefix where libjpeg is installed (optional)
+  --disable-jpeg           disable JPEG decoder [autodetect]
+
   --with-png-prefix=DIR    Prefix where libpng is installed (optional)
   --disable-png            disable PNG decoder [autodetect]
 
@@ -1015,6 +1020,8 @@ for ac_option in $@; do
 	--disable-nasm)           _nasm=no        ;;
 	--enable-mpeg2)           _mpeg2=yes      ;;
 	--disable-mpeg2)          _mpeg2=no       ;;
+	--disable-jpeg)           _jpeg=no        ;;
+	--enable-jpeg)            _jpeg=yes       ;;
 	--disable-png)            _png=no         ;;
 	--enable-png)             _png=yes        ;;
 	--disable-theoradec)      _theoradec=no   ;;
@@ -1096,6 +1103,11 @@ for ac_option in $@; do
 		MAD_CFLAGS="-I$arg/include"
 		MAD_LIBS="-L$arg/lib"
 		;;
+	--with-jpeg-prefix=*)
+		arg=`echo $ac_option | cut -d '=' -f 2`
+		JPEG_CFLAGS="-I$arg/include"
+		JPEG_LIBS="-L$arg/lib"
+		;;
 	--with-png-prefix=*)
 		arg=`echo $ac_option | cut -d '=' -f 2`
 		PNG_CFLAGS="-I$arg/include"
@@ -3339,6 +3351,32 @@ define_in_config_h_if_yes "$_alsa" 'USE_ALSA'
 echo "$_alsa"
 
 #
+# Check for libjpeg
+#
+echocheck "libjpeg >= v6b"
+if test "$_jpeg" = auto ; then
+	_jpeg=no
+	cat > $TMPC << EOF
+#include <stdio.h>
+#include <jpeglib.h>
+int main(void) {
+#if JPEG_LIB_VERSION >= 62
+#else
+  syntax error
+#endif
+  return 0;
+}
+EOF
+	cc_check $JPEG_CFLAGS $JPEG_LIBS -ljpeg && _jpeg=yes
+fi
+if test "$_jpeg" = yes ; then
+	LIBS="$LIBS $JPEG_LIBS -ljpeg"
+	INCLUDES="$INCLUDES $JPEG_CFLAGS"
+fi
+define_in_config_if_yes "$_jpeg" 'USE_JPEG'
+echo "$_jpeg"
+
+#
 # Check for PNG
 #
 echocheck "PNG >= 1.2.8"
diff --git a/engines/configure.engines b/engines/configure.engines
index 963b9f7..8111cd0 100644
--- a/engines/configure.engines
+++ b/engines/configure.engines
@@ -15,7 +15,7 @@ add_engine drascula "Drascula: The Vampire Strikes Back" yes
 add_engine dreamweb "Dreamweb" yes
 add_engine gob "Gobli*ns" yes
 add_engine groovie "Groovie" yes "groovie2" "7th Guest"
-add_engine groovie2 "Groovie 2 games" no
+add_engine groovie2 "Groovie 2 games" no "" "" "jpeg"
 add_engine hopkins "Hopkins FBI" yes "" "" "16bit"
 add_engine hugo "Hugo Trilogy" yes
 add_engine kyra "Kyra" yes "lol eob" "Legend of Kyrandia 1-3"
@@ -51,4 +51,4 @@ add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes
 add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit"
 add_engine tsage "TsAGE" yes
 add_engine tucker "Bud Tucker in Double Trouble" yes
-add_engine wintermute "Wintermute" no "" "" "png zlib vorbis 16bit"
+add_engine wintermute "Wintermute" no "" "" "jpeg png zlib vorbis 16bit"
diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
index f9a938b..d9c682b 100644
--- a/engines/groovie/roq.cpp
+++ b/engines/groovie/roq.cpp
@@ -436,9 +436,9 @@ bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
 
 	Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder();
 	jpg->loadStream(*_file);
-	const byte *y = (const byte *)jpg->getComponent(1)->getPixels();
-	const byte *u = (const byte *)jpg->getComponent(2)->getPixels();
-	const byte *v = (const byte *)jpg->getComponent(3)->getPixels();
+	const byte *y = (const byte *)jpg->getYComponent().getPixels();
+	const byte *u = (const byte *)jpg->getUComponent().getPixels();
+	const byte *v = (const byte *)jpg->getVComponent().getPixels();
 
 	byte *ptr = (byte *)_currBuf->getPixels();
 	for (int i = 0; i < _currBuf->w * _currBuf->h; i++) {
diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index ff018c7..891542c 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -20,6 +20,10 @@
  *
  */
 
+// libjpeg uses forbidden symbols in its header. Thus, we need to allow them
+// here.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
 #include "graphics/pixelformat.h"
 #include "graphics/yuv_to_rgb.h"
 #include "graphics/decoders/jpeg.h"
@@ -29,35 +33,19 @@
 #include "common/stream.h"
 #include "common/textconsole.h"
 
-namespace Graphics {
-
-// Order used to traverse the quantization tables
-static const uint8 _zigZagOrder[64] = {
-	0,   1,  8, 16,  9,  2,  3, 10,
-	17, 24, 32, 25, 18, 11,  4,  5,
-	12, 19, 26, 33, 40, 48, 41, 34,
-	27, 20, 13,  6,  7, 14, 21, 28,
-	35, 42, 49, 56, 57, 50, 43, 36,
-	29, 22, 15, 23, 30, 37, 44, 51,
-	58, 59, 52, 45, 38, 31, 39, 46,
-	53, 60, 61, 54, 47, 55, 62, 63
-};
-
-JPEGDecoder::JPEGDecoder() : ImageDecoder(),
-	_stream(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0),
-	_scanComp(NULL), _currentComp(NULL), _rgbSurface(0) {
+#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
 
-	// Initialize the quantization tables
-	for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++)
-		_quant[i] = NULL;
+namespace Graphics {
 
-	// Initialize the Huffman tables
-	for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
-		_huff[i].count = 0;
-		_huff[i].values = NULL;
-		_huff[i].sizes = NULL;
-		_huff[i].codes = NULL;
-	}
+JPEGDecoder::JPEGDecoder() : ImageDecoder(), _rgbSurface(nullptr) {
 }
 
 JPEGDecoder::~JPEGDecoder() {
@@ -66,7 +54,7 @@ JPEGDecoder::~JPEGDecoder() {
 
 const Surface *JPEGDecoder::getSurface() const {
 	// Make sure we have loaded data
-	if (!isLoaded())
+	if (!_yComponent.getPixels())
 		return 0;
 
 	if (_rgbSurface)
@@ -74,714 +62,189 @@ const Surface *JPEGDecoder::getSurface() const {
 
 	// Create an RGBA8888 surface
 	_rgbSurface = new Graphics::Surface();
-	_rgbSurface->create(_w, _h, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
+	_rgbSurface->create(_yComponent.w, _yComponent.h, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
 
-	// Get our component surfaces
-	const Graphics::Surface *yComponent = getComponent(1);
-	const Graphics::Surface *uComponent = getComponent(2);
-	const Graphics::Surface *vComponent = getComponent(3);
-
-	YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (const byte *)yComponent->getPixels(), (const byte *)uComponent->getPixels(), (const byte *)vComponent->getPixels(), yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
+	YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (const byte *)_yComponent.getPixels(), (const byte *)_uComponent.getPixels(), (const byte *)_vComponent.getPixels(), _yComponent.w, _yComponent.h, _yComponent.pitch, _uComponent.pitch);
 
 	return _rgbSurface;
 }
 
 void JPEGDecoder::destroy() {
-	// Reset member variables
-	_stream = NULL;
-	_w = _h = 0;
-	_restartInterval = 0;
-
-	// Free the components
-	for (int c = 0; c < _numComp; c++)
-		_components[c].surface.free();
-	delete[] _components; _components = NULL;
-	_numComp = 0;
-
-	// Free the scan components
-	delete[] _scanComp; _scanComp = NULL;
-	_numScanComp = 0;
-	_currentComp = NULL;
-
-	// Free the quantization tables
-	for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
-		delete[] _quant[i];
-		_quant[i] = NULL;
-	}
-
-	// Free the Huffman tables
-	for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
-		_huff[i].count = 0;
-		delete[] _huff[i].values; _huff[i].values = NULL;
-		delete[] _huff[i].sizes; _huff[i].sizes = NULL;
-		delete[] _huff[i].codes; _huff[i].codes = NULL;
-	}
-
 	if (_rgbSurface) {
 		_rgbSurface->free();
 		delete _rgbSurface;
 	}
-}
-
-bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
-	// Reset member variables and tables from previous reads
-	destroy();
-
-	// Save the input stream
-	_stream = &stream;
-
-	bool ok = true;
-	bool done = false;
-	while (!_stream->eos() && ok && !done) {
-		// Read the marker
-
-		// WORKAROUND: While each and every JPEG file should end with
-		// an EOI (end of image) tag, in reality this may not be the
-		// case. For instance, at least one image in the Masterpiece
-		// edition of Myst doesn't, yet other programs are able to read
-		// the image without complaining.
-		//
-		// Apparently, the customary workaround is to insert a fake
-		// EOI tag.
-
-		uint16 marker = _stream->readByte();
-		bool fakeEOI = false;
-
-		if (_stream->eos()) {
-			fakeEOI = true;
-			marker = 0xFF;
-		}
-
-		if (marker != 0xFF) {
-			error("JPEG: Invalid marker[0]: 0x%02X", marker);
-			ok = false;
-			break;
-		}
-
-		while (marker == 0xFF && !_stream->eos())
-			marker = _stream->readByte();
-
-		if (_stream->eos()) {
-			fakeEOI = true;
-			marker = 0xD9;
-		}
-
-		if (fakeEOI)
-			warning("JPEG: Inserted fake EOI");
-
-		// Process the marker data
-		switch (marker) {
-		case 0xC0: // Start Of Frame
-			ok = readSOF0();
-			break;
-		case 0xC4: // Define Huffman Tables
-			ok = readDHT();
-			break;
-		case 0xD8: // Start Of Image
-			break;
-		case 0xD9: // End Of Image
-			done = true;
-			break;
-		case 0xDA: // Start Of Scan
-			ok = readSOS();
-			break;
-		case 0xDB: // Define Quantization Tables
-			ok = readDQT();
-			break;
-		case 0xE0: // JFIF/JFXX segment
-			ok = readJFIF();
-			break;
-		case 0xDD: // Define Restart Interval
-			ok = readDRI();
-			break;
-		case 0xFE: // Comment
-			_stream->seek(_stream->readUint16BE() - 2, SEEK_CUR);
-			break;
-		default: { // Unknown marker
-			uint16 size = _stream->readUint16BE();
-
-			if ((marker & 0xE0) != 0xE0)
-				warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2);
-
-			_stream->seek(size - 2, SEEK_CUR);
-		}
-		}
-	}
-
-	_stream = 0;
-	return ok;
-}
-
-bool JPEGDecoder::readJFIF() {
-	uint16 length = _stream->readUint16BE();
-	uint32 tag = _stream->readUint32BE();
-
-	if (tag != MKTAG('J', 'F', 'I', 'F')) {
-		warning("JPEGDecoder::readJFIF() tag mismatch");
-		return false;
-	}
-
-	if (_stream->readByte() != 0)  { // NULL
-		warning("JPEGDecoder::readJFIF() NULL mismatch");
-		return false;
-	}
-
-	byte majorVersion = _stream->readByte();
-	byte minorVersion = _stream->readByte();
-	if (majorVersion != 1 || minorVersion > 2)
-		warning("JPEGDecoder::readJFIF(): v%d.%02d JPEGs may not be handled correctly", majorVersion, minorVersion);
-
-	/* byte densityUnits = */_stream->readByte();
-	/* uint16 xDensity = */_stream->readUint16BE();
-	/* uint16 yDensity = */_stream->readUint16BE();
-	byte thumbW = _stream->readByte();
-	byte thumbH = _stream->readByte();
-
-	_stream->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail
-	if (length != (thumbW * thumbH * 3) + 16) {
-		warning("JPEGDecoder::readJFIF() length mismatch");
-		return false;
-	}
 
-	return true;
+	_yComponent.free();
+	_uComponent.free();
+	_vComponent.free();
 }
 
-// Marker 0xC0 (Start Of Frame, Baseline DCT)
-bool JPEGDecoder::readSOF0() {
-	debug(5, "JPEG: readSOF0");
-	uint16 size = _stream->readUint16BE();
-
-	// Read the sample precision
-	uint8 precision = _stream->readByte();
-	if (precision != 8) {
-		warning("JPEG: Just 8 bit precision supported at the moment");
-		return false;
-	}
-
-	// Image size
-	_h = _stream->readUint16BE();
-	_w = _stream->readUint16BE();
+#ifdef USE_JPEG
+namespace {
 
-	// Number of components
-	_numComp = _stream->readByte();
-	if (size != 8 + 3 * _numComp) {
-		warning("JPEG: Invalid number of components");
-		return false;
-	}
-
-	// Allocate the new components
-	delete[] _components;
-	_components = new Component[_numComp];
-
-	// Read the components details
-	for (int c = 0; c < _numComp; c++) {
-		_components[c].id = _stream->readByte();
-		_components[c].factorH = _stream->readByte();
-		_components[c].factorV = _components[c].factorH & 0xF;
-		_components[c].factorH >>= 4;
-		_components[c].quantTableSelector = _stream->readByte();
-	}
+#define JPEG_BUFFER_SIZE 4096
 
-	return true;
-}
-
-// Marker 0xC4 (Define Huffman Tables)
-bool JPEGDecoder::readDHT() {
-	debug(5, "JPEG: readDHT");
-	uint16 size = _stream->readUint16BE() - 2;
-	uint32 pos = _stream->pos();
-
-	while ((uint32)_stream->pos() < (size + pos)) {
-		// Read the table type and id
-		uint8 tableId = _stream->readByte();
-		uint8 tableType = tableId >> 4; // type 0: DC, 1: AC
-		tableId &= 0xF;
-		uint8 tableNum = (tableId << 1) + tableType;
-
-		// Free the Huffman table
-		delete[] _huff[tableNum].values; _huff[tableNum].values = NULL;
-		delete[] _huff[tableNum].sizes; _huff[tableNum].sizes = NULL;
-		delete[] _huff[tableNum].codes; _huff[tableNum].codes = NULL;
-
-		// Read the number of values for each length
-		uint8 numValues[16];
-		_huff[tableNum].count = 0;
-		for (int len = 0; len < 16; len++) {
-			numValues[len] = _stream->readByte();
-			_huff[tableNum].count += numValues[len];
-		}
-
-		// Allocate memory for the current table
-		_huff[tableNum].values = new uint8[_huff[tableNum].count];
-		_huff[tableNum].sizes = new uint8[_huff[tableNum].count];
-		_huff[tableNum].codes = new uint16[_huff[tableNum].count];
-
-		// Read the table contents
-		int cur = 0;
-		for (int len = 0; len < 16; len++) {
-			for (int i = 0; i < numValues[len]; i++) {
-				_huff[tableNum].values[cur] = _stream->readByte();
-				_huff[tableNum].sizes[cur] = len + 1;
-				cur++;
-			}
-		}
-
-		// Fill the table of Huffman codes
-		cur = 0;
-		uint16 curCode = 0;
-		uint8 curCodeSize = _huff[tableNum].sizes[0];
-		while (cur < _huff[tableNum].count) {
-			// Increase the code size to fit the request
-			while (_huff[tableNum].sizes[cur] != curCodeSize) {
-				curCode <<= 1;
-				curCodeSize++;
-			}
-
-			// Assign the current code
-			_huff[tableNum].codes[cur] = curCode;
-			curCode++;
-			cur++;
-		}
-	}
+struct StreamSource : public jpeg_source_mgr {
+	Common::SeekableReadStream *stream;
+	bool startOfFile;
+	JOCTET buffer[JPEG_BUFFER_SIZE];
+};
 
-	return true;
+void initSource(j_decompress_ptr cinfo) {
+	StreamSource *source = (StreamSource *)cinfo->src;
+	source->startOfFile = true;
 }
 
-// Marker 0xDA (Start Of Scan)
-bool JPEGDecoder::readSOS() {
-	debug(5, "JPEG: readSOS");
-	uint16 size = _stream->readUint16BE();
-
-	// Number of scan components
-	_numScanComp = _stream->readByte();
-	if (size != 6 + 2 * _numScanComp) {
-		warning("JPEG: Invalid number of components");
-		return false;
-	}
-
-	// Allocate the new scan components
-	delete[] _scanComp;
-	_scanComp = new Component *[_numScanComp];
-
-	// Reset the maximum sampling factors
-	_maxFactorV = 0;
-	_maxFactorH = 0;
-
-	// Component-specification parameters
-	for (int c = 0; c < _numScanComp; c++) {
-		// Read the desired component id
-		uint8 id = _stream->readByte();
-
-		// Search the component with the specified id
-		bool found = false;
-		for (int i = 0; !found && i < _numComp; i++) {
-			if (_components[i].id == id) {
-				// We found the desired component
-				found = true;
-
-				// Assign the found component to the c'th scan component
-				_scanComp[c] = &_components[i];
-			}
-		}
+boolean fillInputBuffer(j_decompress_ptr cinfo) {
+	StreamSource *source = (StreamSource *)cinfo->src;
 
-		if (!found) {
-			warning("JPEG: Invalid component");
-			return false;
+	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;
 		}
-
-		// Read the entropy table selectors
-		_scanComp[c]->DCentropyTableSelector = _stream->readByte();
-		_scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF;
-		_scanComp[c]->DCentropyTableSelector >>= 4;
-
-		// Calculate the maximum sampling factors
-		if (_scanComp[c]->factorV > _maxFactorV)
-			_maxFactorV = _scanComp[c]->factorV;
-
-		if (_scanComp[c]->factorH > _maxFactorH)
-			_maxFactorH = _scanComp[c]->factorH;
-
-		// Initialize the DC predictor
-		_scanComp[c]->DCpredictor = 0;
 	}
 
-	// Start of spectral selection
-	if (_stream->readByte() != 0) {
-		warning("JPEG: Progressive scanning not supported");
-		return false;
-	}
+	source->next_input_byte = source->buffer;
+	source->bytes_in_buffer = bufferSize;
+	source->startOfFile = false;
 
-	// End of spectral selection
-	if (_stream->readByte() != 63) {
-		warning("JPEG: Progressive scanning not supported");
-		return false;
-	}
-
-	// Successive approximation parameters
-	if (_stream->readByte() != 0) {
-		warning("JPEG: Progressive scanning not supported");
-		return false;
-	}
-
-	// Entropy coded sequence starts, initialize Huffman decoder
-	_bitsNumber = 0;
-
-	// Read all the scan MCUs
-	uint16 xMCU = _w / (_maxFactorH * 8);
-	uint16 yMCU = _h / (_maxFactorV * 8);
-
-	// Check for non- multiple-of-8 dimensions
-	if (_w % (_maxFactorH * 8) != 0)
-		xMCU++;
-	if (_h % (_maxFactorV * 8) != 0)
-		yMCU++;
-
-	// Initialize the scan surfaces
-	for (uint16 c = 0; c < _numScanComp; c++) {
-		_scanComp[c]->surface.create(xMCU * _maxFactorH * 8, yMCU * _maxFactorV * 8, PixelFormat::createFormatCLUT8());
-	}
-
-	bool ok = true;
-	uint16 interval = _restartInterval;
+	return TRUE;
+}
 
-	for (int y = 0; ok && (y < yMCU); y++) {
-		for (int x = 0; ok && (x < xMCU); x++) {
-			ok = readMCU(x, y);
+void skipInputData(j_decompress_ptr cinfo, long numBytes) {
+	StreamSource *source = (StreamSource *)cinfo->src;
 
-			// If we have a restart interval, we'll need to reset a couple
-			// variables
-			if (_restartInterval != 0) {
-				interval--;
+	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;
 
-				if (interval == 0) {
-					interval = _restartInterval;
-					_bitsNumber = 0;
+			// Skip the remaining bytes
+			source->stream->skip(numBytes);
 
-					for (byte i = 0; i < _numScanComp; i++)
-						_scanComp[i]->DCpredictor = 0;
-				}
-			}
+			// 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;
 		}
-	}
 
-	// Trim Component surfaces back to image height and width
-	// Note: Code using jpeg must use surface.pitch correctly...
-	for (uint16 c = 0; c < _numScanComp; c++) {
-		_scanComp[c]->surface.w = _w;
-		_scanComp[c]->surface.h = _h;
 	}
-
-	return ok;
 }
 
-// Marker 0xDB (Define Quantization Tables)
-bool JPEGDecoder::readDQT() {
-	debug(5, "JPEG: readDQT");
-	uint16 size = _stream->readUint16BE() - 2;
-	uint32 pos = _stream->pos();
-
-	while ((uint32)_stream->pos() < (pos + size)) {
-		// Read the table precision and id
-		uint8 tableId = _stream->readByte();
-		bool highPrecision = (tableId & 0xF0) != 0;
-
-		// Validate the table id
-		tableId &= 0xF;
-		if (tableId >= JPEG_MAX_QUANT_TABLES) {
-			warning("JPEG: Invalid quantization table");
-			return false;
-		}
-
-		// Create the new table if necessary
-		if (!_quant[tableId])
-			_quant[tableId] = new uint16[64];
-
-		// Read the table (stored in Zig-Zag order)
-		for (int i = 0; i < 64; i++)
-			_quant[tableId][i] = highPrecision ? _stream->readUint16BE() : _stream->readByte();
-	}
-
-	return true;
+void termSource(j_decompress_ptr cinfo) {
 }
 
-// Marker 0xDD (Define Restart Interval)
-bool JPEGDecoder::readDRI() {
-	debug(5, "JPEG: readDRI");
-	uint16 size = _stream->readUint16BE() - 2;
+void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
+	StreamSource *source;
 
-	if (size != 2) {
-		warning("JPEG: Invalid DRI size %d", size);
-		return false;
+	// 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));
 	}
 
-	_restartInterval = _stream->readUint16BE();
-	debug(5, "Restart interval: %d", _restartInterval);
-	return true;
-}
-
-bool JPEGDecoder::readMCU(uint16 xMCU, uint16 yMCU) {
-	bool ok = true;
-	for (int c = 0; ok && (c < _numComp); c++) {
-		// Set the current component
-		_currentComp = _scanComp[c];
+	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;
 
-		// Read the data units of the current component
-		for (int y = 0; ok && (y < _scanComp[c]->factorV); y++)
-			for (int x = 0; ok && (x < _scanComp[c]->factorH); x++)
-				ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y);
-	}
+	source->stream = stream;
+}
 
-	return ok;
+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);
 }
 
-// triple-butterfly-add (and possible rounding)
-#define xadd3(xa, xb, xc, xd, h) \
-	p = xa + xb; \
-	n = xa - xb; \
-	xa = p + xc + h; \
-	xb = n + xd + h; \
-	xc = p - xc + h; \
-	xd = n - xd + h;
-
-// butterfly-mul
-#define xmul(xa, xb, k1, k2, sh) \
-	n = k1 * (xa + xb); \
-	p = xa; \
-	xa = (n + (k2 - k1) * xb) >> sh; \
-	xb = (n - (k2 + k1) * p) >> sh;
-
-// IDCT based on public domain code from http://halicery.com/jpeg/idct.html
-void JPEGDecoder::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) {
-	int p, n;
-
-	src[0] <<= 9;
-	src[1] <<= 7;
-	src[3] *= 181;
-	src[4] <<= 9;
-	src[5] *= 181;
-	src[7] <<= 7;
-
-	// Even part
-	xmul(src[6], src[2], 277, 669, 0)
-	xadd3(src[0], src[4], src[6], src[2], half)
-
-	// Odd part
-	xadd3(src[1], src[7], src[3], src[5], 0)
-	xmul(src[5], src[3], 251, 50, 6)
-	xmul(src[1], src[7], 213, 142, 6)
-
-	dest[0 * 8] = (src[0] + src[1]) >> ps;
-	dest[1 * 8] = (src[4] + src[5]) >> ps;
-	dest[2 * 8] = (src[2] + src[3]) >> ps;
-	dest[3 * 8] = (src[6] + src[7]) >> ps;
-	dest[4 * 8] = (src[6] - src[7]) >> ps;
-	dest[5 * 8] = (src[2] - src[3]) >> ps;
-	dest[6 * 8] = (src[4] - src[5]) >> ps;
-	dest[7 * 8] = (src[0] - src[1]) >> ps;
+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);
 }
 
-void JPEGDecoder::idct2D8x8(int32 block[64]) {
-	int32 tmp[64];
-
-	// Apply 1D IDCT to rows
-	for (int i = 0; i < 8; i++)
-		idct1D8x8(&block[i * 8], &tmp[i], 9, 1 << 8);
-
-	// Apply 1D IDCT to columns
-	for (int i = 0; i < 8; i++)
-		idct1D8x8(&tmp[i * 8], &block[i], 12, 1 << 11);
- }
-
-bool JPEGDecoder::readDataUnit(uint16 x, uint16 y) {
-	// Prepare an empty data array
-	int16 readData[64];
-	for (int i = 1; i < 64; i++)
-		readData[i] = 0;
-
-	// Read the DC component
-	readData[0] = _currentComp->DCpredictor + readDC();
-	_currentComp->DCpredictor = readData[0];
-
-	// Read the AC components (stored in Zig-Zag)
-	readAC(readData);
-
-	// Calculate the DCT coefficients from the input sequence
-	int32 block[64];
-	for (uint8 i = 0; i < 64; i++) {
-		// Dequantize
-		int32 val = readData[i];
-		int16 quant = _quant[_currentComp->quantTableSelector][i];
-		val *= quant;
-
-		// Store the normalized coefficients, undoing the Zig-Zag
-		block[_zigZagOrder[i]] = val;
-	}
+} // End of anonymous namespace
+#endif
 
-	// Apply the IDCT
-	idct2D8x8(block);
+bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
+#ifdef USE_JPEG
+	// Reset member variables from previous decodings
+	destroy();
 
-	// Level shift to make the values unsigned
-	for (int i = 0; i < 64; i++) {
-		block[i] = block[i] + 128;
+	jpeg_decompress_struct cinfo;
+	jpeg_error_mgr jerr;
 
-		if (block[i] < 0)
-			block[i] = 0;
+	// Initialize error handling callbacks
+	cinfo.err = jpeg_std_error(&jerr);
+	cinfo.err->error_exit = &errorExit;
+	cinfo.err->output_message = &outputMessage;
 
-		if (block[i] > 255)
-			block[i] = 255;
-	}
+	// Initialize the decompression structure
+	jpeg_create_decompress(&cinfo);
 
-	// Paint the component surface
-	uint8 scalingV = _maxFactorV / _currentComp->factorV;
-	uint8 scalingH = _maxFactorH / _currentComp->factorH;
-
-	// Convert coordinates from MCU blocks to pixels
-	x <<= 3;
-	y <<= 3;
-
-	for (uint8 j = 0; j < 8; j++) {
-		for (uint16 sV = 0; sV < scalingV; sV++) {
-			// Get the beginning of the block line
-			byte *ptr = (byte *)_currentComp->surface.getBasePtr(x * scalingH, (y + j) * scalingV + sV);
-
-			for (uint8 i = 0; i < 8; i++) {
-				for (uint16 sH = 0; sH < scalingH; sH++) {
-					*ptr = (byte)(block[j * 8 + i]);
-					ptr++;
-				}
-			}
-		}
-	}
+	// Initialize our buffer handling
+	jpeg_scummvm_src(&cinfo, &stream);
 
-	return true;
-}
+	// Read the file header
+	jpeg_read_header(&cinfo, TRUE);
 
-int16 JPEGDecoder::readDC() {
-	// DC is type 0
-	uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
+	// We request YUV output because Groovie requires it
+	cinfo.out_color_space = JCS_YCbCr;
 
-	// Get the number of bits to read
-	uint8 numBits = readHuff(tableNum);
+	// Actually start decompressing the image
+	jpeg_start_decompress(&cinfo);
 
-	// Read the requested bits
-	return readSignedBits(numBits);
-}
+	// Allocate buffers for the YUV components
+	_yComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
+	_uComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
+	_vComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
 
-void JPEGDecoder::readAC(int16 *out) {
-	// AC is type 1
-	uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1;
-
-	// Start reading AC element 1
-	uint8 cur = 1;
-	while (cur < 64) {
-		uint8 s = readHuff(tableNum);
-		uint8 r = s >> 4;
-		s &= 0xF;
-
-		if (s == 0) {
-			if (r == 15) {
-				// Skip 16 values
-				cur += 16;
-			} else {
-				// EOB: end of block
-				cur = 64;
-			}
-		} else {
-			// Skip r values
-			cur += r;
+	// Allocate buffer for one scanline
+	JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
+	JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
 
-			// Read the next value
-			out[cur] = readSignedBits(s);
-			cur++;
-		}
-	}
-}
+	// Go through the image data scanline by scanline
+	while (cinfo.output_scanline < cinfo.output_height) {
+		byte *yPtr = (byte *)_yComponent.getBasePtr(0, cinfo.output_scanline);
+		byte *uPtr = (byte *)_uComponent.getBasePtr(0, cinfo.output_scanline);
+		byte *vPtr = (byte *)_vComponent.getBasePtr(0, cinfo.output_scanline);
 
-int16 JPEGDecoder::readSignedBits(uint8 numBits) {
-	uint16 ret = 0;
-	if (numBits > 16)
-		error("requested %d bits", numBits); //XXX
+		jpeg_read_scanlines(&cinfo, buffer, 1);
 
-	// MSB=0 for negatives, 1 for positives
-	for (int i = 0; i < numBits; i++)
-		ret = (ret << 1) + readBit();
-
-	// Extend sign bits (PAG109)
-	if (!(ret >> (numBits - 1))) {
-		uint16 tmp = ((uint16)-1 << numBits) + 1;
-		ret = ret + tmp;
-	}
-	return ret;
-}
-
-// TODO: optimize?
-uint8 JPEGDecoder::readHuff(uint8 table) {
-	bool foundCode = false;
-	uint8 val = 0;
-
-	uint8 cur = 0;
-	uint8 codeSize = 1;
-	uint16 code = readBit();
-	while (!foundCode) {
-		// Prepare a code of the current size
-		while (codeSize < _huff[table].sizes[cur]) {
-			code = (code << 1) + readBit();
-			codeSize++;
-		}
-
-		// Compare the codes of the current size
-		while (!foundCode && (codeSize == _huff[table].sizes[cur])) {
-			if (code == _huff[table].codes[cur]) {
-				// Found the code
-				val = _huff[table].values[cur];
-				foundCode = true;
-			} else {
-				// Continue reading
-				cur++;
-			}
-		}
-	}
-
-	return val;
-}
-
-uint8 JPEGDecoder::readBit() {
-	// Read a whole byte if necessary
-	if (_bitsNumber == 0) {
-		_bitsData = _stream->readByte();
-		_bitsNumber = 8;
-
-		// Detect markers
-		if (_bitsData == 0xFF) {
-			uint8 byte2 = _stream->readByte();
-
-			// A stuffed 0 validates the previous byte
-			if (byte2 != 0) {
-				if (byte2 == 0xDC) {
-					// DNL marker: Define Number of Lines
-					// TODO: terminate scan
-					warning("DNL marker detected: terminate scan");
-				} else if (byte2 >= 0xD0 && byte2 <= 0xD7) {
-					debug(7, "RST%d marker detected", byte2 & 7);
-					_bitsData = _stream->readByte();
-				} else {
-					warning("Error: marker 0x%02X read in entropy data", byte2);
-				}
-			}
+		const byte *src = buffer[0];
+		for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
+			*yPtr++ = *src++;
+			*uPtr++ = *src++;
+			*vPtr++ = *src++;
 		}
 	}
-	_bitsNumber--;
 
-	return (_bitsData & (1 << _bitsNumber)) ? 1 : 0;
-}
-
-const Surface *JPEGDecoder::getComponent(uint c) const {
-	for (int i = 0; i < _numComp; i++)
-		if (_components[i].id == c) // We found the desired component
-			return &_components[i].surface;
+	// We are done with decompressing, thus free all the data
+	jpeg_finish_decompress(&cinfo);
+	jpeg_destroy_decompress(&cinfo);
 
-	error("JPEGDecoder::getComponent: No component %d present", c);
-	return NULL;
+	return true;
+#else
+	return false;
+#endif
 }
 
 } // End of Graphics namespace
diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h
index d59b72a..53e7bb1 100644
--- a/graphics/decoders/jpeg.h
+++ b/graphics/decoders/jpeg.h
@@ -40,11 +40,6 @@ class SeekableReadStream;
 
 namespace Graphics {
 
-struct PixelFormat;
-
-#define JPEG_MAX_QUANT_TABLES 4
-#define JPEG_MAX_HUFF_TABLES 2
-
 class JPEGDecoder : public ImageDecoder {
 public:
 	JPEGDecoder();
@@ -55,85 +50,19 @@ public:
 	bool loadStream(Common::SeekableReadStream &str);
 	const Surface *getSurface() const;
 
-	bool isLoaded() const { return _numComp && _w && _h; }
-	uint16 getWidth() const { return _w; }
-	uint16 getHeight() const { return _h; }
-	const Surface *getComponent(uint c) const;
+	const Surface &getYComponent() const { return _yComponent; }
+	const Surface &getUComponent() const { return _uComponent; }
+	const Surface &getVComponent() const { return _vComponent; }
 
 private:
-	Common::SeekableReadStream *_stream;
-	uint16 _w, _h;
-	uint16 _restartInterval;
-
 	// mutable so that we can convert to RGB only during
 	// a getSurface() call while still upholding the
 	// const requirement in other ImageDecoders
 	mutable Graphics::Surface *_rgbSurface;
 
-	// Image components
-	uint8 _numComp;
-	struct Component {
-		// Global values
-		uint8 id;
-		uint8 factorH;
-		uint8 factorV;
-		uint8 quantTableSelector;
-
-		// Scan specific values
-		uint8 DCentropyTableSelector;
-		uint8 ACentropyTableSelector;
-		int16 DCpredictor;
-
-		// Result image for this component
-		Surface surface;
-	};
-
-	Component *_components;
-
-	// Scan components
-	uint8 _numScanComp;
-	Component **_scanComp;
-	Component *_currentComp;
-
-	// Maximum sampling factors, used to calculate the interleaving of the MCU
-	uint8 _maxFactorV;
-	uint8 _maxFactorH;
-
-	// Quantization tables
-	uint16 *_quant[JPEG_MAX_QUANT_TABLES];
-
-	// Huffman tables
-	struct HuffmanTable {
-		uint8 count;
-		uint8 *values;
-		uint8 *sizes;
-		uint16 *codes;
-	} _huff[2 * JPEG_MAX_HUFF_TABLES];
-
-	// Marker read functions
-	bool readJFIF();
-	bool readSOF0();
-	bool readDHT();
-	bool readSOS();
-	bool readDQT();
-	bool readDRI();
-
-	// Helper functions
-	bool readMCU(uint16 xMCU, uint16 yMCU);
-	bool readDataUnit(uint16 x, uint16 y);
-	int16 readDC();
-	void readAC(int16 *out);
-	int16 readSignedBits(uint8 numBits);
-
-	// Huffman decoding
-	uint8 readHuff(uint8 table);
-	uint8 readBit();
-	uint8 _bitsData;
-	uint8 _bitsNumber;
-
-	// Inverse Discrete Cosine Transformation
-	static void idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half);
-	static void idct2D8x8(int32 block[64]);
+	Graphics::Surface _yComponent;
+	Graphics::Surface _uComponent;
+	Graphics::Surface _vComponent;
 };
 
 } // End of Graphics namespace


Commit: 4809294b43e1c43957874bdfcdadfc299fd7ace4
    https://github.com/scummvm/scummvm/commit/4809294b43e1c43957874bdfcdadfc299fd7ace4
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2013-09-16T10:54:19-07:00

Commit Message:
GRAPHICS: Make JPEGDecoder request RGB output from libjpeg by default.

This fixes loading of JPEG files which contain RGB color space instead of YUV.
It is a pretty odd extension of JPEG files by Adobe which is indicated by this:
http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe

To still support Groovie's need for YUV data I added some possibility to
request direct YUV output.

Changed paths:
    engines/groovie/roq.cpp
    graphics/decoders/jpeg.cpp
    graphics/decoders/jpeg.h



diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
index d9c682b..5592d84 100644
--- a/engines/groovie/roq.cpp
+++ b/engines/groovie/roq.cpp
@@ -435,17 +435,13 @@ bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
 	warning("Groovie::ROQ: JPEG frame (unfinished)");
 
 	Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder();
+	jpg->setOutputColorSpace(Graphics::JPEGDecoder::kColorSpaceYUV);
 	jpg->loadStream(*_file);
-	const byte *y = (const byte *)jpg->getYComponent().getPixels();
-	const byte *u = (const byte *)jpg->getUComponent().getPixels();
-	const byte *v = (const byte *)jpg->getVComponent().getPixels();
 
+	const Graphics::Surface *srcSurf = jpg->getSurface();
+	const byte *src = (const byte *)srcSurf->getPixels();
 	byte *ptr = (byte *)_currBuf->getPixels();
-	for (int i = 0; i < _currBuf->w * _currBuf->h; i++) {
-		*ptr++ = *y++;
-		*ptr++ = *u++;
-		*ptr++ = *v++;
-	}
+	memcpy(ptr, src, _currBuf->w * _currBuf->h);
 
 	delete jpg;
 	return true;
diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index 891542c..ba6292b 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -25,7 +25,6 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "graphics/pixelformat.h"
-#include "graphics/yuv_to_rgb.h"
 #include "graphics/decoders/jpeg.h"
 
 #include "common/debug.h"
@@ -45,7 +44,7 @@ extern "C" {
 
 namespace Graphics {
 
-JPEGDecoder::JPEGDecoder() : ImageDecoder(), _rgbSurface(nullptr) {
+JPEGDecoder::JPEGDecoder() : ImageDecoder(), _surface(), _colorSpace(kColorSpaceRGBA) {
 }
 
 JPEGDecoder::~JPEGDecoder() {
@@ -53,31 +52,11 @@ JPEGDecoder::~JPEGDecoder() {
 }
 
 const Surface *JPEGDecoder::getSurface() const {
-	// Make sure we have loaded data
-	if (!_yComponent.getPixels())
-		return 0;
-
-	if (_rgbSurface)
-		return _rgbSurface;
-
-	// Create an RGBA8888 surface
-	_rgbSurface = new Graphics::Surface();
-	_rgbSurface->create(_yComponent.w, _yComponent.h, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
-
-	YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (const byte *)_yComponent.getPixels(), (const byte *)_uComponent.getPixels(), (const byte *)_vComponent.getPixels(), _yComponent.w, _yComponent.h, _yComponent.pitch, _uComponent.pitch);
-
-	return _rgbSurface;
+	return &_surface;
 }
 
 void JPEGDecoder::destroy() {
-	if (_rgbSurface) {
-		_rgbSurface->free();
-		delete _rgbSurface;
-	}
-
-	_yComponent.free();
-	_uComponent.free();
-	_vComponent.free();
+	_surface.free();
 }
 
 #ifdef USE_JPEG
@@ -206,16 +185,33 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
 	// Read the file header
 	jpeg_read_header(&cinfo, TRUE);
 
-	// We request YUV output because Groovie requires it
-	cinfo.out_color_space = JCS_YCbCr;
+	// 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 YUV components
-	_yComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
-	_uComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
-	_vComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
+	// 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
 	JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
@@ -223,17 +219,35 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
 
 	// Go through the image data scanline by scanline
 	while (cinfo.output_scanline < cinfo.output_height) {
-		byte *yPtr = (byte *)_yComponent.getBasePtr(0, cinfo.output_scanline);
-		byte *uPtr = (byte *)_uComponent.getBasePtr(0, cinfo.output_scanline);
-		byte *vPtr = (byte *)_vComponent.getBasePtr(0, cinfo.output_scanline);
+		byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
 
 		jpeg_read_scanlines(&cinfo, buffer, 1);
 
 		const byte *src = buffer[0];
-		for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
-			*yPtr++ = *src++;
-			*uPtr++ = *src++;
-			*vPtr++ = *src++;
+		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, _surface.pitch);
+			break;
 		}
 	}
 
diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h
index 53e7bb1..8460bc2 100644
--- a/graphics/decoders/jpeg.h
+++ b/graphics/decoders/jpeg.h
@@ -46,23 +46,48 @@ public:
 	~JPEGDecoder();
 
 	// ImageDecoder API
-	void destroy();
-	bool loadStream(Common::SeekableReadStream &str);
-	const Surface *getSurface() const;
+	virtual void destroy();
+	virtual bool loadStream(Common::SeekableReadStream &str);
+	virtual const Surface *getSurface() const;
 
-	const Surface &getYComponent() const { return _yComponent; }
-	const Surface &getUComponent() const { return _uComponent; }
-	const Surface &getVComponent() const { return _vComponent; }
+	// Special API for JPEG
+	enum ColorSpace {
+		/**
+		 * Output 32bit RGBA data.
+		 *
+		 * This is the default output.
+		 */
+		kColorSpaceRGBA,
 
-private:
-	// mutable so that we can convert to RGB only during
-	// a getSurface() call while still upholding the
-	// const requirement in other ImageDecoders
-	mutable Graphics::Surface *_rgbSurface;
+		/**
+		 * Output (interleaved) YUV data.
+		 *
+		 * Be aware that some images cannot be output in YUV mode.
+		 * These are (non-standard) JPEG images which are in RGB colorspace.
+		 *
+		 * The resulting Surface will have a PixelFormat with 3 bytes per
+		 * pixel and the remaining entries are completely zeroed. This works
+		 * around the fact that PixelFormat can only describe RGB formats.
+		 *
+		 * You should only use this when you are really aware of what you are
+		 * doing!
+		 */
+		kColorSpaceYUV
+	};
+
+	/**
+	 * Request the output color space. This can be used to obtain raw YUV
+	 * data from the JPEG file. But this might not work for all files!
+	 *
+	 * The decoder itself defaults to RGBA.
+	 *
+	 * @param outSpace The color space to output.
+	 */
+	void setOutputColorSpace(ColorSpace outSpace) { _colorSpace = outSpace; }
 
-	Graphics::Surface _yComponent;
-	Graphics::Surface _uComponent;
-	Graphics::Surface _vComponent;
+private:
+	Graphics::Surface _surface;
+	ColorSpace _colorSpace;
 };
 
 } // End of Graphics namespace


Commit: 4063de40705c2b21e092f2a275182b0f41070b15
    https://github.com/scummvm/scummvm/commit/4063de40705c2b21e092f2a275182b0f41070b15
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2013-09-16T10:54:19-07:00

Commit Message:
GRAPHICS: Add some paranoia asserts in JPEGDecoder.

Changed paths:
    graphics/decoders/jpeg.cpp



diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index ba6292b..c858884 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -214,7 +214,9 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
 	}
 
 	// 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
@@ -246,7 +248,7 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
 			} break;
 
 		case kColorSpaceYUV:
-			memcpy(dst, src, _surface.pitch);
+			memcpy(dst, src, pitch);
 			break;
 		}
 	}


Commit: 46a69c89f47451ec2a69cf66025624399ca1c2a2
    https://github.com/scummvm/scummvm/commit/46a69c89f47451ec2a69cf66025624399ca1c2a2
Author: Johannes Schickel (lordhoto at gmail.com)
Date: 2013-09-16T17:10:57-07:00

Commit Message:
Merge pull request #376 from lordhoto/libjpeg

GRAPHICS: Implement JPEGDecoder based on libjpeg.

Changed paths:
    configure
    engines/configure.engines
    engines/groovie/roq.cpp
    graphics/decoders/jpeg.cpp
    graphics/decoders/jpeg.h



diff --cc engines/configure.engines
index 195cdda,8111cd0..3c7397f
--- a/engines/configure.engines
+++ b/engines/configure.engines
@@@ -13,10 -13,9 +13,10 @@@ add_engine cruise "Cinematique evo 2" y
  add_engine draci "Dragon History" yes
  add_engine drascula "Drascula: The Vampire Strikes Back" yes
  add_engine dreamweb "Dreamweb" yes
 +add_engine fullpipe "Full Pipe" yes
  add_engine gob "Gobli*ns" yes
  add_engine groovie "Groovie" yes "groovie2" "7th Guest"
- add_engine groovie2 "Groovie 2 games" no
+ add_engine groovie2 "Groovie 2 games" no "" "" "jpeg"
  add_engine hopkins "Hopkins FBI" yes "" "" "16bit"
  add_engine hugo "Hugo Trilogy" yes
  add_engine kyra "Kyra" yes "lol eob" "Legend of Kyrandia 1-3"






More information about the Scummvm-git-logs mailing list