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

291
image/ani.cpp Normal file
View File

@@ -0,0 +1,291 @@
/* 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/memstream.h"
#include "common/stream.h"
#include "common/substream.h"
#include "image/ani.h"
namespace Image {
AniDecoder::Metadata::Metadata()
: numFrames(0), numSteps(0), width(0), height(0), bitCount(0),
numPlanes(0), perFrameDelay(0), haveSeqData(false), isCURFormat(false) {
}
AniDecoder::FrameDef::FrameDef() : delay(0), imageIndex(0) {
}
AniDecoder::AniDecoder() : _stream(nullptr), _disposeAfterUse(DisposeAfterUse::NO) {
}
AniDecoder::~AniDecoder() {
close();
}
void AniDecoder::close() {
if (_disposeAfterUse == DisposeAfterUse::YES && _stream != nullptr)
delete _stream;
_stream = nullptr;
}
const AniDecoder::Metadata &AniDecoder::getMetadata() const {
return _metadata;
}
AniDecoder::FrameDef AniDecoder::getSequenceFrame(uint sequenceIndex) const {
FrameDef frameDef;
if (sequenceIndex >= _rateData.size())
frameDef.delay = _metadata.perFrameDelay;
else
frameDef.delay = _rateData[sequenceIndex];
if (sequenceIndex >= _seqData.size())
frameDef.imageIndex = sequenceIndex;
else
frameDef.imageIndex = _seqData[sequenceIndex];
return frameDef;
}
Common::SeekableReadStream *AniDecoder::openImageStream(uint imageIndex) const {
if (imageIndex >= _frameDataLocations.size())
error("Invalid ANI image index");
const FrameDataRange &frameDataRange = _frameDataLocations[imageIndex];
return new Common::SafeSeekableSubReadStream(_stream, frameDataRange.pos, frameDataRange.pos + frameDataRange.size);
}
bool AniDecoder::open(Common::SeekableReadStream &stream, DisposeAfterUse::Flag disposeAfterUse) {
close();
_stream = &stream;
_disposeAfterUse = disposeAfterUse;
bool loadedOK = load();
if (!loadedOK)
close();
return loadedOK;
}
bool AniDecoder::load() {
if (!parseRIFFChunks(*_stream, Common::Functor2Mem<const RIFFChunkDef &, Common::SeekableReadStream &, bool, AniDecoder>(this, &AniDecoder::parseTopLevelChunk))) {
warning("AniDecoder::load: Failed to load ANI container");
return false;
}
return true;
}
bool AniDecoder::parseRIFFChunks(Common::SeekableReadStream &stream, const RIFFChunkParseFunc_t &callback) {
int64 nextChunkStartPos = 0;
int64 endPos = stream.size();
while (nextChunkStartPos < endPos) {
if (!stream.seek(nextChunkStartPos)) {
warning("AniDecoder::parseRIFFChunks: Failed to reset to start of RIFF chunk");
return false;
}
byte riffChunkHeader[8];
if (stream.read(riffChunkHeader, 8) != 8) {
warning("AniDecoder::parseRIFFChunks: Failed to read RIFF chunk header");
return false;
}
uint32 chunkSize = READ_LE_UINT32(riffChunkHeader + 4);
int64 actualChunkSize = chunkSize;
if (chunkSize & 1)
actualChunkSize++;
int64 chunkAvailable = stream.size() - stream.pos();
if (chunkAvailable < actualChunkSize) {
warning("AniDecoder::parseRIFFChunk: RIFF chunk is too large");
return false;
}
RIFFChunkDef chunkDef;
chunkDef.id = READ_BE_UINT32(riffChunkHeader);
chunkDef.size = chunkSize;
Common::SeekableSubReadStream substream(&stream, static_cast<uint32>(stream.pos()), static_cast<uint32>(stream.pos()) + chunkSize);
if (!callback(chunkDef, substream))
return false;
nextChunkStartPos += actualChunkSize + 8;
}
return true;
}
bool AniDecoder::parseRIFFContainer(Common::SeekableReadStream &chunkStream, const RIFFChunkDef &chunkDef, const RIFFContainerParseFunc_t &callback) {
if (chunkDef.size < 4) {
warning("AniDecoder::parseRIFFContainer: RIFF container is too small");
return false;
}
byte containerTypeID[4];
if (chunkStream.read(containerTypeID, 4) != 4) {
warning("AniDecoder::parseRIFFContainer: Failed to read RIFF container type");
return false;
}
RIFFContainerDef containerDef;
containerDef.id = READ_BE_UINT32(containerTypeID);
containerDef.size = chunkDef.size - 4;
Common::SeekableSubReadStream substream(&chunkStream, 4, chunkDef.size);
return callback(containerDef, substream);
}
bool AniDecoder::parseTopLevelChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream) {
if (chunk.id != MKTAG('R', 'I', 'F', 'F')) {
warning("AniDecoder::parseTopLevelChunk: Top-level chunk isn't RIFF");
return false;
}
return parseRIFFContainer(stream, chunk, Common::Functor2Mem<const RIFFContainerDef &, Common::SeekableReadStream &, bool, AniDecoder>(this, &AniDecoder::parseTopLevelContainer));
}
bool AniDecoder::parseTopLevelContainer(const RIFFContainerDef &container, Common::SeekableReadStream &stream) {
if (container.id == MKTAG('A', 'C', 'O', 'N'))
return parseRIFFChunks(stream, Common::Functor2Mem<const RIFFChunkDef &, Common::SeekableReadStream &, bool, AniDecoder>(this, &AniDecoder::parseSecondLevelChunk));
warning("AniDecoder::parseTopLevelContainer: Top-level container isn't ACON");
return false;
}
bool AniDecoder::parseSecondLevelChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream) {
if (chunk.id == MKTAG('L', 'I', 'S', 'T'))
return parseRIFFContainer(stream, chunk, Common::Functor2Mem<const RIFFContainerDef &, Common::SeekableReadStream &, bool, AniDecoder>(this, &AniDecoder::parseListContainer));
if (chunk.id == MKTAG('a', 'n', 'i', 'h'))
return parseAnimHeaderChunk(chunk, stream);
if (chunk.id == MKTAG('s', 'e', 'q', ' '))
return parseSeqChunk(chunk, stream);
if (chunk.id == MKTAG('r', 'a', 't', 'e'))
return parseRateChunk(chunk, stream);
return true;
}
bool AniDecoder::parseListContainer(const RIFFContainerDef &container, Common::SeekableReadStream &stream) {
if (container.id == MKTAG('f', 'r', 'a', 'm'))
return parseRIFFChunks(stream, Common::Functor2Mem<const RIFFChunkDef &, Common::SeekableReadStream &, bool, AniDecoder>(this, &AniDecoder::parseIconChunk));
return true;
}
bool AniDecoder::parseAnimHeaderChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream) {
const uint32 expectedStructSize = 36;
if (chunk.size < expectedStructSize) {
warning("AniDecoder::parseAnimHeaderChunk: Chunk is too small");
return false;
}
uint32 structSize = 0;
uint32 flags = 0;
if (!stream.readMultipleLE(structSize, _metadata.numFrames, _metadata.numSteps, _metadata.width, _metadata.height,
_metadata.bitCount, _metadata.numPlanes, _metadata.perFrameDelay, flags) || structSize < expectedStructSize) {
warning("AniDecoder::parseAnimHeaderChunk: Read failed");
return false;
}
_metadata.isCURFormat = ((flags & 1) != 0);
_metadata.haveSeqData = ((flags & 2) != 0);
return true;
}
bool AniDecoder::parseSeqChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream) {
uint32 numFrames = chunk.size / 4u;
if (numFrames > 1000u) {
warning("AniDecoder::parseRateChunk: Too many frames");
return false;
}
if (numFrames > _seqData.size())
_seqData.resize(numFrames);
for (uint i = 0; i < numFrames; i++) {
byte seqData[4];
if (stream.read(seqData, 4) != 4) {
warning("AniDecoder::parseRateChunk: Failed to read sequence information");
return false;
}
_seqData[i] = READ_LE_UINT32(seqData);
}
return true;
}
bool AniDecoder::parseRateChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream) {
uint32 numFrames = chunk.size / 4u;
if (numFrames > 1000u) {
warning("AniDecoder::parseRateChunk: Too many frames");
return false;
}
if (numFrames > _rateData.size())
_rateData.resize(numFrames);
for (uint i = 0; i < numFrames; i++) {
byte rateData[4];
if (stream.read(rateData, 4) != 4) {
warning("AniDecoder::parseRateChunk: Failed to read rate information");
return false;
}
_rateData[i] = READ_LE_UINT32(rateData);
}
return true;
}
bool AniDecoder::parseIconChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream) {
FrameDataRange frameDataRange;
// Get the global stream position
frameDataRange.pos = static_cast<uint32>(_stream->pos());
frameDataRange.size = chunk.size;
_frameDataLocations.push_back(frameDataRange);
return true;
}
} // End of namespace Image

136
image/ani.h Normal file
View File

@@ -0,0 +1,136 @@
/* 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_ANI_H
#define IMAGE_ANI_H
#include "common/array.h"
#include "common/types.h"
#include "common/func.h"
namespace Common {
class SeekableReadStream;
struct IFFChunk;
} // End of namespace Common
namespace Graphics {
class Cursor;
struct Surface;
} // End of namespace Graphics
namespace Image {
class AniDecoder {
public:
struct Metadata {
Metadata();
uint32 numFrames; // Number of images
uint32 numSteps; // Number of frames (use the FrameDef to determine which frame)
uint32 width;
uint32 height;
uint32 bitCount;
uint32 numPlanes;
uint32 perFrameDelay;
bool haveSeqData;
bool isCURFormat;
};
struct FrameDef {
FrameDef();
uint32 imageIndex;
uint32 delay; // In 1/60 sec
};
AniDecoder();
~AniDecoder();
bool open(Common::SeekableReadStream &stream, DisposeAfterUse::Flag = DisposeAfterUse::NO);
void close();
const Metadata &getMetadata() const;
FrameDef getSequenceFrame(uint sequenceIndex) const;
/**
* Opens a substream for an image. If the metadata field
* "isCURFormat" is set, you can pass the stream to IcoCurDecoder to
* read it. Otherwise, you must determine the format. The stream
* is valid for as long as the stream used to construct the AniDecoder
* is valid.
*
* @param imageIndex The index of the image in the ANI file.
* @return A substream for the image.
*/
Common::SeekableReadStream *openImageStream(uint imageIndex) const;
private:
struct RIFFContainerDef {
uint32 id;
uint32 size;
};
struct RIFFChunkDef {
uint32 id;
uint32 size;
};
struct FrameDataRange {
uint32 pos;
uint32 size;
};
typedef Common::Functor2<const RIFFContainerDef &, Common::SeekableReadStream &, bool> RIFFContainerParseFunc_t;
typedef Common::Functor2<const RIFFChunkDef &, Common::SeekableReadStream &, bool> RIFFChunkParseFunc_t;
bool load();
static bool parseRIFFChunks(Common::SeekableReadStream &stream, const RIFFChunkParseFunc_t &callback);
static bool parseRIFFContainer(Common::SeekableReadStream &stream, const RIFFChunkDef &chunkDef, const RIFFContainerParseFunc_t &callback);
bool parseTopLevelChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream);
bool parseTopLevelContainer(const RIFFContainerDef &container, Common::SeekableReadStream &stream);
bool parseSecondLevelChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream);
bool parseListContainer(const RIFFContainerDef &container, Common::SeekableReadStream &stream);
bool parseAnimHeaderChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream);
bool parseSeqChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream);
bool parseRateChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream);
bool parseIconChunk(const RIFFChunkDef &chunk, Common::SeekableReadStream &stream);
Metadata _metadata;
Common::Array<uint32> _rateData;
Common::Array<uint32> _seqData;
Common::Array<FrameDataRange> _frameDataLocations;
Common::SeekableReadStream *_stream;
DisposeAfterUse::Flag _disposeAfterUse;
};
} // End of namespace Image
#endif

254
image/bmp.cpp Normal file
View File

@@ -0,0 +1,254 @@
/* 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/bmp.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "image/codecs/codec.h"
// NOTE: This decoder understands only so called BMP Win3.x old format
// In order to produce files suitable for it, use ImageMagick:
//
// convert input.file BMP3:output.bmp
//
namespace Image {
BitmapDecoder::BitmapDecoder(): _codec(nullptr), _surface(nullptr), _palette(0) {
}
BitmapDecoder::~BitmapDecoder() {
destroy();
}
void BitmapDecoder::destroy() {
delete _codec;
_codec = nullptr;
_surface = nullptr;
_palette.clear();
}
bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
uint16 fileType = stream.readUint16BE();
uint32 imageOffset = 0;
if (fileType == MKTAG16('B', 'M')) {
// The bitmap file header is present
/* uint32 fileSize = */ stream.readUint32LE();
/* uint16 res1 = */ stream.readUint16LE();
/* uint16 res2 = */ stream.readUint16LE();
imageOffset = stream.readUint32LE();
} else {
// Not present, let's try to parse as a headerless one
stream.seek(-2, SEEK_CUR);
}
uint32 infoSize = stream.readUint32LE();
if (infoSize != 40 && infoSize != 52 && infoSize != 56 && infoSize != 108 && infoSize != 124) {
warning("Only Windows v1-v5 bitmaps are supported, unknown header: %d", infoSize);
return false;
}
uint32 width = stream.readUint32LE();
int32 height = stream.readSint32LE();
if (width == 0 || height == 0)
return false;
if (height < 0) {
warning("Right-side up bitmaps not supported");
return false;
}
/* uint16 planes = */ stream.readUint16LE();
uint16 bitsPerPixel = stream.readUint16LE();
if (bitsPerPixel != 4 && bitsPerPixel != 8 && bitsPerPixel != 16 && bitsPerPixel != 24 && bitsPerPixel != 32) {
warning("%dbpp bitmaps not supported", bitsPerPixel);
return false;
}
uint32 compression = stream.readUint32BE();
if (bitsPerPixel == 16 && compression != SWAP_CONSTANT_32(0)) {
warning("only RGB555 raw mode supported for %dbpp bitmaps", bitsPerPixel);
return false;
}
uint32 imageSize = stream.readUint32LE();
/* uint32 pixelsPerMeterX = */ stream.readUint32LE();
/* uint32 pixelsPerMeterY = */ stream.readUint32LE();
uint32 paletteColorCount = stream.readUint32LE();
/* uint32 colorsImportant = */ stream.readUint32LE();
stream.seek(infoSize - 40, SEEK_CUR);
if (bitsPerPixel == 4 || bitsPerPixel == 8) {
if (paletteColorCount == 0)
paletteColorCount = bitsPerPixel == 8 ? 256 : 16;
// Read the palette
_palette.resize(paletteColorCount, false);
for (uint16 i = 0; i < paletteColorCount; i++) {
byte b = stream.readByte();
byte g = stream.readByte();
byte r = stream.readByte();
stream.readByte();
_palette.set(i, r, g, b);
}
}
// Create the codec (it will warn about unhandled compression)
_codec = createBitmapCodec(compression, 0, width, height, bitsPerPixel);
if (!_codec)
return false;
// If the image offset is zero (like in headerless ones), set it to the current
// position.
if (imageOffset == 0)
imageOffset = stream.pos();
// If the image size is zero, set it to the rest of the stream.
if (imageSize == 0)
imageSize = stream.size() - imageOffset;
// Grab the frame data
Common::SeekableSubReadStream subStream(&stream, imageOffset, imageOffset + imageSize);
// We only support raw bitmaps for now
_surface = _codec->decodeFrame(subStream);
return true;
}
bool writeBMP(Common::WriteStream &out, const Graphics::Surface &input, const byte *palette) {
const Graphics::PixelFormat requiredFormat_3byte = Graphics::PixelFormat::createFormatBGR24();
Graphics::Surface *tmp = NULL;
const Graphics::Surface *surface;
if (input.format == requiredFormat_3byte) {
surface = &input;
} else {
surface = tmp = input.convertTo(requiredFormat_3byte, palette);
}
int dstPitch = surface->w * 3;
int extraDataLength = (dstPitch % 4) ? 4 - (dstPitch % 4) : 0;
int padding = 0;
out.writeByte('B');
out.writeByte('M');
out.writeUint32LE(surface->h * dstPitch + 54);
out.writeUint32LE(0);
out.writeUint32LE(54);
out.writeUint32LE(40);
out.writeUint32LE(surface->w);
out.writeUint32LE(surface->h);
out.writeUint16LE(1);
out.writeUint16LE(24);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
for (uint y = surface->h; y-- > 0;) {
out.write((const void *)surface->getBasePtr(0, y), dstPitch);
out.write(&padding, extraDataLength);
}
// free tmp surface
if (tmp) {
tmp->free();
delete tmp;
}
return true;
}
bool writePalettedBMP(Common::WriteStream &out, const Graphics::Surface &surface, const byte *palette) {
// We assume surface is already 8bpp indexed
// surface->pitch should be >= surface->w
const int bytesPerPixel = 1;
const int rowSize = surface.w * bytesPerPixel;
const int rowPadding = (4 - (rowSize & 3)) & 3;
const uint32 paletteSize = 256 * 4;
const uint32 pixelDataSize = (rowSize + rowPadding) * surface.h;
const uint32 dataOffset = 14 + 40 + paletteSize;
const uint32 fileSize = dataOffset + pixelDataSize;
/* === BITMAPFILEHEADER === */
out.writeByte('B');
out.writeByte('M');
out.writeUint32LE(fileSize);
out.writeUint16LE(0);
out.writeUint16LE(0);
out.writeUint32LE(dataOffset);
/* === BITMAPINFOHEADER === */
out.writeUint32LE(40); // biSize
out.writeUint32LE(surface.w);
out.writeUint32LE(surface.h);
out.writeUint16LE(1); // planes
out.writeUint16LE(8); // bit count
out.writeUint32LE(0); // BI_RGB
out.writeUint32LE(pixelDataSize);
out.writeUint32LE(0); // x ppm
out.writeUint32LE(0); // y ppm
out.writeUint32LE(256); // colors used
out.writeUint32LE(256); // important colors
/* === PALETTE === */
// Input palette: RGBRGBRGB...
// BMP palette: B G R 0
for (int i = 0; i < 256; i++) {
out.writeByte(palette[i * 3 + 2]); // B
out.writeByte(palette[i * 3 + 1]); // G
out.writeByte(palette[i * 3 + 0]); // R
out.writeByte(0); // reserved
}
/* === PIXEL DATA (bottom-up) === */
byte pad[3] = { 0, 0, 0 };
for (int y = surface.h - 1; y >= 0; y--) {
out.write(surface.getBasePtr(0, y), rowSize);
if (rowPadding)
out.write(pad, rowPadding);
}
return true;
}
} // End of namespace Image

