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