Implement FLIC

This commit is contained in:
Nathan 2024-01-22 08:14:39 -05:00
parent b4bc0e51c9
commit 6b090712e4
5 changed files with 443 additions and 47 deletions

85
3rdparty/flic/flic.h vendored
View File

@ -3,44 +3,69 @@
#include <windows.h> #include <windows.h>
enum FLI_CHUNK_TYPE {
FLI_CHUNK_COLOR256 = 4, // 256-level color palette info
FLI_CHUNK_SS2 = 7, // Word-oriented delta compression
FLI_CHUNK_COLOR64 = 11, // 64-level color palette info
FLI_CHUNK_LC = 12, // Byte-oriented delta compression
FLI_CHUNK_BLACK = 13, // Entire frame is color index 0
FLI_CHUNK_BRUN = 15, // Byte run length compression
FLI_CHUNK_COPY = 16, // No compression
FLI_CHUNK_PSTAMP = 18, // Postage stamp sized image
FLI_CHUNK_FRAME = 0xf1fa // Frame
};
#pragma pack(push, 1)
// A basic FLIC header structure from the "EGI" documentation. Source: https://www.compuphase.com/flic.htm#FLICHEADER // A basic FLIC header structure from the "EGI" documentation. Source: https://www.compuphase.com/flic.htm#FLICHEADER
// This also goes over the FLIC structures: https://github.com/thinkbeforecoding/nomemalloc.handson/blob/master/flic.txt // This also goes over the FLIC structures: https://github.com/thinkbeforecoding/nomemalloc.handson/blob/master/flic.txt
typedef struct { typedef struct {
DWORD size; /* Size of FLIC including this header */ DWORD size; /* Size of the chunk, including subchunks */ // 0x0;
WORD type; /* File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ... */ WORD type; // 0x4;
WORD frames; /* Number of frames in first segment */ } FLIC_CHUNK;
WORD width; /* FLIC width in pixels */
WORD height; /* FLIC height in pixels */ typedef struct : FLIC_CHUNK {
WORD depth; /* Bits per pixel (usually 8) */ WORD frames; /* Number of frames in first segment */ // 0x6
WORD flags; /* Set to zero or to three */ short width; /* FLIC width in pixels */ // 0x8
DWORD speed; /* Delay between frames */ short height; /* FLIC height in pixels */ // 0xa
WORD reserved1; /* Set to zero */ WORD depth; /* Bits per pixel (usually 8) */ // 0xc
DWORD created; /* Date of FLIC creation (FLC only) */ WORD flags; /* Set to zero or to three */ // 0xe
DWORD creator; /* Serial number or compiler id (FLC only) */ DWORD speed; /* Delay between frames */ // 0x10
DWORD updated; /* Date of FLIC update (FLC only) */ WORD reserved1; /* Set to zero */ // 0x14
DWORD updater; /* Serial number (FLC only), see creator */ DWORD created; /* Date of FLIC creation (FLC only) */ // 0x18
WORD aspect_dx; /* Width of square rectangle (FLC only) */ DWORD creator; /* Serial number or compiler id (FLC only) */ // 0x1c
WORD aspect_dy; /* Height of square rectangle (FLC only) */ DWORD updated; /* Date of FLIC update (FLC only) */ // 0x20
WORD ext_flags; /* EGI: flags for specific EGI extensions */ DWORD updater; /* Serial number (FLC only), see creator */ // 0x24
WORD keyframes; /* EGI: key-image frequency */ WORD aspect_dx; /* Width of square rectangle (FLC only) */ // 0x28
WORD totalframes; /* EGI: total number of frames (segments) */ WORD aspect_dy; /* Height of square rectangle (FLC only) */ // 0x2a
DWORD req_memory; /* EGI: maximum chunk size (uncompressed) */ WORD ext_flags; /* EGI: flags for specific EGI extensions */ // 02c
WORD max_regions; /* EGI: max. number of regions in a CHK_REGION chunk */ WORD keyframes; /* EGI: key-image frequency */ // 0x2e
WORD transp_num; /* EGI: number of transparent levels */ WORD totalframes; /* EGI: total number of frames (segments) */ // 0x30
BYTE reserved2[24]; /* Set to zero */ DWORD req_memory; /* EGI: maximum chunk size (uncompressed) */ // 0x32
DWORD oframe1; /* Offset to frame 1 (FLC only) */ WORD max_regions; /* EGI: max. number of regions in a CHK_REGION chunk */ // 0x36
DWORD oframe2; /* Offset to frame 2 (FLC only) */ WORD transp_num; /* EGI: number of transparent levels */ // 0x38
BYTE reserved3[40]; /* Set to zero */ BYTE reserved2[24]; /* Set to zero */ // 3e
DWORD oframe1; /* Offset to frame 1 (FLC only) */ // 0x56
DWORD oframe2; /* Offset to frame 2 (FLC only) */ // 0x5a
BYTE reserved3[40]; /* Set to zero */ // 0x5e
} FLIC_HEADER; } FLIC_HEADER;
#pragma pack(pop)
typedef struct : FLIC_CHUNK {
short chunks; /* Number of subchunks */ // 0x6
WORD delay; /* Delay in milliseconds */ // 0x8
WORD reserved; /* Always zero */ // 0xa
WORD width; /* Frame width override (if non-zero) */ // 0xc
WORD height; /* Frame height override (if non-zero) */ // 0xe
} FLIC_FRAME;
extern "C" extern "C"
{ {
void DecodeFLCFrame( void DecodeFLCFrame(
LPBITMAPINFOHEADER bitmapHeader, LPBITMAPINFOHEADER p_bitmapHeader,
void* pixelData, byte* p_pixelData,
FLIC_HEADER* flcHeader, FLIC_HEADER* p_flcHeader,
void* flcFrame, FLIC_FRAME* p_flcFrame,
unsigned char* decodedColorMap unsigned char* p_decodedColorMap
); );
} }

