Initial commit
This commit is contained in:
143
image/codecs/bmp_raw.cpp
Normal file
143
image/codecs/bmp_raw.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/bmp_raw.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
BitmapRawDecoder::BitmapRawDecoder(int width, int height, int bitsPerPixel, bool ignoreAlpha, bool flip) : Codec(),
|
||||
_width(width), _height(height), _bitsPerPixel(bitsPerPixel), _ignoreAlpha(ignoreAlpha), _flip(flip) {
|
||||
_surface.create(_width, _height, getPixelFormat());
|
||||
}
|
||||
|
||||
BitmapRawDecoder::~BitmapRawDecoder() {
|
||||
_surface.free();
|
||||
}
|
||||
|
||||
const Graphics::Surface *BitmapRawDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
Graphics::PixelFormat format = getPixelFormat();
|
||||
|
||||
int srcPitch = _width * (_bitsPerPixel >> 3);
|
||||
int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
|
||||
|
||||
if (_bitsPerPixel == 1) {
|
||||
srcPitch = (_width + 7) / 8;
|
||||
extraDataLength = (srcPitch % 2) ? 2 - (srcPitch % 2) : 0;
|
||||
} else if (_bitsPerPixel == 4) {
|
||||
srcPitch = (_width + 1) / 2;
|
||||
extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
|
||||
}
|
||||
|
||||
if (_bitsPerPixel == 1) {
|
||||
for (int i = 0; i < _height; i++) {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, i);
|
||||
for (int j = 0; j != _width;) {
|
||||
byte color = stream.readByte();
|
||||
for (int k = 0; k < 8; k++) {
|
||||
*dst++ = (color & 0x80) ? 0x0f : 0x00;
|
||||
color <<= 1;
|
||||
j++;
|
||||
if (j == _width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.skip(extraDataLength);
|
||||
}
|
||||
} else if (_bitsPerPixel == 4) {
|
||||
for (int i = 0; i < _height; i++) {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, _height - i - 1);
|
||||
for (int j = 0; j < _width; j++) {
|
||||
byte color = stream.readByte();
|
||||
|
||||
*dst++ = (color & 0xf0) >> 4;
|
||||
j++;
|
||||
|
||||
if (j ==_width)
|
||||
break;
|
||||
|
||||
*dst++ = color & 0x0f;
|
||||
}
|
||||
|
||||
stream.skip(extraDataLength);
|
||||
}
|
||||
} else if (_bitsPerPixel == 8) {
|
||||
// flip the 8bpp images when we are decoding QTvideo
|
||||
byte *dst = (byte *)_surface.getPixels();
|
||||
|
||||
for (int i = 0; i < _height; i++) {
|
||||
stream.read(dst + (_flip ? i : _height - i - 1) * _width, _width);
|
||||
stream.skip(extraDataLength);
|
||||
}
|
||||
#ifndef SCUMM_LITTLE_ENDIAN
|
||||
} else if (_bitsPerPixel == 16) {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, _height - 1);
|
||||
|
||||
for (int i = 0; i < _height; i++) {
|
||||
for (int j = 0; j < _width; j++) {
|
||||
uint16 color = stream.readUint16LE();
|
||||
|
||||
*(uint16 *)dst = color;
|
||||
dst += format.bytesPerPixel;
|
||||
}
|
||||
|
||||
stream.skip(extraDataLength);
|
||||
dst -= _surface.pitch * 2;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, _height - 1);
|
||||
uint bpp = format.bytesPerPixel;
|
||||
|
||||
for (int i = 0; i < _height; i++) {
|
||||
stream.read(dst, _width * bpp);
|
||||
stream.skip(extraDataLength);
|
||||
dst -= _surface.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat BitmapRawDecoder::getPixelFormat() const {
|
||||
switch (_bitsPerPixel) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 8:
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
case 16:
|
||||
return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
|
||||
case 24:
|
||||
return Graphics::PixelFormat::createFormatBGR24();
|
||||
case 32:
|
||||
return Graphics::PixelFormat::createFormatBGRA32(!_ignoreAlpha);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
error("Unhandled BMP raw %dbpp", _bitsPerPixel);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
55
image/codecs/bmp_raw.h
Normal file
55
image/codecs/bmp_raw.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_BMP_RAW_H
|
||||
#define IMAGE_CODECS_BMP_RAW_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Bitmap raw image decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class BitmapRawDecoder : public Codec {
|
||||
public:
|
||||
BitmapRawDecoder(int width, int height, int bitsPerPixel, bool ignoreAlpha, bool flip = false);
|
||||
~BitmapRawDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
private:
|
||||
Graphics::Surface _surface;
|
||||
int _width, _height;
|
||||
int _bitsPerPixel;
|
||||
bool _ignoreAlpha;
|
||||
|
||||
// this flag indicates whether bitmapRawDecoder is created to decode QTvideo or raw images.
|
||||
// because we need to flip the image when we are dealing with QTvideo
|
||||
bool _flip;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
444
image/codecs/cdtoons.cpp
Normal file
444
image/codecs/cdtoons.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/cdtoons.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
struct CDToonsAction {
|
||||
uint16 blockId;
|
||||
Common::Rect rect;
|
||||
};
|
||||
|
||||
struct CDToonsDiff {
|
||||
byte *data;
|
||||
uint32 size;
|
||||
Common::Rect rect;
|
||||
};
|
||||
|
||||
static Common::Rect readRect(Common::SeekableReadStream &stream) {
|
||||
Common::Rect rect;
|
||||
rect.top = stream.readUint16BE();
|
||||
rect.left = stream.readUint16BE();
|
||||
rect.bottom = stream.readUint16BE();
|
||||
rect.right = stream.readUint16BE();
|
||||
return rect;
|
||||
}
|
||||
|
||||
CDToonsDecoder::CDToonsDecoder(uint16 width, uint16 height) : _palette(256) {
|
||||
debugN(5, "CDToons: width %d, height %d\n", width, height);
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_currentPaletteId = 0;
|
||||
_dirtyPalette = false;
|
||||
}
|
||||
|
||||
CDToonsDecoder::~CDToonsDecoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
|
||||
for (auto &block : _blocks)
|
||||
delete[] block._value.data;
|
||||
}
|
||||
|
||||
Graphics::Surface *CDToonsDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
uint16 u0 = stream.readUint16BE(); // always 9?
|
||||
uint16 frameId = stream.readUint16BE();
|
||||
uint16 blocksValidUntil = stream.readUint16BE();
|
||||
byte u6 = stream.readByte();
|
||||
byte backgroundColor = stream.readByte();
|
||||
debugN(5, "CDToons frame %d, size %d, unknown %04x (at 0), blocks valid until %d, unknown 6 is %02x, bkg color is %02x\n",
|
||||
frameId, (int)stream.size(), u0, blocksValidUntil, u6, backgroundColor);
|
||||
|
||||
Common::Rect clipRect = readRect(stream);
|
||||
debugN(9, "CDToons clipRect: (%d, %d) to (%d, %d)\n",
|
||||
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
|
||||
|
||||
Common::Rect dirtyRect = readRect(stream);
|
||||
debugN(9, "CDToons dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
|
||||
|
||||
uint32 flags = stream.readUint32BE();
|
||||
if (flags & 0x80)
|
||||
error("CDToons: frame already processed?");
|
||||
debugN(5, "CDToons flags: %08x\n", flags);
|
||||
|
||||
uint16 blockCount = stream.readUint16BE();
|
||||
uint16 blockOffset = stream.readUint16BE();
|
||||
debugN(9, "CDToons: %d blocks at 0x%04x\n",
|
||||
blockCount, blockOffset);
|
||||
|
||||
// max block id?
|
||||
uint16 u32 = stream.readUint16BE();
|
||||
debugN(5, "CDToons unknown at 32: %04x\n", u32);
|
||||
|
||||
byte actionCount = stream.readByte();
|
||||
byte u35 = stream.readByte();
|
||||
|
||||
uint16 paletteId = stream.readUint16BE();
|
||||
byte paletteSet = stream.readByte();
|
||||
debugN(9, "CDToons palette id %04x, palette byte %02x\n",
|
||||
paletteId, paletteSet);
|
||||
|
||||
byte u39 = stream.readByte();
|
||||
uint16 u40 = stream.readUint16BE();
|
||||
uint16 u42 = stream.readUint16BE();
|
||||
debugN(5, "CDToons: unknown at 35 is %02x, unknowns at 39: %02x, %04x, %04x\n",
|
||||
u35, u39, u40, u42);
|
||||
|
||||
Common::Array<CDToonsAction> actions;
|
||||
|
||||
for (uint i = 0; i < actionCount; i++) {
|
||||
CDToonsAction action;
|
||||
action.blockId = stream.readUint16BE();
|
||||
action.rect = readRect(stream);
|
||||
debugN(9, "CDToons action: render block %d at (%d, %d) to (%d, %d)\n",
|
||||
action.blockId, action.rect.left, action.rect.top, action.rect.right, action.rect.bottom);
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
if (stream.pos() > blockOffset)
|
||||
error("CDToons header ended at 0x%08x, but blocks should have started at 0x%08x",
|
||||
(int)stream.pos(), blockOffset);
|
||||
|
||||
if (stream.pos() != blockOffset)
|
||||
error("CDToons had %d unknown bytes after header", blockOffset - (int)stream.pos());
|
||||
|
||||
for (uint i = 0; i < blockCount; i++) {
|
||||
uint16 blockId = stream.readUint16BE();
|
||||
if (blockId >= 1200)
|
||||
error("CDToons: block id %d was too high", blockId);
|
||||
if (_blocks.contains(blockId))
|
||||
error("CDToons: new block %d was already seen", blockId);
|
||||
|
||||
CDToonsBlock block;
|
||||
block.flags = stream.readUint16BE();
|
||||
// flag 1 = palette, flag 2 = data?
|
||||
if (block.flags & 0x8000)
|
||||
error("CDToons: block already processed?");
|
||||
block.size = stream.readUint32BE();
|
||||
if (block.size < 14)
|
||||
error("CDToons: block size was %d, too small", block.size);
|
||||
block.size -= 14;
|
||||
block.startFrame = stream.readUint16BE();
|
||||
block.endFrame = stream.readUint16BE();
|
||||
block.unknown12 = stream.readUint16BE();
|
||||
block.data = new byte[block.size];
|
||||
stream.read(block.data, block.size);
|
||||
|
||||
debugN(9, "CDToons block id 0x%04x of size 0x%08x, flags %04x, from frame %d to %d, unknown at 12 is %04x\n",
|
||||
blockId, block.size, block.flags, block.startFrame, block.endFrame, block.unknown12);
|
||||
|
||||
_blocks[blockId] = block;
|
||||
}
|
||||
|
||||
byte xFrmBegin = 0, xFrmCount;
|
||||
Common::Array<CDToonsDiff> diffs;
|
||||
|
||||
while (true) {
|
||||
int32 nextPos = stream.pos();
|
||||
uint32 tag = stream.readUint32BE();
|
||||
uint32 size = stream.readUint32BE();
|
||||
nextPos += size;
|
||||
|
||||
switch (tag) {
|
||||
case MKTAG('D','i','f','f'):
|
||||
{
|
||||
debugN(5, "CDToons: Diff\n");
|
||||
uint16 count = stream.readUint16BE();
|
||||
|
||||
Common::Rect diffClipRect = readRect(stream);
|
||||
debugN(9, "CDToons diffClipRect: (%d, %d) to (%d, %d)\n",
|
||||
diffClipRect.left, diffClipRect.top, diffClipRect.right, diffClipRect.bottom);
|
||||
|
||||
debugN(5, "CDToons Diff: %d subentries\n", count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
CDToonsDiff diff;
|
||||
|
||||
diff.rect = readRect(stream);
|
||||
diff.size = stream.readUint32BE();
|
||||
if (diff.size < 20)
|
||||
error("CDToons: Diff block size was %d, too small", diff.size);
|
||||
|
||||
uint16 diffWidth = stream.readUint16BE();
|
||||
uint16 diffHeight = stream.readUint16BE();
|
||||
uint16 unknown16 = stream.readUint16BE();
|
||||
uint16 unknown18 = stream.readUint16BE();
|
||||
diff.size -= 8;
|
||||
|
||||
if (diffWidth != diff.rect.width() || diffHeight != diff.rect.height())
|
||||
error("CDToons: Diff sizes didn't match");
|
||||
debugN(5, "CDToons Diff: size %d, frame from (%d, %d) to (%d, %d), unknowns %04x, %04x\n",
|
||||
diff.size, diff.rect.left, diff.rect.top, diff.rect.right, diff.rect.bottom,
|
||||
unknown16, unknown18);
|
||||
|
||||
diff.data = new byte[diff.size];
|
||||
stream.read(diff.data, diff.size);
|
||||
diffs.push_back(diff);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MKTAG('X','F','r','m'):
|
||||
{
|
||||
debugN(5, "CDToons: XFrm\n");
|
||||
if (!(flags & 0x10))
|
||||
error("CDToons: useless XFrm?");
|
||||
|
||||
if (xFrmBegin)
|
||||
error("CDToons: duplicate XFrm");
|
||||
xFrmBegin = stream.readByte();
|
||||
xFrmCount = stream.readByte();
|
||||
debugN(9, "CDToons XFrm: run %d actions from %d\n", xFrmCount, xFrmBegin - 1);
|
||||
|
||||
// TODO: don't ignore (if xFrmCount is non-zero)
|
||||
Common::Rect dirtyRectXFrm = readRect(stream);
|
||||
debugN(9, "CDToons XFrm dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRectXFrm.left, dirtyRectXFrm.top, dirtyRectXFrm.right, dirtyRectXFrm.bottom);
|
||||
|
||||
// always zero?
|
||||
Common::Rect dirtyRect2XFrm = readRect(stream);
|
||||
debugN(9, "CDToons XFrm dirtyRect2: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRect2XFrm.left, dirtyRect2XFrm.top, dirtyRect2XFrm.right, dirtyRect2XFrm.bottom);
|
||||
}
|
||||
break;
|
||||
case MKTAG('M','r','k','s'):
|
||||
debugN(5, "CDToons: Mrks\n");
|
||||
if (!(flags & 0x2))
|
||||
error("CDToons: useless Mrks?");
|
||||
|
||||
// TODO
|
||||
warning("CDToons: encountered Mrks, not implemented yet");
|
||||
break;
|
||||
case MKTAG('S','c','a','l'):
|
||||
// TODO
|
||||
warning("CDToons: encountered Scal, not implemented yet");
|
||||
break;
|
||||
case MKTAG('W','r','M','p'):
|
||||
warning("CDToons: encountered WrMp, ignoring");
|
||||
break;
|
||||
case MKTAG('F','r','t','R'):
|
||||
{
|
||||
debugN(5, "CDToons: FrtR\n");
|
||||
if (!(flags & 0x40))
|
||||
error("CDToons: useless FrtR?");
|
||||
|
||||
uint16 count = stream.readUint16BE();
|
||||
debugN(9, "CDToons FrtR: %d dirty rectangles\n", count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
Common::Rect dirtyRectFrtR = readRect(stream);
|
||||
debugN(9, "CDToons FrtR dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRectFrtR.left, dirtyRectFrtR.top, dirtyRectFrtR.right, dirtyRectFrtR.bottom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MKTAG('B','c','k','R'):
|
||||
{
|
||||
debugN(5, "CDToons: BckR\n");
|
||||
if (!(flags & 0x20))
|
||||
error("CDToons: useless BckR?");
|
||||
|
||||
uint16 count = stream.readUint16BE();
|
||||
debugN(9, "CDToons BckR: %d subentries\n", count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
Common::Rect dirtyRectBckR = readRect(stream);
|
||||
debugN(9, "CDToons BckR dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRectBckR.left, dirtyRectBckR.top, dirtyRectBckR.right, dirtyRectBckR.bottom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("Unknown CDToons tag '%s'", tag2str(tag));
|
||||
}
|
||||
|
||||
if (stream.pos() > nextPos)
|
||||
error("CDToons ran off the end of a block while reading it (at %d, next block at %d)",
|
||||
(int)stream.pos(), nextPos);
|
||||
if (stream.pos() != nextPos) {
|
||||
warning("CDToons had %d unknown bytes after block", nextPos - (int32)stream.pos());
|
||||
stream.seek(nextPos);
|
||||
}
|
||||
|
||||
if (stream.pos() == stream.size())
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < diffs.size(); i++) {
|
||||
renderBlock(diffs[i].data, diffs[i].size, diffs[i].rect.left, diffs[i].rect.top, diffs[i].rect.width(), diffs[i].rect.height());
|
||||
delete[] diffs[i].data;
|
||||
}
|
||||
if (!diffs.empty())
|
||||
return _surface;
|
||||
|
||||
for (uint i = 0; i < actions.size(); i++) {
|
||||
CDToonsAction &action = actions[i];
|
||||
if (i == 0 && action.blockId == 0)
|
||||
memset(_surface->getPixels(), backgroundColor, _surface->w * _surface->h);
|
||||
if (!_blocks.contains(action.blockId))
|
||||
continue;
|
||||
if (!action.rect.right)
|
||||
continue;
|
||||
if (i == 0 && !diffs.empty())
|
||||
continue;
|
||||
|
||||
CDToonsBlock &block = _blocks[action.blockId];
|
||||
uint16 width = READ_BE_UINT16(block.data + 2);
|
||||
uint16 height = READ_BE_UINT16(block.data);
|
||||
|
||||
renderBlock(block.data + 14, block.size - 14, action.rect.left, action.rect.top, width, height);
|
||||
}
|
||||
|
||||
if (paletteId && _currentPaletteId != paletteId) {
|
||||
if (!_blocks.contains(paletteId))
|
||||
error("CDToons: no block for palette %04x", paletteId);
|
||||
if (_blocks[paletteId].size != 2 * 3 * 256)
|
||||
error("CDToons: palette %04x is wrong size (%d)", paletteId, _blocks[paletteId].size);
|
||||
|
||||
_currentPaletteId = paletteId;
|
||||
if (!paletteSet)
|
||||
setPalette(_blocks[paletteId].data);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void CDToonsDecoder::renderBlock(byte *data, uint dataSize, int destX, int destY, uint width, uint height) {
|
||||
byte *currData = data;
|
||||
byte *dataEnd = data + dataSize;
|
||||
|
||||
debugN(9, "CDToons renderBlock at (%d, %d), width %d, height %d\n",
|
||||
destX, destY, width, height);
|
||||
|
||||
if (destX + (int)width > _surface->w)
|
||||
width = _surface->w - destX;
|
||||
if (destY + (int)height > _surface->h)
|
||||
height = _surface->h - destY;
|
||||
|
||||
uint skip = 0;
|
||||
if (destX < 0) {
|
||||
skip = -destX;
|
||||
if (width <= skip)
|
||||
return;
|
||||
width -= skip;
|
||||
destX = 0;
|
||||
}
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
if (destY + (int)y >= _surface->h)
|
||||
break;
|
||||
|
||||
if (currData + 2 > dataEnd)
|
||||
error("CDToons renderBlock overran whole data by %d bytes", (uint32)(currData - dataEnd));
|
||||
|
||||
uint16 lineSize = READ_BE_UINT16(currData);
|
||||
currData += 2;
|
||||
byte *nextLine = currData + lineSize;
|
||||
|
||||
if (nextLine > dataEnd)
|
||||
error("CDToons renderBlock was going to overrun data by %d bytes (line size %d)",
|
||||
(uint32)(nextLine - dataEnd), (uint32)(nextLine - currData));
|
||||
|
||||
if (destY + (int)y < 0) {
|
||||
currData = nextLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
byte *pixels = (byte *)_surface->getBasePtr(destX, destY + y);
|
||||
|
||||
int leftToSkip = skip;
|
||||
uint x = 0;
|
||||
bool done = false;
|
||||
while (x < width && !done) {
|
||||
int size = (uint)*currData;
|
||||
currData++;
|
||||
bool raw = !(size & 0x80);
|
||||
size = (size & 0x7f) + 1;
|
||||
|
||||
if (leftToSkip) {
|
||||
if (leftToSkip >= size) {
|
||||
leftToSkip -= size;
|
||||
if (raw)
|
||||
currData += size;
|
||||
else
|
||||
currData++;
|
||||
continue;
|
||||
} else {
|
||||
size -= leftToSkip;
|
||||
if (raw)
|
||||
currData += leftToSkip;
|
||||
leftToSkip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (x + size >= width) {
|
||||
size = width - x;
|
||||
done = true;
|
||||
}
|
||||
if (destX + (int)x + size >= (int)_surface->w) {
|
||||
size = MIN<int>((int)_surface->w - destX - (int)x, width - x);
|
||||
done = true;
|
||||
}
|
||||
if (size <= 0) {
|
||||
size = 0;
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
memcpy(pixels + x, currData, size);
|
||||
currData += size;
|
||||
x += size;
|
||||
} else {
|
||||
byte color = *currData;
|
||||
currData++;
|
||||
if (color) {
|
||||
memset(pixels + x, color, size);
|
||||
}
|
||||
x += size;
|
||||
}
|
||||
|
||||
if (currData > nextLine) {
|
||||
warning("CDToons renderBlock overran line by %d bytes", (uint32)(currData - nextLine));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currData = nextLine;
|
||||
}
|
||||
}
|
||||
|
||||
void CDToonsDecoder::setPalette(byte *data) {
|
||||
_dirtyPalette = true;
|
||||
|
||||
// A lovely QuickTime palette
|
||||
for (uint i = 0; i < 256; i++) {
|
||||
_palette.set(i, *data, *(data + 2), *(data + 4));
|
||||
data += 6;
|
||||
}
|
||||
|
||||
_palette.set(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
71
image/codecs/cdtoons.h
Normal file
71
image/codecs/cdtoons.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CDTOONS_H
|
||||
#define IMAGE_CODECS_CDTOONS_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
struct CDToonsBlock {
|
||||
uint16 flags;
|
||||
uint32 size;
|
||||
uint16 startFrame;
|
||||
uint16 endFrame;
|
||||
uint16 unknown12;
|
||||
byte *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Broderbund CDToons decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class CDToonsDecoder : public Codec {
|
||||
public:
|
||||
CDToonsDecoder(uint16 width, uint16 height);
|
||||
~CDToonsDecoder() override;
|
||||
|
||||
Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
bool containsPalette() const override { return true; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _palette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
Graphics::Palette _palette;
|
||||
bool _dirtyPalette;
|
||||
uint16 _currentPaletteId;
|
||||
|
||||
Common::HashMap<uint16, CDToonsBlock> _blocks;
|
||||
|
||||
void renderBlock(byte *data, uint size, int x, int y, uint width, uint height);
|
||||
void setPalette(byte *data);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
696
image/codecs/cinepak.cpp
Normal file
696
image/codecs/cinepak.cpp
Normal file
@@ -0,0 +1,696 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/cinepak.h"
|
||||
#include "image/codecs/cinepak_tables.h"
|
||||
#include "image/codecs/dither.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 * 2)];
|
||||
g = clipTable[y - (u >> 1) - v];
|
||||
b = clipTable[y + (u * 2)];
|
||||
}
|
||||
|
||||
inline uint32 convertYUVToColor(const byte *clipTable, const Graphics::PixelFormat &format, byte y, int8 u, int8 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<typename PixelInt>
|
||||
static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, PixelInt *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
|
||||
const int8 u = codebook.u, v = codebook.v;
|
||||
|
||||
const PixelInt rgb0 = convertYUVToColor(clipTable, format, codebook.y[0], u, v);
|
||||
const PixelInt rgb1 = convertYUVToColor(clipTable, format, codebook.y[1], u, v);
|
||||
|
||||
dst[0] = dst[1] = rgb0;
|
||||
dst[2] = dst[3] = rgb1;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = dst[1] = rgb0;
|
||||
dst[2] = dst[3] = rgb1;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
const PixelInt rgb2 = convertYUVToColor(clipTable, format, codebook.y[2], u, v);
|
||||
const PixelInt rgb3 = convertYUVToColor(clipTable, format, codebook.y[3], u, v);
|
||||
|
||||
dst[0] = dst[1] = rgb2;
|
||||
dst[2] = dst[3] = rgb3;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = dst[1] = rgb2;
|
||||
dst[2] = dst[3] = rgb3;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
}
|
||||
|
||||
template<typename PixelInt>
|
||||
static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, PixelInt *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]];
|
||||
const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]];
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook1.y[0], codebook1.u, codebook1.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook1.y[1], codebook1.u, codebook1.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook2.y[0], codebook2.u, codebook2.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook2.y[1], codebook2.u, codebook2.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook1.y[2], codebook1.u, codebook1.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook1.y[3], codebook1.u, codebook1.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook2.y[2], codebook2.u, codebook2.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook2.y[3], codebook2.u, codebook2.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]];
|
||||
const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]];
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook3.y[0], codebook3.u, codebook3.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook3.y[1], codebook3.u, codebook3.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook4.y[0], codebook4.u, codebook4.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook4.y[1], codebook4.u, codebook4.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook3.y[2], codebook3.u, codebook3.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook3.y[3], codebook3.u, codebook3.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook4.y[2], codebook4.u, codebook4.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook4.y[3], codebook4.u, codebook4.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The default codebook converter for 8bpp: palettized output.
|
||||
*/
|
||||
struct CodebookConverterPalette {
|
||||
static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
|
||||
|
||||
byte y0 = codebook.y[0], y1 = codebook.y[1];
|
||||
byte y2 = codebook.y[2], y3 = codebook.y[3];
|
||||
|
||||
dst[0] = dst[1] = y0;
|
||||
dst[2] = dst[3] = y1;
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = dst[1] = y0;
|
||||
dst[2] = dst[3] = y1;
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = dst[1] = y2;
|
||||
dst[2] = dst[3] = y3;
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = dst[1] = y2;
|
||||
dst[2] = dst[3] = y3;
|
||||
dst += dstPitch;
|
||||
}
|
||||
|
||||
static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]];
|
||||
const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]];
|
||||
|
||||
dst[0] = codebook1.y[0];
|
||||
dst[1] = codebook1.y[1];
|
||||
dst[2] = codebook2.y[0];
|
||||
dst[3] = codebook2.y[1];
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = codebook1.y[2];
|
||||
dst[1] = codebook1.y[3];
|
||||
dst[2] = codebook2.y[2];
|
||||
dst[3] = codebook2.y[3];
|
||||
dst += dstPitch;
|
||||
|
||||
const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]];
|
||||
const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]];
|
||||
|
||||
dst[0] = codebook3.y[0];
|
||||
dst[1] = codebook3.y[1];
|
||||
dst[2] = codebook4.y[0];
|
||||
dst[3] = codebook4.y[1];
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = codebook3.y[2];
|
||||
dst[1] = codebook3.y[3];
|
||||
dst[2] = codebook4.y[2];
|
||||
dst[3] = codebook4.y[3];
|
||||
dst += dstPitch;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Codebook converter that dithers in QT-style or VFW-style
|
||||
*/
|
||||
struct CodebookConverterDithered {
|
||||
static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const uint32 *colorPtr = (const uint32 *)(strip.v1_dither + codebookIndex);
|
||||
|
||||
*((uint32 *)dst) = colorPtr[0];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint32 *)dst) = colorPtr[256];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint32 *)dst) = colorPtr[512];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint32 *)dst) = colorPtr[768];
|
||||
dst += dstPitch;
|
||||
}
|
||||
|
||||
static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const uint16 *colorPtr1 = (const uint16 *)(strip.v4_dither + codebookIndex[0]);
|
||||
const uint16 *colorPtr2 = (const uint16 *)(strip.v4_dither + codebookIndex[1]);
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr1[0];
|
||||
*((uint16 *)(dst + 2)) = colorPtr2[512];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr1[1];
|
||||
*((uint16 *)(dst + 2)) = colorPtr2[513];
|
||||
dst += dstPitch;
|
||||
|
||||
const uint16 *colorPtr3 = (const uint16 *)(strip.v4_dither + codebookIndex[2]);
|
||||
const uint16 *colorPtr4 = (const uint16 *)(strip.v4_dither + codebookIndex[3]);
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr3[1024];
|
||||
*((uint16 *)(dst + 2)) = colorPtr4[1536];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr3[1025];
|
||||
*((uint16 *)(dst + 2)) = colorPtr4[1537];
|
||||
dst += dstPitch;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename PixelInt, typename CodebookConverter>
|
||||
void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
|
||||
uint32 flag = 0, mask = 0;
|
||||
int32 startPos = stream.pos();
|
||||
PixelInt *dst;
|
||||
|
||||
for (uint16 y = frame.strips[strip].rect.top; y < frame.strips[strip].rect.bottom; y += 4) {
|
||||
dst = (PixelInt *)frame.surface->getBasePtr(frame.strips[strip].rect.left, + y);
|
||||
|
||||
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], dst, frame.surface->pitch, 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], dst, frame.surface->pitch, clipTable, frame.surface->format);
|
||||
}
|
||||
}
|
||||
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPerPixel), _ditherPalette(0) {
|
||||
_curFrame.surface = 0;
|
||||
_curFrame.strips = 0;
|
||||
_y = 0;
|
||||
_colorMap = 0;
|
||||
_ditherType = kDitherTypeUnknown;
|
||||
|
||||
if (bitsPerPixel == 8) {
|
||||
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
|
||||
} else {
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
debugC(kDebugLevelGVideo, 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.size() > 0)
|
||||
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[CLIP<int>(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<byte, CodebookConverterPalette>(_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<uint16, CodebookConverterRGB>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
} else if (_curFrame.surface->format.bytesPerPixel == 4) {
|
||||
decodeVectorsTmpl<uint32, CodebookConverterRGB>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool CinepakDecoder::setOutputPixelFormat(const Graphics::PixelFormat &format) {
|
||||
if (_bitsPerPixel == 8)
|
||||
return format.isCLUT8();
|
||||
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
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;
|
||||
|
||||
_ditherPalette.resize(256, false);
|
||||
_ditherPalette.set(palette, 0, 256);
|
||||
|
||||
_dirtyPalette = true;
|
||||
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
|
||||
_ditherType = type;
|
||||
|
||||
if (type == kDitherTypeVFW) {
|
||||
_colorMap = new byte[1024];
|
||||
|
||||
for (int i = 0; i < 1024; i++)
|
||||
_colorMap[i] = findNearestRGB(s_defaultPaletteLookup[i]);
|
||||
} else {
|
||||
// Generate QuickTime dither table
|
||||
// 4 blocks of 0x4000 bytes (RGB554 lookup)
|
||||
_colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
|
||||
}
|
||||
}
|
||||
|
||||
byte CinepakDecoder::findNearestRGB(int index) const {
|
||||
byte r = s_defaultPalette[index * 3];
|
||||
byte g = s_defaultPalette[index * 3 + 1];
|
||||
byte b = s_defaultPalette[index * 3 + 2];
|
||||
|
||||
return _ditherPalette.findBestColor(r, g, b, Graphics::kColorDistanceEuclidean);
|
||||
}
|
||||
|
||||
void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
|
||||
decodeVectorsTmpl<byte, CodebookConverterDithered>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
111
image/codecs/cinepak.h
Normal file
111
image/codecs/cinepak.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CINEPAK_H
|
||||
#define IMAGE_CODECS_CINEPAK_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
struct CinepakCodebook {
|
||||
// These are not in the normal YUV colorspace, but in the Cinepak YUV colorspace instead.
|
||||
byte y[4]; // [0, 255]
|
||||
int8 u, v; // [-128, 127]
|
||||
};
|
||||
|
||||
struct CinepakStrip {
|
||||
uint16 id;
|
||||
uint16 length;
|
||||
Common::Rect rect;
|
||||
CinepakCodebook v1_codebook[256], v4_codebook[256];
|
||||
uint32 v1_dither[256 * 4 * 4], v4_dither[256 * 4 * 4];
|
||||
};
|
||||
|
||||
struct CinepakFrame {
|
||||
byte flags;
|
||||
uint32 length;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
uint16 stripCount;
|
||||
CinepakStrip *strips;
|
||||
|
||||
Graphics::Surface *surface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cinepak decoder.
|
||||
*
|
||||
* Used by BMP/AVI and PICT/QuickTime.
|
||||
*
|
||||
* Used in engines:
|
||||
* - sherlock
|
||||
*/
|
||||
class CinepakDecoder : public Codec {
|
||||
public:
|
||||
CinepakDecoder(int bitsPerPixel = 24);
|
||||
~CinepakDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override;
|
||||
|
||||
bool containsPalette() const override { return _ditherPalette != 0; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _ditherPalette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
|
||||
private:
|
||||
CinepakFrame _curFrame;
|
||||
int32 _y;
|
||||
int _bitsPerPixel;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
byte *_clipTable, *_clipTableBuf;
|
||||
|
||||
Graphics::Palette _ditherPalette;
|
||||
bool _dirtyPalette;
|
||||
byte *_colorMap;
|
||||
DitherType _ditherType;
|
||||
|
||||
void initializeCodebook(uint16 strip, byte codebookType);
|
||||
void loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize);
|
||||
void decodeVectors8(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
|
||||
void decodeVectors24(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);
|
||||
void ditherCodebookVFW(uint16 strip, byte codebookType, uint16 codebookIndex);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
779
image/codecs/cinepak_tables.h
Normal file
779
image/codecs/cinepak_tables.h
Normal file
@@ -0,0 +1,779 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CINEPAK_TABLES_H
|
||||
#define IMAGE_CODECS_CINEPAK_TABLES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
static const byte s_defaultPaletteLookup[1024] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x08, 0x08, 0x08, 0x09, 0x0A, 0x07, 0x07, 0x07,
|
||||
0x0B, 0x0B, 0x0B, 0x0C, 0x0D, 0x0D, 0x0D, 0x07,
|
||||
0x0E, 0x0E, 0x0E, 0x0F, 0x0D, 0x0D, 0x0D, 0x0D,
|
||||
0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x0D, 0x0D,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x17, 0x17, 0x17, 0x18, 0x19, 0x1A, 0x16, 0x16,
|
||||
0x1B, 0x1B, 0x1B, 0x1C, 0x1D, 0x1E, 0x1E, 0x1E,
|
||||
0x1F, 0x1F, 0x1F, 0x20, 0x21, 0x1E, 0x1E, 0x1E,
|
||||
0x22, 0x22, 0x22, 0x23, 0x24, 0x24, 0x24, 0x1E,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x2A, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2E,
|
||||
0x2F, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x2E, 0x2E,
|
||||
0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
|
||||
0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
|
||||
0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
|
||||
0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
|
||||
0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
|
||||
0x3C, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x40, 0x40,
|
||||
0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x45, 0x45,
|
||||
0x46, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4A, 0x4A,
|
||||
0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
|
||||
0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
|
||||
0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
|
||||
0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
|
||||
0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
|
||||
0x55, 0x55, 0x56, 0x57, 0x58, 0x59, 0x59, 0x59,
|
||||
0x5A, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5E, 0x5E,
|
||||
0x5F, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x63, 0x63,
|
||||
0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
|
||||
0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
|
||||
0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
|
||||
0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
|
||||
0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
|
||||
0x6E, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x72, 0x72,
|
||||
0x73, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77, 0x77,
|
||||
0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7C,
|
||||
0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
|
||||
0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
|
||||
0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
|
||||
0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
|
||||
0x87, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8B, 0x8B,
|
||||
0x8C, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x90, 0x90,
|
||||
0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x95, 0x95,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
|
||||
0x9B, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
|
||||
0x9E, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
|
||||
0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1,
|
||||
0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5,
|
||||
0xA6, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xA9, 0xA9,
|
||||
0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
|
||||
0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
|
||||
0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
|
||||
0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
|
||||
0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
|
||||
0xB4, 0xB1, 0xB1, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3,
|
||||
0xB4, 0xB4, 0xB5, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7,
|
||||
0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0xC2, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
|
||||
0xC2, 0xC2, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
|
||||
0xC2, 0xC2, 0xC2, 0xC3, 0xC4, 0xC4, 0xC4, 0xC4,
|
||||
0xC8, 0xC5, 0xC5, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7,
|
||||
0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB,
|
||||
0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
|
||||
0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
|
||||
0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
|
||||
0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
|
||||
0xD2, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
|
||||
0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
|
||||
0xD2, 0xD2, 0xD2, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
|
||||
0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
|
||||
0xD8, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
|
||||
0xD8, 0xD8, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC
|
||||
};
|
||||
|
||||
static const byte s_defaultPalette[221 * 3] = {
|
||||
0x02, 0x02, 0x02,
|
||||
0x19, 0x19, 0x19,
|
||||
0x47, 0x02, 0x19,
|
||||
0x19, 0x0D, 0x47,
|
||||
0x00, 0x51, 0x00,
|
||||
0x2E, 0x3A, 0x00,
|
||||
0x5C, 0x23, 0x00,
|
||||
0x8F, 0x0A, 0x00,
|
||||
0x00, 0x45, 0x2E,
|
||||
0x2E, 0x2E, 0x2E,
|
||||
0x5C, 0x17, 0x2E,
|
||||
0x00, 0x3A, 0x5C,
|
||||
0x2E, 0x23, 0x5C,
|
||||
0x5C, 0x0C, 0x5C,
|
||||
0x00, 0x2C, 0x95,
|
||||
0x2E, 0x15, 0x95,
|
||||
0x00, 0x1A, 0xDC,
|
||||
0x2E, 0x03, 0xDC,
|
||||
0x15, 0x66, 0x15,
|
||||
0x43, 0x4F, 0x15,
|
||||
0x71, 0x38, 0x15,
|
||||
0xA4, 0x1E, 0x15,
|
||||
0xDB, 0x02, 0x15,
|
||||
0x15, 0x5A, 0x43,
|
||||
0x43, 0x43, 0x43,
|
||||
0x71, 0x2C, 0x43,
|
||||
0xA4, 0x13, 0x43,
|
||||
0x15, 0x4F, 0x71,
|
||||
0x43, 0x38, 0x71,
|
||||
0x71, 0x21, 0x71,
|
||||
0xA4, 0x07, 0x71,
|
||||
0x15, 0x40, 0xAA,
|
||||
0x43, 0x29, 0xAA,
|
||||
0x71, 0x12, 0xAA,
|
||||
0x15, 0x2F, 0xF1,
|
||||
0x43, 0x18, 0xF1,
|
||||
0x71, 0x01, 0xF1,
|
||||
0x29, 0x79, 0x29,
|
||||
0x57, 0x62, 0x29,
|
||||
0x85, 0x4B, 0x29,
|
||||
0xB7, 0x32, 0x29,
|
||||
0xEF, 0x16, 0x29,
|
||||
0x29, 0x6E, 0x57,
|
||||
0x57, 0x57, 0x57,
|
||||
0x85, 0x40, 0x57,
|
||||
0xB7, 0x27, 0x57,
|
||||
0xEF, 0x0B, 0x57,
|
||||
0x29, 0x62, 0x85,
|
||||
0x57, 0x4B, 0x85,
|
||||
0x85, 0x34, 0x85,
|
||||
0xB7, 0x1B, 0x85,
|
||||
0x29, 0x54, 0xBE,
|
||||
0x57, 0x3D, 0xBE,
|
||||
0x85, 0x26, 0xBE,
|
||||
0xB7, 0x0D, 0xBE,
|
||||
0x03, 0xB5, 0x09,
|
||||
0x3C, 0x99, 0x09,
|
||||
0x6A, 0x82, 0x09,
|
||||
0x98, 0x6B, 0x09,
|
||||
0xCA, 0x51, 0x09,
|
||||
0x03, 0xA9, 0x3C,
|
||||
0x3C, 0x8C, 0x3C,
|
||||
0x6A, 0x75, 0x3C,
|
||||
0x98, 0x5E, 0x3C,
|
||||
0xCA, 0x45, 0x3C,
|
||||
0x03, 0x9D, 0x6A,
|
||||
0x3C, 0x81, 0x6A,
|
||||
0x6A, 0x6A, 0x6A,
|
||||
0x98, 0x53, 0x6A,
|
||||
0xCA, 0x39, 0x6A,
|
||||
0x03, 0x92, 0x98,
|
||||
0x3C, 0x75, 0x98,
|
||||
0x6A, 0x5E, 0x98,
|
||||
0x98, 0x47, 0x98,
|
||||
0xCA, 0x2E, 0x98,
|
||||
0x03, 0x83, 0xD1,
|
||||
0x3C, 0x67, 0xD1,
|
||||
0x6A, 0x50, 0xD1,
|
||||
0x98, 0x39, 0xD1,
|
||||
0xCA, 0x20, 0xD1,
|
||||
0x14, 0xC7, 0x1B,
|
||||
0x4D, 0xAB, 0x1B,
|
||||
0x7B, 0x94, 0x1B,
|
||||
0xA9, 0x7D, 0x1B,
|
||||
0xDC, 0x63, 0x1B,
|
||||
0x14, 0xBA, 0x4D,
|
||||
0x4D, 0x9E, 0x4D,
|
||||
0x7B, 0x87, 0x4D,
|
||||
0xA9, 0x70, 0x4D,
|
||||
0xDC, 0x57, 0x4D,
|
||||
0x14, 0xAF, 0x7B,
|
||||
0x4D, 0x92, 0x7B,
|
||||
0x7B, 0x7B, 0x7B,
|
||||
0xA9, 0x64, 0x7B,
|
||||
0xDC, 0x4B, 0x7B,
|
||||
0x14, 0xA3, 0xA9,
|
||||
0x4D, 0x87, 0xA9,
|
||||
0x7B, 0x70, 0xA9,
|
||||
0xA9, 0x59, 0xA9,
|
||||
0xDC, 0x40, 0xA9,
|
||||
0x14, 0x95, 0xE2,
|
||||
0x4D, 0x79, 0xE2,
|
||||
0x7B, 0x62, 0xE2,
|
||||
0xA9, 0x4B, 0xE2,
|
||||
0xDC, 0x31, 0xE2,
|
||||
0x25, 0xD8, 0x2C,
|
||||
0x5E, 0xBB, 0x2C,
|
||||
0x8C, 0xA4, 0x2C,
|
||||
0xBA, 0x8D, 0x2C,
|
||||
0xED, 0x74, 0x2C,
|
||||
0x25, 0xCB, 0x5E,
|
||||
0x5E, 0xAF, 0x5E,
|
||||
0x8C, 0x98, 0x5E,
|
||||
0xBA, 0x81, 0x5E,
|
||||
0xED, 0x67, 0x5E,
|
||||
0x25, 0xC0, 0x8C,
|
||||
0x5E, 0xA3, 0x8C,
|
||||
0x8C, 0x8C, 0x8C,
|
||||
0xBA, 0x75, 0x8C,
|
||||
0xED, 0x5C, 0x8C,
|
||||
0x25, 0xB4, 0xBA,
|
||||
0x5E, 0x98, 0xBA,
|
||||
0x8C, 0x81, 0xBA,
|
||||
0xBA, 0x6A, 0xBA,
|
||||
0xED, 0x50, 0xBA,
|
||||
0x25, 0xA6, 0xF3,
|
||||
0x5E, 0x8A, 0xF3,
|
||||
0x8C, 0x73, 0xF3,
|
||||
0xBA, 0x5C, 0xF3,
|
||||
0xED, 0x42, 0xF3,
|
||||
0x35, 0xF6, 0x04,
|
||||
0x6E, 0xD9, 0x04,
|
||||
0x9C, 0xC2, 0x04,
|
||||
0xCA, 0xAB, 0x04,
|
||||
0xFD, 0x92, 0x04,
|
||||
0x35, 0xE8, 0x3C,
|
||||
0x6E, 0xCB, 0x3C,
|
||||
0x9C, 0xB4, 0x3C,
|
||||
0xCA, 0x9D, 0x3C,
|
||||
0xFD, 0x84, 0x3C,
|
||||
0x35, 0xDB, 0x6E,
|
||||
0x6E, 0xBF, 0x6E,
|
||||
0x9C, 0xA8, 0x6E,
|
||||
0xCA, 0x91, 0x6E,
|
||||
0xFD, 0x78, 0x6E,
|
||||
0x35, 0xD0, 0x9C,
|
||||
0x6E, 0xB3, 0x9C,
|
||||
0x9C, 0x9C, 0x9C,
|
||||
0xCA, 0x85, 0x9C,
|
||||
0xFD, 0x6C, 0x9C,
|
||||
0x35, 0xC4, 0xCA,
|
||||
0x6E, 0xA8, 0xCA,
|
||||
0x9C, 0x91, 0xCA,
|
||||
0xCA, 0x7A, 0xCA,
|
||||
0xFD, 0x61, 0xCA,
|
||||
0x7E, 0xE9, 0x13,
|
||||
0xAC, 0xD2, 0x13,
|
||||
0xDA, 0xBB, 0x13,
|
||||
0x45, 0xF7, 0x4B,
|
||||
0x7E, 0xDB, 0x4B,
|
||||
0xAC, 0xC4, 0x4B,
|
||||
0xDA, 0xAD, 0x4B,
|
||||
0x45, 0xEB, 0x7E,
|
||||
0x7E, 0xCE, 0x7E,
|
||||
0xAC, 0xB7, 0x7E,
|
||||
0xDA, 0xA0, 0x7E,
|
||||
0x45, 0xDF, 0xAC,
|
||||
0x7E, 0xC3, 0xAC,
|
||||
0xAC, 0xAC, 0xAC,
|
||||
0xDA, 0x95, 0xAC,
|
||||
0x45, 0xD4, 0xDA,
|
||||
0x7E, 0xB7, 0xDA,
|
||||
0xAC, 0xA0, 0xDA,
|
||||
0xDA, 0x89, 0xDA,
|
||||
0x8C, 0xF7, 0x22,
|
||||
0xBA, 0xE0, 0x22,
|
||||
0xE8, 0xC9, 0x22,
|
||||
0x8C, 0xE9, 0x59,
|
||||
0xBA, 0xD2, 0x59,
|
||||
0xE8, 0xBB, 0x59,
|
||||
0x53, 0xF9, 0x8C,
|
||||
0x8C, 0xDD, 0x8C,
|
||||
0xBA, 0xC6, 0x8C,
|
||||
0xE8, 0xAF, 0x8C,
|
||||
0x53, 0xEE, 0xBA,
|
||||
0x8C, 0xD1, 0xBA,
|
||||
0xBA, 0xBA, 0xBA,
|
||||
0xE8, 0xA3, 0xBA,
|
||||
0x53, 0xE2, 0xE8,
|
||||
0x8C, 0xC6, 0xE8,
|
||||
0xBA, 0xAF, 0xE8,
|
||||
0xE8, 0x98, 0xE8,
|
||||
0xC8, 0xEE, 0x30,
|
||||
0xF6, 0xD7, 0x30,
|
||||
0x9A, 0xF7, 0x67,
|
||||
0xC8, 0xE0, 0x67,
|
||||
0xF6, 0xC9, 0x67,
|
||||
0x9A, 0xEA, 0x9A,
|
||||
0xC8, 0xD3, 0x9A,
|
||||
0xF6, 0xBC, 0x9A,
|
||||
0x61, 0xFB, 0xC8,
|
||||
0x9A, 0xDF, 0xC8,
|
||||
0xC8, 0xC8, 0xC8,
|
||||
0xF6, 0xB1, 0xC8,
|
||||
0x61, 0xF0, 0xF6,
|
||||
0x9A, 0xD3, 0xF6,
|
||||
0xC8, 0xBC, 0xF6,
|
||||
0xF6, 0xA5, 0xF6,
|
||||
0xD5, 0xFB, 0x3D,
|
||||
0xD5, 0xED, 0x74,
|
||||
0xA7, 0xF7, 0xA7,
|
||||
0xD5, 0xE0, 0xA7,
|
||||
0xA7, 0xEC, 0xD5,
|
||||
0xD5, 0xD5, 0xD5,
|
||||
0xE1, 0xFA, 0x81,
|
||||
0xE1, 0xED, 0xB3,
|
||||
0xB3, 0xF8, 0xE1,
|
||||
0xE1, 0xE1, 0xE1,
|
||||
0xED, 0xF9, 0xBF,
|
||||
0xED, 0xED, 0xED,
|
||||
0xF8, 0xF8, 0xF8
|
||||
};
|
||||
|
||||
static const uint32 s_yLookup[512] = {
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000040, 0x00000000, 0x00000040, 0x00000000,
|
||||
0x00000040, 0x00000000, 0x00000040, 0x00000000,
|
||||
0x00000040, 0x00000000, 0x00000040, 0x00000040,
|
||||
0x00000040, 0x00000040, 0x00000040, 0x00000040,
|
||||
0x00000040, 0x00000040, 0x00400040, 0x00000040,
|
||||
0x00400040, 0x00000040, 0x00400040, 0x00000040,
|
||||
0x00400040, 0x00000040, 0x00400040, 0x00000040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400080, 0x00400040,
|
||||
0x00400080, 0x00400040, 0x00400080, 0x00400040,
|
||||
0x00400080, 0x00400040, 0x00400080, 0x00400080,
|
||||
0x00400080, 0x00400080, 0x00400080, 0x00400080,
|
||||
0x00400080, 0x00400080, 0x00400080, 0x00400080,
|
||||
0x00800080, 0x00400080, 0x00800080, 0x00400080,
|
||||
0x00800080, 0x00400080, 0x00800080, 0x00400080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x008000C0, 0x00800080,
|
||||
0x008000C0, 0x00800080, 0x008000C0, 0x00800080,
|
||||
0x008000C0, 0x00800080, 0x008000C0, 0x008000C0,
|
||||
0x008000C0, 0x008000C0, 0x008000C0, 0x008000C0,
|
||||
0x008000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
|
||||
0x00C000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
|
||||
0x00C000C0, 0x008000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C00100, 0x00C000C0,
|
||||
0x00C00100, 0x00C000C0, 0x00C00100, 0x00C000C0,
|
||||
0x00C00100, 0x00C000C0, 0x00C00100, 0x00C00100,
|
||||
0x00C00100, 0x00C00100, 0x00C00100, 0x00C00100,
|
||||
0x00C00100, 0x00C00100, 0x01000100, 0x00C00100,
|
||||
0x01000100, 0x00C00100, 0x01000100, 0x00C00100,
|
||||
0x01000100, 0x00C00100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000140, 0x01000100,
|
||||
0x01000140, 0x01000100, 0x01000140, 0x01000100,
|
||||
0x01000140, 0x01000140, 0x01000140, 0x01000140,
|
||||
0x01000140, 0x01000140, 0x01000140, 0x01000140,
|
||||
0x01400140, 0x01000140, 0x01400140, 0x01000140,
|
||||
0x01400140, 0x01000140, 0x01400140, 0x01000140,
|
||||
0x01400140, 0x01400140, 0x01400140, 0x01400140,
|
||||
0x01400140, 0x01400140, 0x01400140, 0x01400140,
|
||||
0x01400140, 0x01400140, 0x01400140, 0x01400140,
|
||||
0x01400140, 0x01400140, 0x01400180, 0x01400140,
|
||||
0x01400180, 0x01400140, 0x01400180, 0x01400140,
|
||||
0x01400180, 0x01400140, 0x01400180, 0x01400180,
|
||||
0x01400180, 0x01400180, 0x01400180, 0x01400180,
|
||||
0x01800180, 0x01400180, 0x01800180, 0x01400180,
|
||||
0x01800180, 0x01400180, 0x01800180, 0x01400180,
|
||||
0x01800180, 0x01800180, 0x01800180, 0x01800180,
|
||||
0x01800180, 0x01800180, 0x01800180, 0x01800180,
|
||||
0x01800180, 0x01800180, 0x01800180, 0x01800180,
|
||||
0x01800180, 0x01800180, 0x018001C0, 0x01800180,
|
||||
0x018001C0, 0x01800180, 0x018001C0, 0x01800180,
|
||||
0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
|
||||
0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
|
||||
0x01C001C0, 0x018001C0, 0x01C001C0, 0x018001C0,
|
||||
0x01C001C0, 0x018001C0, 0x01C001C0, 0x01C001C0,
|
||||
0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
|
||||
0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
|
||||
0x01C001C0, 0x01C001C0, 0x01C00200, 0x01C001C0,
|
||||
0x01C00200, 0x01C001C0, 0x01C00200, 0x01C001C0,
|
||||
0x01C00200, 0x01C001C0, 0x01C00200, 0x01C00200,
|
||||
0x01C00200, 0x01C00200, 0x01C00200, 0x01C00200,
|
||||
0x02000200, 0x01C00200, 0x02000200, 0x01C00200,
|
||||
0x02000200, 0x01C00200, 0x02000200, 0x02000200,
|
||||
0x02000200, 0x02000200, 0x02000200, 0x02000200,
|
||||
0x02000200, 0x02000200, 0x02000200, 0x02000200,
|
||||
0x02000200, 0x02000200, 0x02000240, 0x02000200,
|
||||
0x02000240, 0x02000200, 0x02000240, 0x02000200,
|
||||
0x02000240, 0x02000240, 0x02000240, 0x02000240,
|
||||
0x02000240, 0x02000240, 0x02400240, 0x02000240,
|
||||
0x02400240, 0x02000240, 0x02400240, 0x02000240,
|
||||
0x02400240, 0x02000240, 0x02400240, 0x02400240,
|
||||
0x02400240, 0x02400240, 0x02400240, 0x02400240,
|
||||
0x02400240, 0x02400240, 0x02400240, 0x02400240,
|
||||
0x02400280, 0x02400240, 0x02400280, 0x02400240,
|
||||
0x02400280, 0x02400240, 0x02400280, 0x02400280,
|
||||
0x02400280, 0x02400280, 0x02400280, 0x02400280,
|
||||
0x02800280, 0x02400280, 0x02800280, 0x02400280,
|
||||
0x02800280, 0x02400280, 0x02800280, 0x02800280,
|
||||
0x02800280, 0x02800280, 0x02800280, 0x02800280,
|
||||
0x02800280, 0x02800280, 0x02800280, 0x02800280,
|
||||
0x02800280, 0x02800280, 0x028002C0, 0x02800280,
|
||||
0x028002C0, 0x02800280, 0x028002C0, 0x02800280,
|
||||
0x028002C0, 0x028002C0, 0x028002C0, 0x028002C0,
|
||||
0x02C002C0, 0x028002C0, 0x02C002C0, 0x028002C0,
|
||||
0x02C002C0, 0x028002C0, 0x02C002C0, 0x02C002C0,
|
||||
0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
|
||||
0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
|
||||
0x02C00300, 0x02C002C0, 0x02C00300, 0x02C002C0,
|
||||
0x02C00300, 0x02C002C0, 0x02C00300, 0x02C00300,
|
||||
0x02C00300, 0x02C00300, 0x02C00300, 0x02C00300,
|
||||
0x03000300, 0x02C00300, 0x03000300, 0x02C00300,
|
||||
0x03000300, 0x03000300, 0x03000300, 0x03000300,
|
||||
0x03000300, 0x03000300, 0x03000300, 0x03000300,
|
||||
0x03000300, 0x03000300, 0x03000340, 0x03000300,
|
||||
0x03000340, 0x03000300, 0x03000340, 0x03000300,
|
||||
0x03000340, 0x03000340, 0x03000340, 0x03000340,
|
||||
0x03400340, 0x03000340, 0x03400340, 0x03000340,
|
||||
0x03400340, 0x03000340, 0x03400340, 0x03400340,
|
||||
0x03400340, 0x03400340, 0x03400340, 0x03400340,
|
||||
0x03400340, 0x03400340, 0x03400340, 0x03400340,
|
||||
0x03400380, 0x03400340, 0x03400380, 0x03400340,
|
||||
0x03400380, 0x03400380, 0x03400380, 0x03400380,
|
||||
0x03800380, 0x03400380, 0x03800380, 0x03400380,
|
||||
0x03800380, 0x03400380, 0x03800380, 0x03800380,
|
||||
0x03800380, 0x03800380, 0x03800380, 0x03800380,
|
||||
0x03800380, 0x03800380, 0x038003C0, 0x03800380,
|
||||
0x038003C0, 0x03800380, 0x038003C0, 0x03800380,
|
||||
0x038003C0, 0x038003C0, 0x038003C0, 0x038003C0,
|
||||
0x03C003C0, 0x038003C0, 0x03C003C0, 0x038003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0
|
||||
};
|
||||
|
||||
static const uint32 s_uLookup[512] = {
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00280020, 0x00200020,
|
||||
0x00280020, 0x00200020, 0x00280020, 0x00200020,
|
||||
0x00280020, 0x00200020, 0x00280020, 0x00280020,
|
||||
0x00280020, 0x00280020, 0x00280020, 0x00280020,
|
||||
0x00280020, 0x00280020, 0x00280020, 0x00280020,
|
||||
0x00280020, 0x00280028, 0x00280020, 0x00280028,
|
||||
0x00280020, 0x00280028, 0x00280020, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00300028, 0x00280028,
|
||||
0x00300028, 0x00280028, 0x00300028, 0x00280028,
|
||||
0x00300028, 0x00280028, 0x00300028, 0x00280028,
|
||||
0x00300028, 0x00300028, 0x00300028, 0x00300028,
|
||||
0x00300028, 0x00300028, 0x00300028, 0x00300028,
|
||||
0x00300028, 0x00300028, 0x00300028, 0x00300028,
|
||||
0x00300028, 0x00300030, 0x00300028, 0x00300030,
|
||||
0x00300028, 0x00300030, 0x00300028, 0x00300030,
|
||||
0x00300028, 0x00300030, 0x00300028, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380038,
|
||||
0x00380030, 0x00380038, 0x00380030, 0x00380038,
|
||||
0x00380030, 0x00380038, 0x00380030, 0x00380038,
|
||||
0x00380030, 0x00380038, 0x00380030, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00080000, 0x00080000, 0x00080000,
|
||||
0x00080000, 0x00080000, 0x00080000, 0x00080000,
|
||||
0x00080000, 0x00080000, 0x00080000, 0x00080000,
|
||||
0x00080000, 0x00080008, 0x00080000, 0x00080008,
|
||||
0x00080000, 0x00080008, 0x00080000, 0x00080008,
|
||||
0x00080000, 0x00080008, 0x00080000, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00100008, 0x00080008,
|
||||
0x00100008, 0x00080008, 0x00100008, 0x00080008,
|
||||
0x00100008, 0x00080008, 0x00100008, 0x00080008,
|
||||
0x00100008, 0x00080008, 0x00100008, 0x00100008,
|
||||
0x00100008, 0x00100008, 0x00100008, 0x00100008,
|
||||
0x00100008, 0x00100008, 0x00100008, 0x00100008,
|
||||
0x00100008, 0x00100008, 0x00100008, 0x00100010,
|
||||
0x00100008, 0x00100010, 0x00100008, 0x00100010,
|
||||
0x00100008, 0x00100010, 0x00100008, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00180010, 0x00100010,
|
||||
0x00180010, 0x00100010, 0x00180010, 0x00100010,
|
||||
0x00180010, 0x00100010, 0x00180010, 0x00100010,
|
||||
0x00180010, 0x00180010, 0x00180010, 0x00180010,
|
||||
0x00180010, 0x00180010, 0x00180010, 0x00180010,
|
||||
0x00180010, 0x00180010, 0x00180010, 0x00180018,
|
||||
0x00180010, 0x00180018, 0x00180010, 0x00180018,
|
||||
0x00180010, 0x00180018, 0x00180010, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00200018, 0x00180018, 0x00200018, 0x00180018,
|
||||
0x00200018, 0x00180018, 0x00200018, 0x00180018,
|
||||
0x00200018, 0x00200018, 0x00200018, 0x00200018,
|
||||
0x00200018, 0x00200018, 0x00200018, 0x00200018,
|
||||
0x00200018, 0x00200018, 0x00200018, 0x00200020,
|
||||
0x00200018, 0x00200020, 0x00200018, 0x00200020,
|
||||
0x00200018, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020
|
||||
};
|
||||
|
||||
static const uint32 s_vLookup[512] = {
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030004,
|
||||
0x00030003, 0x00030004, 0x00030003, 0x00030004,
|
||||
0x00030003, 0x00030004, 0x00030004, 0x00030004,
|
||||
0x00030004, 0x00030004, 0x00030004, 0x00030004,
|
||||
0x00030004, 0x00030004, 0x00030004, 0x00030004,
|
||||
0x00030004, 0x00040004, 0x00030004, 0x00040004,
|
||||
0x00030004, 0x00040004, 0x00030004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040005, 0x00040004, 0x00040005,
|
||||
0x00040004, 0x00040005, 0x00040004, 0x00040005,
|
||||
0x00040004, 0x00040005, 0x00040005, 0x00040005,
|
||||
0x00040005, 0x00040005, 0x00040005, 0x00040005,
|
||||
0x00040005, 0x00040005, 0x00040005, 0x00040005,
|
||||
0x00040005, 0x00050005, 0x00040005, 0x00050005,
|
||||
0x00040005, 0x00050005, 0x00040005, 0x00050005,
|
||||
0x00040005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050006, 0x00050005, 0x00050006,
|
||||
0x00050005, 0x00050006, 0x00050005, 0x00050006,
|
||||
0x00050005, 0x00050006, 0x00050006, 0x00050006,
|
||||
0x00050006, 0x00050006, 0x00050006, 0x00050006,
|
||||
0x00050006, 0x00050006, 0x00050006, 0x00050006,
|
||||
0x00050006, 0x00050006, 0x00050006, 0x00060006,
|
||||
0x00050006, 0x00060006, 0x00050006, 0x00060006,
|
||||
0x00050006, 0x00060006, 0x00050006, 0x00060006,
|
||||
0x00050006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060007, 0x00060006, 0x00060007,
|
||||
0x00060006, 0x00060007, 0x00060006, 0x00060007,
|
||||
0x00060006, 0x00060007, 0x00060006, 0x00060007,
|
||||
0x00060007, 0x00060007, 0x00060007, 0x00060007,
|
||||
0x00060007, 0x00060007, 0x00060007, 0x00060007,
|
||||
0x00060007, 0x00060007, 0x00060007, 0x00060007,
|
||||
0x00060007, 0x00070007, 0x00060007, 0x00070007,
|
||||
0x00060007, 0x00070007, 0x00060007, 0x00070007,
|
||||
0x00060007, 0x00070007, 0x00060007, 0x00070007,
|
||||
0x00060007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00010001, 0x00000001, 0x00010001,
|
||||
0x00000001, 0x00010001, 0x00000001, 0x00010001,
|
||||
0x00000001, 0x00010001, 0x00000001, 0x00010001,
|
||||
0x00000001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010002, 0x00010001, 0x00010002,
|
||||
0x00010001, 0x00010002, 0x00010001, 0x00010002,
|
||||
0x00010001, 0x00010002, 0x00010001, 0x00010002,
|
||||
0x00010002, 0x00010002, 0x00010002, 0x00010002,
|
||||
0x00010002, 0x00010002, 0x00010002, 0x00010002,
|
||||
0x00010002, 0x00010002, 0x00010002, 0x00010002,
|
||||
0x00010002, 0x00020002, 0x00010002, 0x00020002,
|
||||
0x00010002, 0x00020002, 0x00010002, 0x00020002,
|
||||
0x00010002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020003, 0x00020002, 0x00020003,
|
||||
0x00020002, 0x00020003, 0x00020002, 0x00020003,
|
||||
0x00020003, 0x00020003, 0x00020003, 0x00020003,
|
||||
0x00020003, 0x00020003, 0x00020003, 0x00020003,
|
||||
0x00020003, 0x00020003, 0x00020003, 0x00030003,
|
||||
0x00020003, 0x00030003, 0x00020003, 0x00030003,
|
||||
0x00020003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
220
image/codecs/codec.cpp
Normal file
220
image/codecs/codec.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
#include "image/jpeg.h"
|
||||
#include "image/codecs/bmp_raw.h"
|
||||
#include "image/codecs/cdtoons.h"
|
||||
#include "image/codecs/cinepak.h"
|
||||
#include "image/codecs/indeo3.h"
|
||||
#include "image/codecs/indeo4.h"
|
||||
#include "image/codecs/indeo5.h"
|
||||
#include "image/codecs/jyv1.h"
|
||||
#include "image/codecs/mjpeg.h"
|
||||
#include "image/codecs/mpeg.h"
|
||||
#include "image/codecs/msvideo1.h"
|
||||
#include "image/codecs/msrle.h"
|
||||
#include "image/codecs/msrle4.h"
|
||||
#include "image/codecs/qtrle.h"
|
||||
#include "image/codecs/rpza.h"
|
||||
#include "image/codecs/smc.h"
|
||||
#include "image/codecs/svq1.h"
|
||||
#include "image/codecs/truemotion1.h"
|
||||
#include "image/codecs/xan.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
Graphics::PixelFormat Codec::getDefaultYUVFormat() {
|
||||
Graphics::PixelFormat format = g_system->getScreenFormat();
|
||||
|
||||
// Default to a 32bpp format, if in 8bpp mode
|
||||
if (format.isCLUT8())
|
||||
return Graphics::PixelFormat::createFormatRGBA32();
|
||||
else
|
||||
return format;
|
||||
}
|
||||
|
||||
Codec *createBitmapCodec(uint32 tag, uint32 streamTag, int width, int height, int bitsPerPixel) {
|
||||
#ifdef USE_JYV1
|
||||
// Crusader videos are special cased here because the frame type is not in the "compression"
|
||||
// tag but in the "stream handler" tag for these files
|
||||
if (JYV1Decoder::isJYV1StreamTag(streamTag)) {
|
||||
assert(bitsPerPixel == 8);
|
||||
return new JYV1Decoder(width, height, streamTag);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *missingCodec = nullptr;
|
||||
|
||||
switch (tag) {
|
||||
case SWAP_CONSTANT_32(0):
|
||||
return new BitmapRawDecoder(width, height, bitsPerPixel, false);
|
||||
case SWAP_CONSTANT_32(1):
|
||||
return new MSRLEDecoder(width, height, bitsPerPixel);
|
||||
case SWAP_CONSTANT_32(2):
|
||||
return new MSRLE4Decoder(width, height, bitsPerPixel);
|
||||
case SWAP_CONSTANT_32(3):
|
||||
// Used with v4-v5 BMP headers to produce transparent BMPs
|
||||
return new BitmapRawDecoder(width, height, bitsPerPixel, false);
|
||||
case MKTAG('C','R','A','M'):
|
||||
case MKTAG('m','s','v','c'):
|
||||
case MKTAG('W','H','A','M'):
|
||||
return new MSVideo1Decoder(width, height, bitsPerPixel);
|
||||
case MKTAG('c','v','i','d'):
|
||||
return new CinepakDecoder(bitsPerPixel);
|
||||
|
||||
case MKTAG('I','V','3','2'):
|
||||
#ifdef USE_INDEO3
|
||||
return new Indeo3Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 3";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('I', 'V', '4', '1'):
|
||||
case MKTAG('I', 'V', '4', '2'):
|
||||
#ifdef USE_INDEO45
|
||||
return new Indeo4Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 4";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('I', 'V', '5', '0'):
|
||||
#ifdef USE_INDEO45
|
||||
return new Indeo5Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 5";
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MKTAG('X', 'x', 'a', 'n'):
|
||||
#ifdef USE_XAN
|
||||
return new XanDecoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Xan";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('D','U','C','K'):
|
||||
case MKTAG('d','u','c','k'):
|
||||
#ifdef USE_TRUEMOTION1
|
||||
return new TrueMotion1Decoder();
|
||||
#else
|
||||
missingCodec = "TrueMotion1";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('m','p','g','2'):
|
||||
#ifdef USE_MPEG2
|
||||
return new MPEGDecoder();
|
||||
#else
|
||||
missingCodec = "MPEG2";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('M','J','P','G'):
|
||||
case MKTAG('m','j','p','g'):
|
||||
#ifdef USE_MJPEG
|
||||
return new MJPEGDecoder();
|
||||
#else
|
||||
missingCodec = "MJPEG";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (tag & 0x00FFFFFF)
|
||||
warning("Unknown BMP/AVI compression format \'%s\'", tag2str(tag));
|
||||
else
|
||||
warning("Unknown BMP/AVI compression format %d", SWAP_BYTES_32(tag));
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(missingCodec);
|
||||
warning("createBitmapCodec(): %s codec is not compiled", missingCodec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Codec *createQuickTimeCodec(uint32 tag, int width, int height, int bitsPerPixel) {
|
||||
const char *missingCodec = nullptr;
|
||||
|
||||
switch (tag) {
|
||||
case MKTAG('c','v','i','d'):
|
||||
// Cinepak: As used by most Myst and all Riven videos as well as some Myst ME videos. "The Chief" videos also use this. Very popular for Director titles.
|
||||
return new CinepakDecoder(bitsPerPixel);
|
||||
case MKTAG('r','p','z','a'):
|
||||
// Apple Video ("Road Pizza"): Used by some Myst videos.
|
||||
return new RPZADecoder(width, height);
|
||||
case MKTAG('r','l','e',' '):
|
||||
// QuickTime RLE: Used by some Myst ME videos.
|
||||
return new QTRLEDecoder(width, height, bitsPerPixel);
|
||||
case MKTAG('s','m','c',' '):
|
||||
// Apple SMC: Used by some Myst videos.
|
||||
return new SMCDecoder(width, height);
|
||||
case MKTAG('S','V','Q','1'):
|
||||
#ifdef USE_SVQ1
|
||||
// Sorenson Video 1: Used by some Myst ME videos.
|
||||
return new SVQ1Decoder(width, height);
|
||||
#else
|
||||
missingCodec = "Sorenson Video 1";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('S','V','Q','3'):
|
||||
// Sorenson Video 3: Used by some Myst ME videos.
|
||||
warning("Sorenson Video 3 not yet supported");
|
||||
return 0;
|
||||
case MKTAG('j','p','e','g'):
|
||||
#ifdef USE_JPEG
|
||||
// JPEG: Used by some Myst ME 10th Anniversary videos.
|
||||
return new JPEGDecoder();
|
||||
#else
|
||||
missingCodec = "JPEG";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('Q','k','B','k'):
|
||||
#ifdef USE_CDTOONS
|
||||
// CDToons: Used by most of the Broderbund games.
|
||||
return new CDToonsDecoder(width, height);
|
||||
#else
|
||||
missingCodec = "CDToons";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('r','a','w',' '):
|
||||
// Used my L-Zone-mac (Director game)
|
||||
return new BitmapRawDecoder(width, height, bitsPerPixel, true, true);
|
||||
case MKTAG('I','V','3','2'):
|
||||
#ifdef USE_INDEO3
|
||||
// Indeo 3: Used by Team Xtreme: Operation Weather Disaster (Spanish)
|
||||
return new Indeo3Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 3";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
warning("Unsupported QuickTime codec \'%s\'", tag2str(tag));
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(missingCodec);
|
||||
warning("createBitmapCodec(): %s codec is not compiled", missingCodec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
144
image/codecs/codec.h
Normal file
144
image/codecs/codec.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CODEC_H
|
||||
#define IMAGE_CODECS_CODEC_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
#include "image/codec-options.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* An abstract representation of a image codec.
|
||||
*
|
||||
* Unlike ImageDecoder, the entire info for a frame may not be present
|
||||
* within the stream. The codec may rely on the supporting container
|
||||
* for parameters and can also rely on a previous (or future) frame.
|
||||
* When decoding, the previous frame may not destroyed and could be
|
||||
* maintained for use in the next one.
|
||||
*
|
||||
* An ImageDecoder can always be a Codec, but a Codec may not necessarily
|
||||
* be able to be an ImageDecoder.
|
||||
*
|
||||
* Used in image:
|
||||
* - BitmapDecoder
|
||||
* - PICTDecoder
|
||||
*
|
||||
* Used in video:
|
||||
* - AVIDecoder
|
||||
* - QuickTimeDecoder
|
||||
* - VMDDecoder
|
||||
*/
|
||||
class Codec {
|
||||
public:
|
||||
Codec() {}
|
||||
virtual ~Codec() {}
|
||||
|
||||
/**
|
||||
* A type of dithering.
|
||||
*/
|
||||
enum DitherType {
|
||||
/** Unknown */
|
||||
kDitherTypeUnknown,
|
||||
|
||||
/** Video for Windows dithering */
|
||||
kDitherTypeVFW,
|
||||
|
||||
/** QuickTime dithering */
|
||||
kDitherTypeQT
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode the frame for the given data and return a pointer to a surface
|
||||
* containing the decoded frame.
|
||||
*
|
||||
* @return a pointer to the decoded frame
|
||||
*/
|
||||
virtual const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) = 0;
|
||||
|
||||
/**
|
||||
* Get the format that the surface returned from decodeImage() will
|
||||
* be in.
|
||||
*/
|
||||
virtual Graphics::PixelFormat getPixelFormat() const = 0;
|
||||
|
||||
/**
|
||||
* Select the preferred format to use, for codecs where this is faster than converting
|
||||
* the image afterwards. Returns true if supported, and false otherwise.
|
||||
*/
|
||||
virtual bool setOutputPixelFormat(const Graphics::PixelFormat &format) { return format == getPixelFormat(); }
|
||||
|
||||
/**
|
||||
* Can this codec's frames contain a palette?
|
||||
*/
|
||||
virtual bool containsPalette() const { return false; }
|
||||
|
||||
/**
|
||||
* Get the palette last decoded from decodeImage
|
||||
*/
|
||||
virtual const byte *getPalette() { return 0; }
|
||||
|
||||
/**
|
||||
* Does the codec have a dirty palette?
|
||||
*/
|
||||
virtual bool hasDirtyPalette() const { return false; }
|
||||
|
||||
/**
|
||||
* Can the codec dither down to 8bpp?
|
||||
*/
|
||||
virtual bool canDither(DitherType type) const { return false; }
|
||||
|
||||
/**
|
||||
* Activate dithering mode with a palette
|
||||
*/
|
||||
virtual void setDither(DitherType type, const byte *palette) {}
|
||||
|
||||
/**
|
||||
* Set the decoding accuracy of the codec, if supported
|
||||
*/
|
||||
virtual void setCodecAccuracy(CodecAccuracy accuracy) {}
|
||||
|
||||
/**
|
||||
* Get the preferred default pixel format for use with YUV codecs
|
||||
*/
|
||||
static Graphics::PixelFormat getDefaultYUVFormat();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a codec given a bitmap/AVI compression tag and stream handler tag (can be 0)
|
||||
*/
|
||||
Codec *createBitmapCodec(uint32 tag, uint32 streamTag, int width, int height, int bitsPerPixel);
|
||||
|
||||
/**
|
||||
* Create a codec given a QuickTime compression tag.
|
||||
*/
|
||||
Codec *createQuickTimeCodec(uint32 tag, int width, int height, int bitsPerPixel);
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
332
image/codecs/dither.cpp
Normal file
332
image/codecs/dither.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Add a color to the QuickTime dither table check queue if it hasn't already been found.
|
||||
*/
|
||||
inline void addColorToQueue(uint16 color, uint16 index, byte *checkBuffer, Common::List<uint16> &checkQueue) {
|
||||
if ((READ_UINT16(checkBuffer + color * 2) & 0xFF) == 0) {
|
||||
// Previously unfound color
|
||||
WRITE_UINT16(checkBuffer + color * 2, index);
|
||||
checkQueue.push_back(color);
|
||||
}
|
||||
}
|
||||
|
||||
inline byte adjustColorRange(byte currentColor, byte correctColor, byte palColor) {
|
||||
return CLIP<int>(currentColor - palColor + correctColor, 0, 255);
|
||||
}
|
||||
|
||||
inline uint16 makeQuickTimeDitherColor(byte r, byte g, byte b) {
|
||||
// RGB554
|
||||
return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
DitherCodec::DitherCodec(Codec *codec, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _codec(codec), _disposeAfterUse(disposeAfterUse), _dirtyPalette(false),
|
||||
_forcedDitherPalette(0), _ditherTable(0), _ditherFrame(0), _srcPalette(nullptr) {
|
||||
}
|
||||
|
||||
DitherCodec::~DitherCodec() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _codec;
|
||||
|
||||
delete[] _ditherTable;
|
||||
|
||||
if (_ditherFrame) {
|
||||
_ditherFrame->free();
|
||||
delete _ditherFrame;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Default template to convert a dither color
|
||||
inline uint16 readQT_RGB(uint32 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
byte r, g, b;
|
||||
format.colorToRGB(srcColor, r, g, b);
|
||||
return makeQuickTimeDitherColor(r, g, b);
|
||||
}
|
||||
|
||||
// Specialized version for 8bpp
|
||||
inline uint16 readQT_Palette(uint8 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
return makeQuickTimeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]);
|
||||
}
|
||||
|
||||
// Specialized version for RGB554
|
||||
inline uint16 readQT_RGB554(uint16 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
return srcColor;
|
||||
}
|
||||
|
||||
// Specialized version for RGB555 and ARGB1555
|
||||
inline uint16 readQT_RGB555(uint16 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
return (srcColor >> 1) & 0x3FFF;
|
||||
}
|
||||
|
||||
template<typename PixelInt, class Fn>
|
||||
void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, Fn fn, const byte *palette = 0) {
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
for (int y = 0; y < dst.h; y++) {
|
||||
const PixelInt *srcPtr = (const PixelInt *)src.getBasePtr(0, y);
|
||||
byte *dstPtr = (byte *)dst.getBasePtr(0, y);
|
||||
uint16 colorTableOffset = colorTableOffsets[y & 3];
|
||||
|
||||
for (int x = 0; x < dst.w; x++) {
|
||||
uint16 color = fn(*srcPtr++, src.format, palette);
|
||||
*dstPtr++ = ditherTable[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
const Graphics::Surface *frame = _codec->decodeFrame(stream);
|
||||
if (!frame || _forcedDitherPalette.empty())
|
||||
return frame;
|
||||
|
||||
const byte *curPalette = _codec->containsPalette() ? _codec->getPalette() : _srcPalette;
|
||||
|
||||
if (frame->format.isCLUT8() && curPalette) {
|
||||
// This should always be true, but this is for sanity
|
||||
if (!curPalette)
|
||||
return frame;
|
||||
|
||||
// If the palettes match, bail out
|
||||
if (memcmp(_forcedDitherPalette.data(), curPalette, 256 * 3) == 0)
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Need to create a new one
|
||||
if (!_ditherFrame) {
|
||||
_ditherFrame = new Graphics::Surface();
|
||||
_ditherFrame->create(frame->w, frame->h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
if (frame->format.isCLUT8() && curPalette)
|
||||
ditherQuickTimeFrame<byte>(*frame, *_ditherFrame, _ditherTable, readQT_Palette, curPalette);
|
||||
else if (frame->format == Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0))
|
||||
ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB554);
|
||||
else if (frame->format == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0) ||
|
||||
frame->format == Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15))
|
||||
ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB555);
|
||||
else if (frame->format.bytesPerPixel == 2)
|
||||
ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB);
|
||||
else if (frame->format.bytesPerPixel == 4)
|
||||
ditherQuickTimeFrame<uint32>(*frame, *_ditherFrame, _ditherTable, readQT_RGB);
|
||||
|
||||
return _ditherFrame;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat DitherCodec::getPixelFormat() const {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->getPixelFormat();
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
bool DitherCodec::setOutputPixelFormat(const Graphics::PixelFormat &format) {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->setOutputPixelFormat(format);
|
||||
return format.isCLUT8();
|
||||
}
|
||||
|
||||
bool DitherCodec::containsPalette() const {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->containsPalette();
|
||||
return true;
|
||||
}
|
||||
|
||||
const byte *DitherCodec::getPalette() {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->getPalette();
|
||||
_dirtyPalette = false;
|
||||
return _forcedDitherPalette.data();
|
||||
}
|
||||
|
||||
bool DitherCodec::hasDirtyPalette() const {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->hasDirtyPalette();
|
||||
return _dirtyPalette;
|
||||
}
|
||||
|
||||
bool DitherCodec::canDither(DitherType type) const {
|
||||
return _codec->canDither(type) || (type == kDitherTypeQT);
|
||||
}
|
||||
|
||||
void DitherCodec::setDither(DitherType type, const byte *palette) {
|
||||
if (_codec->canDither(type)) {
|
||||
_codec->setDither(type, palette);
|
||||
} else {
|
||||
assert(type == kDitherTypeQT);
|
||||
assert(_forcedDitherPalette.empty());
|
||||
|
||||
// Forced dither
|
||||
_forcedDitherPalette.resize(256, false);
|
||||
_forcedDitherPalette.set(palette, 0, 256);
|
||||
_dirtyPalette = true;
|
||||
|
||||
_ditherTable = createQuickTimeDitherTable(_forcedDitherPalette.data(), 256);
|
||||
|
||||
// Prefer RGB554 or RGB555 to avoid extra conversion when dithering
|
||||
if (!_codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0)))
|
||||
_codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void DitherCodec::setCodecAccuracy(CodecAccuracy accuracy) {
|
||||
return _codec->setCodecAccuracy(accuracy);
|
||||
}
|
||||
|
||||
byte *DitherCodec::createQuickTimeDitherTable(const byte *palette, uint colorCount) {
|
||||
byte *buf = new byte[0x10000]();
|
||||
|
||||
Common::List<uint16> checkQueue;
|
||||
|
||||
bool foundBlack = false;
|
||||
bool foundWhite = false;
|
||||
|
||||
const byte *palPtr = palette;
|
||||
|
||||
// See what colors we have, and add them to the queue to check
|
||||
for (uint i = 0; i < colorCount; i++) {
|
||||
byte r = *palPtr++;
|
||||
byte g = *palPtr++;
|
||||
byte b = *palPtr++;
|
||||
uint16 n = (i << 8) | 1;
|
||||
uint16 col = makeQuickTimeDitherColor(r, g, b);
|
||||
|
||||
if (col == 0) {
|
||||
// Special case for close-to-black
|
||||
// The original did more here, but it effectively discarded the value
|
||||
// due to a poor if-check (whole 16-bit value instead of lower 8-bits).
|
||||
WRITE_UINT16(buf, n);
|
||||
foundBlack = true;
|
||||
} else if (col == 0x3FFF) {
|
||||
// Special case for close-to-white
|
||||
// The original did more here, but it effectively discarded the value
|
||||
// due to a poor if-check (whole 16-bit value instead of lower 8-bits).
|
||||
WRITE_UINT16(buf + 0x7FFE, n);
|
||||
foundWhite = true;
|
||||
} else {
|
||||
// Previously unfound color
|
||||
addColorToQueue(col, n, buf, checkQueue);
|
||||
}
|
||||
}
|
||||
|
||||
// More special handling for white
|
||||
if (foundWhite)
|
||||
checkQueue.push_front(0x3FFF);
|
||||
|
||||
// More special handling for black
|
||||
if (foundBlack)
|
||||
checkQueue.push_front(0);
|
||||
|
||||
// Go through the list of colors we have and match up similar colors
|
||||
// to fill in the table as best as we can.
|
||||
while (!checkQueue.empty()) {
|
||||
uint16 col = checkQueue.front();
|
||||
checkQueue.pop_front();
|
||||
uint16 index = READ_UINT16(buf + col * 2);
|
||||
|
||||
uint32 x = col << 4;
|
||||
if ((x & 0xFF) < 0xF0)
|
||||
addColorToQueue((x + 0x10) >> 4, index, buf, checkQueue);
|
||||
if ((x & 0xFF) >= 0x10)
|
||||
addColorToQueue((x - 0x10) >> 4, index, buf, checkQueue);
|
||||
|
||||
uint32 y = col << 7;
|
||||
if ((y & 0xFF00) < 0xF800)
|
||||
addColorToQueue((y + 0x800) >> 7, index, buf, checkQueue);
|
||||
if ((y & 0xFF00) >= 0x800)
|
||||
addColorToQueue((y - 0x800) >> 7, index, buf, checkQueue);
|
||||
|
||||
uint32 z = col << 2;
|
||||
if ((z & 0xFF00) < 0xF800)
|
||||
addColorToQueue((z + 0x800) >> 2, index, buf, checkQueue);
|
||||
if ((z & 0xFF00) >= 0x800)
|
||||
addColorToQueue((z - 0x800) >> 2, index, buf, checkQueue);
|
||||
}
|
||||
|
||||
// Contract the table back to just palette entries
|
||||
for (int i = 0; i < 0x4000; i++)
|
||||
buf[i] = READ_UINT16(buf + i * 2) >> 8;
|
||||
|
||||
// Now go through and distribute the error to three more pixels
|
||||
byte *bufPtr = buf;
|
||||
for (uint realR = 0; realR < 0x100; realR += 8) {
|
||||
for (uint realG = 0; realG < 0x100; realG += 8) {
|
||||
for (uint realB = 0; realB < 0x100; realB += 16) {
|
||||
byte palIndex = *bufPtr;
|
||||
byte r = realR;
|
||||
byte g = realG;
|
||||
byte b = realB;
|
||||
|
||||
byte palR = palette[palIndex * 3] & 0xF8;
|
||||
byte palG = palette[palIndex * 3 + 1] & 0xF8;
|
||||
byte palB = palette[palIndex * 3 + 2] & 0xF0;
|
||||
|
||||
r = adjustColorRange(r, realR, palR);
|
||||
g = adjustColorRange(g, realG, palG);
|
||||
b = adjustColorRange(b, realB, palB);
|
||||
palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
|
||||
bufPtr[0x4000] = palIndex;
|
||||
|
||||
palR = palette[palIndex * 3] & 0xF8;
|
||||
palG = palette[palIndex * 3 + 1] & 0xF8;
|
||||
palB = palette[palIndex * 3 + 2] & 0xF0;
|
||||
|
||||
r = adjustColorRange(r, realR, palR);
|
||||
g = adjustColorRange(g, realG, palG);
|
||||
b = adjustColorRange(b, realB, palB);
|
||||
palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
|
||||
bufPtr[0x8000] = palIndex;
|
||||
|
||||
palR = palette[palIndex * 3] & 0xF8;
|
||||
palG = palette[palIndex * 3 + 1] & 0xF8;
|
||||
palB = palette[palIndex * 3 + 2] & 0xF0;
|
||||
|
||||
r = adjustColorRange(r, realR, palR);
|
||||
g = adjustColorRange(g, realG, palG);
|
||||
b = adjustColorRange(b, realB, palB);
|
||||
palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
|
||||
bufPtr[0xC000] = palIndex;
|
||||
|
||||
bufPtr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
71
image/codecs/dither.h
Normal file
71
image/codecs/dither.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_DITHER_H
|
||||
#define IMAGE_CODECS_DITHER_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
#include "common/types.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
class DitherCodec : public Codec {
|
||||
public:
|
||||
DitherCodec(Codec *codec, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
virtual ~DitherCodec() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override;
|
||||
bool containsPalette() const override;
|
||||
const byte *getPalette() override;
|
||||
bool hasDirtyPalette() const override;
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
void setCodecAccuracy(CodecAccuracy accuracy) override;
|
||||
|
||||
/**
|
||||
* Specify the source palette when dithering from CLUT8 to CLUT8.
|
||||
*/
|
||||
void setPalette(const byte *palette) { _srcPalette = palette; }
|
||||
|
||||
/**
|
||||
* Create a dither table, as used by QuickTime codecs.
|
||||
*/
|
||||
static byte *createQuickTimeDitherTable(const byte *palette, uint colorCount);
|
||||
|
||||
private:
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
Codec *_codec;
|
||||
const byte *_srcPalette;
|
||||
|
||||
Graphics::Surface *_ditherFrame;
|
||||
Graphics::Palette _forcedDitherPalette;
|
||||
byte *_ditherTable;
|
||||
bool _dirtyPalette;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
139
image/codecs/hlz.cpp
Normal file
139
image/codecs/hlz.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/hlz.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
HLZDecoder::HLZDecoder(int width, int height) : Codec(),
|
||||
_width(width), _height(height), _surface(nullptr) {
|
||||
}
|
||||
|
||||
HLZDecoder::~HLZDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *HLZDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
}
|
||||
|
||||
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
byte *dst = (byte *)_surface->getPixels();
|
||||
decodeFrameInPlace(stream, uint32(-1), dst);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat HLZDecoder::getPixelFormat() const {
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
static inline uint getReg(Common::SeekableReadStream &stream, uint32 *size, uint32 *reg,
|
||||
int *regBits) {
|
||||
if (*regBits == 0) {
|
||||
if (*size < 4) {
|
||||
error("Can't feed register: not enough data");
|
||||
}
|
||||
*reg = stream.readUint32LE();
|
||||
*size -= 4;
|
||||
*regBits = 32;
|
||||
}
|
||||
uint ret = (*reg >> 31) & 0x1;
|
||||
*reg <<= 1;
|
||||
(*regBits)--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HLZDecoder::decodeFrameInPlace(Common::SeekableReadStream &stream, uint32 size, byte *dst) {
|
||||
bool eof = false;
|
||||
bool checkSize = (size != (uint32) - 1);
|
||||
byte *orig = dst;
|
||||
uint32 reg = 0;
|
||||
int regBits = 0;
|
||||
#define GETREG() getReg(stream, &size, ®, ®Bits)
|
||||
|
||||
while (!eof) {
|
||||
if (GETREG()) {
|
||||
if (size < 1) {
|
||||
error("Can't read pixel byte");
|
||||
}
|
||||
byte c = stream.readByte();
|
||||
*(dst++) = c;
|
||||
size--;
|
||||
} else {
|
||||
int offset, repeat_count;
|
||||
if (GETREG()) {
|
||||
// Long repeat
|
||||
if (size < 2) {
|
||||
error("Can't read repeat count/offset");
|
||||
}
|
||||
uint16 tmp = stream.readUint16LE();
|
||||
size -= 2;
|
||||
repeat_count = tmp & 0x7;
|
||||
offset = (tmp >> 3) - 0x2000;
|
||||
if (repeat_count == 0) {
|
||||
if (size < 1) {
|
||||
error("Can't read long repeat count");
|
||||
}
|
||||
repeat_count = stream.readByte();
|
||||
size--;
|
||||
if (repeat_count == 0) {
|
||||
eof = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Short repeat
|
||||
repeat_count = GETREG() << 1;
|
||||
repeat_count |= GETREG();
|
||||
if (size < 1) {
|
||||
error("Can't read offset byte");
|
||||
}
|
||||
offset = stream.readByte() - 0x100;
|
||||
size--;
|
||||
}
|
||||
repeat_count += 2;
|
||||
if (dst + offset < orig) {
|
||||
error("Invalid offset %d, dst is %d", offset, (int)(dst - orig));
|
||||
}
|
||||
for (; repeat_count > 0; repeat_count--) {
|
||||
// offset is always < 0
|
||||
*dst = *(dst + offset);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkSize && size != 0) {
|
||||
stream.skip(size);
|
||||
}
|
||||
#undef GETREG
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
51
image/codecs/hlz.h
Normal file
51
image/codecs/hlz.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_HLZ_H
|
||||
#define IMAGE_CODECS_HLZ_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* HLZ image decoder.
|
||||
*
|
||||
* Used by HLZ image format and HNM video format.
|
||||
*/
|
||||
class HLZDecoder : public Codec {
|
||||
public:
|
||||
HLZDecoder(int width, int height);
|
||||
~HLZDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
static void decodeFrameInPlace(Common::SeekableReadStream &stream, uint32 size, byte *dst);
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
int _width, _height;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1399
image/codecs/hnm.cpp
Normal file
1399
image/codecs/hnm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
56
image/codecs/hnm.h
Normal file
56
image/codecs/hnm.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_HNM6_H
|
||||
#define IMAGE_CODECS_HNM6_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* HNM6 image decoder interface.
|
||||
*
|
||||
* Used by HNM6 image and video formats.
|
||||
*/
|
||||
class HNM6Decoder : public Codec {
|
||||
public:
|
||||
uint16 getWidth() const { return _width; }
|
||||
uint16 getHeight() const { return _height; }
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _format; }
|
||||
|
||||
void setWarpMode(bool warpMode) { assert(!warpMode || !_videoMode); _warpMode = warpMode; }
|
||||
protected:
|
||||
HNM6Decoder(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
||||
bool videoMode = false) : Codec(),
|
||||
_width(width), _height(height), _format(format), _videoMode(videoMode), _warpMode(false) { }
|
||||
|
||||
uint16 _width, _height;
|
||||
Graphics::PixelFormat _format;
|
||||
bool _warpMode, _videoMode;
|
||||
};
|
||||
|
||||
HNM6Decoder *createHNM6Decoder(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
||||
uint32 bufferSize, bool videoMode = false);
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
89
image/codecs/indeo/get_bits.h
Normal file
89
image/codecs/indeo/get_bits.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_GET_BITS_H
|
||||
#define IMAGE_CODECS_INDEO_GET_BITS_H
|
||||
|
||||
#include "common/bitstream.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* Intel Indeo Bitstream reader
|
||||
*/
|
||||
class GetBits : public Common::BitStreamMemory8LSB {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
GetBits(const byte *dataPtr, uint32 dataSize) : Common::BitStreamMemory8LSB(new Common::BitStreamMemoryStream(dataPtr, dataSize), DisposeAfterUse::YES) {}
|
||||
|
||||
/**
|
||||
* The number of bits left
|
||||
*/
|
||||
int getBitsLeft() const { return size() - pos(); }
|
||||
|
||||
/**
|
||||
* Parse a VLC code.
|
||||
* @param bits is the number of bits which will be read at once, must be
|
||||
* identical to nbBits in init_vlc()
|
||||
* @param maxDepth is the number of times bits bits must be read to completely
|
||||
* read the longest vlc code
|
||||
* = (max_vlc_length + bits - 1) / bits
|
||||
*/
|
||||
template <int maxDepth, int bits>
|
||||
int getVLC2(int16 (*table)[2]) {
|
||||
int code;
|
||||
int n, nbBits;
|
||||
unsigned int index;
|
||||
|
||||
index = peekBits<bits>();
|
||||
code = table[index][0];
|
||||
n = table[index][1];
|
||||
|
||||
if (maxDepth > 1 && n < 0) {
|
||||
skip(bits);
|
||||
nbBits = -n;
|
||||
|
||||
index = peekBits(nbBits) + code;
|
||||
code = table[index][0];
|
||||
n = table[index][1];
|
||||
|
||||
if (maxDepth > 2 && n < 0) {
|
||||
skip(nbBits);
|
||||
nbBits = -n;
|
||||
|
||||
index = peekBits(nbBits) + code;
|
||||
code = table[index][0];
|
||||
n = table[index][1];
|
||||
}
|
||||
}
|
||||
|
||||
skip(n);
|
||||
return code;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1719
image/codecs/indeo/indeo.cpp
Normal file
1719
image/codecs/indeo/indeo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
604
image/codecs/indeo/indeo.h
Normal file
604
image/codecs/indeo/indeo.h
Normal file
@@ -0,0 +1,604 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
/* Common structures, macros, and base class shared by both Indeo4 and
|
||||
* Indeo5 decoders, derived from ffmpeg. We don't currently support Indeo5
|
||||
* decoding, but just in case we eventually need it, this is kept as a separate
|
||||
* file like it is in ffmpeg.
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_INDEO_H
|
||||
#define IMAGE_CODECS_INDEO_INDEO_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
#include "image/codecs/indeo/vlc.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* Indeo 4 frame types.
|
||||
*/
|
||||
enum {
|
||||
IVI4_FRAMETYPE_INTRA = 0,
|
||||
IVI4_FRAMETYPE_INTRA1 = 1, ///< intra frame with slightly different bitstream coding
|
||||
IVI4_FRAMETYPE_INTER = 2, ///< non-droppable P-frame
|
||||
IVI4_FRAMETYPE_BIDIR = 3, ///< bidirectional frame
|
||||
IVI4_FRAMETYPE_INTER_NOREF = 4, ///< droppable P-frame
|
||||
IVI4_FRAMETYPE_NULL_FIRST = 5, ///< empty frame with no data
|
||||
IVI4_FRAMETYPE_NULL_LAST = 6 ///< empty frame with no data
|
||||
};
|
||||
|
||||
enum {
|
||||
IVI_MB_HUFF = 0, /// Huffman table is used for coding macroblocks
|
||||
IVI_BLK_HUFF = 1 /// Huffman table is used for coding blocks
|
||||
};
|
||||
|
||||
/**
|
||||
* Declare inverse transform function types
|
||||
*/
|
||||
typedef void (InvTransformPtr)(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags);
|
||||
typedef void (DCTransformPtr)(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
typedef void (*IviMCFunc)(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
typedef void (*IviMCAvgFunc)(int16 *buf, const int16 *refBuf1, const int16 *refBuf2,
|
||||
uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
///< max number of bits of the ivi's huffman codes
|
||||
#define IVI_VLC_BITS 13
|
||||
#define IVI5_IS_PROTECTED 0x20
|
||||
|
||||
/**
|
||||
* convert unsigned values into signed ones (the sign is in the LSB)
|
||||
*/
|
||||
#define IVI_TOSIGNED(val) (-(((val) >> 1) ^ -((val) & 1)))
|
||||
|
||||
/**
|
||||
* calculate number of macroblocks in a tile
|
||||
*/
|
||||
#define IVI_MBs_PER_TILE(tileWidth, tileHeight, mbSize) \
|
||||
((((tileWidth) + (mbSize) - 1) / (mbSize)) * (((tileHeight) + (mbSize) - 1) / (mbSize)))
|
||||
|
||||
/**
|
||||
* huffman codebook descriptor
|
||||
*/
|
||||
struct IVIHuffDesc {
|
||||
int32 _numRows;
|
||||
uint8 _xBits[16];
|
||||
|
||||
/**
|
||||
* Generate a huffman codebook from the given descriptor
|
||||
* and convert it into the FFmpeg VLC table.
|
||||
*
|
||||
* @param[out] vlc Where to place the generated VLC table
|
||||
* @param[in] flag Flag: true - for static or false for dynamic tables
|
||||
* @returns result code: 0 - OK, -1 = error (invalid codebook descriptor)
|
||||
*/
|
||||
int createHuffFromDesc(VLC *vlc, bool flag) const;
|
||||
|
||||
/**
|
||||
* Compare two huffman codebook descriptors.
|
||||
*
|
||||
* @param[in] desc2 Ptr to the 2nd descriptor to compare
|
||||
* @returns comparison result: 0 - equal, 1 - not equal
|
||||
*/
|
||||
bool huffDescCompare(const IVIHuffDesc *desc2) const;
|
||||
|
||||
/**
|
||||
* Copy huffman codebook descriptors.
|
||||
*
|
||||
* @param[in] src ptr to the source descriptor
|
||||
*/
|
||||
void huffDescCopy(const IVIHuffDesc *src);
|
||||
};
|
||||
|
||||
struct IVI45DecContext;
|
||||
|
||||
/**
|
||||
* Macroblock/block huffman table descriptor
|
||||
*/
|
||||
struct IVIHuffTab {
|
||||
public:
|
||||
int32 _tabSel; /// index of one of the predefined tables
|
||||
/// or "7" for custom one
|
||||
VLC * _tab; /// pointer to the table associated with tab_sel
|
||||
|
||||
/// the following are used only when tab_sel == 7
|
||||
IVIHuffDesc _custDesc; /// custom Huffman codebook descriptor
|
||||
VLC _custTab; /// vlc table for custom codebook
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
IVIHuffTab();
|
||||
|
||||
/**
|
||||
* Decode a huffman codebook descriptor from the bitstream
|
||||
* and select specified huffman table.
|
||||
*
|
||||
* @param[in] ctx Decoder context
|
||||
* @param[in] descCoded Flag signalling if table descriptor was coded
|
||||
* @param[in] whichTab Codebook purpose (IVI_MB_HUFF or IVI_BLK_HUFF)
|
||||
* @returns Zero on success, negative value otherwise
|
||||
*/
|
||||
int decodeHuffDesc(IVI45DecContext *ctx, int descCoded, int whichTab);
|
||||
};
|
||||
|
||||
/**
|
||||
* run-value (RLE) table descriptor
|
||||
*/
|
||||
struct RVMapDesc {
|
||||
uint8 _eobSym; ///< end of block symbol
|
||||
uint8 _escSym; ///< escape symbol
|
||||
uint8 _runtab[256];
|
||||
int8 _valtab[256];
|
||||
};
|
||||
|
||||
/**
|
||||
* information for Indeo macroblock (16x16, 8x8 or 4x4)
|
||||
*/
|
||||
struct IVIMbInfo {
|
||||
int16 _xPos;
|
||||
int16 _yPos;
|
||||
uint32 _bufOffs; ///< address in the output buffer for this mb
|
||||
uint8 _type; ///< macroblock type: 0 - INTRA, 1 - INTER
|
||||
uint8 _cbp; ///< coded block pattern
|
||||
int8 _qDelta; ///< quant delta
|
||||
int8 _mvX; ///< motion vector (x component)
|
||||
int8 _mvY; ///< motion vector (y component)
|
||||
int8 _bMvX; ///< second motion vector (x component)
|
||||
int8 _bMvY; ///< second motion vector (y component)
|
||||
|
||||
IVIMbInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
* information for Indeo tile
|
||||
*/
|
||||
struct IVITile {
|
||||
int _xPos;
|
||||
int _yPos;
|
||||
int _width;
|
||||
int _height;
|
||||
int _mbSize;
|
||||
bool _isEmpty;
|
||||
int _dataSize; ///< size of the data in bytes
|
||||
int _numMBs; ///< number of macroblocks in this tile
|
||||
IVIMbInfo * _mbs; ///< array of macroblock descriptors
|
||||
IVIMbInfo * _refMbs; ///< ptr to the macroblock descriptors of the reference tile
|
||||
|
||||
IVITile();
|
||||
};
|
||||
|
||||
/**
|
||||
* information for Indeo wavelet band
|
||||
*/
|
||||
struct IVIBandDesc {
|
||||
int _plane; ///< plane number this band belongs to
|
||||
int _bandNum; ///< band number
|
||||
int _width;
|
||||
int _height;
|
||||
int _aHeight; ///< aligned band height
|
||||
const uint8 * _dataPtr; ///< ptr to the first byte of the band data
|
||||
int _dataSize; ///< size of the band data
|
||||
int16 * _buf; ///< pointer to the output buffer for this band
|
||||
int16 * _refBuf; ///< pointer to the reference frame buffer (for motion compensation)
|
||||
int16 * _bRefBuf; ///< pointer to the second reference frame buffer (for motion compensation)
|
||||
int16 * _bufs[4]; ///< array of pointers to the band buffers
|
||||
int _pitch; ///< _pitch associated with the buffers above
|
||||
bool _isEmpty;
|
||||
int _mbSize; ///< macroblock size
|
||||
int _blkSize; ///< block size
|
||||
uint8 _isHalfpel; ///< precision of the motion compensation: 0 - fullpel, 1 - halfpel
|
||||
bool _inheritMv; ///< tells if motion vector is inherited from reference macroblock
|
||||
bool _inheritQDelta; ///< tells if quantiser delta is inherited from reference macroblock
|
||||
bool _qdeltaPresent; ///< tells if Qdelta signal is present in the bitstream (Indeo5 only)
|
||||
int _quantMat; ///< dequant matrix index
|
||||
int _globQuant; ///< quant base for this band
|
||||
const uint8 * _scan; ///< ptr to the scan pattern
|
||||
int _scanSize; ///< size of the scantable
|
||||
|
||||
IVIHuffTab _blkVlc; ///< vlc table for decoding block data
|
||||
|
||||
int _numCorr; ///< number of correction entries
|
||||
uint8 _corr[61 * 2]; ///< rvmap correction pairs
|
||||
int _rvmapSel; ///< rvmap table selector
|
||||
RVMapDesc * _rvMap; ///< ptr to the RLE table for this band
|
||||
int _numTiles; ///< number of tiles in this band
|
||||
IVITile * _tiles; ///< array of tile descriptors
|
||||
InvTransformPtr *_invTransform;
|
||||
int _transformSize;
|
||||
DCTransformPtr *_dcTransform;
|
||||
bool _is2dTrans;
|
||||
int32 _checksum; ///< for debug purposes
|
||||
int _checksumPresent;
|
||||
int _bufSize; ///< band buffer size in bytes
|
||||
const uint16 * _intraBase; ///< quantization matrix for intra blocks
|
||||
const uint16 * _interBase; ///< quantization matrix for inter blocks
|
||||
const uint8 * _intraScale; ///< quantization coefficient for intra blocks
|
||||
const uint8 * _interScale; ///< quantization coefficient for inter blocks
|
||||
|
||||
IVIBandDesc();
|
||||
|
||||
int initTiles(IVITile *refTile, int p, int b, int tHeight, int tWidth);
|
||||
};
|
||||
|
||||
struct IVIPicConfig {
|
||||
uint16 _picWidth;
|
||||
uint16 _picHeight;
|
||||
uint16 _chromaWidth;
|
||||
uint16 _chromaHeight;
|
||||
uint16 _tileWidth;
|
||||
uint16 _tileHeight;
|
||||
uint8 _lumaBands;
|
||||
uint8 _chromaBands;
|
||||
|
||||
IVIPicConfig();
|
||||
|
||||
/**
|
||||
* Compare some properties of two pictures
|
||||
*/
|
||||
bool ivi_pic_config_cmp(const IVIPicConfig &cfg2);
|
||||
};
|
||||
|
||||
/**
|
||||
* color plane (luma or chroma) information
|
||||
*/
|
||||
struct IVIPlaneDesc {
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
uint8 _numBands; ///< number of bands this plane subdivided into
|
||||
IVIBandDesc * _bands; ///< array of band descriptors
|
||||
|
||||
IVIPlaneDesc();
|
||||
|
||||
static int initPlanes(IVIPlaneDesc *planes, const IVIPicConfig *cfg, bool isIndeo4);
|
||||
|
||||
static int initTiles(IVIPlaneDesc *planes, int tileWidth, int tileHeight);
|
||||
|
||||
/**
|
||||
* Free planes, bands and macroblocks buffers.
|
||||
*
|
||||
* @param[in] planes pointer to the array of the plane descriptors
|
||||
*/
|
||||
static void freeBuffers(IVIPlaneDesc *planes);
|
||||
|
||||
/**
|
||||
* Check if the given dimension of an image is valid, meaning that all
|
||||
* bytes of the image can be addressed with a signed int.
|
||||
*
|
||||
* @param w the width of the picture
|
||||
* @param h the height of the picture
|
||||
* @param log_offset the offset to sum to the log level for logging with log_ctx
|
||||
* @returns >= 0 if valid, a negative error code otherwise
|
||||
*/
|
||||
static int checkImageSize(unsigned int w, unsigned int h, int logOffset);
|
||||
};
|
||||
|
||||
struct AVFrame {
|
||||
/**
|
||||
* Dimensions
|
||||
*/
|
||||
int _width, _height;
|
||||
|
||||
#define AV_NUM_DATA_POINTERS 3
|
||||
/**
|
||||
* pointer to the picture/channel planes.
|
||||
* This might be different from the first allocated byte
|
||||
*
|
||||
* Some decoders access areas outside 0,0 - width,height, please
|
||||
* see avcodec_align_dimensions2(). Some filters and swscale can read
|
||||
* up to 16 bytes beyond the planes, if these filters are to be used,
|
||||
* then 16 extra bytes must be allocated.
|
||||
*
|
||||
* NOTE: Except for hwaccel formats, pointers not needed by the format
|
||||
* MUST be set to NULL.
|
||||
*/
|
||||
uint8 *_data[AV_NUM_DATA_POINTERS];
|
||||
|
||||
/**
|
||||
* For video, size in bytes of each picture line.
|
||||
* For audio, size in bytes of each plane.
|
||||
*
|
||||
* For audio, only linesize[0] may be set. For planar audio, each channel
|
||||
* plane must be the same size.
|
||||
*
|
||||
* For video the linesizes should be multiples of the CPUs alignment
|
||||
* preference, this is 16 or 32 for modern desktop CPUs.
|
||||
* Some code requires such alignment other code can be slower without
|
||||
* correct alignment, for yet other it makes no difference.
|
||||
*
|
||||
* @note The linesize may be larger than the size of usable data -- there
|
||||
* may be extra padding present for performance reasons.
|
||||
*/
|
||||
int _linesize[AV_NUM_DATA_POINTERS];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
AVFrame();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~AVFrame() { freeFrame(); }
|
||||
|
||||
/**
|
||||
* Sets the frame dimensions
|
||||
*/
|
||||
int setDimensions(uint16 width, uint16 height);
|
||||
|
||||
/**
|
||||
* Get a buffer for a frame
|
||||
*/
|
||||
int getBuffer(int flags);
|
||||
|
||||
/**
|
||||
* Frees any data loaded for the frame
|
||||
*/
|
||||
void freeFrame();
|
||||
};
|
||||
|
||||
struct IVI45DecContext {
|
||||
friend struct IVIHuffTab;
|
||||
private:
|
||||
VLC_TYPE _tableData[8192 * 16][2];
|
||||
VLC _iviMbVlcTabs[8]; ///< static macroblock Huffman tables
|
||||
VLC _iviBlkVlcTabs[8]; ///< static block Huffman tables
|
||||
public:
|
||||
GetBits * _gb;
|
||||
RVMapDesc _rvmapTabs[9]; ///< local corrected copy of the static rvmap tables
|
||||
|
||||
uint32 _frameNum;
|
||||
int _frameType;
|
||||
int _prevFrameType; ///< frame type of the previous frame
|
||||
uint32 _dataSize; ///< size of the frame data in bytes from picture header
|
||||
int _isScalable;
|
||||
const uint8 * _frameData; ///< input frame data pointer
|
||||
int _interScal; ///< signals a sequence of scalable inter frames
|
||||
uint32 _frameSize; ///< frame size in bytes
|
||||
uint32 _picHdrSize; ///< picture header size in bytes
|
||||
uint8 _frameFlags;
|
||||
uint16 _checksum; ///< frame _checksum
|
||||
|
||||
IVIPicConfig _picConf;
|
||||
IVIPlaneDesc _planes[3]; ///< color planes
|
||||
|
||||
int _bufSwitch; ///< used to switch between three buffers
|
||||
int _dstBuf; ///< buffer index for the currently decoded frame
|
||||
int _refBuf; ///< inter frame reference buffer index
|
||||
int _ref2Buf; ///< temporal storage for switching buffers
|
||||
int _bRefBuf; ///< second reference frame buffer index
|
||||
|
||||
IVIHuffTab _mbVlc; ///< current macroblock table descriptor
|
||||
IVIHuffTab _blkVlc; ///< current block table descriptor
|
||||
IVIHuffTab _transVlc; ///< current transparency table descriptor
|
||||
|
||||
uint8 _rvmapSel;
|
||||
bool _inImf;
|
||||
bool _inQ; ///< flag for explicitly stored quantiser delta
|
||||
uint8 _picGlobQuant;
|
||||
uint8 _unknown1;
|
||||
|
||||
uint16 _gopHdrSize;
|
||||
uint8 _gopFlags;
|
||||
uint32 _lockWord;
|
||||
|
||||
bool _hasBFrames;
|
||||
bool _hasTransp; ///< transparency mode enabled
|
||||
bool _usesTiling;
|
||||
bool _usesHaar;
|
||||
bool _usesFullpel;
|
||||
|
||||
bool _gopInvalid;
|
||||
int _bufInvalid[4];
|
||||
|
||||
bool _isIndeo4;
|
||||
uint32 _transKeyColor;
|
||||
|
||||
AVFrame * _pFrame;
|
||||
bool _gotPFrame;
|
||||
|
||||
IVI45DecContext();
|
||||
private:
|
||||
/**
|
||||
* Initial Run-value (RLE) tables.
|
||||
*/
|
||||
static const RVMapDesc _ff_ivi_rvmap_tabs[9];
|
||||
};
|
||||
|
||||
class IndeoDecoderBase : public Codec {
|
||||
private:
|
||||
/**
|
||||
* Decode an Indeo 4 or 5 band.
|
||||
*
|
||||
* @param[in,out] band ptr to the band descriptor
|
||||
* @returns result code: 0 = OK, -1 = error
|
||||
*/
|
||||
int decode_band(IVIBandDesc *band);
|
||||
|
||||
/**
|
||||
* Haar wavelet recomposition filter for Indeo 4
|
||||
*
|
||||
* @param[in] plane Pointer to the descriptor of the plane being processed
|
||||
* @param[out] dst pointer to the destination buffer
|
||||
* @param[in] dstPitch Pitch of the destination buffer
|
||||
*/
|
||||
void recomposeHaar(const IVIPlaneDesc *plane, uint8 *dst, const int dstPitch);
|
||||
|
||||
/**
|
||||
* 5/3 wavelet recomposition filter for Indeo5
|
||||
*
|
||||
* @param[in] plane Pointer to the descriptor of the plane being processed
|
||||
* @param[out] dst Pointer to the destination buffer
|
||||
* @param[in] dstPitch Pitch of the destination buffer
|
||||
*/
|
||||
void recompose53(const IVIPlaneDesc *plane, uint8 *dst, const int dstPitch);
|
||||
|
||||
/*
|
||||
* Convert and output the current plane.
|
||||
* This conversion is done by adding back the bias value of 128
|
||||
* (subtracted in the encoder) and clipping the result.
|
||||
*
|
||||
* @param[in] plane Pointer to the descriptor of the plane being processed
|
||||
* @param[out] dst Pointer to the buffer receiving converted pixels
|
||||
* @param[in] dstPitch Pitch for moving to the next y line
|
||||
*/
|
||||
void outputPlane(IVIPlaneDesc *plane, uint8 *dst, int dstPitch);
|
||||
|
||||
/**
|
||||
* Handle empty tiles by performing data copying and motion
|
||||
* compensation respectively.
|
||||
*
|
||||
* @param[in] band Pointer to the band descriptor
|
||||
* @param[in] tile Pointer to the tile descriptor
|
||||
* @param[in] mvScale Scaling factor for motion vectors
|
||||
*/
|
||||
int processEmptyTile(IVIBandDesc *band, IVITile *tile, int32 mvScale);
|
||||
|
||||
/*
|
||||
* Decode size of the tile data.
|
||||
* The size is stored as a variable-length field having the following format:
|
||||
* if (tile_data_size < 255) than this field is only one byte long
|
||||
* if (tile_data_size >= 255) than this field four is byte long: 0xFF X1 X2 X3
|
||||
* where X1-X3 is size of the tile data
|
||||
*
|
||||
* @param[in,out] gb the GetBit context
|
||||
* @returns Size of the tile data in bytes
|
||||
*/
|
||||
int decodeTileDataSize(GetBits *gb);
|
||||
|
||||
/*
|
||||
* Decode block data:
|
||||
* extract huffman-coded transform coefficients from the bitstream,
|
||||
* dequantize them, apply inverse transform and motion compensation
|
||||
* in order to reconstruct the picture.
|
||||
*
|
||||
* @param[in,out] gb The GetBit context
|
||||
* @param[in] band Pointer to the band descriptor
|
||||
* @param[in] tile Pointer to the tile descriptor
|
||||
* @returns Result code: 0 - OK, -1 = error (corrupted blocks data)
|
||||
*/
|
||||
int decodeBlocks(GetBits *gb, IVIBandDesc *band, IVITile *tile);
|
||||
|
||||
int iviMc(IVIBandDesc *band, IviMCFunc mc, IviMCAvgFunc mcAvg,
|
||||
int offs, int mvX, int mvY, int mvX2, int mvY2, int mcType, int mcType2);
|
||||
|
||||
int decodeCodedBlocks(GetBits *gb, IVIBandDesc *band,
|
||||
IviMCFunc mc, IviMCAvgFunc mcAvg, int mvX, int mvY,
|
||||
int mvX2, int mvY2, int32 *prevDc, int isIntra,
|
||||
int mcType, int mcType2, uint32 quant, int offs);
|
||||
|
||||
int iviDcTransform(IVIBandDesc *band, int32 *prevDc, int bufOffs,
|
||||
int blkSize);
|
||||
protected:
|
||||
IVI45DecContext _ctx;
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
uint _bitsPerPixel;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
/**
|
||||
* Scan patterns shared between indeo4 and indeo5
|
||||
*/
|
||||
static const uint8 _ffIviVerticalScan8x8[64];
|
||||
static const uint8 _ffIviHorizontalScan8x8[64];
|
||||
static const uint8 _ffIviDirectScan4x4[16];
|
||||
protected:
|
||||
/**
|
||||
* Returns the pixel format for the decoder's surface
|
||||
*/
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
|
||||
/**
|
||||
* Select the preferred format to use, for codecs where this is faster than converting
|
||||
* the image afterwards. Returns true if supported, and false otherwise.
|
||||
*/
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the Indeo picture header.
|
||||
* @returns 0 = Ok, negative number = error
|
||||
*/
|
||||
virtual int decodePictureHeader() = 0;
|
||||
|
||||
/**
|
||||
* Rearrange decoding and reference buffers.
|
||||
*/
|
||||
virtual void switchBuffers() = 0;
|
||||
|
||||
virtual bool isNonNullFrame() const = 0;
|
||||
|
||||
/**
|
||||
* Decode Indeo band header.
|
||||
*
|
||||
* @param[in,out] band Pointer to the band descriptor
|
||||
* @returns Result code: 0 = OK, negative number = error
|
||||
*/
|
||||
virtual int decodeBandHeader(IVIBandDesc *band) = 0;
|
||||
|
||||
/**
|
||||
* Decode information (block type, _cbp, quant delta, motion vector)
|
||||
* for all macroblocks in the current tile.
|
||||
*
|
||||
* @param[in,out] band Pointer to the band descriptor
|
||||
* @param[in,out] tile Pointer to the tile descriptor
|
||||
* @returns Result code: 0 = OK, negative number = error
|
||||
*/
|
||||
virtual int decodeMbInfo(IVIBandDesc *band, IVITile *tile) = 0;
|
||||
|
||||
/**
|
||||
* Decodes optional transparency data within Indeo frames
|
||||
*/
|
||||
virtual int decodeTransparency() { return -1; }
|
||||
|
||||
/**
|
||||
* Decodes the Indeo frame from the bit reader already
|
||||
* loaded into the context
|
||||
*/
|
||||
int decodeIndeoFrame();
|
||||
|
||||
/**
|
||||
* scale motion vector
|
||||
*/
|
||||
int scaleMV(int mv, int mvScale);
|
||||
public:
|
||||
IndeoDecoderBase(uint16 width, uint16 height, uint bitsPerPixel);
|
||||
~IndeoDecoderBase() override;
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
607
image/codecs/indeo/indeo_dsp.cpp
Normal file
607
image/codecs/indeo/indeo_dsp.cpp
Normal file
@@ -0,0 +1,607 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "image/codecs/indeo/indeo_dsp.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* butterfly operation for the inverse Haar transform
|
||||
*/
|
||||
#define IVI_HAAR_BFLY(s1, s2, o1, o2, t) \
|
||||
t = ((s1) - (s2)) >> 1;\
|
||||
o1 = ((s1) + (s2)) >> 1;\
|
||||
o2 = (t);\
|
||||
|
||||
/**
|
||||
* inverse 8-point Haar transform
|
||||
*/
|
||||
#define INV_HAAR8(s1, s5, s3, s7, s2, s4, s6, s8,\
|
||||
d1, d2, d3, d4, d5, d6, d7, d8,\
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8) {\
|
||||
t1 = (s1) << 1; t5 = (s5) << 1;\
|
||||
IVI_HAAR_BFLY(t1, t5, t1, t5, t0); IVI_HAAR_BFLY(t1, s3, t1, t3, t0);\
|
||||
IVI_HAAR_BFLY(t5, s7, t5, t7, t0); IVI_HAAR_BFLY(t1, s2, t1, t2, t0);\
|
||||
IVI_HAAR_BFLY(t3, s4, t3, t4, t0); IVI_HAAR_BFLY(t5, s6, t5, t6, t0);\
|
||||
IVI_HAAR_BFLY(t7, s8, t7, t8, t0);\
|
||||
d1 = COMPENSATE(t1);\
|
||||
d2 = COMPENSATE(t2);\
|
||||
d3 = COMPENSATE(t3);\
|
||||
d4 = COMPENSATE(t4);\
|
||||
d5 = COMPENSATE(t5);\
|
||||
d6 = COMPENSATE(t6);\
|
||||
d7 = COMPENSATE(t7);\
|
||||
d8 = COMPENSATE(t8); }
|
||||
|
||||
/**
|
||||
* inverse 4-point Haar transform
|
||||
*/
|
||||
#define INV_HAAR4(s1, s3, s5, s7, d1, d2, d3, d4, t0, t1, t2, t3, t4) {\
|
||||
IVI_HAAR_BFLY(s1, s3, t0, t1, t4);\
|
||||
IVI_HAAR_BFLY(t0, s5, t2, t3, t4);\
|
||||
d1 = COMPENSATE(t2);\
|
||||
d2 = COMPENSATE(t3);\
|
||||
IVI_HAAR_BFLY(t1, s7, t2, t3, t4);\
|
||||
d3 = COMPENSATE(t2);\
|
||||
d4 = COMPENSATE(t3); }
|
||||
|
||||
void IndeoDSP::ffIviInverseHaar8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int32 tmp[64];
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
// apply the InvHaar8 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
// pre-scaling
|
||||
int shift = !(i & 4);
|
||||
int sp1 = src[ 0] << shift;
|
||||
int sp2 = src[ 8] << shift;
|
||||
int sp3 = src[16] << shift;
|
||||
int sp4 = src[24] << shift;
|
||||
INV_HAAR8( sp1, sp2, sp3, sp4,
|
||||
src[32], src[40], src[48], src[56],
|
||||
dst[ 0], dst[ 8], dst[16], dst[24],
|
||||
dst[32], dst[40], dst[48], dst[56],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
dst[ 0] = dst[ 8] = dst[16] = dst[24] =
|
||||
dst[32] = dst[40] = dst[48] = dst[56] = 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
// apply the InvHaar8 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3] &&
|
||||
!src[4] && !src[5] && !src[6] && !src[7]) {
|
||||
memset(out, 0, 8 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR8(src[0], src[1], src[2], src[3],
|
||||
src[4], src[5], src[6], src[7],
|
||||
out[0], out[1], out[2], out[3],
|
||||
out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
src += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
// apply the InvHaar8 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ( !in[0] && !in[1] && !in[2] && !in[3]
|
||||
&& !in[4] && !in[5] && !in[6] && !in[7]) {
|
||||
memset(out, 0, 8 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR8(in[0], in[1], in[2], in[3],
|
||||
in[4], in[5], in[6], in[7],
|
||||
out[0], out[1], out[2], out[3],
|
||||
out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
in += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
// apply the InvHaar8 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
INV_HAAR8(in[ 0], in[ 8], in[16], in[24],
|
||||
in[32], in[40], in[48], in[56],
|
||||
out[0 * pitch], out[1 * pitch],
|
||||
out[2 * pitch], out[3 * pitch],
|
||||
out[4 * pitch], out[5 * pitch],
|
||||
out[6 * pitch], out[7 * pitch],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
out[0 * pitch] = out[1 * pitch] =
|
||||
out[2 * pitch] = out[3 * pitch] =
|
||||
out[4 * pitch] = out[5 * pitch] =
|
||||
out[6 * pitch] = out[7 * pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviInverseHaar4x4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int32 tmp[16];
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
// apply the InvHaar4 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
// pre-scaling
|
||||
int shift = !(i & 2);
|
||||
int sp1 = src[0] << shift;
|
||||
int sp2 = src[4] << shift;
|
||||
INV_HAAR4( sp1, sp2, src[8], src[12],
|
||||
dst[0], dst[4], dst[8], dst[12],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
dst[0] = dst[4] = dst[8] = dst[12] = 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
// apply the InvHaar8 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3]) {
|
||||
memset(out, 0, 4 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR4(src[0], src[1], src[2], src[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
src += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
// apply the InvHaar4 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!in[0] && !in[1] && !in[2] && !in[3]) {
|
||||
memset(out, 0, 4 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR4(in[0], in[1], in[2], in[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
in += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
// apply the InvHaar8 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
INV_HAAR4(in[0], in[4], in[8], in[12],
|
||||
out[0 * pitch], out[1 * pitch],
|
||||
out[2 * pitch], out[3 * pitch],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
out[0 * pitch] = out[1 * pitch] =
|
||||
out[2 * pitch] = out[3 * pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcHaar2d(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize) {
|
||||
int16 dcCoeff = (*in + 0) >> 3;
|
||||
|
||||
for (int y = 0; y < blkSize; out += pitch, y++) {
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = dcCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
//* butterfly operation for the inverse slant transform
|
||||
#define IVI_SLANT_BFLY(s1, s2, o1, o2, t) \
|
||||
t = (s1) - (s2);\
|
||||
o1 = (s1) + (s2);\
|
||||
o2 = (t);\
|
||||
|
||||
//* This is a reflection a,b = 1/2, 5/4 for the inverse slant transform
|
||||
#define IVI_IREFLECT(s1, s2, o1, o2, t) \
|
||||
t = (((s1) + (s2)*2 + 2) >> 2) + (s1);\
|
||||
o2 = (((s1)*2 - (s2) + 2) >> 2) - (s2);\
|
||||
o1 = (t);\
|
||||
|
||||
//* This is a reflection a,b = 1/2, 7/8 for the inverse slant transform
|
||||
#define IVI_SLANT_PART4(s1, s2, o1, o2, t) \
|
||||
t = (s2) + (((s1)*4 - (s2) + 4) >> 3);\
|
||||
o2 = (s1) + ((-(s1) - (s2)*4 + 4) >> 3);\
|
||||
o1 = (t);\
|
||||
|
||||
//* inverse slant8 transform
|
||||
#define IVI_INV_SLANT8(s1, s4, s8, s5, s2, s6, s3, s7,\
|
||||
d1, d2, d3, d4, d5, d6, d7, d8,\
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8) {\
|
||||
IVI_SLANT_PART4(s4, s5, t4, t5, t0);\
|
||||
\
|
||||
IVI_SLANT_BFLY(s1, t5, t1, t5, t0); IVI_SLANT_BFLY(s2, s6, t2, t6, t0);\
|
||||
IVI_SLANT_BFLY(s7, s3, t7, t3, t0); IVI_SLANT_BFLY(t4, s8, t4, t8, t0);\
|
||||
\
|
||||
IVI_SLANT_BFLY(t1, t2, t1, t2, t0); IVI_IREFLECT (t4, t3, t4, t3, t0);\
|
||||
IVI_SLANT_BFLY(t5, t6, t5, t6, t0); IVI_IREFLECT (t8, t7, t8, t7, t0);\
|
||||
IVI_SLANT_BFLY(t1, t4, t1, t4, t0); IVI_SLANT_BFLY(t2, t3, t2, t3, t0);\
|
||||
IVI_SLANT_BFLY(t5, t8, t5, t8, t0); IVI_SLANT_BFLY(t6, t7, t6, t7, t0);\
|
||||
d1 = COMPENSATE(t1);\
|
||||
d2 = COMPENSATE(t2);\
|
||||
d3 = COMPENSATE(t3);\
|
||||
d4 = COMPENSATE(t4);\
|
||||
d5 = COMPENSATE(t5);\
|
||||
d6 = COMPENSATE(t6);\
|
||||
d7 = COMPENSATE(t7);\
|
||||
d8 = COMPENSATE(t8);}
|
||||
|
||||
//* inverse slant4 transform
|
||||
#define IVI_INV_SLANT4(s1, s4, s2, s3, d1, d2, d3, d4, t0, t1, t2, t3, t4) {\
|
||||
IVI_SLANT_BFLY(s1, s2, t1, t2, t0); IVI_IREFLECT (s4, s3, t4, t3, t0);\
|
||||
\
|
||||
IVI_SLANT_BFLY(t1, t4, t1, t4, t0); IVI_SLANT_BFLY(t2, t3, t2, t3, t0);\
|
||||
d1 = COMPENSATE(t1);\
|
||||
d2 = COMPENSATE(t2);\
|
||||
d3 = COMPENSATE(t3);\
|
||||
d4 = COMPENSATE(t4);}
|
||||
|
||||
void IndeoDSP::ffIviInverseSlant8x8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) {
|
||||
int32 tmp[64];
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT8(src[0], src[8], src[16], src[24], src[32], src[40], src[48], src[56],
|
||||
dst[0], dst[8], dst[16], dst[24], dst[32], dst[40], dst[48], dst[56],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
dst[0] = dst[8] = dst[16] = dst[24] = dst[32] = dst[40] = dst[48] = dst[56] = 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3] && !src[4] && !src[5] && !src[6] && !src[7]) {
|
||||
memset(out, 0, 8*sizeof(out[0]));
|
||||
} else {
|
||||
IVI_INV_SLANT8(src[0], src[1], src[2], src[3], src[4], src[5], src[6], src[7],
|
||||
out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
src += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviInverseSlant4x4(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) {
|
||||
int32 tmp[16];
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT4(src[0], src[4], src[8], src[12],
|
||||
dst[0], dst[4], dst[8], dst[12],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
dst[0] = dst[4] = dst[8] = dst[12] = 0;
|
||||
}
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3]) {
|
||||
out[0] = out[1] = out[2] = out[3] = 0;
|
||||
} else {
|
||||
IVI_INV_SLANT4(src[0], src[1], src[2], src[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
src += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcSlant2d(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize) {
|
||||
int16 dcCoeff = (*in + 1) >> 1;
|
||||
|
||||
for (int y = 0; y < blkSize; out += pitch, y++) {
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = dcCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowSlant8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!in[0] && !in[1] && !in[2] && !in[3] && !in[4] && !in[5] && !in[6] && !in[7]) {
|
||||
memset(out, 0, 8*sizeof(out[0]));
|
||||
} else {
|
||||
IVI_INV_SLANT8( in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7],
|
||||
out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
in += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcRowSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize) {
|
||||
int16 dcCoeff = (*in + 1) >> 1;
|
||||
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = dcCoeff;
|
||||
|
||||
out += pitch;
|
||||
|
||||
for (int y = 1; y < blkSize; out += pitch, y++) {
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColSlant8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
int row2 = pitch << 1;
|
||||
int row4 = pitch << 2;
|
||||
int row8 = pitch << 3;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT8(in[0], in[8], in[16], in[24], in[32], in[40], in[48], in[56],
|
||||
out[0], out[pitch], out[row2], out[row2 + pitch], out[row4],
|
||||
out[row4 + pitch], out[row4 + row2], out[row8 - pitch],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
out[0] = out[pitch] = out[row2] = out[row2 + pitch] = out[row4] =
|
||||
out[row4 + pitch] = out[row4 + row2] = out[row8 - pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcColSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize) {
|
||||
int16 dcCoeff = (*in + 1) >> 1;
|
||||
|
||||
for (int y = 0; y < blkSize; out += pitch, y++) {
|
||||
out[0] = dcCoeff;
|
||||
for (int x = 1; x < blkSize; x++)
|
||||
out[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowSlant4(const int32 *in, int16 *out,
|
||||
uint32 pitch, const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!in[0] && !in[1] && !in[2] && !in[3]) {
|
||||
memset(out, 0, 4*sizeof(out[0]));
|
||||
} else {
|
||||
IVI_INV_SLANT4( in[0], in[1], in[2], in[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
in += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColSlant4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
int row2 = pitch << 1;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT4(in[0], in[4], in[8], in[12],
|
||||
out[0], out[pitch], out[row2], out[row2 + pitch],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
out[0] = out[pitch] = out[row2] = out[row2 + pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviPutPixels8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
for (int y = 0; y < 8; out += pitch, in += 8, y++)
|
||||
for (int x = 0; x < 8; x++)
|
||||
out[x] = in[x];
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviPutDcPixel8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize) {
|
||||
out[0] = in[0];
|
||||
memset(out + 1, 0, 7 * sizeof(out[0]));
|
||||
out += pitch;
|
||||
|
||||
for (int y = 1; y < 8; out += pitch, y++)
|
||||
memset(out, 0, 8 * sizeof(out[0]));
|
||||
}
|
||||
|
||||
#define IVI_MC_TEMPLATE(size, suffix, OP) \
|
||||
static void iviMc ## size ##x## size ## suffix(int16 *buf, \
|
||||
uint32 dpitch, \
|
||||
const int16 *refBuf, \
|
||||
uint32 pitch, int mcType) \
|
||||
{ \
|
||||
const int16 *wptr; \
|
||||
\
|
||||
switch (mcType) { \
|
||||
case 0: /* fullpel (no interpolation) */ \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, refBuf += pitch) { \
|
||||
for (int j = 0; j < size; j++) {\
|
||||
OP(buf[j], refBuf[j]); \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
case 1: /* horizontal halfpel interpolation */ \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, refBuf += pitch) \
|
||||
for (int j = 0; j < size; j++) \
|
||||
OP(buf[j], (refBuf[j] + refBuf[j+1]) >> 1); \
|
||||
break; \
|
||||
case 2: /* vertical halfpel interpolation */ \
|
||||
wptr = refBuf + pitch; \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, wptr += pitch, refBuf += pitch) \
|
||||
for (int j = 0; j < size; j++) \
|
||||
OP(buf[j], (refBuf[j] + wptr[j]) >> 1); \
|
||||
break; \
|
||||
case 3: /* vertical and horizontal halfpel interpolation */ \
|
||||
wptr = refBuf + pitch; \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, wptr += pitch, refBuf += pitch) \
|
||||
for (int j = 0; j < size; j++) \
|
||||
OP(buf[j], (refBuf[j] + refBuf[j+1] + wptr[j] + wptr[j+1]) >> 2); \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void IndeoDSP::ffIviMc ## size ##x## size ## suffix(int16 *buf, const int16 *refBuf, \
|
||||
uint32 pitch, int mcType) \
|
||||
{ \
|
||||
iviMc ## size ##x## size ## suffix(buf, pitch, refBuf, pitch, mcType); \
|
||||
}
|
||||
|
||||
#define IVI_MC_AVG_TEMPLATE(size, suffix, OP) \
|
||||
void IndeoDSP::ffIviMcAvg ## size ##x## size ## suffix(int16 *buf, \
|
||||
const int16 *refBuf, \
|
||||
const int16 *refBuf2, \
|
||||
uint32 pitch, \
|
||||
int mcType, int mcType2) \
|
||||
{ \
|
||||
int16 tmp[size * size]; \
|
||||
\
|
||||
iviMc ## size ##x## size ## NoDelta(tmp, size, refBuf, pitch, mcType); \
|
||||
iviMc ## size ##x## size ## Delta(tmp, size, refBuf2, pitch, mcType2); \
|
||||
for (int i = 0; i < size; i++, buf += pitch) { \
|
||||
for (int j = 0; j < size; j++) {\
|
||||
OP(buf[j], tmp[i * size + j] >> 1); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define OP_PUT(a, b) (a) = (b)
|
||||
#define OP_ADD(a, b) (a) += (b)
|
||||
|
||||
IVI_MC_TEMPLATE(8, NoDelta, OP_PUT)
|
||||
IVI_MC_TEMPLATE(8, Delta, OP_ADD)
|
||||
IVI_MC_TEMPLATE(4, NoDelta, OP_PUT)
|
||||
IVI_MC_TEMPLATE(4, Delta, OP_ADD)
|
||||
IVI_MC_AVG_TEMPLATE(8, NoDelta, OP_PUT)
|
||||
IVI_MC_AVG_TEMPLATE(8, Delta, OP_ADD)
|
||||
IVI_MC_AVG_TEMPLATE(4, NoDelta, OP_PUT)
|
||||
IVI_MC_AVG_TEMPLATE(4, Delta, OP_ADD)
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
335
image/codecs/indeo/indeo_dsp.h
Normal file
335
image/codecs/indeo/indeo_dsp.h
Normal file
@@ -0,0 +1,335 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note:
|
||||
* DSP functions (inverse transforms, motion compensation, wavelet recompositions)
|
||||
* for Indeo Video Interactive codecs.
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_INDEO_DSP_H
|
||||
#define IMAGE_CODECS_INDEO_INDEO_DSP_H
|
||||
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
#include "image/codecs/indeo/indeo.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
class IndeoDSP {
|
||||
public:
|
||||
/**
|
||||
* two-dimensional inverse Haar 8x8 transform for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseHaar8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
static void ffIviInverseHaar8x1(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
static void ffIviInverseHaar1x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 8-point Haar transform on rows for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviRowHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 8-point Haar transform on columns for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* two-dimensional inverse Haar 4x4 transform for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseHaar4x4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 4-point Haar transform on rows for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviRowHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 4-point Haar transform on columns for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* DC-only two-dimensional inverse Haar transform for Indeo 4.
|
||||
* Performing the inverse transform in this case is equivalent to
|
||||
* spreading dcCoeff >> 3 over the whole block.
|
||||
*
|
||||
* @param[in] in Pointer to the dc coefficient
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] blkSize Transform block size
|
||||
*/
|
||||
static void ffIviDcHaar2d(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize);
|
||||
|
||||
/**
|
||||
* two-dimensional inverse slant 8x8 transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseSlant8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* two-dimensional inverse slant 4x4 transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseSlant4x4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* DC-only two-dimensional inverse slant transform.
|
||||
* Performing the inverse slant transform in this case is equivalent to
|
||||
* spreading (dcCoeff + 1)/2 over the whole block.
|
||||
* It works much faster than performing the slant transform on a vector of zeroes.
|
||||
*
|
||||
* @param[in] in Pointer to the dc coefficient
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] blkSize Transform block size
|
||||
*/
|
||||
static void ffIviDcSlant2d(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* inverse 1D row slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags (unused here)
|
||||
*/
|
||||
static void ffIviRowSlant8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* inverse 1D column slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColSlant8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* inverse 1D row slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags (unused here)
|
||||
*/
|
||||
static void ffIviRowSlant4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* inverse 1D column slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColSlant4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* DC-only inverse row slant transform
|
||||
*/
|
||||
static void ffIviDcRowSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* DC-only inverse column slant transform
|
||||
*/
|
||||
static void ffIviDcColSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* Copy the pixels into the frame buffer.
|
||||
*/
|
||||
static void ffIviPutPixels8x8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags);
|
||||
|
||||
/**
|
||||
* Copy the DC coefficient into the first pixel of the block and
|
||||
* zero all others.
|
||||
*/
|
||||
static void ffIviPutDcPixel8x8(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* 8x8 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc8x8Delta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc4x4Delta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* motion compensation without adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc8x8NoDelta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation without adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame receiving the result
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc4x4NoDelta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* 8x8 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg8x8Delta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg4x4Delta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
/**
|
||||
* motion compensation without adding delta for B-frames
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg8x8NoDelta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation without adding delta for B-frames
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg4x4NoDelta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
135
image/codecs/indeo/mem.cpp
Normal file
135
image/codecs/indeo/mem.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
const uint8 ffReverse[256] = {
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
|
||||
};
|
||||
|
||||
const uint8 ffZigZagDirect[64] = {
|
||||
0, 1, 8, 16, 9, 2, 3, 10,
|
||||
17, 24, 32, 25, 18, 11, 4, 5,
|
||||
12, 19, 26, 33, 40, 48, 41, 34,
|
||||
27, 20, 13, 6, 7, 14, 21, 28,
|
||||
35, 42, 49, 56, 57, 50, 43, 36,
|
||||
29, 22, 15, 23, 30, 37, 44, 51,
|
||||
58, 59, 52, 45, 38, 31, 39, 46,
|
||||
53, 60, 61, 54, 47, 55, 62, 63
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Multiply two `size_t` values checking for overflow.
|
||||
*
|
||||
* @param[in] a,b Operands of multiplication
|
||||
* @param[out] r Pointer to the result of the operation
|
||||
* @return 0 on success, AVERROR(EINVAL) on overflow
|
||||
*/
|
||||
static inline int avSizeMult(size_t a, size_t b, size_t *r) {
|
||||
size_t t = a * b;
|
||||
|
||||
// Hack inspired from glibc: don't try the division if nelem and elsize
|
||||
// are both less than sqrt(SIZE_MAX).
|
||||
if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b)
|
||||
return -1;
|
||||
*r = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void avFreeP(void *arg) {
|
||||
void **ptr = (void **)arg;
|
||||
free(*ptr);
|
||||
*ptr = nullptr;
|
||||
}
|
||||
|
||||
void *avReallocF(void *ptr, size_t nelem, size_t elsize) {
|
||||
size_t size;
|
||||
void *r;
|
||||
|
||||
if (avSizeMult(elsize, nelem, &size)) {
|
||||
free(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
r = realloc(ptr, size);
|
||||
if (!r)
|
||||
free(ptr);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Swap the order of the bytes in the passed value
|
||||
*/
|
||||
uint32 bitswap32(uint32 x) {
|
||||
return (uint32)ffReverse[x & 0xFF] << 24 |
|
||||
(uint32)ffReverse[(x >> 8) & 0xFF] << 16 |
|
||||
(uint32)ffReverse[(x >> 16) & 0xFF] << 8 |
|
||||
(uint32)ffReverse[x >> 24];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse "nbits" bits of the value "val" and return the result
|
||||
* in the least significant bits.
|
||||
*/
|
||||
uint16 invertBits(uint16 val, int nbits) {
|
||||
uint16 res;
|
||||
|
||||
if (nbits <= 8) {
|
||||
res = ffReverse[val] >> (8 - nbits);
|
||||
} else {
|
||||
res = ((ffReverse[val & 0xFF] << 8) +
|
||||
(ffReverse[val >> 8])) >> (16 - nbits);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
109
image/codecs/indeo/mem.h
Normal file
109
image/codecs/indeo/mem.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
/* Common memory code used by the Indeo decoder
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_MEM_H
|
||||
#define IMAGE_CODECS_INDEO_MEM_H
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define FFALIGN(x, a) (((x) + (a)-1) & ~((a)-1))
|
||||
#define FFSIGN(a) ((a) > 0 ? 1 : -1)
|
||||
#define MAX_INTEGER 0x7ffffff
|
||||
|
||||
/**
|
||||
* Free a memory block which has been allocated with a function of av_malloc()
|
||||
* or av_realloc() family, and set the pointer pointing to it to `NULL`.
|
||||
*
|
||||
* @param ptr Pointer to the pointer to the memory block which should be freed
|
||||
* @note `*ptr = NULL` is safe and leads to no action.
|
||||
*/
|
||||
extern void avFreeP(void *arg);
|
||||
|
||||
|
||||
/**
|
||||
* Allocate, reallocate, or free a block of memory.
|
||||
*
|
||||
* This function does the same thing as av_realloc(), except:
|
||||
* - It takes two size arguments and allocates `nelem * elsize` bytes,
|
||||
* after checking the result of the multiplication for integer overflow.
|
||||
* - It frees the input block in case of failure, thus avoiding the memory
|
||||
* leak with the classic
|
||||
* @code{.c}
|
||||
* buf = realloc(buf);
|
||||
* if (!buf)
|
||||
* return -1;
|
||||
* @endcode
|
||||
* pattern.
|
||||
*/
|
||||
extern void *avReallocF(void *ptr, size_t nelem, size_t elsize);
|
||||
|
||||
/**
|
||||
* Reverse "nbits" bits of the value "val" and return the result
|
||||
* in the least significant bits.
|
||||
*/
|
||||
extern uint16 invertBits(uint16 val, int nbits);
|
||||
|
||||
/**
|
||||
* Swap the order of the bytes in the passed value
|
||||
*/
|
||||
extern uint32 bitswap32(uint32 x);
|
||||
|
||||
/**
|
||||
* Clip a signed integer value into the 0-255 range.
|
||||
* @param a value to clip
|
||||
* @return clipped value
|
||||
*/
|
||||
inline uint8 avClipUint8(int a) {
|
||||
if (a & (~0xFF))
|
||||
return (-a) >> 31;
|
||||
else
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip a signed integer to an unsigned power of two range.
|
||||
* @param a value to clip
|
||||
* @param p bit position to clip at
|
||||
* @return clipped value
|
||||
*/
|
||||
inline unsigned avClipUintp2(int a, int p) {
|
||||
if (a & ~((1 << p) - 1))
|
||||
return -a >> 31 & ((1 << p) - 1);
|
||||
else
|
||||
return a;
|
||||
}
|
||||
|
||||
extern const uint8 ffZigZagDirect[64];
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
335
image/codecs/indeo/vlc.cpp
Normal file
335
image/codecs/indeo/vlc.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "image/codecs/indeo/vlc.h"
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* Quicksort
|
||||
* This sort is fast, and fully inplace but not stable and it is possible
|
||||
* to construct input that requires O(n^2) time but this is very unlikely to
|
||||
* happen with non constructed input.
|
||||
*/
|
||||
#define AV_QSORT(p, num, type, cmp) do {\
|
||||
void *stack[64][2];\
|
||||
int sp = 1;\
|
||||
stack[0][0] = p;\
|
||||
stack[0][1] = (p)+(num)-1;\
|
||||
while(sp){\
|
||||
type *start = (type *)stack[--sp][0];\
|
||||
type *end = (type *)stack[ sp][1];\
|
||||
while (start < end) {\
|
||||
if (start < end-1) {\
|
||||
int checksort = 0;\
|
||||
type *right = end - 2;\
|
||||
type *left = start + 1;\
|
||||
type *mid = start + ((end - start) >> 1);\
|
||||
if(cmp(start, end) > 0) {\
|
||||
if(cmp( end, mid) > 0) SWAP(*start, *mid);\
|
||||
else SWAP(*start, *end);\
|
||||
} else {\
|
||||
if(cmp(start, mid) > 0) SWAP(*start, *mid);\
|
||||
else checksort = 1;\
|
||||
}\
|
||||
if (cmp(mid, end) > 0) { \
|
||||
SWAP(*mid, *end);\
|
||||
checksort = 0;\
|
||||
}\
|
||||
if(start == end - 2) break;\
|
||||
SWAP(end[-1], *mid);\
|
||||
while (left <= right) {\
|
||||
while (left<=right && cmp(left, end - 1) < 0)\
|
||||
left++;\
|
||||
while (left<=right && cmp(right, end - 1) > 0)\
|
||||
right--;\
|
||||
if (left <= right) {\
|
||||
SWAP(*left, *right);\
|
||||
left++;\
|
||||
right--;\
|
||||
}\
|
||||
}\
|
||||
SWAP(end[-1], *left);\
|
||||
if(checksort && (mid == left - 1 || mid == left)){\
|
||||
mid= start;\
|
||||
while(mid<end && cmp(mid, mid+1) <= 0)\
|
||||
mid++;\
|
||||
if(mid==end)\
|
||||
break;\
|
||||
}\
|
||||
if (end - left < left - start){\
|
||||
stack[sp ][0] = start;\
|
||||
stack[sp++][1] = right;\
|
||||
start = left + 1;\
|
||||
} else {\
|
||||
stack[sp ][0] = left+1;\
|
||||
stack[sp++][1] = end;\
|
||||
end = right;\
|
||||
}\
|
||||
} else {\
|
||||
if (cmp(start, end) > 0)\
|
||||
SWAP(*start, *end);\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
#define COPY(condition)\
|
||||
for (i = 0; i < nbCodes; i++) { \
|
||||
buf[j].bits = getData(p_bits, i, bitsWrap, bitsSize); \
|
||||
if (!(condition)) \
|
||||
continue; \
|
||||
if (buf[j].bits > (3 * nbBits) || buf[j].bits > 32) { \
|
||||
warning("Too long VLC (%d) in init_vlc", buf[j].bits); \
|
||||
if (!(flags & INIT_VLC_USE_NEW_STATIC)) \
|
||||
free(buf); \
|
||||
return -1; \
|
||||
} \
|
||||
buf[j].code = getData(codes, i, codesWrap, codesSize); \
|
||||
if (buf[j].code >= (1LL << buf[j].bits)) { \
|
||||
warning("Invalid code %x for %d in init_vlc", buf[j].code, i); \
|
||||
if (!(flags & INIT_VLC_USE_NEW_STATIC)) \
|
||||
free(buf); \
|
||||
return -1; \
|
||||
} \
|
||||
if (flags & INIT_VLC_LE) \
|
||||
buf[j].code = bitswap32(buf[j].code); \
|
||||
else \
|
||||
buf[j].code <<= 32 - buf[j].bits; \
|
||||
if (symbols) \
|
||||
buf[j].symbol = getData(symbols, i, symbolsWrap, symbolsSize); \
|
||||
else \
|
||||
buf[j].symbol = i; \
|
||||
j++; \
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
VLC::VLC() : _bits(0), _tableSize(0), _tableAllocated(0), _table(nullptr) {
|
||||
}
|
||||
|
||||
int VLC::init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, int bitsSize,
|
||||
const void *codes, int codesWrap, int codesSize, int flags) {
|
||||
return init_vlc(nbBits, nbCodes, bits, bitsWrap, bitsSize, codes, codesWrap,
|
||||
codesSize, nullptr, 0, 0, flags);
|
||||
}
|
||||
|
||||
int VLC::init_vlc(int nbBits, int nbCodes, const void *p_bits, int bitsWrap,
|
||||
int bitsSize, const void *codes, int codesWrap, int codesSize,
|
||||
const void *symbols, int symbolsWrap, int symbolsSize, int flags) {
|
||||
VLCcode *buf;
|
||||
int i, j, ret;
|
||||
VLCcode localbuf[1500]; // the maximum currently needed is 1296 by rv34
|
||||
VLC localvlc, *vlc;
|
||||
|
||||
vlc = this;
|
||||
vlc->_bits = nbBits;
|
||||
if (flags & INIT_VLC_USE_NEW_STATIC) {
|
||||
assert((nbCodes + 1) <= (int)FF_ARRAY_ELEMS(localbuf));
|
||||
buf = localbuf;
|
||||
localvlc = *this;
|
||||
vlc = &localvlc;
|
||||
vlc->_tableSize = 0;
|
||||
} else {
|
||||
vlc->_table = NULL;
|
||||
vlc->_tableAllocated = 0;
|
||||
vlc->_tableSize = 0;
|
||||
|
||||
buf = (VLCcode *)malloc((nbCodes + 1) * sizeof(VLCcode));
|
||||
assert(buf);
|
||||
}
|
||||
|
||||
assert(symbolsSize <= 2 || !symbols);
|
||||
j = 0;
|
||||
|
||||
COPY(buf[j].bits > nbBits);
|
||||
|
||||
// qsort is the slowest part of init_vlc, and could probably be improved or avoided
|
||||
AV_QSORT(buf, j, VLCcode, compareVlcSpec);
|
||||
COPY(buf[j].bits && buf[j].bits <= nbBits);
|
||||
nbCodes = j;
|
||||
|
||||
ret = vlc->buildTable(nbBits, nbCodes, buf, flags);
|
||||
|
||||
if (flags & INIT_VLC_USE_NEW_STATIC) {
|
||||
if (vlc->_tableSize != vlc->_tableAllocated)
|
||||
warning("needed %d had %d", vlc->_tableSize, vlc->_tableAllocated);
|
||||
|
||||
assert(ret >= 0);
|
||||
*this = *vlc;
|
||||
} else {
|
||||
free(buf);
|
||||
if (ret < 0) {
|
||||
avFreeP(&vlc->_table);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VLC::freeVlc() {
|
||||
free(_table);
|
||||
}
|
||||
|
||||
int VLC::compareVlcSpec(const void *a, const void *b) {
|
||||
const VLCcode *sa = (const VLCcode *)a, *sb = (const VLCcode *)b;
|
||||
return (sa->code >> 1) - (sb->code >> 1);
|
||||
}
|
||||
|
||||
int VLC::buildTable(int tableNbBits, int nbCodes,
|
||||
VLCcode *codes, int flags) {
|
||||
VLC *vlc = this;
|
||||
int tableSize, tableIndex, index, codePrefix, symbol, subtableBits;
|
||||
int i, j, k, n, nb, inc;
|
||||
uint32 code;
|
||||
VLC_TYPE (*table)[2];
|
||||
|
||||
tableSize = 1 << tableNbBits;
|
||||
if (tableNbBits > 30)
|
||||
return -1;
|
||||
tableIndex = allocTable(tableSize, flags & INIT_VLC_USE_NEW_STATIC);
|
||||
//warning("new table index=%d size=%d", tableIndex, tableSize);
|
||||
if (tableIndex < 0)
|
||||
return tableIndex;
|
||||
table = &vlc->_table[tableIndex];
|
||||
|
||||
// first pass: map codes and compute auxiliary table sizes
|
||||
for (i = 0; i < nbCodes; i++) {
|
||||
n = codes[i].bits;
|
||||
code = codes[i].code;
|
||||
symbol = codes[i].symbol;
|
||||
//warning("i=%d n=%d code=0x%x", i, n, code);
|
||||
|
||||
if (n <= tableNbBits) {
|
||||
// no need to add another table
|
||||
j = code >> (32 - tableNbBits);
|
||||
nb = 1 << (tableNbBits - n);
|
||||
inc = 1;
|
||||
if (flags & INIT_VLC_LE) {
|
||||
j = bitswap32(code);
|
||||
inc = 1 << n;
|
||||
}
|
||||
for (k = 0; k < nb; k++) {
|
||||
int bits = table[j][1];
|
||||
//warning("%4x: code=%d n=%d", j, i, n);
|
||||
|
||||
if (bits != 0 && bits != n) {
|
||||
warning("incorrect codes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
table[j][1] = n; //bits
|
||||
table[j][0] = symbol;
|
||||
j += inc;
|
||||
}
|
||||
} else {
|
||||
// fill auxiliary table recursively
|
||||
n -= tableNbBits;
|
||||
codePrefix = code >> (32 - tableNbBits);
|
||||
subtableBits = n;
|
||||
codes[i].bits = n;
|
||||
codes[i].code = code << tableNbBits;
|
||||
for (k = i + 1; k < nbCodes; k++) {
|
||||
n = codes[k].bits - tableNbBits;
|
||||
if (n <= 0)
|
||||
break;
|
||||
code = codes[k].code;
|
||||
if (code >> (32 - tableNbBits) != (uint)codePrefix)
|
||||
break;
|
||||
codes[k].bits = n;
|
||||
codes[k].code = code << tableNbBits;
|
||||
subtableBits = MAX(subtableBits, n);
|
||||
}
|
||||
subtableBits = MIN(subtableBits, tableNbBits);
|
||||
j = (flags & INIT_VLC_LE) ? bitswap32(codePrefix) >> (32 - tableNbBits) : codePrefix;
|
||||
table[j][1] = -subtableBits;
|
||||
//warning("%4x: n=%d (subtable)", j, codes[i].bits + tableNbBits);
|
||||
index = vlc->buildTable(subtableBits, k - i, codes + i, flags);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
// note: realloc has been done, so reload tables
|
||||
table = (VLC_TYPE (*)[2])&vlc->_table[tableIndex];
|
||||
table[j][0] = index; //code
|
||||
i = k - 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tableSize; i++) {
|
||||
if (table[i][1] == 0) //bits
|
||||
table[i][0] = -1; //codes
|
||||
}
|
||||
|
||||
return tableIndex;
|
||||
}
|
||||
|
||||
int VLC::allocTable(int size, int useStatic) {
|
||||
VLC *vlc = this;
|
||||
int index = vlc->_tableSize;
|
||||
|
||||
vlc->_tableSize += size;
|
||||
if (vlc->_tableSize > vlc->_tableAllocated) {
|
||||
// cannot do anything, init_vlc() is used with too little memory
|
||||
assert(!useStatic);
|
||||
|
||||
vlc->_tableAllocated += (1 << vlc->_bits);
|
||||
vlc->_table = (int16(*)[2])avReallocF(vlc->_table, vlc->_tableAllocated, sizeof(VLC_TYPE) * 2);
|
||||
if (!vlc->_table) {
|
||||
vlc->_tableAllocated = 0;
|
||||
vlc->_tableSize = 0;
|
||||
return -2;
|
||||
}
|
||||
|
||||
memset(vlc->_table + vlc->_tableAllocated - (static_cast<ptrdiff_t>(1) << vlc->_bits), 0, sizeof(VLC_TYPE) * 2 << vlc->_bits);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
uint VLC::getData(const void *table, uint idx, uint wrap, uint size) {
|
||||
const uint8 *ptr = (const uint8 *)table + idx * wrap;
|
||||
|
||||
switch(size) {
|
||||
case 1:
|
||||
return *(const uint8 *)ptr;
|
||||
|
||||
case 2:
|
||||
return *(const uint16 *)ptr;
|
||||
|
||||
default:
|
||||
return *(const uint32 *)ptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
134
image/codecs/indeo/vlc.h
Normal file
134
image/codecs/indeo/vlc.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_VLC_H
|
||||
#define IMAGE_CODECS_INDEO_VLC_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
#define VLC_TYPE int16
|
||||
|
||||
enum VLCFlag {
|
||||
INIT_VLC_LE = 2,
|
||||
INIT_VLC_USE_NEW_STATIC = 4
|
||||
};
|
||||
|
||||
struct VLCcode {
|
||||
uint8 bits;
|
||||
uint16 symbol;
|
||||
|
||||
/**
|
||||
* codeword, with the first bit-to-be-read in the msb
|
||||
* (even if intended for a little-endian bitstream reader)
|
||||
*/
|
||||
uint32 code;
|
||||
};
|
||||
|
||||
struct VLC {
|
||||
private:
|
||||
static int compareVlcSpec(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* Gets a value of a given size from a table
|
||||
* @param table Table to get data from
|
||||
* @param idx Index of value to retrieve
|
||||
* @param wrap Size of elements with alignment
|
||||
* @param size Size of elements
|
||||
*/
|
||||
static uint getData(const void *table, uint idx, uint wrap, uint size);
|
||||
public:
|
||||
int _bits;
|
||||
VLC_TYPE (*_table)[2]; ///< code, bits
|
||||
int _tableSize, _tableAllocated;
|
||||
|
||||
VLC();
|
||||
|
||||
/* Build VLC decoding tables suitable for use with get_vlc().
|
||||
|
||||
'nbBits' sets the decoding table size (2^nbBits) entries. The
|
||||
bigger it is, the faster is the decoding. But it should not be too
|
||||
big to save memory and L1 cache. '9' is a good compromise.
|
||||
|
||||
'nbCodes' : number of vlcs codes
|
||||
|
||||
'bits' : table which gives the size (in bits) of each vlc code.
|
||||
|
||||
'codes' : table which gives the bit pattern of of each vlc code.
|
||||
|
||||
'symbols' : table which gives the values to be returned from get_vlc().
|
||||
|
||||
'xxx_wrap' : give the number of bytes between each entry of the
|
||||
'bits' or 'codes' tables.
|
||||
|
||||
'xxx_size' : gives the number of bytes of each entry of the 'bits'
|
||||
or 'codes' tables.
|
||||
|
||||
'wrap' and 'size' make it possible to use any memory configuration and types
|
||||
(byte/word/long) to store the 'bits', 'codes', and 'symbols' tables.
|
||||
|
||||
'useStatic' should be set to 1 for tables, which should be freed
|
||||
with av_free_static(), 0 if freeVlc() will be used.
|
||||
*/
|
||||
int init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap,
|
||||
int bitsSize, const void *codes, int codesWrap, int codesSize,
|
||||
const void *symbols, int symbolsWrap, int symbolsSize, int flags);
|
||||
|
||||
int init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, int bitsSize,
|
||||
const void *codes, int codesWrap, int codesSize, int flags);
|
||||
|
||||
/**
|
||||
* Free VLC data
|
||||
*/
|
||||
void freeVlc();
|
||||
|
||||
|
||||
/**
|
||||
* Build VLC decoding tables suitable for use with get_vlc().
|
||||
*
|
||||
* @param tableNbBits max length of vlc codes to store directly in this table
|
||||
* (Longer codes are delegated to subtables.)
|
||||
*
|
||||
* @param nbCodes number of elements in codes[]
|
||||
*
|
||||
* @param codes descriptions of the vlc codes
|
||||
* These must be ordered such that codes going into the same subtable are contiguous.
|
||||
* Sorting by VLCcode.code is sufficient, though not necessary.
|
||||
*/
|
||||
int buildTable(int tableNbBits, int nbCodes, VLCcode *codes, int flags);
|
||||
|
||||
int allocTable(int size, int useStatic);
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
3515
image/codecs/indeo3.cpp
Normal file
3515
image/codecs/indeo3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
102
image/codecs/indeo3.h
Normal file
102
image/codecs/indeo3.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
/* Intel Indeo 3 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note:
|
||||
* Intel Indeo 3 (IV31, IV32, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO3_H
|
||||
#define IMAGE_CODECS_INDEO3_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Intel Indeo 3 decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*
|
||||
* Used in video:
|
||||
* - VMDDecoder
|
||||
*/
|
||||
class Indeo3Decoder : public Codec {
|
||||
public:
|
||||
Indeo3Decoder(uint16 width, uint16 height, uint bitsPerPixel = 24);
|
||||
~Indeo3Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isIndeo3(Common::SeekableReadStream &stream);
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
|
||||
static const byte _corrector_type_0[24];
|
||||
static const byte _corrector_type_2[8];
|
||||
static const uint32 correction[];
|
||||
static const uint32 correctionloworder[];
|
||||
static const uint32 correctionhighorder[];
|
||||
|
||||
struct YUVBufs {
|
||||
byte *Ybuf;
|
||||
byte *Ubuf;
|
||||
byte *Vbuf;
|
||||
byte *the_buf;
|
||||
uint32 the_buf_size;
|
||||
uint16 y_w, y_h;
|
||||
uint16 uv_w, uv_h;
|
||||
};
|
||||
|
||||
YUVBufs _iv_frame[2];
|
||||
YUVBufs *_cur_frame;
|
||||
YUVBufs *_ref_frame;
|
||||
|
||||
byte *_ModPred;
|
||||
byte *_corrector_type;
|
||||
|
||||
void buildModPred();
|
||||
void allocFrames();
|
||||
|
||||
void decodeChunk(byte *cur, byte *ref, int width, int height,
|
||||
const byte *buf1, uint32 fflags2, const byte *hdr,
|
||||
const byte *buf2, int min_width_160);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1177
image/codecs/indeo4.cpp
Normal file
1177
image/codecs/indeo4.cpp
Normal file
File diff suppressed because it is too large
Load Diff
149
image/codecs/indeo4.h
Normal file
149
image/codecs/indeo4.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Intel Indeo 4 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note:
|
||||
* Intel Indeo 4 (IV31, IV32, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO4_H
|
||||
#define IMAGE_CODECS_INDEO4_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
#include "image/codecs/indeo/indeo.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
using namespace Indeo;
|
||||
|
||||
/**
|
||||
* Intel Indeo 4 decoder.
|
||||
*
|
||||
* Used by AVI.
|
||||
*
|
||||
* Used in video:
|
||||
* - AVIDecoder
|
||||
*/
|
||||
class Indeo4Decoder : public IndeoDecoderBase {
|
||||
struct Transform {
|
||||
InvTransformPtr *_invTrans;
|
||||
DCTransformPtr *_dcTrans;
|
||||
bool _is2dTrans;
|
||||
};
|
||||
public:
|
||||
Indeo4Decoder(uint16 width, uint16 height, uint bitsPerPixel = 16);
|
||||
~Indeo4Decoder() override {}
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
static bool isIndeo4(Common::SeekableReadStream &stream);
|
||||
protected:
|
||||
/**
|
||||
* Decode the Indeo 4 picture header.
|
||||
* @returns 0 = Ok, negative number = error
|
||||
*/
|
||||
int decodePictureHeader() override;
|
||||
|
||||
/**
|
||||
* Rearrange decoding and reference buffers.
|
||||
*/
|
||||
void switchBuffers() override;
|
||||
|
||||
bool isNonNullFrame() const override;
|
||||
|
||||
/**
|
||||
* Decode Indeo 4 band header.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @returns result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeBandHeader(IVIBandDesc *band) override;
|
||||
|
||||
/**
|
||||
* Decode information (block type, cbp, quant delta, motion vector)
|
||||
* for all macroblocks in the current tile.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @param[in,out] tile pointer to the tile descriptor
|
||||
* @returns result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeMbInfo(IVIBandDesc *band, IVITile *tile) override;
|
||||
|
||||
/**
|
||||
* Decodes huffman + RLE-coded transparency data within Indeo4 frames
|
||||
*/
|
||||
int decodeRLETransparency(VLC_TYPE (*table)[2]);
|
||||
|
||||
/**
|
||||
* Decodes optional transparency data within Indeo4 frames
|
||||
*/
|
||||
int decodeTransparency() override;
|
||||
private:
|
||||
int scaleTileSize(int defSize, int sizeFactor);
|
||||
|
||||
/**
|
||||
* Decode subdivision of a plane.
|
||||
* This is a simplified version that checks for two supported subdivisions:
|
||||
* - 1 wavelet band per plane, size factor 1:1, code pattern: 3
|
||||
* - 4 wavelet bands per plane, size factor 1:4, code pattern: 2,3,3,3,3
|
||||
* Anything else is either unsupported or corrupt.
|
||||
*
|
||||
* @param[in,out] gb The GetBit context
|
||||
* @returns Number of wavelet bands or 0 on error
|
||||
*/
|
||||
int decodePlaneSubdivision();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Standard picture dimensions
|
||||
*/
|
||||
static const uint _ivi4_common_pic_sizes[14];
|
||||
|
||||
/**
|
||||
* Transformations list
|
||||
*/
|
||||
static const Transform _transforms[18];
|
||||
|
||||
static const uint8 *const _scan_index_to_tab[15];
|
||||
|
||||
/**
|
||||
* Indeo 4 dequant tables
|
||||
*/
|
||||
static const uint16 _ivi4_quant_8x8_intra[9][64];
|
||||
|
||||
static const uint16 _ivi4_quant_8x8_inter[9][64];
|
||||
|
||||
static const uint16 _ivi4_quant_4x4_intra[5][16];
|
||||
|
||||
static const uint16 _ivi4_quant_4x4_inter[5][16];
|
||||
|
||||
/**
|
||||
* Table for mapping quant matrix index from the bitstream
|
||||
* into internal quant table number.
|
||||
*/
|
||||
static const uint8 _quant_index_to_tab[22];
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
752
image/codecs/indeo5.cpp
Normal file
752
image/codecs/indeo5.cpp
Normal file
@@ -0,0 +1,752 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Intel Indeo 5 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 5 (IV51, IV52, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
#include "image/codecs/indeo5.h"
|
||||
#include "image/codecs/indeo/indeo_dsp.h"
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Indeo5 frame types.
|
||||
*/
|
||||
enum {
|
||||
FRAMETYPE_INTRA = 0,
|
||||
FRAMETYPE_INTER = 1, ///< non-droppable P-frame
|
||||
FRAMETYPE_INTER_SCAL = 2, ///< droppable P-frame used in the scalability mode
|
||||
FRAMETYPE_INTER_NOREF = 3, ///< droppable P-frame
|
||||
FRAMETYPE_NULL = 4 ///< empty frame with no data
|
||||
};
|
||||
|
||||
#define IVI5_PIC_SIZE_ESC 15
|
||||
|
||||
Indeo5Decoder::Indeo5Decoder(uint16 width, uint16 height, uint bitsPerPixel) :
|
||||
IndeoDecoderBase(width, height, bitsPerPixel) {
|
||||
_ctx._isIndeo4 = false;
|
||||
_ctx._refBuf = 1;
|
||||
_ctx._bRefBuf = 3;
|
||||
_ctx._pFrame = new AVFrame();
|
||||
}
|
||||
|
||||
bool Indeo5Decoder::isIndeo5(Common::SeekableReadStream &stream) {
|
||||
// Less than 16 bytes? This can't be right
|
||||
if (stream.size() < 16)
|
||||
return false;
|
||||
|
||||
// Read in the start of the data
|
||||
byte buffer[16];
|
||||
stream.read(buffer, 16);
|
||||
stream.seek(-16, SEEK_CUR);
|
||||
|
||||
// Validate the first 5-bit word has the correct identifier
|
||||
Indeo::GetBits gb(buffer, 16 * 8);
|
||||
bool isIndeo5 = gb.getBits<5>() == 0x1F;
|
||||
|
||||
return isIndeo5;
|
||||
}
|
||||
|
||||
const Graphics::Surface *Indeo5Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
// Not Indeo 5? Fail
|
||||
if (!isIndeo5(stream))
|
||||
return nullptr;
|
||||
|
||||
// Set up the frame data buffer
|
||||
byte *frameData = new byte[stream.size()];
|
||||
stream.read(frameData, stream.size());
|
||||
_ctx._frameData = frameData;
|
||||
_ctx._frameSize = stream.size();
|
||||
|
||||
// Set up the GetBits instance for reading the data
|
||||
_ctx._gb = new GetBits(_ctx._frameData, _ctx._frameSize);
|
||||
|
||||
// Decode the frame
|
||||
int err = decodeIndeoFrame();
|
||||
|
||||
// Free the bit reader and frame buffer
|
||||
delete _ctx._gb;
|
||||
_ctx._gb = nullptr;
|
||||
delete[] frameData;
|
||||
_ctx._frameData = nullptr;
|
||||
_ctx._frameSize = 0;
|
||||
|
||||
return (err < 0) ? nullptr : _surface;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decodePictureHeader() {
|
||||
IVIPicConfig picConf;
|
||||
int ret;
|
||||
|
||||
if (_ctx._gb->getBits<5>() != 0x1F) {
|
||||
warning("Invalid picture start code!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
_ctx._prevFrameType = _ctx._frameType;
|
||||
_ctx._frameType = _ctx._gb->getBits<3>();
|
||||
if (_ctx._frameType >= 5) {
|
||||
warning("Invalid frame type: %d", _ctx._frameType);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_ctx._frameNum = _ctx._gb->getBits<8>();
|
||||
|
||||
if (_ctx._frameType == FRAMETYPE_INTRA) {
|
||||
if ((ret = decode_gop_header()) < 0) {
|
||||
warning("Invalid GOP header, skipping frames.");
|
||||
_ctx._gopInvalid = true;
|
||||
return ret;
|
||||
}
|
||||
_ctx._gopInvalid = false;
|
||||
}
|
||||
|
||||
if (_ctx._frameType == FRAMETYPE_INTER_SCAL && !_ctx._isScalable) {
|
||||
warning("Scalable inter frame in non scalable stream");
|
||||
_ctx._frameType = FRAMETYPE_INTER;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_ctx._frameType != FRAMETYPE_NULL) {
|
||||
_ctx._frameFlags = _ctx._gb->getBits<8>();
|
||||
|
||||
_ctx._picHdrSize = (_ctx._frameFlags & 1) ? _ctx._gb->getBits<24>() : 0;
|
||||
|
||||
_ctx._checksum = (_ctx._frameFlags & 0x10) ? _ctx._gb->getBits<16>() : 0;
|
||||
|
||||
// skip unknown extension if any
|
||||
if (_ctx._frameFlags & 0x20)
|
||||
skip_hdr_extension(); // XXX: untested
|
||||
|
||||
// decode macroblock huffman codebook
|
||||
ret = _ctx._mbVlc.decodeHuffDesc(&_ctx, _ctx._frameFlags & 0x40,
|
||||
IVI_MB_HUFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
_ctx._gb->skip(3); // FIXME: unknown meaning!
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Indeo5Decoder::switchBuffers() {
|
||||
switch (_ctx._prevFrameType) {
|
||||
case FRAMETYPE_INTRA:
|
||||
case FRAMETYPE_INTER:
|
||||
_ctx._bufSwitch ^= 1;
|
||||
_ctx._dstBuf = _ctx._bufSwitch;
|
||||
_ctx._refBuf = _ctx._bufSwitch ^ 1;
|
||||
break;
|
||||
|
||||
case FRAMETYPE_INTER_SCAL:
|
||||
if (!_ctx._interScal) {
|
||||
_ctx._ref2Buf = 2;
|
||||
_ctx._interScal = 1;
|
||||
}
|
||||
SWAP(_ctx._dstBuf, _ctx._ref2Buf);
|
||||
_ctx._refBuf = _ctx._ref2Buf;
|
||||
break;
|
||||
|
||||
case FRAMETYPE_INTER_NOREF:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_ctx._frameType) {
|
||||
case FRAMETYPE_INTRA:
|
||||
_ctx._bufSwitch = 0;
|
||||
// FALLTHROUGH
|
||||
case FRAMETYPE_INTER:
|
||||
_ctx._interScal = 0;
|
||||
_ctx._dstBuf = _ctx._bufSwitch;
|
||||
_ctx._refBuf = _ctx._bufSwitch ^ 1;
|
||||
break;
|
||||
|
||||
case FRAMETYPE_INTER_SCAL:
|
||||
case FRAMETYPE_INTER_NOREF:
|
||||
case FRAMETYPE_NULL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Indeo5Decoder::isNonNullFrame() const {
|
||||
return _ctx._frameType != FRAMETYPE_NULL;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decodeBandHeader(IVIBandDesc *band) {
|
||||
int i, ret;
|
||||
uint8 bandFlags;
|
||||
|
||||
bandFlags = _ctx._gb->getBits<8>();
|
||||
|
||||
if (bandFlags & 1) {
|
||||
band->_isEmpty = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
band->_dataSize = (_ctx._frameFlags & 0x80) ? _ctx._gb->getBits<24>() : 0;
|
||||
|
||||
band->_inheritMv = (bandFlags & 2) != 0;
|
||||
band->_inheritQDelta = (bandFlags & 8) != 0;
|
||||
band->_qdeltaPresent = (bandFlags & 4) != 0;
|
||||
if (!band->_qdeltaPresent)
|
||||
band->_inheritQDelta = 1;
|
||||
|
||||
// decode rvmap probability corrections if any
|
||||
band->_numCorr = 0; // there are no corrections
|
||||
if (bandFlags & 0x10) {
|
||||
band->_numCorr = _ctx._gb->getBits<8>(); // get number of correction pairs
|
||||
if (band->_numCorr > 61) {
|
||||
warning("Too many corrections: %d", band->_numCorr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read correction pairs
|
||||
for (i = 0; i < band->_numCorr * 2; i++)
|
||||
band->_corr[i] = _ctx._gb->getBits<8>();
|
||||
}
|
||||
|
||||
// select appropriate rvmap table for this band
|
||||
band->_rvmapSel = (bandFlags & 0x40) ? _ctx._gb->getBits<3>() : 8;
|
||||
|
||||
// decode block huffman codebook
|
||||
ret = band->_blkVlc.decodeHuffDesc(&_ctx, bandFlags & 0x80, IVI_BLK_HUFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
band->_checksumPresent = _ctx._gb->getBit();
|
||||
if (band->_checksumPresent)
|
||||
band->_checksum = _ctx._gb->getBits<16>();
|
||||
|
||||
band->_globQuant = _ctx._gb->getBits<5>();
|
||||
|
||||
// skip unknown extension if any
|
||||
if (bandFlags & 0x20) { // XXX: untested
|
||||
_ctx._gb->align();
|
||||
skip_hdr_extension();
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decodeMbInfo(IVIBandDesc *band, IVITile *tile) {
|
||||
int x, y, mvX, mvY, mvDelta, offs, mbOffset, mvScale, s;
|
||||
IVIMbInfo *mb, *refMb;
|
||||
int rowOffset = band->_mbSize * band->_pitch;
|
||||
|
||||
mb = tile->_mbs;
|
||||
refMb = tile->_refMbs;
|
||||
offs = tile->_yPos * band->_pitch + tile->_xPos;
|
||||
|
||||
if (!refMb &&
|
||||
((band->_qdeltaPresent && band->_inheritQDelta) || band->_inheritMv))
|
||||
return -1;
|
||||
|
||||
if (tile->_numMBs != IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize)) {
|
||||
warning("Allocated tile size %d mismatches parameters %d",
|
||||
tile->_numMBs, IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// scale factor for motion vectors
|
||||
mvScale = (_ctx._planes[0]._bands[0]._mbSize >> 3) - (band->_mbSize >> 3);
|
||||
mvX = mvY = 0;
|
||||
|
||||
for (y = tile->_yPos; y < (tile->_yPos + tile->_height); y += band->_mbSize) {
|
||||
mbOffset = offs;
|
||||
|
||||
for (x = tile->_xPos; x < (tile->_xPos + tile->_width); x += band->_mbSize) {
|
||||
mb->_xPos = x;
|
||||
mb->_yPos = y;
|
||||
mb->_bufOffs = mbOffset;
|
||||
|
||||
if (_ctx._gb->getBit()) {
|
||||
if (_ctx._frameType == FRAMETYPE_INTRA) {
|
||||
warning("Empty macroblock in an INTRA picture!");
|
||||
return -1;
|
||||
}
|
||||
mb->_type = 1; // empty macroblocks are always INTER
|
||||
mb->_cbp = 0; // all blocks are empty
|
||||
|
||||
mb->_qDelta = 0;
|
||||
if (!band->_plane && !band->_bandNum && (_ctx._frameFlags & 8)) {
|
||||
mb->_qDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mb->_qDelta = IVI_TOSIGNED(mb->_qDelta);
|
||||
}
|
||||
|
||||
mb->_mvX = mb->_mvY = 0; // no motion vector coded
|
||||
if (band->_inheritMv && refMb) {
|
||||
// motion vector inheritance
|
||||
if (mvScale) {
|
||||
mb->_mvX = scaleMV(refMb->_mvX, mvScale);
|
||||
mb->_mvY = scaleMV(refMb->_mvY, mvScale);
|
||||
} else {
|
||||
mb->_mvX = refMb->_mvX;
|
||||
mb->_mvY = refMb->_mvY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (band->_inheritMv && refMb) {
|
||||
mb->_type = refMb->_type; // copy mb_type from corresponding reference mb
|
||||
} else if (_ctx._frameType == FRAMETYPE_INTRA) {
|
||||
mb->_type = 0; // mb_type is always INTRA for intra-frames
|
||||
} else {
|
||||
mb->_type = _ctx._gb->getBit();
|
||||
}
|
||||
|
||||
if (band->_mbSize != band->_blkSize) {
|
||||
mb->_cbp = _ctx._gb->getBits<4>();
|
||||
} else {
|
||||
mb->_cbp = _ctx._gb->getBit();
|
||||
}
|
||||
|
||||
mb->_qDelta = 0;
|
||||
if (band->_qdeltaPresent) {
|
||||
if (band->_inheritQDelta) {
|
||||
if (refMb) mb->_qDelta = refMb->_qDelta;
|
||||
} else if (mb->_cbp || (!band->_plane && !band->_bandNum &&
|
||||
(_ctx._frameFlags & 8))) {
|
||||
mb->_qDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mb->_qDelta = IVI_TOSIGNED(mb->_qDelta);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mb->_type) {
|
||||
mb->_mvX = mb->_mvY = 0; // there is no motion vector in intra-macroblocks
|
||||
} else {
|
||||
if (band->_inheritMv && refMb) {
|
||||
// motion vector inheritance
|
||||
if (mvScale) {
|
||||
mb->_mvX = scaleMV(refMb->_mvX, mvScale);
|
||||
mb->_mvY = scaleMV(refMb->_mvY, mvScale);
|
||||
} else {
|
||||
mb->_mvX = refMb->_mvX;
|
||||
mb->_mvY = refMb->_mvY;
|
||||
}
|
||||
} else {
|
||||
// decode motion vector deltas
|
||||
mvDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mvY += IVI_TOSIGNED(mvDelta);
|
||||
mvDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mvX += IVI_TOSIGNED(mvDelta);
|
||||
mb->_mvX = mvX;
|
||||
mb->_mvY = mvY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = band->_isHalfpel;
|
||||
if (mb->_type)
|
||||
if (x + (mb->_mvX >> s) + (y + (mb->_mvY >> s)) * band->_pitch < 0 ||
|
||||
x + ((mb->_mvX + s) >> s) + band->_mbSize - 1
|
||||
+ (y + band->_mbSize - 1 + ((mb->_mvY + s) >> s)) * band->_pitch > band->_bufSize - 1) {
|
||||
warning("motion vector %d %d outside reference", x*s + mb->_mvX, y * s + mb->_mvY);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mb++;
|
||||
if (refMb)
|
||||
refMb++;
|
||||
mbOffset += band->_mbSize;
|
||||
}
|
||||
|
||||
offs += rowOffset;
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decode_gop_header() {
|
||||
int result, i, p, tileSize, picSizeIndx, mbSize, blkSize, isScalable;
|
||||
int quantMat;
|
||||
bool blkSizeChanged = false;
|
||||
IVIBandDesc *band, *band1, *band2;
|
||||
IVIPicConfig picConf;
|
||||
|
||||
_ctx._gopFlags = _ctx._gb->getBits<8>();
|
||||
|
||||
_ctx._gopHdrSize = (_ctx._gopFlags & 1) ? _ctx._gb->getBits<16>() : 0;
|
||||
|
||||
if (_ctx._gopFlags & IVI5_IS_PROTECTED)
|
||||
_ctx._lockWord = _ctx._gb->getBits<32>();
|
||||
|
||||
tileSize = (_ctx._gopFlags & 0x40) ? 64 << _ctx._gb->getBits<2>() : 0;
|
||||
if (tileSize > 256) {
|
||||
warning("Invalid tile size: %d", tileSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// decode number of wavelet bands
|
||||
// num_levels * 3 + 1
|
||||
picConf._lumaBands = _ctx._gb->getBits<2>() * 3 + 1;
|
||||
picConf._chromaBands = _ctx._gb->getBit() * 3 + 1;
|
||||
isScalable = picConf._lumaBands != 1 || picConf._chromaBands != 1;
|
||||
if (isScalable && (picConf._lumaBands != 4 || picConf._chromaBands != 1)) {
|
||||
warning("Scalability: unsupported subdivision! Luma bands: %d, chroma bands: %d",
|
||||
picConf._lumaBands, picConf._chromaBands);
|
||||
return -1;
|
||||
}
|
||||
|
||||
picSizeIndx = _ctx._gb->getBits<4>();
|
||||
if (picSizeIndx == IVI5_PIC_SIZE_ESC) {
|
||||
picConf._picHeight = _ctx._gb->getBits<13>();
|
||||
picConf._picWidth = _ctx._gb->getBits<13>();
|
||||
} else {
|
||||
picConf._picHeight = _commonPicSizes[picSizeIndx * 2 + 1] << 2;
|
||||
picConf._picWidth = _commonPicSizes[picSizeIndx * 2] << 2;
|
||||
}
|
||||
|
||||
if (_ctx._gopFlags & 2) {
|
||||
warning("YV12 picture format");
|
||||
return -2;
|
||||
}
|
||||
|
||||
picConf._chromaHeight = (picConf._picHeight + 3) >> 2;
|
||||
picConf._chromaWidth = (picConf._picWidth + 3) >> 2;
|
||||
|
||||
if (!tileSize) {
|
||||
picConf._tileHeight = picConf._picHeight;
|
||||
picConf._tileWidth = picConf._picWidth;
|
||||
} else {
|
||||
picConf._tileHeight = picConf._tileWidth = tileSize;
|
||||
}
|
||||
|
||||
// check if picture layout was changed and reallocate buffers
|
||||
if (picConf.ivi_pic_config_cmp(_ctx._picConf) || _ctx._gopInvalid) {
|
||||
result = IVIPlaneDesc::initPlanes(_ctx._planes, &picConf, 0);
|
||||
if (result < 0) {
|
||||
warning("Couldn't reallocate color planes!");
|
||||
return result;
|
||||
}
|
||||
_ctx._picConf = picConf;
|
||||
_ctx._isScalable = isScalable;
|
||||
blkSizeChanged = 1; // force reallocation of the internal structures
|
||||
}
|
||||
|
||||
for (p = 0; p <= 1; p++) {
|
||||
for (i = 0; i < (!p ? picConf._lumaBands : picConf._chromaBands); i++) {
|
||||
band = &_ctx._planes[p]._bands[i];
|
||||
|
||||
band->_isHalfpel = _ctx._gb->getBit();
|
||||
|
||||
mbSize = _ctx._gb->getBit();
|
||||
blkSize = 8 >> _ctx._gb->getBit();
|
||||
mbSize = blkSize << (!mbSize ? 1 : 0);
|
||||
|
||||
if (p == 0 && blkSize == 4) {
|
||||
warning("4x4 luma blocks are unsupported!");
|
||||
return -2;
|
||||
}
|
||||
|
||||
blkSizeChanged = mbSize != band->_mbSize || blkSize != band->_blkSize;
|
||||
if (blkSizeChanged) {
|
||||
band->_mbSize = mbSize;
|
||||
band->_blkSize = blkSize;
|
||||
}
|
||||
|
||||
if (_ctx._gb->getBit()) {
|
||||
warning("Extended transform info");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// select transform function and scan pattern according to plane and band number
|
||||
switch ((p << 2) + i) {
|
||||
case 0:
|
||||
band->_invTransform = IndeoDSP::ffIviInverseSlant8x8;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcSlant2d;
|
||||
band->_scan = ffZigZagDirect;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
band->_invTransform = IndeoDSP::ffIviRowSlant8;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcRowSlant;
|
||||
band->_scan = _ffIviVerticalScan8x8;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
band->_invTransform = IndeoDSP::ffIviColSlant8;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcColSlant;
|
||||
band->_scan = _ffIviHorizontalScan8x8;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
band->_invTransform = IndeoDSP::ffIviPutPixels8x8;
|
||||
band->_dcTransform = IndeoDSP::ffIviPutDcPixel8x8;
|
||||
band->_scan = _ffIviHorizontalScan8x8;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
band->_invTransform = IndeoDSP::ffIviInverseSlant4x4;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcSlant2d;
|
||||
band->_scan = _ffIviDirectScan4x4;
|
||||
band->_transformSize = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
band->_is2dTrans = band->_invTransform == IndeoDSP::ffIviInverseSlant8x8 ||
|
||||
band->_invTransform == IndeoDSP::ffIviInverseSlant4x4;
|
||||
|
||||
if (band->_transformSize != band->_blkSize) {
|
||||
warning("transform and block size mismatch (%d != %d)", band->_transformSize, band->_blkSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// select dequant matrix according to plane and band number
|
||||
if (!p) {
|
||||
quantMat = (picConf._lumaBands > 1) ? i + 1 : 0;
|
||||
} else {
|
||||
quantMat = 5;
|
||||
}
|
||||
|
||||
if (band->_blkSize == 8) {
|
||||
if (quantMat >= 5) {
|
||||
warning("_quantMat %d too large!", quantMat);
|
||||
return -1;
|
||||
}
|
||||
band->_intraBase = &_baseQuant8x8Intra[quantMat][0];
|
||||
band->_interBase = &_baseQuant8x8Inter[quantMat][0];
|
||||
band->_intraScale = &_scaleQuant8x8Intra[quantMat][0];
|
||||
band->_interScale = &_scaleQuant8x8Inter[quantMat][0];
|
||||
} else {
|
||||
band->_intraBase = _baseQuant4x4Intra;
|
||||
band->_interBase = _baseQuant4x4Inter;
|
||||
band->_intraScale = _scaleQuant4x4Intra;
|
||||
band->_interScale = _scaleQuant4x4Inter;
|
||||
}
|
||||
|
||||
if (_ctx._gb->getBits<2>()) {
|
||||
warning("End marker missing!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy chroma parameters into the 2nd chroma plane
|
||||
for (i = 0; i < picConf._chromaBands; i++) {
|
||||
band1 = &_ctx._planes[1]._bands[i];
|
||||
band2 = &_ctx._planes[2]._bands[i];
|
||||
|
||||
band2->_width = band1->_width;
|
||||
band2->_height = band1->_height;
|
||||
band2->_mbSize = band1->_mbSize;
|
||||
band2->_blkSize = band1->_blkSize;
|
||||
band2->_isHalfpel = band1->_isHalfpel;
|
||||
band2->_intraBase = band1->_intraBase;
|
||||
band2->_interBase = band1->_interBase;
|
||||
band2->_intraScale = band1->_intraScale;
|
||||
band2->_interScale = band1->_interScale;
|
||||
band2->_scan = band1->_scan;
|
||||
band2->_invTransform = band1->_invTransform;
|
||||
band2->_dcTransform = band1->_dcTransform;
|
||||
band2->_is2dTrans = band1->_is2dTrans;
|
||||
band2->_transformSize = band1->_transformSize;
|
||||
}
|
||||
|
||||
// reallocate internal structures if needed
|
||||
if (blkSizeChanged) {
|
||||
result = IVIPlaneDesc::initTiles(_ctx._planes, picConf._tileWidth,
|
||||
picConf._tileHeight);
|
||||
if (result < 0) {
|
||||
warning("Couldn't reallocate internal structures!");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (_ctx._gopFlags & 8) {
|
||||
if (_ctx._gb->getBits<3>()) {
|
||||
warning("Alignment bits are not zero!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_ctx._gb->getBit())
|
||||
_ctx._gb->skip(24); // skip transparency fill color
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
_ctx._gb->skip(23); // FIXME: unknown meaning
|
||||
|
||||
// skip GOP extension if any
|
||||
if (_ctx._gb->getBit()) {
|
||||
do {
|
||||
i = _ctx._gb->getBits<16>();
|
||||
} while (i & 0x8000);
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::skip_hdr_extension() {
|
||||
int i, len;
|
||||
|
||||
do {
|
||||
len = _ctx._gb->getBits<8>();
|
||||
if (_ctx._gb->eos())
|
||||
return -1;
|
||||
for (i = 0; i < len; i++)
|
||||
_ctx._gb->skip(8);
|
||||
} while (len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
const uint8 Indeo5Decoder::_commonPicSizes[30] = {
|
||||
160, 120, 80, 60, 40, 30, 176, 120, 88, 60, 88, 72, 44, 36, 60, 45, 160, 60,
|
||||
176, 60, 20, 15, 22, 18, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant8x8Inter[5][64] = {
|
||||
{0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
|
||||
0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
|
||||
0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
|
||||
0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
|
||||
},
|
||||
{0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
|
||||
0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
|
||||
0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
|
||||
0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
|
||||
},
|
||||
{0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
},
|
||||
{0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
|
||||
0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
},
|
||||
{0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
}
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant8x8Intra[5][64] = {
|
||||
{0x1a, 0x2e, 0x36, 0x42, 0x46, 0x4a, 0x4e, 0x5a, 0x2e, 0x32, 0x3e, 0x42, 0x46, 0x4e, 0x56, 0x6a,
|
||||
0x36, 0x3e, 0x3e, 0x44, 0x4a, 0x54, 0x66, 0x72, 0x42, 0x42, 0x44, 0x4a, 0x52, 0x62, 0x6c, 0x7a,
|
||||
0x46, 0x46, 0x4a, 0x52, 0x5e, 0x66, 0x72, 0x8e, 0x4a, 0x4e, 0x54, 0x62, 0x66, 0x6e, 0x86, 0xa6,
|
||||
0x4e, 0x56, 0x66, 0x6c, 0x72, 0x86, 0x9a, 0xca, 0x5a, 0x6a, 0x72, 0x7a, 0x8e, 0xa6, 0xca, 0xfe,
|
||||
},
|
||||
{0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
|
||||
0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
|
||||
0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
|
||||
0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
|
||||
},
|
||||
{0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
},
|
||||
{0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
|
||||
0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
},
|
||||
{0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
}
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant4x4Inter[16] = {
|
||||
0x1e, 0x3e, 0x4a, 0x52, 0x3e, 0x4a, 0x52, 0x56, 0x4a, 0x52, 0x56, 0x5e, 0x52, 0x56, 0x5e, 0x66
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant4x4Intra[16] = {
|
||||
0x1e, 0x3e, 0x4a, 0x52, 0x3e, 0x4a, 0x52, 0x5e, 0x4a, 0x52, 0x5e, 0x7a, 0x52, 0x5e, 0x7a, 0x92
|
||||
};
|
||||
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant8x8Inter[5][24] = {
|
||||
{0x0b, 0x11, 0x13, 0x14, 0x15, 0x16, 0x18, 0x1a, 0x1b, 0x1d, 0x20, 0x22,
|
||||
0x23, 0x25, 0x28, 0x2a, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x41, 0x44, 0x4a,
|
||||
},
|
||||
{0x07, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35,
|
||||
0x3a, 0x3f, 0x44, 0x4a, 0x50, 0x56, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x7e,
|
||||
},
|
||||
{0x15, 0x25, 0x28, 0x2d, 0x30, 0x34, 0x3a, 0x3d, 0x42, 0x48, 0x4c, 0x51,
|
||||
0x56, 0x5b, 0x60, 0x65, 0x6b, 0x70, 0x76, 0x7c, 0x82, 0x88, 0x8f, 0x97,
|
||||
},
|
||||
{0x13, 0x1f, 0x20, 0x22, 0x25, 0x28, 0x2b, 0x2d, 0x30, 0x33, 0x36, 0x39,
|
||||
0x3c, 0x3f, 0x42, 0x45, 0x48, 0x4b, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x62,
|
||||
},
|
||||
{0x3c, 0x52, 0x58, 0x5d, 0x63, 0x68, 0x68, 0x6d, 0x73, 0x78, 0x7c, 0x80,
|
||||
0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa3, 0xa9, 0xad, 0xb1, 0xb5, 0xba,
|
||||
},
|
||||
};
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant8x8Intra[5][24] = {
|
||||
{0x0b, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20,
|
||||
0x22, 0x24, 0x27, 0x28, 0x2a, 0x2d, 0x2f, 0x31, 0x34, 0x37, 0x39, 0x3c,
|
||||
},
|
||||
{0x01, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x28, 0x2c,
|
||||
0x30, 0x34, 0x38, 0x3d, 0x42, 0x47, 0x4c, 0x52, 0x58, 0x5e, 0x65, 0x6c,
|
||||
},
|
||||
{0x13, 0x22, 0x27, 0x2a, 0x2d, 0x33, 0x36, 0x3c, 0x41, 0x45, 0x49, 0x4e,
|
||||
0x53, 0x58, 0x5d, 0x63, 0x69, 0x6f, 0x75, 0x7c, 0x82, 0x88, 0x8e, 0x95,
|
||||
},
|
||||
{0x13, 0x1f, 0x21, 0x24, 0x27, 0x29, 0x2d, 0x2f, 0x34, 0x37, 0x3a, 0x3d,
|
||||
0x40, 0x44, 0x48, 0x4c, 0x4f, 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6b,
|
||||
},
|
||||
{0x31, 0x42, 0x47, 0x47, 0x4d, 0x52, 0x58, 0x58, 0x5d, 0x63, 0x67, 0x6b,
|
||||
0x6f, 0x73, 0x78, 0x7c, 0x80, 0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa4,
|
||||
}
|
||||
};
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant4x4Inter[24] = {
|
||||
0x0b, 0x0d, 0x0d, 0x0e, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
};
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant4x4Intra[24] = {
|
||||
0x01, 0x0b, 0x0b, 0x0d, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x13, 0x14,
|
||||
0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
135
image/codecs/indeo5.h
Normal file
135
image/codecs/indeo5.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Intel Indeo 4 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note:
|
||||
* Intel Indeo 4 (IV31, IV32, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO5_H
|
||||
#define IMAGE_CODECS_INDEO5_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
#include "image/codecs/indeo/indeo.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
using namespace Indeo;
|
||||
|
||||
/**
|
||||
* Intel Indeo 5 decoder.
|
||||
*
|
||||
* Used by AVI.
|
||||
*
|
||||
* Used in video:
|
||||
* - AVIDecoder
|
||||
*/
|
||||
class Indeo5Decoder : public IndeoDecoderBase {
|
||||
struct Transform {
|
||||
InvTransformPtr *inv_trans;
|
||||
DCTransformPtr *dc_trans;
|
||||
int is_2d_trans;
|
||||
};
|
||||
public:
|
||||
Indeo5Decoder(uint16 width, uint16 height, uint bitsPerPixel = 16);
|
||||
~Indeo5Decoder() override {}
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
static bool isIndeo5(Common::SeekableReadStream &stream);
|
||||
protected:
|
||||
/**
|
||||
* Decode the Indeo 5 picture header.
|
||||
* @returns 0 = Ok, negative number = error
|
||||
*/
|
||||
int decodePictureHeader() override;
|
||||
|
||||
/**
|
||||
* Rearrange decoding and reference buffers.
|
||||
*/
|
||||
void switchBuffers() override;
|
||||
|
||||
bool isNonNullFrame() const override;
|
||||
|
||||
/**
|
||||
* Decode Indeo 4 band header.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @return result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeBandHeader(IVIBandDesc *band) override;
|
||||
|
||||
/**
|
||||
* Decode information (block type, cbp, quant delta, motion vector)
|
||||
* for all macroblocks in the current tile.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @param[in,out] tile pointer to the tile descriptor
|
||||
* @return result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeMbInfo(IVIBandDesc *band, IVITile *tile) override;
|
||||
private:
|
||||
/**
|
||||
* Decode Indeo5 GOP (Group of pictures) header.
|
||||
* This header is present in key frames only.
|
||||
* It defines parameters for all frames in a GOP.
|
||||
* @returns result code: 0 = OK, -1 = error
|
||||
*/
|
||||
int decode_gop_header();
|
||||
|
||||
/**
|
||||
* Skip a header extension.
|
||||
*/
|
||||
int skip_hdr_extension();
|
||||
|
||||
private:
|
||||
/**
|
||||
* standard picture dimensions (width, height divided by 4)
|
||||
*/
|
||||
static const uint8 _commonPicSizes[30];
|
||||
|
||||
/**
|
||||
* Indeo5 dequantization matrixes consist of two tables: base table
|
||||
* and scale table. The base table defines the dequantization matrix
|
||||
* itself and the scale table tells how this matrix should be scaled
|
||||
* for a particular quant level (0...24).
|
||||
*
|
||||
* ivi5_base_quant_bbb_ttt - base tables for block size 'bbb' of type 'ttt'
|
||||
* ivi5_scale_quant_bbb_ttt - scale tables for block size 'bbb' of type 'ttt'
|
||||
*/
|
||||
static const uint16 _baseQuant8x8Inter[5][64];
|
||||
static const uint16 _baseQuant8x8Intra[5][64];
|
||||
|
||||
static const uint16 _baseQuant4x4Inter[16];
|
||||
static const uint16 _baseQuant4x4Intra[16];
|
||||
|
||||
static const uint8 _scaleQuant8x8Inter[5][24];
|
||||
static const uint8 _scaleQuant8x8Intra[5][24];
|
||||
|
||||
static const uint8 _scaleQuant4x4Inter[24];
|
||||
static const uint8 _scaleQuant4x4Intra[24];
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
172
image/codecs/jyv1.cpp
Normal file
172
image/codecs/jyv1.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/jyv1.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/bitstream.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#define ID_JYV1 MKTAG('J','Y','V','1')
|
||||
#define ID_RRV1 MKTAG('R','R','V','1')
|
||||
#define ID_RRV2 MKTAG('R','R','V','2')
|
||||
|
||||
namespace Image {
|
||||
|
||||
/*static*/
|
||||
bool JYV1Decoder::isJYV1StreamTag(uint32 streamTag) {
|
||||
return (streamTag == ID_JYV1 || streamTag == ID_RRV1 || streamTag == ID_RRV2);
|
||||
}
|
||||
|
||||
JYV1Decoder::JYV1Decoder(int width, int height, uint32 streamTag) : Codec(),
|
||||
_width(width), _height(height), _streamType(streamTag) {
|
||||
assert(isJYV1StreamTag(streamTag));
|
||||
_surface.create(_width, _height, getPixelFormat());
|
||||
}
|
||||
|
||||
JYV1Decoder::~JYV1Decoder() {
|
||||
_surface.free();
|
||||
}
|
||||
|
||||
static const uint32 BASE_LEN[] = {0, 1 << 7, 1 << 3, 0, 1 << 1, 0, 1 << 5, 0,
|
||||
1, 1 << 8, 1 << 4, 0, 1 << 2, 0, 1 << 6, 0};
|
||||
static const uint32 FINE_LEN_BITS[] = {0, 7, 3, 0, 1, 16, 5, 0,
|
||||
1, 8, 4, 0, 2, 24, 6, 0};
|
||||
|
||||
/**
|
||||
* Details of this decoding algorithm are here:
|
||||
* https://wiki.multimedia.cx/index.php/Origin_Flic_Codec
|
||||
*/
|
||||
const Graphics::Surface *JYV1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
|
||||
byte *dst = (byte *)_surface.getPixels();
|
||||
|
||||
uint32 offsets[16]; // RRV2 has 15 block offsets, others have 5
|
||||
const int numOffsets = (_streamType == ID_RRV2 ? 15 : 5);
|
||||
const int blockHeight = _height / numOffsets;
|
||||
const int startOffset = stream.pos();
|
||||
|
||||
// Read in the block offsets and convert to stream offsets
|
||||
for (int i = 0; i < numOffsets; i++) {
|
||||
offsets[i] = stream.readUint32LE() + startOffset;
|
||||
}
|
||||
|
||||
bool upscale = false;
|
||||
|
||||
//
|
||||
// Slight HACK: test if we need to scale up this frame without
|
||||
// changing the output data yet. This has a bit of duplicated code
|
||||
// with the loop below just to measure the frame size from the
|
||||
// first block.
|
||||
//
|
||||
if (_streamType == ID_RRV1 || _streamType == ID_RRV2) {
|
||||
stream.seek(offsets[0], SEEK_SET);
|
||||
const int cmdLen = stream.readUint32LE();
|
||||
uint8 *cmdData = new uint8[cmdLen];
|
||||
stream.read(cmdData, cmdLen);
|
||||
Common::BitStreamMemoryStream cmdMemStream(cmdData, cmdLen);
|
||||
Common::BitStreamMemory8MSB cmdBitStream(cmdMemStream);
|
||||
int total = 0;
|
||||
while (!cmdBitStream.eos()) {
|
||||
uint32 idx = cmdBitStream.getBits<4>();
|
||||
total += BASE_LEN[idx];
|
||||
if (idx != 0 && idx != 8) {
|
||||
total += cmdBitStream.getBits(FINE_LEN_BITS[idx]);
|
||||
}
|
||||
}
|
||||
delete [] cmdData;
|
||||
|
||||
if (total == _width * blockHeight / 2)
|
||||
upscale = true;
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
int x = 0;
|
||||
|
||||
for (int i = 0; i < numOffsets && y < _height; i++) {
|
||||
stream.seek(offsets[i], SEEK_SET);
|
||||
const int cmdLen = stream.readUint32LE();
|
||||
|
||||
// TODO: can probably avoid this copy to make it faster
|
||||
uint8 *cmdData = new uint8[cmdLen];
|
||||
stream.read(cmdData, cmdLen);
|
||||
Common::BitStreamMemoryStream cmdMemStream(cmdData, cmdLen);
|
||||
Common::BitStreamMemory8MSB cmdBitStream(cmdMemStream);
|
||||
bool skipping = true;
|
||||
while (cmdBitStream.size() - cmdBitStream.pos() >= 4 && y < _height) {
|
||||
uint32 idx = cmdBitStream.getBits<4>();
|
||||
uint32 blocksize = BASE_LEN[idx];
|
||||
if (idx != 0 && idx != 8) {
|
||||
blocksize += cmdBitStream.getBits(FINE_LEN_BITS[idx]);
|
||||
}
|
||||
if (skipping) {
|
||||
// leave blocksize pixels unchanged
|
||||
if (upscale)
|
||||
blocksize *= 2;
|
||||
|
||||
while (blocksize) {
|
||||
blocksize--;
|
||||
x++;
|
||||
if (x == _width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// draw blocksize pixels from data block
|
||||
while (blocksize && y < _height) {
|
||||
// TODO: would be nicer to read these in whole scanlines.
|
||||
// Also this upscale code is kinda ugly.
|
||||
const uint8 p = stream.readByte();
|
||||
dst[y * _width + x] = p;
|
||||
x++;
|
||||
if (x == _width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
if (upscale) {
|
||||
dst[y * _width + x] = p;
|
||||
x++;
|
||||
if (x == _width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
blocksize--;
|
||||
}
|
||||
}
|
||||
skipping = !skipping;
|
||||
}
|
||||
delete [] cmdData;
|
||||
}
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat JYV1Decoder::getPixelFormat() const {
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
52
image/codecs/jyv1.h
Normal file
52
image/codecs/jyv1.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_JYV1_H
|
||||
#define IMAGE_CODECS_JYV1_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* JYV1/RRV1/RRV2 image decoder.
|
||||
*
|
||||
* Used by Crusader: No Remorse AVI files
|
||||
*/
|
||||
class JYV1Decoder : public Codec {
|
||||
public:
|
||||
JYV1Decoder (int width, int height, uint32 streamTag);
|
||||
~JYV1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
static bool isJYV1StreamTag(uint32 streamTag);
|
||||
|
||||
private:
|
||||
Graphics::Surface _surface;
|
||||
int _width, _height;
|
||||
uint32 _streamType;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
229
image/codecs/mjpeg.cpp
Normal file
229
image/codecs/mjpeg.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on LGPL MJPEG/AVI to JPEG/JFIF conversion code from libav
|
||||
// Copyright (c) 2010 Adrian Daerr and Nicolas George
|
||||
// That in turn was adapted from mjpeg2jpeg.c, with original copyright:
|
||||
// Paris 2010 Adrian Daerr, public domain
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/jpeg.h"
|
||||
|
||||
#include "image/codecs/mjpeg.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
MJPEGDecoder::MJPEGDecoder() : Codec() {
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
|
||||
_surface = 0;
|
||||
_accuracy = CodecAccuracy::Default;
|
||||
}
|
||||
|
||||
MJPEGDecoder::~MJPEGDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
// Header to be inserted
|
||||
static const byte s_jpegHeader[] = {
|
||||
0xff, 0xd8, // SOI
|
||||
0xff, 0xe0, // APP0
|
||||
0x00, 0x10, // APP0 header size (including
|
||||
// this field, but excluding preceding)
|
||||
'J', 'F', 'I', 'F', 0x00, // ID string 'JFIF\0'
|
||||
0x01, 0x01, // version
|
||||
0x00, // bits per type
|
||||
0x00, 0x00, // X density
|
||||
0x00, 0x00, // Y density
|
||||
0x00, // X thumbnail size
|
||||
0x00
|
||||
};
|
||||
|
||||
enum {
|
||||
DHT_SEGMENT_SIZE = 420
|
||||
};
|
||||
|
||||
static const byte s_dhtSegmentHead[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00 };
|
||||
static const byte s_dhtSegmentFrag[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// Set up the standard Huffman tables (cf. JPEG standard section K.3)
|
||||
// IMPORTANT: these are only valid for 8-bit data precision!
|
||||
static const byte s_mjpegBitsDCLuminance[17] = {
|
||||
/* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static const byte s_mjpegValDC[12] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
||||
};
|
||||
|
||||
#if 0
|
||||
static const byte s_mjpegBitsDCChrominance[17] = {
|
||||
/* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
|
||||
};
|
||||
#endif
|
||||
|
||||
static const byte s_mjpegBitsACLuminance[17] = {
|
||||
/* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
|
||||
};
|
||||
|
||||
static const byte s_mjpegValACLuminance[] = {
|
||||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa
|
||||
};
|
||||
|
||||
static const byte s_mjpegBitsACChrominance[17] = {
|
||||
/* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
|
||||
};
|
||||
|
||||
static const byte s_mjpegValACChrominance[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa
|
||||
};
|
||||
|
||||
const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
// We need to reconstruct an actual JPEG stream here, then feed it to the JPEG decoder
|
||||
// Yes, this is a pain.
|
||||
|
||||
stream.readUint32BE(); // Skip nonsense JPEG header
|
||||
uint16 inputSkip = stream.readUint16BE() + 4;
|
||||
uint32 tag = stream.readUint32BE();
|
||||
|
||||
if (tag != MKTAG('A', 'V', 'I', '1')) {
|
||||
warning("Invalid MJPEG tag found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 outputSize = stream.size() - inputSkip + sizeof(s_jpegHeader) + DHT_SEGMENT_SIZE;
|
||||
byte *data = (byte *)malloc(outputSize);
|
||||
|
||||
if (!data) {
|
||||
warning("Failed to allocate data for MJPEG conversion");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the header
|
||||
memcpy(data, s_jpegHeader, sizeof(s_jpegHeader));
|
||||
uint32 dataOffset = sizeof(s_jpegHeader);
|
||||
|
||||
// Write the fake DHT segment
|
||||
memcpy(data + dataOffset, s_dhtSegmentHead, sizeof(s_dhtSegmentHead));
|
||||
dataOffset += sizeof(s_dhtSegmentHead);
|
||||
memcpy(data + dataOffset, s_mjpegBitsDCLuminance + 1, 16);
|
||||
dataOffset += 16;
|
||||
memcpy(data + dataOffset, s_dhtSegmentFrag, sizeof(s_dhtSegmentFrag));
|
||||
dataOffset += sizeof(s_dhtSegmentFrag);
|
||||
memcpy(data + dataOffset, s_mjpegValDC, 12);
|
||||
dataOffset += 12;
|
||||
data[dataOffset++] = 0x10;
|
||||
memcpy(data + dataOffset, s_mjpegBitsACLuminance + 1, 16);
|
||||
dataOffset += 16;
|
||||
memcpy(data + dataOffset, s_mjpegValACLuminance, 162);
|
||||
dataOffset += 162;
|
||||
data[dataOffset++] = 0x11;
|
||||
memcpy(data + dataOffset, s_mjpegBitsACChrominance + 1, 16);
|
||||
dataOffset += 16;
|
||||
memcpy(data + dataOffset, s_mjpegValACChrominance, 162);
|
||||
dataOffset += 162;
|
||||
|
||||
// Write the actual data
|
||||
stream.seek(inputSkip);
|
||||
stream.read(data + dataOffset, stream.size() - inputSkip);
|
||||
|
||||
Common::MemoryReadStream convertedStream(data, outputSize, DisposeAfterUse::YES);
|
||||
JPEGDecoder jpeg;
|
||||
jpeg.setCodecAccuracy(_accuracy);
|
||||
jpeg.setOutputPixelFormat(_pixelFormat);
|
||||
|
||||
if (!jpeg.loadStream(convertedStream)) {
|
||||
warning("Failed to decode MJPEG frame");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->copyFrom(*jpeg.getSurface());
|
||||
|
||||
assert(_surface->format == _pixelFormat);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void MJPEGDecoder::setCodecAccuracy(CodecAccuracy accuracy) {
|
||||
_accuracy = accuracy;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
66
image/codecs/mjpeg.h
Normal file
66
image/codecs/mjpeg.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MJPEG_H
|
||||
#define IMAGE_CODECS_MJPEG_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Motion JPEG decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MJPEGDecoder : public Codec {
|
||||
public:
|
||||
MJPEGDecoder();
|
||||
~MJPEGDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
void setCodecAccuracy(CodecAccuracy accuracy) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.isCLUT8())
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
CodecAccuracy _accuracy;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
117
image/codecs/mpeg.cpp
Normal file
117
image/codecs/mpeg.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
|
||||
#include "image/codecs/mpeg.h"
|
||||
|
||||
extern "C" {
|
||||
#include <mpeg2dec/mpeg2.h>
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
MPEGDecoder::MPEGDecoder() : Codec() {
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
|
||||
_surface = 0;
|
||||
|
||||
_mpegDecoder = mpeg2_init();
|
||||
|
||||
if (!_mpegDecoder)
|
||||
error("Could not initialize libmpeg2");
|
||||
|
||||
_mpegInfo = mpeg2_info(_mpegDecoder);
|
||||
}
|
||||
|
||||
MPEGDecoder::~MPEGDecoder() {
|
||||
mpeg2_close(_mpegDecoder);
|
||||
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *MPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
uint32 framePeriod;
|
||||
decodePacket(stream, framePeriod);
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool MPEGDecoder::decodePacket(Common::SeekableReadStream &packet, uint32 &framePeriod, Graphics::Surface *dst) {
|
||||
// Decode as much as we can out of this packet
|
||||
uint32 size = 0xFFFFFFFF;
|
||||
mpeg2_state_t state;
|
||||
bool foundFrame = false;
|
||||
framePeriod = 0;
|
||||
|
||||
do {
|
||||
state = mpeg2_parse(_mpegDecoder);
|
||||
|
||||
switch (state) {
|
||||
case STATE_BUFFER:
|
||||
size = packet.read(_buffer, BUFFER_SIZE);
|
||||
mpeg2_buffer(_mpegDecoder, _buffer, _buffer + size);
|
||||
break;
|
||||
case STATE_SLICE:
|
||||
case STATE_END:
|
||||
if (_mpegInfo->display_fbuf) {
|
||||
foundFrame = true;
|
||||
const mpeg2_sequence_t *sequence = _mpegInfo->sequence;
|
||||
const mpeg2_picture_t *picture = _mpegInfo->display_picture;
|
||||
|
||||
framePeriod += sequence->frame_period;
|
||||
if (picture->nb_fields > 2) {
|
||||
framePeriod += (sequence->frame_period / 2);
|
||||
|
||||
}
|
||||
|
||||
if (!dst) {
|
||||
// If no destination is specified, use our internal storage
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(sequence->picture_width, sequence->picture_height, _pixelFormat);
|
||||
}
|
||||
|
||||
dst = _surface;
|
||||
}
|
||||
|
||||
YUVToRGBMan.convert420(dst, Graphics::YUVToRGBManager::kScaleITU, _mpegInfo->display_fbuf->buf[0],
|
||||
_mpegInfo->display_fbuf->buf[1], _mpegInfo->display_fbuf->buf[2], sequence->picture_width,
|
||||
sequence->picture_height, sequence->width, sequence->chroma_width);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (size != 0);
|
||||
|
||||
return foundFrame;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
79
image/codecs/mpeg.h
Normal file
79
image/codecs/mpeg.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MPEG_H
|
||||
#define IMAGE_CODECS_MPEG_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
typedef struct mpeg2dec_s mpeg2dec_t;
|
||||
typedef struct mpeg2_info_s mpeg2_info_t;
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* MPEG 1/2 video decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MPEGDecoder : public Codec {
|
||||
public:
|
||||
MPEGDecoder();
|
||||
~MPEGDecoder() override;
|
||||
|
||||
// Codec interface
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
// MPEGPSDecoder call
|
||||
bool decodePacket(Common::SeekableReadStream &packet, uint32 &framePeriod, Graphics::Surface *dst = 0);
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
enum {
|
||||
BUFFER_SIZE = 4096
|
||||
};
|
||||
|
||||
byte _buffer[BUFFER_SIZE];
|
||||
mpeg2dec_t *_mpegDecoder;
|
||||
const mpeg2_info_t *_mpegInfo;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif // IMAGE_CODECS_MPEG_H
|
||||
138
image/codecs/msrle.cpp
Normal file
138
image/codecs/msrle.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's msrledec.c
|
||||
|
||||
#include "image/codecs/msrle.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
MSRLEDecoder::MSRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
}
|
||||
|
||||
MSRLEDecoder::~MSRLEDecoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
const Graphics::Surface *MSRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (_bitsPerPixel == 8) {
|
||||
decode8(stream);
|
||||
} else
|
||||
error("Unhandled %d bit Microsoft RLE encoding", _bitsPerPixel);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void MSRLEDecoder::decode8(Common::SeekableReadStream &stream) {
|
||||
|
||||
int x = 0;
|
||||
int y = _surface->h - 1;
|
||||
|
||||
byte *data = (byte *) _surface->getPixels();
|
||||
uint16 width = _surface->w;
|
||||
uint16 height = _surface->h;
|
||||
uint16 pitch = _surface->pitch;
|
||||
|
||||
byte *output = data + ((height - 1) * pitch);
|
||||
byte *output_end = data + ((height) * pitch) - (pitch - width);
|
||||
|
||||
while (!stream.eos()) {
|
||||
byte count = stream.readByte();
|
||||
byte value = stream.readByte();
|
||||
|
||||
if (count == 0) {
|
||||
if (value == 0) {
|
||||
// End of line
|
||||
|
||||
x = 0;
|
||||
y--;
|
||||
output = data + (y * pitch);
|
||||
|
||||
} else if (value == 1) {
|
||||
// End of image
|
||||
return;
|
||||
|
||||
} else if (value == 2) {
|
||||
// Skip
|
||||
|
||||
count = stream.readByte();
|
||||
value = stream.readByte();
|
||||
|
||||
y -= value;
|
||||
x += count;
|
||||
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Skip beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
output = data + ((y * pitch) + x);
|
||||
|
||||
} else {
|
||||
// Copy data
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Copy data is beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (output + value > output_end) {
|
||||
if (stream.pos() + value >= stream.size())
|
||||
break;
|
||||
else
|
||||
stream.skip(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < value; i++)
|
||||
*output++ = stream.readByte();
|
||||
|
||||
if (value & 1)
|
||||
stream.skip(1);
|
||||
|
||||
x += value;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Run data
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Run data is beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (output + count > output_end)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < count; i++, x++)
|
||||
*output++ = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
warning("MS RLE Codec: No end-of-picture code");
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
52
image/codecs/msrle.h
Normal file
52
image/codecs/msrle.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MSRLE_H
|
||||
#define IMAGE_CODECS_MSRLE_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Microsoft Run-Length Encoding decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MSRLEDecoder : public Codec {
|
||||
public:
|
||||
MSRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~MSRLEDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void decode8(Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
139
image/codecs/msrle4.cpp
Normal file
139
image/codecs/msrle4.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's msrledec.c
|
||||
#include "common/debug.h"
|
||||
#include "image/codecs/msrle4.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
namespace Image {
|
||||
|
||||
MSRLE4Decoder::MSRLE4Decoder(uint16 width, uint16 height, byte bitsPerPixel) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
}
|
||||
|
||||
MSRLE4Decoder::~MSRLE4Decoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
const Graphics::Surface *MSRLE4Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (_bitsPerPixel == 4) {
|
||||
decode4(stream);
|
||||
} else
|
||||
error("Unhandled %d bit Microsoft RLE encoding", _bitsPerPixel);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void MSRLE4Decoder::decode4(Common::SeekableReadStream &stream) {
|
||||
int x = 0;
|
||||
int y = _surface->h - 1;
|
||||
|
||||
byte *output = (byte *)_surface->getBasePtr(x, y);
|
||||
byte *output_end = (byte *)_surface->getBasePtr(_surface->w, y);
|
||||
|
||||
while (!stream.eos()) {
|
||||
byte count = stream.readByte();
|
||||
|
||||
if (count == 0) {
|
||||
byte value = stream.readByte();
|
||||
|
||||
if (value == 0) {
|
||||
// End of line
|
||||
|
||||
x = 0;
|
||||
y--;
|
||||
output = (byte *)_surface->getBasePtr(x, y);
|
||||
} else if (value == 1) {
|
||||
// End of image
|
||||
|
||||
return;
|
||||
} else if (value == 2) {
|
||||
// Skip
|
||||
|
||||
count = stream.readByte();
|
||||
value = stream.readByte();
|
||||
|
||||
x += count;
|
||||
y -= value;
|
||||
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Skip beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
output = (byte *)_surface->getBasePtr(x, y);
|
||||
|
||||
} else {
|
||||
// Copy data
|
||||
|
||||
int odd_pixel = value & 1;
|
||||
int rle_code = (value + 1) / 2;
|
||||
int extra_byte = rle_code & 0x01;
|
||||
|
||||
if (output + value > output_end) {
|
||||
stream.skip(rle_code + extra_byte);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rle_code; i++) {
|
||||
byte color = stream.readByte();
|
||||
*output++ = (color & 0xf0) >> 4;
|
||||
if (i + 1 == rle_code && odd_pixel) {
|
||||
break;
|
||||
}
|
||||
*output++ = color & 0x0f;
|
||||
}
|
||||
|
||||
if (extra_byte)
|
||||
stream.skip(1);
|
||||
|
||||
x += value;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Run data
|
||||
|
||||
if (output + count > output_end)
|
||||
continue;
|
||||
|
||||
byte color = stream.readByte();
|
||||
|
||||
for (int i = 0; i < count; i++, x++) {
|
||||
*output++ = (color & 0xf0) >> 4;
|
||||
i++;
|
||||
x++;
|
||||
if (i == count)
|
||||
break;
|
||||
*output++ = color & 0x0f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
warning("MS RLE Codec: No end-of-picture code");
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
52
image/codecs/msrle4.h
Normal file
52
image/codecs/msrle4.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MSRLE4_H
|
||||
#define IMAGE_CODECS_MSRLE4_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Microsoft Run-Length Encoding decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MSRLE4Decoder : public Codec {
|
||||
public:
|
||||
MSRLE4Decoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~MSRLE4Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void decode4(Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
224
image/codecs/msvideo1.cpp
Normal file
224
image/codecs/msvideo1.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's msvideo.cpp
|
||||
|
||||
#include "image/codecs/msvideo1.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
#define CHECK_STREAM_PTR(n) \
|
||||
if ((stream.pos() + n) > stream.size() ) { \
|
||||
warning ("MS Video-1: Stream out of bounds (%d >= %d) d%d", (int)stream.pos() + n, (int)stream.size(), n); \
|
||||
return; \
|
||||
}
|
||||
|
||||
MSVideo1Decoder::MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, (bitsPerPixel == 8) ? Graphics::PixelFormat::createFormatCLUT8() :
|
||||
Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
|
||||
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
}
|
||||
|
||||
MSVideo1Decoder::~MSVideo1Decoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
void MSVideo1Decoder::decode8(Common::SeekableReadStream &stream) {
|
||||
byte colors[8];
|
||||
byte *pixels = (byte *)_surface->getPixels();
|
||||
uint16 stride = _surface->w;
|
||||
|
||||
int skipBlocks = 0;
|
||||
uint16 blocks_wide = _surface->w / 4;
|
||||
uint16 blocks_high = _surface->h / 4;
|
||||
uint32 totalBlocks = blocks_wide * blocks_high;
|
||||
uint32 blockInc = 4;
|
||||
uint16 rowDec = stride + 4;
|
||||
|
||||
for (uint16 block_y = blocks_high; block_y > 0; block_y--) {
|
||||
uint32 blockPtr = (block_y * 4 - 1) * stride;
|
||||
for (uint16 block_x = blocks_wide; block_x > 0; block_x--) {
|
||||
// check if this block should be skipped
|
||||
if (skipBlocks > 0) {
|
||||
blockPtr += blockInc;
|
||||
skipBlocks--;
|
||||
totalBlocks--;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 pixelPtr = blockPtr;
|
||||
|
||||
/* get the next two bytes in the encoded data stream */
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte byte_a = stream.readByte();
|
||||
byte byte_b = stream.readByte();
|
||||
|
||||
/* check if the decode is finished */
|
||||
if (byte_a == 0 && byte_b == 0 && totalBlocks == 0) {
|
||||
return;
|
||||
} else if ((byte_b & 0xFC) == 0x84) {
|
||||
// skip code, but don't count the current block
|
||||
skipBlocks = ((byte_b - 0x84) << 8) + byte_a - 1;
|
||||
} else if (byte_b < 0x80) {
|
||||
// 2-color encoding
|
||||
uint16 flags = (byte_b << 8) | byte_a;
|
||||
|
||||
CHECK_STREAM_PTR(2);
|
||||
colors[0] = stream.readByte();
|
||||
colors[1] = stream.readByte();
|
||||
|
||||
for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (byte pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixelPtr++] = colors[(flags & 0x1) ^ 1];
|
||||
pixelPtr -= rowDec;
|
||||
}
|
||||
} else if (byte_b >= 0x90) {
|
||||
// 8-color encoding
|
||||
uint16 flags = (byte_b << 8) | byte_a;
|
||||
|
||||
CHECK_STREAM_PTR(8);
|
||||
for (byte i = 0; i < 8; i++)
|
||||
colors[i] = stream.readByte();
|
||||
|
||||
for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (byte pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixelPtr++] = colors[((pixel_y & 0x2) << 1) + (pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
|
||||
pixelPtr -= rowDec;
|
||||
}
|
||||
} else {
|
||||
// 1-color encoding
|
||||
colors[0] = byte_a;
|
||||
|
||||
for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (byte pixel_x = 0; pixel_x < 4; pixel_x++)
|
||||
pixels[pixelPtr++] = colors[0];
|
||||
pixelPtr -= rowDec;
|
||||
}
|
||||
}
|
||||
|
||||
blockPtr += blockInc;
|
||||
totalBlocks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MSVideo1Decoder::decode16(Common::SeekableReadStream &stream) {
|
||||
/* decoding parameters */
|
||||
uint16 colors[8];
|
||||
uint16 *pixels = (uint16 *)_surface->getPixels();
|
||||
int32 stride = _surface->w;
|
||||
|
||||
int32 skip_blocks = 0;
|
||||
int32 blocks_wide = _surface->w / 4;
|
||||
int32 blocks_high = _surface->h / 4;
|
||||
int32 total_blocks = blocks_wide * blocks_high;
|
||||
int32 block_inc = 4;
|
||||
int32 row_dec = stride + 4;
|
||||
|
||||
for (int32 block_y = blocks_high; block_y > 0; block_y--) {
|
||||
int32 block_ptr = ((block_y * 4) - 1) * stride;
|
||||
for (int32 block_x = blocks_wide; block_x > 0; block_x--) {
|
||||
/* check if this block should be skipped */
|
||||
if (skip_blocks) {
|
||||
block_ptr += block_inc;
|
||||
skip_blocks--;
|
||||
total_blocks--;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 pixel_ptr = block_ptr;
|
||||
|
||||
/* get the next two bytes in the encoded data stream */
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte byte_a = stream.readByte();
|
||||
byte byte_b = stream.readByte();
|
||||
|
||||
/* check if the decode is finished */
|
||||
if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0)) {
|
||||
return;
|
||||
} else if ((byte_b & 0xFC) == 0x84) {
|
||||
/* skip code, but don't count the current block */
|
||||
skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
|
||||
} else if (byte_b < 0x80) {
|
||||
/* 2- or 8-color encoding modes */
|
||||
uint16 flags = (byte_b << 8) | byte_a;
|
||||
|
||||
CHECK_STREAM_PTR(4);
|
||||
colors[0] = stream.readUint16LE();
|
||||
colors[1] = stream.readUint16LE();
|
||||
|
||||
if (colors[0] & 0x8000) {
|
||||
/* 8-color encoding */
|
||||
CHECK_STREAM_PTR(12);
|
||||
colors[2] = stream.readUint16LE();
|
||||
colors[3] = stream.readUint16LE();
|
||||
colors[4] = stream.readUint16LE();
|
||||
colors[5] = stream.readUint16LE();
|
||||
colors[6] = stream.readUint16LE();
|
||||
colors[7] = stream.readUint16LE();
|
||||
|
||||
for (int pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (int pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixel_ptr++] =
|
||||
colors[((pixel_y & 0x2) << 1) +
|
||||
(pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
|
||||
pixel_ptr -= row_dec;
|
||||
}
|
||||
} else {
|
||||
/* 2-color encoding */
|
||||
for (int pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (int pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1];
|
||||
pixel_ptr -= row_dec;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* otherwise, it's a 1-color block */
|
||||
colors[0] = (byte_b << 8) | byte_a;
|
||||
|
||||
for (int pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (int pixel_x = 0; pixel_x < 4; pixel_x++)
|
||||
pixels[pixel_ptr++] = colors[0];
|
||||
pixel_ptr -= row_dec;
|
||||
}
|
||||
}
|
||||
|
||||
block_ptr += block_inc;
|
||||
total_blocks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *MSVideo1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (_bitsPerPixel == 8)
|
||||
decode8(stream);
|
||||
else
|
||||
decode16(stream);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
53
image/codecs/msvideo1.h
Normal file
53
image/codecs/msvideo1.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MSVIDEO1_H
|
||||
#define IMAGE_CODECS_MSVIDEO1_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Microsoft Video 1 decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MSVideo1Decoder : public Codec {
|
||||
public:
|
||||
MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~MSVideo1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _surface->format; }
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void decode8(Common::SeekableReadStream &stream);
|
||||
void decode16(Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
679
image/codecs/qtrle.cpp
Normal file
679
image/codecs/qtrle.cpp
Normal file
@@ -0,0 +1,679 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// QuickTime RLE Decoder
|
||||
// Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson)
|
||||
|
||||
#include "image/codecs/qtrle.h"
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec(), _ditherPalette(0) {
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_surface = 0;
|
||||
_dirtyPalette = false;
|
||||
_colorMap = 0;
|
||||
|
||||
// We need to ensure the width is a multiple of 4
|
||||
_paddedWidth = width;
|
||||
uint16 wMod = width % 4;
|
||||
if (wMod != 0)
|
||||
_paddedWidth += 4 - wMod;
|
||||
}
|
||||
|
||||
QTRLEDecoder::~QTRLEDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _colorMap;
|
||||
}
|
||||
|
||||
#define CHECK_STREAM_PTR(n) \
|
||||
do { \
|
||||
if ((stream.pos() + n) > stream.size()) { \
|
||||
warning("QTRLE Problem: stream out of bounds (%d > %d)", (int)stream.pos() + n, (int)stream.size()); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_PIXEL_PTR(n) \
|
||||
do { \
|
||||
if ((int32)pixelPtr + n > (int)_paddedWidth * _surface->h) { \
|
||||
warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _paddedWidth * _surface->h); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *rgb = (byte *)_surface->getPixels();
|
||||
|
||||
while (linesToChange) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte skip = stream.readByte();
|
||||
int rleCode = stream.readSByte();
|
||||
|
||||
if (rleCode == 0)
|
||||
break;
|
||||
|
||||
if (skip & 0x80) {
|
||||
linesToChange--;
|
||||
rowPtr += _paddedWidth;
|
||||
pixelPtr = rowPtr + 2 * (skip & 0x7f);
|
||||
} else
|
||||
pixelPtr += 2 * skip;
|
||||
|
||||
if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
// get the next 2 bytes from the stream, treat them as groups of 8 pixels, and output them rleCode times */
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte pi0 = stream.readByte();
|
||||
byte pi1 = stream.readByte();
|
||||
CHECK_PIXEL_PTR(rleCode * 2);
|
||||
|
||||
while (rleCode--) {
|
||||
rgb[pixelPtr++] = pi0;
|
||||
rgb[pixelPtr++] = pi1;
|
||||
}
|
||||
} else {
|
||||
// copy the same pixel directly to output 2 times
|
||||
rleCode *= 2;
|
||||
CHECK_STREAM_PTR(rleCode);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = stream.readByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *rgb = (byte *)_surface->getPixels();
|
||||
byte numPixels = (bpp == 4) ? 8 : 16;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + (numPixels * (stream.readByte() - 1));
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += (numPixels * (stream.readByte() - 1));
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
// get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times */
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
byte pi[16]; // 16 palette indices
|
||||
|
||||
for (int8 i = numPixels - 1; i >= 0; i--) {
|
||||
pi[numPixels - 1 - i] = (stream.readByte() >> ((i * bpp) & 0x07)) & ((1 << bpp) - 1);
|
||||
|
||||
if ((i & ((numPixels >> 2) - 1)) == 0)
|
||||
stream.readByte();
|
||||
}
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode * numPixels);
|
||||
|
||||
while (rleCode--)
|
||||
for (byte i = 0; i < numPixels; i++)
|
||||
rgb[pixelPtr++] = pi[i];
|
||||
} else {
|
||||
// copy the same pixel directly to output 4 times
|
||||
rleCode *= 4;
|
||||
CHECK_STREAM_PTR(rleCode);
|
||||
CHECK_PIXEL_PTR(rleCode * (numPixels >> 2));
|
||||
|
||||
while (rleCode--) {
|
||||
byte temp = stream.readByte();
|
||||
if (bpp == 4) {
|
||||
rgb[pixelPtr++] = (temp >> 4) & 0x0f;
|
||||
rgb[pixelPtr++] = temp & 0x0f;
|
||||
} else {
|
||||
rgb[pixelPtr++] = (temp >> 6) & 0x03;
|
||||
rgb[pixelPtr++] = (temp >> 4) & 0x03;
|
||||
rgb[pixelPtr++] = (temp >> 2) & 0x03;
|
||||
rgb[pixelPtr++] = temp & 0x03;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *rgb = (byte *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + 4 * (stream.readByte() - 1);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += 4 * (stream.readByte() - 1);
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
// get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
byte pi[4]; // 4 palette indexes
|
||||
|
||||
for (byte i = 0; i < 4; i++)
|
||||
pi[i] = stream.readByte();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode * 4);
|
||||
|
||||
while (rleCode--)
|
||||
for (byte i = 0; i < 4; i++)
|
||||
rgb[pixelPtr++] = pi[i];
|
||||
} else {
|
||||
// copy the same pixel directly to output 4 times
|
||||
rleCode *= 4;
|
||||
CHECK_STREAM_PTR(rleCode);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = stream.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
uint16 *rgb = (uint16 *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + stream.readByte() - 1;
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
uint16 rgb16 = stream.readUint16BE();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = rgb16;
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 2);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = stream.readUint16BE();
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline uint16 readDitherColor16(Common::ReadStream &stream) {
|
||||
return stream.readUint16BE() >> 1;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
void QTRLEDecoder::dither16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *output = (byte *)_surface->getPixels();
|
||||
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
// clone2727 thinks this should be startLine & 3, but the original definitely
|
||||
// isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
|
||||
// purpose of the compression then.
|
||||
byte curColorTableOffset = 0;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
byte rowOffset = stream.readByte() - 1;
|
||||
pixelPtr = rowPtr + rowOffset;
|
||||
uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
uint16 color = readDitherColor16(stream);
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 2);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--) {
|
||||
uint16 color = readDitherColor16(stream);
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
curColorTableOffset = (curColorTableOffset + 1) & 3;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
uint8 *rgb = (uint8 *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + stream.readByte() - 1;
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
CHECK_STREAM_PTR(3);
|
||||
|
||||
byte r = stream.readByte();
|
||||
byte g = stream.readByte();
|
||||
byte b = stream.readByte();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
rgb[(pixelPtr * 3) + 0] = r;
|
||||
rgb[(pixelPtr * 3) + 1] = g;
|
||||
rgb[(pixelPtr * 3) + 2] = b;
|
||||
pixelPtr++;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 3);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
stream.read(&rgb[pixelPtr * 3], rleCode * 3);
|
||||
pixelPtr += rleCode;
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline uint16 readDitherColor24(Common::ReadStream &stream) {
|
||||
uint8 rgb[3];
|
||||
stream.read(rgb, 3);
|
||||
|
||||
uint16 color = (rgb[0] & 0xF8) << 6;
|
||||
color |= (rgb[1] & 0xF8) << 1;
|
||||
color |= rgb[2] >> 4;
|
||||
return color;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
void QTRLEDecoder::dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *output = (byte *)_surface->getPixels();
|
||||
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
// clone2727 thinks this should be startLine & 3, but the original definitely
|
||||
// isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
|
||||
// purpose of the compression then.
|
||||
byte curColorTableOffset = 0;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
byte rowOffset = stream.readByte() - 1;
|
||||
pixelPtr = rowPtr + rowOffset;
|
||||
uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(3);
|
||||
|
||||
uint16 color = readDitherColor24(stream);
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 3);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--) {
|
||||
uint16 color = readDitherColor24(stream);
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
curColorTableOffset = (curColorTableOffset + 1) & 3;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
uint8 *rgb = (uint8 *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + stream.readByte() - 1;
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
byte a = stream.readByte();
|
||||
byte r = stream.readByte();
|
||||
byte g = stream.readByte();
|
||||
byte b = stream.readByte();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
rgb[(pixelPtr * 4) + 0] = a;
|
||||
rgb[(pixelPtr * 4) + 1] = r;
|
||||
rgb[(pixelPtr * 4) + 2] = g;
|
||||
rgb[(pixelPtr * 4) + 3] = b;
|
||||
pixelPtr++;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 4);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
stream.read(&rgb[pixelPtr * 4], rleCode * 4);
|
||||
pixelPtr += rleCode;
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline uint16 readDitherColor32(Common::ReadStream &stream) {
|
||||
uint8 argb[4];
|
||||
stream.read(argb, 4);
|
||||
|
||||
uint16 color = (argb[1] & 0xF8) << 6;
|
||||
color |= (argb[2] & 0xF8) << 1;
|
||||
color |= argb[3] >> 4;
|
||||
return color;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
void QTRLEDecoder::dither32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *output = (byte *)_surface->getPixels();
|
||||
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
// clone2727 thinks this should be startLine & 3, but the original definitely
|
||||
// isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
|
||||
// purpose of the compression then.
|
||||
byte curColorTableOffset = 0;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
byte rowOffset = stream.readByte() - 1;
|
||||
pixelPtr = rowPtr + rowOffset;
|
||||
uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
uint16 color = readDitherColor32(stream);
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 4);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--) {
|
||||
uint16 color = readDitherColor32(stream);
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
curColorTableOffset = (curColorTableOffset + 1) & 3;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (!_surface)
|
||||
createSurface();
|
||||
|
||||
uint16 startLine = 0;
|
||||
uint16 height = _height;
|
||||
|
||||
// check if this frame is even supposed to change
|
||||
if (stream.size() < 8)
|
||||
return _surface;
|
||||
|
||||
// start after the chunk size
|
||||
stream.readUint32BE();
|
||||
|
||||
// fetch the header
|
||||
uint16 header = stream.readUint16BE();
|
||||
|
||||
// if a header is present, fetch additional decoding parameters
|
||||
if (header & 8) {
|
||||
if (stream.size() < 14)
|
||||
return _surface;
|
||||
|
||||
startLine = stream.readUint16BE();
|
||||
stream.readUint16BE(); // Unknown
|
||||
height = stream.readUint16BE();
|
||||
stream.readUint16BE(); // Unknown
|
||||
}
|
||||
|
||||
uint32 rowPtr = _paddedWidth * startLine;
|
||||
|
||||
switch (_bitsPerPixel) {
|
||||
case 1:
|
||||
case 33:
|
||||
decode1(stream, rowPtr, height);
|
||||
break;
|
||||
case 2:
|
||||
case 34:
|
||||
decode2_4(stream, rowPtr, height, 2);
|
||||
break;
|
||||
case 4:
|
||||
case 36:
|
||||
decode2_4(stream, rowPtr, height, 4);
|
||||
break;
|
||||
case 8:
|
||||
case 40:
|
||||
decode8(stream, rowPtr, height);
|
||||
break;
|
||||
case 16:
|
||||
if (_ditherPalette.size() > 0)
|
||||
dither16(stream, rowPtr, height);
|
||||
else
|
||||
decode16(stream, rowPtr, height);
|
||||
break;
|
||||
case 24:
|
||||
if (_ditherPalette.size() > 0)
|
||||
dither24(stream, rowPtr, height);
|
||||
else
|
||||
decode24(stream, rowPtr, height);
|
||||
break;
|
||||
case 32:
|
||||
if (_ditherPalette.size() > 0)
|
||||
dither32(stream, rowPtr, height);
|
||||
else
|
||||
decode32(stream, rowPtr, height);
|
||||
break;
|
||||
default:
|
||||
error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
|
||||
if (_ditherPalette.size() > 0)
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
|
||||
switch (_bitsPerPixel) {
|
||||
case 1:
|
||||
case 33:
|
||||
case 2:
|
||||
case 34:
|
||||
case 4:
|
||||
case 36:
|
||||
case 8:
|
||||
case 40:
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
case 16:
|
||||
return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
|
||||
case 24:
|
||||
return Graphics::PixelFormat::createFormatRGB24();
|
||||
case 32:
|
||||
return Graphics::PixelFormat::createFormatARGB32();
|
||||
default:
|
||||
error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
|
||||
}
|
||||
|
||||
return Graphics::PixelFormat();
|
||||
}
|
||||
|
||||
bool QTRLEDecoder::canDither(DitherType type) const {
|
||||
return type == kDitherTypeQT && (_bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32);
|
||||
}
|
||||
|
||||
void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
|
||||
assert(canDither(type));
|
||||
|
||||
_ditherPalette.resize(256, false);
|
||||
_ditherPalette.set(palette, 0, 256);
|
||||
_dirtyPalette = true;
|
||||
|
||||
delete[] _colorMap;
|
||||
_colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
|
||||
}
|
||||
|
||||
void QTRLEDecoder::createSurface() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_paddedWidth, _height, getPixelFormat());
|
||||
_surface->w = _width;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
74
image/codecs/qtrle.h
Normal file
74
image/codecs/qtrle.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_QTRLE_H
|
||||
#define IMAGE_CODECS_QTRLE_H
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* QuickTime Run-Length Encoding decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class QTRLEDecoder : public Codec {
|
||||
public:
|
||||
QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~QTRLEDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
bool containsPalette() const override { return _ditherPalette != 0; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _ditherPalette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
Graphics::Surface *_surface;
|
||||
uint16 _width, _height;
|
||||
uint32 _paddedWidth;
|
||||
Graphics::Palette _ditherPalette;
|
||||
bool _dirtyPalette;
|
||||
byte *_colorMap;
|
||||
|
||||
void createSurface();
|
||||
|
||||
void decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp);
|
||||
void decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void dither16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void dither32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
365
image/codecs/rpza.cpp
Normal file
365
image/codecs/rpza.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's RPZA decoder
|
||||
|
||||
#include "image/codecs/rpza.h"
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec(), _ditherPalette(0) {
|
||||
_format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
|
||||
_dirtyPalette = false;
|
||||
_colorMap = 0;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_blockWidth = (width + 3) / 4;
|
||||
_blockHeight = (height + 3) / 4;
|
||||
_surface = 0;
|
||||
}
|
||||
|
||||
RPZADecoder::~RPZADecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _colorMap;
|
||||
}
|
||||
|
||||
#define ADVANCE_BLOCK() \
|
||||
blockPtr += 4; \
|
||||
if (blockPtr >= endPtr) { \
|
||||
blockPtr += pitch * 3; \
|
||||
endPtr = blockPtr + pitch; \
|
||||
} \
|
||||
totalBlocks--; \
|
||||
if (totalBlocks < 0) \
|
||||
error("rpza block counter just went negative (this should not happen)") \
|
||||
|
||||
struct BlockDecoderRaw {
|
||||
static inline void drawFillBlock(uint16 *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
}
|
||||
|
||||
static inline void drawRawBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
|
||||
blockPtr[0] = colors[0];
|
||||
blockPtr[1] = colors[1];
|
||||
blockPtr[2] = colors[2];
|
||||
blockPtr[3] = colors[3];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[4];
|
||||
blockPtr[1] = colors[5];
|
||||
blockPtr[2] = colors[6];
|
||||
blockPtr[3] = colors[7];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[8];
|
||||
blockPtr[1] = colors[9];
|
||||
blockPtr[2] = colors[10];
|
||||
blockPtr[3] = colors[11];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[12];
|
||||
blockPtr[1] = colors[13];
|
||||
blockPtr[2] = colors[14];
|
||||
blockPtr[3] = colors[15];
|
||||
}
|
||||
|
||||
static inline void drawBlendBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
|
||||
blockPtr[0] = colors[(indexes[0] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[0] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[0] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[0] >> 0) & 0x03];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[(indexes[1] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[1] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[1] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[1] >> 0) & 0x03];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[(indexes[2] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[2] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[2] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[2] >> 0) & 0x03];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[(indexes[3] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[3] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[3] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[3] >> 0) & 0x03];
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockDecoderDither {
|
||||
static inline void drawFillBlock(byte *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
|
||||
const byte *mapOffset = colorMap + (color >> 1);
|
||||
byte pixel1 = mapOffset[0x0000];
|
||||
byte pixel2 = mapOffset[0x4000];
|
||||
byte pixel3 = mapOffset[0x8000];
|
||||
byte pixel4 = mapOffset[0xC000];
|
||||
|
||||
blockPtr[0] = pixel1;
|
||||
blockPtr[1] = pixel2;
|
||||
blockPtr[2] = pixel3;
|
||||
blockPtr[3] = pixel4;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = pixel4;
|
||||
blockPtr[1] = pixel1;
|
||||
blockPtr[2] = pixel2;
|
||||
blockPtr[3] = pixel3;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = pixel2;
|
||||
blockPtr[1] = pixel3;
|
||||
blockPtr[2] = pixel4;
|
||||
blockPtr[3] = pixel1;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = pixel3;
|
||||
blockPtr[1] = pixel4;
|
||||
blockPtr[2] = pixel1;
|
||||
blockPtr[3] = pixel2;
|
||||
}
|
||||
|
||||
static inline void drawRawBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
|
||||
blockPtr[0] = colorMap[(colors[0] >> 1) + 0x0000];
|
||||
blockPtr[1] = colorMap[(colors[1] >> 1) + 0x4000];
|
||||
blockPtr[2] = colorMap[(colors[2] >> 1) + 0x8000];
|
||||
blockPtr[3] = colorMap[(colors[3] >> 1) + 0xC000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[4] >> 1) + 0xC000];
|
||||
blockPtr[1] = colorMap[(colors[5] >> 1) + 0x0000];
|
||||
blockPtr[2] = colorMap[(colors[6] >> 1) + 0x4000];
|
||||
blockPtr[3] = colorMap[(colors[7] >> 1) + 0x8000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[8] >> 1) + 0x4000];
|
||||
blockPtr[1] = colorMap[(colors[9] >> 1) + 0x8000];
|
||||
blockPtr[2] = colorMap[(colors[10] >> 1) + 0xC000];
|
||||
blockPtr[3] = colorMap[(colors[11] >> 1) + 0x0000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[12] >> 1) + 0x8000];
|
||||
blockPtr[1] = colorMap[(colors[13] >> 1) + 0xC000];
|
||||
blockPtr[2] = colorMap[(colors[14] >> 1) + 0x0000];
|
||||
blockPtr[3] = colorMap[(colors[15] >> 1) + 0x4000];
|
||||
}
|
||||
|
||||
static inline void drawBlendBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
|
||||
blockPtr[0] = colorMap[(colors[(indexes[0] >> 6) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[0] >> 4) & 0x03] >> 1) + 0x4000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[0] >> 2) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[0] >> 0) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[(indexes[1] >> 6) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[1] >> 4) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[1] >> 2) & 0x03] >> 1) + 0x4000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[1] >> 0) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[(indexes[2] >> 6) & 0x03] >> 1) + 0x4000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[2] >> 4) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[2] >> 2) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[2] >> 0) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[(indexes[3] >> 6) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[3] >> 4) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[3] >> 2) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[3] >> 0) & 0x03] >> 1) + 0x4000];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename PixelInt, typename BlockDecoder>
|
||||
static inline void decodeFrameTmpl(Common::SeekableReadStream &stream, PixelInt *ptr, uint16 pitch, uint16 blockWidth, uint16 blockHeight, const byte *colorMap) {
|
||||
uint16 colorA = 0, colorB = 0;
|
||||
uint16 color4[4];
|
||||
|
||||
PixelInt *blockPtr = ptr;
|
||||
PixelInt *endPtr = ptr + pitch;
|
||||
uint16 ta;
|
||||
uint16 tb;
|
||||
|
||||
// First byte is always 0xe1. Warn if it's different
|
||||
byte firstByte = stream.readByte();
|
||||
if (firstByte != 0xe1)
|
||||
warning("First RPZA chunk byte is 0x%02x instead of 0xe1", firstByte);
|
||||
|
||||
// Get chunk size, ingnoring first byte
|
||||
uint32 chunkSize = stream.readUint16BE() << 8;
|
||||
chunkSize += stream.readByte();
|
||||
|
||||
// If length mismatch use size from MOV file and try to decode anyway
|
||||
if (chunkSize != (uint32)stream.size()) {
|
||||
warning("MOV chunk size != encoded chunk size; using MOV chunk size");
|
||||
chunkSize = stream.size();
|
||||
}
|
||||
|
||||
// Number of 4x4 blocks in frame
|
||||
int32 totalBlocks = blockWidth * blockHeight;
|
||||
|
||||
// Process chunk data
|
||||
while ((uint32)stream.pos() < chunkSize) {
|
||||
byte opcode = stream.readByte(); // Get opcode
|
||||
byte numBlocks = (opcode & 0x1f) + 1; // Extract block counter from opcode
|
||||
|
||||
// If opcode MSbit is 0, we need more data to decide what to do
|
||||
if ((opcode & 0x80) == 0) {
|
||||
colorA = (opcode << 8) | stream.readByte();
|
||||
opcode = 0;
|
||||
if (stream.readByte() & 0x80) {
|
||||
// Must behave as opcode 110xxxxx, using colorA computed
|
||||
// above. Use fake opcode 0x20 to enter switch block at
|
||||
// the right place
|
||||
opcode = 0x20;
|
||||
numBlocks = 1;
|
||||
}
|
||||
stream.seek(-1, SEEK_CUR);
|
||||
}
|
||||
|
||||
switch (opcode & 0xe0) {
|
||||
case 0x80: // Skip blocks
|
||||
while (numBlocks--) {
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
case 0xa0: // Fill blocks with one color
|
||||
colorA = stream.readUint16BE();
|
||||
|
||||
while (numBlocks--) {
|
||||
BlockDecoder::drawFillBlock(blockPtr, pitch, colorA, colorMap);
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// Fill blocks with 4 colors
|
||||
case 0xc0:
|
||||
colorA = stream.readUint16BE();
|
||||
// fall through
|
||||
case 0x20:
|
||||
colorB = stream.readUint16BE();
|
||||
|
||||
// Sort out the colors
|
||||
color4[0] = colorB & 0x7FFF;
|
||||
color4[1] = 0;
|
||||
color4[2] = 0;
|
||||
color4[3] = colorA & 0x7FFF;
|
||||
|
||||
// Red components
|
||||
ta = (colorA >> 10) & 0x1F;
|
||||
tb = (colorB >> 10) & 0x1F;
|
||||
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
|
||||
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
|
||||
|
||||
// Green components
|
||||
ta = (colorA >> 5) & 0x1F;
|
||||
tb = (colorB >> 5) & 0x1F;
|
||||
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
|
||||
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
|
||||
|
||||
// Blue components
|
||||
ta = colorA & 0x1F;
|
||||
tb = colorB & 0x1F;
|
||||
color4[1] |= ((11 * ta + 21 * tb) >> 5);
|
||||
color4[2] |= ((21 * ta + 11 * tb) >> 5);
|
||||
|
||||
while (numBlocks--) {
|
||||
byte indexes[4];
|
||||
stream.read(indexes, 4);
|
||||
|
||||
BlockDecoder::drawBlendBlock(blockPtr, pitch, color4, indexes, colorMap);
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// Fill block with 16 colors
|
||||
case 0x00: {
|
||||
uint16 colors[16];
|
||||
colors[0] = colorA;
|
||||
|
||||
for (int i = 0; i < 15; i++)
|
||||
colors[i + 1] = stream.readUint16BE();
|
||||
|
||||
BlockDecoder::drawRawBlock(blockPtr, pitch, colors, colorMap);
|
||||
ADVANCE_BLOCK();
|
||||
break;
|
||||
}
|
||||
|
||||
// Unknown opcode
|
||||
default:
|
||||
error("Unknown opcode %02x in rpza chunk", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
|
||||
// Allocate enough space in the surface for the blocks
|
||||
_surface->create(_blockWidth * 4, _blockHeight * 4, getPixelFormat());
|
||||
|
||||
// Adjust width/height to be the right ones
|
||||
_surface->w = _width;
|
||||
_surface->h = _height;
|
||||
}
|
||||
|
||||
if (_colorMap)
|
||||
decodeFrameTmpl<byte, BlockDecoderDither>(stream, (byte *)_surface->getPixels(), _surface->pitch, _blockWidth, _blockHeight, _colorMap);
|
||||
else
|
||||
decodeFrameTmpl<uint16, BlockDecoderRaw>(stream, (uint16 *)_surface->getPixels(), _surface->pitch / 2, _blockWidth, _blockHeight, _colorMap);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool RPZADecoder::canDither(DitherType type) const {
|
||||
return type == kDitherTypeQT;
|
||||
}
|
||||
|
||||
void RPZADecoder::setDither(DitherType type, const byte *palette) {
|
||||
assert(canDither(type));
|
||||
|
||||
_ditherPalette.resize(256, false);
|
||||
_ditherPalette.set(palette, 0, 256);
|
||||
|
||||
_dirtyPalette = true;
|
||||
_format = Graphics::PixelFormat::createFormatCLUT8();
|
||||
|
||||
delete[] _colorMap;
|
||||
_colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
62
image/codecs/rpza.h
Normal file
62
image/codecs/rpza.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_RPZA_H
|
||||
#define IMAGE_CODECS_RPZA_H
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Apple RPZA decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class RPZADecoder : public Codec {
|
||||
public:
|
||||
RPZADecoder(uint16 width, uint16 height);
|
||||
~RPZADecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _format; }
|
||||
|
||||
bool containsPalette() const override { return _ditherPalette != 0; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _ditherPalette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _format;
|
||||
Graphics::Surface *_surface;
|
||||
Graphics::Palette _ditherPalette;
|
||||
bool _dirtyPalette;
|
||||
byte *_colorMap;
|
||||
uint16 _width, _height;
|
||||
uint16 _blockWidth, _blockHeight;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
417
image/codecs/smc.cpp
Normal file
417
image/codecs/smc.cpp
Normal file
@@ -0,0 +1,417 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's SMC decoder
|
||||
|
||||
#include "image/codecs/smc.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
#define GET_BLOCK_COUNT() \
|
||||
(opcode & 0x10) ? (1 + stream.readByte()) : 1 + (opcode & 0x0F);
|
||||
|
||||
#define ADVANCE_BLOCK() \
|
||||
{ \
|
||||
pixelPtr += 4; \
|
||||
if (pixelPtr >= _surface->w) { \
|
||||
pixelPtr = 0; \
|
||||
rowPtr += _surface->w * 4; \
|
||||
} \
|
||||
totalBlocks--; \
|
||||
if (totalBlocks < 0) { \
|
||||
warning("block counter just went negative (this should not happen)"); \
|
||||
return _surface; \
|
||||
} \
|
||||
}
|
||||
|
||||
SMCDecoder::SMCDecoder(uint16 width, uint16 height) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
SMCDecoder::~SMCDecoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
const Graphics::Surface *SMCDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
byte *pixels = (byte *)_surface->getPixels();
|
||||
|
||||
uint32 numBlocks = 0;
|
||||
uint32 colorFlags = 0;
|
||||
uint32 colorFlagsA = 0;
|
||||
uint32 colorFlagsB = 0;
|
||||
|
||||
const uint16 rowInc = _surface->w - 4;
|
||||
int32 rowPtr = 0;
|
||||
int32 pixelPtr = 0;
|
||||
uint32 blockPtr = 0;
|
||||
uint32 prevBlockPtr = 0;
|
||||
uint32 prevBlockPtr1 = 0, prevBlockPtr2 = 0;
|
||||
byte prevBlockFlag = false;
|
||||
uint32 pixel = 0;
|
||||
|
||||
uint32 colorPairIndex = 0;
|
||||
uint32 colorQuadIndex = 0;
|
||||
uint32 colorOctetIndex = 0;
|
||||
uint32 colorTableIndex = 0; // indices to color pair, quad, or octet tables
|
||||
|
||||
int32 chunkSize = stream.readUint32BE() & 0x00FFFFFF;
|
||||
if (chunkSize != stream.size())
|
||||
warning("MOV chunk size != SMC chunk size (%d != %d); ignoring SMC chunk size", chunkSize, (int)stream.size());
|
||||
|
||||
int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
|
||||
|
||||
uint32 pixelSize = _surface->w * _surface->h;
|
||||
|
||||
// traverse through the blocks
|
||||
while (totalBlocks != 0) {
|
||||
// sanity checks
|
||||
|
||||
// make sure stream ptr hasn't gone out of bounds
|
||||
if (stream.pos() > stream.size()) {
|
||||
warning("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)", (int)stream.pos(), (int)stream.size());
|
||||
return _surface;
|
||||
}
|
||||
|
||||
// make sure the row pointer hasn't gone wild
|
||||
if (rowPtr >= _surface->w * _surface->h) {
|
||||
warning("SMC decoder just went out of bounds (row ptr = %d, size = %d)", rowPtr, _surface->w * _surface->h);
|
||||
return _surface;
|
||||
}
|
||||
|
||||
byte opcode = stream.readByte();
|
||||
|
||||
switch (opcode & 0xF0) {
|
||||
// skip n blocks
|
||||
case 0x00:
|
||||
case 0x10:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
while (numBlocks--) {
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// repeat last block n times
|
||||
case 0x20:
|
||||
case 0x30:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
|
||||
// sanity check
|
||||
if (rowPtr == 0 && pixelPtr == 0) {
|
||||
warning("encountered repeat block opcode (%02X) but no blocks rendered yet", opcode & 0xF0);
|
||||
break;
|
||||
}
|
||||
|
||||
// figure out where the previous block started
|
||||
if (pixelPtr == 0)
|
||||
prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4;
|
||||
else
|
||||
prevBlockPtr1 = rowPtr + pixelPtr - 4;
|
||||
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
prevBlockPtr = prevBlockPtr1;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = pixels[prevBlockPtr];
|
||||
blockPtr++, prevBlockPtr++;
|
||||
}
|
||||
blockPtr += rowInc;
|
||||
prevBlockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// repeat previous pair of blocks n times
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
numBlocks *= 2;
|
||||
|
||||
// sanity check
|
||||
if (rowPtr == 0 && pixelPtr < 2 * 4) {
|
||||
warning("encountered repeat block opcode (%02X) but not enough blocks rendered yet", opcode & 0xF0);
|
||||
break;
|
||||
}
|
||||
|
||||
// figure out where the previous 2 blocks started
|
||||
if (pixelPtr == 0)
|
||||
prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4 * 2;
|
||||
else if (pixelPtr == 4)
|
||||
prevBlockPtr1 = (rowPtr - _surface->w * 4) + rowInc;
|
||||
else
|
||||
prevBlockPtr1 = rowPtr + pixelPtr - 4 * 2;
|
||||
|
||||
if (pixelPtr == 0)
|
||||
prevBlockPtr2 = (rowPtr - _surface->w * 4) + rowInc;
|
||||
else
|
||||
prevBlockPtr2 = rowPtr + pixelPtr - 4;
|
||||
|
||||
prevBlockFlag = 0;
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
|
||||
if (prevBlockFlag)
|
||||
prevBlockPtr = prevBlockPtr2;
|
||||
else
|
||||
prevBlockPtr = prevBlockPtr1;
|
||||
|
||||
prevBlockFlag = !prevBlockFlag;
|
||||
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = pixels[prevBlockPtr];
|
||||
blockPtr++, prevBlockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
prevBlockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 1-color block encoding
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
pixel = stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = pixel;
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 2-color block encoding
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
// figure out which color pair to use to paint the 2-color block
|
||||
if ((opcode & 0xF0) == 0x80) {
|
||||
// fetch the next 2 colors from bytestream and store in next
|
||||
// available entry in the color pair table
|
||||
for (byte i = 0; i < CPAIR; i++) {
|
||||
pixel = stream.readByte();
|
||||
colorTableIndex = CPAIR * colorPairIndex + i;
|
||||
_colorPairs[colorTableIndex] = pixel;
|
||||
}
|
||||
|
||||
// this is the base index to use for this block
|
||||
colorTableIndex = CPAIR * colorPairIndex;
|
||||
colorPairIndex++;
|
||||
|
||||
// wraparound
|
||||
if (colorPairIndex == COLORS_PER_TABLE)
|
||||
colorPairIndex = 0;
|
||||
} else
|
||||
colorTableIndex = CPAIR * stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
colorFlags = stream.readUint16BE();
|
||||
uint16 flagMask = 0x8000;
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (colorFlags & flagMask)
|
||||
pixel = colorTableIndex + 1;
|
||||
else
|
||||
pixel = colorTableIndex;
|
||||
|
||||
flagMask >>= 1;
|
||||
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = _colorPairs[pixel];
|
||||
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 4-color block encoding
|
||||
case 0xA0:
|
||||
case 0xB0:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
// figure out which color quad to use to paint the 4-color block
|
||||
if ((opcode & 0xF0) == 0xA0) {
|
||||
// fetch the next 4 colors from bytestream and store in next
|
||||
// available entry in the color quad table
|
||||
for (byte i = 0; i < CQUAD; i++) {
|
||||
pixel = stream.readByte();
|
||||
colorTableIndex = CQUAD * colorQuadIndex + i;
|
||||
_colorQuads[colorTableIndex] = pixel;
|
||||
}
|
||||
|
||||
// this is the base index to use for this block
|
||||
colorTableIndex = CQUAD * colorQuadIndex;
|
||||
colorQuadIndex++;
|
||||
|
||||
// wraparound
|
||||
if (colorQuadIndex == COLORS_PER_TABLE)
|
||||
colorQuadIndex = 0;
|
||||
} else
|
||||
colorTableIndex = CQUAD * stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
colorFlags = stream.readUint32BE();
|
||||
|
||||
// flag mask actually acts as a bit shift count here
|
||||
byte flagMask = 30;
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x03);
|
||||
flagMask -= 2;
|
||||
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = _colorQuads[pixel];
|
||||
blockPtr++;
|
||||
}
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 8-color block encoding
|
||||
case 0xC0:
|
||||
case 0xD0:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
// figure out which color octet to use to paint the 8-color block
|
||||
if ((opcode & 0xF0) == 0xC0) {
|
||||
// fetch the next 8 colors from bytestream and store in next
|
||||
// available entry in the color octet table
|
||||
for (byte i = 0; i < COCTET; i++) {
|
||||
pixel = stream.readByte();
|
||||
colorTableIndex = COCTET * colorOctetIndex + i;
|
||||
_colorOctets[colorTableIndex] = pixel;
|
||||
}
|
||||
|
||||
// this is the base index to use for this block
|
||||
colorTableIndex = COCTET * colorOctetIndex;
|
||||
colorOctetIndex++;
|
||||
|
||||
// wraparound
|
||||
if (colorOctetIndex == COLORS_PER_TABLE)
|
||||
colorOctetIndex = 0;
|
||||
} else
|
||||
colorTableIndex = COCTET * stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
/*
|
||||
For this input of 6 hex bytes:
|
||||
01 23 45 67 89 AB
|
||||
Mangle it to this output:
|
||||
flags_a = xx012456, flags_b = xx89A37B
|
||||
*/
|
||||
|
||||
// build the color flags
|
||||
byte flagData[6];
|
||||
stream.read(flagData, 6);
|
||||
|
||||
colorFlagsA = ((READ_BE_UINT16(flagData) & 0xFFF0) << 8) | (READ_BE_UINT16(flagData + 2) >> 4);
|
||||
colorFlagsB = ((READ_BE_UINT16(flagData + 4) & 0xFFF0) << 8) | ((flagData[1] & 0xF) << 8) |
|
||||
((flagData[3] & 0xF) << 4) | (flagData[5] & 0xf);
|
||||
|
||||
colorFlags = colorFlagsA;
|
||||
|
||||
// flag mask actually acts as a bit shift count here
|
||||
byte flagMask = 21;
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
// reload flags at third row (iteration y == 2)
|
||||
if (y == 2) {
|
||||
colorFlags = colorFlagsB;
|
||||
flagMask = 21;
|
||||
}
|
||||
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x07);
|
||||
flagMask -= 3;
|
||||
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = _colorOctets[pixel];
|
||||
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 16-color block encoding (every pixel is a different color)
|
||||
case 0xE0:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
byte b = stream.readByte();
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = b;
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
warning("0xF0 opcode seen in SMC chunk (contact the developers)");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
60
image/codecs/smc.h
Normal file
60
image/codecs/smc.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_SMC_H
|
||||
#define IMAGE_CODECS_SMC_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
enum {
|
||||
CPAIR = 2,
|
||||
CQUAD = 4,
|
||||
COCTET = 8,
|
||||
COLORS_PER_TABLE = 256
|
||||
};
|
||||
|
||||
/**
|
||||
* Apple SMC decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class SMCDecoder : public Codec {
|
||||
public:
|
||||
SMCDecoder(uint16 width, uint16 height);
|
||||
~SMCDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
// SMC color tables
|
||||
byte _colorPairs[COLORS_PER_TABLE * CPAIR];
|
||||
byte _colorQuads[COLORS_PER_TABLE * CQUAD];
|
||||
byte _colorOctets[COLORS_PER_TABLE * COCTET];
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
804
image/codecs/svq1.cpp
Normal file
804
image/codecs/svq1.cpp
Normal file
@@ -0,0 +1,804 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Sorenson Video 1 Codec
|
||||
// Based off FFmpeg's SVQ1 decoder (written by Arpi and Nick Kurshev)
|
||||
|
||||
#include "image/codecs/svq1.h"
|
||||
#include "image/codecs/svq1_cb.h"
|
||||
#include "image/codecs/svq1_vlc.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/bitstream.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/compression/huffman.h"
|
||||
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
#define SVQ1_BLOCK_SKIP 0
|
||||
#define SVQ1_BLOCK_INTER 1
|
||||
#define SVQ1_BLOCK_INTER_4V 2
|
||||
#define SVQ1_BLOCK_INTRA 3
|
||||
|
||||
SVQ1Decoder::SVQ1Decoder(uint16 width, uint16 height) {
|
||||
debug(1, "SVQ1Decoder::SVQ1Decoder(width:%d, height:%d)", width, height);
|
||||
_width = width;
|
||||
_height = height;
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
|
||||
_frameWidth = _frameHeight = 0;
|
||||
_surface = 0;
|
||||
|
||||
_last[0] = 0;
|
||||
_last[1] = 0;
|
||||
_last[2] = 0;
|
||||
|
||||
// Setup Variable Length Code Tables
|
||||
_blockType = new HuffmanDecoder(0, 4, s_svq1BlockTypeCodes, s_svq1BlockTypeLengths);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
_intraMultistage[i] = new HuffmanDecoder(0, 8, s_svq1IntraMultistageCodes[i], s_svq1IntraMultistageLengths[i]);
|
||||
_interMultistage[i] = new HuffmanDecoder(0, 8, s_svq1InterMultistageCodes[i], s_svq1InterMultistageLengths[i]);
|
||||
}
|
||||
|
||||
_intraMean = new HuffmanDecoder(0, 256, s_svq1IntraMeanCodes, s_svq1IntraMeanLengths);
|
||||
_interMean = new HuffmanDecoder(0, 512, s_svq1InterMeanCodes, s_svq1InterMeanLengths);
|
||||
_motionComponent = new HuffmanDecoder(0, 33, s_svq1MotionComponentCodes, s_svq1MotionComponentLengths);
|
||||
}
|
||||
|
||||
SVQ1Decoder::~SVQ1Decoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _last[0];
|
||||
delete[] _last[1];
|
||||
delete[] _last[2];
|
||||
|
||||
delete _blockType;
|
||||
delete _intraMean;
|
||||
delete _interMean;
|
||||
delete _motionComponent;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
delete _intraMultistage[i];
|
||||
delete _interMultistage[i];
|
||||
}
|
||||
}
|
||||
|
||||
#define SVQ1_ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
|
||||
|
||||
const Graphics::Surface *SVQ1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
debug(1, "SVQ1Decoder::decodeImage()");
|
||||
|
||||
Common::BitStream32BEMSB frameData(stream);
|
||||
|
||||
uint32 frameCode = frameData.getBits<22>();
|
||||
debug(1, " frameCode: %d", frameCode);
|
||||
|
||||
if ((frameCode & ~0x70) || !(frameCode & 0x60)) { // Invalid
|
||||
warning("Invalid Image at frameCode");
|
||||
return _surface;
|
||||
}
|
||||
|
||||
byte temporalReference = frameData.getBits<8>();
|
||||
debug(1, " temporalReference: %d", temporalReference);
|
||||
static const char *const types[4] = { "I (Key)", "P (Delta from Previous)", "B (Delta from Next)", "Invalid" };
|
||||
byte frameType = frameData.getBits<2>();
|
||||
debug(1, " frameType: %d = %s Frame", frameType, types[frameType]);
|
||||
|
||||
if (frameType == 0) { // I Frame
|
||||
// TODO: Validate checksum if present
|
||||
if (frameCode == 0x50 || frameCode == 0x60) {
|
||||
uint32 checksum = frameData.getBits<16>();
|
||||
debug(1, " checksum:0x%02x", checksum);
|
||||
// We're currently just ignoring the checksum
|
||||
}
|
||||
|
||||
if ((frameCode ^ 0x10) >= 0x50) {
|
||||
// Skip embedded string
|
||||
byte stringLen = frameData.getBits<8>();
|
||||
for (uint16 i = 0; i < stringLen-1; i++)
|
||||
frameData.skip(8);
|
||||
}
|
||||
|
||||
frameData.skip(5); // Unknown
|
||||
|
||||
static const struct { uint w, h; } standardFrameSizes[7] = {
|
||||
{ 160, 120 }, // 0
|
||||
{ 128, 96 }, // 1
|
||||
{ 176, 144 }, // 2
|
||||
{ 352, 288 }, // 3
|
||||
{ 704, 576 }, // 4
|
||||
{ 240, 180 }, // 5
|
||||
{ 320, 240 } // 6
|
||||
};
|
||||
|
||||
byte frameSizeCode = frameData.getBits<3>();
|
||||
debug(1, " frameSizeCode: %d", frameSizeCode);
|
||||
|
||||
if (frameSizeCode == 7) {
|
||||
_frameWidth = frameData.getBits<12>();
|
||||
_frameHeight = frameData.getBits<12>();
|
||||
} else {
|
||||
_frameWidth = standardFrameSizes[frameSizeCode].w;
|
||||
_frameHeight = standardFrameSizes[frameSizeCode].h;
|
||||
}
|
||||
|
||||
debug(1, " frameWidth: %d", _frameWidth);
|
||||
debug(1, " frameHeight: %d", _frameHeight);
|
||||
} else if (frameType == 2) { // B Frame
|
||||
warning("B Frames not supported by SVQ1 decoder (yet)");
|
||||
return _surface;
|
||||
} else if (frameType == 3) { // Invalid
|
||||
warning("Invalid Frame Type");
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool checksumPresent = frameData.getBit() != 0;
|
||||
debug(1, " checksumPresent: %d", checksumPresent);
|
||||
if (checksumPresent) {
|
||||
bool usePacketChecksum = frameData.getBit() != 0;
|
||||
debug(1, " usePacketChecksum: %d", usePacketChecksum);
|
||||
bool componentChecksumsAfterImageData = frameData.getBit() != 0;
|
||||
debug(1, " componentChecksumsAfterImageData: %d", componentChecksumsAfterImageData);
|
||||
byte unk4 = frameData.getBits<2>();
|
||||
debug(1, " unk4: %d", unk4);
|
||||
if (unk4 != 0)
|
||||
warning("Invalid Frame Header in SVQ1 Frame Decode");
|
||||
}
|
||||
|
||||
// Some more unknown data
|
||||
bool unk5 = frameData.getBit() != 0;
|
||||
if (unk5) {
|
||||
frameData.skip(8);
|
||||
|
||||
while (frameData.getBit() != 0)
|
||||
frameData.skip(8);
|
||||
}
|
||||
|
||||
uint yWidth = SVQ1_ALIGN(_frameWidth, 16);
|
||||
uint yHeight = SVQ1_ALIGN(_frameHeight, 16);
|
||||
uint uvWidth = SVQ1_ALIGN(yWidth / 4, 16);
|
||||
uint uvHeight = SVQ1_ALIGN(yHeight / 4, 16);
|
||||
uint uvPitch = uvWidth + 4; // we need at least one extra column and pitch must be divisible by 4
|
||||
|
||||
byte *current[3];
|
||||
|
||||
// Decode Y, U and V component planes
|
||||
for (int i = 0; i < 3; i++) {
|
||||
uint width, height, pitch;
|
||||
if (i == 0) {
|
||||
width = yWidth;
|
||||
height = yHeight;
|
||||
pitch = width;
|
||||
current[i] = new byte[width * height];
|
||||
} else {
|
||||
width = uvWidth;
|
||||
height = uvHeight;
|
||||
pitch = uvPitch;
|
||||
|
||||
// Add an extra row here. See below for more information.
|
||||
current[i] = new byte[pitch * (height + 1)];
|
||||
}
|
||||
|
||||
if (frameType == 0) { // I Frame
|
||||
// Keyframe (I)
|
||||
byte *currentP = current[i];
|
||||
for (uint16 y = 0; y < height; y += 16) {
|
||||
for (uint16 x = 0; x < width; x += 16) {
|
||||
if (!svq1DecodeBlockIntra(&frameData, ¤tP[x], pitch)) {
|
||||
warning("svq1DecodeBlockIntra decode failure");
|
||||
return _surface;
|
||||
}
|
||||
}
|
||||
currentP += 16 * pitch;
|
||||
}
|
||||
} else {
|
||||
// Delta frame (P or B)
|
||||
|
||||
// Prediction Motion Vector
|
||||
Common::Point *pmv = new Common::Point[(width / 8) + 3];
|
||||
|
||||
byte *previous = 0;
|
||||
if (frameType == 2) { // B Frame
|
||||
error("SVQ1 Video: B Frames not supported");
|
||||
//previous = _next[i];
|
||||
} else {
|
||||
previous = _last[i];
|
||||
}
|
||||
|
||||
byte *currentP = current[i];
|
||||
for (uint16 y = 0; y < height; y += 16) {
|
||||
for (uint16 x = 0; x < width; x += 16) {
|
||||
if (!svq1DecodeDeltaBlock(&frameData, ¤tP[x], previous, pitch, pmv, x, y)) {
|
||||
warning("svq1DecodeDeltaBlock decode failure");
|
||||
return _surface;
|
||||
}
|
||||
}
|
||||
|
||||
pmv[0].x = pmv[0].y = 0;
|
||||
|
||||
currentP += 16 * pitch;
|
||||
}
|
||||
|
||||
delete[] pmv;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we'll create the surface
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(yWidth, yHeight, _pixelFormat);
|
||||
_surface->w = _width;
|
||||
_surface->h = _height;
|
||||
}
|
||||
|
||||
// We need to massage the chrominance data a bit to be able to be used by the converter
|
||||
// Since the thing peeks at values one column and one row beyond the data, we need to fill it in
|
||||
|
||||
// First, fill in the column-after-last with the last column's value
|
||||
for (uint i = 0; i < uvHeight; i++) {
|
||||
current[1][i * uvPitch + uvWidth] = current[1][i * uvPitch + uvWidth - 1];
|
||||
current[2][i * uvPitch + uvWidth] = current[2][i * uvPitch + uvWidth - 1];
|
||||
}
|
||||
|
||||
// Then, copy the last row to the one after the last row
|
||||
memcpy(current[1] + uvHeight * uvPitch, current[1] + (uvHeight - 1) * uvPitch, uvWidth + 1);
|
||||
memcpy(current[2] + uvHeight * uvPitch, current[2] + (uvHeight - 1) * uvPitch, uvWidth + 1);
|
||||
|
||||
// Finally, actually do the conversion ;)
|
||||
YUVToRGBMan.convert410(_surface, Graphics::YUVToRGBManager::kScaleFull, current[0], current[1], current[2], yWidth, yHeight, yWidth, uvPitch);
|
||||
|
||||
// Store the current surfaces for later and free the old ones
|
||||
for (int i = 0; i < 3; i++) {
|
||||
delete[] _last[i];
|
||||
_last[i] = current[i];
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeBlockIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch) {
|
||||
// initialize list for breadth first processing of vectors
|
||||
byte *list[63];
|
||||
list[0] = pixels;
|
||||
|
||||
// recursively process vector
|
||||
for (int i = 0, m = 1, n = 1, level = 5; i < n; i++) {
|
||||
for (; level > 0; i++) {
|
||||
// process next depth
|
||||
if (i == m) {
|
||||
m = n;
|
||||
if (--level == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// divide block if next bit set
|
||||
if (s->getBit() == 0)
|
||||
break;
|
||||
|
||||
// add child nodes
|
||||
list[n++] = list[i];
|
||||
list[n++] = list[i] + (((level & 1) ? pitch : 1) << ((level / 2) + 1));
|
||||
}
|
||||
|
||||
// destination address and vector size
|
||||
uint32 *dst = (uint32 *)list[i];
|
||||
uint width = 1 << ((level + 4) / 2);
|
||||
uint height = 1 << ((level + 3) / 2);
|
||||
|
||||
// get number of stages (-1 skips vector, 0 for mean only)
|
||||
int stages = _intraMultistage[level]->getSymbol(*s) - 1;
|
||||
|
||||
if (stages == -1) {
|
||||
for (uint y = 0; y < height; y++)
|
||||
memset(&dst[y * (pitch / 4)], 0, width);
|
||||
|
||||
continue; // skip vector
|
||||
}
|
||||
|
||||
if (stages > 0 && level >= 4) {
|
||||
warning("Error (svq1_decode_block_intra): invalid vector: stages = %d, level = %d", stages, level);
|
||||
return false; // error - invalid vector
|
||||
}
|
||||
|
||||
int mean = _intraMean->getSymbol(*s);
|
||||
|
||||
if (stages == 0) {
|
||||
for (uint y = 0; y < height; y++)
|
||||
memset(&dst[y * (pitch / 4)], mean, width);
|
||||
} else {
|
||||
const uint32 *codebook = (const uint32 *)s_svq1IntraCodebooks[level];
|
||||
uint32 bitCache = s->getBits(stages * 4);
|
||||
|
||||
// calculate codebook entries for this vector
|
||||
int entries[6];
|
||||
for (int j = 0; j < stages; j++)
|
||||
entries[j] = (((bitCache >> ((stages - j - 1) * 4)) & 0xF) + j * 16) << (level + 1);
|
||||
|
||||
mean -= stages * 128;
|
||||
uint32 n4 = ((mean + (mean >> 31)) << 16) | (mean & 0xFFFF);
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
for (uint x = 0; x < (width / 4); x++, codebook++) {
|
||||
uint32 n1 = n4;
|
||||
uint32 n2 = n4;
|
||||
uint32 n3;
|
||||
|
||||
// add codebook entries to vector
|
||||
for (int j = 0; j < stages; j++) {
|
||||
n3 = READ_UINT32(&codebook[entries[j]]) ^ 0x80808080;
|
||||
n1 += (n3 & 0xFF00FF00) >> 8;
|
||||
n2 += n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// clip to [0..255]
|
||||
if (n1 & 0xFF00FF00) {
|
||||
n3 = (((n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 += 0x7F007F00;
|
||||
n1 |= (((~n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
if (n2 & 0xFF00FF00) {
|
||||
n3 = (((n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 += 0x7F007F00;
|
||||
n2 |= (((~n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// store result
|
||||
dst[x] = (n1 << 8) | n2;
|
||||
}
|
||||
|
||||
dst += pitch / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeBlockNonIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch) {
|
||||
// initialize list for breadth first processing of vectors
|
||||
byte *list[63];
|
||||
list[0] = pixels;
|
||||
|
||||
// recursively process vector
|
||||
for (int i = 0, m = 1, n = 1, level = 5; i < n; i++) {
|
||||
for (; level > 0; i++) {
|
||||
// process next depth
|
||||
if (i == m) {
|
||||
m = n;
|
||||
if (--level == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// divide block if next bit set
|
||||
if (s->getBit() == 0)
|
||||
break;
|
||||
|
||||
// add child nodes
|
||||
list[n++] = list[i];
|
||||
list[n++] = list[i] + (((level & 1) ? pitch : 1) << ((level / 2) + 1));
|
||||
}
|
||||
|
||||
// destination address and vector size
|
||||
uint32 *dst = (uint32 *)list[i];
|
||||
int width = 1 << ((level + 4) / 2);
|
||||
int height = 1 << ((level + 3) / 2);
|
||||
|
||||
// get number of stages (-1 skips vector, 0 for mean only)
|
||||
int stages = _interMultistage[level]->getSymbol(*s) - 1;
|
||||
|
||||
if (stages == -1)
|
||||
continue; // skip vector
|
||||
|
||||
if (stages > 0 && level >= 4) {
|
||||
warning("Error (svq1_decode_block_non_intra): invalid vector: stages = %d, level = %d", stages, level);
|
||||
return false; // error - invalid vector
|
||||
}
|
||||
|
||||
int mean = _interMean->getSymbol(*s) - 256;
|
||||
const uint32 *codebook = (const uint32 *)s_svq1InterCodebooks[level];
|
||||
uint32 bitCache = s->getBits(stages * 4);
|
||||
|
||||
// calculate codebook entries for this vector
|
||||
int entries[6];
|
||||
for (int j = 0; j < stages; j++)
|
||||
entries[j] = (((bitCache >> ((stages - j - 1) * 4)) & 0xF) + j * 16) << (level + 1);
|
||||
|
||||
mean -= stages * 128;
|
||||
uint32 n4 = ((mean + (mean >> 31)) << 16) | (mean & 0xFFFF);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < (width / 4); x++, codebook++) {
|
||||
uint32 n3 = dst[x];
|
||||
|
||||
// add mean value to vector
|
||||
uint32 n1 = ((n3 & 0xFF00FF00) >> 8) + n4;
|
||||
uint32 n2 = (n3 & 0x00FF00FF) + n4;
|
||||
|
||||
// add codebook entries to vector
|
||||
for (int j = 0; j < stages; j++) {
|
||||
n3 = READ_UINT32(&codebook[entries[j]]) ^ 0x80808080;
|
||||
n1 += (n3 & 0xFF00FF00) >> 8;
|
||||
n2 += n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// clip to [0..255]
|
||||
if (n1 & 0xFF00FF00) {
|
||||
n3 = ((( n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 += 0x7F007F00;
|
||||
n1 |= (((~n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
if (n2 & 0xFF00FF00) {
|
||||
n3 = ((( n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 += 0x7F007F00;
|
||||
n2 |= (((~n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// store result
|
||||
dst[x] = (n1 << 8) | n2;
|
||||
}
|
||||
|
||||
dst += pitch / 4;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// median of 3
|
||||
static inline int midPred(int a, int b, int c) {
|
||||
if (a > b) {
|
||||
if (c > b) {
|
||||
if (c > a)
|
||||
b = a;
|
||||
else
|
||||
b = c;
|
||||
}
|
||||
} else {
|
||||
if (b > c) {
|
||||
if (c > a)
|
||||
b = c;
|
||||
else
|
||||
b = a;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeMotionVector(Common::BitStream32BEMSB *s, Common::Point *mv, Common::Point **pmv) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// get motion code
|
||||
int diff = _motionComponent->getSymbol(*s);
|
||||
if (diff < 0)
|
||||
return false; // error - invalid motion code
|
||||
else if (diff && s->getBit() != 0)
|
||||
diff = -diff;
|
||||
|
||||
// add median of motion vector predictors and clip result
|
||||
if (i == 1)
|
||||
mv->y = ((diff + midPred(pmv[0]->y, pmv[1]->y, pmv[2]->y)) << 26) >> 26;
|
||||
else
|
||||
mv->x = ((diff + midPred(pmv[0]->x, pmv[1]->x, pmv[2]->x)) << 26) >> 26;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SVQ1Decoder::svq1SkipBlock(byte *current, byte *previous, int pitch, int x, int y) {
|
||||
const byte *src = &previous[x + y * pitch];
|
||||
byte *dst = current;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
memcpy(dst, src, 16);
|
||||
src += pitch;
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
for (int i = 0; i < h; i++) {
|
||||
*((uint32 *)block) = READ_UINT32(pixels);
|
||||
*((uint32 *)(block + 4)) = READ_UINT32(pixels + 4);
|
||||
pixels += lineSize;
|
||||
block += lineSize;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32 rndAvg32(uint32 a, uint32 b) {
|
||||
return (a | b) - (((a ^ b) & ~0x01010101) >> 1);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8L2(byte *dst, const byte *src1, const byte *src2,
|
||||
int dstStride, int srcStride1, int srcStride2, int h) {
|
||||
for (int i = 0; i < h; i++) {
|
||||
uint32 a = READ_UINT32(&src1[srcStride1 * i]);
|
||||
uint32 b = READ_UINT32(&src2[srcStride2 * i]);
|
||||
*((uint32 *)&dst[dstStride * i]) = rndAvg32(a, b);
|
||||
a = READ_UINT32(&src1[srcStride1 * i + 4]);
|
||||
b = READ_UINT32(&src2[srcStride2 * i + 4]);
|
||||
*((uint32 *)&dst[dstStride * i + 4]) = rndAvg32(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8X2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8L2(block, pixels, pixels + 1, lineSize, lineSize, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8Y2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8L2(block, pixels, pixels + lineSize, lineSize, lineSize, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8XY2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
uint32 a = READ_UINT32(pixels);
|
||||
uint32 b = READ_UINT32(pixels + 1);
|
||||
uint32 l0 = (a & 0x03030303UL) + (b & 0x03030303UL) + 0x02020202UL;
|
||||
uint32 h0 = ((a & 0xFCFCFCFCUL) >> 2) + ((b & 0xFCFCFCFCUL) >> 2);
|
||||
|
||||
pixels += lineSize;
|
||||
|
||||
for (int i = 0; i < h; i += 2) {
|
||||
a = READ_UINT32(pixels);
|
||||
b = READ_UINT32(pixels + 1);
|
||||
uint32 l1 = (a & 0x03030303UL) + (b & 0x03030303UL);
|
||||
uint32 h1 = ((a & 0xFCFCFCFCUL) >> 2) + ((b & 0xFCFCFCFCUL) >> 2);
|
||||
*((uint32 *)block) = h0 + h1 + (((l0 + l1) >> 2) & 0x0F0F0F0FUL);
|
||||
pixels += lineSize;
|
||||
block += lineSize;
|
||||
a = READ_UINT32(pixels);
|
||||
b = READ_UINT32(pixels + 1);
|
||||
l0 = (a & 0x03030303UL) + (b & 0x03030303UL) + 0x02020202UL;
|
||||
h0 = ((a & 0xFCFCFCFCUL) >> 2) + ((b & 0xFCFCFCFCUL) >> 2);
|
||||
*((uint32 *)block) = h0 + h1 + (((l0 + l1) >> 2) & 0x0F0F0F0FUL);
|
||||
pixels += lineSize;
|
||||
block += lineSize;
|
||||
}
|
||||
|
||||
pixels += 4 - lineSize * (h + 1);
|
||||
block += 4 - lineSize * h;
|
||||
}
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8C(block, pixels, lineSize, h);
|
||||
putPixels8C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16X2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8X2C(block, pixels, lineSize, h);
|
||||
putPixels8X2C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16Y2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8Y2C(block, pixels, lineSize, h);
|
||||
putPixels8Y2C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16XY2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8XY2C(block, pixels, lineSize, h);
|
||||
putPixels8XY2C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1MotionInterBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y) {
|
||||
|
||||
// predict and decode motion vector
|
||||
Common::Point *pmv[3];
|
||||
pmv[0] = &motion[0];
|
||||
if (y == 0) {
|
||||
pmv[1] = pmv[2] = pmv[0];
|
||||
} else {
|
||||
pmv[1] = &motion[(x / 8) + 2];
|
||||
pmv[2] = &motion[(x / 8) + 4];
|
||||
}
|
||||
|
||||
Common::Point mv;
|
||||
bool resultValid = svq1DecodeMotionVector(ss, &mv, pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
motion[0].x = motion[(x / 8) + 2].x = motion[(x / 8) + 3].x = mv.x;
|
||||
motion[0].y = motion[(x / 8) + 2].y = motion[(x / 8) + 3].y = mv.y;
|
||||
|
||||
if (y + (mv.y >> 1) < 0)
|
||||
mv.y = 0;
|
||||
|
||||
if (x + (mv.x >> 1) < 0)
|
||||
mv.x = 0;
|
||||
|
||||
const byte *src = &previous[(x + (mv.x >> 1)) + (y + (mv.y >> 1)) * pitch];
|
||||
byte *dst = current;
|
||||
|
||||
// Halfpel motion compensation with rounding (a + b + 1) >> 1.
|
||||
// 4 motion compensation functions for the 4 halfpel positions
|
||||
// for 16x16 blocks
|
||||
switch(((mv.y & 1) << 1) + (mv.x & 1)) {
|
||||
case 0:
|
||||
putPixels16C(dst, src, pitch, 16);
|
||||
break;
|
||||
case 1:
|
||||
putPixels16X2C(dst, src, pitch, 16);
|
||||
break;
|
||||
case 2:
|
||||
putPixels16Y2C(dst, src, pitch, 16);
|
||||
break;
|
||||
case 3:
|
||||
putPixels16XY2C(dst, src, pitch, 16);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1MotionInter4vBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y) {
|
||||
// predict and decode motion vector (0)
|
||||
Common::Point *pmv[4];
|
||||
pmv[0] = &motion[0];
|
||||
if (y == 0) {
|
||||
pmv[1] = pmv[2] = pmv[0];
|
||||
} else {
|
||||
pmv[1] = &motion[(x / 8) + 2];
|
||||
pmv[2] = &motion[(x / 8) + 4];
|
||||
}
|
||||
|
||||
Common::Point mv;
|
||||
bool resultValid = svq1DecodeMotionVector(ss, &mv, pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// predict and decode motion vector (1)
|
||||
pmv[0] = &mv;
|
||||
if (y == 0)
|
||||
pmv[1] = pmv[2] = pmv[0];
|
||||
else
|
||||
pmv[1] = &motion[(x / 8) + 3];
|
||||
|
||||
resultValid = svq1DecodeMotionVector(ss, &motion[0], pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// predict and decode motion vector (2)
|
||||
pmv[1] = &motion[0];
|
||||
pmv[2] = &motion[(x / 8) + 1];
|
||||
|
||||
resultValid = svq1DecodeMotionVector(ss, &motion[(x / 8) + 2], pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// predict and decode motion vector (3)
|
||||
pmv[2] = &motion[(x / 8) + 2];
|
||||
pmv[3] = &motion[(x / 8) + 3];
|
||||
|
||||
resultValid = svq1DecodeMotionVector(ss, pmv[3], pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// form predictions
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int mvx = pmv[i]->x + (i & 1) * 16;
|
||||
int mvy = pmv[i]->y + (i >> 1) * 16;
|
||||
|
||||
// FIXME: clipping or padding?
|
||||
if (y + (mvy >> 1) < 0)
|
||||
mvy = 0;
|
||||
|
||||
if (x + (mvx >> 1) < 0)
|
||||
mvx = 0;
|
||||
|
||||
const byte *src = &previous[(x + (mvx >> 1)) + (y + (mvy >> 1)) * pitch];
|
||||
byte *dst = current;
|
||||
|
||||
// Halfpel motion compensation with rounding (a + b + 1) >> 1.
|
||||
// 4 motion compensation functions for the 4 halfpel positions
|
||||
// for 8x8 blocks
|
||||
switch(((mvy & 1) << 1) + (mvx & 1)) {
|
||||
case 0:
|
||||
putPixels8C(dst, src, pitch, 8);
|
||||
break;
|
||||
case 1:
|
||||
putPixels8X2C(dst, src, pitch, 8);
|
||||
break;
|
||||
case 2:
|
||||
putPixels8Y2C(dst, src, pitch, 8);
|
||||
break;
|
||||
case 3:
|
||||
putPixels8XY2C(dst, src, pitch, 8);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// select next block
|
||||
if (i & 1)
|
||||
current += (pitch - 1) * 8;
|
||||
else
|
||||
current += 8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeDeltaBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y) {
|
||||
// get block type
|
||||
uint32 blockType = _blockType->getSymbol(*ss);
|
||||
|
||||
// reset motion vectors
|
||||
if (blockType == SVQ1_BLOCK_SKIP || blockType == SVQ1_BLOCK_INTRA) {
|
||||
motion[0].x =
|
||||
motion[0].y =
|
||||
motion[(x / 8) + 2].x =
|
||||
motion[(x / 8) + 2].y =
|
||||
motion[(x / 8) + 3].x =
|
||||
motion[(x / 8) + 3].y = 0;
|
||||
}
|
||||
|
||||
bool resultValid = true;
|
||||
|
||||
switch (blockType) {
|
||||
case SVQ1_BLOCK_SKIP:
|
||||
svq1SkipBlock(current, previous, pitch, x, y);
|
||||
break;
|
||||
case SVQ1_BLOCK_INTER:
|
||||
resultValid = svq1MotionInterBlock(ss, current, previous, pitch, motion, x, y);
|
||||
if (!resultValid) {
|
||||
warning("svq1MotionInterBlock decode failure");
|
||||
break;
|
||||
}
|
||||
resultValid = svq1DecodeBlockNonIntra(ss, current, pitch);
|
||||
break;
|
||||
case SVQ1_BLOCK_INTER_4V:
|
||||
resultValid = svq1MotionInter4vBlock(ss, current, previous, pitch, motion, x, y);
|
||||
if (!resultValid) {
|
||||
warning("svq1MotionInter4vBlock decode failure");
|
||||
break;
|
||||
}
|
||||
resultValid = svq1DecodeBlockNonIntra(ss, current, pitch);
|
||||
break;
|
||||
case SVQ1_BLOCK_INTRA:
|
||||
resultValid = svq1DecodeBlockIntra(ss, current, pitch);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return resultValid;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
98
image/codecs/svq1.h
Normal file
98
image/codecs/svq1.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_SVQ1_H
|
||||
#define IMAGE_CODECS_SVQ1_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "common/bitstream.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Common {
|
||||
template <class BITSTREAM>
|
||||
class Huffman;
|
||||
struct Point;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Sorenson Vector Quantizer 1 decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class SVQ1Decoder : public Codec {
|
||||
public:
|
||||
SVQ1Decoder(uint16 width, uint16 height);
|
||||
~SVQ1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
uint16 _width, _height;
|
||||
uint16 _frameWidth, _frameHeight;
|
||||
|
||||
byte *_last[3];
|
||||
|
||||
typedef Common::Huffman<Common::BitStream32BEMSB> HuffmanDecoder;
|
||||
|
||||
HuffmanDecoder *_blockType;
|
||||
HuffmanDecoder *_intraMultistage[6];
|
||||
HuffmanDecoder *_interMultistage[6];
|
||||
HuffmanDecoder *_intraMean;
|
||||
HuffmanDecoder *_interMean;
|
||||
HuffmanDecoder *_motionComponent;
|
||||
|
||||
bool svq1DecodeBlockIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch);
|
||||
bool svq1DecodeBlockNonIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch);
|
||||
bool svq1DecodeMotionVector(Common::BitStream32BEMSB *s, Common::Point *mv, Common::Point **pmv);
|
||||
void svq1SkipBlock(byte *current, byte *previous, int pitch, int x, int y);
|
||||
bool svq1MotionInterBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y);
|
||||
bool svq1MotionInter4vBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y);
|
||||
bool svq1DecodeDeltaBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y);
|
||||
|
||||
void putPixels8C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels8L2(byte *dst, const byte *src1, const byte *src2, int dstStride, int srcStride1, int srcStride2, int h);
|
||||
void putPixels8X2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels8Y2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels8XY2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16X2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16Y2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16XY2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1510
image/codecs/svq1_cb.h
Normal file
1510
image/codecs/svq1_cb.h
Normal file
File diff suppressed because it is too large
Load Diff
340
image/codecs/svq1_vlc.h
Normal file
340
image/codecs/svq1_vlc.h
Normal file
@@ -0,0 +1,340 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// These tables are modified versions of the FFmpeg ones so that they
|
||||
// will work with our BitStream class directly.
|
||||
|
||||
#ifndef IMAGE_CODECS_SVQ1_VLC_H
|
||||
#define IMAGE_CODECS_SVQ1_VLC_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
static const byte s_svq1BlockTypeLengths[4] = {
|
||||
1, 2, 3, 3
|
||||
};
|
||||
|
||||
static const uint32 s_svq1BlockTypeCodes[4] = {
|
||||
1, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths0[8] = {
|
||||
5, 1, 3, 3, 4, 4, 5, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes0[8] = {
|
||||
1, 1, 3, 2, 3, 2, 0, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths1[8] = {
|
||||
4, 2, 3, 3, 3, 3, 4, 3
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes1[8] = {
|
||||
1, 3, 5, 4, 3, 2, 0, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths2[8] = {
|
||||
5, 1, 3, 5, 4, 3, 4, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes2[8] = {
|
||||
1, 1, 3, 0, 3, 2, 2, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths3[8] = {
|
||||
6, 1, 2, 6, 4, 4, 5, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes3[8] = {
|
||||
1, 1, 1, 0, 3, 2, 1, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths4[8] = {
|
||||
6, 1, 2, 5, 5, 6, 5, 3
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes4[8] = {
|
||||
1, 1, 1, 3, 2, 0, 1, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths5[8] = {
|
||||
7, 1, 2, 3, 4, 6, 7, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes5[8] = {
|
||||
1, 1, 1, 1, 1, 1, 0, 1
|
||||
};
|
||||
|
||||
static const byte *const s_svq1IntraMultistageLengths[6] = {
|
||||
s_svq1IntraMultistageLengths0, s_svq1IntraMultistageLengths1, s_svq1IntraMultistageLengths2,
|
||||
s_svq1IntraMultistageLengths3, s_svq1IntraMultistageLengths4, s_svq1IntraMultistageLengths5
|
||||
};
|
||||
|
||||
static const uint32 *const s_svq1IntraMultistageCodes[6] = {
|
||||
s_svq1IntraMultistageCodes0, s_svq1IntraMultistageCodes1, s_svq1IntraMultistageCodes2,
|
||||
s_svq1IntraMultistageCodes3, s_svq1IntraMultistageCodes4, s_svq1IntraMultistageCodes5
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths0[8] = {
|
||||
2, 3, 3, 3, 3, 3, 4, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes0[8] = {
|
||||
3, 5, 4, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths1[8] = {
|
||||
2, 3, 3, 3, 3, 3, 4, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes1[8] = {
|
||||
3, 5, 4, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths2[8] = {
|
||||
1, 3, 3, 4, 4, 4, 5, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes2[8] = {
|
||||
1, 3, 2, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths3[8] = {
|
||||
1, 3, 3, 4, 4, 4, 5, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes3[8] = {
|
||||
1, 3, 2, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths4[8] = {
|
||||
1, 3, 3, 4, 4, 4, 5, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes4[8] = {
|
||||
1, 3, 2, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths5[8] = {
|
||||
1, 2, 3, 5, 5, 5, 6, 6
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes5[8] = {
|
||||
1, 1, 1, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte *const s_svq1InterMultistageLengths[6] = {
|
||||
s_svq1InterMultistageLengths0, s_svq1InterMultistageLengths1, s_svq1InterMultistageLengths2,
|
||||
s_svq1InterMultistageLengths3, s_svq1InterMultistageLengths4, s_svq1InterMultistageLengths5
|
||||
};
|
||||
|
||||
static const uint32 *const s_svq1InterMultistageCodes[6] = {
|
||||
s_svq1InterMultistageCodes0, s_svq1InterMultistageCodes1, s_svq1InterMultistageCodes2,
|
||||
s_svq1InterMultistageCodes3, s_svq1InterMultistageCodes4, s_svq1InterMultistageCodes5
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMeanLengths[256] = {
|
||||
6, 7, 17, 20, 20, 20, 20, 20, 20, 19,
|
||||
11, 9, 11, 14, 14, 15, 16, 12, 10, 11,
|
||||
11, 9, 8, 8, 7, 4, 4, 6, 7, 8,
|
||||
8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 7, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 7, 8, 8, 7, 8,
|
||||
8, 8, 8, 8, 7, 8, 7, 7, 8, 7,
|
||||
7, 8, 7, 8, 8, 8, 7, 7, 8, 7,
|
||||
8, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 6, 6,
|
||||
7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 9,
|
||||
9, 9, 9, 9, 8, 8, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 11, 11, 11, 10, 11, 11, 11,
|
||||
11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||
11, 11, 11, 11, 11, 14
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMeanCodes[256] = {
|
||||
55, 86, 1, 1, 2, 3, 0, 4, 5, 3,
|
||||
21, 66, 20, 3, 2, 1, 1, 1, 43, 24,
|
||||
12, 65, 120, 108, 85, 15, 14, 52, 81, 114,
|
||||
110, 64, 63, 62, 61, 60, 59, 58, 57, 56,
|
||||
55, 67, 70, 71, 69, 68, 73, 72, 74, 121,
|
||||
118, 119, 113, 117, 116, 115, 106, 85, 112, 111,
|
||||
82, 109, 76, 107, 64, 105, 104, 103, 102, 101,
|
||||
100, 99, 98, 97, 96, 95, 94, 93, 92, 91,
|
||||
90, 89, 88, 87, 86, 61, 84, 83, 63, 81,
|
||||
80, 79, 78, 77, 65, 75, 83, 62, 72, 79,
|
||||
82, 69, 80, 67, 66, 65, 66, 67, 62, 68,
|
||||
60, 69, 70, 71, 72, 73, 74, 75, 76, 77,
|
||||
78, 88, 89, 90, 91, 92, 93, 68, 73, 41,
|
||||
63, 61, 59, 44, 40, 37, 38, 94, 87, 84,
|
||||
95, 98, 99, 100, 97, 101, 103, 102, 53, 54,
|
||||
96, 57, 58, 56, 55, 54, 53, 52, 51, 50,
|
||||
49, 48, 45, 43, 42, 39, 64, 70, 71, 38,
|
||||
37, 36, 35, 34, 46, 47, 31, 54, 29, 33,
|
||||
27, 28, 25, 26, 24, 23, 22, 30, 32, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 53,
|
||||
49, 50, 51, 52, 25, 42, 23, 22, 21, 40,
|
||||
38, 37, 34, 33, 24, 20, 41, 18, 13, 14,
|
||||
15, 16, 17, 26, 27, 28, 29, 30, 31, 32,
|
||||
19, 35, 36, 9, 8, 7, 39, 5, 11, 6,
|
||||
4, 3, 2, 1, 10, 22, 25, 23, 13, 14,
|
||||
15, 16, 17, 18, 19, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMeanLengths[512] = {
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 21, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 21,
|
||||
22, 22, 22, 22, 22, 22, 20, 21, 22, 21,
|
||||
22, 22, 20, 22, 22, 21, 19, 18, 20, 22,
|
||||
22, 21, 20, 19, 20, 20, 19, 19, 19, 18,
|
||||
19, 18, 19, 20, 19, 19, 18, 18, 18, 19,
|
||||
18, 18, 18, 17, 19, 18, 18, 17, 18, 18,
|
||||
18, 17, 17, 17, 17, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 15, 16, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 12, 12, 12, 12, 12, 12, 11, 11, 11,
|
||||
11, 11, 11, 10, 10, 10, 10, 10, 10, 9,
|
||||
9, 9, 9, 9, 8, 8, 8, 8, 7, 7,
|
||||
7, 6, 6, 5, 5, 4, 1, 3, 5, 5,
|
||||
6, 6, 7, 7, 7, 7, 8, 8, 8, 9,
|
||||
9, 9, 9, 9, 10, 10, 10, 10, 11, 11,
|
||||
11, 11, 11, 11, 11, 12, 12, 12, 12, 12,
|
||||
12, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 18, 17, 17, 17,
|
||||
17, 17, 17, 17, 19, 18, 18, 19, 18, 18,
|
||||
19, 18, 18, 18, 19, 18, 19, 19, 18, 20,
|
||||
20, 19, 19, 19, 19, 19, 19, 18, 19, 20,
|
||||
19, 19, 21, 20, 19, 20, 19, 19, 20, 20,
|
||||
22, 20, 22, 22, 21, 22, 22, 21, 21, 22,
|
||||
22, 20, 22, 22, 21, 22, 22, 22, 20, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 21, 21, 22, 22, 22, 21,
|
||||
22, 21, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMeanCodes[512] = {
|
||||
90, 212, 213, 214, 215, 216, 217, 218, 219, 220,
|
||||
221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
|
||||
232, 203, 233, 234, 231, 236, 237, 238, 239, 240,
|
||||
241, 242, 243, 244, 245, 246, 247, 248, 258, 235,
|
||||
249, 252, 253, 254, 256, 92, 96, 257, 113, 260,
|
||||
261, 251, 255, 134, 250, 124, 117, 259, 120, 211,
|
||||
123, 130, 210, 209, 208, 207, 206, 205, 204, 195,
|
||||
202, 201, 200, 199, 198, 197, 139, 196, 194, 193,
|
||||
192, 191, 190, 189, 188, 187, 186, 185, 97, 132,
|
||||
133, 134, 135, 136, 137, 138, 140, 141, 142, 143,
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
|
||||
154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
|
||||
164, 165, 166, 167, 168, 169, 170, 171, 127, 143,
|
||||
172, 173, 174, 175, 176, 177, 83, 144, 178, 145,
|
||||
179, 180, 84, 181, 182, 140, 52, 61, 85, 183,
|
||||
184, 139, 86, 61, 87, 88, 64, 67, 71, 42,
|
||||
46, 44, 70, 89, 73, 45, 56, 54, 57, 69,
|
||||
40, 48, 53, 32, 68, 50, 49, 31, 47, 46,
|
||||
45, 33, 34, 35, 36, 39, 35, 32, 29, 37,
|
||||
30, 36, 42, 38, 33, 41, 34, 35, 36, 27,
|
||||
26, 29, 31, 39, 23, 24, 25, 27, 28, 30,
|
||||
37, 32, 33, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 24, 23, 21, 20, 19, 18, 15, 16, 18,
|
||||
19, 27, 26, 14, 19, 15, 16, 17, 18, 13,
|
||||
20, 21, 12, 19, 15, 14, 16, 17, 12, 9,
|
||||
10, 8, 9, 9, 8, 5, 1, 3, 7, 6,
|
||||
11, 10, 14, 15, 11, 13, 11, 13, 12, 15,
|
||||
16, 17, 14, 18, 23, 20, 22, 21, 25, 24,
|
||||
23, 22, 21, 20, 17, 25, 26, 22, 29, 27,
|
||||
28, 32, 28, 35, 34, 33, 31, 30, 27, 29,
|
||||
36, 22, 26, 34, 29, 31, 21, 35, 24, 32,
|
||||
41, 40, 38, 37, 25, 28, 30, 23, 44, 43,
|
||||
28, 33, 45, 40, 31, 27, 26, 34, 45, 50,
|
||||
44, 39, 49, 51, 47, 43, 55, 42, 46, 48,
|
||||
41, 40, 38, 37, 47, 51, 52, 48, 58, 59,
|
||||
49, 60, 43, 41, 72, 39, 66, 65, 38, 82,
|
||||
81, 63, 62, 57, 60, 59, 58, 37, 56, 80,
|
||||
55, 54, 135, 79, 53, 78, 51, 50, 77, 76,
|
||||
131, 75, 129, 128, 142, 126, 125, 132, 141, 122,
|
||||
121, 74, 119, 118, 137, 116, 115, 114, 73, 112,
|
||||
111, 110, 109, 108, 107, 106, 105, 104, 103, 102,
|
||||
101, 100, 99, 98, 138, 136, 95, 94, 93, 133,
|
||||
91, 131, 89, 88, 87, 86, 85, 84, 83, 82,
|
||||
81, 80, 79, 78, 77, 76, 75, 74, 73, 72,
|
||||
71, 70, 69, 68, 67, 66, 65, 64, 63, 62,
|
||||
61, 60, 59, 58, 57, 56, 55, 54, 53, 52,
|
||||
51, 50, 49, 48, 47, 46, 45, 44, 43, 42,
|
||||
41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
|
||||
31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
|
||||
21, 20, 19, 18, 17, 16, 15, 14, 13, 12,
|
||||
11, 10, 9, 8, 7, 6, 5, 4, 3, 2,
|
||||
1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1MotionComponentLengths[33] = {
|
||||
1, 2, 3, 4, 6, 7, 7, 7, 9, 9,
|
||||
9, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 11, 11, 11, 11, 11,
|
||||
11, 12, 12
|
||||
};
|
||||
|
||||
static const uint32 s_svq1MotionComponentCodes[33] = {
|
||||
1, 1, 1, 1, 3, 5, 4, 3, 11, 10,
|
||||
9, 17, 16, 15, 14, 13, 12, 11, 10, 9,
|
||||
8, 7, 6, 5, 4, 7, 6, 5, 4, 3,
|
||||
2, 3, 2
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
425
image/codecs/truemotion1.cpp
Normal file
425
image/codecs/truemotion1.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the TrueMotion 1 decoder by Alex Beregszaszi & Mike Melanson in FFmpeg
|
||||
|
||||
#include "image/codecs/truemotion1.h"
|
||||
#include "image/codecs/truemotion1data.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
enum {
|
||||
FLAG_SPRITE = (1 << 5),
|
||||
FLAG_KEYFRAME = (1 << 4),
|
||||
FLAG_INTERFRAME = (1 << 3),
|
||||
FLAG_INTERPOLATED = (1 << 2)
|
||||
};
|
||||
|
||||
enum {
|
||||
ALGO_NOP = 0,
|
||||
ALGO_RGB16V = 1,
|
||||
ALGO_RGB16H = 2,
|
||||
ALGO_RGB24H = 3
|
||||
};
|
||||
|
||||
// these are the various block sizes that can occupy a 4x4 block
|
||||
enum {
|
||||
BLOCK_2x2 = 0,
|
||||
BLOCK_2x4 = 1,
|
||||
BLOCK_4x2 = 2,
|
||||
BLOCK_4x4 = 3
|
||||
};
|
||||
|
||||
// { valid for metatype }, algorithm, num of deltas, vert res, horiz res
|
||||
struct CompressionType {
|
||||
int algorithm;
|
||||
int blockWidth; // vres
|
||||
int blockHeight; // hres
|
||||
int blockType;
|
||||
};
|
||||
|
||||
static const CompressionType compressionTypes[17] = {
|
||||
{ ALGO_NOP, 0, 0, 0 },
|
||||
|
||||
{ ALGO_RGB16V, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_RGB16H, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_RGB16V, 4, 2, BLOCK_4x2 },
|
||||
{ ALGO_RGB16H, 4, 2, BLOCK_4x2 },
|
||||
|
||||
{ ALGO_RGB16V, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_RGB16H, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_RGB16V, 2, 2, BLOCK_2x2 },
|
||||
{ ALGO_RGB16H, 2, 2, BLOCK_2x2 },
|
||||
|
||||
{ ALGO_NOP, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_RGB24H, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_NOP, 4, 2, BLOCK_4x2 },
|
||||
{ ALGO_RGB24H, 4, 2, BLOCK_4x2 },
|
||||
|
||||
{ ALGO_NOP, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_RGB24H, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_NOP, 2, 2, BLOCK_2x2 },
|
||||
{ ALGO_RGB24H, 2, 2, BLOCK_2x2 }
|
||||
};
|
||||
|
||||
TrueMotion1Decoder::TrueMotion1Decoder() {
|
||||
_surface = 0;
|
||||
_vertPred = 0;
|
||||
|
||||
_buf = _mbChangeBits = _indexStream = 0;
|
||||
_lastDeltaset = _lastVectable = -1;
|
||||
}
|
||||
|
||||
TrueMotion1Decoder::~TrueMotion1Decoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _vertPred;
|
||||
}
|
||||
|
||||
void TrueMotion1Decoder::selectDeltaTables(int deltaTableIndex) {
|
||||
if (deltaTableIndex > 3)
|
||||
return;
|
||||
|
||||
for (byte i = 0; i < 8; i++) {
|
||||
_ydt[i] = ydts[deltaTableIndex][i];
|
||||
_cdt[i] = cdts[deltaTableIndex][i];
|
||||
|
||||
// Y skinny deltas need to be halved for some reason; maybe the
|
||||
// skinny Y deltas should be modified
|
||||
// Drop the lsb before dividing by 2-- net effect: round down
|
||||
// when dividing a negative number (e.g., -3/2 = -2, not -1)
|
||||
_ydt[i] &= 0xFFFE;
|
||||
_ydt[i] /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
int TrueMotion1Decoder::makeYdt16Entry(int p1, int p2) {
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
// Swap the values on BE systems. FFmpeg does this too.
|
||||
SWAP<int>(p1, p2);
|
||||
#endif
|
||||
|
||||
int lo = _ydt[p1];
|
||||
lo += (lo << 6) + (lo << 11);
|
||||
int hi = _ydt[p2];
|
||||
hi += (hi << 6) + (hi << 11);
|
||||
return lo + (hi << 16);
|
||||
}
|
||||
|
||||
int TrueMotion1Decoder::makeCdt16Entry(int p1, int p2) {
|
||||
int b = _cdt[p2];
|
||||
int r = _cdt[p1] << 11;
|
||||
int lo = b + r;
|
||||
return lo + (lo << 16);
|
||||
}
|
||||
|
||||
void TrueMotion1Decoder::genVectorTable16(const byte *selVectorTable) {
|
||||
memset(&_yPredictorTable, 0, sizeof(PredictorTableEntry) * 1024);
|
||||
memset(&_cPredictorTable, 0, sizeof(PredictorTableEntry) * 1024);
|
||||
|
||||
for (int i = 0; i < 1024; i += 4) {
|
||||
int len = *selVectorTable++ / 2;
|
||||
for (int j = 0; j < len; j++) {
|
||||
byte deltaPair = *selVectorTable++;
|
||||
_yPredictorTable[i + j].color = makeYdt16Entry(deltaPair >> 4, deltaPair & 0xf);
|
||||
_cPredictorTable[i + j].color = makeCdt16Entry(deltaPair >> 4, deltaPair & 0xf);
|
||||
}
|
||||
|
||||
_yPredictorTable[i + (len - 1)].getNextIndex = true;
|
||||
_cPredictorTable[i + (len - 1)].getNextIndex = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TrueMotion1Decoder::decodeHeader(Common::SeekableReadStream &stream) {
|
||||
_buf = new byte[stream.size()];
|
||||
stream.read(_buf, stream.size());
|
||||
|
||||
byte headerBuffer[128]; // logical maximum size of the header
|
||||
const byte *selVectorTable;
|
||||
|
||||
_header.headerSize = ((_buf[0] >> 5) | (_buf[0] << 3)) & 0x7f;
|
||||
|
||||
if (_buf[0] < 0x10)
|
||||
error("Invalid TrueMotion1 header size %d", _header.headerSize);
|
||||
|
||||
// unscramble the header bytes with a XOR operation
|
||||
memset(headerBuffer, 0, 128);
|
||||
for (int i = 1; i < _header.headerSize; i++)
|
||||
headerBuffer[i - 1] = _buf[i] ^ _buf[i + 1];
|
||||
|
||||
_header.compression = headerBuffer[0];
|
||||
_header.deltaset = headerBuffer[1];
|
||||
_header.vectable = headerBuffer[2];
|
||||
_header.ysize = READ_LE_UINT16(&headerBuffer[3]);
|
||||
_header.xsize = READ_LE_UINT16(&headerBuffer[5]);
|
||||
_header.checksum = READ_LE_UINT16(&headerBuffer[7]);
|
||||
_header.version = headerBuffer[9];
|
||||
_header.headerType = headerBuffer[10];
|
||||
_header.flags = headerBuffer[11];
|
||||
_header.control = headerBuffer[12];
|
||||
|
||||
if (!_vertPred) {
|
||||
// there is a vertical predictor for each pixel in a line; each vertical
|
||||
// predictor is 0 to start with
|
||||
_vertPred = new uint32[_header.xsize];
|
||||
}
|
||||
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_header.xsize, _header.ysize, getPixelFormat());
|
||||
}
|
||||
|
||||
// There is 1 change bit per 4 pixels, so each change byte represents
|
||||
// 32 pixels; divide width by 4 to obtain the number of change bits and
|
||||
// then round up to the nearest byte.
|
||||
_mbChangeBitsRowSize = ((_header.xsize >> 2) + 7) >> 3;
|
||||
|
||||
// Version 2
|
||||
if (_header.version >= 2) {
|
||||
if (_header.headerType > 3) {
|
||||
error("Invalid header type %d", _header.headerType);
|
||||
} else if (_header.headerType == 2 || _header.headerType == 3) {
|
||||
_flags = _header.flags;
|
||||
if (!(_flags & FLAG_INTERFRAME))
|
||||
_flags |= FLAG_KEYFRAME;
|
||||
} else
|
||||
_flags = FLAG_KEYFRAME;
|
||||
} else // Version 1
|
||||
_flags = FLAG_KEYFRAME;
|
||||
|
||||
if (_flags & FLAG_SPRITE) {
|
||||
error("SPRITE frame found, please report the sample to the developers");
|
||||
} else if (_header.headerType < 2 && _header.xsize < 213 && _header.ysize >= 176) {
|
||||
_flags |= FLAG_INTERPOLATED;
|
||||
error("INTERPOLATION selected, please report the sample to the developers");
|
||||
}
|
||||
|
||||
if (_header.compression >= 17)
|
||||
error("Invalid TrueMotion1 compression type %d", _header.compression);
|
||||
|
||||
if (_header.deltaset != _lastDeltaset || _header.vectable != _lastVectable)
|
||||
selectDeltaTables(_header.deltaset);
|
||||
|
||||
if ((_header.compression & 1) && _header.headerType)
|
||||
selVectorTable = pc_tbl2;
|
||||
else if (_header.vectable < 4)
|
||||
selVectorTable = tables[_header.vectable - 1];
|
||||
else
|
||||
error("Invalid vector table id %d", _header.vectable);
|
||||
|
||||
if (_header.deltaset != _lastDeltaset || _header.vectable != _lastVectable)
|
||||
genVectorTable16(selVectorTable);
|
||||
|
||||
// set up pointers to the other key data chunks
|
||||
_mbChangeBits = _buf + _header.headerSize;
|
||||
|
||||
if (_flags & FLAG_KEYFRAME) {
|
||||
// no change bits specified for a keyframe; only index bytes
|
||||
_indexStream = _mbChangeBits;
|
||||
} else {
|
||||
// one change bit per 4x4 block
|
||||
_indexStream = _mbChangeBits + _mbChangeBitsRowSize * (_header.ysize >> 2);
|
||||
}
|
||||
|
||||
_indexStreamSize = stream.size() - (_indexStream - _buf);
|
||||
|
||||
_lastDeltaset = _header.deltaset;
|
||||
_lastVectable = _header.vectable;
|
||||
_blockWidth = compressionTypes[_header.compression].blockWidth;
|
||||
_blockHeight = compressionTypes[_header.compression].blockHeight;
|
||||
_blockType = compressionTypes[_header.compression].blockType;
|
||||
}
|
||||
|
||||
#define GET_NEXT_INDEX() \
|
||||
do { \
|
||||
if (indexStreamIndex >= _indexStreamSize) \
|
||||
error("TrueMotion1 decoder went out of bounds"); \
|
||||
index = _indexStream[indexStreamIndex++] * 4; \
|
||||
} while (0) \
|
||||
|
||||
#define APPLY_C_PREDICTOR() \
|
||||
predictor_pair = _cPredictorTable[index].color; \
|
||||
horizPred += predictor_pair; \
|
||||
if (_cPredictorTable[index].getNextIndex) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
if (!index) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
predictor_pair = _cPredictorTable[index].color; \
|
||||
horizPred += predictor_pair * 5; \
|
||||
if (_cPredictorTable[index].getNextIndex) \
|
||||
GET_NEXT_INDEX(); \
|
||||
else \
|
||||
index++; \
|
||||
} \
|
||||
} else \
|
||||
index++
|
||||
|
||||
#define APPLY_Y_PREDICTOR() \
|
||||
predictor_pair = _yPredictorTable[index].color; \
|
||||
horizPred += predictor_pair; \
|
||||
if (_yPredictorTable[index].getNextIndex) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
if (!index) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
predictor_pair = _yPredictorTable[index].color; \
|
||||
horizPred += predictor_pair * 5; \
|
||||
if (_yPredictorTable[index].getNextIndex) \
|
||||
GET_NEXT_INDEX(); \
|
||||
else \
|
||||
index++; \
|
||||
} \
|
||||
} else \
|
||||
index++
|
||||
|
||||
#define OUTPUT_PIXEL_PAIR() \
|
||||
*currentPixelPair = *vertPred + horizPred; \
|
||||
*vertPred++ = *currentPixelPair++
|
||||
|
||||
void TrueMotion1Decoder::decode16() {
|
||||
uint32 predictor_pair;
|
||||
bool keyframe = _flags & FLAG_KEYFRAME;
|
||||
int indexStreamIndex = 0;
|
||||
|
||||
// these variables are for managing the main index stream
|
||||
int index;
|
||||
|
||||
// clean out the line buffer
|
||||
memset(_vertPred, 0, _header.xsize * 4);
|
||||
|
||||
GET_NEXT_INDEX();
|
||||
|
||||
for (int y = 0; y < _header.ysize; y++) {
|
||||
// re-init variables for the next line iteration
|
||||
uint32 horizPred = 0;
|
||||
uint32 *currentPixelPair = (uint32 *)_surface->getBasePtr(0, y);
|
||||
uint32 *vertPred = _vertPred;
|
||||
int mbChangeIndex = 0;
|
||||
byte mbChangeByte = _mbChangeBits[mbChangeIndex++];
|
||||
byte mbChangeByteMask = 1;
|
||||
|
||||
for (int pixelsLeft = _header.xsize; pixelsLeft > 0; pixelsLeft -= 4) {
|
||||
if (keyframe || (mbChangeByte & mbChangeByteMask) == 0) {
|
||||
switch (y & 3) {
|
||||
case 0:
|
||||
// if macroblock width is 2, apply C-Y-C-Y; else
|
||||
// apply C-Y-Y
|
||||
if (_blockWidth == 2) {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
} else {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
// always apply 2 Y predictors on these iterations
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
break;
|
||||
case 2:
|
||||
// this iteration might be C-Y-C-Y, Y-Y, or C-Y-Y
|
||||
// depending on the macroblock type
|
||||
if (_blockType == BLOCK_2x2) {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
} else if (_blockType == BLOCK_4x2) {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
} else {
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// skip (copy) four pixels, but reassign the horizontal
|
||||
// predictor
|
||||
*vertPred++ = *currentPixelPair++;
|
||||
horizPred = *currentPixelPair - *vertPred;
|
||||
*vertPred++ = *currentPixelPair++;
|
||||
}
|
||||
|
||||
if (!keyframe) {
|
||||
mbChangeByteMask <<= 1;
|
||||
|
||||
// next byte
|
||||
if (!mbChangeByteMask) {
|
||||
mbChangeByte = _mbChangeBits[mbChangeIndex++];
|
||||
mbChangeByteMask = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next change row
|
||||
if (((y + 1) & 3) == 0)
|
||||
_mbChangeBits += _mbChangeBitsRowSize;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *TrueMotion1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
decodeHeader(stream);
|
||||
|
||||
if (compressionTypes[_header.compression].algorithm == ALGO_NOP) {
|
||||
delete[] _buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (compressionTypes[_header.compression].algorithm == ALGO_RGB24H) {
|
||||
warning("Unhandled TrueMotion1 24bpp frame");
|
||||
delete[] _buf;
|
||||
return 0;
|
||||
} else
|
||||
decode16();
|
||||
|
||||
delete[] _buf;
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
102
image/codecs/truemotion1.h
Normal file
102
image/codecs/truemotion1.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the TrueMotion 1 decoder by Alex Beregszaszi & Mike Melanson in FFmpeg
|
||||
|
||||
#ifndef IMAGE_CODECS_TRUEMOTION1_H
|
||||
#define IMAGE_CODECS_TRUEMOTION1_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Duck TrueMotion 1 decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class TrueMotion1Decoder : public Codec {
|
||||
public:
|
||||
TrueMotion1Decoder();
|
||||
~TrueMotion1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
// Always return RGB565
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); }
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
int _mbChangeBitsRowSize;
|
||||
byte *_buf, *_mbChangeBits, *_indexStream;
|
||||
int _indexStreamSize;
|
||||
|
||||
int _flags;
|
||||
|
||||
struct PredictorTableEntry {
|
||||
uint32 color;
|
||||
bool getNextIndex;
|
||||
};
|
||||
|
||||
PredictorTableEntry _yPredictorTable[1024];
|
||||
PredictorTableEntry _cPredictorTable[1024];
|
||||
|
||||
int _blockType;
|
||||
int _blockWidth;
|
||||
int _blockHeight;
|
||||
|
||||
int16 _ydt[8];
|
||||
int16 _cdt[8];
|
||||
|
||||
int _lastDeltaset, _lastVectable;
|
||||
|
||||
uint32 *_vertPred;
|
||||
|
||||
struct {
|
||||
byte headerSize;
|
||||
byte compression;
|
||||
byte deltaset;
|
||||
byte vectable;
|
||||
uint16 ysize;
|
||||
uint16 xsize;
|
||||
uint16 checksum;
|
||||
byte version;
|
||||
byte headerType;
|
||||
byte flags;
|
||||
byte control;
|
||||
uint16 xoffset;
|
||||
uint16 yoffset;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
} _header;
|
||||
|
||||
void selectDeltaTables(int deltaTableIndex);
|
||||
void decodeHeader(Common::SeekableReadStream &stream);
|
||||
void decode16();
|
||||
int makeYdt16Entry(int p1, int p2);
|
||||
int makeCdt16Entry(int p1, int p2);
|
||||
void genVectorTable16(const byte *selVectorTable);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif // IMAGE_CODECS_TRUEMOTION1_H
|
||||
828
image/codecs/truemotion1data.h
Normal file
828
image/codecs/truemotion1data.h
Normal file
@@ -0,0 +1,828 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the TrueMotion 1 decoder by Alex Beregszaszi & Mike Melanson in FFmpeg
|
||||
// These tables were originally part of VpVision from On2
|
||||
|
||||
#ifndef IMAGE_CODECS_TRUEMOTION1DATA_H
|
||||
#define IMAGE_CODECS_TRUEMOTION1DATA_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
// Y delta tables, skinny and fat
|
||||
static const int16 ydt1[8] = { 0, -2, 2, -6, 6, -12, 12, -12 };
|
||||
static const int16 ydt2[8] = { 0, -2, 4, -6, 8, -12, 12, -12 };
|
||||
static const int16 ydt3[8] = { 4, -6, 20, -20, 46, -46, 94, -94 };
|
||||
static const int16 ydt4[8] = { 0, -4, 4, -16, 16, -36, 36, -80 };
|
||||
|
||||
// C delta tables, skinny and fat
|
||||
static const int16 cdt1[8] = { 0, -1, 1, -2, 3, -4, 5, -4 };
|
||||
static const int16 cdt2[8] = { 0, -4, 3, -16, 20, -32, 36, -32 };
|
||||
static const int16 cdt3[8] = { 0, -2, 2, -8, 8, -18, 18, -40 };
|
||||
|
||||
// all the delta tables to choose from, at all 4 delta levels
|
||||
static const int16 * const ydts[] = { ydt1, ydt2, ydt3, ydt4, NULL };
|
||||
static const int16 * const cdts[] = { cdt1, cdt1, cdt2, cdt3, NULL };
|
||||
|
||||
static const byte pc_tbl2[] = {
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x10,0x00,0x00,0x00,
|
||||
0x8,0x01,0x00,0x00,0x00,
|
||||
0x8,0x00,0x10,0x00,0x00,
|
||||
0x8,0x00,0x01,0x00,0x00,
|
||||
0x8,0x00,0x00,0x10,0x00,
|
||||
0x8,0x00,0x00,0x01,0x00,
|
||||
0x8,0x00,0x00,0x00,0x10,
|
||||
0x8,0x00,0x00,0x00,0x01,
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x10,0x00,0x00,
|
||||
0x6,0x01,0x00,0x00,
|
||||
0x6,0x00,0x10,0x00,
|
||||
0x6,0x00,0x01,0x00,
|
||||
0x6,0x00,0x00,0x01,
|
||||
0x6,0x00,0x00,0x10,
|
||||
0x6,0x00,0x00,0x02,
|
||||
0x6,0x00,0x00,0x20,
|
||||
0x6,0x20,0x10,0x00,
|
||||
0x6,0x00,0x02,0x01,
|
||||
0x6,0x00,0x20,0x10,
|
||||
0x6,0x02,0x01,0x00,
|
||||
0x6,0x11,0x00,0x00,
|
||||
0x6,0x00,0x20,0x00,
|
||||
0x6,0x00,0x02,0x00,
|
||||
0x6,0x20,0x00,0x00,
|
||||
0x6,0x01,0x10,0x00,
|
||||
0x6,0x02,0x00,0x00,
|
||||
0x6,0x01,0x00,0x02,
|
||||
0x6,0x10,0x00,0x20,
|
||||
0x6,0x00,0x01,0x02,
|
||||
0x6,0x10,0x01,0x00,
|
||||
0x6,0x00,0x10,0x20,
|
||||
0x6,0x10,0x10,0x00,
|
||||
0x6,0x10,0x00,0x01,
|
||||
0x6,0x20,0x00,0x10,
|
||||
0x6,0x02,0x00,0x01,
|
||||
0x6,0x01,0x01,0x00,
|
||||
0x6,0x01,0x00,0x10,
|
||||
0x6,0x00,0x11,0x00,
|
||||
0x6,0x10,0x00,0x02,
|
||||
0x6,0x00,0x01,0x10,
|
||||
0x6,0x00,0x00,0x11,
|
||||
0x6,0x10,0x00,0x10,
|
||||
0x6,0x01,0x00,0x01,
|
||||
0x6,0x00,0x00,0x22,
|
||||
0x6,0x02,0x01,0x01,
|
||||
0x6,0x10,0x20,0x10,
|
||||
0x6,0x01,0x02,0x01,
|
||||
0x6,0x20,0x10,0x10,
|
||||
0x6,0x01,0x00,0x20,
|
||||
0x6,0x00,0x10,0x01,
|
||||
0x6,0x21,0x10,0x00,
|
||||
0x6,0x10,0x02,0x01,
|
||||
0x6,0x12,0x01,0x00,
|
||||
0x6,0x01,0x20,0x10,
|
||||
0x6,0x01,0x02,0x00,
|
||||
0x6,0x10,0x20,0x00,
|
||||
0x6,0x00,0x10,0x02,
|
||||
0x6,0x00,0x01,0x20,
|
||||
0x6,0x00,0x02,0x21,
|
||||
0x6,0x00,0x02,0x20,
|
||||
0x6,0x00,0x00,0x12,
|
||||
0x6,0x00,0x00,0x21,
|
||||
0x6,0x20,0x11,0x00,
|
||||
0x6,0x00,0x01,0x01,
|
||||
0x6,0x11,0x10,0x00,
|
||||
0x6,0x00,0x20,0x12,
|
||||
0x6,0x00,0x20,0x11,
|
||||
0x6,0x20,0x10,0x02,
|
||||
0x6,0x02,0x01,0x20,
|
||||
0x6,0x00,0x22,0x11,
|
||||
0x6,0x00,0x10,0x10,
|
||||
0x6,0x02,0x11,0x00,
|
||||
0x6,0x00,0x21,0x10,
|
||||
0x6,0x00,0x02,0x03,
|
||||
0x6,0x20,0x10,0x01,
|
||||
0x6,0x00,0x12,0x01,
|
||||
0x4,0x11,0x00,
|
||||
0x4,0x00,0x22,
|
||||
0x4,0x20,0x00,
|
||||
0x4,0x01,0x10,
|
||||
0x4,0x02,0x20,
|
||||
0x4,0x00,0x20,
|
||||
0x4,0x02,0x00,
|
||||
0x4,0x10,0x01,
|
||||
0x4,0x00,0x11,
|
||||
0x4,0x02,0x01,
|
||||
0x4,0x02,0x21,
|
||||
0x4,0x00,0x02,
|
||||
0x4,0x20,0x02,
|
||||
0x4,0x01,0x01,
|
||||
0x4,0x10,0x10,
|
||||
0x4,0x10,0x02,
|
||||
0x4,0x22,0x00,
|
||||
0x4,0x10,0x00,
|
||||
0x4,0x01,0x00,
|
||||
0x4,0x21,0x00,
|
||||
0x4,0x12,0x00,
|
||||
0x4,0x00,0x10,
|
||||
0x4,0x20,0x12,
|
||||
0x4,0x01,0x11,
|
||||
0x4,0x00,0x01,
|
||||
0x4,0x01,0x02,
|
||||
0x4,0x11,0x02,
|
||||
0x4,0x11,0x01,
|
||||
0x4,0x10,0x20,
|
||||
0x4,0x20,0x01,
|
||||
0x4,0x22,0x11,
|
||||
0x4,0x00,0x12,
|
||||
0x4,0x20,0x10,
|
||||
0x4,0x22,0x01,
|
||||
0x4,0x01,0x20,
|
||||
0x4,0x00,0x21,
|
||||
0x4,0x10,0x11,
|
||||
0x4,0x21,0x10,
|
||||
0x4,0x10,0x22,
|
||||
0x4,0x02,0x03,
|
||||
0x4,0x12,0x01,
|
||||
0x4,0x20,0x11,
|
||||
0x4,0x11,0x10,
|
||||
0x4,0x20,0x30,
|
||||
0x4,0x11,0x20,
|
||||
0x4,0x02,0x10,
|
||||
0x4,0x22,0x10,
|
||||
0x4,0x11,0x11,
|
||||
0x4,0x30,0x20,
|
||||
0x4,0x30,0x00,
|
||||
0x4,0x01,0x22,
|
||||
0x4,0x01,0x12,
|
||||
0x4,0x02,0x11,
|
||||
0x4,0x03,0x02,
|
||||
0x4,0x03,0x00,
|
||||
0x4,0x10,0x21,
|
||||
0x4,0x12,0x20,
|
||||
0x4,0x00,0x00,
|
||||
0x4,0x12,0x21,
|
||||
0x4,0x21,0x11,
|
||||
0x4,0x02,0x22,
|
||||
0x4,0x10,0x12,
|
||||
0x4,0x31,0x00,
|
||||
0x4,0x20,0x20,
|
||||
0x4,0x00,0x03,
|
||||
0x4,0x02,0x02,
|
||||
0x4,0x22,0x20,
|
||||
0x4,0x01,0x21,
|
||||
0x4,0x21,0x02,
|
||||
0x4,0x21,0x12,
|
||||
0x4,0x11,0x22,
|
||||
0x4,0x00,0x30,
|
||||
0x4,0x12,0x11,
|
||||
0x4,0x20,0x22,
|
||||
0x4,0x31,0x20,
|
||||
0x4,0x21,0x30,
|
||||
0x4,0x22,0x02,
|
||||
0x4,0x22,0x22,
|
||||
0x4,0x20,0x31,
|
||||
0x4,0x13,0x02,
|
||||
0x4,0x03,0x10,
|
||||
0x4,0x11,0x12,
|
||||
0x4,0x00,0x13,
|
||||
0x4,0x21,0x01,
|
||||
0x4,0x12,0x03,
|
||||
0x4,0x13,0x00,
|
||||
0x4,0x13,0x10,
|
||||
0x4,0x02,0x13,
|
||||
0x4,0x30,0x01,
|
||||
0x4,0x12,0x10,
|
||||
0x4,0x22,0x13,
|
||||
0x4,0x03,0x12,
|
||||
0x4,0x31,0x01,
|
||||
0x4,0x30,0x22,
|
||||
0x4,0x00,0x31,
|
||||
0x4,0x01,0x31,
|
||||
0x4,0x02,0x23,
|
||||
0x4,0x01,0x30,
|
||||
0x4,0x11,0x21,
|
||||
0x4,0x22,0x21,
|
||||
0x4,0x01,0x13,
|
||||
0x4,0x10,0x03,
|
||||
0x4,0x22,0x03,
|
||||
0x4,0x30,0x21,
|
||||
0x4,0x21,0x31,
|
||||
0x4,0x33,0x00,
|
||||
0x4,0x13,0x12,
|
||||
0x4,0x11,0x31,
|
||||
0x4,0x30,0x02,
|
||||
0x4,0x12,0x02,
|
||||
0x4,0x11,0x13,
|
||||
0x4,0x12,0x22,
|
||||
0x4,0x20,0x32,
|
||||
0x4,0x10,0x13,
|
||||
0x4,0x22,0x31,
|
||||
0x4,0x21,0x20,
|
||||
0x4,0x01,0x33,
|
||||
0x4,0x33,0x10,
|
||||
0x4,0x20,0x13,
|
||||
0x4,0x31,0x22,
|
||||
0x4,0x13,0x30,
|
||||
0x4,0x01,0x03,
|
||||
0x4,0x11,0x33,
|
||||
0x4,0x20,0x21,
|
||||
0x4,0x13,0x31,
|
||||
0x4,0x03,0x22,
|
||||
0x4,0x31,0x02,
|
||||
0x4,0x00,0x24,
|
||||
0x2,0x00,
|
||||
0x2,0x10,
|
||||
0x2,0x20,
|
||||
0x2,0x30,
|
||||
0x2,0x40,
|
||||
0x2,0x50,
|
||||
0x2,0x60,
|
||||
0x2,0x01,
|
||||
0x2,0x11,
|
||||
0x2,0x21,
|
||||
0x2,0x31,
|
||||
0x2,0x41,
|
||||
0x2,0x51,
|
||||
0x2,0x61,
|
||||
0x2,0x02,
|
||||
0x2,0x12,
|
||||
0x2,0x22,
|
||||
0x2,0x32,
|
||||
0x2,0x42,
|
||||
0x2,0x52,
|
||||
0x2,0x62,
|
||||
0x2,0x03,
|
||||
0x2,0x13,
|
||||
0x2,0x23,
|
||||
0x2,0x33,
|
||||
0x2,0x43,
|
||||
0x2,0x53,
|
||||
0x2,0x63,
|
||||
0x2,0x04,
|
||||
0x2,0x14,
|
||||
0x2,0x24,
|
||||
0x2,0x34,
|
||||
0x2,0x44,
|
||||
0x2,0x54,
|
||||
0x2,0x64,
|
||||
0x2,0x05,
|
||||
0x2,0x15,
|
||||
0x2,0x25,
|
||||
0x2,0x35,
|
||||
0x2,0x45,
|
||||
0x2,0x55,
|
||||
0x2,0x65,
|
||||
0x2,0x06,
|
||||
0x2,0x16,
|
||||
0x2,0x26,
|
||||
0x2,0x36,
|
||||
0x2,0x46,
|
||||
0x2,0x56,
|
||||
0x2,0x66
|
||||
};
|
||||
|
||||
static const byte pc_tbl3[] = {
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x00,0x00,0x01,
|
||||
0x6,0x00,0x00,0x10,
|
||||
0x6,0x00,0x00,0x11,
|
||||
0x6,0x00,0x01,0x00,
|
||||
0x6,0x00,0x01,0x01,
|
||||
0x6,0x00,0x01,0x10,
|
||||
0x6,0x00,0x01,0x11,
|
||||
0x6,0x00,0x10,0x00,
|
||||
0x6,0x00,0x10,0x01,
|
||||
0x6,0x00,0x10,0x10,
|
||||
0x6,0x00,0x10,0x11,
|
||||
0x6,0x00,0x11,0x00,
|
||||
0x6,0x00,0x11,0x01,
|
||||
0x6,0x00,0x11,0x10,
|
||||
0x6,0x00,0x11,0x11,
|
||||
0x6,0x01,0x00,0x00,
|
||||
0x6,0x01,0x00,0x01,
|
||||
0x6,0x01,0x00,0x10,
|
||||
0x6,0x01,0x00,0x11,
|
||||
0x6,0x01,0x01,0x00,
|
||||
0x6,0x01,0x01,0x01,
|
||||
0x6,0x01,0x01,0x10,
|
||||
0x6,0x01,0x01,0x11,
|
||||
0x6,0x01,0x10,0x00,
|
||||
0x6,0x01,0x10,0x01,
|
||||
0x6,0x01,0x10,0x10,
|
||||
0x6,0x01,0x10,0x11,
|
||||
0x6,0x01,0x11,0x00,
|
||||
0x6,0x01,0x11,0x01,
|
||||
0x6,0x01,0x11,0x10,
|
||||
0x6,0x01,0x11,0x11,
|
||||
0x6,0x10,0x00,0x00,
|
||||
0x6,0x10,0x00,0x01,
|
||||
0x6,0x10,0x00,0x10,
|
||||
0x6,0x10,0x00,0x11,
|
||||
0x6,0x10,0x01,0x00,
|
||||
0x6,0x10,0x01,0x01,
|
||||
0x6,0x10,0x01,0x10,
|
||||
0x6,0x10,0x01,0x11,
|
||||
0x6,0x10,0x10,0x00,
|
||||
0x6,0x10,0x10,0x01,
|
||||
0x6,0x10,0x10,0x10,
|
||||
0x6,0x10,0x10,0x11,
|
||||
0x6,0x10,0x11,0x00,
|
||||
0x6,0x10,0x11,0x01,
|
||||
0x6,0x10,0x11,0x10,
|
||||
0x6,0x10,0x11,0x11,
|
||||
0x6,0x11,0x00,0x00,
|
||||
0x6,0x11,0x00,0x01,
|
||||
0x6,0x11,0x00,0x10,
|
||||
0x6,0x11,0x00,0x11,
|
||||
0x6,0x11,0x01,0x00,
|
||||
0x6,0x11,0x01,0x01,
|
||||
0x6,0x11,0x01,0x10,
|
||||
0x6,0x11,0x01,0x11,
|
||||
0x6,0x11,0x10,0x00,
|
||||
0x6,0x11,0x10,0x01,
|
||||
0x6,0x11,0x10,0x10,
|
||||
0x6,0x11,0x10,0x11,
|
||||
0x6,0x11,0x11,0x00,
|
||||
0x6,0x11,0x11,0x01,
|
||||
0x6,0x11,0x11,0x10,
|
||||
0x4,0x00,0x00,
|
||||
0x4,0x00,0x01,
|
||||
0x4,0x00,0x02,
|
||||
0x4,0x00,0x03,
|
||||
0x4,0x00,0x10,
|
||||
0x4,0x00,0x11,
|
||||
0x4,0x00,0x12,
|
||||
0x4,0x00,0x13,
|
||||
0x4,0x00,0x20,
|
||||
0x4,0x00,0x21,
|
||||
0x4,0x00,0x22,
|
||||
0x4,0x00,0x23,
|
||||
0x4,0x00,0x30,
|
||||
0x4,0x00,0x31,
|
||||
0x4,0x00,0x32,
|
||||
0x4,0x00,0x33,
|
||||
0x4,0x01,0x00,
|
||||
0x4,0x01,0x01,
|
||||
0x4,0x01,0x02,
|
||||
0x4,0x01,0x03,
|
||||
0x4,0x01,0x10,
|
||||
0x4,0x01,0x11,
|
||||
0x4,0x01,0x12,
|
||||
0x4,0x01,0x13,
|
||||
0x4,0x01,0x20,
|
||||
0x4,0x01,0x21,
|
||||
0x4,0x01,0x22,
|
||||
0x4,0x01,0x23,
|
||||
0x4,0x01,0x30,
|
||||
0x4,0x01,0x31,
|
||||
0x4,0x01,0x32,
|
||||
0x4,0x01,0x33,
|
||||
0x4,0x02,0x00,
|
||||
0x4,0x02,0x01,
|
||||
0x4,0x02,0x02,
|
||||
0x4,0x02,0x03,
|
||||
0x4,0x02,0x10,
|
||||
0x4,0x02,0x11,
|
||||
0x4,0x02,0x12,
|
||||
0x4,0x02,0x13,
|
||||
0x4,0x02,0x20,
|
||||
0x4,0x02,0x21,
|
||||
0x4,0x02,0x22,
|
||||
0x4,0x02,0x23,
|
||||
0x4,0x02,0x30,
|
||||
0x4,0x02,0x31,
|
||||
0x4,0x02,0x32,
|
||||
0x4,0x02,0x33,
|
||||
0x4,0x03,0x00,
|
||||
0x4,0x03,0x01,
|
||||
0x4,0x03,0x02,
|
||||
0x4,0x03,0x03,
|
||||
0x4,0x03,0x10,
|
||||
0x4,0x03,0x11,
|
||||
0x4,0x03,0x12,
|
||||
0x4,0x03,0x13,
|
||||
0x4,0x03,0x20,
|
||||
0x4,0x03,0x21,
|
||||
0x4,0x03,0x22,
|
||||
0x4,0x03,0x23,
|
||||
0x4,0x03,0x30,
|
||||
0x4,0x03,0x31,
|
||||
0x4,0x03,0x32,
|
||||
0x4,0x03,0x33,
|
||||
0x4,0x10,0x00,
|
||||
0x4,0x10,0x01,
|
||||
0x4,0x10,0x02,
|
||||
0x4,0x10,0x03,
|
||||
0x4,0x10,0x10,
|
||||
0x4,0x10,0x11,
|
||||
0x4,0x10,0x12,
|
||||
0x4,0x10,0x13,
|
||||
0x4,0x10,0x20,
|
||||
0x4,0x10,0x21,
|
||||
0x4,0x10,0x22,
|
||||
0x4,0x10,0x23,
|
||||
0x4,0x10,0x30,
|
||||
0x4,0x10,0x31,
|
||||
0x4,0x10,0x32,
|
||||
0x4,0x10,0x33,
|
||||
0x4,0x11,0x00,
|
||||
0x4,0x11,0x01,
|
||||
0x4,0x11,0x02,
|
||||
0x4,0x11,0x03,
|
||||
0x4,0x11,0x10,
|
||||
0x4,0x11,0x11,
|
||||
0x4,0x11,0x12,
|
||||
0x4,0x11,0x13,
|
||||
0x4,0x11,0x20,
|
||||
0x4,0x11,0x21,
|
||||
0x4,0x11,0x22,
|
||||
0x4,0x11,0x23,
|
||||
0x4,0x11,0x30,
|
||||
0x4,0x11,0x31,
|
||||
0x4,0x11,0x32,
|
||||
0x4,0x11,0x33,
|
||||
0x4,0x12,0x00,
|
||||
0x4,0x12,0x01,
|
||||
0x4,0x12,0x02,
|
||||
0x4,0x12,0x03,
|
||||
0x4,0x12,0x10,
|
||||
0x4,0x12,0x11,
|
||||
0x4,0x12,0x12,
|
||||
0x4,0x12,0x13,
|
||||
0x4,0x12,0x20,
|
||||
0x4,0x12,0x21,
|
||||
0x4,0x12,0x22,
|
||||
0x4,0x12,0x23,
|
||||
0x4,0x12,0x30,
|
||||
0x4,0x12,0x31,
|
||||
0x4,0x12,0x32,
|
||||
0x4,0x12,0x33,
|
||||
0x4,0x13,0x00,
|
||||
0x4,0x13,0x01,
|
||||
0x4,0x13,0x02,
|
||||
0x4,0x13,0x03,
|
||||
0x4,0x13,0x10,
|
||||
0x4,0x13,0x11,
|
||||
0x4,0x13,0x12,
|
||||
0x4,0x13,0x13,
|
||||
0x4,0x13,0x20,
|
||||
0x4,0x13,0x21,
|
||||
0x4,0x13,0x22,
|
||||
0x4,0x13,0x23,
|
||||
0x4,0x13,0x30,
|
||||
0x4,0x13,0x31,
|
||||
0x4,0x13,0x32,
|
||||
0x4,0x13,0x33,
|
||||
0x2,0x00,
|
||||
0x2,0x10,
|
||||
0x2,0x20,
|
||||
0x2,0x30,
|
||||
0x2,0x40,
|
||||
0x2,0x50,
|
||||
0x2,0x60,
|
||||
0x2,0x70,
|
||||
0x2,0x01,
|
||||
0x2,0x11,
|
||||
0x2,0x21,
|
||||
0x2,0x31,
|
||||
0x2,0x41,
|
||||
0x2,0x51,
|
||||
0x2,0x61,
|
||||
0x2,0x71,
|
||||
0x2,0x02,
|
||||
0x2,0x12,
|
||||
0x2,0x22,
|
||||
0x2,0x32,
|
||||
0x2,0x42,
|
||||
0x2,0x52,
|
||||
0x2,0x62,
|
||||
0x2,0x72,
|
||||
0x2,0x03,
|
||||
0x2,0x13,
|
||||
0x2,0x23,
|
||||
0x2,0x33,
|
||||
0x2,0x43,
|
||||
0x2,0x53,
|
||||
0x2,0x63,
|
||||
0x2,0x73,
|
||||
0x2,0x04,
|
||||
0x2,0x14,
|
||||
0x2,0x24,
|
||||
0x2,0x34,
|
||||
0x2,0x44,
|
||||
0x2,0x54,
|
||||
0x2,0x64,
|
||||
0x2,0x74,
|
||||
0x2,0x05,
|
||||
0x2,0x15,
|
||||
0x2,0x25,
|
||||
0x2,0x35,
|
||||
0x2,0x45,
|
||||
0x2,0x55,
|
||||
0x2,0x65,
|
||||
0x2,0x75,
|
||||
0x2,0x06,
|
||||
0x2,0x16,
|
||||
0x2,0x26,
|
||||
0x2,0x36,
|
||||
0x2,0x46,
|
||||
0x2,0x56,
|
||||
0x2,0x66,
|
||||
0x2,0x76,
|
||||
0x2,0x07,
|
||||
0x2,0x17,
|
||||
0x2,0x27,
|
||||
0x2,0x37,
|
||||
0x2,0x47,
|
||||
0x2,0x57,
|
||||
0x2,0x67,
|
||||
0x2,0x77
|
||||
};
|
||||
|
||||
static const byte pc_tbl4[] = {
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x20,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x01,
|
||||
0x8,0x10,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x02,
|
||||
0x8,0x01,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x10,
|
||||
0x8,0x02,0x00,0x00,0x00,
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x20,0x00,0x00,
|
||||
0x6,0x00,0x00,0x01,
|
||||
0x6,0x10,0x00,0x00,
|
||||
0x6,0x00,0x00,0x02,
|
||||
0x6,0x00,0x10,0x00,
|
||||
0x6,0x00,0x20,0x00,
|
||||
0x6,0x00,0x02,0x00,
|
||||
0x6,0x00,0x01,0x00,
|
||||
0x6,0x01,0x00,0x00,
|
||||
0x6,0x00,0x00,0x20,
|
||||
0x6,0x02,0x00,0x00,
|
||||
0x6,0x00,0x00,0x10,
|
||||
0x6,0x10,0x00,0x20,
|
||||
0x6,0x01,0x00,0x02,
|
||||
0x6,0x20,0x00,0x10,
|
||||
0x6,0x02,0x00,0x01,
|
||||
0x6,0x20,0x10,0x00,
|
||||
0x6,0x00,0x12,0x00,
|
||||
0x6,0x00,0x02,0x01,
|
||||
0x6,0x02,0x01,0x00,
|
||||
0x6,0x00,0x21,0x00,
|
||||
0x6,0x00,0x01,0x02,
|
||||
0x6,0x00,0x20,0x10,
|
||||
0x6,0x00,0x00,0x21,
|
||||
0x6,0x00,0x00,0x12,
|
||||
0x6,0x00,0x01,0x20,
|
||||
0x6,0x12,0x00,0x00,
|
||||
0x6,0x00,0x10,0x20,
|
||||
0x6,0x01,0x20,0x00,
|
||||
0x6,0x02,0x10,0x00,
|
||||
0x6,0x10,0x20,0x00,
|
||||
0x6,0x01,0x02,0x00,
|
||||
0x6,0x21,0x00,0x00,
|
||||
0x6,0x00,0x02,0x10,
|
||||
0x6,0x20,0x01,0x00,
|
||||
0x6,0x00,0x22,0x00,
|
||||
0x6,0x10,0x02,0x00,
|
||||
0x6,0x00,0x10,0x02,
|
||||
0x6,0x11,0x00,0x00,
|
||||
0x6,0x00,0x11,0x00,
|
||||
0x6,0x22,0x00,0x00,
|
||||
0x6,0x20,0x00,0x02,
|
||||
0x6,0x10,0x00,0x01,
|
||||
0x6,0x00,0x20,0x01,
|
||||
0x6,0x02,0x20,0x00,
|
||||
0x6,0x01,0x10,0x00,
|
||||
0x6,0x01,0x00,0x20,
|
||||
0x6,0x00,0x20,0x02,
|
||||
0x6,0x01,0x20,0x02,
|
||||
0x6,0x10,0x01,0x00,
|
||||
0x6,0x02,0x00,0x10,
|
||||
0x6,0x00,0x10,0x01,
|
||||
0x6,0x10,0x01,0x20,
|
||||
0x6,0x20,0x02,0x10,
|
||||
0x6,0x00,0x00,0x22,
|
||||
0x6,0x10,0x00,0x02,
|
||||
0x6,0x00,0x02,0x20,
|
||||
0x6,0x20,0x02,0x00,
|
||||
0x6,0x00,0x00,0x11,
|
||||
0x6,0x02,0x10,0x01,
|
||||
0x6,0x00,0x01,0x10,
|
||||
0x6,0x00,0x02,0x11,
|
||||
0x4,0x01,0x02,
|
||||
0x4,0x02,0x01,
|
||||
0x4,0x01,0x00,
|
||||
0x4,0x10,0x20,
|
||||
0x4,0x20,0x10,
|
||||
0x4,0x20,0x00,
|
||||
0x4,0x11,0x00,
|
||||
0x4,0x02,0x00,
|
||||
0x4,0x12,0x00,
|
||||
0x4,0x00,0x21,
|
||||
0x4,0x22,0x00,
|
||||
0x4,0x00,0x12,
|
||||
0x4,0x21,0x00,
|
||||
0x4,0x02,0x11,
|
||||
0x4,0x00,0x01,
|
||||
0x4,0x10,0x02,
|
||||
0x4,0x02,0x20,
|
||||
0x4,0x20,0x11,
|
||||
0x4,0x01,0x10,
|
||||
0x4,0x21,0x10,
|
||||
0x4,0x10,0x00,
|
||||
0x4,0x10,0x22,
|
||||
0x4,0x20,0x20,
|
||||
0x4,0x00,0x22,
|
||||
0x4,0x01,0x22,
|
||||
0x4,0x20,0x01,
|
||||
0x4,0x02,0x02,
|
||||
0x4,0x00,0x20,
|
||||
0x4,0x00,0x10,
|
||||
0x4,0x00,0x11,
|
||||
0x4,0x22,0x01,
|
||||
0x4,0x11,0x20,
|
||||
0x4,0x12,0x01,
|
||||
0x4,0x12,0x20,
|
||||
0x4,0x11,0x02,
|
||||
0x4,0x10,0x10,
|
||||
0x4,0x01,0x01,
|
||||
0x4,0x02,0x21,
|
||||
0x4,0x20,0x12,
|
||||
0x4,0x01,0x12,
|
||||
0x4,0x22,0x11,
|
||||
0x4,0x21,0x12,
|
||||
0x4,0x22,0x10,
|
||||
0x4,0x21,0x02,
|
||||
0x4,0x20,0x02,
|
||||
0x4,0x10,0x01,
|
||||
0x4,0x00,0x02,
|
||||
0x4,0x10,0x21,
|
||||
0x4,0x01,0x20,
|
||||
0x4,0x11,0x22,
|
||||
0x4,0x12,0x21,
|
||||
0x4,0x22,0x20,
|
||||
0x4,0x02,0x10,
|
||||
0x4,0x02,0x22,
|
||||
0x4,0x11,0x10,
|
||||
0x4,0x22,0x02,
|
||||
0x4,0x20,0x21,
|
||||
0x4,0x01,0x11,
|
||||
0x4,0x11,0x01,
|
||||
0x4,0x10,0x12,
|
||||
0x4,0x02,0x12,
|
||||
0x4,0x20,0x22,
|
||||
0x4,0x21,0x20,
|
||||
0x4,0x01,0x21,
|
||||
0x4,0x12,0x02,
|
||||
0x4,0x21,0x11,
|
||||
0x4,0x12,0x22,
|
||||
0x4,0x12,0x10,
|
||||
0x4,0x22,0x21,
|
||||
0x4,0x10,0x11,
|
||||
0x4,0x21,0x01,
|
||||
0x4,0x11,0x12,
|
||||
0x4,0x12,0x11,
|
||||
0x4,0x66,0x66,
|
||||
0x4,0x22,0x22,
|
||||
0x4,0x11,0x21,
|
||||
0x4,0x11,0x11,
|
||||
0x4,0x21,0x22,
|
||||
0x4,0x00,0x00,
|
||||
0x4,0x22,0x12,
|
||||
0x4,0x12,0x12,
|
||||
0x4,0x21,0x21,
|
||||
0x4,0x42,0x00,
|
||||
0x4,0x00,0x04,
|
||||
0x4,0x40,0x00,
|
||||
0x4,0x30,0x00,
|
||||
0x4,0x31,0x00,
|
||||
0x4,0x00,0x03,
|
||||
0x4,0x00,0x14,
|
||||
0x4,0x00,0x13,
|
||||
0x4,0x01,0x24,
|
||||
0x4,0x20,0x13,
|
||||
0x4,0x01,0x42,
|
||||
0x4,0x14,0x20,
|
||||
0x4,0x42,0x02,
|
||||
0x4,0x13,0x00,
|
||||
0x4,0x00,0x24,
|
||||
0x4,0x31,0x20,
|
||||
0x4,0x22,0x13,
|
||||
0x4,0x11,0x24,
|
||||
0x4,0x12,0x66,
|
||||
0x4,0x30,0x01,
|
||||
0x4,0x02,0x13,
|
||||
0x4,0x12,0x42,
|
||||
0x4,0x40,0x10,
|
||||
0x4,0x40,0x02,
|
||||
0x4,0x01,0x04,
|
||||
0x4,0x24,0x00,
|
||||
0x4,0x42,0x10,
|
||||
0x4,0x21,0x13,
|
||||
0x4,0x13,0x12,
|
||||
0x4,0x31,0x21,
|
||||
0x4,0x21,0x24,
|
||||
0x4,0x00,0x40,
|
||||
0x4,0x10,0x24,
|
||||
0x4,0x10,0x42,
|
||||
0x4,0x32,0x01,
|
||||
0x4,0x11,0x42,
|
||||
0x4,0x20,0x31,
|
||||
0x4,0x12,0x40,
|
||||
0x2,0x00,
|
||||
0x2,0x10,
|
||||
0x2,0x20,
|
||||
0x2,0x30,
|
||||
0x2,0x40,
|
||||
0x2,0x50,
|
||||
0x2,0x60,
|
||||
0x2,0x70,
|
||||
0x2,0x01,
|
||||
0x2,0x11,
|
||||
0x2,0x21,
|
||||
0x2,0x31,
|
||||
0x2,0x41,
|
||||
0x2,0x51,
|
||||
0x2,0x61,
|
||||
0x2,0x71,
|
||||
0x2,0x02,
|
||||
0x2,0x12,
|
||||
0x2,0x22,
|
||||
0x2,0x32,
|
||||
0x2,0x42,
|
||||
0x2,0x52,
|
||||
0x2,0x62,
|
||||
0x2,0x72,
|
||||
0x2,0x03,
|
||||
0x2,0x13,
|
||||
0x2,0x23,
|
||||
0x2,0x33,
|
||||
0x2,0x43,
|
||||
0x2,0x53,
|
||||
0x2,0x63,
|
||||
0x2,0x73,
|
||||
0x2,0x04,
|
||||
0x2,0x14,
|
||||
0x2,0x24,
|
||||
0x2,0x34,
|
||||
0x2,0x44,
|
||||
0x2,0x54,
|
||||
0x2,0x64,
|
||||
0x2,0x74,
|
||||
0x2,0x05,
|
||||
0x2,0x15,
|
||||
0x2,0x25,
|
||||
0x2,0x35,
|
||||
0x2,0x45,
|
||||
0x2,0x55,
|
||||
0x2,0x65,
|
||||
0x2,0x75,
|
||||
0x2,0x06,
|
||||
0x2,0x16,
|
||||
0x2,0x26,
|
||||
0x2,0x36,
|
||||
0x2,0x46,
|
||||
0x2,0x56,
|
||||
0x2,0x66,
|
||||
0x2,0x76,
|
||||
0x2,0x07,
|
||||
0x2,0x17,
|
||||
0x2,0x27,
|
||||
0x2,0x37,
|
||||
0x2,0x47,
|
||||
0x2,0x57,
|
||||
0x2,0x67,
|
||||
0x2,0x77
|
||||
};
|
||||
|
||||
static const byte * const tables[] = { pc_tbl2, pc_tbl3, pc_tbl4 };
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
502
image/codecs/xan.cpp
Normal file
502
image/codecs/xan.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Xan image decoder. (fourcc Xxan)
|
||||
*
|
||||
* Used by Crusader: No Regret AVI files
|
||||
*
|
||||
* This code was created based on the multimedia wiki:
|
||||
* https://wiki.multimedia.cx/index.php/Origin_Xan_Codec
|
||||
* and ffmpeg's libavcodec/xxan.c.
|
||||
* The ffmpeg code is LGPL2 licensed and Copyright (C) 2011
|
||||
* Konstantin Shishkov based on work by Mike Melanson.
|
||||
*
|
||||
* A similar format is used in Wing Commander III (although not in an AVI
|
||||
* container) and IV.
|
||||
*/
|
||||
|
||||
#include "image/codecs/xan.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/bitstream.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
static const int SCRATCH_SPARE = 256;
|
||||
|
||||
XanDecoder::XanDecoder(int width, int height, int bitsPerPixel) : Codec(),
|
||||
_width(width), _height(height), _wc4Mode(false), _surface(nullptr) {
|
||||
assert(bitsPerPixel == 16);
|
||||
if (bitsPerPixel != 16)
|
||||
error("XanDecoder: BPP must be 16 not %d", bitsPerPixel);
|
||||
if (width % 2)
|
||||
error("XanDecoder: width must be even, not %d", width);
|
||||
_scratchbuf = new uint8[_width * _height + SCRATCH_SPARE]();
|
||||
_lumabuf = new uint8[_width * _height]();
|
||||
_ybuf = new uint8[_width * _height]();
|
||||
_ubuf = new uint8[_width * _height / 2]();
|
||||
_vbuf = new uint8[_width * _height / 2]();
|
||||
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
}
|
||||
|
||||
XanDecoder::~XanDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
_surface = nullptr;
|
||||
}
|
||||
delete [] _scratchbuf;
|
||||
delete [] _lumabuf;
|
||||
delete [] _ybuf;
|
||||
delete [] _ubuf;
|
||||
delete [] _vbuf;
|
||||
}
|
||||
|
||||
const Graphics::Surface *XanDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
uint32 frametype = stream.readUint32LE();
|
||||
if (frametype > 1) {
|
||||
error("Xan frametype should be 0 or 1, got %d", frametype);
|
||||
}
|
||||
|
||||
if (frametype == 0) {
|
||||
decodeFrameType0(stream);
|
||||
} else {
|
||||
decodeFrameType1(stream);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
// An unoptimized version of the one from libavutil, but works fine
|
||||
static void _memcpy_backptr(uint8 *dst, int back, int cnt) {
|
||||
if (cnt <= 0 || back <= 0)
|
||||
return;
|
||||
|
||||
const uint8 *src = &dst[-back];
|
||||
if (back == 1) {
|
||||
uint8 val = *src;
|
||||
memset(dst, val, cnt);
|
||||
} else {
|
||||
int blocklen = back;
|
||||
while (cnt > blocklen) {
|
||||
memcpy(dst, src, blocklen);
|
||||
dst += blocklen;
|
||||
cnt -= blocklen;
|
||||
blocklen <<= 1;
|
||||
}
|
||||
memcpy(dst, src, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack using the WC3 algorithm
|
||||
static int _unpack(Common::SeekableReadStream &stream, uint8 *dest, const int dest_len) {
|
||||
const uint8 *orig_dest = dest;
|
||||
const uint8 *dest_end = dest + dest_len;
|
||||
|
||||
memset(dest, 0, dest_len);
|
||||
|
||||
while (dest < dest_end) {
|
||||
if (stream.eos())
|
||||
return -1;
|
||||
|
||||
const uint8 opcode = stream.readByte();
|
||||
|
||||
if (opcode < 0xe0) {
|
||||
int readsize, copysize, back;
|
||||
if ((opcode & 0x80) == 0) {
|
||||
readsize = opcode & 3;
|
||||
back = ((opcode & 0x60) << 3) + stream.readByte() + 1;
|
||||
copysize = ((opcode & 0x1c) >> 2) + 3;
|
||||
} else if ((opcode & 0x40) == 0) {
|
||||
const uint8 b = stream.readByte();
|
||||
readsize = b >> 6;
|
||||
back = ((b & 0x3f) << 8) + stream.readByte() + 1;
|
||||
copysize = (opcode & 0x3f) + 4;
|
||||
} else {
|
||||
readsize = opcode & 3;
|
||||
back = ((opcode & 0x10) << 12) + stream.readUint16BE() + 1;
|
||||
copysize = ((opcode & 0x0c) << 6) + stream.readByte() + 5;
|
||||
if (readsize + copysize > dest_end - dest)
|
||||
break;
|
||||
}
|
||||
if (dest + readsize + copysize > dest_end ||
|
||||
dest - orig_dest + readsize < back)
|
||||
return -1;
|
||||
stream.read(dest, readsize);
|
||||
dest += readsize;
|
||||
_memcpy_backptr(dest, back, copysize);
|
||||
dest += copysize;
|
||||
} else {
|
||||
bool finish = (opcode >= 0xfc);
|
||||
|
||||
int readsize = finish ? opcode & 3 : ((opcode & 0x1f) * 4) + 4;
|
||||
if (dest + readsize > dest_end)
|
||||
return -1;
|
||||
stream.read(dest, readsize);
|
||||
dest += readsize;
|
||||
if (finish)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dest - orig_dest;
|
||||
}
|
||||
|
||||
|
||||
bool XanDecoder::decodeChroma(Common::SeekableReadStream &stream, const int chroma_off) {
|
||||
if (!chroma_off)
|
||||
return 0;
|
||||
|
||||
if (chroma_off + 4 >= stream.size() - stream.pos()) {
|
||||
warning("Invalid chroma block position");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.seek(chroma_off + 4);
|
||||
|
||||
const int mode = stream.readUint16LE();
|
||||
const int table_start = stream.pos();
|
||||
// 2 bytes per table entry
|
||||
const int table_size = stream.readUint16LE() * 2;
|
||||
|
||||
if (mode > 1) {
|
||||
warning("Unexpected chroma mode %d", mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (table_size >= stream.size() - stream.pos()) {
|
||||
warning("Invalid chroma block offset");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.skip(table_size);
|
||||
const int dec_size = _unpack(stream, _scratchbuf, _width * _height);
|
||||
if (dec_size < 0) {
|
||||
warning("Chroma unpacking failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int pitch = _width / 2;
|
||||
|
||||
uint8 *U = _ubuf;
|
||||
uint8 *V = _vbuf;
|
||||
const uint8 *src = _scratchbuf;
|
||||
const uint8 *src_end = src + dec_size;
|
||||
if (mode) {
|
||||
// YUV420 frame
|
||||
for (int y = 0; y < _height / 2; y++) {
|
||||
for (int x = 0; x < pitch; x++) {
|
||||
if (src >= src_end)
|
||||
return true;
|
||||
int toff = *src++ * 2;
|
||||
if (toff) {
|
||||
if (toff > table_size)
|
||||
return false;
|
||||
const int pos = stream.pos();
|
||||
stream.seek(table_start + toff);
|
||||
uint8 uval, vval;
|
||||
if (_wc4Mode) {
|
||||
uint16 val = stream.readUint16LE();
|
||||
uval = (val >> 3) & 0xF8;
|
||||
vval = (val >> 8) & 0xF8;
|
||||
} else {
|
||||
uval = stream.readByte();
|
||||
vval = stream.readByte();
|
||||
}
|
||||
uval = uval | uval >> 5;
|
||||
vval = vval | vval >> 5;
|
||||
stream.seek(pos);
|
||||
U[x] = uval;
|
||||
V[x] = vval;
|
||||
}
|
||||
}
|
||||
U += pitch;
|
||||
V += pitch;
|
||||
}
|
||||
if (_height % 1) {
|
||||
memcpy(U, U - pitch, pitch);
|
||||
memcpy(V, V - pitch, pitch);
|
||||
}
|
||||
} else {
|
||||
// YUV410 frame - expand out U and V components
|
||||
uint8 *U2 = U + pitch;
|
||||
uint8 *V2 = V + pitch;
|
||||
|
||||
for (int y = 0; y < _height / 4; y++) {
|
||||
for (int x = 0; x < pitch; x += 2) {
|
||||
if (src >= src_end)
|
||||
return true;
|
||||
int toff = *src++ * 2;
|
||||
if (toff) {
|
||||
if (toff > table_size)
|
||||
return false;
|
||||
const int pos = stream.pos();
|
||||
stream.seek(table_start + toff);
|
||||
uint8 uval, vval;
|
||||
if (_wc4Mode) {
|
||||
uint16 val = stream.readUint16LE();
|
||||
uval = (val >> 3) & 0xF8;
|
||||
vval = (val >> 8) & 0xF8;
|
||||
} else {
|
||||
uval = stream.readByte();
|
||||
vval = stream.readByte();
|
||||
}
|
||||
uval = uval | uval >> 5;
|
||||
vval = vval | vval >> 5;
|
||||
stream.seek(pos);
|
||||
U[x] = U[x + 1] = U2[x] = U2[x + 1] = uval;
|
||||
V[x] = V[x + 1] = V2[x] = V2[x + 1] = vval;
|
||||
}
|
||||
}
|
||||
U += pitch * 2;
|
||||
V += pitch * 2;
|
||||
U2 += pitch * 2;
|
||||
V2 += pitch * 2;
|
||||
}
|
||||
if (_height % 4) {
|
||||
int lines = ((_height + 1) / 2) - (_height / 4) * 2;
|
||||
|
||||
memcpy(U, U - lines * pitch, lines * pitch);
|
||||
memcpy(V, V - lines * pitch, lines * pitch);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XanDecoder::decodeFrameType0(Common::SeekableReadStream &stream) {
|
||||
const uint32 chroma_offset = stream.readUint32LE();
|
||||
const uint32 refines_offset = stream.readUint32LE();
|
||||
const uint32 luma_offset = stream.pos();
|
||||
const uint32 nbytes = static_cast<uint32>(stream.size());
|
||||
|
||||
if (chroma_offset > nbytes || refines_offset > nbytes) {
|
||||
error("invalid frame type 0 offsets");
|
||||
}
|
||||
|
||||
if (!decodeChroma(stream, chroma_offset)) {
|
||||
warning("xxan chrome decode failed frame type 0");
|
||||
return;
|
||||
}
|
||||
|
||||
stream.seek(luma_offset);
|
||||
decompressLuma(stream);
|
||||
|
||||
//
|
||||
// Expand out the decompressed luma data. For type 0 frames:
|
||||
// * luma vals are 5-bit diffs, where
|
||||
// * top row values are diffs on the last value
|
||||
// * and the remaining rows are deltas on the value above.
|
||||
// * every second pixel in x is linearly interpolated from its horizontal neighbours.
|
||||
// * output values are clipped to 6 bits.
|
||||
// * a set of refinements values can adjust luma of interpolated pixels
|
||||
//
|
||||
const uint8 *lumadecomp = _scratchbuf;
|
||||
uint8 *lumarow = _lumabuf;
|
||||
int last = *lumadecomp++;
|
||||
lumarow[0] = last * 2;
|
||||
int x;
|
||||
// The top row uses only the left value for prediction
|
||||
for (x = 1; x < _width - 1; x += 2) {
|
||||
int cur = (last + *lumadecomp++) & 0x1F;
|
||||
lumarow[x] = last + cur;
|
||||
lumarow[x + 1] = cur * 2;
|
||||
last = cur;
|
||||
}
|
||||
lumarow[x] = last * 2;
|
||||
uint8 const *last_lumarow = lumarow;
|
||||
lumarow += _width;
|
||||
|
||||
// The remaining rows
|
||||
for (int y = 1; y < _height; y++) {
|
||||
last = ((last_lumarow[0] / 2) + *lumadecomp++) & 0x1F;
|
||||
lumarow[0] = last * 2;
|
||||
for (x = 1; x < _width - 1; x += 2) {
|
||||
int cur = ((last_lumarow[x + 1] / 2) + *lumadecomp++) & 0x1F;
|
||||
lumarow[x] = last + cur;
|
||||
lumarow[x + 1] = cur * 2;
|
||||
last = cur;
|
||||
}
|
||||
lumarow[x] = last * 2;
|
||||
last_lumarow = lumarow;
|
||||
lumarow += _width;
|
||||
}
|
||||
|
||||
if (refines_offset) {
|
||||
stream.seek(refines_offset + 8);
|
||||
|
||||
int dec_size = _unpack(stream, _scratchbuf, _width * _height / 2);
|
||||
if (dec_size < 0) {
|
||||
warning("luma refine unpacking failed!");
|
||||
dec_size = 0;
|
||||
} else {
|
||||
dec_size = MIN(dec_size, _width * _height / 2 - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < dec_size; i++)
|
||||
_lumabuf[i * 2 + 1] = (_lumabuf[i * 2 + 1] + _scratchbuf[i] * 2) & 0x3F;
|
||||
}
|
||||
|
||||
convertYUVtoRGBSurface();
|
||||
}
|
||||
|
||||
void XanDecoder::decodeFrameType1(Common::SeekableReadStream &stream) {
|
||||
const uint32 chroma_offset = stream.readUint32LE();
|
||||
const uint32 refines_offset = stream.readUint32LE();
|
||||
const uint32 refine2_offset = stream.readUint32LE();
|
||||
const uint32 luma_offset = stream.pos();
|
||||
const uint32 nbytes = static_cast<uint32>(stream.size());
|
||||
|
||||
if (chroma_offset > nbytes || refines_offset > nbytes || refine2_offset > nbytes) {
|
||||
error("invalid frame type 1 offset");
|
||||
}
|
||||
|
||||
if (!decodeChroma(stream, chroma_offset)) {
|
||||
warning("xxan chrome decode failed frame type 1");
|
||||
return;
|
||||
}
|
||||
|
||||
stream.seek(luma_offset);
|
||||
decompressLuma(stream);
|
||||
|
||||
//
|
||||
// Expand out the decompressed luma data. For type 1 frames:
|
||||
// * luma vals are 5-bit diffs on the previous frame's values
|
||||
// * every second pixel in x is linearly interpolated from its horizontal neighbours.
|
||||
// * output values are clipped to 6 bits.
|
||||
// * a set of refinements values can adjust luma of interpolated pixels
|
||||
//
|
||||
const uint8 *lumadecomp = _scratchbuf;
|
||||
uint8 *lumarow = _lumabuf;
|
||||
for (int y = 0; y < _height; y++) {
|
||||
int x;
|
||||
int last = (lumarow[0] + (*lumadecomp++ * 2)) & 0x3F;
|
||||
lumarow[0] = last;
|
||||
for (x = 1; x < _width - 1; x += 2) {
|
||||
int cur = (lumarow[x + 1] + (*lumadecomp++ * 2)) & 0x3F;
|
||||
lumarow[x] = (last + cur) / 2;
|
||||
lumarow[x + 1] = cur;
|
||||
last = cur;
|
||||
}
|
||||
lumarow[x] = last;
|
||||
lumarow += _width;
|
||||
}
|
||||
|
||||
if (refines_offset) {
|
||||
stream.seek(refines_offset + 8);
|
||||
|
||||
int dec_size = _unpack(stream, _scratchbuf, _width * _height / 2);
|
||||
|
||||
if (dec_size < 0) {
|
||||
warning("luma refine unpacking failed!");
|
||||
dec_size = 0;
|
||||
} else {
|
||||
dec_size = MIN(dec_size, _width * _height / 2 - 1);
|
||||
}
|
||||
|
||||
int dec2_size = 0;
|
||||
uint8 *scratch2 = _scratchbuf + _width * _height / 2;
|
||||
if (refine2_offset) {
|
||||
stream.seek(refine2_offset + 8);
|
||||
dec2_size = _unpack(stream, scratch2, _width * _height / 2);
|
||||
if (dec2_size < 0) {
|
||||
warning("luma refine2 unpacking failed!");
|
||||
dec2_size = 0;
|
||||
} else {
|
||||
dec2_size = MIN(dec_size, _width * _height / 2 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dec_size; i++) {
|
||||
int adjust = _scratchbuf[i] * 2;
|
||||
if (dec2_size)
|
||||
adjust += scratch2[i] * 2;
|
||||
_lumabuf[i * 2 + 1] = (_lumabuf[i * 2 + 1] + adjust) & 0x3F;
|
||||
}
|
||||
}
|
||||
|
||||
convertYUVtoRGBSurface();
|
||||
}
|
||||
|
||||
void XanDecoder::decompressLuma(Common::SeekableReadStream &stream) {
|
||||
const int32 startpos = stream.pos();
|
||||
const int nsymbols = stream.readByte();
|
||||
const int eofsymbol = stream.readByte();
|
||||
|
||||
const int root = nsymbols + eofsymbol;
|
||||
const uint8 *lumaend = _scratchbuf + _width * _height;
|
||||
|
||||
stream.skip(nsymbols * 2);
|
||||
|
||||
uint8 *luma = _scratchbuf;
|
||||
int node = root;
|
||||
int bits = stream.readByte();
|
||||
int mask = 0x80;
|
||||
while (!stream.eos()) {
|
||||
const int bit = ((bits & mask) ? 1 : 0);
|
||||
mask >>= 1;
|
||||
|
||||
const int32 nextbitspos = stream.pos();
|
||||
stream.seek(startpos + node * 2 + bit - eofsymbol * 2);
|
||||
node = stream.readByte();
|
||||
stream.seek(nextbitspos);
|
||||
|
||||
if (node == eofsymbol)
|
||||
break;
|
||||
if (node < eofsymbol) {
|
||||
*luma++ = node;
|
||||
if (luma >= lumaend)
|
||||
break;
|
||||
node = root;
|
||||
}
|
||||
if (!mask) {
|
||||
if (stream.eos())
|
||||
break;
|
||||
bits = stream.readByte();
|
||||
mask = 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XanDecoder::convertYUVtoRGBSurface() {
|
||||
// Expand luma from 6-bit to 8-bit.
|
||||
for (int i = 0; i < _width * _height; i++)
|
||||
_ybuf[i] = _lumabuf[i] << 2 | _lumabuf[i] >> 4;
|
||||
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface;
|
||||
_surface->create(_width, _height, _pixelFormat);
|
||||
}
|
||||
|
||||
YUVToRGBMan.convert420(_surface, Graphics::YUVToRGBManager::kScaleFull,
|
||||
_ybuf, _ubuf, _vbuf, _width, (_height / 2) * 2, _width, _width / 2);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
97
image/codecs/xan.h
Normal file
97
image/codecs/xan.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_XAN_H
|
||||
#define IMAGE_CODECS_XAN_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Xan image decoder. (fourcc Xxan)
|
||||
*
|
||||
* Used by Crusader: No Regret AVI files
|
||||
*
|
||||
* This code was created based on the multimedia wiki:
|
||||
* https://wiki.multimedia.cx/index.php/Origin_Xan_Codec
|
||||
* and ffmpeg's libavcodec/xxan.c.
|
||||
* The ffmpeg code is LGPL2 licensed and Copyright (C) 2011
|
||||
* Konstantin Shishkov based on work by Mike Melanson.
|
||||
*
|
||||
* A similar format is used in Wing Commander III (although not in an AVI
|
||||
* container) and IV.
|
||||
*/
|
||||
class XanDecoder : public Codec {
|
||||
public:
|
||||
XanDecoder (int width, int height, int bitsPerPixel);
|
||||
~XanDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void decodeFrameType0(Common::SeekableReadStream &stream);
|
||||
void decodeFrameType1(Common::SeekableReadStream &stream);
|
||||
|
||||
/** Decompress the huffman table for base luma data */
|
||||
void decompressLuma(Common::SeekableReadStream &stream);
|
||||
|
||||
bool decodeChroma(Common::SeekableReadStream &stream, int chroma_off);
|
||||
|
||||
/** convert the internally expanded YUV to the output RGBA surface */
|
||||
void convertYUVtoRGBSurface();
|
||||
|
||||
/** A buffer to hold the final frame */
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
/** Dest surface width and height */
|
||||
int _width, _height;
|
||||
|
||||
/** Dest surface pixel format */
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
|
||||
/** If true, decode chroma vals in Wing Commander 4 style (false = No Regret style) */
|
||||
bool _wc4Mode;
|
||||
|
||||
/** A buffer to hold scratch data. Either chroma data in progress, or
|
||||
* decompressed delta luma values (5-bit). Interpretation depends on frame type. */
|
||||
uint8 *_scratchbuf;
|
||||
/** A buffer to hold expanded/interpolated absolute luma values (6-bit) from the values in _scratchbuf.
|
||||
* These still need to be multiplied out to make 8-bit values. */
|
||||
uint8 *_lumabuf;
|
||||
/** a buffer for uncompressed and multiplied out "y" values (of yuv) */
|
||||
uint8 *_ybuf;
|
||||
/** a buffer for uncompressed "u" values (of yuv) */
|
||||
uint8 *_ubuf;
|
||||
/** a buffer for uncompressed "v" values (of yuv) */
|
||||
uint8 *_vbuf;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user