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

Quelle  ico-export.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 *
 * GIMP Plug-in for Windows Icon files.
 * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
 *
 * 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/>.
 */


#include "config.h"

#include <errno.h>
#include <string.h>

#include <glib/gstdio.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include <png.h>

/* #define ICO_DBG */

#include "ico.h"
#include "ico-load.h"
#include "ico-export.h"
#include "ico-dialog.h"

#include "libgimp/stdplugins-intl.h"


static gint     ico_write_int8         (FILE    *fp,
                                        guint8  *data,
                                        gint     count);
static gint     ico_write_int16        (FILE    *fp,
                                        guint16 *data,
                                        gint     count);
static gint     ico_write_int32        (FILE    *fp,
                                        guint32 *data,
                                        gint     count);

/* Helpers to set bits in a *cleared* data chunk */
static void     ico_set_bit_in_data    (guint8 *data,
                                        gint    line_width,
                                        gint    bit_num,
                                        gint    bit_val);
static void     ico_set_nibble_in_data (guint8 *data,
                                        gint    line_width,
                                        gint    nibble_num,
                                        gint    nibble_val);
static void     ico_set_byte_in_data   (guint8 *data,
                                        gint    line_width,
                                        gint    byte_num,
                                        gint    byte_val);

static gint     ico_get_layer_num_colors  (GimpLayer            *layer,
                                           gboolean             *uses_alpha_levels);
static void     ico_image_get_reduced_buf (GimpDrawable         *layer,
                                           gint                  bpp,
                                           GimpPalette         **cmap_out,
                                           guchar              **buf_out);

static gboolean ico_save_init             (GimpImage            *image,
                                           gint32                run_mode,
                                           IcoSaveInfo          *info,
                                           gint                  n_hot_spot_x,
                                           const gint32         *hot_spot_x,
                                           gint                  n_hot_spot_y,
                                           const gint32         *hot_spot_y,
                                           GError              **error);
static GimpPDBStatusType
                shared_save_image         (GFile                *file,
                                           FILE                 *fp_ani,
                                           GimpImage            *image,
                                           GimpProcedure        *procedure,
                                           GimpProcedureConfig  *config,
                                           gint32                run_mode,
                                           gsize                *n_hot_spot_x,
                                           const gint32         *hot_spot_x,
                                           gint32              **new_hot_spot_x,
                                           gsize                *n_hot_spot_y,
                                           const gint32         *hot_spot_y,
                                           gint32              **new_hot_spot_y,
                                           gint32                file_offset,
                                           gint                  icon_index,
                                           GError              **error,
                                           IcoSaveInfo          *info);


static gint
ico_write_int32 (FILE     *fp,
                 guint32  *data,
                 gint      count)
{
  gint total;

  total = count;
  if (count > 0)
    {
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      gint i;

      for (i = 0; i < count; i++)
        data[i] = GUINT32_FROM_LE (data[i]);
#endif

      ico_write_int8 (fp, (guint8 *) data, count * 4);

#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      /* Put it back like we found it */
      for (i = 0; i < count; i++)
        data[i] = GUINT32_FROM_LE (data[i]);
#endif
    }

  return total * 4;
}


static gint
ico_write_int16 (FILE     *fp,
                 guint16  *data,
                 gint      count)
{
  gint total;

  total = count;
  if (count > 0)
    {
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      gint i;

      for (i = 0; i < count; i++)
        data[i] = GUINT16_FROM_LE (data[i]);
#endif

      ico_write_int8 (fp, (guint8 *) data, count * 2);

#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      /* Put it back like we found it */
      for (i = 0; i < count; i++)
        data[i] = GUINT16_FROM_LE (data[i]);
#endif
    }

  return total * 2;
}


static gint
ico_write_int8 (FILE     *fp,
                guint8   *data,
                gint      count)
{
  gint total;
  gint bytes;

  total = count;
  while (count > 0)
    {
      bytes = fwrite ((gchar *) data, sizeof (gchar), count, fp);
      if (bytes <= 0) /* something bad happened */
        break;
      count -= bytes;
      data += bytes;
    }

  return total;
}


static gboolean
ico_save_init (GimpImage     *image,
               gint32         run_mode,
               IcoSaveInfo   *info,
               gint           n_hot_spot_x,
               const gint32  *hot_spot_x,
               gint           n_hot_spot_y,
               const gint32  *hot_spot_y,
               GError       **error)
{
  GList     *iter;
  gint       num_colors;
  gint       i;
  gboolean   uses_alpha_values = FALSE;

  info->layers         = gimp_image_list_layers (image);
  info->num_icons      = g_list_length (info->layers);
  info->depths         = g_new (gint, info->num_icons);
  info->default_depths = g_new (gint, info->num_icons);
  info->compress       = g_new (gboolean, info->num_icons);
  info->hot_spot_x     = g_new0 (gint, info->num_icons);
  info->hot_spot_y     = g_new0 (gint, info->num_icons);

  if (run_mode == GIMP_RUN_NONINTERACTIVE &&
      info->is_cursor                     &&
      (n_hot_spot_x != info->num_icons ||
       n_hot_spot_y != info->num_icons))
    {
      /* While it is acceptable for interactive and last values run (in
       * such case, we just drop the previous values), we expect
       * non-interactive calls to have exactly the right numbers of
       * hot-spot values set.
       */

      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Called non-interactively with %d and %d hotspot "
                     "coordinates with an image of %d icons."),
                   n_hot_spot_x, n_hot_spot_y, info->num_icons);

      return FALSE;
    }

  /* Only use existing values if we have exactly the same number of
   * icons. Drop previous/saved values otherwise.
   */

  if (hot_spot_x && n_hot_spot_x == info->num_icons)
    {
      /* XXX This is the limit of our array arguments not self-aware of
       * their length (the separate args may be "lying" with a wrong
       * call). We may end up with a segfault if the arg array was not
       * big enough. There is not much we can really do here. I thought
       * about g_renew() but it won't initialize to 0 if the arg is
       * smaller. Also we don't want to free the original array.
       */

      for (iter = info->layers, i = 0;
           iter;
           iter = g_list_next (iter), i++)
        info->hot_spot_x[i] = hot_spot_x[i];
    }

  if (hot_spot_y && n_hot_spot_y == info->num_icons)
    {
      for (iter = info->layers, i = 0;
           iter;
           iter = g_list_next (iter), i++)
        info->hot_spot_y[i] = hot_spot_y[i];
    }

  /* Limit the color depths to values that don't cause any color loss
   * -- the user should pick these anyway, so we can save her some
   * time.  If the user wants to lose some colors, the settings can
   * always be changed in the dialog:
   */

  for (iter = info->layers, i = 0;
       iter;
       iter = g_list_next (iter), i++)
    {
      num_colors = ico_get_layer_num_colors (iter->data, &uses_alpha_values);

      if (! uses_alpha_values)
        {
          if (num_colors <= 2)
            {
              /* Let's suggest monochrome */
              info->default_depths [i] = 1;
            }
          else if (num_colors <= 16)
            {
              /* Let's suggest 4bpp */
              info->default_depths [i] = 4;
            }
          else if (num_colors <= 256)
            {
              /* Let's suggest 8bpp */
              info->default_depths [i] = 8;
            }
          else
            {
              /* Let's suggest 24bpp */
              info->default_depths [i] = 24;
            }
        }
      else
        {
          /* Otherwise, or if real alpha levels are used, stick with 32bpp */
          info->default_depths [i] = 32;
        }

      /* vista icons */
      if (gimp_drawable_get_width  (iter->data) > 255 ||
          gimp_drawable_get_height (iter->data) > 255)
        {
          info->compress[i] = TRUE;
        }
      else
        {
          info->compress[i] = FALSE;
        }
    }

  /* set with default values */
  memcpy (info->depths, info->default_depths,
          sizeof (gint) * info->num_icons);

  return TRUE;
}



