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.
This commit is contained in:
Johannes Schickel 2013-08-12 00:53:34 +02:00
parent ac66cc9219
commit 4809294b43
3 changed files with 94 additions and 59 deletions

View file

@ -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;

View file

@ -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
// 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];
switch (_colorSpace) {
case kColorSpaceRGBA: {
for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
*yPtr++ = *src++;
*uPtr++ = *src++;
*vPtr++ = *src++;
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;
}
}

View file

@ -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,
/**
* 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; }
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;
Graphics::Surface _yComponent;
Graphics::Surface _uComponent;
Graphics::Surface _vComponent;
Graphics::Surface _surface;
ColorSpace _colorSpace;
};
} // End of Graphics namespace