/* FLI Decoder for MPlayer (C) 2001 Mike Melanson 32bpp support (c) alex */ #define LE_16(x) *(unsigned short *)(x) #define LE_32(x) *(unsigned int *)(x) #define FLI_256_COLOR 4 #define FLI_DELTA 7 #define FLI_COLOR 11 #define FLI_LC 12 #define FLI_BLACK 13 #define FLI_BRUN 15 #define FLI_COPY 16 #define FLI_MINI 18 // 256 RGB entries; 25% of these bytes will be unused, but it's faster // to index 4-byte entries static unsigned char palette[256 * 4]; void AVI_Decode_Fli( unsigned char *encoded, int encoded_size, unsigned char *decoded, int width, int height, int bytes_per_pixel) { int stream_ptr = 0; int pixel_ptr; int palette_ptr1; int palette_ptr2; unsigned int frame_size; int num_chunks; unsigned int chunk_size; int chunk_type; int i, j; int color_packets; int color_changes; int color_scale; int lines; int compressed_lines; int starting_line; signed short line_packets; int y_ptr; int line_inc; signed char byte_run; frame_size = LE_32(&encoded[stream_ptr]); stream_ptr += 6; // skip the magic number num_chunks = LE_16(&encoded[stream_ptr]); stream_ptr += 10; // skip padding // iterate through the chunks frame_size -= 16; while ((frame_size > 0) && (num_chunks > 0)) { chunk_size = LE_32(&encoded[stream_ptr]); stream_ptr += 4; chunk_type = LE_16(&encoded[stream_ptr]); stream_ptr += 2; switch (chunk_type) { case FLI_256_COLOR: case FLI_COLOR: if (chunk_type == FLI_COLOR) color_scale = 4; else color_scale = 1; // set up the palette color_packets = LE_16(&encoded[stream_ptr]); stream_ptr += 2; palette_ptr1 = 0; for (i = 0; i < color_packets; i++) { // first byte is how many colors to skip palette_ptr1 += (encoded[stream_ptr++] * 4); // next byte indicates how many entries to change color_changes = encoded[stream_ptr++]; // if there are 0 color changes, there are actually 256 if (color_changes == 0) color_changes = 256; for (j = 0; j < color_changes; j++) { palette[palette_ptr1++] = encoded[stream_ptr + 2] * color_scale; palette[palette_ptr1++] = encoded[stream_ptr + 1] * color_scale; palette[palette_ptr1++] = encoded[stream_ptr + 0] * color_scale; palette_ptr1++; stream_ptr += 3; } } break; case FLI_DELTA: line_inc = width * bytes_per_pixel; y_ptr = 0; compressed_lines = LE_16(&encoded[stream_ptr]); stream_ptr += 2; while (compressed_lines > 0) { line_packets = LE_16(&encoded[stream_ptr]); stream_ptr += 2; if (line_packets < 0) { line_packets = -line_packets; y_ptr += (line_packets * line_inc); } else { pixel_ptr = y_ptr; for (i = 0; i < line_packets; i++) { // account for the skip bytes pixel_ptr += encoded[stream_ptr++] * bytes_per_pixel; byte_run = encoded[stream_ptr++]; if (byte_run < 0) { byte_run = -byte_run; palette_ptr1 = encoded[stream_ptr++] * 4; palette_ptr2 = encoded[stream_ptr++] * 4; for (j = 0; j < byte_run; j++) { decoded[pixel_ptr++] = palette[palette_ptr1 + 0]; decoded[pixel_ptr++] = palette[palette_ptr1 + 1]; decoded[pixel_ptr++] = palette[palette_ptr1 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; decoded[pixel_ptr++] = palette[palette_ptr2 + 0]; decoded[pixel_ptr++] = palette[palette_ptr2 + 1]; decoded[pixel_ptr++] = palette[palette_ptr2 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } } else { for (j = 0; j < byte_run * 2; j++) { palette_ptr1 = encoded[stream_ptr++] * 4; decoded[pixel_ptr++] = palette[palette_ptr1 + 0]; decoded[pixel_ptr++] = palette[palette_ptr1 + 1]; decoded[pixel_ptr++] = palette[palette_ptr1 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } } } y_ptr += line_inc; compressed_lines--; } } break; case FLI_LC: // line compressed line_inc = width * bytes_per_pixel; starting_line = LE_16(&encoded[stream_ptr]); stream_ptr += 2; y_ptr = starting_line * line_inc; compressed_lines = LE_16(&encoded[stream_ptr]); stream_ptr += 2; while (compressed_lines > 0) { pixel_ptr = y_ptr; line_packets = encoded[stream_ptr++]; if (line_packets > 0) { for (i = 0; i < line_packets; i++) { // account for the skip bytes pixel_ptr += encoded[stream_ptr++] * bytes_per_pixel; byte_run = encoded[stream_ptr++]; if (byte_run > 0) { for (j = 0; j < byte_run; j++) { palette_ptr1 = encoded[stream_ptr++] * 4; decoded[pixel_ptr++] = palette[palette_ptr1 + 0]; decoded[pixel_ptr++] = palette[palette_ptr1 + 1]; decoded[pixel_ptr++] = palette[palette_ptr1 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } } else { byte_run = -byte_run; palette_ptr1 = encoded[stream_ptr++] * 4; for (j = 0; j < byte_run; j++) { decoded[pixel_ptr++] = palette[palette_ptr1 + 0]; decoded[pixel_ptr++] = palette[palette_ptr1 + 1]; decoded[pixel_ptr++] = palette[palette_ptr1 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } } } } y_ptr += line_inc; compressed_lines--; } break; case FLI_BLACK: // set the whole frame to color 0 (which is usually black) for (pixel_ptr = 0; pixel_ptr < (width * height * bytes_per_pixel); pixel_ptr++) { decoded[pixel_ptr++] = palette[0]; decoded[pixel_ptr++] = palette[1]; decoded[pixel_ptr++] = palette[2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } break; case FLI_BRUN: // byte run compression line_inc = width * bytes_per_pixel; y_ptr = 0; for (lines = 0; lines < height; lines++) { pixel_ptr = y_ptr; line_packets = encoded[stream_ptr++]; for (i = 0; i < line_packets; i++) { byte_run = encoded[stream_ptr++]; if (byte_run > 0) { palette_ptr1 = encoded[stream_ptr++] * 4; for (j = 0; j < byte_run; j++) { decoded[pixel_ptr++] = palette[palette_ptr1 + 0]; decoded[pixel_ptr++] = palette[palette_ptr1 + 1]; decoded[pixel_ptr++] = palette[palette_ptr1 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } } else // copy bytes if byte_run < 0 { byte_run = -byte_run; for (j = 0; j < byte_run; j++) { palette_ptr1 = encoded[stream_ptr++] * 4; decoded[pixel_ptr++] = palette[palette_ptr1 + 0]; decoded[pixel_ptr++] = palette[palette_ptr1 + 1]; decoded[pixel_ptr++] = palette[palette_ptr1 + 2]; if (bytes_per_pixel == 4) /* 32bpp */ pixel_ptr++; } } } y_ptr += line_inc; } break; case FLI_COPY: // currently unimplemented printf ("FLI_COPY chunk (currently unimplemented)\n"); stream_ptr += chunk_size - 6; break; case FLI_MINI: // sort of a thumbnail? disregard this chunk... stream_ptr += chunk_size - 6; break; default: printf ("FLI: Unrecognized chunk type: %d\n", chunk_type); break; } frame_size -= chunk_size; num_chunks--; } }