diff options
Diffstat (limited to 'plug-ins/common/file-xwd.c')
-rw-r--r-- | plug-ins/common/file-xwd.c | 2587 |
1 files changed, 2587 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..20943c9 --- /dev/null +++ b/plug-ins/common/file-xwd.c @@ -0,0 +1,2587 @@ +/* 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 + + 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; +} |