View File

@ -252,6 +252,7 @@ add_library(lego1 SHARED
LEGO1/viewmanager/viewlodlist.cpp LEGO1/viewmanager/viewlodlist.cpp
LEGO1/viewmanager/viewmanager.cpp LEGO1/viewmanager/viewmanager.cpp
LEGO1/viewmanager/viewroi.cpp LEGO1/viewmanager/viewroi.cpp
LEGO1/flic.cpp
) )
if (MINGW) if (MINGW)

370
LEGO1/flic.cpp Normal file
View File

@ -0,0 +1,370 @@
#include "flic.h"
// Private forward declarations
void WritePixel(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, short p_column, short p_row, byte p_pixel);
void WritePixels(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
short p_column,
short p_row,
byte* p_data,
short p_count
);
int ClampLine(LPBITMAPINFOHEADER p_bitmapHeader, short& p_column, short& p_row, short& p_count);
void WritePixelRun(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
short p_column,
short p_row,
byte p_pixel,
short p_count
);
void WritePixelPairs(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
short p_column,
short p_row,
WORD p_pixel,
short p_count
);
short DecodeChunks(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
FLIC_HEADER* p_flcHeader,
FLIC_FRAME* p_flcFrame,
byte* p_flcSubchunks,
unsigned char* p_decodedColorMap
);
void DecodeColors256(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data);
void DecodeColorPackets(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data);
void DecodeColorPacket(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data, short p_index, WORD p_count);
void DecodeColors64(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data);
void DecodeBrun(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader);
void DecodeLC(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader);
void DecodeSS2(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader);
void DecodeBlack(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader);
void DecodeCopy(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader);
// FUNCTION: LEGO1 0x100bd530
void WritePixel(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, short p_column, short p_row, byte p_pixel)
{
if (p_column >= 0 && p_row >= 0 && p_column < p_bitmapHeader->biWidth && p_row < p_bitmapHeader->biHeight) {
*(((p_bitmapHeader->biWidth + 3) & -4) * p_row + p_column + p_pixelData) = p_pixel;
}
}
// FUNCTION: LEGO1 0x100bd580
void WritePixels(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
short p_column,
short p_row,
byte* p_data,
short p_count
)
{
short col = p_column;
if (ClampLine(p_bitmapHeader, p_column, p_row, p_count)) {
short offset = p_column - col;
byte* pixels = offset ? p_data + offset : p_data;
memcpy(((p_bitmapHeader->biWidth + 3) & -4) * p_row + p_column + p_pixelData, pixels, p_count);
}
}
// FUNCTION: LEGO1 0x100bd600
int ClampLine(LPBITMAPINFOHEADER p_bitmapHeader, short& p_column, short& p_row, short& p_count)
{
short lp_count;
short end = p_column + p_count;
if (p_row >= 0 && p_row < p_bitmapHeader->biHeight && end >= 0 && p_column < p_bitmapHeader->biWidth) {
if (p_column < 0) {
lp_count = end;
p_count = end;
p_column = 0;
}
if (end > p_bitmapHeader->biWidth) {
lp_count -= end;
lp_count += p_bitmapHeader->biWidth;
p_count = lp_count;
}
return lp_count >= 0;
}
return 0;
}
// FUNCTION: LEGO1 0x100bd680
void WritePixelRun(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
short p_column,
short p_row,
byte p_pixel,
short p_count
)
{
short col = p_column;
if (ClampLine(p_bitmapHeader, p_column, p_row, p_count)) {
byte* dst = ((p_bitmapHeader->biWidth + 3) & -4) * p_row + p_column + p_pixelData;
while (--p_count >= 0) {
*(dst++) = p_pixel;
}
}
}
// FUNCTION: LEGO1 0x100bd6e0
void WritePixelPairs(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
short p_column,
short p_row,
WORD p_pixel,
short p_count
)
{
p_count <<= 1;
if (ClampLine(p_bitmapHeader, p_column, p_row, p_count)) {
short odd = p_count & 1;
p_count >>= 1;
WORD* dst = (WORD*) (((p_bitmapHeader->biWidth + 3) & -4) * p_row + p_column + p_pixelData);
while (--p_count >= 0) {
*(dst++) = p_pixel;
}
if (odd)
*((byte*) dst) = p_pixel;
}
}
// FUNCTION: LEGO1 0x100bd760
short DecodeChunks(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
FLIC_HEADER* p_flcHeader,
FLIC_FRAME* p_flcFrame,
byte* p_flcSubchunks,
unsigned char* p_decodedColorMap
)
{
*p_decodedColorMap = 0;
for (short subchunk = 0; subchunk < p_flcFrame->chunks; subchunk++) {
FLIC_CHUNK* chunk = (FLIC_CHUNK*) p_flcSubchunks;
p_flcSubchunks += chunk->size;
switch (chunk->type) {
case FLI_CHUNK_COLOR256:
DecodeColors256(p_bitmapHeader, (byte*) (chunk + 1));
*p_decodedColorMap = 1;
break;
case FLI_CHUNK_SS2:
DecodeSS2(p_bitmapHeader, p_pixelData, (byte*) (chunk + 1), p_flcHeader);
break;
case FLI_CHUNK_COLOR64:
DecodeColors64(p_bitmapHeader, (byte*) (chunk + 1));
*p_decodedColorMap = 1;
break;
case FLI_CHUNK_LC:
DecodeLC(p_bitmapHeader, p_pixelData, (byte*) (chunk + 1), p_flcHeader);
break;
case FLI_CHUNK_BLACK:
DecodeBlack(p_bitmapHeader, p_pixelData, (byte*) (chunk + 1), p_flcHeader);
break;
case FLI_CHUNK_BRUN:
DecodeBrun(p_bitmapHeader, p_pixelData, (byte*) (chunk + 1), p_flcHeader);
break;
case FLI_CHUNK_COPY:
DecodeCopy(p_bitmapHeader, p_pixelData, (byte*) (chunk + 1), p_flcHeader);
break;
}
}
return 0;
}
// FUNCTION: LEGO1 0x100bd880
void DecodeColors256(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data)
{
DecodeColorPackets(p_bitmapHeader, p_data);
}
// FUNCTION: LEGO1 0x100bd8a0
void DecodeColorPackets(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data)
{
WORD colorIndex = 0;
byte* colors = p_data + 2;
for (short packet = *((short*) p_data) - 1; packet >= 0; packet--) {
colorIndex += colors[0];
short colorCount = colors[1];
colors++;
colors++;
if (!colorCount)
colorCount = 256;
DecodeColorPacket(p_bitmapHeader, colors, colorIndex, colorCount);
colorIndex += colorCount;
colors += (colorCount * 3);
}
}
// FUNCTION: LEGO1 0x100bd8f0
void DecodeColorPacket(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data, short index, WORD p_count)
{
byte* palette = (byte*) (p_bitmapHeader) + p_bitmapHeader->biSize + index * 4;
while (p_count-- > 0) {
palette[2] = p_data[0];
palette[1] = p_data[1];
palette[0] = p_data[2];
palette += 4;
p_data += 3;
}
}
// FUNCTION: LEGO1 0x100bd940
void DecodeColors64(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_data)
{
DecodeColorPackets(p_bitmapHeader, p_data);
}
// FUNCTION: LEGO1 0x100bd960
void DecodeBrun(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader)
{
byte* offset = ((p_bitmapHeader->biWidth + 3) & -4) * (p_flcHeader->height - 1) + p_pixelData;
short line = p_flcHeader->height;
while (--line >= 0) {
p_data++;
for (short p_pixel = 0; p_pixel < p_flcHeader->width;) {
char p_count = *(p_data++);
if (p_count >= 0) {
for (short i = p_count; i > 0; i--) {
*(offset++) = *p_data;
}
p_data++;
}
else {
for (short i = -p_count; i > 0; i--) {
*(offset++) = *(p_data++);
}
}
p_pixel += p_count;
}
offset -= (((p_bitmapHeader->biWidth + 3) & -4) + p_flcHeader->width);
}
}
// FUNCTION: LEGO1 0x100bda10
void DecodeLC(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader)
{
short row = p_flcHeader->height - *((short*) p_data) - 1;
byte* pixels = p_data + 4;
short numLines = *((short*) (p_data + 2));
while (--numLines >= 0) {
WORD column = 0;
byte i = *(pixels++);
while (i) {
column += *(pixels++);
char type = *(pixels++);
short p_count;
if (type < 0) {
p_count = -type;
WritePixelRun(p_bitmapHeader, p_pixelData, column, row, *(pixels++), p_count);
}
else {
p_count = type;
WritePixels(p_bitmapHeader, p_pixelData, column, row, pixels, p_count);
pixels += p_count;
}
column += p_count;
i--;
}
row--;
}
}
// FUNCTION: LEGO1 0x100bdac0
void DecodeSS2(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader)
{
short width = p_flcHeader->width - 1;
short row = p_flcHeader->height - 1;
short lines = *p_data;
byte* data = p_data + 2;
do {
short token;
while (true) {
token = *((WORD*) data);
data += 2;
if (token < 0) {
if (token & 0x4000) {
row += token;
continue;
}
}
break;
}
if (token < 0) {
WritePixel(p_bitmapHeader, p_pixelData, width, row, token);
token = *((WORD*) data);
data += 2;
if (!token) {
row--;
continue;
}
}
short column = 0;
do {
column += *(data++);
short type = ((short) *(data++));
type += type;
if (type >= 0) {
WritePixels(p_bitmapHeader, p_pixelData, column, row, data, type);
column += type;
data += type;
}
else {
type = -type;
short p_pixel = *((WORD*) data);
data += 2;
WritePixelPairs(p_bitmapHeader, p_pixelData, column, row, p_pixel, type);
column += type;
}
} while (--token);
} while (--lines > 0);
}
// FUNCTION: LEGO1 0x100bdc00
void DecodeBlack(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader)
{
short width = p_flcHeader->width;
byte pixel[2];
pixel[1] = 0;
pixel[0] = 0;
short line = p_flcHeader->height;
while (--line >= 0) {
short p_count = width / 2;
short odd = width & 1;
WritePixelPairs(p_bitmapHeader, p_pixelData, 0, line, *((WORD*) pixel), p_count);
if (odd) {
WritePixel(p_bitmapHeader, p_pixelData, width - 1, line, 0);
}
}
}
// FUNCTION: LEGO1 0x100bdc90
void DecodeCopy(LPBITMAPINFOHEADER p_bitmapHeader, byte* p_pixelData, byte* p_data, FLIC_HEADER* p_flcHeader)
{
short line = p_flcHeader->height;
int width = p_flcHeader->width;
while (--line >= 0) {
WritePixels(p_bitmapHeader, p_pixelData, 0, line, p_data, p_flcHeader->width);
p_data += width;
}
}
// FUNCTION: LEGO1 0x100bdce0
void DecodeFLCFrame(
LPBITMAPINFOHEADER p_bitmapHeader,
byte* p_pixelData,
FLIC_HEADER* p_flcHeader,
FLIC_FRAME* p_flcFrame,
unsigned char* p_decodedColorMap
)
{
if (p_flcFrame->type == FLI_CHUNK_FRAME) {
DecodeChunks(p_bitmapHeader, p_pixelData, p_flcHeader, p_flcFrame, (byte*) (p_flcFrame + 1), p_decodedColorMap);
}
}

