summaryrefslogtreecommitdiffstats
path: root/plug-ins/common/file-xwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/common/file-xwd.c')
-rw-r--r--plug-ins/common/file-xwd.c2595
1 files changed, 2595 insertions, 0 deletions
diff --git a/plug-ins/common/file-xwd.c b/plug-ins/common/file-xwd.c
new file mode 100644
index 0000000..53e4cd2
--- /dev/null
+++ b/plug-ins/common/file-xwd.c
@@ -0,0 +1,2595 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * XWD reading and writing code Copyright (C) 1996 Peter Kirchgessner
+ * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * XWD-input/output was written by Peter Kirchgessner (peter@kirchgessner.net)
+ * Examples from mainly used UNIX-systems have been used for testing.
+ * If a file does not work, please return a small (!!) compressed example.
+ * Currently the following formats are supported:
+ * pixmap_format | pixmap_depth | bits_per_pixel
+ * ---------------------------------------------
+ * 0 | 1 | 1
+ * 1 | 1,...,24 | 1
+ * 2 | 1 | 1
+ * 2 | 1,...,8 | 8
+ * 2 | 1,...,16 | 16
+ * 2 | 1,...,24 | 24
+ * 2 | 1,...,32 | 32
+ */
+/* Event history:
+ * PK = Peter Kirchgessner, ME = Mattias EngdegÄrd
+ * V 1.00, PK, xx-Aug-96: First try
+ * V 1.01, PK, 03-Sep-96: Check for bitmap_bit_order
+ * V 1.90, PK, 17-Mar-97: Upgrade to work with GIMP V0.99
+ * Use visual class 3 to write indexed image
+ * Set gimp b/w-colormap if no xwdcolormap present
+ * V 1.91, PK, 05-Apr-97: Return all arguments, even in case of an error
+ * V 1.92, PK, 12-Oct-97: No progress bars for non-interactive mode
+ * V 1.93, PK, 11-Apr-98: Fix problem with overwriting memory
+ * V 1.94, ME, 27-Feb-00: Remove superfluous little-endian support (format is
+ specified as big-endian). Trim magic header
+ * V 1.95, PK, 02-Jul-01: Fix problem with 8 bit image
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-xwd-load"
+#define SAVE_PROC "file-xwd-save"
+#define PLUG_IN_BINARY "file-xwd"
+#define PLUG_IN_ROLE "gimp-file-xwd"
+
+
+typedef gulong L_CARD32;
+typedef gushort L_CARD16;
+typedef guchar L_CARD8;
+
+typedef struct
+{
+ L_CARD32 l_header_size; /* Header size */
+
+ L_CARD32 l_file_version; /* File version (7) */
+ L_CARD32 l_pixmap_format; /* Image type */
+ L_CARD32 l_pixmap_depth; /* Number of planes */
+ L_CARD32 l_pixmap_width; /* Image width */
+ L_CARD32 l_pixmap_height; /* Image height */
+ L_CARD32 l_xoffset; /* x-offset (0 ?) */
+ L_CARD32 l_byte_order; /* Byte ordering */
+
+ L_CARD32 l_bitmap_unit;
+ L_CARD32 l_bitmap_bit_order; /* Bit order */
+ L_CARD32 l_bitmap_pad;
+ L_CARD32 l_bits_per_pixel; /* Number of bits per pixel */
+
+ L_CARD32 l_bytes_per_line; /* Number of bytes per scanline */
+ L_CARD32 l_visual_class; /* Visual class */
+ L_CARD32 l_red_mask; /* Red mask */
+ L_CARD32 l_green_mask; /* Green mask */
+ L_CARD32 l_blue_mask; /* Blue mask */
+ L_CARD32 l_bits_per_rgb; /* Number of bits per RGB-part */
+ L_CARD32 l_colormap_entries; /* Number of colors in color table (?) */
+ L_CARD32 l_ncolors; /* Number of xwdcolor structures */
+ L_CARD32 l_window_width; /* Window width */
+ L_CARD32 l_window_height; /* Window height */
+ L_CARD32 l_window_x; /* Window position x */
+ L_CARD32 l_window_y; /* Window position y */
+ L_CARD32 l_window_bdrwidth;/* Window border width */
+} L_XWDFILEHEADER;
+
+typedef struct
+{
+ L_CARD32 l_pixel; /* Color index */
+ L_CARD16 l_red, l_green, l_blue; /* RGB-values */
+ L_CARD8 l_flags, l_pad;
+} L_XWDCOLOR;
+
+
+/* Some structures for mapping up to 32bit-pixel */
+/* values which are kept in the XWD-Colormap */
+
+#define MAPPERBITS 12
+#define MAPPERMASK ((1 << MAPPERBITS)-1)
+
+typedef struct
+{
+ L_CARD32 pixel_val;
+ guchar red;
+ guchar green;
+ guchar blue;
+} PMAP;
+
+typedef struct
+{
+ gint npixel; /* Number of pixel values in map */
+ guchar pixel_in_map[1 << MAPPERBITS];
+ PMAP pmap[256];
+} PIXEL_MAP;
+
+#define XWDHDR_PAD 0 /* Total number of padding bytes for XWD header */
+#define XWDCOL_PAD 0 /* Total number of padding bytes for each XWD color */
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gint32 create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ GimpImageType gdtype,
+ gint32 *layer_ID,
+ GeglBuffer **buffer);
+
+static int set_pixelmap (gint ncols,
+ L_XWDCOLOR *xwdcol,
+ PIXEL_MAP *pixelmap);
+static gboolean get_pixelmap (L_CARD32 pixelval,
+ PIXEL_MAP *pixelmap,
+ guchar *red,
+ guchar *green,
+ guchar *glue);
+
+static void set_bw_color_table (gint32 image_ID);
+static void set_color_table (gint32 image_ID,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+
+static gint32 load_xwd_f2_d1_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f2_d8_b8 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f2_d16_b16 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f2_d24_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error);
+static gint32 load_xwd_f2_d32_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f1_d24_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error);
+
+static L_CARD32 read_card32 (FILE *ifp,
+ gint *err);
+static L_CARD16 read_card16 (FILE *ifp,
+ gint *err);
+static L_CARD8 read_card8 (FILE *ifp,
+ gint *err);
+
+static gboolean write_card32 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error);
+static gboolean write_card16 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error);
+static gboolean write_card8 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error);
+
+static void read_xwd_header (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr);
+
+static gboolean write_xwd_header (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ GError **error);
+
+static void read_xwd_cols (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+
+static gboolean write_xwd_cols (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *colormap,
+ GError **error);
+
+static gint save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean gray,
+ GError **error);
+static gint save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the XWD (X Window Dump) format",
+ "Loads files in the XWD (X Window Dump) format. "
+ "XWD image files are produced by the program xwd. "
+ "Xwd is an X Window System window dumping utility.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1996",
+ N_("X window dump"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-xwindowdump");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "xwd",
+ "",
+ "4,long,0x00000007");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in the XWD (X Window Dump) format",
+ "XWD exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1996",
+ N_("X window dump"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-xwindowdump");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "xwd", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "XWD",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* No additional data to retrieve */
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (! save_image (file, image_ID, drawable_ID, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_object_unref (file);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *ifp = NULL;
+ gint depth, bpp;
+ gint32 image_ID = -1;
+ L_XWDFILEHEADER xwdhdr;
+ L_XWDCOLOR *xwdcolmap = NULL;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ ifp = g_fopen (filename, "rb");
+ if (!ifp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ goto out;
+ }
+
+ read_xwd_header (ifp, &xwdhdr);
+ if (xwdhdr.l_file_version != 7)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not read XWD header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+#ifdef XWD_COL_WAIT_DEBUG
+ {
+ int k = 1;
+
+ while (k)
+ k = k;
+ }
+#endif
+
+ /* Position to start of XWDColor structures */
+ fseek (ifp, (long)xwdhdr.l_header_size, SEEK_SET);
+
+ /* Guard against insanely huge color maps -- gimp_image_set_colormap() only
+ * accepts colormaps with 0..256 colors anyway. */
+ if (xwdhdr.l_colormap_entries > 256)
+ {
+ g_message (_("'%s':\nIllegal number of colormap entries: %ld"),
+ gimp_filename_to_utf8 (filename),
+ (long)xwdhdr.l_colormap_entries);
+ goto out;
+ }
+
+ if (xwdhdr.l_colormap_entries > 0)
+ {
+ if (xwdhdr.l_colormap_entries < xwdhdr.l_ncolors)
+ {
+ g_message (_("'%s':\nNumber of colormap entries < number of colors"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ xwdcolmap = g_new (L_XWDCOLOR, xwdhdr.l_colormap_entries);
+
+ read_xwd_cols (ifp, &xwdhdr, xwdcolmap);
+
+#ifdef XWD_COL_DEBUG
+ {
+ int j;
+ g_printf ("File %s\n",filename);
+ for (j = 0; j < xwdhdr.l_colormap_entries; j++)
+ g_printf ("Entry 0x%08lx: 0x%04lx, 0x%04lx, 0x%04lx, %d\n",
+ (long)xwdcolmap[j].l_pixel,(long)xwdcolmap[j].l_red,
+ (long)xwdcolmap[j].l_green,(long)xwdcolmap[j].l_blue,
+ (int)xwdcolmap[j].l_flags);
+ }
+#endif
+
+ if (xwdhdr.l_file_version != 7)
+ {
+ g_message (_("Can't read color entries"));
+ goto out;
+ }
+ }
+
+ if (xwdhdr.l_pixmap_width <= 0)
+ {
+ g_message (_("'%s':\nNo image width specified"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (xwdhdr.l_pixmap_width > GIMP_MAX_IMAGE_SIZE
+ || xwdhdr.l_bytes_per_line > GIMP_MAX_IMAGE_SIZE * 3)
+ {
+ g_message (_("'%s':\nImage width is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (xwdhdr.l_pixmap_height <= 0)
+ {
+ g_message (_("'%s':\nNo image height specified"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (xwdhdr.l_pixmap_height > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_message (_("'%s':\nImage height is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ depth = xwdhdr.l_pixmap_depth;
+ bpp = xwdhdr.l_bits_per_pixel;
+
+ image_ID = -1;
+ switch (xwdhdr.l_pixmap_format)
+ {
+ case 0: /* Single plane bitmap */
+ if ((depth == 1) && (bpp == 1))
+ { /* Can be performed by format 2 loader */
+ image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ break;
+
+ case 1: /* Single plane pixmap */
+ if ((depth <= 24) && (bpp == 1))
+ {
+ image_ID = load_xwd_f1_d24_b1 (filename, ifp, &xwdhdr, xwdcolmap,
+ error);
+ }
+ break;
+
+ case 2: /* Multiplane pixmaps */
+ if ((depth == 1) && (bpp == 1))
+ {
+ image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ else if ((depth <= 8) && (bpp == 8))
+ {
+ image_ID = load_xwd_f2_d8_b8 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ else if ((depth <= 16) && (bpp == 16))
+ {
+ image_ID = load_xwd_f2_d16_b16 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ else if ((depth <= 24) && ((bpp == 24) || (bpp == 32)))
+ {
+ image_ID = load_xwd_f2_d24_b32 (filename, ifp, &xwdhdr, xwdcolmap,
+ error);
+ }
+ else if ((depth <= 32) && (bpp == 32))
+ {
+ image_ID = load_xwd_f2_d32_b32 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ break;
+ }
+ gimp_progress_update (1.0);
+
+ if (image_ID == -1 && ! (error && *error))
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("XWD-file %s has format %d, depth %d and bits per pixel %d. "
+ "Currently this is not supported."),
+ gimp_filename_to_utf8 (filename),
+ (gint) xwdhdr.l_pixmap_format, depth, bpp);
+
+out:
+ if (ifp)
+ {
+ fclose (ifp);
+ }
+
+ if (xwdcolmap)
+ {
+ g_free (xwdcolmap);
+ }
+
+ return image_ID;
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GimpImageType drawable_type;
+ gboolean success;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not exporting an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Cannot export images with alpha channels."));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_GRAY_IMAGE:
+ case GIMP_RGB_IMAGE:
+ break;
+ default:
+ g_message (_("Cannot operate on unknown image types."));
+ return FALSE;
+ break;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, 0, NULL, error));
+ if (! output)
+ {
+ g_prefix_error (error,
+ _("Could not open '%s' for writing: "),
+ gimp_file_get_utf8_name (file));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ success = save_index (output, image_ID, drawable_ID, FALSE, error);
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ success = save_index (output, image_ID, drawable_ID, TRUE, error);
+ break;
+
+ case GIMP_RGB_IMAGE:
+ success = save_rgb (output, image_ID, drawable_ID, error);
+ break;
+
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success && ! g_output_stream_close (output, NULL, error))
+ {
+ g_prefix_error (error,
+ _("Error exporting '%s': "),
+ gimp_file_get_utf8_name (file));
+ success = FALSE;
+ }
+ else if (! success)
+ {
+ GCancellable *cancellable;
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+ }
+
+ g_object_unref (output);
+
+ gimp_progress_update (1.0);
+
+ return success;
+}
+
+
+static L_CARD32
+read_card32 (FILE *ifp,
+ int *err)
+
+{
+ L_CARD32 c;
+
+ c = (((L_CARD32) (getc (ifp))) << 24);
+ c |= (((L_CARD32) (getc (ifp))) << 16);
+ c |= (((L_CARD32) (getc (ifp))) << 8);
+ c |= ((L_CARD32) (*err = getc (ifp)));
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+static L_CARD16
+read_card16 (FILE *ifp,
+ int *err)
+
+{
+ L_CARD16 c;
+
+ c = (((L_CARD16) (getc (ifp))) << 8);
+ c |= ((L_CARD16) (*err = getc (ifp)));
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+static L_CARD8
+read_card8 (FILE *ifp,
+ int *err)
+{
+ L_CARD8 c;
+
+ c = ((L_CARD8) (*err = getc (ifp)));
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+
+static gboolean
+write_card32 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error)
+{
+ guchar buffer[4];
+
+ buffer[0] = (c >> 24) & 0xff;
+ buffer[1] = (c >> 16) & 0xff;
+ buffer[2] = (c >> 8) & 0xff;
+ buffer[3] = (c) & 0xff;
+
+ return g_output_stream_write_all (output, buffer, 4,
+ NULL, NULL, error);
+}
+
+static gboolean
+write_card16 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error)
+{
+ guchar buffer[2];
+
+ buffer[0] = (c >> 8) & 0xff;
+ buffer[1] = (c) & 0xff;
+
+ return g_output_stream_write_all (output, buffer, 2,
+ NULL, NULL, error);
+}
+
+static gboolean
+write_card8 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error)
+{
+ guchar buffer[1];
+
+ buffer[0] = c & 0xff;
+
+ return g_output_stream_write_all (output, buffer, 1,
+ NULL, NULL, error);
+}
+
+
+static void
+read_xwd_header (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr)
+{
+ gint j, err;
+ L_CARD32 *cp;
+
+ cp = (L_CARD32 *) xwdhdr;
+
+ /* Read in all 32-bit values of the header and check for byte order */
+ for (j = 0; j < sizeof (L_XWDFILEHEADER) / sizeof(xwdhdr->l_file_version); j++)
+ {
+ *(cp++) = read_card32 (ifp, &err);
+
+ if (err)
+ break;
+ }
+
+ if (err)
+ xwdhdr->l_file_version = 0; /* Not a valid XWD-file */
+}
+
+
+/* Write out an XWD-fileheader. The header size is calculated here */
+static gboolean
+write_xwd_header (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ GError **error)
+
+{
+ gint j, hdrpad, hdr_entries;
+ L_CARD32 *cp;
+
+ hdrpad = XWDHDR_PAD;
+ hdr_entries = sizeof (L_XWDFILEHEADER) / sizeof(xwdhdr->l_file_version);
+ xwdhdr->l_header_size = hdr_entries * 4 + hdrpad;
+
+ cp = (L_CARD32 *) xwdhdr;
+
+ /* Write out all 32-bit values of the header and check for byte order */
+ for (j = 0; j < sizeof (L_XWDFILEHEADER) / sizeof(xwdhdr->l_file_version); j++)
+ {
+ if (! write_card32 (output, *(cp++), error))
+ return FALSE;
+ }
+
+ /* Add padding bytes after XWD header */
+ for (j = 0; j < hdrpad; j++)
+ if (! write_card8 (output, (L_CARD32) 0, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+read_xwd_cols (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *colormap)
+{
+ gint j, err = 0;
+ gint flag_is_bad, index_is_bad;
+ gint indexed = (xwdhdr->l_pixmap_depth <= 8);
+ glong colmappos = ftell (ifp);
+
+ /* Read in XWD-Color structures (the usual case) */
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ colormap[j].l_red = read_card16 (ifp, &err);
+ colormap[j].l_green = read_card16 (ifp, &err);
+ colormap[j].l_blue = read_card16 (ifp, &err);
+
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (err) /* Not a valid XWD-file ? */
+ xwdhdr->l_file_version = 0;
+ if (err || ((flag_is_bad == 0) && (index_is_bad == 0)))
+ return;
+
+ /* Read in XWD-Color structures (with 4 bytes inserted infront of RGB) */
+ fseek (ifp, colmappos, SEEK_SET);
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ read_card32 (ifp, &err); /* Empty bytes on Alpha OSF */
+
+ colormap[j].l_red = read_card16 (ifp, &err);
+ colormap[j].l_green = read_card16 (ifp, &err);
+ colormap[j].l_blue = read_card16 (ifp, &err);
+
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (err) /* Not a valid XWD-file ? */
+ xwdhdr->l_file_version = 0;
+ if (err || ((flag_is_bad == 0) && (index_is_bad == 0)))
+ return;
+
+ /* Read in XWD-Color structures (with 2 bytes inserted infront of RGB) */
+ fseek (ifp, colmappos, SEEK_SET);
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ read_card16 (ifp, &err); /* Empty bytes (from where ?) */
+
+ colormap[j].l_red = read_card16 (ifp, &err);
+ colormap[j].l_green = read_card16 (ifp, &err);
+ colormap[j].l_blue = read_card16 (ifp, &err);
+
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+
+ /* if ((colormap[j].l_flags == 0) || (colormap[j].l_flags > 7))
+ flag_is_bad++; */
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (err) /* Not a valid XWD-file ? */
+ xwdhdr->l_file_version = 0;
+ if (err || ((flag_is_bad == 0) && (index_is_bad == 0)))
+ return;
+
+ /* Read in XWD-Color structures (every value is 8 bytes from a CRAY) */
+ fseek (ifp, colmappos, SEEK_SET);
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ read_card32 (ifp, &err);
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ read_card32 (ifp, &err);
+ colormap[j].l_red = read_card32 (ifp, &err);
+ read_card32 (ifp, &err);
+ colormap[j].l_green = read_card32 (ifp, &err);
+ read_card32 (ifp, &err);
+ colormap[j].l_blue = read_card32 (ifp, &err);
+
+ /* The flag byte is kept in the first byte */
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+ read_card16 (ifp, &err);
+ read_card32 (ifp, &err);
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (flag_is_bad || index_is_bad)
+ {
+ g_printf ("xwd: Warning. Error in XWD-color-structure (");
+
+ if (flag_is_bad) g_printf ("flag");
+ if (index_is_bad) g_printf ("index");
+
+ g_printf (")\n");
+ }
+
+ if (err)
+ xwdhdr->l_file_version = 0; /* Not a valid XWD-file */
+}
+
+static gboolean
+write_xwd_cols (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *colormap,
+ GError **error)
+{
+ gint j;
+
+ for (j = 0; j < xwdhdr->l_colormap_entries; j++)
+ {
+#ifdef CRAY
+ if (! (write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, colormap[j].l_pixel, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)colormap[j].l_red, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)colormap[j].l_green, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)colormap[j].l_blue, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_flags, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_pad, error) &&
+ write_card16 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)0, error)))
+ {
+ return FALSE;
+ }
+#else
+#ifdef __alpha
+ if (! (write_card32 (output, colormap[j].l_pixel, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_red, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_green, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_blue, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_flags, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_pad, error)))
+ {
+ return FALSE;
+ }
+#else
+ if (! (write_card32 (output, colormap[j].l_pixel, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_red, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_green, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_blue, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_flags, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_pad, error)))
+ {
+ return FALSE;
+ }
+#endif
+#endif
+ }
+
+ return TRUE;
+}
+
+
+/* Create a map for mapping up to 32 bit pixelvalues to RGB.
+ * Returns number of colors kept in the map (up to 256)
+ */
+static gint
+set_pixelmap (int ncols,
+ L_XWDCOLOR *xwdcol,
+ PIXEL_MAP *pixelmap)
+
+{
+ gint i, j, k, maxcols;
+ L_CARD32 pixel_val;
+
+ memset ((gchar *) pixelmap, 0, sizeof (PIXEL_MAP));
+
+ maxcols = 0;
+
+ for (j = 0; j < ncols; j++) /* For each entry of the XWD colormap */
+ {
+ pixel_val = xwdcol[j].l_pixel;
+ for (k = 0; k < maxcols; k++) /* Where to insert in list ? */
+ {
+ if (pixel_val <= pixelmap->pmap[k].pixel_val)
+ break;
+ }
+ if ((k < maxcols) && (pixel_val == pixelmap->pmap[k].pixel_val))
+ break; /* It was already in list */
+
+ if (k >= 256)
+ break;
+
+ if (k < maxcols) /* Must move entries to the back ? */
+ {
+ for (i = maxcols-1; i >= k; i--)
+ memcpy ((char *)&(pixelmap->pmap[i+1]),(char *)&(pixelmap->pmap[i]),
+ sizeof (PMAP));
+ }
+ pixelmap->pmap[k].pixel_val = pixel_val;
+ pixelmap->pmap[k].red = xwdcol[j].l_red >> 8;
+ pixelmap->pmap[k].green = xwdcol[j].l_green >> 8;
+ pixelmap->pmap[k].blue = xwdcol[j].l_blue >> 8;
+ pixelmap->pixel_in_map[pixel_val & MAPPERMASK] = 1;
+ maxcols++;
+ }
+ pixelmap->npixel = maxcols;
+#ifdef XWD_COL_DEBUG
+ g_printf ("Colors in pixelmap: %d\n",pixelmap->npixel);
+ for (j=0; j<pixelmap->npixel; j++)
+ g_printf ("Pixelvalue 0x%08lx, 0x%02x 0x%02x 0x%02x\n",
+ pixelmap->pmap[j].pixel_val,
+ pixelmap->pmap[j].red,pixelmap->pmap[j].green,
+ pixelmap->pmap[j].blue);
+ for (j=0; j<=MAPPERMASK; j++)
+ g_printf ("0x%08lx: %d\n",(long)j,pixelmap->pixel_in_map[j]);
+#endif
+
+ return pixelmap->npixel;
+}
+
+
+/* Search a pixel value in the pixel map. Returns FALSE if the
+ * pixelval was not found in map. Returns TRUE if found.
+ */
+static gboolean
+get_pixelmap (L_CARD32 pixelval,
+ PIXEL_MAP *pixelmap,
+ guchar *red,
+ guchar *green,
+ guchar *blue)
+
+{
+ register PMAP *low, *high, *middle;
+
+ if (pixelmap->npixel == 0)
+ return FALSE;
+
+ if (!(pixelmap->pixel_in_map[pixelval & MAPPERMASK]))
+ return FALSE;
+
+ low = &(pixelmap->pmap[0]);
+ high = &(pixelmap->pmap[pixelmap->npixel-1]);
+
+ /* Do a binary search on the array */
+ while (low < high)
+ {
+ middle = low + ((high - low)/2);
+ if (pixelval <= middle->pixel_val)
+ high = middle;
+ else
+ low = middle+1;
+ }
+
+ if (pixelval == low->pixel_val)
+ {
+ *red = low->red; *green = low->green; *blue = low->blue;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+set_bw_color_table (gint32 image_ID)
+{
+ static guchar BWColorMap[2*3] = { 255, 255, 255, 0, 0, 0 };
+
+#ifdef XWD_COL_DEBUG
+ g_printf ("Set GIMP b/w-colortable:\n");
+#endif
+
+ gimp_image_set_colormap (image_ID, BWColorMap, 2);
+}
+
+
+/* Initialize an 8-bit colortable from the mask-values of the XWD-header */
+static void
+init_color_table256 (L_XWDFILEHEADER *xwdhdr,
+ guchar *ColorMap)
+
+{
+ gint i, j, k, cuind;
+ gint redshift, greenshift, blueshift;
+ gint maxred, maxgreen, maxblue;
+
+ /* Assume: the bit masks for red/green/blue are grouped together
+ * Example: redmask = 0xe0, greenmask = 0x1c, bluemask = 0x03
+ * We need to know where to place the RGB-values (shifting)
+ * and the maximum value for each component.
+ */
+ redshift = greenshift = blueshift = 0;
+ if ((maxred = xwdhdr->l_red_mask) == 0)
+ return;
+
+ /* Shift the redmask to the rightmost bit position to get
+ * maximum value for red.
+ */
+ while ((maxred & 1) == 0)
+ {
+ redshift++;
+ maxred >>= 1;
+ }
+
+ if ((maxgreen = xwdhdr->l_green_mask) == 0)
+ return;
+
+ while ((maxgreen & 1) == 0)
+ {
+ greenshift++;
+ maxgreen >>= 1;
+ }
+
+ if ((maxblue = xwdhdr->l_blue_mask) == 0)
+ return;
+
+ while ((maxblue & 1) == 0)
+ {
+ blueshift++;
+ maxblue >>= 1;
+ }
+
+ memset ((gchar *) ColorMap, 0, 256 * 3);
+
+ for (i = 0; i <= maxred; i++)
+ for (j = 0; j <= maxgreen; j++)
+ for (k = 0; k <= maxblue; k++)
+ {
+ cuind = (i << redshift) | (j << greenshift) | (k << blueshift);
+
+ if (cuind < 256)
+ {
+ ColorMap[cuind*3] = (i * 255)/maxred;
+ ColorMap[cuind*3+1] = (j * 255)/maxgreen;
+ ColorMap[cuind*3+2] = (k * 255)/maxblue;
+ }
+ }
+}
+
+
+static void
+set_color_table (gint32 image_ID,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+
+{
+ gint ncols, i, j;
+ guchar ColorMap[256 * 3];
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+ if (ncols <= 0)
+ return;
+ if (ncols > 256)
+ ncols = 256;
+
+ /* Initialize color table for all 256 entries from mask-values */
+ init_color_table256 (xwdhdr, ColorMap);
+
+ for (j = 0; j < ncols; j++)
+ {
+ i = xwdcolmap[j].l_pixel;
+ if ((i >= 0) && (i < 256))
+ {
+ ColorMap[i*3] = (xwdcolmap[j].l_red) >> 8;
+ ColorMap[i*3+1] = (xwdcolmap[j].l_green) >> 8;
+ ColorMap[i*3+2] = (xwdcolmap[j].l_blue) >> 8;
+ }
+ }
+
+#ifdef XWD_COL_DEBUG
+ g_printf ("Set GIMP colortable:\n");
+ for (j = 0; j < 256; j++)
+ g_printf ("%3d: 0x%02x 0x%02x 0x%02x\n", j,
+ ColorMap[j*3], ColorMap[j*3+1], ColorMap[j*3+2]);
+#endif
+
+ gimp_image_set_colormap (image_ID, ColorMap, 256);
+}
+
+
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ GimpImageType gdtype,
+ gint32 *layer_ID,
+ GeglBuffer **buffer)
+{
+ gint32 image_ID;
+
+ image_ID = gimp_image_new (width, height, type);
+ gimp_image_set_filename (image_ID, filename);
+
+ *layer_ID = gimp_layer_new (image_ID, "Background", width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ *buffer = gimp_drawable_get_buffer (*layer_ID);
+
+ return image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth 1, bits_per_pixel 1 */
+
+static gint32
+load_xwd_f2_d1_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ register int pix8;
+ register guchar *dest, *src;
+ guchar c1, c2, c3, c4;
+ gint width, height, scan_lines, tile_height;
+ gint i, j, ncols;
+ gchar *temp;
+ guchar bit2byte[256 * 8];
+ guchar *data, *scanline;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d1_b1 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_INDEXED,
+ GIMP_INDEXED_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width);
+
+ scanline = g_new (guchar, xwdhdr->l_bytes_per_line + 8);
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ if (ncols < 2)
+ set_bw_color_table (image_ID);
+ else
+ set_color_table (image_ID, xwdhdr, xwdcolmap);
+
+ temp = (gchar *) bit2byte;
+
+ /* Get an array for mapping 8 bits in a byte to 8 bytes */
+ if (!xwdhdr->l_bitmap_bit_order)
+ {
+ for (j = 0; j < 256; j++)
+ for (i = 0; i < 8; i++)
+ *(temp++) = ((j & (1 << i)) != 0);
+ }
+ else
+ {
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ *(temp++) = ((j & (1 << i)) != 0);
+ }
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ if (fread (scanline, xwdhdr->l_bytes_per_line, 1, ifp) != 1)
+ {
+ err = 1;
+ break;
+ }
+
+ /* Need to check byte order ? */
+ if (xwdhdr->l_bitmap_bit_order != xwdhdr->l_byte_order)
+ {
+ src = scanline;
+ switch (xwdhdr->l_bitmap_unit)
+ {
+ case 16:
+ j = xwdhdr->l_bytes_per_line;
+ while (j > 0)
+ {
+ c1 = src[0]; c2 = src[1];
+ *(src++) = c2; *(src++) = c1;
+ j -= 2;
+ }
+ break;
+
+ case 32:
+ j = xwdhdr->l_bytes_per_line;
+ while (j > 0)
+ {
+ c1 = src[0]; c2 = src[1]; c3 = src[2]; c4 = src[3];
+ *(src++) = c4; *(src++) = c3; *(src++) = c2; *(src++) = c1;
+ j -= 4;
+ }
+ break;
+ }
+ }
+ src = scanline;
+ j = width;
+ while (j >= 8)
+ {
+ pix8 = *(src++);
+ memcpy (dest, bit2byte + pix8*8, 8);
+ dest += 8;
+ j -= 8;
+ }
+ if (j > 0)
+ {
+ pix8 = *(src++);
+ memcpy (dest, bit2byte + pix8*8, j);
+ dest += j;
+ }
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i+1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ if (err) break;
+ }
+
+ g_free (data);
+ g_free (scanline);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth 8, bits_per_pixel 8 */
+
+static gint32
+load_xwd_f2_d8_b8 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ gint width, height, linepad, tile_height, scan_lines;
+ gint i, j, ncols;
+ gint grayscale;
+ guchar *dest, *data;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d8_b8 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ /* This could also be a grayscale image. Check it */
+ grayscale = 0;
+ if ((xwdhdr->l_ncolors == 256) && (xwdhdr->l_colormap_entries == 256))
+ {
+ for (j = 0; j < 256; j++)
+ {
+ if ((xwdcolmap[j].l_pixel != j)
+ || ((xwdcolmap[j].l_red >> 8) != j)
+ || ((xwdcolmap[j].l_green >> 8) != j)
+ || ((xwdcolmap[j].l_blue >> 8) != j))
+ break;
+ }
+
+ grayscale = (j == 256);
+ }
+
+ image_ID = create_new_image (filename, width, height,
+ grayscale ? GIMP_GRAY : GIMP_INDEXED,
+ grayscale ? GIMP_GRAY_IMAGE : GIMP_INDEXED_IMAGE,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width);
+
+ if (!grayscale)
+ {
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols) ncols = xwdhdr->l_ncolors;
+ if (ncols < 2)
+ set_bw_color_table (image_ID);
+ else
+ set_color_table (image_ID, xwdhdr, xwdcolmap);
+ }
+
+ linepad = xwdhdr->l_bytes_per_line - xwdhdr->l_pixmap_width;
+ if (linepad < 0)
+ linepad = 0;
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ if (fread (dest, 1, width, ifp) != width)
+ {
+ err = 1;
+ break;
+ }
+ dest += width;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth up to 16, bits_per_pixel 16 */
+
+static gint32
+load_xwd_f2_d16_b16 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ register guchar *dest, lsbyte_first;
+ gint width, height, linepad, i, j, c0, c1, ncols;
+ gint red, green, blue, redval, greenval, blueval;
+ gint maxred, maxgreen, maxblue;
+ gint tile_height, scan_lines;
+ gulong redmask, greenmask, bluemask;
+ guint redshift, greenshift, blueshift;
+ gulong maxval;
+ guchar *ColorMap, *cm, *data;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d16_b16 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ GIMP_RGB_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 3);
+
+ /* Get memory for mapping 16 bit XWD-pixel to GIMP-RGB */
+ maxval = 0x10000 * 3;
+ ColorMap = g_new0 (guchar, maxval);
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+ redshift = greenshift = blueshift = 0;
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ /* Built up the array to map XWD-pixel value to GIMP-RGB */
+ for (red = 0; red <= maxred; red++)
+ {
+ redval = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ {
+ greenval = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ {
+ blueval = (blue * 255) / maxblue;
+ cm = ColorMap + ((red << redshift) + (green << greenshift)
+ + (blue << blueshift)) * 3;
+ *(cm++) = redval;
+ *(cm++) = greenval;
+ *cm = blueval;
+ }
+ }
+ }
+
+ /* Now look what was written to the XWD-Colormap */
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ for (j = 0; j < ncols; j++)
+ {
+ cm = ColorMap + xwdcolmap[j].l_pixel * 3;
+ *(cm++) = (xwdcolmap[j].l_red >> 8);
+ *(cm++) = (xwdcolmap[j].l_green >> 8);
+ *cm = (xwdcolmap[j].l_blue >> 8);
+ }
+
+ /* What do we have to consume after a line has finished ? */
+ linepad = xwdhdr->l_bytes_per_line
+ - (xwdhdr->l_pixmap_width*xwdhdr->l_bits_per_pixel)/8;
+ if (linepad < 0) linepad = 0;
+
+ lsbyte_first = (xwdhdr->l_byte_order == 0);
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ if (c1 < 0)
+ {
+ err = 1;
+ break;
+ }
+
+ if (lsbyte_first)
+ c0 = c0 | (c1 << 8);
+ else
+ c0 = (c0 << 8) | c1;
+
+ cm = ColorMap + c0 * 3;
+ *(dest++) = *(cm++);
+ *(dest++) = *(cm++);
+ *(dest++) = *cm;
+ }
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i+1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+ g_free (data);
+ g_free (ColorMap);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth up to 24, bits_per_pixel 24/32 */
+
+static gint32
+load_xwd_f2_d24_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error)
+{
+ register guchar *dest, lsbyte_first;
+ gint width, height, linepad, i, j, c0, c1, c2, c3;
+ gint tile_height, scan_lines;
+ L_CARD32 pixelval;
+ gint red, green, blue, ncols;
+ gint maxred, maxgreen, maxblue;
+ gulong redmask, greenmask, bluemask;
+ guint redshift, greenshift, blueshift;
+ guchar redmap[256], greenmap[256], bluemap[256];
+ guchar *data;
+ PIXEL_MAP pixel_map;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d24_b32 (%s)\n", filename);
+#endif
+
+ /* issue #8082: depth and bits per pixel is 24, but 4 bytes are used per pixel */
+ if (xwdhdr->l_bits_per_pixel == 24)
+ {
+ if (xwdhdr->l_bytes_per_line / xwdhdr->l_pixmap_width == 4 &&
+ xwdhdr->l_bytes_per_line % xwdhdr->l_pixmap_width == 0)
+ xwdhdr->l_bits_per_pixel = 32;
+ }
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ if (redmask == 0) redmask = 0xff0000;
+ if (greenmask == 0) greenmask = 0x00ff00;
+ if (bluemask == 0) bluemask = 0x0000ff;
+
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+ redshift = greenshift = blueshift = 0;
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ if (maxred > sizeof (redmap) ||
+ maxgreen > sizeof (greenmap) ||
+ maxblue > sizeof (bluemap))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("XWD-file %s is corrupt."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ GIMP_RGB_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 3);
+
+ /* Set map-arrays for red, green, blue */
+ for (red = 0; red <= maxred; red++)
+ redmap[red] = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ greenmap[green] = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ bluemap[blue] = (blue * 255) / maxblue;
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ set_pixelmap (ncols, xwdcolmap, &pixel_map);
+
+ /* What do we have to consume after a line has finished ? */
+ linepad = xwdhdr->l_bytes_per_line
+ - (xwdhdr->l_pixmap_width*xwdhdr->l_bits_per_pixel)/8;
+ if (linepad < 0) linepad = 0;
+
+ lsbyte_first = (xwdhdr->l_byte_order == 0);
+
+ dest = data;
+ scan_lines = 0;
+
+ if (xwdhdr->l_bits_per_pixel == 32)
+ {
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ c2 = getc (ifp);
+ c3 = getc (ifp);
+ if (c3 < 0)
+ {
+ err = 1;
+ break;
+ }
+ if (lsbyte_first)
+ pixelval = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
+ else
+ pixelval = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2))
+ {
+ dest += 3;
+ }
+ else
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ }
+ }
+ scan_lines++;
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+ }
+ else /* 24 bits per pixel */
+ {
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ c2 = getc (ifp);
+ if (c2 < 0)
+ {
+ err = 1;
+ break;
+ }
+ if (lsbyte_first)
+ pixelval = c0 | (c1 << 8) | (c2 << 16);
+ else
+ pixelval = (c0 << 16) | (c1 << 8) | c2;
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2))
+ {
+ dest += 3;
+ }
+ else
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ }
+ }
+ scan_lines++;
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+/* Load XWD with pixmap_format 2, pixmap_depth up to 32, bits_per_pixel 32 */
+
+static gint32
+load_xwd_f2_d32_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ register guchar *dest, lsbyte_first;
+ gint width, height, linepad, i, j, c0, c1, c2, c3;
+ gint tile_height, scan_lines;
+ L_CARD32 pixelval;
+ gint red, green, blue, alpha, ncols;
+ gint maxred, maxgreen, maxblue, maxalpha;
+ gulong redmask, greenmask, bluemask, alphamask;
+ guint redshift, greenshift, blueshift, alphashift;
+ guchar redmap[256], greenmap[256], bluemap[256], alphamap[256];
+ guchar *data;
+ PIXEL_MAP pixel_map;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d32_b32 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ GIMP_RGBA_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 4);
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ if (redmask == 0) redmask = 0xff0000;
+ if (greenmask == 0) greenmask = 0x00ff00;
+ if (bluemask == 0) bluemask = 0x0000ff;
+ alphamask = 0xffffffff & ~(redmask | greenmask | bluemask);
+
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+ redshift = greenshift = blueshift = alphashift = 0;
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+ while (((1 << alphashift) & alphamask) == 0) alphashift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ maxalpha = 0; while (alphamask >> (alphashift + maxalpha)) maxalpha++;
+ maxalpha = (1 << maxalpha) - 1;
+
+ /* Set map-arrays for red, green, blue */
+ for (red = 0; red <= maxred; red++)
+ redmap[red] = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ greenmap[green] = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ bluemap[blue] = (blue * 255) / maxblue;
+ for (alpha = 0; alpha <= maxalpha; alpha++)
+ alphamap[alpha] = (alpha * 255) / maxalpha;
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ set_pixelmap (ncols, xwdcolmap, &pixel_map);
+
+ /* What do we have to consume after a line has finished ? */
+ linepad = xwdhdr->l_bytes_per_line
+ - (xwdhdr->l_pixmap_width*xwdhdr->l_bits_per_pixel)/8;
+ if (linepad < 0) linepad = 0;
+
+ lsbyte_first = (xwdhdr->l_byte_order == 0);
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ c2 = getc (ifp);
+ c3 = getc (ifp);
+ if (c3 < 0)
+ {
+ err = 1;
+ break;
+ }
+ if (lsbyte_first)
+ pixelval = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
+ else
+ pixelval = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2))
+ {
+ /* FIXME: is it always transparent or encoded in an unknown way? */
+ *(dest+3) = 0x00;
+ dest += 4;
+ }
+ else
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ *(dest++) = alphamap[(pixelval & alphamask) >> alphashift];
+ }
+ }
+ scan_lines++;
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+/* Load XWD with pixmap_format 1, pixmap_depth up to 24, bits_per_pixel 1 */
+
+static gint32
+load_xwd_f1_d24_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error)
+{
+ register guchar *dest, outmask, inmask, do_reverse;
+ gint width, height, i, j, plane, fromright;
+ gint tile_height, tile_start, tile_end;
+ gint indexed, bytes_per_pixel;
+ gint maxred, maxgreen, maxblue;
+ gint red, green, blue, ncols, standard_rgb;
+ glong data_offset, plane_offset, tile_offset;
+ gulong redmask, greenmask, bluemask;
+ guint redshift, greenshift, blueshift;
+ gulong g;
+ guchar redmap[256], greenmap[256], bluemap[256];
+ guchar bit_reverse[256];
+ guchar *xwddata, *xwdin, *data;
+ L_CARD32 pixelval;
+ PIXEL_MAP pixel_map;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f1_d24_b1 (%s)\n", filename);
+#endif
+
+ xwddata = g_malloc (xwdhdr->l_bytes_per_line);
+ if (xwddata == NULL)
+ return -1;
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+ indexed = (xwdhdr->l_pixmap_depth <= 8);
+ bytes_per_pixel = (indexed ? 1 : 3);
+
+ for (j = 0; j < 256; j++) /* Create an array for reversing bits */
+ {
+ inmask = 0;
+ for (i = 0; i < 8; i++)
+ {
+ inmask <<= 1;
+ if (j & (1 << i)) inmask |= 1;
+ }
+ bit_reverse[j] = inmask;
+ }
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ if (redmask == 0) redmask = 0xff0000;
+ if (greenmask == 0) greenmask = 0x00ff00;
+ if (bluemask == 0) bluemask = 0x0000ff;
+
+ standard_rgb = (redmask == 0xff0000) && (greenmask == 0x00ff00)
+ && (bluemask == 0x0000ff);
+ redshift = greenshift = blueshift = 0;
+
+ if (!standard_rgb) /* Do we need to re-map the pixel-values ? */
+ {
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ if (maxred > sizeof (redmap) ||
+ maxgreen > sizeof (greenmap) ||
+ maxblue > sizeof (bluemap))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("XWD-file %s is corrupt."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ /* Set map-arrays for red, green, blue */
+ for (red = 0; red <= maxred; red++)
+ redmap[red] = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ greenmap[green] = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ bluemap[blue] = (blue * 255) / maxblue;
+ }
+
+ image_ID = create_new_image (filename, width, height,
+ indexed ? GIMP_INDEXED : GIMP_RGB,
+ indexed ? GIMP_INDEXED_IMAGE : GIMP_RGB_IMAGE,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * bytes_per_pixel);
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ if (indexed)
+ {
+ if (ncols < 2)
+ set_bw_color_table (image_ID);
+ else
+ set_color_table (image_ID, xwdhdr, xwdcolmap);
+ }
+ else
+ {
+ set_pixelmap (ncols, xwdcolmap, &pixel_map);
+ }
+
+ do_reverse = !xwdhdr->l_bitmap_bit_order;
+
+ /* This is where the image data starts within the file */
+ data_offset = ftell (ifp);
+
+ for (tile_start = 0; tile_start < height; tile_start += tile_height)
+ {
+ memset (data, 0, width*tile_height*bytes_per_pixel);
+
+ tile_end = tile_start + tile_height - 1;
+ if (tile_end >= height)
+ tile_end = height - 1;
+
+ for (plane = 0; plane < xwdhdr->l_pixmap_depth; plane++)
+ {
+ dest = data; /* Position to start of tile within the plane */
+ plane_offset = data_offset + plane*height*xwdhdr->l_bytes_per_line;
+ tile_offset = plane_offset + tile_start*xwdhdr->l_bytes_per_line;
+ fseek (ifp, tile_offset, SEEK_SET);
+
+ /* Place the last plane at the least significant bit */
+
+ if (indexed) /* Only 1 byte per pixel */
+ {
+ fromright = xwdhdr->l_pixmap_depth-1-plane;
+ outmask = (1 << fromright);
+ }
+ else /* 3 bytes per pixel */
+ {
+ fromright = xwdhdr->l_pixmap_depth-1-plane;
+ dest += 2 - fromright/8;
+ outmask = (1 << (fromright % 8));
+ }
+
+ for (i = tile_start; i <= tile_end; i++)
+ {
+ if (fread (xwddata,xwdhdr->l_bytes_per_line,1,ifp) != 1)
+ {
+ err = 1;
+ break;
+ }
+ xwdin = xwddata;
+
+ /* Handle bitmap unit */
+ if (xwdhdr->l_bitmap_unit == 16)
+ {
+ if (xwdhdr->l_bitmap_bit_order != xwdhdr->l_byte_order)
+ {
+ j = xwdhdr->l_bytes_per_line/2;
+ while (j--)
+ {
+ inmask = xwdin[0]; xwdin[0] = xwdin[1]; xwdin[1] = inmask;
+ xwdin += 2;
+ }
+ xwdin = xwddata;
+ }
+ }
+ else if (xwdhdr->l_bitmap_unit == 32)
+ {
+ if (xwdhdr->l_bitmap_bit_order != xwdhdr->l_byte_order)
+ {
+ j = xwdhdr->l_bytes_per_line/4;
+ while (j--)
+ {
+ inmask = xwdin[0]; xwdin[0] = xwdin[3]; xwdin[3] = inmask;
+ inmask = xwdin[1]; xwdin[1] = xwdin[2]; xwdin[2] = inmask;
+ xwdin += 4;
+ }
+ xwdin = xwddata;
+ }
+ }
+
+ g = inmask = 0;
+ for (j = 0; j < width; j++)
+ {
+ if (!inmask)
+ {
+ g = *(xwdin++);
+ if (do_reverse)
+ g = bit_reverse[g];
+ inmask = 0x80;
+ }
+
+ if (g & inmask)
+ *dest |= outmask;
+ dest += bytes_per_pixel;
+
+ inmask >>= 1;
+ }
+ }
+ }
+
+ /* For indexed images, the mapping to colors is done by the color table. */
+ /* Otherwise we must do the mapping by ourself. */
+ if (!indexed)
+ {
+ dest = data;
+ for (i = tile_start; i <= tile_end; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ pixelval = (*dest << 16) | (*(dest+1) << 8) | *(dest+2);
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2)
+ || standard_rgb)
+ {
+ dest += 3;
+ }
+ else /* We have to map RGB to 0,...,255 */
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ }
+ }
+ }
+ }
+
+ gimp_progress_update ((gdouble) tile_end / (gdouble) height);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, tile_start,
+ width, tile_end-tile_start+1), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (data);
+ g_free (xwddata);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+static gboolean
+save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean gray,
+ GError **error)
+{
+ gint height, width;
+ gint linepad;
+ gint tile_height;
+ gint i, j;
+ gint ncolors, vclass;
+ glong tmp = 0;
+ guchar *data, *src, *cmap;
+ L_XWDFILEHEADER xwdhdr;
+ L_XWDCOLOR xwdcolmap[256];
+ const Babl *format;
+ GeglBuffer *buffer;
+ gboolean success = TRUE;
+
+#ifdef XWD_DEBUG
+ g_printf ("save_index ()\n");
+#endif
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ tile_height = gimp_tile_height ();
+
+ if (gray)
+ format = babl_format ("Y' u8");
+ else
+ format = gegl_buffer_get_format (buffer);
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar,
+ tile_height * width *
+ babl_format_get_bytes_per_pixel (format));
+
+ linepad = width % 4;
+ if (linepad)
+ linepad = 4 - linepad;
+
+ /* Fill XWD-color map */
+ if (gray)
+ {
+ vclass = 0;
+ ncolors = 256;
+
+ for (j = 0; j < ncolors; j++)
+ {
+ xwdcolmap[j].l_pixel = j;
+ xwdcolmap[j].l_red = (j << 8) | j;
+ xwdcolmap[j].l_green = (j << 8) | j;
+ xwdcolmap[j].l_blue = (j << 8) | j;
+ xwdcolmap[j].l_flags = 7;
+ xwdcolmap[j].l_pad = 0;
+ }
+ }
+ else
+ {
+ vclass = 3;
+ cmap = gimp_image_get_colormap (image_ID, &ncolors);
+
+ for (j = 0; j < ncolors; j++)
+ {
+ xwdcolmap[j].l_pixel = j;
+ xwdcolmap[j].l_red = ((*cmap) << 8) | *cmap; cmap++;
+ xwdcolmap[j].l_green = ((*cmap) << 8) | *cmap; cmap++;
+ xwdcolmap[j].l_blue = ((*cmap) << 8) | *cmap; cmap++;
+ xwdcolmap[j].l_flags = 7;
+ xwdcolmap[j].l_pad = 0;
+ }
+ }
+
+ /* Fill in the XWD header (header_size is evaluated by write_xwd_hdr ()) */
+ xwdhdr.l_header_size = 0;
+ xwdhdr.l_file_version = 7;
+ xwdhdr.l_pixmap_format = 2;
+ xwdhdr.l_pixmap_depth = 8;
+ xwdhdr.l_pixmap_width = width;
+ xwdhdr.l_pixmap_height = height;
+ xwdhdr.l_xoffset = 0;
+ xwdhdr.l_byte_order = 1;
+ xwdhdr.l_bitmap_unit = 32;
+ xwdhdr.l_bitmap_bit_order = 1;
+ xwdhdr.l_bitmap_pad = 32;
+ xwdhdr.l_bits_per_pixel = 8;
+ xwdhdr.l_bytes_per_line = width + linepad;
+ xwdhdr.l_visual_class = vclass;
+ xwdhdr.l_red_mask = 0x000000;
+ xwdhdr.l_green_mask = 0x000000;
+ xwdhdr.l_blue_mask = 0x000000;
+ xwdhdr.l_bits_per_rgb = 8;
+ xwdhdr.l_colormap_entries = ncolors;
+ xwdhdr.l_ncolors = ncolors;
+ xwdhdr.l_window_width = width;
+ xwdhdr.l_window_height = height;
+ xwdhdr.l_window_x = 64;
+ xwdhdr.l_window_y = 64;
+ xwdhdr.l_window_bdrwidth = 0;
+
+ success = (write_xwd_header (output, &xwdhdr, error) &&
+ write_xwd_cols (output, &xwdhdr, xwdcolmap, error));
+ if (! success)
+ goto out;
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0) /* Get more data */
+ {
+ gint scan_lines = (i + tile_height - 1 < height) ? tile_height : (height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = data;
+ }
+
+ success = g_output_stream_write_all (output, src, width,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+
+ if (linepad)
+ {
+ success = g_output_stream_write_all (output, &tmp, linepad,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+ }
+
+ src += width;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ out:
+ g_free (data);
+ g_object_unref (buffer);
+
+ return success;
+}
+
+static gboolean
+save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ gint height, width;
+ gint linepad;
+ gint tile_height;
+ gint i;
+ glong tmp = 0;
+ guchar *data, *src;
+ L_XWDFILEHEADER xwdhdr;
+ const Babl *format;
+ GeglBuffer *buffer;
+ gboolean success = TRUE;
+
+#ifdef XWD_DEBUG
+ g_printf ("save_rgb ()\n");
+#endif
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ tile_height = gimp_tile_height ();
+ format = babl_format ("R'G'B' u8");
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar,
+ tile_height * width *
+ babl_format_get_bytes_per_pixel (format));
+
+ linepad = (width * 3) % 4;
+ if (linepad)
+ linepad = 4 - linepad;
+
+ /* Fill in the XWD header (header_size is evaluated by write_xwd_hdr ()) */
+ xwdhdr.l_header_size = 0;
+ xwdhdr.l_file_version = 7;
+ xwdhdr.l_pixmap_format = 2;
+ xwdhdr.l_pixmap_depth = 24;
+ xwdhdr.l_pixmap_width = width;
+ xwdhdr.l_pixmap_height = height;
+ xwdhdr.l_xoffset = 0;
+ xwdhdr.l_byte_order = 1;
+
+ xwdhdr.l_bitmap_unit = 32;
+ xwdhdr.l_bitmap_bit_order = 1;
+ xwdhdr.l_bitmap_pad = 32;
+ xwdhdr.l_bits_per_pixel = 24;
+
+ xwdhdr.l_bytes_per_line = width * 3 + linepad;
+ xwdhdr.l_visual_class = 5;
+ xwdhdr.l_red_mask = 0xff0000;
+ xwdhdr.l_green_mask = 0x00ff00;
+ xwdhdr.l_blue_mask = 0x0000ff;
+ xwdhdr.l_bits_per_rgb = 8;
+ xwdhdr.l_colormap_entries = 0;
+ xwdhdr.l_ncolors = 0;
+ xwdhdr.l_window_width = width;
+ xwdhdr.l_window_height = height;
+ xwdhdr.l_window_x = 64;
+ xwdhdr.l_window_y = 64;
+ xwdhdr.l_window_bdrwidth = 0;
+
+ success = write_xwd_header (output, &xwdhdr, error);
+ if (! success)
+ goto out;
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0) /* Get more data */
+ {
+ gint scan_lines = (i + tile_height - 1 < height) ? tile_height : (height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = data;
+ }
+
+ success = g_output_stream_write_all (output, src, width * 3,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+
+ if (linepad)
+ {
+ success = g_output_stream_write_all (output, &tmp, linepad,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+ }
+
+ src += width * 3;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ out:
+ g_free (data);
+ g_object_unref (buffer);
+
+ return success;
+}