99
image/bmp.h Normal file
View File

@@ -0,0 +1,99 @@
/* 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/>.
*
*/
/**
* @file
* Image decoder used in engines:
* - buried
* - hugo
* - mohawk
* - qdengine
* - wintermute
*/
#ifndef IMAGE_BMP_H
#define IMAGE_BMP_H
#include "common/scummsys.h"
#include "common/str.h"
#include "graphics/palette.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_bmp BMP decoder
* @ingroup image
*
* @brief Decoder for BMP images.
*
* Used in engines:
* - Hugo
* - Mohawk
* - Petka
* - TwinE
* - Wintermute
* - Ultima8
* @{
*/
class Codec;
class BitmapDecoder : public ImageDecoder {
public:
BitmapDecoder();
virtual ~BitmapDecoder();
// ImageDecoder API
void destroy() override;
virtual bool loadStream(Common::SeekableReadStream &stream) override;
const Graphics::Surface *getSurface() const override { return _surface; }
const Graphics::Palette &getPalette() const override { return _palette; }
private:
Codec *_codec;
const Graphics::Surface *_surface;
Graphics::Palette _palette;
};
/**
* Outputs an uncompressed BMP stream of the given input surface.
*/
bool writeBMP(Common::WriteStream &out, const Graphics::Surface &input, const byte *palette = nullptr);
/**
* Outputs an uncompressed BMP stream of the given paletted input surface, without converting it to 24 bpp.
*/
bool writePalettedBMP(Common::WriteStream &out, const Graphics::Surface &surface, const byte *palette);
/** @} */
} // End of namespace Image
#endif

160
image/cel_3do.cpp Normal file
View File

@@ -0,0 +1,160 @@
/* 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/cel_3do.h"
#include "common/file.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "image/bmp.h"
namespace Image {
enum CCBFlags {
kCCBPacked = 1 << 9,
kCCBNoPre0 = 1 << 22
};
Cel3DODecoder::Cel3DODecoder(): _surface(nullptr), _palette(0) {
}
Cel3DODecoder::~Cel3DODecoder() {
destroy();
}
void Cel3DODecoder::destroy() {
_surface = nullptr;
_palette.clear();
}
bool Cel3DODecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
// This is not a full implementaion of CEL support,
// just what is currently needed for following games:
// * Plumbers don't wear ties
// TODO: support paletted
if (stream.readUint32BE() != MKTAG('C', 'C', 'B', ' '))
return false;
if (stream.readUint32BE() != 0x50) // block size
return false;
if (stream.readUint32BE() != 0) // CCB version
return false;
uint32 flags = stream.readUint32BE();
stream.skip(0x30);
uint32 pre0 = stream.readUint32BE();
/* pre1 = */ stream.readUint32BE();
uint32 width = stream.readUint32BE();
uint32 height = stream.readUint32BE();
while (!stream.eos()) {
if (stream.readUint32BE() == MKTAG('P', 'D', 'A', 'T'))
break;
stream.skip(stream.readUint32BE() - 8);
}
if (stream.eos())
return false;
if (width == 0 || height == 0)
return false;
/* pdat_size = */ stream.readUint32BE();
Graphics::PixelFormat format(2, 5, 5, 5, 1, 10, 5, 0, 15);
Graphics::Surface *surface = new Graphics::Surface();
surface->create(width, height, format);
uint16 *dst = (uint16 *)surface->getBasePtr(0, 0);
if(!(flags & kCCBNoPre0)) {
pre0 = stream.readUint32BE();
if(!(flags & kCCBPacked)) {
/* pre1 = */ stream.readUint32BE();
}
}
// Only RGB555 is supported
if ((pre0 & 0x17) != 0x16) {
surface->free();
delete surface;
return false;
}
if(!(flags & kCCBPacked)) {
// RAW
// TODO: this can be optimized, especially on BE systems, but do we care?
for (uint xy = 0; xy < width * height; xy++)
*dst++ = stream.readUint16BE();
} else {
// RLE
for (uint y = 0; y < height; y++) {
int linecomprem = (stream.readUint16BE() + 2) * 4 - 2;
int linerem = width;
bool stopLine = false;
while (linerem > 0 && linecomprem > 0 && !stopLine) {
byte lead = stream.readByte();
linecomprem--;
switch (lead >> 6) {
case 0: // end of the line
stopLine = true;
break;
case 1: // copy
for (uint i = 0; i <= (lead & 0x3fu) && linerem > 0 && linecomprem > 0;
i++, linerem--, linecomprem -= 2)
*dst++ = stream.readUint16BE();
break;
case 2: // black
for (uint i = 0; i <= (lead & 0x3fu) && linerem > 0; i++, linerem--)
*dst++ = 0;
break;
case 3: { // RLE multiply
uint16 rleval = stream.readUint16BE();
linecomprem -= 2;
for (uint i = 0; i <= (lead & 0x3fu) && linerem > 0; i++, linerem--)
*dst++ = rleval;
break;
}
}
}
if (linecomprem > 0)
stream.skip(linecomprem);
if (linerem > 0) {
memset(dst, 0, 2 * linerem);
dst += linerem;
}
}
}
_surface = surface;
return true;
}
} // End of namespace Image

69
image/cel_3do.h Normal file
View File

@@ -0,0 +1,69 @@
/* 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_CEL_3DO_H
#define IMAGE_CEL_3DO_H
#include "common/scummsys.h"
#include "common/str.h"
#include "graphics/palette.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_cel CEL decoder
* @ingroup image
*
* @brief Decoder for CEL images.
* @{
*/
class Codec;
class Cel3DODecoder : public ImageDecoder {
public:
Cel3DODecoder();
virtual ~Cel3DODecoder();
// ImageDecoder API
void destroy() override;
virtual bool loadStream(Common::SeekableReadStream &stream) override;
const Graphics::Surface *getSurface() const override { return _surface; }
const Graphics::Palette &getPalette() const override { return _palette; }
private:
const Graphics::Surface *_surface;
Graphics::Palette _palette;
};
/** @} */
} // End of namespace Image
#endif

164
image/cicn.cpp Normal file
View File

@@ -0,0 +1,164 @@
/* 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/stream.h"
#include "image/cicn.h"
#include "image/pict.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
namespace Image {
CicnDecoder::CicnDecoder(): _surface(nullptr), _palette(0), _mask(nullptr) {
}
CicnDecoder::~CicnDecoder() {
destroy();
}
void CicnDecoder::destroy() {
if (_surface) {
_surface->free();
delete _surface;
_surface = nullptr;
}
_palette.clear();
if (_mask) {
_mask->free();
delete _mask;
_mask = nullptr;
}
}
bool CicnDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
Image::PICTDecoder::PixMap pixMap = Image::PICTDecoder::readPixMap(stream);
// Mask header
stream.skip(4);
uint16 maskRowBytes = stream.readUint16BE();
stream.readUint16BE(); // top
stream.readUint16BE(); // left
uint16 maskHeight = stream.readUint16BE(); // bottom
uint16 maskWidth = stream.readUint16BE(); // right
// Bitmap header
stream.skip(4);
uint16 bitmapRowBytes = stream.readUint16BE();
stream.readUint16BE(); // top
stream.readUint16BE(); // left
uint16 bitmapHeight = stream.readUint16BE(); // bottom
uint16 bitmapWidth = stream.readUint16BE(); // right
// Mask and bitmap data
stream.skip(4);
if (maskRowBytes && maskHeight) {
_mask = new Graphics::Surface();
_mask->create(maskWidth, maskHeight, Graphics::PixelFormat::createFormatCLUT8());
byte *mask = new byte[maskRowBytes * maskHeight];
stream.read(mask, maskRowBytes * maskHeight);
for (uint y = 0; y < maskHeight; y++) {
for (uint x = 0; x < maskWidth; x++) {
if ((mask[y * maskRowBytes + x / 8] & (0x80 >> (x % 8))) == 0)
_mask->setPixel(x, y, 0);
else
_mask->setPixel(x, y, 255);
}
}
delete[] mask;
}
stream.skip(bitmapRowBytes * bitmapHeight);
// Palette
stream.skip(6);
uint16 paletteColorCount = stream.readUint16BE() + 1;
_palette.resize(paletteColorCount, false);
for (uint i = 0; i < paletteColorCount; i++) {
stream.skip(2);
byte r = stream.readUint16BE() >> 8;
byte g = stream.readUint16BE() >> 8;
byte b = stream.readUint16BE() >> 8;
_palette.set(i, r, g, b);
}
_surface = new Graphics::Surface();
_surface->create(pixMap.bounds.width(), pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
if (pixMap.pixelSize == 1) {
byte *buf = new byte[pixMap.rowBytes];
for (int y = 0; y < pixMap.bounds.height(); y++) {
stream.read(buf, pixMap.rowBytes);
for (int x = 0; x < bitmapWidth; x += 8) {
for (int i = 0; i < 8 && x + i < bitmapWidth; i++) {
_surface->setPixel(x + i, y, (buf[x / 8] >> (7 - i)) & 0x01);
}
}
}
delete[] buf;
} else if (pixMap.pixelSize == 2) {
byte *buf = new byte[pixMap.rowBytes];
for (int y = 0; y < bitmapHeight; y++) {
stream.read(buf, pixMap.rowBytes);
for (int x = 0; x < bitmapWidth; x += 4) {
for (int i = 0; i < 4 && x + i < bitmapWidth; i++) {
_surface->setPixel(x + i, y, (buf[x / 4] >> (6 - 2 * i)) & 0x03);
}
}
}
delete[] buf;
} else if (pixMap.pixelSize == 4) {
byte *buf = new byte[pixMap.rowBytes];
for (int y = 0; y < bitmapHeight; y++) {
stream.read(buf, pixMap.rowBytes);
for (int x = 0; x < bitmapWidth; x += 2) {
for (int i = 0; i < 2 && x + i < bitmapWidth; i++) {
_surface->setPixel(x + i, y, (buf[x / 2] >> (4 - 4 * i)) & 0x0F);
}
}
}
delete[] buf;
} else if (pixMap.pixelSize == 8) {
byte *buf = new byte[pixMap.rowBytes];
for (int y = 0; y < bitmapHeight; y++) {
stream.read(buf, pixMap.rowBytes);
memcpy(_surface->getBasePtr(0, y), buf, bitmapWidth);
}
delete[] buf;
} else {
error("CicnDecoder::loadStream(): Invalid pixel size %d", pixMap.pixelSize);
}
return true;
}
} // End of namespace Image

62
image/cicn.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_CICN_H
#define IMAGE_CICN_H
#include "graphics/palette.h"
#include "image/image_decoder.h"
namespace Image {
/**
* @defgroup image_cicn cicn decoder
* @ingroup image
*
* @brief Decoder for cicn images.
*
* Used in engines:
* - SCUMM
* @{
*/
class CicnDecoder : public ImageDecoder {
public:
CicnDecoder();
virtual ~CicnDecoder();
// ImageDecoder API
void destroy() override;
bool loadStream(Common::SeekableReadStream &stream) override;
const Graphics::Surface *getSurface() const override { return _surface; }
const Graphics::Palette &getPalette() const override { return _palette; }
const Graphics::Surface *getMask() const override { return _mask; }
private:
Graphics::Surface *_surface;
Graphics::Palette _palette;
Graphics::Surface *_mask;
};
/** @} */
} // End of namespace Image
#endif

35
image/codec-options.h Normal file
View File

@@ -0,0 +1,35 @@
/* 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_CODEC_OPTIONS_H
#define IMAGE_CODEC_OPTIONS_H
namespace Image {
enum class CodecAccuracy {
Fast,
Default,
Accurate,
};
} // End of namespace Image
#endif

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

134
image/gif.cpp 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 "image/gif.h"
#include "common/array.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/surface.h"
#include "graphics/pixelformat.h"
#include <gif_lib.h>
namespace Image {
GIFDecoder::GIFDecoder() : _outputSurface(nullptr), _palette(0) {
}
GIFDecoder::~GIFDecoder() {
destroy();
}
static int gifReadFromStream(GifFileType *gif, GifByteType *bytes, int size) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)gif->UserData;
return stream->read(bytes, size);
}
bool GIFDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
int error = 0;
GifFileType *gif = DGifOpen(&stream, gifReadFromStream, &error);
if (!gif) {
warning("GIF open failed with error %s", GifErrorString(error));
return false;
}
const int errcode = DGifSlurp(gif);
if (errcode != GIF_OK) {
warning("GIF failed to load");
DGifCloseFile(gif, 0);
return false;
}
if (gif->ImageCount <= 0) {
warning("GIF doesn't contain valid image data");
DGifCloseFile(gif, 0);
return false;
}
if (gif->ImageCount > 1) {
warning("GIF contains more than one frame - only loading the first one");
}
const SavedImage *gifImage = gif->SavedImages;
const int width = gif->SWidth;
const int height = gif->SHeight;
const ColorMapObject *colorMap = gif->SColorMap;
_hasTransparentColor = false;
for (int i = 0; i < gif->ExtensionBlockCount; ++i) {
const ExtensionBlock &eb = gif->ExtensionBlocks[i];
GraphicsControlBlock gcb;
DGifExtensionToGCB(eb.ByteCount, eb.Bytes, &gcb);
if (gcb.TransparentColor != NO_TRANSPARENT_COLOR) {
_hasTransparentColor = true;
_transparentColor = gcb.TransparentColor;
break;
}
}
int colorCount = colorMap->ColorCount;
_outputSurface = new Graphics::Surface();
_palette.resize(colorCount, false);
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
for (int i = 0; i < colorCount; ++i) {
_palette.set(i, colorMap->Colors[i].Red,
colorMap->Colors[i].Green,
colorMap->Colors[i].Blue);
}
// TODO: support transparency
_outputSurface->create(width, height, format);
const uint8 *in = (const uint8 *)gifImage->RasterBits;
uint8 *pixelPtr = (uint8 *)_outputSurface->getBasePtr(0, 0);
if (gif->Image.Interlace) {
const int interlacedOffset[] = {0, 4, 2, 1};
const int interlacedJumps[] = {8, 8, 4, 2};
for (int i = 0; i < 4; ++i) {
for (int row = interlacedOffset[i]; row < height; row += interlacedJumps[i]) {
memcpy(pixelPtr + width * row, in, width);
in += width;
}
}
} else {
memcpy(pixelPtr, in, width * height);
}
DGifCloseFile(gif, 0);
return true;
}
void GIFDecoder::destroy() {
if (_outputSurface) {
_outputSurface->free();
delete _outputSurface;
_outputSurface = 0;
}
_palette.clear();
}
} // End of namespace Image

75
image/gif.h Normal file
View File

@@ -0,0 +1,75 @@
/* 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_GIF_H
#define IMAGE_GIF_H
#include "graphics/palette.h"
#include "image/image_decoder.h"
#ifdef USE_GIF
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_gif GIF decoder
* @ingroup image
*
* @brief Decoder for images encoded as Graphics Interchange Format (GIF).
*
* This decoder has a dependency on the libgif library.
*
* Used in engines:
* - TwinE
* @{
*/
class GIFDecoder : public ImageDecoder {
public:
GIFDecoder();
~GIFDecoder();
bool loadStream(Common::SeekableReadStream &stream) override;
void destroy() override;
const Graphics::Palette &getPalette() const override { return _palette; }
const Graphics::Surface *getSurface() const override { return _outputSurface; }
bool hasTransparentColor() const override { return _hasTransparentColor; }
uint32 getTransparentColor() const override { return _transparentColor; }
private:
Graphics::Surface *_outputSurface;
Graphics::Palette _palette;
bool _hasTransparentColor;
uint32 _transparentColor;
};
/** @} */
} // End of namespace Image
#endif // USE_GIF
#endif

