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 46 kB image not shown  

Quelle  file-pcx.c   Sprache: C

 

/*
 * pcx.c GIMP plug-in for loading & exporting PCX files
 *
 * 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/>.
 */


/* This code is based in parts on code by Francisco Bustamante, but the
   largest portion of the code has been rewritten and is now maintained
   occasionally by Nick Lamb njl195@zepler.org.uk */


#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-pcx-load"
#define LOAD_PROC_DCX  "file-dcx-load"
#define EXPORT_PROC    "file-pcx-export"
#define PLUG_IN_BINARY "file-pcx"
#define PLUG_IN_ROLE   "gimp-file-pcx"


typedef struct _Pcx      Pcx;
typedef struct _PcxClass PcxClass;

struct _Pcx
{
  GimpPlugIn      parent_instance;
};

struct _PcxClass
{
  GimpPlugInClass parent_class;
};


#define PCX_TYPE  (pcx_get_type ())
#define PCX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PCX_TYPE, Pcx))

GType                   pcx_get_type         (void) G_GNUC_CONST;

static GList          * pcx_query_procedures (GimpPlugIn            *plug_in);
static GimpProcedure  * pcx_create_procedure (GimpPlugIn            *plug_in,
                                              const gchar           *name);

static GimpValueArray * pcx_load             (GimpProcedure         *procedure,
                                              GimpRunMode            run_mode,
                                              GFile                 *file,
                                              GimpMetadata          *metadata,
                                              GimpMetadataLoadFlags *flags,
                                              GimpProcedureConfig   *config,
                                              gpointer               run_data);
static GimpValueArray * dcx_load             (GimpProcedure         *procedure,
                                              GimpRunMode            run_mode,
                                              GFile                 *file,
                                              GimpMetadata          *metadata,
                                              GimpMetadataLoadFlags *flags,
                                              GimpProcedureConfig   *config,
                                              gpointer               run_data);
static GimpValueArray * pcx_export           (GimpProcedure         *procedure,
                                              GimpRunMode            run_mode,
                                              GimpImage             *image,
                                              GFile                 *file,
                                              GimpExportOptions     *options,
                                              GimpMetadata          *metadata,
                                              GimpProcedureConfig   *config,
                                              gpointer               run_data);

static GimpImage      * load_single          (GimpProcedure         *procedure,
                                              GFile                 *file,
                                              GObject               *config,
                                              gint                   run_mode,
                                              GError               **error);
static GimpImage      * load_multi           (GimpProcedure         *procedure,
                                              GFile                 *file,
                                              GObject               *config,
                                              gint                   run_mode,
                                              GError               **error);

static GimpImage      * load_image           (GimpProcedure         *procedure,
                                              FILE                  *fd,
                                              const gchar           *filename,
                                              GObject               *config,
                                              gint                   run_mode,
                                              gint                   image_num,
                                              GError               **error);
static gboolean         pcx_load_dialog      (GimpProcedure         *procedure,
                                              GObject               *config);

static void             load_1               (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              guchar                *buf,
                                              guint16                bytes);
static void             load_4               (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              guchar                *buf,
                                              guint16                bytes);
static void             load_sub_8           (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              gint                   bpp,
                                              gint                   plane,
                                              guchar                *buf,
                                              guint16                bytes);
static void             load_8               (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              guchar                *buf,
                                              guint16                bytes);
static void             load_24              (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              guchar                *buf,
                                              guint16                bytes,
                                              guint8                 planes);
static void             readline             (FILE                  *fp,
                                              guchar                *buf,
                                              gint                   bytes);

static gboolean         export_image         (GFile                 *file,
                                              GimpImage             *image,
                                              GimpDrawable          *drawable,
                                              GError               **error);
static void             save_less_than_8     (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              const gint             bpp,
                                              const guchar          *buf,
                                              gboolean               padding);
static void             save_8               (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              const guchar          *buf,
                                              gboolean               padding);
static void             save_24              (FILE                  *fp,
                                              gint                   width,
                                              gint                   height,
                                              const guchar          *buf,
                                              gboolean               padding);
static void             writeline            (FILE                  *fp,
                                              const guchar          *buf,
                                              gint                   bytes);


G_DEFINE_TYPE (Pcx, pcx, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (PCX_TYPE)
DEFINE_STD_SET_I18N


static void
pcx_class_init (PcxClass *klass)
{
  GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);

  plug_in_class->query_procedures = pcx_query_procedures;
  plug_in_class->create_procedure = pcx_create_procedure;
  plug_in_class->set_i18n         = STD_SET_I18N;
}

