Exemplo n.º 1
0
static GimpChannel *
gimp_image_duplicate_channels (GimpImage *image,
                               GimpImage *new_image)
{
  GimpChannel *active_channel = NULL;
  GList       *list;
  gint         count;

  for (list = gimp_image_get_channel_iter (image), count = 0;
       list;
       list = g_list_next (list))
    {
      GimpChannel  *channel = list->data;
      GimpChannel  *new_channel;

      new_channel = GIMP_CHANNEL (gimp_image_duplicate_item (GIMP_ITEM (channel),
                                                             new_image));

      if (gimp_image_get_active_channel (image) == channel)
        active_channel = new_channel;

      gimp_image_add_channel (new_image, new_channel,
                              NULL, count++, FALSE);
    }

  return active_channel;
}
Exemplo n.º 2
0
static void
gimp_channel_undo_pop (GimpUndo            *undo,
                       GimpUndoMode         undo_mode,
                       GimpUndoAccumulator *accum)
{
  GimpChannelUndo *channel_undo = GIMP_CHANNEL_UNDO (undo);
  GimpChannel     *channel      = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item);

  GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);

  if ((undo_mode       == GIMP_UNDO_MODE_UNDO &&
       undo->undo_type == GIMP_UNDO_CHANNEL_ADD) ||
      (undo_mode       == GIMP_UNDO_MODE_REDO &&
       undo->undo_type == GIMP_UNDO_CHANNEL_REMOVE))
    {
      /*  remove channel  */

      /*  record the current parent and position  */
      channel_undo->prev_parent   = gimp_channel_get_parent (channel);
      channel_undo->prev_position = gimp_item_get_index (GIMP_ITEM (channel));

      gimp_image_remove_channel (undo->image, channel, FALSE,
                                 channel_undo->prev_channel);
    }
  else
    {
      /*  restore channel  */

      /*  record the active channel  */
      channel_undo->prev_channel = gimp_image_get_active_channel (undo->image);

      gimp_image_add_channel (undo->image, channel,
                              channel_undo->prev_parent,
                              channel_undo->prev_position, FALSE);
    }
}
Exemplo n.º 3
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d X C F I m a g e                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  ReadXCFImage() reads a GIMP (GNU Image Manipulation Program) image
%  file and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadXCFImage method is:
%
%      image=ReadXCFImage(image_info)
%
%  A description of each parameter follows:
%
%    o image_info: the image info.
%
%    o exception: return any errors or warnings in this structure.
%
%
*/
static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  char
    magick[14];

  Image
    *image;

  int
    foundPropEnd = 0;

  MagickBooleanType
    status;

  MagickOffsetType
    offset;

  register ssize_t
    i;

  size_t
    length;

  ssize_t
    count;

  size_t
    image_type;

  XCFDocInfo
    doc_info;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickSignature);
  image=AcquireImage(image_info);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  count=ReadBlob(image,14,(unsigned char *) magick);
  if ((count == 0) ||
      (LocaleNCompare((char *) magick,"gimp xcf",8) != 0))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  (void) ResetMagickMemory(&doc_info,0,sizeof(XCFDocInfo));
  doc_info.exception=exception;
  doc_info.width=ReadBlobMSBLong(image);
  doc_info.height=ReadBlobMSBLong(image);
  if ((doc_info.width > 262144) || (doc_info.height > 262144))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  doc_info.image_type=ReadBlobMSBLong(image);
  /*
    Initialize image attributes.
  */
  image->columns=doc_info.width;
  image->rows=doc_info.height;
  image_type=doc_info.image_type;
  doc_info.file_size=GetBlobSize(image);
  image->compression=NoCompression;
  image->depth=8;
  if (image_type == GIMP_RGB)
    image->colorspace=RGBColorspace;
  else
    if (image_type == GIMP_GRAY)
      image->colorspace=GRAYColorspace;
    else
      if (image_type == GIMP_INDEXED)
        ThrowReaderException(CoderError,"ColormapTypeNotSupported");
  (void) SetImageBackgroundColor(image);
  image->matte=MagickTrue;
  /*
    Read properties.
  */
  while ((foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse))
  {
    PropType prop_type = (PropType) ReadBlobMSBLong(image);
    size_t prop_size = ReadBlobMSBLong(image);

    switch (prop_type)
    {
      case PROP_END:
        foundPropEnd=1;
        break;
      case PROP_COLORMAP:
      {
        /* Cannot rely on prop_size here--the value is set incorrectly
           by some Gimp versions.
        */
        size_t num_colours = ReadBlobMSBLong(image);
        for (i=0; i < (ssize_t) (3L*num_colours); i++ )
          (void) ReadBlobByte(image);
    /*
      if (info->file_version == 0)
      {
        gint i;

        g_message (_("XCF warning: version 0 of XCF file format\n"
           "did not save indexed colormaps correctly.\n"
           "Substituting grayscale map."));
        info->cp +=
          xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
        gimage->cmap = g_new (guchar, gimage->num_cols*3);
        xcf_seek_pos (info, info->cp + gimage->num_cols);
        for (i = 0; i<gimage->num_cols; i++)
          {
            gimage->cmap[i*3+0] = i;
            gimage->cmap[i*3+1] = i;
            gimage->cmap[i*3+2] = i;
          }
      }
      else
      {
        info->cp +=
          xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
        gimage->cmap = g_new (guchar, gimage->num_cols*3);
        info->cp +=
          xcf_read_int8 (info->fp,
                   (guint8*) gimage->cmap, gimage->num_cols*3);
      }
     */
        break;
      }
      case PROP_COMPRESSION:
      {
        doc_info.compression = ReadBlobByte(image);
        if ((doc_info.compression != COMPRESS_NONE) &&
            (doc_info.compression != COMPRESS_RLE) &&
            (doc_info.compression != COMPRESS_ZLIB) &&
            (doc_info.compression != COMPRESS_FRACTAL))
          ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
      }
      break;

      case PROP_GUIDES:
      {
         /* just skip it - we don't care about guides */
        for (i=0; i < (ssize_t) prop_size; i++ )
          if (ReadBlobByte(image) == EOF)
            ThrowFileException(exception,CorruptImageError,
              "UnexpectedEndOfFile",image->filename);
      }
      break;

    case PROP_RESOLUTION:
      {
        /* float xres = (float) */ (void) ReadBlobMSBLong(image);
        /* float yres = (float) */ (void) ReadBlobMSBLong(image);

        /*
        if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
            yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
        {
        g_message ("Warning, resolution out of range in XCF file");
        xres = gimage->gimp->config->default_xresolution;
        yres = gimage->gimp->config->default_yresolution;
        }
        */


        /* BOGUS: we don't write these yet because we aren't
              reading them properly yet :(
              image->x_resolution = xres;
              image->y_resolution = yres;
        */
      }
      break;

    case PROP_TATTOO:
      {
        /* we need to read it, even if we ignore it */
        /*size_t  tattoo_state = */ (void) ReadBlobMSBLong(image);
      }
      break;

    case PROP_PARASITES:
      {
        /* BOGUS: we may need these for IPTC stuff */
        for (i=0; i < (ssize_t) prop_size; i++ )
          if (ReadBlobByte(image) == EOF)
            ThrowFileException(exception,CorruptImageError,
              "UnexpectedEndOfFile",image->filename);

        /*
      gssize_t         base = info->cp;
      GimpParasite *p;

      while (info->cp - base < prop_size)
        {
          p = xcf_load_parasite (info);
          gimp_image_parasite_attach (gimage, p);
          gimp_parasite_free (p);
        }
      if (info->cp - base != prop_size)
        g_message ("Error detected while loading an image's parasites");
      */
          }
      break;

    case PROP_UNIT:
      {
        /* BOGUS: ignore for now... */
      /*size_t unit =  */ (void) ReadBlobMSBLong(image);
      }
      break;

    case PROP_PATHS:
      {
      /* BOGUS: just skip it for now */
        for (i=0; i< (ssize_t) prop_size; i++ )
          if (ReadBlobByte(image) == EOF)
            ThrowFileException(exception,CorruptImageError,
              "UnexpectedEndOfFile",image->filename);

        /*
      PathList *paths = xcf_load_bzpaths (gimage, info);
      gimp_image_set_paths (gimage, paths);
      */
      }
      break;

    case PROP_USER_UNIT:
      {
        char  unit_string[1000];
        /*BOGUS: ignored for now */
        /*float  factor = (float) */ (void) ReadBlobMSBLong(image);
        /* size_t digits =  */ (void) ReadBlobMSBLong(image);
        for (i=0; i<5; i++)
         (void) ReadBlobStringWithLongSize(image, unit_string,
           sizeof(unit_string));
      }
     break;

      default:
      {
        int buf[16];
        ssize_t amount;

      /* read over it... */
      while ((prop_size > 0) && (EOFBlob(image) == MagickFalse))
      {
        amount=(ssize_t) MagickMin(16, prop_size);
        amount=(ssize_t) ReadBlob(image,(size_t) amount,(unsigned char *) &buf);
        if (!amount)
          ThrowReaderException(CorruptImageError,"CorruptImage");
        prop_size -= (size_t) MagickMin(16,(size_t) amount);
      }
    }
    break;
  }
  }
  if (foundPropEnd == MagickFalse)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");

  if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
    {
      ; /* do nothing, were just pinging! */
    }
  else
    {
      int
        current_layer = 0,
        foundAllLayers = MagickFalse,
        number_layers = 0;

      MagickOffsetType
        oldPos=TellBlob(image);

      XCFLayerInfo
        *layer_info;

      /* 
        the read pointer
      */
      do
      {
        ssize_t offset = (ssize_t) ReadBlobMSBLong(image);
        if (offset == 0)
          foundAllLayers=MagickTrue;
        else
          number_layers++;
        if (EOFBlob(image) != MagickFalse)
          {
            ThrowFileException(exception,CorruptImageError,
              "UnexpectedEndOfFile",image->filename);
            break;
          }
    } while (foundAllLayers == MagickFalse);
    offset=SeekBlob(image,oldPos,SEEK_SET); /* restore the position! */
    if (offset < 0)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    /* allocate our array of layer info blocks */
    length=(size_t) number_layers;
    layer_info=(XCFLayerInfo *) AcquireQuantumMemory(length,
      sizeof(*layer_info));
    if (layer_info == (XCFLayerInfo *) NULL)
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    (void) ResetMagickMemory(layer_info,0,number_layers*sizeof(XCFLayerInfo));
    for ( ; ; )
    {
      MagickBooleanType
        layer_ok;

      MagickOffsetType
        offset,
        saved_pos;

      /* read in the offset of the next layer */
      offset=(MagickOffsetType) ReadBlobMSBLong(image);
      /* if the offset is 0 then we are at the end
      *  of the layer list.
      */
      if (offset == 0)
        break;
      /* save the current position as it is where the
      *  next layer offset is stored.
      */
      saved_pos=TellBlob(image);
      /* seek to the layer offset */
      offset=SeekBlob(image,offset,SEEK_SET);
      /* read in the layer */
      layer_ok=ReadOneLayer(image,&doc_info,&layer_info[current_layer]);
      if (layer_ok == MagickFalse)
        {
          int j;

          for (j=0; j < current_layer; j++)
            layer_info[j].image=DestroyImage(layer_info[j].image);
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
      /* restore the saved position so we'll be ready to
      *  read the next offset.
      */
      offset=SeekBlob(image, saved_pos, SEEK_SET);
      current_layer++;
    }
    if (number_layers == 1)
      {
        /*
          Composite the layer data onto the main image, dispose the layer.
        */
        (void) CompositeImage(image,OverCompositeOp,layer_info[0].image,
          layer_info[0].offset_x,layer_info[0].offset_y);
        layer_info[0].image =DestroyImage( layer_info[0].image);
      }
    else
      {
#if 0
        {
        /* NOTE: XCF layers are REVERSED from composite order! */
        signed int  j;
        for (j=number_layers-1; j>=0; j--) {
          /* BOGUS: need to consider layer blending modes!! */

          if ( layer_info[j].visible ) { /* only visible ones, please! */
            CompositeImage(image, OverCompositeOp, layer_info[j].image,
                     layer_info[j].offset_x, layer_info[j].offset_y );
             layer_info[j].image =DestroyImage( layer_info[j].image );

            /* Bob says that if we do this, we'll get REAL gray images! */
            if ( image_type == GIMP_GRAY ) {
              QuantizeInfo  qi;
              GetQuantizeInfo(&qi);
              qi.colorspace = GRAYColorspace;
              QuantizeImage( &qi, layer_info[j].image );
            }
          }
        }
      }
#else
      {
        /* NOTE: XCF layers are REVERSED from composite order! */
        signed int  j;

        /* first we copy the last layer on top of the main image */
        (void) CompositeImage(image,CopyCompositeOp,
          layer_info[number_layers-1].image,
          layer_info[number_layers-1].offset_x,
          layer_info[number_layers-1].offset_y);
          layer_info[number_layers-1].image=DestroyImage(
            layer_info[number_layers-1].image);

        /* now reverse the order of the layers as they are put
           into subimages
        */
        j=number_layers-2;
        image->next=layer_info[j].image;
        layer_info[j].image->previous=image;
        layer_info[j].image->page.x=layer_info[j].offset_x;
        layer_info[j].image->page.y=layer_info[j].offset_y;
        layer_info[j].image->page.width=layer_info[j].width;
        layer_info[j].image->page.height=layer_info[j].height;
        for (j=number_layers-3; j>=0; j--)
        {
          if (j > 0)
            layer_info[j].image->next=layer_info[j-1].image;
          if (j < (number_layers-1))
            layer_info[j].image->previous=layer_info[j+1].image;
          layer_info[j].image->page.x=layer_info[j].offset_x;
          layer_info[j].image->page.y=layer_info[j].offset_y;
          layer_info[j].image->page.width=layer_info[j].width;
          layer_info[j].image->page.height=layer_info[j].height;
        }
      }
#endif
    }

    layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info);

#if 0  /* BOGUS: do we need the channels?? */
    while (MagickTrue)
    {
      /* read in the offset of the next channel */
      info->cp += xcf_read_int32 (info->fp, &offset, 1);

      /* if the offset is 0 then we are at the end
      *  of the channel list.
      */
      if (offset == 0)
        break;

      /* save the current position as it is where the
      *  next channel offset is stored.
      */
      saved_pos = info->cp;

      /* seek to the channel offset */
      xcf_seek_pos (info, offset);

      /* read in the layer */
      channel = xcf_load_channel (info, gimage);
      if (channel == 0)
        goto error;

      num_successful_elements++;

      /* add the channel to the image if its not the selection */
      if (channel != gimage->selection_mask)
        gimp_image_add_channel (gimage, channel, -1);

      /* restore the saved position so we'll be ready to
      *  read the next offset.
      */
      xcf_seek_pos (info, saved_pos);
    }
#endif
  }

  (void) CloseBlob(image);
  if (image_type == GIMP_GRAY)
    image->type=GrayscaleType;
  return(GetFirstImageInList(image));
}
Exemplo n.º 4
0
void
gimp_image_set_quick_mask_state (GimpImage *image,
                                 gboolean   active)
{
  GimpChannel *selection;
  GimpChannel *mask;
  gboolean     channel_was_active;

  g_return_if_fail (GIMP_IS_IMAGE (image));

  if (active == gimp_image_get_quick_mask_state (image))
    return;

  /*  Keep track of the state so that we can make the right drawable
   *  active again when deactiviting quick mask (see bug #134371).
   */
  if (image->quick_mask_state)
    channel_was_active = (image->quick_mask_state & CHANNEL_WAS_ACTIVE) != 0;
  else
    channel_was_active = gimp_image_get_active_channel (image) != NULL;

  /*  Set image->quick_mask_state early so we can return early when
   *  being called recursively.
   */
  image->quick_mask_state = (active
                             ? TRUE | (channel_was_active ?
                                       CHANNEL_WAS_ACTIVE : 0)
                             : FALSE);

  selection = gimp_image_get_mask (image);
  mask      = gimp_image_get_quick_mask (image);

  if (active)
    {
      if (! mask)
        {
          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_QUICK_MASK,
                                       _("Enable Quick Mask"));

          if (gimp_channel_is_empty (selection))
            {
              /* if no selection */

              GimpLayer *floating_sel = gimp_image_floating_sel (image);

              if (floating_sel)
                floating_sel_to_layer (floating_sel);

              mask = gimp_channel_new (image,
                                       image->width,
                                       image->height,
                                       GIMP_IMAGE_QUICK_MASK_NAME,
                                       &image->quick_mask_color);

              /* Clear the mask */
              gimp_channel_clear (mask, NULL, FALSE);
            }
          else
            {
              /* if selection */

              mask = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (selection),
                                                        GIMP_TYPE_CHANNEL,
                                                        FALSE));

              /* Clear the selection */
              gimp_channel_clear (selection, NULL, TRUE);

              gimp_channel_set_color (mask, &image->quick_mask_color, FALSE);
              gimp_item_rename (GIMP_ITEM (mask), GIMP_IMAGE_QUICK_MASK_NAME);
            }

          if (image->quick_mask_inverted)
            gimp_channel_invert (mask, FALSE);

          gimp_image_add_channel (image, mask, 0);

          gimp_image_undo_group_end (image);
        }
    }
  else
    {
      if (mask)
        {
          GimpLayer *floating_sel = gimp_image_floating_sel (image);

          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_QUICK_MASK,
                                       _("Disable Quick Mask"));

          if (image->quick_mask_inverted)
            gimp_channel_invert (mask, TRUE);

          if (floating_sel && floating_sel->fs.drawable == GIMP_DRAWABLE (mask))
            floating_sel_anchor (floating_sel);

          gimp_selection_load (gimp_image_get_mask (image), mask);
          gimp_image_remove_channel (image, mask);

          if (! channel_was_active)
            gimp_image_unset_active_channel (image);

          gimp_image_undo_group_end (image);
        }
    }

  gimp_image_quick_mask_changed (image);
}
Exemplo n.º 5
0
GimpImage *
gimp_image_duplicate (GimpImage *image)
{
  GimpImage    *new_image;
  GimpLayer    *floating_layer;
  GList        *list;
  GimpLayer    *active_layer              = NULL;
  GimpChannel  *active_channel            = NULL;
  GimpVectors  *active_vectors            = NULL;
  GimpDrawable *new_floating_sel_drawable = NULL;
  GimpDrawable *floating_sel_drawable     = NULL;
  gchar        *filename;
  gint          count;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  gimp_set_busy_until_idle (image->gimp);

  /*  Create a new image  */
  new_image = gimp_create_image (image->gimp,
                                 image->width, image->height,
                                 image->base_type,
                                 FALSE);
  gimp_image_undo_disable (new_image);

  /*  Store the folder to be used by the save dialog  */
  filename = gimp_image_get_filename (image);
  if (filename)
    {
      g_object_set_data_full (G_OBJECT (new_image), "gimp-image-dirname",
                              g_path_get_dirname (filename),
                              (GDestroyNotify) g_free);
      g_free (filename);
    }

  /*  Copy the colormap if necessary  */
  if (new_image->base_type == GIMP_INDEXED)
    gimp_image_set_colormap (new_image,
                             gimp_image_get_colormap (image),
                             gimp_image_get_colormap_size (image),
                             FALSE);

  /*  Copy resolution information  */
  new_image->xresolution     = image->xresolution;
  new_image->yresolution     = image->yresolution;
  new_image->resolution_unit = image->resolution_unit;

  /*  Copy floating layer  */
  floating_layer = gimp_image_floating_sel (image);
  if (floating_layer)
    {
      floating_sel_relax (floating_layer, FALSE);

      floating_sel_drawable = floating_layer->fs.drawable;
      floating_layer = NULL;
    }

  /*  Copy the layers  */
  for (list = GIMP_LIST (image->layers)->list, count = 0;
       list;
       list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      GimpLayer *new_layer;

      new_layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (layer),
                                                 new_image,
                                                 G_TYPE_FROM_INSTANCE (layer),
                                                 FALSE));

      /*  Make sure the copied layer doesn't say: "<old layer> copy"  */
      gimp_object_set_name (GIMP_OBJECT (new_layer),
                            gimp_object_get_name (GIMP_OBJECT (layer)));

      /*  Make sure that if the layer has a layer mask,
       *  its name isn't screwed up
       */
      if (new_layer->mask)
        gimp_object_set_name (GIMP_OBJECT (new_layer->mask),
                              gimp_object_get_name (GIMP_OBJECT (layer->mask)));

      if (gimp_image_get_active_layer (image) == layer)
        active_layer = new_layer;

      if (image->floating_sel == layer)
        floating_layer = new_layer;

      if (floating_sel_drawable == GIMP_DRAWABLE (layer))
        new_floating_sel_drawable = GIMP_DRAWABLE (new_layer);

      if (floating_layer != new_layer)
        gimp_image_add_layer (new_image, new_layer, count++);
    }

  /*  Copy the channels  */
  for (list = GIMP_LIST (image->channels)->list, count = 0;
       list;
       list = g_list_next (list))
    {
      GimpChannel *channel = list->data;
      GimpChannel *new_channel;

      new_channel =
        GIMP_CHANNEL (gimp_item_convert (GIMP_ITEM (channel),
                                         new_image,
                                         G_TYPE_FROM_INSTANCE (channel),
                                         FALSE));

      /*  Make sure the copied channel doesn't say: "<old channel> copy"  */
      gimp_object_set_name (GIMP_OBJECT (new_channel),
                            gimp_object_get_name (GIMP_OBJECT (channel)));

      if (gimp_image_get_active_channel (image) == channel)
        active_channel = (new_channel);

      if (floating_sel_drawable == GIMP_DRAWABLE (channel))
        new_floating_sel_drawable = GIMP_DRAWABLE (new_channel);

      gimp_image_add_channel (new_image, new_channel, count++);
    }

  /*  Copy any vectors  */
  for (list = GIMP_LIST (image->vectors)->list, count = 0;
       list;
       list = g_list_next (list))
    {
      GimpVectors *vectors = list->data;
      GimpVectors *new_vectors;

      new_vectors =
        GIMP_VECTORS (gimp_item_convert (GIMP_ITEM (vectors),
                                         new_image,
                                         G_TYPE_FROM_INSTANCE (vectors),
                                         FALSE));

      /*  Make sure the copied vectors doesn't say: "<old vectors> copy"  */
      gimp_object_set_name (GIMP_OBJECT (new_vectors),
                            gimp_object_get_name (GIMP_OBJECT (vectors)));

      if (gimp_image_get_active_vectors (image) == vectors)
        active_vectors = new_vectors;

      gimp_image_add_vectors (new_image, new_vectors, count++);
    }

  /*  Copy the selection mask  */
  {
    TileManager *src_tiles;
    TileManager *dest_tiles;
    PixelRegion  srcPR, destPR;

    src_tiles  =
      gimp_drawable_get_tiles (GIMP_DRAWABLE (image->selection_mask));
    dest_tiles =
      gimp_drawable_get_tiles (GIMP_DRAWABLE (new_image->selection_mask));

    pixel_region_init (&srcPR, src_tiles,
                       0, 0, image->width, image->height, FALSE);
    pixel_region_init (&destPR, dest_tiles,
                       0, 0, image->width, image->height, TRUE);

    copy_region (&srcPR, &destPR);

    new_image->selection_mask->bounds_known   = FALSE;
    new_image->selection_mask->boundary_known = FALSE;
  }

  if (floating_layer)
    floating_sel_attach (floating_layer, new_floating_sel_drawable);

  /*  Set active layer, active channel, active vectors  */
  if (active_layer)
    gimp_image_set_active_layer (new_image, active_layer);

  if (active_channel)
    gimp_image_set_active_channel (new_image, active_channel);

  if (active_vectors)
    gimp_image_set_active_vectors (new_image, active_vectors);

  /*  Copy state of all color channels  */
  for (count = 0; count < MAX_CHANNELS; count++)
    {
      new_image->visible[count] = image->visible[count];
      new_image->active[count]  = image->active[count];
    }

  /*  Copy any guides  */
  for (list = image->guides; list; list = g_list_next (list))
    {
      GimpGuide *guide    = list->data;
      gint       position = gimp_guide_get_position (guide);

      switch (gimp_guide_get_orientation (guide))
        {
        case GIMP_ORIENTATION_HORIZONTAL:
          gimp_image_add_hguide (new_image, position, FALSE);
          break;

        case GIMP_ORIENTATION_VERTICAL:
          gimp_image_add_vguide (new_image, position, FALSE);
          break;

        default:
          g_error ("Unknown guide orientation.\n");
        }
    }

  /*  Copy any sample points  */
  for (list = image->sample_points; list; list = g_list_next (list))
    {
      GimpSamplePoint *sample_point = list->data;

      gimp_image_add_sample_point_at_pos (new_image,
                                          sample_point->x,
                                          sample_point->y,
                                          FALSE);
    }

  /*  Copy the grid  */
  if (image->grid)
    gimp_image_set_grid (new_image, image->grid, FALSE);

  /*  Copy the quick mask info  */
  new_image->quick_mask_state    = image->quick_mask_state;
  new_image->quick_mask_inverted = image->quick_mask_inverted;
  new_image->quick_mask_color    = image->quick_mask_color;

  /*  Copy parasites  */
  if (image->parasites)
    {
      g_object_unref (new_image->parasites);
      new_image->parasites = gimp_parasite_list_copy (image->parasites);
    }

  gimp_image_undo_enable (new_image);

  return new_image;
}