142
image/icocur.cpp Normal file
View File

@@ -0,0 +1,142 @@
/* 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/stream.h"
#include "common/substream.h"
#include "common/memstream.h"
#include "image/icocur.h"
#include "graphics/wincursor.h"
namespace Image {
IcoCurDecoder::IcoCurDecoder() : _type(kTypeInvalid), _stream(nullptr), _disposeAfterUse(DisposeAfterUse::NO) {
}
IcoCurDecoder::~IcoCurDecoder() {
close();
}
void IcoCurDecoder::close() {
if (_disposeAfterUse == DisposeAfterUse::YES && _stream != nullptr)
delete _stream;
_stream = nullptr;
_type = kTypeInvalid;
_items.clear();
}
bool IcoCurDecoder::open(Common::SeekableReadStream &stream, DisposeAfterUse::Flag disposeAfterUse) {
close();
_stream = &stream;
_disposeAfterUse = disposeAfterUse;
bool loadedOK = load();
if (!loadedOK)
close();
return loadedOK;
}
bool IcoCurDecoder::load() {
uint8 iconDirData[6];
if (_stream->read(iconDirData, 6) != 6)
return false;
if (iconDirData[0] != 0 || iconDirData[1] != 0 || (iconDirData[2] != 1 && iconDirData[2] != 2) || iconDirData[3] != 0) {
warning("Malformed ICO/CUR header");
return false;
}
uint16 numImages = READ_LE_UINT16(iconDirData + 4);
_type = static_cast<Type>(iconDirData[2]);
if (numImages == 0)
return true;
uint32 dirSize = static_cast<uint32>(numImages) * 16;
Common::Array<uint8> iconDir;
iconDir.resize(dirSize);
if (_stream->read(&iconDir[0], dirSize) != dirSize)
return false;
_items.resize(numImages);
for (uint i = 0; i < numImages; i++) {
const uint8 *entryData = &iconDir[i * 16u];
Item &item = _items[i];
item.width = entryData[0];
if (item.width == 0)
item.width = 256;
item.height = entryData[1];
if (item.height == 0)
item.height = 256;
item.numColors = entryData[2];
item.data.ico.numPlanes = READ_LE_UINT16(entryData + 4);
item.data.ico.bitsPerPixel = READ_LE_UINT16(entryData + 6);
item.dataSize = READ_LE_UINT32(entryData + 8);
item.dataOffset = READ_LE_UINT32(entryData + 12);
}
return true;
}
IcoCurDecoder::Type IcoCurDecoder::getType() const {
return _type;
}
uint IcoCurDecoder::numItems() const {
return _items.size();
}
const IcoCurDecoder::Item &IcoCurDecoder::getItem(uint itemIndex) const {
return _items[itemIndex];
}
Graphics::Cursor *IcoCurDecoder::loadItemAsCursor(uint itemIndex) const {
const IcoCurDecoder::Item &dirItem = _items[itemIndex];
if (_type != kTypeCUR)
warning("ICO/CUR file type wasn't a cursor, but is being requested as a cursor anyway");
if (static_cast<int64>(dirItem.dataOffset) > _stream->size()) {
warning("ICO/CUR data offset was outside of the file");
return nullptr;
}
if (_stream->size() - static_cast<int64>(dirItem.dataOffset) < static_cast<int64>(dirItem.dataSize)) {
warning("ICO/CUR data bounds were outside of the file");
return nullptr;
}
Common::SeekableSubReadStream substream(_stream, dirItem.dataOffset, dirItem.dataOffset + dirItem.dataSize);
return Graphics::loadWindowsCursorFromDIB(substream, dirItem.data.cur.hotspotX, dirItem.data.cur.hotspotY);
}
} // End of namespace Image

106
image/icocur.h Normal file
View File

@@ -0,0 +1,106 @@
/* 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_ICOCUR_H
#define IMAGE_ICOCUR_H
#include "common/array.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
} // End of namespace Common
namespace Graphics {
class Cursor;
struct Surface;
} // End of namespace Graphics
namespace Image {
class IcoCurDecoder {
public:
enum Type {
kTypeInvalid,
kTypeICO,
kTypeCUR,
};
struct Item {
struct IconData {
uint16 numPlanes;
uint16 bitsPerPixel;
};
struct CursorData {
uint16 hotspotX;
uint16 hotspotY;
};
union DataUnion {
IconData ico;
CursorData cur;
};
uint16 width;
uint16 height;
uint8 numColors; // May be 0
DataUnion data;
uint32 dataSize;
uint32 dataOffset;
};
IcoCurDecoder();
~IcoCurDecoder();
bool open(Common::SeekableReadStream &stream, DisposeAfterUse::Flag = DisposeAfterUse::NO);
void close();
Type getType() const;
uint numItems() const;
const Item &getItem(uint itemIndex) const;
/**
* Loads an item from the directory as a cursor.
*
* @param itemIndex The index of the item in the directory.
* @return Loaded cursor.
*/
Graphics::Cursor *loadItemAsCursor(uint itemIndex) const;
private:
bool load();
Type _type;
Common::Array<Item> _items;
Common::SeekableReadStream *_stream;
DisposeAfterUse::Flag _disposeAfterUse;
};
} // End of namespace Image
#endif

256
image/iff.cpp Normal file
View File

@@ -0,0 +1,256 @@
/* 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/iff.h"
#include "common/formats/iff_container.h"
#include "common/stream.h"
#include "common/util.h"
namespace Image {
IFFDecoder::IFFDecoder(): _surface(nullptr), _palette(0) {
// these 2 properties are not reset by destroy(), so the default is set here.
_numRelevantPlanes = 8;
_pixelPacking = false;
destroy();
}
IFFDecoder::~IFFDecoder() {
destroy();
}
void IFFDecoder::destroy() {
if (_surface) {
_surface->free();
delete _surface;
_surface = nullptr;
}
_palette.clear();
memset(&_header, 0, sizeof(Header));
_paletteRanges.clear();
_type = TYPE_UNKNOWN;
}
bool IFFDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
const uint32 form = stream.readUint32BE();
if (form != ID_FORM) {
warning("Failed reading IFF-file");
return false;
}
stream.skip(4);
const uint32 type = stream.readUint32BE();
switch (type) {
case ID_ILBM:
_type = TYPE_ILBM;
break;
case ID_PBM:
_type = TYPE_PBM;
break;
case TYPE_UNKNOWN:
default:
_type = TYPE_UNKNOWN;
break;
}
if (type == TYPE_UNKNOWN) {
warning("Failed reading IFF-file");
return false;
}
while (1) {
if (stream.size() < stream.pos() + 8)
break;
const uint32 chunkType = stream.readUint32BE();
uint32 chunkSize = stream.readUint32BE();
// According to the format specs:
// "If ckData is an odd number of bytes long, a 0 pad byte follows which is not included in ckSize."
// => fix the length
if (chunkSize % 2)
chunkSize++;
if (stream.eos())
break;
switch (chunkType) {
case ID_BMHD:
loadHeader(stream);
break;
case ID_CMAP:
loadPalette(stream, chunkSize);
break;
case ID_CRNG:
loadPaletteRange(stream, chunkSize);
break;
case ID_BODY:
loadBitmap(stream);
break;
default:
if (stream.size() < stream.pos() + (int32)chunkSize)
break;
stream.skip(chunkSize);
}
}
return true;
}
void IFFDecoder::loadHeader(Common::SeekableReadStream &stream) {
_header.width = stream.readUint16BE();
_header.height = stream.readUint16BE();
_header.x = stream.readUint16BE();
_header.y = stream.readUint16BE();
_header.numPlanes = stream.readByte();
_header.masking = stream.readByte();
_header.compression = stream.readByte();
_header.flags = stream.readByte();
_header.transparentColor = stream.readUint16BE();
_header.xAspect = stream.readByte();
_header.yAspect = stream.readByte();
_header.pageWidth = stream.readUint16BE();
_header.pageHeight = stream.readUint16BE();
assert(_header.width >= 1);
assert(_header.height >= 1);
assert(_header.numPlanes >= 1 && _header.numPlanes <= 8 && _header.numPlanes != 7);
}
void IFFDecoder::loadPalette(Common::SeekableReadStream &stream, const uint32 size) {
_palette.resize(size / 3, false);
for (uint i = 0; i < _palette.size(); i++) {
byte r = stream.readByte();
byte g = stream.readByte();
byte b = stream.readByte();
_palette.set(i, r, g, b);
}
}
void IFFDecoder::loadPaletteRange(Common::SeekableReadStream &stream, const uint32 size) {
PaletteRange range;
range.timer = stream.readSint16BE();
range.step = stream.readSint16BE();
range.flags = stream.readSint16BE();
range.first = stream.readByte();
range.last = stream.readByte();
_paletteRanges.push_back(range);
}
void IFFDecoder::loadBitmap(Common::SeekableReadStream &stream) {
_numRelevantPlanes = MIN(_numRelevantPlanes, _header.numPlanes);
if (_numRelevantPlanes != 1 && _numRelevantPlanes != 2 && _numRelevantPlanes != 4)
_pixelPacking = false;
uint16 outPitch = _header.width;
if (_pixelPacking)
outPitch /= (8 / _numRelevantPlanes);
// FIXME: CLUT8 is not a proper format for packed bitmaps but there is no way to tell it to use 1, 2 or 4 bits per pixel
_surface = new Graphics::Surface();
_surface->create(outPitch, _header.height, Graphics::PixelFormat::createFormatCLUT8());
if (_type == TYPE_ILBM) {
uint32 scanlinePitch = ((_header.width + 15) >> 4) << 1;
byte *scanlines = new byte[scanlinePitch * _header.numPlanes];
byte *data = (byte *)_surface->getPixels();
for (uint16 i = 0; i < _header.height; ++i) {
byte *scanline = scanlines;
for (uint16 j = 0; j < _header.numPlanes; ++j) {
uint16 outSize = scanlinePitch;
if (_header.compression) {
Common::PackBitsReadStream packStream(stream);
packStream.read(scanline, outSize);
} else {
stream.read(scanline, outSize);
}
scanline += outSize;
}
packPixels(scanlines, data, scanlinePitch, outPitch);
data += outPitch;
}
delete[] scanlines;
} else if (_type == TYPE_PBM) {
byte *data = (byte *)_surface->getPixels();
uint32 outSize = _header.width * _header.height;
if (_header.compression) {
Common::PackBitsReadStream packStream(stream);
packStream.read(data, outSize);
} else {
stream.read(data, outSize);
}
}
}
void IFFDecoder::packPixels(byte *scanlines, byte *data, const uint16 scanlinePitch, const uint16 outPitch) {
uint32 numPixels = _header.width;
if (_pixelPacking)
numPixels = outPitch * (8 / _numRelevantPlanes);
for (uint32 x = 0; x < numPixels; ++x) {
byte *scanline = scanlines;
byte pixel = 0;
byte offset = x >> 3;
byte bit = 0x80 >> (x & 7);
// first build a pixel by scanning all the usable planes in the input
for (uint32 plane = 0; plane < _numRelevantPlanes; ++plane) {
if (scanline[offset] & bit)
pixel |= (1 << plane);
scanline += scanlinePitch;
}
// then output the pixel according to the requested packing
if (!_pixelPacking)
data[x] = pixel;
else if (_numRelevantPlanes == 1)
data[x / 8] |= (pixel << (x & 7));
else if (_numRelevantPlanes == 2)
data[x / 4] |= (pixel << ((x & 3) << 1));
else if (_numRelevantPlanes == 4)
data[x / 2] |= (pixel << ((x & 1) << 2));
}
}
} // End of namespace Image

129
image/iff.h Normal file
View File

@@ -0,0 +1,129 @@
/* 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_IFF_H
#define IMAGE_IFF_H
#include "common/array.h"
#include "common/endian.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_iff IFF decoder
* @ingroup image
*
* @brief Decoder for images encoded as Interchange File Format (IFF).
*
* Used in engines:
* - Gob
* - Parallaction
* - Queen
* - Saga
* @{
*/
class IFFDecoder : public ImageDecoder {
public:
struct Header {
uint16 width, height;
uint16 x, y;
byte numPlanes;
byte masking;
byte compression;
byte flags;
uint16 transparentColor;
byte xAspect, yAspect;
uint16 pageWidth, pageHeight;
};
struct PaletteRange {
int16 timer, step, flags;
byte first, last;
};
enum Type {
TYPE_UNKNOWN = 0,
TYPE_ILBM,
TYPE_PBM
};
IFFDecoder();
virtual ~IFFDecoder();
// ImageDecoder API
void destroy() override;
bool loadStream(Common::SeekableReadStream &stream) override;
const Header *getHeader() const { return &_header; }
const Graphics::Surface *getSurface() const override { return _surface; }
const Graphics::Palette &getPalette() const override { return _palette; }
const Common::Array<PaletteRange> &getPaletteRanges() const { return _paletteRanges; }
/**
* The number of planes to decode, also determines the pixel packing if _packPixels is true.
* 8 == decode all planes, map 1 pixel in 1 byte. (default, no packing even if _packPixels is true)
*
* NOTE: this property must be reset manually, and is not reset by a call to destroy().
*/
void setNumRelevantPlanes(const uint8 numRelevantPlanes) { _numRelevantPlanes = numRelevantPlanes; }
/**
* Enables pixel packing, the amount of packing is determined by _numRelevantPlanes
* 1 == decode first plane, pack 8 pixels in 1 byte. This makes _surface->w 1/8th of _header.width
* 2 == decode first 2 planes, pack 4 pixels in 1 byte. This makes _surface->w 1/4th of _header.width
* 4 == decode first 4 planes, pack 2 pixels in 1 byte. This makes _surface->w half of _header.width
* Packed bitmaps won't have a proper surface format since there is no way to tell it to use 1, 2 or 4 bits per pixel
*
* NOTE: this property must be reset manually, and is not reset by a call to destroy().
*/
void setPixelPacking(const bool pixelPacking) { _pixelPacking = pixelPacking; }
private:
Header _header;
Graphics::Surface *_surface;
Graphics::Palette _palette;
Common::Array<PaletteRange> _paletteRanges;
Type _type;
uint8 _numRelevantPlanes;
bool _pixelPacking;
void loadHeader(Common::SeekableReadStream &stream);
void loadPalette(Common::SeekableReadStream &stream, const uint32 size);
void loadPaletteRange(Common::SeekableReadStream &stream, const uint32 size);
void loadBitmap(Common::SeekableReadStream &stream);
void packPixels(byte *scanlines, byte *data, const uint16 scanlinePitch, const uint16 outPitch);
};
/** @} */
} // End of namespace Image
#endif

125
image/image_decoder.h Normal file
View File

@@ -0,0 +1,125 @@
/* 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_IMAGEDECODER_H
#define IMAGE_IMAGEDECODER_H
#include "common/scummsys.h"
#include "common/str.h"
#include "graphics/palette.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_decoder Image decoder
* @ingroup image
*
* @brief ImageDecoder class used for representing and managing various image decoders.
* @{
*/
/**
* A representation of an image decoder that maintains ownership of the surface
* and palette it decodes to.
*
* This is designed for still frames only.
*/
class ImageDecoder {
public:
virtual ~ImageDecoder() {}
/**
* Load an image from the specified stream.
*
* loadStream() should implicitly call destroy() to free the memory
* of the last loadStream() call.
*
* @param stream Input stream.
*
* @return Whether loading the file succeeded.
*
* @see getSurface
* @see getPalette
*/
virtual bool loadStream(Common::SeekableReadStream &stream) = 0;
/**
* Destroy this decoder's surface and palette.
*
* This should be called by a loadStream() implementation as well
* as by the destructor.
*/
virtual void destroy() = 0;
/**
* Get the decoded surface.
*
* This surface is owned by this ImageDecoder and remains valid
* until destroy() or loadStream() is called, or until the destructor of
* this ImageDecoder is called.
*
* @return The decoded surface, or 0 if no surface is present.
*/
virtual const Graphics::Surface *getSurface() const = 0;
/**
* Get the decoded palette.
*
* This palette is owned by this ImageDecoder and remains valid
* until destroy() or loadStream() is called, or until the destructor of
* this ImageDecoder is called.
*
* @return The decoded palette, or empty if no palette is present.
*/
virtual const Graphics::Palette &getPalette() const = 0;
/**
* Query whether the decoded image has a palette.
*/
virtual bool hasPalette() const { return !getPalette().empty(); }
/** Query whether the decoded image has a transparent color. */
virtual bool hasTransparentColor() const { return false; }
/** Return the transparent color. */
virtual uint32 getTransparentColor() const { return 0; }
/**
* Get the mask data for the decoded image.
*/
virtual const Graphics::Surface *getMask() const { return 0; }
/**
* Query whether the decoded image has a mask.
*/
virtual bool hasMask() const { return getMask() != 0; }
};
/** @} */
} // End of namespace Image
#endif

