[gimp] plug-ins: improve psp image reader stability by always using the block/chunk length.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] plug-ins: improve psp image reader stability by always using the block/chunk length.
- Date: Wed, 19 Aug 2020 09:21:52 +0000 (UTC)
commit 15ad9522880dc540f3c0ba46234b96a20487a08c
Author: Jacob Boerema <jgboerema gmail com>
Date: Tue Aug 18 15:59:58 2020 -0400
plug-ins: improve psp image reader stability by always using the block/chunk length.
Starting from psp file version 4 the specification recommends to always use the
block/chunk length to determine the next part of the image. This way it is
possible to skip parts you don't know or don't care about or additions in
newer versions.
This change makes sure to always do this which fixes reading several images
which crashed the plug-in before.
Also only try to read layer data if it is a raster layer.
plug-ins/common/file-psp.c | 329 +++++++++++++++++++++++++++------------------
1 file changed, 195 insertions(+), 134 deletions(-)
---
diff --git a/plug-ins/common/file-psp.c b/plug-ins/common/file-psp.c
index ab84243000..4c764aeec3 100644
--- a/plug-ins/common/file-psp.c
+++ b/plug-ins/common/file-psp.c
@@ -908,16 +908,34 @@ read_block_header (FILE *f,
return GUINT16_FROM_LE (id);
}
+static gint
+try_fseek (FILE *f,
+ glong pos,
+ gint whence,
+ GError **error)
+{
+ if (fseek (f, pos, whence) < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Seek error: %s"), g_strerror (errno));
+ fclose (f);
+ return -1;
+ }
+ return 0;
+}
+
/* Read the PSP_IMAGE_BLOCK */
static gint
read_general_image_attribute_block (FILE *f,
guint init_len,
guint total_len,
- PSPimage *ia)
+ PSPimage *ia,
+ GError **error)
{
gchar buf[6];
guint64 res;
gchar graphics_content[4];
+ long chunk_start;
if (init_len < 38 || total_len < 38)
{
@@ -925,17 +943,10 @@ read_general_image_attribute_block (FILE *f,
return -1;
}
- if (psp_ver_major >= 4)
- {
- /* TODO: This causes the chunk size to be ignored. Better verify if it is
- * valid since it might create read offset problems with the
- * "expansion field" (which follows after the "graphics content" and
- * is of unknown size).
- */
- fseek (f, 4, SEEK_CUR);
- }
-
- if (fread (&ia->width, 4, 1, f) < 1
+ chunk_start = ftell (f);
+ if ((psp_ver_major >= 4
+ && (fread (&init_len, 4, 1, f) < 1 || (init_len = GUINT32_FROM_LE (init_len) < 42)))
+ || fread (&ia->width, 4, 1, f) < 1
|| fread (&ia->height, 4, 1, f) < 1
|| fread (&res, 8, 1, f) < 1
|| fread (&ia->metric, 1, 1, f) < 1
@@ -946,7 +957,8 @@ read_general_image_attribute_block (FILE *f,
|| fread (buf, 4, 1, f) < 1 /* Skip total image size */
|| fread (&ia->active_layer, 4, 1, f) < 1
|| fread (&ia->layer_count, 2, 1, f) < 1
- || (psp_ver_major >= 4 && fread (graphics_content, 4, 1, f) < 1))
+ || (psp_ver_major >= 4 && fread (graphics_content, 4, 1, f) < 1)
+ || try_fseek (f, chunk_start + init_len, SEEK_SET, error) < 0)
{
g_message ("Error reading general image attribute block");
return -1;
@@ -979,22 +991,6 @@ read_general_image_attribute_block (FILE *f,
return 0;
}
-static gint
-try_fseek (FILE *f,
- glong pos,
- gint whence,
- GError **error)
-{
- if (fseek (f, pos, whence) < 0)
- {
- g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
- _("Seek error: %s"), g_strerror (errno));
- fclose (f);
- return -1;
- }
- return 0;
-}
-
static gint
read_creator_block (FILE *f,
GimpImage *image,
@@ -1465,14 +1461,16 @@ read_layer_block (FILE *f,
{
gint i;
long block_start, sub_block_start, channel_start;
+ long layer_extension_start;
gint sub_id;
guint32 sub_init_len, sub_total_len;
+ guint32 chunk_len, layer_extension_len;
gchar *name = NULL;
guint16 namelen;
guchar type, opacity, blend_mode, visibility, transparency_protected;
guchar link_group_id, mask_linked, mask_disabled;
guint32 image_rect[4], saved_image_rect[4], mask_rect[4], saved_mask_rect[4];
- gboolean null_layer = FALSE;
+ gboolean null_layer, can_handle_layer;
guint16 bitmap_count, channel_count;
GimpImageType drawable_type;
GimpLayer *layer = NULL;
@@ -1488,6 +1486,9 @@ read_layer_block (FILE *f,
while (ftell (f) < block_start + total_len)
{
+ null_layer = FALSE;
+ can_handle_layer = FALSE;
+
/* Read the layer sub-block header */
sub_id = read_block_header (f, &sub_init_len, &sub_total_len, error);
if (sub_id == -1)
@@ -1506,7 +1507,7 @@ read_layer_block (FILE *f,
/* Read layer information chunk */
if (psp_ver_major >= 4)
{
- if (fseek (f, 4, SEEK_CUR) < 0
+ if (fread (&chunk_len, 4, 1, f) < 1
|| fread (&namelen, 2, 1, f) < 1
|| ((namelen = GUINT16_FROM_LE (namelen)) && FALSE)
|| (name = g_malloc (namelen + 1)) == NULL
@@ -1522,10 +1523,7 @@ read_layer_block (FILE *f,
|| fread (&mask_rect, 16, 1, f) < 1
|| fread (&saved_mask_rect, 16, 1, f) < 1
|| fread (&mask_linked, 1, 1, f) < 1
- || fread (&mask_disabled, 1, 1, f) < 1
- || fseek (f, 47, SEEK_CUR) < 0
- || fread (&bitmap_count, 2, 1, f) < 1
- || fread (&channel_count, 2, 1, f) < 1)
+ || fread (&mask_disabled, 1, 1, f) < 1)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Error reading layer information chunk"));
@@ -1534,7 +1532,46 @@ read_layer_block (FILE *f,
}
name[namelen] = 0;
- type = PSP_LAYER_NORMAL; /* ??? */
+ chunk_len = GUINT32_FROM_LE (chunk_len);
+
+ /* Skip remainder of layer info and read layer extension length */
+ layer_extension_start = sub_block_start + chunk_len;
+ if (fseek(f, layer_extension_start, SEEK_SET) < 0
+ || fread(&layer_extension_len, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer extension information"));
+ g_free (name);
+ return NULL;
+ }
+ layer_extension_len = GUINT32_FROM_LE (layer_extension_len);
+ switch (type)
+ {
+ case keGLTFloatingRasterSelection:
+ g_message ("Floating selection restored as normal layer (%s)", name);
+ case keGLTRaster:
+ can_handle_layer = TRUE;
+ if (fread (&bitmap_count, 2, 1, f) < 1
+ || fread (&channel_count, 2, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer information"));
+ g_free (name);
+ return NULL;
+ }
+ break;
+ default:
+ bitmap_count = 0;
+ channel_count = 0;
+ g_message ("Unsupported layer type %d (%s)", type, name);
+ break;
+ }
+
+ if (try_fseek (f, layer_extension_start + layer_extension_len, SEEK_SET, error) < 0)
+ {
+ g_free (name);
+ return NULL;
+ }
}
else
{
@@ -1563,11 +1600,16 @@ read_layer_block (FILE *f,
g_free (name);
return NULL;
}
+ if (type == PSP_LAYER_FLOATING_SELECTION)
+ g_message ("Floating selection restored as normal layer");
+ type = keGLTRaster;
+ can_handle_layer = TRUE;
+ if (try_fseek (f, sub_block_start + sub_init_len, SEEK_SET, error) < 0)
+ {
+ return NULL;
+ }
}
- if (type == PSP_LAYER_FLOATING_SELECTION)
- g_message ("Floating selection restored as normal layer");
-
swab_rect (image_rect);
swab_rect (saved_image_rect);
swab_rect (mask_rect);
@@ -1663,118 +1705,137 @@ read_layer_block (FILE *f,
gimp_layer_set_lock_alpha (layer, transparency_protected);
- if (psp_ver_major < 4)
- if (try_fseek (f, sub_block_start + sub_init_len, SEEK_SET, error) < 0)
- {
- return NULL;
- }
-
- pixel = g_malloc0 (height * width * bytespp);
- if (null_layer)
- {
- pixels = NULL;
- }
- else
- {
- pixels = g_new (guchar *, height);
- for (i = 0; i < height; i++)
- pixels[i] = pixel + width * bytespp * i;
- }
-
- buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
-
- /* Read the layer channel sub-blocks */
- while (ftell (f) < sub_block_start + sub_total_len)
+ if (can_handle_layer)
{
- sub_id = read_block_header (f, &channel_init_len,
- &channel_total_len, error);
- if (sub_id == -1)
+ pixel = g_malloc0 (height * width * bytespp);
+ if (null_layer)
{
- gimp_image_delete (image);
- return NULL;
+ pixels = NULL;
}
-
- if (sub_id != PSP_CHANNEL_BLOCK)
+ else
{
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- _("Invalid layer sub-block %s, should be CHANNEL"),
- block_name (sub_id));
- return NULL;
+ pixels = g_new (guchar *, height);
+ for (i = 0; i < height; i++)
+ pixels[i] = pixel + width * bytespp * i;
}
- channel_start = ftell (f);
-
- if (psp_ver_major == 4)
- fseek (f, 4, SEEK_CUR); /* Unknown field */
+ buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
- if (fread (&compressed_len, 4, 1, f) < 1
- || fread (&uncompressed_len, 4, 1, f) < 1
- || fread (&bitmap_type, 2, 1, f) < 1
- || fread (&channel_type, 2, 1, f) < 1)
+ /* Read the layer channel sub-blocks */
+ while (ftell (f) < sub_block_start + sub_total_len)
{
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- _("Error reading channel information chunk"));
- return NULL;
- }
-
- compressed_len = GUINT32_FROM_LE (compressed_len);
- uncompressed_len = GUINT32_FROM_LE (uncompressed_len);
- bitmap_type = GUINT16_FROM_LE (bitmap_type);
- channel_type = GUINT16_FROM_LE (channel_type);
+ sub_id = read_block_header (f, &channel_init_len,
+ &channel_total_len, error);
+ if (sub_id == -1)
+ {
+ gimp_image_delete (image);
+ return NULL;
+ }
- if (bitmap_type > PSP_DIB_USER_MASK)
- {
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- _("Invalid bitmap type %d in channel information chunk"),
- bitmap_type);
- return NULL;
- }
+ if (sub_id != PSP_CHANNEL_BLOCK)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid layer sub-block %s, should be CHANNEL"),
+ block_name (sub_id));
+ return NULL;
+ }
- if (channel_type > PSP_CHANNEL_BLUE)
- {
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- _("Invalid channel type %d in channel information chunk"),
- channel_type);
- return NULL;
- }
+ channel_start = ftell (f);
+ chunk_len = channel_init_len; /* init chunk_len for psp_ver_major == 3 */
+ if ((psp_ver_major >= 4
+ && (fread (&chunk_len, 4, 1, f) < 1
+ || ((chunk_len = GUINT32_FROM_LE (chunk_len)) < 16)))
+ || fread (&compressed_len, 4, 1, f) < 1
+ || fread (&uncompressed_len, 4, 1, f) < 1
+ || fread (&bitmap_type, 2, 1, f) < 1
+ || fread (&channel_type, 2, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading channel information chunk"));
+ return NULL;
+ }
- IFDBG(2) g_message ("channel: %s %s %d (%d) bytes %d bytespp",
- bitmap_type_name (bitmap_type),
- channel_type_name (channel_type),
- uncompressed_len, compressed_len,
- bytespp);
+ compressed_len = GUINT32_FROM_LE (compressed_len);
+ uncompressed_len = GUINT32_FROM_LE (uncompressed_len);
+ bitmap_type = GUINT16_FROM_LE (bitmap_type);
+ channel_type = GUINT16_FROM_LE (channel_type);
- if (bitmap_type == PSP_DIB_TRANS_MASK)
- offset = 3;
- else
- offset = channel_type - PSP_CHANNEL_RED;
+ if (bitmap_type > PSP_DIB_USER_MASK)
+ {
+ g_message ("Conversion of bitmap type %d is not supported.", bitmap_type);
+ }
+ else if (bitmap_type == PSP_DIB_USER_MASK)
+ {
+ /* FIXME: Add as layer mask */
+ g_message ("Conversion of layer mask is not supported");
+ }
+ else
+ {
+ if (channel_type > PSP_CHANNEL_BLUE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid channel type %d in channel information chunk"),
+ channel_type);
+ return NULL;
+ }
+
+ IFDBG(2) g_message ("channel: %s %s %d (%d) bytes %d bytespp",
+ bitmap_type_name (bitmap_type),
+ channel_type_name (channel_type),
+ uncompressed_len, compressed_len,
+ bytespp);
+
+ if (bitmap_type == PSP_DIB_TRANS_MASK)
+ offset = 3;
+ else
+ offset = channel_type - PSP_CHANNEL_RED;
+
+ if (!null_layer)
+ {
+ if (try_fseek (f, channel_start + chunk_len, SEEK_SET, error) < 0)
+ {
+ return NULL;
+ }
+
+ if (read_channel_data (f, ia, pixels, bytespp, offset,
+ buffer, compressed_len, error) == -1)
+ {
+ return NULL;
+ }
+ }
+ }
+ if (try_fseek (f, channel_start + channel_total_len, SEEK_SET, error) < 0)
+ {
+ return NULL;
+ }
+ }
- if (psp_ver_major < 4)
- if (try_fseek (f, channel_start + channel_init_len, SEEK_SET, error) < 0)
- {
- return NULL;
- }
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, pixel, GEGL_AUTO_ROWSTRIDE);
- if (!null_layer)
- if (read_channel_data (f, ia, pixels, bytespp, offset,
- buffer, compressed_len, error) == -1)
- {
- return NULL;
- }
+ g_object_unref (buffer);
- if (try_fseek (f, channel_start + channel_total_len, SEEK_SET, error) < 0)
+ g_free (pixels);
+ g_free (pixel);
+ if (psp_ver_major >= 4)
{
- return NULL;
+ if (try_fseek (f, sub_block_start + sub_total_len, SEEK_SET, error) < 0)
+ {
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ /* Can't handle this type of layer, skip the data so we can read the next layer. */
+ if (psp_ver_major >= 4)
+ {
+ if (try_fseek (f, sub_block_start + sub_total_len, SEEK_SET, error) < 0)
+ {
+ return NULL;
+ }
}
}
-
- gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
- NULL, pixel, GEGL_AUTO_ROWSTRIDE);
-
- g_object_unref (buffer);
-
- g_free (pixels);
- g_free (pixel);
}
if (try_fseek (f, block_start + total_len, SEEK_SET, error) < 0)
@@ -1981,7 +2042,7 @@ load_image (GFile *file,
goto error;
}
if (read_general_image_attribute_block (f, block_init_len,
- block_total_len, &ia) == -1)
+ block_total_len, &ia, error) == -1)
{
goto error;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]