aboutsummaryrefslogtreecommitdiff
path: root/image/codecs
diff options
context:
space:
mode:
authorMatthew Hoops2015-03-15 19:35:13 -0400
committerMatthew Hoops2015-04-11 14:37:13 -0400
commit94b317b0cd979bd924da14d8e3925c6c324692e6 (patch)
treeecd699a0c7fccbe7f92aafd8ccb7082f2e309cce /image/codecs
parent7f12db95c6c7689f7f7f8905aaf122db6de13e4f (diff)
downloadscummvm-rg350-94b317b0cd979bd924da14d8e3925c6c324692e6.tar.gz
scummvm-rg350-94b317b0cd979bd924da14d8e3925c6c324692e6.tar.bz2
scummvm-rg350-94b317b0cd979bd924da14d8e3925c6c324692e6.zip
IMAGE: Implement QTRLE dithering
Diffstat (limited to 'image/codecs')
-rw-r--r--image/codecs/qtrle.cpp164
-rw-r--r--image/codecs/qtrle.h15
2 files changed, 153 insertions, 26 deletions
diff --git a/image/codecs/qtrle.cpp b/image/codecs/qtrle.cpp
index 94744efa5a..8a83cfa2bd 100644
--- a/image/codecs/qtrle.cpp
+++ b/image/codecs/qtrle.cpp
@@ -37,27 +37,45 @@ namespace Image {
QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
_bitsPerPixel = bitsPerPixel;
+ _ditherPalette = 0;
+ _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)
- width += 4 - wMod;
+ _paddedWidth += 4 - wMod;
+}
- _surface = new Graphics::Surface();
- _surface->create(width, height, getPixelFormat());
+QTRLEDecoder::~QTRLEDecoder() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+
+ delete[] _colorMap;
+ delete[] _ditherPalette;
}
#define CHECK_STREAM_PTR(n) \
- if ((stream.pos() + n) > stream.size()) { \
- warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
- return; \
- }
+ do { \
+ if ((stream.pos() + n) > stream.size()) { \
+ warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
+ return; \
+ } \
+ } while (0)
#define CHECK_PIXEL_PTR(n) \
- if ((int32)pixelPtr + n > _surface->w * _surface->h) { \
- warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _surface->w * _surface->h); \
- return; \
- } \
+ 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;
@@ -73,7 +91,7 @@ void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, ui
if (skip & 0x80) {
linesToChange--;
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
pixelPtr = rowPtr + 2 * (skip & 0x7f);
} else
pixelPtr += 2 * skip;
@@ -159,7 +177,7 @@ void QTRLEDecoder::decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr,
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
@@ -204,7 +222,7 @@ void QTRLEDecoder::decode8(Common::SeekableReadStream &stream, uint32 rowPtr, ui
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
@@ -242,7 +260,7 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
@@ -288,7 +306,72 @@ void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
+ }
+}
+
+namespace {
+
+inline uint16 readDitherColor24(Common::ReadStream &stream) {
+ uint16 color = (stream.readByte() & 0xF8) << 6;
+ color |= (stream.readByte() & 0xF8) << 1;
+ color |= stream.readByte() >> 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 (int8 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);
+ CHECK_PIXEL_PTR(rleCode);
+
+ uint16 color = readDitherColor24(stream);
+
+ 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;
}
}
@@ -336,13 +419,16 @@ void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
+ if (!_surface)
+ createSurface();
+
uint16 startLine = 0;
- uint16 height = _surface->h;
+ uint16 height = _height;
// check if this frame is even supposed to change
if (stream.size() < 8)
@@ -365,7 +451,7 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
stream.readUint16BE(); // Unknown
}
- uint32 rowPtr = _surface->w * startLine;
+ uint32 rowPtr = _paddedWidth * startLine;
switch (_bitsPerPixel) {
case 1:
@@ -388,7 +474,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
decode16(stream, rowPtr, height);
break;
case 24:
- decode24(stream, rowPtr, height);
+ if (_ditherPalette)
+ dither24(stream, rowPtr, height);
+ else
+ decode24(stream, rowPtr, height);
break;
case 32:
decode32(stream, rowPtr, height);
@@ -400,12 +489,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
return _surface;
}
-QTRLEDecoder::~QTRLEDecoder() {
- _surface->free();
- delete _surface;
-}
-
Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
+ if (_ditherPalette)
+ return Graphics::PixelFormat::createFormatCLUT8();
+
switch (_bitsPerPixel) {
case 1:
case 33:
@@ -428,4 +515,31 @@ Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
return Graphics::PixelFormat();
}
+bool QTRLEDecoder::canDither(DitherType type) const {
+ // Only 24-bit dithering is implemented at the moment
+ return type == kDitherTypeQT && _bitsPerPixel == 24;
+}
+
+void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
+ assert(canDither(type));
+
+ _ditherPalette = new byte[256 * 3];
+ memcpy(_ditherPalette, palette, 256 * 3);
+ _dirtyPalette = true;
+
+ delete[] _colorMap;
+ _colorMap = 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
diff --git a/image/codecs/qtrle.h b/image/codecs/qtrle.h
index b44a46c3e2..e345fbeedf 100644
--- a/image/codecs/qtrle.h
+++ b/image/codecs/qtrle.h
@@ -41,16 +41,29 @@ public:
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
Graphics::PixelFormat getPixelFormat() const;
+ bool containsPalette() const { return _ditherPalette != 0; }
+ const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+ bool canDither(DitherType type) const;
+ void setDither(DitherType type, const byte *palette);
+
private:
byte _bitsPerPixel;
-
Graphics::Surface *_surface;
+ uint16 _width, _height;
+ uint32 _paddedWidth;
+ byte *_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 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);
};