387
image/jpeg.cpp Normal file
View File

@@ -0,0 +1,387 @@
/* 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/>.
*
*/
// libjpeg uses forbidden symbols in its header. Thus, we need to allow them
// here.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "image/jpeg.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/pixelformat.h"
#ifdef USE_JPEG
// The original release of libjpeg v6b did not contain any extern "C" in case
// its header files are included in a C++ environment. To avoid any linking
// issues we need to add it on our own.
extern "C" {
#include <jpeglib.h>
#include <jerror.h>
}
#include <setjmp.h>
#endif
namespace Image {
JPEGDecoder::JPEGDecoder() :
_surface(),
_palette(0),
_colorSpace(kColorSpaceRGB),
_accuracy(CodecAccuracy::Default),
_requestedPixelFormat(getByteOrderRgbPixelFormat()) {
}
JPEGDecoder::~JPEGDecoder() {
destroy();
}
Graphics::PixelFormat JPEGDecoder::getByteOrderRgbPixelFormat() const {
return Graphics::PixelFormat::createFormatRGB24();
}
const Graphics::Surface *JPEGDecoder::getSurface() const {
return &_surface;
}
void JPEGDecoder::destroy() {
_surface.free();
}
const Graphics::Surface *JPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
if (!loadStream(stream))
return 0;
return getSurface();
}
void JPEGDecoder::setCodecAccuracy(CodecAccuracy accuracy) {
_accuracy = accuracy;
}
Graphics::PixelFormat JPEGDecoder::getPixelFormat() const {
if (_surface.getPixels())
return _surface.format;
return _requestedPixelFormat;
}
#ifdef USE_JPEG
namespace {
#define JPEG_BUFFER_SIZE 4096
struct StreamSource : public jpeg_source_mgr {
Common::SeekableReadStream *stream;
bool startOfFile;
JOCTET buffer[JPEG_BUFFER_SIZE];
};
void initSource(j_decompress_ptr cinfo) {
StreamSource *source = (StreamSource *)cinfo->src;
source->startOfFile = true;
}
boolean fillInputBuffer(j_decompress_ptr cinfo) {
StreamSource *source = (StreamSource *)cinfo->src;
uint32 bufferSize = source->stream->read((byte *)source->buffer, sizeof(source->buffer));
if (bufferSize == 0) {
if (source->startOfFile) {
// An empty file is a fatal error
ERREXIT(cinfo, JERR_INPUT_EMPTY);
} else {
// Otherwise we insert an EOF marker
WARNMS(cinfo, JWRN_JPEG_EOF);
source->buffer[0] = (JOCTET)0xFF;
source->buffer[1] = (JOCTET)JPEG_EOI;
bufferSize = 2;
}
}
source->next_input_byte = source->buffer;
source->bytes_in_buffer = bufferSize;
source->startOfFile = false;
return TRUE;
}
void skipInputData(j_decompress_ptr cinfo, long numBytes) {
StreamSource *source = (StreamSource *)cinfo->src;
if (numBytes > 0) {
if (numBytes > (long)source->bytes_in_buffer) {
// In case we need to skip more bytes than there are in the buffer
// we will skip the remaining data and fill the buffer again
numBytes -= (long)source->bytes_in_buffer;
// Skip the remaining bytes
source->stream->skip(numBytes);
// Fill up the buffer again
(*source->fill_input_buffer)(cinfo);
} else {
source->next_input_byte += (size_t)numBytes;
source->bytes_in_buffer -= (size_t)numBytes;
}
}
}
void termSource(j_decompress_ptr cinfo) {
}
void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
StreamSource *source;
// Initialize the source in case it has not been done yet.
if (cinfo->src == NULL) {
cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(StreamSource));
}
source = (StreamSource *)cinfo->src;
source->init_source = &initSource;
source->fill_input_buffer = &fillInputBuffer;
source->skip_input_data = &skipInputData;
source->resync_to_restart = &jpeg_resync_to_restart;
source->term_source = &termSource;
source->bytes_in_buffer = 0;
source->next_input_byte = NULL;
source->stream = stream;
}
struct jpeg_error_mgr_ext : public jpeg_error_mgr {
jmp_buf jmp;
bool jmp_valid;
};
void errorExit(j_common_ptr cinfo) {
jpeg_error_mgr_ext *err = (jpeg_error_mgr_ext *)cinfo->err;
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buffer);
if (err->jmp_valid) {
/* We will jump back to the loading stream function
* but, before, warn the user */
warning("libjpeg: %s", buffer);
longjmp(err->jmp, 1);
}
// This function is not allowed to return to the caller, thus we simply
// error out with our error handling here.
error("libjpeg: %s", buffer);
}
void outputMessage(j_common_ptr cinfo) {
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buffer);
// Is using debug here a good idea? Or do we want to ignore all libjpeg
// messages?
debug(3, "libjpeg: %s", buffer);
}
J_COLOR_SPACE fromScummvmPixelFormat(const Graphics::PixelFormat &format) {
#if defined(JCS_EXTENSIONS) || defined(JCS_ALPHA_EXTENSIONS)
struct PixelFormatMapping {
Graphics::PixelFormat pixelFormat;
J_COLOR_SPACE colorSpace;
};
static const PixelFormatMapping mappings[] = {
#ifdef JCS_ALPHA_EXTENSIONS
{ Graphics::PixelFormat::createFormatRGBA32(true), JCS_EXT_RGBA },
{ Graphics::PixelFormat::createFormatBGRA32(true), JCS_EXT_BGRA },
{ Graphics::PixelFormat::createFormatARGB32(true), JCS_EXT_ARGB },
{ Graphics::PixelFormat::createFormatABGR32(true), JCS_EXT_ABGR }
#endif
#if defined(JCS_EXTENSIONS) && defined(JCS_ALPHA_EXTENSIONS)
,
#endif
#ifdef JCS_EXTENSIONS
{ Graphics::PixelFormat::createFormatRGB24(), JCS_EXT_RGB },
{ Graphics::PixelFormat::createFormatBGR24(), JCS_EXT_BGR },
{ Graphics::PixelFormat::createFormatRGBA32(false), JCS_EXT_RGBX },
{ Graphics::PixelFormat::createFormatBGRA32(false), JCS_EXT_BGRX },
{ Graphics::PixelFormat::createFormatARGB32(false), JCS_EXT_XRGB },
{ Graphics::PixelFormat::createFormatABGR32(false), JCS_EXT_XBGR }
#endif
};
for (uint i = 0; i < ARRAYSIZE(mappings); i++) {
if (mappings[i].pixelFormat == format) {
return mappings[i].colorSpace;
}
}
#endif
return JCS_UNKNOWN;
}
} // End of anonymous namespace
#endif
bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
#ifdef USE_JPEG
// Reset member variables from previous decodings
destroy();
jpeg_decompress_struct cinfo;
jpeg_error_mgr_ext jerr;
jerr.jmp_valid = false;
// Initialize error handling callbacks
cinfo.err = jpeg_std_error(&jerr);
cinfo.err->error_exit = &errorExit;
cinfo.err->output_message = &outputMessage;
// Initialize the decompression structure
jpeg_create_decompress(&cinfo);
if (_accuracy <= CodecAccuracy::Fast)
cinfo.dct_method = JDCT_FASTEST;
else if (_accuracy >= CodecAccuracy::Accurate)
cinfo.dct_method = JDCT_ISLOW;
// Initialize our buffer handling
jpeg_scummvm_src(&cinfo, &stream);
if (setjmp(jerr.jmp)) {
/* File is invalid */
jpeg_destroy_decompress(&cinfo);
return false;
}
jerr.jmp_valid = true;
// Read the file header
jpeg_read_header(&cinfo, TRUE);
// We can request YUV output because Groovie requires it
switch (_colorSpace) {
case kColorSpaceRGB: {
J_COLOR_SPACE colorSpace = fromScummvmPixelFormat(_requestedPixelFormat);
if (colorSpace == JCS_UNKNOWN) {
// When libjpeg-turbo is not available or an unhandled pixel
// format was requested, ask libjpeg to decode to byte order RGB
// as it's always available.
colorSpace = JCS_RGB;
}
cinfo.out_color_space = colorSpace;
break;
}
case kColorSpaceYUV:
cinfo.out_color_space = JCS_YCbCr;
break;
default:
break;
}
// Semi-hack:
// In case 4 components jpeglib expect CMYK or CYYK color space output.
// To avoid any color space conversion, CMYK must be used.
// HPL1 engine use it to pass RGBA jpeg bitmaps.
if (cinfo.num_components == 4) {
cinfo.out_color_space = JCS_CMYK;
}
if (setjmp(jerr.jmp)) {
/* Output color space seems invalid
* Try again with the most basic one */
if (_colorSpace == kColorSpaceRGB && cinfo.num_components == 3) {
warning("Falling back to RGB slow path");
cinfo.out_color_space = JCS_RGB;
}
if (setjmp(jerr.jmp)) {
/* There is something definitely wrong here */
jpeg_destroy_decompress(&cinfo);
return false;
}
}
// Actually start decompressing the image
jpeg_start_decompress(&cinfo);
if (setjmp(jerr.jmp)) {
/* Something went wrong */
jpeg_destroy_decompress(&cinfo);
return false;
}
// Allocate buffers for the output data
switch (_colorSpace) {
case kColorSpaceRGB: {
Graphics::PixelFormat outputPixelFormat;
if (cinfo.out_color_space == JCS_RGB) {
outputPixelFormat = getByteOrderRgbPixelFormat();
} else {
outputPixelFormat = _requestedPixelFormat;
}
_surface.create(cinfo.output_width, cinfo.output_height, outputPixelFormat);
break;
}
case kColorSpaceYUV:
// We use YUV with 3 bytes per pixel otherwise.
// This is pretty ugly since our PixelFormat cannot express YUV...
_surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
break;
default:
break;
}
// Size of output pixel must match 4 bytes.
if (cinfo.out_color_space == JCS_CMYK) {
assert(_surface.format.bytesPerPixel == 4);
}
// Allocate buffer for one scanline
JDIMENSION pitch = cinfo.output_width * _surface.format.bytesPerPixel;
assert(_surface.pitch >= (int)pitch);
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
// Go through the image data scanline by scanline
while (cinfo.output_scanline < cinfo.output_height) {
byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
jpeg_read_scanlines(&cinfo, buffer, 1);
memcpy(dst, buffer[0], pitch);
}
// We are done with decompressing, thus free all the data
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
if (_colorSpace == kColorSpaceRGB && _surface.format != _requestedPixelFormat) {
_surface.convertToInPlace(_requestedPixelFormat); // Slow path
}
return true;
#else
return false;
#endif
}
} // End of Graphics namespace

121
image/jpeg.h Normal file
View File

@@ -0,0 +1,121 @@
/* 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_JPEG_H
#define IMAGE_JPEG_H
#include "graphics/palette.h"
#include "graphics/surface.h"
#include "image/image_decoder.h"
#include "image/codecs/codec.h"
namespace Common {
class SeekableReadStream;
}
namespace Image {
/**
* @defgroup image_jpeg JPEG decoder
* @ingroup image
*
* @brief Decoder for JPEG images.
*
* Used in engines:
* - groovie
* - mohawk
* - vcruise
* - wintermute
* @{
*/
class JPEGDecoder : public ImageDecoder, public Codec {
public:
JPEGDecoder();
~JPEGDecoder();
// ImageDecoder API
void destroy() override;
bool loadStream(Common::SeekableReadStream &str) override;
const Graphics::Surface *getSurface() const override;
const Graphics::Palette &getPalette() const override { return _palette; }
// Codec API
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
void setCodecAccuracy(CodecAccuracy accuracy) override;
Graphics::PixelFormat getPixelFormat() const override;
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
if (format.isCLUT8())
return false;
_requestedPixelFormat = format;
return true;
}
// Special API for JPEG
enum ColorSpace {
/**
* Output RGB data in the pixel format specified using `setOutputPixelFormat`.
*
* This is the default output.
*/
kColorSpaceRGB,
/**
* Output (interleaved) YUV data.
*
* Be aware that some images cannot be output in YUV mode.
* These are (non-standard) JPEG images which are in RGB colorspace.
*
* The resulting Surface will have a PixelFormat with 3 bytes per
* pixel and the remaining entries are completely zeroed. This works
* around the fact that PixelFormat can only describe RGB formats.
*
* You should only use this when you are really aware of what you are
* doing!
*/
kColorSpaceYUV
};
/**
* Request the output color space. This can be used to obtain raw YUV
* data from the JPEG file. But this might not work for all files!
*
* The decoder itself defaults to RGB.
*
* @param outSpace The color space to output.
*/
void setOutputColorSpace(ColorSpace outSpace) { _colorSpace = outSpace; }
private:
// TODO: Avoid inheriting from multiple superclasses that have identical member functions.
using Codec::getPalette;
Graphics::Surface _surface;
Graphics::Palette _palette;
ColorSpace _colorSpace;
Graphics::PixelFormat _requestedPixelFormat;
CodecAccuracy _accuracy;
Graphics::PixelFormat getByteOrderRgbPixelFormat() const;
};
/** @} */
} // End of namespace Image
#endif

91
image/module.mk Normal file
View File

@@ -0,0 +1,91 @@
MODULE := image
MODULE_OBJS := \
ani.o \
bmp.o \
cel_3do.o \
cicn.o \
icocur.o \
iff.o \
jpeg.o \
neo.o \
pcx.o \
pict.o \
png.o \
scr.o \
tga.o \
xbm.o \
codecs/bmp_raw.o \
codecs/cinepak.o \
codecs/codec.o \
codecs/dither.o \
codecs/hlz.o \
codecs/msrle.o \
codecs/msrle4.o \
codecs/msvideo1.o \
codecs/qtrle.o \
codecs/rpza.o \
codecs/smc.o
ifdef USE_GIF
MODULE_OBJS += \
gif.o
endif
ifdef USE_JPEG
MODULE_OBJS += \
codecs/mjpeg.o
endif
ifdef USE_MPEG2
MODULE_OBJS += \
codecs/mpeg.o
endif
ifdef USE_CDTOONS
MODULE_OBJS += \
codecs/cdtoons.o
endif
ifdef USE_INDEO3
MODULE_OBJS += \
codecs/indeo3.o
endif
ifdef USE_INDEO45
MODULE_OBJS += \
codecs/indeo4.o \
codecs/indeo5.o \
codecs/indeo/indeo.o \
codecs/indeo/indeo_dsp.o \
codecs/indeo/mem.o \
codecs/indeo/vlc.o
endif
ifdef USE_HNM
MODULE_OBJS += \
codecs/hnm.o
endif
ifdef USE_JYV1
MODULE_OBJS += \
codecs/jyv1.o
endif
ifdef USE_SVQ1
MODULE_OBJS += \
codecs/svq1.o
endif
ifdef USE_TRUEMOTION1
MODULE_OBJS += \
codecs/truemotion1.o
endif
ifdef USE_XAN
MODULE_OBJS += \
codecs/xan.o
endif
# Include common rules
include $(srcdir)/rules.mk

119
image/neo.cpp Normal file
View File

