/* 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 3 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, see . * */ #include "image/codecs/cinepak.h" #include "image/codecs/cinepak_tables.h" #include "common/debug.h" #include "common/stream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/util.h" #include "graphics/surface.h" // Code here partially based off of ffmpeg ;) namespace Image { 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); } /** * The default codebook converter for 24bpp: RGB output. */ struct CodebookConverterRGB { template static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const Graphics::PixelFormat &format) { const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex]; const PixelInt rgb0 = convertYUVToColor(clipTable, format, codebook.y[0], codebook.u, codebook.v); rows[0][0] = rows[0][1] = rows[1][0] = rows[1][1] = rgb0; const PixelInt rgb1 = convertYUVToColor(clipTable, format, codebook.y[1], codebook.u, codebook.v); rows[0][2] = rows[0][3] = rows[1][2] = rows[1][3] = rgb1; const PixelInt rgb2 = convertYUVToColor(clipTable, format, codebook.y[2], codebook.u, codebook.v); rows[2][0] = rows[2][1] = rows[3][0] = rows[3][1] = rgb2; const PixelInt rgb3 = convertYUVToColor(clipTable, format, codebook.y[3], codebook.u, codebook.v); rows[2][2] = rows[2][3] = rows[3][2] = rows[3][3] = rgb3; } template static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const Graphics::PixelFormat &format) { const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]]; rows[0][0] = convertYUVToColor(clipTable, format, codebook1.y[0], codebook1.u, codebook1.v); rows[0][1] = convertYUVToColor(clipTable, format, codebook1.y[1], codebook1.u, codebook1.v); rows[1][0] = convertYUVToColor(clipTable, format, codebook1.y[2], codebook1.u, codebook1.v); rows[1][1] = convertYUVToColor(clipTable, format, codebook1.y[3], codebook1.u, codebook1.v); const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]]; rows[0][2] = convertYUVToColor(clipTable, format, codebook2.y[0], codebook2.u, codebook2.v); rows[0][3] = convertYUVToColor(clipTable, format, codebook2.y[1], codebook2.u, codebook2.v); rows[1][2] = convertYUVToColor(clipTable, format, codebook2.y[2], codebook2.u, codebook2.v); rows[1][3] = convertYUVToColor(clipTable, format, codebook2.y[3], codebook2.u, codebook2.v); const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]]; rows[2][0] = convertYUVToColor(clipTable, format, codebook3.y[0], codebook3.u, codebook3.v); rows[2][1] = convertYUVToColor(clipTable, format, codebook3.y[1], codebook3.u, codebook3.v); rows[3][0] = convertYUVToColor(clipTable, format, codebook3.y[2], codebook3.u, codebook3.v); rows[3][1] = convertYUVToColor(clipTable, format, codebook3.y[3], codebook3.u, codebook3.v); const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]]; rows[2][2] = convertYUVToColor(clipTable, format, codebook4.y[0], codebook4.u, codebook4.v); rows[2][3] = convertYUVToColor(clipTable, format, codebook4.y[1], codebook4.u, codebook4.v); rows[3][2] = convertYUVToColor(clipTable, format, codebook4.y[2], codebook4.u, codebook4.v); rows[3][3] = convertYUVToColor(clipTable, format, codebook4.y[3], codebook4.u, codebook4.v); } }; /** * The default codebook converter for 8bpp: palettized output. */ struct CodebookConverterPalette { static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const Graphics::PixelFormat &format) { const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex]; rows[0][0] = rows[0][1] = rows[1][0] = rows[1][1] = codebook.y[0]; rows[0][2] = rows[0][3] = rows[1][2] = rows[1][3] = codebook.y[1]; rows[2][0] = rows[2][1] = rows[3][0] = rows[3][1] = codebook.y[2]; rows[2][2] = rows[2][3] = rows[3][2] = rows[3][3] = codebook.y[3]; } static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const Graphics::PixelFormat &format) { const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]]; rows[0][0] = codebook1.y[0]; rows[0][1] = codebook1.y[1]; rows[1][0] = codebook1.y[2]; rows[1][1] = codebook1.y[3]; const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]]; rows[0][2] = codebook2.y[0]; rows[0][3] = codebook2.y[1]; rows[1][2] = codebook2.y[2]; rows[1][3] = codebook2.y[3]; const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]]; rows[2][0] = codebook3.y[0]; rows[2][1] = codebook3.y[1]; rows[3][0] = codebook3.y[2]; rows[3][1] = codebook3.y[3]; const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]]; rows[2][2] = codebook4.y[0]; rows[2][3] = codebook4.y[1]; rows[3][2] = codebook4.y[2]; rows[3][3] = codebook4.y[3]; } }; /** * Codebook converter that dithers in QT-style or VFW-style */ struct CodebookConverterDithered { static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const Graphics::PixelFormat &format) { const uint32 *colorPtr = (const uint32 *)(strip.v1_dither + codebookIndex); *((uint32 *)rows[0]) = colorPtr[0]; *((uint32 *)rows[1]) = colorPtr[256]; *((uint32 *)rows[2]) = colorPtr[512]; *((uint32 *)rows[3]) = colorPtr[768]; } static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const Graphics::PixelFormat &format) { const uint16 *colorPtr = (const uint16 *)(strip.v4_dither + codebookIndex[0]); *((uint16 *)(rows[0] + 0)) = colorPtr[0]; *((uint16 *)(rows[1] + 0)) = colorPtr[1]; colorPtr = (const uint16 *)(strip.v4_dither + codebookIndex[1]); *((uint16 *)(rows[0] + 2)) = colorPtr[512]; *((uint16 *)(rows[1] + 2)) = colorPtr[513]; colorPtr = (const uint16 *)(strip.v4_dither + codebookIndex[2]); *((uint16 *)(rows[2] + 0)) = colorPtr[1024]; *((uint16 *)(rows[3] + 0)) = colorPtr[1025]; colorPtr = (const uint16 *)(strip.v4_dither + codebookIndex[3]); *((uint16 *)(rows[2] + 2)) = colorPtr[1536]; *((uint16 *)(rows[3] + 2)) = colorPtr[1537]; } }; template void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, 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, 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, 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(); } else { _pixelFormat = g_system->getScreenFormat(); // Default to a 32bpp format, if in 8bpp mode if (_pixelFormat.bytesPerPixel == 1) _pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); } // Create a lookup for the clip function // This dramatically improves the performance of the color conversion _clipTableBuf = new byte[1024]; for (uint i = 0; i < 1024; i++) { if (i <= 512) _clipTableBuf[i] = 0; else if (i >= 768) _clipTableBuf[i] = 255; else _clipTableBuf[i] = i - 512; } _clipTable = _clipTableBuf + 512; } CinepakDecoder::~CinepakDecoder() { if (_curFrame.surface) { _curFrame.surface->free(); delete _curFrame.surface; } delete[] _curFrame.strips; delete[] _clipTableBuf; delete[] _colorMap; delete[] _ditherPalette; } const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream &stream) { _curFrame.flags = stream.readByte(); _curFrame.length = (stream.readByte() << 16); _curFrame.length |= stream.readUint16BE(); _curFrame.width = stream.readUint16BE(); _curFrame.height = stream.readUint16BE(); _curFrame.stripCount = stream.readUint16BE(); if (!_curFrame.strips) { _curFrame.strips = new CinepakStrip[_curFrame.stripCount]; for (uint16 i = 0; i < _curFrame.stripCount; i++) { initializeCodebook(i, 1); initializeCodebook(i, 4); } } debug(4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); // Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless). // The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;) if (_curFrame.length != (uint32)stream.size()) { if (stream.readUint16BE() == 0xFE00) stream.readUint32BE(); else if ((stream.size() % _curFrame.length) == 0) stream.seek(-2, SEEK_CUR); } if (!_curFrame.surface) { _curFrame.surface = new Graphics::Surface(); _curFrame.surface->create(_curFrame.width, _curFrame.height, _pixelFormat); } _y = 0; 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]; } // Copy the dither tables memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * sizeof(uint32)); memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * sizeof(uint32)); } _curFrame.strips[i].id = stream.readUint16BE(); _curFrame.strips[i].length = stream.readUint16BE() - 12; // Subtract the 12 byte header _curFrame.strips[i].rect.top = _y; stream.readUint16BE(); // Ignore, substitute with our own. _curFrame.strips[i].rect.left = 0; stream.readUint16BE(); // Ignore, substitute with our own _curFrame.strips[i].rect.bottom = _y + stream.readUint16BE(); _curFrame.strips[i].rect.right = _curFrame.width; stream.readUint16BE(); // Ignore, substitute with our own // Sanity check. Because Cinepak is based on 4x4 blocks, the width and height of each strip needs to be divisible by 4. assert(!(_curFrame.strips[i].rect.width() % 4) && !(_curFrame.strips[i].rect.height() % 4)); uint32 pos = stream.pos(); while ((uint32)stream.pos() < (pos + _curFrame.strips[i].length) && !stream.eos()) { byte chunkID = stream.readByte(); if (stream.eos()) break; // Chunk Size is 24-bit, ignore the first 4 bytes uint32 chunkSize = stream.readByte() << 16; chunkSize += stream.readUint16BE() - 4; int32 startPos = stream.pos(); switch (chunkID) { case 0x20: case 0x21: case 0x24: case 0x25: loadCodebook(stream, i, 4, chunkID, chunkSize); break; case 0x22: case 0x23: case 0x26: case 0x27: loadCodebook(stream, i, 1, chunkID, chunkSize); break; case 0x30: case 0x31: case 0x32: if (_ditherPalette) ditherVectors(stream, i, chunkID, chunkSize); else if (_bitsPerPixel == 8) decodeVectors8(stream, i, chunkID, chunkSize); else decodeVectors24(stream, i, chunkID, chunkSize); break; default: warning("Unknown Cinepak chunk ID %02x", chunkID); return _curFrame.surface; } if (stream.pos() != startPos + (int32)chunkSize) stream.seek(startPos + chunkSize); } _y = _curFrame.strips[i].rect.bottom; } return _curFrame.surface; } void CinepakDecoder::initializeCodebook(uint16 strip, byte codebookType) { CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook; for (uint16 i = 0; i < 256; i++) { memset(codebook[i].y, 0, 4); codebook[i].u = 0; codebook[i].v = 0; if (_ditherType == kDitherTypeQT) ditherCodebookQT(strip, codebookType, i); else if (_ditherType == kDitherTypeVFW) ditherCodebookVFW(strip, codebookType, i); } } void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize) { CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook; int32 startPos = stream.pos(); uint32 flag = 0, mask = 0; for (uint16 i = 0; i < 256; i++) { if ((chunkID & 0x01) && !(mask >>= 1)) { if ((stream.pos() - startPos + 4) > (int32)chunkSize) break; flag = stream.readUint32BE(); mask = 0x80000000; } if (!(chunkID & 0x01) || (flag & mask)) { byte n = (chunkID & 0x04) ? 4 : 6; if ((stream.pos() - startPos + n) > (int32)chunkSize) break; stream.read(codebook[i].y, 4); if (n == 6) { codebook[i].u = stream.readSByte(); codebook[i].v = stream.readSByte(); } else { // This codebook type indicates either greyscale or // palettized video. For greyscale, default us to // 0 for both u and v. codebook[i].u = 0; codebook[i].v = 0; } // Dither the codebook if we're dithering for QuickTime if (_ditherType == kDitherTypeQT) ditherCodebookQT(strip, codebookType, i); else if (_ditherType == kDitherTypeVFW) ditherCodebookVFW(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 = (byte *)(_curFrame.strips[strip].v1_dither + codebookIndex); 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 = (byte *)(_curFrame.strips[strip].v4_dither + codebookIndex); 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]; } } static inline byte getRGBLookupEntry(const byte *colorMap, uint16 index) { return colorMap[s_defaultPaletteLookup[CLIP(index, 0, 1023)]]; } void CinepakDecoder::ditherCodebookVFW(uint16 strip, byte codebookType, uint16 codebookIndex) { if (codebookType == 1) { const CinepakCodebook &codebook = _curFrame.strips[strip].v1_codebook[codebookIndex]; byte *output = (byte *)(_curFrame.strips[strip].v1_dither + codebookIndex); 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]; output[0x000] = getRGBLookupEntry(_colorMap, pixelGroup1 & 0xFFFF); output[0x001] = getRGBLookupEntry(_colorMap, pixelGroup1 >> 16); output[0x002] = getRGBLookupEntry(_colorMap, pixelGroup2 & 0xFFFF); output[0x003] = getRGBLookupEntry(_colorMap, pixelGroup2 >> 16); output[0x400] = getRGBLookupEntry(_colorMap, pixelGroup3 & 0xFFFF); output[0x401] = getRGBLookupEntry(_colorMap, pixelGroup3 >> 16); output[0x402] = getRGBLookupEntry(_colorMap, pixelGroup4 & 0xFFFF); output[0x403] = getRGBLookupEntry(_colorMap, pixelGroup4 >> 16); output[0x800] = getRGBLookupEntry(_colorMap, pixelGroup5 >> 16); output[0x801] = getRGBLookupEntry(_colorMap, pixelGroup6 & 0xFFFF); output[0x802] = getRGBLookupEntry(_colorMap, pixelGroup7 >> 16); output[0x803] = getRGBLookupEntry(_colorMap, pixelGroup8 & 0xFFFF); output[0xC00] = getRGBLookupEntry(_colorMap, pixelGroup6 >> 16); output[0xC01] = getRGBLookupEntry(_colorMap, pixelGroup5 & 0xFFFF); output[0xC02] = getRGBLookupEntry(_colorMap, pixelGroup8 >> 16); output[0xC03] = getRGBLookupEntry(_colorMap, pixelGroup7 & 0xFFFF); } else { const CinepakCodebook &codebook = _curFrame.strips[strip].v4_codebook[codebookIndex]; byte *output = (byte *)(_curFrame.strips[strip].v4_dither + codebookIndex); 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]; output[0x000] = getRGBLookupEntry(_colorMap, pixelGroup1 & 0xFFFF); output[0x001] = getRGBLookupEntry(_colorMap, pixelGroup2 >> 16); output[0x400] = getRGBLookupEntry(_colorMap, pixelGroup5 & 0xFFFF); output[0x401] = getRGBLookupEntry(_colorMap, pixelGroup6 >> 16); output[0x002] = getRGBLookupEntry(_colorMap, pixelGroup3 & 0xFFFF); output[0x003] = getRGBLookupEntry(_colorMap, pixelGroup4 >> 16); output[0x402] = getRGBLookupEntry(_colorMap, pixelGroup7 & 0xFFFF); output[0x403] = getRGBLookupEntry(_colorMap, pixelGroup8 >> 16); output[0x800] = getRGBLookupEntry(_colorMap, pixelGroup1 >> 16); output[0x801] = getRGBLookupEntry(_colorMap, pixelGroup6 & 0xFFFF); output[0xC00] = getRGBLookupEntry(_colorMap, pixelGroup5 >> 16); output[0xC01] = getRGBLookupEntry(_colorMap, pixelGroup2 & 0xFFFF); output[0x802] = getRGBLookupEntry(_colorMap, pixelGroup3 >> 16); output[0x803] = getRGBLookupEntry(_colorMap, pixelGroup8 & 0xFFFF); output[0xC02] = getRGBLookupEntry(_colorMap, pixelGroup7 >> 16); output[0xC03] = getRGBLookupEntry(_colorMap, pixelGroup4 & 0xFFFF); } } void CinepakDecoder::decodeVectors8(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { decodeVectorsTmpl(_curFrame, _clipTable, stream, strip, chunkID, chunkSize); } void CinepakDecoder::decodeVectors24(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { if (_curFrame.surface->format.bytesPerPixel == 2) { decodeVectorsTmpl(_curFrame, _clipTable, stream, strip, chunkID, chunkSize); } else if (_curFrame.surface->format.bytesPerPixel == 4) { decodeVectorsTmpl(_curFrame, _clipTable, stream, strip, chunkID, chunkSize); } } bool CinepakDecoder::setOutputPixelFormat(const Graphics::PixelFormat &format) { if (_bitsPerPixel == 8) return false; _pixelFormat = format; return true; } bool CinepakDecoder::canDither(DitherType type) const { return (type == kDitherTypeVFW || type == kDitherTypeQT) && _bitsPerPixel == 24; } void CinepakDecoder::setDither(DitherType type, const byte *palette) { assert(canDither(type)); delete[] _colorMap; delete[] _ditherPalette; _ditherPalette = new byte[256 * 3]; memcpy(_ditherPalette, palette, 256 * 3); _dirtyPalette = true; _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); _ditherType = type; if (type == kDitherTypeVFW) { _colorMap = new byte[221]; 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); if (curDiffR > 0) { diff -= curDiffR; result = i; if (diff == 0) break; } } } } return result; } void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) { decodeVectorsTmpl(_curFrame, _clipTable, stream, strip, chunkID, chunkSize); } } // End of namespace Image