Initial commit
This commit is contained in:
291
image/ani.cpp
Normal file
291
image/ani.cpp
Normal 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
136
image/ani.h
Normal 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
254
image/bmp.cpp
Normal 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
99
image/bmp.h
Normal 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
160
image/cel_3do.cpp
Normal 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
69
image/cel_3do.h
Normal 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
164
image/cicn.cpp
Normal 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
62
image/cicn.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_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
35
image/codec-options.h
Normal 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
143
image/codecs/bmp_raw.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/bmp_raw.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
BitmapRawDecoder::BitmapRawDecoder(int width, int height, int bitsPerPixel, bool ignoreAlpha, bool flip) : Codec(),
|
||||
_width(width), _height(height), _bitsPerPixel(bitsPerPixel), _ignoreAlpha(ignoreAlpha), _flip(flip) {
|
||||
_surface.create(_width, _height, getPixelFormat());
|
||||
}
|
||||
|
||||
BitmapRawDecoder::~BitmapRawDecoder() {
|
||||
_surface.free();
|
||||
}
|
||||
|
||||
const Graphics::Surface *BitmapRawDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
Graphics::PixelFormat format = getPixelFormat();
|
||||
|
||||
int srcPitch = _width * (_bitsPerPixel >> 3);
|
||||
int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
|
||||
|
||||
if (_bitsPerPixel == 1) {
|
||||
srcPitch = (_width + 7) / 8;
|
||||
extraDataLength = (srcPitch % 2) ? 2 - (srcPitch % 2) : 0;
|
||||
} else if (_bitsPerPixel == 4) {
|
||||
srcPitch = (_width + 1) / 2;
|
||||
extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
|
||||
}
|
||||
|
||||
if (_bitsPerPixel == 1) {
|
||||
for (int i = 0; i < _height; i++) {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, i);
|
||||
for (int j = 0; j != _width;) {
|
||||
byte color = stream.readByte();
|
||||
for (int k = 0; k < 8; k++) {
|
||||
*dst++ = (color & 0x80) ? 0x0f : 0x00;
|
||||
color <<= 1;
|
||||
j++;
|
||||
if (j == _width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.skip(extraDataLength);
|
||||
}
|
||||
} else if (_bitsPerPixel == 4) {
|
||||
for (int i = 0; i < _height; i++) {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, _height - i - 1);
|
||||
for (int j = 0; j < _width; j++) {
|
||||
byte color = stream.readByte();
|
||||
|
||||
*dst++ = (color & 0xf0) >> 4;
|
||||
j++;
|
||||
|
||||
if (j ==_width)
|
||||
break;
|
||||
|
||||
*dst++ = color & 0x0f;
|
||||
}
|
||||
|
||||
stream.skip(extraDataLength);
|
||||
}
|
||||
} else if (_bitsPerPixel == 8) {
|
||||
// flip the 8bpp images when we are decoding QTvideo
|
||||
byte *dst = (byte *)_surface.getPixels();
|
||||
|
||||
for (int i = 0; i < _height; i++) {
|
||||
stream.read(dst + (_flip ? i : _height - i - 1) * _width, _width);
|
||||
stream.skip(extraDataLength);
|
||||
}
|
||||
#ifndef SCUMM_LITTLE_ENDIAN
|
||||
} else if (_bitsPerPixel == 16) {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, _height - 1);
|
||||
|
||||
for (int i = 0; i < _height; i++) {
|
||||
for (int j = 0; j < _width; j++) {
|
||||
uint16 color = stream.readUint16LE();
|
||||
|
||||
*(uint16 *)dst = color;
|
||||
dst += format.bytesPerPixel;
|
||||
}
|
||||
|
||||
stream.skip(extraDataLength);
|
||||
dst -= _surface.pitch * 2;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
byte *dst = (byte *)_surface.getBasePtr(0, _height - 1);
|
||||
uint bpp = format.bytesPerPixel;
|
||||
|
||||
for (int i = 0; i < _height; i++) {
|
||||
stream.read(dst, _width * bpp);
|
||||
stream.skip(extraDataLength);
|
||||
dst -= _surface.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat BitmapRawDecoder::getPixelFormat() const {
|
||||
switch (_bitsPerPixel) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 8:
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
case 16:
|
||||
return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
|
||||
case 24:
|
||||
return Graphics::PixelFormat::createFormatBGR24();
|
||||
case 32:
|
||||
return Graphics::PixelFormat::createFormatBGRA32(!_ignoreAlpha);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
error("Unhandled BMP raw %dbpp", _bitsPerPixel);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
55
image/codecs/bmp_raw.h
Normal file
55
image/codecs/bmp_raw.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_BMP_RAW_H
|
||||
#define IMAGE_CODECS_BMP_RAW_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Bitmap raw image decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class BitmapRawDecoder : public Codec {
|
||||
public:
|
||||
BitmapRawDecoder(int width, int height, int bitsPerPixel, bool ignoreAlpha, bool flip = false);
|
||||
~BitmapRawDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
private:
|
||||
Graphics::Surface _surface;
|
||||
int _width, _height;
|
||||
int _bitsPerPixel;
|
||||
bool _ignoreAlpha;
|
||||
|
||||
// this flag indicates whether bitmapRawDecoder is created to decode QTvideo or raw images.
|
||||
// because we need to flip the image when we are dealing with QTvideo
|
||||
bool _flip;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
444
image/codecs/cdtoons.cpp
Normal file
444
image/codecs/cdtoons.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/cdtoons.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
struct CDToonsAction {
|
||||
uint16 blockId;
|
||||
Common::Rect rect;
|
||||
};
|
||||
|
||||
struct CDToonsDiff {
|
||||
byte *data;
|
||||
uint32 size;
|
||||
Common::Rect rect;
|
||||
};
|
||||
|
||||
static Common::Rect readRect(Common::SeekableReadStream &stream) {
|
||||
Common::Rect rect;
|
||||
rect.top = stream.readUint16BE();
|
||||
rect.left = stream.readUint16BE();
|
||||
rect.bottom = stream.readUint16BE();
|
||||
rect.right = stream.readUint16BE();
|
||||
return rect;
|
||||
}
|
||||
|
||||
CDToonsDecoder::CDToonsDecoder(uint16 width, uint16 height) : _palette(256) {
|
||||
debugN(5, "CDToons: width %d, height %d\n", width, height);
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_currentPaletteId = 0;
|
||||
_dirtyPalette = false;
|
||||
}
|
||||
|
||||
CDToonsDecoder::~CDToonsDecoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
|
||||
for (auto &block : _blocks)
|
||||
delete[] block._value.data;
|
||||
}
|
||||
|
||||
Graphics::Surface *CDToonsDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
uint16 u0 = stream.readUint16BE(); // always 9?
|
||||
uint16 frameId = stream.readUint16BE();
|
||||
uint16 blocksValidUntil = stream.readUint16BE();
|
||||
byte u6 = stream.readByte();
|
||||
byte backgroundColor = stream.readByte();
|
||||
debugN(5, "CDToons frame %d, size %d, unknown %04x (at 0), blocks valid until %d, unknown 6 is %02x, bkg color is %02x\n",
|
||||
frameId, (int)stream.size(), u0, blocksValidUntil, u6, backgroundColor);
|
||||
|
||||
Common::Rect clipRect = readRect(stream);
|
||||
debugN(9, "CDToons clipRect: (%d, %d) to (%d, %d)\n",
|
||||
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
|
||||
|
||||
Common::Rect dirtyRect = readRect(stream);
|
||||
debugN(9, "CDToons dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
|
||||
|
||||
uint32 flags = stream.readUint32BE();
|
||||
if (flags & 0x80)
|
||||
error("CDToons: frame already processed?");
|
||||
debugN(5, "CDToons flags: %08x\n", flags);
|
||||
|
||||
uint16 blockCount = stream.readUint16BE();
|
||||
uint16 blockOffset = stream.readUint16BE();
|
||||
debugN(9, "CDToons: %d blocks at 0x%04x\n",
|
||||
blockCount, blockOffset);
|
||||
|
||||
// max block id?
|
||||
uint16 u32 = stream.readUint16BE();
|
||||
debugN(5, "CDToons unknown at 32: %04x\n", u32);
|
||||
|
||||
byte actionCount = stream.readByte();
|
||||
byte u35 = stream.readByte();
|
||||
|
||||
uint16 paletteId = stream.readUint16BE();
|
||||
byte paletteSet = stream.readByte();
|
||||
debugN(9, "CDToons palette id %04x, palette byte %02x\n",
|
||||
paletteId, paletteSet);
|
||||
|
||||
byte u39 = stream.readByte();
|
||||
uint16 u40 = stream.readUint16BE();
|
||||
uint16 u42 = stream.readUint16BE();
|
||||
debugN(5, "CDToons: unknown at 35 is %02x, unknowns at 39: %02x, %04x, %04x\n",
|
||||
u35, u39, u40, u42);
|
||||
|
||||
Common::Array<CDToonsAction> actions;
|
||||
|
||||
for (uint i = 0; i < actionCount; i++) {
|
||||
CDToonsAction action;
|
||||
action.blockId = stream.readUint16BE();
|
||||
action.rect = readRect(stream);
|
||||
debugN(9, "CDToons action: render block %d at (%d, %d) to (%d, %d)\n",
|
||||
action.blockId, action.rect.left, action.rect.top, action.rect.right, action.rect.bottom);
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
if (stream.pos() > blockOffset)
|
||||
error("CDToons header ended at 0x%08x, but blocks should have started at 0x%08x",
|
||||
(int)stream.pos(), blockOffset);
|
||||
|
||||
if (stream.pos() != blockOffset)
|
||||
error("CDToons had %d unknown bytes after header", blockOffset - (int)stream.pos());
|
||||
|
||||
for (uint i = 0; i < blockCount; i++) {
|
||||
uint16 blockId = stream.readUint16BE();
|
||||
if (blockId >= 1200)
|
||||
error("CDToons: block id %d was too high", blockId);
|
||||
if (_blocks.contains(blockId))
|
||||
error("CDToons: new block %d was already seen", blockId);
|
||||
|
||||
CDToonsBlock block;
|
||||
block.flags = stream.readUint16BE();
|
||||
// flag 1 = palette, flag 2 = data?
|
||||
if (block.flags & 0x8000)
|
||||
error("CDToons: block already processed?");
|
||||
block.size = stream.readUint32BE();
|
||||
if (block.size < 14)
|
||||
error("CDToons: block size was %d, too small", block.size);
|
||||
block.size -= 14;
|
||||
block.startFrame = stream.readUint16BE();
|
||||
block.endFrame = stream.readUint16BE();
|
||||
block.unknown12 = stream.readUint16BE();
|
||||
block.data = new byte[block.size];
|
||||
stream.read(block.data, block.size);
|
||||
|
||||
debugN(9, "CDToons block id 0x%04x of size 0x%08x, flags %04x, from frame %d to %d, unknown at 12 is %04x\n",
|
||||
blockId, block.size, block.flags, block.startFrame, block.endFrame, block.unknown12);
|
||||
|
||||
_blocks[blockId] = block;
|
||||
}
|
||||
|
||||
byte xFrmBegin = 0, xFrmCount;
|
||||
Common::Array<CDToonsDiff> diffs;
|
||||
|
||||
while (true) {
|
||||
int32 nextPos = stream.pos();
|
||||
uint32 tag = stream.readUint32BE();
|
||||
uint32 size = stream.readUint32BE();
|
||||
nextPos += size;
|
||||
|
||||
switch (tag) {
|
||||
case MKTAG('D','i','f','f'):
|
||||
{
|
||||
debugN(5, "CDToons: Diff\n");
|
||||
uint16 count = stream.readUint16BE();
|
||||
|
||||
Common::Rect diffClipRect = readRect(stream);
|
||||
debugN(9, "CDToons diffClipRect: (%d, %d) to (%d, %d)\n",
|
||||
diffClipRect.left, diffClipRect.top, diffClipRect.right, diffClipRect.bottom);
|
||||
|
||||
debugN(5, "CDToons Diff: %d subentries\n", count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
CDToonsDiff diff;
|
||||
|
||||
diff.rect = readRect(stream);
|
||||
diff.size = stream.readUint32BE();
|
||||
if (diff.size < 20)
|
||||
error("CDToons: Diff block size was %d, too small", diff.size);
|
||||
|
||||
uint16 diffWidth = stream.readUint16BE();
|
||||
uint16 diffHeight = stream.readUint16BE();
|
||||
uint16 unknown16 = stream.readUint16BE();
|
||||
uint16 unknown18 = stream.readUint16BE();
|
||||
diff.size -= 8;
|
||||
|
||||
if (diffWidth != diff.rect.width() || diffHeight != diff.rect.height())
|
||||
error("CDToons: Diff sizes didn't match");
|
||||
debugN(5, "CDToons Diff: size %d, frame from (%d, %d) to (%d, %d), unknowns %04x, %04x\n",
|
||||
diff.size, diff.rect.left, diff.rect.top, diff.rect.right, diff.rect.bottom,
|
||||
unknown16, unknown18);
|
||||
|
||||
diff.data = new byte[diff.size];
|
||||
stream.read(diff.data, diff.size);
|
||||
diffs.push_back(diff);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MKTAG('X','F','r','m'):
|
||||
{
|
||||
debugN(5, "CDToons: XFrm\n");
|
||||
if (!(flags & 0x10))
|
||||
error("CDToons: useless XFrm?");
|
||||
|
||||
if (xFrmBegin)
|
||||
error("CDToons: duplicate XFrm");
|
||||
xFrmBegin = stream.readByte();
|
||||
xFrmCount = stream.readByte();
|
||||
debugN(9, "CDToons XFrm: run %d actions from %d\n", xFrmCount, xFrmBegin - 1);
|
||||
|
||||
// TODO: don't ignore (if xFrmCount is non-zero)
|
||||
Common::Rect dirtyRectXFrm = readRect(stream);
|
||||
debugN(9, "CDToons XFrm dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRectXFrm.left, dirtyRectXFrm.top, dirtyRectXFrm.right, dirtyRectXFrm.bottom);
|
||||
|
||||
// always zero?
|
||||
Common::Rect dirtyRect2XFrm = readRect(stream);
|
||||
debugN(9, "CDToons XFrm dirtyRect2: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRect2XFrm.left, dirtyRect2XFrm.top, dirtyRect2XFrm.right, dirtyRect2XFrm.bottom);
|
||||
}
|
||||
break;
|
||||
case MKTAG('M','r','k','s'):
|
||||
debugN(5, "CDToons: Mrks\n");
|
||||
if (!(flags & 0x2))
|
||||
error("CDToons: useless Mrks?");
|
||||
|
||||
// TODO
|
||||
warning("CDToons: encountered Mrks, not implemented yet");
|
||||
break;
|
||||
case MKTAG('S','c','a','l'):
|
||||
// TODO
|
||||
warning("CDToons: encountered Scal, not implemented yet");
|
||||
break;
|
||||
case MKTAG('W','r','M','p'):
|
||||
warning("CDToons: encountered WrMp, ignoring");
|
||||
break;
|
||||
case MKTAG('F','r','t','R'):
|
||||
{
|
||||
debugN(5, "CDToons: FrtR\n");
|
||||
if (!(flags & 0x40))
|
||||
error("CDToons: useless FrtR?");
|
||||
|
||||
uint16 count = stream.readUint16BE();
|
||||
debugN(9, "CDToons FrtR: %d dirty rectangles\n", count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
Common::Rect dirtyRectFrtR = readRect(stream);
|
||||
debugN(9, "CDToons FrtR dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRectFrtR.left, dirtyRectFrtR.top, dirtyRectFrtR.right, dirtyRectFrtR.bottom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MKTAG('B','c','k','R'):
|
||||
{
|
||||
debugN(5, "CDToons: BckR\n");
|
||||
if (!(flags & 0x20))
|
||||
error("CDToons: useless BckR?");
|
||||
|
||||
uint16 count = stream.readUint16BE();
|
||||
debugN(9, "CDToons BckR: %d subentries\n", count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
Common::Rect dirtyRectBckR = readRect(stream);
|
||||
debugN(9, "CDToons BckR dirtyRect: (%d, %d) to (%d, %d)\n",
|
||||
dirtyRectBckR.left, dirtyRectBckR.top, dirtyRectBckR.right, dirtyRectBckR.bottom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("Unknown CDToons tag '%s'", tag2str(tag));
|
||||
}
|
||||
|
||||
if (stream.pos() > nextPos)
|
||||
error("CDToons ran off the end of a block while reading it (at %d, next block at %d)",
|
||||
(int)stream.pos(), nextPos);
|
||||
if (stream.pos() != nextPos) {
|
||||
warning("CDToons had %d unknown bytes after block", nextPos - (int32)stream.pos());
|
||||
stream.seek(nextPos);
|
||||
}
|
||||
|
||||
if (stream.pos() == stream.size())
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < diffs.size(); i++) {
|
||||
renderBlock(diffs[i].data, diffs[i].size, diffs[i].rect.left, diffs[i].rect.top, diffs[i].rect.width(), diffs[i].rect.height());
|
||||
delete[] diffs[i].data;
|
||||
}
|
||||
if (!diffs.empty())
|
||||
return _surface;
|
||||
|
||||
for (uint i = 0; i < actions.size(); i++) {
|
||||
CDToonsAction &action = actions[i];
|
||||
if (i == 0 && action.blockId == 0)
|
||||
memset(_surface->getPixels(), backgroundColor, _surface->w * _surface->h);
|
||||
if (!_blocks.contains(action.blockId))
|
||||
continue;
|
||||
if (!action.rect.right)
|
||||
continue;
|
||||
if (i == 0 && !diffs.empty())
|
||||
continue;
|
||||
|
||||
CDToonsBlock &block = _blocks[action.blockId];
|
||||
uint16 width = READ_BE_UINT16(block.data + 2);
|
||||
uint16 height = READ_BE_UINT16(block.data);
|
||||
|
||||
renderBlock(block.data + 14, block.size - 14, action.rect.left, action.rect.top, width, height);
|
||||
}
|
||||
|
||||
if (paletteId && _currentPaletteId != paletteId) {
|
||||
if (!_blocks.contains(paletteId))
|
||||
error("CDToons: no block for palette %04x", paletteId);
|
||||
if (_blocks[paletteId].size != 2 * 3 * 256)
|
||||
error("CDToons: palette %04x is wrong size (%d)", paletteId, _blocks[paletteId].size);
|
||||
|
||||
_currentPaletteId = paletteId;
|
||||
if (!paletteSet)
|
||||
setPalette(_blocks[paletteId].data);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void CDToonsDecoder::renderBlock(byte *data, uint dataSize, int destX, int destY, uint width, uint height) {
|
||||
byte *currData = data;
|
||||
byte *dataEnd = data + dataSize;
|
||||
|
||||
debugN(9, "CDToons renderBlock at (%d, %d), width %d, height %d\n",
|
||||
destX, destY, width, height);
|
||||
|
||||
if (destX + (int)width > _surface->w)
|
||||
width = _surface->w - destX;
|
||||
if (destY + (int)height > _surface->h)
|
||||
height = _surface->h - destY;
|
||||
|
||||
uint skip = 0;
|
||||
if (destX < 0) {
|
||||
skip = -destX;
|
||||
if (width <= skip)
|
||||
return;
|
||||
width -= skip;
|
||||
destX = 0;
|
||||
}
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
if (destY + (int)y >= _surface->h)
|
||||
break;
|
||||
|
||||
if (currData + 2 > dataEnd)
|
||||
error("CDToons renderBlock overran whole data by %d bytes", (uint32)(currData - dataEnd));
|
||||
|
||||
uint16 lineSize = READ_BE_UINT16(currData);
|
||||
currData += 2;
|
||||
byte *nextLine = currData + lineSize;
|
||||
|
||||
if (nextLine > dataEnd)
|
||||
error("CDToons renderBlock was going to overrun data by %d bytes (line size %d)",
|
||||
(uint32)(nextLine - dataEnd), (uint32)(nextLine - currData));
|
||||
|
||||
if (destY + (int)y < 0) {
|
||||
currData = nextLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
byte *pixels = (byte *)_surface->getBasePtr(destX, destY + y);
|
||||
|
||||
int leftToSkip = skip;
|
||||
uint x = 0;
|
||||
bool done = false;
|
||||
while (x < width && !done) {
|
||||
int size = (uint)*currData;
|
||||
currData++;
|
||||
bool raw = !(size & 0x80);
|
||||
size = (size & 0x7f) + 1;
|
||||
|
||||
if (leftToSkip) {
|
||||
if (leftToSkip >= size) {
|
||||
leftToSkip -= size;
|
||||
if (raw)
|
||||
currData += size;
|
||||
else
|
||||
currData++;
|
||||
continue;
|
||||
} else {
|
||||
size -= leftToSkip;
|
||||
if (raw)
|
||||
currData += leftToSkip;
|
||||
leftToSkip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (x + size >= width) {
|
||||
size = width - x;
|
||||
done = true;
|
||||
}
|
||||
if (destX + (int)x + size >= (int)_surface->w) {
|
||||
size = MIN<int>((int)_surface->w - destX - (int)x, width - x);
|
||||
done = true;
|
||||
}
|
||||
if (size <= 0) {
|
||||
size = 0;
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
memcpy(pixels + x, currData, size);
|
||||
currData += size;
|
||||
x += size;
|
||||
} else {
|
||||
byte color = *currData;
|
||||
currData++;
|
||||
if (color) {
|
||||
memset(pixels + x, color, size);
|
||||
}
|
||||
x += size;
|
||||
}
|
||||
|
||||
if (currData > nextLine) {
|
||||
warning("CDToons renderBlock overran line by %d bytes", (uint32)(currData - nextLine));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currData = nextLine;
|
||||
}
|
||||
}
|
||||
|
||||
void CDToonsDecoder::setPalette(byte *data) {
|
||||
_dirtyPalette = true;
|
||||
|
||||
// A lovely QuickTime palette
|
||||
for (uint i = 0; i < 256; i++) {
|
||||
_palette.set(i, *data, *(data + 2), *(data + 4));
|
||||
data += 6;
|
||||
}
|
||||
|
||||
_palette.set(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
71
image/codecs/cdtoons.h
Normal file
71
image/codecs/cdtoons.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CDTOONS_H
|
||||
#define IMAGE_CODECS_CDTOONS_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
struct CDToonsBlock {
|
||||
uint16 flags;
|
||||
uint32 size;
|
||||
uint16 startFrame;
|
||||
uint16 endFrame;
|
||||
uint16 unknown12;
|
||||
byte *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Broderbund CDToons decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class CDToonsDecoder : public Codec {
|
||||
public:
|
||||
CDToonsDecoder(uint16 width, uint16 height);
|
||||
~CDToonsDecoder() override;
|
||||
|
||||
Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
bool containsPalette() const override { return true; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _palette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
Graphics::Palette _palette;
|
||||
bool _dirtyPalette;
|
||||
uint16 _currentPaletteId;
|
||||
|
||||
Common::HashMap<uint16, CDToonsBlock> _blocks;
|
||||
|
||||
void renderBlock(byte *data, uint size, int x, int y, uint width, uint height);
|
||||
void setPalette(byte *data);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
696
image/codecs/cinepak.cpp
Normal file
696
image/codecs/cinepak.cpp
Normal file
@@ -0,0 +1,696 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/cinepak.h"
|
||||
#include "image/codecs/cinepak_tables.h"
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
// Code here partially based off of ffmpeg ;)
|
||||
|
||||
namespace Image {
|
||||
|
||||
namespace {
|
||||
|
||||
inline void convertYUVToRGB(const byte *clipTable, byte y, int8 u, int8 v, byte &r, byte &g, byte &b) {
|
||||
r = clipTable[y + (v * 2)];
|
||||
g = clipTable[y - (u >> 1) - v];
|
||||
b = clipTable[y + (u * 2)];
|
||||
}
|
||||
|
||||
inline uint32 convertYUVToColor(const byte *clipTable, const Graphics::PixelFormat &format, byte y, int8 u, int8 v) {
|
||||
byte r, g, b;
|
||||
convertYUVToRGB(clipTable, y, u, v, r, g, b);
|
||||
return format.RGBToColor(r, g, b);
|
||||
}
|
||||
|
||||
inline uint16 createDitherTableIndex(const byte *clipTable, byte y, int8 u, int8 v) {
|
||||
byte r, g, b;
|
||||
convertYUVToRGB(clipTable, y, u, v, r, g, b);
|
||||
return ((r & 0xF8) << 6) |
|
||||
((g & 0xF8) << 1) |
|
||||
((b & 0xF0) >> 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default codebook converter for 24bpp: RGB output.
|
||||
*/
|
||||
struct CodebookConverterRGB {
|
||||
template<typename PixelInt>
|
||||
static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, PixelInt *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
|
||||
const int8 u = codebook.u, v = codebook.v;
|
||||
|
||||
const PixelInt rgb0 = convertYUVToColor(clipTable, format, codebook.y[0], u, v);
|
||||
const PixelInt rgb1 = convertYUVToColor(clipTable, format, codebook.y[1], u, v);
|
||||
|
||||
dst[0] = dst[1] = rgb0;
|
||||
dst[2] = dst[3] = rgb1;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = dst[1] = rgb0;
|
||||
dst[2] = dst[3] = rgb1;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
const PixelInt rgb2 = convertYUVToColor(clipTable, format, codebook.y[2], u, v);
|
||||
const PixelInt rgb3 = convertYUVToColor(clipTable, format, codebook.y[3], u, v);
|
||||
|
||||
dst[0] = dst[1] = rgb2;
|
||||
dst[2] = dst[3] = rgb3;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = dst[1] = rgb2;
|
||||
dst[2] = dst[3] = rgb3;
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
}
|
||||
|
||||
template<typename PixelInt>
|
||||
static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, PixelInt *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]];
|
||||
const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]];
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook1.y[0], codebook1.u, codebook1.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook1.y[1], codebook1.u, codebook1.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook2.y[0], codebook2.u, codebook2.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook2.y[1], codebook2.u, codebook2.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook1.y[2], codebook1.u, codebook1.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook1.y[3], codebook1.u, codebook1.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook2.y[2], codebook2.u, codebook2.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook2.y[3], codebook2.u, codebook2.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]];
|
||||
const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]];
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook3.y[0], codebook3.u, codebook3.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook3.y[1], codebook3.u, codebook3.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook4.y[0], codebook4.u, codebook4.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook4.y[1], codebook4.u, codebook4.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
|
||||
dst[0] = convertYUVToColor(clipTable, format, codebook3.y[2], codebook3.u, codebook3.v);
|
||||
dst[1] = convertYUVToColor(clipTable, format, codebook3.y[3], codebook3.u, codebook3.v);
|
||||
dst[2] = convertYUVToColor(clipTable, format, codebook4.y[2], codebook4.u, codebook4.v);
|
||||
dst[3] = convertYUVToColor(clipTable, format, codebook4.y[3], codebook4.u, codebook4.v);
|
||||
dst = (PixelInt *)((uint8 *)dst + dstPitch);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The default codebook converter for 8bpp: palettized output.
|
||||
*/
|
||||
struct CodebookConverterPalette {
|
||||
static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
|
||||
|
||||
byte y0 = codebook.y[0], y1 = codebook.y[1];
|
||||
byte y2 = codebook.y[2], y3 = codebook.y[3];
|
||||
|
||||
dst[0] = dst[1] = y0;
|
||||
dst[2] = dst[3] = y1;
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = dst[1] = y0;
|
||||
dst[2] = dst[3] = y1;
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = dst[1] = y2;
|
||||
dst[2] = dst[3] = y3;
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = dst[1] = y2;
|
||||
dst[2] = dst[3] = y3;
|
||||
dst += dstPitch;
|
||||
}
|
||||
|
||||
static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]];
|
||||
const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]];
|
||||
|
||||
dst[0] = codebook1.y[0];
|
||||
dst[1] = codebook1.y[1];
|
||||
dst[2] = codebook2.y[0];
|
||||
dst[3] = codebook2.y[1];
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = codebook1.y[2];
|
||||
dst[1] = codebook1.y[3];
|
||||
dst[2] = codebook2.y[2];
|
||||
dst[3] = codebook2.y[3];
|
||||
dst += dstPitch;
|
||||
|
||||
const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]];
|
||||
const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]];
|
||||
|
||||
dst[0] = codebook3.y[0];
|
||||
dst[1] = codebook3.y[1];
|
||||
dst[2] = codebook4.y[0];
|
||||
dst[3] = codebook4.y[1];
|
||||
dst += dstPitch;
|
||||
|
||||
dst[0] = codebook3.y[2];
|
||||
dst[1] = codebook3.y[3];
|
||||
dst[2] = codebook4.y[2];
|
||||
dst[3] = codebook4.y[3];
|
||||
dst += dstPitch;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Codebook converter that dithers in QT-style or VFW-style
|
||||
*/
|
||||
struct CodebookConverterDithered {
|
||||
static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const uint32 *colorPtr = (const uint32 *)(strip.v1_dither + codebookIndex);
|
||||
|
||||
*((uint32 *)dst) = colorPtr[0];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint32 *)dst) = colorPtr[256];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint32 *)dst) = colorPtr[512];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint32 *)dst) = colorPtr[768];
|
||||
dst += dstPitch;
|
||||
}
|
||||
|
||||
static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *dst, size_t dstPitch, const byte *clipTable, const Graphics::PixelFormat &format) {
|
||||
const uint16 *colorPtr1 = (const uint16 *)(strip.v4_dither + codebookIndex[0]);
|
||||
const uint16 *colorPtr2 = (const uint16 *)(strip.v4_dither + codebookIndex[1]);
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr1[0];
|
||||
*((uint16 *)(dst + 2)) = colorPtr2[512];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr1[1];
|
||||
*((uint16 *)(dst + 2)) = colorPtr2[513];
|
||||
dst += dstPitch;
|
||||
|
||||
const uint16 *colorPtr3 = (const uint16 *)(strip.v4_dither + codebookIndex[2]);
|
||||
const uint16 *colorPtr4 = (const uint16 *)(strip.v4_dither + codebookIndex[3]);
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr3[1024];
|
||||
*((uint16 *)(dst + 2)) = colorPtr4[1536];
|
||||
dst += dstPitch;
|
||||
|
||||
*((uint16 *)(dst + 0)) = colorPtr3[1025];
|
||||
*((uint16 *)(dst + 2)) = colorPtr4[1537];
|
||||
dst += dstPitch;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename PixelInt, typename CodebookConverter>
|
||||
void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
|
||||
uint32 flag = 0, mask = 0;
|
||||
int32 startPos = stream.pos();
|
||||
PixelInt *dst;
|
||||
|
||||
for (uint16 y = frame.strips[strip].rect.top; y < frame.strips[strip].rect.bottom; y += 4) {
|
||||
dst = (PixelInt *)frame.surface->getBasePtr(frame.strips[strip].rect.left, + y);
|
||||
|
||||
for (uint16 x = frame.strips[strip].rect.left; x < frame.strips[strip].rect.right; x += 4) {
|
||||
if ((chunkID & 0x01) && !(mask >>= 1)) {
|
||||
if ((stream.pos() - startPos + 4) > (int32)chunkSize)
|
||||
return;
|
||||
|
||||
flag = stream.readUint32BE();
|
||||
mask = 0x80000000;
|
||||
}
|
||||
|
||||
if (!(chunkID & 0x01) || (flag & mask)) {
|
||||
if (!(chunkID & 0x02) && !(mask >>= 1)) {
|
||||
if ((stream.pos() - startPos + 4) > (int32)chunkSize)
|
||||
return;
|
||||
|
||||
flag = stream.readUint32BE();
|
||||
mask = 0x80000000;
|
||||
}
|
||||
|
||||
if ((chunkID & 0x02) || (~flag & mask)) {
|
||||
if ((stream.pos() - startPos + 1) > (int32)chunkSize)
|
||||
return;
|
||||
|
||||
// Get the codebook
|
||||
byte codebook = stream.readByte();
|
||||
CodebookConverter::decodeBlock1(codebook, frame.strips[strip], dst, frame.surface->pitch, clipTable, frame.surface->format);
|
||||
} else if (flag & mask) {
|
||||
if ((stream.pos() - startPos + 4) > (int32)chunkSize)
|
||||
return;
|
||||
|
||||
byte codebook[4];
|
||||
stream.read(codebook, 4);
|
||||
CodebookConverter::decodeBlock4(codebook, frame.strips[strip], dst, frame.surface->pitch, clipTable, frame.surface->format);
|
||||
}
|
||||
}
|
||||
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPerPixel), _ditherPalette(0) {
|
||||
_curFrame.surface = 0;
|
||||
_curFrame.strips = 0;
|
||||
_y = 0;
|
||||
_colorMap = 0;
|
||||
_ditherType = kDitherTypeUnknown;
|
||||
|
||||
if (bitsPerPixel == 8) {
|
||||
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
|
||||
} else {
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
}
|
||||
|
||||
// Create a lookup for the clip function
|
||||
// This dramatically improves the performance of the color conversion
|
||||
_clipTableBuf = new byte[1024];
|
||||
|
||||
for (uint i = 0; i < 1024; i++) {
|
||||
if (i <= 512)
|
||||
_clipTableBuf[i] = 0;
|
||||
else if (i >= 768)
|
||||
_clipTableBuf[i] = 255;
|
||||
else
|
||||
_clipTableBuf[i] = i - 512;
|
||||
}
|
||||
|
||||
_clipTable = _clipTableBuf + 512;
|
||||
}
|
||||
|
||||
CinepakDecoder::~CinepakDecoder() {
|
||||
if (_curFrame.surface) {
|
||||
_curFrame.surface->free();
|
||||
delete _curFrame.surface;
|
||||
}
|
||||
|
||||
delete[] _curFrame.strips;
|
||||
delete[] _clipTableBuf;
|
||||
|
||||
delete[] _colorMap;
|
||||
}
|
||||
|
||||
const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
_curFrame.flags = stream.readByte();
|
||||
_curFrame.length = (stream.readByte() << 16);
|
||||
_curFrame.length |= stream.readUint16BE();
|
||||
_curFrame.width = stream.readUint16BE();
|
||||
_curFrame.height = stream.readUint16BE();
|
||||
_curFrame.stripCount = stream.readUint16BE();
|
||||
|
||||
if (!_curFrame.strips) {
|
||||
_curFrame.strips = new CinepakStrip[_curFrame.stripCount];
|
||||
for (uint16 i = 0; i < _curFrame.stripCount; i++) {
|
||||
initializeCodebook(i, 1);
|
||||
initializeCodebook(i, 4);
|
||||
}
|
||||
}
|
||||
|
||||
debugC(kDebugLevelGVideo, 4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount);
|
||||
|
||||
// Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless).
|
||||
// The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;)
|
||||
if (_curFrame.length != (uint32)stream.size()) {
|
||||
if (stream.readUint16BE() == 0xFE00)
|
||||
stream.readUint32BE();
|
||||
else if ((stream.size() % _curFrame.length) == 0)
|
||||
stream.seek(-2, SEEK_CUR);
|
||||
}
|
||||
|
||||
if (!_curFrame.surface) {
|
||||
_curFrame.surface = new Graphics::Surface();
|
||||
_curFrame.surface->create(_curFrame.width, _curFrame.height, _pixelFormat);
|
||||
}
|
||||
|
||||
_y = 0;
|
||||
|
||||
for (uint16 i = 0; i < _curFrame.stripCount; i++) {
|
||||
if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip
|
||||
|
||||
for (uint16 j = 0; j < 256; j++) {
|
||||
_curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j];
|
||||
_curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j];
|
||||
}
|
||||
|
||||
// Copy the dither tables
|
||||
memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * sizeof(uint32));
|
||||
memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * sizeof(uint32));
|
||||
}
|
||||
|
||||
_curFrame.strips[i].id = stream.readUint16BE();
|
||||
_curFrame.strips[i].length = stream.readUint16BE() - 12; // Subtract the 12 byte header
|
||||
_curFrame.strips[i].rect.top = _y; stream.readUint16BE(); // Ignore, substitute with our own.
|
||||
_curFrame.strips[i].rect.left = 0; stream.readUint16BE(); // Ignore, substitute with our own
|
||||
_curFrame.strips[i].rect.bottom = _y + stream.readUint16BE();
|
||||
_curFrame.strips[i].rect.right = _curFrame.width; stream.readUint16BE(); // Ignore, substitute with our own
|
||||
|
||||
// Sanity check. Because Cinepak is based on 4x4 blocks, the width and height of each strip needs to be divisible by 4.
|
||||
assert(!(_curFrame.strips[i].rect.width() % 4) && !(_curFrame.strips[i].rect.height() % 4));
|
||||
|
||||
uint32 pos = stream.pos();
|
||||
|
||||
while ((uint32)stream.pos() < (pos + _curFrame.strips[i].length) && !stream.eos()) {
|
||||
byte chunkID = stream.readByte();
|
||||
|
||||
if (stream.eos())
|
||||
break;
|
||||
|
||||
// Chunk Size is 24-bit, ignore the first 4 bytes
|
||||
uint32 chunkSize = stream.readByte() << 16;
|
||||
chunkSize += stream.readUint16BE() - 4;
|
||||
|
||||
int32 startPos = stream.pos();
|
||||
|
||||
switch (chunkID) {
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x24:
|
||||
case 0x25:
|
||||
loadCodebook(stream, i, 4, chunkID, chunkSize);
|
||||
break;
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x26:
|
||||
case 0x27:
|
||||
loadCodebook(stream, i, 1, chunkID, chunkSize);
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x32:
|
||||
if (_ditherPalette.size() > 0)
|
||||
ditherVectors(stream, i, chunkID, chunkSize);
|
||||
else if (_bitsPerPixel == 8)
|
||||
decodeVectors8(stream, i, chunkID, chunkSize);
|
||||
else
|
||||
decodeVectors24(stream, i, chunkID, chunkSize);
|
||||
break;
|
||||
default:
|
||||
warning("Unknown Cinepak chunk ID %02x", chunkID);
|
||||
return _curFrame.surface;
|
||||
}
|
||||
|
||||
if (stream.pos() != startPos + (int32)chunkSize)
|
||||
stream.seek(startPos + chunkSize);
|
||||
}
|
||||
|
||||
_y = _curFrame.strips[i].rect.bottom;
|
||||
}
|
||||
|
||||
return _curFrame.surface;
|
||||
}
|
||||
|
||||
void CinepakDecoder::initializeCodebook(uint16 strip, byte codebookType) {
|
||||
CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook;
|
||||
|
||||
for (uint16 i = 0; i < 256; i++) {
|
||||
memset(codebook[i].y, 0, 4);
|
||||
codebook[i].u = 0;
|
||||
codebook[i].v = 0;
|
||||
|
||||
if (_ditherType == kDitherTypeQT)
|
||||
ditherCodebookQT(strip, codebookType, i);
|
||||
else if (_ditherType == kDitherTypeVFW)
|
||||
ditherCodebookVFW(strip, codebookType, i);
|
||||
}
|
||||
}
|
||||
|
||||
void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize) {
|
||||
CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook;
|
||||
|
||||
int32 startPos = stream.pos();
|
||||
uint32 flag = 0, mask = 0;
|
||||
|
||||
for (uint16 i = 0; i < 256; i++) {
|
||||
if ((chunkID & 0x01) && !(mask >>= 1)) {
|
||||
if ((stream.pos() - startPos + 4) > (int32)chunkSize)
|
||||
break;
|
||||
|
||||
flag = stream.readUint32BE();
|
||||
mask = 0x80000000;
|
||||
}
|
||||
|
||||
if (!(chunkID & 0x01) || (flag & mask)) {
|
||||
byte n = (chunkID & 0x04) ? 4 : 6;
|
||||
if ((stream.pos() - startPos + n) > (int32)chunkSize)
|
||||
break;
|
||||
|
||||
stream.read(codebook[i].y, 4);
|
||||
|
||||
if (n == 6) {
|
||||
codebook[i].u = stream.readSByte();
|
||||
codebook[i].v = stream.readSByte();
|
||||
} else {
|
||||
// This codebook type indicates either greyscale or
|
||||
// palettized video. For greyscale, default us to
|
||||
// 0 for both u and v.
|
||||
codebook[i].u = 0;
|
||||
codebook[i].v = 0;
|
||||
}
|
||||
|
||||
// Dither the codebook if we're dithering for QuickTime
|
||||
if (_ditherType == kDitherTypeQT)
|
||||
ditherCodebookQT(strip, codebookType, i);
|
||||
else if (_ditherType == kDitherTypeVFW)
|
||||
ditherCodebookVFW(strip, codebookType, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CinepakDecoder::ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex) {
|
||||
if (codebookType == 1) {
|
||||
const CinepakCodebook &codebook = _curFrame.strips[strip].v1_codebook[codebookIndex];
|
||||
byte *output = (byte *)(_curFrame.strips[strip].v1_dither + codebookIndex);
|
||||
|
||||
byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v);
|
||||
output[0x000] = ditherEntry[0x0000];
|
||||
output[0x001] = ditherEntry[0x4000];
|
||||
output[0x400] = ditherEntry[0xC000];
|
||||
output[0x401] = ditherEntry[0x0000];
|
||||
|
||||
ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v);
|
||||
output[0x002] = ditherEntry[0x8000];
|
||||
output[0x003] = ditherEntry[0xC000];
|
||||
output[0x402] = ditherEntry[0x4000];
|
||||
output[0x403] = ditherEntry[0x8000];
|
||||
|
||||
ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v);
|
||||
output[0x800] = ditherEntry[0x4000];
|
||||
output[0x801] = ditherEntry[0x8000];
|
||||
output[0xC00] = ditherEntry[0x8000];
|
||||
output[0xC01] = ditherEntry[0xC000];
|
||||
|
||||
ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v);
|
||||
output[0x802] = ditherEntry[0xC000];
|
||||
output[0x803] = ditherEntry[0x0000];
|
||||
output[0xC02] = ditherEntry[0x0000];
|
||||
output[0xC03] = ditherEntry[0x4000];
|
||||
} else {
|
||||
const CinepakCodebook &codebook = _curFrame.strips[strip].v4_codebook[codebookIndex];
|
||||
byte *output = (byte *)(_curFrame.strips[strip].v4_dither + codebookIndex);
|
||||
|
||||
byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v);
|
||||
output[0x000] = ditherEntry[0x0000];
|
||||
output[0x400] = ditherEntry[0x8000];
|
||||
output[0x800] = ditherEntry[0x4000];
|
||||
output[0xC00] = ditherEntry[0xC000];
|
||||
|
||||
ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v);
|
||||
output[0x001] = ditherEntry[0x4000];
|
||||
output[0x401] = ditherEntry[0xC000];
|
||||
output[0x801] = ditherEntry[0x8000];
|
||||
output[0xC01] = ditherEntry[0x0000];
|
||||
|
||||
ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v);
|
||||
output[0x002] = ditherEntry[0xC000];
|
||||
output[0x402] = ditherEntry[0x4000];
|
||||
output[0x802] = ditherEntry[0x8000];
|
||||
output[0xC02] = ditherEntry[0x0000];
|
||||
|
||||
ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v);
|
||||
output[0x003] = ditherEntry[0x0000];
|
||||
output[0x403] = ditherEntry[0x8000];
|
||||
output[0x803] = ditherEntry[0xC000];
|
||||
output[0xC03] = ditherEntry[0x4000];
|
||||
}
|
||||
}
|
||||
|
||||
static inline byte getRGBLookupEntry(const byte *colorMap, uint16 index) {
|
||||
return colorMap[CLIP<int>(index, 0, 1023)];
|
||||
}
|
||||
|
||||
void CinepakDecoder::ditherCodebookVFW(uint16 strip, byte codebookType, uint16 codebookIndex) {
|
||||
if (codebookType == 1) {
|
||||
const CinepakCodebook &codebook = _curFrame.strips[strip].v1_codebook[codebookIndex];
|
||||
byte *output = (byte *)(_curFrame.strips[strip].v1_dither + codebookIndex);
|
||||
|
||||
int uLookup = (byte)codebook.u * 2;
|
||||
int vLookup = (byte)codebook.v * 2;
|
||||
uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
|
||||
uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
|
||||
|
||||
int yLookup1 = codebook.y[0] * 2;
|
||||
int yLookup2 = codebook.y[1] * 2;
|
||||
int yLookup3 = codebook.y[2] * 2;
|
||||
int yLookup4 = codebook.y[3] * 2;
|
||||
|
||||
uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
|
||||
uint32 pixelGroup2 = uv1 | s_yLookup[yLookup2];
|
||||
uint32 pixelGroup3 = uv1 | s_yLookup[yLookup1];
|
||||
uint32 pixelGroup4 = uv2 | s_yLookup[yLookup2 + 1];
|
||||
uint32 pixelGroup5 = uv2 | s_yLookup[yLookup3 + 1];
|
||||
uint32 pixelGroup6 = uv1 | s_yLookup[yLookup3];
|
||||
uint32 pixelGroup7 = uv1 | s_yLookup[yLookup4];
|
||||
uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
|
||||
|
||||
output[0x000] = getRGBLookupEntry(_colorMap, pixelGroup1 & 0xFFFF);
|
||||
output[0x001] = getRGBLookupEntry(_colorMap, pixelGroup1 >> 16);
|
||||
output[0x002] = getRGBLookupEntry(_colorMap, pixelGroup2 & 0xFFFF);
|
||||
output[0x003] = getRGBLookupEntry(_colorMap, pixelGroup2 >> 16);
|
||||
output[0x400] = getRGBLookupEntry(_colorMap, pixelGroup3 & 0xFFFF);
|
||||
output[0x401] = getRGBLookupEntry(_colorMap, pixelGroup3 >> 16);
|
||||
output[0x402] = getRGBLookupEntry(_colorMap, pixelGroup4 & 0xFFFF);
|
||||
output[0x403] = getRGBLookupEntry(_colorMap, pixelGroup4 >> 16);
|
||||
output[0x800] = getRGBLookupEntry(_colorMap, pixelGroup5 >> 16);
|
||||
output[0x801] = getRGBLookupEntry(_colorMap, pixelGroup6 & 0xFFFF);
|
||||
output[0x802] = getRGBLookupEntry(_colorMap, pixelGroup7 >> 16);
|
||||
output[0x803] = getRGBLookupEntry(_colorMap, pixelGroup8 & 0xFFFF);
|
||||
output[0xC00] = getRGBLookupEntry(_colorMap, pixelGroup6 >> 16);
|
||||
output[0xC01] = getRGBLookupEntry(_colorMap, pixelGroup5 & 0xFFFF);
|
||||
output[0xC02] = getRGBLookupEntry(_colorMap, pixelGroup8 >> 16);
|
||||
output[0xC03] = getRGBLookupEntry(_colorMap, pixelGroup7 & 0xFFFF);
|
||||
} else {
|
||||
const CinepakCodebook &codebook = _curFrame.strips[strip].v4_codebook[codebookIndex];
|
||||
byte *output = (byte *)(_curFrame.strips[strip].v4_dither + codebookIndex);
|
||||
|
||||
int uLookup = (byte)codebook.u * 2;
|
||||
int vLookup = (byte)codebook.v * 2;
|
||||
uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
|
||||
uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
|
||||
|
||||
int yLookup1 = codebook.y[0] * 2;
|
||||
int yLookup2 = codebook.y[1] * 2;
|
||||
int yLookup3 = codebook.y[2] * 2;
|
||||
int yLookup4 = codebook.y[3] * 2;
|
||||
|
||||
uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
|
||||
uint32 pixelGroup2 = uv2 | s_yLookup[yLookup2 + 1];
|
||||
uint32 pixelGroup3 = uv1 | s_yLookup[yLookup3];
|
||||
uint32 pixelGroup4 = uv1 | s_yLookup[yLookup4];
|
||||
uint32 pixelGroup5 = uv1 | s_yLookup[yLookup1];
|
||||
uint32 pixelGroup6 = uv1 | s_yLookup[yLookup2];
|
||||
uint32 pixelGroup7 = uv2 | s_yLookup[yLookup3 + 1];
|
||||
uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
|
||||
|
||||
output[0x000] = getRGBLookupEntry(_colorMap, pixelGroup1 & 0xFFFF);
|
||||
output[0x001] = getRGBLookupEntry(_colorMap, pixelGroup2 >> 16);
|
||||
output[0x400] = getRGBLookupEntry(_colorMap, pixelGroup5 & 0xFFFF);
|
||||
output[0x401] = getRGBLookupEntry(_colorMap, pixelGroup6 >> 16);
|
||||
output[0x002] = getRGBLookupEntry(_colorMap, pixelGroup3 & 0xFFFF);
|
||||
output[0x003] = getRGBLookupEntry(_colorMap, pixelGroup4 >> 16);
|
||||
output[0x402] = getRGBLookupEntry(_colorMap, pixelGroup7 & 0xFFFF);
|
||||
output[0x403] = getRGBLookupEntry(_colorMap, pixelGroup8 >> 16);
|
||||
output[0x800] = getRGBLookupEntry(_colorMap, pixelGroup1 >> 16);
|
||||
output[0x801] = getRGBLookupEntry(_colorMap, pixelGroup6 & 0xFFFF);
|
||||
output[0xC00] = getRGBLookupEntry(_colorMap, pixelGroup5 >> 16);
|
||||
output[0xC01] = getRGBLookupEntry(_colorMap, pixelGroup2 & 0xFFFF);
|
||||
output[0x802] = getRGBLookupEntry(_colorMap, pixelGroup3 >> 16);
|
||||
output[0x803] = getRGBLookupEntry(_colorMap, pixelGroup8 & 0xFFFF);
|
||||
output[0xC02] = getRGBLookupEntry(_colorMap, pixelGroup7 >> 16);
|
||||
output[0xC03] = getRGBLookupEntry(_colorMap, pixelGroup4 & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
void CinepakDecoder::decodeVectors8(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
|
||||
decodeVectorsTmpl<byte, CodebookConverterPalette>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
}
|
||||
|
||||
void CinepakDecoder::decodeVectors24(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
|
||||
if (_curFrame.surface->format.bytesPerPixel == 2) {
|
||||
decodeVectorsTmpl<uint16, CodebookConverterRGB>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
} else if (_curFrame.surface->format.bytesPerPixel == 4) {
|
||||
decodeVectorsTmpl<uint32, CodebookConverterRGB>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool CinepakDecoder::setOutputPixelFormat(const Graphics::PixelFormat &format) {
|
||||
if (_bitsPerPixel == 8)
|
||||
return format.isCLUT8();
|
||||
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CinepakDecoder::canDither(DitherType type) const {
|
||||
return (type == kDitherTypeVFW || type == kDitherTypeQT) && _bitsPerPixel == 24;
|
||||
}
|
||||
|
||||
void CinepakDecoder::setDither(DitherType type, const byte *palette) {
|
||||
assert(canDither(type));
|
||||
|
||||
delete[] _colorMap;
|
||||
|
||||
_ditherPalette.resize(256, false);
|
||||
_ditherPalette.set(palette, 0, 256);
|
||||
|
||||
_dirtyPalette = true;
|
||||
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
|
||||
_ditherType = type;
|
||||
|
||||
if (type == kDitherTypeVFW) {
|
||||
_colorMap = new byte[1024];
|
||||
|
||||
for (int i = 0; i < 1024; i++)
|
||||
_colorMap[i] = findNearestRGB(s_defaultPaletteLookup[i]);
|
||||
} else {
|
||||
// Generate QuickTime dither table
|
||||
// 4 blocks of 0x4000 bytes (RGB554 lookup)
|
||||
_colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
|
||||
}
|
||||
}
|
||||
|
||||
byte CinepakDecoder::findNearestRGB(int index) const {
|
||||
byte r = s_defaultPalette[index * 3];
|
||||
byte g = s_defaultPalette[index * 3 + 1];
|
||||
byte b = s_defaultPalette[index * 3 + 2];
|
||||
|
||||
return _ditherPalette.findBestColor(r, g, b, Graphics::kColorDistanceEuclidean);
|
||||
}
|
||||
|
||||
void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
|
||||
decodeVectorsTmpl<byte, CodebookConverterDithered>(_curFrame, _clipTable, stream, strip, chunkID, chunkSize);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
111
image/codecs/cinepak.h
Normal file
111
image/codecs/cinepak.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CINEPAK_H
|
||||
#define IMAGE_CODECS_CINEPAK_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
struct CinepakCodebook {
|
||||
// These are not in the normal YUV colorspace, but in the Cinepak YUV colorspace instead.
|
||||
byte y[4]; // [0, 255]
|
||||
int8 u, v; // [-128, 127]
|
||||
};
|
||||
|
||||
struct CinepakStrip {
|
||||
uint16 id;
|
||||
uint16 length;
|
||||
Common::Rect rect;
|
||||
CinepakCodebook v1_codebook[256], v4_codebook[256];
|
||||
uint32 v1_dither[256 * 4 * 4], v4_dither[256 * 4 * 4];
|
||||
};
|
||||
|
||||
struct CinepakFrame {
|
||||
byte flags;
|
||||
uint32 length;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
uint16 stripCount;
|
||||
CinepakStrip *strips;
|
||||
|
||||
Graphics::Surface *surface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cinepak decoder.
|
||||
*
|
||||
* Used by BMP/AVI and PICT/QuickTime.
|
||||
*
|
||||
* Used in engines:
|
||||
* - sherlock
|
||||
*/
|
||||
class CinepakDecoder : public Codec {
|
||||
public:
|
||||
CinepakDecoder(int bitsPerPixel = 24);
|
||||
~CinepakDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override;
|
||||
|
||||
bool containsPalette() const override { return _ditherPalette != 0; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _ditherPalette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
|
||||
private:
|
||||
CinepakFrame _curFrame;
|
||||
int32 _y;
|
||||
int _bitsPerPixel;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
byte *_clipTable, *_clipTableBuf;
|
||||
|
||||
Graphics::Palette _ditherPalette;
|
||||
bool _dirtyPalette;
|
||||
byte *_colorMap;
|
||||
DitherType _ditherType;
|
||||
|
||||
void initializeCodebook(uint16 strip, byte codebookType);
|
||||
void loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize);
|
||||
void decodeVectors8(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
|
||||
void decodeVectors24(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
|
||||
|
||||
byte findNearestRGB(int index) const;
|
||||
void ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
|
||||
void ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex);
|
||||
void ditherCodebookVFW(uint16 strip, byte codebookType, uint16 codebookIndex);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
779
image/codecs/cinepak_tables.h
Normal file
779
image/codecs/cinepak_tables.h
Normal file
@@ -0,0 +1,779 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CINEPAK_TABLES_H
|
||||
#define IMAGE_CODECS_CINEPAK_TABLES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
static const byte s_defaultPaletteLookup[1024] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
|
||||
0x08, 0x08, 0x08, 0x09, 0x0A, 0x07, 0x07, 0x07,
|
||||
0x0B, 0x0B, 0x0B, 0x0C, 0x0D, 0x0D, 0x0D, 0x07,
|
||||
0x0E, 0x0E, 0x0E, 0x0F, 0x0D, 0x0D, 0x0D, 0x0D,
|
||||
0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x0D, 0x0D,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x17, 0x17, 0x17, 0x18, 0x19, 0x1A, 0x16, 0x16,
|
||||
0x1B, 0x1B, 0x1B, 0x1C, 0x1D, 0x1E, 0x1E, 0x1E,
|
||||
0x1F, 0x1F, 0x1F, 0x20, 0x21, 0x1E, 0x1E, 0x1E,
|
||||
0x22, 0x22, 0x22, 0x23, 0x24, 0x24, 0x24, 0x1E,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
|
||||
0x2A, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2E,
|
||||
0x2F, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x2E, 0x2E,
|
||||
0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
|
||||
0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
|
||||
0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
|
||||
0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
|
||||
0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
|
||||
0x3C, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x40, 0x40,
|
||||
0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x45, 0x45,
|
||||
0x46, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4A, 0x4A,
|
||||
0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
|
||||
0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
|
||||
0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
|
||||
0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
|
||||
0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
|
||||
0x55, 0x55, 0x56, 0x57, 0x58, 0x59, 0x59, 0x59,
|
||||
0x5A, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5E, 0x5E,
|
||||
0x5F, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x63, 0x63,
|
||||
0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
|
||||
0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
|
||||
0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
|
||||
0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
|
||||
0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
|
||||
0x6E, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x72, 0x72,
|
||||
0x73, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77, 0x77,
|
||||
0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7C,
|
||||
0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
|
||||
0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
|
||||
0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
|
||||
0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
|
||||
0x87, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8B, 0x8B,
|
||||
0x8C, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x90, 0x90,
|
||||
0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x95, 0x95,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
|
||||
0x9B, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
|
||||
0x9E, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
|
||||
0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1,
|
||||
0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5,
|
||||
0xA6, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xA9, 0xA9,
|
||||
0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
|
||||
0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
|
||||
0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
|
||||
0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
|
||||
0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
|
||||
0xB4, 0xB1, 0xB1, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3,
|
||||
0xB4, 0xB4, 0xB5, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7,
|
||||
0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0xC2, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
|
||||
0xC2, 0xC2, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
|
||||
0xC2, 0xC2, 0xC2, 0xC3, 0xC4, 0xC4, 0xC4, 0xC4,
|
||||
0xC8, 0xC5, 0xC5, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7,
|
||||
0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB,
|
||||
0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
|
||||
0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
|
||||
0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
|
||||
0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
|
||||
0xD2, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
|
||||
0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
|
||||
0xD2, 0xD2, 0xD2, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
|
||||
0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
|
||||
0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
|
||||
0xD8, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
|
||||
0xD8, 0xD8, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
|
||||
0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC
|
||||
};
|
||||
|
||||
static const byte s_defaultPalette[221 * 3] = {
|
||||
0x02, 0x02, 0x02,
|
||||
0x19, 0x19, 0x19,
|
||||
0x47, 0x02, 0x19,
|
||||
0x19, 0x0D, 0x47,
|
||||
0x00, 0x51, 0x00,
|
||||
0x2E, 0x3A, 0x00,
|
||||
0x5C, 0x23, 0x00,
|
||||
0x8F, 0x0A, 0x00,
|
||||
0x00, 0x45, 0x2E,
|
||||
0x2E, 0x2E, 0x2E,
|
||||
0x5C, 0x17, 0x2E,
|
||||
0x00, 0x3A, 0x5C,
|
||||
0x2E, 0x23, 0x5C,
|
||||
0x5C, 0x0C, 0x5C,
|
||||
0x00, 0x2C, 0x95,
|
||||
0x2E, 0x15, 0x95,
|
||||
0x00, 0x1A, 0xDC,
|
||||
0x2E, 0x03, 0xDC,
|
||||
0x15, 0x66, 0x15,
|
||||
0x43, 0x4F, 0x15,
|
||||
0x71, 0x38, 0x15,
|
||||
0xA4, 0x1E, 0x15,
|
||||
0xDB, 0x02, 0x15,
|
||||
0x15, 0x5A, 0x43,
|
||||
0x43, 0x43, 0x43,
|
||||
0x71, 0x2C, 0x43,
|
||||
0xA4, 0x13, 0x43,
|
||||
0x15, 0x4F, 0x71,
|
||||
0x43, 0x38, 0x71,
|
||||
0x71, 0x21, 0x71,
|
||||
0xA4, 0x07, 0x71,
|
||||
0x15, 0x40, 0xAA,
|
||||
0x43, 0x29, 0xAA,
|
||||
0x71, 0x12, 0xAA,
|
||||
0x15, 0x2F, 0xF1,
|
||||
0x43, 0x18, 0xF1,
|
||||
0x71, 0x01, 0xF1,
|
||||
0x29, 0x79, 0x29,
|
||||
0x57, 0x62, 0x29,
|
||||
0x85, 0x4B, 0x29,
|
||||
0xB7, 0x32, 0x29,
|
||||
0xEF, 0x16, 0x29,
|
||||
0x29, 0x6E, 0x57,
|
||||
0x57, 0x57, 0x57,
|
||||
0x85, 0x40, 0x57,
|
||||
0xB7, 0x27, 0x57,
|
||||
0xEF, 0x0B, 0x57,
|
||||
0x29, 0x62, 0x85,
|
||||
0x57, 0x4B, 0x85,
|
||||
0x85, 0x34, 0x85,
|
||||
0xB7, 0x1B, 0x85,
|
||||
0x29, 0x54, 0xBE,
|
||||
0x57, 0x3D, 0xBE,
|
||||
0x85, 0x26, 0xBE,
|
||||
0xB7, 0x0D, 0xBE,
|
||||
0x03, 0xB5, 0x09,
|
||||
0x3C, 0x99, 0x09,
|
||||
0x6A, 0x82, 0x09,
|
||||
0x98, 0x6B, 0x09,
|
||||
0xCA, 0x51, 0x09,
|
||||
0x03, 0xA9, 0x3C,
|
||||
0x3C, 0x8C, 0x3C,
|
||||
0x6A, 0x75, 0x3C,
|
||||
0x98, 0x5E, 0x3C,
|
||||
0xCA, 0x45, 0x3C,
|
||||
0x03, 0x9D, 0x6A,
|
||||
0x3C, 0x81, 0x6A,
|
||||
0x6A, 0x6A, 0x6A,
|
||||
0x98, 0x53, 0x6A,
|
||||
0xCA, 0x39, 0x6A,
|
||||
0x03, 0x92, 0x98,
|
||||
0x3C, 0x75, 0x98,
|
||||
0x6A, 0x5E, 0x98,
|
||||
0x98, 0x47, 0x98,
|
||||
0xCA, 0x2E, 0x98,
|
||||
0x03, 0x83, 0xD1,
|
||||
0x3C, 0x67, 0xD1,
|
||||
0x6A, 0x50, 0xD1,
|
||||
0x98, 0x39, 0xD1,
|
||||
0xCA, 0x20, 0xD1,
|
||||
0x14, 0xC7, 0x1B,
|
||||
0x4D, 0xAB, 0x1B,
|
||||
0x7B, 0x94, 0x1B,
|
||||
0xA9, 0x7D, 0x1B,
|
||||
0xDC, 0x63, 0x1B,
|
||||
0x14, 0xBA, 0x4D,
|
||||
0x4D, 0x9E, 0x4D,
|
||||
0x7B, 0x87, 0x4D,
|
||||
0xA9, 0x70, 0x4D,
|
||||
0xDC, 0x57, 0x4D,
|
||||
0x14, 0xAF, 0x7B,
|
||||
0x4D, 0x92, 0x7B,
|
||||
0x7B, 0x7B, 0x7B,
|
||||
0xA9, 0x64, 0x7B,
|
||||
0xDC, 0x4B, 0x7B,
|
||||
0x14, 0xA3, 0xA9,
|
||||
0x4D, 0x87, 0xA9,
|
||||
0x7B, 0x70, 0xA9,
|
||||
0xA9, 0x59, 0xA9,
|
||||
0xDC, 0x40, 0xA9,
|
||||
0x14, 0x95, 0xE2,
|
||||
0x4D, 0x79, 0xE2,
|
||||
0x7B, 0x62, 0xE2,
|
||||
0xA9, 0x4B, 0xE2,
|
||||
0xDC, 0x31, 0xE2,
|
||||
0x25, 0xD8, 0x2C,
|
||||
0x5E, 0xBB, 0x2C,
|
||||
0x8C, 0xA4, 0x2C,
|
||||
0xBA, 0x8D, 0x2C,
|
||||
0xED, 0x74, 0x2C,
|
||||
0x25, 0xCB, 0x5E,
|
||||
0x5E, 0xAF, 0x5E,
|
||||
0x8C, 0x98, 0x5E,
|
||||
0xBA, 0x81, 0x5E,
|
||||
0xED, 0x67, 0x5E,
|
||||
0x25, 0xC0, 0x8C,
|
||||
0x5E, 0xA3, 0x8C,
|
||||
0x8C, 0x8C, 0x8C,
|
||||
0xBA, 0x75, 0x8C,
|
||||
0xED, 0x5C, 0x8C,
|
||||
0x25, 0xB4, 0xBA,
|
||||
0x5E, 0x98, 0xBA,
|
||||
0x8C, 0x81, 0xBA,
|
||||
0xBA, 0x6A, 0xBA,
|
||||
0xED, 0x50, 0xBA,
|
||||
0x25, 0xA6, 0xF3,
|
||||
0x5E, 0x8A, 0xF3,
|
||||
0x8C, 0x73, 0xF3,
|
||||
0xBA, 0x5C, 0xF3,
|
||||
0xED, 0x42, 0xF3,
|
||||
0x35, 0xF6, 0x04,
|
||||
0x6E, 0xD9, 0x04,
|
||||
0x9C, 0xC2, 0x04,
|
||||
0xCA, 0xAB, 0x04,
|
||||
0xFD, 0x92, 0x04,
|
||||
0x35, 0xE8, 0x3C,
|
||||
0x6E, 0xCB, 0x3C,
|
||||
0x9C, 0xB4, 0x3C,
|
||||
0xCA, 0x9D, 0x3C,
|
||||
0xFD, 0x84, 0x3C,
|
||||
0x35, 0xDB, 0x6E,
|
||||
0x6E, 0xBF, 0x6E,
|
||||
0x9C, 0xA8, 0x6E,
|
||||
0xCA, 0x91, 0x6E,
|
||||
0xFD, 0x78, 0x6E,
|
||||
0x35, 0xD0, 0x9C,
|
||||
0x6E, 0xB3, 0x9C,
|
||||
0x9C, 0x9C, 0x9C,
|
||||
0xCA, 0x85, 0x9C,
|
||||
0xFD, 0x6C, 0x9C,
|
||||
0x35, 0xC4, 0xCA,
|
||||
0x6E, 0xA8, 0xCA,
|
||||
0x9C, 0x91, 0xCA,
|
||||
0xCA, 0x7A, 0xCA,
|
||||
0xFD, 0x61, 0xCA,
|
||||
0x7E, 0xE9, 0x13,
|
||||
0xAC, 0xD2, 0x13,
|
||||
0xDA, 0xBB, 0x13,
|
||||
0x45, 0xF7, 0x4B,
|
||||
0x7E, 0xDB, 0x4B,
|
||||
0xAC, 0xC4, 0x4B,
|
||||
0xDA, 0xAD, 0x4B,
|
||||
0x45, 0xEB, 0x7E,
|
||||
0x7E, 0xCE, 0x7E,
|
||||
0xAC, 0xB7, 0x7E,
|
||||
0xDA, 0xA0, 0x7E,
|
||||
0x45, 0xDF, 0xAC,
|
||||
0x7E, 0xC3, 0xAC,
|
||||
0xAC, 0xAC, 0xAC,
|
||||
0xDA, 0x95, 0xAC,
|
||||
0x45, 0xD4, 0xDA,
|
||||
0x7E, 0xB7, 0xDA,
|
||||
0xAC, 0xA0, 0xDA,
|
||||
0xDA, 0x89, 0xDA,
|
||||
0x8C, 0xF7, 0x22,
|
||||
0xBA, 0xE0, 0x22,
|
||||
0xE8, 0xC9, 0x22,
|
||||
0x8C, 0xE9, 0x59,
|
||||
0xBA, 0xD2, 0x59,
|
||||
0xE8, 0xBB, 0x59,
|
||||
0x53, 0xF9, 0x8C,
|
||||
0x8C, 0xDD, 0x8C,
|
||||
0xBA, 0xC6, 0x8C,
|
||||
0xE8, 0xAF, 0x8C,
|
||||
0x53, 0xEE, 0xBA,
|
||||
0x8C, 0xD1, 0xBA,
|
||||
0xBA, 0xBA, 0xBA,
|
||||
0xE8, 0xA3, 0xBA,
|
||||
0x53, 0xE2, 0xE8,
|
||||
0x8C, 0xC6, 0xE8,
|
||||
0xBA, 0xAF, 0xE8,
|
||||
0xE8, 0x98, 0xE8,
|
||||
0xC8, 0xEE, 0x30,
|
||||
0xF6, 0xD7, 0x30,
|
||||
0x9A, 0xF7, 0x67,
|
||||
0xC8, 0xE0, 0x67,
|
||||
0xF6, 0xC9, 0x67,
|
||||
0x9A, 0xEA, 0x9A,
|
||||
0xC8, 0xD3, 0x9A,
|
||||
0xF6, 0xBC, 0x9A,
|
||||
0x61, 0xFB, 0xC8,
|
||||
0x9A, 0xDF, 0xC8,
|
||||
0xC8, 0xC8, 0xC8,
|
||||
0xF6, 0xB1, 0xC8,
|
||||
0x61, 0xF0, 0xF6,
|
||||
0x9A, 0xD3, 0xF6,
|
||||
0xC8, 0xBC, 0xF6,
|
||||
0xF6, 0xA5, 0xF6,
|
||||
0xD5, 0xFB, 0x3D,
|
||||
0xD5, 0xED, 0x74,
|
||||
0xA7, 0xF7, 0xA7,
|
||||
0xD5, 0xE0, 0xA7,
|
||||
0xA7, 0xEC, 0xD5,
|
||||
0xD5, 0xD5, 0xD5,
|
||||
0xE1, 0xFA, 0x81,
|
||||
0xE1, 0xED, 0xB3,
|
||||
0xB3, 0xF8, 0xE1,
|
||||
0xE1, 0xE1, 0xE1,
|
||||
0xED, 0xF9, 0xBF,
|
||||
0xED, 0xED, 0xED,
|
||||
0xF8, 0xF8, 0xF8
|
||||
};
|
||||
|
||||
static const uint32 s_yLookup[512] = {
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000040, 0x00000000, 0x00000040, 0x00000000,
|
||||
0x00000040, 0x00000000, 0x00000040, 0x00000000,
|
||||
0x00000040, 0x00000000, 0x00000040, 0x00000040,
|
||||
0x00000040, 0x00000040, 0x00000040, 0x00000040,
|
||||
0x00000040, 0x00000040, 0x00400040, 0x00000040,
|
||||
0x00400040, 0x00000040, 0x00400040, 0x00000040,
|
||||
0x00400040, 0x00000040, 0x00400040, 0x00000040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400040, 0x00400040,
|
||||
0x00400040, 0x00400040, 0x00400080, 0x00400040,
|
||||
0x00400080, 0x00400040, 0x00400080, 0x00400040,
|
||||
0x00400080, 0x00400040, 0x00400080, 0x00400080,
|
||||
0x00400080, 0x00400080, 0x00400080, 0x00400080,
|
||||
0x00400080, 0x00400080, 0x00400080, 0x00400080,
|
||||
0x00800080, 0x00400080, 0x00800080, 0x00400080,
|
||||
0x00800080, 0x00400080, 0x00800080, 0x00400080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x00800080, 0x00800080,
|
||||
0x00800080, 0x00800080, 0x008000C0, 0x00800080,
|
||||
0x008000C0, 0x00800080, 0x008000C0, 0x00800080,
|
||||
0x008000C0, 0x00800080, 0x008000C0, 0x008000C0,
|
||||
0x008000C0, 0x008000C0, 0x008000C0, 0x008000C0,
|
||||
0x008000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
|
||||
0x00C000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
|
||||
0x00C000C0, 0x008000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
|
||||
0x00C000C0, 0x00C000C0, 0x00C00100, 0x00C000C0,
|
||||
0x00C00100, 0x00C000C0, 0x00C00100, 0x00C000C0,
|
||||
0x00C00100, 0x00C000C0, 0x00C00100, 0x00C00100,
|
||||
0x00C00100, 0x00C00100, 0x00C00100, 0x00C00100,
|
||||
0x00C00100, 0x00C00100, 0x01000100, 0x00C00100,
|
||||
0x01000100, 0x00C00100, 0x01000100, 0x00C00100,
|
||||
0x01000100, 0x00C00100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000100, 0x01000100,
|
||||
0x01000100, 0x01000100, 0x01000140, 0x01000100,
|
||||
0x01000140, 0x01000100, 0x01000140, 0x01000100,
|
||||
0x01000140, 0x01000140, 0x01000140, 0x01000140,
|
||||
0x01000140, 0x01000140, 0x01000140, 0x01000140,
|
||||
0x01400140, 0x01000140, 0x01400140, 0x01000140,
|
||||
0x01400140, 0x01000140, 0x01400140, 0x01000140,
|
||||
0x01400140, 0x01400140, 0x01400140, 0x01400140,
|
||||
0x01400140, 0x01400140, 0x01400140, 0x01400140,
|
||||
0x01400140, 0x01400140, 0x01400140, 0x01400140,
|
||||
0x01400140, 0x01400140, 0x01400180, 0x01400140,
|
||||
0x01400180, 0x01400140, 0x01400180, 0x01400140,
|
||||
0x01400180, 0x01400140, 0x01400180, 0x01400180,
|
||||
0x01400180, 0x01400180, 0x01400180, 0x01400180,
|
||||
0x01800180, 0x01400180, 0x01800180, 0x01400180,
|
||||
0x01800180, 0x01400180, 0x01800180, 0x01400180,
|
||||
0x01800180, 0x01800180, 0x01800180, 0x01800180,
|
||||
0x01800180, 0x01800180, 0x01800180, 0x01800180,
|
||||
0x01800180, 0x01800180, 0x01800180, 0x01800180,
|
||||
0x01800180, 0x01800180, 0x018001C0, 0x01800180,
|
||||
0x018001C0, 0x01800180, 0x018001C0, 0x01800180,
|
||||
0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
|
||||
0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
|
||||
0x01C001C0, 0x018001C0, 0x01C001C0, 0x018001C0,
|
||||
0x01C001C0, 0x018001C0, 0x01C001C0, 0x01C001C0,
|
||||
0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
|
||||
0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
|
||||
0x01C001C0, 0x01C001C0, 0x01C00200, 0x01C001C0,
|
||||
0x01C00200, 0x01C001C0, 0x01C00200, 0x01C001C0,
|
||||
0x01C00200, 0x01C001C0, 0x01C00200, 0x01C00200,
|
||||
0x01C00200, 0x01C00200, 0x01C00200, 0x01C00200,
|
||||
0x02000200, 0x01C00200, 0x02000200, 0x01C00200,
|
||||
0x02000200, 0x01C00200, 0x02000200, 0x02000200,
|
||||
0x02000200, 0x02000200, 0x02000200, 0x02000200,
|
||||
0x02000200, 0x02000200, 0x02000200, 0x02000200,
|
||||
0x02000200, 0x02000200, 0x02000240, 0x02000200,
|
||||
0x02000240, 0x02000200, 0x02000240, 0x02000200,
|
||||
0x02000240, 0x02000240, 0x02000240, 0x02000240,
|
||||
0x02000240, 0x02000240, 0x02400240, 0x02000240,
|
||||
0x02400240, 0x02000240, 0x02400240, 0x02000240,
|
||||
0x02400240, 0x02000240, 0x02400240, 0x02400240,
|
||||
0x02400240, 0x02400240, 0x02400240, 0x02400240,
|
||||
0x02400240, 0x02400240, 0x02400240, 0x02400240,
|
||||
0x02400280, 0x02400240, 0x02400280, 0x02400240,
|
||||
0x02400280, 0x02400240, 0x02400280, 0x02400280,
|
||||
0x02400280, 0x02400280, 0x02400280, 0x02400280,
|
||||
0x02800280, 0x02400280, 0x02800280, 0x02400280,
|
||||
0x02800280, 0x02400280, 0x02800280, 0x02800280,
|
||||
0x02800280, 0x02800280, 0x02800280, 0x02800280,
|
||||
0x02800280, 0x02800280, 0x02800280, 0x02800280,
|
||||
0x02800280, 0x02800280, 0x028002C0, 0x02800280,
|
||||
0x028002C0, 0x02800280, 0x028002C0, 0x02800280,
|
||||
0x028002C0, 0x028002C0, 0x028002C0, 0x028002C0,
|
||||
0x02C002C0, 0x028002C0, 0x02C002C0, 0x028002C0,
|
||||
0x02C002C0, 0x028002C0, 0x02C002C0, 0x02C002C0,
|
||||
0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
|
||||
0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
|
||||
0x02C00300, 0x02C002C0, 0x02C00300, 0x02C002C0,
|
||||
0x02C00300, 0x02C002C0, 0x02C00300, 0x02C00300,
|
||||
0x02C00300, 0x02C00300, 0x02C00300, 0x02C00300,
|
||||
0x03000300, 0x02C00300, 0x03000300, 0x02C00300,
|
||||
0x03000300, 0x03000300, 0x03000300, 0x03000300,
|
||||
0x03000300, 0x03000300, 0x03000300, 0x03000300,
|
||||
0x03000300, 0x03000300, 0x03000340, 0x03000300,
|
||||
0x03000340, 0x03000300, 0x03000340, 0x03000300,
|
||||
0x03000340, 0x03000340, 0x03000340, 0x03000340,
|
||||
0x03400340, 0x03000340, 0x03400340, 0x03000340,
|
||||
0x03400340, 0x03000340, 0x03400340, 0x03400340,
|
||||
0x03400340, 0x03400340, 0x03400340, 0x03400340,
|
||||
0x03400340, 0x03400340, 0x03400340, 0x03400340,
|
||||
0x03400380, 0x03400340, 0x03400380, 0x03400340,
|
||||
0x03400380, 0x03400380, 0x03400380, 0x03400380,
|
||||
0x03800380, 0x03400380, 0x03800380, 0x03400380,
|
||||
0x03800380, 0x03400380, 0x03800380, 0x03800380,
|
||||
0x03800380, 0x03800380, 0x03800380, 0x03800380,
|
||||
0x03800380, 0x03800380, 0x038003C0, 0x03800380,
|
||||
0x038003C0, 0x03800380, 0x038003C0, 0x03800380,
|
||||
0x038003C0, 0x038003C0, 0x038003C0, 0x038003C0,
|
||||
0x03C003C0, 0x038003C0, 0x03C003C0, 0x038003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
|
||||
0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0
|
||||
};
|
||||
|
||||
static const uint32 s_uLookup[512] = {
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00280020, 0x00200020,
|
||||
0x00280020, 0x00200020, 0x00280020, 0x00200020,
|
||||
0x00280020, 0x00200020, 0x00280020, 0x00280020,
|
||||
0x00280020, 0x00280020, 0x00280020, 0x00280020,
|
||||
0x00280020, 0x00280020, 0x00280020, 0x00280020,
|
||||
0x00280020, 0x00280028, 0x00280020, 0x00280028,
|
||||
0x00280020, 0x00280028, 0x00280020, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00280028, 0x00280028,
|
||||
0x00280028, 0x00280028, 0x00300028, 0x00280028,
|
||||
0x00300028, 0x00280028, 0x00300028, 0x00280028,
|
||||
0x00300028, 0x00280028, 0x00300028, 0x00280028,
|
||||
0x00300028, 0x00300028, 0x00300028, 0x00300028,
|
||||
0x00300028, 0x00300028, 0x00300028, 0x00300028,
|
||||
0x00300028, 0x00300028, 0x00300028, 0x00300028,
|
||||
0x00300028, 0x00300030, 0x00300028, 0x00300030,
|
||||
0x00300028, 0x00300030, 0x00300028, 0x00300030,
|
||||
0x00300028, 0x00300030, 0x00300028, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00300030, 0x00300030,
|
||||
0x00300030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00300030, 0x00380030, 0x00300030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380030,
|
||||
0x00380030, 0x00380030, 0x00380030, 0x00380038,
|
||||
0x00380030, 0x00380038, 0x00380030, 0x00380038,
|
||||
0x00380030, 0x00380038, 0x00380030, 0x00380038,
|
||||
0x00380030, 0x00380038, 0x00380030, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00380038, 0x00380038, 0x00380038, 0x00380038,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00000000, 0x00080000, 0x00000000,
|
||||
0x00080000, 0x00080000, 0x00080000, 0x00080000,
|
||||
0x00080000, 0x00080000, 0x00080000, 0x00080000,
|
||||
0x00080000, 0x00080000, 0x00080000, 0x00080000,
|
||||
0x00080000, 0x00080008, 0x00080000, 0x00080008,
|
||||
0x00080000, 0x00080008, 0x00080000, 0x00080008,
|
||||
0x00080000, 0x00080008, 0x00080000, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00080008, 0x00080008,
|
||||
0x00080008, 0x00080008, 0x00100008, 0x00080008,
|
||||
0x00100008, 0x00080008, 0x00100008, 0x00080008,
|
||||
0x00100008, 0x00080008, 0x00100008, 0x00080008,
|
||||
0x00100008, 0x00080008, 0x00100008, 0x00100008,
|
||||
0x00100008, 0x00100008, 0x00100008, 0x00100008,
|
||||
0x00100008, 0x00100008, 0x00100008, 0x00100008,
|
||||
0x00100008, 0x00100008, 0x00100008, 0x00100010,
|
||||
0x00100008, 0x00100010, 0x00100008, 0x00100010,
|
||||
0x00100008, 0x00100010, 0x00100008, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00100010, 0x00100010,
|
||||
0x00100010, 0x00100010, 0x00180010, 0x00100010,
|
||||
0x00180010, 0x00100010, 0x00180010, 0x00100010,
|
||||
0x00180010, 0x00100010, 0x00180010, 0x00100010,
|
||||
0x00180010, 0x00180010, 0x00180010, 0x00180010,
|
||||
0x00180010, 0x00180010, 0x00180010, 0x00180010,
|
||||
0x00180010, 0x00180010, 0x00180010, 0x00180018,
|
||||
0x00180010, 0x00180018, 0x00180010, 0x00180018,
|
||||
0x00180010, 0x00180018, 0x00180010, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00180018, 0x00180018, 0x00180018, 0x00180018,
|
||||
0x00200018, 0x00180018, 0x00200018, 0x00180018,
|
||||
0x00200018, 0x00180018, 0x00200018, 0x00180018,
|
||||
0x00200018, 0x00200018, 0x00200018, 0x00200018,
|
||||
0x00200018, 0x00200018, 0x00200018, 0x00200018,
|
||||
0x00200018, 0x00200018, 0x00200018, 0x00200020,
|
||||
0x00200018, 0x00200020, 0x00200018, 0x00200020,
|
||||
0x00200018, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020,
|
||||
0x00200020, 0x00200020, 0x00200020, 0x00200020
|
||||
};
|
||||
|
||||
static const uint32 s_vLookup[512] = {
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030004,
|
||||
0x00030003, 0x00030004, 0x00030003, 0x00030004,
|
||||
0x00030003, 0x00030004, 0x00030004, 0x00030004,
|
||||
0x00030004, 0x00030004, 0x00030004, 0x00030004,
|
||||
0x00030004, 0x00030004, 0x00030004, 0x00030004,
|
||||
0x00030004, 0x00040004, 0x00030004, 0x00040004,
|
||||
0x00030004, 0x00040004, 0x00030004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040004, 0x00040004, 0x00040004,
|
||||
0x00040004, 0x00040005, 0x00040004, 0x00040005,
|
||||
0x00040004, 0x00040005, 0x00040004, 0x00040005,
|
||||
0x00040004, 0x00040005, 0x00040005, 0x00040005,
|
||||
0x00040005, 0x00040005, 0x00040005, 0x00040005,
|
||||
0x00040005, 0x00040005, 0x00040005, 0x00040005,
|
||||
0x00040005, 0x00050005, 0x00040005, 0x00050005,
|
||||
0x00040005, 0x00050005, 0x00040005, 0x00050005,
|
||||
0x00040005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050005, 0x00050005, 0x00050005,
|
||||
0x00050005, 0x00050006, 0x00050005, 0x00050006,
|
||||
0x00050005, 0x00050006, 0x00050005, 0x00050006,
|
||||
0x00050005, 0x00050006, 0x00050006, 0x00050006,
|
||||
0x00050006, 0x00050006, 0x00050006, 0x00050006,
|
||||
0x00050006, 0x00050006, 0x00050006, 0x00050006,
|
||||
0x00050006, 0x00050006, 0x00050006, 0x00060006,
|
||||
0x00050006, 0x00060006, 0x00050006, 0x00060006,
|
||||
0x00050006, 0x00060006, 0x00050006, 0x00060006,
|
||||
0x00050006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060006, 0x00060006, 0x00060006,
|
||||
0x00060006, 0x00060007, 0x00060006, 0x00060007,
|
||||
0x00060006, 0x00060007, 0x00060006, 0x00060007,
|
||||
0x00060006, 0x00060007, 0x00060006, 0x00060007,
|
||||
0x00060007, 0x00060007, 0x00060007, 0x00060007,
|
||||
0x00060007, 0x00060007, 0x00060007, 0x00060007,
|
||||
0x00060007, 0x00060007, 0x00060007, 0x00060007,
|
||||
0x00060007, 0x00070007, 0x00060007, 0x00070007,
|
||||
0x00060007, 0x00070007, 0x00060007, 0x00070007,
|
||||
0x00060007, 0x00070007, 0x00060007, 0x00070007,
|
||||
0x00060007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00070007, 0x00070007, 0x00070007, 0x00070007,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
||||
0x00000001, 0x00010001, 0x00000001, 0x00010001,
|
||||
0x00000001, 0x00010001, 0x00000001, 0x00010001,
|
||||
0x00000001, 0x00010001, 0x00000001, 0x00010001,
|
||||
0x00000001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010001, 0x00010001, 0x00010001,
|
||||
0x00010001, 0x00010002, 0x00010001, 0x00010002,
|
||||
0x00010001, 0x00010002, 0x00010001, 0x00010002,
|
||||
0x00010001, 0x00010002, 0x00010001, 0x00010002,
|
||||
0x00010002, 0x00010002, 0x00010002, 0x00010002,
|
||||
0x00010002, 0x00010002, 0x00010002, 0x00010002,
|
||||
0x00010002, 0x00010002, 0x00010002, 0x00010002,
|
||||
0x00010002, 0x00020002, 0x00010002, 0x00020002,
|
||||
0x00010002, 0x00020002, 0x00010002, 0x00020002,
|
||||
0x00010002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020002, 0x00020002, 0x00020002,
|
||||
0x00020002, 0x00020003, 0x00020002, 0x00020003,
|
||||
0x00020002, 0x00020003, 0x00020002, 0x00020003,
|
||||
0x00020003, 0x00020003, 0x00020003, 0x00020003,
|
||||
0x00020003, 0x00020003, 0x00020003, 0x00020003,
|
||||
0x00020003, 0x00020003, 0x00020003, 0x00030003,
|
||||
0x00020003, 0x00030003, 0x00020003, 0x00030003,
|
||||
0x00020003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003,
|
||||
0x00030003, 0x00030003, 0x00030003, 0x00030003
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
220
image/codecs/codec.cpp
Normal file
220
image/codecs/codec.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
#include "image/jpeg.h"
|
||||
#include "image/codecs/bmp_raw.h"
|
||||
#include "image/codecs/cdtoons.h"
|
||||
#include "image/codecs/cinepak.h"
|
||||
#include "image/codecs/indeo3.h"
|
||||
#include "image/codecs/indeo4.h"
|
||||
#include "image/codecs/indeo5.h"
|
||||
#include "image/codecs/jyv1.h"
|
||||
#include "image/codecs/mjpeg.h"
|
||||
#include "image/codecs/mpeg.h"
|
||||
#include "image/codecs/msvideo1.h"
|
||||
#include "image/codecs/msrle.h"
|
||||
#include "image/codecs/msrle4.h"
|
||||
#include "image/codecs/qtrle.h"
|
||||
#include "image/codecs/rpza.h"
|
||||
#include "image/codecs/smc.h"
|
||||
#include "image/codecs/svq1.h"
|
||||
#include "image/codecs/truemotion1.h"
|
||||
#include "image/codecs/xan.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
Graphics::PixelFormat Codec::getDefaultYUVFormat() {
|
||||
Graphics::PixelFormat format = g_system->getScreenFormat();
|
||||
|
||||
// Default to a 32bpp format, if in 8bpp mode
|
||||
if (format.isCLUT8())
|
||||
return Graphics::PixelFormat::createFormatRGBA32();
|
||||
else
|
||||
return format;
|
||||
}
|
||||
|
||||
Codec *createBitmapCodec(uint32 tag, uint32 streamTag, int width, int height, int bitsPerPixel) {
|
||||
#ifdef USE_JYV1
|
||||
// Crusader videos are special cased here because the frame type is not in the "compression"
|
||||
// tag but in the "stream handler" tag for these files
|
||||
if (JYV1Decoder::isJYV1StreamTag(streamTag)) {
|
||||
assert(bitsPerPixel == 8);
|
||||
return new JYV1Decoder(width, height, streamTag);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *missingCodec = nullptr;
|
||||
|
||||
switch (tag) {
|
||||
case SWAP_CONSTANT_32(0):
|
||||
return new BitmapRawDecoder(width, height, bitsPerPixel, false);
|
||||
case SWAP_CONSTANT_32(1):
|
||||
return new MSRLEDecoder(width, height, bitsPerPixel);
|
||||
case SWAP_CONSTANT_32(2):
|
||||
return new MSRLE4Decoder(width, height, bitsPerPixel);
|
||||
case SWAP_CONSTANT_32(3):
|
||||
// Used with v4-v5 BMP headers to produce transparent BMPs
|
||||
return new BitmapRawDecoder(width, height, bitsPerPixel, false);
|
||||
case MKTAG('C','R','A','M'):
|
||||
case MKTAG('m','s','v','c'):
|
||||
case MKTAG('W','H','A','M'):
|
||||
return new MSVideo1Decoder(width, height, bitsPerPixel);
|
||||
case MKTAG('c','v','i','d'):
|
||||
return new CinepakDecoder(bitsPerPixel);
|
||||
|
||||
case MKTAG('I','V','3','2'):
|
||||
#ifdef USE_INDEO3
|
||||
return new Indeo3Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 3";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('I', 'V', '4', '1'):
|
||||
case MKTAG('I', 'V', '4', '2'):
|
||||
#ifdef USE_INDEO45
|
||||
return new Indeo4Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 4";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('I', 'V', '5', '0'):
|
||||
#ifdef USE_INDEO45
|
||||
return new Indeo5Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 5";
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MKTAG('X', 'x', 'a', 'n'):
|
||||
#ifdef USE_XAN
|
||||
return new XanDecoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Xan";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('D','U','C','K'):
|
||||
case MKTAG('d','u','c','k'):
|
||||
#ifdef USE_TRUEMOTION1
|
||||
return new TrueMotion1Decoder();
|
||||
#else
|
||||
missingCodec = "TrueMotion1";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('m','p','g','2'):
|
||||
#ifdef USE_MPEG2
|
||||
return new MPEGDecoder();
|
||||
#else
|
||||
missingCodec = "MPEG2";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('M','J','P','G'):
|
||||
case MKTAG('m','j','p','g'):
|
||||
#ifdef USE_MJPEG
|
||||
return new MJPEGDecoder();
|
||||
#else
|
||||
missingCodec = "MJPEG";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (tag & 0x00FFFFFF)
|
||||
warning("Unknown BMP/AVI compression format \'%s\'", tag2str(tag));
|
||||
else
|
||||
warning("Unknown BMP/AVI compression format %d", SWAP_BYTES_32(tag));
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(missingCodec);
|
||||
warning("createBitmapCodec(): %s codec is not compiled", missingCodec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Codec *createQuickTimeCodec(uint32 tag, int width, int height, int bitsPerPixel) {
|
||||
const char *missingCodec = nullptr;
|
||||
|
||||
switch (tag) {
|
||||
case MKTAG('c','v','i','d'):
|
||||
// Cinepak: As used by most Myst and all Riven videos as well as some Myst ME videos. "The Chief" videos also use this. Very popular for Director titles.
|
||||
return new CinepakDecoder(bitsPerPixel);
|
||||
case MKTAG('r','p','z','a'):
|
||||
// Apple Video ("Road Pizza"): Used by some Myst videos.
|
||||
return new RPZADecoder(width, height);
|
||||
case MKTAG('r','l','e',' '):
|
||||
// QuickTime RLE: Used by some Myst ME videos.
|
||||
return new QTRLEDecoder(width, height, bitsPerPixel);
|
||||
case MKTAG('s','m','c',' '):
|
||||
// Apple SMC: Used by some Myst videos.
|
||||
return new SMCDecoder(width, height);
|
||||
case MKTAG('S','V','Q','1'):
|
||||
#ifdef USE_SVQ1
|
||||
// Sorenson Video 1: Used by some Myst ME videos.
|
||||
return new SVQ1Decoder(width, height);
|
||||
#else
|
||||
missingCodec = "Sorenson Video 1";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('S','V','Q','3'):
|
||||
// Sorenson Video 3: Used by some Myst ME videos.
|
||||
warning("Sorenson Video 3 not yet supported");
|
||||
return 0;
|
||||
case MKTAG('j','p','e','g'):
|
||||
#ifdef USE_JPEG
|
||||
// JPEG: Used by some Myst ME 10th Anniversary videos.
|
||||
return new JPEGDecoder();
|
||||
#else
|
||||
missingCodec = "JPEG";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('Q','k','B','k'):
|
||||
#ifdef USE_CDTOONS
|
||||
// CDToons: Used by most of the Broderbund games.
|
||||
return new CDToonsDecoder(width, height);
|
||||
#else
|
||||
missingCodec = "CDToons";
|
||||
break;
|
||||
#endif
|
||||
case MKTAG('r','a','w',' '):
|
||||
// Used my L-Zone-mac (Director game)
|
||||
return new BitmapRawDecoder(width, height, bitsPerPixel, true, true);
|
||||
case MKTAG('I','V','3','2'):
|
||||
#ifdef USE_INDEO3
|
||||
// Indeo 3: Used by Team Xtreme: Operation Weather Disaster (Spanish)
|
||||
return new Indeo3Decoder(width, height, bitsPerPixel);
|
||||
#else
|
||||
missingCodec = "Indeo 3";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
warning("Unsupported QuickTime codec \'%s\'", tag2str(tag));
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(missingCodec);
|
||||
warning("createBitmapCodec(): %s codec is not compiled", missingCodec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
144
image/codecs/codec.h
Normal file
144
image/codecs/codec.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_CODEC_H
|
||||
#define IMAGE_CODECS_CODEC_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
#include "image/codec-options.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* An abstract representation of a image codec.
|
||||
*
|
||||
* Unlike ImageDecoder, the entire info for a frame may not be present
|
||||
* within the stream. The codec may rely on the supporting container
|
||||
* for parameters and can also rely on a previous (or future) frame.
|
||||
* When decoding, the previous frame may not destroyed and could be
|
||||
* maintained for use in the next one.
|
||||
*
|
||||
* An ImageDecoder can always be a Codec, but a Codec may not necessarily
|
||||
* be able to be an ImageDecoder.
|
||||
*
|
||||
* Used in image:
|
||||
* - BitmapDecoder
|
||||
* - PICTDecoder
|
||||
*
|
||||
* Used in video:
|
||||
* - AVIDecoder
|
||||
* - QuickTimeDecoder
|
||||
* - VMDDecoder
|
||||
*/
|
||||
class Codec {
|
||||
public:
|
||||
Codec() {}
|
||||
virtual ~Codec() {}
|
||||
|
||||
/**
|
||||
* A type of dithering.
|
||||
*/
|
||||
enum DitherType {
|
||||
/** Unknown */
|
||||
kDitherTypeUnknown,
|
||||
|
||||
/** Video for Windows dithering */
|
||||
kDitherTypeVFW,
|
||||
|
||||
/** QuickTime dithering */
|
||||
kDitherTypeQT
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode the frame for the given data and return a pointer to a surface
|
||||
* containing the decoded frame.
|
||||
*
|
||||
* @return a pointer to the decoded frame
|
||||
*/
|
||||
virtual const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) = 0;
|
||||
|
||||
/**
|
||||
* Get the format that the surface returned from decodeImage() will
|
||||
* be in.
|
||||
*/
|
||||
virtual Graphics::PixelFormat getPixelFormat() const = 0;
|
||||
|
||||
/**
|
||||
* Select the preferred format to use, for codecs where this is faster than converting
|
||||
* the image afterwards. Returns true if supported, and false otherwise.
|
||||
*/
|
||||
virtual bool setOutputPixelFormat(const Graphics::PixelFormat &format) { return format == getPixelFormat(); }
|
||||
|
||||
/**
|
||||
* Can this codec's frames contain a palette?
|
||||
*/
|
||||
virtual bool containsPalette() const { return false; }
|
||||
|
||||
/**
|
||||
* Get the palette last decoded from decodeImage
|
||||
*/
|
||||
virtual const byte *getPalette() { return 0; }
|
||||
|
||||
/**
|
||||
* Does the codec have a dirty palette?
|
||||
*/
|
||||
virtual bool hasDirtyPalette() const { return false; }
|
||||
|
||||
/**
|
||||
* Can the codec dither down to 8bpp?
|
||||
*/
|
||||
virtual bool canDither(DitherType type) const { return false; }
|
||||
|
||||
/**
|
||||
* Activate dithering mode with a palette
|
||||
*/
|
||||
virtual void setDither(DitherType type, const byte *palette) {}
|
||||
|
||||
/**
|
||||
* Set the decoding accuracy of the codec, if supported
|
||||
*/
|
||||
virtual void setCodecAccuracy(CodecAccuracy accuracy) {}
|
||||
|
||||
/**
|
||||
* Get the preferred default pixel format for use with YUV codecs
|
||||
*/
|
||||
static Graphics::PixelFormat getDefaultYUVFormat();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a codec given a bitmap/AVI compression tag and stream handler tag (can be 0)
|
||||
*/
|
||||
Codec *createBitmapCodec(uint32 tag, uint32 streamTag, int width, int height, int bitsPerPixel);
|
||||
|
||||
/**
|
||||
* Create a codec given a QuickTime compression tag.
|
||||
*/
|
||||
Codec *createQuickTimeCodec(uint32 tag, int width, int height, int bitsPerPixel);
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
332
image/codecs/dither.cpp
Normal file
332
image/codecs/dither.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Add a color to the QuickTime dither table check queue if it hasn't already been found.
|
||||
*/
|
||||
inline void addColorToQueue(uint16 color, uint16 index, byte *checkBuffer, Common::List<uint16> &checkQueue) {
|
||||
if ((READ_UINT16(checkBuffer + color * 2) & 0xFF) == 0) {
|
||||
// Previously unfound color
|
||||
WRITE_UINT16(checkBuffer + color * 2, index);
|
||||
checkQueue.push_back(color);
|
||||
}
|
||||
}
|
||||
|
||||
inline byte adjustColorRange(byte currentColor, byte correctColor, byte palColor) {
|
||||
return CLIP<int>(currentColor - palColor + correctColor, 0, 255);
|
||||
}
|
||||
|
||||
inline uint16 makeQuickTimeDitherColor(byte r, byte g, byte b) {
|
||||
// RGB554
|
||||
return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
DitherCodec::DitherCodec(Codec *codec, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _codec(codec), _disposeAfterUse(disposeAfterUse), _dirtyPalette(false),
|
||||
_forcedDitherPalette(0), _ditherTable(0), _ditherFrame(0), _srcPalette(nullptr) {
|
||||
}
|
||||
|
||||
DitherCodec::~DitherCodec() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _codec;
|
||||
|
||||
delete[] _ditherTable;
|
||||
|
||||
if (_ditherFrame) {
|
||||
_ditherFrame->free();
|
||||
delete _ditherFrame;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Default template to convert a dither color
|
||||
inline uint16 readQT_RGB(uint32 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
byte r, g, b;
|
||||
format.colorToRGB(srcColor, r, g, b);
|
||||
return makeQuickTimeDitherColor(r, g, b);
|
||||
}
|
||||
|
||||
// Specialized version for 8bpp
|
||||
inline uint16 readQT_Palette(uint8 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
return makeQuickTimeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]);
|
||||
}
|
||||
|
||||
// Specialized version for RGB554
|
||||
inline uint16 readQT_RGB554(uint16 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
return srcColor;
|
||||
}
|
||||
|
||||
// Specialized version for RGB555 and ARGB1555
|
||||
inline uint16 readQT_RGB555(uint16 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
|
||||
return (srcColor >> 1) & 0x3FFF;
|
||||
}
|
||||
|
||||
template<typename PixelInt, class Fn>
|
||||
void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, Fn fn, const byte *palette = 0) {
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
for (int y = 0; y < dst.h; y++) {
|
||||
const PixelInt *srcPtr = (const PixelInt *)src.getBasePtr(0, y);
|
||||
byte *dstPtr = (byte *)dst.getBasePtr(0, y);
|
||||
uint16 colorTableOffset = colorTableOffsets[y & 3];
|
||||
|
||||
for (int x = 0; x < dst.w; x++) {
|
||||
uint16 color = fn(*srcPtr++, src.format, palette);
|
||||
*dstPtr++ = ditherTable[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
const Graphics::Surface *frame = _codec->decodeFrame(stream);
|
||||
if (!frame || _forcedDitherPalette.empty())
|
||||
return frame;
|
||||
|
||||
const byte *curPalette = _codec->containsPalette() ? _codec->getPalette() : _srcPalette;
|
||||
|
||||
if (frame->format.isCLUT8() && curPalette) {
|
||||
// This should always be true, but this is for sanity
|
||||
if (!curPalette)
|
||||
return frame;
|
||||
|
||||
// If the palettes match, bail out
|
||||
if (memcmp(_forcedDitherPalette.data(), curPalette, 256 * 3) == 0)
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Need to create a new one
|
||||
if (!_ditherFrame) {
|
||||
_ditherFrame = new Graphics::Surface();
|
||||
_ditherFrame->create(frame->w, frame->h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
if (frame->format.isCLUT8() && curPalette)
|
||||
ditherQuickTimeFrame<byte>(*frame, *_ditherFrame, _ditherTable, readQT_Palette, curPalette);
|
||||
else if (frame->format == Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0))
|
||||
ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB554);
|
||||
else if (frame->format == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0) ||
|
||||
frame->format == Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15))
|
||||
ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB555);
|
||||
else if (frame->format.bytesPerPixel == 2)
|
||||
ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB);
|
||||
else if (frame->format.bytesPerPixel == 4)
|
||||
ditherQuickTimeFrame<uint32>(*frame, *_ditherFrame, _ditherTable, readQT_RGB);
|
||||
|
||||
return _ditherFrame;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat DitherCodec::getPixelFormat() const {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->getPixelFormat();
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
bool DitherCodec::setOutputPixelFormat(const Graphics::PixelFormat &format) {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->setOutputPixelFormat(format);
|
||||
return format.isCLUT8();
|
||||
}
|
||||
|
||||
bool DitherCodec::containsPalette() const {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->containsPalette();
|
||||
return true;
|
||||
}
|
||||
|
||||
const byte *DitherCodec::getPalette() {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->getPalette();
|
||||
_dirtyPalette = false;
|
||||
return _forcedDitherPalette.data();
|
||||
}
|
||||
|
||||
bool DitherCodec::hasDirtyPalette() const {
|
||||
if (_forcedDitherPalette.empty())
|
||||
return _codec->hasDirtyPalette();
|
||||
return _dirtyPalette;
|
||||
}
|
||||
|
||||
bool DitherCodec::canDither(DitherType type) const {
|
||||
return _codec->canDither(type) || (type == kDitherTypeQT);
|
||||
}
|
||||
|
||||
void DitherCodec::setDither(DitherType type, const byte *palette) {
|
||||
if (_codec->canDither(type)) {
|
||||
_codec->setDither(type, palette);
|
||||
} else {
|
||||
assert(type == kDitherTypeQT);
|
||||
assert(_forcedDitherPalette.empty());
|
||||
|
||||
// Forced dither
|
||||
_forcedDitherPalette.resize(256, false);
|
||||
_forcedDitherPalette.set(palette, 0, 256);
|
||||
_dirtyPalette = true;
|
||||
|
||||
_ditherTable = createQuickTimeDitherTable(_forcedDitherPalette.data(), 256);
|
||||
|
||||
// Prefer RGB554 or RGB555 to avoid extra conversion when dithering
|
||||
if (!_codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0)))
|
||||
_codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void DitherCodec::setCodecAccuracy(CodecAccuracy accuracy) {
|
||||
return _codec->setCodecAccuracy(accuracy);
|
||||
}
|
||||
|
||||
byte *DitherCodec::createQuickTimeDitherTable(const byte *palette, uint colorCount) {
|
||||
byte *buf = new byte[0x10000]();
|
||||
|
||||
Common::List<uint16> checkQueue;
|
||||
|
||||
bool foundBlack = false;
|
||||
bool foundWhite = false;
|
||||
|
||||
const byte *palPtr = palette;
|
||||
|
||||
// See what colors we have, and add them to the queue to check
|
||||
for (uint i = 0; i < colorCount; i++) {
|
||||
byte r = *palPtr++;
|
||||
byte g = *palPtr++;
|
||||
byte b = *palPtr++;
|
||||
uint16 n = (i << 8) | 1;
|
||||
uint16 col = makeQuickTimeDitherColor(r, g, b);
|
||||
|
||||
if (col == 0) {
|
||||
// Special case for close-to-black
|
||||
// The original did more here, but it effectively discarded the value
|
||||
// due to a poor if-check (whole 16-bit value instead of lower 8-bits).
|
||||
WRITE_UINT16(buf, n);
|
||||
foundBlack = true;
|
||||
} else if (col == 0x3FFF) {
|
||||
// Special case for close-to-white
|
||||
// The original did more here, but it effectively discarded the value
|
||||
// due to a poor if-check (whole 16-bit value instead of lower 8-bits).
|
||||
WRITE_UINT16(buf + 0x7FFE, n);
|
||||
foundWhite = true;
|
||||
} else {
|
||||
// Previously unfound color
|
||||
addColorToQueue(col, n, buf, checkQueue);
|
||||
}
|
||||
}
|
||||
|
||||
// More special handling for white
|
||||
if (foundWhite)
|
||||
checkQueue.push_front(0x3FFF);
|
||||
|
||||
// More special handling for black
|
||||
if (foundBlack)
|
||||
checkQueue.push_front(0);
|
||||
|
||||
// Go through the list of colors we have and match up similar colors
|
||||
// to fill in the table as best as we can.
|
||||
while (!checkQueue.empty()) {
|
||||
uint16 col = checkQueue.front();
|
||||
checkQueue.pop_front();
|
||||
uint16 index = READ_UINT16(buf + col * 2);
|
||||
|
||||
uint32 x = col << 4;
|
||||
if ((x & 0xFF) < 0xF0)
|
||||
addColorToQueue((x + 0x10) >> 4, index, buf, checkQueue);
|
||||
if ((x & 0xFF) >= 0x10)
|
||||
addColorToQueue((x - 0x10) >> 4, index, buf, checkQueue);
|
||||
|
||||
uint32 y = col << 7;
|
||||
if ((y & 0xFF00) < 0xF800)
|
||||
addColorToQueue((y + 0x800) >> 7, index, buf, checkQueue);
|
||||
if ((y & 0xFF00) >= 0x800)
|
||||
addColorToQueue((y - 0x800) >> 7, index, buf, checkQueue);
|
||||
|
||||
uint32 z = col << 2;
|
||||
if ((z & 0xFF00) < 0xF800)
|
||||
addColorToQueue((z + 0x800) >> 2, index, buf, checkQueue);
|
||||
if ((z & 0xFF00) >= 0x800)
|
||||
addColorToQueue((z - 0x800) >> 2, index, buf, checkQueue);
|
||||
}
|
||||
|
||||
// Contract the table back to just palette entries
|
||||
for (int i = 0; i < 0x4000; i++)
|
||||
buf[i] = READ_UINT16(buf + i * 2) >> 8;
|
||||
|
||||
// Now go through and distribute the error to three more pixels
|
||||
byte *bufPtr = buf;
|
||||
for (uint realR = 0; realR < 0x100; realR += 8) {
|
||||
for (uint realG = 0; realG < 0x100; realG += 8) {
|
||||
for (uint realB = 0; realB < 0x100; realB += 16) {
|
||||
byte palIndex = *bufPtr;
|
||||
byte r = realR;
|
||||
byte g = realG;
|
||||
byte b = realB;
|
||||
|
||||
byte palR = palette[palIndex * 3] & 0xF8;
|
||||
byte palG = palette[palIndex * 3 + 1] & 0xF8;
|
||||
byte palB = palette[palIndex * 3 + 2] & 0xF0;
|
||||
|
||||
r = adjustColorRange(r, realR, palR);
|
||||
g = adjustColorRange(g, realG, palG);
|
||||
b = adjustColorRange(b, realB, palB);
|
||||
palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
|
||||
bufPtr[0x4000] = palIndex;
|
||||
|
||||
palR = palette[palIndex * 3] & 0xF8;
|
||||
palG = palette[palIndex * 3 + 1] & 0xF8;
|
||||
palB = palette[palIndex * 3 + 2] & 0xF0;
|
||||
|
||||
r = adjustColorRange(r, realR, palR);
|
||||
g = adjustColorRange(g, realG, palG);
|
||||
b = adjustColorRange(b, realB, palB);
|
||||
palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
|
||||
bufPtr[0x8000] = palIndex;
|
||||
|
||||
palR = palette[palIndex * 3] & 0xF8;
|
||||
palG = palette[palIndex * 3 + 1] & 0xF8;
|
||||
palB = palette[palIndex * 3 + 2] & 0xF0;
|
||||
|
||||
r = adjustColorRange(r, realR, palR);
|
||||
g = adjustColorRange(g, realG, palG);
|
||||
b = adjustColorRange(b, realB, palB);
|
||||
palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
|
||||
bufPtr[0xC000] = palIndex;
|
||||
|
||||
bufPtr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
71
image/codecs/dither.h
Normal file
71
image/codecs/dither.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_DITHER_H
|
||||
#define IMAGE_CODECS_DITHER_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
#include "common/types.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
class DitherCodec : public Codec {
|
||||
public:
|
||||
DitherCodec(Codec *codec, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
virtual ~DitherCodec() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override;
|
||||
bool containsPalette() const override;
|
||||
const byte *getPalette() override;
|
||||
bool hasDirtyPalette() const override;
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
void setCodecAccuracy(CodecAccuracy accuracy) override;
|
||||
|
||||
/**
|
||||
* Specify the source palette when dithering from CLUT8 to CLUT8.
|
||||
*/
|
||||
void setPalette(const byte *palette) { _srcPalette = palette; }
|
||||
|
||||
/**
|
||||
* Create a dither table, as used by QuickTime codecs.
|
||||
*/
|
||||
static byte *createQuickTimeDitherTable(const byte *palette, uint colorCount);
|
||||
|
||||
private:
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
Codec *_codec;
|
||||
const byte *_srcPalette;
|
||||
|
||||
Graphics::Surface *_ditherFrame;
|
||||
Graphics::Palette _forcedDitherPalette;
|
||||
byte *_ditherTable;
|
||||
bool _dirtyPalette;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
139
image/codecs/hlz.cpp
Normal file
139
image/codecs/hlz.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/hlz.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
HLZDecoder::HLZDecoder(int width, int height) : Codec(),
|
||||
_width(width), _height(height), _surface(nullptr) {
|
||||
}
|
||||
|
||||
HLZDecoder::~HLZDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *HLZDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
}
|
||||
|
||||
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
byte *dst = (byte *)_surface->getPixels();
|
||||
decodeFrameInPlace(stream, uint32(-1), dst);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat HLZDecoder::getPixelFormat() const {
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
static inline uint getReg(Common::SeekableReadStream &stream, uint32 *size, uint32 *reg,
|
||||
int *regBits) {
|
||||
if (*regBits == 0) {
|
||||
if (*size < 4) {
|
||||
error("Can't feed register: not enough data");
|
||||
}
|
||||
*reg = stream.readUint32LE();
|
||||
*size -= 4;
|
||||
*regBits = 32;
|
||||
}
|
||||
uint ret = (*reg >> 31) & 0x1;
|
||||
*reg <<= 1;
|
||||
(*regBits)--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HLZDecoder::decodeFrameInPlace(Common::SeekableReadStream &stream, uint32 size, byte *dst) {
|
||||
bool eof = false;
|
||||
bool checkSize = (size != (uint32) - 1);
|
||||
byte *orig = dst;
|
||||
uint32 reg = 0;
|
||||
int regBits = 0;
|
||||
#define GETREG() getReg(stream, &size, ®, ®Bits)
|
||||
|
||||
while (!eof) {
|
||||
if (GETREG()) {
|
||||
if (size < 1) {
|
||||
error("Can't read pixel byte");
|
||||
}
|
||||
byte c = stream.readByte();
|
||||
*(dst++) = c;
|
||||
size--;
|
||||
} else {
|
||||
int offset, repeat_count;
|
||||
if (GETREG()) {
|
||||
// Long repeat
|
||||
if (size < 2) {
|
||||
error("Can't read repeat count/offset");
|
||||
}
|
||||
uint16 tmp = stream.readUint16LE();
|
||||
size -= 2;
|
||||
repeat_count = tmp & 0x7;
|
||||
offset = (tmp >> 3) - 0x2000;
|
||||
if (repeat_count == 0) {
|
||||
if (size < 1) {
|
||||
error("Can't read long repeat count");
|
||||
}
|
||||
repeat_count = stream.readByte();
|
||||
size--;
|
||||
if (repeat_count == 0) {
|
||||
eof = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Short repeat
|
||||
repeat_count = GETREG() << 1;
|
||||
repeat_count |= GETREG();
|
||||
if (size < 1) {
|
||||
error("Can't read offset byte");
|
||||
}
|
||||
offset = stream.readByte() - 0x100;
|
||||
size--;
|
||||
}
|
||||
repeat_count += 2;
|
||||
if (dst + offset < orig) {
|
||||
error("Invalid offset %d, dst is %d", offset, (int)(dst - orig));
|
||||
}
|
||||
for (; repeat_count > 0; repeat_count--) {
|
||||
// offset is always < 0
|
||||
*dst = *(dst + offset);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkSize && size != 0) {
|
||||
stream.skip(size);
|
||||
}
|
||||
#undef GETREG
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
51
image/codecs/hlz.h
Normal file
51
image/codecs/hlz.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_HLZ_H
|
||||
#define IMAGE_CODECS_HLZ_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* HLZ image decoder.
|
||||
*
|
||||
* Used by HLZ image format and HNM video format.
|
||||
*/
|
||||
class HLZDecoder : public Codec {
|
||||
public:
|
||||
HLZDecoder(int width, int height);
|
||||
~HLZDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
static void decodeFrameInPlace(Common::SeekableReadStream &stream, uint32 size, byte *dst);
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
int _width, _height;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1399
image/codecs/hnm.cpp
Normal file
1399
image/codecs/hnm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
56
image/codecs/hnm.h
Normal file
56
image/codecs/hnm.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_HNM6_H
|
||||
#define IMAGE_CODECS_HNM6_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* HNM6 image decoder interface.
|
||||
*
|
||||
* Used by HNM6 image and video formats.
|
||||
*/
|
||||
class HNM6Decoder : public Codec {
|
||||
public:
|
||||
uint16 getWidth() const { return _width; }
|
||||
uint16 getHeight() const { return _height; }
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _format; }
|
||||
|
||||
void setWarpMode(bool warpMode) { assert(!warpMode || !_videoMode); _warpMode = warpMode; }
|
||||
protected:
|
||||
HNM6Decoder(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
||||
bool videoMode = false) : Codec(),
|
||||
_width(width), _height(height), _format(format), _videoMode(videoMode), _warpMode(false) { }
|
||||
|
||||
uint16 _width, _height;
|
||||
Graphics::PixelFormat _format;
|
||||
bool _warpMode, _videoMode;
|
||||
};
|
||||
|
||||
HNM6Decoder *createHNM6Decoder(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
||||
uint32 bufferSize, bool videoMode = false);
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
89
image/codecs/indeo/get_bits.h
Normal file
89
image/codecs/indeo/get_bits.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_GET_BITS_H
|
||||
#define IMAGE_CODECS_INDEO_GET_BITS_H
|
||||
|
||||
#include "common/bitstream.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* Intel Indeo Bitstream reader
|
||||
*/
|
||||
class GetBits : public Common::BitStreamMemory8LSB {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
GetBits(const byte *dataPtr, uint32 dataSize) : Common::BitStreamMemory8LSB(new Common::BitStreamMemoryStream(dataPtr, dataSize), DisposeAfterUse::YES) {}
|
||||
|
||||
/**
|
||||
* The number of bits left
|
||||
*/
|
||||
int getBitsLeft() const { return size() - pos(); }
|
||||
|
||||
/**
|
||||
* Parse a VLC code.
|
||||
* @param bits is the number of bits which will be read at once, must be
|
||||
* identical to nbBits in init_vlc()
|
||||
* @param maxDepth is the number of times bits bits must be read to completely
|
||||
* read the longest vlc code
|
||||
* = (max_vlc_length + bits - 1) / bits
|
||||
*/
|
||||
template <int maxDepth, int bits>
|
||||
int getVLC2(int16 (*table)[2]) {
|
||||
int code;
|
||||
int n, nbBits;
|
||||
unsigned int index;
|
||||
|
||||
index = peekBits<bits>();
|
||||
code = table[index][0];
|
||||
n = table[index][1];
|
||||
|
||||
if (maxDepth > 1 && n < 0) {
|
||||
skip(bits);
|
||||
nbBits = -n;
|
||||
|
||||
index = peekBits(nbBits) + code;
|
||||
code = table[index][0];
|
||||
n = table[index][1];
|
||||
|
||||
if (maxDepth > 2 && n < 0) {
|
||||
skip(nbBits);
|
||||
nbBits = -n;
|
||||
|
||||
index = peekBits(nbBits) + code;
|
||||
code = table[index][0];
|
||||
n = table[index][1];
|
||||
}
|
||||
}
|
||||
|
||||
skip(n);
|
||||
return code;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1719
image/codecs/indeo/indeo.cpp
Normal file
1719
image/codecs/indeo/indeo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
604
image/codecs/indeo/indeo.h
Normal file
604
image/codecs/indeo/indeo.h
Normal file
@@ -0,0 +1,604 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
/* Common structures, macros, and base class shared by both Indeo4 and
|
||||
* Indeo5 decoders, derived from ffmpeg. We don't currently support Indeo5
|
||||
* decoding, but just in case we eventually need it, this is kept as a separate
|
||||
* file like it is in ffmpeg.
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_INDEO_H
|
||||
#define IMAGE_CODECS_INDEO_INDEO_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
#include "image/codecs/indeo/vlc.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* Indeo 4 frame types.
|
||||
*/
|
||||
enum {
|
||||
IVI4_FRAMETYPE_INTRA = 0,
|
||||
IVI4_FRAMETYPE_INTRA1 = 1, ///< intra frame with slightly different bitstream coding
|
||||
IVI4_FRAMETYPE_INTER = 2, ///< non-droppable P-frame
|
||||
IVI4_FRAMETYPE_BIDIR = 3, ///< bidirectional frame
|
||||
IVI4_FRAMETYPE_INTER_NOREF = 4, ///< droppable P-frame
|
||||
IVI4_FRAMETYPE_NULL_FIRST = 5, ///< empty frame with no data
|
||||
IVI4_FRAMETYPE_NULL_LAST = 6 ///< empty frame with no data
|
||||
};
|
||||
|
||||
enum {
|
||||
IVI_MB_HUFF = 0, /// Huffman table is used for coding macroblocks
|
||||
IVI_BLK_HUFF = 1 /// Huffman table is used for coding blocks
|
||||
};
|
||||
|
||||
/**
|
||||
* Declare inverse transform function types
|
||||
*/
|
||||
typedef void (InvTransformPtr)(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags);
|
||||
typedef void (DCTransformPtr)(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
typedef void (*IviMCFunc)(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
typedef void (*IviMCAvgFunc)(int16 *buf, const int16 *refBuf1, const int16 *refBuf2,
|
||||
uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
///< max number of bits of the ivi's huffman codes
|
||||
#define IVI_VLC_BITS 13
|
||||
#define IVI5_IS_PROTECTED 0x20
|
||||
|
||||
/**
|
||||
* convert unsigned values into signed ones (the sign is in the LSB)
|
||||
*/
|
||||
#define IVI_TOSIGNED(val) (-(((val) >> 1) ^ -((val) & 1)))
|
||||
|
||||
/**
|
||||
* calculate number of macroblocks in a tile
|
||||
*/
|
||||
#define IVI_MBs_PER_TILE(tileWidth, tileHeight, mbSize) \
|
||||
((((tileWidth) + (mbSize) - 1) / (mbSize)) * (((tileHeight) + (mbSize) - 1) / (mbSize)))
|
||||
|
||||
/**
|
||||
* huffman codebook descriptor
|
||||
*/
|
||||
struct IVIHuffDesc {
|
||||
int32 _numRows;
|
||||
uint8 _xBits[16];
|
||||
|
||||
/**
|
||||
* Generate a huffman codebook from the given descriptor
|
||||
* and convert it into the FFmpeg VLC table.
|
||||
*
|
||||
* @param[out] vlc Where to place the generated VLC table
|
||||
* @param[in] flag Flag: true - for static or false for dynamic tables
|
||||
* @returns result code: 0 - OK, -1 = error (invalid codebook descriptor)
|
||||
*/
|
||||
int createHuffFromDesc(VLC *vlc, bool flag) const;
|
||||
|
||||
/**
|
||||
* Compare two huffman codebook descriptors.
|
||||
*
|
||||
* @param[in] desc2 Ptr to the 2nd descriptor to compare
|
||||
* @returns comparison result: 0 - equal, 1 - not equal
|
||||
*/
|
||||
bool huffDescCompare(const IVIHuffDesc *desc2) const;
|
||||
|
||||
/**
|
||||
* Copy huffman codebook descriptors.
|
||||
*
|
||||
* @param[in] src ptr to the source descriptor
|
||||
*/
|
||||
void huffDescCopy(const IVIHuffDesc *src);
|
||||
};
|
||||
|
||||
struct IVI45DecContext;
|
||||
|
||||
/**
|
||||
* Macroblock/block huffman table descriptor
|
||||
*/
|
||||
struct IVIHuffTab {
|
||||
public:
|
||||
int32 _tabSel; /// index of one of the predefined tables
|
||||
/// or "7" for custom one
|
||||
VLC * _tab; /// pointer to the table associated with tab_sel
|
||||
|
||||
/// the following are used only when tab_sel == 7
|
||||
IVIHuffDesc _custDesc; /// custom Huffman codebook descriptor
|
||||
VLC _custTab; /// vlc table for custom codebook
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
IVIHuffTab();
|
||||
|
||||
/**
|
||||
* Decode a huffman codebook descriptor from the bitstream
|
||||
* and select specified huffman table.
|
||||
*
|
||||
* @param[in] ctx Decoder context
|
||||
* @param[in] descCoded Flag signalling if table descriptor was coded
|
||||
* @param[in] whichTab Codebook purpose (IVI_MB_HUFF or IVI_BLK_HUFF)
|
||||
* @returns Zero on success, negative value otherwise
|
||||
*/
|
||||
int decodeHuffDesc(IVI45DecContext *ctx, int descCoded, int whichTab);
|
||||
};
|
||||
|
||||
/**
|
||||
* run-value (RLE) table descriptor
|
||||
*/
|
||||
struct RVMapDesc {
|
||||
uint8 _eobSym; ///< end of block symbol
|
||||
uint8 _escSym; ///< escape symbol
|
||||
uint8 _runtab[256];
|
||||
int8 _valtab[256];
|
||||
};
|
||||
|
||||
/**
|
||||
* information for Indeo macroblock (16x16, 8x8 or 4x4)
|
||||
*/
|
||||
struct IVIMbInfo {
|
||||
int16 _xPos;
|
||||
int16 _yPos;
|
||||
uint32 _bufOffs; ///< address in the output buffer for this mb
|
||||
uint8 _type; ///< macroblock type: 0 - INTRA, 1 - INTER
|
||||
uint8 _cbp; ///< coded block pattern
|
||||
int8 _qDelta; ///< quant delta
|
||||
int8 _mvX; ///< motion vector (x component)
|
||||
int8 _mvY; ///< motion vector (y component)
|
||||
int8 _bMvX; ///< second motion vector (x component)
|
||||
int8 _bMvY; ///< second motion vector (y component)
|
||||
|
||||
IVIMbInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
* information for Indeo tile
|
||||
*/
|
||||
struct IVITile {
|
||||
int _xPos;
|
||||
int _yPos;
|
||||
int _width;
|
||||
int _height;
|
||||
int _mbSize;
|
||||
bool _isEmpty;
|
||||
int _dataSize; ///< size of the data in bytes
|
||||
int _numMBs; ///< number of macroblocks in this tile
|
||||
IVIMbInfo * _mbs; ///< array of macroblock descriptors
|
||||
IVIMbInfo * _refMbs; ///< ptr to the macroblock descriptors of the reference tile
|
||||
|
||||
IVITile();
|
||||
};
|
||||
|
||||
/**
|
||||
* information for Indeo wavelet band
|
||||
*/
|
||||
struct IVIBandDesc {
|
||||
int _plane; ///< plane number this band belongs to
|
||||
int _bandNum; ///< band number
|
||||
int _width;
|
||||
int _height;
|
||||
int _aHeight; ///< aligned band height
|
||||
const uint8 * _dataPtr; ///< ptr to the first byte of the band data
|
||||
int _dataSize; ///< size of the band data
|
||||
int16 * _buf; ///< pointer to the output buffer for this band
|
||||
int16 * _refBuf; ///< pointer to the reference frame buffer (for motion compensation)
|
||||
int16 * _bRefBuf; ///< pointer to the second reference frame buffer (for motion compensation)
|
||||
int16 * _bufs[4]; ///< array of pointers to the band buffers
|
||||
int _pitch; ///< _pitch associated with the buffers above
|
||||
bool _isEmpty;
|
||||
int _mbSize; ///< macroblock size
|
||||
int _blkSize; ///< block size
|
||||
uint8 _isHalfpel; ///< precision of the motion compensation: 0 - fullpel, 1 - halfpel
|
||||
bool _inheritMv; ///< tells if motion vector is inherited from reference macroblock
|
||||
bool _inheritQDelta; ///< tells if quantiser delta is inherited from reference macroblock
|
||||
bool _qdeltaPresent; ///< tells if Qdelta signal is present in the bitstream (Indeo5 only)
|
||||
int _quantMat; ///< dequant matrix index
|
||||
int _globQuant; ///< quant base for this band
|
||||
const uint8 * _scan; ///< ptr to the scan pattern
|
||||
int _scanSize; ///< size of the scantable
|
||||
|
||||
IVIHuffTab _blkVlc; ///< vlc table for decoding block data
|
||||
|
||||
int _numCorr; ///< number of correction entries
|
||||
uint8 _corr[61 * 2]; ///< rvmap correction pairs
|
||||
int _rvmapSel; ///< rvmap table selector
|
||||
RVMapDesc * _rvMap; ///< ptr to the RLE table for this band
|
||||
int _numTiles; ///< number of tiles in this band
|
||||
IVITile * _tiles; ///< array of tile descriptors
|
||||
InvTransformPtr *_invTransform;
|
||||
int _transformSize;
|
||||
DCTransformPtr *_dcTransform;
|
||||
bool _is2dTrans;
|
||||
int32 _checksum; ///< for debug purposes
|
||||
int _checksumPresent;
|
||||
int _bufSize; ///< band buffer size in bytes
|
||||
const uint16 * _intraBase; ///< quantization matrix for intra blocks
|
||||
const uint16 * _interBase; ///< quantization matrix for inter blocks
|
||||
const uint8 * _intraScale; ///< quantization coefficient for intra blocks
|
||||
const uint8 * _interScale; ///< quantization coefficient for inter blocks
|
||||
|
||||
IVIBandDesc();
|
||||
|
||||
int initTiles(IVITile *refTile, int p, int b, int tHeight, int tWidth);
|
||||
};
|
||||
|
||||
struct IVIPicConfig {
|
||||
uint16 _picWidth;
|
||||
uint16 _picHeight;
|
||||
uint16 _chromaWidth;
|
||||
uint16 _chromaHeight;
|
||||
uint16 _tileWidth;
|
||||
uint16 _tileHeight;
|
||||
uint8 _lumaBands;
|
||||
uint8 _chromaBands;
|
||||
|
||||
IVIPicConfig();
|
||||
|
||||
/**
|
||||
* Compare some properties of two pictures
|
||||
*/
|
||||
bool ivi_pic_config_cmp(const IVIPicConfig &cfg2);
|
||||
};
|
||||
|
||||
/**
|
||||
* color plane (luma or chroma) information
|
||||
*/
|
||||
struct IVIPlaneDesc {
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
uint8 _numBands; ///< number of bands this plane subdivided into
|
||||
IVIBandDesc * _bands; ///< array of band descriptors
|
||||
|
||||
IVIPlaneDesc();
|
||||
|
||||
static int initPlanes(IVIPlaneDesc *planes, const IVIPicConfig *cfg, bool isIndeo4);
|
||||
|
||||
static int initTiles(IVIPlaneDesc *planes, int tileWidth, int tileHeight);
|
||||
|
||||
/**
|
||||
* Free planes, bands and macroblocks buffers.
|
||||
*
|
||||
* @param[in] planes pointer to the array of the plane descriptors
|
||||
*/
|
||||
static void freeBuffers(IVIPlaneDesc *planes);
|
||||
|
||||
/**
|
||||
* Check if the given dimension of an image is valid, meaning that all
|
||||
* bytes of the image can be addressed with a signed int.
|
||||
*
|
||||
* @param w the width of the picture
|
||||
* @param h the height of the picture
|
||||
* @param log_offset the offset to sum to the log level for logging with log_ctx
|
||||
* @returns >= 0 if valid, a negative error code otherwise
|
||||
*/
|
||||
static int checkImageSize(unsigned int w, unsigned int h, int logOffset);
|
||||
};
|
||||
|
||||
struct AVFrame {
|
||||
/**
|
||||
* Dimensions
|
||||
*/
|
||||
int _width, _height;
|
||||
|
||||
#define AV_NUM_DATA_POINTERS 3
|
||||
/**
|
||||
* pointer to the picture/channel planes.
|
||||
* This might be different from the first allocated byte
|
||||
*
|
||||
* Some decoders access areas outside 0,0 - width,height, please
|
||||
* see avcodec_align_dimensions2(). Some filters and swscale can read
|
||||
* up to 16 bytes beyond the planes, if these filters are to be used,
|
||||
* then 16 extra bytes must be allocated.
|
||||
*
|
||||
* NOTE: Except for hwaccel formats, pointers not needed by the format
|
||||
* MUST be set to NULL.
|
||||
*/
|
||||
uint8 *_data[AV_NUM_DATA_POINTERS];
|
||||
|
||||
/**
|
||||
* For video, size in bytes of each picture line.
|
||||
* For audio, size in bytes of each plane.
|
||||
*
|
||||
* For audio, only linesize[0] may be set. For planar audio, each channel
|
||||
* plane must be the same size.
|
||||
*
|
||||
* For video the linesizes should be multiples of the CPUs alignment
|
||||
* preference, this is 16 or 32 for modern desktop CPUs.
|
||||
* Some code requires such alignment other code can be slower without
|
||||
* correct alignment, for yet other it makes no difference.
|
||||
*
|
||||
* @note The linesize may be larger than the size of usable data -- there
|
||||
* may be extra padding present for performance reasons.
|
||||
*/
|
||||
int _linesize[AV_NUM_DATA_POINTERS];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
AVFrame();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~AVFrame() { freeFrame(); }
|
||||
|
||||
/**
|
||||
* Sets the frame dimensions
|
||||
*/
|
||||
int setDimensions(uint16 width, uint16 height);
|
||||
|
||||
/**
|
||||
* Get a buffer for a frame
|
||||
*/
|
||||
int getBuffer(int flags);
|
||||
|
||||
/**
|
||||
* Frees any data loaded for the frame
|
||||
*/
|
||||
void freeFrame();
|
||||
};
|
||||
|
||||
struct IVI45DecContext {
|
||||
friend struct IVIHuffTab;
|
||||
private:
|
||||
VLC_TYPE _tableData[8192 * 16][2];
|
||||
VLC _iviMbVlcTabs[8]; ///< static macroblock Huffman tables
|
||||
VLC _iviBlkVlcTabs[8]; ///< static block Huffman tables
|
||||
public:
|
||||
GetBits * _gb;
|
||||
RVMapDesc _rvmapTabs[9]; ///< local corrected copy of the static rvmap tables
|
||||
|
||||
uint32 _frameNum;
|
||||
int _frameType;
|
||||
int _prevFrameType; ///< frame type of the previous frame
|
||||
uint32 _dataSize; ///< size of the frame data in bytes from picture header
|
||||
int _isScalable;
|
||||
const uint8 * _frameData; ///< input frame data pointer
|
||||
int _interScal; ///< signals a sequence of scalable inter frames
|
||||
uint32 _frameSize; ///< frame size in bytes
|
||||
uint32 _picHdrSize; ///< picture header size in bytes
|
||||
uint8 _frameFlags;
|
||||
uint16 _checksum; ///< frame _checksum
|
||||
|
||||
IVIPicConfig _picConf;
|
||||
IVIPlaneDesc _planes[3]; ///< color planes
|
||||
|
||||
int _bufSwitch; ///< used to switch between three buffers
|
||||
int _dstBuf; ///< buffer index for the currently decoded frame
|
||||
int _refBuf; ///< inter frame reference buffer index
|
||||
int _ref2Buf; ///< temporal storage for switching buffers
|
||||
int _bRefBuf; ///< second reference frame buffer index
|
||||
|
||||
IVIHuffTab _mbVlc; ///< current macroblock table descriptor
|
||||
IVIHuffTab _blkVlc; ///< current block table descriptor
|
||||
IVIHuffTab _transVlc; ///< current transparency table descriptor
|
||||
|
||||
uint8 _rvmapSel;
|
||||
bool _inImf;
|
||||
bool _inQ; ///< flag for explicitly stored quantiser delta
|
||||
uint8 _picGlobQuant;
|
||||
uint8 _unknown1;
|
||||
|
||||
uint16 _gopHdrSize;
|
||||
uint8 _gopFlags;
|
||||
uint32 _lockWord;
|
||||
|
||||
bool _hasBFrames;
|
||||
bool _hasTransp; ///< transparency mode enabled
|
||||
bool _usesTiling;
|
||||
bool _usesHaar;
|
||||
bool _usesFullpel;
|
||||
|
||||
bool _gopInvalid;
|
||||
int _bufInvalid[4];
|
||||
|
||||
bool _isIndeo4;
|
||||
uint32 _transKeyColor;
|
||||
|
||||
AVFrame * _pFrame;
|
||||
bool _gotPFrame;
|
||||
|
||||
IVI45DecContext();
|
||||
private:
|
||||
/**
|
||||
* Initial Run-value (RLE) tables.
|
||||
*/
|
||||
static const RVMapDesc _ff_ivi_rvmap_tabs[9];
|
||||
};
|
||||
|
||||
class IndeoDecoderBase : public Codec {
|
||||
private:
|
||||
/**
|
||||
* Decode an Indeo 4 or 5 band.
|
||||
*
|
||||
* @param[in,out] band ptr to the band descriptor
|
||||
* @returns result code: 0 = OK, -1 = error
|
||||
*/
|
||||
int decode_band(IVIBandDesc *band);
|
||||
|
||||
/**
|
||||
* Haar wavelet recomposition filter for Indeo 4
|
||||
*
|
||||
* @param[in] plane Pointer to the descriptor of the plane being processed
|
||||
* @param[out] dst pointer to the destination buffer
|
||||
* @param[in] dstPitch Pitch of the destination buffer
|
||||
*/
|
||||
void recomposeHaar(const IVIPlaneDesc *plane, uint8 *dst, const int dstPitch);
|
||||
|
||||
/**
|
||||
* 5/3 wavelet recomposition filter for Indeo5
|
||||
*
|
||||
* @param[in] plane Pointer to the descriptor of the plane being processed
|
||||
* @param[out] dst Pointer to the destination buffer
|
||||
* @param[in] dstPitch Pitch of the destination buffer
|
||||
*/
|
||||
void recompose53(const IVIPlaneDesc *plane, uint8 *dst, const int dstPitch);
|
||||
|
||||
/*
|
||||
* Convert and output the current plane.
|
||||
* This conversion is done by adding back the bias value of 128
|
||||
* (subtracted in the encoder) and clipping the result.
|
||||
*
|
||||
* @param[in] plane Pointer to the descriptor of the plane being processed
|
||||
* @param[out] dst Pointer to the buffer receiving converted pixels
|
||||
* @param[in] dstPitch Pitch for moving to the next y line
|
||||
*/
|
||||
void outputPlane(IVIPlaneDesc *plane, uint8 *dst, int dstPitch);
|
||||
|
||||
/**
|
||||
* Handle empty tiles by performing data copying and motion
|
||||
* compensation respectively.
|
||||
*
|
||||
* @param[in] band Pointer to the band descriptor
|
||||
* @param[in] tile Pointer to the tile descriptor
|
||||
* @param[in] mvScale Scaling factor for motion vectors
|
||||
*/
|
||||
int processEmptyTile(IVIBandDesc *band, IVITile *tile, int32 mvScale);
|
||||
|
||||
/*
|
||||
* Decode size of the tile data.
|
||||
* The size is stored as a variable-length field having the following format:
|
||||
* if (tile_data_size < 255) than this field is only one byte long
|
||||
* if (tile_data_size >= 255) than this field four is byte long: 0xFF X1 X2 X3
|
||||
* where X1-X3 is size of the tile data
|
||||
*
|
||||
* @param[in,out] gb the GetBit context
|
||||
* @returns Size of the tile data in bytes
|
||||
*/
|
||||
int decodeTileDataSize(GetBits *gb);
|
||||
|
||||
/*
|
||||
* Decode block data:
|
||||
* extract huffman-coded transform coefficients from the bitstream,
|
||||
* dequantize them, apply inverse transform and motion compensation
|
||||
* in order to reconstruct the picture.
|
||||
*
|
||||
* @param[in,out] gb The GetBit context
|
||||
* @param[in] band Pointer to the band descriptor
|
||||
* @param[in] tile Pointer to the tile descriptor
|
||||
* @returns Result code: 0 - OK, -1 = error (corrupted blocks data)
|
||||
*/
|
||||
int decodeBlocks(GetBits *gb, IVIBandDesc *band, IVITile *tile);
|
||||
|
||||
int iviMc(IVIBandDesc *band, IviMCFunc mc, IviMCAvgFunc mcAvg,
|
||||
int offs, int mvX, int mvY, int mvX2, int mvY2, int mcType, int mcType2);
|
||||
|
||||
int decodeCodedBlocks(GetBits *gb, IVIBandDesc *band,
|
||||
IviMCFunc mc, IviMCAvgFunc mcAvg, int mvX, int mvY,
|
||||
int mvX2, int mvY2, int32 *prevDc, int isIntra,
|
||||
int mcType, int mcType2, uint32 quant, int offs);
|
||||
|
||||
int iviDcTransform(IVIBandDesc *band, int32 *prevDc, int bufOffs,
|
||||
int blkSize);
|
||||
protected:
|
||||
IVI45DecContext _ctx;
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
uint _bitsPerPixel;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
/**
|
||||
* Scan patterns shared between indeo4 and indeo5
|
||||
*/
|
||||
static const uint8 _ffIviVerticalScan8x8[64];
|
||||
static const uint8 _ffIviHorizontalScan8x8[64];
|
||||
static const uint8 _ffIviDirectScan4x4[16];
|
||||
protected:
|
||||
/**
|
||||
* Returns the pixel format for the decoder's surface
|
||||
*/
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
|
||||
/**
|
||||
* Select the preferred format to use, for codecs where this is faster than converting
|
||||
* the image afterwards. Returns true if supported, and false otherwise.
|
||||
*/
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the Indeo picture header.
|
||||
* @returns 0 = Ok, negative number = error
|
||||
*/
|
||||
virtual int decodePictureHeader() = 0;
|
||||
|
||||
/**
|
||||
* Rearrange decoding and reference buffers.
|
||||
*/
|
||||
virtual void switchBuffers() = 0;
|
||||
|
||||
virtual bool isNonNullFrame() const = 0;
|
||||
|
||||
/**
|
||||
* Decode Indeo band header.
|
||||
*
|
||||
* @param[in,out] band Pointer to the band descriptor
|
||||
* @returns Result code: 0 = OK, negative number = error
|
||||
*/
|
||||
virtual int decodeBandHeader(IVIBandDesc *band) = 0;
|
||||
|
||||
/**
|
||||
* Decode information (block type, _cbp, quant delta, motion vector)
|
||||
* for all macroblocks in the current tile.
|
||||
*
|
||||
* @param[in,out] band Pointer to the band descriptor
|
||||
* @param[in,out] tile Pointer to the tile descriptor
|
||||
* @returns Result code: 0 = OK, negative number = error
|
||||
*/
|
||||
virtual int decodeMbInfo(IVIBandDesc *band, IVITile *tile) = 0;
|
||||
|
||||
/**
|
||||
* Decodes optional transparency data within Indeo frames
|
||||
*/
|
||||
virtual int decodeTransparency() { return -1; }
|
||||
|
||||
/**
|
||||
* Decodes the Indeo frame from the bit reader already
|
||||
* loaded into the context
|
||||
*/
|
||||
int decodeIndeoFrame();
|
||||
|
||||
/**
|
||||
* scale motion vector
|
||||
*/
|
||||
int scaleMV(int mv, int mvScale);
|
||||
public:
|
||||
IndeoDecoderBase(uint16 width, uint16 height, uint bitsPerPixel);
|
||||
~IndeoDecoderBase() override;
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
607
image/codecs/indeo/indeo_dsp.cpp
Normal file
607
image/codecs/indeo/indeo_dsp.cpp
Normal file
@@ -0,0 +1,607 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "image/codecs/indeo/indeo_dsp.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* butterfly operation for the inverse Haar transform
|
||||
*/
|
||||
#define IVI_HAAR_BFLY(s1, s2, o1, o2, t) \
|
||||
t = ((s1) - (s2)) >> 1;\
|
||||
o1 = ((s1) + (s2)) >> 1;\
|
||||
o2 = (t);\
|
||||
|
||||
/**
|
||||
* inverse 8-point Haar transform
|
||||
*/
|
||||
#define INV_HAAR8(s1, s5, s3, s7, s2, s4, s6, s8,\
|
||||
d1, d2, d3, d4, d5, d6, d7, d8,\
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8) {\
|
||||
t1 = (s1) << 1; t5 = (s5) << 1;\
|
||||
IVI_HAAR_BFLY(t1, t5, t1, t5, t0); IVI_HAAR_BFLY(t1, s3, t1, t3, t0);\
|
||||
IVI_HAAR_BFLY(t5, s7, t5, t7, t0); IVI_HAAR_BFLY(t1, s2, t1, t2, t0);\
|
||||
IVI_HAAR_BFLY(t3, s4, t3, t4, t0); IVI_HAAR_BFLY(t5, s6, t5, t6, t0);\
|
||||
IVI_HAAR_BFLY(t7, s8, t7, t8, t0);\
|
||||
d1 = COMPENSATE(t1);\
|
||||
d2 = COMPENSATE(t2);\
|
||||
d3 = COMPENSATE(t3);\
|
||||
d4 = COMPENSATE(t4);\
|
||||
d5 = COMPENSATE(t5);\
|
||||
d6 = COMPENSATE(t6);\
|
||||
d7 = COMPENSATE(t7);\
|
||||
d8 = COMPENSATE(t8); }
|
||||
|
||||
/**
|
||||
* inverse 4-point Haar transform
|
||||
*/
|
||||
#define INV_HAAR4(s1, s3, s5, s7, d1, d2, d3, d4, t0, t1, t2, t3, t4) {\
|
||||
IVI_HAAR_BFLY(s1, s3, t0, t1, t4);\
|
||||
IVI_HAAR_BFLY(t0, s5, t2, t3, t4);\
|
||||
d1 = COMPENSATE(t2);\
|
||||
d2 = COMPENSATE(t3);\
|
||||
IVI_HAAR_BFLY(t1, s7, t2, t3, t4);\
|
||||
d3 = COMPENSATE(t2);\
|
||||
d4 = COMPENSATE(t3); }
|
||||
|
||||
void IndeoDSP::ffIviInverseHaar8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int32 tmp[64];
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
// apply the InvHaar8 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
// pre-scaling
|
||||
int shift = !(i & 4);
|
||||
int sp1 = src[ 0] << shift;
|
||||
int sp2 = src[ 8] << shift;
|
||||
int sp3 = src[16] << shift;
|
||||
int sp4 = src[24] << shift;
|
||||
INV_HAAR8( sp1, sp2, sp3, sp4,
|
||||
src[32], src[40], src[48], src[56],
|
||||
dst[ 0], dst[ 8], dst[16], dst[24],
|
||||
dst[32], dst[40], dst[48], dst[56],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
dst[ 0] = dst[ 8] = dst[16] = dst[24] =
|
||||
dst[32] = dst[40] = dst[48] = dst[56] = 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
// apply the InvHaar8 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3] &&
|
||||
!src[4] && !src[5] && !src[6] && !src[7]) {
|
||||
memset(out, 0, 8 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR8(src[0], src[1], src[2], src[3],
|
||||
src[4], src[5], src[6], src[7],
|
||||
out[0], out[1], out[2], out[3],
|
||||
out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
src += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
// apply the InvHaar8 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ( !in[0] && !in[1] && !in[2] && !in[3]
|
||||
&& !in[4] && !in[5] && !in[6] && !in[7]) {
|
||||
memset(out, 0, 8 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR8(in[0], in[1], in[2], in[3],
|
||||
in[4], in[5], in[6], in[7],
|
||||
out[0], out[1], out[2], out[3],
|
||||
out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
in += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
// apply the InvHaar8 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
INV_HAAR8(in[ 0], in[ 8], in[16], in[24],
|
||||
in[32], in[40], in[48], in[56],
|
||||
out[0 * pitch], out[1 * pitch],
|
||||
out[2 * pitch], out[3 * pitch],
|
||||
out[4 * pitch], out[5 * pitch],
|
||||
out[6 * pitch], out[7 * pitch],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
out[0 * pitch] = out[1 * pitch] =
|
||||
out[2 * pitch] = out[3 * pitch] =
|
||||
out[4 * pitch] = out[5 * pitch] =
|
||||
out[6 * pitch] = out[7 * pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviInverseHaar4x4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int32 tmp[16];
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
// apply the InvHaar4 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
// pre-scaling
|
||||
int shift = !(i & 2);
|
||||
int sp1 = src[0] << shift;
|
||||
int sp2 = src[4] << shift;
|
||||
INV_HAAR4( sp1, sp2, src[8], src[12],
|
||||
dst[0], dst[4], dst[8], dst[12],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
dst[0] = dst[4] = dst[8] = dst[12] = 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
// apply the InvHaar8 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3]) {
|
||||
memset(out, 0, 4 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR4(src[0], src[1], src[2], src[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
src += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
// apply the InvHaar4 to all rows
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!in[0] && !in[1] && !in[2] && !in[3]) {
|
||||
memset(out, 0, 4 * sizeof(out[0]));
|
||||
} else {
|
||||
INV_HAAR4(in[0], in[1], in[2], in[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
in += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
// apply the InvHaar8 to all columns
|
||||
#define COMPENSATE(x) (x)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
INV_HAAR4(in[0], in[4], in[8], in[12],
|
||||
out[0 * pitch], out[1 * pitch],
|
||||
out[2 * pitch], out[3 * pitch],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
out[0 * pitch] = out[1 * pitch] =
|
||||
out[2 * pitch] = out[3 * pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcHaar2d(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize) {
|
||||
int16 dcCoeff = (*in + 0) >> 3;
|
||||
|
||||
for (int y = 0; y < blkSize; out += pitch, y++) {
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = dcCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
//* butterfly operation for the inverse slant transform
|
||||
#define IVI_SLANT_BFLY(s1, s2, o1, o2, t) \
|
||||
t = (s1) - (s2);\
|
||||
o1 = (s1) + (s2);\
|
||||
o2 = (t);\
|
||||
|
||||
//* This is a reflection a,b = 1/2, 5/4 for the inverse slant transform
|
||||
#define IVI_IREFLECT(s1, s2, o1, o2, t) \
|
||||
t = (((s1) + (s2)*2 + 2) >> 2) + (s1);\
|
||||
o2 = (((s1)*2 - (s2) + 2) >> 2) - (s2);\
|
||||
o1 = (t);\
|
||||
|
||||
//* This is a reflection a,b = 1/2, 7/8 for the inverse slant transform
|
||||
#define IVI_SLANT_PART4(s1, s2, o1, o2, t) \
|
||||
t = (s2) + (((s1)*4 - (s2) + 4) >> 3);\
|
||||
o2 = (s1) + ((-(s1) - (s2)*4 + 4) >> 3);\
|
||||
o1 = (t);\
|
||||
|
||||
//* inverse slant8 transform
|
||||
#define IVI_INV_SLANT8(s1, s4, s8, s5, s2, s6, s3, s7,\
|
||||
d1, d2, d3, d4, d5, d6, d7, d8,\
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8) {\
|
||||
IVI_SLANT_PART4(s4, s5, t4, t5, t0);\
|
||||
\
|
||||
IVI_SLANT_BFLY(s1, t5, t1, t5, t0); IVI_SLANT_BFLY(s2, s6, t2, t6, t0);\
|
||||
IVI_SLANT_BFLY(s7, s3, t7, t3, t0); IVI_SLANT_BFLY(t4, s8, t4, t8, t0);\
|
||||
\
|
||||
IVI_SLANT_BFLY(t1, t2, t1, t2, t0); IVI_IREFLECT (t4, t3, t4, t3, t0);\
|
||||
IVI_SLANT_BFLY(t5, t6, t5, t6, t0); IVI_IREFLECT (t8, t7, t8, t7, t0);\
|
||||
IVI_SLANT_BFLY(t1, t4, t1, t4, t0); IVI_SLANT_BFLY(t2, t3, t2, t3, t0);\
|
||||
IVI_SLANT_BFLY(t5, t8, t5, t8, t0); IVI_SLANT_BFLY(t6, t7, t6, t7, t0);\
|
||||
d1 = COMPENSATE(t1);\
|
||||
d2 = COMPENSATE(t2);\
|
||||
d3 = COMPENSATE(t3);\
|
||||
d4 = COMPENSATE(t4);\
|
||||
d5 = COMPENSATE(t5);\
|
||||
d6 = COMPENSATE(t6);\
|
||||
d7 = COMPENSATE(t7);\
|
||||
d8 = COMPENSATE(t8);}
|
||||
|
||||
//* inverse slant4 transform
|
||||
#define IVI_INV_SLANT4(s1, s4, s2, s3, d1, d2, d3, d4, t0, t1, t2, t3, t4) {\
|
||||
IVI_SLANT_BFLY(s1, s2, t1, t2, t0); IVI_IREFLECT (s4, s3, t4, t3, t0);\
|
||||
\
|
||||
IVI_SLANT_BFLY(t1, t4, t1, t4, t0); IVI_SLANT_BFLY(t2, t3, t2, t3, t0);\
|
||||
d1 = COMPENSATE(t1);\
|
||||
d2 = COMPENSATE(t2);\
|
||||
d3 = COMPENSATE(t3);\
|
||||
d4 = COMPENSATE(t4);}
|
||||
|
||||
void IndeoDSP::ffIviInverseSlant8x8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) {
|
||||
int32 tmp[64];
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT8(src[0], src[8], src[16], src[24], src[32], src[40], src[48], src[56],
|
||||
dst[0], dst[8], dst[16], dst[24], dst[32], dst[40], dst[48], dst[56],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
dst[0] = dst[8] = dst[16] = dst[24] = dst[32] = dst[40] = dst[48] = dst[56] = 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3] && !src[4] && !src[5] && !src[6] && !src[7]) {
|
||||
memset(out, 0, 8*sizeof(out[0]));
|
||||
} else {
|
||||
IVI_INV_SLANT8(src[0], src[1], src[2], src[3], src[4], src[5], src[6], src[7],
|
||||
out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
src += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviInverseSlant4x4(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) {
|
||||
int32 tmp[16];
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
#define COMPENSATE(x) (x)
|
||||
const int32 *src = in;
|
||||
int32 *dst = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT4(src[0], src[4], src[8], src[12],
|
||||
dst[0], dst[4], dst[8], dst[12],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
dst[0] = dst[4] = dst[8] = dst[12] = 0;
|
||||
}
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
src = tmp;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!src[0] && !src[1] && !src[2] && !src[3]) {
|
||||
out[0] = out[1] = out[2] = out[3] = 0;
|
||||
} else {
|
||||
IVI_INV_SLANT4(src[0], src[1], src[2], src[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
src += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcSlant2d(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize) {
|
||||
int16 dcCoeff = (*in + 1) >> 1;
|
||||
|
||||
for (int y = 0; y < blkSize; out += pitch, y++) {
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = dcCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowSlant8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!in[0] && !in[1] && !in[2] && !in[3] && !in[4] && !in[5] && !in[6] && !in[7]) {
|
||||
memset(out, 0, 8*sizeof(out[0]));
|
||||
} else {
|
||||
IVI_INV_SLANT8( in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7],
|
||||
out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
in += 8;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcRowSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize) {
|
||||
int16 dcCoeff = (*in + 1) >> 1;
|
||||
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = dcCoeff;
|
||||
|
||||
out += pitch;
|
||||
|
||||
for (int y = 1; y < blkSize; out += pitch, y++) {
|
||||
for (int x = 0; x < blkSize; x++)
|
||||
out[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColSlant8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4, t5, t6, t7, t8;
|
||||
|
||||
int row2 = pitch << 1;
|
||||
int row4 = pitch << 2;
|
||||
int row8 = pitch << 3;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT8(in[0], in[8], in[16], in[24], in[32], in[40], in[48], in[56],
|
||||
out[0], out[pitch], out[row2], out[row2 + pitch], out[row4],
|
||||
out[row4 + pitch], out[row4 + row2], out[row8 - pitch],
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
} else {
|
||||
out[0] = out[pitch] = out[row2] = out[row2 + pitch] = out[row4] =
|
||||
out[row4 + pitch] = out[row4 + row2] = out[row8 - pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviDcColSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize) {
|
||||
int16 dcCoeff = (*in + 1) >> 1;
|
||||
|
||||
for (int y = 0; y < blkSize; out += pitch, y++) {
|
||||
out[0] = dcCoeff;
|
||||
for (int x = 1; x < blkSize; x++)
|
||||
out[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviRowSlant4(const int32 *in, int16 *out,
|
||||
uint32 pitch, const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!in[0] && !in[1] && !in[2] && !in[3]) {
|
||||
memset(out, 0, 4*sizeof(out[0]));
|
||||
} else {
|
||||
IVI_INV_SLANT4( in[0], in[1], in[2], in[3],
|
||||
out[0], out[1], out[2], out[3],
|
||||
t0, t1, t2, t3, t4);
|
||||
}
|
||||
in += 4;
|
||||
out += pitch;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviColSlant4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
int t0, t1, t2, t3, t4;
|
||||
|
||||
int row2 = pitch << 1;
|
||||
|
||||
#define COMPENSATE(x) (((x) + 1)>>1)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (flags[i]) {
|
||||
IVI_INV_SLANT4(in[0], in[4], in[8], in[12],
|
||||
out[0], out[pitch], out[row2], out[row2 + pitch],
|
||||
t0, t1, t2, t3, t4);
|
||||
} else {
|
||||
out[0] = out[pitch] = out[row2] = out[row2 + pitch] = 0;
|
||||
}
|
||||
|
||||
in++;
|
||||
out++;
|
||||
}
|
||||
#undef COMPENSATE
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviPutPixels8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags) {
|
||||
for (int y = 0; y < 8; out += pitch, in += 8, y++)
|
||||
for (int x = 0; x < 8; x++)
|
||||
out[x] = in[x];
|
||||
}
|
||||
|
||||
void IndeoDSP::ffIviPutDcPixel8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize) {
|
||||
out[0] = in[0];
|
||||
memset(out + 1, 0, 7 * sizeof(out[0]));
|
||||
out += pitch;
|
||||
|
||||
for (int y = 1; y < 8; out += pitch, y++)
|
||||
memset(out, 0, 8 * sizeof(out[0]));
|
||||
}
|
||||
|
||||
#define IVI_MC_TEMPLATE(size, suffix, OP) \
|
||||
static void iviMc ## size ##x## size ## suffix(int16 *buf, \
|
||||
uint32 dpitch, \
|
||||
const int16 *refBuf, \
|
||||
uint32 pitch, int mcType) \
|
||||
{ \
|
||||
const int16 *wptr; \
|
||||
\
|
||||
switch (mcType) { \
|
||||
case 0: /* fullpel (no interpolation) */ \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, refBuf += pitch) { \
|
||||
for (int j = 0; j < size; j++) {\
|
||||
OP(buf[j], refBuf[j]); \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
case 1: /* horizontal halfpel interpolation */ \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, refBuf += pitch) \
|
||||
for (int j = 0; j < size; j++) \
|
||||
OP(buf[j], (refBuf[j] + refBuf[j+1]) >> 1); \
|
||||
break; \
|
||||
case 2: /* vertical halfpel interpolation */ \
|
||||
wptr = refBuf + pitch; \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, wptr += pitch, refBuf += pitch) \
|
||||
for (int j = 0; j < size; j++) \
|
||||
OP(buf[j], (refBuf[j] + wptr[j]) >> 1); \
|
||||
break; \
|
||||
case 3: /* vertical and horizontal halfpel interpolation */ \
|
||||
wptr = refBuf + pitch; \
|
||||
for (int i = 0; i < size; i++, buf += dpitch, wptr += pitch, refBuf += pitch) \
|
||||
for (int j = 0; j < size; j++) \
|
||||
OP(buf[j], (refBuf[j] + refBuf[j+1] + wptr[j] + wptr[j+1]) >> 2); \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void IndeoDSP::ffIviMc ## size ##x## size ## suffix(int16 *buf, const int16 *refBuf, \
|
||||
uint32 pitch, int mcType) \
|
||||
{ \
|
||||
iviMc ## size ##x## size ## suffix(buf, pitch, refBuf, pitch, mcType); \
|
||||
}
|
||||
|
||||
#define IVI_MC_AVG_TEMPLATE(size, suffix, OP) \
|
||||
void IndeoDSP::ffIviMcAvg ## size ##x## size ## suffix(int16 *buf, \
|
||||
const int16 *refBuf, \
|
||||
const int16 *refBuf2, \
|
||||
uint32 pitch, \
|
||||
int mcType, int mcType2) \
|
||||
{ \
|
||||
int16 tmp[size * size]; \
|
||||
\
|
||||
iviMc ## size ##x## size ## NoDelta(tmp, size, refBuf, pitch, mcType); \
|
||||
iviMc ## size ##x## size ## Delta(tmp, size, refBuf2, pitch, mcType2); \
|
||||
for (int i = 0; i < size; i++, buf += pitch) { \
|
||||
for (int j = 0; j < size; j++) {\
|
||||
OP(buf[j], tmp[i * size + j] >> 1); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define OP_PUT(a, b) (a) = (b)
|
||||
#define OP_ADD(a, b) (a) += (b)
|
||||
|
||||
IVI_MC_TEMPLATE(8, NoDelta, OP_PUT)
|
||||
IVI_MC_TEMPLATE(8, Delta, OP_ADD)
|
||||
IVI_MC_TEMPLATE(4, NoDelta, OP_PUT)
|
||||
IVI_MC_TEMPLATE(4, Delta, OP_ADD)
|
||||
IVI_MC_AVG_TEMPLATE(8, NoDelta, OP_PUT)
|
||||
IVI_MC_AVG_TEMPLATE(8, Delta, OP_ADD)
|
||||
IVI_MC_AVG_TEMPLATE(4, NoDelta, OP_PUT)
|
||||
IVI_MC_AVG_TEMPLATE(4, Delta, OP_ADD)
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
335
image/codecs/indeo/indeo_dsp.h
Normal file
335
image/codecs/indeo/indeo_dsp.h
Normal file
@@ -0,0 +1,335 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note:
|
||||
* DSP functions (inverse transforms, motion compensation, wavelet recompositions)
|
||||
* for Indeo Video Interactive codecs.
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_INDEO_DSP_H
|
||||
#define IMAGE_CODECS_INDEO_INDEO_DSP_H
|
||||
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
#include "image/codecs/indeo/indeo.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
class IndeoDSP {
|
||||
public:
|
||||
/**
|
||||
* two-dimensional inverse Haar 8x8 transform for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseHaar8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
static void ffIviInverseHaar8x1(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
static void ffIviInverseHaar1x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 8-point Haar transform on rows for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviRowHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 8-point Haar transform on columns for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColHaar8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* two-dimensional inverse Haar 4x4 transform for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseHaar4x4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 4-point Haar transform on rows for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviRowHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* one-dimensional inverse 4-point Haar transform on columns for Indeo 4
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColHaar4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* DC-only two-dimensional inverse Haar transform for Indeo 4.
|
||||
* Performing the inverse transform in this case is equivalent to
|
||||
* spreading dcCoeff >> 3 over the whole block.
|
||||
*
|
||||
* @param[in] in Pointer to the dc coefficient
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] blkSize Transform block size
|
||||
*/
|
||||
static void ffIviDcHaar2d(const int32 *in, int16 *out, uint32 pitch,
|
||||
int blkSize);
|
||||
|
||||
/**
|
||||
* two-dimensional inverse slant 8x8 transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseSlant8x8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* two-dimensional inverse slant 4x4 transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviInverseSlant4x4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* DC-only two-dimensional inverse slant transform.
|
||||
* Performing the inverse slant transform in this case is equivalent to
|
||||
* spreading (dcCoeff + 1)/2 over the whole block.
|
||||
* It works much faster than performing the slant transform on a vector of zeroes.
|
||||
*
|
||||
* @param[in] in Pointer to the dc coefficient
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] blkSize Transform block size
|
||||
*/
|
||||
static void ffIviDcSlant2d(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* inverse 1D row slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags (unused here)
|
||||
*/
|
||||
static void ffIviRowSlant8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* inverse 1D column slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColSlant8(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* inverse 1D row slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags (unused here)
|
||||
*/
|
||||
static void ffIviRowSlant4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* inverse 1D column slant transform
|
||||
*
|
||||
* @param[in] in Pointer to the vector of transform coefficients
|
||||
* @param[out] out Pointer to the output buffer (frame)
|
||||
* @param[in] pitch Pitch to move to the next y line
|
||||
* @param[in] flags Pointer to the array of column flags:
|
||||
* != 0 - non_empty column, 0 - empty one
|
||||
* (this array must be filled by caller)
|
||||
*/
|
||||
static void ffIviColSlant4(const int32 *in, int16 *out, uint32 pitch,
|
||||
const uint8 *flags);
|
||||
|
||||
/**
|
||||
* DC-only inverse row slant transform
|
||||
*/
|
||||
static void ffIviDcRowSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* DC-only inverse column slant transform
|
||||
*/
|
||||
static void ffIviDcColSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* Copy the pixels into the frame buffer.
|
||||
*/
|
||||
static void ffIviPutPixels8x8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags);
|
||||
|
||||
/**
|
||||
* Copy the DC coefficient into the first pixel of the block and
|
||||
* zero all others.
|
||||
*/
|
||||
static void ffIviPutDcPixel8x8(const int32 *in, int16 *out, uint32 pitch, int blkSize);
|
||||
|
||||
/**
|
||||
* 8x8 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc8x8Delta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc4x4Delta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* motion compensation without adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc8x8NoDelta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation without adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame receiving the result
|
||||
* @param[in] refBuf Pointer to the corresponding block in the reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type
|
||||
*/
|
||||
static void ffIviMc4x4NoDelta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType);
|
||||
|
||||
/**
|
||||
* 8x8 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg8x8Delta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation with adding delta
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg4x4Delta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
/**
|
||||
* motion compensation without adding delta for B-frames
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg8x8NoDelta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
|
||||
/**
|
||||
* 4x4 block motion compensation without adding delta for B-frames
|
||||
*
|
||||
* @param[in,out] buf Pointer to the block in the current frame buffer containing delta
|
||||
* @param[in] refBuf Pointer to the corresponding block in the backward reference frame
|
||||
* @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame
|
||||
* @param[in] pitch Pitch for moving to the next y line
|
||||
* @param[in] mcType Interpolation type for backward reference
|
||||
* @param[in] mcType2 Interpolation type for forward reference
|
||||
*/
|
||||
static void ffIviMcAvg4x4NoDelta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2);
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
135
image/codecs/indeo/mem.cpp
Normal file
135
image/codecs/indeo/mem.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
const uint8 ffReverse[256] = {
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
|
||||
};
|
||||
|
||||
const uint8 ffZigZagDirect[64] = {
|
||||
0, 1, 8, 16, 9, 2, 3, 10,
|
||||
17, 24, 32, 25, 18, 11, 4, 5,
|
||||
12, 19, 26, 33, 40, 48, 41, 34,
|
||||
27, 20, 13, 6, 7, 14, 21, 28,
|
||||
35, 42, 49, 56, 57, 50, 43, 36,
|
||||
29, 22, 15, 23, 30, 37, 44, 51,
|
||||
58, 59, 52, 45, 38, 31, 39, 46,
|
||||
53, 60, 61, 54, 47, 55, 62, 63
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Multiply two `size_t` values checking for overflow.
|
||||
*
|
||||
* @param[in] a,b Operands of multiplication
|
||||
* @param[out] r Pointer to the result of the operation
|
||||
* @return 0 on success, AVERROR(EINVAL) on overflow
|
||||
*/
|
||||
static inline int avSizeMult(size_t a, size_t b, size_t *r) {
|
||||
size_t t = a * b;
|
||||
|
||||
// Hack inspired from glibc: don't try the division if nelem and elsize
|
||||
// are both less than sqrt(SIZE_MAX).
|
||||
if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b)
|
||||
return -1;
|
||||
*r = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void avFreeP(void *arg) {
|
||||
void **ptr = (void **)arg;
|
||||
free(*ptr);
|
||||
*ptr = nullptr;
|
||||
}
|
||||
|
||||
void *avReallocF(void *ptr, size_t nelem, size_t elsize) {
|
||||
size_t size;
|
||||
void *r;
|
||||
|
||||
if (avSizeMult(elsize, nelem, &size)) {
|
||||
free(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
r = realloc(ptr, size);
|
||||
if (!r)
|
||||
free(ptr);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Swap the order of the bytes in the passed value
|
||||
*/
|
||||
uint32 bitswap32(uint32 x) {
|
||||
return (uint32)ffReverse[x & 0xFF] << 24 |
|
||||
(uint32)ffReverse[(x >> 8) & 0xFF] << 16 |
|
||||
(uint32)ffReverse[(x >> 16) & 0xFF] << 8 |
|
||||
(uint32)ffReverse[x >> 24];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse "nbits" bits of the value "val" and return the result
|
||||
* in the least significant bits.
|
||||
*/
|
||||
uint16 invertBits(uint16 val, int nbits) {
|
||||
uint16 res;
|
||||
|
||||
if (nbits <= 8) {
|
||||
res = ffReverse[val] >> (8 - nbits);
|
||||
} else {
|
||||
res = ((ffReverse[val & 0xFF] << 8) +
|
||||
(ffReverse[val >> 8])) >> (16 - nbits);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
109
image/codecs/indeo/mem.h
Normal file
109
image/codecs/indeo/mem.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
/* Common memory code used by the Indeo decoder
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_MEM_H
|
||||
#define IMAGE_CODECS_INDEO_MEM_H
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define FFALIGN(x, a) (((x) + (a)-1) & ~((a)-1))
|
||||
#define FFSIGN(a) ((a) > 0 ? 1 : -1)
|
||||
#define MAX_INTEGER 0x7ffffff
|
||||
|
||||
/**
|
||||
* Free a memory block which has been allocated with a function of av_malloc()
|
||||
* or av_realloc() family, and set the pointer pointing to it to `NULL`.
|
||||
*
|
||||
* @param ptr Pointer to the pointer to the memory block which should be freed
|
||||
* @note `*ptr = NULL` is safe and leads to no action.
|
||||
*/
|
||||
extern void avFreeP(void *arg);
|
||||
|
||||
|
||||
/**
|
||||
* Allocate, reallocate, or free a block of memory.
|
||||
*
|
||||
* This function does the same thing as av_realloc(), except:
|
||||
* - It takes two size arguments and allocates `nelem * elsize` bytes,
|
||||
* after checking the result of the multiplication for integer overflow.
|
||||
* - It frees the input block in case of failure, thus avoiding the memory
|
||||
* leak with the classic
|
||||
* @code{.c}
|
||||
* buf = realloc(buf);
|
||||
* if (!buf)
|
||||
* return -1;
|
||||
* @endcode
|
||||
* pattern.
|
||||
*/
|
||||
extern void *avReallocF(void *ptr, size_t nelem, size_t elsize);
|
||||
|
||||
/**
|
||||
* Reverse "nbits" bits of the value "val" and return the result
|
||||
* in the least significant bits.
|
||||
*/
|
||||
extern uint16 invertBits(uint16 val, int nbits);
|
||||
|
||||
/**
|
||||
* Swap the order of the bytes in the passed value
|
||||
*/
|
||||
extern uint32 bitswap32(uint32 x);
|
||||
|
||||
/**
|
||||
* Clip a signed integer value into the 0-255 range.
|
||||
* @param a value to clip
|
||||
* @return clipped value
|
||||
*/
|
||||
inline uint8 avClipUint8(int a) {
|
||||
if (a & (~0xFF))
|
||||
return (-a) >> 31;
|
||||
else
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip a signed integer to an unsigned power of two range.
|
||||
* @param a value to clip
|
||||
* @param p bit position to clip at
|
||||
* @return clipped value
|
||||
*/
|
||||
inline unsigned avClipUintp2(int a, int p) {
|
||||
if (a & ~((1 << p) - 1))
|
||||
return -a >> 31 & ((1 << p) - 1);
|
||||
else
|
||||
return a;
|
||||
}
|
||||
|
||||
extern const uint8 ffZigZagDirect[64];
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
335
image/codecs/indeo/vlc.cpp
Normal file
335
image/codecs/indeo/vlc.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "image/codecs/indeo/vlc.h"
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
/**
|
||||
* Quicksort
|
||||
* This sort is fast, and fully inplace but not stable and it is possible
|
||||
* to construct input that requires O(n^2) time but this is very unlikely to
|
||||
* happen with non constructed input.
|
||||
*/
|
||||
#define AV_QSORT(p, num, type, cmp) do {\
|
||||
void *stack[64][2];\
|
||||
int sp = 1;\
|
||||
stack[0][0] = p;\
|
||||
stack[0][1] = (p)+(num)-1;\
|
||||
while(sp){\
|
||||
type *start = (type *)stack[--sp][0];\
|
||||
type *end = (type *)stack[ sp][1];\
|
||||
while (start < end) {\
|
||||
if (start < end-1) {\
|
||||
int checksort = 0;\
|
||||
type *right = end - 2;\
|
||||
type *left = start + 1;\
|
||||
type *mid = start + ((end - start) >> 1);\
|
||||
if(cmp(start, end) > 0) {\
|
||||
if(cmp( end, mid) > 0) SWAP(*start, *mid);\
|
||||
else SWAP(*start, *end);\
|
||||
} else {\
|
||||
if(cmp(start, mid) > 0) SWAP(*start, *mid);\
|
||||
else checksort = 1;\
|
||||
}\
|
||||
if (cmp(mid, end) > 0) { \
|
||||
SWAP(*mid, *end);\
|
||||
checksort = 0;\
|
||||
}\
|
||||
if(start == end - 2) break;\
|
||||
SWAP(end[-1], *mid);\
|
||||
while (left <= right) {\
|
||||
while (left<=right && cmp(left, end - 1) < 0)\
|
||||
left++;\
|
||||
while (left<=right && cmp(right, end - 1) > 0)\
|
||||
right--;\
|
||||
if (left <= right) {\
|
||||
SWAP(*left, *right);\
|
||||
left++;\
|
||||
right--;\
|
||||
}\
|
||||
}\
|
||||
SWAP(end[-1], *left);\
|
||||
if(checksort && (mid == left - 1 || mid == left)){\
|
||||
mid= start;\
|
||||
while(mid<end && cmp(mid, mid+1) <= 0)\
|
||||
mid++;\
|
||||
if(mid==end)\
|
||||
break;\
|
||||
}\
|
||||
if (end - left < left - start){\
|
||||
stack[sp ][0] = start;\
|
||||
stack[sp++][1] = right;\
|
||||
start = left + 1;\
|
||||
} else {\
|
||||
stack[sp ][0] = left+1;\
|
||||
stack[sp++][1] = end;\
|
||||
end = right;\
|
||||
}\
|
||||
} else {\
|
||||
if (cmp(start, end) > 0)\
|
||||
SWAP(*start, *end);\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
#define COPY(condition)\
|
||||
for (i = 0; i < nbCodes; i++) { \
|
||||
buf[j].bits = getData(p_bits, i, bitsWrap, bitsSize); \
|
||||
if (!(condition)) \
|
||||
continue; \
|
||||
if (buf[j].bits > (3 * nbBits) || buf[j].bits > 32) { \
|
||||
warning("Too long VLC (%d) in init_vlc", buf[j].bits); \
|
||||
if (!(flags & INIT_VLC_USE_NEW_STATIC)) \
|
||||
free(buf); \
|
||||
return -1; \
|
||||
} \
|
||||
buf[j].code = getData(codes, i, codesWrap, codesSize); \
|
||||
if (buf[j].code >= (1LL << buf[j].bits)) { \
|
||||
warning("Invalid code %x for %d in init_vlc", buf[j].code, i); \
|
||||
if (!(flags & INIT_VLC_USE_NEW_STATIC)) \
|
||||
free(buf); \
|
||||
return -1; \
|
||||
} \
|
||||
if (flags & INIT_VLC_LE) \
|
||||
buf[j].code = bitswap32(buf[j].code); \
|
||||
else \
|
||||
buf[j].code <<= 32 - buf[j].bits; \
|
||||
if (symbols) \
|
||||
buf[j].symbol = getData(symbols, i, symbolsWrap, symbolsSize); \
|
||||
else \
|
||||
buf[j].symbol = i; \
|
||||
j++; \
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
VLC::VLC() : _bits(0), _tableSize(0), _tableAllocated(0), _table(nullptr) {
|
||||
}
|
||||
|
||||
int VLC::init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, int bitsSize,
|
||||
const void *codes, int codesWrap, int codesSize, int flags) {
|
||||
return init_vlc(nbBits, nbCodes, bits, bitsWrap, bitsSize, codes, codesWrap,
|
||||
codesSize, nullptr, 0, 0, flags);
|
||||
}
|
||||
|
||||
int VLC::init_vlc(int nbBits, int nbCodes, const void *p_bits, int bitsWrap,
|
||||
int bitsSize, const void *codes, int codesWrap, int codesSize,
|
||||
const void *symbols, int symbolsWrap, int symbolsSize, int flags) {
|
||||
VLCcode *buf;
|
||||
int i, j, ret;
|
||||
VLCcode localbuf[1500]; // the maximum currently needed is 1296 by rv34
|
||||
VLC localvlc, *vlc;
|
||||
|
||||
vlc = this;
|
||||
vlc->_bits = nbBits;
|
||||
if (flags & INIT_VLC_USE_NEW_STATIC) {
|
||||
assert((nbCodes + 1) <= (int)FF_ARRAY_ELEMS(localbuf));
|
||||
buf = localbuf;
|
||||
localvlc = *this;
|
||||
vlc = &localvlc;
|
||||
vlc->_tableSize = 0;
|
||||
} else {
|
||||
vlc->_table = NULL;
|
||||
vlc->_tableAllocated = 0;
|
||||
vlc->_tableSize = 0;
|
||||
|
||||
buf = (VLCcode *)malloc((nbCodes + 1) * sizeof(VLCcode));
|
||||
assert(buf);
|
||||
}
|
||||
|
||||
assert(symbolsSize <= 2 || !symbols);
|
||||
j = 0;
|
||||
|
||||
COPY(buf[j].bits > nbBits);
|
||||
|
||||
// qsort is the slowest part of init_vlc, and could probably be improved or avoided
|
||||
AV_QSORT(buf, j, VLCcode, compareVlcSpec);
|
||||
COPY(buf[j].bits && buf[j].bits <= nbBits);
|
||||
nbCodes = j;
|
||||
|
||||
ret = vlc->buildTable(nbBits, nbCodes, buf, flags);
|
||||
|
||||
if (flags & INIT_VLC_USE_NEW_STATIC) {
|
||||
if (vlc->_tableSize != vlc->_tableAllocated)
|
||||
warning("needed %d had %d", vlc->_tableSize, vlc->_tableAllocated);
|
||||
|
||||
assert(ret >= 0);
|
||||
*this = *vlc;
|
||||
} else {
|
||||
free(buf);
|
||||
if (ret < 0) {
|
||||
avFreeP(&vlc->_table);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VLC::freeVlc() {
|
||||
free(_table);
|
||||
}
|
||||
|
||||
int VLC::compareVlcSpec(const void *a, const void *b) {
|
||||
const VLCcode *sa = (const VLCcode *)a, *sb = (const VLCcode *)b;
|
||||
return (sa->code >> 1) - (sb->code >> 1);
|
||||
}
|
||||
|
||||
int VLC::buildTable(int tableNbBits, int nbCodes,
|
||||
VLCcode *codes, int flags) {
|
||||
VLC *vlc = this;
|
||||
int tableSize, tableIndex, index, codePrefix, symbol, subtableBits;
|
||||
int i, j, k, n, nb, inc;
|
||||
uint32 code;
|
||||
VLC_TYPE (*table)[2];
|
||||
|
||||
tableSize = 1 << tableNbBits;
|
||||
if (tableNbBits > 30)
|
||||
return -1;
|
||||
tableIndex = allocTable(tableSize, flags & INIT_VLC_USE_NEW_STATIC);
|
||||
//warning("new table index=%d size=%d", tableIndex, tableSize);
|
||||
if (tableIndex < 0)
|
||||
return tableIndex;
|
||||
table = &vlc->_table[tableIndex];
|
||||
|
||||
// first pass: map codes and compute auxiliary table sizes
|
||||
for (i = 0; i < nbCodes; i++) {
|
||||
n = codes[i].bits;
|
||||
code = codes[i].code;
|
||||
symbol = codes[i].symbol;
|
||||
//warning("i=%d n=%d code=0x%x", i, n, code);
|
||||
|
||||
if (n <= tableNbBits) {
|
||||
// no need to add another table
|
||||
j = code >> (32 - tableNbBits);
|
||||
nb = 1 << (tableNbBits - n);
|
||||
inc = 1;
|
||||
if (flags & INIT_VLC_LE) {
|
||||
j = bitswap32(code);
|
||||
inc = 1 << n;
|
||||
}
|
||||
for (k = 0; k < nb; k++) {
|
||||
int bits = table[j][1];
|
||||
//warning("%4x: code=%d n=%d", j, i, n);
|
||||
|
||||
if (bits != 0 && bits != n) {
|
||||
warning("incorrect codes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
table[j][1] = n; //bits
|
||||
table[j][0] = symbol;
|
||||
j += inc;
|
||||
}
|
||||
} else {
|
||||
// fill auxiliary table recursively
|
||||
n -= tableNbBits;
|
||||
codePrefix = code >> (32 - tableNbBits);
|
||||
subtableBits = n;
|
||||
codes[i].bits = n;
|
||||
codes[i].code = code << tableNbBits;
|
||||
for (k = i + 1; k < nbCodes; k++) {
|
||||
n = codes[k].bits - tableNbBits;
|
||||
if (n <= 0)
|
||||
break;
|
||||
code = codes[k].code;
|
||||
if (code >> (32 - tableNbBits) != (uint)codePrefix)
|
||||
break;
|
||||
codes[k].bits = n;
|
||||
codes[k].code = code << tableNbBits;
|
||||
subtableBits = MAX(subtableBits, n);
|
||||
}
|
||||
subtableBits = MIN(subtableBits, tableNbBits);
|
||||
j = (flags & INIT_VLC_LE) ? bitswap32(codePrefix) >> (32 - tableNbBits) : codePrefix;
|
||||
table[j][1] = -subtableBits;
|
||||
//warning("%4x: n=%d (subtable)", j, codes[i].bits + tableNbBits);
|
||||
index = vlc->buildTable(subtableBits, k - i, codes + i, flags);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
// note: realloc has been done, so reload tables
|
||||
table = (VLC_TYPE (*)[2])&vlc->_table[tableIndex];
|
||||
table[j][0] = index; //code
|
||||
i = k - 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tableSize; i++) {
|
||||
if (table[i][1] == 0) //bits
|
||||
table[i][0] = -1; //codes
|
||||
}
|
||||
|
||||
return tableIndex;
|
||||
}
|
||||
|
||||
int VLC::allocTable(int size, int useStatic) {
|
||||
VLC *vlc = this;
|
||||
int index = vlc->_tableSize;
|
||||
|
||||
vlc->_tableSize += size;
|
||||
if (vlc->_tableSize > vlc->_tableAllocated) {
|
||||
// cannot do anything, init_vlc() is used with too little memory
|
||||
assert(!useStatic);
|
||||
|
||||
vlc->_tableAllocated += (1 << vlc->_bits);
|
||||
vlc->_table = (int16(*)[2])avReallocF(vlc->_table, vlc->_tableAllocated, sizeof(VLC_TYPE) * 2);
|
||||
if (!vlc->_table) {
|
||||
vlc->_tableAllocated = 0;
|
||||
vlc->_tableSize = 0;
|
||||
return -2;
|
||||
}
|
||||
|
||||
memset(vlc->_table + vlc->_tableAllocated - (static_cast<ptrdiff_t>(1) << vlc->_bits), 0, sizeof(VLC_TYPE) * 2 << vlc->_bits);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
uint VLC::getData(const void *table, uint idx, uint wrap, uint size) {
|
||||
const uint8 *ptr = (const uint8 *)table + idx * wrap;
|
||||
|
||||
switch(size) {
|
||||
case 1:
|
||||
return *(const uint8 *)ptr;
|
||||
|
||||
case 2:
|
||||
return *(const uint16 *)ptr;
|
||||
|
||||
default:
|
||||
return *(const uint32 *)ptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
134
image/codecs/indeo/vlc.h
Normal file
134
image/codecs/indeo/vlc.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
/* VLC code
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO_VLC_H
|
||||
#define IMAGE_CODECS_INDEO_VLC_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
|
||||
namespace Image {
|
||||
namespace Indeo {
|
||||
|
||||
#define VLC_TYPE int16
|
||||
|
||||
enum VLCFlag {
|
||||
INIT_VLC_LE = 2,
|
||||
INIT_VLC_USE_NEW_STATIC = 4
|
||||
};
|
||||
|
||||
struct VLCcode {
|
||||
uint8 bits;
|
||||
uint16 symbol;
|
||||
|
||||
/**
|
||||
* codeword, with the first bit-to-be-read in the msb
|
||||
* (even if intended for a little-endian bitstream reader)
|
||||
*/
|
||||
uint32 code;
|
||||
};
|
||||
|
||||
struct VLC {
|
||||
private:
|
||||
static int compareVlcSpec(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* Gets a value of a given size from a table
|
||||
* @param table Table to get data from
|
||||
* @param idx Index of value to retrieve
|
||||
* @param wrap Size of elements with alignment
|
||||
* @param size Size of elements
|
||||
*/
|
||||
static uint getData(const void *table, uint idx, uint wrap, uint size);
|
||||
public:
|
||||
int _bits;
|
||||
VLC_TYPE (*_table)[2]; ///< code, bits
|
||||
int _tableSize, _tableAllocated;
|
||||
|
||||
VLC();
|
||||
|
||||
/* Build VLC decoding tables suitable for use with get_vlc().
|
||||
|
||||
'nbBits' sets the decoding table size (2^nbBits) entries. The
|
||||
bigger it is, the faster is the decoding. But it should not be too
|
||||
big to save memory and L1 cache. '9' is a good compromise.
|
||||
|
||||
'nbCodes' : number of vlcs codes
|
||||
|
||||
'bits' : table which gives the size (in bits) of each vlc code.
|
||||
|
||||
'codes' : table which gives the bit pattern of of each vlc code.
|
||||
|
||||
'symbols' : table which gives the values to be returned from get_vlc().
|
||||
|
||||
'xxx_wrap' : give the number of bytes between each entry of the
|
||||
'bits' or 'codes' tables.
|
||||
|
||||
'xxx_size' : gives the number of bytes of each entry of the 'bits'
|
||||
or 'codes' tables.
|
||||
|
||||
'wrap' and 'size' make it possible to use any memory configuration and types
|
||||
(byte/word/long) to store the 'bits', 'codes', and 'symbols' tables.
|
||||
|
||||
'useStatic' should be set to 1 for tables, which should be freed
|
||||
with av_free_static(), 0 if freeVlc() will be used.
|
||||
*/
|
||||
int init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap,
|
||||
int bitsSize, const void *codes, int codesWrap, int codesSize,
|
||||
const void *symbols, int symbolsWrap, int symbolsSize, int flags);
|
||||
|
||||
int init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, int bitsSize,
|
||||
const void *codes, int codesWrap, int codesSize, int flags);
|
||||
|
||||
/**
|
||||
* Free VLC data
|
||||
*/
|
||||
void freeVlc();
|
||||
|
||||
|
||||
/**
|
||||
* Build VLC decoding tables suitable for use with get_vlc().
|
||||
*
|
||||
* @param tableNbBits max length of vlc codes to store directly in this table
|
||||
* (Longer codes are delegated to subtables.)
|
||||
*
|
||||
* @param nbCodes number of elements in codes[]
|
||||
*
|
||||
* @param codes descriptions of the vlc codes
|
||||
* These must be ordered such that codes going into the same subtable are contiguous.
|
||||
* Sorting by VLCcode.code is sufficient, though not necessary.
|
||||
*/
|
||||
int buildTable(int tableNbBits, int nbCodes, VLCcode *codes, int flags);
|
||||
|
||||
int allocTable(int size, int useStatic);
|
||||
};
|
||||
|
||||
} // End of namespace Indeo
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
3515
image/codecs/indeo3.cpp
Normal file
3515
image/codecs/indeo3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
102
image/codecs/indeo3.h
Normal file
102
image/codecs/indeo3.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
/* Intel Indeo 3 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note:
|
||||
* Intel Indeo 3 (IV31, IV32, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO3_H
|
||||
#define IMAGE_CODECS_INDEO3_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Intel Indeo 3 decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*
|
||||
* Used in video:
|
||||
* - VMDDecoder
|
||||
*/
|
||||
class Indeo3Decoder : public Codec {
|
||||
public:
|
||||
Indeo3Decoder(uint16 width, uint16 height, uint bitsPerPixel = 24);
|
||||
~Indeo3Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isIndeo3(Common::SeekableReadStream &stream);
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
|
||||
static const byte _corrector_type_0[24];
|
||||
static const byte _corrector_type_2[8];
|
||||
static const uint32 correction[];
|
||||
static const uint32 correctionloworder[];
|
||||
static const uint32 correctionhighorder[];
|
||||
|
||||
struct YUVBufs {
|
||||
byte *Ybuf;
|
||||
byte *Ubuf;
|
||||
byte *Vbuf;
|
||||
byte *the_buf;
|
||||
uint32 the_buf_size;
|
||||
uint16 y_w, y_h;
|
||||
uint16 uv_w, uv_h;
|
||||
};
|
||||
|
||||
YUVBufs _iv_frame[2];
|
||||
YUVBufs *_cur_frame;
|
||||
YUVBufs *_ref_frame;
|
||||
|
||||
byte *_ModPred;
|
||||
byte *_corrector_type;
|
||||
|
||||
void buildModPred();
|
||||
void allocFrames();
|
||||
|
||||
void decodeChunk(byte *cur, byte *ref, int width, int height,
|
||||
const byte *buf1, uint32 fflags2, const byte *hdr,
|
||||
const byte *buf2, int min_width_160);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1177
image/codecs/indeo4.cpp
Normal file
1177
image/codecs/indeo4.cpp
Normal file
File diff suppressed because it is too large
Load Diff
149
image/codecs/indeo4.h
Normal file
149
image/codecs/indeo4.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Intel Indeo 4 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note:
|
||||
* Intel Indeo 4 (IV31, IV32, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO4_H
|
||||
#define IMAGE_CODECS_INDEO4_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
#include "image/codecs/indeo/indeo.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
using namespace Indeo;
|
||||
|
||||
/**
|
||||
* Intel Indeo 4 decoder.
|
||||
*
|
||||
* Used by AVI.
|
||||
*
|
||||
* Used in video:
|
||||
* - AVIDecoder
|
||||
*/
|
||||
class Indeo4Decoder : public IndeoDecoderBase {
|
||||
struct Transform {
|
||||
InvTransformPtr *_invTrans;
|
||||
DCTransformPtr *_dcTrans;
|
||||
bool _is2dTrans;
|
||||
};
|
||||
public:
|
||||
Indeo4Decoder(uint16 width, uint16 height, uint bitsPerPixel = 16);
|
||||
~Indeo4Decoder() override {}
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
static bool isIndeo4(Common::SeekableReadStream &stream);
|
||||
protected:
|
||||
/**
|
||||
* Decode the Indeo 4 picture header.
|
||||
* @returns 0 = Ok, negative number = error
|
||||
*/
|
||||
int decodePictureHeader() override;
|
||||
|
||||
/**
|
||||
* Rearrange decoding and reference buffers.
|
||||
*/
|
||||
void switchBuffers() override;
|
||||
|
||||
bool isNonNullFrame() const override;
|
||||
|
||||
/**
|
||||
* Decode Indeo 4 band header.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @returns result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeBandHeader(IVIBandDesc *band) override;
|
||||
|
||||
/**
|
||||
* Decode information (block type, cbp, quant delta, motion vector)
|
||||
* for all macroblocks in the current tile.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @param[in,out] tile pointer to the tile descriptor
|
||||
* @returns result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeMbInfo(IVIBandDesc *band, IVITile *tile) override;
|
||||
|
||||
/**
|
||||
* Decodes huffman + RLE-coded transparency data within Indeo4 frames
|
||||
*/
|
||||
int decodeRLETransparency(VLC_TYPE (*table)[2]);
|
||||
|
||||
/**
|
||||
* Decodes optional transparency data within Indeo4 frames
|
||||
*/
|
||||
int decodeTransparency() override;
|
||||
private:
|
||||
int scaleTileSize(int defSize, int sizeFactor);
|
||||
|
||||
/**
|
||||
* Decode subdivision of a plane.
|
||||
* This is a simplified version that checks for two supported subdivisions:
|
||||
* - 1 wavelet band per plane, size factor 1:1, code pattern: 3
|
||||
* - 4 wavelet bands per plane, size factor 1:4, code pattern: 2,3,3,3,3
|
||||
* Anything else is either unsupported or corrupt.
|
||||
*
|
||||
* @param[in,out] gb The GetBit context
|
||||
* @returns Number of wavelet bands or 0 on error
|
||||
*/
|
||||
int decodePlaneSubdivision();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Standard picture dimensions
|
||||
*/
|
||||
static const uint _ivi4_common_pic_sizes[14];
|
||||
|
||||
/**
|
||||
* Transformations list
|
||||
*/
|
||||
static const Transform _transforms[18];
|
||||
|
||||
static const uint8 *const _scan_index_to_tab[15];
|
||||
|
||||
/**
|
||||
* Indeo 4 dequant tables
|
||||
*/
|
||||
static const uint16 _ivi4_quant_8x8_intra[9][64];
|
||||
|
||||
static const uint16 _ivi4_quant_8x8_inter[9][64];
|
||||
|
||||
static const uint16 _ivi4_quant_4x4_intra[5][16];
|
||||
|
||||
static const uint16 _ivi4_quant_4x4_inter[5][16];
|
||||
|
||||
/**
|
||||
* Table for mapping quant matrix index from the bitstream
|
||||
* into internal quant table number.
|
||||
*/
|
||||
static const uint8 _quant_index_to_tab[22];
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
752
image/codecs/indeo5.cpp
Normal file
752
image/codecs/indeo5.cpp
Normal file
@@ -0,0 +1,752 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Intel Indeo 5 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note: * Intel Indeo 5 (IV51, IV52, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
#include "image/codecs/indeo5.h"
|
||||
#include "image/codecs/indeo/indeo_dsp.h"
|
||||
#include "image/codecs/indeo/mem.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Indeo5 frame types.
|
||||
*/
|
||||
enum {
|
||||
FRAMETYPE_INTRA = 0,
|
||||
FRAMETYPE_INTER = 1, ///< non-droppable P-frame
|
||||
FRAMETYPE_INTER_SCAL = 2, ///< droppable P-frame used in the scalability mode
|
||||
FRAMETYPE_INTER_NOREF = 3, ///< droppable P-frame
|
||||
FRAMETYPE_NULL = 4 ///< empty frame with no data
|
||||
};
|
||||
|
||||
#define IVI5_PIC_SIZE_ESC 15
|
||||
|
||||
Indeo5Decoder::Indeo5Decoder(uint16 width, uint16 height, uint bitsPerPixel) :
|
||||
IndeoDecoderBase(width, height, bitsPerPixel) {
|
||||
_ctx._isIndeo4 = false;
|
||||
_ctx._refBuf = 1;
|
||||
_ctx._bRefBuf = 3;
|
||||
_ctx._pFrame = new AVFrame();
|
||||
}
|
||||
|
||||
bool Indeo5Decoder::isIndeo5(Common::SeekableReadStream &stream) {
|
||||
// Less than 16 bytes? This can't be right
|
||||
if (stream.size() < 16)
|
||||
return false;
|
||||
|
||||
// Read in the start of the data
|
||||
byte buffer[16];
|
||||
stream.read(buffer, 16);
|
||||
stream.seek(-16, SEEK_CUR);
|
||||
|
||||
// Validate the first 5-bit word has the correct identifier
|
||||
Indeo::GetBits gb(buffer, 16 * 8);
|
||||
bool isIndeo5 = gb.getBits<5>() == 0x1F;
|
||||
|
||||
return isIndeo5;
|
||||
}
|
||||
|
||||
const Graphics::Surface *Indeo5Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
// Not Indeo 5? Fail
|
||||
if (!isIndeo5(stream))
|
||||
return nullptr;
|
||||
|
||||
// Set up the frame data buffer
|
||||
byte *frameData = new byte[stream.size()];
|
||||
stream.read(frameData, stream.size());
|
||||
_ctx._frameData = frameData;
|
||||
_ctx._frameSize = stream.size();
|
||||
|
||||
// Set up the GetBits instance for reading the data
|
||||
_ctx._gb = new GetBits(_ctx._frameData, _ctx._frameSize);
|
||||
|
||||
// Decode the frame
|
||||
int err = decodeIndeoFrame();
|
||||
|
||||
// Free the bit reader and frame buffer
|
||||
delete _ctx._gb;
|
||||
_ctx._gb = nullptr;
|
||||
delete[] frameData;
|
||||
_ctx._frameData = nullptr;
|
||||
_ctx._frameSize = 0;
|
||||
|
||||
return (err < 0) ? nullptr : _surface;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decodePictureHeader() {
|
||||
IVIPicConfig picConf;
|
||||
int ret;
|
||||
|
||||
if (_ctx._gb->getBits<5>() != 0x1F) {
|
||||
warning("Invalid picture start code!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
_ctx._prevFrameType = _ctx._frameType;
|
||||
_ctx._frameType = _ctx._gb->getBits<3>();
|
||||
if (_ctx._frameType >= 5) {
|
||||
warning("Invalid frame type: %d", _ctx._frameType);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_ctx._frameNum = _ctx._gb->getBits<8>();
|
||||
|
||||
if (_ctx._frameType == FRAMETYPE_INTRA) {
|
||||
if ((ret = decode_gop_header()) < 0) {
|
||||
warning("Invalid GOP header, skipping frames.");
|
||||
_ctx._gopInvalid = true;
|
||||
return ret;
|
||||
}
|
||||
_ctx._gopInvalid = false;
|
||||
}
|
||||
|
||||
if (_ctx._frameType == FRAMETYPE_INTER_SCAL && !_ctx._isScalable) {
|
||||
warning("Scalable inter frame in non scalable stream");
|
||||
_ctx._frameType = FRAMETYPE_INTER;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_ctx._frameType != FRAMETYPE_NULL) {
|
||||
_ctx._frameFlags = _ctx._gb->getBits<8>();
|
||||
|
||||
_ctx._picHdrSize = (_ctx._frameFlags & 1) ? _ctx._gb->getBits<24>() : 0;
|
||||
|
||||
_ctx._checksum = (_ctx._frameFlags & 0x10) ? _ctx._gb->getBits<16>() : 0;
|
||||
|
||||
// skip unknown extension if any
|
||||
if (_ctx._frameFlags & 0x20)
|
||||
skip_hdr_extension(); // XXX: untested
|
||||
|
||||
// decode macroblock huffman codebook
|
||||
ret = _ctx._mbVlc.decodeHuffDesc(&_ctx, _ctx._frameFlags & 0x40,
|
||||
IVI_MB_HUFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
_ctx._gb->skip(3); // FIXME: unknown meaning!
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Indeo5Decoder::switchBuffers() {
|
||||
switch (_ctx._prevFrameType) {
|
||||
case FRAMETYPE_INTRA:
|
||||
case FRAMETYPE_INTER:
|
||||
_ctx._bufSwitch ^= 1;
|
||||
_ctx._dstBuf = _ctx._bufSwitch;
|
||||
_ctx._refBuf = _ctx._bufSwitch ^ 1;
|
||||
break;
|
||||
|
||||
case FRAMETYPE_INTER_SCAL:
|
||||
if (!_ctx._interScal) {
|
||||
_ctx._ref2Buf = 2;
|
||||
_ctx._interScal = 1;
|
||||
}
|
||||
SWAP(_ctx._dstBuf, _ctx._ref2Buf);
|
||||
_ctx._refBuf = _ctx._ref2Buf;
|
||||
break;
|
||||
|
||||
case FRAMETYPE_INTER_NOREF:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_ctx._frameType) {
|
||||
case FRAMETYPE_INTRA:
|
||||
_ctx._bufSwitch = 0;
|
||||
// FALLTHROUGH
|
||||
case FRAMETYPE_INTER:
|
||||
_ctx._interScal = 0;
|
||||
_ctx._dstBuf = _ctx._bufSwitch;
|
||||
_ctx._refBuf = _ctx._bufSwitch ^ 1;
|
||||
break;
|
||||
|
||||
case FRAMETYPE_INTER_SCAL:
|
||||
case FRAMETYPE_INTER_NOREF:
|
||||
case FRAMETYPE_NULL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Indeo5Decoder::isNonNullFrame() const {
|
||||
return _ctx._frameType != FRAMETYPE_NULL;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decodeBandHeader(IVIBandDesc *band) {
|
||||
int i, ret;
|
||||
uint8 bandFlags;
|
||||
|
||||
bandFlags = _ctx._gb->getBits<8>();
|
||||
|
||||
if (bandFlags & 1) {
|
||||
band->_isEmpty = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
band->_dataSize = (_ctx._frameFlags & 0x80) ? _ctx._gb->getBits<24>() : 0;
|
||||
|
||||
band->_inheritMv = (bandFlags & 2) != 0;
|
||||
band->_inheritQDelta = (bandFlags & 8) != 0;
|
||||
band->_qdeltaPresent = (bandFlags & 4) != 0;
|
||||
if (!band->_qdeltaPresent)
|
||||
band->_inheritQDelta = 1;
|
||||
|
||||
// decode rvmap probability corrections if any
|
||||
band->_numCorr = 0; // there are no corrections
|
||||
if (bandFlags & 0x10) {
|
||||
band->_numCorr = _ctx._gb->getBits<8>(); // get number of correction pairs
|
||||
if (band->_numCorr > 61) {
|
||||
warning("Too many corrections: %d", band->_numCorr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read correction pairs
|
||||
for (i = 0; i < band->_numCorr * 2; i++)
|
||||
band->_corr[i] = _ctx._gb->getBits<8>();
|
||||
}
|
||||
|
||||
// select appropriate rvmap table for this band
|
||||
band->_rvmapSel = (bandFlags & 0x40) ? _ctx._gb->getBits<3>() : 8;
|
||||
|
||||
// decode block huffman codebook
|
||||
ret = band->_blkVlc.decodeHuffDesc(&_ctx, bandFlags & 0x80, IVI_BLK_HUFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
band->_checksumPresent = _ctx._gb->getBit();
|
||||
if (band->_checksumPresent)
|
||||
band->_checksum = _ctx._gb->getBits<16>();
|
||||
|
||||
band->_globQuant = _ctx._gb->getBits<5>();
|
||||
|
||||
// skip unknown extension if any
|
||||
if (bandFlags & 0x20) { // XXX: untested
|
||||
_ctx._gb->align();
|
||||
skip_hdr_extension();
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decodeMbInfo(IVIBandDesc *band, IVITile *tile) {
|
||||
int x, y, mvX, mvY, mvDelta, offs, mbOffset, mvScale, s;
|
||||
IVIMbInfo *mb, *refMb;
|
||||
int rowOffset = band->_mbSize * band->_pitch;
|
||||
|
||||
mb = tile->_mbs;
|
||||
refMb = tile->_refMbs;
|
||||
offs = tile->_yPos * band->_pitch + tile->_xPos;
|
||||
|
||||
if (!refMb &&
|
||||
((band->_qdeltaPresent && band->_inheritQDelta) || band->_inheritMv))
|
||||
return -1;
|
||||
|
||||
if (tile->_numMBs != IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize)) {
|
||||
warning("Allocated tile size %d mismatches parameters %d",
|
||||
tile->_numMBs, IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// scale factor for motion vectors
|
||||
mvScale = (_ctx._planes[0]._bands[0]._mbSize >> 3) - (band->_mbSize >> 3);
|
||||
mvX = mvY = 0;
|
||||
|
||||
for (y = tile->_yPos; y < (tile->_yPos + tile->_height); y += band->_mbSize) {
|
||||
mbOffset = offs;
|
||||
|
||||
for (x = tile->_xPos; x < (tile->_xPos + tile->_width); x += band->_mbSize) {
|
||||
mb->_xPos = x;
|
||||
mb->_yPos = y;
|
||||
mb->_bufOffs = mbOffset;
|
||||
|
||||
if (_ctx._gb->getBit()) {
|
||||
if (_ctx._frameType == FRAMETYPE_INTRA) {
|
||||
warning("Empty macroblock in an INTRA picture!");
|
||||
return -1;
|
||||
}
|
||||
mb->_type = 1; // empty macroblocks are always INTER
|
||||
mb->_cbp = 0; // all blocks are empty
|
||||
|
||||
mb->_qDelta = 0;
|
||||
if (!band->_plane && !band->_bandNum && (_ctx._frameFlags & 8)) {
|
||||
mb->_qDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mb->_qDelta = IVI_TOSIGNED(mb->_qDelta);
|
||||
}
|
||||
|
||||
mb->_mvX = mb->_mvY = 0; // no motion vector coded
|
||||
if (band->_inheritMv && refMb) {
|
||||
// motion vector inheritance
|
||||
if (mvScale) {
|
||||
mb->_mvX = scaleMV(refMb->_mvX, mvScale);
|
||||
mb->_mvY = scaleMV(refMb->_mvY, mvScale);
|
||||
} else {
|
||||
mb->_mvX = refMb->_mvX;
|
||||
mb->_mvY = refMb->_mvY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (band->_inheritMv && refMb) {
|
||||
mb->_type = refMb->_type; // copy mb_type from corresponding reference mb
|
||||
} else if (_ctx._frameType == FRAMETYPE_INTRA) {
|
||||
mb->_type = 0; // mb_type is always INTRA for intra-frames
|
||||
} else {
|
||||
mb->_type = _ctx._gb->getBit();
|
||||
}
|
||||
|
||||
if (band->_mbSize != band->_blkSize) {
|
||||
mb->_cbp = _ctx._gb->getBits<4>();
|
||||
} else {
|
||||
mb->_cbp = _ctx._gb->getBit();
|
||||
}
|
||||
|
||||
mb->_qDelta = 0;
|
||||
if (band->_qdeltaPresent) {
|
||||
if (band->_inheritQDelta) {
|
||||
if (refMb) mb->_qDelta = refMb->_qDelta;
|
||||
} else if (mb->_cbp || (!band->_plane && !band->_bandNum &&
|
||||
(_ctx._frameFlags & 8))) {
|
||||
mb->_qDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mb->_qDelta = IVI_TOSIGNED(mb->_qDelta);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mb->_type) {
|
||||
mb->_mvX = mb->_mvY = 0; // there is no motion vector in intra-macroblocks
|
||||
} else {
|
||||
if (band->_inheritMv && refMb) {
|
||||
// motion vector inheritance
|
||||
if (mvScale) {
|
||||
mb->_mvX = scaleMV(refMb->_mvX, mvScale);
|
||||
mb->_mvY = scaleMV(refMb->_mvY, mvScale);
|
||||
} else {
|
||||
mb->_mvX = refMb->_mvX;
|
||||
mb->_mvY = refMb->_mvY;
|
||||
}
|
||||
} else {
|
||||
// decode motion vector deltas
|
||||
mvDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mvY += IVI_TOSIGNED(mvDelta);
|
||||
mvDelta = _ctx._gb->getVLC2<1, IVI_VLC_BITS>(_ctx._mbVlc._tab->_table);
|
||||
mvX += IVI_TOSIGNED(mvDelta);
|
||||
mb->_mvX = mvX;
|
||||
mb->_mvY = mvY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = band->_isHalfpel;
|
||||
if (mb->_type)
|
||||
if (x + (mb->_mvX >> s) + (y + (mb->_mvY >> s)) * band->_pitch < 0 ||
|
||||
x + ((mb->_mvX + s) >> s) + band->_mbSize - 1
|
||||
+ (y + band->_mbSize - 1 + ((mb->_mvY + s) >> s)) * band->_pitch > band->_bufSize - 1) {
|
||||
warning("motion vector %d %d outside reference", x*s + mb->_mvX, y * s + mb->_mvY);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mb++;
|
||||
if (refMb)
|
||||
refMb++;
|
||||
mbOffset += band->_mbSize;
|
||||
}
|
||||
|
||||
offs += rowOffset;
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::decode_gop_header() {
|
||||
int result, i, p, tileSize, picSizeIndx, mbSize, blkSize, isScalable;
|
||||
int quantMat;
|
||||
bool blkSizeChanged = false;
|
||||
IVIBandDesc *band, *band1, *band2;
|
||||
IVIPicConfig picConf;
|
||||
|
||||
_ctx._gopFlags = _ctx._gb->getBits<8>();
|
||||
|
||||
_ctx._gopHdrSize = (_ctx._gopFlags & 1) ? _ctx._gb->getBits<16>() : 0;
|
||||
|
||||
if (_ctx._gopFlags & IVI5_IS_PROTECTED)
|
||||
_ctx._lockWord = _ctx._gb->getBits<32>();
|
||||
|
||||
tileSize = (_ctx._gopFlags & 0x40) ? 64 << _ctx._gb->getBits<2>() : 0;
|
||||
if (tileSize > 256) {
|
||||
warning("Invalid tile size: %d", tileSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// decode number of wavelet bands
|
||||
// num_levels * 3 + 1
|
||||
picConf._lumaBands = _ctx._gb->getBits<2>() * 3 + 1;
|
||||
picConf._chromaBands = _ctx._gb->getBit() * 3 + 1;
|
||||
isScalable = picConf._lumaBands != 1 || picConf._chromaBands != 1;
|
||||
if (isScalable && (picConf._lumaBands != 4 || picConf._chromaBands != 1)) {
|
||||
warning("Scalability: unsupported subdivision! Luma bands: %d, chroma bands: %d",
|
||||
picConf._lumaBands, picConf._chromaBands);
|
||||
return -1;
|
||||
}
|
||||
|
||||
picSizeIndx = _ctx._gb->getBits<4>();
|
||||
if (picSizeIndx == IVI5_PIC_SIZE_ESC) {
|
||||
picConf._picHeight = _ctx._gb->getBits<13>();
|
||||
picConf._picWidth = _ctx._gb->getBits<13>();
|
||||
} else {
|
||||
picConf._picHeight = _commonPicSizes[picSizeIndx * 2 + 1] << 2;
|
||||
picConf._picWidth = _commonPicSizes[picSizeIndx * 2] << 2;
|
||||
}
|
||||
|
||||
if (_ctx._gopFlags & 2) {
|
||||
warning("YV12 picture format");
|
||||
return -2;
|
||||
}
|
||||
|
||||
picConf._chromaHeight = (picConf._picHeight + 3) >> 2;
|
||||
picConf._chromaWidth = (picConf._picWidth + 3) >> 2;
|
||||
|
||||
if (!tileSize) {
|
||||
picConf._tileHeight = picConf._picHeight;
|
||||
picConf._tileWidth = picConf._picWidth;
|
||||
} else {
|
||||
picConf._tileHeight = picConf._tileWidth = tileSize;
|
||||
}
|
||||
|
||||
// check if picture layout was changed and reallocate buffers
|
||||
if (picConf.ivi_pic_config_cmp(_ctx._picConf) || _ctx._gopInvalid) {
|
||||
result = IVIPlaneDesc::initPlanes(_ctx._planes, &picConf, 0);
|
||||
if (result < 0) {
|
||||
warning("Couldn't reallocate color planes!");
|
||||
return result;
|
||||
}
|
||||
_ctx._picConf = picConf;
|
||||
_ctx._isScalable = isScalable;
|
||||
blkSizeChanged = 1; // force reallocation of the internal structures
|
||||
}
|
||||
|
||||
for (p = 0; p <= 1; p++) {
|
||||
for (i = 0; i < (!p ? picConf._lumaBands : picConf._chromaBands); i++) {
|
||||
band = &_ctx._planes[p]._bands[i];
|
||||
|
||||
band->_isHalfpel = _ctx._gb->getBit();
|
||||
|
||||
mbSize = _ctx._gb->getBit();
|
||||
blkSize = 8 >> _ctx._gb->getBit();
|
||||
mbSize = blkSize << (!mbSize ? 1 : 0);
|
||||
|
||||
if (p == 0 && blkSize == 4) {
|
||||
warning("4x4 luma blocks are unsupported!");
|
||||
return -2;
|
||||
}
|
||||
|
||||
blkSizeChanged = mbSize != band->_mbSize || blkSize != band->_blkSize;
|
||||
if (blkSizeChanged) {
|
||||
band->_mbSize = mbSize;
|
||||
band->_blkSize = blkSize;
|
||||
}
|
||||
|
||||
if (_ctx._gb->getBit()) {
|
||||
warning("Extended transform info");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// select transform function and scan pattern according to plane and band number
|
||||
switch ((p << 2) + i) {
|
||||
case 0:
|
||||
band->_invTransform = IndeoDSP::ffIviInverseSlant8x8;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcSlant2d;
|
||||
band->_scan = ffZigZagDirect;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
band->_invTransform = IndeoDSP::ffIviRowSlant8;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcRowSlant;
|
||||
band->_scan = _ffIviVerticalScan8x8;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
band->_invTransform = IndeoDSP::ffIviColSlant8;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcColSlant;
|
||||
band->_scan = _ffIviHorizontalScan8x8;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
band->_invTransform = IndeoDSP::ffIviPutPixels8x8;
|
||||
band->_dcTransform = IndeoDSP::ffIviPutDcPixel8x8;
|
||||
band->_scan = _ffIviHorizontalScan8x8;
|
||||
band->_transformSize = 8;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
band->_invTransform = IndeoDSP::ffIviInverseSlant4x4;
|
||||
band->_dcTransform = IndeoDSP::ffIviDcSlant2d;
|
||||
band->_scan = _ffIviDirectScan4x4;
|
||||
band->_transformSize = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
band->_is2dTrans = band->_invTransform == IndeoDSP::ffIviInverseSlant8x8 ||
|
||||
band->_invTransform == IndeoDSP::ffIviInverseSlant4x4;
|
||||
|
||||
if (band->_transformSize != band->_blkSize) {
|
||||
warning("transform and block size mismatch (%d != %d)", band->_transformSize, band->_blkSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// select dequant matrix according to plane and band number
|
||||
if (!p) {
|
||||
quantMat = (picConf._lumaBands > 1) ? i + 1 : 0;
|
||||
} else {
|
||||
quantMat = 5;
|
||||
}
|
||||
|
||||
if (band->_blkSize == 8) {
|
||||
if (quantMat >= 5) {
|
||||
warning("_quantMat %d too large!", quantMat);
|
||||
return -1;
|
||||
}
|
||||
band->_intraBase = &_baseQuant8x8Intra[quantMat][0];
|
||||
band->_interBase = &_baseQuant8x8Inter[quantMat][0];
|
||||
band->_intraScale = &_scaleQuant8x8Intra[quantMat][0];
|
||||
band->_interScale = &_scaleQuant8x8Inter[quantMat][0];
|
||||
} else {
|
||||
band->_intraBase = _baseQuant4x4Intra;
|
||||
band->_interBase = _baseQuant4x4Inter;
|
||||
band->_intraScale = _scaleQuant4x4Intra;
|
||||
band->_interScale = _scaleQuant4x4Inter;
|
||||
}
|
||||
|
||||
if (_ctx._gb->getBits<2>()) {
|
||||
warning("End marker missing!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy chroma parameters into the 2nd chroma plane
|
||||
for (i = 0; i < picConf._chromaBands; i++) {
|
||||
band1 = &_ctx._planes[1]._bands[i];
|
||||
band2 = &_ctx._planes[2]._bands[i];
|
||||
|
||||
band2->_width = band1->_width;
|
||||
band2->_height = band1->_height;
|
||||
band2->_mbSize = band1->_mbSize;
|
||||
band2->_blkSize = band1->_blkSize;
|
||||
band2->_isHalfpel = band1->_isHalfpel;
|
||||
band2->_intraBase = band1->_intraBase;
|
||||
band2->_interBase = band1->_interBase;
|
||||
band2->_intraScale = band1->_intraScale;
|
||||
band2->_interScale = band1->_interScale;
|
||||
band2->_scan = band1->_scan;
|
||||
band2->_invTransform = band1->_invTransform;
|
||||
band2->_dcTransform = band1->_dcTransform;
|
||||
band2->_is2dTrans = band1->_is2dTrans;
|
||||
band2->_transformSize = band1->_transformSize;
|
||||
}
|
||||
|
||||
// reallocate internal structures if needed
|
||||
if (blkSizeChanged) {
|
||||
result = IVIPlaneDesc::initTiles(_ctx._planes, picConf._tileWidth,
|
||||
picConf._tileHeight);
|
||||
if (result < 0) {
|
||||
warning("Couldn't reallocate internal structures!");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (_ctx._gopFlags & 8) {
|
||||
if (_ctx._gb->getBits<3>()) {
|
||||
warning("Alignment bits are not zero!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_ctx._gb->getBit())
|
||||
_ctx._gb->skip(24); // skip transparency fill color
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
_ctx._gb->skip(23); // FIXME: unknown meaning
|
||||
|
||||
// skip GOP extension if any
|
||||
if (_ctx._gb->getBit()) {
|
||||
do {
|
||||
i = _ctx._gb->getBits<16>();
|
||||
} while (i & 0x8000);
|
||||
}
|
||||
|
||||
_ctx._gb->align();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Indeo5Decoder::skip_hdr_extension() {
|
||||
int i, len;
|
||||
|
||||
do {
|
||||
len = _ctx._gb->getBits<8>();
|
||||
if (_ctx._gb->eos())
|
||||
return -1;
|
||||
for (i = 0; i < len; i++)
|
||||
_ctx._gb->skip(8);
|
||||
} while (len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
const uint8 Indeo5Decoder::_commonPicSizes[30] = {
|
||||
160, 120, 80, 60, 40, 30, 176, 120, 88, 60, 88, 72, 44, 36, 60, 45, 160, 60,
|
||||
176, 60, 20, 15, 22, 18, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant8x8Inter[5][64] = {
|
||||
{0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
|
||||
0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
|
||||
0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
|
||||
0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
|
||||
},
|
||||
{0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
|
||||
0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
|
||||
0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
|
||||
0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
|
||||
},
|
||||
{0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
},
|
||||
{0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
|
||||
0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
},
|
||||
{0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
}
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant8x8Intra[5][64] = {
|
||||
{0x1a, 0x2e, 0x36, 0x42, 0x46, 0x4a, 0x4e, 0x5a, 0x2e, 0x32, 0x3e, 0x42, 0x46, 0x4e, 0x56, 0x6a,
|
||||
0x36, 0x3e, 0x3e, 0x44, 0x4a, 0x54, 0x66, 0x72, 0x42, 0x42, 0x44, 0x4a, 0x52, 0x62, 0x6c, 0x7a,
|
||||
0x46, 0x46, 0x4a, 0x52, 0x5e, 0x66, 0x72, 0x8e, 0x4a, 0x4e, 0x54, 0x62, 0x66, 0x6e, 0x86, 0xa6,
|
||||
0x4e, 0x56, 0x66, 0x6c, 0x72, 0x86, 0x9a, 0xca, 0x5a, 0x6a, 0x72, 0x7a, 0x8e, 0xa6, 0xca, 0xfe,
|
||||
},
|
||||
{0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
|
||||
0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
|
||||
0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
|
||||
0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
|
||||
},
|
||||
{0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
|
||||
},
|
||||
{0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
|
||||
0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
|
||||
},
|
||||
{0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
|
||||
}
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant4x4Inter[16] = {
|
||||
0x1e, 0x3e, 0x4a, 0x52, 0x3e, 0x4a, 0x52, 0x56, 0x4a, 0x52, 0x56, 0x5e, 0x52, 0x56, 0x5e, 0x66
|
||||
};
|
||||
|
||||
const uint16 Indeo5Decoder::_baseQuant4x4Intra[16] = {
|
||||
0x1e, 0x3e, 0x4a, 0x52, 0x3e, 0x4a, 0x52, 0x5e, 0x4a, 0x52, 0x5e, 0x7a, 0x52, 0x5e, 0x7a, 0x92
|
||||
};
|
||||
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant8x8Inter[5][24] = {
|
||||
{0x0b, 0x11, 0x13, 0x14, 0x15, 0x16, 0x18, 0x1a, 0x1b, 0x1d, 0x20, 0x22,
|
||||
0x23, 0x25, 0x28, 0x2a, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x41, 0x44, 0x4a,
|
||||
},
|
||||
{0x07, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35,
|
||||
0x3a, 0x3f, 0x44, 0x4a, 0x50, 0x56, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x7e,
|
||||
},
|
||||
{0x15, 0x25, 0x28, 0x2d, 0x30, 0x34, 0x3a, 0x3d, 0x42, 0x48, 0x4c, 0x51,
|
||||
0x56, 0x5b, 0x60, 0x65, 0x6b, 0x70, 0x76, 0x7c, 0x82, 0x88, 0x8f, 0x97,
|
||||
},
|
||||
{0x13, 0x1f, 0x20, 0x22, 0x25, 0x28, 0x2b, 0x2d, 0x30, 0x33, 0x36, 0x39,
|
||||
0x3c, 0x3f, 0x42, 0x45, 0x48, 0x4b, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x62,
|
||||
},
|
||||
{0x3c, 0x52, 0x58, 0x5d, 0x63, 0x68, 0x68, 0x6d, 0x73, 0x78, 0x7c, 0x80,
|
||||
0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa3, 0xa9, 0xad, 0xb1, 0xb5, 0xba,
|
||||
},
|
||||
};
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant8x8Intra[5][24] = {
|
||||
{0x0b, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20,
|
||||
0x22, 0x24, 0x27, 0x28, 0x2a, 0x2d, 0x2f, 0x31, 0x34, 0x37, 0x39, 0x3c,
|
||||
},
|
||||
{0x01, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x28, 0x2c,
|
||||
0x30, 0x34, 0x38, 0x3d, 0x42, 0x47, 0x4c, 0x52, 0x58, 0x5e, 0x65, 0x6c,
|
||||
},
|
||||
{0x13, 0x22, 0x27, 0x2a, 0x2d, 0x33, 0x36, 0x3c, 0x41, 0x45, 0x49, 0x4e,
|
||||
0x53, 0x58, 0x5d, 0x63, 0x69, 0x6f, 0x75, 0x7c, 0x82, 0x88, 0x8e, 0x95,
|
||||
},
|
||||
{0x13, 0x1f, 0x21, 0x24, 0x27, 0x29, 0x2d, 0x2f, 0x34, 0x37, 0x3a, 0x3d,
|
||||
0x40, 0x44, 0x48, 0x4c, 0x4f, 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6b,
|
||||
},
|
||||
{0x31, 0x42, 0x47, 0x47, 0x4d, 0x52, 0x58, 0x58, 0x5d, 0x63, 0x67, 0x6b,
|
||||
0x6f, 0x73, 0x78, 0x7c, 0x80, 0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa4,
|
||||
}
|
||||
};
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant4x4Inter[24] = {
|
||||
0x0b, 0x0d, 0x0d, 0x0e, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
};
|
||||
|
||||
const uint8 Indeo5Decoder::_scaleQuant4x4Intra[24] = {
|
||||
0x01, 0x0b, 0x0b, 0x0d, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x13, 0x14,
|
||||
0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
135
image/codecs/indeo5.h
Normal file
135
image/codecs/indeo5.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Intel Indeo 4 decompressor, derived from ffmpeg.
|
||||
*
|
||||
* Original copyright note:
|
||||
* Intel Indeo 4 (IV31, IV32, etc.) video decoder for ffmpeg
|
||||
* written, produced, and directed by Alan Smithee
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_INDEO5_H
|
||||
#define IMAGE_CODECS_INDEO5_H
|
||||
|
||||
#include "image/codecs/indeo/get_bits.h"
|
||||
#include "image/codecs/indeo/indeo.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
using namespace Indeo;
|
||||
|
||||
/**
|
||||
* Intel Indeo 5 decoder.
|
||||
*
|
||||
* Used by AVI.
|
||||
*
|
||||
* Used in video:
|
||||
* - AVIDecoder
|
||||
*/
|
||||
class Indeo5Decoder : public IndeoDecoderBase {
|
||||
struct Transform {
|
||||
InvTransformPtr *inv_trans;
|
||||
DCTransformPtr *dc_trans;
|
||||
int is_2d_trans;
|
||||
};
|
||||
public:
|
||||
Indeo5Decoder(uint16 width, uint16 height, uint bitsPerPixel = 16);
|
||||
~Indeo5Decoder() override {}
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
static bool isIndeo5(Common::SeekableReadStream &stream);
|
||||
protected:
|
||||
/**
|
||||
* Decode the Indeo 5 picture header.
|
||||
* @returns 0 = Ok, negative number = error
|
||||
*/
|
||||
int decodePictureHeader() override;
|
||||
|
||||
/**
|
||||
* Rearrange decoding and reference buffers.
|
||||
*/
|
||||
void switchBuffers() override;
|
||||
|
||||
bool isNonNullFrame() const override;
|
||||
|
||||
/**
|
||||
* Decode Indeo 4 band header.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @return result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeBandHeader(IVIBandDesc *band) override;
|
||||
|
||||
/**
|
||||
* Decode information (block type, cbp, quant delta, motion vector)
|
||||
* for all macroblocks in the current tile.
|
||||
*
|
||||
* @param[in,out] band pointer to the band descriptor
|
||||
* @param[in,out] tile pointer to the tile descriptor
|
||||
* @return result code: 0 = OK, negative number = error
|
||||
*/
|
||||
int decodeMbInfo(IVIBandDesc *band, IVITile *tile) override;
|
||||
private:
|
||||
/**
|
||||
* Decode Indeo5 GOP (Group of pictures) header.
|
||||
* This header is present in key frames only.
|
||||
* It defines parameters for all frames in a GOP.
|
||||
* @returns result code: 0 = OK, -1 = error
|
||||
*/
|
||||
int decode_gop_header();
|
||||
|
||||
/**
|
||||
* Skip a header extension.
|
||||
*/
|
||||
int skip_hdr_extension();
|
||||
|
||||
private:
|
||||
/**
|
||||
* standard picture dimensions (width, height divided by 4)
|
||||
*/
|
||||
static const uint8 _commonPicSizes[30];
|
||||
|
||||
/**
|
||||
* Indeo5 dequantization matrixes consist of two tables: base table
|
||||
* and scale table. The base table defines the dequantization matrix
|
||||
* itself and the scale table tells how this matrix should be scaled
|
||||
* for a particular quant level (0...24).
|
||||
*
|
||||
* ivi5_base_quant_bbb_ttt - base tables for block size 'bbb' of type 'ttt'
|
||||
* ivi5_scale_quant_bbb_ttt - scale tables for block size 'bbb' of type 'ttt'
|
||||
*/
|
||||
static const uint16 _baseQuant8x8Inter[5][64];
|
||||
static const uint16 _baseQuant8x8Intra[5][64];
|
||||
|
||||
static const uint16 _baseQuant4x4Inter[16];
|
||||
static const uint16 _baseQuant4x4Intra[16];
|
||||
|
||||
static const uint8 _scaleQuant8x8Inter[5][24];
|
||||
static const uint8 _scaleQuant8x8Intra[5][24];
|
||||
|
||||
static const uint8 _scaleQuant4x4Inter[24];
|
||||
static const uint8 _scaleQuant4x4Intra[24];
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
172
image/codecs/jyv1.cpp
Normal file
172
image/codecs/jyv1.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "image/codecs/jyv1.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/bitstream.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#define ID_JYV1 MKTAG('J','Y','V','1')
|
||||
#define ID_RRV1 MKTAG('R','R','V','1')
|
||||
#define ID_RRV2 MKTAG('R','R','V','2')
|
||||
|
||||
namespace Image {
|
||||
|
||||
/*static*/
|
||||
bool JYV1Decoder::isJYV1StreamTag(uint32 streamTag) {
|
||||
return (streamTag == ID_JYV1 || streamTag == ID_RRV1 || streamTag == ID_RRV2);
|
||||
}
|
||||
|
||||
JYV1Decoder::JYV1Decoder(int width, int height, uint32 streamTag) : Codec(),
|
||||
_width(width), _height(height), _streamType(streamTag) {
|
||||
assert(isJYV1StreamTag(streamTag));
|
||||
_surface.create(_width, _height, getPixelFormat());
|
||||
}
|
||||
|
||||
JYV1Decoder::~JYV1Decoder() {
|
||||
_surface.free();
|
||||
}
|
||||
|
||||
static const uint32 BASE_LEN[] = {0, 1 << 7, 1 << 3, 0, 1 << 1, 0, 1 << 5, 0,
|
||||
1, 1 << 8, 1 << 4, 0, 1 << 2, 0, 1 << 6, 0};
|
||||
static const uint32 FINE_LEN_BITS[] = {0, 7, 3, 0, 1, 16, 5, 0,
|
||||
1, 8, 4, 0, 2, 24, 6, 0};
|
||||
|
||||
/**
|
||||
* Details of this decoding algorithm are here:
|
||||
* https://wiki.multimedia.cx/index.php/Origin_Flic_Codec
|
||||
*/
|
||||
const Graphics::Surface *JYV1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
|
||||
byte *dst = (byte *)_surface.getPixels();
|
||||
|
||||
uint32 offsets[16]; // RRV2 has 15 block offsets, others have 5
|
||||
const int numOffsets = (_streamType == ID_RRV2 ? 15 : 5);
|
||||
const int blockHeight = _height / numOffsets;
|
||||
const int startOffset = stream.pos();
|
||||
|
||||
// Read in the block offsets and convert to stream offsets
|
||||
for (int i = 0; i < numOffsets; i++) {
|
||||
offsets[i] = stream.readUint32LE() + startOffset;
|
||||
}
|
||||
|
||||
bool upscale = false;
|
||||
|
||||
//
|
||||
// Slight HACK: test if we need to scale up this frame without
|
||||
// changing the output data yet. This has a bit of duplicated code
|
||||
// with the loop below just to measure the frame size from the
|
||||
// first block.
|
||||
//
|
||||
if (_streamType == ID_RRV1 || _streamType == ID_RRV2) {
|
||||
stream.seek(offsets[0], SEEK_SET);
|
||||
const int cmdLen = stream.readUint32LE();
|
||||
uint8 *cmdData = new uint8[cmdLen];
|
||||
stream.read(cmdData, cmdLen);
|
||||
Common::BitStreamMemoryStream cmdMemStream(cmdData, cmdLen);
|
||||
Common::BitStreamMemory8MSB cmdBitStream(cmdMemStream);
|
||||
int total = 0;
|
||||
while (!cmdBitStream.eos()) {
|
||||
uint32 idx = cmdBitStream.getBits<4>();
|
||||
total += BASE_LEN[idx];
|
||||
if (idx != 0 && idx != 8) {
|
||||
total += cmdBitStream.getBits(FINE_LEN_BITS[idx]);
|
||||
}
|
||||
}
|
||||
delete [] cmdData;
|
||||
|
||||
if (total == _width * blockHeight / 2)
|
||||
upscale = true;
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
int x = 0;
|
||||
|
||||
for (int i = 0; i < numOffsets && y < _height; i++) {
|
||||
stream.seek(offsets[i], SEEK_SET);
|
||||
const int cmdLen = stream.readUint32LE();
|
||||
|
||||
// TODO: can probably avoid this copy to make it faster
|
||||
uint8 *cmdData = new uint8[cmdLen];
|
||||
stream.read(cmdData, cmdLen);
|
||||
Common::BitStreamMemoryStream cmdMemStream(cmdData, cmdLen);
|
||||
Common::BitStreamMemory8MSB cmdBitStream(cmdMemStream);
|
||||
bool skipping = true;
|
||||
while (cmdBitStream.size() - cmdBitStream.pos() >= 4 && y < _height) {
|
||||
uint32 idx = cmdBitStream.getBits<4>();
|
||||
uint32 blocksize = BASE_LEN[idx];
|
||||
if (idx != 0 && idx != 8) {
|
||||
blocksize += cmdBitStream.getBits(FINE_LEN_BITS[idx]);
|
||||
}
|
||||
if (skipping) {
|
||||
// leave blocksize pixels unchanged
|
||||
if (upscale)
|
||||
blocksize *= 2;
|
||||
|
||||
while (blocksize) {
|
||||
blocksize--;
|
||||
x++;
|
||||
if (x == _width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// draw blocksize pixels from data block
|
||||
while (blocksize && y < _height) {
|
||||
// TODO: would be nicer to read these in whole scanlines.
|
||||
// Also this upscale code is kinda ugly.
|
||||
const uint8 p = stream.readByte();
|
||||
dst[y * _width + x] = p;
|
||||
x++;
|
||||
if (x == _width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
if (upscale) {
|
||||
dst[y * _width + x] = p;
|
||||
x++;
|
||||
if (x == _width) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
blocksize--;
|
||||
}
|
||||
}
|
||||
skipping = !skipping;
|
||||
}
|
||||
delete [] cmdData;
|
||||
}
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat JYV1Decoder::getPixelFormat() const {
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
52
image/codecs/jyv1.h
Normal file
52
image/codecs/jyv1.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_JYV1_H
|
||||
#define IMAGE_CODECS_JYV1_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* JYV1/RRV1/RRV2 image decoder.
|
||||
*
|
||||
* Used by Crusader: No Remorse AVI files
|
||||
*/
|
||||
class JYV1Decoder : public Codec {
|
||||
public:
|
||||
JYV1Decoder (int width, int height, uint32 streamTag);
|
||||
~JYV1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
static bool isJYV1StreamTag(uint32 streamTag);
|
||||
|
||||
private:
|
||||
Graphics::Surface _surface;
|
||||
int _width, _height;
|
||||
uint32 _streamType;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
229
image/codecs/mjpeg.cpp
Normal file
229
image/codecs/mjpeg.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on LGPL MJPEG/AVI to JPEG/JFIF conversion code from libav
|
||||
// Copyright (c) 2010 Adrian Daerr and Nicolas George
|
||||
// That in turn was adapted from mjpeg2jpeg.c, with original copyright:
|
||||
// Paris 2010 Adrian Daerr, public domain
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/jpeg.h"
|
||||
|
||||
#include "image/codecs/mjpeg.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
MJPEGDecoder::MJPEGDecoder() : Codec() {
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
|
||||
_surface = 0;
|
||||
_accuracy = CodecAccuracy::Default;
|
||||
}
|
||||
|
||||
MJPEGDecoder::~MJPEGDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
// Header to be inserted
|
||||
static const byte s_jpegHeader[] = {
|
||||
0xff, 0xd8, // SOI
|
||||
0xff, 0xe0, // APP0
|
||||
0x00, 0x10, // APP0 header size (including
|
||||
// this field, but excluding preceding)
|
||||
'J', 'F', 'I', 'F', 0x00, // ID string 'JFIF\0'
|
||||
0x01, 0x01, // version
|
||||
0x00, // bits per type
|
||||
0x00, 0x00, // X density
|
||||
0x00, 0x00, // Y density
|
||||
0x00, // X thumbnail size
|
||||
0x00
|
||||
};
|
||||
|
||||
enum {
|
||||
DHT_SEGMENT_SIZE = 420
|
||||
};
|
||||
|
||||
static const byte s_dhtSegmentHead[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00 };
|
||||
static const byte s_dhtSegmentFrag[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// Set up the standard Huffman tables (cf. JPEG standard section K.3)
|
||||
// IMPORTANT: these are only valid for 8-bit data precision!
|
||||
static const byte s_mjpegBitsDCLuminance[17] = {
|
||||
/* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static const byte s_mjpegValDC[12] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
||||
};
|
||||
|
||||
#if 0
|
||||
static const byte s_mjpegBitsDCChrominance[17] = {
|
||||
/* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
|
||||
};
|
||||
#endif
|
||||
|
||||
static const byte s_mjpegBitsACLuminance[17] = {
|
||||
/* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
|
||||
};
|
||||
|
||||
static const byte s_mjpegValACLuminance[] = {
|
||||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa
|
||||
};
|
||||
|
||||
static const byte s_mjpegBitsACChrominance[17] = {
|
||||
/* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
|
||||
};
|
||||
|
||||
static const byte s_mjpegValACChrominance[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa
|
||||
};
|
||||
|
||||
const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
// We need to reconstruct an actual JPEG stream here, then feed it to the JPEG decoder
|
||||
// Yes, this is a pain.
|
||||
|
||||
stream.readUint32BE(); // Skip nonsense JPEG header
|
||||
uint16 inputSkip = stream.readUint16BE() + 4;
|
||||
uint32 tag = stream.readUint32BE();
|
||||
|
||||
if (tag != MKTAG('A', 'V', 'I', '1')) {
|
||||
warning("Invalid MJPEG tag found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 outputSize = stream.size() - inputSkip + sizeof(s_jpegHeader) + DHT_SEGMENT_SIZE;
|
||||
byte *data = (byte *)malloc(outputSize);
|
||||
|
||||
if (!data) {
|
||||
warning("Failed to allocate data for MJPEG conversion");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the header
|
||||
memcpy(data, s_jpegHeader, sizeof(s_jpegHeader));
|
||||
uint32 dataOffset = sizeof(s_jpegHeader);
|
||||
|
||||
// Write the fake DHT segment
|
||||
memcpy(data + dataOffset, s_dhtSegmentHead, sizeof(s_dhtSegmentHead));
|
||||
dataOffset += sizeof(s_dhtSegmentHead);
|
||||
memcpy(data + dataOffset, s_mjpegBitsDCLuminance + 1, 16);
|
||||
dataOffset += 16;
|
||||
memcpy(data + dataOffset, s_dhtSegmentFrag, sizeof(s_dhtSegmentFrag));
|
||||
dataOffset += sizeof(s_dhtSegmentFrag);
|
||||
memcpy(data + dataOffset, s_mjpegValDC, 12);
|
||||
dataOffset += 12;
|
||||
data[dataOffset++] = 0x10;
|
||||
memcpy(data + dataOffset, s_mjpegBitsACLuminance + 1, 16);
|
||||
dataOffset += 16;
|
||||
memcpy(data + dataOffset, s_mjpegValACLuminance, 162);
|
||||
dataOffset += 162;
|
||||
data[dataOffset++] = 0x11;
|
||||
memcpy(data + dataOffset, s_mjpegBitsACChrominance + 1, 16);
|
||||
dataOffset += 16;
|
||||
memcpy(data + dataOffset, s_mjpegValACChrominance, 162);
|
||||
dataOffset += 162;
|
||||
|
||||
// Write the actual data
|
||||
stream.seek(inputSkip);
|
||||
stream.read(data + dataOffset, stream.size() - inputSkip);
|
||||
|
||||
Common::MemoryReadStream convertedStream(data, outputSize, DisposeAfterUse::YES);
|
||||
JPEGDecoder jpeg;
|
||||
jpeg.setCodecAccuracy(_accuracy);
|
||||
jpeg.setOutputPixelFormat(_pixelFormat);
|
||||
|
||||
if (!jpeg.loadStream(convertedStream)) {
|
||||
warning("Failed to decode MJPEG frame");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->copyFrom(*jpeg.getSurface());
|
||||
|
||||
assert(_surface->format == _pixelFormat);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void MJPEGDecoder::setCodecAccuracy(CodecAccuracy accuracy) {
|
||||
_accuracy = accuracy;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
66
image/codecs/mjpeg.h
Normal file
66
image/codecs/mjpeg.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MJPEG_H
|
||||
#define IMAGE_CODECS_MJPEG_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Motion JPEG decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MJPEGDecoder : public Codec {
|
||||
public:
|
||||
MJPEGDecoder();
|
||||
~MJPEGDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
void setCodecAccuracy(CodecAccuracy accuracy) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.isCLUT8())
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
CodecAccuracy _accuracy;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
117
image/codecs/mpeg.cpp
Normal file
117
image/codecs/mpeg.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
|
||||
#include "image/codecs/mpeg.h"
|
||||
|
||||
extern "C" {
|
||||
#include <mpeg2dec/mpeg2.h>
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
MPEGDecoder::MPEGDecoder() : Codec() {
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
|
||||
_surface = 0;
|
||||
|
||||
_mpegDecoder = mpeg2_init();
|
||||
|
||||
if (!_mpegDecoder)
|
||||
error("Could not initialize libmpeg2");
|
||||
|
||||
_mpegInfo = mpeg2_info(_mpegDecoder);
|
||||
}
|
||||
|
||||
MPEGDecoder::~MPEGDecoder() {
|
||||
mpeg2_close(_mpegDecoder);
|
||||
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *MPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
uint32 framePeriod;
|
||||
decodePacket(stream, framePeriod);
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool MPEGDecoder::decodePacket(Common::SeekableReadStream &packet, uint32 &framePeriod, Graphics::Surface *dst) {
|
||||
// Decode as much as we can out of this packet
|
||||
uint32 size = 0xFFFFFFFF;
|
||||
mpeg2_state_t state;
|
||||
bool foundFrame = false;
|
||||
framePeriod = 0;
|
||||
|
||||
do {
|
||||
state = mpeg2_parse(_mpegDecoder);
|
||||
|
||||
switch (state) {
|
||||
case STATE_BUFFER:
|
||||
size = packet.read(_buffer, BUFFER_SIZE);
|
||||
mpeg2_buffer(_mpegDecoder, _buffer, _buffer + size);
|
||||
break;
|
||||
case STATE_SLICE:
|
||||
case STATE_END:
|
||||
if (_mpegInfo->display_fbuf) {
|
||||
foundFrame = true;
|
||||
const mpeg2_sequence_t *sequence = _mpegInfo->sequence;
|
||||
const mpeg2_picture_t *picture = _mpegInfo->display_picture;
|
||||
|
||||
framePeriod += sequence->frame_period;
|
||||
if (picture->nb_fields > 2) {
|
||||
framePeriod += (sequence->frame_period / 2);
|
||||
|
||||
}
|
||||
|
||||
if (!dst) {
|
||||
// If no destination is specified, use our internal storage
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(sequence->picture_width, sequence->picture_height, _pixelFormat);
|
||||
}
|
||||
|
||||
dst = _surface;
|
||||
}
|
||||
|
||||
YUVToRGBMan.convert420(dst, Graphics::YUVToRGBManager::kScaleITU, _mpegInfo->display_fbuf->buf[0],
|
||||
_mpegInfo->display_fbuf->buf[1], _mpegInfo->display_fbuf->buf[2], sequence->picture_width,
|
||||
sequence->picture_height, sequence->width, sequence->chroma_width);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (size != 0);
|
||||
|
||||
return foundFrame;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
79
image/codecs/mpeg.h
Normal file
79
image/codecs/mpeg.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MPEG_H
|
||||
#define IMAGE_CODECS_MPEG_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
typedef struct mpeg2dec_s mpeg2dec_t;
|
||||
typedef struct mpeg2_info_s mpeg2_info_t;
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* MPEG 1/2 video decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MPEGDecoder : public Codec {
|
||||
public:
|
||||
MPEGDecoder();
|
||||
~MPEGDecoder() override;
|
||||
|
||||
// Codec interface
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
// MPEGPSDecoder call
|
||||
bool decodePacket(Common::SeekableReadStream &packet, uint32 &framePeriod, Graphics::Surface *dst = 0);
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
enum {
|
||||
BUFFER_SIZE = 4096
|
||||
};
|
||||
|
||||
byte _buffer[BUFFER_SIZE];
|
||||
mpeg2dec_t *_mpegDecoder;
|
||||
const mpeg2_info_t *_mpegInfo;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif // IMAGE_CODECS_MPEG_H
|
||||
138
image/codecs/msrle.cpp
Normal file
138
image/codecs/msrle.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's msrledec.c
|
||||
|
||||
#include "image/codecs/msrle.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
MSRLEDecoder::MSRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
}
|
||||
|
||||
MSRLEDecoder::~MSRLEDecoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
const Graphics::Surface *MSRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (_bitsPerPixel == 8) {
|
||||
decode8(stream);
|
||||
} else
|
||||
error("Unhandled %d bit Microsoft RLE encoding", _bitsPerPixel);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void MSRLEDecoder::decode8(Common::SeekableReadStream &stream) {
|
||||
|
||||
int x = 0;
|
||||
int y = _surface->h - 1;
|
||||
|
||||
byte *data = (byte *) _surface->getPixels();
|
||||
uint16 width = _surface->w;
|
||||
uint16 height = _surface->h;
|
||||
uint16 pitch = _surface->pitch;
|
||||
|
||||
byte *output = data + ((height - 1) * pitch);
|
||||
byte *output_end = data + ((height) * pitch) - (pitch - width);
|
||||
|
||||
while (!stream.eos()) {
|
||||
byte count = stream.readByte();
|
||||
byte value = stream.readByte();
|
||||
|
||||
if (count == 0) {
|
||||
if (value == 0) {
|
||||
// End of line
|
||||
|
||||
x = 0;
|
||||
y--;
|
||||
output = data + (y * pitch);
|
||||
|
||||
} else if (value == 1) {
|
||||
// End of image
|
||||
return;
|
||||
|
||||
} else if (value == 2) {
|
||||
// Skip
|
||||
|
||||
count = stream.readByte();
|
||||
value = stream.readByte();
|
||||
|
||||
y -= value;
|
||||
x += count;
|
||||
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Skip beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
output = data + ((y * pitch) + x);
|
||||
|
||||
} else {
|
||||
// Copy data
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Copy data is beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (output + value > output_end) {
|
||||
if (stream.pos() + value >= stream.size())
|
||||
break;
|
||||
else
|
||||
stream.skip(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < value; i++)
|
||||
*output++ = stream.readByte();
|
||||
|
||||
if (value & 1)
|
||||
stream.skip(1);
|
||||
|
||||
x += value;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Run data
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Run data is beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (output + count > output_end)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < count; i++, x++)
|
||||
*output++ = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
warning("MS RLE Codec: No end-of-picture code");
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
52
image/codecs/msrle.h
Normal file
52
image/codecs/msrle.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MSRLE_H
|
||||
#define IMAGE_CODECS_MSRLE_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Microsoft Run-Length Encoding decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MSRLEDecoder : public Codec {
|
||||
public:
|
||||
MSRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~MSRLEDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void decode8(Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
139
image/codecs/msrle4.cpp
Normal file
139
image/codecs/msrle4.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's msrledec.c
|
||||
#include "common/debug.h"
|
||||
#include "image/codecs/msrle4.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
namespace Image {
|
||||
|
||||
MSRLE4Decoder::MSRLE4Decoder(uint16 width, uint16 height, byte bitsPerPixel) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
}
|
||||
|
||||
MSRLE4Decoder::~MSRLE4Decoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
const Graphics::Surface *MSRLE4Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (_bitsPerPixel == 4) {
|
||||
decode4(stream);
|
||||
} else
|
||||
error("Unhandled %d bit Microsoft RLE encoding", _bitsPerPixel);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void MSRLE4Decoder::decode4(Common::SeekableReadStream &stream) {
|
||||
int x = 0;
|
||||
int y = _surface->h - 1;
|
||||
|
||||
byte *output = (byte *)_surface->getBasePtr(x, y);
|
||||
byte *output_end = (byte *)_surface->getBasePtr(_surface->w, y);
|
||||
|
||||
while (!stream.eos()) {
|
||||
byte count = stream.readByte();
|
||||
|
||||
if (count == 0) {
|
||||
byte value = stream.readByte();
|
||||
|
||||
if (value == 0) {
|
||||
// End of line
|
||||
|
||||
x = 0;
|
||||
y--;
|
||||
output = (byte *)_surface->getBasePtr(x, y);
|
||||
} else if (value == 1) {
|
||||
// End of image
|
||||
|
||||
return;
|
||||
} else if (value == 2) {
|
||||
// Skip
|
||||
|
||||
count = stream.readByte();
|
||||
value = stream.readByte();
|
||||
|
||||
x += count;
|
||||
y -= value;
|
||||
|
||||
if (y < 0) {
|
||||
warning("MS RLE Codec: Skip beyond picture bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
output = (byte *)_surface->getBasePtr(x, y);
|
||||
|
||||
} else {
|
||||
// Copy data
|
||||
|
||||
int odd_pixel = value & 1;
|
||||
int rle_code = (value + 1) / 2;
|
||||
int extra_byte = rle_code & 0x01;
|
||||
|
||||
if (output + value > output_end) {
|
||||
stream.skip(rle_code + extra_byte);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rle_code; i++) {
|
||||
byte color = stream.readByte();
|
||||
*output++ = (color & 0xf0) >> 4;
|
||||
if (i + 1 == rle_code && odd_pixel) {
|
||||
break;
|
||||
}
|
||||
*output++ = color & 0x0f;
|
||||
}
|
||||
|
||||
if (extra_byte)
|
||||
stream.skip(1);
|
||||
|
||||
x += value;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Run data
|
||||
|
||||
if (output + count > output_end)
|
||||
continue;
|
||||
|
||||
byte color = stream.readByte();
|
||||
|
||||
for (int i = 0; i < count; i++, x++) {
|
||||
*output++ = (color & 0xf0) >> 4;
|
||||
i++;
|
||||
x++;
|
||||
if (i == count)
|
||||
break;
|
||||
*output++ = color & 0x0f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
warning("MS RLE Codec: No end-of-picture code");
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
52
image/codecs/msrle4.h
Normal file
52
image/codecs/msrle4.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MSRLE4_H
|
||||
#define IMAGE_CODECS_MSRLE4_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Microsoft Run-Length Encoding decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MSRLE4Decoder : public Codec {
|
||||
public:
|
||||
MSRLE4Decoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~MSRLE4Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void decode4(Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
224
image/codecs/msvideo1.cpp
Normal file
224
image/codecs/msvideo1.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's msvideo.cpp
|
||||
|
||||
#include "image/codecs/msvideo1.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
#define CHECK_STREAM_PTR(n) \
|
||||
if ((stream.pos() + n) > stream.size() ) { \
|
||||
warning ("MS Video-1: Stream out of bounds (%d >= %d) d%d", (int)stream.pos() + n, (int)stream.size(), n); \
|
||||
return; \
|
||||
}
|
||||
|
||||
MSVideo1Decoder::MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, (bitsPerPixel == 8) ? Graphics::PixelFormat::createFormatCLUT8() :
|
||||
Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
|
||||
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
}
|
||||
|
||||
MSVideo1Decoder::~MSVideo1Decoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
void MSVideo1Decoder::decode8(Common::SeekableReadStream &stream) {
|
||||
byte colors[8];
|
||||
byte *pixels = (byte *)_surface->getPixels();
|
||||
uint16 stride = _surface->w;
|
||||
|
||||
int skipBlocks = 0;
|
||||
uint16 blocks_wide = _surface->w / 4;
|
||||
uint16 blocks_high = _surface->h / 4;
|
||||
uint32 totalBlocks = blocks_wide * blocks_high;
|
||||
uint32 blockInc = 4;
|
||||
uint16 rowDec = stride + 4;
|
||||
|
||||
for (uint16 block_y = blocks_high; block_y > 0; block_y--) {
|
||||
uint32 blockPtr = (block_y * 4 - 1) * stride;
|
||||
for (uint16 block_x = blocks_wide; block_x > 0; block_x--) {
|
||||
// check if this block should be skipped
|
||||
if (skipBlocks > 0) {
|
||||
blockPtr += blockInc;
|
||||
skipBlocks--;
|
||||
totalBlocks--;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 pixelPtr = blockPtr;
|
||||
|
||||
/* get the next two bytes in the encoded data stream */
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte byte_a = stream.readByte();
|
||||
byte byte_b = stream.readByte();
|
||||
|
||||
/* check if the decode is finished */
|
||||
if (byte_a == 0 && byte_b == 0 && totalBlocks == 0) {
|
||||
return;
|
||||
} else if ((byte_b & 0xFC) == 0x84) {
|
||||
// skip code, but don't count the current block
|
||||
skipBlocks = ((byte_b - 0x84) << 8) + byte_a - 1;
|
||||
} else if (byte_b < 0x80) {
|
||||
// 2-color encoding
|
||||
uint16 flags = (byte_b << 8) | byte_a;
|
||||
|
||||
CHECK_STREAM_PTR(2);
|
||||
colors[0] = stream.readByte();
|
||||
colors[1] = stream.readByte();
|
||||
|
||||
for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (byte pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixelPtr++] = colors[(flags & 0x1) ^ 1];
|
||||
pixelPtr -= rowDec;
|
||||
}
|
||||
} else if (byte_b >= 0x90) {
|
||||
// 8-color encoding
|
||||
uint16 flags = (byte_b << 8) | byte_a;
|
||||
|
||||
CHECK_STREAM_PTR(8);
|
||||
for (byte i = 0; i < 8; i++)
|
||||
colors[i] = stream.readByte();
|
||||
|
||||
for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (byte pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixelPtr++] = colors[((pixel_y & 0x2) << 1) + (pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
|
||||
pixelPtr -= rowDec;
|
||||
}
|
||||
} else {
|
||||
// 1-color encoding
|
||||
colors[0] = byte_a;
|
||||
|
||||
for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (byte pixel_x = 0; pixel_x < 4; pixel_x++)
|
||||
pixels[pixelPtr++] = colors[0];
|
||||
pixelPtr -= rowDec;
|
||||
}
|
||||
}
|
||||
|
||||
blockPtr += blockInc;
|
||||
totalBlocks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MSVideo1Decoder::decode16(Common::SeekableReadStream &stream) {
|
||||
/* decoding parameters */
|
||||
uint16 colors[8];
|
||||
uint16 *pixels = (uint16 *)_surface->getPixels();
|
||||
int32 stride = _surface->w;
|
||||
|
||||
int32 skip_blocks = 0;
|
||||
int32 blocks_wide = _surface->w / 4;
|
||||
int32 blocks_high = _surface->h / 4;
|
||||
int32 total_blocks = blocks_wide * blocks_high;
|
||||
int32 block_inc = 4;
|
||||
int32 row_dec = stride + 4;
|
||||
|
||||
for (int32 block_y = blocks_high; block_y > 0; block_y--) {
|
||||
int32 block_ptr = ((block_y * 4) - 1) * stride;
|
||||
for (int32 block_x = blocks_wide; block_x > 0; block_x--) {
|
||||
/* check if this block should be skipped */
|
||||
if (skip_blocks) {
|
||||
block_ptr += block_inc;
|
||||
skip_blocks--;
|
||||
total_blocks--;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 pixel_ptr = block_ptr;
|
||||
|
||||
/* get the next two bytes in the encoded data stream */
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte byte_a = stream.readByte();
|
||||
byte byte_b = stream.readByte();
|
||||
|
||||
/* check if the decode is finished */
|
||||
if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0)) {
|
||||
return;
|
||||
} else if ((byte_b & 0xFC) == 0x84) {
|
||||
/* skip code, but don't count the current block */
|
||||
skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
|
||||
} else if (byte_b < 0x80) {
|
||||
/* 2- or 8-color encoding modes */
|
||||
uint16 flags = (byte_b << 8) | byte_a;
|
||||
|
||||
CHECK_STREAM_PTR(4);
|
||||
colors[0] = stream.readUint16LE();
|
||||
colors[1] = stream.readUint16LE();
|
||||
|
||||
if (colors[0] & 0x8000) {
|
||||
/* 8-color encoding */
|
||||
CHECK_STREAM_PTR(12);
|
||||
colors[2] = stream.readUint16LE();
|
||||
colors[3] = stream.readUint16LE();
|
||||
colors[4] = stream.readUint16LE();
|
||||
colors[5] = stream.readUint16LE();
|
||||
colors[6] = stream.readUint16LE();
|
||||
colors[7] = stream.readUint16LE();
|
||||
|
||||
for (int pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (int pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixel_ptr++] =
|
||||
colors[((pixel_y & 0x2) << 1) +
|
||||
(pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
|
||||
pixel_ptr -= row_dec;
|
||||
}
|
||||
} else {
|
||||
/* 2-color encoding */
|
||||
for (int pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (int pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
|
||||
pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1];
|
||||
pixel_ptr -= row_dec;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* otherwise, it's a 1-color block */
|
||||
colors[0] = (byte_b << 8) | byte_a;
|
||||
|
||||
for (int pixel_y = 0; pixel_y < 4; pixel_y++) {
|
||||
for (int pixel_x = 0; pixel_x < 4; pixel_x++)
|
||||
pixels[pixel_ptr++] = colors[0];
|
||||
pixel_ptr -= row_dec;
|
||||
}
|
||||
}
|
||||
|
||||
block_ptr += block_inc;
|
||||
total_blocks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *MSVideo1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (_bitsPerPixel == 8)
|
||||
decode8(stream);
|
||||
else
|
||||
decode16(stream);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
53
image/codecs/msvideo1.h
Normal file
53
image/codecs/msvideo1.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_MSVIDEO1_H
|
||||
#define IMAGE_CODECS_MSVIDEO1_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Microsoft Video 1 decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class MSVideo1Decoder : public Codec {
|
||||
public:
|
||||
MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~MSVideo1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _surface->format; }
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void decode8(Common::SeekableReadStream &stream);
|
||||
void decode16(Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
679
image/codecs/qtrle.cpp
Normal file
679
image/codecs/qtrle.cpp
Normal file
@@ -0,0 +1,679 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// QuickTime RLE Decoder
|
||||
// Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson)
|
||||
|
||||
#include "image/codecs/qtrle.h"
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec(), _ditherPalette(0) {
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_surface = 0;
|
||||
_dirtyPalette = false;
|
||||
_colorMap = 0;
|
||||
|
||||
// We need to ensure the width is a multiple of 4
|
||||
_paddedWidth = width;
|
||||
uint16 wMod = width % 4;
|
||||
if (wMod != 0)
|
||||
_paddedWidth += 4 - wMod;
|
||||
}
|
||||
|
||||
QTRLEDecoder::~QTRLEDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _colorMap;
|
||||
}
|
||||
|
||||
#define CHECK_STREAM_PTR(n) \
|
||||
do { \
|
||||
if ((stream.pos() + n) > stream.size()) { \
|
||||
warning("QTRLE Problem: stream out of bounds (%d > %d)", (int)stream.pos() + n, (int)stream.size()); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_PIXEL_PTR(n) \
|
||||
do { \
|
||||
if ((int32)pixelPtr + n > (int)_paddedWidth * _surface->h) { \
|
||||
warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _paddedWidth * _surface->h); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *rgb = (byte *)_surface->getPixels();
|
||||
|
||||
while (linesToChange) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte skip = stream.readByte();
|
||||
int rleCode = stream.readSByte();
|
||||
|
||||
if (rleCode == 0)
|
||||
break;
|
||||
|
||||
if (skip & 0x80) {
|
||||
linesToChange--;
|
||||
rowPtr += _paddedWidth;
|
||||
pixelPtr = rowPtr + 2 * (skip & 0x7f);
|
||||
} else
|
||||
pixelPtr += 2 * skip;
|
||||
|
||||
if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
// get the next 2 bytes from the stream, treat them as groups of 8 pixels, and output them rleCode times */
|
||||
CHECK_STREAM_PTR(2);
|
||||
byte pi0 = stream.readByte();
|
||||
byte pi1 = stream.readByte();
|
||||
CHECK_PIXEL_PTR(rleCode * 2);
|
||||
|
||||
while (rleCode--) {
|
||||
rgb[pixelPtr++] = pi0;
|
||||
rgb[pixelPtr++] = pi1;
|
||||
}
|
||||
} else {
|
||||
// copy the same pixel directly to output 2 times
|
||||
rleCode *= 2;
|
||||
CHECK_STREAM_PTR(rleCode);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = stream.readByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *rgb = (byte *)_surface->getPixels();
|
||||
byte numPixels = (bpp == 4) ? 8 : 16;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + (numPixels * (stream.readByte() - 1));
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += (numPixels * (stream.readByte() - 1));
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
// get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times */
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
byte pi[16]; // 16 palette indices
|
||||
|
||||
for (int8 i = numPixels - 1; i >= 0; i--) {
|
||||
pi[numPixels - 1 - i] = (stream.readByte() >> ((i * bpp) & 0x07)) & ((1 << bpp) - 1);
|
||||
|
||||
if ((i & ((numPixels >> 2) - 1)) == 0)
|
||||
stream.readByte();
|
||||
}
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode * numPixels);
|
||||
|
||||
while (rleCode--)
|
||||
for (byte i = 0; i < numPixels; i++)
|
||||
rgb[pixelPtr++] = pi[i];
|
||||
} else {
|
||||
// copy the same pixel directly to output 4 times
|
||||
rleCode *= 4;
|
||||
CHECK_STREAM_PTR(rleCode);
|
||||
CHECK_PIXEL_PTR(rleCode * (numPixels >> 2));
|
||||
|
||||
while (rleCode--) {
|
||||
byte temp = stream.readByte();
|
||||
if (bpp == 4) {
|
||||
rgb[pixelPtr++] = (temp >> 4) & 0x0f;
|
||||
rgb[pixelPtr++] = temp & 0x0f;
|
||||
} else {
|
||||
rgb[pixelPtr++] = (temp >> 6) & 0x03;
|
||||
rgb[pixelPtr++] = (temp >> 4) & 0x03;
|
||||
rgb[pixelPtr++] = (temp >> 2) & 0x03;
|
||||
rgb[pixelPtr++] = temp & 0x03;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *rgb = (byte *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + 4 * (stream.readByte() - 1);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += 4 * (stream.readByte() - 1);
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
// get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
byte pi[4]; // 4 palette indexes
|
||||
|
||||
for (byte i = 0; i < 4; i++)
|
||||
pi[i] = stream.readByte();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode * 4);
|
||||
|
||||
while (rleCode--)
|
||||
for (byte i = 0; i < 4; i++)
|
||||
rgb[pixelPtr++] = pi[i];
|
||||
} else {
|
||||
// copy the same pixel directly to output 4 times
|
||||
rleCode *= 4;
|
||||
CHECK_STREAM_PTR(rleCode);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = stream.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
uint16 *rgb = (uint16 *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + stream.readByte() - 1;
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
uint16 rgb16 = stream.readUint16BE();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = rgb16;
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 2);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--)
|
||||
rgb[pixelPtr++] = stream.readUint16BE();
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline uint16 readDitherColor16(Common::ReadStream &stream) {
|
||||
return stream.readUint16BE() >> 1;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
void QTRLEDecoder::dither16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *output = (byte *)_surface->getPixels();
|
||||
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
// clone2727 thinks this should be startLine & 3, but the original definitely
|
||||
// isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
|
||||
// purpose of the compression then.
|
||||
byte curColorTableOffset = 0;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
byte rowOffset = stream.readByte() - 1;
|
||||
pixelPtr = rowPtr + rowOffset;
|
||||
uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
uint16 color = readDitherColor16(stream);
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 2);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--) {
|
||||
uint16 color = readDitherColor16(stream);
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
curColorTableOffset = (curColorTableOffset + 1) & 3;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
uint8 *rgb = (uint8 *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + stream.readByte() - 1;
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
CHECK_STREAM_PTR(3);
|
||||
|
||||
byte r = stream.readByte();
|
||||
byte g = stream.readByte();
|
||||
byte b = stream.readByte();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
rgb[(pixelPtr * 3) + 0] = r;
|
||||
rgb[(pixelPtr * 3) + 1] = g;
|
||||
rgb[(pixelPtr * 3) + 2] = b;
|
||||
pixelPtr++;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 3);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
stream.read(&rgb[pixelPtr * 3], rleCode * 3);
|
||||
pixelPtr += rleCode;
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline uint16 readDitherColor24(Common::ReadStream &stream) {
|
||||
uint8 rgb[3];
|
||||
stream.read(rgb, 3);
|
||||
|
||||
uint16 color = (rgb[0] & 0xF8) << 6;
|
||||
color |= (rgb[1] & 0xF8) << 1;
|
||||
color |= rgb[2] >> 4;
|
||||
return color;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
void QTRLEDecoder::dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *output = (byte *)_surface->getPixels();
|
||||
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
// clone2727 thinks this should be startLine & 3, but the original definitely
|
||||
// isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
|
||||
// purpose of the compression then.
|
||||
byte curColorTableOffset = 0;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
byte rowOffset = stream.readByte() - 1;
|
||||
pixelPtr = rowPtr + rowOffset;
|
||||
uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(3);
|
||||
|
||||
uint16 color = readDitherColor24(stream);
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 3);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--) {
|
||||
uint16 color = readDitherColor24(stream);
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
curColorTableOffset = (curColorTableOffset + 1) & 3;
|
||||
}
|
||||
}
|
||||
|
||||
void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
uint8 *rgb = (uint8 *)_surface->getPixels();
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
pixelPtr = rowPtr + stream.readByte() - 1;
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
byte a = stream.readByte();
|
||||
byte r = stream.readByte();
|
||||
byte g = stream.readByte();
|
||||
byte b = stream.readByte();
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
rgb[(pixelPtr * 4) + 0] = a;
|
||||
rgb[(pixelPtr * 4) + 1] = r;
|
||||
rgb[(pixelPtr * 4) + 2] = g;
|
||||
rgb[(pixelPtr * 4) + 3] = b;
|
||||
pixelPtr++;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 4);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
stream.read(&rgb[pixelPtr * 4], rleCode * 4);
|
||||
pixelPtr += rleCode;
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline uint16 readDitherColor32(Common::ReadStream &stream) {
|
||||
uint8 argb[4];
|
||||
stream.read(argb, 4);
|
||||
|
||||
uint16 color = (argb[1] & 0xF8) << 6;
|
||||
color |= (argb[2] & 0xF8) << 1;
|
||||
color |= argb[3] >> 4;
|
||||
return color;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
void QTRLEDecoder::dither32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
|
||||
uint32 pixelPtr = 0;
|
||||
byte *output = (byte *)_surface->getPixels();
|
||||
|
||||
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
|
||||
|
||||
// clone2727 thinks this should be startLine & 3, but the original definitely
|
||||
// isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
|
||||
// purpose of the compression then.
|
||||
byte curColorTableOffset = 0;
|
||||
|
||||
while (linesToChange--) {
|
||||
CHECK_STREAM_PTR(2);
|
||||
|
||||
byte rowOffset = stream.readByte() - 1;
|
||||
pixelPtr = rowPtr + rowOffset;
|
||||
uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
|
||||
|
||||
for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
|
||||
if (rleCode == 0) {
|
||||
// there's another skip code in the stream
|
||||
CHECK_STREAM_PTR(1);
|
||||
pixelPtr += stream.readByte() - 1;
|
||||
} else if (rleCode < 0) {
|
||||
// decode the run length code
|
||||
rleCode = -rleCode;
|
||||
CHECK_STREAM_PTR(4);
|
||||
|
||||
uint16 color = readDitherColor32(stream);
|
||||
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
while (rleCode--) {
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
} else {
|
||||
CHECK_STREAM_PTR(rleCode * 4);
|
||||
CHECK_PIXEL_PTR(rleCode);
|
||||
|
||||
// copy pixels directly to output
|
||||
while (rleCode--) {
|
||||
uint16 color = readDitherColor32(stream);
|
||||
output[pixelPtr++] = _colorMap[colorTableOffset + color];
|
||||
colorTableOffset += 0x4000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowPtr += _paddedWidth;
|
||||
curColorTableOffset = (curColorTableOffset + 1) & 3;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (!_surface)
|
||||
createSurface();
|
||||
|
||||
uint16 startLine = 0;
|
||||
uint16 height = _height;
|
||||
|
||||
// check if this frame is even supposed to change
|
||||
if (stream.size() < 8)
|
||||
return _surface;
|
||||
|
||||
// start after the chunk size
|
||||
stream.readUint32BE();
|
||||
|
||||
// fetch the header
|
||||
uint16 header = stream.readUint16BE();
|
||||
|
||||
// if a header is present, fetch additional decoding parameters
|
||||
if (header & 8) {
|
||||
if (stream.size() < 14)
|
||||
return _surface;
|
||||
|
||||
startLine = stream.readUint16BE();
|
||||
stream.readUint16BE(); // Unknown
|
||||
height = stream.readUint16BE();
|
||||
stream.readUint16BE(); // Unknown
|
||||
}
|
||||
|
||||
uint32 rowPtr = _paddedWidth * startLine;
|
||||
|
||||
switch (_bitsPerPixel) {
|
||||
case 1:
|
||||
case 33:
|
||||
decode1(stream, rowPtr, height);
|
||||
break;
|
||||
case 2:
|
||||
case 34:
|
||||
decode2_4(stream, rowPtr, height, 2);
|
||||
break;
|
||||
case 4:
|
||||
case 36:
|
||||
decode2_4(stream, rowPtr, height, 4);
|
||||
break;
|
||||
case 8:
|
||||
case 40:
|
||||
decode8(stream, rowPtr, height);
|
||||
break;
|
||||
case 16:
|
||||
if (_ditherPalette.size() > 0)
|
||||
dither16(stream, rowPtr, height);
|
||||
else
|
||||
decode16(stream, rowPtr, height);
|
||||
break;
|
||||
case 24:
|
||||
if (_ditherPalette.size() > 0)
|
||||
dither24(stream, rowPtr, height);
|
||||
else
|
||||
decode24(stream, rowPtr, height);
|
||||
break;
|
||||
case 32:
|
||||
if (_ditherPalette.size() > 0)
|
||||
dither32(stream, rowPtr, height);
|
||||
else
|
||||
decode32(stream, rowPtr, height);
|
||||
break;
|
||||
default:
|
||||
error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
|
||||
if (_ditherPalette.size() > 0)
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
|
||||
switch (_bitsPerPixel) {
|
||||
case 1:
|
||||
case 33:
|
||||
case 2:
|
||||
case 34:
|
||||
case 4:
|
||||
case 36:
|
||||
case 8:
|
||||
case 40:
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
case 16:
|
||||
return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
|
||||
case 24:
|
||||
return Graphics::PixelFormat::createFormatRGB24();
|
||||
case 32:
|
||||
return Graphics::PixelFormat::createFormatARGB32();
|
||||
default:
|
||||
error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
|
||||
}
|
||||
|
||||
return Graphics::PixelFormat();
|
||||
}
|
||||
|
||||
bool QTRLEDecoder::canDither(DitherType type) const {
|
||||
return type == kDitherTypeQT && (_bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32);
|
||||
}
|
||||
|
||||
void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
|
||||
assert(canDither(type));
|
||||
|
||||
_ditherPalette.resize(256, false);
|
||||
_ditherPalette.set(palette, 0, 256);
|
||||
_dirtyPalette = true;
|
||||
|
||||
delete[] _colorMap;
|
||||
_colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
|
||||
}
|
||||
|
||||
void QTRLEDecoder::createSurface() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_paddedWidth, _height, getPixelFormat());
|
||||
_surface->w = _width;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
74
image/codecs/qtrle.h
Normal file
74
image/codecs/qtrle.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_QTRLE_H
|
||||
#define IMAGE_CODECS_QTRLE_H
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* QuickTime Run-Length Encoding decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class QTRLEDecoder : public Codec {
|
||||
public:
|
||||
QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel);
|
||||
~QTRLEDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
|
||||
bool containsPalette() const override { return _ditherPalette != 0; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _ditherPalette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
|
||||
private:
|
||||
byte _bitsPerPixel;
|
||||
Graphics::Surface *_surface;
|
||||
uint16 _width, _height;
|
||||
uint32 _paddedWidth;
|
||||
Graphics::Palette _ditherPalette;
|
||||
bool _dirtyPalette;
|
||||
byte *_colorMap;
|
||||
|
||||
void createSurface();
|
||||
|
||||
void decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp);
|
||||
void decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void dither16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
void dither32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
365
image/codecs/rpza.cpp
Normal file
365
image/codecs/rpza.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's RPZA decoder
|
||||
|
||||
#include "image/codecs/rpza.h"
|
||||
#include "image/codecs/dither.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec(), _ditherPalette(0) {
|
||||
_format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
|
||||
_dirtyPalette = false;
|
||||
_colorMap = 0;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_blockWidth = (width + 3) / 4;
|
||||
_blockHeight = (height + 3) / 4;
|
||||
_surface = 0;
|
||||
}
|
||||
|
||||
RPZADecoder::~RPZADecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _colorMap;
|
||||
}
|
||||
|
||||
#define ADVANCE_BLOCK() \
|
||||
blockPtr += 4; \
|
||||
if (blockPtr >= endPtr) { \
|
||||
blockPtr += pitch * 3; \
|
||||
endPtr = blockPtr + pitch; \
|
||||
} \
|
||||
totalBlocks--; \
|
||||
if (totalBlocks < 0) \
|
||||
error("rpza block counter just went negative (this should not happen)") \
|
||||
|
||||
struct BlockDecoderRaw {
|
||||
static inline void drawFillBlock(uint16 *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = color;
|
||||
blockPtr[1] = color;
|
||||
blockPtr[2] = color;
|
||||
blockPtr[3] = color;
|
||||
}
|
||||
|
||||
static inline void drawRawBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
|
||||
blockPtr[0] = colors[0];
|
||||
blockPtr[1] = colors[1];
|
||||
blockPtr[2] = colors[2];
|
||||
blockPtr[3] = colors[3];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[4];
|
||||
blockPtr[1] = colors[5];
|
||||
blockPtr[2] = colors[6];
|
||||
blockPtr[3] = colors[7];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[8];
|
||||
blockPtr[1] = colors[9];
|
||||
blockPtr[2] = colors[10];
|
||||
blockPtr[3] = colors[11];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[12];
|
||||
blockPtr[1] = colors[13];
|
||||
blockPtr[2] = colors[14];
|
||||
blockPtr[3] = colors[15];
|
||||
}
|
||||
|
||||
static inline void drawBlendBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
|
||||
blockPtr[0] = colors[(indexes[0] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[0] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[0] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[0] >> 0) & 0x03];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[(indexes[1] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[1] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[1] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[1] >> 0) & 0x03];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[(indexes[2] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[2] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[2] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[2] >> 0) & 0x03];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colors[(indexes[3] >> 6) & 0x03];
|
||||
blockPtr[1] = colors[(indexes[3] >> 4) & 0x03];
|
||||
blockPtr[2] = colors[(indexes[3] >> 2) & 0x03];
|
||||
blockPtr[3] = colors[(indexes[3] >> 0) & 0x03];
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockDecoderDither {
|
||||
static inline void drawFillBlock(byte *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
|
||||
const byte *mapOffset = colorMap + (color >> 1);
|
||||
byte pixel1 = mapOffset[0x0000];
|
||||
byte pixel2 = mapOffset[0x4000];
|
||||
byte pixel3 = mapOffset[0x8000];
|
||||
byte pixel4 = mapOffset[0xC000];
|
||||
|
||||
blockPtr[0] = pixel1;
|
||||
blockPtr[1] = pixel2;
|
||||
blockPtr[2] = pixel3;
|
||||
blockPtr[3] = pixel4;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = pixel4;
|
||||
blockPtr[1] = pixel1;
|
||||
blockPtr[2] = pixel2;
|
||||
blockPtr[3] = pixel3;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = pixel2;
|
||||
blockPtr[1] = pixel3;
|
||||
blockPtr[2] = pixel4;
|
||||
blockPtr[3] = pixel1;
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = pixel3;
|
||||
blockPtr[1] = pixel4;
|
||||
blockPtr[2] = pixel1;
|
||||
blockPtr[3] = pixel2;
|
||||
}
|
||||
|
||||
static inline void drawRawBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
|
||||
blockPtr[0] = colorMap[(colors[0] >> 1) + 0x0000];
|
||||
blockPtr[1] = colorMap[(colors[1] >> 1) + 0x4000];
|
||||
blockPtr[2] = colorMap[(colors[2] >> 1) + 0x8000];
|
||||
blockPtr[3] = colorMap[(colors[3] >> 1) + 0xC000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[4] >> 1) + 0xC000];
|
||||
blockPtr[1] = colorMap[(colors[5] >> 1) + 0x0000];
|
||||
blockPtr[2] = colorMap[(colors[6] >> 1) + 0x4000];
|
||||
blockPtr[3] = colorMap[(colors[7] >> 1) + 0x8000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[8] >> 1) + 0x4000];
|
||||
blockPtr[1] = colorMap[(colors[9] >> 1) + 0x8000];
|
||||
blockPtr[2] = colorMap[(colors[10] >> 1) + 0xC000];
|
||||
blockPtr[3] = colorMap[(colors[11] >> 1) + 0x0000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[12] >> 1) + 0x8000];
|
||||
blockPtr[1] = colorMap[(colors[13] >> 1) + 0xC000];
|
||||
blockPtr[2] = colorMap[(colors[14] >> 1) + 0x0000];
|
||||
blockPtr[3] = colorMap[(colors[15] >> 1) + 0x4000];
|
||||
}
|
||||
|
||||
static inline void drawBlendBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
|
||||
blockPtr[0] = colorMap[(colors[(indexes[0] >> 6) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[0] >> 4) & 0x03] >> 1) + 0x4000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[0] >> 2) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[0] >> 0) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[(indexes[1] >> 6) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[1] >> 4) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[1] >> 2) & 0x03] >> 1) + 0x4000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[1] >> 0) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[(indexes[2] >> 6) & 0x03] >> 1) + 0x4000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[2] >> 4) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[2] >> 2) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[2] >> 0) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr += pitch;
|
||||
blockPtr[0] = colorMap[(colors[(indexes[3] >> 6) & 0x03] >> 1) + 0x8000];
|
||||
blockPtr[1] = colorMap[(colors[(indexes[3] >> 4) & 0x03] >> 1) + 0xC000];
|
||||
blockPtr[2] = colorMap[(colors[(indexes[3] >> 2) & 0x03] >> 1) + 0x0000];
|
||||
blockPtr[3] = colorMap[(colors[(indexes[3] >> 0) & 0x03] >> 1) + 0x4000];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename PixelInt, typename BlockDecoder>
|
||||
static inline void decodeFrameTmpl(Common::SeekableReadStream &stream, PixelInt *ptr, uint16 pitch, uint16 blockWidth, uint16 blockHeight, const byte *colorMap) {
|
||||
uint16 colorA = 0, colorB = 0;
|
||||
uint16 color4[4];
|
||||
|
||||
PixelInt *blockPtr = ptr;
|
||||
PixelInt *endPtr = ptr + pitch;
|
||||
uint16 ta;
|
||||
uint16 tb;
|
||||
|
||||
// First byte is always 0xe1. Warn if it's different
|
||||
byte firstByte = stream.readByte();
|
||||
if (firstByte != 0xe1)
|
||||
warning("First RPZA chunk byte is 0x%02x instead of 0xe1", firstByte);
|
||||
|
||||
// Get chunk size, ingnoring first byte
|
||||
uint32 chunkSize = stream.readUint16BE() << 8;
|
||||
chunkSize += stream.readByte();
|
||||
|
||||
// If length mismatch use size from MOV file and try to decode anyway
|
||||
if (chunkSize != (uint32)stream.size()) {
|
||||
warning("MOV chunk size != encoded chunk size; using MOV chunk size");
|
||||
chunkSize = stream.size();
|
||||
}
|
||||
|
||||
// Number of 4x4 blocks in frame
|
||||
int32 totalBlocks = blockWidth * blockHeight;
|
||||
|
||||
// Process chunk data
|
||||
while ((uint32)stream.pos() < chunkSize) {
|
||||
byte opcode = stream.readByte(); // Get opcode
|
||||
byte numBlocks = (opcode & 0x1f) + 1; // Extract block counter from opcode
|
||||
|
||||
// If opcode MSbit is 0, we need more data to decide what to do
|
||||
if ((opcode & 0x80) == 0) {
|
||||
colorA = (opcode << 8) | stream.readByte();
|
||||
opcode = 0;
|
||||
if (stream.readByte() & 0x80) {
|
||||
// Must behave as opcode 110xxxxx, using colorA computed
|
||||
// above. Use fake opcode 0x20 to enter switch block at
|
||||
// the right place
|
||||
opcode = 0x20;
|
||||
numBlocks = 1;
|
||||
}
|
||||
stream.seek(-1, SEEK_CUR);
|
||||
}
|
||||
|
||||
switch (opcode & 0xe0) {
|
||||
case 0x80: // Skip blocks
|
||||
while (numBlocks--) {
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
case 0xa0: // Fill blocks with one color
|
||||
colorA = stream.readUint16BE();
|
||||
|
||||
while (numBlocks--) {
|
||||
BlockDecoder::drawFillBlock(blockPtr, pitch, colorA, colorMap);
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// Fill blocks with 4 colors
|
||||
case 0xc0:
|
||||
colorA = stream.readUint16BE();
|
||||
// fall through
|
||||
case 0x20:
|
||||
colorB = stream.readUint16BE();
|
||||
|
||||
// Sort out the colors
|
||||
color4[0] = colorB & 0x7FFF;
|
||||
color4[1] = 0;
|
||||
color4[2] = 0;
|
||||
color4[3] = colorA & 0x7FFF;
|
||||
|
||||
// Red components
|
||||
ta = (colorA >> 10) & 0x1F;
|
||||
tb = (colorB >> 10) & 0x1F;
|
||||
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
|
||||
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
|
||||
|
||||
// Green components
|
||||
ta = (colorA >> 5) & 0x1F;
|
||||
tb = (colorB >> 5) & 0x1F;
|
||||
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
|
||||
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
|
||||
|
||||
// Blue components
|
||||
ta = colorA & 0x1F;
|
||||
tb = colorB & 0x1F;
|
||||
color4[1] |= ((11 * ta + 21 * tb) >> 5);
|
||||
color4[2] |= ((21 * ta + 11 * tb) >> 5);
|
||||
|
||||
while (numBlocks--) {
|
||||
byte indexes[4];
|
||||
stream.read(indexes, 4);
|
||||
|
||||
BlockDecoder::drawBlendBlock(blockPtr, pitch, color4, indexes, colorMap);
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// Fill block with 16 colors
|
||||
case 0x00: {
|
||||
uint16 colors[16];
|
||||
colors[0] = colorA;
|
||||
|
||||
for (int i = 0; i < 15; i++)
|
||||
colors[i + 1] = stream.readUint16BE();
|
||||
|
||||
BlockDecoder::drawRawBlock(blockPtr, pitch, colors, colorMap);
|
||||
ADVANCE_BLOCK();
|
||||
break;
|
||||
}
|
||||
|
||||
// Unknown opcode
|
||||
default:
|
||||
error("Unknown opcode %02x in rpza chunk", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
|
||||
// Allocate enough space in the surface for the blocks
|
||||
_surface->create(_blockWidth * 4, _blockHeight * 4, getPixelFormat());
|
||||
|
||||
// Adjust width/height to be the right ones
|
||||
_surface->w = _width;
|
||||
_surface->h = _height;
|
||||
}
|
||||
|
||||
if (_colorMap)
|
||||
decodeFrameTmpl<byte, BlockDecoderDither>(stream, (byte *)_surface->getPixels(), _surface->pitch, _blockWidth, _blockHeight, _colorMap);
|
||||
else
|
||||
decodeFrameTmpl<uint16, BlockDecoderRaw>(stream, (uint16 *)_surface->getPixels(), _surface->pitch / 2, _blockWidth, _blockHeight, _colorMap);
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool RPZADecoder::canDither(DitherType type) const {
|
||||
return type == kDitherTypeQT;
|
||||
}
|
||||
|
||||
void RPZADecoder::setDither(DitherType type, const byte *palette) {
|
||||
assert(canDither(type));
|
||||
|
||||
_ditherPalette.resize(256, false);
|
||||
_ditherPalette.set(palette, 0, 256);
|
||||
|
||||
_dirtyPalette = true;
|
||||
_format = Graphics::PixelFormat::createFormatCLUT8();
|
||||
|
||||
delete[] _colorMap;
|
||||
_colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
62
image/codecs/rpza.h
Normal file
62
image/codecs/rpza.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_RPZA_H
|
||||
#define IMAGE_CODECS_RPZA_H
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Apple RPZA decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class RPZADecoder : public Codec {
|
||||
public:
|
||||
RPZADecoder(uint16 width, uint16 height);
|
||||
~RPZADecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _format; }
|
||||
|
||||
bool containsPalette() const override { return _ditherPalette != 0; }
|
||||
const byte *getPalette() override { _dirtyPalette = false; return _ditherPalette.data(); }
|
||||
bool hasDirtyPalette() const override { return _dirtyPalette; }
|
||||
bool canDither(DitherType type) const override;
|
||||
void setDither(DitherType type, const byte *palette) override;
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _format;
|
||||
Graphics::Surface *_surface;
|
||||
Graphics::Palette _ditherPalette;
|
||||
bool _dirtyPalette;
|
||||
byte *_colorMap;
|
||||
uint16 _width, _height;
|
||||
uint16 _blockWidth, _blockHeight;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
417
image/codecs/smc.cpp
Normal file
417
image/codecs/smc.cpp
Normal file
@@ -0,0 +1,417 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based off ffmpeg's SMC decoder
|
||||
|
||||
#include "image/codecs/smc.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
#define GET_BLOCK_COUNT() \
|
||||
(opcode & 0x10) ? (1 + stream.readByte()) : 1 + (opcode & 0x0F);
|
||||
|
||||
#define ADVANCE_BLOCK() \
|
||||
{ \
|
||||
pixelPtr += 4; \
|
||||
if (pixelPtr >= _surface->w) { \
|
||||
pixelPtr = 0; \
|
||||
rowPtr += _surface->w * 4; \
|
||||
} \
|
||||
totalBlocks--; \
|
||||
if (totalBlocks < 0) { \
|
||||
warning("block counter just went negative (this should not happen)"); \
|
||||
return _surface; \
|
||||
} \
|
||||
}
|
||||
|
||||
SMCDecoder::SMCDecoder(uint16 width, uint16 height) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
SMCDecoder::~SMCDecoder() {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
const Graphics::Surface *SMCDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
byte *pixels = (byte *)_surface->getPixels();
|
||||
|
||||
uint32 numBlocks = 0;
|
||||
uint32 colorFlags = 0;
|
||||
uint32 colorFlagsA = 0;
|
||||
uint32 colorFlagsB = 0;
|
||||
|
||||
const uint16 rowInc = _surface->w - 4;
|
||||
int32 rowPtr = 0;
|
||||
int32 pixelPtr = 0;
|
||||
uint32 blockPtr = 0;
|
||||
uint32 prevBlockPtr = 0;
|
||||
uint32 prevBlockPtr1 = 0, prevBlockPtr2 = 0;
|
||||
byte prevBlockFlag = false;
|
||||
uint32 pixel = 0;
|
||||
|
||||
uint32 colorPairIndex = 0;
|
||||
uint32 colorQuadIndex = 0;
|
||||
uint32 colorOctetIndex = 0;
|
||||
uint32 colorTableIndex = 0; // indices to color pair, quad, or octet tables
|
||||
|
||||
int32 chunkSize = stream.readUint32BE() & 0x00FFFFFF;
|
||||
if (chunkSize != stream.size())
|
||||
warning("MOV chunk size != SMC chunk size (%d != %d); ignoring SMC chunk size", chunkSize, (int)stream.size());
|
||||
|
||||
int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
|
||||
|
||||
uint32 pixelSize = _surface->w * _surface->h;
|
||||
|
||||
// traverse through the blocks
|
||||
while (totalBlocks != 0) {
|
||||
// sanity checks
|
||||
|
||||
// make sure stream ptr hasn't gone out of bounds
|
||||
if (stream.pos() > stream.size()) {
|
||||
warning("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)", (int)stream.pos(), (int)stream.size());
|
||||
return _surface;
|
||||
}
|
||||
|
||||
// make sure the row pointer hasn't gone wild
|
||||
if (rowPtr >= _surface->w * _surface->h) {
|
||||
warning("SMC decoder just went out of bounds (row ptr = %d, size = %d)", rowPtr, _surface->w * _surface->h);
|
||||
return _surface;
|
||||
}
|
||||
|
||||
byte opcode = stream.readByte();
|
||||
|
||||
switch (opcode & 0xF0) {
|
||||
// skip n blocks
|
||||
case 0x00:
|
||||
case 0x10:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
while (numBlocks--) {
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// repeat last block n times
|
||||
case 0x20:
|
||||
case 0x30:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
|
||||
// sanity check
|
||||
if (rowPtr == 0 && pixelPtr == 0) {
|
||||
warning("encountered repeat block opcode (%02X) but no blocks rendered yet", opcode & 0xF0);
|
||||
break;
|
||||
}
|
||||
|
||||
// figure out where the previous block started
|
||||
if (pixelPtr == 0)
|
||||
prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4;
|
||||
else
|
||||
prevBlockPtr1 = rowPtr + pixelPtr - 4;
|
||||
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
prevBlockPtr = prevBlockPtr1;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = pixels[prevBlockPtr];
|
||||
blockPtr++, prevBlockPtr++;
|
||||
}
|
||||
blockPtr += rowInc;
|
||||
prevBlockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// repeat previous pair of blocks n times
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
numBlocks *= 2;
|
||||
|
||||
// sanity check
|
||||
if (rowPtr == 0 && pixelPtr < 2 * 4) {
|
||||
warning("encountered repeat block opcode (%02X) but not enough blocks rendered yet", opcode & 0xF0);
|
||||
break;
|
||||
}
|
||||
|
||||
// figure out where the previous 2 blocks started
|
||||
if (pixelPtr == 0)
|
||||
prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4 * 2;
|
||||
else if (pixelPtr == 4)
|
||||
prevBlockPtr1 = (rowPtr - _surface->w * 4) + rowInc;
|
||||
else
|
||||
prevBlockPtr1 = rowPtr + pixelPtr - 4 * 2;
|
||||
|
||||
if (pixelPtr == 0)
|
||||
prevBlockPtr2 = (rowPtr - _surface->w * 4) + rowInc;
|
||||
else
|
||||
prevBlockPtr2 = rowPtr + pixelPtr - 4;
|
||||
|
||||
prevBlockFlag = 0;
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
|
||||
if (prevBlockFlag)
|
||||
prevBlockPtr = prevBlockPtr2;
|
||||
else
|
||||
prevBlockPtr = prevBlockPtr1;
|
||||
|
||||
prevBlockFlag = !prevBlockFlag;
|
||||
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = pixels[prevBlockPtr];
|
||||
blockPtr++, prevBlockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
prevBlockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 1-color block encoding
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
numBlocks = GET_BLOCK_COUNT();
|
||||
pixel = stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = pixel;
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 2-color block encoding
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
// figure out which color pair to use to paint the 2-color block
|
||||
if ((opcode & 0xF0) == 0x80) {
|
||||
// fetch the next 2 colors from bytestream and store in next
|
||||
// available entry in the color pair table
|
||||
for (byte i = 0; i < CPAIR; i++) {
|
||||
pixel = stream.readByte();
|
||||
colorTableIndex = CPAIR * colorPairIndex + i;
|
||||
_colorPairs[colorTableIndex] = pixel;
|
||||
}
|
||||
|
||||
// this is the base index to use for this block
|
||||
colorTableIndex = CPAIR * colorPairIndex;
|
||||
colorPairIndex++;
|
||||
|
||||
// wraparound
|
||||
if (colorPairIndex == COLORS_PER_TABLE)
|
||||
colorPairIndex = 0;
|
||||
} else
|
||||
colorTableIndex = CPAIR * stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
colorFlags = stream.readUint16BE();
|
||||
uint16 flagMask = 0x8000;
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
if (colorFlags & flagMask)
|
||||
pixel = colorTableIndex + 1;
|
||||
else
|
||||
pixel = colorTableIndex;
|
||||
|
||||
flagMask >>= 1;
|
||||
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = _colorPairs[pixel];
|
||||
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 4-color block encoding
|
||||
case 0xA0:
|
||||
case 0xB0:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
// figure out which color quad to use to paint the 4-color block
|
||||
if ((opcode & 0xF0) == 0xA0) {
|
||||
// fetch the next 4 colors from bytestream and store in next
|
||||
// available entry in the color quad table
|
||||
for (byte i = 0; i < CQUAD; i++) {
|
||||
pixel = stream.readByte();
|
||||
colorTableIndex = CQUAD * colorQuadIndex + i;
|
||||
_colorQuads[colorTableIndex] = pixel;
|
||||
}
|
||||
|
||||
// this is the base index to use for this block
|
||||
colorTableIndex = CQUAD * colorQuadIndex;
|
||||
colorQuadIndex++;
|
||||
|
||||
// wraparound
|
||||
if (colorQuadIndex == COLORS_PER_TABLE)
|
||||
colorQuadIndex = 0;
|
||||
} else
|
||||
colorTableIndex = CQUAD * stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
colorFlags = stream.readUint32BE();
|
||||
|
||||
// flag mask actually acts as a bit shift count here
|
||||
byte flagMask = 30;
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x03);
|
||||
flagMask -= 2;
|
||||
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = _colorQuads[pixel];
|
||||
blockPtr++;
|
||||
}
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 8-color block encoding
|
||||
case 0xC0:
|
||||
case 0xD0:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
// figure out which color octet to use to paint the 8-color block
|
||||
if ((opcode & 0xF0) == 0xC0) {
|
||||
// fetch the next 8 colors from bytestream and store in next
|
||||
// available entry in the color octet table
|
||||
for (byte i = 0; i < COCTET; i++) {
|
||||
pixel = stream.readByte();
|
||||
colorTableIndex = COCTET * colorOctetIndex + i;
|
||||
_colorOctets[colorTableIndex] = pixel;
|
||||
}
|
||||
|
||||
// this is the base index to use for this block
|
||||
colorTableIndex = COCTET * colorOctetIndex;
|
||||
colorOctetIndex++;
|
||||
|
||||
// wraparound
|
||||
if (colorOctetIndex == COLORS_PER_TABLE)
|
||||
colorOctetIndex = 0;
|
||||
} else
|
||||
colorTableIndex = COCTET * stream.readByte();
|
||||
|
||||
while (numBlocks--) {
|
||||
/*
|
||||
For this input of 6 hex bytes:
|
||||
01 23 45 67 89 AB
|
||||
Mangle it to this output:
|
||||
flags_a = xx012456, flags_b = xx89A37B
|
||||
*/
|
||||
|
||||
// build the color flags
|
||||
byte flagData[6];
|
||||
stream.read(flagData, 6);
|
||||
|
||||
colorFlagsA = ((READ_BE_UINT16(flagData) & 0xFFF0) << 8) | (READ_BE_UINT16(flagData + 2) >> 4);
|
||||
colorFlagsB = ((READ_BE_UINT16(flagData + 4) & 0xFFF0) << 8) | ((flagData[1] & 0xF) << 8) |
|
||||
((flagData[3] & 0xF) << 4) | (flagData[5] & 0xf);
|
||||
|
||||
colorFlags = colorFlagsA;
|
||||
|
||||
// flag mask actually acts as a bit shift count here
|
||||
byte flagMask = 21;
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
// reload flags at third row (iteration y == 2)
|
||||
if (y == 2) {
|
||||
colorFlags = colorFlagsB;
|
||||
flagMask = 21;
|
||||
}
|
||||
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x07);
|
||||
flagMask -= 3;
|
||||
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = _colorOctets[pixel];
|
||||
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
// 16-color block encoding (every pixel is a different color)
|
||||
case 0xE0:
|
||||
numBlocks = (opcode & 0x0F) + 1;
|
||||
|
||||
while (numBlocks--) {
|
||||
blockPtr = rowPtr + pixelPtr;
|
||||
for (byte y = 0; y < 4; y++) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
byte b = stream.readByte();
|
||||
if (blockPtr < pixelSize)
|
||||
pixels[blockPtr] = b;
|
||||
blockPtr++;
|
||||
}
|
||||
|
||||
blockPtr += rowInc;
|
||||
}
|
||||
ADVANCE_BLOCK();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
warning("0xF0 opcode seen in SMC chunk (contact the developers)");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
60
image/codecs/smc.h
Normal file
60
image/codecs/smc.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_SMC_H
|
||||
#define IMAGE_CODECS_SMC_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
enum {
|
||||
CPAIR = 2,
|
||||
CQUAD = 4,
|
||||
COCTET = 8,
|
||||
COLORS_PER_TABLE = 256
|
||||
};
|
||||
|
||||
/**
|
||||
* Apple SMC decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class SMCDecoder : public Codec {
|
||||
public:
|
||||
SMCDecoder(uint16 width, uint16 height);
|
||||
~SMCDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
// SMC color tables
|
||||
byte _colorPairs[COLORS_PER_TABLE * CPAIR];
|
||||
byte _colorQuads[COLORS_PER_TABLE * CQUAD];
|
||||
byte _colorOctets[COLORS_PER_TABLE * COCTET];
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
804
image/codecs/svq1.cpp
Normal file
804
image/codecs/svq1.cpp
Normal file
@@ -0,0 +1,804 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Sorenson Video 1 Codec
|
||||
// Based off FFmpeg's SVQ1 decoder (written by Arpi and Nick Kurshev)
|
||||
|
||||
#include "image/codecs/svq1.h"
|
||||
#include "image/codecs/svq1_cb.h"
|
||||
#include "image/codecs/svq1_vlc.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/bitstream.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/compression/huffman.h"
|
||||
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
#define SVQ1_BLOCK_SKIP 0
|
||||
#define SVQ1_BLOCK_INTER 1
|
||||
#define SVQ1_BLOCK_INTER_4V 2
|
||||
#define SVQ1_BLOCK_INTRA 3
|
||||
|
||||
SVQ1Decoder::SVQ1Decoder(uint16 width, uint16 height) {
|
||||
debug(1, "SVQ1Decoder::SVQ1Decoder(width:%d, height:%d)", width, height);
|
||||
_width = width;
|
||||
_height = height;
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
|
||||
_frameWidth = _frameHeight = 0;
|
||||
_surface = 0;
|
||||
|
||||
_last[0] = 0;
|
||||
_last[1] = 0;
|
||||
_last[2] = 0;
|
||||
|
||||
// Setup Variable Length Code Tables
|
||||
_blockType = new HuffmanDecoder(0, 4, s_svq1BlockTypeCodes, s_svq1BlockTypeLengths);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
_intraMultistage[i] = new HuffmanDecoder(0, 8, s_svq1IntraMultistageCodes[i], s_svq1IntraMultistageLengths[i]);
|
||||
_interMultistage[i] = new HuffmanDecoder(0, 8, s_svq1InterMultistageCodes[i], s_svq1InterMultistageLengths[i]);
|
||||
}
|
||||
|
||||
_intraMean = new HuffmanDecoder(0, 256, s_svq1IntraMeanCodes, s_svq1IntraMeanLengths);
|
||||
_interMean = new HuffmanDecoder(0, 512, s_svq1InterMeanCodes, s_svq1InterMeanLengths);
|
||||
_motionComponent = new HuffmanDecoder(0, 33, s_svq1MotionComponentCodes, s_svq1MotionComponentLengths);
|
||||
}
|
||||
|
||||
SVQ1Decoder::~SVQ1Decoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _last[0];
|
||||
delete[] _last[1];
|
||||
delete[] _last[2];
|
||||
|
||||
delete _blockType;
|
||||
delete _intraMean;
|
||||
delete _interMean;
|
||||
delete _motionComponent;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
delete _intraMultistage[i];
|
||||
delete _interMultistage[i];
|
||||
}
|
||||
}
|
||||
|
||||
#define SVQ1_ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
|
||||
|
||||
const Graphics::Surface *SVQ1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
debug(1, "SVQ1Decoder::decodeImage()");
|
||||
|
||||
Common::BitStream32BEMSB frameData(stream);
|
||||
|
||||
uint32 frameCode = frameData.getBits<22>();
|
||||
debug(1, " frameCode: %d", frameCode);
|
||||
|
||||
if ((frameCode & ~0x70) || !(frameCode & 0x60)) { // Invalid
|
||||
warning("Invalid Image at frameCode");
|
||||
return _surface;
|
||||
}
|
||||
|
||||
byte temporalReference = frameData.getBits<8>();
|
||||
debug(1, " temporalReference: %d", temporalReference);
|
||||
static const char *const types[4] = { "I (Key)", "P (Delta from Previous)", "B (Delta from Next)", "Invalid" };
|
||||
byte frameType = frameData.getBits<2>();
|
||||
debug(1, " frameType: %d = %s Frame", frameType, types[frameType]);
|
||||
|
||||
if (frameType == 0) { // I Frame
|
||||
// TODO: Validate checksum if present
|
||||
if (frameCode == 0x50 || frameCode == 0x60) {
|
||||
uint32 checksum = frameData.getBits<16>();
|
||||
debug(1, " checksum:0x%02x", checksum);
|
||||
// We're currently just ignoring the checksum
|
||||
}
|
||||
|
||||
if ((frameCode ^ 0x10) >= 0x50) {
|
||||
// Skip embedded string
|
||||
byte stringLen = frameData.getBits<8>();
|
||||
for (uint16 i = 0; i < stringLen-1; i++)
|
||||
frameData.skip(8);
|
||||
}
|
||||
|
||||
frameData.skip(5); // Unknown
|
||||
|
||||
static const struct { uint w, h; } standardFrameSizes[7] = {
|
||||
{ 160, 120 }, // 0
|
||||
{ 128, 96 }, // 1
|
||||
{ 176, 144 }, // 2
|
||||
{ 352, 288 }, // 3
|
||||
{ 704, 576 }, // 4
|
||||
{ 240, 180 }, // 5
|
||||
{ 320, 240 } // 6
|
||||
};
|
||||
|
||||
byte frameSizeCode = frameData.getBits<3>();
|
||||
debug(1, " frameSizeCode: %d", frameSizeCode);
|
||||
|
||||
if (frameSizeCode == 7) {
|
||||
_frameWidth = frameData.getBits<12>();
|
||||
_frameHeight = frameData.getBits<12>();
|
||||
} else {
|
||||
_frameWidth = standardFrameSizes[frameSizeCode].w;
|
||||
_frameHeight = standardFrameSizes[frameSizeCode].h;
|
||||
}
|
||||
|
||||
debug(1, " frameWidth: %d", _frameWidth);
|
||||
debug(1, " frameHeight: %d", _frameHeight);
|
||||
} else if (frameType == 2) { // B Frame
|
||||
warning("B Frames not supported by SVQ1 decoder (yet)");
|
||||
return _surface;
|
||||
} else if (frameType == 3) { // Invalid
|
||||
warning("Invalid Frame Type");
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool checksumPresent = frameData.getBit() != 0;
|
||||
debug(1, " checksumPresent: %d", checksumPresent);
|
||||
if (checksumPresent) {
|
||||
bool usePacketChecksum = frameData.getBit() != 0;
|
||||
debug(1, " usePacketChecksum: %d", usePacketChecksum);
|
||||
bool componentChecksumsAfterImageData = frameData.getBit() != 0;
|
||||
debug(1, " componentChecksumsAfterImageData: %d", componentChecksumsAfterImageData);
|
||||
byte unk4 = frameData.getBits<2>();
|
||||
debug(1, " unk4: %d", unk4);
|
||||
if (unk4 != 0)
|
||||
warning("Invalid Frame Header in SVQ1 Frame Decode");
|
||||
}
|
||||
|
||||
// Some more unknown data
|
||||
bool unk5 = frameData.getBit() != 0;
|
||||
if (unk5) {
|
||||
frameData.skip(8);
|
||||
|
||||
while (frameData.getBit() != 0)
|
||||
frameData.skip(8);
|
||||
}
|
||||
|
||||
uint yWidth = SVQ1_ALIGN(_frameWidth, 16);
|
||||
uint yHeight = SVQ1_ALIGN(_frameHeight, 16);
|
||||
uint uvWidth = SVQ1_ALIGN(yWidth / 4, 16);
|
||||
uint uvHeight = SVQ1_ALIGN(yHeight / 4, 16);
|
||||
uint uvPitch = uvWidth + 4; // we need at least one extra column and pitch must be divisible by 4
|
||||
|
||||
byte *current[3];
|
||||
|
||||
// Decode Y, U and V component planes
|
||||
for (int i = 0; i < 3; i++) {
|
||||
uint width, height, pitch;
|
||||
if (i == 0) {
|
||||
width = yWidth;
|
||||
height = yHeight;
|
||||
pitch = width;
|
||||
current[i] = new byte[width * height];
|
||||
} else {
|
||||
width = uvWidth;
|
||||
height = uvHeight;
|
||||
pitch = uvPitch;
|
||||
|
||||
// Add an extra row here. See below for more information.
|
||||
current[i] = new byte[pitch * (height + 1)];
|
||||
}
|
||||
|
||||
if (frameType == 0) { // I Frame
|
||||
// Keyframe (I)
|
||||
byte *currentP = current[i];
|
||||
for (uint16 y = 0; y < height; y += 16) {
|
||||
for (uint16 x = 0; x < width; x += 16) {
|
||||
if (!svq1DecodeBlockIntra(&frameData, ¤tP[x], pitch)) {
|
||||
warning("svq1DecodeBlockIntra decode failure");
|
||||
return _surface;
|
||||
}
|
||||
}
|
||||
currentP += 16 * pitch;
|
||||
}
|
||||
} else {
|
||||
// Delta frame (P or B)
|
||||
|
||||
// Prediction Motion Vector
|
||||
Common::Point *pmv = new Common::Point[(width / 8) + 3];
|
||||
|
||||
byte *previous = 0;
|
||||
if (frameType == 2) { // B Frame
|
||||
error("SVQ1 Video: B Frames not supported");
|
||||
//previous = _next[i];
|
||||
} else {
|
||||
previous = _last[i];
|
||||
}
|
||||
|
||||
byte *currentP = current[i];
|
||||
for (uint16 y = 0; y < height; y += 16) {
|
||||
for (uint16 x = 0; x < width; x += 16) {
|
||||
if (!svq1DecodeDeltaBlock(&frameData, ¤tP[x], previous, pitch, pmv, x, y)) {
|
||||
warning("svq1DecodeDeltaBlock decode failure");
|
||||
return _surface;
|
||||
}
|
||||
}
|
||||
|
||||
pmv[0].x = pmv[0].y = 0;
|
||||
|
||||
currentP += 16 * pitch;
|
||||
}
|
||||
|
||||
delete[] pmv;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we'll create the surface
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(yWidth, yHeight, _pixelFormat);
|
||||
_surface->w = _width;
|
||||
_surface->h = _height;
|
||||
}
|
||||
|
||||
// We need to massage the chrominance data a bit to be able to be used by the converter
|
||||
// Since the thing peeks at values one column and one row beyond the data, we need to fill it in
|
||||
|
||||
// First, fill in the column-after-last with the last column's value
|
||||
for (uint i = 0; i < uvHeight; i++) {
|
||||
current[1][i * uvPitch + uvWidth] = current[1][i * uvPitch + uvWidth - 1];
|
||||
current[2][i * uvPitch + uvWidth] = current[2][i * uvPitch + uvWidth - 1];
|
||||
}
|
||||
|
||||
// Then, copy the last row to the one after the last row
|
||||
memcpy(current[1] + uvHeight * uvPitch, current[1] + (uvHeight - 1) * uvPitch, uvWidth + 1);
|
||||
memcpy(current[2] + uvHeight * uvPitch, current[2] + (uvHeight - 1) * uvPitch, uvWidth + 1);
|
||||
|
||||
// Finally, actually do the conversion ;)
|
||||
YUVToRGBMan.convert410(_surface, Graphics::YUVToRGBManager::kScaleFull, current[0], current[1], current[2], yWidth, yHeight, yWidth, uvPitch);
|
||||
|
||||
// Store the current surfaces for later and free the old ones
|
||||
for (int i = 0; i < 3; i++) {
|
||||
delete[] _last[i];
|
||||
_last[i] = current[i];
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeBlockIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch) {
|
||||
// initialize list for breadth first processing of vectors
|
||||
byte *list[63];
|
||||
list[0] = pixels;
|
||||
|
||||
// recursively process vector
|
||||
for (int i = 0, m = 1, n = 1, level = 5; i < n; i++) {
|
||||
for (; level > 0; i++) {
|
||||
// process next depth
|
||||
if (i == m) {
|
||||
m = n;
|
||||
if (--level == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// divide block if next bit set
|
||||
if (s->getBit() == 0)
|
||||
break;
|
||||
|
||||
// add child nodes
|
||||
list[n++] = list[i];
|
||||
list[n++] = list[i] + (((level & 1) ? pitch : 1) << ((level / 2) + 1));
|
||||
}
|
||||
|
||||
// destination address and vector size
|
||||
uint32 *dst = (uint32 *)list[i];
|
||||
uint width = 1 << ((level + 4) / 2);
|
||||
uint height = 1 << ((level + 3) / 2);
|
||||
|
||||
// get number of stages (-1 skips vector, 0 for mean only)
|
||||
int stages = _intraMultistage[level]->getSymbol(*s) - 1;
|
||||
|
||||
if (stages == -1) {
|
||||
for (uint y = 0; y < height; y++)
|
||||
memset(&dst[y * (pitch / 4)], 0, width);
|
||||
|
||||
continue; // skip vector
|
||||
}
|
||||
|
||||
if (stages > 0 && level >= 4) {
|
||||
warning("Error (svq1_decode_block_intra): invalid vector: stages = %d, level = %d", stages, level);
|
||||
return false; // error - invalid vector
|
||||
}
|
||||
|
||||
int mean = _intraMean->getSymbol(*s);
|
||||
|
||||
if (stages == 0) {
|
||||
for (uint y = 0; y < height; y++)
|
||||
memset(&dst[y * (pitch / 4)], mean, width);
|
||||
} else {
|
||||
const uint32 *codebook = (const uint32 *)s_svq1IntraCodebooks[level];
|
||||
uint32 bitCache = s->getBits(stages * 4);
|
||||
|
||||
// calculate codebook entries for this vector
|
||||
int entries[6];
|
||||
for (int j = 0; j < stages; j++)
|
||||
entries[j] = (((bitCache >> ((stages - j - 1) * 4)) & 0xF) + j * 16) << (level + 1);
|
||||
|
||||
mean -= stages * 128;
|
||||
uint32 n4 = ((mean + (mean >> 31)) << 16) | (mean & 0xFFFF);
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
for (uint x = 0; x < (width / 4); x++, codebook++) {
|
||||
uint32 n1 = n4;
|
||||
uint32 n2 = n4;
|
||||
uint32 n3;
|
||||
|
||||
// add codebook entries to vector
|
||||
for (int j = 0; j < stages; j++) {
|
||||
n3 = READ_UINT32(&codebook[entries[j]]) ^ 0x80808080;
|
||||
n1 += (n3 & 0xFF00FF00) >> 8;
|
||||
n2 += n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// clip to [0..255]
|
||||
if (n1 & 0xFF00FF00) {
|
||||
n3 = (((n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 += 0x7F007F00;
|
||||
n1 |= (((~n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
if (n2 & 0xFF00FF00) {
|
||||
n3 = (((n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 += 0x7F007F00;
|
||||
n2 |= (((~n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// store result
|
||||
dst[x] = (n1 << 8) | n2;
|
||||
}
|
||||
|
||||
dst += pitch / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeBlockNonIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch) {
|
||||
// initialize list for breadth first processing of vectors
|
||||
byte *list[63];
|
||||
list[0] = pixels;
|
||||
|
||||
// recursively process vector
|
||||
for (int i = 0, m = 1, n = 1, level = 5; i < n; i++) {
|
||||
for (; level > 0; i++) {
|
||||
// process next depth
|
||||
if (i == m) {
|
||||
m = n;
|
||||
if (--level == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// divide block if next bit set
|
||||
if (s->getBit() == 0)
|
||||
break;
|
||||
|
||||
// add child nodes
|
||||
list[n++] = list[i];
|
||||
list[n++] = list[i] + (((level & 1) ? pitch : 1) << ((level / 2) + 1));
|
||||
}
|
||||
|
||||
// destination address and vector size
|
||||
uint32 *dst = (uint32 *)list[i];
|
||||
int width = 1 << ((level + 4) / 2);
|
||||
int height = 1 << ((level + 3) / 2);
|
||||
|
||||
// get number of stages (-1 skips vector, 0 for mean only)
|
||||
int stages = _interMultistage[level]->getSymbol(*s) - 1;
|
||||
|
||||
if (stages == -1)
|
||||
continue; // skip vector
|
||||
|
||||
if (stages > 0 && level >= 4) {
|
||||
warning("Error (svq1_decode_block_non_intra): invalid vector: stages = %d, level = %d", stages, level);
|
||||
return false; // error - invalid vector
|
||||
}
|
||||
|
||||
int mean = _interMean->getSymbol(*s) - 256;
|
||||
const uint32 *codebook = (const uint32 *)s_svq1InterCodebooks[level];
|
||||
uint32 bitCache = s->getBits(stages * 4);
|
||||
|
||||
// calculate codebook entries for this vector
|
||||
int entries[6];
|
||||
for (int j = 0; j < stages; j++)
|
||||
entries[j] = (((bitCache >> ((stages - j - 1) * 4)) & 0xF) + j * 16) << (level + 1);
|
||||
|
||||
mean -= stages * 128;
|
||||
uint32 n4 = ((mean + (mean >> 31)) << 16) | (mean & 0xFFFF);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < (width / 4); x++, codebook++) {
|
||||
uint32 n3 = dst[x];
|
||||
|
||||
// add mean value to vector
|
||||
uint32 n1 = ((n3 & 0xFF00FF00) >> 8) + n4;
|
||||
uint32 n2 = (n3 & 0x00FF00FF) + n4;
|
||||
|
||||
// add codebook entries to vector
|
||||
for (int j = 0; j < stages; j++) {
|
||||
n3 = READ_UINT32(&codebook[entries[j]]) ^ 0x80808080;
|
||||
n1 += (n3 & 0xFF00FF00) >> 8;
|
||||
n2 += n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// clip to [0..255]
|
||||
if (n1 & 0xFF00FF00) {
|
||||
n3 = ((( n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 += 0x7F007F00;
|
||||
n1 |= (((~n1 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n1 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
if (n2 & 0xFF00FF00) {
|
||||
n3 = ((( n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 += 0x7F007F00;
|
||||
n2 |= (((~n2 >> 15) & 0x00010001) | 0x01000100) - 0x00010001;
|
||||
n2 &= n3 & 0x00FF00FF;
|
||||
}
|
||||
|
||||
// store result
|
||||
dst[x] = (n1 << 8) | n2;
|
||||
}
|
||||
|
||||
dst += pitch / 4;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// median of 3
|
||||
static inline int midPred(int a, int b, int c) {
|
||||
if (a > b) {
|
||||
if (c > b) {
|
||||
if (c > a)
|
||||
b = a;
|
||||
else
|
||||
b = c;
|
||||
}
|
||||
} else {
|
||||
if (b > c) {
|
||||
if (c > a)
|
||||
b = c;
|
||||
else
|
||||
b = a;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeMotionVector(Common::BitStream32BEMSB *s, Common::Point *mv, Common::Point **pmv) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// get motion code
|
||||
int diff = _motionComponent->getSymbol(*s);
|
||||
if (diff < 0)
|
||||
return false; // error - invalid motion code
|
||||
else if (diff && s->getBit() != 0)
|
||||
diff = -diff;
|
||||
|
||||
// add median of motion vector predictors and clip result
|
||||
if (i == 1)
|
||||
mv->y = ((diff + midPred(pmv[0]->y, pmv[1]->y, pmv[2]->y)) << 26) >> 26;
|
||||
else
|
||||
mv->x = ((diff + midPred(pmv[0]->x, pmv[1]->x, pmv[2]->x)) << 26) >> 26;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SVQ1Decoder::svq1SkipBlock(byte *current, byte *previous, int pitch, int x, int y) {
|
||||
const byte *src = &previous[x + y * pitch];
|
||||
byte *dst = current;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
memcpy(dst, src, 16);
|
||||
src += pitch;
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
for (int i = 0; i < h; i++) {
|
||||
*((uint32 *)block) = READ_UINT32(pixels);
|
||||
*((uint32 *)(block + 4)) = READ_UINT32(pixels + 4);
|
||||
pixels += lineSize;
|
||||
block += lineSize;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32 rndAvg32(uint32 a, uint32 b) {
|
||||
return (a | b) - (((a ^ b) & ~0x01010101) >> 1);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8L2(byte *dst, const byte *src1, const byte *src2,
|
||||
int dstStride, int srcStride1, int srcStride2, int h) {
|
||||
for (int i = 0; i < h; i++) {
|
||||
uint32 a = READ_UINT32(&src1[srcStride1 * i]);
|
||||
uint32 b = READ_UINT32(&src2[srcStride2 * i]);
|
||||
*((uint32 *)&dst[dstStride * i]) = rndAvg32(a, b);
|
||||
a = READ_UINT32(&src1[srcStride1 * i + 4]);
|
||||
b = READ_UINT32(&src2[srcStride2 * i + 4]);
|
||||
*((uint32 *)&dst[dstStride * i + 4]) = rndAvg32(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8X2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8L2(block, pixels, pixels + 1, lineSize, lineSize, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8Y2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8L2(block, pixels, pixels + lineSize, lineSize, lineSize, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels8XY2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
uint32 a = READ_UINT32(pixels);
|
||||
uint32 b = READ_UINT32(pixels + 1);
|
||||
uint32 l0 = (a & 0x03030303UL) + (b & 0x03030303UL) + 0x02020202UL;
|
||||
uint32 h0 = ((a & 0xFCFCFCFCUL) >> 2) + ((b & 0xFCFCFCFCUL) >> 2);
|
||||
|
||||
pixels += lineSize;
|
||||
|
||||
for (int i = 0; i < h; i += 2) {
|
||||
a = READ_UINT32(pixels);
|
||||
b = READ_UINT32(pixels + 1);
|
||||
uint32 l1 = (a & 0x03030303UL) + (b & 0x03030303UL);
|
||||
uint32 h1 = ((a & 0xFCFCFCFCUL) >> 2) + ((b & 0xFCFCFCFCUL) >> 2);
|
||||
*((uint32 *)block) = h0 + h1 + (((l0 + l1) >> 2) & 0x0F0F0F0FUL);
|
||||
pixels += lineSize;
|
||||
block += lineSize;
|
||||
a = READ_UINT32(pixels);
|
||||
b = READ_UINT32(pixels + 1);
|
||||
l0 = (a & 0x03030303UL) + (b & 0x03030303UL) + 0x02020202UL;
|
||||
h0 = ((a & 0xFCFCFCFCUL) >> 2) + ((b & 0xFCFCFCFCUL) >> 2);
|
||||
*((uint32 *)block) = h0 + h1 + (((l0 + l1) >> 2) & 0x0F0F0F0FUL);
|
||||
pixels += lineSize;
|
||||
block += lineSize;
|
||||
}
|
||||
|
||||
pixels += 4 - lineSize * (h + 1);
|
||||
block += 4 - lineSize * h;
|
||||
}
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8C(block, pixels, lineSize, h);
|
||||
putPixels8C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16X2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8X2C(block, pixels, lineSize, h);
|
||||
putPixels8X2C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16Y2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8Y2C(block, pixels, lineSize, h);
|
||||
putPixels8Y2C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
void SVQ1Decoder::putPixels16XY2C(byte *block, const byte *pixels, int lineSize, int h) {
|
||||
putPixels8XY2C(block, pixels, lineSize, h);
|
||||
putPixels8XY2C(block + 8, pixels + 8, lineSize, h);
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1MotionInterBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y) {
|
||||
|
||||
// predict and decode motion vector
|
||||
Common::Point *pmv[3];
|
||||
pmv[0] = &motion[0];
|
||||
if (y == 0) {
|
||||
pmv[1] = pmv[2] = pmv[0];
|
||||
} else {
|
||||
pmv[1] = &motion[(x / 8) + 2];
|
||||
pmv[2] = &motion[(x / 8) + 4];
|
||||
}
|
||||
|
||||
Common::Point mv;
|
||||
bool resultValid = svq1DecodeMotionVector(ss, &mv, pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
motion[0].x = motion[(x / 8) + 2].x = motion[(x / 8) + 3].x = mv.x;
|
||||
motion[0].y = motion[(x / 8) + 2].y = motion[(x / 8) + 3].y = mv.y;
|
||||
|
||||
if (y + (mv.y >> 1) < 0)
|
||||
mv.y = 0;
|
||||
|
||||
if (x + (mv.x >> 1) < 0)
|
||||
mv.x = 0;
|
||||
|
||||
const byte *src = &previous[(x + (mv.x >> 1)) + (y + (mv.y >> 1)) * pitch];
|
||||
byte *dst = current;
|
||||
|
||||
// Halfpel motion compensation with rounding (a + b + 1) >> 1.
|
||||
// 4 motion compensation functions for the 4 halfpel positions
|
||||
// for 16x16 blocks
|
||||
switch(((mv.y & 1) << 1) + (mv.x & 1)) {
|
||||
case 0:
|
||||
putPixels16C(dst, src, pitch, 16);
|
||||
break;
|
||||
case 1:
|
||||
putPixels16X2C(dst, src, pitch, 16);
|
||||
break;
|
||||
case 2:
|
||||
putPixels16Y2C(dst, src, pitch, 16);
|
||||
break;
|
||||
case 3:
|
||||
putPixels16XY2C(dst, src, pitch, 16);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1MotionInter4vBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y) {
|
||||
// predict and decode motion vector (0)
|
||||
Common::Point *pmv[4];
|
||||
pmv[0] = &motion[0];
|
||||
if (y == 0) {
|
||||
pmv[1] = pmv[2] = pmv[0];
|
||||
} else {
|
||||
pmv[1] = &motion[(x / 8) + 2];
|
||||
pmv[2] = &motion[(x / 8) + 4];
|
||||
}
|
||||
|
||||
Common::Point mv;
|
||||
bool resultValid = svq1DecodeMotionVector(ss, &mv, pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// predict and decode motion vector (1)
|
||||
pmv[0] = &mv;
|
||||
if (y == 0)
|
||||
pmv[1] = pmv[2] = pmv[0];
|
||||
else
|
||||
pmv[1] = &motion[(x / 8) + 3];
|
||||
|
||||
resultValid = svq1DecodeMotionVector(ss, &motion[0], pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// predict and decode motion vector (2)
|
||||
pmv[1] = &motion[0];
|
||||
pmv[2] = &motion[(x / 8) + 1];
|
||||
|
||||
resultValid = svq1DecodeMotionVector(ss, &motion[(x / 8) + 2], pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// predict and decode motion vector (3)
|
||||
pmv[2] = &motion[(x / 8) + 2];
|
||||
pmv[3] = &motion[(x / 8) + 3];
|
||||
|
||||
resultValid = svq1DecodeMotionVector(ss, pmv[3], pmv);
|
||||
if (!resultValid)
|
||||
return false;
|
||||
|
||||
// form predictions
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int mvx = pmv[i]->x + (i & 1) * 16;
|
||||
int mvy = pmv[i]->y + (i >> 1) * 16;
|
||||
|
||||
// FIXME: clipping or padding?
|
||||
if (y + (mvy >> 1) < 0)
|
||||
mvy = 0;
|
||||
|
||||
if (x + (mvx >> 1) < 0)
|
||||
mvx = 0;
|
||||
|
||||
const byte *src = &previous[(x + (mvx >> 1)) + (y + (mvy >> 1)) * pitch];
|
||||
byte *dst = current;
|
||||
|
||||
// Halfpel motion compensation with rounding (a + b + 1) >> 1.
|
||||
// 4 motion compensation functions for the 4 halfpel positions
|
||||
// for 8x8 blocks
|
||||
switch(((mvy & 1) << 1) + (mvx & 1)) {
|
||||
case 0:
|
||||
putPixels8C(dst, src, pitch, 8);
|
||||
break;
|
||||
case 1:
|
||||
putPixels8X2C(dst, src, pitch, 8);
|
||||
break;
|
||||
case 2:
|
||||
putPixels8Y2C(dst, src, pitch, 8);
|
||||
break;
|
||||
case 3:
|
||||
putPixels8XY2C(dst, src, pitch, 8);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// select next block
|
||||
if (i & 1)
|
||||
current += (pitch - 1) * 8;
|
||||
else
|
||||
current += 8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVQ1Decoder::svq1DecodeDeltaBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y) {
|
||||
// get block type
|
||||
uint32 blockType = _blockType->getSymbol(*ss);
|
||||
|
||||
// reset motion vectors
|
||||
if (blockType == SVQ1_BLOCK_SKIP || blockType == SVQ1_BLOCK_INTRA) {
|
||||
motion[0].x =
|
||||
motion[0].y =
|
||||
motion[(x / 8) + 2].x =
|
||||
motion[(x / 8) + 2].y =
|
||||
motion[(x / 8) + 3].x =
|
||||
motion[(x / 8) + 3].y = 0;
|
||||
}
|
||||
|
||||
bool resultValid = true;
|
||||
|
||||
switch (blockType) {
|
||||
case SVQ1_BLOCK_SKIP:
|
||||
svq1SkipBlock(current, previous, pitch, x, y);
|
||||
break;
|
||||
case SVQ1_BLOCK_INTER:
|
||||
resultValid = svq1MotionInterBlock(ss, current, previous, pitch, motion, x, y);
|
||||
if (!resultValid) {
|
||||
warning("svq1MotionInterBlock decode failure");
|
||||
break;
|
||||
}
|
||||
resultValid = svq1DecodeBlockNonIntra(ss, current, pitch);
|
||||
break;
|
||||
case SVQ1_BLOCK_INTER_4V:
|
||||
resultValid = svq1MotionInter4vBlock(ss, current, previous, pitch, motion, x, y);
|
||||
if (!resultValid) {
|
||||
warning("svq1MotionInter4vBlock decode failure");
|
||||
break;
|
||||
}
|
||||
resultValid = svq1DecodeBlockNonIntra(ss, current, pitch);
|
||||
break;
|
||||
case SVQ1_BLOCK_INTRA:
|
||||
resultValid = svq1DecodeBlockIntra(ss, current, pitch);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return resultValid;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
98
image/codecs/svq1.h
Normal file
98
image/codecs/svq1.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_SVQ1_H
|
||||
#define IMAGE_CODECS_SVQ1_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "common/bitstream.h"
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Common {
|
||||
template <class BITSTREAM>
|
||||
class Huffman;
|
||||
struct Point;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Sorenson Vector Quantizer 1 decoder.
|
||||
*
|
||||
* Used by PICT/QuickTime.
|
||||
*/
|
||||
class SVQ1Decoder : public Codec {
|
||||
public:
|
||||
SVQ1Decoder(uint16 width, uint16 height);
|
||||
~SVQ1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Graphics::Surface *_surface;
|
||||
uint16 _width, _height;
|
||||
uint16 _frameWidth, _frameHeight;
|
||||
|
||||
byte *_last[3];
|
||||
|
||||
typedef Common::Huffman<Common::BitStream32BEMSB> HuffmanDecoder;
|
||||
|
||||
HuffmanDecoder *_blockType;
|
||||
HuffmanDecoder *_intraMultistage[6];
|
||||
HuffmanDecoder *_interMultistage[6];
|
||||
HuffmanDecoder *_intraMean;
|
||||
HuffmanDecoder *_interMean;
|
||||
HuffmanDecoder *_motionComponent;
|
||||
|
||||
bool svq1DecodeBlockIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch);
|
||||
bool svq1DecodeBlockNonIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch);
|
||||
bool svq1DecodeMotionVector(Common::BitStream32BEMSB *s, Common::Point *mv, Common::Point **pmv);
|
||||
void svq1SkipBlock(byte *current, byte *previous, int pitch, int x, int y);
|
||||
bool svq1MotionInterBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y);
|
||||
bool svq1MotionInter4vBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y);
|
||||
bool svq1DecodeDeltaBlock(Common::BitStream32BEMSB *ss, byte *current, byte *previous, int pitch,
|
||||
Common::Point *motion, int x, int y);
|
||||
|
||||
void putPixels8C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels8L2(byte *dst, const byte *src1, const byte *src2, int dstStride, int srcStride1, int srcStride2, int h);
|
||||
void putPixels8X2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels8Y2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels8XY2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16X2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16Y2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
void putPixels16XY2C(byte *block, const byte *pixels, int lineSize, int h);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
1510
image/codecs/svq1_cb.h
Normal file
1510
image/codecs/svq1_cb.h
Normal file
File diff suppressed because it is too large
Load Diff
340
image/codecs/svq1_vlc.h
Normal file
340
image/codecs/svq1_vlc.h
Normal file
@@ -0,0 +1,340 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// These tables are modified versions of the FFmpeg ones so that they
|
||||
// will work with our BitStream class directly.
|
||||
|
||||
#ifndef IMAGE_CODECS_SVQ1_VLC_H
|
||||
#define IMAGE_CODECS_SVQ1_VLC_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
static const byte s_svq1BlockTypeLengths[4] = {
|
||||
1, 2, 3, 3
|
||||
};
|
||||
|
||||
static const uint32 s_svq1BlockTypeCodes[4] = {
|
||||
1, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths0[8] = {
|
||||
5, 1, 3, 3, 4, 4, 5, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes0[8] = {
|
||||
1, 1, 3, 2, 3, 2, 0, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths1[8] = {
|
||||
4, 2, 3, 3, 3, 3, 4, 3
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes1[8] = {
|
||||
1, 3, 5, 4, 3, 2, 0, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths2[8] = {
|
||||
5, 1, 3, 5, 4, 3, 4, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes2[8] = {
|
||||
1, 1, 3, 0, 3, 2, 2, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths3[8] = {
|
||||
6, 1, 2, 6, 4, 4, 5, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes3[8] = {
|
||||
1, 1, 1, 0, 3, 2, 1, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths4[8] = {
|
||||
6, 1, 2, 5, 5, 6, 5, 3
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes4[8] = {
|
||||
1, 1, 1, 3, 2, 0, 1, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMultistageLengths5[8] = {
|
||||
7, 1, 2, 3, 4, 6, 7, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMultistageCodes5[8] = {
|
||||
1, 1, 1, 1, 1, 1, 0, 1
|
||||
};
|
||||
|
||||
static const byte *const s_svq1IntraMultistageLengths[6] = {
|
||||
s_svq1IntraMultistageLengths0, s_svq1IntraMultistageLengths1, s_svq1IntraMultistageLengths2,
|
||||
s_svq1IntraMultistageLengths3, s_svq1IntraMultistageLengths4, s_svq1IntraMultistageLengths5
|
||||
};
|
||||
|
||||
static const uint32 *const s_svq1IntraMultistageCodes[6] = {
|
||||
s_svq1IntraMultistageCodes0, s_svq1IntraMultistageCodes1, s_svq1IntraMultistageCodes2,
|
||||
s_svq1IntraMultistageCodes3, s_svq1IntraMultistageCodes4, s_svq1IntraMultistageCodes5
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths0[8] = {
|
||||
2, 3, 3, 3, 3, 3, 4, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes0[8] = {
|
||||
3, 5, 4, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths1[8] = {
|
||||
2, 3, 3, 3, 3, 3, 4, 4
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes1[8] = {
|
||||
3, 5, 4, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths2[8] = {
|
||||
1, 3, 3, 4, 4, 4, 5, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes2[8] = {
|
||||
1, 3, 2, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths3[8] = {
|
||||
1, 3, 3, 4, 4, 4, 5, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes3[8] = {
|
||||
1, 3, 2, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths4[8] = {
|
||||
1, 3, 3, 4, 4, 4, 5, 5
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes4[8] = {
|
||||
1, 3, 2, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMultistageLengths5[8] = {
|
||||
1, 2, 3, 5, 5, 5, 6, 6
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMultistageCodes5[8] = {
|
||||
1, 1, 1, 3, 2, 1, 1, 0
|
||||
};
|
||||
|
||||
static const byte *const s_svq1InterMultistageLengths[6] = {
|
||||
s_svq1InterMultistageLengths0, s_svq1InterMultistageLengths1, s_svq1InterMultistageLengths2,
|
||||
s_svq1InterMultistageLengths3, s_svq1InterMultistageLengths4, s_svq1InterMultistageLengths5
|
||||
};
|
||||
|
||||
static const uint32 *const s_svq1InterMultistageCodes[6] = {
|
||||
s_svq1InterMultistageCodes0, s_svq1InterMultistageCodes1, s_svq1InterMultistageCodes2,
|
||||
s_svq1InterMultistageCodes3, s_svq1InterMultistageCodes4, s_svq1InterMultistageCodes5
|
||||
};
|
||||
|
||||
static const byte s_svq1IntraMeanLengths[256] = {
|
||||
6, 7, 17, 20, 20, 20, 20, 20, 20, 19,
|
||||
11, 9, 11, 14, 14, 15, 16, 12, 10, 11,
|
||||
11, 9, 8, 8, 7, 4, 4, 6, 7, 8,
|
||||
8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 7, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 7, 8, 8, 7, 8,
|
||||
8, 8, 8, 8, 7, 8, 7, 7, 8, 7,
|
||||
7, 8, 7, 8, 8, 8, 7, 7, 8, 7,
|
||||
8, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 6, 6,
|
||||
7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 9,
|
||||
9, 9, 9, 9, 8, 8, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 11, 11, 11, 10, 11, 11, 11,
|
||||
11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||
11, 11, 11, 11, 11, 14
|
||||
};
|
||||
|
||||
static const uint32 s_svq1IntraMeanCodes[256] = {
|
||||
55, 86, 1, 1, 2, 3, 0, 4, 5, 3,
|
||||
21, 66, 20, 3, 2, 1, 1, 1, 43, 24,
|
||||
12, 65, 120, 108, 85, 15, 14, 52, 81, 114,
|
||||
110, 64, 63, 62, 61, 60, 59, 58, 57, 56,
|
||||
55, 67, 70, 71, 69, 68, 73, 72, 74, 121,
|
||||
118, 119, 113, 117, 116, 115, 106, 85, 112, 111,
|
||||
82, 109, 76, 107, 64, 105, 104, 103, 102, 101,
|
||||
100, 99, 98, 97, 96, 95, 94, 93, 92, 91,
|
||||
90, 89, 88, 87, 86, 61, 84, 83, 63, 81,
|
||||
80, 79, 78, 77, 65, 75, 83, 62, 72, 79,
|
||||
82, 69, 80, 67, 66, 65, 66, 67, 62, 68,
|
||||
60, 69, 70, 71, 72, 73, 74, 75, 76, 77,
|
||||
78, 88, 89, 90, 91, 92, 93, 68, 73, 41,
|
||||
63, 61, 59, 44, 40, 37, 38, 94, 87, 84,
|
||||
95, 98, 99, 100, 97, 101, 103, 102, 53, 54,
|
||||
96, 57, 58, 56, 55, 54, 53, 52, 51, 50,
|
||||
49, 48, 45, 43, 42, 39, 64, 70, 71, 38,
|
||||
37, 36, 35, 34, 46, 47, 31, 54, 29, 33,
|
||||
27, 28, 25, 26, 24, 23, 22, 30, 32, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 53,
|
||||
49, 50, 51, 52, 25, 42, 23, 22, 21, 40,
|
||||
38, 37, 34, 33, 24, 20, 41, 18, 13, 14,
|
||||
15, 16, 17, 26, 27, 28, 29, 30, 31, 32,
|
||||
19, 35, 36, 9, 8, 7, 39, 5, 11, 6,
|
||||
4, 3, 2, 1, 10, 22, 25, 23, 13, 14,
|
||||
15, 16, 17, 18, 19, 1
|
||||
};
|
||||
|
||||
static const byte s_svq1InterMeanLengths[512] = {
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 21, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 21,
|
||||
22, 22, 22, 22, 22, 22, 20, 21, 22, 21,
|
||||
22, 22, 20, 22, 22, 21, 19, 18, 20, 22,
|
||||
22, 21, 20, 19, 20, 20, 19, 19, 19, 18,
|
||||
19, 18, 19, 20, 19, 19, 18, 18, 18, 19,
|
||||
18, 18, 18, 17, 19, 18, 18, 17, 18, 18,
|
||||
18, 17, 17, 17, 17, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 15, 16, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 12, 12, 12, 12, 12, 12, 11, 11, 11,
|
||||
11, 11, 11, 10, 10, 10, 10, 10, 10, 9,
|
||||
9, 9, 9, 9, 8, 8, 8, 8, 7, 7,
|
||||
7, 6, 6, 5, 5, 4, 1, 3, 5, 5,
|
||||
6, 6, 7, 7, 7, 7, 8, 8, 8, 9,
|
||||
9, 9, 9, 9, 10, 10, 10, 10, 11, 11,
|
||||
11, 11, 11, 11, 11, 12, 12, 12, 12, 12,
|
||||
12, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 18, 17, 17, 17,
|
||||
17, 17, 17, 17, 19, 18, 18, 19, 18, 18,
|
||||
19, 18, 18, 18, 19, 18, 19, 19, 18, 20,
|
||||
20, 19, 19, 19, 19, 19, 19, 18, 19, 20,
|
||||
19, 19, 21, 20, 19, 20, 19, 19, 20, 20,
|
||||
22, 20, 22, 22, 21, 22, 22, 21, 21, 22,
|
||||
22, 20, 22, 22, 21, 22, 22, 22, 20, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 21, 21, 22, 22, 22, 21,
|
||||
22, 21, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22
|
||||
};
|
||||
|
||||
static const uint32 s_svq1InterMeanCodes[512] = {
|
||||
90, 212, 213, 214, 215, 216, 217, 218, 219, 220,
|
||||
221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
|
||||
232, 203, 233, 234, 231, 236, 237, 238, 239, 240,
|
||||
241, 242, 243, 244, 245, 246, 247, 248, 258, 235,
|
||||
249, 252, 253, 254, 256, 92, 96, 257, 113, 260,
|
||||
261, 251, 255, 134, 250, 124, 117, 259, 120, 211,
|
||||
123, 130, 210, 209, 208, 207, 206, 205, 204, 195,
|
||||
202, 201, 200, 199, 198, 197, 139, 196, 194, 193,
|
||||
192, 191, 190, 189, 188, 187, 186, 185, 97, 132,
|
||||
133, 134, 135, 136, 137, 138, 140, 141, 142, 143,
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
|
||||
154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
|
||||
164, 165, 166, 167, 168, 169, 170, 171, 127, 143,
|
||||
172, 173, 174, 175, 176, 177, 83, 144, 178, 145,
|
||||
179, 180, 84, 181, 182, 140, 52, 61, 85, 183,
|
||||
184, 139, 86, 61, 87, 88, 64, 67, 71, 42,
|
||||
46, 44, 70, 89, 73, 45, 56, 54, 57, 69,
|
||||
40, 48, 53, 32, 68, 50, 49, 31, 47, 46,
|
||||
45, 33, 34, 35, 36, 39, 35, 32, 29, 37,
|
||||
30, 36, 42, 38, 33, 41, 34, 35, 36, 27,
|
||||
26, 29, 31, 39, 23, 24, 25, 27, 28, 30,
|
||||
37, 32, 33, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 24, 23, 21, 20, 19, 18, 15, 16, 18,
|
||||
19, 27, 26, 14, 19, 15, 16, 17, 18, 13,
|
||||
20, 21, 12, 19, 15, 14, 16, 17, 12, 9,
|
||||
10, 8, 9, 9, 8, 5, 1, 3, 7, 6,
|
||||
11, 10, 14, 15, 11, 13, 11, 13, 12, 15,
|
||||
16, 17, 14, 18, 23, 20, 22, 21, 25, 24,
|
||||
23, 22, 21, 20, 17, 25, 26, 22, 29, 27,
|
||||
28, 32, 28, 35, 34, 33, 31, 30, 27, 29,
|
||||
36, 22, 26, 34, 29, 31, 21, 35, 24, 32,
|
||||
41, 40, 38, 37, 25, 28, 30, 23, 44, 43,
|
||||
28, 33, 45, 40, 31, 27, 26, 34, 45, 50,
|
||||
44, 39, 49, 51, 47, 43, 55, 42, 46, 48,
|
||||
41, 40, 38, 37, 47, 51, 52, 48, 58, 59,
|
||||
49, 60, 43, 41, 72, 39, 66, 65, 38, 82,
|
||||
81, 63, 62, 57, 60, 59, 58, 37, 56, 80,
|
||||
55, 54, 135, 79, 53, 78, 51, 50, 77, 76,
|
||||
131, 75, 129, 128, 142, 126, 125, 132, 141, 122,
|
||||
121, 74, 119, 118, 137, 116, 115, 114, 73, 112,
|
||||
111, 110, 109, 108, 107, 106, 105, 104, 103, 102,
|
||||
101, 100, 99, 98, 138, 136, 95, 94, 93, 133,
|
||||
91, 131, 89, 88, 87, 86, 85, 84, 83, 82,
|
||||
81, 80, 79, 78, 77, 76, 75, 74, 73, 72,
|
||||
71, 70, 69, 68, 67, 66, 65, 64, 63, 62,
|
||||
61, 60, 59, 58, 57, 56, 55, 54, 53, 52,
|
||||
51, 50, 49, 48, 47, 46, 45, 44, 43, 42,
|
||||
41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
|
||||
31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
|
||||
21, 20, 19, 18, 17, 16, 15, 14, 13, 12,
|
||||
11, 10, 9, 8, 7, 6, 5, 4, 3, 2,
|
||||
1, 0
|
||||
};
|
||||
|
||||
static const byte s_svq1MotionComponentLengths[33] = {
|
||||
1, 2, 3, 4, 6, 7, 7, 7, 9, 9,
|
||||
9, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 11, 11, 11, 11, 11,
|
||||
11, 12, 12
|
||||
};
|
||||
|
||||
static const uint32 s_svq1MotionComponentCodes[33] = {
|
||||
1, 1, 1, 1, 3, 5, 4, 3, 11, 10,
|
||||
9, 17, 16, 15, 14, 13, 12, 11, 10, 9,
|
||||
8, 7, 6, 5, 4, 7, 6, 5, 4, 3,
|
||||
2, 3, 2
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
425
image/codecs/truemotion1.cpp
Normal file
425
image/codecs/truemotion1.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the TrueMotion 1 decoder by Alex Beregszaszi & Mike Melanson in FFmpeg
|
||||
|
||||
#include "image/codecs/truemotion1.h"
|
||||
#include "image/codecs/truemotion1data.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
enum {
|
||||
FLAG_SPRITE = (1 << 5),
|
||||
FLAG_KEYFRAME = (1 << 4),
|
||||
FLAG_INTERFRAME = (1 << 3),
|
||||
FLAG_INTERPOLATED = (1 << 2)
|
||||
};
|
||||
|
||||
enum {
|
||||
ALGO_NOP = 0,
|
||||
ALGO_RGB16V = 1,
|
||||
ALGO_RGB16H = 2,
|
||||
ALGO_RGB24H = 3
|
||||
};
|
||||
|
||||
// these are the various block sizes that can occupy a 4x4 block
|
||||
enum {
|
||||
BLOCK_2x2 = 0,
|
||||
BLOCK_2x4 = 1,
|
||||
BLOCK_4x2 = 2,
|
||||
BLOCK_4x4 = 3
|
||||
};
|
||||
|
||||
// { valid for metatype }, algorithm, num of deltas, vert res, horiz res
|
||||
struct CompressionType {
|
||||
int algorithm;
|
||||
int blockWidth; // vres
|
||||
int blockHeight; // hres
|
||||
int blockType;
|
||||
};
|
||||
|
||||
static const CompressionType compressionTypes[17] = {
|
||||
{ ALGO_NOP, 0, 0, 0 },
|
||||
|
||||
{ ALGO_RGB16V, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_RGB16H, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_RGB16V, 4, 2, BLOCK_4x2 },
|
||||
{ ALGO_RGB16H, 4, 2, BLOCK_4x2 },
|
||||
|
||||
{ ALGO_RGB16V, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_RGB16H, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_RGB16V, 2, 2, BLOCK_2x2 },
|
||||
{ ALGO_RGB16H, 2, 2, BLOCK_2x2 },
|
||||
|
||||
{ ALGO_NOP, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_RGB24H, 4, 4, BLOCK_4x4 },
|
||||
{ ALGO_NOP, 4, 2, BLOCK_4x2 },
|
||||
{ ALGO_RGB24H, 4, 2, BLOCK_4x2 },
|
||||
|
||||
{ ALGO_NOP, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_RGB24H, 2, 4, BLOCK_2x4 },
|
||||
{ ALGO_NOP, 2, 2, BLOCK_2x2 },
|
||||
{ ALGO_RGB24H, 2, 2, BLOCK_2x2 }
|
||||
};
|
||||
|
||||
TrueMotion1Decoder::TrueMotion1Decoder() {
|
||||
_surface = 0;
|
||||
_vertPred = 0;
|
||||
|
||||
_buf = _mbChangeBits = _indexStream = 0;
|
||||
_lastDeltaset = _lastVectable = -1;
|
||||
}
|
||||
|
||||
TrueMotion1Decoder::~TrueMotion1Decoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
delete[] _vertPred;
|
||||
}
|
||||
|
||||
void TrueMotion1Decoder::selectDeltaTables(int deltaTableIndex) {
|
||||
if (deltaTableIndex > 3)
|
||||
return;
|
||||
|
||||
for (byte i = 0; i < 8; i++) {
|
||||
_ydt[i] = ydts[deltaTableIndex][i];
|
||||
_cdt[i] = cdts[deltaTableIndex][i];
|
||||
|
||||
// Y skinny deltas need to be halved for some reason; maybe the
|
||||
// skinny Y deltas should be modified
|
||||
// Drop the lsb before dividing by 2-- net effect: round down
|
||||
// when dividing a negative number (e.g., -3/2 = -2, not -1)
|
||||
_ydt[i] &= 0xFFFE;
|
||||
_ydt[i] /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
int TrueMotion1Decoder::makeYdt16Entry(int p1, int p2) {
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
// Swap the values on BE systems. FFmpeg does this too.
|
||||
SWAP<int>(p1, p2);
|
||||
#endif
|
||||
|
||||
int lo = _ydt[p1];
|
||||
lo += (lo << 6) + (lo << 11);
|
||||
int hi = _ydt[p2];
|
||||
hi += (hi << 6) + (hi << 11);
|
||||
return lo + (hi << 16);
|
||||
}
|
||||
|
||||
int TrueMotion1Decoder::makeCdt16Entry(int p1, int p2) {
|
||||
int b = _cdt[p2];
|
||||
int r = _cdt[p1] << 11;
|
||||
int lo = b + r;
|
||||
return lo + (lo << 16);
|
||||
}
|
||||
|
||||
void TrueMotion1Decoder::genVectorTable16(const byte *selVectorTable) {
|
||||
memset(&_yPredictorTable, 0, sizeof(PredictorTableEntry) * 1024);
|
||||
memset(&_cPredictorTable, 0, sizeof(PredictorTableEntry) * 1024);
|
||||
|
||||
for (int i = 0; i < 1024; i += 4) {
|
||||
int len = *selVectorTable++ / 2;
|
||||
for (int j = 0; j < len; j++) {
|
||||
byte deltaPair = *selVectorTable++;
|
||||
_yPredictorTable[i + j].color = makeYdt16Entry(deltaPair >> 4, deltaPair & 0xf);
|
||||
_cPredictorTable[i + j].color = makeCdt16Entry(deltaPair >> 4, deltaPair & 0xf);
|
||||
}
|
||||
|
||||
_yPredictorTable[i + (len - 1)].getNextIndex = true;
|
||||
_cPredictorTable[i + (len - 1)].getNextIndex = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TrueMotion1Decoder::decodeHeader(Common::SeekableReadStream &stream) {
|
||||
_buf = new byte[stream.size()];
|
||||
stream.read(_buf, stream.size());
|
||||
|
||||
byte headerBuffer[128]; // logical maximum size of the header
|
||||
const byte *selVectorTable;
|
||||
|
||||
_header.headerSize = ((_buf[0] >> 5) | (_buf[0] << 3)) & 0x7f;
|
||||
|
||||
if (_buf[0] < 0x10)
|
||||
error("Invalid TrueMotion1 header size %d", _header.headerSize);
|
||||
|
||||
// unscramble the header bytes with a XOR operation
|
||||
memset(headerBuffer, 0, 128);
|
||||
for (int i = 1; i < _header.headerSize; i++)
|
||||
headerBuffer[i - 1] = _buf[i] ^ _buf[i + 1];
|
||||
|
||||
_header.compression = headerBuffer[0];
|
||||
_header.deltaset = headerBuffer[1];
|
||||
_header.vectable = headerBuffer[2];
|
||||
_header.ysize = READ_LE_UINT16(&headerBuffer[3]);
|
||||
_header.xsize = READ_LE_UINT16(&headerBuffer[5]);
|
||||
_header.checksum = READ_LE_UINT16(&headerBuffer[7]);
|
||||
_header.version = headerBuffer[9];
|
||||
_header.headerType = headerBuffer[10];
|
||||
_header.flags = headerBuffer[11];
|
||||
_header.control = headerBuffer[12];
|
||||
|
||||
if (!_vertPred) {
|
||||
// there is a vertical predictor for each pixel in a line; each vertical
|
||||
// predictor is 0 to start with
|
||||
_vertPred = new uint32[_header.xsize];
|
||||
}
|
||||
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_header.xsize, _header.ysize, getPixelFormat());
|
||||
}
|
||||
|
||||
// There is 1 change bit per 4 pixels, so each change byte represents
|
||||
// 32 pixels; divide width by 4 to obtain the number of change bits and
|
||||
// then round up to the nearest byte.
|
||||
_mbChangeBitsRowSize = ((_header.xsize >> 2) + 7) >> 3;
|
||||
|
||||
// Version 2
|
||||
if (_header.version >= 2) {
|
||||
if (_header.headerType > 3) {
|
||||
error("Invalid header type %d", _header.headerType);
|
||||
} else if (_header.headerType == 2 || _header.headerType == 3) {
|
||||
_flags = _header.flags;
|
||||
if (!(_flags & FLAG_INTERFRAME))
|
||||
_flags |= FLAG_KEYFRAME;
|
||||
} else
|
||||
_flags = FLAG_KEYFRAME;
|
||||
} else // Version 1
|
||||
_flags = FLAG_KEYFRAME;
|
||||
|
||||
if (_flags & FLAG_SPRITE) {
|
||||
error("SPRITE frame found, please report the sample to the developers");
|
||||
} else if (_header.headerType < 2 && _header.xsize < 213 && _header.ysize >= 176) {
|
||||
_flags |= FLAG_INTERPOLATED;
|
||||
error("INTERPOLATION selected, please report the sample to the developers");
|
||||
}
|
||||
|
||||
if (_header.compression >= 17)
|
||||
error("Invalid TrueMotion1 compression type %d", _header.compression);
|
||||
|
||||
if (_header.deltaset != _lastDeltaset || _header.vectable != _lastVectable)
|
||||
selectDeltaTables(_header.deltaset);
|
||||
|
||||
if ((_header.compression & 1) && _header.headerType)
|
||||
selVectorTable = pc_tbl2;
|
||||
else if (_header.vectable < 4)
|
||||
selVectorTable = tables[_header.vectable - 1];
|
||||
else
|
||||
error("Invalid vector table id %d", _header.vectable);
|
||||
|
||||
if (_header.deltaset != _lastDeltaset || _header.vectable != _lastVectable)
|
||||
genVectorTable16(selVectorTable);
|
||||
|
||||
// set up pointers to the other key data chunks
|
||||
_mbChangeBits = _buf + _header.headerSize;
|
||||
|
||||
if (_flags & FLAG_KEYFRAME) {
|
||||
// no change bits specified for a keyframe; only index bytes
|
||||
_indexStream = _mbChangeBits;
|
||||
} else {
|
||||
// one change bit per 4x4 block
|
||||
_indexStream = _mbChangeBits + _mbChangeBitsRowSize * (_header.ysize >> 2);
|
||||
}
|
||||
|
||||
_indexStreamSize = stream.size() - (_indexStream - _buf);
|
||||
|
||||
_lastDeltaset = _header.deltaset;
|
||||
_lastVectable = _header.vectable;
|
||||
_blockWidth = compressionTypes[_header.compression].blockWidth;
|
||||
_blockHeight = compressionTypes[_header.compression].blockHeight;
|
||||
_blockType = compressionTypes[_header.compression].blockType;
|
||||
}
|
||||
|
||||
#define GET_NEXT_INDEX() \
|
||||
do { \
|
||||
if (indexStreamIndex >= _indexStreamSize) \
|
||||
error("TrueMotion1 decoder went out of bounds"); \
|
||||
index = _indexStream[indexStreamIndex++] * 4; \
|
||||
} while (0) \
|
||||
|
||||
#define APPLY_C_PREDICTOR() \
|
||||
predictor_pair = _cPredictorTable[index].color; \
|
||||
horizPred += predictor_pair; \
|
||||
if (_cPredictorTable[index].getNextIndex) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
if (!index) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
predictor_pair = _cPredictorTable[index].color; \
|
||||
horizPred += predictor_pair * 5; \
|
||||
if (_cPredictorTable[index].getNextIndex) \
|
||||
GET_NEXT_INDEX(); \
|
||||
else \
|
||||
index++; \
|
||||
} \
|
||||
} else \
|
||||
index++
|
||||
|
||||
#define APPLY_Y_PREDICTOR() \
|
||||
predictor_pair = _yPredictorTable[index].color; \
|
||||
horizPred += predictor_pair; \
|
||||
if (_yPredictorTable[index].getNextIndex) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
if (!index) { \
|
||||
GET_NEXT_INDEX(); \
|
||||
predictor_pair = _yPredictorTable[index].color; \
|
||||
horizPred += predictor_pair * 5; \
|
||||
if (_yPredictorTable[index].getNextIndex) \
|
||||
GET_NEXT_INDEX(); \
|
||||
else \
|
||||
index++; \
|
||||
} \
|
||||
} else \
|
||||
index++
|
||||
|
||||
#define OUTPUT_PIXEL_PAIR() \
|
||||
*currentPixelPair = *vertPred + horizPred; \
|
||||
*vertPred++ = *currentPixelPair++
|
||||
|
||||
void TrueMotion1Decoder::decode16() {
|
||||
uint32 predictor_pair;
|
||||
bool keyframe = _flags & FLAG_KEYFRAME;
|
||||
int indexStreamIndex = 0;
|
||||
|
||||
// these variables are for managing the main index stream
|
||||
int index;
|
||||
|
||||
// clean out the line buffer
|
||||
memset(_vertPred, 0, _header.xsize * 4);
|
||||
|
||||
GET_NEXT_INDEX();
|
||||
|
||||
for (int y = 0; y < _header.ysize; y++) {
|
||||
// re-init variables for the next line iteration
|
||||
uint32 horizPred = 0;
|
||||
uint32 *currentPixelPair = (uint32 *)_surface->getBasePtr(0, y);
|
||||
uint32 *vertPred = _vertPred;
|
||||
int mbChangeIndex = 0;
|
||||
byte mbChangeByte = _mbChangeBits[mbChangeIndex++];
|
||||
byte mbChangeByteMask = 1;
|
||||
|
||||
for (int pixelsLeft = _header.xsize; pixelsLeft > 0; pixelsLeft -= 4) {
|
||||
if (keyframe || (mbChangeByte & mbChangeByteMask) == 0) {
|
||||
switch (y & 3) {
|
||||
case 0:
|
||||
// if macroblock width is 2, apply C-Y-C-Y; else
|
||||
// apply C-Y-Y
|
||||
if (_blockWidth == 2) {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
} else {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
// always apply 2 Y predictors on these iterations
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
break;
|
||||
case 2:
|
||||
// this iteration might be C-Y-C-Y, Y-Y, or C-Y-Y
|
||||
// depending on the macroblock type
|
||||
if (_blockType == BLOCK_2x2) {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
} else if (_blockType == BLOCK_4x2) {
|
||||
APPLY_C_PREDICTOR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
} else {
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
APPLY_Y_PREDICTOR();
|
||||
OUTPUT_PIXEL_PAIR();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// skip (copy) four pixels, but reassign the horizontal
|
||||
// predictor
|
||||
*vertPred++ = *currentPixelPair++;
|
||||
horizPred = *currentPixelPair - *vertPred;
|
||||
*vertPred++ = *currentPixelPair++;
|
||||
}
|
||||
|
||||
if (!keyframe) {
|
||||
mbChangeByteMask <<= 1;
|
||||
|
||||
// next byte
|
||||
if (!mbChangeByteMask) {
|
||||
mbChangeByte = _mbChangeBits[mbChangeIndex++];
|
||||
mbChangeByteMask = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next change row
|
||||
if (((y + 1) & 3) == 0)
|
||||
_mbChangeBits += _mbChangeBitsRowSize;
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *TrueMotion1Decoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
decodeHeader(stream);
|
||||
|
||||
if (compressionTypes[_header.compression].algorithm == ALGO_NOP) {
|
||||
delete[] _buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (compressionTypes[_header.compression].algorithm == ALGO_RGB24H) {
|
||||
warning("Unhandled TrueMotion1 24bpp frame");
|
||||
delete[] _buf;
|
||||
return 0;
|
||||
} else
|
||||
decode16();
|
||||
|
||||
delete[] _buf;
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
102
image/codecs/truemotion1.h
Normal file
102
image/codecs/truemotion1.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the TrueMotion 1 decoder by Alex Beregszaszi & Mike Melanson in FFmpeg
|
||||
|
||||
#ifndef IMAGE_CODECS_TRUEMOTION1_H
|
||||
#define IMAGE_CODECS_TRUEMOTION1_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Duck TrueMotion 1 decoder.
|
||||
*
|
||||
* Used by BMP/AVI.
|
||||
*/
|
||||
class TrueMotion1Decoder : public Codec {
|
||||
public:
|
||||
TrueMotion1Decoder();
|
||||
~TrueMotion1Decoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
// Always return RGB565
|
||||
Graphics::PixelFormat getPixelFormat() const override { return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); }
|
||||
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
int _mbChangeBitsRowSize;
|
||||
byte *_buf, *_mbChangeBits, *_indexStream;
|
||||
int _indexStreamSize;
|
||||
|
||||
int _flags;
|
||||
|
||||
struct PredictorTableEntry {
|
||||
uint32 color;
|
||||
bool getNextIndex;
|
||||
};
|
||||
|
||||
PredictorTableEntry _yPredictorTable[1024];
|
||||
PredictorTableEntry _cPredictorTable[1024];
|
||||
|
||||
int _blockType;
|
||||
int _blockWidth;
|
||||
int _blockHeight;
|
||||
|
||||
int16 _ydt[8];
|
||||
int16 _cdt[8];
|
||||
|
||||
int _lastDeltaset, _lastVectable;
|
||||
|
||||
uint32 *_vertPred;
|
||||
|
||||
struct {
|
||||
byte headerSize;
|
||||
byte compression;
|
||||
byte deltaset;
|
||||
byte vectable;
|
||||
uint16 ysize;
|
||||
uint16 xsize;
|
||||
uint16 checksum;
|
||||
byte version;
|
||||
byte headerType;
|
||||
byte flags;
|
||||
byte control;
|
||||
uint16 xoffset;
|
||||
uint16 yoffset;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
} _header;
|
||||
|
||||
void selectDeltaTables(int deltaTableIndex);
|
||||
void decodeHeader(Common::SeekableReadStream &stream);
|
||||
void decode16();
|
||||
int makeYdt16Entry(int p1, int p2);
|
||||
int makeCdt16Entry(int p1, int p2);
|
||||
void genVectorTable16(const byte *selVectorTable);
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif // IMAGE_CODECS_TRUEMOTION1_H
|
||||
828
image/codecs/truemotion1data.h
Normal file
828
image/codecs/truemotion1data.h
Normal file
@@ -0,0 +1,828 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the TrueMotion 1 decoder by Alex Beregszaszi & Mike Melanson in FFmpeg
|
||||
// These tables were originally part of VpVision from On2
|
||||
|
||||
#ifndef IMAGE_CODECS_TRUEMOTION1DATA_H
|
||||
#define IMAGE_CODECS_TRUEMOTION1DATA_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
// Y delta tables, skinny and fat
|
||||
static const int16 ydt1[8] = { 0, -2, 2, -6, 6, -12, 12, -12 };
|
||||
static const int16 ydt2[8] = { 0, -2, 4, -6, 8, -12, 12, -12 };
|
||||
static const int16 ydt3[8] = { 4, -6, 20, -20, 46, -46, 94, -94 };
|
||||
static const int16 ydt4[8] = { 0, -4, 4, -16, 16, -36, 36, -80 };
|
||||
|
||||
// C delta tables, skinny and fat
|
||||
static const int16 cdt1[8] = { 0, -1, 1, -2, 3, -4, 5, -4 };
|
||||
static const int16 cdt2[8] = { 0, -4, 3, -16, 20, -32, 36, -32 };
|
||||
static const int16 cdt3[8] = { 0, -2, 2, -8, 8, -18, 18, -40 };
|
||||
|
||||
// all the delta tables to choose from, at all 4 delta levels
|
||||
static const int16 * const ydts[] = { ydt1, ydt2, ydt3, ydt4, NULL };
|
||||
static const int16 * const cdts[] = { cdt1, cdt1, cdt2, cdt3, NULL };
|
||||
|
||||
static const byte pc_tbl2[] = {
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x10,0x00,0x00,0x00,
|
||||
0x8,0x01,0x00,0x00,0x00,
|
||||
0x8,0x00,0x10,0x00,0x00,
|
||||
0x8,0x00,0x01,0x00,0x00,
|
||||
0x8,0x00,0x00,0x10,0x00,
|
||||
0x8,0x00,0x00,0x01,0x00,
|
||||
0x8,0x00,0x00,0x00,0x10,
|
||||
0x8,0x00,0x00,0x00,0x01,
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x10,0x00,0x00,
|
||||
0x6,0x01,0x00,0x00,
|
||||
0x6,0x00,0x10,0x00,
|
||||
0x6,0x00,0x01,0x00,
|
||||
0x6,0x00,0x00,0x01,
|
||||
0x6,0x00,0x00,0x10,
|
||||
0x6,0x00,0x00,0x02,
|
||||
0x6,0x00,0x00,0x20,
|
||||
0x6,0x20,0x10,0x00,
|
||||
0x6,0x00,0x02,0x01,
|
||||
0x6,0x00,0x20,0x10,
|
||||
0x6,0x02,0x01,0x00,
|
||||
0x6,0x11,0x00,0x00,
|
||||
0x6,0x00,0x20,0x00,
|
||||
0x6,0x00,0x02,0x00,
|
||||
0x6,0x20,0x00,0x00,
|
||||
0x6,0x01,0x10,0x00,
|
||||
0x6,0x02,0x00,0x00,
|
||||
0x6,0x01,0x00,0x02,
|
||||
0x6,0x10,0x00,0x20,
|
||||
0x6,0x00,0x01,0x02,
|
||||
0x6,0x10,0x01,0x00,
|
||||
0x6,0x00,0x10,0x20,
|
||||
0x6,0x10,0x10,0x00,
|
||||
0x6,0x10,0x00,0x01,
|
||||
0x6,0x20,0x00,0x10,
|
||||
0x6,0x02,0x00,0x01,
|
||||
0x6,0x01,0x01,0x00,
|
||||
0x6,0x01,0x00,0x10,
|
||||
0x6,0x00,0x11,0x00,
|
||||
0x6,0x10,0x00,0x02,
|
||||
0x6,0x00,0x01,0x10,
|
||||
0x6,0x00,0x00,0x11,
|
||||
0x6,0x10,0x00,0x10,
|
||||
0x6,0x01,0x00,0x01,
|
||||
0x6,0x00,0x00,0x22,
|
||||
0x6,0x02,0x01,0x01,
|
||||
0x6,0x10,0x20,0x10,
|
||||
0x6,0x01,0x02,0x01,
|
||||
0x6,0x20,0x10,0x10,
|
||||
0x6,0x01,0x00,0x20,
|
||||
0x6,0x00,0x10,0x01,
|
||||
0x6,0x21,0x10,0x00,
|
||||
0x6,0x10,0x02,0x01,
|
||||
0x6,0x12,0x01,0x00,
|
||||
0x6,0x01,0x20,0x10,
|
||||
0x6,0x01,0x02,0x00,
|
||||
0x6,0x10,0x20,0x00,
|
||||
0x6,0x00,0x10,0x02,
|
||||
0x6,0x00,0x01,0x20,
|
||||
0x6,0x00,0x02,0x21,
|
||||
0x6,0x00,0x02,0x20,
|
||||
0x6,0x00,0x00,0x12,
|
||||
0x6,0x00,0x00,0x21,
|
||||
0x6,0x20,0x11,0x00,
|
||||
0x6,0x00,0x01,0x01,
|
||||
0x6,0x11,0x10,0x00,
|
||||
0x6,0x00,0x20,0x12,
|
||||
0x6,0x00,0x20,0x11,
|
||||
0x6,0x20,0x10,0x02,
|
||||
0x6,0x02,0x01,0x20,
|
||||
0x6,0x00,0x22,0x11,
|
||||
0x6,0x00,0x10,0x10,
|
||||
0x6,0x02,0x11,0x00,
|
||||
0x6,0x00,0x21,0x10,
|
||||
0x6,0x00,0x02,0x03,
|
||||
0x6,0x20,0x10,0x01,
|
||||
0x6,0x00,0x12,0x01,
|
||||
0x4,0x11,0x00,
|
||||
0x4,0x00,0x22,
|
||||
0x4,0x20,0x00,
|
||||
0x4,0x01,0x10,
|
||||
0x4,0x02,0x20,
|
||||
0x4,0x00,0x20,
|
||||
0x4,0x02,0x00,
|
||||
0x4,0x10,0x01,
|
||||
0x4,0x00,0x11,
|
||||
0x4,0x02,0x01,
|
||||
0x4,0x02,0x21,
|
||||
0x4,0x00,0x02,
|
||||
0x4,0x20,0x02,
|
||||
0x4,0x01,0x01,
|
||||
0x4,0x10,0x10,
|
||||
0x4,0x10,0x02,
|
||||
0x4,0x22,0x00,
|
||||
0x4,0x10,0x00,
|
||||
0x4,0x01,0x00,
|
||||
0x4,0x21,0x00,
|
||||
0x4,0x12,0x00,
|
||||
0x4,0x00,0x10,
|
||||
0x4,0x20,0x12,
|
||||
0x4,0x01,0x11,
|
||||
0x4,0x00,0x01,
|
||||
0x4,0x01,0x02,
|
||||
0x4,0x11,0x02,
|
||||
0x4,0x11,0x01,
|
||||
0x4,0x10,0x20,
|
||||
0x4,0x20,0x01,
|
||||
0x4,0x22,0x11,
|
||||
0x4,0x00,0x12,
|
||||
0x4,0x20,0x10,
|
||||
0x4,0x22,0x01,
|
||||
0x4,0x01,0x20,
|
||||
0x4,0x00,0x21,
|
||||
0x4,0x10,0x11,
|
||||
0x4,0x21,0x10,
|
||||
0x4,0x10,0x22,
|
||||
0x4,0x02,0x03,
|
||||
0x4,0x12,0x01,
|
||||
0x4,0x20,0x11,
|
||||
0x4,0x11,0x10,
|
||||
0x4,0x20,0x30,
|
||||
0x4,0x11,0x20,
|
||||
0x4,0x02,0x10,
|
||||
0x4,0x22,0x10,
|
||||
0x4,0x11,0x11,
|
||||
0x4,0x30,0x20,
|
||||
0x4,0x30,0x00,
|
||||
0x4,0x01,0x22,
|
||||
0x4,0x01,0x12,
|
||||
0x4,0x02,0x11,
|
||||
0x4,0x03,0x02,
|
||||
0x4,0x03,0x00,
|
||||
0x4,0x10,0x21,
|
||||
0x4,0x12,0x20,
|
||||
0x4,0x00,0x00,
|
||||
0x4,0x12,0x21,
|
||||
0x4,0x21,0x11,
|
||||
0x4,0x02,0x22,
|
||||
0x4,0x10,0x12,
|
||||
0x4,0x31,0x00,
|
||||
0x4,0x20,0x20,
|
||||
0x4,0x00,0x03,
|
||||
0x4,0x02,0x02,
|
||||
0x4,0x22,0x20,
|
||||
0x4,0x01,0x21,
|
||||
0x4,0x21,0x02,
|
||||
0x4,0x21,0x12,
|
||||
0x4,0x11,0x22,
|
||||
0x4,0x00,0x30,
|
||||
0x4,0x12,0x11,
|
||||
0x4,0x20,0x22,
|
||||
0x4,0x31,0x20,
|
||||
0x4,0x21,0x30,
|
||||
0x4,0x22,0x02,
|
||||
0x4,0x22,0x22,
|
||||
0x4,0x20,0x31,
|
||||
0x4,0x13,0x02,
|
||||
0x4,0x03,0x10,
|
||||
0x4,0x11,0x12,
|
||||
0x4,0x00,0x13,
|
||||
0x4,0x21,0x01,
|
||||
0x4,0x12,0x03,
|
||||
0x4,0x13,0x00,
|
||||
0x4,0x13,0x10,
|
||||
0x4,0x02,0x13,
|
||||
0x4,0x30,0x01,
|
||||
0x4,0x12,0x10,
|
||||
0x4,0x22,0x13,
|
||||
0x4,0x03,0x12,
|
||||
0x4,0x31,0x01,
|
||||
0x4,0x30,0x22,
|
||||
0x4,0x00,0x31,
|
||||
0x4,0x01,0x31,
|
||||
0x4,0x02,0x23,
|
||||
0x4,0x01,0x30,
|
||||
0x4,0x11,0x21,
|
||||
0x4,0x22,0x21,
|
||||
0x4,0x01,0x13,
|
||||
0x4,0x10,0x03,
|
||||
0x4,0x22,0x03,
|
||||
0x4,0x30,0x21,
|
||||
0x4,0x21,0x31,
|
||||
0x4,0x33,0x00,
|
||||
0x4,0x13,0x12,
|
||||
0x4,0x11,0x31,
|
||||
0x4,0x30,0x02,
|
||||
0x4,0x12,0x02,
|
||||
0x4,0x11,0x13,
|
||||
0x4,0x12,0x22,
|
||||
0x4,0x20,0x32,
|
||||
0x4,0x10,0x13,
|
||||
0x4,0x22,0x31,
|
||||
0x4,0x21,0x20,
|
||||
0x4,0x01,0x33,
|
||||
0x4,0x33,0x10,
|
||||
0x4,0x20,0x13,
|
||||
0x4,0x31,0x22,
|
||||
0x4,0x13,0x30,
|
||||
0x4,0x01,0x03,
|
||||
0x4,0x11,0x33,
|
||||
0x4,0x20,0x21,
|
||||
0x4,0x13,0x31,
|
||||
0x4,0x03,0x22,
|
||||
0x4,0x31,0x02,
|
||||
0x4,0x00,0x24,
|
||||
0x2,0x00,
|
||||
0x2,0x10,
|
||||
0x2,0x20,
|
||||
0x2,0x30,
|
||||
0x2,0x40,
|
||||
0x2,0x50,
|
||||
0x2,0x60,
|
||||
0x2,0x01,
|
||||
0x2,0x11,
|
||||
0x2,0x21,
|
||||
0x2,0x31,
|
||||
0x2,0x41,
|
||||
0x2,0x51,
|
||||
0x2,0x61,
|
||||
0x2,0x02,
|
||||
0x2,0x12,
|
||||
0x2,0x22,
|
||||
0x2,0x32,
|
||||
0x2,0x42,
|
||||
0x2,0x52,
|
||||
0x2,0x62,
|
||||
0x2,0x03,
|
||||
0x2,0x13,
|
||||
0x2,0x23,
|
||||
0x2,0x33,
|
||||
0x2,0x43,
|
||||
0x2,0x53,
|
||||
0x2,0x63,
|
||||
0x2,0x04,
|
||||
0x2,0x14,
|
||||
0x2,0x24,
|
||||
0x2,0x34,
|
||||
0x2,0x44,
|
||||
0x2,0x54,
|
||||
0x2,0x64,
|
||||
0x2,0x05,
|
||||
0x2,0x15,
|
||||
0x2,0x25,
|
||||
0x2,0x35,
|
||||
0x2,0x45,
|
||||
0x2,0x55,
|
||||
0x2,0x65,
|
||||
0x2,0x06,
|
||||
0x2,0x16,
|
||||
0x2,0x26,
|
||||
0x2,0x36,
|
||||
0x2,0x46,
|
||||
0x2,0x56,
|
||||
0x2,0x66
|
||||
};
|
||||
|
||||
static const byte pc_tbl3[] = {
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x00,0x00,0x01,
|
||||
0x6,0x00,0x00,0x10,
|
||||
0x6,0x00,0x00,0x11,
|
||||
0x6,0x00,0x01,0x00,
|
||||
0x6,0x00,0x01,0x01,
|
||||
0x6,0x00,0x01,0x10,
|
||||
0x6,0x00,0x01,0x11,
|
||||
0x6,0x00,0x10,0x00,
|
||||
0x6,0x00,0x10,0x01,
|
||||
0x6,0x00,0x10,0x10,
|
||||
0x6,0x00,0x10,0x11,
|
||||
0x6,0x00,0x11,0x00,
|
||||
0x6,0x00,0x11,0x01,
|
||||
0x6,0x00,0x11,0x10,
|
||||
0x6,0x00,0x11,0x11,
|
||||
0x6,0x01,0x00,0x00,
|
||||
0x6,0x01,0x00,0x01,
|
||||
0x6,0x01,0x00,0x10,
|
||||
0x6,0x01,0x00,0x11,
|
||||
0x6,0x01,0x01,0x00,
|
||||
0x6,0x01,0x01,0x01,
|
||||
0x6,0x01,0x01,0x10,
|
||||
0x6,0x01,0x01,0x11,
|
||||
0x6,0x01,0x10,0x00,
|
||||
0x6,0x01,0x10,0x01,
|
||||
0x6,0x01,0x10,0x10,
|
||||
0x6,0x01,0x10,0x11,
|
||||
0x6,0x01,0x11,0x00,
|
||||
0x6,0x01,0x11,0x01,
|
||||
0x6,0x01,0x11,0x10,
|
||||
0x6,0x01,0x11,0x11,
|
||||
0x6,0x10,0x00,0x00,
|
||||
0x6,0x10,0x00,0x01,
|
||||
0x6,0x10,0x00,0x10,
|
||||
0x6,0x10,0x00,0x11,
|
||||
0x6,0x10,0x01,0x00,
|
||||
0x6,0x10,0x01,0x01,
|
||||
0x6,0x10,0x01,0x10,
|
||||
0x6,0x10,0x01,0x11,
|
||||
0x6,0x10,0x10,0x00,
|
||||
0x6,0x10,0x10,0x01,
|
||||
0x6,0x10,0x10,0x10,
|
||||
0x6,0x10,0x10,0x11,
|
||||
0x6,0x10,0x11,0x00,
|
||||
0x6,0x10,0x11,0x01,
|
||||
0x6,0x10,0x11,0x10,
|
||||
0x6,0x10,0x11,0x11,
|
||||
0x6,0x11,0x00,0x00,
|
||||
0x6,0x11,0x00,0x01,
|
||||
0x6,0x11,0x00,0x10,
|
||||
0x6,0x11,0x00,0x11,
|
||||
0x6,0x11,0x01,0x00,
|
||||
0x6,0x11,0x01,0x01,
|
||||
0x6,0x11,0x01,0x10,
|
||||
0x6,0x11,0x01,0x11,
|
||||
0x6,0x11,0x10,0x00,
|
||||
0x6,0x11,0x10,0x01,
|
||||
0x6,0x11,0x10,0x10,
|
||||
0x6,0x11,0x10,0x11,
|
||||
0x6,0x11,0x11,0x00,
|
||||
0x6,0x11,0x11,0x01,
|
||||
0x6,0x11,0x11,0x10,
|
||||
0x4,0x00,0x00,
|
||||
0x4,0x00,0x01,
|
||||
0x4,0x00,0x02,
|
||||
0x4,0x00,0x03,
|
||||
0x4,0x00,0x10,
|
||||
0x4,0x00,0x11,
|
||||
0x4,0x00,0x12,
|
||||
0x4,0x00,0x13,
|
||||
0x4,0x00,0x20,
|
||||
0x4,0x00,0x21,
|
||||
0x4,0x00,0x22,
|
||||
0x4,0x00,0x23,
|
||||
0x4,0x00,0x30,
|
||||
0x4,0x00,0x31,
|
||||
0x4,0x00,0x32,
|
||||
0x4,0x00,0x33,
|
||||
0x4,0x01,0x00,
|
||||
0x4,0x01,0x01,
|
||||
0x4,0x01,0x02,
|
||||
0x4,0x01,0x03,
|
||||
0x4,0x01,0x10,
|
||||
0x4,0x01,0x11,
|
||||
0x4,0x01,0x12,
|
||||
0x4,0x01,0x13,
|
||||
0x4,0x01,0x20,
|
||||
0x4,0x01,0x21,
|
||||
0x4,0x01,0x22,
|
||||
0x4,0x01,0x23,
|
||||
0x4,0x01,0x30,
|
||||
0x4,0x01,0x31,
|
||||
0x4,0x01,0x32,
|
||||
0x4,0x01,0x33,
|
||||
0x4,0x02,0x00,
|
||||
0x4,0x02,0x01,
|
||||
0x4,0x02,0x02,
|
||||
0x4,0x02,0x03,
|
||||
0x4,0x02,0x10,
|
||||
0x4,0x02,0x11,
|
||||
0x4,0x02,0x12,
|
||||
0x4,0x02,0x13,
|
||||
0x4,0x02,0x20,
|
||||
0x4,0x02,0x21,
|
||||
0x4,0x02,0x22,
|
||||
0x4,0x02,0x23,
|
||||
0x4,0x02,0x30,
|
||||
0x4,0x02,0x31,
|
||||
0x4,0x02,0x32,
|
||||
0x4,0x02,0x33,
|
||||
0x4,0x03,0x00,
|
||||
0x4,0x03,0x01,
|
||||
0x4,0x03,0x02,
|
||||
0x4,0x03,0x03,
|
||||
0x4,0x03,0x10,
|
||||
0x4,0x03,0x11,
|
||||
0x4,0x03,0x12,
|
||||
0x4,0x03,0x13,
|
||||
0x4,0x03,0x20,
|
||||
0x4,0x03,0x21,
|
||||
0x4,0x03,0x22,
|
||||
0x4,0x03,0x23,
|
||||
0x4,0x03,0x30,
|
||||
0x4,0x03,0x31,
|
||||
0x4,0x03,0x32,
|
||||
0x4,0x03,0x33,
|
||||
0x4,0x10,0x00,
|
||||
0x4,0x10,0x01,
|
||||
0x4,0x10,0x02,
|
||||
0x4,0x10,0x03,
|
||||
0x4,0x10,0x10,
|
||||
0x4,0x10,0x11,
|
||||
0x4,0x10,0x12,
|
||||
0x4,0x10,0x13,
|
||||
0x4,0x10,0x20,
|
||||
0x4,0x10,0x21,
|
||||
0x4,0x10,0x22,
|
||||
0x4,0x10,0x23,
|
||||
0x4,0x10,0x30,
|
||||
0x4,0x10,0x31,
|
||||
0x4,0x10,0x32,
|
||||
0x4,0x10,0x33,
|
||||
0x4,0x11,0x00,
|
||||
0x4,0x11,0x01,
|
||||
0x4,0x11,0x02,
|
||||
0x4,0x11,0x03,
|
||||
0x4,0x11,0x10,
|
||||
0x4,0x11,0x11,
|
||||
0x4,0x11,0x12,
|
||||
0x4,0x11,0x13,
|
||||
0x4,0x11,0x20,
|
||||
0x4,0x11,0x21,
|
||||
0x4,0x11,0x22,
|
||||
0x4,0x11,0x23,
|
||||
0x4,0x11,0x30,
|
||||
0x4,0x11,0x31,
|
||||
0x4,0x11,0x32,
|
||||
0x4,0x11,0x33,
|
||||
0x4,0x12,0x00,
|
||||
0x4,0x12,0x01,
|
||||
0x4,0x12,0x02,
|
||||
0x4,0x12,0x03,
|
||||
0x4,0x12,0x10,
|
||||
0x4,0x12,0x11,
|
||||
0x4,0x12,0x12,
|
||||
0x4,0x12,0x13,
|
||||
0x4,0x12,0x20,
|
||||
0x4,0x12,0x21,
|
||||
0x4,0x12,0x22,
|
||||
0x4,0x12,0x23,
|
||||
0x4,0x12,0x30,
|
||||
0x4,0x12,0x31,
|
||||
0x4,0x12,0x32,
|
||||
0x4,0x12,0x33,
|
||||
0x4,0x13,0x00,
|
||||
0x4,0x13,0x01,
|
||||
0x4,0x13,0x02,
|
||||
0x4,0x13,0x03,
|
||||
0x4,0x13,0x10,
|
||||
0x4,0x13,0x11,
|
||||
0x4,0x13,0x12,
|
||||
0x4,0x13,0x13,
|
||||
0x4,0x13,0x20,
|
||||
0x4,0x13,0x21,
|
||||
0x4,0x13,0x22,
|
||||
0x4,0x13,0x23,
|
||||
0x4,0x13,0x30,
|
||||
0x4,0x13,0x31,
|
||||
0x4,0x13,0x32,
|
||||
0x4,0x13,0x33,
|
||||
0x2,0x00,
|
||||
0x2,0x10,
|
||||
0x2,0x20,
|
||||
0x2,0x30,
|
||||
0x2,0x40,
|
||||
0x2,0x50,
|
||||
0x2,0x60,
|
||||
0x2,0x70,
|
||||
0x2,0x01,
|
||||
0x2,0x11,
|
||||
0x2,0x21,
|
||||
0x2,0x31,
|
||||
0x2,0x41,
|
||||
0x2,0x51,
|
||||
0x2,0x61,
|
||||
0x2,0x71,
|
||||
0x2,0x02,
|
||||
0x2,0x12,
|
||||
0x2,0x22,
|
||||
0x2,0x32,
|
||||
0x2,0x42,
|
||||
0x2,0x52,
|
||||
0x2,0x62,
|
||||
0x2,0x72,
|
||||
0x2,0x03,
|
||||
0x2,0x13,
|
||||
0x2,0x23,
|
||||
0x2,0x33,
|
||||
0x2,0x43,
|
||||
0x2,0x53,
|
||||
0x2,0x63,
|
||||
0x2,0x73,
|
||||
0x2,0x04,
|
||||
0x2,0x14,
|
||||
0x2,0x24,
|
||||
0x2,0x34,
|
||||
0x2,0x44,
|
||||
0x2,0x54,
|
||||
0x2,0x64,
|
||||
0x2,0x74,
|
||||
0x2,0x05,
|
||||
0x2,0x15,
|
||||
0x2,0x25,
|
||||
0x2,0x35,
|
||||
0x2,0x45,
|
||||
0x2,0x55,
|
||||
0x2,0x65,
|
||||
0x2,0x75,
|
||||
0x2,0x06,
|
||||
0x2,0x16,
|
||||
0x2,0x26,
|
||||
0x2,0x36,
|
||||
0x2,0x46,
|
||||
0x2,0x56,
|
||||
0x2,0x66,
|
||||
0x2,0x76,
|
||||
0x2,0x07,
|
||||
0x2,0x17,
|
||||
0x2,0x27,
|
||||
0x2,0x37,
|
||||
0x2,0x47,
|
||||
0x2,0x57,
|
||||
0x2,0x67,
|
||||
0x2,0x77
|
||||
};
|
||||
|
||||
static const byte pc_tbl4[] = {
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x00,
|
||||
0x8,0x20,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x01,
|
||||
0x8,0x10,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x02,
|
||||
0x8,0x01,0x00,0x00,0x00,
|
||||
0x8,0x00,0x00,0x00,0x10,
|
||||
0x8,0x02,0x00,0x00,0x00,
|
||||
0x6,0x00,0x00,0x00,
|
||||
0x6,0x20,0x00,0x00,
|
||||
0x6,0x00,0x00,0x01,
|
||||
0x6,0x10,0x00,0x00,
|
||||
0x6,0x00,0x00,0x02,
|
||||
0x6,0x00,0x10,0x00,
|
||||
0x6,0x00,0x20,0x00,
|
||||
0x6,0x00,0x02,0x00,
|
||||
0x6,0x00,0x01,0x00,
|
||||
0x6,0x01,0x00,0x00,
|
||||
0x6,0x00,0x00,0x20,
|
||||
0x6,0x02,0x00,0x00,
|
||||
0x6,0x00,0x00,0x10,
|
||||
0x6,0x10,0x00,0x20,
|
||||
0x6,0x01,0x00,0x02,
|
||||
0x6,0x20,0x00,0x10,
|
||||
0x6,0x02,0x00,0x01,
|
||||
0x6,0x20,0x10,0x00,
|
||||
0x6,0x00,0x12,0x00,
|
||||
0x6,0x00,0x02,0x01,
|
||||
0x6,0x02,0x01,0x00,
|
||||
0x6,0x00,0x21,0x00,
|
||||
0x6,0x00,0x01,0x02,
|
||||
0x6,0x00,0x20,0x10,
|
||||
0x6,0x00,0x00,0x21,
|
||||
0x6,0x00,0x00,0x12,
|
||||
0x6,0x00,0x01,0x20,
|
||||
0x6,0x12,0x00,0x00,
|
||||
0x6,0x00,0x10,0x20,
|
||||
0x6,0x01,0x20,0x00,
|
||||
0x6,0x02,0x10,0x00,
|
||||
0x6,0x10,0x20,0x00,
|
||||
0x6,0x01,0x02,0x00,
|
||||
0x6,0x21,0x00,0x00,
|
||||
0x6,0x00,0x02,0x10,
|
||||
0x6,0x20,0x01,0x00,
|
||||
0x6,0x00,0x22,0x00,
|
||||
0x6,0x10,0x02,0x00,
|
||||
0x6,0x00,0x10,0x02,
|
||||
0x6,0x11,0x00,0x00,
|
||||
0x6,0x00,0x11,0x00,
|
||||
0x6,0x22,0x00,0x00,
|
||||
0x6,0x20,0x00,0x02,
|
||||
0x6,0x10,0x00,0x01,
|
||||
0x6,0x00,0x20,0x01,
|
||||
0x6,0x02,0x20,0x00,
|
||||
0x6,0x01,0x10,0x00,
|
||||
0x6,0x01,0x00,0x20,
|
||||
0x6,0x00,0x20,0x02,
|
||||
0x6,0x01,0x20,0x02,
|
||||
0x6,0x10,0x01,0x00,
|
||||
0x6,0x02,0x00,0x10,
|
||||
0x6,0x00,0x10,0x01,
|
||||
0x6,0x10,0x01,0x20,
|
||||
0x6,0x20,0x02,0x10,
|
||||
0x6,0x00,0x00,0x22,
|
||||
0x6,0x10,0x00,0x02,
|
||||
0x6,0x00,0x02,0x20,
|
||||
0x6,0x20,0x02,0x00,
|
||||
0x6,0x00,0x00,0x11,
|
||||
0x6,0x02,0x10,0x01,
|
||||
0x6,0x00,0x01,0x10,
|
||||
0x6,0x00,0x02,0x11,
|
||||
0x4,0x01,0x02,
|
||||
0x4,0x02,0x01,
|
||||
0x4,0x01,0x00,
|
||||
0x4,0x10,0x20,
|
||||
0x4,0x20,0x10,
|
||||
0x4,0x20,0x00,
|
||||
0x4,0x11,0x00,
|
||||
0x4,0x02,0x00,
|
||||
0x4,0x12,0x00,
|
||||
0x4,0x00,0x21,
|
||||
0x4,0x22,0x00,
|
||||
0x4,0x00,0x12,
|
||||
0x4,0x21,0x00,
|
||||
0x4,0x02,0x11,
|
||||
0x4,0x00,0x01,
|
||||
0x4,0x10,0x02,
|
||||
0x4,0x02,0x20,
|
||||
0x4,0x20,0x11,
|
||||
0x4,0x01,0x10,
|
||||
0x4,0x21,0x10,
|
||||
0x4,0x10,0x00,
|
||||
0x4,0x10,0x22,
|
||||
0x4,0x20,0x20,
|
||||
0x4,0x00,0x22,
|
||||
0x4,0x01,0x22,
|
||||
0x4,0x20,0x01,
|
||||
0x4,0x02,0x02,
|
||||
0x4,0x00,0x20,
|
||||
0x4,0x00,0x10,
|
||||
0x4,0x00,0x11,
|
||||
0x4,0x22,0x01,
|
||||
0x4,0x11,0x20,
|
||||
0x4,0x12,0x01,
|
||||
0x4,0x12,0x20,
|
||||
0x4,0x11,0x02,
|
||||
0x4,0x10,0x10,
|
||||
0x4,0x01,0x01,
|
||||
0x4,0x02,0x21,
|
||||
0x4,0x20,0x12,
|
||||
0x4,0x01,0x12,
|
||||
0x4,0x22,0x11,
|
||||
0x4,0x21,0x12,
|
||||
0x4,0x22,0x10,
|
||||
0x4,0x21,0x02,
|
||||
0x4,0x20,0x02,
|
||||
0x4,0x10,0x01,
|
||||
0x4,0x00,0x02,
|
||||
0x4,0x10,0x21,
|
||||
0x4,0x01,0x20,
|
||||
0x4,0x11,0x22,
|
||||
0x4,0x12,0x21,
|
||||
0x4,0x22,0x20,
|
||||
0x4,0x02,0x10,
|
||||
0x4,0x02,0x22,
|
||||
0x4,0x11,0x10,
|
||||
0x4,0x22,0x02,
|
||||
0x4,0x20,0x21,
|
||||
0x4,0x01,0x11,
|
||||
0x4,0x11,0x01,
|
||||
0x4,0x10,0x12,
|
||||
0x4,0x02,0x12,
|
||||
0x4,0x20,0x22,
|
||||
0x4,0x21,0x20,
|
||||
0x4,0x01,0x21,
|
||||
0x4,0x12,0x02,
|
||||
0x4,0x21,0x11,
|
||||
0x4,0x12,0x22,
|
||||
0x4,0x12,0x10,
|
||||
0x4,0x22,0x21,
|
||||
0x4,0x10,0x11,
|
||||
0x4,0x21,0x01,
|
||||
0x4,0x11,0x12,
|
||||
0x4,0x12,0x11,
|
||||
0x4,0x66,0x66,
|
||||
0x4,0x22,0x22,
|
||||
0x4,0x11,0x21,
|
||||
0x4,0x11,0x11,
|
||||
0x4,0x21,0x22,
|
||||
0x4,0x00,0x00,
|
||||
0x4,0x22,0x12,
|
||||
0x4,0x12,0x12,
|
||||
0x4,0x21,0x21,
|
||||
0x4,0x42,0x00,
|
||||
0x4,0x00,0x04,
|
||||
0x4,0x40,0x00,
|
||||
0x4,0x30,0x00,
|
||||
0x4,0x31,0x00,
|
||||
0x4,0x00,0x03,
|
||||
0x4,0x00,0x14,
|
||||
0x4,0x00,0x13,
|
||||
0x4,0x01,0x24,
|
||||
0x4,0x20,0x13,
|
||||
0x4,0x01,0x42,
|
||||
0x4,0x14,0x20,
|
||||
0x4,0x42,0x02,
|
||||
0x4,0x13,0x00,
|
||||
0x4,0x00,0x24,
|
||||
0x4,0x31,0x20,
|
||||
0x4,0x22,0x13,
|
||||
0x4,0x11,0x24,
|
||||
0x4,0x12,0x66,
|
||||
0x4,0x30,0x01,
|
||||
0x4,0x02,0x13,
|
||||
0x4,0x12,0x42,
|
||||
0x4,0x40,0x10,
|
||||
0x4,0x40,0x02,
|
||||
0x4,0x01,0x04,
|
||||
0x4,0x24,0x00,
|
||||
0x4,0x42,0x10,
|
||||
0x4,0x21,0x13,
|
||||
0x4,0x13,0x12,
|
||||
0x4,0x31,0x21,
|
||||
0x4,0x21,0x24,
|
||||
0x4,0x00,0x40,
|
||||
0x4,0x10,0x24,
|
||||
0x4,0x10,0x42,
|
||||
0x4,0x32,0x01,
|
||||
0x4,0x11,0x42,
|
||||
0x4,0x20,0x31,
|
||||
0x4,0x12,0x40,
|
||||
0x2,0x00,
|
||||
0x2,0x10,
|
||||
0x2,0x20,
|
||||
0x2,0x30,
|
||||
0x2,0x40,
|
||||
0x2,0x50,
|
||||
0x2,0x60,
|
||||
0x2,0x70,
|
||||
0x2,0x01,
|
||||
0x2,0x11,
|
||||
0x2,0x21,
|
||||
0x2,0x31,
|
||||
0x2,0x41,
|
||||
0x2,0x51,
|
||||
0x2,0x61,
|
||||
0x2,0x71,
|
||||
0x2,0x02,
|
||||
0x2,0x12,
|
||||
0x2,0x22,
|
||||
0x2,0x32,
|
||||
0x2,0x42,
|
||||
0x2,0x52,
|
||||
0x2,0x62,
|
||||
0x2,0x72,
|
||||
0x2,0x03,
|
||||
0x2,0x13,
|
||||
0x2,0x23,
|
||||
0x2,0x33,
|
||||
0x2,0x43,
|
||||
0x2,0x53,
|
||||
0x2,0x63,
|
||||
0x2,0x73,
|
||||
0x2,0x04,
|
||||
0x2,0x14,
|
||||
0x2,0x24,
|
||||
0x2,0x34,
|
||||
0x2,0x44,
|
||||
0x2,0x54,
|
||||
0x2,0x64,
|
||||
0x2,0x74,
|
||||
0x2,0x05,
|
||||
0x2,0x15,
|
||||
0x2,0x25,
|
||||
0x2,0x35,
|
||||
0x2,0x45,
|
||||
0x2,0x55,
|
||||
0x2,0x65,
|
||||
0x2,0x75,
|
||||
0x2,0x06,
|
||||
0x2,0x16,
|
||||
0x2,0x26,
|
||||
0x2,0x36,
|
||||
0x2,0x46,
|
||||
0x2,0x56,
|
||||
0x2,0x66,
|
||||
0x2,0x76,
|
||||
0x2,0x07,
|
||||
0x2,0x17,
|
||||
0x2,0x27,
|
||||
0x2,0x37,
|
||||
0x2,0x47,
|
||||
0x2,0x57,
|
||||
0x2,0x67,
|
||||
0x2,0x77
|
||||
};
|
||||
|
||||
static const byte * const tables[] = { pc_tbl2, pc_tbl3, pc_tbl4 };
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
502
image/codecs/xan.cpp
Normal file
502
image/codecs/xan.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Xan image decoder. (fourcc Xxan)
|
||||
*
|
||||
* Used by Crusader: No Regret AVI files
|
||||
*
|
||||
* This code was created based on the multimedia wiki:
|
||||
* https://wiki.multimedia.cx/index.php/Origin_Xan_Codec
|
||||
* and ffmpeg's libavcodec/xxan.c.
|
||||
* The ffmpeg code is LGPL2 licensed and Copyright (C) 2011
|
||||
* Konstantin Shishkov based on work by Mike Melanson.
|
||||
*
|
||||
* A similar format is used in Wing Commander III (although not in an AVI
|
||||
* container) and IV.
|
||||
*/
|
||||
|
||||
#include "image/codecs/xan.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/bitstream.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/yuv_to_rgb.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
static const int SCRATCH_SPARE = 256;
|
||||
|
||||
XanDecoder::XanDecoder(int width, int height, int bitsPerPixel) : Codec(),
|
||||
_width(width), _height(height), _wc4Mode(false), _surface(nullptr) {
|
||||
assert(bitsPerPixel == 16);
|
||||
if (bitsPerPixel != 16)
|
||||
error("XanDecoder: BPP must be 16 not %d", bitsPerPixel);
|
||||
if (width % 2)
|
||||
error("XanDecoder: width must be even, not %d", width);
|
||||
_scratchbuf = new uint8[_width * _height + SCRATCH_SPARE]();
|
||||
_lumabuf = new uint8[_width * _height]();
|
||||
_ybuf = new uint8[_width * _height]();
|
||||
_ubuf = new uint8[_width * _height / 2]();
|
||||
_vbuf = new uint8[_width * _height / 2]();
|
||||
|
||||
_pixelFormat = getDefaultYUVFormat();
|
||||
}
|
||||
|
||||
XanDecoder::~XanDecoder() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
_surface = nullptr;
|
||||
}
|
||||
delete [] _scratchbuf;
|
||||
delete [] _lumabuf;
|
||||
delete [] _ybuf;
|
||||
delete [] _ubuf;
|
||||
delete [] _vbuf;
|
||||
}
|
||||
|
||||
const Graphics::Surface *XanDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
uint32 frametype = stream.readUint32LE();
|
||||
if (frametype > 1) {
|
||||
error("Xan frametype should be 0 or 1, got %d", frametype);
|
||||
}
|
||||
|
||||
if (frametype == 0) {
|
||||
decodeFrameType0(stream);
|
||||
} else {
|
||||
decodeFrameType1(stream);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
// An unoptimized version of the one from libavutil, but works fine
|
||||
static void _memcpy_backptr(uint8 *dst, int back, int cnt) {
|
||||
if (cnt <= 0 || back <= 0)
|
||||
return;
|
||||
|
||||
const uint8 *src = &dst[-back];
|
||||
if (back == 1) {
|
||||
uint8 val = *src;
|
||||
memset(dst, val, cnt);
|
||||
} else {
|
||||
int blocklen = back;
|
||||
while (cnt > blocklen) {
|
||||
memcpy(dst, src, blocklen);
|
||||
dst += blocklen;
|
||||
cnt -= blocklen;
|
||||
blocklen <<= 1;
|
||||
}
|
||||
memcpy(dst, src, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack using the WC3 algorithm
|
||||
static int _unpack(Common::SeekableReadStream &stream, uint8 *dest, const int dest_len) {
|
||||
const uint8 *orig_dest = dest;
|
||||
const uint8 *dest_end = dest + dest_len;
|
||||
|
||||
memset(dest, 0, dest_len);
|
||||
|
||||
while (dest < dest_end) {
|
||||
if (stream.eos())
|
||||
return -1;
|
||||
|
||||
const uint8 opcode = stream.readByte();
|
||||
|
||||
if (opcode < 0xe0) {
|
||||
int readsize, copysize, back;
|
||||
if ((opcode & 0x80) == 0) {
|
||||
readsize = opcode & 3;
|
||||
back = ((opcode & 0x60) << 3) + stream.readByte() + 1;
|
||||
copysize = ((opcode & 0x1c) >> 2) + 3;
|
||||
} else if ((opcode & 0x40) == 0) {
|
||||
const uint8 b = stream.readByte();
|
||||
readsize = b >> 6;
|
||||
back = ((b & 0x3f) << 8) + stream.readByte() + 1;
|
||||
copysize = (opcode & 0x3f) + 4;
|
||||
} else {
|
||||
readsize = opcode & 3;
|
||||
back = ((opcode & 0x10) << 12) + stream.readUint16BE() + 1;
|
||||
copysize = ((opcode & 0x0c) << 6) + stream.readByte() + 5;
|
||||
if (readsize + copysize > dest_end - dest)
|
||||
break;
|
||||
}
|
||||
if (dest + readsize + copysize > dest_end ||
|
||||
dest - orig_dest + readsize < back)
|
||||
return -1;
|
||||
stream.read(dest, readsize);
|
||||
dest += readsize;
|
||||
_memcpy_backptr(dest, back, copysize);
|
||||
dest += copysize;
|
||||
} else {
|
||||
bool finish = (opcode >= 0xfc);
|
||||
|
||||
int readsize = finish ? opcode & 3 : ((opcode & 0x1f) * 4) + 4;
|
||||
if (dest + readsize > dest_end)
|
||||
return -1;
|
||||
stream.read(dest, readsize);
|
||||
dest += readsize;
|
||||
if (finish)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dest - orig_dest;
|
||||
}
|
||||
|
||||
|
||||
bool XanDecoder::decodeChroma(Common::SeekableReadStream &stream, const int chroma_off) {
|
||||
if (!chroma_off)
|
||||
return 0;
|
||||
|
||||
if (chroma_off + 4 >= stream.size() - stream.pos()) {
|
||||
warning("Invalid chroma block position");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.seek(chroma_off + 4);
|
||||
|
||||
const int mode = stream.readUint16LE();
|
||||
const int table_start = stream.pos();
|
||||
// 2 bytes per table entry
|
||||
const int table_size = stream.readUint16LE() * 2;
|
||||
|
||||
if (mode > 1) {
|
||||
warning("Unexpected chroma mode %d", mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (table_size >= stream.size() - stream.pos()) {
|
||||
warning("Invalid chroma block offset");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.skip(table_size);
|
||||
const int dec_size = _unpack(stream, _scratchbuf, _width * _height);
|
||||
if (dec_size < 0) {
|
||||
warning("Chroma unpacking failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int pitch = _width / 2;
|
||||
|
||||
uint8 *U = _ubuf;
|
||||
uint8 *V = _vbuf;
|
||||
const uint8 *src = _scratchbuf;
|
||||
const uint8 *src_end = src + dec_size;
|
||||
if (mode) {
|
||||
// YUV420 frame
|
||||
for (int y = 0; y < _height / 2; y++) {
|
||||
for (int x = 0; x < pitch; x++) {
|
||||
if (src >= src_end)
|
||||
return true;
|
||||
int toff = *src++ * 2;
|
||||
if (toff) {
|
||||
if (toff > table_size)
|
||||
return false;
|
||||
const int pos = stream.pos();
|
||||
stream.seek(table_start + toff);
|
||||
uint8 uval, vval;
|
||||
if (_wc4Mode) {
|
||||
uint16 val = stream.readUint16LE();
|
||||
uval = (val >> 3) & 0xF8;
|
||||
vval = (val >> 8) & 0xF8;
|
||||
} else {
|
||||
uval = stream.readByte();
|
||||
vval = stream.readByte();
|
||||
}
|
||||
uval = uval | uval >> 5;
|
||||
vval = vval | vval >> 5;
|
||||
stream.seek(pos);
|
||||
U[x] = uval;
|
||||
V[x] = vval;
|
||||
}
|
||||
}
|
||||
U += pitch;
|
||||
V += pitch;
|
||||
}
|
||||
if (_height % 1) {
|
||||
memcpy(U, U - pitch, pitch);
|
||||
memcpy(V, V - pitch, pitch);
|
||||
}
|
||||
} else {
|
||||
// YUV410 frame - expand out U and V components
|
||||
uint8 *U2 = U + pitch;
|
||||
uint8 *V2 = V + pitch;
|
||||
|
||||
for (int y = 0; y < _height / 4; y++) {
|
||||
for (int x = 0; x < pitch; x += 2) {
|
||||
if (src >= src_end)
|
||||
return true;
|
||||
int toff = *src++ * 2;
|
||||
if (toff) {
|
||||
if (toff > table_size)
|
||||
return false;
|
||||
const int pos = stream.pos();
|
||||
stream.seek(table_start + toff);
|
||||
uint8 uval, vval;
|
||||
if (_wc4Mode) {
|
||||
uint16 val = stream.readUint16LE();
|
||||
uval = (val >> 3) & 0xF8;
|
||||
vval = (val >> 8) & 0xF8;
|
||||
} else {
|
||||
uval = stream.readByte();
|
||||
vval = stream.readByte();
|
||||
}
|
||||
uval = uval | uval >> 5;
|
||||
vval = vval | vval >> 5;
|
||||
stream.seek(pos);
|
||||
U[x] = U[x + 1] = U2[x] = U2[x + 1] = uval;
|
||||
V[x] = V[x + 1] = V2[x] = V2[x + 1] = vval;
|
||||
}
|
||||
}
|
||||
U += pitch * 2;
|
||||
V += pitch * 2;
|
||||
U2 += pitch * 2;
|
||||
V2 += pitch * 2;
|
||||
}
|
||||
if (_height % 4) {
|
||||
int lines = ((_height + 1) / 2) - (_height / 4) * 2;
|
||||
|
||||
memcpy(U, U - lines * pitch, lines * pitch);
|
||||
memcpy(V, V - lines * pitch, lines * pitch);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XanDecoder::decodeFrameType0(Common::SeekableReadStream &stream) {
|
||||
const uint32 chroma_offset = stream.readUint32LE();
|
||||
const uint32 refines_offset = stream.readUint32LE();
|
||||
const uint32 luma_offset = stream.pos();
|
||||
const uint32 nbytes = static_cast<uint32>(stream.size());
|
||||
|
||||
if (chroma_offset > nbytes || refines_offset > nbytes) {
|
||||
error("invalid frame type 0 offsets");
|
||||
}
|
||||
|
||||
if (!decodeChroma(stream, chroma_offset)) {
|
||||
warning("xxan chrome decode failed frame type 0");
|
||||
return;
|
||||
}
|
||||
|
||||
stream.seek(luma_offset);
|
||||
decompressLuma(stream);
|
||||
|
||||
//
|
||||
// Expand out the decompressed luma data. For type 0 frames:
|
||||
// * luma vals are 5-bit diffs, where
|
||||
// * top row values are diffs on the last value
|
||||
// * and the remaining rows are deltas on the value above.
|
||||
// * every second pixel in x is linearly interpolated from its horizontal neighbours.
|
||||
// * output values are clipped to 6 bits.
|
||||
// * a set of refinements values can adjust luma of interpolated pixels
|
||||
//
|
||||
const uint8 *lumadecomp = _scratchbuf;
|
||||
uint8 *lumarow = _lumabuf;
|
||||
int last = *lumadecomp++;
|
||||
lumarow[0] = last * 2;
|
||||
int x;
|
||||
// The top row uses only the left value for prediction
|
||||
for (x = 1; x < _width - 1; x += 2) {
|
||||
int cur = (last + *lumadecomp++) & 0x1F;
|
||||
lumarow[x] = last + cur;
|
||||
lumarow[x + 1] = cur * 2;
|
||||
last = cur;
|
||||
}
|
||||
lumarow[x] = last * 2;
|
||||
uint8 const *last_lumarow = lumarow;
|
||||
lumarow += _width;
|
||||
|
||||
// The remaining rows
|
||||
for (int y = 1; y < _height; y++) {
|
||||
last = ((last_lumarow[0] / 2) + *lumadecomp++) & 0x1F;
|
||||
lumarow[0] = last * 2;
|
||||
for (x = 1; x < _width - 1; x += 2) {
|
||||
int cur = ((last_lumarow[x + 1] / 2) + *lumadecomp++) & 0x1F;
|
||||
lumarow[x] = last + cur;
|
||||
lumarow[x + 1] = cur * 2;
|
||||
last = cur;
|
||||
}
|
||||
lumarow[x] = last * 2;
|
||||
last_lumarow = lumarow;
|
||||
lumarow += _width;
|
||||
}
|
||||
|
||||
if (refines_offset) {
|
||||
stream.seek(refines_offset + 8);
|
||||
|
||||
int dec_size = _unpack(stream, _scratchbuf, _width * _height / 2);
|
||||
if (dec_size < 0) {
|
||||
warning("luma refine unpacking failed!");
|
||||
dec_size = 0;
|
||||
} else {
|
||||
dec_size = MIN(dec_size, _width * _height / 2 - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < dec_size; i++)
|
||||
_lumabuf[i * 2 + 1] = (_lumabuf[i * 2 + 1] + _scratchbuf[i] * 2) & 0x3F;
|
||||
}
|
||||
|
||||
convertYUVtoRGBSurface();
|
||||
}
|
||||
|
||||
void XanDecoder::decodeFrameType1(Common::SeekableReadStream &stream) {
|
||||
const uint32 chroma_offset = stream.readUint32LE();
|
||||
const uint32 refines_offset = stream.readUint32LE();
|
||||
const uint32 refine2_offset = stream.readUint32LE();
|
||||
const uint32 luma_offset = stream.pos();
|
||||
const uint32 nbytes = static_cast<uint32>(stream.size());
|
||||
|
||||
if (chroma_offset > nbytes || refines_offset > nbytes || refine2_offset > nbytes) {
|
||||
error("invalid frame type 1 offset");
|
||||
}
|
||||
|
||||
if (!decodeChroma(stream, chroma_offset)) {
|
||||
warning("xxan chrome decode failed frame type 1");
|
||||
return;
|
||||
}
|
||||
|
||||
stream.seek(luma_offset);
|
||||
decompressLuma(stream);
|
||||
|
||||
//
|
||||
// Expand out the decompressed luma data. For type 1 frames:
|
||||
// * luma vals are 5-bit diffs on the previous frame's values
|
||||
// * every second pixel in x is linearly interpolated from its horizontal neighbours.
|
||||
// * output values are clipped to 6 bits.
|
||||
// * a set of refinements values can adjust luma of interpolated pixels
|
||||
//
|
||||
const uint8 *lumadecomp = _scratchbuf;
|
||||
uint8 *lumarow = _lumabuf;
|
||||
for (int y = 0; y < _height; y++) {
|
||||
int x;
|
||||
int last = (lumarow[0] + (*lumadecomp++ * 2)) & 0x3F;
|
||||
lumarow[0] = last;
|
||||
for (x = 1; x < _width - 1; x += 2) {
|
||||
int cur = (lumarow[x + 1] + (*lumadecomp++ * 2)) & 0x3F;
|
||||
lumarow[x] = (last + cur) / 2;
|
||||
lumarow[x + 1] = cur;
|
||||
last = cur;
|
||||
}
|
||||
lumarow[x] = last;
|
||||
lumarow += _width;
|
||||
}
|
||||
|
||||
if (refines_offset) {
|
||||
stream.seek(refines_offset + 8);
|
||||
|
||||
int dec_size = _unpack(stream, _scratchbuf, _width * _height / 2);
|
||||
|
||||
if (dec_size < 0) {
|
||||
warning("luma refine unpacking failed!");
|
||||
dec_size = 0;
|
||||
} else {
|
||||
dec_size = MIN(dec_size, _width * _height / 2 - 1);
|
||||
}
|
||||
|
||||
int dec2_size = 0;
|
||||
uint8 *scratch2 = _scratchbuf + _width * _height / 2;
|
||||
if (refine2_offset) {
|
||||
stream.seek(refine2_offset + 8);
|
||||
dec2_size = _unpack(stream, scratch2, _width * _height / 2);
|
||||
if (dec2_size < 0) {
|
||||
warning("luma refine2 unpacking failed!");
|
||||
dec2_size = 0;
|
||||
} else {
|
||||
dec2_size = MIN(dec_size, _width * _height / 2 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dec_size; i++) {
|
||||
int adjust = _scratchbuf[i] * 2;
|
||||
if (dec2_size)
|
||||
adjust += scratch2[i] * 2;
|
||||
_lumabuf[i * 2 + 1] = (_lumabuf[i * 2 + 1] + adjust) & 0x3F;
|
||||
}
|
||||
}
|
||||
|
||||
convertYUVtoRGBSurface();
|
||||
}
|
||||
|
||||
void XanDecoder::decompressLuma(Common::SeekableReadStream &stream) {
|
||||
const int32 startpos = stream.pos();
|
||||
const int nsymbols = stream.readByte();
|
||||
const int eofsymbol = stream.readByte();
|
||||
|
||||
const int root = nsymbols + eofsymbol;
|
||||
const uint8 *lumaend = _scratchbuf + _width * _height;
|
||||
|
||||
stream.skip(nsymbols * 2);
|
||||
|
||||
uint8 *luma = _scratchbuf;
|
||||
int node = root;
|
||||
int bits = stream.readByte();
|
||||
int mask = 0x80;
|
||||
while (!stream.eos()) {
|
||||
const int bit = ((bits & mask) ? 1 : 0);
|
||||
mask >>= 1;
|
||||
|
||||
const int32 nextbitspos = stream.pos();
|
||||
stream.seek(startpos + node * 2 + bit - eofsymbol * 2);
|
||||
node = stream.readByte();
|
||||
stream.seek(nextbitspos);
|
||||
|
||||
if (node == eofsymbol)
|
||||
break;
|
||||
if (node < eofsymbol) {
|
||||
*luma++ = node;
|
||||
if (luma >= lumaend)
|
||||
break;
|
||||
node = root;
|
||||
}
|
||||
if (!mask) {
|
||||
if (stream.eos())
|
||||
break;
|
||||
bits = stream.readByte();
|
||||
mask = 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XanDecoder::convertYUVtoRGBSurface() {
|
||||
// Expand luma from 6-bit to 8-bit.
|
||||
for (int i = 0; i < _width * _height; i++)
|
||||
_ybuf[i] = _lumabuf[i] << 2 | _lumabuf[i] >> 4;
|
||||
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface;
|
||||
_surface->create(_width, _height, _pixelFormat);
|
||||
}
|
||||
|
||||
YUVToRGBMan.convert420(_surface, Graphics::YUVToRGBManager::kScaleFull,
|
||||
_ybuf, _ubuf, _vbuf, _width, (_height / 2) * 2, _width, _width / 2);
|
||||
}
|
||||
|
||||
} // End of namespace Image
|
||||
97
image/codecs/xan.h
Normal file
97
image/codecs/xan.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_CODECS_XAN_H
|
||||
#define IMAGE_CODECS_XAN_H
|
||||
|
||||
#include "image/codecs/codec.h"
|
||||
|
||||
namespace Image {
|
||||
|
||||
/**
|
||||
* Xan image decoder. (fourcc Xxan)
|
||||
*
|
||||
* Used by Crusader: No Regret AVI files
|
||||
*
|
||||
* This code was created based on the multimedia wiki:
|
||||
* https://wiki.multimedia.cx/index.php/Origin_Xan_Codec
|
||||
* and ffmpeg's libavcodec/xxan.c.
|
||||
* The ffmpeg code is LGPL2 licensed and Copyright (C) 2011
|
||||
* Konstantin Shishkov based on work by Mike Melanson.
|
||||
*
|
||||
* A similar format is used in Wing Commander III (although not in an AVI
|
||||
* container) and IV.
|
||||
*/
|
||||
class XanDecoder : public Codec {
|
||||
public:
|
||||
XanDecoder (int width, int height, int bitsPerPixel);
|
||||
~XanDecoder() override;
|
||||
|
||||
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
|
||||
bool setOutputPixelFormat(const Graphics::PixelFormat &format) override {
|
||||
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4)
|
||||
return false;
|
||||
_pixelFormat = format;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void decodeFrameType0(Common::SeekableReadStream &stream);
|
||||
void decodeFrameType1(Common::SeekableReadStream &stream);
|
||||
|
||||
/** Decompress the huffman table for base luma data */
|
||||
void decompressLuma(Common::SeekableReadStream &stream);
|
||||
|
||||
bool decodeChroma(Common::SeekableReadStream &stream, int chroma_off);
|
||||
|
||||
/** convert the internally expanded YUV to the output RGBA surface */
|
||||
void convertYUVtoRGBSurface();
|
||||
|
||||
/** A buffer to hold the final frame */
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
/** Dest surface width and height */
|
||||
int _width, _height;
|
||||
|
||||
/** Dest surface pixel format */
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
|
||||
/** If true, decode chroma vals in Wing Commander 4 style (false = No Regret style) */
|
||||
bool _wc4Mode;
|
||||
|
||||
/** A buffer to hold scratch data. Either chroma data in progress, or
|
||||
* decompressed delta luma values (5-bit). Interpretation depends on frame type. */
|
||||
uint8 *_scratchbuf;
|
||||
/** A buffer to hold expanded/interpolated absolute luma values (6-bit) from the values in _scratchbuf.
|
||||
* These still need to be multiplied out to make 8-bit values. */
|
||||
uint8 *_lumabuf;
|
||||
/** a buffer for uncompressed and multiplied out "y" values (of yuv) */
|
||||
uint8 *_ybuf;
|
||||
/** a buffer for uncompressed "u" values (of yuv) */
|
||||
uint8 *_ubuf;
|
||||
/** a buffer for uncompressed "v" values (of yuv) */
|
||||
uint8 *_vbuf;
|
||||
};
|
||||
|
||||
} // End of namespace Image
|
||||
|
||||
#endif
|
||||
134
image/gif.cpp
Normal file
134
image/gif.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "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
75
image/gif.h
Normal 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
142
image/icocur.cpp
Normal 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
106
image/icocur.h
Normal 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
256
image/iff.cpp
Normal 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
129
image/iff.h
Normal 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
125
image/image_decoder.h
Normal 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
387
image/jpeg.cpp
Normal 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
121
image/jpeg.h
Normal 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
91
image/module.mk
Normal 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
119
image/neo.cpp
Normal 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
64
image/neo.h
Normal 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
202
image/pcx.cpp
Normal 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
71
image/pcx.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_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
818
image/pict.cpp
Normal 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
158
image/pict.h
Normal 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
368
image/png.cpp
Normal 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
107
image/png.h
Normal 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
115
image/scr.cpp
Normal 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
65
image/scr.h
Normal 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
410
image/tga.cpp
Normal 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
109
image/tga.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* 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
82
image/xbm.cpp
Normal 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
76
image/xbm.h
Normal 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
|
||||
Reference in New Issue
Block a user