@@ -0,0 +1,119 @@
/* 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/stream.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "image/neo.h"
namespace Image {
NeoDecoder::NeoDecoder(byte *palette) : _palette(0) {
_surface = nullptr;
if (palette) {
_palette.resize(16, false);
_palette.set(palette, 0, 16);
}
}
NeoDecoder::~NeoDecoder() {
destroy();
}
void NeoDecoder::destroy() {
if (_surface) {
_surface->free();
delete _surface;
_surface = nullptr;
}
_palette.clear();
}
bool NeoDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
if (_palette.empty()) {
int start = stream.pos();
if (stream.readUint16LE() != 0x00)
warning("Header check failed for reading neo image");
if (stream.readUint16LE() != 0x00)
warning("Header check failed for reading neo image");
_palette.resize(16, false);
for (int i = 0; i < 16; ++i) {
byte v1 = stream.readByte();
byte v2 = stream.readByte();
byte r = floor((v1 & 0x07) * 255.0 / 7.0);
byte g = floor((v2 & 0x70) * 255.0 / 7.0 / 16.0);
byte b = floor((v2 & 0x07) * 255.0 / 7.0);
_palette.set(i, r, g, b);
}
stream.seek(start + 128);
}
int width = 320;
int height = 200;
_surface = new Graphics::Surface();
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
// 200 rows of image:
for (int y = 0; y < 200; y++) {
// 20 column blocks:
for (int x = 0; x < 20; x++) {
// Fetch the 4 words that make up the
// next 16 pixels across 4 bitplanes:
uint16 uW0 = stream.readUint16BE();
uint16 uW1 = stream.readUint16BE();
uint16 uW2 = stream.readUint16BE();
uint16 uW3 = stream.readUint16BE();
// The first pixel is found in the highest bit:
uint32 uBit = 0x8000;
// 16 pixels to process:
for (int z = 0; z < 16; z++) {
// Work out the colour index:
int idx = 0;
if (uW0 & uBit)
idx += 1;
if (uW1 & uBit)
idx += 2;
if (uW2 & uBit)
idx += 4;
if (uW3 & uBit)
idx += 8;
_surface->setPixel(x * 16 + z, y, idx);
uBit >>= 1;
}
}
}
return true;
}
} // End of namespace Image

64
image/neo.h Normal file
View File

@@ -0,0 +1,64 @@
/* 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_NEO_H
#define IMAGE_NEO_H
#include "graphics/palette.h"
#include "image/image_decoder.h"
/**
* @defgroup image_neo Neochrome decoder
* @ingroup image
*
* @brief Atari-ST Neochrome decoder based on NEOLoad by Jason "Joefish" Railton
*
*
* Used in engines:
* - Freescape
* @{
*/
namespace Common {
class SeekableReadStream;
}
namespace Image {
class NeoDecoder : public Image::ImageDecoder {
public:
NeoDecoder(byte *palette = nullptr);
virtual ~NeoDecoder();
// ImageDecoder API
void destroy();
virtual bool loadStream(Common::SeekableReadStream &stream);
virtual const Graphics::Surface *getSurface() const { return _surface; }
const Graphics::Palette &getPalette() const { return _palette; }
uint16 getPaletteColorCount() const { return _palette.size(); }
private:
Graphics::Surface *_surface;
Graphics::Palette _palette;
};
} // End of namespace Image
#endif // IMAGE_NEO_H

202
image/pcx.cpp Normal file
View File

@@ -0,0 +1,202 @@
/* 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/pcx.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
/**
* Based on the PCX specs:
* https://web.archive.org/web/20151028182714/http://www.fileformat.info/format/pcx/spec/a10e75307b3a4cc49c3bbe6db4c41fa2/view.htm
* and the PCX decoder of FFmpeg (libavcodec/pcx.c):
* https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/tags/n0.11.1:/libavcodec/pcx.c
*/
namespace Image {
PCXDecoder::PCXDecoder(): _surface(nullptr), _palette(0) {
}
PCXDecoder::~PCXDecoder() {
destroy();
}
void PCXDecoder::destroy() {
if (_surface) {
_surface->free();
delete _surface;
_surface = nullptr;
}
_palette.clear();
}
bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
if (stream.readByte() != 0x0a) // ZSoft PCX
return false;
byte version = stream.readByte(); // 0 - 5
if (version > 5)
return false;
bool compressed = stream.readByte(); // encoding, 1 = run length encoding
byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8
// Window
uint16 xMin = stream.readUint16LE();
uint16 yMin = stream.readUint16LE();
uint16 xMax = stream.readUint16LE();
uint16 yMax = stream.readUint16LE();
uint16 width = xMax - xMin + 1;
uint16 height = yMax - yMin + 1;
if (xMax < xMin || yMax < yMin) {
warning("Invalid PCX image dimensions");
return false;
}
stream.skip(4); // HDpi, VDpi
// Read the EGA palette (colormap)
_palette.resize(16, false);
for (uint16 i = 0; i < 16; i++) {
byte r = stream.readByte();
byte g = stream.readByte();
byte b = stream.readByte();
_palette.set(i, r, g, b);
}
if (stream.readByte() != 0) // reserved, should be set to 0
return false;
byte nPlanes = stream.readByte();
uint16 bytesPerLine = stream.readUint16LE();
uint16 bytesPerscanLine = nPlanes * bytesPerLine;
if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) {
warning("PCX data is corrupted");
return false;
}
stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler
_surface = new Graphics::Surface();
byte *scanLine = new byte[bytesPerscanLine];
byte *dst;
int x, y;
if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp
_surface->create(width, height, Graphics::PixelFormat::createFormatRGB24());
dst = (byte *)_surface->getPixels();
_palette.clear();
for (y = 0; y < height; y++) {
decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
for (x = 0; x < width; x++) {
*dst++ = scanLine[x];
*dst++ = scanLine[x + bytesPerLine];
*dst++ = scanLine[x + (bytesPerLine << 1)];
}
}
} else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
dst = (byte *)_surface->getPixels();
_palette.resize(16, true);
for (y = 0; y < height; y++, dst += _surface->pitch) {
decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
memcpy(dst, scanLine, width);
}
if (version == 5) {
if (stream.readByte() != 12) {
warning("Expected a palette after the PCX image data");
delete[] scanLine;
return false;
}
// Read the VGA palette
_palette.resize(256, false);
for (uint16 i = 0; i < 256; i++) {
byte r = stream.readByte();
byte g = stream.readByte();
byte b = stream.readByte();
_palette.set(i, r, g, b);
}
}
} else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
dst = (byte *)_surface->getPixels();
_palette.resize(16, true);
for (y = 0; y < height; y++, dst += _surface->pitch) {
decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
for (x = 0; x < width; x++) {
int m = 0x80 >> (x & 7), v = 0;
for (int i = nPlanes - 1; i >= 0; i--) {
v <<= 1;
v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1;
}
dst[x] = v;
}
}
} else {
// Known unsupported case: 1 plane and bpp < 8 (1, 2 or 4)
warning("Invalid PCX file (%d planes, %d bpp)", nPlanes, bitsPerPixel);
delete[] scanLine;
return false;
}
delete[] scanLine;
return true;
}
void PCXDecoder::decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerscanLine, bool compressed) {
uint32 i = 0;
byte run, value;
if (compressed) {
while (i < bytesPerscanLine) {
run = 1;
value = stream.readByte();
if (value >= 0xc0) {
run = value & 0x3f;
value = stream.readByte();
}
while (i < bytesPerscanLine && run--)
dst[i++] = value;
}
} else {
stream.read(dst, bytesPerscanLine);
}
}
} // End of namespace Image

71
image/pcx.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_PCX_H
#define IMAGE_PCX_H
#include "common/scummsys.h"
#include "common/str.h"
#include "graphics/palette.h"
#include "image/image_decoder.h"
namespace Common{
class SeekableReadStream;
}
namespace Image {
/**
* @defgroup image_pcx PCX decoder
* @ingroup image
*
* @brief Decoder for PCX images.
*
* Used in engines:
* - Dreamweb
* - Hugo
* - Queen
* - Tucker
* - TwinE
* @{
*/
class PCXDecoder : public ImageDecoder {
public:
PCXDecoder();
virtual ~PCXDecoder();
// ImageDecoder API
void destroy() override;
virtual bool loadStream(Common::SeekableReadStream &stream) override;
const Graphics::Surface *getSurface() const override { return _surface; }
const Graphics::Palette &getPalette() const override { return _palette; }
private:
void decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerScanline, bool compressed);
Graphics::Surface *_surface;
Graphics::Palette _palette;
};
/** @} */
} // End of namespace Image
#endif

818
image/pict.cpp Normal file
View File

