mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-02 17:03:13 +00:00
32bd7cf446
This clears static analysis error
1400 lines
39 KiB
C++
1400 lines
39 KiB
C++
/* 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/hnm.h"
|
|
|
|
#include "common/stream.h"
|
|
#include "common/textconsole.h"
|
|
#include "common/util.h"
|
|
|
|
#include "graphics/surface.h"
|
|
|
|
#include "common/debug.h"
|
|
|
|
// This define enables code which generate bit accurate images in RGB565 mode
|
|
//#define BITEXACT
|
|
|
|
namespace Image {
|
|
|
|
namespace HNM6 {
|
|
|
|
struct BitBuffer {
|
|
BitBuffer() :
|
|
_buffer(nullptr), _size(0), _pos(0), _reg(0), _bits(0) { }
|
|
|
|
inline void reset(byte *buffer, uint32 size);
|
|
FORCEINLINE byte next();
|
|
|
|
private:
|
|
inline void loadReg();
|
|
|
|
byte *_buffer;
|
|
uint32 _size;
|
|
uint32 _pos;
|
|
|
|
uint32 _reg;
|
|
uint32 _bits;
|
|
};
|
|
|
|
struct MotionBuffer {
|
|
MotionBuffer() : _buffer(nullptr), _size(0), _pos(0) { }
|
|
|
|
inline void reset(byte *buffer, uint32 size);
|
|
FORCEINLINE uint16 next();
|
|
|
|
private:
|
|
byte *_buffer;
|
|
uint32 _size;
|
|
uint32 _pos;
|
|
};
|
|
|
|
struct ShortMotionBuffer {
|
|
ShortMotionBuffer() :
|
|
_buffer(nullptr), _size(0), _pos(0), _other(0) { }
|
|
|
|
inline void reset(byte *buffer, uint32 size);
|
|
FORCEINLINE uint16 next();
|
|
|
|
private:
|
|
inline uint16 loadReg();
|
|
|
|
byte *_buffer;
|
|
uint32 _size;
|
|
uint32 _pos;
|
|
|
|
uint16 _other;
|
|
};
|
|
|
|
struct JPEGBuffer {
|
|
JPEGBuffer() :
|
|
_buffer(nullptr), _size(0), _pos(0), _other(0) { }
|
|
|
|
inline void reset(byte *buffer, uint32 size);
|
|
FORCEINLINE byte next();
|
|
|
|
private:
|
|
inline byte loadReg();
|
|
|
|
byte *_buffer;
|
|
uint32 _size;
|
|
uint32 _pos;
|
|
|
|
byte _other;
|
|
};
|
|
|
|
void BitBuffer::reset(byte *buffer, uint32 size) {
|
|
_buffer = buffer;
|
|
_size = size;
|
|
_pos = 0;
|
|
//_reg = 0; // Useless
|
|
_bits = 0;
|
|
}
|
|
|
|
byte BitBuffer::next() {
|
|
if (!_bits) {
|
|
loadReg();
|
|
}
|
|
byte ret = _reg >> 31;
|
|
_reg <<= 1;
|
|
_bits--;
|
|
return ret;
|
|
}
|
|
|
|
void BitBuffer::loadReg() {
|
|
if (_bits) {
|
|
error("BUG: Loading register while still loaded");
|
|
}
|
|
if (_pos + 4 > _size) {
|
|
error("Can't feed bitbuffer register: not enough data");
|
|
}
|
|
_reg = READ_LE_UINT32(_buffer + _pos);
|
|
_pos += sizeof(uint32);
|
|
_bits = sizeof(uint32) * 8;
|
|
}
|
|
|
|
void MotionBuffer::reset(byte *buffer, uint32 size) {
|
|
_buffer = buffer;
|
|
_size = size;
|
|
_pos = 0;
|
|
}
|
|
|
|
uint16 MotionBuffer::next() {
|
|
if (_pos + 2 > _size) {
|
|
error("Can't get motion word: not enough data");
|
|
}
|
|
uint16 ret = READ_LE_UINT16(_buffer + _pos);
|
|
_pos += sizeof(uint16);
|
|
return ret;
|
|
}
|
|
|
|
void ShortMotionBuffer::reset(byte *buffer, uint32 size) {
|
|
_buffer = buffer;
|
|
_size = size;
|
|
_pos = 0;
|
|
_other = 0;
|
|
}
|
|
|
|
uint16 ShortMotionBuffer::next() {
|
|
if (_other) {
|
|
uint16 ret = _other & 0xfff;
|
|
_other = 0;
|
|
return ret;
|
|
}
|
|
|
|
return loadReg();
|
|
}
|
|
|
|
uint16 ShortMotionBuffer::loadReg() {
|
|
if (_other) {
|
|
error("BUG: Loading register while still loaded");
|
|
}
|
|
if (_pos + 2 > _size) {
|
|
error("Can't feed shortmo register: not enough data");
|
|
}
|
|
if (_pos + 3 > _size) {
|
|
// We are at the end: load the last 2 bytes for a last word
|
|
uint16 ret = READ_LE_UINT16(_buffer + _pos);
|
|
_pos += sizeof(uint16);
|
|
return ret & 0xfff;
|
|
}
|
|
uint32 fullword = READ_LE_UINT16(_buffer + _pos);
|
|
fullword |= *(_buffer + _pos + 2) << 16;
|
|
_pos += 3 * sizeof(byte);
|
|
|
|
// 0x8000 is a marker of validity
|
|
_other = ((fullword >> 12) & 0xfff) | 0x8000;
|
|
return fullword & 0xfff;
|
|
}
|
|
|
|
void JPEGBuffer::reset(byte *buffer, uint32 size) {
|
|
_buffer = buffer;
|
|
_size = size;
|
|
_pos = 0;
|
|
_other = 0;
|
|
}
|
|
|
|
byte JPEGBuffer::next() {
|
|
if (_other) {
|
|
byte ret = _other & 0xf;
|
|
_other = 0;
|
|
return ret;
|
|
}
|
|
|
|
return loadReg();
|
|
}
|
|
|
|
byte JPEGBuffer::loadReg() {
|
|
if (_other) {
|
|
error("BUG: Loading register while still loaded");
|
|
}
|
|
if (_pos > _size) {
|
|
error("Can't feed JPEG register: not enough data");
|
|
}
|
|
|
|
byte fullword = *(_buffer + _pos);
|
|
_pos++;
|
|
|
|
// 0x80 is a marker of validity
|
|
_other = (fullword >> 4) | 0x80;
|
|
return fullword & 0xf;
|
|
}
|
|
|
|
static void YUVtoRGB(Graphics::Surface ¤t, uint x, uint y, int32 *coeffs) {
|
|
#define CR(x) ((16 * (x - 256) + 8) / 10)
|
|
#define CB(x) ((x - 256) / 3)
|
|
Graphics::PixelFormat &format = current.format;
|
|
for (uint ry = 0; ry < 8; ry++) {
|
|
void *linePtr = current.getBasePtr(x, y + ry);
|
|
for (uint rx = 0; rx < 8; rx++, coeffs++) {
|
|
int32 cy = coeffs[0];
|
|
int32 cu = coeffs[64];
|
|
int32 cv = coeffs[128];
|
|
|
|
cy = cy >> 4;
|
|
|
|
int32 cr = CR((cv >> 4) + 256);
|
|
int32 cb = CB((cu >> 4) + 256);
|
|
|
|
byte r = CLIP<int32>(cy + 128 + cr, 0, 255);
|
|
#ifdef BITEXACT
|
|
// In original code G is clipped a little more
|
|
byte g = CLIP<int32>(cy + 128 - (cr >> 1) - cb, 0, 251);
|
|
#else
|
|
byte g = CLIP<int32>(cy + 128 - (cr >> 1) - cb, 0, 255);
|
|
#endif
|
|
byte b = CLIP<int32>(cy + 128 + (cu >> 3), 0, 255);
|
|
|
|
#ifdef BITEXACT
|
|
// Original shifts R and B by 3 and G by 2
|
|
r &= 0xf8;
|
|
g &= 0xfc;
|
|
b &= 0xf8;
|
|
#endif
|
|
|
|
if (format.bytesPerPixel == 2) {
|
|
((uint16 *)linePtr)[rx] = format.RGBToColor(r, g, b);
|
|
} else if (format.bytesPerPixel == 4) {
|
|
((uint32 *)linePtr)[rx] = format.RGBToColor(r, g, b);
|
|
}
|
|
}
|
|
}
|
|
#undef CR
|
|
#undef CB
|
|
}
|
|
|
|
/**
|
|
* This is an optimized Arai, Agui & Nakajima implementation
|
|
* Only the rows and columns set with values are taken into account
|
|
* The order of the IDCT being equivalent, the mask is used to
|
|
* determine where to start
|
|
*/
|
|
#define AAN_STRIDE(inout, stride, inc, mask) do { \
|
|
int32 *x = inout; \
|
|
byte mask_ = mask; \
|
|
for(uint i = 0; i < 8 && mask_; \
|
|
i++, x += inc, mask_ >>= 1) { \
|
|
int32 x2n6 = x[2*stride] - x[6*stride]; \
|
|
int32 x2p6 = x[2*stride] + x[6*stride]; \
|
|
int32 x0n4 = x[0*stride] - x[4*stride]; \
|
|
int32 x0p4 = x[0*stride] + x[4*stride]; \
|
|
\
|
|
int32 x5n3 = x[5*stride] - x[3*stride]; \
|
|
int32 x3p5 = x[3*stride] + x[5*stride]; \
|
|
int32 x1n7 = x[1*stride] - x[7*stride]; \
|
|
int32 x1p7 = x[1*stride] + x[7*stride]; \
|
|
\
|
|
int32 tmp0 = ((3* x2n6) >> 1) - x2p6; \
|
|
int32 tmp1 = (3 * (x1p7 - x3p5)) >> 1; \
|
|
int32 tmp2 = 30 * (x5n3 + x1n7); \
|
|
int32 tmp3 = (17 * x1n7 - tmp2) >> 4; \
|
|
int32 tmp4 = (tmp2 - 40 * x5n3) >> 4; \
|
|
int32 tmp5 = tmp4 - x3p5 - x1p7; \
|
|
\
|
|
x[0*stride] = x1p7 + x3p5 + x0p4 + x2p6; \
|
|
x[7*stride] = x0p4 + x2p6 - x1p7 - x3p5; \
|
|
x[1*stride] = tmp0 + x0n4 + tmp4 - x3p5 - x1p7; \
|
|
x[6*stride] = tmp0 + x0n4 - tmp4 + x3p5 + x1p7; \
|
|
x[2*stride] = x0n4 - tmp0 + tmp1 - tmp5; \
|
|
x[5*stride] = x0n4 - tmp0 - tmp1 + tmp5; \
|
|
x[3*stride] = x0p4 - x2p6 - tmp1 + tmp5 - tmp3; \
|
|
x[4*stride] = x0p4 - x2p6 + tmp1 - tmp5 + tmp3; \
|
|
} \
|
|
} while(0)
|
|
|
|
static void aanidct(int32 *inout, uint16 mask) {
|
|
if (mask == 0x0101) {
|
|
// Simple case: 1 coefficient in the top left corner: it means constant
|
|
for (uint i = 1; i < 64; i++) {
|
|
inout[i] = inout[0];
|
|
}
|
|
return;
|
|
}
|
|
|
|
#ifdef BITEXACT
|
|
// Original decoder has a bug where the masks are compared
|
|
// with a sign compare. This means that 0x80 < 0x1.
|
|
// The AAN order is then not optimal.
|
|
int8 rowmask, colmask;
|
|
#else
|
|
uint8 rowmask, colmask;
|
|
#endif
|
|
rowmask = mask >> 8;
|
|
colmask = mask & 0xff;
|
|
|
|
if (rowmask < colmask) {
|
|
// Inside row
|
|
AAN_STRIDE(inout, 1, 8, rowmask);
|
|
if (rowmask == 0x01) {
|
|
// Simple case: Only one line filled, copy to others
|
|
for (uint i = 1; i < 8; i++) {
|
|
memcpy(inout + 8 * i, inout, sizeof(*inout) * 8);
|
|
}
|
|
} else {
|
|
AAN_STRIDE(inout, 8, 1, 0xff);
|
|
}
|
|
} else {
|
|
// Inside column
|
|
AAN_STRIDE(inout, 8, 1, colmask);
|
|
if (colmask == 0x01) {
|
|
// Simple case: Only one column filled, copy to others
|
|
for (uint i = 0; i < 8; i++) {
|
|
// NlogN memset
|
|
inout[8 * i + 1] = inout[8 * i + 0];
|
|
memcpy(inout + 8 * i + 2, inout + 8 * i, sizeof(*inout) * 2);
|
|
memcpy(inout + 8 * i + 4, inout + 8 * i, sizeof(*inout) * 4);
|
|
}
|
|
} else {
|
|
AAN_STRIDE(inout, 1, 8, 0xff);
|
|
}
|
|
}
|
|
}
|
|
#undef AAN_STRIDE
|
|
|
|
template<int sx, int sy, typename PixelType>
|
|
static void doMotion(byte xform, Graphics::Surface &dst, const Graphics::Surface &src,
|
|
int srx, int sry, int dx, int dy) {
|
|
int bpp = sizeof(PixelType);
|
|
int stride = dst.pitch;
|
|
const byte *srcP;
|
|
byte *dstP;
|
|
#define COPY_PIXEL() *((PixelType *)dstP) = *((const PixelType *)srcP)
|
|
|
|
switch (xform) {
|
|
case 0: // Copy
|
|
srcP = (const byte *)src.getBasePtr(srx, sry);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
memmove(dstP, srcP, bpp * sx);
|
|
dstP += stride;
|
|
srcP += stride;
|
|
}
|
|
break;
|
|
case 1: // Horizontal Flip
|
|
srcP = (const byte *)src.getBasePtr(srx + sx - 1, sry);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
for (int x = 0; x < sx; x++) {
|
|
COPY_PIXEL();
|
|
dstP += bpp;
|
|
srcP -= bpp;
|
|
}
|
|
dstP += stride - sx * bpp;
|
|
srcP += stride + sx * bpp;
|
|
}
|
|
break;
|
|
case 2: // Vertical Flip
|
|
srcP = (const byte *)src.getBasePtr(srx, sry + sy - 1);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
memmove(dstP, srcP, bpp * sx);
|
|
dstP += stride;
|
|
srcP -= stride;
|
|
}
|
|
break;
|
|
case 3: // Cross Flip
|
|
srcP = (const byte *)src.getBasePtr(srx + sx - 1, sry + sy - 1);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
for (int x = 0; x < sx; x++) {
|
|
COPY_PIXEL();
|
|
dstP += bpp;
|
|
srcP -= bpp;
|
|
}
|
|
dstP += stride - sx * bpp;
|
|
srcP -= stride - sx * bpp;
|
|
}
|
|
break;
|
|
case 4: // Forward Flip
|
|
srcP = (const byte *)src.getBasePtr(srx + sy - 1, sry + sx - 1);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
for (int x = 0; x < sx; x++) {
|
|
COPY_PIXEL();
|
|
dstP += bpp;
|
|
srcP -= stride;
|
|
}
|
|
dstP += stride - sx * bpp;
|
|
srcP += sx * stride - bpp;
|
|
}
|
|
break;
|
|
case 5: // Forward Rotate
|
|
srcP = (const byte *)src.getBasePtr(srx, sry + sx - 1);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
for (int x = 0; x < sx; x++) {
|
|
COPY_PIXEL();
|
|
dstP += bpp;
|
|
srcP -= stride;
|
|
}
|
|
dstP += stride - sx * bpp;
|
|
srcP += sx * stride + bpp;
|
|
}
|
|
break;
|
|
case 6: // Backward Rotate
|
|
srcP = (const byte *)src.getBasePtr(srx + sy - 1, sry);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
for (int x = 0; x < sx; x++) {
|
|
COPY_PIXEL();
|
|
dstP += bpp;
|
|
srcP += stride;
|
|
}
|
|
dstP += stride - sx * bpp;
|
|
srcP -= sx * stride + bpp;
|
|
}
|
|
break;
|
|
case 7: // Backward Flip
|
|
srcP = (const byte *)src.getBasePtr(srx, sry);
|
|
dstP = (byte *)dst.getBasePtr(dx, dy);
|
|
for (int y = 0; y < sy; y++) {
|
|
for (int x = 0; x < sx; x++) {
|
|
COPY_PIXEL();
|
|
dstP += bpp;
|
|
srcP += stride;
|
|
}
|
|
dstP += stride - sx * bpp;
|
|
srcP -= sx * stride - bpp;
|
|
}
|
|
break;
|
|
default:
|
|
error("BUG: Invalid motion transform");
|
|
}
|
|
#undef COPY_PIXEL
|
|
}
|
|
|
|
template<int sx, int sy>
|
|
FORCEINLINE static void doMotion(byte xform, Graphics::Surface &dst, const Graphics::Surface &src,
|
|
int srx, int sry, int dx, int dy) {
|
|
if (dst.format.bytesPerPixel == 2) {
|
|
doMotion<sx, sy, uint16>(xform, dst, src, srx, sry, dx, dy);
|
|
} else if (dst.format.bytesPerPixel == 4) {
|
|
doMotion<sx, sy, uint32>(xform, dst, src, srx, sry, dx, dy);
|
|
}
|
|
}
|
|
|
|
/* Classic JPEG quantization tables */
|
|
static const int32 LUMA_QUANT_TABLE[] = {
|
|
16, 11, 10, 16, 24, 40, 51, 61,
|
|
12, 12, 14, 19, 26, 58, 60, 55,
|
|
14, 13, 16, 24, 40, 57, 69, 56,
|
|
14, 17, 22, 29, 51, 87, 80, 62,
|
|
18, 22, 37, 56, 68, 109, 103, 77,
|
|
24, 35, 55, 64, 81, 104, 113, 92,
|
|
49, 64, 78, 87, 103, 121, 120, 101,
|
|
72, 92, 95, 98, 112, 100, 103, 99
|
|
};
|
|
|
|
static const int32 CHROMA_QUANT_TABLE[] = {
|
|
17, 18, 24, 47, 99, 99, 99, 99,
|
|
18, 21, 26, 66, 99, 99, 99, 99,
|
|
24, 26, 56, 99, 99, 99, 99, 99,
|
|
47, 66, 99, 99, 99, 99, 99, 99,
|
|
99, 99, 99, 99, 99, 99, 99, 99,
|
|
99, 99, 99, 99, 99, 99, 99, 99,
|
|
99, 99, 99, 99, 99, 99, 99, 99,
|
|
99, 99, 99, 99, 99, 99, 99, 99
|
|
};
|
|
|
|
/* Factors for the Arai, Agui & Nakajima IDCT */
|
|
static const int32 AAN_FACTORS[] = {
|
|
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
|
|
22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270,
|
|
21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906,
|
|
19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315,
|
|
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
|
|
12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552,
|
|
8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446,
|
|
4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247
|
|
};
|
|
|
|
/* Classic JPEG zig-zag encoding table */
|
|
static const byte ZIGZAG[] = {
|
|
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
|
|
};
|
|
|
|
/* These masks are used to tell which rows (high order) and columns (low order) have values.
|
|
* This allow to skip some operations in IDCT. */
|
|
static const uint16 MASKS[] = {
|
|
0x0101, 0x0102, 0x0201, 0x0401, 0x0202, 0x0104, 0x0108, 0x0204,
|
|
0x0402, 0x0801, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x0120,
|
|
0x0210, 0x0408, 0x0804, 0x1002, 0x2001, 0x4001, 0x2002, 0x1004,
|
|
0x0808, 0x0410, 0x0220, 0x0140, 0x0180, 0x0240, 0x0420, 0x0810,
|
|
0x1008, 0x2004, 0x4002, 0x8001, 0x8002, 0x4004, 0x2008, 0x1010,
|
|
0x0820, 0x0440, 0x0280, 0x0480, 0x0840, 0x1020, 0x2010, 0x4008,
|
|
0x8004, 0x8008, 0x4010, 0x2020, 0x1040, 0x0880, 0x1080, 0x2040,
|
|
0x4020, 0x8010, 0x8020, 0x4040, 0x2080, 0x4080, 0x8040, 0x8080
|
|
};
|
|
|
|
static const int8 S2[] = {
|
|
1, 1, 1, 2, 1, -2, 1, -1,
|
|
2, 1, 2, 2, 2, -2, 2, -1,
|
|
-2, 1, -2, 2, -2, -2, -2, -1,
|
|
-1, 1, -1, 2, -1, -2, -1, -1
|
|
};
|
|
|
|
static const int8 S4[] = {
|
|
1, 2, 3, 4, 5, 6, 7, 8,
|
|
-8, -7, -6, -5, -4, -3, -2, -1
|
|
};
|
|
|
|
STATIC_ASSERT(ARRAYSIZE(LUMA_QUANT_TABLE) == 64, "invalid luma table size");
|
|
STATIC_ASSERT(ARRAYSIZE(CHROMA_QUANT_TABLE) == 64, "invalid chromaa table size");
|
|
STATIC_ASSERT(ARRAYSIZE(AAN_FACTORS) == 64, "invalid AAN factors table size");
|
|
STATIC_ASSERT(ARRAYSIZE(ZIGZAG) == 64, "invalid zigzag table size");
|
|
STATIC_ASSERT(ARRAYSIZE(S2) == 16 * 2, "invalid S2 table size");
|
|
STATIC_ASSERT(ARRAYSIZE(S4) == 16, "invalid S4 table size");
|
|
|
|
static const int HEADER_SIZE = 6 * sizeof(uint32);
|
|
|
|
/**
|
|
* HNM6 image decoder interface.
|
|
*
|
|
* Used by HNM6 image and video format.
|
|
*/
|
|
class DecoderImpl : public HNM6Decoder {
|
|
public:
|
|
DecoderImpl(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
|
uint32 bufferSize, bool videoMode = false);
|
|
~DecoderImpl() override;
|
|
|
|
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
|
|
|
|
private:
|
|
uint32 _bufferSize;
|
|
byte *_buffer;
|
|
Graphics::Surface _surface;
|
|
Graphics::Surface _surfaceOld;
|
|
|
|
bool _keyframe;
|
|
HNM6::BitBuffer _bitbuf;
|
|
HNM6::MotionBuffer _motion;
|
|
HNM6::ShortMotionBuffer _shortmo;
|
|
HNM6::JPEGBuffer _jpeg;
|
|
|
|
int32 coeffs[3 * 64];
|
|
int32 luma_quant_table[64];
|
|
int32 chroma_quant_table[64];
|
|
|
|
// Simple functions and called at one place only
|
|
FORCEINLINE void reset(byte *buffer, uint32 bit_start, uint32 motion_start,
|
|
uint32 shortmotion_start, uint32 jpeg_start, uint32 end,
|
|
int32 quality);
|
|
// Just a wrapper
|
|
FORCEINLINE void decode(Graphics::Surface ¤t, Graphics::Surface &old);
|
|
// Just a loop wrapper
|
|
FORCEINLINE void decodeIWkf(Graphics::Surface ¤t);
|
|
void decodeIWkf88(Graphics::Surface ¤t, uint x, uint y);
|
|
void decodeIWkf44(Graphics::Surface ¤t, uint x, uint y);
|
|
// Just a loop wrapper
|
|
FORCEINLINE void decodeIXkf(Graphics::Surface ¤t);
|
|
inline void decodeIXkf88(Graphics::Surface ¤t, uint x, uint y);
|
|
// Simple functions and called at one place only
|
|
FORCEINLINE void decodeIXkf48(Graphics::Surface ¤t, uint x, uint y);
|
|
FORCEINLINE void decodeIXkf84(Graphics::Surface ¤t, uint x, uint y);
|
|
void decodeIXkf44(Graphics::Surface ¤t, uint x, uint y);
|
|
// Simple functions and called at one place only
|
|
FORCEINLINE void decodeIXkf24(Graphics::Surface ¤t, uint x, uint y);
|
|
FORCEINLINE void decodeIXkf42(Graphics::Surface ¤t, uint x, uint y);
|
|
// Just a loop wrapper
|
|
FORCEINLINE void decodeIXif(Graphics::Surface ¤t, Graphics::Surface &previous);
|
|
void decodeIXif88(Graphics::Surface ¤t, Graphics::Surface &previous, uint x, uint y);
|
|
// Simple functions and called at one place only
|
|
FORCEINLINE void decodeIXif48(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
FORCEINLINE void decodeIXif84(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
void decodeIXif44(Graphics::Surface ¤t, Graphics::Surface &previous, uint x, uint y);
|
|
// Simple functions and called at one place only
|
|
FORCEINLINE void decodeIXif24(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
FORCEINLINE void decodeIXif42(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
void decodeIXif22(Graphics::Surface ¤t, Graphics::Surface &previous, uint x, uint y);
|
|
// Simple function and called at 3 places only
|
|
FORCEINLINE void renderKeyblock(Graphics::Surface ¤t, uint x, uint y);
|
|
void renderPlane(int32 *dst, int32 *quant_table);
|
|
// Time-critical parsing functions
|
|
template<int sx, int sy>
|
|
FORCEINLINE void renderIWmotion(Graphics::Surface ¤t, uint x, uint y);
|
|
FORCEINLINE void renderIWshortmo(Graphics::Surface ¤t, uint x, uint y);
|
|
template<int sx, int sy, bool small = false>
|
|
FORCEINLINE void renderIXkfMotion(Graphics::Surface ¤t, uint x, uint y);
|
|
template<int sx, int sy>
|
|
FORCEINLINE void renderSkip(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
template<int sx, int sy, bool small = false>
|
|
FORCEINLINE void renderIXifMotion(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
template<int sx, int sy>
|
|
FORCEINLINE void renderIXifShortmo(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y);
|
|
};
|
|
|
|
void DecoderImpl::reset(byte *buffer, uint32 bit_start, uint32 motion_start,
|
|
uint32 shortmotion_start, uint32 jpeg_start, uint32 end,
|
|
int32 quality) {
|
|
_bitbuf.reset(buffer + bit_start, motion_start - bit_start);
|
|
_motion.reset(buffer + motion_start, shortmotion_start - motion_start);
|
|
_shortmo.reset(buffer + shortmotion_start, jpeg_start - shortmotion_start);
|
|
_jpeg.reset(buffer + jpeg_start, end - jpeg_start);
|
|
|
|
assert(!_warpMode || quality > 0);
|
|
_keyframe = quality < 0;
|
|
|
|
quality = ABS(quality);
|
|
quality = CLIP<int32>(quality, 1, 100);
|
|
|
|
uint32 qf;
|
|
if (quality >= 50) {
|
|
qf = 200 - 2 * quality;
|
|
} else {
|
|
qf = 5000 / quality;
|
|
}
|
|
|
|
for (uint i = 0; i < 64; i++) {
|
|
luma_quant_table[i] = (CLIP<int32>(
|
|
(LUMA_QUANT_TABLE[ZIGZAG[i]] * qf + 50) / 100,
|
|
8, 255) *
|
|
AAN_FACTORS[ZIGZAG[i]]) >> 13;
|
|
}
|
|
for (uint i = 0; i < 64; i++) {
|
|
chroma_quant_table[i] = (CLIP<int32>(
|
|
(CHROMA_QUANT_TABLE[ZIGZAG[i]] * qf + 50) / 100,
|
|
8, 255) *
|
|
AAN_FACTORS[ZIGZAG[i]]) >> 13;
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decode(Graphics::Surface ¤t, Graphics::Surface &old) {
|
|
assert((current.w & 0x7) == 0 && (current.h & 0x7) == 0);
|
|
if (_warpMode) {
|
|
decodeIWkf(current);
|
|
} else if (_keyframe) {
|
|
decodeIXkf(current);
|
|
} else {
|
|
assert((old.w & 0x7) == 0 && (old.h & 0x7) == 0);
|
|
assert(old.getPixels() != nullptr);
|
|
decodeIXif(current, old);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIWkf(Graphics::Surface ¤t) {
|
|
// WARP decoder
|
|
for (int16 y = 0; y < current.h; y += 8) {
|
|
for (int16 x = 0; x < current.w; x += 8) {
|
|
decodeIWkf88(current, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIWkf88(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11 : Keyblock
|
|
renderKeyblock(current, x, y);
|
|
} else { // 10 : Motion
|
|
renderIWmotion<8, 8>(current, x, y);
|
|
}
|
|
} else { // 0 : Cross-cut
|
|
decodeIWkf44(current, x + 0, y + 0);
|
|
decodeIWkf44(current, x + 4, y + 0);
|
|
decodeIWkf44(current, x + 0, y + 4);
|
|
decodeIWkf44(current, x + 4, y + 4);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIWkf44(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1 : Double cross-cut
|
|
renderIWshortmo(current, x + 0, y + 0);
|
|
renderIWshortmo(current, x + 2, y + 0);
|
|
renderIWshortmo(current, x + 0, y + 2);
|
|
renderIWshortmo(current, x + 2, y + 2);
|
|
} else { // 0 : Motion
|
|
renderIWmotion<4, 4>(current, x, y);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf(Graphics::Surface ¤t) {
|
|
// Standard decoder for keyframes
|
|
for (int16 y = 0; y < current.h; y += 8) {
|
|
for (int16 x = 0; x < current.w; x += 8) {
|
|
decodeIXkf88(current, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf88(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 111: Motion
|
|
renderIXkfMotion<8, 8>(current, x, y);
|
|
} else { // 110: Keyblock
|
|
renderKeyblock(current, x, y);
|
|
}
|
|
} else { // 10: Cross-cut
|
|
decodeIXkf44(current, x + 0, y + 0);
|
|
decodeIXkf44(current, x + 4, y + 0);
|
|
decodeIXkf44(current, x + 0, y + 4);
|
|
decodeIXkf44(current, x + 4, y + 4);
|
|
}
|
|
} else { // 0
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 01: V-cut
|
|
decodeIXkf48(current, x + 0, y + 0);
|
|
decodeIXkf48(current, x + 4, y + 0);
|
|
} else { // 00: H-cut
|
|
decodeIXkf84(current, x + 0, y + 0);
|
|
decodeIXkf84(current, x + 0, y + 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf48(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1: Motion
|
|
renderIXkfMotion<4, 8>(current, x, y);
|
|
} else { // 0: Cross-cut
|
|
decodeIXkf44(current, x + 0, y + 0);
|
|
decodeIXkf44(current, x + 0, y + 4);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf84(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1: Motion
|
|
renderIXkfMotion<8, 4>(current, x, y);
|
|
} else { // 0: Cross-cut
|
|
decodeIXkf44(current, x + 0, y + 0);
|
|
decodeIXkf44(current, x + 4, y + 0);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf44(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11: Double cross-cut
|
|
// 2x2 blocks are always motion small
|
|
renderIXkfMotion<2, 2, true>(current, x + 0, y + 0);
|
|
renderIXkfMotion<2, 2, true>(current, x + 2, y + 0);
|
|
renderIXkfMotion<2, 2, true>(current, x + 0, y + 2);
|
|
renderIXkfMotion<2, 2, true>(current, x + 2, y + 2);
|
|
} else { // 10: DV-cut
|
|
decodeIXkf24(current, x + 0, y + 0);
|
|
decodeIXkf24(current, x + 2, y + 0);
|
|
}
|
|
} else { // 0
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 01: DH-cut
|
|
decodeIXkf42(current, x + 0, y + 0);
|
|
decodeIXkf42(current, x + 0, y + 2);
|
|
} else { // 00: Motion
|
|
renderIXkfMotion<4, 4>(current, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf24(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1: Double cross-cut
|
|
// 2x2 blocks are always motion small
|
|
renderIXkfMotion<2, 2, true>(current, x + 0, y + 0);
|
|
renderIXkfMotion<2, 2, true>(current, x + 0, y + 2);
|
|
} else { // 0: Motion small
|
|
renderIXkfMotion<2, 4, true>(current, x, y);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXkf42(Graphics::Surface ¤t, uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1: Double cross-cut
|
|
// 2x2 blocks are always motion small
|
|
renderIXkfMotion<2, 2, true>(current, x + 0, y + 0);
|
|
renderIXkfMotion<2, 2, true>(current, x + 2, y + 0);
|
|
} else { // 0: Motion small
|
|
renderIXkfMotion<4, 2, true>(current, x, y);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif(Graphics::Surface ¤t, Graphics::Surface &previous) {
|
|
// Standard decoder for keyframes
|
|
for (int16 y = 0; y < current.h; y += 8) {
|
|
for (int16 x = 0; x < current.w; x += 8) {
|
|
decodeIXif88(current, previous, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif88(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 111
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 1111: Unknown
|
|
assert(false);
|
|
} else { // 1110: Cross-cut
|
|
decodeIXif44(current, previous, x + 0, y + 0);
|
|
decodeIXif44(current, previous, x + 4, y + 0);
|
|
decodeIXif44(current, previous, x + 0, y + 4);
|
|
decodeIXif44(current, previous, x + 4, y + 4);
|
|
}
|
|
} else { // 110: Skip
|
|
renderSkip<8, 8>(current, previous, x, y);
|
|
}
|
|
} else { // 10
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 101: V-cut
|
|
decodeIXif48(current, previous, x + 0, y + 0);
|
|
decodeIXif48(current, previous, x + 4, y + 0);
|
|
} else { // 100: H-cut
|
|
decodeIXif84(current, previous, x + 0, y + 0);
|
|
decodeIXif84(current, previous, x + 0, y + 4);
|
|
}
|
|
}
|
|
} else { // 0
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 01
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 011: Keyblock
|
|
renderKeyblock(current, x, y);
|
|
} else { // 010: Motion
|
|
renderIXifMotion<8, 8>(current, previous, x, y);
|
|
}
|
|
} else { // 00: Short motion
|
|
renderIXifShortmo<8, 8>(current, previous, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif48(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11: Skip
|
|
renderSkip<4, 8>(current, previous, x, y);
|
|
} else { // 10: Cross-cut
|
|
decodeIXif44(current, previous, x + 0, y + 0);
|
|
decodeIXif44(current, previous, x + 0, y + 4);
|
|
}
|
|
} else { // 0
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 01: Motion
|
|
renderIXifMotion<4, 8>(current, previous, x, y);
|
|
} else { // 00: Short motion
|
|
renderIXifShortmo<4, 8>(current, previous, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif84(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11: Skip
|
|
renderSkip<8, 4>(current, previous, x, y);
|
|
} else { // 10: Cross-cut
|
|
decodeIXif44(current, previous, x + 0, y + 0);
|
|
decodeIXif44(current, previous, x + 4, y + 0);
|
|
}
|
|
} else { // 0
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 01: Motion
|
|
renderIXifMotion<8, 4>(current, previous, x, y);
|
|
} else { // 00: Short motion
|
|
renderIXifShortmo<8, 4>(current, previous, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif44(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 111: Double cross-cut
|
|
decodeIXif22(current, previous, x + 0, y + 0);
|
|
decodeIXif22(current, previous, x + 2, y + 0);
|
|
decodeIXif22(current, previous, x + 0, y + 2);
|
|
decodeIXif22(current, previous, x + 2, y + 2);
|
|
} else { // 110: DV-cut
|
|
decodeIXif24(current, previous, x + 0, y + 0);
|
|
decodeIXif24(current, previous, x + 2, y + 0);
|
|
}
|
|
} else { // 10
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 101: DH-cut
|
|
decodeIXif42(current, previous, x + 0, y + 0);
|
|
decodeIXif42(current, previous, x + 0, y + 2);
|
|
} else { // 100: Skip
|
|
renderSkip<4, 4>(current, previous, x, y);
|
|
}
|
|
}
|
|
} else { // 0
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 01: Motion
|
|
renderIXifMotion<4, 4>(current, previous, x, y);
|
|
} else { // 00: Short motion
|
|
renderIXifShortmo<4, 4>(current, previous, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif24(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 111: Double cross-cut
|
|
decodeIXif22(current, previous, x + 0, y + 0);
|
|
decodeIXif22(current, previous, x + 0, y + 2);
|
|
} else { // 110: Skip
|
|
renderSkip<2, 4>(current, previous, x, y);
|
|
}
|
|
} else { // 10: Motion small
|
|
renderIXifMotion<2, 4, true>(current, previous, x, y);
|
|
}
|
|
} else { // 0: Short motion
|
|
renderIXifShortmo<2, 4>(current, previous, x, y);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif42(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 111: Double cross-cut
|
|
decodeIXif22(current, previous, x + 0, y + 0);
|
|
decodeIXif22(current, previous, x + 2, y + 0);
|
|
} else { // 110: Skip
|
|
renderSkip<4, 2>(current, previous, x, y);
|
|
}
|
|
} else { // 10: Motion small
|
|
renderIXifMotion<4, 2, true>(current, previous, x, y);
|
|
}
|
|
} else { // 0: Short motion
|
|
renderIXifShortmo<4, 2>(current, previous, x, y);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::decodeIXif22(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
byte bit = _bitbuf.next();
|
|
if (bit) { // 1
|
|
bit = _bitbuf.next();
|
|
if (bit) { // 11: Skip
|
|
renderSkip<2, 2>(current, previous, x, y);
|
|
} else { // 10: Motion small
|
|
renderIXifMotion<2, 2, true>(current, previous, x, y);
|
|
}
|
|
} else { // 0: Short motion
|
|
renderIXifShortmo<2, 2>(current, previous, x, y);
|
|
}
|
|
}
|
|
|
|
void DecoderImpl::renderKeyblock(Graphics::Surface ¤t, uint x, uint y) {
|
|
renderPlane(coeffs, luma_quant_table);
|
|
renderPlane(coeffs + 64, chroma_quant_table);
|
|
renderPlane(coeffs + 128, chroma_quant_table);
|
|
HNM6::YUVtoRGB(current, x, y, coeffs);
|
|
}
|
|
|
|
void DecoderImpl::renderPlane(int32 *dst, int32 *quant_table) {
|
|
uint i = 0;
|
|
// This mask is used to optimize the IDCT
|
|
uint16 colrow_mask = 0;
|
|
int8 b;
|
|
memset(dst, 0, 64 * sizeof(*dst));
|
|
|
|
#define WRITE_COEFF_0(y) do { i += y; } while(0)
|
|
#define WRITE_COEFF(x) do { \
|
|
dst[ZIGZAG[i]] = (x) * quant_table[i]; \
|
|
colrow_mask |= MASKS[i]; \
|
|
i++; \
|
|
} while(0)
|
|
|
|
b = _jpeg.next() << 4;
|
|
b |= _jpeg.next();
|
|
WRITE_COEFF(b);
|
|
while (i < 64) {
|
|
b = _jpeg.next();
|
|
switch (b) {
|
|
case 0: // Finish with 0
|
|
i = 64;
|
|
break;
|
|
case 1:
|
|
WRITE_COEFF_0(5);
|
|
break;
|
|
case 2:
|
|
WRITE_COEFF_0(1);
|
|
WRITE_COEFF(1);
|
|
break;
|
|
case 3:
|
|
WRITE_COEFF_0(1);
|
|
WRITE_COEFF(-1);
|
|
break;
|
|
case 4:
|
|
WRITE_COEFF_0(2);
|
|
WRITE_COEFF(1);
|
|
break;
|
|
case 5:
|
|
WRITE_COEFF_0(2);
|
|
WRITE_COEFF(-1);
|
|
break;
|
|
case 6:
|
|
WRITE_COEFF_0(3);
|
|
WRITE_COEFF(1);
|
|
break;
|
|
case 7:
|
|
WRITE_COEFF_0(3);
|
|
WRITE_COEFF(-1);
|
|
break;
|
|
case 8: {
|
|
b = _jpeg.next();
|
|
byte z = (b >> 2) & 0x3;
|
|
byte t = b & 0x3;
|
|
z++;
|
|
t += 2;
|
|
WRITE_COEFF_0(z);
|
|
for (; t > 1; t -= 2) {
|
|
b = _jpeg.next();
|
|
const int8 *p = S2 + 2 * b;
|
|
WRITE_COEFF(p[0]);
|
|
WRITE_COEFF(p[1]);
|
|
}
|
|
if (t) {
|
|
b = _jpeg.next();
|
|
const int8 *p = S2 + 2 * b;
|
|
WRITE_COEFF(p[0]);
|
|
t--;
|
|
}
|
|
break;
|
|
}
|
|
case 9: {
|
|
b = _jpeg.next();
|
|
byte z = (b >> 2) & 0x3;
|
|
byte t = b & 0x3;
|
|
z++;
|
|
t++;
|
|
WRITE_COEFF_0(z);
|
|
for (; t > 0; t--) {
|
|
b = _jpeg.next();
|
|
WRITE_COEFF(S4[b]);
|
|
}
|
|
break;
|
|
}
|
|
case 10:
|
|
b = _jpeg.next();
|
|
WRITE_COEFF(S2[2 * b]);
|
|
WRITE_COEFF(S2[2 * b + 1]);
|
|
break;
|
|
case 13:
|
|
b = _jpeg.next();
|
|
WRITE_COEFF(S4[b]);
|
|
// fall through
|
|
case 12:
|
|
b = _jpeg.next();
|
|
WRITE_COEFF(S4[b]);
|
|
// fall through
|
|
case 11:
|
|
b = _jpeg.next();
|
|
WRITE_COEFF(S4[b]);
|
|
break;
|
|
case 14:
|
|
WRITE_COEFF_0(1);
|
|
break;
|
|
case 15:
|
|
b = _jpeg.next() << 4;
|
|
b |= _jpeg.next();
|
|
WRITE_COEFF(b);
|
|
break;
|
|
default:
|
|
error("BUG: JPEG control word out of range");
|
|
break;
|
|
}
|
|
}
|
|
#undef WRITE_COEFF_0
|
|
#undef WRITE_COEFF
|
|
|
|
HNM6::aanidct(dst, colrow_mask);
|
|
}
|
|
|
|
// These macros are used in motion rendering
|
|
// Wrap the x coordinate around the line
|
|
#define WRAP_LINE(x, w) do { \
|
|
if (x < 0) { \
|
|
x += w; \
|
|
} else if (x >= w) { \
|
|
x -= w; \
|
|
} \
|
|
} while(0)
|
|
// Convert a coordinate to its block (8x8) one
|
|
#define BALIGN(v) ((v) & ~0x7)
|
|
|
|
template<int sx, int sy>
|
|
void DecoderImpl::renderIWmotion(Graphics::Surface ¤t, uint x, uint y) {
|
|
uint16 moword = _motion.next();
|
|
byte xform = _bitbuf.next() << 2;
|
|
xform |= _bitbuf.next() << 1;
|
|
xform |= moword >> 15;
|
|
|
|
int offy = (sx == 8) ? 0 : 4;
|
|
int srx = BALIGN(x) + 128 - ((moword >> 7) & 0xFF);
|
|
int sry = BALIGN(y) + offy - (moword & 0x7F);
|
|
WRAP_LINE(srx, current.w);
|
|
HNM6::doMotion<sx, sy>(xform, current, current, srx, sry, x, y);
|
|
}
|
|
|
|
void DecoderImpl::renderIWshortmo(Graphics::Surface ¤t, uint x, uint y) {
|
|
uint16 moword = _shortmo.next();
|
|
byte xform = (moword >> 1) & 7;
|
|
|
|
uint16 index = (moword & 0x1) << 8 | (moword & 0xF0) | (moword >> 8);
|
|
|
|
int xmotion, ymotion;
|
|
if (index < 12 * 8) {
|
|
xmotion = -2 - index % 12;
|
|
ymotion = 6 - index / 12;
|
|
} else {
|
|
index -= 12 * 8;
|
|
xmotion = 19 - index % 32;
|
|
ymotion = -2 - index / 32;
|
|
if (ymotion <= -8) {
|
|
ymotion--;
|
|
}
|
|
}
|
|
|
|
// If srx goes beyond the line it will end up on previous or next line
|
|
// This is expected.
|
|
// For this to work, lines must be contiguous.
|
|
int srx = BALIGN(x) + xmotion;
|
|
int sry = BALIGN(y) + ymotion;
|
|
|
|
HNM6::doMotion<2, 2>(xform, current, current, srx, sry, x, y);
|
|
}
|
|
|
|
template<int sx, int sy, bool small>
|
|
void DecoderImpl::renderIXkfMotion(Graphics::Surface ¤t, uint x, uint y) {
|
|
uint16 moword = _motion.next();
|
|
|
|
byte xform;
|
|
int srx, sry;
|
|
|
|
if (small) {
|
|
xform = (moword >> 12) & 0x7;
|
|
srx = BALIGN(x) + 63 - (moword & 0x7F);
|
|
sry = BALIGN(y) + 6 - ((moword >> 7) & 0x1F);
|
|
// If srx goes beyond the line it will end up on previous or next line
|
|
// This is expected.
|
|
// For this to work, lines must be contiguous.
|
|
} else {
|
|
xform = _bitbuf.next() << 2;
|
|
xform |= _bitbuf.next() << 1;
|
|
xform |= moword >> 15;
|
|
|
|
int offy = (sx == 8 && sy == 8) ? 0 : 4;
|
|
srx = BALIGN(x) + 128 - ((moword >> 7) & 0xFF);
|
|
sry = BALIGN(y) + offy - (moword & 0x7F);
|
|
WRAP_LINE(srx, current.w);
|
|
}
|
|
|
|
HNM6::doMotion<sx, sy>(xform, current, current, srx, sry, x, y);
|
|
}
|
|
|
|
template<int sx, int sy>
|
|
void DecoderImpl::renderSkip(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
// Use already coded copy transform
|
|
doMotion<sx, sy>(0, current, previous, x, y, x, y);
|
|
}
|
|
|
|
template<int sx, int sy, bool small>
|
|
void DecoderImpl::renderIXifMotion(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
uint16 moword = _motion.next();
|
|
|
|
byte xform;
|
|
int srx, sry;
|
|
|
|
bool use_current = (moword & 0x8000) != 0;
|
|
|
|
if (small) {
|
|
xform = (moword >> 12) & 0x7;
|
|
if (use_current) {
|
|
srx = BALIGN(x) + 63 - (moword & 0x7F);
|
|
sry = BALIGN(y) + 6 - ((moword >> 7) & 0x1F);
|
|
} else {
|
|
srx = BALIGN(x) + 31 - (moword & 0x3F);
|
|
sry = BALIGN(y) + 31 - ((moword >> 6) & 0x3F);
|
|
}
|
|
// If srx goes beyond the line it will end up on previous or next line
|
|
// This is expected.
|
|
// For this to work, lines must be contiguous.
|
|
} else {
|
|
xform = _bitbuf.next() << 2;
|
|
xform |= _bitbuf.next() << 1;
|
|
xform |= _bitbuf.next();
|
|
|
|
int offy = use_current ? ((sx == 8 && sy == 8) ? 0 : 4) : 64;
|
|
srx = BALIGN(x) + 128 - ((moword >> 7) & 0xFF);
|
|
sry = BALIGN(y) + offy - (moword & 0x7F);
|
|
WRAP_LINE(srx, current.w);
|
|
}
|
|
|
|
HNM6::doMotion<sx, sy>(xform, current, use_current ? current : previous, srx, sry, x, y);
|
|
}
|
|
|
|
template<int sx, int sy>
|
|
void DecoderImpl::renderIXifShortmo(Graphics::Surface ¤t, Graphics::Surface &previous,
|
|
uint x, uint y) {
|
|
uint16 somoword = _shortmo.next();
|
|
|
|
int ox, oy;
|
|
if (somoword == 0) {
|
|
ox = 1;
|
|
oy = 0;
|
|
} else {
|
|
somoword--;
|
|
int span = 1;
|
|
while (somoword >= 8 * span) {
|
|
somoword -= 8 * span;
|
|
span++;
|
|
}
|
|
int baseX = -span + 1;
|
|
int baseY = -span + 1;
|
|
|
|
int side_sz = (span * 8) / 4;
|
|
byte side = somoword / side_sz;
|
|
int side_pos = somoword % side_sz;
|
|
|
|
int offX, offY;
|
|
switch (side) {
|
|
case 0:
|
|
offX = 0;
|
|
offY = side_pos;
|
|
break;
|
|
case 1:
|
|
offX = side_pos + 1;
|
|
offY = side_sz - 1;
|
|
break;
|
|
case 2:
|
|
offX = side_sz;
|
|
offY = side_sz - 1 - side_pos - 1;
|
|
break;
|
|
case 3:
|
|
offX = side_sz - 1 - side_pos;
|
|
offY = -1;
|
|
break;
|
|
default:
|
|
error("BUG: Invalid side in short motion");
|
|
}
|
|
ox = baseX + offX;
|
|
oy = baseY + offY;
|
|
}
|
|
|
|
// This is NOT aligned on block coordinates
|
|
// If srx goes beyond the line it will end up on previous or next line
|
|
// This is expected.
|
|
// For this to work, lines must be contiguous.
|
|
int srx = x + ox;
|
|
int sry = y + oy;
|
|
|
|
// Use already coded copy transform
|
|
HNM6::doMotion<sx, sy>(0, current, previous, srx, sry, x, y);
|
|
}
|
|
|
|
#undef WRAP_LINE
|
|
#undef BALIGN
|
|
|
|
DecoderImpl::DecoderImpl(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
|
uint32 bufferSize, bool videoMode) :
|
|
HNM6Decoder(width, height, format, videoMode), _bufferSize(bufferSize), _keyframe(false) {
|
|
if (format.bytesPerPixel != 2 && format.bytesPerPixel != 4) {
|
|
error("Unsupported bpp");
|
|
}
|
|
|
|
if (bufferSize < HEADER_SIZE) {
|
|
error("Invalid buffer size");
|
|
}
|
|
_buffer = new byte[bufferSize];
|
|
_surface.create(_width, _height, _format);
|
|
if (videoMode) {
|
|
_surfaceOld.create(_width, _height, _format);
|
|
}
|
|
}
|
|
|
|
DecoderImpl::~DecoderImpl() {
|
|
delete [] _buffer;
|
|
_surface.free();
|
|
_surfaceOld.free();
|
|
}
|
|
|
|
const Graphics::Surface *DecoderImpl::decodeFrame(Common::SeekableReadStream &stream) {
|
|
// Switch both surfaces
|
|
void *oldPixels = _surfaceOld.getPixels();
|
|
if (oldPixels) {
|
|
_surfaceOld.setPixels(_surface.getPixels());
|
|
_surface.setPixels(oldPixels);
|
|
}
|
|
|
|
if (stream.read(_buffer, HEADER_SIZE) != HEADER_SIZE) {
|
|
error("Invalid header");
|
|
}
|
|
|
|
int32 quality = READ_LE_UINT32(_buffer + 0 * 4);
|
|
uint32 bit_start = READ_LE_UINT32(_buffer + 1 * 4);
|
|
uint32 motion_start = READ_LE_UINT32(_buffer + 2 * 4);
|
|
uint32 shortmotion_start = READ_LE_UINT32(_buffer + 3 * 4);
|
|
uint32 jpeg_start = READ_LE_UINT32(_buffer + 4 * 4);
|
|
uint32 end = READ_LE_UINT32(_buffer + 5 * 4);
|
|
|
|
if (bit_start > end ||
|
|
motion_start > end ||
|
|
shortmotion_start > end ||
|
|
jpeg_start > end ||
|
|
end > _bufferSize) {
|
|
error("Invalid header offsets");
|
|
}
|
|
|
|
if (stream.read(_buffer + HEADER_SIZE, end - HEADER_SIZE) != end - HEADER_SIZE) {
|
|
error("Invalid chunk length");
|
|
}
|
|
|
|
reset(_buffer, bit_start, motion_start,
|
|
shortmotion_start, jpeg_start, end, quality);
|
|
decode(_surface, _surfaceOld);
|
|
|
|
return &_surface;
|
|
}
|
|
|
|
} // End of namespace HNM6
|
|
|
|
HNM6Decoder *createHNM6Decoder(uint16 width, uint16 height, const Graphics::PixelFormat &format,
|
|
uint32 bufferSize, bool videoMode) {
|
|
return new HNM6::DecoderImpl(width, height, format, bufferSize, videoMode);
|
|
}
|
|
|
|
} // End of namespace Image
|