Files
gimp/plug-ins/common/file-xwd.c
Jehan ab851924dd plug-ins: make various usage of g_file_replace() safer.
As I did on app/, finalizing an output stream also implicitly flushes
and closes it. Hence if an export ended with an error, we'd end up with
incomplete data file (possibly overwriting a previously exported image).
Only 2 plug-ins I haven't fixed yet are file-tiff-io and file-gif-save.
The later one don't even clean up its memory (which somehow is good here
as at least the output stream is never finalized hence sane files are
not overwritten in case of errors). As for the former (TIFF plug-in), it
doesn't even seem to have any error control AFAICS, apart from printing
error messages on standard error output.

(cherry picked from commit 66ec467217)
2018-12-07 00:51:13 +01:00

2588 lines
76 KiB
C

/* 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;
}