static void
pcx_init (Pcx *pcx)
{
}

static GList *
pcx_query_procedures (GimpPlugIn *plug_in)
{
  GList *list = NULL;

  list = g_list_append (list, g_strdup (LOAD_PROC));
  list = g_list_append (list, g_strdup (LOAD_PROC_DCX));
  list = g_list_append (list, g_strdup (EXPORT_PROC));

  return list;
}

static GimpProcedure *
pcx_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,
                                           pcx_load, NULL, NULL);

      gimp_procedure_set_menu_label (procedure, _("ZSoft PCX image"));

      gimp_procedure_set_documentation (procedure,
                                        _("Loads files in Zsoft PCX file format"),
                                        "FIXME: write help for pcx_load",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Francisco Bustamante & Nick Lamb",
                                      "Nick Lamb <njl195@zepler.org.uk>",
                                      "January 1997");

      gimp_procedure_add_choice_argument (procedure, "override-palette",
                                          _("_Palette Options"),
                                          _("Whether to use the built-in palette or "
                                            "a black and white palette for 1 bit images."),
                                          gimp_choice_new_with_values ("use-built-in-palette", 0, _("Use PCX image's built-in palette"), NULL,
                                                                       "use-bw-palette",       1, _("Use black and white palette"),      NULL,
                                                                       NULL),
                                          "use-built-in-palette",
                                          G_PARAM_READWRITE);

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-pcx");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "pcx,pcc");
      gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
                                      "0&,byte,10,2&,byte,1,3&,byte,>0,3,byte,<9");
    }
  else if (! strcmp (name, LOAD_PROC_DCX))
    {
      procedure = gimp_load_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
                                           dcx_load, NULL, NULL);

      gimp_procedure_set_menu_label (procedure, _("ZSoft DCX image"));

      gimp_procedure_set_documentation (procedure,
                                        _("Loads files in Zsoft DCX file format"),
                                        "FIXME: write help for dcx_load",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Francisco Bustamante, Nick Lamb, Alex S.",
                                      "Alex S.",
                                      "2023");

      gimp_procedure_add_choice_argument (procedure, "override-palette",
                                          _("_Palette Options"),
                                          _("Whether to use the built-in palette or "
                                            "a black and white palette for 1 bit images."),
                                          gimp_choice_new_with_values ("use-built-in-palette", 0, _("Use PCX image's built-in palette"), NULL,
                                                                       "use-bw-palette",       1, _("Use black and white palette"),      NULL,
                                                                       NULL),
                                          "use-built-in-palette",
                                          G_PARAM_READWRITE);

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-dcx");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "dcx");
      gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
                                      "0,string,\xB1\x68\xDE\x3A");
    }
  else if (! strcmp (name, EXPORT_PROC))
    {
      procedure = gimp_export_procedure_new (plug_in, name,
                                             GIMP_PDB_PROC_TYPE_PLUGIN,
                                             FALSE, pcx_export, NULL, NULL);

      gimp_procedure_set_image_types (procedure, "INDEXED, RGB, GRAY");

      gimp_procedure_set_menu_label (procedure, _("ZSoft PCX image"));

      gimp_procedure_set_documentation (procedure,
                                        _("Exports files in ZSoft PCX file format"),
                                        "FIXME: write help for pcx_export",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Francisco Bustamante & Nick Lamb",
                                      "Nick Lamb <njl195@zepler.org.uk>",
                                      "January 1997");

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-pcx");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "pcx,pcc");

      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 *
pcx_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_single (procedure, file, G_OBJECT (config), run_mode, &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 *
dcx_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_multi (procedure, file, G_OBJECT (config), run_mode, &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 *
pcx_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 struct
{
  guint8  manufacturer;
  guint8  version;
  guint8  compression;
  guint8  bpp;
  guint16 x1, y1;
  guint16 x2, y2;
  guint16 hdpi;
  guint16 vdpi;
  guint8  colormap[48];
  guint8  reserved;
  guint8  planes;
  guint16 bytesperline;
  guint16 color;
  guint8  filler[58];
} pcx_header;

static struct {
  size_t   size;
  gpointer address;
} const pcx_header_buf_xlate[] = {
  { 1,  &pcx_header.manufacturer },
  { 1,  &pcx_header.version      },
  { 1,  &pcx_header.compression  },
  { 1,  &pcx_header.bpp          },
  { 2,  &pcx_header.x1           },
  { 2,  &pcx_header.y1           },
  { 2,  &pcx_header.x2           },
  { 2,  &pcx_header.y2           },
  { 2,  &pcx_header.hdpi         },
  { 2,  &pcx_header.vdpi         },
  { 48, &pcx_header.colormap     },
  { 1,  &pcx_header.reserved     },
  { 1,  &pcx_header.planes       },
  { 2,  &pcx_header.bytesperline },
  { 2,  &pcx_header.color        },
  { 58, &pcx_header.filler       },
  { 0,  NULL }
};

static void
pcx_header_from_buffer (guint8 *buf)
{
  gint i;
  gint buf_offset = 0;

  for (i = 0; pcx_header_buf_xlate[i].size != 0; i++)
    {
      memmove (pcx_header_buf_xlate[i].address, buf + buf_offset,
               pcx_header_buf_xlate[i].size);
      buf_offset += pcx_header_buf_xlate[i].size;
    }
}

static void
pcx_header_to_buffer (guint8 *buf)
{
  gint i;
  gint buf_offset = 0;

  for (i = 0; pcx_header_buf_xlate[i].size != 0; i++)
    {
      memmove (buf + buf_offset, pcx_header_buf_xlate[i].address,
               pcx_header_buf_xlate[i].size);
      buf_offset += pcx_header_buf_xlate[i].size;
    }
}

static GimpImage *
load_single (GimpProcedure  *procedure,
             GFile          *file,
             GObject        *config,
             gint            run_mode,
             GError        **error)
{
  FILE      *fd;
  GimpImage *image;

  gimp_progress_init_printf (_("Opening '%s'"),
                             gimp_file_get_utf8_name (file));

  fd = g_fopen (g_file_peek_path (file), "rb");

  if (! fd)
    {
      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));
      return NULL;
    }

  image = load_image (procedure, fd, gimp_file_get_utf8_name (file),
                      G_OBJECT (config), run_mode, 0, error);
  fclose (fd);

  if (! image)
    {
      g_prefix_error (error,
                      _("Could not load PCX image: "));
      return NULL;
    }

  gimp_progress_update (1.0);

  return image;
}

static GimpImage *
load_multi (GimpProcedure  *procedure,
            GFile          *file,
            GObject        *config,
            gint            run_mode,
            GError        **error)
{
  FILE      *fd;
  GimpImage *image       = NULL;
  GimpImage *temp_image  = NULL;
  gint       offset;
  gint       next_id     = 8;
  gint       valid_offset;

  gimp_progress_init_printf (_("Opening '%s'"),
                             gimp_file_get_utf8_name (file));

  fd = g_fopen (g_file_peek_path (file), "rb");

  if (! fd)
    {
      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));
      return NULL;
    }

  /* Skip header */
  fread (&offset, 1, 4, fd);

  /* Read the first offset */
  fread (&offset, 1, 4, fd);
  valid_offset = fseek (fd, offset, SEEK_SET);

  if (valid_offset != 0)
    {
      fclose (fd);
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("DCX image offset exceeds the file size"));
      return NULL;
    }

  image = load_image (procedure, fd, gimp_file_get_utf8_name (file),
                      G_OBJECT (config), run_mode, 0, error);

  if (! image)
    {
      fclose (fd);
      g_prefix_error (error,
                      _("Could not load DCX image: "));
      return NULL;
    }

  fseek (fd, next_id, SEEK_SET);

  /* DCX can hold a maximum of 1023 images,
   * plus a terminal value of 0 */

  for (gint i = 1; i < 1023; i++)
    {
      GimpLayer **layers;
      GimpLayer  *new_layer;

      fread (&offset, 1, 4, fd);

      if (offset == 0)
        break;

      valid_offset = fseek (fd, offset, SEEK_SET);
      if (valid_offset != 0)
        {
          fclose (fd);
          g_message (_("%s: DCX image offset exceeds the file size: %s\n"),
                     G_STRFUNC, (*error)->message);
          g_clear_error (error);
          return image;
        }

      temp_image = load_image (procedure, fd, gimp_file_get_utf8_name (file),
                               G_OBJECT (config), run_mode, i, error);

      if (temp_image)
        {
          layers = gimp_image_get_layers (temp_image);
          new_layer = gimp_layer_new_from_drawable (GIMP_DRAWABLE (layers[0]),
                                                    image);
          gimp_item_set_name (GIMP_ITEM (new_layer),
                              g_file_get_basename (file));
          if (! gimp_image_insert_layer (image, new_layer, NULL, 0))
            g_message (_("Mixed-mode DCX image not loaded"));

          g_free (layers);
          gimp_image_delete (temp_image);
        }
      else
        {
          fclose (fd);
          g_message (_("%s: Could not load all DCX images: %s\n"),
                     G_STRFUNC, (*error)->message);
          g_clear_error (error);
          return image;
        }

      next_id += 4;
      fseek (fd, next_id, SEEK_SET);
    }

  if (image)
    fclose (fd);

  gimp_progress_update (1.0);

  return image;
}

