Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

143
image/codecs/bmp_raw.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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, &reg, &regBits)
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
View 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

File diff suppressed because it is too large Load Diff

56
image/codecs/hnm.h Normal file
View 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

View 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

File diff suppressed because it is too large Load Diff

604
image/codecs/indeo/indeo.h Normal file
View 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

View 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

View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

102
image/codecs/indeo3.h Normal file
View 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

File diff suppressed because it is too large Load Diff

149
image/codecs/indeo4.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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, &currentP[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, &currentP[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
View 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

File diff suppressed because it is too large Load Diff

340
image/codecs/svq1_vlc.h Normal file
View 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

View 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
View 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

View 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
View 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
View 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