From 680ebede22bf7f34f78e5342b4df207892559406 Mon Sep 17 00:00:00 2001 From: Alx Sa Date: Mon, 23 Mar 2026 15:07:33 +0000 Subject: [PATCH] plug-ins: Read full channel data for PSP selection 968cf06a only loaded the rectangle area that the selection was contained in, rather than the actual channel data. This patch extended the code to also read in the PSP_CHANNEL_BLOCK data, add it to the selection GeglBuffer, and then shift it over into the correct location. --- plug-ins/common/file-psp.c | 140 +++++++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 14 deletions(-) diff --git a/plug-ins/common/file-psp.c b/plug-ins/common/file-psp.c index 763dd994fc..3015f21f09 100644 --- a/plug-ins/common/file-psp.c +++ b/plug-ins/common/file-psp.c @@ -2410,10 +2410,25 @@ read_selection_block (FILE *f, PSPimage *ia, GError **error) { - gsize current_location; - gsize file_size; - guint32 chunk_size; - guint32 rect[4]; + gsize current_location; + gsize file_size; + guint32 chunk_size; + guint32 rect[4]; + guint32 saved_rect[4]; + gushort channel_counts[2]; + /* Channel information */ + gint sub_id; + guint32 channel_init_len; + guint32 channel_total_len; + guint32 compressed_len; + guint32 uncompressed_len; + guint16 bitmap_type; + guint16 channel_type; + GimpSelection *selection; + GeglBuffer *buffer; + guchar *pixels; + gint width; + gint height; current_location = ftell (f); fseek (f, 0, SEEK_END); @@ -2436,7 +2451,8 @@ read_selection_block (FILE *f, return -1; } - if (fread (&rect, 16, 1, f) < 1) + if (fread (&rect, 16, 1, f) < 1 || + fread (&saved_rect, 16, 1, f) < 1) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error reading selection chunk")); @@ -2444,15 +2460,112 @@ read_selection_block (FILE *f, } swab_rect (rect); - gimp_image_select_rectangle (image, GIMP_CHANNEL_OP_ADD, - rect[0], rect[1], - rect[2] - rect[0], - rect[3] - rect[1]); + width = rect[2] - rect[0]; + height = rect[3] - rect[1]; + + total_len -= sizeof (chunk_size) + sizeof (rect) + sizeof (saved_rect); + chunk_size -= sizeof (chunk_size) + sizeof (rect) + sizeof (saved_rect); + + if (try_fseek (f, chunk_size, SEEK_CUR, error) < 0) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading end of selection chunk")); + return -1; + } + + /* Per the specifications, selections always have only one channel */ + if (fread (&chunk_size, 4, 1, f) < 1) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading selection chunk")); + return -1; + } + chunk_size = GUINT32_FROM_LE (chunk_size); + + if (fread (&channel_counts, 4, 1, f) < 1) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading selection chunk")); + return -1; + } + total_len -= sizeof (chunk_size) + sizeof (channel_counts); + chunk_size -= sizeof (chunk_size) + sizeof (channel_counts); + if (try_fseek (f, chunk_size, SEEK_CUR, error) < 0) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading end of selection chunk")); + return -1; + } + + /* Load PSP_CHANNEL_BLOCK subblock data for selection */ + sub_id = read_block_header (f, &channel_init_len, &channel_total_len, error); + if (sub_id != PSP_CHANNEL_BLOCK) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Invalid selection sub-block %s, should be CHANNEL"), + block_name (sub_id)); + return -1; + } + + if ((psp_ver_major >= 4 && (fread (&chunk_size, 4, 1, f) < 1 || + ((chunk_size = GUINT32_FROM_LE (chunk_size)) < 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 -1; + } + total_len -= sizeof (chunk_size) + sizeof (compressed_len) + + sizeof (uncompressed_len) + sizeof (bitmap_type) + + sizeof (channel_type); + chunk_size -= sizeof (chunk_size) + sizeof (compressed_len) + + sizeof (uncompressed_len) + sizeof (bitmap_type) + + sizeof (channel_type); + if (try_fseek (f, chunk_size, SEEK_CUR, error) < 0) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading end of selection chunk")); + return -1; + } + + pixels = g_try_malloc0 (width * height); + if (pixels == NULL) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading channel information chunk")); + return -1; + } + + selection = gimp_image_get_selection (image); + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (selection)); + + /* Per the specification, this will always be a 1 byte grayscale channel */ + if (ia->compression == PSP_COMP_NONE) + { + fread (pixels, width * height, 1, f); + } + else + { + if (read_channel_data (f, ia, &pixels, 1, 0, buffer, compressed_len, + error) == -1) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Error reading channel information chunk")); + g_free (pixels); + return -1; + } + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, + NULL, pixels, GEGL_AUTO_ROWSTRIDE); + g_object_unref (buffer); + g_free (pixels); + + gimp_selection_translate (image, rect[0], rect[1]); - /* The file format specifies multiple selections, but - * as of PSP 8 they only allow one. Skipping the remaining - * information in the block. */ - total_len -= sizeof (guint32) + sizeof (rect); if (try_fseek (f, total_len, SEEK_SET, error) < 0) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, @@ -2487,7 +2600,6 @@ read_extended_block (FILE *f, if (memcmp (header, "~FL\0", 4) != 0) { - g_print ("Header: %s\n", header); g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Invalid extended block chunk header")); return -1;