diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/decoders/bmp.cpp | 159 | ||||
-rw-r--r-- | graphics/decoders/bmp.h (renamed from graphics/imagedec.h) | 43 | ||||
-rw-r--r-- | graphics/decoders/image_decoder.h | 85 | ||||
-rw-r--r-- | graphics/decoders/jpeg.cpp (renamed from graphics/jpeg.cpp) | 114 | ||||
-rw-r--r-- | graphics/decoders/jpeg.h (renamed from graphics/jpeg.h) | 24 | ||||
-rw-r--r-- | graphics/decoders/pict.cpp (renamed from graphics/pict.cpp) | 407 | ||||
-rw-r--r-- | graphics/decoders/pict.h (renamed from graphics/pict.h) | 32 | ||||
-rw-r--r-- | graphics/decoders/png.cpp (renamed from graphics/png.cpp) | 292 | ||||
-rw-r--r-- | graphics/decoders/png.h (renamed from graphics/png.h) | 113 | ||||
-rw-r--r-- | graphics/imagedec.cpp | 176 | ||||
-rw-r--r-- | graphics/module.mk | 10 | ||||
-rw-r--r-- | graphics/surface.cpp | 79 | ||||
-rw-r--r-- | graphics/surface.h | 11 | ||||
-rw-r--r-- | graphics/yuv_to_rgb.cpp | 46 | ||||
-rw-r--r-- | graphics/yuv_to_rgb.h | 15 |
15 files changed, 881 insertions, 725 deletions
diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp new file mode 100644 index 0000000000..0d44881d7c --- /dev/null +++ b/graphics/decoders/bmp.cpp @@ -0,0 +1,159 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "common/stream.h" +#include "common/textconsole.h" + +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "graphics/decoders/bmp.h" + +namespace Graphics { + +BitmapDecoder::BitmapDecoder() { + _surface = 0; + _palette = 0; +} + +BitmapDecoder::~BitmapDecoder() { + destroy(); +} + +void BitmapDecoder::destroy() { + if (_surface) { + _surface->free(); + delete _surface; _surface = 0; + } + + delete[] _palette; _palette = 0; +} + +bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + + if (stream.readByte() != 'B') + return false; + + if (stream.readByte() != 'M') + return false; + + /* uint32 fileSize = */ stream.readUint32LE(); + /* uint16 res1 = */ stream.readUint16LE(); + /* uint16 res2 = */ stream.readUint16LE(); + uint32 imageOffset = stream.readUint32LE(); + + uint32 infoSize = stream.readUint32LE(); + if (infoSize != 40) { + warning("Only Windows v3 bitmaps are supported"); + return false; + } + + uint32 width = stream.readUint32LE(); + int32 height = stream.readSint32LE(); + + if (width == 0 || height == 0) + return false; + + if (height < 0) { + warning("Right-side up bitmaps not supported"); + return false; + } + + /* uint16 planes = */ stream.readUint16LE(); + uint16 bitsPerPixel = stream.readUint16LE(); + + if (bitsPerPixel != 8 && bitsPerPixel != 24) { + warning("%dbpp bitmaps not supported", bitsPerPixel); + return false; + } + + uint32 compression = stream.readUint32LE(); + + if (compression != 0) { + warning("Compressed bitmaps not supported"); + return false; + } + + /* uint32 imageSize = */ stream.readUint32LE(); + /* uint32 pixelsPerMeterX = */ stream.readUint32LE(); + /* uint32 pixelsPerMeterY = */ stream.readUint32LE(); + uint32 colorsUsed = stream.readUint32LE(); + /* uint32 colorsImportant = */ stream.readUint32LE(); + + if (colorsUsed == 0) + colorsUsed = 256; + + if (bitsPerPixel == 8) { + // Read the palette + _palette = new byte[colorsUsed * 3]; + for (uint16 i = 0; i < colorsUsed; i++) { + _palette[i * 3 + 2] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 0] = stream.readByte(); + stream.readByte(); + } + } + + // Start us at the beginning of the image + stream.seek(imageOffset); + + Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + + // BGRA for 24bpp + if (bitsPerPixel == 24) + format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); + + _surface = new Graphics::Surface(); + _surface->create(width, height, format); + + int srcPitch = width * (bitsPerPixel >> 3); + const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0; + + if (bitsPerPixel == 8) { + byte *dst = (byte *)_surface->pixels; + + for (int32 i = 0; i < height; i++) { + stream.read(dst + (height - i - 1) * width, width); + stream.skip(extraDataLength); + } + } else { + byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch; + + for (int32 i = 0; i < height; i++) { + for (uint32 j = 0; j < width; j++) { + byte b = stream.readByte(); + byte g = stream.readByte(); + byte r = stream.readByte(); + uint32 color = format.RGBToColor(r, g, b); + + *((uint32 *)dst) = color; + dst += format.bytesPerPixel; + } + + stream.skip(extraDataLength); + dst -= _surface->pitch * 2; + } + } + + return true; +} + +} // End of namespace Graphics diff --git a/graphics/imagedec.h b/graphics/decoders/bmp.h index b03b8bc36b..e11b12fad6 100644 --- a/graphics/imagedec.h +++ b/graphics/decoders/bmp.h @@ -19,11 +19,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef GRAPHICS_IMAGEDEC_H -#define GRAPHICS_IMAGEDEC_H +#ifndef GRAPHICS_DECODERS_BMP_H +#define GRAPHICS_DECODERS_BMP_H #include "common/scummsys.h" #include "common/str.h" +#include "graphics/decoders/image_decoder.h" namespace Common{ class SeekableReadStream; @@ -34,32 +35,22 @@ namespace Graphics { struct PixelFormat; struct Surface; -class ImageDecoder { +class BitmapDecoder : public ImageDecoder { public: - virtual ~ImageDecoder() {} - - static Surface *loadFile(const Common::String &name, const PixelFormat &format); - static Surface *loadFile(Common::SeekableReadStream &stream, const PixelFormat &format); - - /** - * checks if the data can be decoded by this decoder - * - * @param stream memory read stream - * @return true if it can be decoded, otherwise false - */ - virtual bool decodeable(Common::SeekableReadStream &stream) = 0; - - /** - * decodes the data and returns an pointer to the resulting surface. - * Surface::free() must be called by the user also it must be deleted - * with delete; - * - * @param stream the memory stream which should be decoded - * @param format the pixel format used to generate the surface - * @return returns a new surface if the image could be decoded, otherwise 0 - */ - virtual Surface *decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format) = 0; + BitmapDecoder(); + virtual ~BitmapDecoder(); + + // ImageDecoder API + void destroy(); + virtual bool loadStream(Common::SeekableReadStream &stream); + virtual const Surface *getSurface() const { return _surface; } + virtual const byte *getPalette() { return _palette; } + +private: + Surface *_surface; + byte *_palette; }; + } // End of namespace Graphics #endif diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h new file mode 100644 index 0000000000..e768f7f9a2 --- /dev/null +++ b/graphics/decoders/image_decoder.h @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GRAPHICS_DECODERS_IMAGEDECODER_H +#define GRAPHICS_DECODERS_IMAGEDECODER_H + +#include "common/scummsys.h" +#include "common/str.h" + +namespace Common{ +class SeekableReadStream; +} + +namespace Graphics { + +struct PixelFormat; +struct Surface; + +/** + * A representation of an image decoder that maintains ownership of the surface + * and palette it decodes to. + */ +class ImageDecoder { +public: + virtual ~ImageDecoder() {} + + /** + * Load an image from the specified stream + * + * @param stream the input stream + * @return whether loading the file succeeded + * @see getSurface + * @see getPalette + */ + virtual bool loadStream(Common::SeekableReadStream &stream) = 0; + + /** + * Destroy this decoder's surface and palette + */ + virtual void destroy() = 0; + + /** + * Get the decoded surface + * + * This surface is owned by this ImageDecoder and will remain valid + * until destroy() or loadStream() is called, or until this ImageDecoder's + * destructor is called. + * + * @return the decoded surface, or 0 if no surface is present + */ + virtual const Surface *getSurface() const = 0; + + /** + * Get the decoded palette + * + * This palette is owned by this ImageDecoder and will remain valid + * until destroy() or loadStream() is called, or until this ImageDecoder's + * destructor is called. + * + * @return the decoded palette, or 0 if no palette is present + */ + virtual const byte *getPalette() const { return 0; } +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/jpeg.cpp b/graphics/decoders/jpeg.cpp index 53e693a045..a871377ca1 100644 --- a/graphics/jpeg.cpp +++ b/graphics/decoders/jpeg.cpp @@ -20,9 +20,9 @@ * */ -#include "graphics/conversion.h" -#include "graphics/jpeg.h" #include "graphics/pixelformat.h" +#include "graphics/yuv_to_rgb.h" +#include "graphics/decoders/jpeg.h" #include "common/debug.h" #include "common/endian.h" @@ -43,9 +43,9 @@ static const uint8 _zigZagOrder[64] = { 53, 60, 61, 54, 47, 55, 62, 63 }; -JPEG::JPEG() : +JPEGDecoder::JPEGDecoder() : ImageDecoder(), _stream(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0), - _scanComp(NULL), _currentComp(NULL) { + _scanComp(NULL), _currentComp(NULL), _rgbSurface(0) { // Initialize the quantization tables for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) @@ -60,42 +60,33 @@ JPEG::JPEG() : } } -JPEG::~JPEG() { - reset(); +JPEGDecoder::~JPEGDecoder() { + destroy(); } -Surface *JPEG::getSurface(const PixelFormat &format) { +const Surface *JPEGDecoder::getSurface() const { // Make sure we have loaded data if (!isLoaded()) return 0; - // Only accept >8bpp surfaces - if (format.bytesPerPixel == 1) - return 0; + if (_rgbSurface) + return _rgbSurface; + + // Create an RGBA8888 surface + _rgbSurface = new Graphics::Surface(); + _rgbSurface->create(_w, _h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // Get our component surfaces - Graphics::Surface *yComponent = getComponent(1); - Graphics::Surface *uComponent = getComponent(2); - Graphics::Surface *vComponent = getComponent(3); - - Graphics::Surface *output = new Graphics::Surface(); - output->create(yComponent->w, yComponent->h, format); - - for (uint16 i = 0; i < output->h; i++) { - for (uint16 j = 0; j < output->w; j++) { - byte r = 0, g = 0, b = 0; - YUV2RGB(*((byte *)yComponent->getBasePtr(j, i)), *((byte *)uComponent->getBasePtr(j, i)), *((byte *)vComponent->getBasePtr(j, i)), r, g, b); - if (format.bytesPerPixel == 2) - *((uint16 *)output->getBasePtr(j, i)) = format.RGBToColor(r, g, b); - else - *((uint32 *)output->getBasePtr(j, i)) = format.RGBToColor(r, g, b); - } - } + const Graphics::Surface *yComponent = getComponent(1); + const Graphics::Surface *uComponent = getComponent(2); + const Graphics::Surface *vComponent = getComponent(3); + + convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch); - return output; + return _rgbSurface; } -void JPEG::reset() { +void JPEGDecoder::destroy() { // Reset member variables _stream = NULL; _w = _h = 0; @@ -125,14 +116,19 @@ void JPEG::reset() { delete[] _huff[i].sizes; _huff[i].sizes = NULL; delete[] _huff[i].codes; _huff[i].codes = NULL; } + + if (_rgbSurface) { + _rgbSurface->free(); + delete _rgbSurface; + } } -bool JPEG::read(Common::SeekableReadStream *stream) { +bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) { // Reset member variables and tables from previous reads - reset(); + destroy(); // Save the input stream - _stream = stream; + _stream = &stream; bool ok = true; bool done = false; @@ -211,41 +207,41 @@ bool JPEG::read(Common::SeekableReadStream *stream) { } } } + + _stream = 0; return ok; } -bool JPEG::readJFIF() { +bool JPEGDecoder::readJFIF() { uint16 length = _stream->readUint16BE(); uint32 tag = _stream->readUint32BE(); if (tag != MKTAG('J', 'F', 'I', 'F')) { - warning("JPEG::readJFIF() tag mismatch"); + warning("JPEGDecoder::readJFIF() tag mismatch"); return false; } if (_stream->readByte() != 0) { // NULL - warning("JPEG::readJFIF() NULL mismatch"); + warning("JPEGDecoder::readJFIF() NULL mismatch"); return false; } byte majorVersion = _stream->readByte(); byte minorVersion = _stream->readByte(); - - if (majorVersion != 1 || (minorVersion != 1 && minorVersion != 2)) - warning("JPEG::readJFIF() Non-v1.1/1.2 JPEGs may not be handled correctly"); - - /* byte densityUnits = */ _stream->readByte(); - /* uint16 xDensity = */ _stream->readUint16BE(); - /* uint16 yDensity = */ _stream->readUint16BE(); + if (majorVersion != 1 || minorVersion != 1) + warning("JPEGDecoder::readJFIF() Non-v1.1 JPEGs may not be handled correctly"); + /* 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("JPEG::readJFIF() length mismatch"); + warning("JPEGDecoder::readJFIF() length mismatch"); return false; } return true; } // Marker 0xC0 (Start Of Frame, Baseline DCT) -bool JPEG::readSOF0() { +bool JPEGDecoder::readSOF0() { debug(5, "JPEG: readSOF0"); uint16 size = _stream->readUint16BE(); @@ -284,7 +280,7 @@ bool JPEG::readSOF0() { } // Marker 0xC4 (Define Huffman Tables) -bool JPEG::readDHT() { +bool JPEGDecoder::readDHT() { debug(5, "JPEG: readDHT"); uint16 size = _stream->readUint16BE() - 2; uint32 pos = _stream->pos(); @@ -346,7 +342,7 @@ bool JPEG::readDHT() { } // Marker 0xDA (Start Of Scan) -bool JPEG::readSOS() { +bool JPEGDecoder::readSOS() { debug(5, "JPEG: readSOS"); uint16 size = _stream->readUint16BE(); @@ -473,7 +469,7 @@ bool JPEG::readSOS() { } // Marker 0xDB (Define Quantization Tables) -bool JPEG::readDQT() { +bool JPEGDecoder::readDQT() { debug(5, "JPEG: readDQT"); uint16 size = _stream->readUint16BE() - 2; uint32 pos = _stream->pos(); @@ -503,7 +499,7 @@ bool JPEG::readDQT() { } // Marker 0xDD (Define Restart Interval) -bool JPEG::readDRI() { +bool JPEGDecoder::readDRI() { debug(5, "JPEG: readDRI"); uint16 size = _stream->readUint16BE() - 2; @@ -517,7 +513,7 @@ bool JPEG::readDRI() { return true; } -bool JPEG::readMCU(uint16 xMCU, uint16 yMCU) { +bool JPEGDecoder::readMCU(uint16 xMCU, uint16 yMCU) { bool ok = true; for (int c = 0; ok && (c < _numComp); c++) { // Set the current component @@ -549,7 +545,7 @@ bool JPEG::readMCU(uint16 xMCU, uint16 yMCU) { xb = (n - (k2 + k1) * p) >> sh; // IDCT based on public domain code from http://halicery.com/jpeg/idct.html -void JPEG::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) { +void JPEGDecoder::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) { int p, n; src[0] <<= 9; @@ -578,7 +574,7 @@ void JPEG::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) { dest[7 * 8] = (src[0] - src[1]) >> ps; } -void JPEG::idct2D8x8(int32 block[64]) { +void JPEGDecoder::idct2D8x8(int32 block[64]) { int32 tmp[64]; // Apply 1D IDCT to rows @@ -590,7 +586,7 @@ void JPEG::idct2D8x8(int32 block[64]) { idct1D8x8(&tmp[i * 8], &block[i], 12, 1 << 11); } -bool JPEG::readDataUnit(uint16 x, uint16 y) { +bool JPEGDecoder::readDataUnit(uint16 x, uint16 y) { // Prepare an empty data array int16 readData[64]; for (int i = 1; i < 64; i++) @@ -654,7 +650,7 @@ bool JPEG::readDataUnit(uint16 x, uint16 y) { return true; } -int16 JPEG::readDC() { +int16 JPEGDecoder::readDC() { // DC is type 0 uint8 tableNum = _currentComp->DCentropyTableSelector << 1; @@ -665,7 +661,7 @@ int16 JPEG::readDC() { return readSignedBits(numBits); } -void JPEG::readAC(int16 *out) { +void JPEGDecoder::readAC(int16 *out) { // AC is type 1 uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1; @@ -695,7 +691,7 @@ void JPEG::readAC(int16 *out) { } } -int16 JPEG::readSignedBits(uint8 numBits) { +int16 JPEGDecoder::readSignedBits(uint8 numBits) { uint16 ret = 0; if (numBits > 16) error("requested %d bits", numBits); //XXX @@ -713,7 +709,7 @@ int16 JPEG::readSignedBits(uint8 numBits) { } // TODO: optimize? -uint8 JPEG::readHuff(uint8 table) { +uint8 JPEGDecoder::readHuff(uint8 table) { bool foundCode = false; uint8 val = 0; @@ -743,7 +739,7 @@ uint8 JPEG::readHuff(uint8 table) { return val; } -uint8 JPEG::readBit() { +uint8 JPEGDecoder::readBit() { // Read a whole byte if necessary if (_bitsNumber == 0) { _bitsData = _stream->readByte(); @@ -773,12 +769,12 @@ uint8 JPEG::readBit() { return (_bitsData & (1 << _bitsNumber)) ? 1 : 0; } -Surface *JPEG::getComponent(uint c) { +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("JPEG::getComponent: No component %d present", c); + error("JPEGDecoder::getComponent: No component %d present", c); return NULL; } diff --git a/graphics/jpeg.h b/graphics/decoders/jpeg.h index b87791470f..c566d5ad21 100644 --- a/graphics/jpeg.h +++ b/graphics/decoders/jpeg.h @@ -24,6 +24,7 @@ #define GRAPHICS_JPEG_H #include "graphics/surface.h" +#include "graphics/decoders/image_decoder.h" namespace Common { class SeekableReadStream; @@ -36,26 +37,31 @@ struct PixelFormat; #define JPEG_MAX_QUANT_TABLES 4 #define JPEG_MAX_HUFF_TABLES 2 -class JPEG { +class JPEGDecoder : public ImageDecoder { public: - JPEG(); - ~JPEG(); + JPEGDecoder(); + ~JPEGDecoder(); + + // ImageDecoder API + void destroy(); + bool loadStream(Common::SeekableReadStream &str); + const Surface *getSurface() const; - bool read(Common::SeekableReadStream *str); bool isLoaded() const { return _numComp && _w && _h; } uint16 getWidth() const { return _w; } uint16 getHeight() const { return _h; } - - Surface *getComponent(uint c); - Surface *getSurface(const PixelFormat &format); + const Surface *getComponent(uint c) const; private: - void reset(); - Common::SeekableReadStream *_stream; uint16 _w, _h; uint16 _restartInterval; + // 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; + // Image components uint8 _numComp; struct Component { diff --git a/graphics/pict.cpp b/graphics/decoders/pict.cpp index 872f2f224a..f8b2553ea0 100644 --- a/graphics/pict.cpp +++ b/graphics/decoders/pict.cpp @@ -26,9 +26,9 @@ #include "common/substream.h" #include "common/textconsole.h" -#include "graphics/jpeg.h" -#include "graphics/pict.h" #include "graphics/surface.h" +#include "graphics/decoders/jpeg.h" +#include "graphics/decoders/pict.h" namespace Graphics { @@ -36,18 +36,25 @@ namespace Graphics { // http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-461.html // http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-269.html -PictDecoder::PictDecoder(PixelFormat pixelFormat) { - _jpeg = new JPEG(); - _pixelFormat = pixelFormat; +PICTDecoder::PICTDecoder() { + _outputSurface = 0; +} + +PICTDecoder::~PICTDecoder() { + destroy(); } -PictDecoder::~PictDecoder() { - delete _jpeg; +void PICTDecoder::destroy() { + if (_outputSurface) { + _outputSurface->free(); + delete _outputSurface; + _outputSurface = 0; + } } -#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PictDecoder::b, c)) +#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PICTDecoder::b, c)) -void PictDecoder::setupOpcodesCommon() { +void PICTDecoder::setupOpcodesCommon() { OPCODE(0x0000, o_nop, "NOP"); OPCODE(0x0001, o_clip, "Clip"); OPCODE(0x0003, o_txFont, "TxFont"); @@ -63,14 +70,14 @@ void PictDecoder::setupOpcodesCommon() { OPCODE(0x0C00, o_headerOp, "HeaderOp"); } -void PictDecoder::setupOpcodesNormal() { +void PICTDecoder::setupOpcodesNormal() { setupOpcodesCommon(); OPCODE(0x0098, on_packBitsRect, "PackBitsRect"); OPCODE(0x009A, on_directBitsRect, "DirectBitsRect"); OPCODE(0x8200, on_compressedQuickTime, "CompressedQuickTime"); } -void PictDecoder::setupOpcodesQuickTime() { +void PICTDecoder::setupOpcodesQuickTime() { setupOpcodesCommon(); OPCODE(0x0098, oq_packBitsRect, "PackBitsRect"); OPCODE(0x009A, oq_directBitsRect, "DirectBitsRect"); @@ -79,93 +86,93 @@ void PictDecoder::setupOpcodesQuickTime() { #undef OPCODE -void PictDecoder::o_nop(Common::SeekableReadStream *) { +void PICTDecoder::o_nop(Common::SeekableReadStream &) { // Nothing to do } -void PictDecoder::o_clip(Common::SeekableReadStream *stream) { +void PICTDecoder::o_clip(Common::SeekableReadStream &stream) { // Ignore - stream->skip(stream->readUint16BE() - 2); + stream.skip(stream.readUint16BE() - 2); } -void PictDecoder::o_txFont(Common::SeekableReadStream *stream) { +void PICTDecoder::o_txFont(Common::SeekableReadStream &stream) { // Ignore - stream->readUint16BE(); + stream.readUint16BE(); } -void PictDecoder::o_txFace(Common::SeekableReadStream *stream) { +void PICTDecoder::o_txFace(Common::SeekableReadStream &stream) { // Ignore - stream->readByte(); + stream.readByte(); } -void PictDecoder::o_pnSize(Common::SeekableReadStream *stream) { +void PICTDecoder::o_pnSize(Common::SeekableReadStream &stream) { // Ignore - stream->readUint16BE(); - stream->readUint16BE(); + stream.readUint16BE(); + stream.readUint16BE(); } -void PictDecoder::o_txSize(Common::SeekableReadStream *stream) { +void PICTDecoder::o_txSize(Common::SeekableReadStream &stream) { // Ignore - stream->readUint16BE(); + stream.readUint16BE(); } -void PictDecoder::o_txRatio(Common::SeekableReadStream *stream) { +void PICTDecoder::o_txRatio(Common::SeekableReadStream &stream) { // Ignore - stream->readUint16BE(); - stream->readUint16BE(); - stream->readUint16BE(); - stream->readUint16BE(); + stream.readUint16BE(); + stream.readUint16BE(); + stream.readUint16BE(); + stream.readUint16BE(); } -void PictDecoder::o_versionOp(Common::SeekableReadStream *stream) { +void PICTDecoder::o_versionOp(Common::SeekableReadStream &stream) { // We only support v2 extended - if (stream->readUint16BE() != 0x02FF) + if (stream.readUint16BE() != 0x02FF) error("Unknown PICT version"); } -void PictDecoder::o_longText(Common::SeekableReadStream *stream) { +void PICTDecoder::o_longText(Common::SeekableReadStream &stream) { // Ignore - stream->readUint16BE(); - stream->readUint16BE(); - stream->skip(stream->readByte()); + stream.readUint16BE(); + stream.readUint16BE(); + stream.skip(stream.readByte()); } -void PictDecoder::o_longComment(Common::SeekableReadStream *stream) { +void PICTDecoder::o_longComment(Common::SeekableReadStream &stream) { // Ignore - stream->readUint16BE(); - stream->skip(stream->readUint16BE()); + stream.readUint16BE(); + stream.skip(stream.readUint16BE()); } -void PictDecoder::o_opEndPic(Common::SeekableReadStream *stream) { +void PICTDecoder::o_opEndPic(Common::SeekableReadStream &stream) { // We've reached the end of the picture _continueParsing = false; } -void PictDecoder::o_headerOp(Common::SeekableReadStream *stream) { +void PICTDecoder::o_headerOp(Common::SeekableReadStream &stream) { // Read the basic header, but we don't really have to do anything with it - /* uint16 version = */ stream->readUint16BE(); - stream->readUint16BE(); // Reserved - /* uint32 hRes = */ stream->readUint32BE(); - /* uint32 vRes = */ stream->readUint32BE(); + /* uint16 version = */ stream.readUint16BE(); + stream.readUint16BE(); // Reserved + /* uint32 hRes = */ stream.readUint32BE(); + /* uint32 vRes = */ stream.readUint32BE(); Common::Rect origResRect; - origResRect.top = stream->readUint16BE(); - origResRect.left = stream->readUint16BE(); - origResRect.bottom = stream->readUint16BE(); - origResRect.right = stream->readUint16BE(); - stream->readUint32BE(); // Reserved + origResRect.top = stream.readUint16BE(); + origResRect.left = stream.readUint16BE(); + origResRect.bottom = stream.readUint16BE(); + origResRect.right = stream.readUint16BE(); + stream.readUint32BE(); // Reserved } -void PictDecoder::on_packBitsRect(Common::SeekableReadStream *stream) { +void PICTDecoder::on_packBitsRect(Common::SeekableReadStream &stream) { // Unpack data (8bpp or lower) unpackBitsRect(stream, true); } -void PictDecoder::on_directBitsRect(Common::SeekableReadStream *stream) { +void PICTDecoder::on_directBitsRect(Common::SeekableReadStream &stream) { // Unpack data (16bpp or higher) unpackBitsRect(stream, false); } -void PictDecoder::on_compressedQuickTime(Common::SeekableReadStream *stream) { +void PICTDecoder::on_compressedQuickTime(Common::SeekableReadStream &stream) { // OK, here's the fun. We get to completely change how QuickDraw draws // the data in PICT files. @@ -173,63 +180,57 @@ void PictDecoder::on_compressedQuickTime(Common::SeekableReadStream *stream) { _opcodes.clear(); setupOpcodesQuickTime(); - // We set up the surface for JPEG here too - if (!_outputSurface) - _outputSurface = new Graphics::Surface(); - _outputSurface->create(_imageRect.width(), _imageRect.height(), _pixelFormat); - // We'll decode the first QuickTime data from here, but the QuickTime-specific // opcodes will take over from here on out. Normal opcodes, signing off. decodeCompressedQuickTime(stream); } -void PictDecoder::oq_packBitsRect(Common::SeekableReadStream *stream) { +void PICTDecoder::oq_packBitsRect(Common::SeekableReadStream &stream) { // Skip any data here (8bpp or lower) skipBitsRect(stream, true); } -void PictDecoder::oq_directBitsRect(Common::SeekableReadStream *stream) { +void PICTDecoder::oq_directBitsRect(Common::SeekableReadStream &stream) { // Skip any data here (16bpp or higher) skipBitsRect(stream, false); } -void PictDecoder::oq_compressedQuickTime(Common::SeekableReadStream *stream) { +void PICTDecoder::oq_compressedQuickTime(Common::SeekableReadStream &stream) { // Just pass the data along decodeCompressedQuickTime(stream); } -Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *palette) { - assert(stream); +bool PICTDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); // Initialize opcodes to their normal state _opcodes.clear(); setupOpcodesNormal(); - _outputSurface = 0; _continueParsing = true; memset(_palette, 0, sizeof(_palette)); - uint16 fileSize = stream->readUint16BE(); + uint16 fileSize = stream.readUint16BE(); // If we have no file size here, we probably have a PICT from a file // and not a resource. The other two bytes are the fileSize which we // don't actually need (and already read if from a resource). if (!fileSize) - stream->seek(512 + 2); + stream.seek(512 + 2); - _imageRect.top = stream->readUint16BE(); - _imageRect.left = stream->readUint16BE(); - _imageRect.bottom = stream->readUint16BE(); - _imageRect.right = stream->readUint16BE(); + _imageRect.top = stream.readUint16BE(); + _imageRect.left = stream.readUint16BE(); + _imageRect.bottom = stream.readUint16BE(); + _imageRect.right = stream.readUint16BE(); _imageRect.debugPrint(0, "PICT Rect:"); // NOTE: This is only a subset of the full PICT format. // - Only V2 (Extended) Images Supported // - CompressedQuickTime (JPEG) compressed data is supported // - DirectBitsRect/PackBitsRect compressed data is supported - for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size() && _continueParsing; opNum++) { + for (uint32 opNum = 0; !stream.eos() && !stream.err() && stream.pos() < stream.size() && _continueParsing; opNum++) { // PICT v2 opcodes are two bytes - uint16 opcode = stream->readUint16BE(); + uint16 opcode = stream.readUint16BE(); if (opNum == 0 && opcode != 0x0011) error("Cannot find PICT version opcode"); @@ -238,7 +239,7 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale // Since opcodes are word-aligned, we need to mark our starting // position here. - uint32 startPos = stream->pos(); + uint32 startPos = stream.pos(); for (uint32 i = 0; i < _opcodes.size(); i++) { if (_opcodes[i].op == opcode) { @@ -252,76 +253,70 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale } // Align - stream->skip((stream->pos() - startPos) & 1); + stream.skip((stream.pos() - startPos) & 1); } - // If we got a palette throughout this nonsense, go and grab it - if (palette) - memcpy(palette, _palette, 256 * 3); - return _outputSurface; } -PictDecoder::PixMap PictDecoder::readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr) { +PICTDecoder::PixMap PICTDecoder::readPixMap(Common::SeekableReadStream &stream, bool hasBaseAddr) { PixMap pixMap; - pixMap.baseAddr = hasBaseAddr ? stream->readUint32BE() : 0; - pixMap.rowBytes = stream->readUint16BE() & 0x3fff; - pixMap.bounds.top = stream->readUint16BE(); - pixMap.bounds.left = stream->readUint16BE(); - pixMap.bounds.bottom = stream->readUint16BE(); - pixMap.bounds.right = stream->readUint16BE(); - pixMap.pmVersion = stream->readUint16BE(); - pixMap.packType = stream->readUint16BE(); - pixMap.packSize = stream->readUint32BE(); - pixMap.hRes = stream->readUint32BE(); - pixMap.vRes = stream->readUint32BE(); - pixMap.pixelType = stream->readUint16BE(); - pixMap.pixelSize = stream->readUint16BE(); - pixMap.cmpCount = stream->readUint16BE(); - pixMap.cmpSize = stream->readUint16BE(); - pixMap.planeBytes = stream->readUint32BE(); - pixMap.pmTable = stream->readUint32BE(); - pixMap.pmReserved = stream->readUint32BE(); + pixMap.baseAddr = hasBaseAddr ? stream.readUint32BE() : 0; + pixMap.rowBytes = stream.readUint16BE() & 0x3fff; + pixMap.bounds.top = stream.readUint16BE(); + pixMap.bounds.left = stream.readUint16BE(); + pixMap.bounds.bottom = stream.readUint16BE(); + pixMap.bounds.right = stream.readUint16BE(); + pixMap.pmVersion = stream.readUint16BE(); + pixMap.packType = stream.readUint16BE(); + pixMap.packSize = stream.readUint32BE(); + pixMap.hRes = stream.readUint32BE(); + pixMap.vRes = stream.readUint32BE(); + pixMap.pixelType = stream.readUint16BE(); + pixMap.pixelSize = stream.readUint16BE(); + pixMap.cmpCount = stream.readUint16BE(); + pixMap.cmpSize = stream.readUint16BE(); + pixMap.planeBytes = stream.readUint32BE(); + pixMap.pmTable = stream.readUint32BE(); + pixMap.pmReserved = stream.readUint32BE(); return pixMap; } struct PackBitsRectData { - PictDecoder::PixMap pixMap; + PICTDecoder::PixMap pixMap; Common::Rect srcRect; Common::Rect dstRect; uint16 mode; }; -void PictDecoder::unpackBitsRect(Common::SeekableReadStream *stream, bool hasPalette) { - static const PixelFormat directBitsFormat16 = PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - +void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette) { PackBitsRectData packBitsData; packBitsData.pixMap = readPixMap(stream, !hasPalette); // Read in the palette if there is one present if (hasPalette) { // See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html - stream->readUint32BE(); // seed - stream->readUint16BE(); // flags - uint16 colorCount = stream->readUint16BE() + 1; + stream.readUint32BE(); // seed + stream.readUint16BE(); // flags + uint16 colorCount = stream.readUint16BE() + 1; for (uint32 i = 0; i < colorCount; i++) { - stream->readUint16BE(); - _palette[i * 3] = stream->readUint16BE() >> 8; - _palette[i * 3 + 1] = stream->readUint16BE() >> 8; - _palette[i * 3 + 2] = stream->readUint16BE() >> 8; + stream.readUint16BE(); + _palette[i * 3] = stream.readUint16BE() >> 8; + _palette[i * 3 + 1] = stream.readUint16BE() >> 8; + _palette[i * 3 + 2] = stream.readUint16BE() >> 8; } } - packBitsData.srcRect.top = stream->readUint16BE(); - packBitsData.srcRect.left = stream->readUint16BE(); - packBitsData.srcRect.bottom = stream->readUint16BE(); - packBitsData.srcRect.right = stream->readUint16BE(); - packBitsData.dstRect.top = stream->readUint16BE(); - packBitsData.dstRect.left = stream->readUint16BE(); - packBitsData.dstRect.bottom = stream->readUint16BE(); - packBitsData.dstRect.right = stream->readUint16BE(); - packBitsData.mode = stream->readUint16BE(); + packBitsData.srcRect.top = stream.readUint16BE(); + packBitsData.srcRect.left = stream.readUint16BE(); + packBitsData.srcRect.bottom = stream.readUint16BE(); + packBitsData.srcRect.right = stream.readUint16BE(); + packBitsData.dstRect.top = stream.readUint16BE(); + packBitsData.dstRect.left = stream.readUint16BE(); + packBitsData.dstRect.bottom = stream.readUint16BE(); + packBitsData.dstRect.right = stream.readUint16BE(); + packBitsData.mode = stream.readUint16BE(); uint16 width = packBitsData.srcRect.width(); uint16 height = packBitsData.srcRect.height(); @@ -335,9 +330,6 @@ void PictDecoder::unpackBitsRect(Common::SeekableReadStream *stream, bool hasPal else bytesPerPixel = packBitsData.pixMap.pixelSize / 8; - _outputSurface = new Graphics::Surface(); - _outputSurface->create(width, height, (bytesPerPixel == 1) ? PixelFormat::createFormatCLUT8() : _pixelFormat); - // Ensure we have enough space in the buffer to hold an entire line's worth of pixels uint32 lineSize = MAX<int>(width * bytesPerPixel + (8 * 2 / packBitsData.pixMap.pixelSize), packBitsData.pixMap.rowBytes); byte *buffer = new byte[lineSize * height]; @@ -355,56 +347,48 @@ void PictDecoder::unpackBitsRect(Common::SeekableReadStream *stream, bool hasPal // TODO: Finish this. Hasn't been needed (yet). error("Unpacked DirectBitsRect data (not padded)"); } else if (packBitsData.pixMap.packType == 0 || packBitsData.pixMap.packType > 2) { // Packed - uint16 byteCount = (packBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte(); - unpackBitsLine(buffer + i * _outputSurface->w * bytesPerPixel, packBitsData.pixMap.rowBytes, stream->readStream(byteCount), packBitsData.pixMap.pixelSize, bytesPerPixel); + uint16 byteCount = (packBitsData.pixMap.rowBytes > 250) ? stream.readUint16BE() : stream.readByte(); + unpackBitsLine(buffer + i * width * bytesPerPixel, packBitsData.pixMap.rowBytes, stream.readStream(byteCount), packBitsData.pixMap.pixelSize, bytesPerPixel); } } + _outputSurface = new Graphics::Surface(); + switch (bytesPerPixel) { case 1: // Just copy to the image + _outputSurface->create(width, height, PixelFormat::createFormatCLUT8()); memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h); break; case 2: // Convert from 16-bit to whatever surface we need - for (uint16 y = 0; y < _outputSurface->h; y++) { - for (uint16 x = 0; x < _outputSurface->w; x++) { - byte r = 0, g = 0, b = 0; - uint32 color = READ_BE_UINT16(buffer + (y * _outputSurface->w + x) * bytesPerPixel); - directBitsFormat16.colorToRGB(color, r, g, b); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - } - } + _outputSurface->create(width, height, PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); + for (uint16 y = 0; y < _outputSurface->h; y++) + for (uint16 x = 0; x < _outputSurface->w; x++) + WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_BE_UINT16(buffer + (y * _outputSurface->w + x) * 2)); break; case 3: // Convert from 24-bit (planar!) to whatever surface we need + _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) { for (uint16 x = 0; x < _outputSurface->w; x++) { byte r = *(buffer + y * _outputSurface->w * 3 + x); byte g = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w + x); byte b = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 2 + x); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.RGBToColor(r, g, b); } } break; case 4: // Convert from 32-bit (planar!) to whatever surface we need + _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) { for (uint16 x = 0; x < _outputSurface->w; x++) { byte r = *(buffer + y * _outputSurface->w * 4 + x); byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.ARGBToColor(r, g, b, a); - else - *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.ARGBToColor(r, g, b, a); + *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(r, g, b, a); } } break; @@ -413,7 +397,7 @@ void PictDecoder::unpackBitsRect(Common::SeekableReadStream *stream, bool hasPal delete[] buffer; } -void PictDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) { +void PICTDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) { uint32 dataDecoded = 0; byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1; @@ -426,7 +410,7 @@ void PictDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadS for (uint32 i = 0; i < runSize; i++) { if (bytesPerDecode == 2) { - WRITE_BE_UINT16(out, value); + WRITE_UINT16(out, value); out += 2; } else { outputPixelBuffer(out, value, bitsPerPixel); @@ -434,12 +418,19 @@ void PictDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadS } dataDecoded += runSize * bytesPerDecode; } else { - uint32 runSize = (op + 1) * bytesPerDecode; - - for (uint32 i = 0; i < runSize; i++) - outputPixelBuffer(out, data->readByte(), bitsPerPixel); + uint32 runSize = op + 1; + + if (bytesPerDecode == 1) { + for (uint32 i = 0; i < runSize; i++) + outputPixelBuffer(out, data->readByte(), bitsPerPixel); + } else { + for (uint32 i = 0; i < runSize; i++) { + WRITE_UINT16(out, data->readUint16BE()); + out += 2; + } + } - dataDecoded += runSize; + dataDecoded += runSize * bytesPerDecode; } } @@ -453,33 +444,52 @@ void PictDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadS delete data; } -void PictDecoder::skipBitsRect(Common::SeekableReadStream *stream, bool hasPalette) { +void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) { + switch (bitsPerPixel) { + case 1: + for (int i = 7; i >= 0; i--) + *out++ = (value >> i) & 1; + break; + case 2: + for (int i = 6; i >= 0; i -= 2) + *out++ = (value >> i) & 3; + break; + case 4: + *out++ = (value >> 4) & 0xf; + *out++ = value & 0xf; + break; + default: + *out++ = value; + } +} + +void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette) { // Step through a PackBitsRect/DirectBitsRect function if (!hasPalette) - stream->readUint32BE(); + stream.readUint32BE(); - uint16 rowBytes = stream->readUint16BE(); - uint16 height = stream->readUint16BE(); - stream->readUint16BE(); - height = stream->readUint16BE() - height; - stream->readUint16BE(); + uint16 rowBytes = stream.readUint16BE(); + uint16 height = stream.readUint16BE(); + stream.readUint16BE(); + height = stream.readUint16BE() - height; + stream.readUint16BE(); uint16 packType; // Top two bits signify PixMap vs BitMap if (rowBytes & 0xC000) { // PixMap - stream->readUint16BE(); - packType = stream->readUint16BE(); - stream->skip(14); - stream->readUint16BE(); // pixelSize - stream->skip(16); + stream.readUint16BE(); + packType = stream.readUint16BE(); + stream.skip(14); + stream.readUint16BE(); // pixelSize + stream.skip(16); if (hasPalette) { - stream->readUint32BE(); - stream->readUint16BE(); - stream->skip((stream->readUint16BE() + 1) * 8); + stream.readUint32BE(); + stream.readUint16BE(); + stream.skip((stream.readUint16BE() + 1) * 8); } rowBytes &= 0x3FFF; @@ -488,85 +498,34 @@ void PictDecoder::skipBitsRect(Common::SeekableReadStream *stream, bool hasPalet packType = 0; } - stream->skip(18); + stream.skip(18); for (uint16 i = 0; i < height; i++) { if (packType == 1 || packType == 2 || rowBytes < 8) error("Unpacked PackBitsRect data"); else if (packType == 0 || packType > 2) - stream->skip((rowBytes > 250) ? stream->readUint16BE() : stream->readByte()); - } -} - -void PictDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) { - switch (bitsPerPixel) { - case 1: - for (int i = 7; i >= 0; i--) - *out++ = (value >> i) & 1; - break; - case 2: - for (int i = 6; i >= 0; i -= 2) - *out++ = (value >> i) & 3; - break; - case 4: - *out++ = (value >> 4) & 0xf; - *out++ = value & 0xf; - break; - default: - *out++ = value; + stream.skip((rowBytes > 250) ? stream.readUint16BE() : stream.readByte()); } } // Compressed QuickTime details can be found here: // http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html // http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html +void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) { + JPEGDecoder jpeg; -void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream) { - // First, read all the fields from the opcode - uint32 dataSize = stream->readUint32BE(); - uint32 startPos = stream->pos(); - - /* uint16 version = */ stream->readUint16BE(); - - // Read in the display matrix - uint32 matrix[3][3]; - for (uint32 i = 0; i < 3; i++) - for (uint32 j = 0; j < 3; j++) - matrix[i][j] = stream->readUint32BE(); - - // We currently only support offseting images vertically from the matrix - uint16 xOffset = 0; - uint16 yOffset = matrix[2][1] >> 16; + uint32 dataSize = stream.readUint32BE(); + uint32 startPos = stream.pos(); - uint32 matteSize = stream->readUint32BE(); - stream->skip(8); // matte rect - /* uint16 transferMode = */ stream->readUint16BE(); - stream->skip(8); // src rect - /* uint32 accuracy = */ stream->readUint32BE(); - uint32 maskSize = stream->readUint32BE(); + Common::SeekableSubReadStream jpegStream(&stream, stream.pos() + 156, stream.pos() + dataSize); - // Skip the matte and mask - stream->skip(matteSize + maskSize); - - // Now we've reached the image descriptor, so read the relevant data from that - uint32 idStart = stream->pos(); - uint32 idSize = stream->readUint32BE(); - stream->skip(40); // miscellaneous stuff - uint32 jpegSize = stream->readUint32BE(); - stream->skip(idSize - (stream->pos() - idStart)); // more useless stuff + if (!jpeg.loadStream(jpegStream)) + error("PICTDecoder::decodeCompressedQuickTime(): Could not decode JPEG data"); - Common::SeekableReadStream *jpegStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + jpegSize); - - if (!_jpeg->read(jpegStream)) - error("PictDecoder::decodeCompressedQuickTime(): Could not decode JPEG data"); - - Graphics::Surface *jpegSurface = _jpeg->getSurface(_pixelFormat); - - for (uint16 y = 0; y < jpegSurface->h; y++) - memcpy(_outputSurface->getBasePtr(0 + xOffset, y + yOffset), jpegSurface->getBasePtr(0, y), jpegSurface->w * _pixelFormat.bytesPerPixel); + _outputSurface = new Graphics::Surface(); + _outputSurface->copyFrom(*jpeg.getSurface()); - stream->seek(startPos + dataSize); - delete jpegStream; + stream.seek(startPos + dataSize); } } // End of namespace Graphics diff --git a/graphics/pict.h b/graphics/decoders/pict.h index b426c6ee35..b1e45a6bc1 100644 --- a/graphics/pict.h +++ b/graphics/decoders/pict.h @@ -27,6 +27,7 @@ #include "common/rect.h" #include "common/scummsys.h" +#include "graphics/decoders/image_decoder.h" #include "graphics/pixelformat.h" namespace Common { @@ -35,16 +36,20 @@ class SeekableReadStream; namespace Graphics { -class JPEG; struct Surface; -#define DECLARE_OPCODE(x) void x(Common::SeekableReadStream *stream) +#define DECLARE_OPCODE(x) void x(Common::SeekableReadStream &stream) -class PictDecoder { +class PICTDecoder : public ImageDecoder { public: - PictDecoder(Graphics::PixelFormat pixelFormat); - ~PictDecoder(); - Surface *decodeImage(Common::SeekableReadStream *stream, byte *palette = 0); + PICTDecoder(); + ~PICTDecoder(); + + // ImageDecoder API + bool loadStream(Common::SeekableReadStream &stream); + void destroy(); + const Surface *getSurface() const { return _outputSurface; } + const byte *getPalette() const { return _palette; } struct PixMap { uint32 baseAddr; @@ -64,26 +69,23 @@ public: uint32 pmReserved; }; - static PixMap readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr = true); + static PixMap readPixMap(Common::SeekableReadStream &stream, bool hasBaseAddr = true); private: Common::Rect _imageRect; - PixelFormat _pixelFormat; - JPEG *_jpeg; byte _palette[256 * 3]; - bool _isPaletted; Graphics::Surface *_outputSurface; bool _continueParsing; // Utility Functions - void unpackBitsRect(Common::SeekableReadStream *stream, bool hasPalette); - void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel); - void skipBitsRect(Common::SeekableReadStream *stream, bool hasPalette); - void decodeCompressedQuickTime(Common::SeekableReadStream *stream); + void unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette); + void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel); + void skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette); + void decodeCompressedQuickTime(Common::SeekableReadStream &stream); void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel); // Opcodes - typedef void (PictDecoder::*OpcodeProcPICT)(Common::SeekableReadStream *stream); + typedef void (PICTDecoder::*OpcodeProcPICT)(Common::SeekableReadStream &stream); struct PICTOpcode { PICTOpcode() { op = 0; proc = 0; desc = 0; } PICTOpcode(uint16 o, OpcodeProcPICT p, const char *d) { op = o; proc = p; desc = d; } diff --git a/graphics/png.cpp b/graphics/decoders/png.cpp index cea8b575ad..b87b6fdc7a 100644 --- a/graphics/png.cpp +++ b/graphics/decoders/png.cpp @@ -20,7 +20,7 @@ * */ -#include "graphics/png.h" +#include "graphics/decoders/png.h" #include "graphics/pixelformat.h" #include "graphics/surface.h" @@ -98,116 +98,30 @@ enum PNGFilters { kFilterPaeth = 4 }; -PNG::PNG() : _compressedBuffer(0), _compressedBufferSize(0), - _unfilteredSurface(0), _transparentColorSpecified(false) { +PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0), + _transparentColorSpecified(false), _outputSurface(0) { } -PNG::~PNG() { - if (_unfilteredSurface) { - _unfilteredSurface->free(); - delete _unfilteredSurface; - } +PNGDecoder::~PNGDecoder() { + destroy(); } -Graphics::Surface *PNG::getSurface(const PixelFormat &format) { - Graphics::Surface *output = new Graphics::Surface(); - output->create(_unfilteredSurface->w, _unfilteredSurface->h, format); - byte *src = (byte *)_unfilteredSurface->pixels; - byte a = 0xFF; - byte bpp = _unfilteredSurface->format.bytesPerPixel; - - if (_header.colorType != kIndexed) { - if (_header.colorType == kTrueColor || - _header.colorType == kTrueColorWithAlpha) { - if (bpp != 3 && bpp != 4) - error("Unsupported truecolor PNG format"); - } else if (_header.colorType == kGrayScale || - _header.colorType == kGrayScaleWithAlpha) { - if (bpp != 1 && bpp != 2) - error("Unsupported grayscale PNG format"); - } - - for (uint16 i = 0; i < output->h; i++) { - for (uint16 j = 0; j < output->w; j++) { - uint32 result = 0; - - switch (bpp) { - case 1: // Grayscale - if (_transparentColorSpecified) - a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; - result = format.ARGBToColor( a, src[0], src[0], src[0]); - break; - case 2: // Grayscale + alpha - result = format.ARGBToColor(src[1], src[0], src[0], src[0]); - break; - case 3: // RGB - if (_transparentColorSpecified) { - bool isTransparentColor = (src[0] == _transparentColor[0] && - src[1] == _transparentColor[1] && - src[2] == _transparentColor[2]); - a = isTransparentColor ? 0 : 0xFF; - } - result = format.ARGBToColor( a, src[0], src[1], src[2]); - break; - case 4: // RGBA - result = format.ARGBToColor(src[3], src[0], src[1], src[2]); - break; - } - - if (format.bytesPerPixel == 2) // 2bpp - *((uint16 *)output->getBasePtr(j, i)) = (uint16)result; - else // 4bpp - *((uint32 *)output->getBasePtr(j, i)) = result; - - src += bpp; - } - } - } else { - byte index, r, g, b; - uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth); - - // Convert the indexed surface to the target pixel format - for (uint16 i = 0; i < output->h; i++) { - int data = 0; - int bitCount = 8; - byte *src1 = src; - - for (uint16 j = 0; j < output->w; j++) { - if (bitCount == 8) { - data = *src; - src++; - } - - index = (data & mask) >> (8 - _header.bitDepth); - data = (data << _header.bitDepth) & 0xff; - bitCount -= _header.bitDepth; - - if (bitCount == 0) - bitCount = 8; - - r = _palette[index * 4 + 0]; - g = _palette[index * 4 + 1]; - b = _palette[index * 4 + 2]; - a = _palette[index * 4 + 3]; - - if (format.bytesPerPixel == 2) - *((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor(a, r, g, b); - else - *((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor(a, r, g, b); - } - src = src1 + output->w; - } +void PNGDecoder::destroy() { + if (_outputSurface) { + _outputSurface->free(); + delete _outputSurface; + _outputSurface = 0; } - - return output; } -bool PNG::read(Common::SeekableReadStream *str) { +bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + uint32 chunkLength = 0, chunkType = 0; - _stream = str; + _stream = &stream; // First, check the PNG signature - if (_stream->readUint32BE() != MKTAG(0x89, 0x50, 0x4e, 0x47)) { + if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) { delete _stream; return false; } @@ -249,8 +163,10 @@ bool PNG::read(Common::SeekableReadStream *str) { error("A palette chunk has been found in a non-indexed PNG file"); if (chunkLength % 3 != 0) error("Palette chunk not divisible by 3"); + _paletteEntries = chunkLength / 3; - readPaletteChunk(); + _stream->read(_palette, _paletteEntries * 3); + memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency)); break; case kChunkIEND: // End of stream @@ -269,7 +185,6 @@ bool PNG::read(Common::SeekableReadStream *str) { } // We no longer need the file stream, thus close it here - delete _stream; _stream = 0; // Unpack the compressed buffer @@ -295,7 +210,7 @@ bool PNG::read(Common::SeekableReadStream *str) { * Taken from lodePNG, with a slight patch: * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx */ -byte PNG::paethPredictor(int16 a, int16 b, int16 c) { +byte PNGDecoder::paethPredictor(int16 a, int16 b, int16 c) { int16 pa = ABS<int16>(b - c); int16 pb = ABS<int16>(a - c); int16 pc = ABS<int16>(a + b - c - c); @@ -315,7 +230,7 @@ byte PNG::paethPredictor(int16 a, int16 b, int16 c) { * * Taken from lodePNG */ -void PNG::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) { +void PNGDecoder::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) { uint16 i; switch (filterType) { @@ -370,33 +285,29 @@ void PNG::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLin } -void PNG::constructImage() { +int PNGDecoder::getBytesPerPixel() const { + return (getNumColorChannels() * _header.bitDepth + 7) / 8; +} + +void PNGDecoder::constructImage() { assert (_header.bitDepth != 0); - byte *dest; - byte *scanLine; - byte *prevLine = 0; - byte filterType; + int bytesPerPixel = getBytesPerPixel(); + int pitch = bytesPerPixel * _header.width; + byte *unfilteredSurface = new byte[pitch * _header.height]; + byte *dest = unfilteredSurface; uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8; - - if (_unfilteredSurface) { - _unfilteredSurface->free(); - delete _unfilteredSurface; - } - _unfilteredSurface = new Graphics::Surface(); - // TODO/FIXME: It seems we can not properly determine the format here. But maybe there is a way... - _unfilteredSurface->create(_header.width, _header.height, PixelFormat((getNumColorChannels() * _header.bitDepth + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0)); - scanLine = new byte[_unfilteredSurface->pitch]; - dest = (byte *)_unfilteredSurface->getBasePtr(0, 0); + byte *scanLine = new byte[scanLineWidth]; + byte *prevLine = 0; switch(_header.interlaceType) { case kNonInterlaced: - for (uint16 y = 0; y < _unfilteredSurface->h; y++) { - filterType = _imageData->readByte(); + for (uint16 y = 0; y < _header.height; y++) { + byte filterType = _imageData->readByte(); _imageData->read(scanLine, scanLineWidth); - unfilterScanLine(dest, scanLine, prevLine, _unfilteredSurface->format.bytesPerPixel, filterType, scanLineWidth); + unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth); prevLine = dest; - dest += _unfilteredSurface->pitch; + dest += pitch; } break; case kInterlaced: @@ -409,9 +320,123 @@ void PNG::constructImage() { } delete[] scanLine; + + constructOutput(unfilteredSurface); + delete[] unfilteredSurface; } -void PNG::readHeaderChunk() { +Graphics::PixelFormat PNGDecoder::findPixelFormat() const { + // Try to find the best pixel format based on what we have here + // Which is basically 8bpp for paletted non-transparent + // and 32bpp for everything else + + switch (_header.colorType) { + case kIndexed: + if (!_transparentColorSpecified) + return Graphics::PixelFormat::createFormatCLUT8(); + // fall through + case kGrayScale: + case kTrueColor: + case kGrayScaleWithAlpha: + case kTrueColorWithAlpha: + // We'll go with standard RGBA 32-bit + return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + } + + error("Unknown PNG color type"); + return Graphics::PixelFormat(); +} + +void PNGDecoder::constructOutput(const byte *surface) { + _outputSurface = new Graphics::Surface(); + _outputSurface->create(_header.width, _header.height, findPixelFormat()); + + const byte *src = surface; + byte a = 0xFF; + int bytesPerPixel = getBytesPerPixel(); + + if (_header.colorType != kIndexed) { + if (_header.colorType == kTrueColor || + _header.colorType == kTrueColorWithAlpha) { + if (bytesPerPixel != 3 && bytesPerPixel != 4) + error("Unsupported truecolor PNG format"); + } else if (_header.colorType == kGrayScale || + _header.colorType == kGrayScaleWithAlpha) { + if (bytesPerPixel != 1 && bytesPerPixel != 2) + error("Unsupported grayscale PNG format"); + } + + for (uint16 i = 0; i < _outputSurface->h; i++) { + for (uint16 j = 0; j < _outputSurface->w; j++) { + uint32 result = 0; + + switch (bytesPerPixel) { + case 1: // Grayscale + if (_transparentColorSpecified) + a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; + result = _outputSurface->format.ARGBToColor(a, src[0], src[0], src[0]); + break; + case 2: // Grayscale + alpha + result = _outputSurface->format.ARGBToColor(src[1], src[0], src[0], src[0]); + break; + case 3: // RGB + if (_transparentColorSpecified) { + bool isTransparentColor = (src[0] == _transparentColor[0] && + src[1] == _transparentColor[1] && + src[2] == _transparentColor[2]); + a = isTransparentColor ? 0 : 0xFF; + } + + result = _outputSurface->format.ARGBToColor(a, src[0], src[1], src[2]); + break; + case 4: // RGBA + result = _outputSurface->format.ARGBToColor(src[3], src[0], src[1], src[2]); + break; + } + + *((uint32 *)_outputSurface->getBasePtr(j, i)) = result; + src += bytesPerPixel; + } + } + } else { + uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth); + + // Convert the indexed surface to the target pixel format + for (uint16 i = 0; i < _outputSurface->h; i++) { + int data = 0; + int bitCount = 8; + const byte *src1 = src; + + for (uint16 j = 0; j < _outputSurface->w; j++) { + if (bitCount == 8) { + data = *src; + src++; + } + + byte index = (data & mask) >> (8 - _header.bitDepth); + data = (data << _header.bitDepth) & 0xff; + bitCount -= _header.bitDepth; + + if (bitCount == 0) + bitCount = 8; + + if (_transparentColorSpecified) { + byte r = _palette[index * 3 + 0]; + byte g = _palette[index * 3 + 1]; + byte b = _palette[index * 3 + 2]; + a = _paletteTransparency[index]; + *((uint32 *)_outputSurface->getBasePtr(j, i)) = _outputSurface->format.ARGBToColor(a, r, g, b); + } else { + *((byte *)_outputSurface->getBasePtr(j, i)) = index; + } + } + + src = src1 + _outputSurface->w; + } + } +} + +void PNGDecoder::readHeaderChunk() { _header.width = _stream->readUint32BE(); _header.height = _stream->readUint32BE(); _header.bitDepth = _stream->readByte(); @@ -431,7 +456,7 @@ void PNG::readHeaderChunk() { _header.interlaceType = (PNGInterlaceType)_stream->readByte(); } -byte PNG::getNumColorChannels() { +byte PNGDecoder::getNumColorChannels() const { switch (_header.colorType) { case kGrayScale: return 1; // Gray @@ -448,16 +473,7 @@ byte PNG::getNumColorChannels() { } } -void PNG::readPaletteChunk() { - for (uint16 i = 0; i < _paletteEntries; i++) { - _palette[i * 4 + 0] = _stream->readByte(); // R - _palette[i * 4 + 1] = _stream->readByte(); // G - _palette[i * 4 + 2] = _stream->readByte(); // B - _palette[i * 4 + 3] = 0xFF; // Alpha, set in the tRNS chunk - } -} - -void PNG::readTransparencyChunk(uint32 chunkLength) { +void PNGDecoder::readTransparencyChunk(uint32 chunkLength) { _transparentColorSpecified = true; switch(_header.colorType) { @@ -472,8 +488,8 @@ void PNG::readTransparencyChunk(uint32 chunkLength) { _transparentColor[2] = _stream->readUint16BE(); break; case kIndexed: - for (uint32 i = 0; i < chunkLength; i++) - _palette[i * 4 + 3] = _stream->readByte(); + _stream->read(_paletteTransparency, chunkLength); + // A transparency chunk may have less entries // than the palette entries. The remaining ones // are unmodified (set to 255). Check here: diff --git a/graphics/png.h b/graphics/decoders/png.h index 078c76fc6b..1da0bea1ab 100644 --- a/graphics/png.h +++ b/graphics/decoders/png.h @@ -53,6 +53,7 @@ #include "common/scummsys.h" #include "common/textconsole.h" +#include "graphics/decoders/image_decoder.h" namespace Common { class SeekableReadStream; @@ -61,82 +62,44 @@ class SeekableReadStream; namespace Graphics { struct Surface; - -enum PNGColorType { - kGrayScale = 0, // bit depths: 1, 2, 4, 8, 16 - kTrueColor = 2, // bit depths: 8, 16 - kIndexed = 3, // bit depths: 1, 2, 4, 8 - kGrayScaleWithAlpha = 4, // bit depths: 8, 16 - kTrueColorWithAlpha = 6 // bit depths: 8, 16 -}; - -enum PNGInterlaceType { - kNonInterlaced = 0, - kInterlaced = 1 -}; - -struct PNGHeader { - uint32 width; - uint32 height; - byte bitDepth; - PNGColorType colorType; - byte compressionMethod; - byte filterMethod; - PNGInterlaceType interlaceType; -}; - struct PixelFormat; -class PNG { +class PNGDecoder : public ImageDecoder { public: - PNG(); - ~PNG(); - - /** - * Reads a PNG image from the specified stream - */ - bool read(Common::SeekableReadStream *str); - - /** - * Returns the information obtained from the PNG header. - */ - PNGHeader getHeader() const { return _header; } - - /** - * Returns the PNG image, formatted for the specified pixel format. - */ - Graphics::Surface *getSurface(const PixelFormat &format); - - /** - * Returns the indexed PNG8 image. Used for PNGs with an indexed 256 color - * palette, when they're shown on an 8-bit color screen, as no translation - * is taking place. - */ - Graphics::Surface *getIndexedSurface() { - if (_header.colorType != kIndexed) - error("Indexed surface requested for a non-indexed PNG"); - return _unfilteredSurface; - } - - /** - * Returns the palette of the specified PNG8 image, given a pointer to - * an RGBA palette array (4 x 256). - */ - void getPalette(byte *palette, uint16 &entries) { - if (_header.colorType != kIndexed) - error("Palette requested for a non-indexed PNG"); - for (int i = 0; i < 256; i++) { - palette[0 + i * 4] = _palette[0 + i * 4]; // R - palette[1 + i * 4] = _palette[1 + i * 4]; // G - palette[2 + i * 4] = _palette[2 + i * 4]; // B - palette[3 + i * 4] = _palette[3 + i * 4]; // A - } - entries = _paletteEntries; - } + PNGDecoder(); + ~PNGDecoder(); + + bool loadStream(Common::SeekableReadStream &stream); + void destroy(); + const Graphics::Surface *getSurface() const { return _outputSurface; } + const byte *getPalette() const { return _palette; } private: + enum PNGColorType { + kGrayScale = 0, // bit depths: 1, 2, 4, 8, 16 + kTrueColor = 2, // bit depths: 8, 16 + kIndexed = 3, // bit depths: 1, 2, 4, 8 + kGrayScaleWithAlpha = 4, // bit depths: 8, 16 + kTrueColorWithAlpha = 6 // bit depths: 8, 16 + }; + + enum PNGInterlaceType { + kNonInterlaced = 0, + kInterlaced = 1 + }; + + struct PNGHeader { + uint32 width; + uint32 height; + byte bitDepth; + PNGColorType colorType; + byte compressionMethod; + byte filterMethod; + PNGInterlaceType interlaceType; + }; + void readHeaderChunk(); - byte getNumColorChannels(); + byte getNumColorChannels() const; void readPaletteChunk(); void readTransparencyChunk(uint32 chunkLength); @@ -152,7 +115,8 @@ private: PNGHeader _header; - byte _palette[256 * 4]; // RGBA + byte _palette[256 * 3]; // RGB + byte _paletteTransparency[256]; uint16 _paletteEntries; uint16 _transparentColor[3]; bool _transparentColorSpecified; @@ -160,9 +124,12 @@ private: byte *_compressedBuffer; uint32 _compressedBufferSize; - Graphics::Surface *_unfilteredSurface; + Graphics::Surface *_outputSurface; + Graphics::PixelFormat findPixelFormat() const; + int getBytesPerPixel() const; + void constructOutput(const byte *surface); }; -} // End of Graphics namespace +} // End of namespace Graphics #endif // GRAPHICS_PNG_H diff --git a/graphics/imagedec.cpp b/graphics/imagedec.cpp deleted file mode 100644 index 9552f095fa..0000000000 --- a/graphics/imagedec.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "graphics/imagedec.h" -#include "graphics/pixelformat.h" -#include "graphics/surface.h" - -#include "common/file.h" - -namespace Graphics { -// -// BMP Decoder -// -class BMPDecoder : public ImageDecoder { -public: - BMPDecoder() {} - virtual ~BMPDecoder() {} - - bool decodeable(Common::SeekableReadStream &stream); - Surface *decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format); - - struct BitmapHeader { - uint16 type; - uint32 size; - uint16 res1; - uint16 res2; - uint32 imageOffset; - }; - - struct InfoHeader { - uint32 size; - uint32 width; - uint32 height; - uint16 planes; - uint16 bitsPerPixel; - uint32 compression; - uint32 imageSize; - uint32 pixelsPerMeterX; - uint32 pixelsPerMeterY; - uint32 colorsUsed; - uint32 colorsImportant; - }; -}; - -bool BMPDecoder::decodeable(Common::SeekableReadStream &stream) { - BitmapHeader header; - stream.seek(0); - header.type = stream.readUint16BE(); - header.size = stream.readUint32LE(); - - // TODO: maybe improve this detection - if (header.size == 0 || header.type != 'BM') - return false; - - return true; -} - -Surface *BMPDecoder::decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format) { - if (!decodeable(stream)) { - return 0; - } - - BitmapHeader header; - InfoHeader info; - - stream.seek(0); - header.type = stream.readUint16BE(); - header.size = stream.readUint32LE(); - header.res1 = stream.readUint16LE(); - header.res2 = stream.readUint16LE(); - header.imageOffset = stream.readUint32LE(); - - if (header.size == 0 || header.type != 'BM') { - stream.seek(0); - return 0; - } - - info.size = stream.readUint32LE(); - info.width = stream.readUint32LE(); - info.height = stream.readUint32LE(); - info.planes = stream.readUint16LE(); - info.bitsPerPixel = stream.readUint16LE(); - info.compression = stream.readUint32LE(); - info.imageSize = stream.readUint32LE(); - info.pixelsPerMeterX = stream.readUint32LE(); - info.pixelsPerMeterY = stream.readUint32LE(); - info.colorsUsed = stream.readUint32LE(); - info.colorsImportant = stream.readUint32LE(); - - stream.seek(header.imageOffset); - - if (info.bitsPerPixel != 24) { - stream.seek(0); - return 0; - } - - uint8 r = 0, g = 0, b = 0; - Surface *newSurf = new Surface; - assert(newSurf); - newSurf->create(info.width, info.height, format); - assert(newSurf->pixels); - OverlayColor *curPixel = (OverlayColor *)newSurf->pixels + (newSurf->h-1) * newSurf->w; - int pitchAdd = info.width % 4; - for (int i = 0; i < newSurf->h; ++i) { - for (int i2 = 0; i2 < newSurf->w; ++i2) { - b = stream.readByte(); - g = stream.readByte(); - r = stream.readByte(); - *curPixel = format.RGBToColor(r, g, b); - ++curPixel; - } - stream.seek(pitchAdd, SEEK_CUR); - curPixel -= newSurf->w*2; - } - - stream.seek(0); - return newSurf; -} - -#pragma mark - - -Surface *ImageDecoder::loadFile(const Common::String &name, const PixelFormat &format) { - Surface *newSurf = 0; - - Common::File imageFile; - if (imageFile.open(name)) { - newSurf = loadFile(imageFile, format); - } - - return newSurf; -} - -Surface *ImageDecoder::loadFile(Common::SeekableReadStream &stream, const PixelFormat &format) { - // TODO: implement support for bzipped memory - - // FIXME: this is not a very nice solution but it should work - // for the moment, we should use a different way to get all - // decoders - static BMPDecoder bmpDecoder; - static ImageDecoder *decoderList[] = { - &bmpDecoder, // for uncompressed .BMP files - 0 - }; - - ImageDecoder *decoder = 0; - for (int i = 0; decoderList[i] != 0; ++i) { - if (decoderList[i]->decodeable(stream)) { - decoder = decoderList[i]; - break; - } - } - - if (!decoder) - return 0; - - return decoder->decodeImage(stream, format); -} -} // End of namespace Graphics diff --git a/graphics/module.mk b/graphics/module.mk index 1e84b2425d..281f904b38 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -12,11 +12,7 @@ MODULE_OBJS := \ fonts/ttf.o \ fonts/winfont.o \ iff.o \ - imagedec.o \ - jpeg.o \ maccursor.o \ - pict.o \ - png.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ @@ -26,7 +22,11 @@ MODULE_OBJS := \ VectorRenderer.o \ VectorRendererSpec.o \ wincursor.o \ - yuv_to_rgb.o + yuv_to_rgb.o \ + decoders/bmp.o \ + decoders/jpeg.o \ + decoders/pict.o \ + decoders/png.o ifdef USE_SCALERS MODULE_OBJS += \ diff --git a/graphics/surface.cpp b/graphics/surface.cpp index 79a7821feb..fcd702241a 100644 --- a/graphics/surface.cpp +++ b/graphics/surface.cpp @@ -20,6 +20,7 @@ */ #include "common/algorithm.h" +#include "common/endian.h" #include "common/util.h" #include "common/rect.h" #include "common/textconsole.h" @@ -270,4 +271,82 @@ void Surface::move(int dx, int dy, int height) { } } +Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const { + assert(pixels); + + Graphics::Surface *surface = new Graphics::Surface(); + + // If the target format is the same, just copy + if (format == dstFormat) { + surface->copyFrom(*this); + return surface; + } + + if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4) + error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp"); + + if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4) + error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp"); + + surface->create(w, h, dstFormat); + + if (format.bytesPerPixel == 1) { + // Converting from paletted to high color + assert(palette); + + for (int y = 0; y < h; y++) { + const byte *srcRow = (byte *)getBasePtr(0, y); + byte *dstRow = (byte *)surface->getBasePtr(0, y); + + for (int x = 0; x < w; x++) { + byte index = *srcRow++; + byte r = palette[index * 3]; + byte g = palette[index * 3 + 1]; + byte b = palette[index * 3 + 2]; + + uint32 color = dstFormat.RGBToColor(r, g, b); + + if (dstFormat.bytesPerPixel == 2) + *((uint16 *)dstRow) = color; + else + *((uint32 *)dstRow) = color; + + dstRow += dstFormat.bytesPerPixel; + } + } + } else { + // Converting from high color to high color + for (int y = 0; y < h; y++) { + const byte *srcRow = (byte *)getBasePtr(0, y); + byte *dstRow = (byte *)surface->getBasePtr(0, y); + + for (int x = 0; x < w; x++) { + uint32 srcColor; + if (format.bytesPerPixel == 2) + srcColor = READ_UINT16(srcRow); + else if (format.bytesPerPixel == 3) + srcColor = READ_UINT24(srcRow); + else + srcColor = READ_UINT32(srcRow); + + srcRow += format.bytesPerPixel; + + // Convert that color to the new format + byte r, g, b, a; + format.colorToARGB(srcColor, a, r, g, b); + uint32 color = dstFormat.ARGBToColor(a, r, g, b); + + if (dstFormat.bytesPerPixel == 2) + *((uint16 *)dstRow) = color; + else + *((uint32 *)dstRow) = color; + + dstRow += dstFormat.bytesPerPixel; + } + } + } + + return surface; +} + } // End of namespace Graphics diff --git a/graphics/surface.h b/graphics/surface.h index 018a283aad..eb8d1ac42e 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -135,6 +135,17 @@ struct Surface { void copyFrom(const Surface &surf); /** + * Convert the data to another pixel format. + * + * The calling code must call free on the returned surface and then delete + * it. + * + * @param dstFormat The desired format + * @param palette The palette (in RGB888), if the source format has a Bpp of 1 + */ + Graphics::Surface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const; + + /** * Draw a line. * * @param x0 The x coordinate of the start point. diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp index feda48bf6d..ac7f217fee 100644 --- a/graphics/yuv_to_rgb.cpp +++ b/graphics/yuv_to_rgb.cpp @@ -199,6 +199,52 @@ namespace Graphics { *((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b]) template<typename PixelInt> +void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { + // Keep the tables in pointers here to avoid a dereference on each pixel + const int16 *Cr_r_tab = lookup->_colorTab; + const int16 *Cr_g_tab = Cr_r_tab + 256; + const int16 *Cb_g_tab = Cr_g_tab + 256; + const int16 *Cb_b_tab = Cb_g_tab + 256; + const uint32 *rgbToPix = lookup->_rgbToPix; + + for (int h = 0; h < yHeight; h++) { + for (int w = 0; w < yWidth; w++) { + register const uint32 *L; + + int16 cr_r = Cr_r_tab[*vSrc]; + int16 crb_g = Cr_g_tab[*vSrc] + Cb_g_tab[*uSrc]; + int16 cb_b = Cb_b_tab[*uSrc]; + ++uSrc; + ++vSrc; + + PUT_PIXEL(*ySrc, dstPtr); + ySrc++; + dstPtr += sizeof(PixelInt); + } + + dstPtr += dstPitch - yWidth * sizeof(PixelInt); + ySrc += yPitch - yWidth; + uSrc += uvPitch - yWidth; + vSrc += uvPitch - yWidth; + } +} + +void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { + // Sanity checks + assert(dst && dst->pixels); + assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); + assert(ySrc && uSrc && vSrc); + + const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format); + + // Use a templated function to avoid an if check on every pixel + if (dst->format.bytesPerPixel == 2) + convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + else + convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); +} + +template<typename PixelInt> void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { int halfHeight = yHeight >> 1; int halfWidth = yWidth >> 1; diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h index 259ba09810..8e025042dc 100644 --- a/graphics/yuv_to_rgb.h +++ b/graphics/yuv_to_rgb.h @@ -23,6 +23,7 @@ /** * @file * YUV to RGB conversion used in engines: + * - mohawk * - scumm (he) * - sword25 */ @@ -36,6 +37,20 @@ namespace Graphics { /** + * Convert a YUV444 image to an RGB surface + * + * @param dst the destination surface + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface + * @param yHeight the height of the y surface + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ +void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + +/** * Convert a YUV420 image to an RGB surface * * @param dst the destination surface |