@@ -0,0 +1,818 @@
/* 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/pict.h"
#include "image/codecs/codec.h"
#include "common/bitstream.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "graphics/surface.h"
namespace Image {
// The PICT code is based off of the QuickDraw specs:
// https://developer.apple.com/library/archive/documentation/mac/QuickDraw/QuickDraw-461.html
// https://developer.apple.com/library/archive/documentation/mac/QuickDraw/QuickDraw-269.html
PICTDecoder::PICTDecoder() : _outputSurface(nullptr), _palette(0), _version(2) {
}
PICTDecoder::~PICTDecoder() {
destroy();
}
void PICTDecoder::destroy() {
if (_outputSurface) {
_outputSurface->free();
delete _outputSurface;
_outputSurface = nullptr;
}
_palette.clear();
}
#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PICTDecoder::b, c))
void PICTDecoder::setupOpcodesCommon() {
OPCODE(0x0000, o_nop, "NOP");
OPCODE(0x0001, o_clip, "Clip");
OPCODE(0x0003, o_txFont, "TxFont");
OPCODE(0x0004, o_txFace, "TxFace");
OPCODE(0x0007, o_pnSize, "PnSize");
OPCODE(0x0009, o_pnPat, "PnPat");
OPCODE(0x000D, o_txSize, "TxSize");
OPCODE(0x0010, o_txRatio, "TxRatio");
OPCODE(0x0011, o_versionOp, "VersionOp");
OPCODE(0x001E, o_nop, "DefHilite");
OPCODE(0x0022, o_shortLine, "ShortLine");
OPCODE(0x0023, o_shortLineFrom, "ShortLineFrom");
OPCODE(0x0028, o_longText, "LongText");
OPCODE(0x0091, o_bitsRgn, "BitsRgn");
OPCODE(0x0099, o_packBitsRgn, "PackBitsRgn");
OPCODE(0x00A0, o_shortComment, "ShortComment");
OPCODE(0x00A1, o_longComment, "LongComment");
OPCODE(0x00FF, o_opEndPic, "OpEndPic");
OPCODE(0x0C00, o_headerOp, "HeaderOp");
OPCODE(0x1101, o_versionOp1, "VersionOp1");
}
void PICTDecoder::setupOpcodesNormal() {
setupOpcodesCommon();
OPCODE(0x0090, on_bitsRect, "BitsRect");
OPCODE(0x0098, on_packBitsRect, "PackBitsRect");
OPCODE(0x009A, on_directBitsRect, "DirectBitsRect");
OPCODE(0x8200, on_compressedQuickTime, "CompressedQuickTime");
}
void PICTDecoder::setupOpcodesQuickTime() {
setupOpcodesCommon();
OPCODE(0x0098, oq_packBitsRect, "PackBitsRect");
OPCODE(0x009A, oq_directBitsRect, "DirectBitsRect");
OPCODE(0x8200, oq_compressedQuickTime, "CompressedQuickTime");
}
#undef OPCODE
void PICTDecoder::o_nop(Common::SeekableReadStream &) {
// Nothing to do
}
void PICTDecoder::o_clip(Common::SeekableReadStream &stream) {
// Ignore
int size = stream.readUint16BE();
debugC(3, kDebugLevelGGraphics, "CLIP: size is %d", size);
if (size >= 10) {
int x1 = stream.readSint16BE();
int y1 = stream.readSint16BE();
int x2 = stream.readSint16BE();
int y2 = stream.readSint16BE();
debugC(3, kDebugLevelGGraphics, "CLIP: RECT encountered: %d %d %d %d", x1, y1, x2, y2);
stream.skip(size - 10);
debugC(3, kDebugLevelGGraphics, "CLIP: skipped %d bytes", size - 10);
} else {
stream.skip(size - 2);
debugC(3, kDebugLevelGGraphics, "CLIP: skipped %d bytes", size - 2);
}
}
void PICTDecoder::o_txFont(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
}
void PICTDecoder::o_txFace(Common::SeekableReadStream &stream) {
// Ignore
stream.readByte();
}
void PICTDecoder::o_pnSize(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
stream.readUint16BE();
}
void PICTDecoder::o_pnPat(Common::SeekableReadStream &stream) {
for (int i = 0; i < 8; i++) {
_penPattern[i] = stream.readByte();
}
}
void PICTDecoder::o_txSize(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
}
void PICTDecoder::o_txRatio(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
stream.readUint16BE();
stream.readUint16BE();
stream.readUint16BE();
}
void PICTDecoder::o_versionOp(Common::SeekableReadStream &stream) {
// We only support v2 extended
if (stream.readUint16BE() != 0x02FF)
error("Unknown PICT version");
else
_version = 2;
}
void PICTDecoder::o_versionOp1(Common::SeekableReadStream& stream) {
_version = 1;
}
void PICTDecoder::o_shortLine(Common::SeekableReadStream &stream) {
// Read the pen location (pnLoc)
int16 pnLocX = stream.readSint16BE();
int16 pnLocY = stream.readSint16BE();
// Update the current pen position
_currentPenPosition.x = pnLocX;
_currentPenPosition.y = pnLocY;
// Read the relative coordinates for the end of the line (dh, dv)
int8 dh = stream.readByte(); // Delta horizontal
int8 dv = stream.readByte(); // Delta vertical
// Calculate the end position of the line
int16 endX = pnLocX + dh;
int16 endY = pnLocY + dv;
// Draw the line from the current pen location to the new position
//drawLine(pnLocX, pnLocY, endX, endY);
// Update the pen position
_currentPenPosition.x = endX;
_currentPenPosition.y = endY;
}
void PICTDecoder::o_shortLineFrom(Common::SeekableReadStream &stream) {
// Read the relative coordinates (dh, dv)
int8 dh = stream.readByte(); // Delta horizontal
int8 dv = stream.readByte(); // Delta vertical
// Calculate the new pen position
int16 newX = _currentPenPosition.x + dh;
int16 newY = _currentPenPosition.y + dv;
// Draw the line from the current pen position to the new position
//drawLine(_currentPenPosition.x, _currentPenPosition.y, newX, newY);
// Update the pen position
_currentPenPosition.x = newX;
_currentPenPosition.y = newY;
}
void PICTDecoder::o_longText(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
stream.readUint16BE();
stream.skip(stream.readByte());
}
void PICTDecoder::o_bitsRgn(Common::SeekableReadStream &stream) {
// Copy unpacked data with clipped region (8bpp or lower)
unpackBitsRectOrRgn(stream, false, true);
}
void PICTDecoder::o_packBitsRgn(Common::SeekableReadStream &stream) {
unpackBitsRectOrRgn(stream, true, true);
}
void PICTDecoder::o_shortComment(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
}
void PICTDecoder::o_longComment(Common::SeekableReadStream &stream) {
// Ignore
stream.readUint16BE();
stream.skip(stream.readUint16BE());
}
void PICTDecoder::o_opEndPic(Common::SeekableReadStream &stream) {
// We've reached the end of the picture
_continueParsing = false;
}
void PICTDecoder::o_headerOp(Common::SeekableReadStream &stream) {
// Read the basic header, but we don't really have to do anything with it
/* uint16 version = */ stream.readUint16BE();
stream.readUint16BE(); // Reserved
/* uint32 hRes = */ stream.readUint32BE();
/* uint32 vRes = */ stream.readUint32BE();
Common::Rect origResRect;
origResRect.top = stream.readUint16BE();
origResRect.left = stream.readUint16BE();
origResRect.bottom = stream.readUint16BE();
origResRect.right = stream.readUint16BE();
stream.readUint32BE(); // Reserved
}
void PICTDecoder::on_bitsRect(Common::SeekableReadStream &stream) {
// Copy unpacked data with clipped rectangle (8bpp or lower)
unpackBitsRectOrRgn(stream, false, false);
}
void PICTDecoder::on_packBitsRect(Common::SeekableReadStream &stream) {
// Unpack data (8bpp or lower)
unpackBitsRectOrRgn(stream, true, false);
}
void PICTDecoder::on_directBitsRect(Common::SeekableReadStream &stream) {
// Unpack data (16bpp or higher)
PixMap pixMap = readRowBytes(stream, true);
pixMap.rowBytes = pixMap.rowBytes & 0x7fff;
unpackBitsRect(stream, false, pixMap);
}
void PICTDecoder::unpackBitsRectOrRgn(Common::SeekableReadStream &stream, bool compressed, bool hasRegion) {
PixMap pixMap = readRowBytes(stream, false);
bool hasPixMap = (pixMap.rowBytes & 0x8000);
pixMap.rowBytes = pixMap.rowBytes & 0x7fff;
if (hasPixMap)
unpackBitsRect(stream, true, pixMap);
else
unpackBits(stream, compressed, hasRegion);
}
void PICTDecoder::on_compressedQuickTime(Common::SeekableReadStream &stream) {
// OK, here's the fun. We get to completely change how QuickDraw draws
// the data in PICT files.
// Swap out the opcodes to the new ones
_opcodes.clear();
setupOpcodesQuickTime();
// We'll decode the first QuickTime data from here, but the QuickTime-specific
// opcodes will take over from here on out. Normal opcodes, signing off.
decodeCompressedQuickTime(stream);
}
void PICTDecoder::oq_packBitsRect(Common::SeekableReadStream &stream) {
// Skip any data here (8bpp or lower)
skipBitsRect(stream, true);
}
void PICTDecoder::oq_directBitsRect(Common::SeekableReadStream &stream) {
// Skip any data here (16bpp or higher)
skipBitsRect(stream, false);
}
void PICTDecoder::oq_compressedQuickTime(Common::SeekableReadStream &stream) {
// Just pass the data along
decodeCompressedQuickTime(stream);
}
bool PICTDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
// Initialize opcodes to their normal state
_opcodes.clear();
setupOpcodesNormal();
_continueParsing = true;
_palette.clear();
uint16 fileSize = stream.readUint16BE();
// If we have no file size here, we probably have a PICT from a file
// and not a resource. The other two bytes are the fileSize which we
// don't actually need (and already read if from a resource).
if (!fileSize)
stream.seek(512 + 2);
_imageRect.top = stream.readUint16BE();
_imageRect.left = stream.readUint16BE();
_imageRect.bottom = stream.readUint16BE();
_imageRect.right = stream.readUint16BE();
_imageRect.debugPrintC(8, kDebugLevelGGraphics, "PICTDecoder::loadStream(): loaded rect: ");
// NOTE: This is only a subset of the full PICT format.
// - Only V2 (Extended) Images Supported
// - CompressedQuickTime compressed data is supported
// - DirectBitsRect/PackBitsRect compressed data is supported
for (uint32 opNum = 0; !stream.eos() && !stream.err() && stream.pos() < stream.size() && _continueParsing; opNum++) {
// PICT v2 opcodes are two bytes
uint16 opcode;
if (_version != 1)
opcode = stream.readUint16BE();
else
opcode = stream.readByte();
if (opNum == 0 && (opcode != 0x0011 && opcode != 0x1101)) {
warning("Cannot find PICT version opcode");
return false;
} else if (opNum == 1 && _version == 2 && opcode != 0x0C00) {
warning("Cannot find PICT header opcode");
return false;
}
// Since opcodes are word-aligned, we need to mark our starting
// position here.
uint32 startPos = stream.pos();
for (uint32 i = 0; i < _opcodes.size(); i++) {
if (_opcodes[i].op == opcode) {
debugC(4, kDebugLevelGGraphics, "Running PICT opcode %04x '%s'", opcode, _opcodes[i].desc);
(this->*(_opcodes[i].proc))(stream);
break;
} else if (i == _opcodes.size() - 1) {
// Unknown opcode; attempt to continue forward
warning("Unknown PICT opcode %04x", opcode);
}
}
// Align
if (_version == 2)
stream.skip((stream.pos() - startPos) & 1);
}
return _outputSurface;
}
PICTDecoder::PixMap PICTDecoder::readPixMap(Common::SeekableReadStream &stream, bool hasBaseAddr, bool hasRowBytes) {
PixMap pixMap;
if (hasRowBytes) {
pixMap.baseAddr = hasBaseAddr ? stream.readUint32BE() : 0;
uint16 rowBytes = stream.readUint16BE();
pixMap.rowBytes = rowBytes & 0x7fff;
}
pixMap.bounds.top = stream.readUint16BE();
pixMap.bounds.left = stream.readUint16BE();
pixMap.bounds.bottom = stream.readUint16BE();
pixMap.bounds.right = stream.readUint16BE();
pixMap.pmVersion = stream.readUint16BE();
pixMap.packType = stream.readUint16BE();
pixMap.packSize = stream.readUint32BE();
pixMap.hRes = stream.readUint32BE();
pixMap.vRes = stream.readUint32BE();
pixMap.pixelType = stream.readUint16BE();
pixMap.pixelSize = stream.readUint16BE();
pixMap.cmpCount = stream.readUint16BE();
pixMap.cmpSize = stream.readUint16BE();
pixMap.planeBytes = stream.readUint32BE();
pixMap.pmTable = stream.readUint32BE();
pixMap.pmReserved = stream.readUint32BE();
return pixMap;
}
PICTDecoder::PixMap PICTDecoder::readRowBytes(Common::SeekableReadStream &stream, bool hasBaseAddr) {
PixMap pixMap;
pixMap.baseAddr = hasBaseAddr ? stream.readUint32BE() : 0;
uint16 rowBytes = stream.readUint16BE();
pixMap.rowBytes = rowBytes;
return pixMap;
}
struct PackBitsRectData {
PICTDecoder::PixMap pixMap;
Common::Rect srcRect;
Common::Rect dstRect;
uint16 mode;
};
void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette, PixMap pixMap) {
PackBitsRectData packBitsData;
packBitsData.pixMap = readPixMap(stream, !withPalette, false);
packBitsData.pixMap.baseAddr = pixMap.baseAddr;
packBitsData.pixMap.rowBytes = pixMap.rowBytes;
// Read in the palette if there is one present
if (withPalette) {
// See https://developer.apple.com/library/archive/documentation/mac/QuickDraw/QuickDraw-267.html
stream.readUint32BE(); // seed
stream.readUint16BE(); // flags
uint16 paletteColorCount = stream.readUint16BE() + 1;
_palette.resize(paletteColorCount, false);
for (uint16 i = 0; i < paletteColorCount; i++) {
stream.readUint16BE();
byte r = stream.readUint16BE() >> 8;
byte g = stream.readUint16BE() >> 8;
byte b = stream.readUint16BE() >> 8;
_palette.set(i, r, g, b);
}
}
packBitsData.srcRect.top = stream.readUint16BE();
packBitsData.srcRect.left = stream.readUint16BE();
packBitsData.srcRect.bottom = stream.readUint16BE();
packBitsData.srcRect.right = stream.readUint16BE();
packBitsData.dstRect.top = stream.readUint16BE();
packBitsData.dstRect.left = stream.readUint16BE();
packBitsData.dstRect.bottom = stream.readUint16BE();
packBitsData.dstRect.right = stream.readUint16BE();
packBitsData.mode = stream.readUint16BE();
uint16 width = packBitsData.srcRect.width();
uint16 height = packBitsData.srcRect.height();
byte bytesPerPixel = 0;
if (packBitsData.pixMap.pixelSize <= 8)
bytesPerPixel = 1;
else if (packBitsData.pixMap.pixelSize == 32)
bytesPerPixel = packBitsData.pixMap.cmpCount;
else
bytesPerPixel = packBitsData.pixMap.pixelSize / 8;
// Ensure we have enough space in the buffer to hold an entire line's worth of pixels
uint32 lineSize = MAX<int>(width * bytesPerPixel + (8 * 2 / packBitsData.pixMap.pixelSize), packBitsData.pixMap.rowBytes);
byte *buffer = new byte[lineSize * height]();
// Read in amount of data per row
for (uint16 i = 0; i < packBitsData.pixMap.bounds.height(); i++) {
// NOTE: Compression 0 is "default". The format in SCI games is packed when 0
// unless rowBytes is less than 8 in which case the pict can't be packed,
// such as the shovel inventory icon in FPFP Mac. (bug #7059)
// In the future, we may need to have something to set the "default" packing
// format, but this is good for now.
if (packBitsData.pixMap.packType == 1 || packBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte (on 24-bit)
// only support 1 bpp for now as there is currently only one known
// SCI pict that requires any unpacked support.
if (bytesPerPixel == 1 && packBitsData.pixMap.pixelSize == 8) {
stream.read(&buffer[i * width], width);
if (width < packBitsData.pixMap.rowBytes) {
// skip padding and/or clipped bytes
stream.seek(packBitsData.pixMap.rowBytes - width, SEEK_CUR);
}
} else {
// TODO: Finish this. Hasn't been needed (yet).
error("Unpacked DirectBitsRect data (padded) with bytes per pixel: %d and pixel size: %d", bytesPerPixel, packBitsData.pixMap.pixelSize);
}
} else if (packBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte (on 24-bit)
// TODO: Finish this. Hasn't been needed (yet).
error("Unpacked DirectBitsRect data (not padded)");
} else if (packBitsData.pixMap.packType == 0 || packBitsData.pixMap.packType > 2) { // Packed
uint16 byteCount = (packBitsData.pixMap.rowBytes > 250) ? stream.readUint16BE() : stream.readByte();
unpackBitsLine(buffer + i * width * bytesPerPixel, packBitsData.pixMap.rowBytes, stream.readStream(byteCount), packBitsData.pixMap.pixelSize, bytesPerPixel);
}
}
_outputSurface = new Graphics::Surface();
switch (bytesPerPixel) {
case 1:
// Just copy to the image
_outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
memcpy(_outputSurface->getPixels(), buffer, _outputSurface->w * _outputSurface->h);
break;
case 2:
// We have a 16-bit surface
_outputSurface->create(width, height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
for (uint16 y = 0; y < _outputSurface->h; y++)
for (uint16 x = 0; x < _outputSurface->w; x++)
WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_UINT16(buffer + (y * _outputSurface->w + x) * 2));
break;
case 3:
// We have a planar 24-bit surface
_outputSurface->create(width, height, Graphics::PixelFormat::createFormatRGB24());
for (uint16 y = 0; y < _outputSurface->h; y++) {
byte *dst = (byte *)_outputSurface->getBasePtr(0, y);
for (uint16 x = 0; x < _outputSurface->w; x++) {
*dst++ = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 0 + x);
*dst++ = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 1 + x);
*dst++ = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 2 + x);
}
}
break;
case 4:
// We have a planar 32-bit surface
// Note that we ignore the alpha channel since it seems to not be correct
// macOS does not ignore it, but then displays it incorrectly. Photoshop
// does ignore it and displays it correctly.
_outputSurface->create(width, height, Graphics::PixelFormat::createFormatRGB24());
for (uint16 y = 0; y < _outputSurface->h; y++) {
byte *dst = (byte *)_outputSurface->getBasePtr(0, y);
for (uint16 x = 0; x < _outputSurface->w; x++) {
// *dst++ = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 0 + x);
*dst++ = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 1 + x);
*dst++ = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x);
*dst++ = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x);
}
}
break;
default:
break;
}
delete[] buffer;
}
// TODO: It should be possible to merge this with unpackBitsRect, but that's
// a story for another day because this works for now.
void PICTDecoder::unpackBits(Common::SeekableReadStream &stream, bool compressed, bool hasRegion) {
if (!_outputSurface) {
_outputSurface = new Graphics::Surface();
_outputSurface->create(_imageRect.width(), _imageRect.height(), Graphics::PixelFormat::createFormatCLUT8());
}
int y1 = stream.readSint16BE();
int x1 = stream.readSint16BE();
int y2 = stream.readSint16BE();
int x2 = stream.readSint16BE();
stream.skip(8); // srcRect
stream.skip(8); // dstRect
stream.skip(2); // mode
if (hasRegion)
stream.skip(stream.readUint16BE() - 2);
Common::Rect outputRect(_outputSurface->w, _outputSurface->h);
if (!compressed) {
Common::BitStream8MSB bs(stream);
for (int y = y1; y < y2; y++) {
int yPos = y - _imageRect.top;
for (int x = x1; x < x2; x++) {
int xPos = x - _imageRect.left;
uint bit = bs.getBit();
if (outputRect.contains(xPos, yPos))
_outputSurface->setPixel(xPos, yPos, bit);
}
}
return;
}
for (int y = y1; y < y2; y++) {
int yPos = y - _imageRect.top;
int x = x1;
byte rowBytes = stream.readByte();
byte readBytes = 0;
while (readBytes < rowBytes) {
byte rowBuf[128];
byte bufLen;
byte value = stream.readByte();
readBytes++;
if (value >= 128) {
bufLen = (256 - value) + 1;
byte repeatValue = stream.readByte();
memset(rowBuf, repeatValue, bufLen);
readBytes++;
} else {
bufLen = value + 1;
stream.read(rowBuf, bufLen);
readBytes += bufLen;
}
Common::MemoryReadStream ms(rowBuf, bufLen);
Common::BitStream8MSB bs(ms);
for (int i = 0; i < 8 * bufLen; i++) {
int xPos = x - _imageRect.left;
uint bit = bs.getBit();
if (outputRect.contains(xPos, yPos))
_outputSurface->setPixel(xPos, yPos, bit);
x++;
}
}
}
}
void PICTDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) {
uint32 dataDecoded = 0;
byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1;
while (data->pos() < data->size() && dataDecoded < length) {
byte op = data->readByte();
if (op & 0x80) {
uint32 runSize = (op ^ 255) + 2;
uint16 value = (bytesPerDecode == 2) ? data->readUint16BE() : data->readByte();
for (uint32 i = 0; i < runSize; i++) {
if (bytesPerDecode == 2) {
WRITE_UINT16(out, value);
out += 2;
} else {
outputPixelBuffer(out, value, bitsPerPixel);
}
}
dataDecoded += runSize * bytesPerDecode;
} else {
uint32 runSize = op + 1;
if (bytesPerDecode == 1) {
for (uint32 i = 0; i < runSize; i++)
outputPixelBuffer(out, data->readByte(), bitsPerPixel);
} else {
for (uint32 i = 0; i < runSize; i++) {
WRITE_UINT16(out, data->readUint16BE());
out += 2;
}
}
dataDecoded += runSize * bytesPerDecode;
}
}
// HACK: Even if the data is 24-bit, rowBytes is still 32-bit
if (bytesPerPixel == 3)
dataDecoded += length / 4;
if (length != dataDecoded)
warning("Mismatched PackBits read (%d/%d)", dataDecoded, length);
delete data;
}
void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {
switch (bitsPerPixel) {
case 1:
for (int i = 7; i >= 0; i--)
*out++ = (value >> i) & 1;
break;
case 2:
for (int i = 6; i >= 0; i -= 2)
*out++ = (value >> i) & 3;
break;
case 4:
*out++ = (value >> 4) & 0xf;
*out++ = value & 0xf;
break;
default:
*out++ = value;
}
}
void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool withPalette) {
// Step through a PackBitsRect/DirectBitsRect function
if (!withPalette)
stream.readUint32BE();
uint16 rowBytes = stream.readUint16BE();
uint16 height = stream.readUint16BE();
stream.readUint16BE();
height = stream.readUint16BE() - height;
stream.readUint16BE();
uint16 packType;
// Top two bits signify PixMap vs BitMap
if (rowBytes & 0xC000) {
// PixMap
stream.readUint16BE();
packType = stream.readUint16BE();
stream.skip(14);
stream.readUint16BE(); // pixelSize
stream.skip(16);
if (withPalette) {
stream.readUint32BE();
stream.readUint16BE();
stream.skip((stream.readUint16BE() + 1) * 8);
}
rowBytes &= 0x7FFF;
} else {
// BitMap
packType = 0;
}
stream.skip(18);
for (uint16 i = 0; i < height; i++) {
if (packType == 1 || packType == 2 || rowBytes < 8)
error("Unpacked PackBitsRect data");
else if (packType == 0 || packType > 2)
stream.skip((rowBytes > 250) ? stream.readUint16BE() : stream.readByte());
}
}
// Compressed QuickTime details can be found here:
// https://developer.apple.com/library/archive/documentation/QuickTime/RM/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html
// https://developer.apple.com/library/archive/documentation/QuickTime/RM/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html
void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) {
// First, read all the fields from the opcode
uint32 dataSize = stream.readUint32BE();
uint32 startPos = stream.pos();
/* uint16 version = */ stream.readUint16BE();
// Read in the display matrix
uint32 matrix[3][3];
for (uint32 i = 0; i < 3; i++)
for (uint32 j = 0; j < 3; j++)
matrix[i][j] = stream.readUint32BE();
// We currently only support offseting images vertically from the matrix
uint16 xOffset = 0;
uint16 yOffset = matrix[2][1] >> 16;
uint32 matteSize = stream.readUint32BE();
stream.skip(8); // matte rect
/* uint16 transferMode = */ stream.readUint16BE();
stream.skip(8); // src rect
/* uint32 accuracy = */ stream.readUint32BE();
uint32 maskSize = stream.readUint32BE();
// Skip the matte and mask
stream.skip(matteSize + maskSize);
// Now we've reached the image descriptor, so read the relevant data from that
uint32 idStart = stream.pos();
uint32 idSize = stream.readUint32BE();
uint32 codecTag = stream.readUint32BE();
stream.skip(24); // miscellaneous stuff
uint16 width = stream.readUint16BE();
uint16 height = stream.readUint16BE();
stream.skip(8); // resolution, dpi
uint32 imageSize = stream.readUint32BE();
stream.skip(34);
uint16 bitsPerPixel = stream.readUint16BE();
stream.skip(idSize - (stream.pos() - idStart)); // more useless stuff
Common::SeekableSubReadStream imageStream(&stream, stream.pos(), stream.pos() + imageSize);
Codec *codec = createQuickTimeCodec(codecTag, width, height, bitsPerPixel);
if (!codec)
error("Unhandled CompressedQuickTime format");
const Graphics::Surface *surface = codec->decodeFrame(imageStream);
if (!surface)
error("PICTDecoder::decodeCompressedQuickTime(): Could not decode data");
if (!_outputSurface) {
_outputSurface = new Graphics::Surface();
_outputSurface->create(_imageRect.width(), _imageRect.height(), surface->format);
}
assert(_outputSurface->format == surface->format);
Common::Rect outputRect(surface->w, surface->h);
outputRect.translate(xOffset, yOffset);
outputRect.clip(_imageRect);
for (uint16 y = 0; y < outputRect.height(); y++)
memcpy(_outputSurface->getBasePtr(outputRect.left, y + outputRect.top), surface->getBasePtr(0, y), outputRect.width() * surface->format.bytesPerPixel);
stream.seek(startPos + dataSize);
delete codec;
}
} // End of namespace Image

