diff options
Diffstat (limited to 'image/codecs/cinepak.cpp')
-rw-r--r-- | image/codecs/cinepak.cpp | 557 |
1 files changed, 461 insertions, 96 deletions
diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp index 8464aa3889..32f6be2cd5 100644 --- a/image/codecs/cinepak.cpp +++ b/image/codecs/cinepak.cpp @@ -21,6 +21,7 @@ */ #include "image/codecs/cinepak.h" +#include "image/codecs/cinepak_tables.h" #include "common/debug.h" #include "common/stream.h" @@ -34,23 +35,328 @@ namespace Image { -#define PUT_PIXEL(offset, lum, u, v) \ - if (_pixelFormat.bytesPerPixel != 1) { \ - byte r = _clipTable[lum + (v << 1)]; \ - byte g = _clipTable[lum - (u >> 1) - v]; \ - byte b = _clipTable[lum + (u << 1)]; \ - \ - if (_pixelFormat.bytesPerPixel == 2) \ - *((uint16 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \ - else \ - *((uint32 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \ - } else \ - *((byte *)_curFrame.surface->getPixels() + offset) = lum - -CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec() { - _curFrame.surface = NULL; - _curFrame.strips = NULL; +namespace { + +inline void convertYUVToRGB(const byte *clipTable, byte y, int8 u, int8 v, byte &r, byte &g, byte &b) { + r = clipTable[y + (v << 1)]; + g = clipTable[y - (u >> 1) - v]; + b = clipTable[y + (u << 1)]; +} + +inline uint32 convertYUVToColor(const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) { + byte r, g, b; + convertYUVToRGB(clipTable, y, u, v, r, g, b); + return format.RGBToColor(r, g, b); +} + +inline uint16 createDitherTableIndex(const byte *clipTable, byte y, int8 u, int8 v) { + byte r, g, b; + convertYUVToRGB(clipTable, y, u, v, r, g, b); + return ((r & 0xF8) << 6) | + ((g & 0xF8) << 1) | + ((b & 0xF0) >> 4); +} + +/** + * Put a raw pixel to the destination surface + */ +template<typename PixelInt> +inline void putPixelRaw(PixelInt *dst, const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) { + *dst = convertYUVToColor(clipTable, format, y, u, v); +} + +/** + * Specialized putPixelRaw for palettized 8bpp output + */ +template<> +inline void putPixelRaw(byte *dst, const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) { + *dst = y; +} + +/** + * The default codebook converter: raw output. + */ +struct CodebookConverterRaw { + template<typename PixelInt> + static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) { + const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex]; + putPixelRaw(rows[0] + 0, clipTable, format, codebook.y[0], codebook.u, codebook.v); + putPixelRaw(rows[0] + 1, clipTable, format, codebook.y[0], codebook.u, codebook.v); + putPixelRaw(rows[1] + 0, clipTable, format, codebook.y[0], codebook.u, codebook.v); + putPixelRaw(rows[1] + 1, clipTable, format, codebook.y[0], codebook.u, codebook.v); + + putPixelRaw(rows[0] + 2, clipTable, format, codebook.y[1], codebook.u, codebook.v); + putPixelRaw(rows[0] + 3, clipTable, format, codebook.y[1], codebook.u, codebook.v); + putPixelRaw(rows[1] + 2, clipTable, format, codebook.y[1], codebook.u, codebook.v); + putPixelRaw(rows[1] + 3, clipTable, format, codebook.y[1], codebook.u, codebook.v); + + putPixelRaw(rows[2] + 0, clipTable, format, codebook.y[2], codebook.u, codebook.v); + putPixelRaw(rows[2] + 1, clipTable, format, codebook.y[2], codebook.u, codebook.v); + putPixelRaw(rows[3] + 0, clipTable, format, codebook.y[2], codebook.u, codebook.v); + putPixelRaw(rows[3] + 1, clipTable, format, codebook.y[2], codebook.u, codebook.v); + + putPixelRaw(rows[2] + 2, clipTable, format, codebook.y[3], codebook.u, codebook.v); + putPixelRaw(rows[2] + 3, clipTable, format, codebook.y[3], codebook.u, codebook.v); + putPixelRaw(rows[3] + 2, clipTable, format, codebook.y[3], codebook.u, codebook.v); + putPixelRaw(rows[3] + 3, clipTable, format, codebook.y[3], codebook.u, codebook.v); + } + + template<typename PixelInt> + static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) { + const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]]; + putPixelRaw(rows[0] + 0, clipTable, format, codebook1.y[0], codebook1.u, codebook1.v); + putPixelRaw(rows[0] + 1, clipTable, format, codebook1.y[1], codebook1.u, codebook1.v); + putPixelRaw(rows[1] + 0, clipTable, format, codebook1.y[2], codebook1.u, codebook1.v); + putPixelRaw(rows[1] + 1, clipTable, format, codebook1.y[3], codebook1.u, codebook1.v); + + const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]]; + putPixelRaw(rows[0] + 2, clipTable, format, codebook2.y[0], codebook2.u, codebook2.v); + putPixelRaw(rows[0] + 3, clipTable, format, codebook2.y[1], codebook2.u, codebook2.v); + putPixelRaw(rows[1] + 2, clipTable, format, codebook2.y[2], codebook2.u, codebook2.v); + putPixelRaw(rows[1] + 3, clipTable, format, codebook2.y[3], codebook2.u, codebook2.v); + + const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]]; + putPixelRaw(rows[2] + 0, clipTable, format, codebook3.y[0], codebook3.u, codebook3.v); + putPixelRaw(rows[2] + 1, clipTable, format, codebook3.y[1], codebook3.u, codebook3.v); + putPixelRaw(rows[3] + 0, clipTable, format, codebook3.y[2], codebook3.u, codebook3.v); + putPixelRaw(rows[3] + 1, clipTable, format, codebook3.y[3], codebook3.u, codebook3.v); + + const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]]; + putPixelRaw(rows[2] + 2, clipTable, format, codebook4.y[0], codebook4.u, codebook4.v); + putPixelRaw(rows[2] + 3, clipTable, format, codebook4.y[1], codebook4.u, codebook4.v); + putPixelRaw(rows[3] + 2, clipTable, format, codebook4.y[2], codebook4.u, codebook4.v); + putPixelRaw(rows[3] + 3, clipTable, format, codebook4.y[3], codebook4.u, codebook4.v); + } +}; + +/** + * Codebook converter that dithers in VFW-style + */ +struct CodebookConverterDitherVFW { + static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) { + const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex]; + byte blockBuffer[16]; + ditherCodebookSmooth(codebook, blockBuffer, colorMap); + rows[0][0] = blockBuffer[0]; + rows[0][1] = blockBuffer[1]; + rows[0][2] = blockBuffer[2]; + rows[0][3] = blockBuffer[3]; + rows[1][0] = blockBuffer[4]; + rows[1][1] = blockBuffer[5]; + rows[1][2] = blockBuffer[6]; + rows[1][3] = blockBuffer[7]; + rows[2][0] = blockBuffer[8]; + rows[2][1] = blockBuffer[9]; + rows[2][2] = blockBuffer[10]; + rows[2][3] = blockBuffer[11]; + rows[3][0] = blockBuffer[12]; + rows[3][1] = blockBuffer[13]; + rows[3][2] = blockBuffer[14]; + rows[3][3] = blockBuffer[15]; + } + + static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) { + byte blockBuffer[16]; + ditherCodebookDetail(strip.v4_codebook[codebookIndex[0]], blockBuffer, colorMap); + rows[0][0] = blockBuffer[0]; + rows[0][1] = blockBuffer[1]; + rows[1][0] = blockBuffer[4]; + rows[1][1] = blockBuffer[5]; + + ditherCodebookDetail(strip.v4_codebook[codebookIndex[1]], blockBuffer, colorMap); + rows[0][2] = blockBuffer[2]; + rows[0][3] = blockBuffer[3]; + rows[1][2] = blockBuffer[6]; + rows[1][3] = blockBuffer[7]; + + ditherCodebookDetail(strip.v4_codebook[codebookIndex[2]], blockBuffer, colorMap); + rows[2][0] = blockBuffer[8]; + rows[2][1] = blockBuffer[9]; + rows[3][0] = blockBuffer[12]; + rows[3][1] = blockBuffer[13]; + + ditherCodebookDetail(strip.v4_codebook[codebookIndex[3]], blockBuffer, colorMap); + rows[2][2] = blockBuffer[10]; + rows[2][3] = blockBuffer[11]; + rows[3][2] = blockBuffer[14]; + rows[3][3] = blockBuffer[15]; + } + +private: + static inline void ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst, const byte *colorMap) { + int uLookup = (byte)codebook.u * 2; + int vLookup = (byte)codebook.v * 2; + uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup]; + uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1]; + + int yLookup1 = codebook.y[0] * 2; + int yLookup2 = codebook.y[1] * 2; + int yLookup3 = codebook.y[2] * 2; + int yLookup4 = codebook.y[3] * 2; + + uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1]; + uint32 pixelGroup2 = uv2 | s_yLookup[yLookup2 + 1]; + uint32 pixelGroup3 = uv1 | s_yLookup[yLookup3]; + uint32 pixelGroup4 = uv1 | s_yLookup[yLookup4]; + uint32 pixelGroup5 = uv1 | s_yLookup[yLookup1]; + uint32 pixelGroup6 = uv1 | s_yLookup[yLookup2]; + uint32 pixelGroup7 = uv2 | s_yLookup[yLookup3 + 1]; + uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1]; + + dst[0] = getRGBLookupEntry(colorMap, pixelGroup1 & 0xFFFF); + dst[1] = getRGBLookupEntry(colorMap, pixelGroup2 >> 16); + dst[2] = getRGBLookupEntry(colorMap, pixelGroup5 & 0xFFFF); + dst[3] = getRGBLookupEntry(colorMap, pixelGroup6 >> 16); + dst[4] = getRGBLookupEntry(colorMap, pixelGroup3 & 0xFFFF); + dst[5] = getRGBLookupEntry(colorMap, pixelGroup4 >> 16); + dst[6] = getRGBLookupEntry(colorMap, pixelGroup7 & 0xFFFF); + dst[7] = getRGBLookupEntry(colorMap, pixelGroup8 >> 16); + dst[8] = getRGBLookupEntry(colorMap, pixelGroup1 >> 16); + dst[9] = getRGBLookupEntry(colorMap, pixelGroup6 & 0xFFFF); + dst[10] = getRGBLookupEntry(colorMap, pixelGroup5 >> 16); + dst[11] = getRGBLookupEntry(colorMap, pixelGroup2 & 0xFFFF); + dst[12] = getRGBLookupEntry(colorMap, pixelGroup3 >> 16); + dst[13] = getRGBLookupEntry(colorMap, pixelGroup8 & 0xFFFF); + dst[14] = getRGBLookupEntry(colorMap, pixelGroup7 >> 16); + dst[15] = getRGBLookupEntry(colorMap, pixelGroup4 & 0xFFFF); + } + + static inline void ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst, const byte *colorMap) { + int uLookup = (byte)codebook.u * 2; + int vLookup = (byte)codebook.v * 2; + uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup]; + uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1]; + + int yLookup1 = codebook.y[0] * 2; + int yLookup2 = codebook.y[1] * 2; + int yLookup3 = codebook.y[2] * 2; + int yLookup4 = codebook.y[3] * 2; + + uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1]; + uint32 pixelGroup2 = uv1 | s_yLookup[yLookup2]; + uint32 pixelGroup3 = uv1 | s_yLookup[yLookup1]; + uint32 pixelGroup4 = uv2 | s_yLookup[yLookup2 + 1]; + uint32 pixelGroup5 = uv2 | s_yLookup[yLookup3 + 1]; + uint32 pixelGroup6 = uv1 | s_yLookup[yLookup3]; + uint32 pixelGroup7 = uv1 | s_yLookup[yLookup4]; + uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1]; + + dst[0] = getRGBLookupEntry(colorMap, pixelGroup1 & 0xFFFF); + dst[1] = getRGBLookupEntry(colorMap, pixelGroup1 >> 16); + dst[2] = getRGBLookupEntry(colorMap, pixelGroup2 & 0xFFFF); + dst[3] = getRGBLookupEntry(colorMap, pixelGroup2 >> 16); + dst[4] = getRGBLookupEntry(colorMap, pixelGroup3 & 0xFFFF); + dst[5] = getRGBLookupEntry(colorMap, pixelGroup3 >> 16); + dst[6] = getRGBLookupEntry(colorMap, pixelGroup4 & 0xFFFF); + dst[7] = getRGBLookupEntry(colorMap, pixelGroup4 >> 16); + dst[8] = getRGBLookupEntry(colorMap, pixelGroup5 >> 16); + dst[9] = getRGBLookupEntry(colorMap, pixelGroup6 & 0xFFFF); + dst[10] = getRGBLookupEntry(colorMap, pixelGroup7 >> 16); + dst[11] = getRGBLookupEntry(colorMap, pixelGroup8 & 0xFFFF); + dst[12] = getRGBLookupEntry(colorMap, pixelGroup6 >> 16); + dst[13] = getRGBLookupEntry(colorMap, pixelGroup5 & 0xFFFF); + dst[14] = getRGBLookupEntry(colorMap, pixelGroup8 >> 16); + dst[15] = getRGBLookupEntry(colorMap, pixelGroup7 & 0xFFFF); + } + + static inline byte getRGBLookupEntry(const byte *colorMap, uint16 index) { + return colorMap[s_defaultPaletteLookup[CLIP<int>(index, 0, 1024)]]; + } +}; + +/** + * 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<typename PixelInt, typename CodebookConverter> +void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *colorMap, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { + uint32 flag = 0, mask = 0; + PixelInt *iy[4]; + int32 startPos = stream.pos(); + + for (uint16 y = frame.strips[strip].rect.top; y < frame.strips[strip].rect.bottom; y += 4) { + iy[0] = (PixelInt *)frame.surface->getBasePtr(frame.strips[strip].rect.left, + y); + iy[1] = iy[0] + frame.width; + iy[2] = iy[1] + frame.width; + iy[3] = iy[2] + frame.width; + + for (uint16 x = frame.strips[strip].rect.left; x < frame.strips[strip].rect.right; x += 4) { + if ((chunkID & 0x01) && !(mask >>= 1)) { + if ((stream.pos() - startPos + 4) > (int32)chunkSize) + return; + + flag = stream.readUint32BE(); + mask = 0x80000000; + } + + if (!(chunkID & 0x01) || (flag & mask)) { + if (!(chunkID & 0x02) && !(mask >>= 1)) { + if ((stream.pos() - startPos + 4) > (int32)chunkSize) + return; + + flag = stream.readUint32BE(); + mask = 0x80000000; + } + + if ((chunkID & 0x02) || (~flag & mask)) { + if ((stream.pos() - startPos + 1) > (int32)chunkSize) + return; + + // Get the codebook + byte codebook = stream.readByte(); + CodebookConverter::decodeBlock1(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format); + } else if (flag & mask) { + if ((stream.pos() - startPos + 4) > (int32)chunkSize) + return; + + byte codebook[4]; + stream.read(codebook, 4); + CodebookConverter::decodeBlock4(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format); + } + } + + for (byte i = 0; i < 4; i++) + iy[i] += 4; + } + } +} + +} // End of anonymous namespace + +CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPerPixel) { + _curFrame.surface = 0; + _curFrame.strips = 0; _y = 0; + _colorMap = 0; + _ditherPalette = 0; + _ditherType = kDitherTypeUnknown; if (bitsPerPixel == 8) { _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); @@ -86,6 +392,9 @@ CinepakDecoder::~CinepakDecoder() { delete[] _curFrame.strips; delete[] _clipTableBuf; + + delete[] _colorMap; + delete[] _ditherPalette; } const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream &stream) { @@ -96,7 +405,7 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream _curFrame.height = stream.readUint16BE(); _curFrame.stripCount = stream.readUint16BE(); - if (_curFrame.strips == NULL) + if (!_curFrame.strips) _curFrame.strips = new CinepakStrip[_curFrame.stripCount]; debug(4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); @@ -120,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); } } @@ -166,7 +478,10 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream case 0x30: case 0x31: case 0x32: - decodeVectors(stream, i, chunkID, chunkSize); + if (_ditherPalette) + ditherVectors(stream, i, chunkID, chunkSize); + else + decodeVectors(stream, i, chunkID, chunkSize); break; default: warning("Unknown Cinepak chunk ID %02x", chunkID); @@ -203,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(); @@ -216,99 +530,150 @@ 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) { - uint32 flag = 0, mask = 0; - uint32 iy[4]; - int32 startPos = stream.pos(); + if (_curFrame.surface->format.bytesPerPixel == 1) { + decodeVectorsTmpl<byte, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); + } else if (_curFrame.surface->format.bytesPerPixel == 2) { + decodeVectorsTmpl<uint16, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); + } else if (_curFrame.surface->format.bytesPerPixel == 4) { + decodeVectorsTmpl<uint32, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); + } +} - for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) { - iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width; - iy[1] = iy[0] + _curFrame.width; - iy[2] = iy[1] + _curFrame.width; - iy[3] = iy[2] + _curFrame.width; +bool CinepakDecoder::canDither(DitherType type) const { + return (type == kDitherTypeVFW || type == kDitherTypeQT) && _bitsPerPixel == 24; +} - for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) { - if ((chunkID & 0x01) && !(mask >>= 1)) { - if ((stream.pos() - startPos + 4) > (int32)chunkSize) - return; +void CinepakDecoder::setDither(DitherType type, const byte *palette) { + assert(canDither(type)); - flag = stream.readUint32BE(); - mask = 0x80000000; - } + delete[] _colorMap; + delete[] _ditherPalette; - if (!(chunkID & 0x01) || (flag & mask)) { - if (!(chunkID & 0x02) && !(mask >>= 1)) { - if ((stream.pos() - startPos + 4) > (int32)chunkSize) - return; + _ditherPalette = new byte[256 * 3]; + memcpy(_ditherPalette, palette, 256 * 3); - flag = stream.readUint32BE(); - mask = 0x80000000; - } + _dirtyPalette = true; + _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); + _ditherType = type; - if ((chunkID & 0x02) || (~flag & mask)) { - if ((stream.pos() - startPos + 1) > (int32)chunkSize) - return; + if (type == kDitherTypeVFW) { + _colorMap = new byte[221]; - // Get the codebook - CinepakCodebook codebook = _curFrame.strips[strip].v1_codebook[stream.readByte()]; - - PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 1, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 1, codebook.y[0], codebook.u, codebook.v); - - PUT_PIXEL(iy[0] + 2, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 2, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 3, codebook.y[1], codebook.u, codebook.v); - - PUT_PIXEL(iy[2] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 1, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 1, codebook.y[2], codebook.u, codebook.v); - - PUT_PIXEL(iy[2] + 2, codebook.y[3], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 3, codebook.y[3], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 2, codebook.y[3], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v); - } else if (flag & mask) { - if ((stream.pos() - startPos + 4) > (int32)chunkSize) - return; + 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 { + int r = s_defaultPalette[index * 3]; + int g = s_defaultPalette[index * 3 + 1]; + int b = s_defaultPalette[index * 3 + 2]; + + byte result = 0; + int diff = 0x7FFFFFFF; + + for (int i = 0; i < 256; i++) { + int bDiff = b - (int)_ditherPalette[i * 3 + 2]; + int curDiffB = diff - (bDiff * bDiff); + + if (curDiffB > 0) { + int gDiff = g - (int)_ditherPalette[i * 3 + 1]; + int curDiffG = curDiffB - (gDiff * gDiff); + + if (curDiffG > 0) { + int rDiff = r - (int)_ditherPalette[i * 3]; + int curDiffR = curDiffG - (rDiff * rDiff); - CinepakCodebook codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()]; - PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 1, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 1, codebook.y[3], codebook.u, codebook.v); - - codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()]; - PUT_PIXEL(iy[0] + 2, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 2, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 3, codebook.y[3], codebook.u, codebook.v); - - codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()]; - PUT_PIXEL(iy[2] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 1, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 1, codebook.y[3], codebook.u, codebook.v); - - codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()]; - PUT_PIXEL(iy[2] + 2, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 3, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 2, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v); + if (curDiffR > 0) { + diff -= curDiffR; + result = i; + + if (diff == 0) + break; } } - - for (byte i = 0; i < 4; i++) - iy[i] += 4; } } + + return result; +} + +void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { + if (_ditherType == kDitherTypeVFW) + decodeVectorsTmpl<byte, CodebookConverterDitherVFW>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); + else + decodeVectorsTmpl<byte, CodebookConverterDitherQT>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize); } } // End of namespace Image |