static gboolean
ico_save_dialog (GimpImage            *image,
                 GimpProcedure        *procedure,
                 GimpProcedureConfig  *config,
                 IcoSaveInfo          *info,
                 AniFileHeader        *ani_header,
                 AniSaveInfo          *ani_info)
{
  GtkWidget     *dialog;
  GList         *iter;
  gint           i;
  gboolean       response;

  gimp_ui_init (PLUG_IN_BINARY);

  dialog = ico_dialog_new (image, procedure, config, info, ani_header,
                           ani_info);
  for (iter = info->layers, i = 0;
       iter;
       iter = g_list_next (iter), i++)
    {
      if (info->is_cursor)
        {
          GimpParasite *parasite = NULL;

          /* Loading hot spots for cursors if applicable */
          parasite = gimp_item_get_parasite (GIMP_ITEM (iter->data), "cur-hot-spot");

          if (parasite)
            {
              gchar   *parasite_data;
              guint32  parasite_size;
              gint x, y;

              parasite_data = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
              parasite_data = g_strndup (parasite_data, parasite_size);

              if (sscanf (parasite_data, "%i %i", &x, &y) == 2)
                {
                  info->hot_spot_x[i] = x;
                  info->hot_spot_y[i] = y;
                }

              gimp_parasite_free (parasite);
              g_free (parasite_data);
            }
        }

      /* if (gimp_layer_get_visible(layers[i])) */
      ico_dialog_add_icon (dialog, iter->data, i);
    }

  /* Scale the thing to approximately fit its content, but not too large ... */
  gtk_window_set_default_size (GTK_WINDOW (dialog),
                               -1,
                               200 + (info->num_icons > 4 ?
                                      500 : info->num_icons * 120));

  gtk_widget_set_visible (dialog, TRUE);

  response = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));

  gtk_widget_destroy (dialog);

  return response;
}

static void
ico_set_bit_in_data (guint8 *data,
                     gint    line_width,
                     gint    bit_num,
                     gint    bit_val)
{
  gint line;
  gint width32;
  gint offset;

  /* width per line in multiples of 32 bits */
  width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1);

  line = bit_num / line_width;
  offset = bit_num % line_width;
  bit_val = bit_val & 0x00000001;

  data[line * width32 * 4 + offset/8] |= (bit_val << (7 - (offset % 8)));
}


static void
ico_set_nibble_in_data (guint8 *data,
                        gint    line_width,
                        gint    nibble_num,
                        gint    nibble_val)
{
  gint line;
  gint width8;
  gint offset;

  /* width per line in multiples of 32 bits */
  width8 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1);

  line = nibble_num / line_width;
  offset = nibble_num % line_width;
  nibble_val = nibble_val & 0x0000000F;

  data[line * width8 * 4 + offset/2] |=
    (nibble_val << (4 * (1 - (offset % 2))));
}


static void
ico_set_byte_in_data (guint8 *data,
                      gint    line_width,
                      gint    byte_num,
                      gint    byte_val)
{
  gint line;
  gint width4;
  gint offset;
  gint byte;

  /* width per line in multiples of 32 bits */
  width4 = (line_width % 4 == 0 ? line_width/4 : line_width/4 + 1);

  line = byte_num / line_width;
  offset = byte_num % line_width;
  byte = byte_val & 0x000000FF;

  data[line * width4 * 4 + offset] = byte;
}


/* Create a colormap from the given buffer data */
static guint32 *
ico_create_palette (GimpPalette *reduced_cmap,
                    gint         num_colors,
                    gint        *black_slot)
{
  const Babl *format;
  guchar     *palette;
  gint        num_colors_used = 0;
  gint        i;

  if (reduced_cmap)
    num_colors_used = gimp_palette_get_color_count (reduced_cmap);

  g_return_val_if_fail (num_colors_used <= num_colors, NULL);

  palette = g_new0 (guchar, num_colors * 4);
  *black_slot = -1;

  format = babl_format ("R'G'B' u8");

  for (i = 0; i < num_colors_used; i++)
    {
      GeglColor *color;
      guchar     rgb[3];

      color = gimp_palette_get_entry_color (reduced_cmap, i);
      gegl_color_get_pixel (color, format, rgb);

      palette[i * 4 + 2] = rgb[0];
      palette[i * 4 + 1] = rgb[1];
      palette[i * 4]     = rgb[2];

      if ((rgb[0] == 0) &&
          (rgb[1] == 0) &&
          (rgb[2] == 0))
        {
          *black_slot = i;
        }
    }

  if (*black_slot == -1)
    {
      if (num_colors_used == num_colors)
        {
          D(("WARNING -- no room for black, this shouldn't happen.\n"));
          *black_slot = num_colors - 1;

          palette[(num_colors-1) * 4]     = 0;
          palette[(num_colors-1) * 4 + 1] = 0;
          palette[(num_colors-1) * 4 + 2] = 0;
        }
      else
        {
          *black_slot = num_colors_used;
        }
    }

  return (guint32 *) palette;
}


