gimp/plug-ins/file-fli/fli.c

1034 lines
27 KiB
C

/*
* Written 1998 Jens Ch. Restemeier <jchrr@hrz.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/*
* This code can be used to read and write FLI movies. It is currently
* only used for the GIMP fli plug-in, but it can be used for other
* programs, too.
*/
#include "config.h"
#include <glib/gstdio.h>
#include <string.h>
#include <stdio.h>
#include "fli.h"
/*
* To avoid endian-problems I wrote these functions:
*/
static unsigned char
fli_read_char (FILE *f)
{
unsigned char b;
fread (&b, 1, 1, f);
return b;
}
static unsigned short
fli_read_short (FILE *f)
{
unsigned char b[2];
fread (&b, 1, 2, f);
return (unsigned short) (b[1]<<8) | b[0];
}
static unsigned long
fli_read_long (FILE *f)
{
unsigned char b[4];
fread (&b, 1, 4, f);
return (unsigned long) (b[3]<<24) | (b[2]<<16) | (b[1]<<8) | b[0];
}
static void
fli_write_char (FILE *f,
unsigned char b)
{
fwrite (&b, 1, 1, f);
}
static void
fli_write_short (FILE *f,
unsigned short w)
{
unsigned char b[2];
b[0] = w & 255;
b[1] = (w >> 8) & 255;
fwrite (&b, 1, 2, f);
}
static void
fli_write_long (FILE *f,
unsigned long l)
{
unsigned char b[4];
b[0] = l & 255;
b[1] = (l >> 8) & 255;
b[2] = (l >> 16) & 255;
b[3] = (l >> 24) & 255;
fwrite (&b, 1, 4, f);
}
void
fli_read_header (FILE *f,
s_fli_header *fli_header)
{
fli_header->filesize = fli_read_long (f); /* 0 */
fli_header->magic = fli_read_short (f); /* 4 */
fli_header->frames = fli_read_short (f); /* 6 */
fli_header->width = fli_read_short (f); /* 8 */
fli_header->height = fli_read_short (f); /* 10 */
fli_header->depth = fli_read_short (f); /* 12 */
fli_header->flags = fli_read_short (f); /* 14 */
if (fli_header->magic == HEADER_FLI)
{
/* FLI saves speed in 1/70s */
fli_header->speed = fli_read_short (f) * 14; /* 16 */
}
else
{
if (fli_header->magic == HEADER_FLC)
{
/* FLC saves speed in 1/1000s */
fli_header->speed = fli_read_long (f); /* 16 */
}
else
{
fprintf (stderr, "error: magic number is wrong !\n");
fli_header->magic = NO_HEADER;
}
}
if (fli_header->width == 0)
fli_header->width = 320;
if (fli_header->height == 0)
fli_header->height = 200;
}
void
fli_write_header (FILE *f,
s_fli_header *fli_header)
{
fli_header->filesize = ftell (f);
fseek (f, 0, SEEK_SET);
fli_write_long (f, fli_header->filesize); /* 0 */
fli_write_short (f, fli_header->magic); /* 4 */
fli_write_short (f, fli_header->frames); /* 6 */
fli_write_short (f, fli_header->width); /* 8 */
fli_write_short (f, fli_header->height); /* 10 */
fli_write_short (f, fli_header->depth); /* 12 */
fli_write_short (f, fli_header->flags); /* 14 */
if (fli_header->magic == HEADER_FLI)
{
/* FLI saves speed in 1/70s */
fli_write_short (f, fli_header->speed / 14); /* 16 */
}
else
{
if (fli_header->magic == HEADER_FLC)
{
/* FLC saves speed in 1/1000s */
fli_write_long (f, fli_header->speed); /* 16 */
fseek (f, 80, SEEK_SET);
fli_write_long (f, fli_header->oframe1); /* 80 */
fli_write_long (f, fli_header->oframe2); /* 84 */
}
else
{
fprintf (stderr, "error: magic number in header is wrong !\n");
}
}
}
void
fli_read_frame (FILE *f,
s_fli_header *fli_header,
unsigned char *old_framebuf,
unsigned char *old_cmap,
unsigned char *framebuf,
unsigned char *cmap)
{
s_fli_frame fli_frame;
unsigned long framepos;
int c;
framepos = ftell (f);
fli_frame.size = fli_read_long (f);
fli_frame.magic = fli_read_short (f);
fli_frame.chunks = fli_read_short (f);
if (fli_frame.magic == FRAME)
{
fseek (f, framepos + 16, SEEK_SET);
for (c = 0; c < fli_frame.chunks; c++)
{
s_fli_chunk chunk;
unsigned long chunkpos;
chunkpos = ftell (f);
chunk.size = fli_read_long (f);
chunk.magic = fli_read_short (f);
switch (chunk.magic)
{
case FLI_COLOR:
fli_read_color (f, fli_header, old_cmap, cmap);
break;
case FLI_COLOR_2:
fli_read_color_2 (f, fli_header, old_cmap, cmap);
break;
case FLI_BLACK:
fli_read_black (f, fli_header, framebuf);
break;
case FLI_BRUN:
fli_read_brun (f, fli_header, framebuf);
break;
case FLI_COPY:
fli_read_copy (f, fli_header, framebuf);
break;
case FLI_LC:
fli_read_lc (f, fli_header, old_framebuf, framebuf);
break;
case FLI_LC_2:
fli_read_lc_2 (f, fli_header, old_framebuf, framebuf);
break;
case FLI_MINI:
/* unused, skip */
break;
default:
/* unknown, skip */
break;
}
if (chunk.size & 1)
chunk.size++;
fseek (f, chunkpos + chunk.size, SEEK_SET);
}
}
/* else: unknown, skip */
fseek (f, framepos + fli_frame.size, SEEK_SET);
}
void
fli_write_frame (FILE *f,
s_fli_header *fli_header,
unsigned char *old_framebuf,
unsigned char *old_cmap,
unsigned char *framebuf,
unsigned char *cmap,
unsigned short codec_mask)
{
s_fli_frame fli_frame;
unsigned long framepos, frameend;
framepos = ftell (f);
fseek (f, framepos + 16, SEEK_SET);
switch (fli_header->frames)
{
case 0:
fli_header->oframe1 = framepos;
break;
case 1:
fli_header->oframe2 = framepos;
break;
}
fli_frame.size = 0;
fli_frame.magic = FRAME;
fli_frame.chunks = 0;
/*
* create color chunk
*/
if (fli_header->magic == HEADER_FLI)
{
if (fli_write_color (f, fli_header, old_cmap, cmap))
fli_frame.chunks++;
}
else
{
if (fli_header->magic == HEADER_FLC)
{
if (fli_write_color_2 (f, fli_header, old_cmap, cmap))
fli_frame.chunks++;
}
else
{
fprintf (stderr, "error: magic number in header is wrong !\n");
}
}
#if 0
if (codec_mask & W_COLOR)
{
if (fli_write_color (f, fli_header, old_cmap, cmap))
fli_frame.chunks++;
}
if (codec_mask & W_COLOR_2)
{
if (fli_write_color_2 (f, fli_header, old_cmap, cmap))
fli_frame.chunks++;
}
#endif
/* create bitmap chunk */
if (old_framebuf == NULL)
{
fli_write_brun (f, fli_header, framebuf);
}
else
{
fli_write_lc (f, fli_header, old_framebuf, framebuf);
}
fli_frame.chunks++;
frameend = ftell (f);
fli_frame.size = frameend - framepos;
fseek (f, framepos, SEEK_SET);
fli_write_long (f, fli_frame.size);
fli_write_short (f, fli_frame.magic);
fli_write_short (f, fli_frame.chunks);
fseek (f, frameend, SEEK_SET);
fli_header->frames++;
}
/*
* palette chunks from the classical Autodesk Animator.
*/
void
fli_read_color (FILE *f,
s_fli_header *fli_header,
unsigned char *old_cmap,
unsigned char *cmap)
{
unsigned short num_packets, cnt_packets, col_pos;
col_pos = 0;
num_packets = fli_read_short (f);
for (cnt_packets = num_packets; cnt_packets > 0; cnt_packets--)
{
unsigned short skip_col, num_col, col_cnt;
skip_col = fli_read_char (f);
num_col = fli_read_char (f);
if (num_col == 0)
{
for (col_pos = 0; col_pos < 768; col_pos++)
{
cmap[col_pos] = fli_read_char (f) << 2;
}
return;
}
for (col_cnt = skip_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
{
cmap[col_pos] = old_cmap[col_pos];col_pos++;
cmap[col_pos] = old_cmap[col_pos];col_pos++;
cmap[col_pos] = old_cmap[col_pos];col_pos++;
}
for (col_cnt = num_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
{
cmap[col_pos++] = fli_read_char (f) << 2;
cmap[col_pos++] = fli_read_char (f) << 2;
cmap[col_pos++] = fli_read_char (f) << 2;
}
}
}
int
fli_write_color (FILE *f,
s_fli_header *fli_header,
unsigned char *old_cmap,
unsigned char *cmap)
{
unsigned long chunkpos;
unsigned short num_packets;
s_fli_chunk chunk;
chunkpos = ftell (f);
fseek (f, chunkpos + 8, SEEK_SET);
num_packets = 0;
if (old_cmap == NULL)
{
unsigned short col_pos;
num_packets = 1;
fli_write_char (f, 0); /* skip no color */
fli_write_char (f, 0); /* 256 color */
for (col_pos = 0; col_pos < 768; col_pos++)
{
fli_write_char (f, cmap[col_pos] >> 2);
}
}
else
{
unsigned short cnt_skip, cnt_col, col_pos, col_start;
col_pos = 0;
do
{
cnt_skip = 0;
while ((col_pos < 256) &&
(old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
(old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
(old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2]))
{
cnt_skip++;
col_pos++;
}
col_start = col_pos * 3;
cnt_col = 0;
while ((col_pos < 256) &&
!((old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
(old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
(old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2])))
{
cnt_col++;
col_pos++;
}
if (cnt_col > 0)
{
num_packets++;
fli_write_char (f, cnt_skip & 255);
fli_write_char (f, cnt_col & 255);
while (cnt_col > 0)
{
fli_write_char (f, cmap[col_start++] >> 2);
fli_write_char (f, cmap[col_start++] >> 2);
fli_write_char (f, cmap[col_start++] >> 2);
cnt_col--;
}
}
} while (col_pos < 256);
}
if (num_packets > 0)
{
chunk.size = ftell (f) - chunkpos;
chunk.magic = FLI_COLOR;
fseek (f, chunkpos, SEEK_SET);
fli_write_long (f, chunk.size);
fli_write_short (f, chunk.magic);
fli_write_short (f, num_packets);
if (chunk.size & 1)
chunk.size++;
fseek (f, chunkpos + chunk.size, SEEK_SET);
return 1;
}
fseek (f, chunkpos, SEEK_SET);
return 0;
}
/*
* palette chunks from Autodesk Animator pro
*/
void
fli_read_color_2 (FILE *f,
s_fli_header *fli_header,
unsigned char *old_cmap,
unsigned char *cmap)
{
unsigned short num_packets, cnt_packets, col_pos;
num_packets = fli_read_short (f);
col_pos = 0;
for (cnt_packets = num_packets; cnt_packets > 0; cnt_packets--)
{
unsigned short skip_col, num_col, col_cnt;
skip_col = fli_read_char (f);
num_col = fli_read_char (f);
if (num_col == 0)
{
for (col_pos = 0; col_pos < 768; col_pos++)
{
cmap[col_pos] = fli_read_char (f);
}
return;
}
for (col_cnt = skip_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
{
cmap[col_pos] = old_cmap[col_pos];
col_pos++;
cmap[col_pos] = old_cmap[col_pos];
col_pos++;
cmap[col_pos] = old_cmap[col_pos];
col_pos++;
}
for (col_cnt = num_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
{
cmap[col_pos++] = fli_read_char (f);
cmap[col_pos++] = fli_read_char (f);
cmap[col_pos++] = fli_read_char (f);
}
}
}
int
fli_write_color_2 (FILE *f,
s_fli_header *fli_header,
unsigned char *old_cmap,
unsigned char *cmap)
{
unsigned long chunkpos;
unsigned short num_packets;
s_fli_chunk chunk;
chunkpos = ftell (f);
fseek (f, chunkpos + 8, SEEK_SET);
num_packets = 0;
if (old_cmap == NULL)
{
unsigned short col_pos;
num_packets = 1;
fli_write_char (f, 0); /* skip no color */
fli_write_char (f, 0); /* 256 color */
for (col_pos = 0; col_pos < 768; col_pos++)
{
fli_write_char (f, cmap[col_pos]);
}
}
else
{
unsigned short cnt_skip, cnt_col, col_pos, col_start;
col_pos = 0;
do {
cnt_skip = 0;
while ((col_pos < 256) &&
(old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
(old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
(old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2]))
{
cnt_skip++; col_pos++;
}
col_start = col_pos * 3;
cnt_col = 0;
while ((col_pos < 256) &&
!((old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
(old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
(old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2])))
{
cnt_col++;
col_pos++;
}
if (cnt_col > 0)
{
num_packets++;
fli_write_char (f, cnt_skip);
fli_write_char (f, cnt_col);
while (cnt_col > 0)
{
fli_write_char (f, cmap[col_start++]);
fli_write_char (f, cmap[col_start++]);
fli_write_char (f, cmap[col_start++]);
cnt_col--;
}
}
} while (col_pos < 256);
}
if (num_packets > 0)
{
chunk.size = ftell (f) - chunkpos;
chunk.magic = FLI_COLOR_2;
fseek (f, chunkpos, SEEK_SET);
fli_write_long (f, chunk.size);
fli_write_short (f, chunk.magic);
fli_write_short (f, num_packets);
if (chunk.size & 1)
chunk.size++;
fseek (f, chunkpos + chunk.size, SEEK_SET);
return 1;
}
fseek (f, chunkpos, SEEK_SET);
return 0;
}
/*
* completely black frame
*/
void
fli_read_black (FILE *f,
s_fli_header *fli_header,
unsigned char *framebuf)
{
memset (framebuf, 0, fli_header->width * fli_header->height);
}
void
fli_write_black (FILE *f,
s_fli_header *fli_header,
unsigned char *framebuf)
{
s_fli_chunk chunk;
chunk.size = 6;
chunk.magic = FLI_BLACK;
fli_write_long (f, chunk.size);
fli_write_short (f, chunk.magic);
}
/*
* Uncompressed frame
*/
void
fli_read_copy (FILE *f,
s_fli_header *fli_header,
unsigned char *framebuf)
{
fread (framebuf, fli_header->width, fli_header->height, f);
}
void
fli_write_copy (FILE *f,
s_fli_header *fli_header,
unsigned char *framebuf)
{
unsigned long chunkpos;
s_fli_chunk chunk;
chunkpos = ftell (f);
fseek (f, chunkpos + 6, SEEK_SET);
fwrite (framebuf, fli_header->width, fli_header->height, f);
chunk.size = ftell (f) - chunkpos;
chunk.magic = FLI_COPY;
fseek (f, chunkpos, SEEK_SET);
fli_write_long (f, chunk.size);
fli_write_short (f, chunk.magic);
if (chunk.size & 1)
chunk.size++;
fseek (f, chunkpos + chunk.size, SEEK_SET);
}
/*
* This is a RLE algorithm, used for the first image of an animation
*/
void
fli_read_brun (FILE *f,
s_fli_header *fli_header,
unsigned char *framebuf)
{
unsigned short yc;
unsigned char *pos;
for (yc = 0; yc < fli_header->height; yc++)
{
unsigned short pc, pcnt;
size_t n, xc;
pc = fli_read_char (f);
xc = 0;
pos = framebuf + (fli_header->width * yc);
n = (size_t) fli_header->width * (fli_header->height - yc);
for (pcnt = pc; pcnt > 0; pcnt--)
{
unsigned short ps;
ps = fli_read_char (f);
if (ps & 0x80)
{
unsigned short len;
for (len = -(signed char) ps; len > 0 && xc < n; len--)
{
pos[xc++] = fli_read_char (f);
}
}
else
{
unsigned char val;
size_t len;
len = MIN (n - xc, ps);
val = fli_read_char (f);
memset (&(pos[xc]), val, len);
xc+=len;
}
}
}
}
void
fli_write_brun (FILE *f,
s_fli_header *fli_header,
unsigned char *framebuf)
{
unsigned long chunkpos;
s_fli_chunk chunk;
unsigned short yc;
unsigned char *linebuf;
chunkpos = ftell (f);
fseek (f, chunkpos + 6, SEEK_SET);
for (yc = 0; yc < fli_header->height; yc++)
{
unsigned short xc, t1, pc, tc;
unsigned long linepos, lineend, bc;
linepos = ftell (f); bc = 0;
fseek (f, 1, SEEK_CUR);
linebuf = framebuf + (yc * fli_header->width);
xc = 0; tc = 0; t1 = 0;
while (xc < fli_header->width)
{
pc = 1;
while ((pc < 120) &&
((xc + pc) < fli_header->width) &&
(linebuf[xc + pc] == linebuf[xc]))
{
pc++;
}
if (pc > 2)
{
if (tc > 0)
{
bc++;
fli_write_char (f, (tc - 1)^0xFF);
fwrite (linebuf + t1, 1, tc, f);
tc = 0;
}
bc++;
fli_write_char (f, pc);
fli_write_char (f, linebuf[xc]);
t1 = xc + pc;
}
else
{
tc+=pc;
if (tc > 120)
{
bc++;
fli_write_char (f, (tc - 1)^0xFF);
fwrite (linebuf + t1, 1, tc, f);
tc = 0;
t1 = xc + pc;
}
}
xc+=pc;
}
if (tc > 0)
{
bc++;
fli_write_char (f, (tc - 1)^0xFF);
fwrite (linebuf + t1, 1, tc, f);
tc = 0;
}
lineend = ftell (f);
fseek (f, linepos, SEEK_SET);
fli_write_char (f, bc);
fseek (f, lineend, SEEK_SET);
}
chunk.size = ftell (f) - chunkpos;
chunk.magic = FLI_BRUN;
fseek (f, chunkpos, SEEK_SET);
fli_write_long (f, chunk.size);
fli_write_short (f, chunk.magic);
if (chunk.size & 1)
chunk.size++;
fseek (f, chunkpos + chunk.size, SEEK_SET);
}
/*
* This is the delta-compression method from the classic Autodesk
* Animator. It's basically the RLE method from above, but it
* supports skipping unchanged lines at the beginning and end of an
* image, and unchanged pixels in a line. This chunk is used in FLI
* files.
*/
void
fli_read_lc (FILE *f,
s_fli_header *fli_header,
unsigned char *old_framebuf,
unsigned char *framebuf)
{
unsigned short yc, firstline, numline;
unsigned char *pos;
memcpy (framebuf, old_framebuf, fli_header->width * fli_header->height);
firstline = fli_read_short (f);
numline = fli_read_short (f);
if (numline > fli_header->height || fli_header->height - numline < firstline)
return;
for (yc = 0; yc < numline; yc++)
{
unsigned short pc, pcnt;
size_t n, xc;
pc = fli_read_char (f);
xc = 0;
pos = framebuf + (fli_header->width * (firstline + yc));
n = (size_t) fli_header->width * (fli_header->height - firstline - yc);
for (pcnt = pc; pcnt > 0; pcnt--)
{
unsigned short ps, skip;
skip = fli_read_char (f);
ps = fli_read_char (f);
xc+=MIN (n - xc, skip);
if (ps & 0x80)
{
unsigned char val;
size_t len;
ps = -(signed char) ps;
val = fli_read_char (f);
len = MIN (n - xc, ps);
memset (&(pos[xc]), val, len);
xc+=len;
}
else
{
size_t len;
len = MIN (n - xc, ps);
fread (&(pos[xc]), len, 1, f);
xc+=len;
}
}
}
}
void
fli_write_lc (FILE *f,
s_fli_header *fli_header,
unsigned char *old_framebuf,
unsigned char *framebuf)
{
unsigned long chunkpos;
s_fli_chunk chunk;
unsigned short yc, firstline, numline, lastline;
unsigned char *linebuf, *old_linebuf;
chunkpos = ftell (f);
fseek (f, chunkpos + 6, SEEK_SET);
/* first check, how many lines are unchanged at the beginning */
firstline = 0;
while ((memcmp (old_framebuf + (firstline * fli_header->width),
framebuf + (firstline * fli_header->width),
fli_header->width) == 0) &&
(firstline < fli_header->height))
firstline++;
/* then check from the end, how many lines are unchanged */
if (firstline < fli_header->height)
{
lastline = fli_header->height - 1;
while ((memcmp (old_framebuf + (lastline * fli_header->width),
framebuf + (lastline * fli_header->width),
fli_header->width) == 0) &&
(lastline > firstline))
lastline--;
numline = (lastline - firstline) + 1;
}
else
{
numline = 0;
}
if (numline == 0)
firstline = 0;
fli_write_short (f, firstline);
fli_write_short (f, numline);
for (yc = 0; yc < numline; yc++)
{
unsigned short xc, sc, cc, tc;
unsigned long linepos, lineend, bc;
linepos = ftell (f); bc = 0;
fseek (f, 1, SEEK_CUR);
linebuf = framebuf + ((firstline + yc)*fli_header->width);
old_linebuf = old_framebuf + ((firstline + yc)*fli_header->width);
xc = 0;
while (xc < fli_header->width)
{
sc = 0;
while ((xc < fli_header->width) &&
(linebuf[xc] == old_linebuf[xc]) &&
(sc < 255))
{
xc++;
sc++;
}
fli_write_char (f, sc);
cc = 1;
while ((linebuf[xc] == linebuf[xc + cc]) &&
((xc + cc)<fli_header->width) &&
(cc < 120))
{
cc++;
}
if (cc > 2)
{
bc++;
fli_write_char (f, (cc - 1)^0xFF);
fli_write_char (f, linebuf[xc]);
xc+=cc;
}
else
{
tc = 0;
do {
sc = 0;
while ((linebuf[tc + xc + sc] == old_linebuf[tc + xc + sc]) &&
((tc + xc + sc)<fli_header->width) &&
(sc < 5))
{
sc++;
}
cc = 1;
while ((linebuf[tc + xc] == linebuf[tc + xc + cc]) &&
((tc + xc + cc)<fli_header->width) &&
(cc < 10))
{
cc++;
}
tc++;
} while ((tc < 120) &&
(cc < 9) &&
(sc < 4) &&
((xc + tc) < fli_header->width));
bc++;
fli_write_char (f, tc);
fwrite (linebuf + xc, tc, 1, f);
xc+=tc;
}
}
lineend = ftell (f);
fseek (f, linepos, SEEK_SET);
fli_write_char (f, bc);
fseek (f, lineend, SEEK_SET);
}
chunk.size = ftell (f) - chunkpos;
chunk.magic = FLI_LC;
fseek (f, chunkpos, SEEK_SET);
fli_write_long (f, chunk.size);
fli_write_short (f, chunk.magic);
if (chunk.size & 1)
chunk.size++;
fseek (f, chunkpos + chunk.size, SEEK_SET);
}
/*
* This is an enhanced version of the old delta-compression used by
* the autodesk animator pro. It's word-oriented, and supports
* skipping larger parts of the image. This chunk is used in FLC
* files.
*/
void
fli_read_lc_2 (FILE *f,
s_fli_header *fli_header,
unsigned char *old_framebuf,
unsigned char *framebuf)
{
unsigned short yc, lc, numline;
unsigned char *pos;
memcpy (framebuf, old_framebuf, fli_header->width * fli_header->height);
yc = 0;
numline = fli_read_short (f);
for (lc = 0; lc < numline; lc++)
{
unsigned short pc, pcnt, lpf, lpn;
size_t n, xc;
pc = fli_read_short (f);
lpf = 0; lpn = 0;
while (pc & 0x8000)
{
if (pc & 0x4000)
{
yc += -(signed short) pc;
}
else
{
lpf = 1;
lpn = pc & 0xFF;
}
pc = fli_read_short (f);
}
yc = MIN (yc, fli_header->height);
xc = 0;
pos = framebuf + (fli_header->width * yc);
n = (size_t) fli_header->width * (fli_header->height - yc);
for (pcnt = pc; pcnt > 0; pcnt--)
{
unsigned short ps, skip;
skip = fli_read_char (f);
ps = fli_read_char (f);
xc += MIN (n - xc, skip);
if (ps & 0x80)
{
unsigned char v1, v2;
ps = -(signed char) ps;
v1 = fli_read_char (f);
v2 = fli_read_char (f);
while (ps > 0 && xc + 1 < n)
{
pos[xc++] = v1;
pos[xc++] = v2;
ps--;
}
}
else
{
size_t len;
len = MIN ((n - xc)/2, ps);
fread (&(pos[xc]), len, 2, f);
xc += len << 1;
}
}
if (lpf)
pos[xc] = lpn;
yc++;
}
}