static GimpImage *
load_image (GimpProcedure  *procedure,
            FILE           *fd,
            const gchar    *filename,
            GObject        *config,
            gint            run_mode,
            gint            image_num,
            GError        **error)
{
  GeglBuffer   *buffer;
  guint16       offset_x, offset_y, bytesperline, computed_bytesperline;
  gint32        width, height;
  guint16       resolution_x, resolution_y;
  GimpImage    *image;
  GimpLayer    *layer;
  guchar       *dest, cmap[768];
  guint8        header_buf[128];
  gboolean      override_palette = FALSE;

  if (fread (header_buf, 128, 1, fd) == 0)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Could not read header from '%s'"),
                   filename);
      return NULL;
    }

  pcx_header_from_buffer (header_buf);

  if (pcx_header.manufacturer != 10)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a PCX file"),
                   filename);
      return NULL;
    }

  offset_x     = GUINT16_FROM_LE (pcx_header.x1);
  offset_y     = GUINT16_FROM_LE (pcx_header.y1);
  width        = GUINT16_FROM_LE (pcx_header.x2) - offset_x + 1;
  height       = GUINT16_FROM_LE (pcx_header.y2) - offset_y + 1;
  bytesperline = GUINT16_FROM_LE (pcx_header.bytesperline);
  resolution_x = GUINT16_FROM_LE (pcx_header.hdpi);
  resolution_y = GUINT16_FROM_LE (pcx_header.vdpi);

  if ((width <= 0) || (width > GIMP_MAX_IMAGE_SIZE))
    {
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   _("Unsupported or invalid image width: %d"),
                   width);
      return NULL;
    }
  if ((height <= 0) || (height > GIMP_MAX_IMAGE_SIZE))
    {
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   _("Unsupported or invalid image height: %d"),
                   height);
      return NULL;
    }

  if (pcx_header.bpp != 1 && pcx_header.bpp != 2 && pcx_header.bpp != 4 &&
      pcx_header.bpp != 8)
    {
      /* FIXME: After string freeze this should be changed to a more descriptive error. */
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   _("Unusual PCX flavour, giving up"));
      return NULL;
    }

  /* Some legacy images have incorrect values for bytesperline, that are
   * off by 1. To be able to load these, we will allow a difference of 1 here.
   * However, that means we need to allocate 1 more byte than officially
   * required to make sure we don't cause a buffer overrun.
   * For larger differences we will compute the value of bytesperline.
   */

  computed_bytesperline = (width * pcx_header.bpp + 7) / 8;
  if (bytesperline + 1 < computed_bytesperline || bytesperline == 0)
    {
      g_message (_("Invalid number of bytes per line in PCX header"));
      bytesperline = (width * pcx_header.bpp + 7) / 8;
    }

  if ((resolution_x < 1) || (resolution_x > GIMP_MAX_RESOLUTION) ||
      (resolution_y < 1) || (resolution_y > GIMP_MAX_RESOLUTION))
    {
      g_message (_("Resolution out of bounds in XCX header, using 72x72"));
      resolution_x = 72;
      resolution_y = 72;
    }

  /* Shield against potential buffer overflows in load_*() functions. */
  if (G_MAXSIZE / width / height < 3)
    {
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   _("Image dimensions too large: width %d x height %d"),
                   width, height);
      return NULL;
    }

  if (pcx_header.planes == 3 && pcx_header.bpp == 8)
    {
      image = gimp_image_new (width, height, GIMP_RGB);
      layer = gimp_layer_new (image, _("Background"), width, height,
                              GIMP_RGB_IMAGE,
                              100,
                              gimp_image_get_default_new_layer_mode (image));
    }
  else if (pcx_header.planes == 4 && pcx_header.bpp == 8)
    {
      image = gimp_image_new (width, height, GIMP_RGB);
      layer = gimp_layer_new (image, _("Background"), width, height,
                              GIMP_RGBA_IMAGE,
                              100,
                              gimp_image_get_default_new_layer_mode (image));
    }
  else
    {
      image = gimp_image_new (width, height, GIMP_INDEXED);
      layer = gimp_layer_new (image, _("Background"), width, height,
                              GIMP_INDEXED_IMAGE,
                              100,
                              gimp_image_get_default_new_layer_mode (image));
    }

  gimp_image_set_resolution (image, resolution_x, resolution_y);

  gimp_image_insert_layer (image, layer, NULL, 0);
  gimp_layer_set_offsets (layer, offset_x, offset_y);

  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));

  if (pcx_header.planes == 1 && pcx_header.bpp == 1)
    {
      const guint8 *colormap = pcx_header.colormap;
      dest = g_new (guchar, ((gsize) width) * height);
      load_1 (fd, width, height, dest, bytesperline);

      if (run_mode == GIMP_RUN_INTERACTIVE)
        {
          override_palette = gimp_procedure_config_get_choice_id (GIMP_PROCEDURE_CONFIG (config),
                                                                  "override-palette");

          /* Only show dialogue once for DCX import */
          if (image_num == 0 && pcx_load_dialog (procedure, config))
            override_palette = gimp_procedure_config_get_choice_id (GIMP_PROCEDURE_CONFIG (config),
                                                                    "override-palette");
        }
      /* Monochrome does not mean necessarily B&W. Therefore we still
       * want to check the header palette, even for just 2 colors.
       * Hopefully the header palette will always be filled with
       * meaningful colors and the creator software did not just assume
       * B&W by being monochrome.
       * Until now test samples showed that even when B&W the header
       * palette was correctly filled with these 2 colors and we didn't
       * find counter-examples.
       * See bug 159947, comment 21 and 23.
       */

      /* ... Actually, there *are* files out there with a zeroed 1-bit palette,
       * which are supposed to be displayed as B&W (see issue #2997.)  These
       * files *might* be in the wrong (who knows...) but the fact is that
       * other software, including older versions of GIMP, do display them
       * "correctly", so let's follow suit: if the two palette colors are
       * equal (or if the user chooses the option in the load dialog),
       * use a B&W palette instead.
       */

      if (! memcmp (colormap, colormap + 3, 3) || override_palette)
        {
          static const guint8 bw_colormap[6] = {  0,   0,   0,
                                                255, 255, 255};
          colormap = bw_colormap;
        }
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 (guint8 *) colormap, 6);
    }
  else if (pcx_header.bpp == 1 && pcx_header.planes == 2)
    {
      dest = g_new (guchar, ((gsize) width) * height);
      load_sub_8 (fd, width, height, 1, 2, dest, bytesperline);
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 pcx_header.colormap, 4 * 3);
    }
  else if (pcx_header.bpp == 2 && pcx_header.planes == 1)
    {
      dest = g_new (guchar, ((gsize) width) * height);
      load_sub_8 (fd, width, height, 2, 1, dest, bytesperline);
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 pcx_header.colormap, 4 * 3);
    }
  else if (pcx_header.bpp == 1 && pcx_header.planes == 3)
    {
      dest = g_new (guchar, ((gsize) width) * height);
      load_sub_8 (fd, width, height, 1, 3, dest, bytesperline);
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 pcx_header.colormap, 8 * 3);
    }
  else if (pcx_header.bpp == 1 && pcx_header.planes == 4)
    {
      dest = g_new (guchar, ((gsize) width) * height);
      load_4 (fd, width, height, dest, bytesperline);
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 pcx_header.colormap, 16 * 3);
    }
  else if (pcx_header.bpp == 4 && pcx_header.planes == 1)
    {
      dest = g_new (guchar, ((gsize) width) * height);
      load_sub_8 (fd, width, height, 4, 1, dest, bytesperline);
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 pcx_header.colormap, 16 * 3);
    }
  else if (pcx_header.bpp == 8 && pcx_header.planes == 1)
    {
      dest = g_new (guchar, ((gsize) width) * height);
      load_8 (fd, width, height, dest, bytesperline);
      fseek (fd, -768L, SEEK_END);
      fread (cmap, 768, 1, fd);
      gimp_palette_set_colormap (gimp_image_get_palette (image),
                                 babl_format ("R'G'B' u8"),
                                 cmap, 256 * 3);
    }
  else if (pcx_header.bpp == 8 && (pcx_header.planes == 3 || pcx_header.planes == 4))
    {
      dest = g_new (guchar, ((gsize) width) * height * pcx_header.planes);
      load_24 (fd, width, height, dest, bytesperline, pcx_header.planes);
    }
  else
    {
      /* FIXME: flavour is British English, should be flavor. */
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   _("Unusual PCX flavour, giving up"));
      g_object_unref (buffer);
      return NULL;
    }

  gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
                   NULL, dest, GEGL_AUTO_ROWSTRIDE);

  g_free (dest);
  g_object_unref (buffer);

  return image;
}

