syndilights/blib-1.1.7/blib/bmovie-blm.c

366 lines
10 KiB
C

/* blib - Library of useful things to hack the Blinkenlights
*
* Copyright (c) 2001-2002 The Blinkenlights Crew
* Sven Neumann <sven@gimp.org>
* Michael Natterer <mitch@gimp.org>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <glib-object.h>
#include "btypes.h"
#include "bmovie-blm.h"
static void b_movie_blm_class_init (BMovieBLMClass *class);
static void b_movie_blm_init (BMovie *movie);
static gboolean b_movie_blm_load_info (BMovie *movie,
GIOChannel *io,
GError **error);
static gboolean b_movie_blm_load_all (BMovie *movie,
GIOChannel *io,
GError **error);
static gboolean b_movie_blm_save (BMovie *movie,
FILE *stream,
GError **error);
static gboolean b_movie_blm_parse_header_line (BMovie *movie,
const gchar *buf,
gsize len,
gboolean *magic);
static void b_movie_blm_save_header (FILE *stream,
const gchar *name,
const gchar *value);
static BMovieClass *parent_class = NULL;
GType
b_movie_blm_get_type (void)
{
static GType movie_type = 0;
if (!movie_type)
{
static const GTypeInfo movie_info =
{
sizeof (BMovieBLMClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) b_movie_blm_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (BMovieBLM),
0, /* n_preallocs */
(GInstanceInitFunc) b_movie_blm_init
};
movie_type = g_type_register_static (B_TYPE_MOVIE,
"BMovieBLM",
&movie_info, 0);
}
return movie_type;
}
static void
b_movie_blm_class_init (BMovieBLMClass *class)
{
BMovieClass *movie_class;
parent_class = g_type_class_peek_parent (class);
movie_class = B_MOVIE_CLASS (class);
movie_class->load_info = b_movie_blm_load_info;
movie_class->load_all = b_movie_blm_load_all;
movie_class->save = b_movie_blm_save;
}
static void
b_movie_blm_init (BMovie *movie)
{
movie->maxval = 1;
}
static gboolean
b_movie_blm_load_info (BMovie *movie,
GIOChannel *io,
GError **error)
{
GString *str = g_string_new_len (NULL, 128);
gboolean magic = FALSE;
while (g_io_channel_read_line_string (io, str, NULL,
error) == G_IO_STATUS_NORMAL)
{
if (! b_movie_blm_parse_header_line (movie, str->str, str->len, &magic))
{
if (!magic)
g_set_error (error, 0, 0, "Error parsing movie header.");
break;
}
}
g_string_free (str, TRUE);
return magic;
}
static gboolean
b_movie_blm_load_all (BMovie *movie,
GIOChannel *io,
GError **error)
{
GString *str = g_string_new_len (NULL, 128);
guchar *data = NULL;
gboolean header_parsed = FALSE;
gboolean magic = FALSE;
gint line = -1;
gint duration;
while (g_io_channel_read_line_string (io, str, NULL,
error) == G_IO_STATUS_NORMAL)
{
if (!header_parsed)
{
if (! b_movie_blm_parse_header_line (movie,
str->str, str->len, &magic))
{
if (magic)
header_parsed = TRUE;
else
break;
movie->duration = 0;
}
}
if (header_parsed)
{
const gchar *buf = str->str;
gint len = str->len;
if (!data)
data = g_malloc (movie->width * movie->height);
/* skip empty lines and bufs */
if (len == 0 || buf[0] == '#' || g_ascii_isspace (buf[0]))
continue;
if (line == -1)
{
if (buf[0] == '@')
{
if (sscanf (buf+1, "%d", &duration) == 1 && duration > 0)
line = 0;
if (duration < B_MOVIE_MIN_DELAY)
{
g_printerr ("Frame with %d ms duration, "
"using %d ms instead\n",
duration, B_MOVIE_MIN_DELAY);
duration = B_MOVIE_MIN_DELAY;
}
}
}
else
{
if (buf[0] == '@' || len - 1 < movie->width)
{
g_printerr ("Invalid frame, skipping.\n");
line = -1;
}
else
{
gint i;
for (i = 0; i < movie->width; i++)
{
gint d = (buf[i] == '1' ? 1 : 0);
data[movie->width * line + i] = d * movie->maxval;
}
if (++line == movie->height)
{
b_movie_prepend_frame (movie, duration, data);
line = -1;
}
}
}
}
}
g_free (data);
g_string_free (str, TRUE);
/* caller checks movie->n_frames and sets an error if no frames were found */
return TRUE;
}
static gboolean
b_movie_blm_save (BMovie *movie,
FILE *stream,
GError **error)
{
GList *list;
if (movie->channels != 1)
{
g_set_error (error, 0, 0,
"Cannot save movie with more than one channel as BLM");
return FALSE;
}
fprintf (stream,
"# Blinkenlights Movie %dx%d\n", movie->width, movie->height);
b_movie_blm_save_header (stream, "name", movie->title);
b_movie_blm_save_header (stream, "description", movie->description);
b_movie_blm_save_header (stream, "creator", movie->creator);
b_movie_blm_save_header (stream, "creator", "blib " VERSION);
b_movie_blm_save_header (stream, "author", movie->author);
b_movie_blm_save_header (stream, "email", movie->email);
b_movie_blm_save_header (stream, "url", movie->url);
fprintf (stream, "# duration = %d\n", movie->duration);
for (list = movie->frames; list; list = list->next)
{
BMovieFrame *frame = list->data;
const guchar *data = frame->data;
gint x, y;
fprintf (stream, "\n@%d\n", frame->duration);
for (y = 0; y < movie->height; y++)
{
for (x = 0; x < movie->width; x++, data++)
fputc ((*data > movie->maxval / 2) ? '1' : '0', stream);
fputc ('\n', stream);
}
}
return TRUE;
}
static gboolean
b_movie_blm_parse_header_line (BMovie *movie,
const gchar *buf,
gsize len,
gboolean *magic)
{
const gchar *comment;
if (!len)
return FALSE;
if (! *magic)
{
gint width;
gint height;
if (sscanf (buf, " %dx%d", &width, &height) != 2 ||
width < 1 || height < 1)
{
return FALSE;
}
movie->width = width;
movie->height = height;
*magic = TRUE;
return TRUE;
}
if (buf[0] != '#')
return FALSE;
comment = buf + 1;
len--;
while (len && g_ascii_isspace (*comment))
{
comment++;
len--;
}
if (!g_utf8_validate (comment, -1, NULL))
return TRUE; /* ignore silently */
if (!movie->title)
{
if (g_ascii_strncasecmp (comment, "name=", 5) == 0)
movie->title = g_strstrip (g_strndup (comment + 5, len - 6));
else if (g_ascii_strncasecmp (comment, "name =", 6) == 0)
movie->title = g_strstrip (g_strndup (comment + 6, len - 7));
}
if (!movie->description)
{
if (g_ascii_strncasecmp (comment, "description=", 12) == 0)
movie->description = g_strstrip (g_strndup (comment + 12, len - 13));
else if (g_ascii_strncasecmp (comment, "description =", 13) == 0)
movie->description = g_strstrip (g_strndup (comment + 13, len - 14));
}
if (!movie->duration)
{
if (g_ascii_strncasecmp (comment, "duration=", 9) == 0 ||
g_ascii_strncasecmp (comment, "duration =", 10) == 0)
{
sscanf (comment + 9, "%d", &movie->duration);
}
}
return TRUE;
}
static void
b_movie_blm_save_header (FILE *stream,
const gchar *name,
const gchar *value)
{
const gchar *end;
if (!value || !*value)
return;
fprintf (stream, "# %s = ", name);
end = strchr (value, '\n');
if (!end)
end = value + MIN (strlen (value), 70 - strlen (name));
if (*end)
{
gchar *dup = g_strndup (value, end - value);
fprintf (stream, dup);
g_free (dup);
}
else
{
fprintf (stream, value);
}
fputc ('\n', stream);
}