gimp/plug-ins/common/file-xwd.c

2744 lines
84 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 EXPORT_PROC "file-xwd-export"
#define PLUG_IN_BINARY "file-xwd"
#define PLUG_IN_ROLE "gimp-file-xwd"
typedef guint32 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 */
typedef struct _Xwd Xwd;
typedef struct _XwdClass XwdClass;
struct _Xwd
{
GimpPlugIn parent_instance;
};
struct _XwdClass
{
GimpPlugInClass parent_class;
};
#define XWD_TYPE (xwd_get_type ())
#define XWD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XWD_TYPE, Xwd))
GType xwd_get_type (void) G_GNUC_CONST;
static GList * xwd_query_procedures (GimpPlugIn *plug_in);
static GimpProcedure * xwd_create_procedure (GimpPlugIn *plug_in,
const gchar *name);
static GimpValueArray * xwd_load (GimpProcedure *procedure,
GimpRunMode run_mode,
GFile *file,
GimpMetadata *metadata,
GimpMetadataLoadFlags *flags,
GimpProcedureConfig *config,
gpointer run_data);
static GimpValueArray * xwd_export (GimpProcedure *procedure,
GimpRunMode run_mode,
GimpImage *image,
GFile *file,
GimpExportOptions *options,
GimpMetadata *metadata,
GimpProcedureConfig *config,
gpointer run_data);
static GimpImage * load_image (GFile *file,
GError **error);
static gboolean export_image (GFile *file,
GimpImage *image,
GimpDrawable *drawable,
GError **error);
static GimpImage * create_new_image (GFile *file,
guint width,
guint height,
GimpImageBaseType type,
GimpImageType gdtype,
GimpLayer **layer,
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 (GimpImage *image);
static void set_color_table (GimpImage *image,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap);
static GimpImage * load_xwd_f2_d1_b1 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error);
static GimpImage * load_xwd_f2_d8_b8 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error);
static GimpImage * load_xwd_f2_d16_b16 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error);
static GimpImage * load_xwd_f2_d24_b32 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error);
static GimpImage * load_xwd_f2_d32_b32 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error);
static GimpImage * load_xwd_f1_d24_b1 (GFile *file,
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,
GError **error);
static gboolean write_xwd_cols (GOutputStream *output,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *colormap,
GError **error);
static gint save_index (GOutputStream *output,
GimpImage *image,
GimpDrawable *drawable,
gboolean gray,
GError **error);
static gint save_rgb (GOutputStream *output,
GimpImage *image,
GimpDrawable *drawable,
GError **error);
G_DEFINE_TYPE (Xwd, xwd, GIMP_TYPE_PLUG_IN)
GIMP_MAIN (XWD_TYPE)
DEFINE_STD_SET_I18N
static void
xwd_class_init (XwdClass *klass)
{
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = xwd_query_procedures;
plug_in_class->create_procedure = xwd_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
xwd_init (Xwd *xwd)
{
}
static GList *
xwd_query_procedures (GimpPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (EXPORT_PROC));
return list;
}
static GimpProcedure *
xwd_create_procedure (GimpPlugIn *plug_in,
const gchar *name)
{
GimpProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = gimp_load_procedure_new (plug_in, name,
GIMP_PDB_PROC_TYPE_PLUGIN,
xwd_load, NULL, NULL);
gimp_procedure_set_menu_label (procedure, _("X window dump"));
gimp_procedure_set_documentation (procedure,
"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.",
name);
gimp_procedure_set_attribution (procedure,
"Peter Kirchgessner",
"Peter Kirchgessner",
"1996");
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
"image/x-xwindowdump");
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
"xwd");
gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
"4,long,0x00000007");
}
else if (! strcmp (name, EXPORT_PROC))
{
procedure = gimp_export_procedure_new (plug_in, name,
GIMP_PDB_PROC_TYPE_PLUGIN,
FALSE, xwd_export, NULL, NULL);
gimp_procedure_set_image_types (procedure, "RGB, GRAY, INDEXED");
gimp_procedure_set_menu_label (procedure, _("X window dump"));
gimp_procedure_set_documentation (procedure,
"Exports files in the XWD (X Window "
"Dump) format",
"XWD exporting handles all image "
"types except those with alpha channels.",
name);
gimp_procedure_set_attribution (procedure,
"Peter Kirchgessner",
"Peter Kirchgessner",
"1996");
gimp_file_procedure_set_handles_remote (GIMP_FILE_PROCEDURE (procedure),
TRUE);
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
"image/x-xwindowdump");
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
"xwd");
gimp_export_procedure_set_capabilities (GIMP_EXPORT_PROCEDURE (procedure),
GIMP_EXPORT_CAN_HANDLE_RGB |
GIMP_EXPORT_CAN_HANDLE_GRAY |
GIMP_EXPORT_CAN_HANDLE_INDEXED,
NULL, NULL, NULL);
}
return procedure;
}
static GimpValueArray *
xwd_load (GimpProcedure *procedure,
GimpRunMode run_mode,
GFile *file,
GimpMetadata *metadata,
GimpMetadataLoadFlags *flags,
GimpProcedureConfig *config,
gpointer run_data)
{
GimpValueArray *return_vals;
GimpImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = load_image (file, &error);
if (! image)
return gimp_procedure_new_return_values (procedure,
GIMP_PDB_EXECUTION_ERROR,
error);
return_vals = gimp_procedure_new_return_values (procedure,
GIMP_PDB_SUCCESS,
NULL);
GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static GimpValueArray *
xwd_export (GimpProcedure *procedure,
GimpRunMode run_mode,
GimpImage *image,
GFile *file,
GimpExportOptions *options,
GimpMetadata *metadata,
GimpProcedureConfig *config,
gpointer run_data)
{
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpExportReturn export = GIMP_EXPORT_IGNORE;
GList *drawables;
GError *error = NULL;
gegl_init (NULL, NULL);
export = gimp_export_options_get_image (options, &image);
drawables = gimp_image_list_layers (image);
if (! export_image (file, image, drawables->data, &error))
status = GIMP_PDB_EXECUTION_ERROR;
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image);
g_list_free (drawables);
return gimp_procedure_new_return_values (procedure, status, error);
}
static GimpImage *
load_image (GFile *file,
GError **error)
{
FILE *ifp;
gint depth, bpp;
GimpImage *image = NULL;
L_XWDFILEHEADER xwdhdr;
L_XWDCOLOR *xwdcolmap = NULL;
gimp_progress_init_printf (_("Opening '%s'"),
gimp_file_get_utf8_name (file));
ifp = g_fopen (g_file_peek_path (file), "rb");
if (! ifp)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"),
gimp_file_get_utf8_name (file), g_strerror (errno));
goto out;
}
read_xwd_header (ifp, &xwdhdr);
#ifdef XWD_DEBUG
/* Write info about header */
g_printerr ("XWD header:\n\t"
"Header size: %u, filer version: %u, image type: %u\n\t"
"Depth: %u, Width: %u, Height: %u\n\t"
"X-offset: %u, byte order: %u, bitmap unit: %u, bit order: %u\n\t"
"bitmap pad: %u, bits per pixel: %u, bytes per line: %u\n\t"
"visual class: %u, Masks: red %x, green %x, blue %x, bits per rgb: %u\n\t"
"Number of colors: %u, color map entries: %u\n\t"
"Window width: %u, height: %u, x: %u, y: %u, border width: %u"
"\n", xwdhdr.l_header_size, xwdhdr.l_file_version, xwdhdr.l_pixmap_format,
xwdhdr.l_pixmap_depth, xwdhdr.l_pixmap_width, xwdhdr.l_pixmap_height,
xwdhdr.l_xoffset, xwdhdr.l_byte_order, xwdhdr.l_bitmap_unit,
xwdhdr.l_bitmap_bit_order, xwdhdr.l_bitmap_pad, xwdhdr.l_bits_per_pixel,
xwdhdr.l_bytes_per_line, xwdhdr.l_visual_class,
xwdhdr.l_red_mask, xwdhdr.l_green_mask, xwdhdr.l_blue_mask,
xwdhdr.l_bits_per_rgb, xwdhdr.l_ncolors, xwdhdr.l_colormap_entries,
xwdhdr.l_window_width, xwdhdr.l_window_height,
xwdhdr.l_window_x, xwdhdr.l_window_y, xwdhdr.l_window_bdrwidth);
#endif
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_file_get_utf8_name (file));
goto out;
}
#ifdef XWD_COL_WAIT_DEBUG
{
int k = 1;
while (k)
k = k;
}
#endif
/* Position to start of XWDColor structures */
if (fseek (ifp, (long)xwdhdr.l_header_size, SEEK_SET) != 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nSeek error"),
gimp_file_get_utf8_name (file));
goto out;
}
/* Guard against insanely huge color maps -- gimp_palette_set_colormap() only
* accepts colormaps with 0..256 colors anyway. */
if (xwdhdr.l_colormap_entries > 256)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nIllegal number of colormap entries: %u"),
gimp_file_get_utf8_name (file),
xwdhdr.l_colormap_entries);
goto out;
}
if (xwdhdr.l_colormap_entries > 0)
{
if (xwdhdr.l_colormap_entries < xwdhdr.l_ncolors)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nNumber of colormap entries < number of colors"),
gimp_file_get_utf8_name (file));
goto out;
}
xwdcolmap = g_new (L_XWDCOLOR, xwdhdr.l_colormap_entries);
read_xwd_cols (ifp, &xwdhdr, xwdcolmap, error);
if (error && *error)
goto out;
#ifdef XWD_COL_DEBUG
{
int j;
g_printf ("File %s\n", g_file_peek_path (file));
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_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Can't read color entries"));
goto out;
}
}
if (xwdhdr.l_pixmap_width <= 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nNo image width specified"),
gimp_file_get_utf8_name (file));
goto out;
}
if (xwdhdr.l_pixmap_width > GIMP_MAX_IMAGE_SIZE
|| xwdhdr.l_bytes_per_line > GIMP_MAX_IMAGE_SIZE * 3)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nImage width is larger than GIMP can handle"),
gimp_file_get_utf8_name (file));
goto out;
}
if (xwdhdr.l_pixmap_height <= 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nNo image height specified"),
gimp_file_get_utf8_name (file));
goto out;
}
if (xwdhdr.l_pixmap_height > GIMP_MAX_IMAGE_SIZE)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nImage height is larger than GIMP can handle"),
gimp_file_get_utf8_name (file));
goto out;
}
depth = xwdhdr.l_pixmap_depth;
bpp = xwdhdr.l_bits_per_pixel;
switch (xwdhdr.l_pixmap_format)
{
case 0: /* Single plane bitmap */
if ((depth == 1) && (bpp == 1))
{ /* Can be performed by format 2 loader */
image = load_xwd_f2_d1_b1 (file, ifp, &xwdhdr, xwdcolmap, error);
}
break;
case 1: /* Single plane pixmap */
if ((depth <= 24) && (bpp == 1))
{
image = load_xwd_f1_d24_b1 (file, ifp, &xwdhdr, xwdcolmap, error);
}
break;
case 2: /* Multiplane pixmaps */
if ((depth == 1) && (bpp == 1))
{
image = load_xwd_f2_d1_b1 (file, ifp, &xwdhdr, xwdcolmap, error);
}
else if ((depth <= 8) && (bpp == 8))
{
image = load_xwd_f2_d8_b8 (file, ifp, &xwdhdr, xwdcolmap, error);
}
else if ((depth <= 16) && (bpp == 16))
{
if (xwdcolmap)
image = load_xwd_f2_d16_b16 (file, ifp, &xwdhdr, xwdcolmap, error);
}
else if ((depth <= 24) && ((bpp == 24) || (bpp == 32)))
{
image = load_xwd_f2_d24_b32 (file, ifp, &xwdhdr, xwdcolmap, error);
}
else if ((depth <= 32) && (bpp == 32))
{
image = load_xwd_f2_d32_b32 (file, ifp, &xwdhdr, xwdcolmap, error);
}
break;
}
gimp_progress_update (1.0);
if (! image && ! (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_file_get_utf8_name (file),
(gint) xwdhdr.l_pixmap_format, depth, bpp);
out:
if (ifp)
fclose (ifp);
if (xwdcolmap)
g_free (xwdcolmap);
return image;
}
static gboolean
export_image (GFile *file,
GimpImage *image,
GimpDrawable *drawable,
GError **error)
{
GOutputStream *output;
GimpImageType drawable_type;
gboolean success;
drawable_type = gimp_drawable_type (drawable);
/* Make sure we're not exporting an image with an alpha channel */
if (gimp_drawable_has_alpha (drawable))
{
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, drawable, FALSE, error);
break;
case GIMP_GRAY_IMAGE:
success = save_index (output, image, drawable, TRUE, error);
break;
case GIMP_RGB_IMAGE:
success = save_rgb (output, image, drawable, 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,
GError **error)
{
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) */
if (fseek (ifp, colmappos, SEEK_SET) != 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Seek error"));
return;
}
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) */
if (fseek (ifp, colmappos, SEEK_SET) !=0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Seek error"));
return;
}
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) */
if (fseek (ifp, colmappos, SEEK_SET) != 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Seek error"));
return;
}
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 (GimpImage *image)
{
static guchar BWColorMap[2*3] = { 255, 255, 255, 0, 0, 0 };
GimpPalette *palette = gimp_image_get_palette (image);
#ifdef XWD_COL_DEBUG
g_printf ("Set GIMP b/w-colortable:\n");
#endif
gimp_palette_set_colormap (palette, babl_format ("R'G'B' u8"), BWColorMap, 2 * 3);
}
/* 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 (GimpImage *image,
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_palette_set_colormap (gimp_image_get_palette (image), babl_format ("R'G'B' u8"), ColorMap, 256 * 3);
}
/* Create an image. Sets layer, drawable and rgn. Returns image */
static GimpImage *
create_new_image (GFile *file,
guint width,
guint height,
GimpImageBaseType type,
GimpImageType gdtype,
GimpLayer **layer,
GeglBuffer **buffer)
{
GimpImage *image;
image = gimp_image_new (width, height, type);
*layer = gimp_layer_new (image, "Background", width, height,
gdtype,
100,
gimp_image_get_default_new_layer_mode (image));
gimp_image_insert_layer (image, *layer, NULL, 0);
*buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (*layer));
return image;
}
/* Load XWD with pixmap_format 2, pixmap_depth 1, bits_per_pixel 1 */
static GimpImage *
load_xwd_f2_d1_b1 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error)
{
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;
GimpImage *image;
GimpLayer *layer;
GeglBuffer *buffer;
#ifdef XWD_DEBUG
g_printf ("load_xwd_f2_d1_b1 (%s)\n", gimp_file_get_utf8_name (file));
#endif
width = xwdhdr->l_pixmap_width;
height = xwdhdr->l_pixmap_height;
image = create_new_image (file, width, height, GIMP_INDEXED,
GIMP_INDEXED_IMAGE, &layer, &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);
else
set_color_table (image, 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;
}
}
g_free (data);
g_free (scanline);
g_object_unref (buffer);
if (err)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("EOF encountered on reading"));
return NULL;
}
return image;
}
/* Load XWD with pixmap_format 2, pixmap_depth 8, bits_per_pixel 8 */
static GimpImage *
load_xwd_f2_d8_b8 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error)
{
gint width, height, linepad, tile_height, scan_lines;
gint i, j, ncols;
gint grayscale;
guchar *dest, *data;
gint err = 0;
GimpImage *image;
GimpLayer *layer;
GeglBuffer *buffer;
#ifdef XWD_DEBUG
g_printf ("load_xwd_f2_d8_b8 (%s)\n", gimp_file_get_utf8_name (file));
#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 = create_new_image (file, width, height,
grayscale ? GIMP_GRAY : GIMP_INDEXED,
grayscale ? GIMP_GRAY_IMAGE : GIMP_INDEXED_IMAGE,
&layer, &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);
else
set_color_table (image, 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++)
if (getc (ifp) < 0)
{
err = 1;
break;
}
if (err)
break;
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);
g_object_unref (buffer);
if (err)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("EOF encountered on reading"));
return NULL;
}
return image;
}
/* Load XWD with pixmap_format 2, pixmap_depth up to 16, bits_per_pixel 16 */
static GimpImage *
load_xwd_f2_d16_b16 (GFile *file,
FILE *ifp,
L_XWDFILEHEADER *xwdhdr,
L_XWDCOLOR *xwdcolmap,
GError **error)
{
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;
guint32 redmask, greenmask, bluemask;
guint redshift, greenshift, blueshift;
guint32 maxval;
guchar *ColorMap, *cm, *data;
gint err = 0;
GimpImage *image;
GimpLayer *layer;
GeglBuffer *buffer;
#ifdef XWD_DEBUG
g_printf ("load_xwd_f2_d16_b16 (%s)\n", gimp_file_get_utf8_name (file));
#endif
width = xwdhdr->l_pixmap_width;
height = xwdhdr->l_pixmap_height;
image = create_new_image (file, width, height, GIMP_RGB,
GIMP_RGB_IMAGE, &layer, &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++)
if (getc (ifp) < 0)
{
err = 1;
break;
}
if (err)
break;
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);
g_object_unref (buffer);
if (err)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("EOF encountered on reading"));
return NULL;
}
return image;
}
/* Load XWD with pixmap_format 2, pixmap_depth up to 24, bits_per_pixel 24/32 */
static GimpImage *
load_xwd_f2_d24_b32 (GFile *file,
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;
guint32 maxred, maxgreen, maxblue;
guint32 redmask, greenmask, bluemask;
guint redshift, greenshift, blueshift;
guchar redmap[256], greenmap[256], bluemap[256];
guchar *data;
PIXEL_MAP pixel_map;
gint err = 0;
GimpImage *image;
GimpLayer *layer;
GeglBuffer *buffer;
#ifdef XWD_DEBUG
g_printf ("load_xwd_f2_d24_b32 (%s)\n", gimp_file_get_utf8_name (file));
#endif
/* issue #8082: depth and bits per pixel is 24, but 4 bytes are used per pixel */
if (xwdhdr->l_bits_per_pixel == 24)
{
if (xwdhdr->l_bytes_per_line / xwdhdr->l_pixmap_width == 4 &&
xwdhdr->l_bytes_per_line % xwdhdr->l_pixmap_width == 0)
xwdhdr->l_bits_per_pixel = 32;
}
width = xwdhdr->l_pixmap_width;
height = xwdhdr->l_pixmap_height;
redmask = xwdhdr->l_red_mask;
greenmask = xwdhdr->l_green_mask;
bluemask = xwdhdr->l_blue_mask;
if (redmask == 0) redmask = 0xff0000;
if (greenmask == 0) greenmask = 0x00ff00;
if (bluemask == 0) bluemask = 0x0000ff;
/* How to shift RGB to be right aligned ? */
/* (We rely on the the mask bits are grouped and not mixed) */
redshift = greenshift = blueshift = 0;
while (((1 << redshift) & redmask) == 0) redshift++;
while (((1 << greenshift) & greenmask) == 0) greenshift++;
while (((1 << blueshift) & bluemask) == 0) blueshift++;
/* The bits_per_rgb may not be correct. Use redmask instead */
maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
maxred = (1 << maxred) - 1;
maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
maxgreen = (1 << maxgreen) - 1;
maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
maxblue = (1 << maxblue) - 1;
if (maxred == 0 || maxred >= sizeof (redmap) ||
maxgreen == 0 || maxgreen >= sizeof (greenmap) ||
maxblue == 0 || maxblue >= sizeof (bluemap))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("XWD-file %s is corrupt."),
gimp_file_get_utf8_name (file));
return NULL;
}
image = create_new_image (file, width, height, GIMP_RGB,
GIMP_RGB_IMAGE, &layer, &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++)
if (getc (ifp) < 0)
{
err = 1;
break;
}
if (err)
break;
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++)
if (getc (ifp) < 0)
{
err = 1;
break;
}
if (err)
break;
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);
g_object_unref (buffer);
if (err)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("EOF encountered on reading"));
return NULL;
}
return image;
}
/* Load XWD with pixmap_format 2, pixmap_depth up to 32, bits_per_pixel 32 */
static GimpImage *
load_xwd_f2_d32_b32 (GFile *file,
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, alpha, ncols;
guint32 maxred, maxgreen, maxblue, maxalpha;
guint32 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;
GimpImage *image;
GimpLayer *layer;
GeglBuffer *buffer;
#ifdef XWD_DEBUG
g_printf ("load_xwd_f2_d32_b32 (%s)\n", gimp_file_get_utf8_name (file));
#endif
if (! xwdcolmap)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s':\nInvalid color map"),
gimp_file_get_utf8_name (file));
return NULL;
}
width = xwdhdr->l_pixmap_width;
height = xwdhdr->l_pixmap_height;
image = create_new_image (file, width, height, GIMP_RGB,
GIMP_RGBA_IMAGE, &layer, &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;
if (maxred == 0 || maxred >= sizeof (redmap) ||
maxgreen == 0 || maxgreen >= sizeof (greenmap) ||
maxblue == 0 || maxblue >= sizeof (bluemap) ||
maxalpha == 0 || maxalpha >= sizeof (alphamap))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("XWD-file %s is corrupt."),
gimp_file_get_utf8_name (file));
return NULL;
}
/* 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++)
if (getc (ifp) < 0)
{
err = 1;
break;
}
if (err)
break;
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);
g_object_unref (buffer);
if (err)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("EOF encountered on reading"));
return NULL;
}
return image;
}
/* Load XWD with pixmap_format 1, pixmap_depth up to 24, bits_per_pixel 1 */
static GimpImage *
load_xwd_f1_d24_b1 (GFile *file,
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;
goffset data_offset, plane_offset, tile_offset;
guint32 redmask, greenmask, bluemask;
guint redshift, greenshift, blueshift;
guint32 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;
GimpImage *image;
GimpLayer *layer;
GeglBuffer *buffer;
#ifdef XWD_DEBUG
g_printf ("load_xwd_f1_d24_b1 (%s)\n", gimp_file_get_utf8_name (file));
#endif
xwddata = g_malloc (xwdhdr->l_bytes_per_line);
if (xwddata == NULL)
return NULL;
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 == 0 || maxred >= sizeof (redmap) ||
maxgreen == 0 || maxgreen >= sizeof (greenmap) ||
maxblue == 0 || maxblue >= sizeof (bluemap))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("XWD-file %s is corrupt."),
gimp_file_get_utf8_name (file));
return NULL;
}
/* 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 = create_new_image (file, width, height,
indexed ? GIMP_INDEXED : GIMP_RGB,
indexed ? GIMP_INDEXED_IMAGE : GIMP_RGB_IMAGE,
&layer, &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);
else
set_color_table (image, 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;
if (fseek (ifp, tile_offset, SEEK_SET) != 0)
{
err = 1;
break;
}
/* 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;
}
}
if (err)
break;
}
if (err)
break;
/* 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);
g_object_unref (buffer);
if (err)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("EOF encountered on reading"));
return NULL;
}
return image;
}
static gboolean
save_index (GOutputStream *output,
GimpImage *image,
GimpDrawable *drawable,
gboolean gray,
GError **error)
{
gint height, width;
gint linepad;
gint tile_height;
gint i, j;
gint ncolors, vclass;
gsize 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);
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_palette_get_colormap (gimp_image_get_palette (image), babl_format ("R'G'B' u8"), &ncolors, NULL);
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,
GimpImage *image,
GimpDrawable *drawable,
GError **error)
{
gint height, width;
gint linepad;
gint tile_height;
gint i;
gsize 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);
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;
}