View File

@ -1,51 +1,51 @@
#ifdef 0 #ifdef 0
// LIBRARY: LEGO1 0x100bd530 // XLIBRARY: LEGO1 0x100bd530
// _FUN_100bd530 // _FUN_100bd530
// LIBRARY: LEGO1 0x100bd580 // XLIBRARY: LEGO1 0x100bd580
// _FUN_100bd580 // _FUN_100bd580
// LIBRARY: LEGO1 0x100bd600 // XLIBRARY: LEGO1 0x100bd600
// _FUN_100bd600 // _FUN_100bd600
// LIBRARY: LEGO1 0x100bd680 // XLIBRARY: LEGO1 0x100bd680
// _FUN_100bd680 // _FUN_100bd680
// LIBRARY: LEGO1 0x100bd6e0 // XLIBRARY: LEGO1 0x100bd6e0
// _FUN_100bd6e0 // _FUN_100bd6e0
// LIBRARY: LEGO1 0x100bd760 // XLIBRARY: LEGO1 0x100bd760
// _FUN_100bd760 // _FUN_100bd760
// LIBRARY: LEGO1 0x100bd880 // XLIBRARY: LEGO1 0x100bd880
// _FUN_100bd880 // _FUN_100bd880
// LIBRARY: LEGO1 0x100bd8a0 // XLIBRARY: LEGO1 0x100bd8a0
// _FUN_100bd8a0 // _FUN_100bd8a0
// LIBRARY: LEGO1 0x100bd8f0 // XLIBRARY: LEGO1 0x100bd8f0
// _FUN_100bd8f0 // _FUN_100bd8f0
// LIBRARY: LEGO1 0x100bd940 // XLIBRARY: LEGO1 0x100bd940
// _FUN_100bd940 // _FUN_100bd940
// LIBRARY: LEGO1 0x100bd960 // XLIBRARY: LEGO1 0x100bd960
// _FUN_100bd960 // _FUN_100bd960
// LIBRARY: LEGO1 0x100bda10 // XLIBRARY: LEGO1 0x100bda10
// _FUN_100bda10 // _FUN_100bda10
// LIBRARY: LEGO1 0x100bdac0 // XLIBRARY: LEGO1 0x100bdac0
// _FUN_100bdac0 // _FUN_100bdac0
// LIBRARY: LEGO1 0x100bdc00 // XLIBRARY: LEGO1 0x100bdc00
// _FUN_100bdc00 // _FUN_100bdc00
// LIBRARY: LEGO1 0x100bdc90 // XLIBRARY: LEGO1 0x100bdc90
// _FUN_100bdc90 // _FUN_100bdc90
// LIBRARY: LEGO1 0x100bdce0 // XLIBRARY: LEGO1 0x100bdce0
// _DecodeFLCFrame // _DecodeFLCFrame
#endif #endif

View File

@ -58,7 +58,7 @@ void MxFlcPresenter::LoadFrame(MxStreamChunk* p_chunk)
&m_bitmap->GetBitmapInfo()->m_bmiHeader, &m_bitmap->GetBitmapInfo()->m_bmiHeader,
m_bitmap->GetBitmapData(), m_bitmap->GetBitmapData(),
m_flcHeader, m_flcHeader,
data, (FLIC_FRAME*) data,
&decodedColorMap &decodedColorMap
); );