static GHashTable *
ico_create_color_to_palette_map (const guint32 *palette,
                                 gint           num_colors)
{
  GHashTable *hash;
  gint        i;

  hash = g_hash_table_new_full (g_int_hash, g_int_equal,
                                (GDestroyNotify) g_free,
                                (GDestroyNotify) g_free);

  for (i = 0; i < num_colors; i++)
    {
      const guint8 *pixel = (const guint8 *) &palette[i];
      gint         *color;
      gint         *slot;

      color = g_new (gint, 1);
      slot = g_new (gint, 1);

      *color = (pixel[2] << 16 | pixel[1] << 8 | pixel[0]);
      *slot = i;

      g_hash_table_insert (hash, color, slot);
    }

  return hash;
}

static gint
ico_get_palette_index (GHashTable *hash,
                       gint        red,
                       gint        green,
                       gint        blue)
{
  gint  color = 0;
  gint *slot;

  color = (red << 16 | green << 8 | blue);
  slot = g_hash_table_lookup (hash, &color);

  if (!slot)
    {
      return 0;
    }

  return *slot;
}

static gint
ico_get_layer_num_colors (GimpLayer *layer,
                          gboolean  *uses_alpha_levels)
{
  gint        w, h;
  gint        bpp;
  gint        num_colors = 0;
  guint       num_pixels;
  guchar     *buf;
  guchar     *src;
  guint32    *colors;
  guint32    *c;
  GHashTable *hash;
  GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
  const Babl *format;

  w = gegl_buffer_get_width  (buffer);
  h = gegl_buffer_get_height (buffer);

  num_pixels = w * h;

  switch (gimp_drawable_type (GIMP_DRAWABLE (layer)))
    {
    case GIMP_RGB_IMAGE:
      format = babl_format ("R'G'B' u8");
      break;

    case GIMP_RGBA_IMAGE:
      format = babl_format ("R'G'B'A u8");
      break;

    case GIMP_GRAY_IMAGE:
      format = babl_format ("Y' u8");
      break;

    case GIMP_GRAYA_IMAGE:
      format = babl_format ("Y'A u8");
      break;

    case GIMP_INDEXED_IMAGE:
    case GIMP_INDEXEDA_IMAGE:
      format = gegl_buffer_get_format (buffer);
      /* It is possible to count the colors of indexed image more easily
       * with gimp_image_get_palette(), but counting only the colors
       * actually used will allow more efficient bpp if possible. */

      break;

    default:
      g_return_val_if_reached (0);
    }

  bpp = babl_format_get_bytes_per_pixel (format);

  buf = src = g_new (guchar, num_pixels * bpp);

  gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                   format, buf,
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

  g_object_unref (buffer);

  hash = g_hash_table_new (g_int_hash, g_int_equal);
  *uses_alpha_levels = FALSE;

  colors = c = g_new (guint32, num_pixels);

  switch (bpp)
    {
    case 1:
      while (num_pixels--)
        {
          *c = *src;
          g_hash_table_insert (hash, c, c);
          src++;
          c++;
        }
      break;

    case 2:
      while (num_pixels--)
        {
          *c = (src[1] << 8) | src[0];
          if (src[1] != 0 && src[1] != 255)
            *uses_alpha_levels = TRUE;
          g_hash_table_insert (hash, c, c);
          src += 2;
          c++;
        }
      break;

    case 3:
      while (num_pixels--)
        {
          *c = (src[2] << 16) | (src[1] << 8) | src[0];
          g_hash_table_insert (hash, c, c);
          src += 3;
          c++;
        }
      break;

    case 4:
      while (num_pixels--)
        {
          *c = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
          if (src[3] != 0 && src[3] != 255)
            *uses_alpha_levels = TRUE;
          g_hash_table_insert (hash, c, c);
          src += 4;
          c++;
        }
      break;
    }

  num_colors = g_hash_table_size (hash);

  /* Icons with 1 bit masks are converted to
   * full transparency when loaded in GIMP.
   * If the layer is not semi-transparent, we
   * can subtract the "mask color" out to fix
   * the default depth value for 1/4/8 bpp icons.
   */

  if (! *uses_alpha_levels)
    num_colors--;

  g_hash_table_destroy (hash);

  g_free (colors);
  g_free (buf);

  return num_colors;
}

gboolean
ico_cmap_contains_black (GimpPalette *cmap)
{
  const Babl *format;
  gint        num_colors;
  gint        i;
  gboolean    success = FALSE;

  format     = babl_format ("R'G'B' u8");
  num_colors = gimp_palette_get_color_count (cmap);

  for (i = 0; i < num_colors; i++)
    {
      GeglColor *color = gimp_palette_get_entry_color (cmap, i);
      guchar     rgb[3];

      gegl_color_get_pixel (color, format, rgb);
      if (rgb[0] == 0 &&
          rgb[1] == 0 &&
          rgb[2] == 0)
        {
          g_object_unref (color);
          success = TRUE;
          break;
        }
    }

  return success;
}

