mpv/msvidc.c

361 lines
10 KiB
C

/*
Microsoft Video 1 Decoder
(C) 2001 Mike Melanson
The description of the algorithm you can read here:
http://www.pcisys.net/~melanson/video1.txt
*/
#define LE_16(x) *(unsigned short *)(x)
#define DECODE_BGR555_TO_BGR888(x) \
x.c1_b = (x.c1 >> 7) & 0xF8; \
x.c1_g = (x.c1 >> 2) & 0xF8; \
x.c1_r = (x.c1 << 3) & 0xF8; \
x.c2_b = (x.c2 >> 7) & 0xF8; \
x.c2_g = (x.c2 >> 2) & 0xF8; \
x.c2_r = (x.c2 << 3) & 0xF8;
#define DECODE_PALETTE_TO_BGR888(x) \
x.c1_b = palette_map[x.c1 * 4 + 2]; \
x.c1_g = palette_map[x.c1 * 4 + 1]; \
x.c1_r = palette_map[x.c1 * 4 + 0]; \
x.c2_b = palette_map[x.c2 * 4 + 2]; \
x.c2_g = palette_map[x.c2 * 4 + 1]; \
x.c2_r = palette_map[x.c2 * 4 + 0];
struct
{
unsigned short c1, c2;
unsigned char c1_r, c1_g, c1_b;
unsigned char c2_r, c2_g, c2_b;
} quad[2][2];
void AVI_Decode_Video1_16(
char *encoded,
int encoded_size,
char *decoded,
int width,
int height,
int bytes_per_pixel)
{
int block_ptr, pixel_ptr;
int pixel_x, pixel_y; // pixel width and height iterators
int block_x, block_y; // block width and height iterators
int blocks_wide, blocks_high; // width and height in 4x4 blocks
int block_inc;
int row_dec;
// decoding parameters
int stream_ptr;
unsigned char byte_a, byte_b;
unsigned short flags;
int skip_blocks;
stream_ptr = 0;
skip_blocks = 0;
blocks_wide = width / 4;
blocks_high = height / 4;
block_inc = 4 * bytes_per_pixel;
row_dec = (width + 4) * bytes_per_pixel;
for (block_y = blocks_high; block_y > 0; block_y--)
{
block_ptr = ((block_y * 4) - 1) * (width * bytes_per_pixel);
for (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--;
continue;
}
pixel_ptr = block_ptr;
// get the next two bytes in the encoded data stream
byte_a = encoded[stream_ptr++];
byte_b = encoded[stream_ptr++];
// check if the decode is finished
if ((byte_a == 0) && (byte_b == 0))
return;
// check if this is a skip code
else if ((byte_b & 0xFC) == 0x84)
{
// but don't count the current block
skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
}
// check if this is in the 2- or 8-color classes
else if (byte_b < 0x80)
{
flags = (byte_b << 8) | byte_a;
quad[0][0].c1 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
quad[0][0].c2 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
DECODE_BGR555_TO_BGR888(quad[0][0]);
if (quad[0][0].c1 & 0x8000)
{
// 8-color encoding
quad[1][0].c1 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
quad[1][0].c2 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
quad[0][1].c1 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
quad[0][1].c2 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
quad[1][1].c1 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
quad[1][1].c2 = LE_16(&encoded[stream_ptr]);
stream_ptr += 2;
DECODE_BGR555_TO_BGR888(quad[0][1]);
DECODE_BGR555_TO_BGR888(quad[1][0]);
DECODE_BGR555_TO_BGR888(quad[1][1]);
for (pixel_y = 0; pixel_y < 4; pixel_y++)
{
for (pixel_x = 0; pixel_x < 4; pixel_x++)
{
if (flags & 1)
{
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c1_r;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c1_g;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c1_b;
}
else
{
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c2_r;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c2_g;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c2_b;
}
// get the next flag ready to go
flags >>= 1;
}
pixel_ptr -= row_dec;
}
}
else
{
// 2-color encoding
for (pixel_y = 0; pixel_y < 4; pixel_y++)
{
for (pixel_x = 0; pixel_x < 4; pixel_x++)
{
if (flags & 1)
{
decoded[pixel_ptr++] = quad[0][0].c1_r;
decoded[pixel_ptr++] = quad[0][0].c1_g;
decoded[pixel_ptr++] = quad[0][0].c1_b;
}
else
{
decoded[pixel_ptr++] = quad[0][0].c2_r;
decoded[pixel_ptr++] = quad[0][0].c2_g;
decoded[pixel_ptr++] = quad[0][0].c2_b;
}
// get the next flag ready to go
flags >>= 1;
}
pixel_ptr -= row_dec;
}
}
}
// otherwise, it's a 1-color block
else
{
quad[0][0].c1 = (byte_b << 8) | byte_a;
DECODE_BGR555_TO_BGR888(quad[0][0]);
for (pixel_y = 0; pixel_y < 4; pixel_y++)
{
for (pixel_x = 0; pixel_x < 4; pixel_x++)
{
decoded[pixel_ptr++] = quad[0][0].c1_r;
decoded[pixel_ptr++] = quad[0][0].c1_g;
decoded[pixel_ptr++] = quad[0][0].c1_b;
}
pixel_ptr -= row_dec;
}
}
block_ptr += block_inc;
}
}
}
void AVI_Decode_Video1_8(
char *encoded,
int encoded_size,
char *decoded,
int width,
int height,
unsigned char *palette_map,
int bytes_per_pixel)
{
int block_ptr, pixel_ptr;
int pixel_x, pixel_y; // pixel width and height iterators
int block_x, block_y; // block width and height iterators
int blocks_wide, blocks_high; // width and height in 4x4 blocks
int block_inc;
int row_dec;
// decoding parameters
int stream_ptr;
unsigned char byte_a, byte_b;
unsigned short flags;
int skip_blocks;
stream_ptr = 0;
skip_blocks = 0;
blocks_wide = width / 4;
blocks_high = height / 4;
block_inc = 4 * bytes_per_pixel;
row_dec = (width + 4) * bytes_per_pixel;
for (block_y = blocks_high; block_y > 0; block_y--)
{
block_ptr = ((block_y * 4) - 1) * (width * bytes_per_pixel);
for (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--;
continue;
}
pixel_ptr = block_ptr;
// get the next two bytes in the encoded data stream
byte_a = encoded[stream_ptr++];
byte_b = encoded[stream_ptr++];
// check if the decode is finished
if ((byte_a == 0) && (byte_b == 0))
return;
// check if this is a skip code
else if ((byte_b & 0xFC) == 0x84)
{
// but don't count the current block
skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
}
// check if this is a 2-color block
else if (byte_b < 0x80)
{
flags = (byte_b << 8) | byte_a;
quad[0][0].c1 = (unsigned char)encoded[stream_ptr++];
quad[0][0].c2 = (unsigned char)encoded[stream_ptr++];
DECODE_PALETTE_TO_BGR888(quad[0][0]);
// 2-color encoding
for (pixel_y = 0; pixel_y < 4; pixel_y++)
{
for (pixel_x = 0; pixel_x < 4; pixel_x++)
{
if (flags & 1)
{
decoded[pixel_ptr++] = quad[0][0].c1_r;
decoded[pixel_ptr++] = quad[0][0].c1_g;
decoded[pixel_ptr++] = quad[0][0].c1_b;
}
else
{
decoded[pixel_ptr++] = quad[0][0].c2_r;
decoded[pixel_ptr++] = quad[0][0].c2_g;
decoded[pixel_ptr++] = quad[0][0].c2_b;
}
// get the next flag ready to go
flags >>= 1;
}
pixel_ptr -= row_dec;
}
}
// check if it's an 8-color block
else if (byte_b >= 0x90)
{
flags = (byte_b << 8) | byte_a;
quad[0][0].c1 = (unsigned char)encoded[stream_ptr++];
quad[0][0].c2 = (unsigned char)encoded[stream_ptr++];
quad[1][0].c1 = (unsigned char)encoded[stream_ptr++];
quad[1][0].c2 = (unsigned char)encoded[stream_ptr++];
quad[0][1].c1 = (unsigned char)encoded[stream_ptr++];
quad[0][1].c2 = (unsigned char)encoded[stream_ptr++];
quad[1][1].c1 = (unsigned char)encoded[stream_ptr++];
quad[1][1].c2 = (unsigned char)encoded[stream_ptr++];
DECODE_PALETTE_TO_BGR888(quad[0][0]);
DECODE_PALETTE_TO_BGR888(quad[0][1]);
DECODE_PALETTE_TO_BGR888(quad[1][0]);
DECODE_PALETTE_TO_BGR888(quad[1][1]);
for (pixel_y = 0; pixel_y < 4; pixel_y++)
{
for (pixel_x = 0; pixel_x < 4; pixel_x++)
{
if (flags & 1)
{
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c1_r;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c1_g;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c1_b;
}
else
{
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c2_r;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c2_g;
decoded[pixel_ptr++] = quad[pixel_x >> 1][pixel_y >> 1].c2_b;
}
// get the next flag ready to go
flags >>= 1;
}
pixel_ptr -= row_dec;
}
}
// otherwise, it's a 1-color block
else
{
// init c2 along with c1 just so c2 is a known value for macro
quad[0][0].c1 = quad[0][0].c2 = byte_a;
DECODE_PALETTE_TO_BGR888(quad[0][0]);
for (pixel_y = 0; pixel_y < 4; pixel_y++)
{
for (pixel_x = 0; pixel_x < 4; pixel_x++)
{
decoded[pixel_ptr++] = quad[0][0].c1_r;
decoded[pixel_ptr++] = quad[0][0].c1_g;
decoded[pixel_ptr++] = quad[0][0].c1_b;
}
pixel_ptr -= row_dec;
}
}
block_ptr += block_inc;
}
}
}