1538 lines
43 KiB
C
1538 lines
43 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 PIKA fli plug-in, but it can be used for other
|
|
* programs, too.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <libpika/pika.h>
|
|
|
|
#include "fli.h"
|
|
|
|
#include "libpika/stdplugins-intl.h"
|
|
|
|
/*
|
|
* To avoid endian-problems I wrote these functions:
|
|
*/
|
|
static gboolean
|
|
fli_read_char (FILE *f, guchar *value, GError **error)
|
|
{
|
|
if (fread (value, 1, 1, f) != 1)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error reading from file."));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fli_read_short (FILE *f, gushort *value, GError **error)
|
|
{
|
|
guchar b[2];
|
|
|
|
if (fread (&b, 1, 2, f) != 2)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error reading from file."));
|
|
return FALSE;
|
|
}
|
|
|
|
*value = (gushort) (b[1]<<8) | b[0];
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fli_read_uint32 (FILE *f, guint32 *value, GError **error)
|
|
{
|
|
guchar b[4];
|
|
|
|
if (fread (&b, 1, 4, f) != 4)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error reading from file."));
|
|
return FALSE;
|
|
}
|
|
|
|
*value = (guint32) (b[3]<<24) | (b[2]<<16) | (b[1]<<8) | b[0];
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fli_write_char (FILE *f,
|
|
guchar b,
|
|
GError **error)
|
|
{
|
|
if (fwrite (&b, 1, 1, f) != 1)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error writing to file."));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fli_write_short (FILE *f,
|
|
gushort w,
|
|
GError **error)
|
|
{
|
|
guchar b[2];
|
|
|
|
b[0] = w & 255;
|
|
b[1] = (w >> 8) & 255;
|
|
|
|
if (fwrite (&b, 1, 2, f) != 2)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error writing to file."));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fli_write_uint32 (FILE *f,
|
|
guint32 l,
|
|
GError **error)
|
|
{
|
|
guchar b[4];
|
|
|
|
b[0] = l & 255;
|
|
b[1] = (l >> 8) & 255;
|
|
b[2] = (l >> 16) & 255;
|
|
b[3] = (l >> 24) & 255;
|
|
|
|
if (fwrite (&b, 1, 4, f) != 4)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error writing to file."));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_read_header (FILE *f,
|
|
s_fli_header *fli_header,
|
|
GError **error)
|
|
{
|
|
goffset actual_size;
|
|
|
|
/* Get the actual file size, since filesize in header could be wrong. */
|
|
fseek(f, 0, SEEK_END);
|
|
actual_size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
if (! fli_read_uint32 (f, &fli_header->filesize, error) || /* 0 */
|
|
! fli_read_short (f, &fli_header->magic, error) || /* 4 */
|
|
! fli_read_short (f, &fli_header->frames, error) || /* 6 */
|
|
! fli_read_short (f, &fli_header->width, error) || /* 8 */
|
|
! fli_read_short (f, &fli_header->height, error) || /* 10 */
|
|
! fli_read_short (f, &fli_header->depth, error) || /* 12 */
|
|
! fli_read_short (f, &fli_header->flags, error)) /* 14 */
|
|
{
|
|
g_prefix_error (error, _("Error reading header. "));
|
|
return FALSE;
|
|
}
|
|
|
|
if (fli_header->magic == HEADER_FLI)
|
|
{
|
|
gushort speed;
|
|
/* FLI saves speed in 1/70s */
|
|
if (! fli_read_short (f, &speed, error)) /* 16 */
|
|
{
|
|
g_prefix_error (error, _("Error reading header. "));
|
|
return FALSE;
|
|
}
|
|
fli_header->speed = speed * 14;
|
|
}
|
|
else
|
|
{
|
|
if (fli_header->magic == HEADER_FLC)
|
|
{
|
|
/* FLC saves speed in 1/1000s */
|
|
if (! fli_read_uint32 (f, &fli_header->speed, error)) /* 16 */
|
|
{
|
|
g_prefix_error (error, _("Error reading header. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fli_header->magic = NO_HEADER;
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
_("Invalid header: not a FLI/FLC animation!"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fli_header->width == 0)
|
|
fli_header->width = 320;
|
|
|
|
if (fli_header->height == 0)
|
|
fli_header->height = 200;
|
|
|
|
if (actual_size != fli_header->filesize && actual_size >= 0)
|
|
{
|
|
/* Older versions of PIKA or other apps may incorrectly finish chunks on
|
|
* an odd length, but write filesize as if that last byte was written.
|
|
* Don't fail on off-by-one file size. */
|
|
if (actual_size + 1 != fli_header->filesize)
|
|
{
|
|
g_warning (_("Incorrect file size in header: %u, should be: %u."),
|
|
fli_header->filesize, (guint) actual_size);
|
|
fli_header->filesize = actual_size;
|
|
}
|
|
}
|
|
|
|
if (fli_header->frames == 0)
|
|
{
|
|
g_warning (_("Number of frames is 0. Setting to 2."));
|
|
fli_header->frames = 2;
|
|
}
|
|
|
|
/* A delay longer than 10 seconds is suspicious. */
|
|
if (fli_header->speed > 10000 || fli_header->speed == 0)
|
|
{
|
|
g_warning (_("Suspicious frame delay of %ums. Setting delay to 70ms."),
|
|
fli_header->speed);
|
|
fli_header->speed = 70;
|
|
}
|
|
|
|
g_debug ("Filesize: %u, magic: %x, frames: %u, wxh: %ux%u, depth: %u, flags: %x, speed: %u",
|
|
fli_header->filesize, fli_header->magic, fli_header->frames,
|
|
fli_header->width, fli_header->height, fli_header->depth,
|
|
fli_header->flags, fli_header->speed);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_header (FILE *f,
|
|
s_fli_header *fli_header,
|
|
GError **error)
|
|
{
|
|
fli_header->filesize = ftell (f);
|
|
fseek (f, 0, SEEK_SET);
|
|
|
|
if (! fli_write_uint32 (f, fli_header->filesize, error) || /* 0 */
|
|
! fli_write_short (f, fli_header->magic, error) || /* 4 */
|
|
! fli_write_short (f, fli_header->frames, error) || /* 6 */
|
|
! fli_write_short (f, fli_header->width, error) || /* 8 */
|
|
! fli_write_short (f, fli_header->height, error) || /* 10 */
|
|
! fli_write_short (f, fli_header->depth, error) || /* 12 */
|
|
! fli_write_short (f, fli_header->flags, error)) /* 14 */
|
|
{
|
|
g_prefix_error (error, _("Error writing header. "));
|
|
return FALSE;
|
|
}
|
|
|
|
if (fli_header->magic == HEADER_FLI)
|
|
{
|
|
/* FLI saves speed in 1/70s */
|
|
if (! fli_write_short (f, (fli_header->speed + 7) / 14, error)) /* 16 */
|
|
{
|
|
g_prefix_error (error, _("Error writing header. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fli_header->magic == HEADER_FLC)
|
|
{
|
|
/* FLC saves speed in 1/1000s */
|
|
if (! fli_write_uint32 (f, fli_header->speed, error)) /* 16 */
|
|
{
|
|
g_prefix_error (error, _("Error writing header. "));
|
|
return FALSE;
|
|
}
|
|
fseek (f, 80, SEEK_SET);
|
|
if (! fli_write_uint32 (f, fli_header->oframe1, error) || /* 80 */
|
|
! fli_write_uint32 (f, fli_header->oframe2, error)) /* 84 */
|
|
{
|
|
g_prefix_error (error, _("Error writing header. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, PIKA_PLUG_IN_ERROR, 0,
|
|
_("Invalid header: unrecognized magic number!"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_read_frame (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_framebuf,
|
|
guchar *old_cmap,
|
|
guchar *framebuf,
|
|
guchar *cmap,
|
|
GError **error)
|
|
{
|
|
s_fli_frame fli_frame;
|
|
gint64 framepos;
|
|
int c;
|
|
|
|
while (TRUE)
|
|
{
|
|
framepos = ftell (f);
|
|
|
|
if (framepos < 0 ||
|
|
! fli_read_uint32 (f, &fli_frame.size, error) ||
|
|
! fli_read_short (f, &fli_frame.magic, error) ||
|
|
! fli_read_short (f, &fli_frame.chunks, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading frame. "));
|
|
return FALSE;
|
|
}
|
|
|
|
g_debug ("Offset: %u, frame size: %u, magic: %x, chunks: %u",
|
|
(guint) framepos, fli_frame.size, fli_frame.magic, fli_frame.chunks);
|
|
|
|
if (framepos + fli_frame.size > fli_header->filesize)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
_("Invalid frame size points past end of file!"));
|
|
return FALSE;
|
|
}
|
|
if (fli_frame.magic == FRAME)
|
|
break;
|
|
fseek (f, framepos + fli_frame.size, SEEK_SET);
|
|
}
|
|
|
|
if (fli_frame.magic == FRAME)
|
|
{
|
|
fseek (f, framepos + 16, SEEK_SET);
|
|
for (c = 0; c < fli_frame.chunks; c++)
|
|
{
|
|
s_fli_chunk chunk;
|
|
gint64 chunkpos;
|
|
gboolean read_ok;
|
|
|
|
chunkpos = ftell (f);
|
|
if (chunkpos < 0 ||
|
|
! fli_read_uint32 (f, &chunk.size, error) ||
|
|
! fli_read_short (f, &chunk.magic, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading frame. "));
|
|
return FALSE;
|
|
}
|
|
g_debug ("Chunk offset: %u, chunk size: %u, chunk type: %u",
|
|
(guint) chunkpos, chunk.size, chunk.magic);
|
|
if (chunkpos + chunk.size > fli_header->filesize)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
_("Invalid chunk size points past end of file!"));
|
|
return FALSE;
|
|
}
|
|
|
|
read_ok = TRUE;
|
|
switch (chunk.magic)
|
|
{
|
|
case FLI_COLOR:
|
|
read_ok = fli_read_color (f, fli_header, old_cmap, cmap, error);
|
|
break;
|
|
case FLI_COLOR_2:
|
|
read_ok = fli_read_color_2 (f, fli_header, old_cmap, cmap, error);
|
|
break;
|
|
case FLI_BLACK:
|
|
read_ok = fli_read_black (f, fli_header, framebuf, error);
|
|
break;
|
|
case FLI_BRUN:
|
|
read_ok = fli_read_brun (f, fli_header, framebuf, error);
|
|
break;
|
|
case FLI_COPY:
|
|
read_ok = fli_read_copy (f, fli_header, framebuf, error);
|
|
break;
|
|
case FLI_LC:
|
|
read_ok = fli_read_lc (f, fli_header, old_framebuf, framebuf, error);
|
|
break;
|
|
case FLI_LC_2:
|
|
read_ok = fli_read_lc_2 (f, fli_header, old_framebuf, framebuf, error);
|
|
break;
|
|
case FLI_MINI:
|
|
/* unused, skip */
|
|
break;
|
|
default:
|
|
/* unknown, skip */
|
|
g_debug ("Unrecognized chunk magic: %u", chunk.magic);
|
|
break;
|
|
}
|
|
if (! read_ok)
|
|
return FALSE;
|
|
|
|
fseek (f, chunkpos + chunk.size, SEEK_SET);
|
|
}
|
|
if (fli_frame.chunks == 0)
|
|
{
|
|
/* Silence a warning: wxh could in theory be more than INT_MAX. */
|
|
memcpy (framebuf, old_framebuf, (gint64) fli_header->width * fli_header->height);
|
|
}
|
|
}
|
|
else /* unknown, skip */
|
|
{
|
|
g_debug ("Unrecognized frame magic: %u (%x)", fli_frame.magic, fli_frame.magic);
|
|
}
|
|
|
|
fseek (f, framepos + fli_frame.size, SEEK_SET);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_frame (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_framebuf,
|
|
guchar *old_cmap,
|
|
guchar *framebuf,
|
|
guchar *cmap,
|
|
gushort codec_mask,
|
|
GError **error)
|
|
{
|
|
s_fli_frame fli_frame;
|
|
guint32 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)
|
|
{
|
|
gboolean more = FALSE;
|
|
|
|
if (fli_write_color (f, fli_header, old_cmap, cmap, &more, error))
|
|
{
|
|
if (more)
|
|
fli_frame.chunks++;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fli_header->magic == HEADER_FLC)
|
|
{
|
|
gboolean more = FALSE;
|
|
|
|
if (fli_write_color_2 (f, fli_header, old_cmap, cmap, &more, error))
|
|
{
|
|
if (more)
|
|
fli_frame.chunks++;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, PIKA_PLUG_IN_ERROR, 0,
|
|
_("Invalid header: magic number is wrong!"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#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)
|
|
{
|
|
if (! fli_write_brun (f, fli_header, framebuf, error))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (! fli_write_lc (f, fli_header, old_framebuf, framebuf, error))
|
|
return FALSE;
|
|
}
|
|
fli_frame.chunks++;
|
|
|
|
frameend = ftell (f);
|
|
fli_frame.size = frameend - framepos;
|
|
fseek (f, framepos, SEEK_SET);
|
|
if (! fli_write_uint32 (f, fli_frame.size, error) ||
|
|
! fli_write_short (f, fli_frame.magic, error) ||
|
|
! fli_write_short (f, fli_frame.chunks, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame header. "));
|
|
return FALSE;
|
|
}
|
|
fseek (f, frameend, SEEK_SET);
|
|
fli_header->frames++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* palette chunks from the classical Autodesk Animator.
|
|
*/
|
|
gboolean
|
|
fli_read_color (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_cmap,
|
|
guchar *cmap,
|
|
GError **error)
|
|
{
|
|
gushort num_packets, cnt_packets, col_pos;
|
|
|
|
col_pos = 0;
|
|
if (! fli_read_short (f, &num_packets, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
for (cnt_packets = num_packets; cnt_packets > 0; cnt_packets--)
|
|
{
|
|
guchar skip_col, num_col, col_cnt;
|
|
|
|
if (! fli_read_char (f, &skip_col, error) ||
|
|
! fli_read_char (f, &num_col, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
if (num_col == 0)
|
|
{
|
|
for (col_pos = 0; col_pos < 768; col_pos++)
|
|
{
|
|
if (! fli_read_char (f, &cmap[col_pos], error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
cmap[col_pos] = cmap[col_pos] << 2;
|
|
}
|
|
return TRUE;
|
|
}
|
|
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--)
|
|
{
|
|
if (! fli_read_char (f, &cmap[col_pos ], error) ||
|
|
! fli_read_char (f, &cmap[col_pos+1], error) ||
|
|
! fli_read_char (f, &cmap[col_pos+2], error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
cmap[col_pos] = cmap[col_pos] << 2; col_pos++;
|
|
cmap[col_pos] = cmap[col_pos] << 2; col_pos++;
|
|
cmap[col_pos] = cmap[col_pos] << 2; col_pos++;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_color (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_cmap,
|
|
guchar *cmap,
|
|
gboolean *more,
|
|
GError **error)
|
|
{
|
|
guint32 chunkpos;
|
|
gushort num_packets;
|
|
s_fli_chunk chunk;
|
|
|
|
*more = FALSE;
|
|
chunkpos = ftell (f);
|
|
fseek (f, chunkpos + 8, SEEK_SET);
|
|
num_packets = 0;
|
|
if (old_cmap == NULL)
|
|
{
|
|
gushort col_pos;
|
|
|
|
num_packets = 1;
|
|
if (! fli_write_char (f, 0, error) || /* skip no color */
|
|
! fli_write_char (f, 0, error)) /* 256 color */
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
|
|
for (col_pos = 0; col_pos < 768; col_pos++)
|
|
{
|
|
if (! fli_write_char (f, cmap[col_pos] >> 2, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gushort 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++;
|
|
|
|
if (! fli_write_char (f, cnt_skip & 255, error) ||
|
|
! fli_write_char (f, cnt_col & 255, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
while (cnt_col > 0)
|
|
{
|
|
if (! fli_write_char (f, cmap[col_start++] >> 2, error) ||
|
|
! fli_write_char (f, cmap[col_start++] >> 2, error) ||
|
|
! fli_write_char (f, cmap[col_start++] >> 2, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
cnt_col--;
|
|
}
|
|
}
|
|
} while (col_pos < 256);
|
|
}
|
|
|
|
if (num_packets > 0)
|
|
{
|
|
chunk.size = ftell (f) - chunkpos;
|
|
chunk.magic = FLI_COLOR;
|
|
|
|
fseek (f, chunkpos, SEEK_SET);
|
|
if (! fli_write_uint32 (f, chunk.size, error) ||
|
|
! fli_write_short (f, chunk.magic, error) ||
|
|
! fli_write_short (f, num_packets, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
|
|
if (chunk.size & 1)
|
|
chunk.size++;
|
|
|
|
fseek (f, chunkpos + chunk.size, SEEK_SET);
|
|
*more = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
fseek (f, chunkpos, SEEK_SET);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* palette chunks from Autodesk Animator pro
|
|
*/
|
|
gboolean
|
|
fli_read_color_2 (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_cmap,
|
|
guchar *cmap,
|
|
GError **error)
|
|
{
|
|
gushort num_packets, cnt_packets, col_pos;
|
|
|
|
if (! fli_read_short (f, &num_packets, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
col_pos = 0;
|
|
for (cnt_packets = num_packets; cnt_packets > 0; cnt_packets--)
|
|
{
|
|
guchar skip_col, num_col, col_cnt;
|
|
|
|
if (! fli_read_char (f, &skip_col, error) ||
|
|
! fli_read_char (f, &num_col, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
if (num_col == 0)
|
|
{
|
|
for (col_pos = 0; col_pos < 768; col_pos++)
|
|
{
|
|
if (! fli_read_char (f, &cmap[col_pos], error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
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--)
|
|
{
|
|
if (! fli_read_char (f, &cmap[col_pos++], error) ||
|
|
! fli_read_char (f, &cmap[col_pos++], error) ||
|
|
! fli_read_char (f, &cmap[col_pos++], error))
|
|
{
|
|
g_prefix_error (error, _("Error reading palette. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_color_2 (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_cmap,
|
|
guchar *cmap,
|
|
gboolean *more,
|
|
GError **error)
|
|
{
|
|
guint32 chunkpos;
|
|
gushort num_packets;
|
|
s_fli_chunk chunk;
|
|
|
|
*more = FALSE;
|
|
chunkpos = ftell (f);
|
|
fseek (f, chunkpos + 8, SEEK_SET);
|
|
num_packets = 0;
|
|
if (old_cmap == NULL)
|
|
{
|
|
gushort col_pos;
|
|
|
|
num_packets = 1;
|
|
if (! fli_write_char (f, 0, error) || /* skip no color */
|
|
! fli_write_char (f, 0, error)) /* 256 color */
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
|
|
for (col_pos = 0; col_pos < 768; col_pos++)
|
|
{
|
|
if (! fli_write_char (f, cmap[col_pos], error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gushort 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++;
|
|
if (! fli_write_char (f, cnt_skip, error) ||
|
|
! fli_write_char (f, cnt_col, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
|
|
while (cnt_col > 0)
|
|
{
|
|
if (! fli_write_char (f, cmap[col_start++], error) ||
|
|
! fli_write_char (f, cmap[col_start++], error) ||
|
|
! fli_write_char (f, cmap[col_start++], error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
|
|
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);
|
|
if (! fli_write_uint32 (f, chunk.size, error) ||
|
|
! fli_write_short (f, chunk.magic, error) ||
|
|
! fli_write_short (f, num_packets, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing color map. "));
|
|
return FALSE;
|
|
}
|
|
|
|
if (chunk.size & 1)
|
|
chunk.size++;
|
|
fseek (f, chunkpos + chunk.size, SEEK_SET);
|
|
*more = TRUE;
|
|
return TRUE;
|
|
}
|
|
fseek (f, chunkpos, SEEK_SET);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* completely black frame
|
|
*/
|
|
gboolean
|
|
fli_read_black (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
memset (framebuf, 0, fli_header->width * fli_header->height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_black (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
s_fli_chunk chunk;
|
|
|
|
chunk.size = 6;
|
|
chunk.magic = FLI_BLACK;
|
|
|
|
if (! fli_write_uint32 (f, chunk.size, error) ||
|
|
! fli_write_short (f, chunk.magic, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing black. "));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Uncompressed frame
|
|
*/
|
|
gboolean
|
|
fli_read_copy (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
if (fread (framebuf, fli_header->width, fli_header->height, f) != fli_header->height)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error reading from file."));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_copy (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
guint32 chunkpos;
|
|
s_fli_chunk chunk;
|
|
|
|
chunkpos = ftell (f);
|
|
fseek (f, chunkpos + 6, SEEK_SET);
|
|
if (fwrite (framebuf, fli_header->width, fli_header->height, f) != fli_header->height)
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
chunk.size = ftell (f) - chunkpos;
|
|
chunk.magic = FLI_COPY;
|
|
|
|
if (chunk.size & 1)
|
|
{
|
|
/* Dummy char to make the chunk end on an even boundary. */
|
|
if (! fli_write_char (f, 0, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
chunk.size++;
|
|
}
|
|
|
|
fseek (f, chunkpos, SEEK_SET);
|
|
if (! fli_write_uint32 (f, chunk.size, error) ||
|
|
! fli_write_short (f, chunk.magic, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
|
|
fseek (f, chunkpos + chunk.size, SEEK_SET);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* This is a RLE algorithm, used for the first image of an animation
|
|
*/
|
|
gboolean
|
|
fli_read_brun (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
gushort yc;
|
|
guchar *pos;
|
|
|
|
for (yc = 0; yc < fli_header->height; yc++)
|
|
{
|
|
guchar pc, pcnt;
|
|
size_t n, xc;
|
|
|
|
if (! fli_read_char (f, &pc, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
xc = 0;
|
|
pos = framebuf + (fli_header->width * yc);
|
|
n = (size_t) fli_header->width * (fli_header->height - yc);
|
|
for (pcnt = pc; pcnt > 0; pcnt--)
|
|
{
|
|
guchar ps;
|
|
|
|
if (! fli_read_char (f, &ps, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
if (ps & 0x80)
|
|
{
|
|
gushort len;
|
|
|
|
for (len = -(signed char) ps; len > 0 && xc < n; len--)
|
|
{
|
|
if (! fli_read_char (f, &pos[xc++], error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (len > 0 && xc >= n)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, 0,
|
|
_("Overflow reading compressed data. Possibly corrupt file."));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
guchar val;
|
|
size_t len;
|
|
|
|
len = MIN (n - xc, ps);
|
|
if (! fli_read_char (f, &val, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
memset (&(pos[xc]), val, len);
|
|
xc+=len;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_brun (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
guint32 chunkpos;
|
|
s_fli_chunk chunk;
|
|
gushort yc;
|
|
guchar *linebuf;
|
|
|
|
chunkpos = ftell (f);
|
|
fseek (f, chunkpos + 6, SEEK_SET);
|
|
|
|
for (yc = 0; yc < fli_header->height; yc++)
|
|
{
|
|
gushort xc, t1, pc, tc;
|
|
guint32 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++;
|
|
if (! fli_write_char (f, (tc - 1)^0xFF, error) ||
|
|
fwrite (linebuf + t1, 1, tc, f) != tc)
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
tc = 0;
|
|
}
|
|
bc++;
|
|
if (! fli_write_char (f, pc, error) ||
|
|
! fli_write_char (f, linebuf[xc], error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
t1 = xc + pc;
|
|
}
|
|
else
|
|
{
|
|
tc+=pc;
|
|
if (tc > 120)
|
|
{
|
|
bc++;
|
|
if (! fli_write_char (f, (tc - 1)^0xFF, error) ||
|
|
fwrite (linebuf + t1, 1, tc, f) != tc)
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
tc = 0;
|
|
t1 = xc + pc;
|
|
}
|
|
}
|
|
xc+=pc;
|
|
}
|
|
if (tc > 0)
|
|
{
|
|
bc++;
|
|
if (! fli_write_char (f, (tc - 1)^0xFF, error) ||
|
|
fwrite (linebuf + t1, 1, tc, f) != tc)
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
tc = 0;
|
|
}
|
|
lineend = ftell (f);
|
|
fseek (f, linepos, SEEK_SET);
|
|
if (! fli_write_char (f, bc, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
fseek (f, lineend, SEEK_SET);
|
|
}
|
|
|
|
chunk.size = ftell (f) - chunkpos;
|
|
chunk.magic = FLI_BRUN;
|
|
|
|
if (chunk.size & 1)
|
|
{
|
|
/* Dummy char to make the chunk end on an even boundary. */
|
|
if (! fli_write_char (f, 0, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
chunk.size++;
|
|
}
|
|
|
|
fseek (f, chunkpos, SEEK_SET);
|
|
if (! fli_write_uint32 (f, chunk.size, error) ||
|
|
! fli_write_short (f, chunk.magic, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
|
|
fseek (f, chunkpos + chunk.size, SEEK_SET);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
gboolean
|
|
fli_read_lc (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_framebuf,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
gushort yc, firstline, numline;
|
|
guchar *pos;
|
|
|
|
memcpy (framebuf, old_framebuf, fli_header->width * fli_header->height);
|
|
|
|
if (! fli_read_short (f, &firstline, error) ||
|
|
! fli_read_short (f, &numline, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
|
|
if (numline > fli_header->height || fli_header->height - numline < firstline)
|
|
return TRUE;
|
|
|
|
for (yc = 0; yc < numline; yc++)
|
|
{
|
|
guchar pc, pcnt;
|
|
size_t n, xc;
|
|
|
|
if (! fli_read_char (f, &pc, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
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--)
|
|
{
|
|
guchar ps, skip;
|
|
|
|
if (! fli_read_char (f, &skip, error) ||
|
|
! fli_read_char (f, &ps, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
|
|
xc += MIN (n - xc, skip);
|
|
if (ps & 0x80)
|
|
{
|
|
guchar val;
|
|
size_t len;
|
|
|
|
ps = -(signed char) ps;
|
|
if (! fli_read_char (f, &val, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
len = MIN (n - xc, ps);
|
|
memset (&(pos[xc]), val, len);
|
|
xc += len;
|
|
}
|
|
else
|
|
{
|
|
size_t len, len_read;
|
|
|
|
len = MIN (n - xc, ps);
|
|
if (len > 0)
|
|
{
|
|
len_read = fread (&pos[xc], len, 1, f);
|
|
if (len_read != 1)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error reading from file."));
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
xc += len;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fli_write_lc (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_framebuf,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
guint32 chunkpos;
|
|
s_fli_chunk chunk;
|
|
gushort yc, firstline, numline, lastline;
|
|
guchar *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;
|
|
|
|
if (! fli_write_short (f, firstline, error) ||
|
|
! fli_write_short (f, numline, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
|
|
for (yc = 0; yc < numline; yc++)
|
|
{
|
|
gushort xc, sc, cc, tc;
|
|
guint32 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++;
|
|
}
|
|
if (! fli_write_char (f, sc, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
cc = 1;
|
|
while ((linebuf[xc] == linebuf[xc + cc]) &&
|
|
((xc + cc)<fli_header->width) &&
|
|
(cc < 120))
|
|
{
|
|
cc++;
|
|
}
|
|
if (cc > 2)
|
|
{
|
|
bc++;
|
|
if (! fli_write_char (f, (cc - 1)^0xFF, error) ||
|
|
! fli_write_char (f, linebuf[xc], error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
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++;
|
|
if (! fli_write_char (f, tc, error) ||
|
|
fwrite (linebuf + xc, tc, 1, f) != 1)
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
xc += tc;
|
|
}
|
|
}
|
|
lineend = ftell (f);
|
|
fseek (f, linepos, SEEK_SET);
|
|
if (! fli_write_char (f, bc, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
fseek (f, lineend, SEEK_SET);
|
|
}
|
|
|
|
chunk.size = ftell (f) - chunkpos;
|
|
chunk.magic = FLI_LC;
|
|
|
|
if (chunk.size & 1)
|
|
{
|
|
/* Dummy char to make the chunk end on an even boundary. */
|
|
if (! fli_write_char (f, 0, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
chunk.size++;
|
|
}
|
|
|
|
fseek (f, chunkpos, SEEK_SET);
|
|
if (! fli_write_uint32 (f, chunk.size, error) ||
|
|
! fli_write_short (f, chunk.magic, error))
|
|
{
|
|
g_prefix_error (error, _("Error writing frame. "));
|
|
return FALSE;
|
|
}
|
|
|
|
fseek (f, chunkpos + chunk.size, SEEK_SET);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
gboolean
|
|
fli_read_lc_2 (FILE *f,
|
|
s_fli_header *fli_header,
|
|
guchar *old_framebuf,
|
|
guchar *framebuf,
|
|
GError **error)
|
|
{
|
|
gushort yc, lc, numline;
|
|
guchar *pos;
|
|
guint32 len_read;
|
|
|
|
memcpy (framebuf, old_framebuf, fli_header->width * fli_header->height);
|
|
yc = 0;
|
|
|
|
if (! fli_read_short (f, &numline, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
if (numline > fli_header->height)
|
|
{
|
|
g_warning ("Number of lines %u larger than frame height %u.", numline, fli_header->height);
|
|
numline = fli_header->height;
|
|
}
|
|
|
|
for (lc = 0; lc < numline; lc++)
|
|
{
|
|
gushort pc, pcnt, lpf, lpn;
|
|
size_t n, xc;
|
|
|
|
if (! fli_read_short (f, &pc, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
|
|
lpf = 0; lpn = 0;
|
|
while (pc & 0x8000)
|
|
{
|
|
if (pc & 0x4000)
|
|
{
|
|
yc += -(signed short) pc;
|
|
}
|
|
else
|
|
{
|
|
lpf = 1;
|
|
lpn = pc & 0xFF;
|
|
}
|
|
|
|
if (! fli_read_short (f, &pc, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
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--)
|
|
{
|
|
guchar ps, skip;
|
|
|
|
if (! fli_read_char (f, &skip, error) ||
|
|
! fli_read_char (f, &ps, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
|
|
xc += MIN (n - xc, skip);
|
|
if (ps & 0x80)
|
|
{
|
|
guchar v1, v2;
|
|
|
|
ps = -(signed char) ps;
|
|
if (! fli_read_char (f, &v1, error) ||
|
|
! fli_read_char (f, &v2, error))
|
|
{
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
while (ps > 0 && xc + 1 < n)
|
|
{
|
|
pos[xc++] = v1;
|
|
pos[xc++] = v2;
|
|
ps--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t len;
|
|
|
|
len = MIN ((n - xc)/2, ps);
|
|
if (len > 0)
|
|
{
|
|
len_read = fread (&pos[xc], len, 2, f);
|
|
if (len_read != 2)
|
|
{
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Error reading from file."));
|
|
g_prefix_error (error, _("Error reading compressed data. "));
|
|
return FALSE;
|
|
}
|
|
}
|
|
xc += len << 1;
|
|
}
|
|
}
|
|
if (lpf)
|
|
pos[xc] = lpn;
|
|
yc++;
|
|
}
|
|
return TRUE;
|
|
}
|