static gboolean
pcx_load_dialog (GimpProcedure *procedure,
                 GObject       *config)
{
  GtkWidget    *dialog;
  gboolean      run;

  gimp_ui_init (PLUG_IN_BINARY);

  dialog = gimp_procedure_dialog_new (procedure,
                                      GIMP_PROCEDURE_CONFIG (config),
                                      _("Import from PCX"));

  gimp_procedure_dialog_get_widget (GIMP_PROCEDURE_DIALOG (dialog),
                                    "override-palette", GIMP_TYPE_INT_RADIO_FRAME);

  gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
                              NULL);

  gtk_widget_show (dialog);

  run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));

  gtk_widget_destroy (dialog);

  return run;
}

static void
load_8 (FILE    *fp,
        gint     width,
        gint     height,
        guchar  *buf,
        guint16  bytes)
{
  gint    row;
  guchar *line = g_new0 (guchar, bytes + 1);

  for (row = 0; row < height; buf += width, ++row)
    {
      readline (fp, line, bytes);
      memcpy (buf, line, width);
      gimp_progress_update ((double) row / (double) height);
    }

  g_free (line);
}

static void
load_24 (FILE    *fp,
         gint     width,
         gint     height,
         guchar  *buf,
         guint16  bytes,
         guint8   planes)
{
  gint    x, y, c;
  guchar *line = g_new0 (guchar, bytes + 1);

  for (y = 0; y < height; buf += width * planes, ++y)
    {
      for (c = 0; c < planes; ++c)
        {
          readline (fp, line, bytes);
          for (x = 0; x < width; ++x)
            {
              buf[x * planes + c] = line[x];
            }
        }
      gimp_progress_update ((double) y / (double) height);
    }

  g_free (line);
}

