From 0d5d04ca3a5473f24f45112bb40a009679024acc Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Tue, 22 Jan 2019 15:02:05 +0100 Subject: IMAGE: Allow setting the output pixel format to the JPEG decoder --- image/codecs/mjpeg.cpp | 6 ++- image/jpeg.cpp | 114 +++++++++++++++++++++++++++++++++---------------- image/jpeg.h | 16 +++++-- 3 files changed, 96 insertions(+), 40 deletions(-) (limited to 'image') diff --git a/image/codecs/mjpeg.cpp b/image/codecs/mjpeg.cpp index 6e7faf1045..c5f815342b 100644 --- a/image/codecs/mjpeg.cpp +++ b/image/codecs/mjpeg.cpp @@ -200,6 +200,7 @@ const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &s Common::MemoryReadStream convertedStream(data, outputSize, DisposeAfterUse::YES); JPEGDecoder jpeg; + jpeg.setOutputPixelFormat(_pixelFormat); if (!jpeg.loadStream(convertedStream)) { warning("Failed to decode MJPEG frame"); @@ -211,7 +212,10 @@ const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &s delete _surface; } - _surface = jpeg.getSurface()->convertTo(_pixelFormat); + _surface = new Graphics::Surface(); + _surface->copyFrom(*jpeg.getSurface()); + + assert(_surface->format == _pixelFormat); return _surface; } diff --git a/image/jpeg.cpp b/image/jpeg.cpp index 1ce45f2539..5cc348fdfe 100644 --- a/image/jpeg.cpp +++ b/image/jpeg.cpp @@ -30,6 +30,7 @@ #include "common/endian.h" #include "common/stream.h" #include "common/textconsole.h" +#include "common/util.h" #include "graphics/pixelformat.h" #ifdef USE_JPEG @@ -44,13 +45,24 @@ extern "C" { namespace Image { -JPEGDecoder::JPEGDecoder() : _surface(), _colorSpace(kColorSpaceRGBA) { +JPEGDecoder::JPEGDecoder() : + _surface(), + _colorSpace(kColorSpaceRGB), + _requestedPixelFormat(getByteOrderRgbPixelFormat()) { } JPEGDecoder::~JPEGDecoder() { 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 { return &_surface; } @@ -171,6 +183,44 @@ void outputMessage(j_common_ptr cinfo) { 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 #endif @@ -198,10 +248,19 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) { // We can request YUV output because Groovie requires it switch (_colorSpace) { - case kColorSpaceRGBA: - cinfo.out_color_space = JCS_RGB; - break; + case kColorSpaceRGB: { + J_COLOR_SPACE colorSpace = fromScummvmPixelFormat(_requestedPixelFormat); + + 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: cinfo.out_color_space = JCS_YCbCr; break; @@ -212,11 +271,16 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) { // 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)); + case kColorSpaceRGB: { + Graphics::PixelFormat outputPixelFormat; + if (cinfo.out_color_space == JCS_RGB) { + outputPixelFormat = getByteOrderRgbPixelFormat(); + } else { + outputPixelFormat = _requestedPixelFormat; + } + _surface.create(cinfo.output_width, cinfo.output_height, outputPixelFormat); break; - + } case kColorSpaceYUV: // We use YUV with 3 bytes per pixel otherwise. // 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 - assert(cinfo.output_components == 3); - JDIMENSION pitch = cinfo.output_width * cinfo.output_components; + JDIMENSION pitch = cinfo.output_width * _surface.format.bytesPerPixel; assert(_surface.pitch >= pitch); 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); - 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; - } + memcpy(dst, buffer[0], pitch); } // We are done with decompressing, thus free all the data jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); + if (_colorSpace == kColorSpaceRGB && _surface.format != _requestedPixelFormat) { + _surface.convertToInPlace(_requestedPixelFormat); // Slow path + } + return true; #else return false; diff --git a/image/jpeg.h b/image/jpeg.h index ac0d22d129..08b5c01dc4 100644 --- a/image/jpeg.h +++ b/image/jpeg.h @@ -60,11 +60,11 @@ public: // Special API for JPEG enum ColorSpace { /** - * Output 32bit RGBA data. + * Output RGB data in the pixel format specified using `setOutputPixelFormat`. * * This is the default output. */ - kColorSpaceRGBA, + kColorSpaceRGB, /** * Output (interleaved) YUV data. @@ -86,15 +86,25 @@ public: * 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. + * The decoder itself defaults to RGB. * * @param outSpace The color space to output. */ 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: Graphics::Surface _surface; ColorSpace _colorSpace; + Graphics::PixelFormat _requestedPixelFormat; + + Graphics::PixelFormat getByteOrderRgbPixelFormat() const; }; } // End of namespace Image -- cgit v1.2.3