aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--image/codecs/rpza.cpp270
-rw-r--r--image/codecs/rpza.h14
2 files changed, 230 insertions, 54 deletions
diff --git a/image/codecs/rpza.cpp b/image/codecs/rpza.cpp
index 5aeee7c90b..8d648e1cc1 100644
--- a/image/codecs/rpza.cpp
+++ b/image/codecs/rpza.cpp
@@ -32,43 +32,185 @@
namespace Image {
RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() {
- // We need to increase the surface size to a multiple of 4
- uint16 wMod = width % 4;
- if (wMod != 0)
- width += 4 - wMod;
-
- _surface = new Graphics::Surface();
- _surface->create(width, height, getPixelFormat());
+ _format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ _ditherPalette = 0;
+ _dirtyPalette = false;
+ _colorMap = 0;
+ _width = width;
+ _height = height;
+ _blockWidth = (width + 3) / 4;
+ _blockHeight = (height + 3) / 4;
+ _surface = 0;
}
RPZADecoder::~RPZADecoder() {
- _surface->free();
- delete _surface;
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+
+ delete[] _ditherPalette;
}
#define ADVANCE_BLOCK() \
- pixelPtr += 4; \
- if (pixelPtr >= _surface->w) { \
- pixelPtr = 0; \
- rowPtr += _surface->w * 4; \
+ 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)") \
-#define PUT_PIXEL(color) \
- if ((int32)blockPtr < _surface->w * _surface->h) \
- WRITE_UINT16((uint16 *)_surface->getPixels() + blockPtr, color); \
- blockPtr++
+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;
+ }
-const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
+ 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];
- uint32 rowPtr = 0;
- uint32 pixelPtr = 0;
- uint32 blockPtr = 0;
- uint32 rowInc = _surface->w - 4;
+ PixelInt *blockPtr = ptr;
+ PixelInt *endPtr = ptr + pitch;
uint16 ta;
uint16 tb;
@@ -88,7 +230,7 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
}
// Number of 4x4 blocks in frame
- int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
+ int32 totalBlocks = blockWidth * blockHeight;
// Process chunk data
while ((uint32)stream.pos() < chunkSize) {
@@ -117,14 +259,9 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
break;
case 0xa0: // Fill blocks with one color
colorA = stream.readUint16BE();
+
while (numBlocks--) {
- blockPtr = rowPtr + pixelPtr;
- for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
- for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
- PUT_PIXEL(colorA);
- }
- blockPtr += rowInc;
- }
+ BlockDecoder::drawFillBlock(blockPtr, pitch, colorA, colorMap);
ADVANCE_BLOCK();
}
break;
@@ -136,10 +273,10 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
colorB = stream.readUint16BE();
// Sort out the colors
- color4[0] = colorB;
+ color4[0] = colorB & 0x7FFF;
color4[1] = 0;
color4[2] = 0;
- color4[3] = colorA;
+ color4[3] = colorA & 0x7FFF;
// Red components
ta = (colorA >> 10) & 0x1F;
@@ -160,42 +297,69 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
color4[2] |= ((21 * ta + 11 * tb) >> 5);
while (numBlocks--) {
- blockPtr = rowPtr + pixelPtr;
- for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
- byte index = stream.readByte();
- for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
- byte idx = (index >> (2 * (3 - pixel_x))) & 0x03;
- PUT_PIXEL(color4[idx]);
- }
- blockPtr += rowInc;
- }
+ byte indexes[4];
+ stream.read(indexes, 4);
+
+ BlockDecoder::drawBlendBlock(blockPtr, pitch, color4, indexes, colorMap);
ADVANCE_BLOCK();
}
break;
// Fill block with 16 colors
- case 0x00:
- blockPtr = rowPtr + pixelPtr;
- for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
- for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
- // We already have color of upper left pixel
- if (pixel_y != 0 || pixel_x != 0)
- colorA = stream.readUint16BE();
-
- PUT_PIXEL(colorA);
- }
- blockPtr += rowInc;
- }
+ 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 = new byte[256 * 3];
+ memcpy(_ditherPalette, palette, 256 * 3);
+
+ _dirtyPalette = true;
+ _format = Graphics::PixelFormat::createFormatCLUT8();
+
+ delete[] _colorMap;
+ _colorMap = createQuickTimeDitherTable(palette, 256);
+}
+
} // End of namespace Image
diff --git a/image/codecs/rpza.h b/image/codecs/rpza.h
index d1dbbdb676..d62b385330 100644
--- a/image/codecs/rpza.h
+++ b/image/codecs/rpza.h
@@ -39,10 +39,22 @@ public:
~RPZADecoder();
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
- Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); }
+ Graphics::PixelFormat getPixelFormat() const { return _format; }
+
+ 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:
+ Graphics::PixelFormat _format;
Graphics::Surface *_surface;
+ byte *_ditherPalette;
+ bool _dirtyPalette;
+ byte *_colorMap;
+ uint16 _width, _height;
+ uint16 _blockWidth, _blockHeight;
};
} // End of namespace Image