static void
load_1 (FILE    *fp,
        gint     width,
        gint     height,
        guchar  *buf,
        guint16  bytes)
{
  gint    x, y;
  guchar *line = g_new0 (guchar, bytes + 1);

  for (y = 0; y < height; buf += width, ++y)
    {
      readline (fp, line, bytes);
      for (x = 0; x < width; ++x)
        {
          if (line[x / 8] & (128 >> (x % 8)))
            buf[x] = 1;
          else
            buf[x] = 0;
        }
      gimp_progress_update ((double) y / (double) height);
    }

  g_free (line);
}

static void
load_4 (FILE    *fp,
        gint     width,
        gint     height,
        guchar  *buf,
        guint16  bytes)
{
  gint    x, y, c;
  guchar *line = g_new0 (guchar, bytes + 1);

  for (y = 0; y < height; buf += width, ++y)
    {
      for (x = 0; x < width; ++x)
        buf[x] = 0;
      for (c = 0; c < 4; ++c)
        {
          readline(fp, line, bytes);
          for (x = 0; x < width; ++x)
            {
              if (line[x / 8] & (128 >> (x % 8)))
                buf[x] += (1 << c);
            }
        }
      gimp_progress_update ((double) y / (double) height);
    }

  g_free (line);
}

static void
load_sub_8 (FILE    *fp,
            gint     width,
            gint     height,
            gint     bpp,
            gint     plane,
            guchar  *buf,
            guint16  bytes)
{
  gint    x, y, c, b;
  guchar *line = g_new0 (guchar, bytes + 1);
  gint    real_bpp = bpp - 1;
  gint    current_bit = 0;

  for (y = 0; y < height; buf += width, ++y)
    {
      for (x = 0; x < width; ++x)
        buf[x] = 0;
      for (c = 0; c < plane; ++c)
        {
          readline (fp, line, bytes);
          for (x = 0; x < width; ++x)
            {
              for (b = 0; b < bpp; b++)
                {
                  current_bit = bpp * x + b;
                  if (line[current_bit / 8] & (128 >> (current_bit % 8)))
                    buf[x] += (1 << (real_bpp - b + c));
                }
            }
        }
      gimp_progress_update ((double) y / (double) height);
    }

  g_free (line);
}

