gimp/plug-ins/common/file-xwd.c
Jehan 2e1bf0e44c plug-ins: get rid of all remaining usage of gimp_image_[gs]et_colormap().
When I see that we are just using R'G'B' format with no space
everywhere, I'm pretty sure some of this code must be wrong (even though
for some formats, maybe only sRGB is supported, I am guessing that for
some others, the palette may be in specific color spaces).
This will have to be improved with time.
2024-09-23 18:20:14 +02:00

2743 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;
}