From 1271fbde8f30fdf2d0664bb1e3382928380f9aa9 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 5 Sep 2014 00:30:05 -0400 Subject: IMAGE: Add support for QuickTime dithered Cinepak --- image/codecs/cinepak.cpp | 124 +++++++++++++++++++++++++++++++++++++++++++---- image/codecs/cinepak.h | 3 ++ 2 files changed, 118 insertions(+), 9 deletions(-) diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp index f43c952c2c..32f6be2cd5 100644 --- a/image/codecs/cinepak.cpp +++ b/image/codecs/cinepak.cpp @@ -264,6 +264,37 @@ private: } }; +/** + * Codebook converter that dithers in QT-style + */ +struct CodebookConverterDitherQT { + static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) { + const byte *colorPtr = strip.v1_dither + (codebookIndex << 2); + WRITE_UINT32(rows[0], READ_UINT32(colorPtr)); + WRITE_UINT32(rows[1], READ_UINT32(colorPtr + 1024)); + WRITE_UINT32(rows[2], READ_UINT32(colorPtr + 2048)); + WRITE_UINT32(rows[3], READ_UINT32(colorPtr + 3072)); + } + + static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) { + const byte *colorPtr = strip.v4_dither + (codebookIndex[0] << 2); + WRITE_UINT16(rows[0] + 0, READ_UINT16(colorPtr + 0)); + WRITE_UINT16(rows[1] + 0, READ_UINT16(colorPtr + 2)); + + colorPtr = strip.v4_dither + (codebookIndex[1] << 2); + WRITE_UINT16(rows[0] + 2, READ_UINT16(colorPtr + 1024)); + WRITE_UINT16(rows[1] + 2, READ_UINT16(colorPtr + 1026)); + + colorPtr = strip.v4_dither + (codebookIndex[2] << 2); + WRITE_UINT16(rows[2] + 0, READ_UINT16(colorPtr + 2048)); + WRITE_UINT16(rows[3] + 0, READ_UINT16(colorPtr + 2050)); + + colorPtr = strip.v4_dither + (codebookIndex[3] << 2); + WRITE_UINT16(rows[2] + 2, READ_UINT16(colorPtr + 3072)); + WRITE_UINT16(rows[3] + 2, READ_UINT16(colorPtr + 3074)); + } +}; + template void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *colorMap, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { uint32 flag = 0, mask = 0; @@ -306,8 +337,7 @@ void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *c return; byte codebook[4]; - for (byte i = 0; i < 4; i++) - codebook[i] = stream.readByte(); + stream.read(codebook, 4); CodebookConverter::decodeBlock4(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format); } } @@ -326,6 +356,7 @@ CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPe _y = 0; _colorMap = 0; _ditherPalette = 0; + _ditherType = kDitherTypeUnknown; if (bitsPerPixel == 8) { _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); @@ -398,9 +429,12 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream for (uint16 i = 0; i < _curFrame.stripCount; i++) { if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip + for (uint16 j = 0; j < 256; j++) { _curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j]; _curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j]; + memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * 4); + memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * 4); } } @@ -484,8 +518,7 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str if ((stream.pos() - startPos + n) > (int32)chunkSize) break; - for (byte j = 0; j < 4; j++) - codebook[i].y[j] = stream.readByte(); + stream.read(codebook[i].y, 4); if (n == 6) { codebook[i].u = stream.readSByte(); @@ -497,10 +530,72 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str codebook[i].u = 0; codebook[i].v = 0; } + + // Dither the codebook if we're dithering for QuickTime + if (_ditherType == kDitherTypeQT) + ditherCodebookQT(strip, codebookType, i); } } } +void CinepakDecoder::ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex) { + if (codebookType == 1) { + const CinepakCodebook &codebook = _curFrame.strips[strip].v1_codebook[codebookIndex]; + byte *output = _curFrame.strips[strip].v1_dither + (codebookIndex << 2); + + byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v); + output[0x000] = ditherEntry[0x0000]; + output[0x001] = ditherEntry[0x4000]; + output[0x400] = ditherEntry[0xC000]; + output[0x401] = ditherEntry[0x0000]; + + ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v); + output[0x002] = ditherEntry[0x8000]; + output[0x003] = ditherEntry[0xC000]; + output[0x402] = ditherEntry[0x4000]; + output[0x403] = ditherEntry[0x8000]; + + ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v); + output[0x800] = ditherEntry[0x4000]; + output[0x801] = ditherEntry[0x8000]; + output[0xC00] = ditherEntry[0x8000]; + output[0xC01] = ditherEntry[0xC000]; + + ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v); + output[0x802] = ditherEntry[0xC000]; + output[0x803] = ditherEntry[0x0000]; + output[0xC02] = ditherEntry[0x0000]; + output[0xC03] = ditherEntry[0x4000]; + } else { + const CinepakCodebook &codebook = _curFrame.strips[strip].v4_codebook[codebookIndex]; + byte *output = _curFrame.strips[strip].v4_dither + (codebookIndex << 2); + + byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v); + output[0x000] = ditherEntry[0x0000]; + output[0x400] = ditherEntry[0x8000]; + output[0x800] = ditherEntry[0x4000]; + output[0xC00] = ditherEntry[0xC000]; + + ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v); + output[0x001] = ditherEntry[0x4000]; + output[0x401] = ditherEntry[0xC000]; + output[0x801] = ditherEntry[0x8000]; + output[0xC01] = ditherEntry[0x0000]; + + ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v); + output[0x002] = ditherEntry[0xC000]; + output[0x402] = ditherEntry[0x4000]; + output[0x802] = ditherEntry[0x8000]; + output[0xC02] = ditherEntry[0x0000]; + + ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v); + output[0x003] = ditherEntry[0x0000]; + output[0x403] = ditherEntry[0x8000]; + output[0x803] = ditherEntry[0xC000]; + output[0xC03] = ditherEntry[0x4000]; + } +} + void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { if (_curFrame.surface->format.bytesPerPixel == 1) { decodeVectorsTmpl(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); @@ -512,7 +607,7 @@ void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 st } bool CinepakDecoder::canDither(DitherType type) const { - return type == kDitherTypeVFW && _bitsPerPixel == 24; + return (type == kDitherTypeVFW || type == kDitherTypeQT) && _bitsPerPixel == 24; } void CinepakDecoder::setDither(DitherType type, const byte *palette) { @@ -526,10 +621,18 @@ void CinepakDecoder::setDither(DitherType type, const byte *palette) { _dirtyPalette = true; _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); - _colorMap = new byte[221]; + _ditherType = type; + + if (type == kDitherTypeVFW) { + _colorMap = new byte[221]; - for (int i = 0; i < 221; i++) - _colorMap[i] = findNearestRGB(i); + for (int i = 0; i < 221; i++) + _colorMap[i] = findNearestRGB(i); + } else { + // Generate QuickTime dither table + // 4 blocks of 0x4000 bytes (RGB554 lookup) + _colorMap = createQuickTimeDitherTable(palette, 256); + } } byte CinepakDecoder::findNearestRGB(int index) const { @@ -567,7 +670,10 @@ byte CinepakDecoder::findNearestRGB(int index) const { } void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { - decodeVectorsTmpl(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); + if (_ditherType == kDitherTypeVFW) + decodeVectorsTmpl(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); + else + decodeVectorsTmpl(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); } } // End of namespace Image diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h index 02c09b6ddb..dc8172ea0f 100644 --- a/image/codecs/cinepak.h +++ b/image/codecs/cinepak.h @@ -46,6 +46,7 @@ struct CinepakStrip { uint16 length; Common::Rect rect; CinepakCodebook v1_codebook[256], v4_codebook[256]; + byte v1_dither[256 * 4 * 4 * 4], v4_dither[256 * 4 * 4 * 4]; }; struct CinepakFrame { @@ -89,12 +90,14 @@ private: bool _dirtyPalette; byte *_rgbLookup; byte *_colorMap; + DitherType _ditherType; void loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize); void decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize); byte findNearestRGB(int index) const; void ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize); + void ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex); }; } // End of namespace Image -- cgit v1.2.3