context: // GIMP# - A C# wrapper around the GIMP Library
  // Copyright (C) 2004-2018 Maurits Rijk
  //
  // Image.cs
  //
  // This library is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Lesser General Public
  // License as published by the Free Software Foundation; either
  // version 2 of the License, or (at your option) any later version.
  //
  // This library 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
  // Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public
  // License along with this library; if not, write to the
  // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  // Boston, MA 02111-1307, USA.
  //
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Runtime.InteropServices;
  
  using Gdk;
  using GLib;
  
  namespace Gimp
  {
    public sealed class Image : IEnumerable
    {
      internal Int32 _imageID;
  
      public bool IsValid => gimp_image_is_valid(ID);
      public ImageBaseType BaseType => gimp_image_base_type(ID);
      public int Width => gimp_image_width(ID);
      public int Height => gimp_image_height(ID);
      public Rectangle Bounds => new Rectangle(0, 0, Width, Height);
      public Dimensions Dimensions => new Dimensions(Width, Height);
       
      internal Image(Int32 imageID)
      {
        ID = imageID;
      }
         
      public Image(int width, int height, ImageBaseType type)
      {
        ID = gimp_image_new(width, height, type);
      }
  
      public Image(Dimensions dimensions, ImageBaseType type) :
        this(dimensions.Width, dimensions.Height, type)
      {
      }
  
      public Image(Image image)
      {
        ID = gimp_image_duplicate(image._imageID);
      }
  
      public Image Duplicate() => new Image(this);
  
      public override bool Equals(object o) => (o as Image)?.ID == ID;
  
      public override int GetHashCode() =>  ID.GetHashCode();
  
      public IEnumerator GetEnumerator()
      {
        throw new NotImplementedException();
      }
  
      public static Image Load(RunMode runMode, string filename, 
                               string raw_filename)
      {
        Int32 imageID = gimp_file_load(runMode, filename, raw_filename);
        return (imageID >= 0) ? new Image(imageID) : null;
      }
  
      public Layer LoadLayer(RunMode runMode, string filename) =>
        new Layer(gimp_file_load_layer(runMode, ID, filename));
  
      public LayerList LoadLayers(RunMode runMode, string filename)
      {
        IntPtr ptr = gimp_file_load_layers(runMode, ID, filename, 
  					 out int numLayers);
        return new LayerList(ptr, numLayers);
      }
  
      public bool Save(RunMode run_mode, string filename, string raw_filename)
      {
        Int32 drawableID = ActiveDrawable.ID;	// Check this!
        return gimp_file_save(run_mode, ID, drawableID, filename,
                              raw_filename);
      }
  
      // Used internally to save a single drawable
      internal bool Save(Drawable drawable, string filename) =>
        gimp_file_save(RunMode.Noninteractive, ID, drawable.ID, 
  		     filename, filename);
  
      public void Delete()
      {
        if (!gimp_image_delete(ID))
          {
  	  throw new GimpSharpException();
          }
      }
      
      public void Flip(OrientationType flip_type)
      {
        if (!gimp_image_flip(ID, flip_type))
          {
  	  throw new GimpSharpException();
          }
      }
  
      public void Rotate(RotationType rotate_type)
      {
        if (!gimp_image_rotate(ID, rotate_type))
          {
  	  throw new GimpSharpException();
          }
      }
  
      public void Resize(int newWidth, int newHeight, int offX, int offY)
      {
        if (!gimp_image_resize(ID, newWidth, newHeight, offX, offY))
          {
  	  throw new GimpSharpException();
          }
      }
  
      public void Resize(Dimensions dimensions, Offset offset)
      {
        Resize(dimensions.Width, dimensions.Height, offset.X, offset.Y);
      }
  
      public void ResizeToLayers()
      {
        if (!gimp_image_resize_to_layers(ID))
          {
  	  throw new GimpSharpException();
          }
      }
         
      public void Scale(int newWidth, int newHeight)
      {
        if (!gimp_image_scale(ID, newWidth, newHeight))
          {
  	  throw new GimpSharpException();
          }
      }
  
      public void Scale(Dimensions dimensions)
      {
        Scale(dimensions.Width, dimensions.Height);
      }
  
      public void Scale(int newWidth, int newHeight, 
  		      InterpolationType interpolation)
      {
        if (!gimp_image_scale_full(ID, newWidth, newHeight, interpolation))
          {
  	  throw new GimpSharpException();
          }
      }
  
      public void Scale(Dimensions dimensions, InterpolationType interpolation)
      {
        Scale(dimensions.Width, dimensions.Height, interpolation);
      }
  
      public void Crop(int newWidth, int newHeight, int offx, int offy)
      {
        if (!gimp_image_crop(ID, newWidth, newHeight, offx, offy))
          {
  	  throw new GimpSharpException();
          }
      }
  
      public void Crop(Dimensions dimensions, Offset offset)
      {
        Crop(dimensions.Width, dimensions.Height, offset.X, offset.Y);
      }
  
      public void Crop(Rectangle rectangle)
      {
        Crop(rectangle.Width, rectangle.Height, rectangle.X1, rectangle.Y1);
      }
        
      public LayerList Layers => new LayerList(this);
  
      // Beware: this ChannelList isn't updated when channels are added!
      public ChannelList Channels => new ChannelList(this);
  
      public Drawable ActiveDrawable => 
        new Drawable(gimp_image_get_active_drawable(ID));
  
      public FloatingSelection FloatingSelection
      {
        get
  	{
            Int32 layerID = gimp_image_get_floating_sel(ID);
            return (layerID == -1) ? null : new FloatingSelection(layerID);
  	}
      }
  
      public Drawable FloatingSelectionAttachedTo
      {
        get
  	{
            Int32 drawableID = gimp_image_floating_sel_attached_to(ID);
  
gt:           return (drawableID == -1) ? null : new Drawable(drawableID);