aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schickel2013-09-16 17:10:57 -0700
committerJohannes Schickel2013-09-16 17:10:57 -0700
commit46a69c89f47451ec2a69cf66025624399ca1c2a2 (patch)
tree88bcf27a7d060c59e41dccd2c3413d3bcf5dba23
parentce63a325103234a0bd382891b81a7a6ac4f857a2 (diff)
parent4063de40705c2b21e092f2a275182b0f41070b15 (diff)
downloadscummvm-rg350-46a69c89f47451ec2a69cf66025624399ca1c2a2.tar.gz
scummvm-rg350-46a69c89f47451ec2a69cf66025624399ca1c2a2.tar.bz2
scummvm-rg350-46a69c89f47451ec2a69cf66025624399ca1c2a2.zip
Merge pull request #376 from lordhoto/libjpeg
GRAPHICS: Implement JPEGDecoder based on libjpeg.
-rwxr-xr-xconfigure38
-rw-r--r--engines/configure.engines4
-rw-r--r--engines/groovie/roq.cpp12
-rw-r--r--graphics/decoders/jpeg.cpp861
-rw-r--r--graphics/decoders/jpeg.h124
5 files changed, 253 insertions, 786 deletions
diff --git a/configure b/configure
index 1da7d7fb78..d4ee1e5a8c 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 195cdda6c7..3c7397fcba 100644
--- a/engines/configure.engines
+++ b/engines/configure.engines
@@ -16,7 +16,7 @@ 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"
@@ -52,4 +52,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 f9a938bfd4..5592d848a9 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->getComponent(1)->getPixels();
- const byte *u = (const byte *)jpg->getComponent(2)->getPixels();
- const byte *v = (const byte *)jpg->getComponent(3)->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 ff018c799a..c858884095 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -20,8 +20,11 @@
*
*/
+// 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"
#include "common/debug.h"
@@ -29,35 +32,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(), _surface(), _colorSpace(kColorSpaceRGBA) {
}
JPEGDecoder::~JPEGDecoder() {
@@ -65,723 +52,215 @@ JPEGDecoder::~JPEGDecoder() {
}
const Surface *JPEGDecoder::getSurface() const {
- // Make sure we have loaded data
- if (!isLoaded())
- return 0;
-
- if (_rgbSurface)
- return _rgbSurface;
-
- // Create an RGBA8888 surface
- _rgbSurface = new Graphics::Surface();
- _rgbSurface->create(_w, _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);
-
- return _rgbSurface;
+ return &_surface;
}
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;
- }
+ _surface.free();
}
-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");
+#ifdef USE_JPEG
+namespace {
- // 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();
+#define JPEG_BUFFER_SIZE 4096
- if ((marker & 0xE0) != 0xE0)
- warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2);
-
- _stream->seek(size - 2, SEEK_CUR);
- }
- }
- }
+struct StreamSource : public jpeg_source_mgr {
+ Common::SeekableReadStream *stream;
+ bool startOfFile;
+ JOCTET buffer[JPEG_BUFFER_SIZE];
+};
- _stream = 0;
- return ok;
+void initSource(j_decompress_ptr cinfo) {
+ StreamSource *source = (StreamSource *)cinfo->src;
+ source->startOfFile = true;
}
-bool JPEGDecoder::readJFIF() {
- uint16 length = _stream->readUint16BE();
- uint32 tag = _stream->readUint32BE();
+boolean fillInputBuffer(j_decompress_ptr cinfo) {
+ StreamSource *source = (StreamSource *)cinfo->src;
- 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;
+ 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;
+ }
}
- 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;
- }
+ source->next_input_byte = source->buffer;
+ source->bytes_in_buffer = bufferSize;
+ source->startOfFile = false;
- return true;
+ return TRUE;
}
-// 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;
- }
+void skipInputData(j_decompress_ptr cinfo, long numBytes) {
+ StreamSource *source = (StreamSource *)cinfo->src;
- // Image size
- _h = _stream->readUint16BE();
- _w = _stream->readUint16BE();
-
- // Number of components
- _numComp = _stream->readByte();
- if (size != 8 + 3 * _numComp) {
- warning("JPEG: Invalid number of components");
- return false;
- }
+ 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;
- // 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();
- }
-
- return true;
-}
+ // Skip the remaining bytes
+ source->stream->skip(numBytes);
-// 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 up the buffer again
+ (*source->fill_input_buffer)(cinfo);
+ } else {
+ source->next_input_byte += (size_t)numBytes;
+ source->bytes_in_buffer -= (size_t)numBytes;
}
- // 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++;
- }
}
-
- return 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];
- }
- }
-
- if (!found) {
- warning("JPEG: Invalid component");
- return false;
- }
-
- // 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;
- }
-
- // 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;
-
- for (int y = 0; ok && (y < yMCU); y++) {
- for (int x = 0; ok && (x < xMCU); x++) {
- ok = readMCU(x, y);
-
- // If we have a restart interval, we'll need to reset a couple
- // variables
- if (_restartInterval != 0) {
- interval--;
-
- if (interval == 0) {
- interval = _restartInterval;
- _bitsNumber = 0;
-
- for (byte i = 0; i < _numScanComp; i++)
- _scanComp[i]->DCpredictor = 0;
- }
- }
- }
- }
-
- // 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;
+void termSource(j_decompress_ptr cinfo) {
}
-// 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];
+void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
+ StreamSource *source;
- // Read the table (stored in Zig-Zag order)
- for (int i = 0; i < 64; i++)
- _quant[tableId][i] = highPrecision ? _stream->readUint16BE() : _stream->readByte();
+ // 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));
}
- return true;
-}
+ 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;
-// Marker 0xDD (Define Restart Interval)
-bool JPEGDecoder::readDRI() {
- debug(5, "JPEG: readDRI");
- uint16 size = _stream->readUint16BE() - 2;
-
- if (size != 2) {
- warning("JPEG: Invalid DRI size %d", size);
- return false;
- }
-
- _restartInterval = _stream->readUint16BE();
- debug(5, "Restart interval: %d", _restartInterval);
- return true;
+ source->stream = stream;
}
-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];
-
- // 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);
- }
-
- 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;
+ // Initialize our buffer handling
+ jpeg_scummvm_src(&cinfo, &stream);
- // Convert coordinates from MCU blocks to pixels
- x <<= 3;
- y <<= 3;
+ // Read the file header
+ jpeg_read_header(&cinfo, TRUE);
- 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);
+ // We can request YUV output because Groovie requires it
+ switch (_colorSpace) {
+ case kColorSpaceRGBA:
+ cinfo.out_color_space = JCS_RGB;
+ break;
- for (uint8 i = 0; i < 8; i++) {
- for (uint16 sH = 0; sH < scalingH; sH++) {
- *ptr = (byte)(block[j * 8 + i]);
- ptr++;
- }
- }
- }
+ case kColorSpaceYUV:
+ cinfo.out_color_space = JCS_YCbCr;
+ break;
}
- return true;
-}
-
-int16 JPEGDecoder::readDC() {
- // DC is type 0
- uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
-
- // Get the number of bits to read
- uint8 numBits = readHuff(tableNum);
-
- // Read the requested bits
- return readSignedBits(numBits);
-}
-
-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;
-
- // Read the next value
- out[cur] = readSignedBits(s);
- cur++;
- }
+ // 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;
}
-}
-
-int16 JPEGDecoder::readSignedBits(uint8 numBits) {
- uint16 ret = 0;
- if (numBits > 16)
- error("requested %d bits", numBits); //XXX
-
- // 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++;
+ // 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;
- 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);
- }
- }
+ case kColorSpaceYUV:
+ memcpy(dst, src, pitch);
+ break;
}
}
- _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 d59b72adf4..8460bc2698 100644
--- a/graphics/decoders/jpeg.h
+++ b/graphics/decoders/jpeg.h
@@ -40,100 +40,54 @@ class SeekableReadStream;
namespace Graphics {
-struct PixelFormat;
-
-#define JPEG_MAX_QUANT_TABLES 4
-#define JPEG_MAX_HUFF_TABLES 2
-
class JPEGDecoder : public ImageDecoder {
public:
JPEGDecoder();
~JPEGDecoder();
// ImageDecoder API
- void destroy();
- 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;
-
-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;
+ virtual void destroy();
+ virtual bool loadStream(Common::SeekableReadStream &str);
+ virtual const Surface *getSurface() const;
+
+ // Special API for JPEG
+ enum ColorSpace {
+ /**
+ * Output 32bit RGBA data.
+ *
+ * This is the default output.
+ */
+ kColorSpaceRGBA,
+
+ /**
+ * 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
};
- Component *_components;
+ /**
+ * 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; }
- // 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]);
+private:
+ Graphics::Surface _surface;
+ ColorSpace _colorSpace;
};
} // End of Graphics namespace