static void
ico_image_get_reduced_buf (GimpDrawable *layer,
                           gint          bpp,
                           GimpPalette **cmap_out,
                           guchar      **buf_out)
{
  GimpImage   *tmp_image;
  GimpLayer   *tmp_layer;
  gint         w, h;
  guchar      *buf;
  GimpPalette *palette = NULL;
  GeglBuffer  *buffer  = gimp_drawable_get_buffer (layer);
  const Babl  *format;

  w = gegl_buffer_get_width  (buffer);
  h = gegl_buffer_get_height (buffer);

  switch (gimp_drawable_type (layer))
    {
    case GIMP_RGB_IMAGE:
      format = babl_format ("R'G'B' u8");
      break;

    case GIMP_RGBA_IMAGE:
      format = babl_format ("R'G'B'A u8");
      break;

    case GIMP_GRAY_IMAGE:
      format = babl_format ("Y' u8");
      break;

    case GIMP_GRAYA_IMAGE:
      format = babl_format ("Y'A u8");
      break;

    case GIMP_INDEXED_IMAGE:
    case GIMP_INDEXEDA_IMAGE:
      format = gegl_buffer_get_format (buffer);
      break;

    default:
      g_return_if_reached ();
    }

  buf = g_new (guchar, w * h * 4);

  if (bpp <= 8 || bpp == 24 || babl_format_get_bytes_per_pixel (format) != 4)
    {
      GimpImage   *image = gimp_item_get_image (GIMP_ITEM (layer));
      GeglBuffer  *tmp;
      gint         num_colors;
      gint         entry_num;
      GeglColor  **colors;

      tmp_image = gimp_image_new (w, h, gimp_image_get_base_type (image));
      gimp_image_undo_disable (tmp_image);

      if (gimp_drawable_is_indexed (layer))
        gimp_image_set_palette (tmp_image, gimp_image_get_palette (image));

      tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h,
                                  gimp_drawable_type (layer),
                                  100,
                                  gimp_image_get_default_new_layer_mode (tmp_image));
      gimp_image_insert_layer (tmp_image, tmp_layer, NULL, 0);

      tmp = gimp_drawable_get_buffer (GIMP_DRAWABLE (tmp_layer));

      gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                       format, buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

      gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);

      g_object_unref (tmp);

      if (! gimp_drawable_is_rgb (GIMP_DRAWABLE (tmp_layer)))
        gimp_image_convert_rgb (tmp_image);

      if (bpp <= 8)
        {
          gimp_image_convert_indexed (tmp_image,
                                      GIMP_CONVERT_DITHER_FS,
                                      GIMP_CONVERT_PALETTE_GENERATE,
                                      1 << bpp, TRUE, FALSE, "dummy");

          palette = gimp_image_get_palette (tmp_image);

          if (gimp_palette_get_color_count (palette) == (1 << bpp) &&
              ! ico_cmap_contains_black (palette))
            {
              /* Windows icons with color maps need the color black.
               * We need to eliminate one more color to make room for black.
               */


              if (gimp_drawable_is_indexed (layer))
                {
                  gimp_image_set_palette (tmp_image, gimp_image_get_palette (image));
                }
              else if (gimp_drawable_is_gray (layer))
                {
                  gimp_image_convert_grayscale (tmp_image);
                }
              else
                {
                  gimp_image_convert_rgb (tmp_image);
                }

              tmp = gimp_drawable_get_buffer (GIMP_DRAWABLE (tmp_layer));

              gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0,
                               format, buf, GEGL_AUTO_ROWSTRIDE);

              g_object_unref (tmp);

              if (! gimp_drawable_is_rgb (layer))
                gimp_image_convert_rgb (tmp_image);

              gimp_image_convert_indexed (tmp_image,
                                          GIMP_CONVERT_DITHER_FS,
                                          GIMP_CONVERT_PALETTE_GENERATE,
                                          (1<<bpp) - 1, TRUE, FALSE, "dummy");
              palette = gimp_image_get_palette (tmp_image);
            }

          /* Copy the palette over now - otherwise, it goes away when we
           * convert the image to RGB */

          *cmap_out  = gimp_palette_new ("ICO");
          num_colors = gimp_palette_get_color_count (palette);
          colors     = gimp_palette_get_colors (palette);

          for (gint i = 0; i < num_colors; i++)
            gimp_palette_add_entry (*cmap_out, NULL,  colors[i], &entry_num);
          gimp_color_array_free (colors);

          gimp_image_convert_rgb (tmp_image);
        }
      else if (bpp == 24)
        {
          gimp_drawable_merge_new_filter (GIMP_DRAWABLE (tmp_layer),
                                          "gimp:threshold-alpha", NULL,
                                          GIMP_LAYER_MODE_REPLACE, 1.0,
                                          "value", ICO_ALPHA_THRESHOLD / 255.0,
                                          NULL);
        }

      gimp_layer_add_alpha (tmp_layer);

      tmp = gimp_drawable_get_buffer (GIMP_DRAWABLE (tmp_layer));

      gegl_buffer_get (tmp, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                       NULL, buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

      g_object_unref (tmp);

      gimp_image_delete (tmp_image);
    }
  else
    {
      gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                       format, buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
    }

  g_object_unref (buffer);

  *buf_out = buf;
}

static gboolean
ico_write_png (FILE         *fp,
               GimpDrawable *layer,
               gint32        depth)
{
  png_structp   png_ptr;
  png_infop     info_ptr;
  png_byte    **row_pointers;
  gint          i, rowstride;
  gint          width, height;
  GimpPalette  *palette;
  guchar       *buf;

  row_pointers = NULL;
  palette = NULL;
  buf = NULL;

  width = gimp_drawable_get_width (layer);
  height = gimp_drawable_get_height (layer);

  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if ( !png_ptr )
    return FALSE;

  info_ptr = png_create_info_struct (png_ptr);
  if ( !info_ptr )
    {
      png_destroy_write_struct (&png_ptr, NULL);
      return FALSE;
    }

  if (setjmp (png_jmpbuf (png_ptr)))
    {
      png_destroy_write_struct (&png_ptr, &info_ptr);
      if ( row_pointers )
        g_free (row_pointers);
      if (buf)
        g_free (buf);
      return FALSE;
    }

  ico_image_get_reduced_buf (layer, depth, &palette, &buf);

  png_init_io (png_ptr, fp);
  png_set_IHDR (png_ptr, info_ptr, width, height,
                8,
                PNG_COLOR_TYPE_RGBA,
                PNG_INTERLACE_NONE,
                PNG_COMPRESSION_TYPE_DEFAULT,
                PNG_FILTER_TYPE_DEFAULT);
  png_write_info (png_ptr, info_ptr);

  rowstride = ico_rowstride (width, 32);
  row_pointers = g_new (png_byte*, height);
  for (i = 0; i < height; i++)
    {
      row_pointers[i] = buf + rowstride * i;
    }
  png_write_image (png_ptr, row_pointers);

  row_pointers = NULL;

  png_write_end (png_ptr, info_ptr);
  png_destroy_write_struct (&png_ptr, &info_ptr);

  g_free (row_pointers);
  g_free (buf);
  return TRUE;
}