static void
readline (FILE   *fp,
          guchar *buf,
          gint    bytes)
{
  static guchar count = 0, value = 0;

  if (pcx_header.compression)
    {
      while (bytes--)
        {
          if (count == 0)
            {
              value = fgetc (fp);
              if (value < 0xc0)
                {
                  count = 1;
                }
              else
                {
                  count = value - 0xc0;
                  value = fgetc (fp);
                }
            }
          count--;
          *(buf++) = value;
        }
    }
  else
    {
      fread (buf, bytes, 1, fp);
    }
}

static gboolean
export_image (GFile         *file,
              GimpImage     *image,
              GimpDrawable  *drawable,
              GError       **error)
{
  FILE          *fp;
  GeglBuffer    *buffer;
  const Babl    *format;
  GimpImageType  drawable_type;
  guchar        *cmap= NULL;
  guchar        *pixels;
  gint           offset_x, offset_y;
  guint          width, height;
  gdouble        resolution_x, resolution_y;
  gint           colors, i;
  guint8         header_buf[128];
  gboolean       padding = FALSE;

  drawable_type = gimp_drawable_type (drawable);
  gimp_drawable_get_offsets (drawable, &offset_x, &offset_y);

  buffer = gimp_drawable_get_buffer (drawable);

  width  = gegl_buffer_get_width  (buffer);
  height = gegl_buffer_get_height (buffer);

  gimp_progress_init_printf (_("Exporting '%s'"),
                             gimp_file_get_utf8_name (file));

  pcx_header.manufacturer = 0x0a;
  pcx_header.version      = 5;
  pcx_header.compression  = 1;

  switch (drawable_type)
    {
    case GIMP_INDEXED_IMAGE:
      cmap = gimp_palette_get_colormap (gimp_image_get_palette (image),
                                        babl_format ("R'G'B' u8"), &colors, NULL);

      if (colors > 16)
        {
          pcx_header.bpp            = 8;
          pcx_header.planes         = 1;
          pcx_header.bytesperline   = width;
        }
      else if (colors > 2)
        {
          pcx_header.bpp            = 4;
          pcx_header.planes         = 1;
          pcx_header.bytesperline   = (width + 1) / 2;
        }
      else
       {
          pcx_header.bpp            = 1;
          pcx_header.planes         = 1;
          pcx_header.bytesperline   = (width + 7) / 8;
        }
      pcx_header.color        = GUINT16_TO_LE (1);
      format                  = NULL;

      /* Some references explain that 2bpp/1plane and 4bpp/1plane files
       * would use the palette at EOF (not the one from the header) if
       * we are in version 5 of PCX. Other sources affirm that even in
       * version 5, EOF palette must be used only when there are more
       * than 16 colors. We go with this second assumption.
       * See bug 159947, comment 21 and 23.
       */

      if (colors <= 16)
        {
          for (i = 0; i < (colors * 3); i++)
            {
              pcx_header.colormap[i] = cmap[i];
            }
        }

      break;

    case GIMP_RGB_IMAGE:
      pcx_header.bpp          = 8;
      pcx_header.planes       = 3;
      pcx_header.color        = GUINT16_TO_LE (1);
      pcx_header.bytesperline = width;
      format                  = babl_format ("R'G'B' u8");
      break;

    case GIMP_GRAY_IMAGE:
      pcx_header.bpp          = 8;
      pcx_header.planes       = 1;
      pcx_header.color        = GUINT16_TO_LE (2);
      pcx_header.bytesperline = width;
      format                  = babl_format ("Y' u8");
      break;

    default:
      g_message (_("Cannot export images with alpha channel."));
      return FALSE;
  }

  /* Bytes per Line must be an even number, according to spec */
  if (pcx_header.bytesperline % 2 != 0)
    {
      pcx_header.bytesperline++;
      padding = TRUE;
    }
  pcx_header.bytesperline = GUINT16_TO_LE (pcx_header.bytesperline);

  pixels = (guchar *) g_malloc (width * height * pcx_header.planes);

  gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
                   format, pixels,
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

  if ((offset_x < 0) || (offset_x > (1<<16)))
    {
      g_message (_("Invalid X offset: %d"), offset_x);
      return FALSE;
    }

  if ((offset_y < 0) || (offset_y > (1<<16)))
    {
      g_message (_("Invalid Y offset: %d"), offset_y);
      return FALSE;
    }

  if (offset_x + width - 1 > (1<<16))
    {
      g_message (_("Right border out of bounds (must be < %d): %d"), (1<<16),
                 offset_x + width - 1);
      return FALSE;
    }

  if (offset_y + height - 1 > (1<<16))
    {
      g_message (_("Bottom border out of bounds (must be < %d): %d"), (1<<16),
                 offset_y + height - 1);
      return FALSE;
    }

  fp = g_fopen (g_file_peek_path (file), "wb");

  if (! fp)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for writing: %s"),
                   gimp_file_get_utf8_name (file), g_strerror (errno));
      return FALSE;
    }

  pcx_header.x1 = GUINT16_TO_LE ((guint16)offset_x);
  pcx_header.y1 = GUINT16_TO_LE ((guint16)offset_y);
  pcx_header.x2 = GUINT16_TO_LE ((guint16)(offset_x + width - 1));
  pcx_header.y2 = GUINT16_TO_LE ((guint16)(offset_y + height - 1));

  gimp_image_get_resolution (image, &resolution_x, &resolution_y);

  pcx_header.hdpi = GUINT16_TO_LE (RINT (MAX (resolution_x, 1.0)));
  pcx_header.vdpi = GUINT16_TO_LE (RINT (MAX (resolution_y, 1.0)));
  pcx_header.reserved = 0;

  pcx_header_to_buffer (header_buf);

  fwrite (header_buf, 128, 1, fp);

  switch (drawable_type)
    {
    case GIMP_INDEXED_IMAGE:
      if (colors > 16)
        {
          save_8 (fp, width, height, pixels, padding);
          fputc (0x0c, fp);
          fwrite (cmap, colors, 3, fp);
          for (i = colors; i < 256; i++)
            {
              fputc (0, fp);
              fputc (0, fp);
              fputc (0, fp);
            }
        }
      else /* Covers 1 and 4 bpp */
        {
          save_less_than_8 (fp, width, height, pcx_header.bpp, pixels, padding);
        }
      break;

    case GIMP_RGB_IMAGE:
      save_24 (fp, width, height, pixels, padding);
      break;

    case GIMP_GRAY_IMAGE:
      save_8 (fp, width, height, pixels, padding);
      fputc (0x0c, fp);
      for (i = 0; i < 256; i++)
        {
          fputc ((guchar) i, fp);
          fputc ((guchar) i, fp);
          fputc ((guchar) i, fp);
        }
      break;

    default:
      return FALSE;
    }

  g_object_unref (buffer);
  g_free (pixels);

  if (fclose (fp) != 0)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Writing to file '%s' failed: %s"),
                   gimp_file_get_utf8_name (file), g_strerror (errno));
      return FALSE;
    }

  return TRUE;
}