158
image/pict.h Normal file
View File

@@ -0,0 +1,158 @@
/* 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_PICT_H
#define IMAGE_PICT_H
#include "common/array.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "graphics/palette.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_pict PICT decoder
* @ingroup image
*
* @brief Decoder for PICT images.
*
* Used in engines:
* - Mohawk
* - Pegasus
* - SCI
* @{
*/
#define DECLARE_OPCODE(x) void x(Common::SeekableReadStream &stream)
class PICTDecoder : public ImageDecoder {
public:
PICTDecoder();
~PICTDecoder();
// ImageDecoder API
bool loadStream(Common::SeekableReadStream &stream) override;
void destroy() override;
const Graphics::Surface *getSurface() const override { return _outputSurface; }
const Graphics::Palette &getPalette() const override { return _palette; }
struct PixMap {
uint32 baseAddr;
uint16 rowBytes;
Common::Rect bounds;
uint16 pmVersion;
uint16 packType;
uint32 packSize;
uint32 hRes;
uint32 vRes;
uint16 pixelType;
uint16 pixelSize;
uint16 cmpCount;
uint16 cmpSize;
uint32 planeBytes;
uint32 pmTable;
uint32 pmReserved;
};
static PixMap readRowBytes(Common::SeekableReadStream &stream, bool hasBaseAddr = true);
static PixMap readPixMap(Common::SeekableReadStream &stream, bool hasBaseAddr = true, bool hasRowBytes = true);
private:
Common::Rect _imageRect;
Graphics::Palette _palette;
byte _penPattern[8];
Common::Point _currentPenPosition;
Graphics::Surface *_outputSurface;
bool _continueParsing;
int _version;
// Utility Functions
void unpackBitsRectOrRgn(Common::SeekableReadStream &stream, bool compressed, bool hasRegion);
void unpackBits(Common::SeekableReadStream &stream, bool compressed, bool hasRegion);
void unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette, PixMap pixMap);
void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel);
void skipBitsRect(Common::SeekableReadStream &stream, bool withPalette);
void decodeCompressedQuickTime(Common::SeekableReadStream &stream);
void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel);
// Opcodes
typedef void (PICTDecoder::*OpcodeProcPICT)(Common::SeekableReadStream &stream);
struct PICTOpcode {
PICTOpcode() { op = 0; proc = 0; desc = 0; }
PICTOpcode(uint16 o, OpcodeProcPICT p, const char *d) { op = o; proc = p; desc = d; }
uint16 op;
OpcodeProcPICT proc;
const char *desc;
};
Common::Array<PICTOpcode> _opcodes;
// Common Opcodes
void setupOpcodesCommon();
DECLARE_OPCODE(o_nop);
DECLARE_OPCODE(o_clip);
DECLARE_OPCODE(o_txFont);
DECLARE_OPCODE(o_txFace);
DECLARE_OPCODE(o_pnSize);
DECLARE_OPCODE(o_pnPat);
DECLARE_OPCODE(o_txSize);
DECLARE_OPCODE(o_txRatio);
DECLARE_OPCODE(o_versionOp);
DECLARE_OPCODE(o_shortLine);
DECLARE_OPCODE(o_shortLineFrom);
DECLARE_OPCODE(o_longText);
DECLARE_OPCODE(o_bitsRgn);
DECLARE_OPCODE(o_packBitsRgn);
DECLARE_OPCODE(o_shortComment);
DECLARE_OPCODE(o_longComment);
DECLARE_OPCODE(o_opEndPic);
DECLARE_OPCODE(o_headerOp);
DECLARE_OPCODE(o_versionOp1);
// Regular-mode Opcodes
void setupOpcodesNormal();
DECLARE_OPCODE(on_bitsRect);
DECLARE_OPCODE(on_packBitsRect);
DECLARE_OPCODE(on_directBitsRect);
DECLARE_OPCODE(on_compressedQuickTime);
// QuickTime-mode Opcodes
void setupOpcodesQuickTime();
DECLARE_OPCODE(oq_packBitsRect);
DECLARE_OPCODE(oq_directBitsRect);
DECLARE_OPCODE(oq_compressedQuickTime);
};
#undef DECLARE_OPCODE
/** @} */
} // End of namespace Image
#endif

368
image/png.cpp Normal file
View File

@@ -0,0 +1,368 @@
/* 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/>.
*
*/
// Since we need to work with libpng here, we need to allow all symbols
// to avoid compilation issues.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#ifdef USE_PNG
#include <png.h>
#endif
#include "image/png.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "common/debug.h"
#include "common/array.h"
#include "common/stream.h"
namespace Image {
PNGDecoder::PNGDecoder() :
_outputSurface(0),
_palette(0),
_skipSignature(false),
_keepTransparencyPaletted(false),
_hasTransparentColor(false),
_transparentColor(0) {
}
PNGDecoder::~PNGDecoder() {
destroy();
}
void PNGDecoder::destroy() {
if (_outputSurface) {
_outputSurface->free();
delete _outputSurface;
_outputSurface = 0;
}
_palette.clear();
_hasTransparentColor = false;
}
Graphics::PixelFormat PNGDecoder::getByteOrderRgbaPixelFormat(bool isAlpha) const {
if (isAlpha)
return Graphics::PixelFormat::createFormatRGBA32();
else
return Graphics::PixelFormat::createFormatRGB24();
}
#ifdef USE_PNG
// libpng-error-handling:
void pngError(png_structp pngptr, png_const_charp errorMsg) {
error("libpng: %s", errorMsg);
}
void pngWarning(png_structp pngptr, png_const_charp warningMsg) {
debug(3, "libpng: %s", warningMsg);
}
// libpng-I/O-helpers:
void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) {
void *readIOptr = png_get_io_ptr(pngPtr);
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr;
stream->read(data, length);
}
void pngWriteToStream(png_structp pngPtr, png_bytep data, png_size_t length) {
void *writeIOptr = png_get_io_ptr(pngPtr);
Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
stream->write(data, length);
}
void pngFlushStream(png_structp pngPtr) {
void *writeIOptr = png_get_io_ptr(pngPtr);
Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
stream->flush();
}
#endif
/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
*
* Licensed under GNU GPL v2
*
*/
bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
#ifdef USE_PNG
destroy();
// First, check the PNG signature (if not set to skip it)
if (!_skipSignature) {
if (stream.readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
return false;
}
if (stream.readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) {
return false;
}
}
// The following is based on the guide provided in:
// https://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3
// https://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf
// along with the png-loading code used in the sword25-engine.
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngPtr) {
return false;
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_read_struct(&pngPtr, NULL, NULL);
return false;
}
png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
// TODO: The manual says errors should be handled via setjmp
png_set_read_fn(pngPtr, &stream, pngReadFromStream);
png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE);
// We already verified the PNG-header
png_set_sig_bytes(pngPtr, 8);
// Read PNG header
png_read_info(pngPtr, infoPtr);
// No handling for unknown chunks yet.
int bitDepth, colorType, width, height, interlaceType;
png_uint_32 w, h;
uint32 rgbaPalette[256];
bool hasRgbaPalette = false;
png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL);
width = w;
height = h;
// Allocate memory for the final image data.
// To keep memory framentation low this happens before allocating memory for temporary image data.
_outputSurface = new Graphics::Surface();
// Images of all color formats except PNG_COLOR_TYPE_PALETTE
// will be transformed into ARGB images
if (colorType == PNG_COLOR_TYPE_PALETTE && (_keepTransparencyPaletted || !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))) {
int numPalette = 0;
png_colorp palette = NULL;
png_bytep trans = nullptr;
int numTrans = 0;
uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette);
if (success != PNG_INFO_PLTE) {
png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
return false;
}
_palette.resize(numPalette, false);
for (int i = 0; i < numPalette; i++) {
_palette.set(i, palette[i].red, palette[i].green, palette[i].blue);
}
if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
png_color_16p transColor;
png_get_tRNS(pngPtr, infoPtr, &trans, &numTrans, &transColor);
if (numTrans == 1) {
// For a single transparency color, the alpha should be fully transparent
assert(*trans == 0);
_hasTransparentColor = true;
_transparentColor = 0;
} else {
// Multiple alphas are being specified for the palette, so we can't use
// _transparentColor, and will instead need to build an RGBA surface
assert(numTrans > 1);
hasRgbaPalette = true;
}
}
_outputSurface->create(width, height,
hasRgbaPalette ? getByteOrderRgbaPixelFormat(true) : Graphics::PixelFormat::createFormatCLUT8());
png_set_packing(pngPtr);
if (hasRgbaPalette) {
// Build up the RGBA palette using the transparency alphas
Common::fill(&rgbaPalette[0], &rgbaPalette[256], 0);
for (int i = 0; i < numPalette; ++i) {
byte a = (i < numTrans) ? trans[i] : 0xff;
rgbaPalette[i] = _outputSurface->format.ARGBToColor(
a, palette[i].red, palette[i].green, palette[i].blue);
}
// We won't be needing a separate palette
_palette.clear();
}
} else {
bool isAlpha = (colorType & PNG_COLOR_MASK_ALPHA);
if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
isAlpha = true;
png_set_expand(pngPtr);
}
_outputSurface->create(width, height, getByteOrderRgbaPixelFormat(isAlpha));
if (!_outputSurface->getPixels()) {
error("Could not allocate memory for output image.");
}
if (bitDepth == 16)
png_set_strip_16(pngPtr);
if (bitDepth < 8)
png_set_expand(pngPtr);
if (colorType == PNG_COLOR_TYPE_GRAY ||
colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(pngPtr);
}
// After the transformations have been registered, the image data is read again.
png_set_interlace_handling(pngPtr);
png_read_update_info(pngPtr, infoPtr);
png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);
width = w;
height = h;
if (hasRgbaPalette) {
// Build up the RGBA surface from paletted rows
png_bytep rowPtr = new byte[width];
if (!rowPtr)
error("Could not allocate memory for row.");
for (int yp = 0; yp < height; ++yp) {
png_read_row(pngPtr, rowPtr, nullptr);
uint32 *destRowP = (uint32 *)_outputSurface->getBasePtr(0, yp);
for (int xp = 0; xp < width; ++xp)
destRowP[xp] = rgbaPalette[rowPtr[xp]];
}
delete[] rowPtr;
} else if (interlaceType == PNG_INTERLACE_NONE) {
// PNGs without interlacing can simply be read row by row.
for (int i = 0; i < height; i++) {
png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL);
}
} else {
// PNGs with interlacing require us to allocate an auxiliary
// buffer with pointers to all row starts.
// Allocate row pointer buffer
png_bytep *rowPtr = new png_bytep[height];
if (!rowPtr) {
error("Could not allocate memory for row pointers.");
}
// Initialize row pointers
for (int i = 0; i < height; i++)
rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i);
// Read image data
png_read_image(pngPtr, rowPtr);
// Free row pointer buffer
delete[] rowPtr;
}
// Read additional data at the end.
png_read_end(pngPtr, NULL);
// Destroy libpng structures
png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
return true;
#else
return false;
#endif
}
bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const Graphics::Palette &palette) {
return writePNG(out, input, palette.data(), palette.size());
}
bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const byte *palette, uint paletteCount) {
#ifdef USE_PNG
const Graphics::PixelFormat requiredFormat_3byte = Graphics::PixelFormat::createFormatRGB24();
const Graphics::PixelFormat requiredFormat_4byte = Graphics::PixelFormat::createFormatRGBA32();
int colorType;
Graphics::Surface *tmp = NULL;
const Graphics::Surface *surface;
if (input.format == requiredFormat_3byte) {
surface = &input;
colorType = PNG_COLOR_TYPE_RGB;
} else {
if (input.format == requiredFormat_4byte) {
surface = &input;
} else {
surface = tmp = input.convertTo(requiredFormat_4byte, palette, paletteCount);
}
colorType = PNG_COLOR_TYPE_RGB_ALPHA;
}
png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngPtr) {
if (tmp) {
tmp->free();
delete tmp;
}
return false;
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_write_struct(&pngPtr, NULL);
if (tmp) {
tmp->free();
delete tmp;
}
return false;
}
png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
// TODO: The manual says errors should be handled via setjmp
png_set_write_fn(pngPtr, &out, pngWriteToStream, pngFlushStream);
png_set_IHDR(pngPtr, infoPtr, surface->w, surface->h, 8, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
Common::Array<const uint8 *> rows;
rows.reserve(surface->h);
for (int y = 0; y < surface->h; ++y) {
rows.push_back((const uint8 *)surface->getBasePtr(0, y));
}
png_set_rows(pngPtr, infoPtr, const_cast<uint8 **>(&rows.front()));
png_write_png(pngPtr, infoPtr, 0, NULL);
png_destroy_write_struct(&pngPtr, &infoPtr);
// free tmp surface
if (tmp) {
tmp->free();
delete tmp;
}
return true;
#else
return false;
#endif
}
} // End of namespace Image

107
image/png.h Normal file
View File

@@ -0,0 +1,107 @@
/* 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_PNG_H
#define IMAGE_PNG_H
#include "common/scummsys.h"
#include "common/textconsole.h"
#include "graphics/palette.h"
#include "graphics/pixelformat.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_png PNG decoder
* @ingroup image
*
* @brief Decoder for PNG images.
*
* This decoder has a dependency on the libpng library.
*
* Used in engines:
* - Sword25
* - TwinE
* - Wintermute
* @{
*/
class PNGDecoder : public ImageDecoder {
public:
PNGDecoder();
~PNGDecoder();
bool loadStream(Common::SeekableReadStream &stream) override;
void destroy() override;
const Graphics::Surface *getSurface() const override { return _outputSurface; }
const Graphics::Palette &getPalette() const override { return _palette; }
bool hasTransparentColor() const override { return _hasTransparentColor; }
uint32 getTransparentColor() const override { return _transparentColor; }
void setSkipSignature(bool skip) { _skipSignature = skip; }
void setKeepTransparencyPaletted(bool keep) { _keepTransparencyPaletted = keep; }
private:
Graphics::PixelFormat getByteOrderRgbaPixelFormat(bool isAlpha) const;
Graphics::Palette _palette;
// flag to skip the png signature check for headless png files
bool _skipSignature;
// Flag to keep paletted images paletted, even when the image has transparency
bool _keepTransparencyPaletted;
bool _hasTransparentColor;
uint32 _transparentColor;
Graphics::Surface *_outputSurface;
};
/**
* Outputs a compressed PNG stream of the given input surface.
*
* @param out Stream to which to write the PNG image.
* @param input The surface to save as a PNG image..
* @param palette The palette (in RGB888), if the source format has a bpp of 1.
* @param paletteCount Number of colors in the palette (default: 256).
*/
bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const byte *palette = nullptr, uint paletteCount = 256);
/**
* Outputs a compressed PNG stream of the given input surface.
*
* @param out Stream to which to write the PNG image.
* @param input The surface to save as a PNG image..
* @param palette The palette if the source format has a bpp of 1.
*/
bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const Graphics::Palette &palette);
/** @} */
} // End of namespace Image
#endif

115
image/scr.cpp Normal file
View File