static gboolean
ico_write_icon (FILE         *fp,
                GimpDrawable *layer,
                gint32        depth)
{
  IcoFileDataHeader  header;
  gint               and_len, xor_len, palette_index, x, y;
  gint               num_colors = 0, black_index = 0;
  gint               width, height;
  guchar            *buf = NULL, *pixel;
  guint32           *buf32;
  GimpPalette       *reduced_palette;
  GHashTable        *color_to_slot = NULL;
  guchar            *xor_map, *and_map;

  guint32           *palette32 = NULL;
  gint               palette_len = 0;

  guint8             alpha_threshold;

  D(("Creating data structures for icon %i ------------------------\n",
     num_icon));

  width = gimp_drawable_get_width (layer);
  height = gimp_drawable_get_height (layer);

  header.header_size    = 40;
  header.width          = width;
  header.height         = 2 * height;
  header.planes         = 1;
  header.bpp            = depth;
  header.compression    = 0;
  header.image_size     = 0;
  header.x_res          = 0;
  header.y_res          = 0;
  header.used_clrs      = 0;
  header.important_clrs = 0;

  num_colors = (1L << header.bpp);

  D(("  header size %i, w %i, h %i, planes %i, bpp %i\n",
     header.header_size, header.width, header.height, header.planes,
     header.bpp));

  /* Reduce colors in copy of image */
  ico_image_get_reduced_buf (layer, header.bpp, &reduced_palette, &buf);
  buf32 = (guint32 *) buf;

  /* Set up colormap and and_map when necessary: */
  if (header.bpp <= 8)
    {
      gint num_colors_used = gimp_palette_get_color_count (reduced_palette);

      /* Create a colormap */
      palette32 = ico_create_palette (reduced_palette, num_colors, &black_index);
      palette_len = num_colors * 4;

      color_to_slot = ico_create_color_to_palette_map (palette32, num_colors_used);
      D(("  created %i-slot colormap with %i colors, black at slot %i\n",
         num_colors, num_colors_used, black_index));
    }

  /* Create and_map. It's padded out to 32 bits per line: */
  and_map = ico_alloc_map (width, height, 1, &and_len);

  /* 32-bit bitmaps have an alpha channel as well as a mask. Any partially or
   * fully opaque pixel should have an opaque mask (some ICO code in Windows
   * draws pixels as black if they have a transparent mask but a non-transparent
   * alpha value).
   *
   * For bitmaps without an alpha channel, we use the normal threshold to build
   * the mask, so that the mask is as close as possible to the original alpha
   * channel.
   */

  alpha_threshold = header.bpp < 32 ? ICO_ALPHA_THRESHOLD : 0;

  for (y = 0; y < height; y++)
    for (x = 0; x < width; x++)
      {
        pixel = (guint8 *) &buf32[y * width + x];

        ico_set_bit_in_data (and_map, width,
                             (height - y -1) * width + x,
                             (pixel[3] > alpha_threshold ? 0 : 1));
      }

  xor_map = ico_alloc_map (width, height, header.bpp, &xor_len);

  /* Now fill in the xor map */
  switch (header.bpp)
    {
    case 1:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
            pixel = (guint8 *) &buf32[y * width + x];
            palette_index = ico_get_palette_index (color_to_slot, pixel[0],
                                                   pixel[1], pixel[2]);

            if (ico_get_bit_from_data (and_map, width,
                                       (height - y - 1) * width + x))
              {
                ico_set_bit_in_data (xor_map, width,
                                     (height - y -1) * width + x,
                                     black_index);
              }
            else
              {
                ico_set_bit_in_data (xor_map, width,
                                     (height - y -1) * width + x,
                                     palette_index);
              }
          }
      break;

    case 4:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
            pixel = (guint8 *) &buf32[y * width + x];
            palette_index = ico_get_palette_index(color_to_slot, pixel[0],
                                                  pixel[1], pixel[2]);

            if (ico_get_bit_from_data (and_map, width,
                                       (height - y - 1) * width + x))
              {
                ico_set_nibble_in_data (xor_map, width,
                                        (height - y -1) * width + x,
                                        black_index);
              }
            else
              {
                ico_set_nibble_in_data (xor_map, width,
                                        (height - y - 1) * width + x,
                                        palette_index);
              }
          }
      break;

    case 8:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
            pixel = (guint8 *) &buf32[y * width + x];
            palette_index = ico_get_palette_index (color_to_slot,
                                                   pixel[0],
                                                   pixel[1],
                                                   pixel[2]);

            if (ico_get_bit_from_data (and_map, width,
                                       (height - y - 1) * width + x))
              {
                ico_set_byte_in_data (xor_map, width,
                                      (height - y - 1) * width + x,
                                      black_index);
              }
            else
              {
                ico_set_byte_in_data (xor_map, width,
                                      (height - y - 1) * width + x,
                                      palette_index);
              }

          }
      break;

    case 24:
      for (y = 0; y < height; y++)
        {
          guchar *row = xor_map + (xor_len * (height - y - 1) / height);

          for (x = 0; x < width; x++)
            {
              pixel = (guint8 *) &buf32[y * width + x];

              row[0] = pixel[2];
              row[1] = pixel[1];
              row[2] = pixel[0];

              row += 3;
            }
        }
      break;

    default:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
            pixel = (guint8 *) &buf32[y * width + x];

            ((guint32 *) xor_map)[(height - y -1) * width + x] =
              GUINT32_TO_LE ((pixel[0] << 16) |
                             (pixel[1] << 8)  |
                             (pixel[2])       |
                             (pixel[3] << 24));
          }
    }

  D(("  filled and_map of length %i, xor_map of length %i\n",
     and_len, xor_len));

  if (color_to_slot)
    g_hash_table_destroy (color_to_slot);

  g_free (buf);

  ico_write_int32 (fp, (guint32*) &header, 3);
  ico_write_int16 (fp, &header.planes, 2);
  ico_write_int32 (fp, &header.compression, 6);

  if (palette_len)
    ico_write_int8 (fp, (guint8 *) palette32, palette_len);

  ico_write_int8 (fp, xor_map, xor_len);
  ico_write_int8 (fp, and_map, and_len);

  g_free (palette32);
  g_free (xor_map);
  g_free (and_map);

  return TRUE;
}

