Load FLI/FLC autodesk animator files.

This commit is contained in:
Thomas Bernard 2018-04-16 07:03:17 +00:00 committed by Adrien Destugues
parent 28c755a776
commit bb7b187275
5 changed files with 422 additions and 4 deletions

View File

@ -147,6 +147,7 @@ enum FILE_FORMATS
FORMAT_XPM,
FORMAT_ICO,
FORMAT_INFO,
FORMAT_FLI,
FORMAT_MISC, ///< Must be last of enum: others formats recognized by SDL_image
};

View File

@ -215,8 +215,6 @@ void Save_IMG(T_IO_Context * context)
}
else
{
fclose(file);
Remove_file(context);
File_error=1;
}
}
@ -1946,7 +1944,7 @@ void Load_IFF(T_IO_Context * context)
else if (memcmp(section, "BODY", 4) == 0)
{
long offset = ftell(IFF_file);
if (file_size > (offset + section_size + 8))
if (file_size > (unsigned long)(offset + section_size + 8))
{
// Chunk RAST is placed AFTER the BODY, but we need the palette now to decode the image
// In addition, some files break the IFF standard by not aligning

View File

@ -137,4 +137,8 @@ void Save_PNG(T_IO_Context *);
void Test_INFO(T_IO_Context *, FILE *);
void Load_INFO(T_IO_Context *);
// -- FLI/FLC (Autodesk animator) -------------------------------------------
void Test_FLI(T_IO_Context *, FILE *);
void Load_FLI(T_IO_Context *);
#endif

View File

@ -77,7 +77,7 @@ void Load_Recoil_Image(T_IO_Context *);
// ENUM Name TestFunc LoadFunc SaveFunc PalOnly Comment Layers Ext Exts
const T_Format File_formats[] = {
{FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "", "gif;png;bmp;2bp;pcx;pkm;iff;lbm;ilbm;sham;ham;ham6;ham8;acbm;pic;anim;img;sci;scq;scf;scn;sco;pi1;pc1;cel;neo;c64;koa;koala;fli;bml;cdu;prg;tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico;ic2;cur;cm5;pph;info"},
{FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "", "gif;png;bmp;2bp;pcx;pkm;iff;lbm;ilbm;sham;ham;ham6;ham8;acbm;pic;anim;img;sci;scq;scf;scn;sco;pi1;pc1;cel;neo;c64;koa;koala;fli;bml;cdu;prg;tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico;ic2;cur;cm5;pph;info;fli;flc"},
{FORMAT_ALL_PALETTES, "(pal)", NULL, NULL, NULL, 1, 0, 0, "", "kcf;pal;gpl"},
{FORMAT_ALL_FILES, "(*.*)", NULL, NULL, NULL, 0, 0, 0, "", "*"},
{FORMAT_GIF, " gif", Test_GIF, Load_GIF, Save_GIF, 0, 1, 1, "gif", "gif"},
@ -106,6 +106,7 @@ const T_Format File_formats[] = {
{FORMAT_XPM, " xpm", NULL, NULL, Save_XPM, 0, 0, 0, "xpm", "xpm"},
{FORMAT_ICO, " ico", Test_ICO, Load_ICO, Save_ICO, 0, 0, 0, "ico", "ico;ic2;cur"},
{FORMAT_INFO," info",Test_INFO,Load_INFO,NULL, 0, 0, 0, "info", "info"},
{FORMAT_FLI, " fli", Test_FLI, Load_FLI, NULL, 0, 0, 0, "fli", "fli;flc"},
{FORMAT_MISC,"misc.",NULL, NULL, NULL, 0, 0, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff"},
};

View File

@ -42,6 +42,7 @@
#include "struct.h"
#include "windows.h"
#include "oldies.h"
#include "pages.h"
#include "fileformats.h"
//////////////////////////////////// PAL ////////////////////////////////////
@ -3794,3 +3795,416 @@ void Save_PPH(T_IO_Context* context)
// R: use 16 used colors (or 16 first?)
// B1: find the 16 colors used in a line? Or assume they are in-order already?
}
/////////////////////////////////// FLI/FLC /////////////////////////////////
typedef struct {
dword size; /* Size of FLIC including this header */
word type; /* File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ... */
word frames; /* Number of frames in first segment */
word width; /* FLIC width in pixels */
word height; /* FLIC height in pixels */
word depth; /* Bits per pixel (usually 8) */
word flags; /* Set to zero or to three */
dword speed; /* Delay between frames */
word reserved1; /* Set to zero */
dword created; /* Date of FLIC creation (FLC only) */
dword creator; /* Serial number or compiler id (FLC only) */
dword updated; /* Date of FLIC update (FLC only) */
dword updater; /* Serial number (FLC only), see creator */
word aspect_dx; /* Width of square rectangle (FLC only) */
word aspect_dy; /* Height of square rectangle (FLC only) */
word ext_flags; /* EGI: flags for specific EGI extensions */
word keyframes; /* EGI: key-image frequency */
word totalframes; /* EGI: total number of frames (segments) */
dword req_memory; /* EGI: maximum chunk size (uncompressed) */
word max_regions; /* EGI: max. number of regions in a CHK_REGION chunk */
word transp_num; /* EGI: number of transparent levels */
byte reserved2[24]; /* Set to zero */
dword oframe1; /* Offset to frame 1 (FLC only) */
dword oframe2; /* Offset to frame 2 (FLC only) */
byte reserved3[40]; /* Set to zero */
} T_FLIC_Header;
static void Load_FLI_Header(FILE * file, T_FLIC_Header * header)
{
if (!(Read_dword_le(file,&header->size)
&& Read_word_le(file,&header->type)
&& Read_word_le(file,&header->frames)
&& Read_word_le(file,&header->width)
&& Read_word_le(file,&header->height)
&& Read_word_le(file,&header->depth)
&& Read_word_le(file,&header->flags)
&& Read_dword_le(file,&header->speed)
&& Read_word_le(file,&header->reserved1)
&& Read_dword_le(file,&header->created)
&& Read_dword_le(file,&header->creator)
&& Read_dword_le(file,&header->updated)
&& Read_dword_le(file,&header->updater)
&& Read_word_le(file,&header->aspect_dx)
&& Read_word_le(file,&header->aspect_dy)
&& Read_word_le(file,&header->ext_flags)
&& Read_word_le(file,&header->keyframes)
&& Read_word_le(file,&header->totalframes)
&& Read_dword_le(file,&header->req_memory)
&& Read_word_le(file,&header->max_regions)
&& Read_word_le(file,&header->transp_num)
&& Read_bytes(file,header->reserved2,24)
&& Read_dword_le(file,&header->oframe1)
&& Read_dword_le(file,&header->oframe2)
&& Read_bytes(file,header->reserved2,40) ))
{
File_error=1;
}
}
void Test_FLI(T_IO_Context * context, FILE * file)
{
T_FLIC_Header header;
(void)context;
File_error=0;
Load_FLI_Header(file, &header);
if (File_error != 0) return;
switch (header.type)
{
case 0xAF11: // standard FLI
case 0xAF12: // FLC (8bpp)
#if 0
case 0xAF30: // Huffman or BWT compression
case 0xAF31: // frame shift compression
case 0xAF44: // bpp != 8
#endif
File_error=0;
break;
default:
File_error=1;
}
}
void Load_FLI(T_IO_Context * context)
{
FILE * file;
unsigned long file_size;
T_FLIC_Header header;
dword chunk_size;
word chunk_type;
word sub_chunk_count, sub_chunk_index;
dword sub_chunk_size;
word sub_chunk_type;
word frame_delay, frame_width, frame_height;
int current_frame = 0;
file = Open_file_read(context);
if (file == NULL)
{
File_error=1;
return;
}
File_error=0;
file_size = File_length_file(file);
Load_FLI_Header(file, &header);
if (File_error != 0)
{
fclose(file);
return;
}
if (header.size == 12)
{
// special "magic carpet" format
header.depth = 8;
header.speed = 66; // about 15fps
fseek(file, 12, SEEK_SET);
}
else if (file_size != header.size)
Warning("Load_FLI(): file size mismatch in header");
if (header.speed == 0)
{
if (header.type == 0xAF11) // FLI
header.speed = 1; // 1/70th seconds
else
header.speed = 10; // 10ms
}
Pre_load(context, header.width,header.height,file_size,FORMAT_FLI,PIXEL_SIMPLE,header.depth);
if (context->Type == CONTEXT_MAIN_IMAGE)
{
Main.backups->Pages->Image_mode = IMAGE_MODE_ANIMATION;
Update_screen_targets();
}
while (File_error == 0
&& Read_dword_le(file,&chunk_size) && Read_word_le(file,&chunk_type))
{
chunk_size -= 6;
switch (chunk_type)
{
case 0xf1fa: // FRAME
Read_word_le(file, &sub_chunk_count);
Read_word_le(file, &frame_delay);
fseek(file, 2, SEEK_CUR);
Read_word_le(file, &frame_width);
Read_word_le(file, &frame_height);
if (frame_width == 0)
frame_width = header.width;
if (frame_height == 0)
frame_height = header.height;
if (frame_delay == 0)
frame_delay = header.speed;
chunk_size -= 10;
for (sub_chunk_index = 0; sub_chunk_index < sub_chunk_count; sub_chunk_index++)
{
if (!(Read_dword_le(file,&sub_chunk_size) && Read_word_le(file,&sub_chunk_type)))
File_error = 1;
else
{
chunk_size -= sub_chunk_size;
sub_chunk_size -= 6;
if (sub_chunk_type == 0x04 || sub_chunk_type == 0x0b) // color map
{
word packet_count;
int i = 0;
sub_chunk_size -= 2;
if (!Read_word_le(file, &packet_count))
File_error = 1;
else
while (packet_count-- > 0 && File_error == 0)
{
byte skip, count;
if (!(Read_byte(file, &skip) && Read_byte(file, &count)))
File_error = 1;
else
{
sub_chunk_size -= 2;
i += skip; // count 0 means 256
do
{
byte r, g, b;
if (!(Read_byte(file, &r) && Read_byte(file, &g) && Read_byte(file, &b)))
{
File_error = 1;
break;
}
if (sub_chunk_type == 0x0b || header.size == 12) // 6bit per color
{
r = (r << 2) | (r >> 4);
g = (g << 2) | (g >> 4);
b = (b << 2) | (b >> 4);
}
context->Palette[i].R = r;
context->Palette[i].G = g;
context->Palette[i].B = b;
i++;
sub_chunk_size -= 3;
} while (--count != 0);
}
}
}
else if (sub_chunk_type == 0x0f) // full frame RLE
{
word x, y;
if (header.type == 0xAF11) // FLI
Set_frame_duration(context, (frame_delay * 100) / 7); // 1/70th sec
else
Set_frame_duration(context, frame_delay); // msec
for (y = 0; y < frame_height && File_error == 0; y++)
{
byte count, data;
Read_byte(file, &count); // packet count, but dont rely on it
sub_chunk_size--;
for (x = 0; x < frame_width; )
{
if (!Read_byte(file, &count))
{
File_error = 1;
break;
}
sub_chunk_size--;
if ((count & 0x80) == 0)
{
if (!Read_byte(file, &data)) // repeat data count times
{
File_error = 1;
break;
}
sub_chunk_size--;
while (count-- > 0 && x < frame_width)
Set_pixel(context, x++, y, data);
}
else
while (count++ != 0 && x < frame_width) // copy count bytes
{
if (!Read_byte(file, &data))
{
File_error = 1;
break;
}
Set_pixel(context, x++, y, data);
sub_chunk_size--;
}
}
}
if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE)
{ // load only 1st frame in preview
fclose(file);
return;
}
}
else if (sub_chunk_type == 0x0c) // delta image, RLE
{
word x, y, line_count;
Set_loading_layer(context, ++current_frame);
if (header.type == 0xAF11) // FLI
Set_frame_duration(context, (frame_delay * 100) / 7); // 1/70th sec
else
Set_frame_duration(context, frame_delay); // msec
if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// Copy the content of previous frame
memcpy(
Main.backups->Pages->Image[Main.current_layer].Pixels,
Main.backups->Pages->Image[Main.current_layer-1].Pixels,
Main.backups->Pages->Width*Main.backups->Pages->Height);
}
Read_word_le(file, &y);
Read_word_le(file, &line_count);
sub_chunk_size -= 4;
while (sub_chunk_size > 0 && line_count > 0 && File_error == 0)
{
byte packet_count;
x = 0;
if (!Read_byte(file, &packet_count))
File_error = 1;
else
{
sub_chunk_size--;
while (packet_count-- > 0 && File_error == 0)
{
byte skip, count, data;
if (!(Read_byte(file, &skip) && Read_byte(file, &count)))
File_error = 1;
else
{
sub_chunk_size -= 2;
x += skip;
if (count & 0x80)
{
Read_byte(file, &data);
sub_chunk_size--;
while (count++ != 0)
Set_pixel(context, x++, y, data);
}
else
while (count-- > 0)
{
Read_byte(file, &data);
sub_chunk_size--;
Set_pixel(context, x++, y, data);
}
}
}
}
y++;
line_count--;
}
}
else if (sub_chunk_type == 0x07) // FLC delta image
{
word opcode, y, line_count;
Set_loading_layer(context, ++current_frame);
if (header.type == 0xAF11) // FLI
Set_frame_duration(context, (frame_delay * 100) / 7); // 1/70th sec
else
Set_frame_duration(context, frame_delay); // msec
if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// Copy the content of previous frame
memcpy(
Main.backups->Pages->Image[Main.current_layer].Pixels,
Main.backups->Pages->Image[Main.current_layer-1].Pixels,
Main.backups->Pages->Width*Main.backups->Pages->Height);
}
y = 0;
Read_word_le(file, &line_count);
sub_chunk_size -= 2;
while (line_count > 0)
{
Read_word_le(file, &opcode);
sub_chunk_size -= 2;
if ((opcode & 0xc000) == 0x0000) // packet count
{
word x = 0;
while (opcode-- > 0)
{
byte skip, count, data1, data2;
if (!(Read_byte(file, &skip) && Read_byte(file, &count)))
File_error = 1;
else
{
sub_chunk_size -= 2;
x += skip;
if (count & 0x80)
{
Read_byte(file, &data1);
Read_byte(file, &data2);
sub_chunk_size -= 2;
while (count++ != 0)
{
Set_pixel(context, x++, y, data1);
Set_pixel(context, x++, y, data2);
}
}
else
while (count-- > 0)
{
Read_byte(file, &data1);
Set_pixel(context, x++, y, data1);
Read_byte(file, &data2);
Set_pixel(context, x++, y, data2);
sub_chunk_size -= 2;
}
}
}
y++;
line_count--;
}
else if ((opcode & 0xc000) == 0xc000) // line skip
{
y -= opcode;
}
else if ((opcode & 0xc000) == 0x8000) // last byte
{
Set_pixel(context, frame_width - 1, y, opcode & 0xff);
}
else
{
Warning("Unsupported opcode");
File_error = 2;
break;
}
}
}
if (sub_chunk_size > 0)
{
fseek(file, sub_chunk_size, SEEK_CUR);
}
}
}
break;
default: // skip
Warning("Load_FLI(): unrecognized chunk");
}
if (chunk_size > 0 && header.size != 12)
{
fseek(file, chunk_size, SEEK_CUR);
}
}
fclose(file);
}