/* Microsoft Video 1 Decoder (C) 2001 Mike Melanson The description of the algorithm you can read here: http://www.pcisys.net/~melanson/video1.txt 32bpp support (c) alex */ #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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } // 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } // 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } // 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } // 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; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } pixel_ptr -= row_dec; } } block_ptr += block_inc; } } }