@@ -0,0 +1,115 @@
/* 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/stream.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "image/scr.h"
namespace Image {
static const byte scrPalette[16 * 3] = {
0x00, 0x00, 0x00, /* black */
0x00, 0x00, 0xD8, /* blue */
0xD8, 0x00, 0x00, /* red */
0xD8, 0x00, 0xD8, /* magenta */
0x00, 0xD8, 0x00, /* green */
0x00, 0xD8, 0xD8, /* cyan */
0xD8, 0xD8, 0x00, /* yellow */
0xD8, 0xD8, 0xD8, /* white */
0x00, 0x00, 0x00, /* bright black */
0x00, 0x00, 0xFF, /* bright blue */
0xFF, 0x00, 0x00, /* bright red */
0xFF, 0x00, 0xFF, /* bright magenta */
0x00, 0xFF, 0x00, /* bright green */
0x00, 0xFF, 0xFF, /* bright cyan */
0xFF, 0xFF, 0x00, /* bright yellow */
0xFF, 0xFF, 0xFF, /* bright white */
};
ScrDecoder::ScrDecoder() : _surface(nullptr), _palette(scrPalette, 16) {
}
ScrDecoder::~ScrDecoder() {
destroy();
}
void ScrDecoder::destroy() {
if (_surface) {
_surface->free();
delete _surface;
_surface = nullptr;
}
}
uint32 ScrDecoder::getPixelAddress(int x, int y) {
uint32 y76 = y & 0xc0;
uint32 y53 = y & 0x38;
uint32 y20 = y & 0x07;
return (y76 << 5) + (y20 << 8) + (y53 << 2) + (x >> 3);
}
uint32 ScrDecoder::getAttributeAddress(int x, int y) {
uint32 y73 = y & 0xf8;
return (y73 << 2) + (x >> 3);
}
bool ScrDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
if (stream.size() != 6912)
warning("Header check failed for reading scr image");
byte *data = (byte *)malloc(6144 * sizeof(byte));
byte *attributes = (byte *)malloc(768 * sizeof(byte));
stream.read(data, 6144);
stream.read(attributes, 768);
int width = 256;
int height = 192;
_surface = new Graphics::Surface();
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
for (int y = 0; y < height; y++) {
for (int col = 0; col < width >> 3; col++) {
int x = col << 3;
byte byt = data[getPixelAddress(x, y)];
byte attr = attributes[getAttributeAddress(x, y)];
byte ink = attr & 0x07;
byte paper = (attr >> 3) & 0x07;
byte bright = (attr >> 6) & 1;
for (int bit = 0; bit < 8; bit++) {
bool set = (byt >> (7 - bit)) & 1;
int color = (bright << 3) | (set ? ink : paper);
_surface->setPixel(x + bit, y, color);
}
}
}
free(data);
free(attributes);
return true;
}
} // End of namespace Image

65
image/scr.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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_SCR_H
#define IMAGE_SCR_H
#include "graphics/palette.h"
#include "image/image_decoder.h"
/**
* @defgroup image_scr SCR decoder
* @ingroup image
*
* @brief Decoder for ZX-Spectrum SCREEN$ based on:
* https://gist.github.com/alexanderk23/f459c76847d9412548f7
*
*
* Used in engines:
* - Freescape
* @{
*/
namespace Common {
class SeekableReadStream;
}
namespace Image {
class ScrDecoder : public Image::ImageDecoder {
public:
ScrDecoder();
virtual ~ScrDecoder();
// ImageDecoder API
void destroy();
virtual bool loadStream(Common::SeekableReadStream &stream);
virtual const Graphics::Surface *getSurface() const { return _surface; }
const Graphics::Palette &getPalette() const { return _palette; }
private:
Graphics::Surface *_surface;
Graphics::Palette _palette;
uint32 getPixelAddress(int x, int y);
uint32 getAttributeAddress(int x, int y);
};
} // End of namespace Image
#endif // IMAGE_SCR_H

410
image/tga.cpp Normal file
View File

@@ -0,0 +1,410 @@
/* 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 code from xoreos https://github.com/DrMcCoy/xoreos/
* relicensed under GPLv2+ with permission from DrMcCoy and clone2727
*/
#include "image/tga.h"
#include "common/util.h"
#include "common/algorithm.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/error.h"
namespace Image {
TGADecoder::TGADecoder() : _colorMap(0) {
_colorMapSize = 0;
_colorMapOrigin = 0;
_colorMapLength = 0;
_colorMapEntryLength = 0;
}
TGADecoder::~TGADecoder() {
destroy();
}
void TGADecoder::destroy() {
_surface.free();
_colorMap.clear();
}
bool TGADecoder::loadStream(Common::SeekableReadStream &tga) {
destroy();
byte imageType, pixelDepth;
bool success;
success = readHeader(tga, imageType, pixelDepth);
if (success) {
switch (imageType) {
case TYPE_BW:
case TYPE_TRUECOLOR:
success = readData(tga, imageType, pixelDepth);
break;
case TYPE_RLE_BW:
case TYPE_RLE_TRUECOLOR:
case TYPE_RLE_CMAP:
success = readDataRLE(tga, imageType, pixelDepth);
break;
case TYPE_CMAP:
success = readDataColorMapped(tga, imageType, pixelDepth);
break;
default:
success = false;
break;
}
}
if (tga.err() || !success) {
warning("Failed reading TGA-file");
return false;
}
return success;
}
bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) {
if (!tga.seek(0)) {
warning("Failed reading TGA-file");
return false;
}
// TGAs have an optional "id" string in the header
uint32 idLength = tga.readByte();
// Number of colors in the color map / palette
int hasColorMap = tga.readByte();
// Image type. See header for numeric constants
imageType = tga.readByte();
switch (imageType) {
case TYPE_CMAP:
case TYPE_TRUECOLOR:
case TYPE_BW:
case TYPE_RLE_CMAP:
case TYPE_RLE_TRUECOLOR:
case TYPE_RLE_BW:
break;
default:
warning("Unsupported image type: %d", imageType);
return false;
}
// Color map specifications
if (hasColorMap == 0) {
tga.skip(5);
} else {
_colorMapOrigin = tga.readUint16LE();
_colorMapLength = tga.readUint16LE();
_colorMapEntryLength = tga.readByte();
}
// Origin-defintions
tga.skip(2 + 2);
// Image dimensions
_surface.w = tga.readUint16LE();
_surface.h = tga.readUint16LE();
// Bits per pixel
pixelDepth = tga.readByte();
_surface.format.bytesPerPixel = pixelDepth / 8;
// Image descriptor
byte imgDesc = tga.readByte();
int attributeBits = imgDesc & 0x0F;
assert((imgDesc & 0x10) == 0);
_originTop = (imgDesc & 0x20);
// Interleaving is not handled at this point
//int interleave = (imgDesc & 0xC);
if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) {
if (pixelDepth == 8) {
_format = Graphics::PixelFormat::createFormatCLUT8();
} else {
warning("Unsupported index-depth: %d", pixelDepth);
return false;
}
} else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) {
if (pixelDepth == 24) {
_format = Graphics::PixelFormat::createFormatBGR24();
} else if (pixelDepth == 32) {
// HACK: According to the spec, attributeBits should determine the amount
// of alpha-bits, however, as the game files that use this decoder seems
// to ignore that fact, we force the amount to 8 for 32bpp files for now.
_format = Graphics::PixelFormat::createFormatBGRA32(/* attributeBits */);
} else if (pixelDepth == 16) {
// 16bpp TGA is ARGB1555
_format = Graphics::PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15);
} else {
warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
return false;
}
} else if (imageType == TYPE_BW || imageType == TYPE_RLE_BW) {
if (pixelDepth == 8) {
_format = Graphics::PixelFormat::createFormatBGR24();
} else {
warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
return false;
}
} else {
warning("Unsupported image type: %d", imageType);
return false;
}
// Skip the id string
tga.skip(idLength);
if (hasColorMap) {
return readColorMap(tga, imageType, pixelDepth);
}
return true;
}
bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
_colorMap.resize(_colorMapLength, false);
for (int i = 0; i < _colorMapLength; i++) {
byte r, g, b;
if (_colorMapEntryLength == 32) {
b = tga.readByte();
g = tga.readByte();
r = tga.readByte();
tga.readByte(); // for alpha
} else if (_colorMapEntryLength == 24) {
b = tga.readByte();
g = tga.readByte();
r = tga.readByte();
} else if (_colorMapEntryLength == 16) {
static const Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
uint16 color = tga.readUint16LE();
format.colorToRGB(color, r, g, b);
} else {
warning("Unsupported image type: %d", imageType);
r = g = b = 0;
}
_colorMap.set(i, r, g, b);
}
return true;
}
// Additional information found from https://paulbourke.net/dataformats/tga/
// With some details from the link referenced in the header.
bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
// TrueColor
if (imageType == TYPE_TRUECOLOR) {
_surface.create(_surface.w, _surface.h, _format);
if (pixelDepth == 16) {
for (int i = 0; i < _surface.h; i++) {
uint16 *dst;
if (!_originTop) {
dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1);
} else {
dst = (uint16 *)_surface.getBasePtr(0, i);
}
for (int j = 0; j < _surface.w; j++) {
*dst++ = tga.readUint16LE();
}
}
} else if (pixelDepth == 32) {
for (int i = 0; i < _surface.h; i++) {
uint32 *dst;
if (!_originTop) {
dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1);
} else {
dst = (uint32 *)_surface.getBasePtr(0, i);
}
tga.read(dst, _surface.w * 4);
}
} else if (pixelDepth == 24) {
for (int i = 0; i < _surface.h; i++) {
byte *dst;
if (!_originTop) {
dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
} else {
dst = (byte *)_surface.getBasePtr(0, i);
}
tga.read(dst, _surface.w * 3);
}
}
// Black/White
} else if (imageType == TYPE_BW) {
_surface.create(_surface.w, _surface.h, _format);
byte *data = (byte *)_surface.getPixels();
uint32 count = _surface.w * _surface.h;
while (count-- > 0) {
byte g = tga.readByte();
*data++ = g;
*data++ = g;
*data++ = g;
}
}
return true;
}
bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) {
// Color-mapped
if (imageType == TYPE_CMAP) {
_surface.create(_surface.w, _surface.h, _format);
if (indexDepth == 8) {
for (int i = 0; i < _surface.h; i++) {
byte *dst;
if (!_originTop) {
dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
} else {
dst = (byte *)_surface.getBasePtr(0, i);
}
tga.read(dst, _surface.w);
}
} else if (indexDepth == 16) {
warning("16 bit indexes not supported");
return false;
}
} else {
return false;
}
return true;
}
bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
// RLE-TrueColor / RLE-Black/White
if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) {
_surface.create(_surface.w, _surface.h, _format);
uint32 count = _surface.w * _surface.h;
byte *data = (byte *)_surface.getPixels();
while (count > 0) {
uint32 header = tga.readByte();
byte type = (header & 0x80) >> 7;
uint32 rleCount = (header & 0x7F) + 1;
// RLE-packet
if (type == 1) {
if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
byte b = tga.readByte();
byte g = tga.readByte();
byte r = tga.readByte();
byte a = tga.readByte();
while (rleCount-- > 0) {
*data++ = b;
*data++ = g;
*data++ = r;
*data++ = a;
count--;
}
} else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
byte b = tga.readByte();
byte g = tga.readByte();
byte r = tga.readByte();
while (rleCount-- > 0) {
*data++ = b;
*data++ = g;
*data++ = r;
count--;
}
} else if (pixelDepth == 16 && imageType == TYPE_RLE_TRUECOLOR) {
const uint16 rgb = tga.readUint16LE();
while (rleCount-- > 0) {
*((uint16 *)data) = rgb;
data += 2;
count--;
}
} else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
byte color = tga.readByte();
while (rleCount-- > 0) {
*data++ = color;
*data++ = color;
*data++ = color;
count--;
}
} else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
byte index = tga.readByte();
while (rleCount-- > 0) {
*data++ = index;
count--;
}
} else {
warning("Unhandled pixel-depth for image-type 10");
return false;
}
// Raw-packet
} else if (type == 0) {
if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
tga.read(data, rleCount * 4);
data += rleCount * 4;
count -= rleCount;
} else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
tga.read(data, rleCount * 3);
data += rleCount * 3;
count -= rleCount;
} else if (pixelDepth == 16 && imageType == TYPE_RLE_TRUECOLOR) {
while (rleCount-- > 0) {
*((uint16 *)data) = tga.readUint16LE();
data += 2;
count--;
}
} else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
while (rleCount-- > 0) {
byte color = tga.readByte();
*data++ = color;
*data++ = color;
*data++ = color;
count--;
}
} else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
tga.read(data, rleCount);
data += rleCount;
count -= rleCount;
} else {
warning("Unhandled pixel-depth for image-type 10");
return false;
}
} else {
warning("Unknown header for RLE-packet %d", type);
return false;
}
}
} else {
return false;
}
// If it's a bottom origin image, we need to vertically flip the image
if (!_originTop) {
byte *tempLine = new byte[_surface.pitch];
byte *line1 = (byte *)_surface.getBasePtr(0, 0);
byte *line2 = (byte *)_surface.getBasePtr(0, _surface.h - 1);
for (int y = 0; y < (_surface.h / 2); ++y, line1 += _surface.pitch, line2 -= _surface.pitch) {
Common::copy(line1, line1 + _surface.pitch, tempLine);
Common::copy(line2, line2 + _surface.pitch, line1);
Common::copy(tempLine, tempLine + _surface.pitch, line2);
}
delete[] tempLine;
}
return true;
}
} // End of namespace Image

109
image/tga.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/>.
*
*/
/* Based on code from eos https://github.com/DrMcCoy/xoreos/
* relicensed under GPLv2+ with permission from DrMcCoy and clone2727
*/
/*
* TGA decoder used in engines:
* - gob
* - titanic
* - wintermute
* - zvision
*/
#ifndef IMAGE_TGA_H
#define IMAGE_TGA_H
#include "graphics/palette.h"
#include "graphics/surface.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
}
namespace Image {
/**
* @defgroup image_tga TGA (TARGA) decoder
* @ingroup image
*
* @brief Decoder for TGA images.
* @{
*/
/** TarGa image-decoder
* The following variations of TGA are supported:
* - Type 1 - Color-mapped images in 16/24/32 bpp with 8 bit indexes
* - Type 2 - 16/24/32 bpp Top AND Bottom origined.
* - Type 3 - Black/White images, 8bpp.
* - Type 9 - RLE-encoded color-mapped images. (8 bit indexes only)
* - Type 10 - RLE-encoded TrueColor, 24/32bpp.
* - Type 11 - RLE-encoded Black/White, 8bpp.
*
* No images are returned with a palette, instead they are converted
* to 16 bpp for Type 1, or 32 bpp for Black/White-images.
*/
class TGADecoder : public ImageDecoder {
public:
TGADecoder();
virtual ~TGADecoder();
virtual void destroy() override;
const Graphics::Surface *getSurface() const override { return &_surface; }
const Graphics::Palette &getPalette() const override { return _colorMap; }
virtual bool loadStream(Common::SeekableReadStream &stream) override;
private:
// Format-spec from:
// https://www.ludorg.net/amnesia/TGA_File_Format_Spec.html
enum {
TYPE_CMAP = 1,
TYPE_TRUECOLOR = 2,
TYPE_BW = 3,
TYPE_RLE_CMAP = 9,
TYPE_RLE_TRUECOLOR = 10,
TYPE_RLE_BW = 11
};
// Color-map:
bool _colorMapSize;
Graphics::Palette _colorMap;
int16 _colorMapOrigin;
int16 _colorMapLength;
byte _colorMapEntryLength;
// Origin may be at the top, or bottom
bool _originTop;
Graphics::PixelFormat _format;
Graphics::Surface _surface;
// Loading helpers
bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth);
bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
bool readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth);
bool readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
bool readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
};
/** @} */
} // End of namespace Image
#endif

82
image/xbm.cpp Normal file
View File

@@ -0,0 +1,82 @@
/* 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/xbm.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
namespace Image {
static const byte xbmPalette[2 * 3] = {
0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00
};
XBMDecoder::XBMDecoder() : _surface(nullptr), _palette(xbmPalette, 2) {
}
XBMDecoder::~XBMDecoder() {
destroy();
}
void XBMDecoder::destroy() {
if (_surface) {
_surface->free();
delete _surface;
_surface = 0;
}
}
bool XBMDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
warning("External XBM files are not yet supported");
return false;
}
bool XBMDecoder::loadBits(const unsigned char *bits, int width, int height) {
destroy();
_surface = new Graphics::Surface();
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
for (int i = 0; i < height; i++) {
byte *dst = (byte *)_surface->getBasePtr(0, i);
for (int j = 0; j != width;) {
byte color = *bits++;
for (int k = 0; k < 8; k++) {
*dst++ = (color & 1);
color >>= 1;
j++;
if (j == width) {
break;
}
}
}
}
return true;
}
} // End of namespace Image

76
image/xbm.h Normal file
View File

@@ -0,0 +1,76 @@
/* 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_XBM_H
#define IMAGE_XBM_H
#include "graphics/palette.h"
#include "image/image_decoder.h"
namespace Image {
/**
* @defgroup image_xbm XBM decoder
* @ingroup image
*
* @brief Decoder for XBM images.
*
* Used in engines:
* - Glk
* @{
*/
class XBMDecoder : public ImageDecoder {
public:
XBMDecoder();
virtual ~XBMDecoder();
// ImageDecoder API
void destroy() override;
bool loadStream(Common::SeekableReadStream &stream) override;
const Graphics::Surface *getSurface() const override { return _surface; }
const Graphics::Palette &getPalette() const override { return _palette; }
/**
* Load an image from an embedded XBM file.
*
* loadStream() should implicitly call destroy() to free the memory
* of the last loadStream() call.
*
* @param bits Image data.
* @param width Image width.
* @param height Image height.
*
* @return Whether loading the image succeeded.
*
* @see getSurface
*/
bool loadBits(const unsigned char *bits, int width, int height);
private:
Graphics::Surface *_surface;
Graphics::Palette _palette;
};
/** @} */
} // End of namespace Image
#endif