static void
ico_save_info_free (IcoSaveInfo  *info)
{
  g_free (info->depths);
  g_free (info->default_depths);
  g_free (info->compress);
  g_list_free (info->layers);
  g_free (info->hot_spot_x);
  g_free (info->hot_spot_y);
  memset (info, 0, sizeof (IcoSaveInfo));
}

GimpPDBStatusType
ico_export_image (GFile                *file,
                  GimpImage            *image,
                  GimpProcedure        *procedure,
                  GimpProcedureConfig  *config,
                  gint32                run_mode,
                  GError              **error)
{
  IcoSaveInfo info;

  D(("*** Exporting Microsoft icon file %s\n",
     gimp_file_get_utf8_name (file)));

  info.is_cursor = FALSE;

  return shared_save_image (file, NULL, image, procedure,
                            config, run_mode,
                            0, NULL, NULL, 0, NULL, NULL,
                            0, 0, error, &info);
}

GimpPDBStatusType
cur_export_image (GFile                *file,
                  GimpImage            *image,
                  GimpProcedure        *procedure,
                  GimpProcedureConfig  *config,
                  gint32                run_mode,
                  gsize                *n_hot_spot_x,
                  const gint32         *hot_spot_x,
                  gint32              **new_hot_spot_x,
                  gsize                *n_hot_spot_y,
                  const gint32         *hot_spot_y,
                  gint32              **new_hot_spot_y,
                  GError              **error)
{
  IcoSaveInfo info;

  D(("*** Exporting Microsoft cursor file %s\n",
     gimp_file_get_utf8_name (file)));

  info.is_cursor = TRUE;

  return shared_save_image (file, NULL, image, procedure,
                            config, run_mode,
                            n_hot_spot_x, hot_spot_x, new_hot_spot_x,
                            n_hot_spot_y, hot_spot_y, new_hot_spot_y,
                            0, 0, error, &info);
}

