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:
parent
ac66cc9219
commit
4809294b43
3 changed files with 94 additions and 59 deletions
|
@ -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->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();
|
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;
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
#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"
|
||||||
|
@ -45,7 +44,7 @@ extern "C" {
|
||||||
|
|
||||||
namespace Graphics {
|
namespace Graphics {
|
||||||
|
|
||||||
JPEGDecoder::JPEGDecoder() : ImageDecoder(), _rgbSurface(nullptr) {
|
JPEGDecoder::JPEGDecoder() : ImageDecoder(), _surface(), _colorSpace(kColorSpaceRGBA) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JPEGDecoder::~JPEGDecoder() {
|
JPEGDecoder::~JPEGDecoder() {
|
||||||
|
@ -53,31 +52,11 @@ JPEGDecoder::~JPEGDecoder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Surface *JPEGDecoder::getSurface() const {
|
const Surface *JPEGDecoder::getSurface() const {
|
||||||
// Make sure we have loaded data
|
return &_surface;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JPEGDecoder::destroy() {
|
void JPEGDecoder::destroy() {
|
||||||
if (_rgbSurface) {
|
_surface.free();
|
||||||
_rgbSurface->free();
|
|
||||||
delete _rgbSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
_yComponent.free();
|
|
||||||
_uComponent.free();
|
|
||||||
_vComponent.free();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_JPEG
|
#ifdef USE_JPEG
|
||||||
|
@ -206,16 +185,33 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||||
// Read the file header
|
// Read the file header
|
||||||
jpeg_read_header(&cinfo, TRUE);
|
jpeg_read_header(&cinfo, TRUE);
|
||||||
|
|
||||||
// We request YUV output because Groovie requires it
|
// We can request YUV output because Groovie requires it
|
||||||
cinfo.out_color_space = JCS_YCbCr;
|
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
|
// Actually start decompressing the image
|
||||||
jpeg_start_decompress(&cinfo);
|
jpeg_start_decompress(&cinfo);
|
||||||
|
|
||||||
// Allocate buffers for the YUV components
|
// Allocate buffers for the output data
|
||||||
_yComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
|
switch (_colorSpace) {
|
||||||
_uComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
|
case kColorSpaceRGBA:
|
||||||
_vComponent.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat::createFormatCLUT8());
|
// 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
|
// Allocate buffer for one scanline
|
||||||
JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
|
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
|
// Go through the image data scanline by scanline
|
||||||
while (cinfo.output_scanline < cinfo.output_height) {
|
while (cinfo.output_scanline < cinfo.output_height) {
|
||||||
byte *yPtr = (byte *)_yComponent.getBasePtr(0, cinfo.output_scanline);
|
byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
|
||||||
byte *uPtr = (byte *)_uComponent.getBasePtr(0, cinfo.output_scanline);
|
|
||||||
byte *vPtr = (byte *)_vComponent.getBasePtr(0, cinfo.output_scanline);
|
|
||||||
|
|
||||||
jpeg_read_scanlines(&cinfo, buffer, 1);
|
jpeg_read_scanlines(&cinfo, buffer, 1);
|
||||||
|
|
||||||
const byte *src = buffer[0];
|
const byte *src = buffer[0];
|
||||||
for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
|
switch (_colorSpace) {
|
||||||
*yPtr++ = *src++;
|
case kColorSpaceRGBA: {
|
||||||
*uPtr++ = *src++;
|
for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
|
||||||
*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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,23 +46,48 @@ public:
|
||||||
~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;
|
||||||
|
|
||||||
const Surface &getYComponent() const { return _yComponent; }
|
// Special API for JPEG
|
||||||
const Surface &getUComponent() const { return _uComponent; }
|
enum ColorSpace {
|
||||||
const Surface &getVComponent() const { return _vComponent; }
|
/**
|
||||||
|
* 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:
|
private:
|
||||||
// mutable so that we can convert to RGB only during
|
Graphics::Surface _surface;
|
||||||
// a getSurface() call while still upholding the
|
ColorSpace _colorSpace;
|
||||||
// const requirement in other ImageDecoders
|
|
||||||
mutable Graphics::Surface *_rgbSurface;
|
|
||||||
|
|
||||||
Graphics::Surface _yComponent;
|
|
||||||
Graphics::Surface _uComponent;
|
|
||||||
Graphics::Surface _vComponent;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of Graphics namespace
|
} // End of Graphics namespace
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue