app: port ABR brush loading to GIO

This commit is contained in:
Michael Natterer 2014-07-04 02:18:52 +02:00
parent 16ca9a4dcf
commit e7d9e01d63
1 changed files with 322 additions and 181 deletions

View File

@ -19,10 +19,7 @@
#include "config.h"
#include <errno.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glib/gstdio.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
@ -70,38 +67,44 @@ struct _AbrSampledBrushHeader
/* local function prototypes */
static GList * gimp_brush_load_abr_v12 (FILE *f,
AbrHeader *abr_hdr,
GFile *file,
GError **error);
static GList * gimp_brush_load_abr_v6 (FILE *f,
AbrHeader *abr_hdr,
GFile *file,
GError **error);
static GimpBrush * gimp_brush_load_abr_brush_v12 (FILE *f,
AbrHeader *abr_hdr,
gint index,
GFile *file,
GError **error);
static GimpBrush * gimp_brush_load_abr_brush_v6 (FILE *f,
AbrHeader *abr_hdr,
gint32 max_offset,
gint index,
GFile *file,
GError **error);
static GList * gimp_brush_load_abr_v12 (GDataInputStream *input,
AbrHeader *abr_hdr,
GFile *file,
GError **error);
static GList * gimp_brush_load_abr_v6 (GDataInputStream *input,
AbrHeader *abr_hdr,
GFile *file,
GError **error);
static GimpBrush * gimp_brush_load_abr_brush_v12 (GDataInputStream *input,
AbrHeader *abr_hdr,
gint index,
GFile *file,
GError **error);
static GimpBrush * gimp_brush_load_abr_brush_v6 (GDataInputStream *input,
AbrHeader *abr_hdr,
gint32 max_offset,
gint index,
GFile *file,
GError **error);
static gchar abr_read_char (FILE *f);
static gint16 abr_read_short (FILE *f);
static gint32 abr_read_long (FILE *f);
static gchar * abr_read_ucs2_text (FILE *f);
static gboolean abr_supported (AbrHeader *abr_hdr,
GFile *f,
GError **error);
static gboolean abr_reach_8bim_section (FILE *abr,
const gchar *name);
static gint32 abr_rle_decode (FILE *f,
gchar *buffer,
gint32 height);
static gchar abr_read_char (GDataInputStream *input,
GError **error);
static gint16 abr_read_short (GDataInputStream *input,
GError **error);
static gint32 abr_read_long (GDataInputStream *input,
GError **error);
static gchar * abr_read_ucs2_text (GDataInputStream *input,
GError **error);
static gboolean abr_supported (AbrHeader *abr_hdr,
GFile *f,
GError **error);
static gboolean abr_reach_8bim_section (GDataInputStream *input,
const gchar *name,
GError **error);
static gboolean abr_rle_decode (GDataInputStream *input,
gchar *buffer,
gint32 height,
GError **error);
/* public functions */
@ -428,55 +431,74 @@ gimp_brush_load_abr (GimpContext *context,
GFile *file,
GError **error)
{
gchar *path;
FILE *f;
AbrHeader abr_hdr;
GList *brush_list = NULL;
GInputStream *input;
GDataInputStream *data_input;
AbrHeader abr_hdr;
GList *brush_list = NULL;
GError *my_error = NULL;
g_return_val_if_fail (G_IS_FILE (file), NULL);
path = g_file_get_path (file);
g_return_val_if_fail (g_path_is_absolute (path), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
f = g_fopen (path, "rb");
g_free (path);
if (! f)
input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error));
if (! input)
{
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
_("Could not open '%s' for reading: %s"),
gimp_file_get_utf8_name (file), g_strerror (errno));
gimp_file_get_utf8_name (file), my_error->message);
g_clear_error (&my_error);
return NULL;
}
abr_hdr.version = abr_read_short (f);
abr_hdr.count = abr_read_short (f); /* sub-version for ABR v6 */
data_input = g_data_input_stream_new (input);
g_object_unref (input);
if (abr_supported (&abr_hdr, file, error))
g_data_input_stream_set_byte_order (data_input,
G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
abr_hdr.version = abr_read_short (data_input, &my_error);
if (my_error)
goto done;
/* sub-version for ABR v6 */
abr_hdr.count = abr_read_short (data_input, &my_error);
if (my_error)
goto done;
if (abr_supported (&abr_hdr, file, &my_error))
{
switch (abr_hdr.version)
{
case 1:
case 2:
brush_list = gimp_brush_load_abr_v12 (f, &abr_hdr,
file, error);
brush_list = gimp_brush_load_abr_v12 (data_input, &abr_hdr,
file, &my_error);
break;
case 6:
brush_list = gimp_brush_load_abr_v6 (f, &abr_hdr,
file, error);
brush_list = gimp_brush_load_abr_v6 (data_input, &abr_hdr,
file, &my_error);
break;
}
}
fclose (f);
done:
if (! brush_list && (error && ! *error))
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"unable to decode abr format version %d."),
gimp_file_get_utf8_name (file), abr_hdr.version);
g_object_unref (data_input);
if (! brush_list)
{
if (! my_error)
g_set_error (&my_error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Unable to decode abr format version %d."),
abr_hdr.version);
g_set_error (error, my_error->domain, my_error->code,
_("Fatal parse error in brush file '%s': %s"),
gimp_file_get_utf8_name (file), my_error->message);
}
g_clear_error (&my_error);
return g_list_reverse (brush_list);
}
@ -485,10 +507,10 @@ gimp_brush_load_abr (GimpContext *context,
/* private functions */
static GList *
gimp_brush_load_abr_v12 (FILE *f,
AbrHeader *abr_hdr,
GFile *file,
GError **error)
gimp_brush_load_abr_v12 (GDataInputStream *input,
AbrHeader *abr_hdr,
GFile *file,
GError **error)
{
GList *brush_list = NULL;
gint i;
@ -498,7 +520,7 @@ gimp_brush_load_abr_v12 (FILE *f,
GimpBrush *brush;
GError *my_error = NULL;
brush = gimp_brush_load_abr_brush_v12 (f, abr_hdr, i,
brush = gimp_brush_load_abr_brush_v12 (input, abr_hdr, i,
file, &my_error);
/* a NULL brush without an error means an unsupported brush
@ -520,28 +542,32 @@ gimp_brush_load_abr_v12 (FILE *f,
}
static GList *
gimp_brush_load_abr_v6 (FILE *f,
AbrHeader *abr_hdr,
GFile *file,
GError **error)
gimp_brush_load_abr_v6 (GDataInputStream *input,
AbrHeader *abr_hdr,
GFile *file,
GError **error)
{
GList *brush_list = NULL;
gint32 sample_section_size;
gint32 sample_section_end;
gint i = 1;
GList *brush_list = NULL;
gint32 sample_section_size;
goffset sample_section_end;
gint i = 1;
if (! abr_reach_8bim_section (f, "samp"))
if (! abr_reach_8bim_section (input, "samp", error))
return brush_list;
sample_section_size = abr_read_long (f);
sample_section_end = sample_section_size + ftell (f);
sample_section_size = abr_read_long (input, error);
if (error && *error)
return brush_list;
while (ftell (f) < sample_section_end)
sample_section_end = (sample_section_size +
g_seekable_tell (G_SEEKABLE (input)));
while (g_seekable_tell (G_SEEKABLE (input)) < sample_section_end)
{
GimpBrush *brush;
GError *my_error = NULL;
brush = gimp_brush_load_abr_brush_v6 (f, abr_hdr, sample_section_end,
brush = gimp_brush_load_abr_brush_v6 (input, abr_hdr, sample_section_end,
i, file, &my_error);
/* a NULL brush without an error means an unsupported brush
@ -565,17 +591,22 @@ gimp_brush_load_abr_v6 (FILE *f,
}
static GimpBrush *
gimp_brush_load_abr_brush_v12 (FILE *f,
AbrHeader *abr_hdr,
gint index,
GFile *file,
GError **error)
gimp_brush_load_abr_brush_v12 (GDataInputStream *input,
AbrHeader *abr_hdr,
gint index,
GFile *file,
GError **error)
{
GimpBrush *brush = NULL;
AbrBrushHeader abr_brush_hdr;
abr_brush_hdr.type = abr_read_short (f);
abr_brush_hdr.size = abr_read_long (f);
abr_brush_hdr.type = abr_read_short (input, error);
if (error && *error)
return NULL;
abr_brush_hdr.size = abr_read_long (input, error);
if (error && *error)
return NULL;
/* g_print(" + BRUSH\n | << type: %i block size: %i bytes\n",
* abr_brush_hdr.type, abr_brush_hdr.size);
@ -591,7 +622,8 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
* types -akl
*/
g_printerr ("WARNING: computed brush unsupported, skipping.\n");
fseek (f, abr_brush_hdr.size, SEEK_CUR);
g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
G_SEEK_CUR, NULL, NULL);
break;
case 2: /* sampled brush */
@ -607,21 +639,42 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
gchar *tmp;
gshort compress;
abr_sampled_brush_hdr.misc = abr_read_long (f);
abr_sampled_brush_hdr.spacing = abr_read_short (f);
abr_sampled_brush_hdr.misc = abr_read_long (input, error);
if (error && *error)
break;
abr_sampled_brush_hdr.spacing = abr_read_short (input, error);
if (error && *error)
break;
if (abr_hdr->version == 2)
sample_name = abr_read_ucs2_text (f);
{
sample_name = abr_read_ucs2_text (input, error);
if (error && *error)
break;
}
abr_sampled_brush_hdr.antialiasing = abr_read_char (f);
abr_sampled_brush_hdr.antialiasing = abr_read_char (input, error);
if (error && *error)
break;
for (i = 0; i < 4; i++)
abr_sampled_brush_hdr.bounds[i] = abr_read_short (f);
{
abr_sampled_brush_hdr.bounds[i] = abr_read_short (input, error);
if (error && *error)
break;
}
for (i = 0; i < 4; i++)
abr_sampled_brush_hdr.bounds_long[i] = abr_read_long (f);
{
abr_sampled_brush_hdr.bounds_long[i] = abr_read_long (input, error);
if (error && *error)
break;
}
abr_sampled_brush_hdr.depth = abr_read_short (f);
abr_sampled_brush_hdr.depth = abr_read_short (input, error);
if (error && *error)
break;
height = (abr_sampled_brush_hdr.bounds_long[2] -
abr_sampled_brush_hdr.bounds_long[0]); /* bottom - top */
@ -641,7 +694,7 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
_("Fatal parse error in brush file '%s': "
"Wide brushes are not supported."),
gimp_file_get_utf8_name (file));
return NULL;
break;
}
tmp = g_path_get_basename (gimp_file_get_utf8_name (file));
@ -677,7 +730,13 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
mask = gimp_temp_buf_get_data (brush->mask);
size = width * height * bytes;
compress = abr_read_char (f);
compress = abr_read_char (input, error);
if (error && *error)
{
g_object_unref (brush);
brush = NULL;
break;
}
/* g_print(" | << size: %dx%d %d bit (%d bytes) %s\n",
* width, height, abr_sampled_brush_hdr.depth, size,
@ -685,15 +744,35 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
*/
if (! compress)
fread (mask, size, 1, f);
{
gsize bytes_read;
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
mask, size,
&bytes_read, NULL, error) ||
bytes_read != size)
{
g_object_unref (brush);
brush = NULL;
break;
}
}
else
abr_rle_decode (f, (gchar *) mask, height);
{
if (! abr_rle_decode (input, (gchar *) mask, height, error))
{
g_object_unref (brush);
brush = NULL;
break;
}
}
}
break;
default:
g_printerr ("WARNING: unknown brush type, skipping.\n");
fseek (f, abr_brush_hdr.size, SEEK_CUR);
g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
G_SEEK_CUR, NULL, NULL);
break;
}
@ -701,19 +780,19 @@ gimp_brush_load_abr_brush_v12 (FILE *f,
}
static GimpBrush *
gimp_brush_load_abr_brush_v6 (FILE *f,
AbrHeader *abr_hdr,
gint32 max_offset,
gint index,
GFile *file,
GError **error)
gimp_brush_load_abr_brush_v6 (GDataInputStream *input,
AbrHeader *abr_hdr,
gint32 max_offset,
gint index,
GFile *file,
GError **error)
{
GimpBrush *brush = NULL;
guchar *mask;
gint32 brush_size;
gint32 brush_end;
gint32 next_brush;
goffset next_brush;
gint32 top, left, bottom, right;
gint16 depth;
@ -724,39 +803,48 @@ gimp_brush_load_abr_brush_v6 (FILE *f,
gchar *tmp;
gchar *name;
gint r;
gboolean r;
brush_size = abr_read_long (input, error);
if (error && *error)
return NULL;
brush_size = abr_read_long (f);
brush_end = brush_size;
/* complement to 4 */
while (brush_end % 4 != 0)
brush_end++;
next_brush = ftell (f) + brush_end;
next_brush = (brush_end + g_seekable_tell (G_SEEKABLE (input)));
if (abr_hdr->count == 1)
/* discard key and short coordinates and unknown short */
r = fseek (f, 47, SEEK_CUR);
else
/* discard key and unknown bytes */
r = fseek (f, 301, SEEK_CUR);
if (r == -1)
{
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"File appears truncated."),
gimp_file_get_utf8_name (file));
/* discard key and short coordinates and unknown short */
r = g_seekable_seek (G_SEEKABLE (input), 47, G_SEEK_CUR,
NULL, error);
}
else
{
/* discard key and unknown bytes */
r = g_seekable_seek (G_SEEKABLE (input), 301, G_SEEK_CUR,
NULL, error);
}
if (! r)
{
g_prefix_error (error,
_("Fatal parse error in brush file '%s': "
"File appears truncated: "),
gimp_file_get_utf8_name (file));
return NULL;
}
top = abr_read_long (f);
left = abr_read_long (f);
bottom = abr_read_long (f);
right = abr_read_long (f);
depth = abr_read_short (f);
compress = abr_read_char (f);
top = abr_read_long (input, error); if (error && *error) return NULL;
left = abr_read_long (input, error); if (error && *error) return NULL;
bottom = abr_read_long (input, error); if (error && *error) return NULL;
right = abr_read_long (input, error); if (error && *error) return NULL;
depth = abr_read_short (input, error); if (error && *error) return NULL;
compress = abr_read_char (input, error); if (error && *error) return NULL;
width = right - left;
height = bottom - top;
@ -786,44 +874,58 @@ gimp_brush_load_abr_brush_v6 (FILE *f,
/* data decoding */
if (! compress)
/* not compressed - read raw bytes as brush data */
fread (mask, size, 1, f);
else
abr_rle_decode (f, (gchar *) mask, height);
{
/* not compressed - read raw bytes as brush data */
gsize bytes_read;
fseek (f, next_brush, SEEK_SET);
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
mask, size,
&bytes_read, NULL, error) ||
bytes_read != size)
{
g_object_unref (brush);
return NULL;
}
}
else
{
if (! abr_rle_decode (input, (gchar *) mask, height, error))
{
g_object_unref (brush);
return NULL;
}
}
g_seekable_seek (G_SEEKABLE (input), next_brush, G_SEEK_SET,
NULL, NULL);
return brush;
}
static gchar
abr_read_char (FILE *f)
abr_read_char (GDataInputStream *input,
GError **error)
{
return fgetc (f);
return g_data_input_stream_read_byte (input, NULL, error);
}
static gint16
abr_read_short (FILE *f)
abr_read_short (GDataInputStream *input,
GError **error)
{
gint16 val;
fread (&val, sizeof (val), 1, f);
return GINT16_FROM_BE (val);
return g_data_input_stream_read_int16 (input, NULL, error);
}
static gint32
abr_read_long (FILE *f)
abr_read_long (GDataInputStream *input,
GError **error)
{
gint32 val;
fread (&val, sizeof (val), 1, f);
return GINT32_FROM_BE (val);
return g_data_input_stream_read_int32 (input, NULL, error);
}
static gchar *
abr_read_ucs2_text (FILE *f)
abr_read_ucs2_text (GDataInputStream *input,
GError **error)
{
gchar *name_ucs2;
gchar *name_utf8;
@ -836,14 +938,21 @@ abr_read_ucs2_text (FILE *f)
* data : zero terminated UCS-2 string
*/
len = 2 * abr_read_long (f);
len = 2 * abr_read_long (input, error);
if (len <= 0)
return NULL;
name_ucs2 = g_new (gchar, len);
for (i = 0; i < len; i++)
name_ucs2[i] = abr_read_char (f);
{
name_ucs2[i] = abr_read_char (input, error);
if (error && *error)
{
g_free (name_ucs2);
return NULL;
}
}
name_utf8 = g_convert (name_ucs2, len,
"UTF-8", "UCS-2BE",
@ -871,17 +980,16 @@ abr_supported (AbrHeader *abr_hdr,
if (abr_hdr->count == 1 || abr_hdr->count == 2)
return TRUE;
if (error && ! (*error))
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"unable to decode abr format version %d."),
gimp_file_get_utf8_name (file),
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"unable to decode abr format version %d."),
gimp_file_get_utf8_name (file),
/* horrid subversion display, but better than
* having yet another translatable string for
* this
*/
abr_hdr->version * 10 + abr_hdr->count);
/* horrid subversion display, but better than
* having yet another translatable string for
* this
*/
abr_hdr->version * 10 + abr_hdr->count);
break;
}
@ -889,25 +997,30 @@ abr_supported (AbrHeader *abr_hdr,
}
static gboolean
abr_reach_8bim_section (FILE *abr,
const gchar *name)
abr_reach_8bim_section (GDataInputStream *input,
const gchar *name,
GError **error)
{
gchar tag[4];
gchar tagname[5];
gint32 section_size;
gint r;
while (! feof (abr))
while (TRUE)
{
r = fread (&tag, 1, 4, abr);
if (r != 4)
gchar tag[4];
gchar tagname[5];
gint32 section_size;
gsize bytes_read;
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
tag, 4,
&bytes_read, NULL, error) ||
bytes_read != 4)
return FALSE;
if (strncmp (tag, "8BIM", 4))
return FALSE;
r = fread (&tagname, 1, 4, abr);
if (r != 4)
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
tagname, 4,
&bytes_read, NULL, error) ||
bytes_read != 4)
return FALSE;
tagname[4] = '\0';
@ -915,19 +1028,23 @@ abr_reach_8bim_section (FILE *abr,
if (! strncmp (tagname, name, 4))
return TRUE;
section_size = abr_read_long (abr);
r = fseek (abr, section_size, SEEK_CUR);
if (r == -1)
section_size = abr_read_long (input, error);
if (error && *error)
return FALSE;
if (! g_seekable_seek (G_SEEKABLE (input), section_size, G_SEEK_CUR,
NULL, error))
return FALSE;
}
return FALSE;
}
static gint32
abr_rle_decode (FILE *f,
gchar *buffer,
gint32 height)
static gboolean
abr_rle_decode (GDataInputStream *input,
gchar *buffer,
gint32 height,
GError **error)
{
gchar ch;
gint i, j, c;
@ -937,14 +1054,26 @@ abr_rle_decode (FILE *f,
/* read compressed size foreach scanline */
cscanline_len = g_new0 (gshort, height);
for (i = 0; i < height; i++)
cscanline_len[i] = abr_read_short (f);
{
cscanline_len[i] = abr_read_short (input, error);
if (error && *error)
{
g_free (cscanline_len);
return FALSE;
}
}
/* unpack each scanline data */
for (i = 0; i < height; i++)
{
for (j = 0; j < cscanline_len[i];)
{
gint32 n = abr_read_char (f);
gint32 n = abr_read_char (input, error);
if (error && *error)
{
g_free (cscanline_len);
return FALSE;
}
j++;
@ -959,7 +1088,12 @@ abr_rle_decode (FILE *f,
continue;
n = -n + 1;
ch = abr_read_char (f);
ch = abr_read_char (input, error);
if (error && *error)
{
g_free (cscanline_len);
return FALSE;
}
j++;
for (c = 0; c < n; c++, data++)
@ -970,12 +1104,19 @@ abr_rle_decode (FILE *f,
/* read the following n + 1 chars (no compr) */
for (c = 0; c < n + 1; c++, j++, data++)
*data = abr_read_char (f);
{
*data = abr_read_char (input, error);
if (error && *error)
{
g_free (cscanline_len);
return FALSE;
}
}
}
}
}
g_free (cscanline_len);
return 0;
return TRUE;
}