mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-11 11:45:21 +00:00
Merge pull request #376 from lordhoto/libjpeg
GRAPHICS: Implement JPEGDecoder based on libjpeg.
This commit is contained in:
commit
46a69c89f4
38
configure
vendored
38
configure
vendored
@ -115,6 +115,7 @@ _timidity=auto
|
|||||||
_zlib=auto
|
_zlib=auto
|
||||||
_mpeg2=auto
|
_mpeg2=auto
|
||||||
_sparkle=auto
|
_sparkle=auto
|
||||||
|
_jpeg=auto
|
||||||
_png=auto
|
_png=auto
|
||||||
_theoradec=auto
|
_theoradec=auto
|
||||||
_faad=auto
|
_faad=auto
|
||||||
@ -188,6 +189,7 @@ add_feature faad "libfaad" "_faad"
|
|||||||
add_feature flac "FLAC" "_flac"
|
add_feature flac "FLAC" "_flac"
|
||||||
add_feature freetype2 "FreeType2" "_freetype2"
|
add_feature freetype2 "FreeType2" "_freetype2"
|
||||||
add_feature mad "MAD" "_mad"
|
add_feature mad "MAD" "_mad"
|
||||||
|
add_feature jpeg "JPEG" "_jpeg"
|
||||||
add_feature png "PNG" "_png"
|
add_feature png "PNG" "_png"
|
||||||
add_feature theoradec "libtheoradec" "_theoradec"
|
add_feature theoradec "libtheoradec" "_theoradec"
|
||||||
add_feature vorbis "Vorbis file support" "_vorbis _tremor"
|
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)
|
--with-opengl-prefix=DIR Prefix where OpenGL (ES) is installed (optional)
|
||||||
--disable-opengl disable OpenGL (ES) support [autodetect]
|
--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)
|
--with-png-prefix=DIR Prefix where libpng is installed (optional)
|
||||||
--disable-png disable PNG decoder [autodetect]
|
--disable-png disable PNG decoder [autodetect]
|
||||||
|
|
||||||
@ -1015,6 +1020,8 @@ for ac_option in $@; do
|
|||||||
--disable-nasm) _nasm=no ;;
|
--disable-nasm) _nasm=no ;;
|
||||||
--enable-mpeg2) _mpeg2=yes ;;
|
--enable-mpeg2) _mpeg2=yes ;;
|
||||||
--disable-mpeg2) _mpeg2=no ;;
|
--disable-mpeg2) _mpeg2=no ;;
|
||||||
|
--disable-jpeg) _jpeg=no ;;
|
||||||
|
--enable-jpeg) _jpeg=yes ;;
|
||||||
--disable-png) _png=no ;;
|
--disable-png) _png=no ;;
|
||||||
--enable-png) _png=yes ;;
|
--enable-png) _png=yes ;;
|
||||||
--disable-theoradec) _theoradec=no ;;
|
--disable-theoradec) _theoradec=no ;;
|
||||||
@ -1096,6 +1103,11 @@ for ac_option in $@; do
|
|||||||
MAD_CFLAGS="-I$arg/include"
|
MAD_CFLAGS="-I$arg/include"
|
||||||
MAD_LIBS="-L$arg/lib"
|
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=*)
|
--with-png-prefix=*)
|
||||||
arg=`echo $ac_option | cut -d '=' -f 2`
|
arg=`echo $ac_option | cut -d '=' -f 2`
|
||||||
PNG_CFLAGS="-I$arg/include"
|
PNG_CFLAGS="-I$arg/include"
|
||||||
@ -3338,6 +3350,32 @@ fi
|
|||||||
define_in_config_h_if_yes "$_alsa" 'USE_ALSA'
|
define_in_config_h_if_yes "$_alsa" 'USE_ALSA'
|
||||||
echo "$_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
|
# Check for PNG
|
||||||
#
|
#
|
||||||
|
@ -16,7 +16,7 @@ add_engine dreamweb "Dreamweb" yes
|
|||||||
add_engine fullpipe "Full Pipe" yes
|
add_engine fullpipe "Full Pipe" yes
|
||||||
add_engine gob "Gobli*ns" yes
|
add_engine gob "Gobli*ns" yes
|
||||||
add_engine groovie "Groovie" yes "groovie2" "7th Guest"
|
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 hopkins "Hopkins FBI" yes "" "" "16bit"
|
||||||
add_engine hugo "Hugo Trilogy" yes
|
add_engine hugo "Hugo Trilogy" yes
|
||||||
add_engine kyra "Kyra" yes "lol eob" "Legend of Kyrandia 1-3"
|
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 tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit"
|
||||||
add_engine tsage "TsAGE" yes
|
add_engine tsage "TsAGE" yes
|
||||||
add_engine tucker "Bud Tucker in Double Trouble" 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"
|
||||||
|
@ -435,17 +435,13 @@ bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
|
|||||||
warning("Groovie::ROQ: JPEG frame (unfinished)");
|
warning("Groovie::ROQ: JPEG frame (unfinished)");
|
||||||
|
|
||||||
Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder();
|
Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder();
|
||||||
|
jpg->setOutputColorSpace(Graphics::JPEGDecoder::kColorSpaceYUV);
|
||||||
jpg->loadStream(*_file);
|
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();
|
byte *ptr = (byte *)_currBuf->getPixels();
|
||||||
for (int i = 0; i < _currBuf->w * _currBuf->h; i++) {
|
memcpy(ptr, src, _currBuf->w * _currBuf->h);
|
||||||
*ptr++ = *y++;
|
|
||||||
*ptr++ = *u++;
|
|
||||||
*ptr++ = *v++;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete jpg;
|
delete jpg;
|
||||||
return true;
|
return true;
|
||||||
|
@ -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/pixelformat.h"
|
||||||
#include "graphics/yuv_to_rgb.h"
|
|
||||||
#include "graphics/decoders/jpeg.h"
|
#include "graphics/decoders/jpeg.h"
|
||||||
|
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
@ -29,35 +32,19 @@
|
|||||||
#include "common/stream.h"
|
#include "common/stream.h"
|
||||||
#include "common/textconsole.h"
|
#include "common/textconsole.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 Graphics {
|
namespace Graphics {
|
||||||
|
|
||||||
// Order used to traverse the quantization tables
|
JPEGDecoder::JPEGDecoder() : ImageDecoder(), _surface(), _colorSpace(kColorSpaceRGBA) {
|
||||||
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) {
|
|
||||||
|
|
||||||
// Initialize the quantization tables
|
|
||||||
for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++)
|
|
||||||
_quant[i] = NULL;
|
|
||||||
|
|
||||||
// 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() {
|
JPEGDecoder::~JPEGDecoder() {
|
||||||
@ -65,723 +52,215 @@ JPEGDecoder::~JPEGDecoder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Surface *JPEGDecoder::getSurface() const {
|
const Surface *JPEGDecoder::getSurface() const {
|
||||||
// Make sure we have loaded data
|
return &_surface;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JPEGDecoder::destroy() {
|
void JPEGDecoder::destroy() {
|
||||||
// Reset member variables
|
_surface.free();
|
||||||
_stream = NULL;
|
}
|
||||||
_w = _h = 0;
|
|
||||||
_restartInterval = 0;
|
|
||||||
|
|
||||||
// Free the components
|
#ifdef USE_JPEG
|
||||||
for (int c = 0; c < _numComp; c++)
|
namespace {
|
||||||
_components[c].surface.free();
|
|
||||||
delete[] _components; _components = NULL;
|
|
||||||
_numComp = 0;
|
|
||||||
|
|
||||||
// Free the scan components
|
#define JPEG_BUFFER_SIZE 4096
|
||||||
delete[] _scanComp; _scanComp = NULL;
|
|
||||||
_numScanComp = 0;
|
|
||||||
_currentComp = NULL;
|
|
||||||
|
|
||||||
// Free the quantization tables
|
struct StreamSource : public jpeg_source_mgr {
|
||||||
for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
|
Common::SeekableReadStream *stream;
|
||||||
delete[] _quant[i];
|
bool startOfFile;
|
||||||
_quant[i] = NULL;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the Huffman tables
|
source->next_input_byte = source->buffer;
|
||||||
for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
|
source->bytes_in_buffer = bufferSize;
|
||||||
_huff[i].count = 0;
|
source->startOfFile = false;
|
||||||
delete[] _huff[i].values; _huff[i].values = NULL;
|
|
||||||
delete[] _huff[i].sizes; _huff[i].sizes = NULL;
|
return TRUE;
|
||||||
delete[] _huff[i].codes; _huff[i].codes = NULL;
|
}
|
||||||
}
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (_rgbSurface) {
|
|
||||||
_rgbSurface->free();
|
|
||||||
delete _rgbSurface;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||||
// Reset member variables and tables from previous reads
|
#ifdef USE_JPEG
|
||||||
|
// Reset member variables from previous decodings
|
||||||
destroy();
|
destroy();
|
||||||
|
|
||||||
// Save the input stream
|
jpeg_decompress_struct cinfo;
|
||||||
_stream = &stream;
|
jpeg_error_mgr jerr;
|
||||||
|
|
||||||
bool ok = true;
|
// Initialize error handling callbacks
|
||||||
bool done = false;
|
cinfo.err = jpeg_std_error(&jerr);
|
||||||
while (!_stream->eos() && ok && !done) {
|
cinfo.err->error_exit = &errorExit;
|
||||||
// Read the marker
|
cinfo.err->output_message = &outputMessage;
|
||||||
|
|
||||||
// WORKAROUND: While each and every JPEG file should end with
|
// Initialize the decompression structure
|
||||||
// an EOI (end of image) tag, in reality this may not be the
|
jpeg_create_decompress(&cinfo);
|
||||||
// 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();
|
// Initialize our buffer handling
|
||||||
bool fakeEOI = false;
|
jpeg_scummvm_src(&cinfo, &stream);
|
||||||
|
|
||||||
if (_stream->eos()) {
|
// Read the file header
|
||||||
fakeEOI = true;
|
jpeg_read_header(&cinfo, TRUE);
|
||||||
marker = 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (marker != 0xFF) {
|
// We can request YUV output because Groovie requires it
|
||||||
error("JPEG: Invalid marker[0]: 0x%02X", marker);
|
switch (_colorSpace) {
|
||||||
ok = false;
|
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;
|
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;
|
// We are done with decompressing, thus free all the data
|
||||||
return ok;
|
jpeg_finish_decompress(&cinfo);
|
||||||
}
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
|
||||||
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;
|
return true;
|
||||||
}
|
#else
|
||||||
|
return false;
|
||||||
// Marker 0xC0 (Start Of Frame, Baseline DCT)
|
#endif
|
||||||
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();
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the IDCT
|
|
||||||
idct2D8x8(block);
|
|
||||||
|
|
||||||
// Level shift to make the values unsigned
|
|
||||||
for (int i = 0; i < 64; i++) {
|
|
||||||
block[i] = block[i] + 128;
|
|
||||||
|
|
||||||
if (block[i] < 0)
|
|
||||||
block[i] = 0;
|
|
||||||
|
|
||||||
if (block[i] > 255)
|
|
||||||
block[i] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_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;
|
|
||||||
|
|
||||||
error("JPEGDecoder::getComponent: No component %d present", c);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // End of Graphics namespace
|
} // End of Graphics namespace
|
||||||
|
@ -40,100 +40,54 @@ class SeekableReadStream;
|
|||||||
|
|
||||||
namespace Graphics {
|
namespace Graphics {
|
||||||
|
|
||||||
struct PixelFormat;
|
|
||||||
|
|
||||||
#define JPEG_MAX_QUANT_TABLES 4
|
|
||||||
#define JPEG_MAX_HUFF_TABLES 2
|
|
||||||
|
|
||||||
class JPEGDecoder : public ImageDecoder {
|
class JPEGDecoder : public ImageDecoder {
|
||||||
public:
|
public:
|
||||||
JPEGDecoder();
|
JPEGDecoder();
|
||||||
~JPEGDecoder();
|
~JPEGDecoder();
|
||||||
|
|
||||||
// ImageDecoder API
|
// ImageDecoder API
|
||||||
void destroy();
|
virtual void destroy();
|
||||||
bool loadStream(Common::SeekableReadStream &str);
|
virtual bool loadStream(Common::SeekableReadStream &str);
|
||||||
const Surface *getSurface() const;
|
virtual const Surface *getSurface() const;
|
||||||
|
|
||||||
bool isLoaded() const { return _numComp && _w && _h; }
|
// Special API for JPEG
|
||||||
uint16 getWidth() const { return _w; }
|
enum ColorSpace {
|
||||||
uint16 getHeight() const { return _h; }
|
/**
|
||||||
const Surface *getComponent(uint c) const;
|
* Output 32bit RGBA data.
|
||||||
|
*
|
||||||
|
* This is the default output.
|
||||||
|
*/
|
||||||
|
kColorSpaceRGBA,
|
||||||
|
|
||||||
private:
|
/**
|
||||||
Common::SeekableReadStream *_stream;
|
* Output (interleaved) YUV data.
|
||||||
uint16 _w, _h;
|
*
|
||||||
uint16 _restartInterval;
|
* Be aware that some images cannot be output in YUV mode.
|
||||||
|
* These are (non-standard) JPEG images which are in RGB colorspace.
|
||||||
// mutable so that we can convert to RGB only during
|
*
|
||||||
// a getSurface() call while still upholding the
|
* The resulting Surface will have a PixelFormat with 3 bytes per
|
||||||
// const requirement in other ImageDecoders
|
* pixel and the remaining entries are completely zeroed. This works
|
||||||
mutable Graphics::Surface *_rgbSurface;
|
* around the fact that PixelFormat can only describe RGB formats.
|
||||||
|
*
|
||||||
// Image components
|
* You should only use this when you are really aware of what you are
|
||||||
uint8 _numComp;
|
* doing!
|
||||||
struct Component {
|
*/
|
||||||
// Global values
|
kColorSpaceYUV
|
||||||
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;
|
/**
|
||||||
|
* 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
|
private:
|
||||||
uint8 _numScanComp;
|
Graphics::Surface _surface;
|
||||||
Component **_scanComp;
|
ColorSpace _colorSpace;
|
||||||
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]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of Graphics namespace
|
} // End of Graphics namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user