IMAGE: Allow setting the output pixel format to the JPEG decoder
This commit is contained in:
parent
5196ae1cd4
commit
0d5d04ca3a
7 changed files with 108 additions and 50 deletions
|
@ -125,6 +125,7 @@ Picture *Pictures::load(uint32 id) {
|
||||||
palette = png.getPalette();
|
palette = png.getPalette();
|
||||||
palCount = png.getPaletteColorCount();
|
palCount = png.getPaletteColorCount();
|
||||||
} else if (f.open(Common::String::format("pic%u.jpg", id))) {
|
} else if (f.open(Common::String::format("pic%u.jpg", id))) {
|
||||||
|
jpg.setOutputPixelFormat(g_system->getScreenFormat());
|
||||||
jpg.loadStream(f);
|
jpg.loadStream(f);
|
||||||
img = jpg.getSurface();
|
img = jpg.getSurface();
|
||||||
} else if (f.open(Common::String::format("pic%u.raw", id))) {
|
} else if (f.open(Common::String::format("pic%u.raw", id))) {
|
||||||
|
|
|
@ -470,6 +470,7 @@ bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
|
||||||
debugC(5, kDebugVideo, "Groovie::ROQ: Processing still (JPEG) block");
|
debugC(5, kDebugVideo, "Groovie::ROQ: Processing still (JPEG) block");
|
||||||
|
|
||||||
Image::JPEGDecoder jpg;
|
Image::JPEGDecoder jpg;
|
||||||
|
jpg.setOutputPixelFormat(_vm->_pixelFormat);
|
||||||
|
|
||||||
uint32 startPos = _file->pos();
|
uint32 startPos = _file->pos();
|
||||||
Common::SeekableSubReadStream subStream(_file, startPos, startPos + blockHeader.size, DisposeAfterUse::NO);
|
Common::SeekableSubReadStream subStream(_file, startPos, startPos + blockHeader.size, DisposeAfterUse::NO);
|
||||||
|
@ -478,7 +479,9 @@ bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
|
||||||
const Graphics::Surface *srcSurf = jpg.getSurface();
|
const Graphics::Surface *srcSurf = jpg.getSurface();
|
||||||
_currBuf->free();
|
_currBuf->free();
|
||||||
delete _currBuf;
|
delete _currBuf;
|
||||||
_currBuf = srcSurf->convertTo(_vm->_pixelFormat);
|
|
||||||
|
_currBuf = new Graphics::Surface();
|
||||||
|
_currBuf->copyFrom(*srcSurf);
|
||||||
|
|
||||||
_file->seek(startPos + blockHeader.size);
|
_file->seek(startPos + blockHeader.size);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "common/system.h"
|
||||||
#include "titanic/support/image_decoders.h"
|
#include "titanic/support/image_decoders.h"
|
||||||
|
|
||||||
namespace Titanic {
|
namespace Titanic {
|
||||||
|
@ -29,7 +30,8 @@ void CJPEGDecode::decode(OSVideoSurface &surface, const CString &name) {
|
||||||
StdCWadFile file;
|
StdCWadFile file;
|
||||||
file.open(name);
|
file.open(name);
|
||||||
|
|
||||||
// Use the ScucmmVM deoder to decode it
|
// Use the ScummVM decoder to decode it
|
||||||
|
setOutputPixelFormat(g_system->getScreenFormat());
|
||||||
loadStream(*file.readStream());
|
loadStream(*file.readStream());
|
||||||
const Graphics::Surface *srcSurf = getSurface();
|
const Graphics::Surface *srcSurf = getSurface();
|
||||||
|
|
||||||
|
@ -38,15 +40,14 @@ void CJPEGDecode::decode(OSVideoSurface &surface, const CString &name) {
|
||||||
|| surface.getHeight() != srcSurf->h)
|
|| surface.getHeight() != srcSurf->h)
|
||||||
surface.recreate(srcSurf->w, srcSurf->h, 16);
|
surface.recreate(srcSurf->w, srcSurf->h, 16);
|
||||||
|
|
||||||
// Convert the decoded surface to the correct pixel format, and then copy it over
|
// Copy the decoded surface
|
||||||
surface.lock();
|
surface.lock();
|
||||||
Graphics::Surface *convertedSurface = srcSurf->convertTo(surface._rawSurface->format);
|
|
||||||
|
|
||||||
Common::copy((byte *)convertedSurface->getPixels(), (byte *)convertedSurface->getPixels() +
|
assert(srcSurf->format == surface._rawSurface->format);
|
||||||
|
|
||||||
|
Common::copy((const byte *)srcSurf->getPixels(), (const byte *)srcSurf->getPixels() +
|
||||||
surface.getPitch() * surface.getHeight(), (byte *)surface._rawSurface->getPixels());
|
surface.getPitch() * surface.getHeight(), (byte *)surface._rawSurface->getPixels());
|
||||||
|
|
||||||
convertedSurface->free();
|
|
||||||
delete convertedSurface;
|
|
||||||
surface.unlock();
|
surface.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,6 @@
|
||||||
* @file
|
* @file
|
||||||
* YUV to RGB conversion.
|
* YUV to RGB conversion.
|
||||||
*
|
*
|
||||||
* Used in graphics:
|
|
||||||
* - JPEGDecoder
|
|
||||||
*
|
|
||||||
* Used in video:
|
* Used in video:
|
||||||
* - BinkDecoder
|
* - BinkDecoder
|
||||||
* - Indeo3Decoder
|
* - Indeo3Decoder
|
||||||
|
|
|
@ -200,6 +200,7 @@ const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &s
|
||||||
|
|
||||||
Common::MemoryReadStream convertedStream(data, outputSize, DisposeAfterUse::YES);
|
Common::MemoryReadStream convertedStream(data, outputSize, DisposeAfterUse::YES);
|
||||||
JPEGDecoder jpeg;
|
JPEGDecoder jpeg;
|
||||||
|
jpeg.setOutputPixelFormat(_pixelFormat);
|
||||||
|
|
||||||
if (!jpeg.loadStream(convertedStream)) {
|
if (!jpeg.loadStream(convertedStream)) {
|
||||||
warning("Failed to decode MJPEG frame");
|
warning("Failed to decode MJPEG frame");
|
||||||
|
@ -211,7 +212,10 @@ const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &s
|
||||||
delete _surface;
|
delete _surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
_surface = jpeg.getSurface()->convertTo(_pixelFormat);
|
_surface = new Graphics::Surface();
|
||||||
|
_surface->copyFrom(*jpeg.getSurface());
|
||||||
|
|
||||||
|
assert(_surface->format == _pixelFormat);
|
||||||
|
|
||||||
return _surface;
|
return _surface;
|
||||||
}
|
}
|
||||||
|
|
114
image/jpeg.cpp
114
image/jpeg.cpp
|
@ -30,6 +30,7 @@
|
||||||
#include "common/endian.h"
|
#include "common/endian.h"
|
||||||
#include "common/stream.h"
|
#include "common/stream.h"
|
||||||
#include "common/textconsole.h"
|
#include "common/textconsole.h"
|
||||||
|
#include "common/util.h"
|
||||||
#include "graphics/pixelformat.h"
|
#include "graphics/pixelformat.h"
|
||||||
|
|
||||||
#ifdef USE_JPEG
|
#ifdef USE_JPEG
|
||||||
|
@ -44,13 +45,24 @@ extern "C" {
|
||||||
|
|
||||||
namespace Image {
|
namespace Image {
|
||||||
|
|
||||||
JPEGDecoder::JPEGDecoder() : _surface(), _colorSpace(kColorSpaceRGBA) {
|
JPEGDecoder::JPEGDecoder() :
|
||||||
|
_surface(),
|
||||||
|
_colorSpace(kColorSpaceRGB),
|
||||||
|
_requestedPixelFormat(getByteOrderRgbPixelFormat()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JPEGDecoder::~JPEGDecoder() {
|
JPEGDecoder::~JPEGDecoder() {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Graphics::PixelFormat JPEGDecoder::getByteOrderRgbPixelFormat() const {
|
||||||
|
#ifdef SCUMM_BIG_ENDIAN
|
||||||
|
return Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
|
||||||
|
#else
|
||||||
|
return Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
const Graphics::Surface *JPEGDecoder::getSurface() const {
|
const Graphics::Surface *JPEGDecoder::getSurface() const {
|
||||||
return &_surface;
|
return &_surface;
|
||||||
}
|
}
|
||||||
|
@ -171,6 +183,44 @@ void outputMessage(j_common_ptr cinfo) {
|
||||||
debug(3, "libjpeg: %s", buffer);
|
debug(3, "libjpeg: %s", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
J_COLOR_SPACE fromScummvmPixelFormat(const Graphics::PixelFormat &format) {
|
||||||
|
struct PixelFormatMapping {
|
||||||
|
Graphics::PixelFormat pixelFormat;
|
||||||
|
J_COLOR_SPACE bigEndianColorSpace;
|
||||||
|
J_COLOR_SPACE littleEndianColorSpace;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const PixelFormatMapping mappings[] = {
|
||||||
|
#ifdef JCS_EXTENSIONS
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0), JCS_EXT_RGBX, JCS_EXT_XBGR },
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 0, 0, 8, 16, 24), JCS_EXT_XBGR, JCS_EXT_RGBX },
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 24), JCS_EXT_XRGB, JCS_EXT_BGRX },
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 0, 8, 16, 24, 0), JCS_EXT_BGRX, JCS_EXT_XRGB },
|
||||||
|
{ Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0), JCS_EXT_RGB, JCS_EXT_BGR },
|
||||||
|
{ Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0), JCS_EXT_BGR, JCS_EXT_RGB },
|
||||||
|
#endif
|
||||||
|
#ifdef JCS_ALPHA_EXTENSIONS
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), JCS_EXT_RGBA, JCS_EXT_ABGR },
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), JCS_EXT_ABGR, JCS_EXT_RGBA },
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), JCS_EXT_ARGB, JCS_EXT_BGRA },
|
||||||
|
{ Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0), JCS_EXT_BGRA, JCS_EXT_ARGB },
|
||||||
|
#endif
|
||||||
|
{ Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), JCS_RGB565, JCS_RGB565 }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint i = 0; i < ARRAYSIZE(mappings); i++) {
|
||||||
|
if (mappings[i].pixelFormat == format) {
|
||||||
|
#ifdef SCUMM_BIG_ENDIAN
|
||||||
|
return mappings[i].bigEndianColorSpace;
|
||||||
|
#else
|
||||||
|
return mappings[i].littleEndianColorSpace;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JCS_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
} // End of anonymous namespace
|
} // End of anonymous namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -198,10 +248,19 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||||
|
|
||||||
// We can request YUV output because Groovie requires it
|
// We can request YUV output because Groovie requires it
|
||||||
switch (_colorSpace) {
|
switch (_colorSpace) {
|
||||||
case kColorSpaceRGBA:
|
case kColorSpaceRGB: {
|
||||||
cinfo.out_color_space = JCS_RGB;
|
J_COLOR_SPACE colorSpace = fromScummvmPixelFormat(_requestedPixelFormat);
|
||||||
break;
|
|
||||||
|
|
||||||
|
if (colorSpace == JCS_UNKNOWN) {
|
||||||
|
// When libjpeg-turbo is not available or an unhandled pixel
|
||||||
|
// format was requested, ask libjpeg to decode to byte order RGB
|
||||||
|
// as it's always available.
|
||||||
|
colorSpace = JCS_RGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
cinfo.out_color_space = colorSpace;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case kColorSpaceYUV:
|
case kColorSpaceYUV:
|
||||||
cinfo.out_color_space = JCS_YCbCr;
|
cinfo.out_color_space = JCS_YCbCr;
|
||||||
break;
|
break;
|
||||||
|
@ -212,11 +271,16 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||||
|
|
||||||
// Allocate buffers for the output data
|
// Allocate buffers for the output data
|
||||||
switch (_colorSpace) {
|
switch (_colorSpace) {
|
||||||
case kColorSpaceRGBA:
|
case kColorSpaceRGB: {
|
||||||
// We use RGBA8888 in this scenario
|
Graphics::PixelFormat outputPixelFormat;
|
||||||
_surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
|
if (cinfo.out_color_space == JCS_RGB) {
|
||||||
|
outputPixelFormat = getByteOrderRgbPixelFormat();
|
||||||
|
} else {
|
||||||
|
outputPixelFormat = _requestedPixelFormat;
|
||||||
|
}
|
||||||
|
_surface.create(cinfo.output_width, cinfo.output_height, outputPixelFormat);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case kColorSpaceYUV:
|
case kColorSpaceYUV:
|
||||||
// We use YUV with 3 bytes per pixel otherwise.
|
// We use YUV with 3 bytes per pixel otherwise.
|
||||||
// This is pretty ugly since our PixelFormat cannot express YUV...
|
// This is pretty ugly since our PixelFormat cannot express YUV...
|
||||||
|
@ -225,8 +289,7 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffer for one scanline
|
// Allocate buffer for one scanline
|
||||||
assert(cinfo.output_components == 3);
|
JDIMENSION pitch = cinfo.output_width * _surface.format.bytesPerPixel;
|
||||||
JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
|
|
||||||
assert(_surface.pitch >= pitch);
|
assert(_surface.pitch >= pitch);
|
||||||
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
|
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
|
||||||
|
|
||||||
|
@ -236,38 +299,17 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||||
|
|
||||||
jpeg_read_scanlines(&cinfo, buffer, 1);
|
jpeg_read_scanlines(&cinfo, buffer, 1);
|
||||||
|
|
||||||
const byte *src = buffer[0];
|
memcpy(dst, buffer[0], pitch);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are done with decompressing, thus free all the data
|
// We are done with decompressing, thus free all the data
|
||||||
jpeg_finish_decompress(&cinfo);
|
jpeg_finish_decompress(&cinfo);
|
||||||
jpeg_destroy_decompress(&cinfo);
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
|
||||||
|
if (_colorSpace == kColorSpaceRGB && _surface.format != _requestedPixelFormat) {
|
||||||
|
_surface.convertToInPlace(_requestedPixelFormat); // Slow path
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
|
16
image/jpeg.h
16
image/jpeg.h
|
@ -60,11 +60,11 @@ public:
|
||||||
// Special API for JPEG
|
// Special API for JPEG
|
||||||
enum ColorSpace {
|
enum ColorSpace {
|
||||||
/**
|
/**
|
||||||
* Output 32bit RGBA data.
|
* Output RGB data in the pixel format specified using `setOutputPixelFormat`.
|
||||||
*
|
*
|
||||||
* This is the default output.
|
* This is the default output.
|
||||||
*/
|
*/
|
||||||
kColorSpaceRGBA,
|
kColorSpaceRGB,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output (interleaved) YUV data.
|
* Output (interleaved) YUV data.
|
||||||
|
@ -86,15 +86,25 @@ public:
|
||||||
* Request the output color space. This can be used to obtain raw YUV
|
* 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!
|
* data from the JPEG file. But this might not work for all files!
|
||||||
*
|
*
|
||||||
* The decoder itself defaults to RGBA.
|
* The decoder itself defaults to RGB.
|
||||||
*
|
*
|
||||||
* @param outSpace The color space to output.
|
* @param outSpace The color space to output.
|
||||||
*/
|
*/
|
||||||
void setOutputColorSpace(ColorSpace outSpace) { _colorSpace = outSpace; }
|
void setOutputColorSpace(ColorSpace outSpace) { _colorSpace = outSpace; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the output pixel format. The JPEG decoder provides high performance
|
||||||
|
* color conversion routines for some pixel formats. This setting allows to use
|
||||||
|
* them and avoid costly subsequent color conversion.
|
||||||
|
*/
|
||||||
|
void setOutputPixelFormat(const Graphics::PixelFormat &format) { _requestedPixelFormat = format; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Graphics::Surface _surface;
|
Graphics::Surface _surface;
|
||||||
ColorSpace _colorSpace;
|
ColorSpace _colorSpace;
|
||||||
|
Graphics::PixelFormat _requestedPixelFormat;
|
||||||
|
|
||||||
|
Graphics::PixelFormat getByteOrderRgbPixelFormat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace Image
|
} // End of namespace Image
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue