Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/plug-ins/common/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 85 kB image not shown  

Quelle  file-xwd.c   Sprache: 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++)
            {
              guint32 offset = ((red << redshift) + (green << greenshift) +
                                (blue << blueshift)) * 3;

              if (offset+2 >= maxval)
                {
                  g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                               _("Invalid colormap offset. Possibly corrupt image."));
                  g_free (data);
                  g_free (ColorMap);
                  g_object_unref (buffer);
                  return NULL;
                }
              blueval = (blue * 255) / maxblue;
              cm = ColorMap + offset;
              *(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++)
    {
      goffset offset = xwdcolmap[j].l_pixel * 3;

      if (offset+2 >= maxval)
        {
          g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                       _("Invalid colormap offset. Possibly corrupt image."));
          return NULL;
        }
      cm = ColorMap + offset;
      *(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;
  guint32          maxval;
  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);
  maxval      = 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)
    {
      guint current_dest = 0;

      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;

              current_dest += 2 - fromright / 8;
              if (current_dest < maxval)
                {
                  dest += 2 - fromright / 8;
                }
              else
                {
                  err = 1;
                  break;
                }

              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;

                  current_dest += bytes_per_pixel;
                  if (current_dest < maxval)
                    {
                      dest += bytes_per_pixel;
                    }
                  else
                    {
                      err = 1;
                      break;
                    }

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

Messung
CHZG
83828884

¤ Dauer der Verarbeitung: 0.57 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.