/* Ported from James Huang's ani.c code, under the GPL v3 license */
GimpPDBStatusType
ani_export_image (GFile                *file,
                  GimpImage            *image,
                  GimpProcedure        *procedure,
                  GimpProcedureConfig  *config,
                  gint32                run_mode,
                  gsize                *n_hot_spot_x,
                  const gint32         *hot_spot_x,
                  gint32              **new_hot_spot_x,
                  gsize                *n_hot_spot_y,
                  const gint32         *hot_spot_y,
                  gint32              **new_hot_spot_y,
                  AniFileHeader        *header,
                  AniSaveInfo          *ani_info,
                  GError              **error)
{
  FILE         *fp;
  gint32        i;
  gchar        *str;
  GimpParasite *parasite = NULL;
  gchar         id[5];
  guint32       size;
  guint8        padding       = 0;
  gint32        offset, ofs_size_riff, ofs_size_list, ofs_size_icon;
  gint32        ofs_size_info = 0;
  gint32        ofs_metadata  = 0;
  IcoSaveInfo   info;

  if (! ico_save_init (image, run_mode, &info,
                       *n_hot_spot_x, hot_spot_x,
                       *n_hot_spot_y, hot_spot_y,
                       error))
    {
      return GIMP_PDB_EXECUTION_ERROR;
    }

  /* Save individual frames as .cur so we can retain
   * the hotspot information
   */

  info.is_cursor = TRUE;

  /* Default header values */
  header->bSizeOf = sizeof (*header);
  header->frames = info.num_icons;
  header->steps = info.num_icons;
  header->x = 0;
  header->y = 0;
  if (info.depths[0] == 24)
    {
      header->bpp = 4;
      header->planes = 1;
    }
  else
    {
      header->bpp = 0;
      header->planes = 0;
    }
  header->flags = 1;

  /* Load metadata from parasite */
  parasite = gimp_image_get_parasite (image, "ani-header");
  if (parasite)
    {
      gchar   *parasite_data;
      guint32  parasite_size;
      gint     jif_rate;

      parasite_data = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
      parasite_data = g_strndup (parasite_data, parasite_size);

      if (sscanf (parasite_data, "%i", &jif_rate) == 1)
        {
          header->jif_rate = jif_rate;
        }

      gimp_parasite_free (parasite);
      g_free (parasite_data);
    }

  parasite = gimp_image_get_parasite (image, "ani-info-inam");
  if (parasite)
    {
      guint32  parasite_size;
      gchar   *inam = NULL;

      inam = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
      ani_info->inam = g_strndup (inam, parasite_size);

      gimp_parasite_free (parasite);
    }

  parasite = gimp_image_get_parasite (image, "ani-info-iart");
  if (parasite)
    {
      guint32  parasite_size;
      gchar   *iart = NULL;

      iart = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
      ani_info->iart = g_strndup (iart, parasite_size);

      gimp_parasite_free (parasite);
    }

  if (run_mode == GIMP_RUN_INTERACTIVE)
    {
      if (! ico_save_dialog (image, procedure, config, &info,
                             header, ani_info))
        return GIMP_PDB_CANCEL;

      for (i = 1; i < info.num_icons; i++)
        {
          info.depths[i] = info.depths[0];
          info.default_depths[i] = info.default_depths[0];
          info.compress[i] = info.compress[0];
        }
    }

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

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

  /* Writing the .ani header data */
  strcpy (id, "RIFF");
  size = 0;
  fwrite (id, 4, 1, fp);
  ofs_size_riff = ftell (fp);
  fwrite (&size, sizeof (size), 1, fp);

  strcpy (id, "ACON");
  fwrite (id, 4, 1, fp);

  if ((ani_info->inam && strlen (ani_info->inam) > 0) ||
      (ani_info->iart && strlen (ani_info->iart) > 0))
    {
      gint32 string_size;

      strcpy (id, "LIST");
      fwrite (id, 4, 1, fp);
      ofs_size_info = ftell (fp);
      fwrite (&size, sizeof (size), 1, fp);

      strcpy (id, "INFO");
      fwrite (id, 4, 1, fp);
      if (ani_info->inam && strlen (ani_info->inam) > 0) /* Cursor name */
        {
          strcpy (id, "INAM");
          fwrite (id, 4, 1, fp);
          string_size = strlen (ani_info->inam) + 1;
          fwrite (&string_size, 4, 1, fp);
          fwrite (ani_info->inam, string_size, 1, fp);
          ofs_metadata += 4;

          /* Length of metadata must be even. */
          if (string_size % 2 != 0)
            fwrite (&padding, sizeof (padding), 1, fp);
        }
      if (ani_info->iart && strlen (ani_info->iart) > 0) /* Author name */
        {
          strcpy (id, "IART");
          fwrite (id, 4, 1, fp);
          string_size = strlen (ani_info->iart) + 1;
          fwrite (&string_size, 4, 1, fp);
          fwrite (ani_info->iart, string_size, 1, fp);
          ofs_metadata += 4;

          if (string_size % 2 != 0)
            fwrite (&padding, sizeof (padding), 1, fp);
        }

      /* Go back and update info list size */
      fseek (fp, 0L, SEEK_END);
      size = ftell (fp) - ofs_size_info - 4;
      fseek (fp, ofs_size_info, SEEK_SET);
      fwrite (&size, sizeof (size), 1, fp);
      fseek (fp, 0L, SEEK_END);
    }

  strcpy (id, "anih");
  size = sizeof (*header);
  fwrite (id, 4, 1, fp);
  fwrite (&size, sizeof (size), 1, fp);
  fwrite (header, sizeof (*header), 1, fp);

  strcpy (id, "LIST");
  fwrite (id, 4, 1, fp);
  ofs_size_list = ftell (fp);
  fwrite (&size, sizeof (size), 1, fp);

  strcpy (id, "fram");
  fwrite (id, 4, 1, fp);

  strcpy (id, "icon");
  for (i = 0; i < info.num_icons; i++ )
    {
      GimpPDBStatusType status;
      fwrite (id, 4, 1, fp);
      ofs_size_icon = ftell (fp);
      fwrite (&size, sizeof (size), 1, fp);
      offset = ftell (fp);
      status = shared_save_image (file, fp, image, procedure,
                                  config, run_mode,
                                  n_hot_spot_x, hot_spot_x, new_hot_spot_x,
                                  n_hot_spot_y, hot_spot_y, new_hot_spot_y,
                                  offset, i, error, &info);

      if (status != GIMP_PDB_SUCCESS)
        {
          ico_save_info_free (&info);
          g_clear_pointer (&ani_info->inam, g_free);
          g_clear_pointer (&ani_info->iart, g_free);
          fclose (fp);
          return GIMP_PDB_EXECUTION_ERROR;
        }
      fseek (fp, 0L, SEEK_END);
      size = ftell (fp) - offset;
      fseek (fp, ofs_size_icon, SEEK_SET);
      fwrite (&size, sizeof (size), 1, fp);
      fseek (fp, 0L, SEEK_END);

      gimp_progress_update ((gdouble) i / (gdouble) info.num_icons);
    }
  ico_save_info_free (&info);

  fseek (fp, 0L, SEEK_END);
  size = ftell (fp);
  size -= ofs_metadata;
  fseek (fp, ofs_size_riff, SEEK_SET);
  fwrite (&size, sizeof (size), 1, fp);

  size -= ofs_size_list;
  size += (ofs_metadata - 4);
  fseek (fp, ofs_size_list, SEEK_SET);
  fwrite (&size, sizeof (size), 1, fp);
  fclose (fp);

  /* Update metadata if needed */
  str = g_strdup_printf ("%d", header->jif_rate);
  parasite = gimp_parasite_new ("ani-header",
                                GIMP_PARASITE_PERSISTENT,
                                strlen (str) + 1, (gpointer) str);
  g_free (str);
  gimp_image_attach_parasite (image, parasite);
  gimp_parasite_free (parasite);

  if (ani_info->inam && strlen (ani_info->inam) > 0)
    {
      str = g_strdup_printf ("%s", ani_info->inam);
      parasite = gimp_parasite_new ("ani-info-inam",
                                    GIMP_PARASITE_PERSISTENT,
                                    strlen (ani_info->inam) + 1, (gpointer) str);
      g_free (str);
      gimp_image_attach_parasite (image, parasite);
      gimp_parasite_free (parasite);
    }
  if (ani_info->iart && strlen (ani_info->iart) > 0)
    {
      str = g_strdup_printf ("%s", ani_info->iart);
      parasite = gimp_parasite_new ("ani-info-iart",
                                    GIMP_PARASITE_PERSISTENT,
                                    strlen (ani_info->iart) + 1, (gpointer) str);
      g_free (str);
      gimp_image_attach_parasite (image, parasite);
      gimp_parasite_free (parasite);
    }

  gimp_progress_update (1.0);

  return GIMP_PDB_SUCCESS;
}

