diff options
-rw-r--r-- | engines/sword25/gfx/image/imgloader.cpp | 13 | ||||
-rw-r--r-- | graphics/decoders/png.cpp (renamed from graphics/png.cpp) | 290 | ||||
-rw-r--r-- | graphics/decoders/png.h (renamed from graphics/png.h) | 113 | ||||
-rw-r--r-- | graphics/module.mk | 4 |
4 files changed, 202 insertions, 218 deletions
diff --git a/engines/sword25/gfx/image/imgloader.cpp b/engines/sword25/gfx/image/imgloader.cpp index 1df0fba70c..e103626416 100644 --- a/engines/sword25/gfx/image/imgloader.cpp +++ b/engines/sword25/gfx/image/imgloader.cpp @@ -33,18 +33,19 @@ #include "sword25/gfx/image/image.h" #include "sword25/gfx/image/imgloader.h" #include "graphics/pixelformat.h" -#include "graphics/png.h" +#include "graphics/decoders/png.h" namespace Sword25 { bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) { Common::MemoryReadStream *fileStr = new Common::MemoryReadStream(fileDataPtr, fileSize, DisposeAfterUse::NO); - Graphics::PNG *png = new Graphics::PNG(); - if (!png->read(fileStr)) // the fileStr pointer, and thus pFileData will be deleted after this is done + + Graphics::PNGDecoder png; + if (!png.loadStream(*fileStr)) // the fileStr pointer, and thus pFileData will be deleted after this is done error("Error while reading PNG image"); - Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24); - Graphics::Surface *pngSurface = png->getSurface(format); + const Graphics::Surface *sourceSurface = png.getSurface(); + Graphics::Surface *pngSurface = sourceSurface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), png.getPalette()); width = pngSurface->w; height = pngSurface->h; @@ -53,7 +54,7 @@ bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, byte *&un pngSurface->free(); delete pngSurface; - delete png; + delete fileStr; // Signal success return true; diff --git a/graphics/png.cpp b/graphics/decoders/png.cpp index 156c405780..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,113 +98,27 @@ 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, 'P', 'N', 'G')) { @@ -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/module.mk b/graphics/module.mk index d9f3802a68..281f904b38 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -13,7 +13,6 @@ MODULE_OBJS := \ fonts/winfont.o \ iff.o \ maccursor.o \ - png.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ @@ -26,7 +25,8 @@ MODULE_OBJS := \ yuv_to_rgb.o \ decoders/bmp.o \ decoders/jpeg.o \ - decoders/pict.o + decoders/pict.o \ + decoders/png.o ifdef USE_SCALERS MODULE_OBJS += \ |