static void
save_less_than_8 (FILE         *fp,
                  gint          width,
                  gint          height,
                  const gint    bpp,
                  const guchar *buf,
                  gboolean      padding)
{
  const gint  bit_limit     = (8 - bpp);
  const gint  buf_size      = width * height;
  const gint  line_end      = width - 1;
  gint        j             = bit_limit;
  gint        count         = 0;
  guchar      byte_to_write = 0x00;
  guchar     *line;
  gint        x;

  line = (guchar *) g_malloc (((width + 7) / 8) * bpp);

  for (x = 0; x < buf_size; x++)
    {
      byte_to_write |= (buf[x] << j);
      j -= bpp;

      if (j < 0 || (x % width == line_end))
        {
          line[count] = byte_to_write;
          count++;
          byte_to_write = 0x00;
          j = bit_limit;

          if ((x % width == line_end))
            {
              writeline (fp, line, count);
              count = 0;
              if (padding)
                fputc ('\0', fp);
              gimp_progress_update ((double) x / (double) buf_size);
            }
        }
    }
  g_free (line);
}

static void
save_8 (FILE         *fp,
        gint          width,
        gint          height,
        const guchar *buf,
        gboolean      padding)
{
  int row;

  for (row = 0; row < height; ++row)
    {
      writeline (fp, buf, width);
      buf += width;
      if (padding)
        fputc ('\0', fp);
      gimp_progress_update ((double) row / (double) height);
    }
}

static void
save_24 (FILE         *fp,
         gint          width,
         gint          height,
         const guchar *buf,
         gboolean      padding)
{
  int     x, y, c;
  guchar *line;

  line = (guchar *) g_malloc (width);

  for (y = 0; y < height; ++y)
    {
      for (c = 0; c < 3; ++c)
        {
          for (x = 0; x < width; ++x)
            {
              line[x] = buf[(3*x) + c];
            }
          writeline (fp, line, width);
          if (padding)
            fputc ('\0', fp);
        }
      buf += width * 3;
      gimp_progress_update ((double) y / (double) height);
    }
  g_free (line);
}

static void
writeline (FILE         *fp,
           const guchar *buf,
           gint          bytes)
{
  const guchar *finish = buf + bytes;
  guchar        value;
  guchar        count;

  while (buf < finish)
    {
      value = *(buf++);
      count = 1;

      while (buf < finish && count < 63 && *buf == value)
        {
          count++; buf++;
        }

      if (value < 0xc0 && count == 1)
        {
          fputc (value, fp);
        }
      else
        {
          fputc (0xc0 + count, fp);
          fputc (value, fp);
        }
    }
}

Messung
CHZG
82979792

¤ Dauer der Verarbeitung: 0.29 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.