GimpPDBStatusType
shared_save_image (GFile                *file,
                   FILE                 *fp_ani,
                   GimpImage            *image,
                   GimpProcedure        *procedure,
                   GimpProcedureConfig  *config,
                   gint32                run_mode,
                   gsize                *n_hot_spot_x,
                   const gint32         *hot_spot_x,
                   gint32              **new_hot_spot_x,
                   gsize                *n_hot_spot_y,
                   const gint32         *hot_spot_y,
                   gint32              **new_hot_spot_y,
                   gint32                file_offset,
                   gint                  icon_index,
                   GError              **error,
                   IcoSaveInfo          *info)
{
  FILE          *fp;
  GList         *iter;
  gint           width;
  gint           height;
  IcoFileHeader  header;
  IcoFileEntry  *entries;
  gboolean       saved;
  gint           i;
  gint           num_icons;
  GimpParasite  *parasite = NULL;
  gchar         *str;

  if (! fp_ani &&
      ! ico_save_init (image, run_mode, info,
                       n_hot_spot_x ? *n_hot_spot_x : 0,
                       hot_spot_x,
                       n_hot_spot_y ? *n_hot_spot_y : 0,
                       hot_spot_y,
                       error))
    {
      return GIMP_PDB_EXECUTION_ERROR;
    }

  if (run_mode == GIMP_RUN_INTERACTIVE && ! fp_ani)
    {
      /* Allow user to override default values */
      if (! ico_save_dialog (image, procedure, config, info,
                             NULL, NULL))
        return GIMP_PDB_CANCEL;
    }

  num_icons = (fp_ani) ? 1 : info->num_icons;

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

  /* If saving an .ani file, we append the next icon frame. */
  if (! fp_ani)
    {
      fp = g_fopen (g_file_peek_path (file), "wb");
    }
  else
    {
      fp = fp_ani;
    }

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

  header.reserved = 0;
  header.resource_type = 1;
  if (info->is_cursor)
    header.resource_type = 2;
  header.icon_count = num_icons;
  if (! ico_write_int16 (fp, &header.reserved, 1)      ||
      ! ico_write_int16 (fp, &header.resource_type, 1) ||
      ! ico_write_int16 (fp, &header.icon_count, 1))
    {
      ico_save_info_free (info);
      fclose (fp);
      return GIMP_PDB_EXECUTION_ERROR;
    }

  entries = g_new0 (IcoFileEntry, num_icons);
  if (fwrite (entries, sizeof (IcoFileEntry), num_icons, fp) <= 0)
    {
      ico_save_info_free (info);
      g_free (entries);
      fclose (fp);
      return GIMP_PDB_EXECUTION_ERROR;
    }

  for (iter = info->layers, i = 0;
       iter;
       iter = g_list_next (iter), i++)
    {
      if (! fp_ani)
        gimp_progress_update ((gdouble)i / (gdouble)info->num_icons);

      /* If saving .ani file, jump to the correct frame */
      if (fp_ani)
        iter = g_list_nth (info->layers, icon_index);

      width = gimp_drawable_get_width (iter->data);
      height = gimp_drawable_get_height (iter->data);
      if (width <= 255 && height <= 255)
        {
          entries[i].width = width;
          entries[i].height = height;
        }
      else
        {
          entries[i].width = 0;
          entries[i].height = 0;
        }
      if (info->depths[i] <= 8 )
        entries[i].num_colors = 1 << info->depths[i];
      else
        entries[i].num_colors = 0;
      entries[i].reserved = 0;
      entries[i].planes = 1;
      entries[i].bpp = info->depths[i];
      /* .cur file reuses these fields for cursor offsets */
      if (info->is_cursor)
        {
          gint hot_spot_index = icon_index ? icon_index : i;

          entries[i].planes = info->hot_spot_x[hot_spot_index];
          entries[i].bpp = info->hot_spot_y[hot_spot_index];
        }
      entries[i].offset = ftell (fp) - file_offset;

      if (info->compress[i])
        saved = ico_write_png (fp, iter->data, info->depths[i]);
      else
        saved = ico_write_icon (fp, iter->data, info->depths[i]);

      if (!saved)
        {
          ico_save_info_free (info);
          fclose (fp);
          return GIMP_PDB_EXECUTION_ERROR;
        }

      entries[i].size = ftell (fp) - file_offset - entries[i].offset;

      if (fp_ani)
        break;
    }

  for (i = 0; i < num_icons; i++)
    {
      entries[i].planes = GUINT16_TO_LE (entries[i].planes);
      entries[i].bpp    = GUINT16_TO_LE (entries[i].bpp);
      entries[i].size   = GUINT32_TO_LE (entries[i].size);
      entries[i].offset = GUINT32_TO_LE (entries[i].offset);
    }

  if (fseek (fp, sizeof (IcoFileHeader) + file_offset, SEEK_SET) < 0 ||
      fwrite (entries, sizeof (IcoFileEntry), num_icons, fp) <= 0)
    {
      ico_save_info_free (info);
      fclose (fp);
      return GIMP_PDB_EXECUTION_ERROR;
    }

  if (! fp_ani)
    gimp_progress_update (1.0);

  /* Updating parasite hot spots if needed */
  if (info->is_cursor)
    {
      for (iter = info->layers, i = 0;
           iter;
           iter = g_list_next (iter), i++)
        {
          str = g_strdup_printf ("%d %d", info->hot_spot_x[i], info->hot_spot_y[i]);
          parasite = gimp_parasite_new ("cur-hot-spot",
                                        GIMP_PARASITE_PERSISTENT,
                                        strlen (str) + 1, (gpointer) str);
          g_free (str);
          gimp_item_attach_parasite (GIMP_ITEM (iter->data), parasite);
          gimp_parasite_free (parasite);
        }
    }

  if (! fp_ani)
    {
      if (new_hot_spot_x)
        {
          *new_hot_spot_x  = info->hot_spot_x;
          info->hot_spot_x = NULL;

          *n_hot_spot_x    = num_icons;
        }
      if (new_hot_spot_y)
        {
          *new_hot_spot_y  = info->hot_spot_y;
          info->hot_spot_y = NULL;

          *n_hot_spot_y    = num_icons;
        }
    }

  /* If saving .ani file, don't clear until
   * all icons are saved in ani_save_image ()
   */

  if (! file_offset)
    {
      ico_save_info_free (info);
      fclose (fp);
    }
  g_free (entries);

  return GIMP_PDB_SUCCESS;
}

Messung
CHZG
84949892

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