+//
+// System.Windows.Forms.ImageList.cs
+//
+// Authors:
+// Peter Bartok <pbartok@novell.com>
+// Kornél Pál <http://www.kornelpal.hu/>
+//
+// Copyright (C) 2004-2005 Novell, Inc.
+// Copyright (C) 2005 Kornél Pál
+//
+
+//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
-//
+//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Copyright (c) 2004-2005 Novell, Inc.
-//
-// Authors:
-// Peter Bartok pbartok@novell.com
-//
-
// COMPLETE
+//
+// Differences between MS.NET ImageList and this implementation:
+//
+// This is a fully managed image list implementation.
+//
+// Images are stored as Format32bppArgb internally but ColorDepth is applied
+// to the colors of images. Images[index] returns a Format32bppArgb copy of
+// the image so this difference is only internal.
+//
+// MS.NET has no alpha channel support (except for icons in 32-bit mode with
+// comctl32.dll version 6.0) but this implementation has full alpha channel
+// support in 32-bit mode.
+//
+// Handle should be an HIMAGELIST returned by ImageList_Create. This
+// implementation uses (IntPtr)(-1) that is a non-zero but invalid handle.
+//
+// MS.NET destroys handles using the garbage collector this implementation
+// does the same with Image objects stored in an ArrayList.
+//
+// MS.NET 1.x shares the same HIMAGELIST between ImageLists that were
+// initialized from the same ImageListStreamer and doesn't update ImageSize
+// and ColorDepth that are treated as bugs and MS.NET 2.0 behavior is
+// implemented.
+//
+// MS.NET 2.0 does not clear keys when handle is destroyed that is treated as
+// a bug.
+//
+
using System.Collections;
+using System.Collections.Specialized;
using System.ComponentModel;
+using System.ComponentModel.Design.Serialization;
using System.Drawing;
+using System.Drawing.Design;
using System.Drawing.Imaging;
+using System.Globalization;
+using System.Runtime.InteropServices;
-namespace System.Windows.Forms {
+namespace System.Windows.Forms
+{
[DefaultProperty("Images")]
[Designer("System.Windows.Forms.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)]
- [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
- [TypeConverter("System.Windows.Forms.ImageListConverter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public sealed class ImageList : System.ComponentModel.Component {
- #region Local Variables
- private ColorDepth color_depth;
- private ImageCollection image_collection;
- private Size size;
- private Color transparency_color;
- private Delegate handler;
- private ImageListStreamer image_stream;
- #endregion // Local Variables
+#if NET_2_0
+ [DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
+#endif
+ [ToolboxItemFilter("System.Windows.Forms")]
+ [TypeConverter(typeof(ImageListConverter))]
+ public sealed class ImageList : System.ComponentModel.Component
+ {
+ #region Private Fields
+ private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit;
+ private static readonly Size DefaultImageSize = new Size(16, 16);
+ private static readonly Color DefaultTransparentColor = Color.Transparent;
+
+#if NET_2_0
+ private object tag;
+#endif
+ private readonly ImageCollection images;
+ #endregion // Private Fields
#region Sub-classes
- [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
- public sealed class ImageCollection : IList, ICollection, IEnumerable {
- #region ImageCollection Local Variables
- private ImageList owner;
- private ArrayList list;
- #endregion // ImageCollection Local Variables
+ [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
+ public sealed class ImageCollection : IList, ICollection, IEnumerable
+ {
+ private const int AlphaMask = unchecked((int)0xFF000000);
+
+ private
+#if NET_2_0
+ static
+#else
+ sealed
+#endif
+ class IndexedColorDepths
+ {
+#if !NET_2_0
+ private IndexedColorDepths()
+ {
+ }
+#endif
+ internal static readonly ColorPalette Palette4Bit;
+ internal static readonly ColorPalette Palette8Bit;
+ private static readonly int[] squares;
+
+ static IndexedColorDepths()
+ {
+ Bitmap bitmap;
+ int index;
+
+ bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
+ Palette4Bit = bitmap.Palette;
+ bitmap.Dispose();
+
+ bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
+ Palette8Bit = bitmap.Palette;
+ bitmap.Dispose();
+
+ squares = new int[511];
+ for (index = 0; index < 256; index++)
+ squares[255 + index] = squares[255 - index] = index * index;
+ }
+
+ internal static int GetNearestColor(Color[] palette, int color)
+ {
+ int index;
+ int count;
+ int red;
+ int green;
+ int blue;
+ int nearestColor;
+ int minDistance;
+ int distance;
+
+ count = palette.Length;
+ for (index = 0; index < count; index++)
+ if (palette[index].ToArgb() == color)
+ return color;
+
+ red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF);
+ green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF);
+ blue = color & 0xFF;
+ nearestColor = AlphaMask;
+ minDistance = int.MaxValue;
+
+ for (index = 0; index < count; index++)
+ if ((distance = squares[255 + palette[index].R - red] + squares[255 + palette[index].G - green] + squares[255 + palette[index].B - blue]) < minDistance) {
+ nearestColor = palette[index].ToArgb();
+ minDistance = distance;
+ }
+
+ return nearestColor;
+ }
+ }
+
+ [Flags()]
+ private enum ItemFlags
+ {
+ None = 0,
+ UseTransparentColor = 1,
+ ImageStrip = 2
+ }
+
+ private sealed class ImageListItem
+ {
+ internal readonly object Image;
+ internal readonly ItemFlags Flags;
+ internal readonly Color TransparentColor;
+ internal readonly int ImageCount = 1;
+
+ internal ImageListItem(Icon value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ // Icons are cloned.
+ this.Image = (Icon)value.Clone();
+ }
+
+ internal ImageListItem(Image value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (!(value is Bitmap))
+ throw new ArgumentException("Image must be a Bitmap.");
+
+ // Images are not cloned.
+ this.Image = value;
+ }
+
+ internal ImageListItem(Image value, Color transparentColor) : this(value)
+ {
+ this.Flags = ItemFlags.UseTransparentColor;
+ this.TransparentColor = transparentColor;
+ }
+
+ internal ImageListItem(Image value, int imageCount) : this(value)
+ {
+ this.Flags = ItemFlags.ImageStrip;
+ this.ImageCount = imageCount;
+ }
+ }
+
+ #region ImageCollection Private Fields
+ private ColorDepth colorDepth = DefaultColorDepth;
+ private Size imageSize = DefaultImageSize;
+ private Color transparentColor = DefaultTransparentColor;
+ private ArrayList list = new ArrayList();
+#if NET_2_0
+ private ArrayList keys = new ArrayList();
+#endif
+ private int count;
+ private bool handleCreated;
+#if NET_2_0
+ private int lastKeyIndex = -1;
+#endif
+ private readonly ImageList owner;
+ #endregion // ImageCollection Private Fields
+
+ #region ImageCollection Internal Constructors
+ // For use in ImageList
+ internal ImageCollection(ImageList owner)
+ {
+ this.owner = owner;
+ }
+ #endregion // ImageCollection Internal Constructor
+
+ #region ImageCollection Internal Instance Properties
+ // For use in ImageList
+ internal ColorDepth ColorDepth {
+ get {
+ return this.colorDepth;
+ }
+
+ set {
+ if (!Enum.IsDefined(typeof(ColorDepth), value))
+ throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
+
+ if (this.colorDepth != value) {
+ this.colorDepth = value;
+ RecreateHandle();
+ }
+ }
+ }
+
+ // For use in ImageList
+ internal IntPtr Handle {
+ get {
+ CreateHandle();
+ return (IntPtr)(-1);
+ }
+ }
+
+ // For use in ImageList
+ internal bool HandleCreated {
+ get {
+ return this.handleCreated;
+ }
+ }
+
+ // For use in ImageList
+ internal Size ImageSize {
+ get {
+ return this.imageSize;
+ }
+
+ set {
+ if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256)
+ throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value");
+
+ if (this.imageSize != value) {
+ this.imageSize = value;
+ RecreateHandle();
+ }
+ }
+ }
+
+ // For use in ImageList
+ internal ImageListStreamer ImageStream {
+ get {
+ return this.Empty ? null : new ImageListStreamer(this);
+ }
+
+ set {
+ int index;
+ Image[] streamImages;
+
+ if (value == null) {
+#if NET_2_0
+ if (this.handleCreated)
+ DestroyHandle();
+ else
+ this.Clear();
+#endif
+ }
+ // Only deserialized ImageListStreamers are used.
+ else if ((streamImages = value.Images) != null) {
+ this.list = new ArrayList(streamImages.Length);
+ this.count = 0;
+ this.handleCreated = true;
+#if NET_2_0
+ this.keys = new ArrayList(streamImages.Length);
+#endif
+
+ for (index = 0; index < streamImages.Length; index++) {
+ list.Add((Image)streamImages[index].Clone());
+#if NET_2_0
+ keys.Add(null);
+#endif
+ }
+
+ // Invalid ColorDepth values are ignored.
+ if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth))
+ this.colorDepth = (ColorDepth)value.ColorDepth;
+ this.imageSize = value.ImageSize;
+
+#if NET_2_0
+ // Event is raised even when handle was not created yet.
+ owner.OnRecreateHandle();
+#endif
+ }
+ }
+ }
+
+ // For use in ImageList
+ internal Color TransparentColor {
+ get {
+ return this.transparentColor;
+ }
- #region ImageCollection Private Constructors
- internal ImageCollection(ImageList owner) {
- this.owner=owner;
- this.list=new ArrayList();
+ set {
+ this.transparentColor = value;
+ }
}
- #endregion // ImageCollection Private Constructor
+ #endregion // ImageCollection Internal Instance Properties
- #region ImageCollection Public Instance Properties
+ #region ImageCollection Public Instance Properties
[Browsable(false)]
public int Count {
get {
- return list.Count;
+ return this.handleCreated ? list.Count : this.count;
}
}
public bool Empty {
get {
- return list.Count==0;
+ return this.Count == 0;
}
}
public bool IsReadOnly {
get {
- return list.IsReadOnly;
+ return false;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Image this[int index] {
get {
- if (index<0 || index>=list.Count) {
- throw new ArgumentOutOfRangeException("index", index, "ImageCollection does not have that many images");
- }
- return (Image)list[index];
+ return (Image)GetImage(index).Clone();
}
set {
- if (index<0 || index>=list.Count) {
- throw new ArgumentOutOfRangeException("index", index, "ImageCollection does not have that many images");
- }
+ Image image;
- if (value==null) {
- throw new ArgumentOutOfRangeException("value", value, "Image cannot be null");
- }
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index");
- list[index]=value;
- // What happens if the bitmap had a previous 'MakeTransparent' done to it?
- ((Bitmap)list[index]).MakeTransparent(owner.transparency_color);
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (!(value is Bitmap))
+ throw new ArgumentException("Image must be a Bitmap.");
+
+ image = CreateImage(value, this.transparentColor);
+ CreateHandle();
+ list[index] = image;
}
}
- #endregion // ImageCollection Public Instance Properties
- #region ImageCollection Private Instance Methods
- private int AddInternal(Image image) {
- int width;
- int height;
- PixelFormat format;
+#if NET_2_0
+ public Image this[string key] {
+ get {
+ int index;
- width=owner.ImageSize.Width;
- height=owner.ImageSize.Height;
- switch(owner.color_depth) {
- case ColorDepth.Depth4Bit: format=PixelFormat.Format4bppIndexed; break;
- case ColorDepth.Depth8Bit: format=PixelFormat.Format8bppIndexed; break;
- case ColorDepth.Depth16Bit: format=PixelFormat.Format16bppRgb555; break;
- case ColorDepth.Depth24Bit: format=PixelFormat.Format24bppRgb; break;
- case ColorDepth.Depth32Bit: format=PixelFormat.Format32bppArgb; break;
- default: format=PixelFormat.Format32bppArgb; break;
+ return (index = IndexOfKey(key)) == -1 ? null : this[index];
}
+ }
- // Check if we can add straight or if we have to resize
- if (image.Width!=width || image.Height!=height || image.PixelFormat!=format) {
- Graphics g;
- Bitmap reformatted_image;
+ public StringCollection Keys {
+ get {
+ int index;
+ string key;
+ StringCollection keyCollection;
- reformatted_image = new Bitmap(width, height, format);
- g=Graphics.FromImage(reformatted_image);
+ // Returns all keys even when there are more keys than
+ // images. Null keys are returned as empty strings.
- g.DrawImage(image, new Rectangle(0, 0, width, height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
- g.Dispose();
+ keyCollection = new StringCollection();
+ for (index = 0; index < keys.Count; index++)
+ keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
- return list.Add(reformatted_image);
- } else {
- return list.Add(image);
+ return keyCollection;
}
}
+#endif
+ #endregion // ImageCollection Public Instance Properties
- internal void Dispose() {
-#if dontwantthis
- if (list!=null) {
- for (int i=0; i<list.Count; i++) {
- ((Image)list[i]).Dispose();
- }
+ #region ImageCollection Private Static Methods
+#if NET_2_0
+ private static bool CompareKeys(string key1, string key2)
+ {
+ // Keys are case-insensitive and keys with different length
+ // are not equal even when string.Compare treats them equal.
+
+ if (key1 == null || key2 == null || key1.Length != key2.Length)
+ return false;
+
+ return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
+ }
+#endif
+ #endregion // ImageCollection Private Static Methods
+
+ #region ImageCollection Private Instance Methods
+#if NET_2_0
+ private int AddItem(string key, ImageListItem item)
+#else
+ private int AddItem(ImageListItem item)
+#endif
+ {
+ int itemIndex;
+#if NET_2_0
+ int index;
+#endif
+
+ if (this.handleCreated)
+ itemIndex = AddItemInternal(item);
+ else {
+ // Image strips are counted as a single item in the return
+ // value of Add and AddStrip until handle is created.
+
+ itemIndex = list.Add(item);
+ this.count += item.ImageCount;
}
+
+#if NET_2_0
+ if ((item.Flags & ItemFlags.ImageStrip) == 0)
+ keys.Add(key);
+ else
+ for (index = 0; index < item.ImageCount; index++)
+ keys.Add(null);
#endif
+
+ return itemIndex;
}
- #endregion // ImageCollection Private Instance Methods
- #region ImageCollection Public Instance Methods
- public int Add(Image value, Color transparentColor) {
- if (value==null) {
- throw new ArgumentNullException("value", "Cannot add null image");
+ private int AddItemInternal(ImageListItem item)
+ {
+ if (item.Image is Icon) {
+ int imageWidth;
+ int imageHeight;
+ Bitmap bitmap;
+ Graphics graphics;
+
+ bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
+ graphics = Graphics.FromImage(bitmap);
+ graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight));
+ graphics.Dispose();
+
+ ReduceColorDepth(bitmap);
+ return list.Add(bitmap);
}
+ else if ((item.Flags & ItemFlags.ImageStrip) == 0)
+ return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor));
+ else {
+ int imageX;
+ int width;
+ int imageWidth;
+ int imageHeight;
+ int index;
+ Image image;
+ Bitmap bitmap;
+ Graphics graphics;
+ Rectangle imageRect;
+ ImageAttributes imageAttributes;
+
+ // When ImageSize was changed after adding image strips
+ // Count will return invalid values based on old ImageSize
+ // but when creating handle either ArgumentException will
+ // be thrown or image strip will be added according to the
+ // new ImageSize. This can result in image count
+ // difference that can result in exceptions in methods
+ // that use Count before creating handle. In addition this
+ // can result in the loss of sync with keys. When doing
+ // the same after handle was created there are no problems
+ // as handle will be recreated after changing ImageSize
+ // that results in the loss of images added previously.
+
+ if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
+ throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
+
+ if (image.Height != (imageHeight = this.imageSize.Height))
+ throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
+
+ imageRect = new Rectangle(0, 0, imageWidth, imageHeight);
+ if (this.transparentColor.A == 0)
+ imageAttributes = null;
+ else {
+ imageAttributes = new ImageAttributes();
+ imageAttributes.SetColorKey(this.transparentColor, this.transparentColor);
+ }
- ((Bitmap)value).MakeTransparent(owner.transparency_color);
- return AddInternal(value);
-
- }
+ index = list.Count;
+ for (imageX = 0; imageX < width; imageX += imageWidth) {
+ bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb);
+ graphics = Graphics.FromImage(bitmap);
+ graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes);
+ graphics.Dispose();
- public void Add(Icon value) {
- Image image;
+ ReduceColorDepth(bitmap);
+ list.Add(bitmap);
+ }
- image = value.ToBitmap();
+ if (imageAttributes != null)
+ imageAttributes.Dispose();
- if (value==null || image==null) {
- throw new ArgumentNullException("value", "Cannot add null icon");
+ return index;
}
-
- ((Bitmap)image).MakeTransparent(owner.transparency_color);
- AddInternal(image);
}
- public void Add(Image value) {
- if (value==null) {
- throw new ArgumentNullException("value", "Cannot add null image");
+ private void CreateHandle()
+ {
+ int index;
+ ArrayList items;
+
+ if (!this.handleCreated) {
+ items = this.list;
+ this.list = new ArrayList(this.count);
+ this.count = 0;
+ this.handleCreated = true;
+
+ for (index = 0; index < items.Count; index++)
+ AddItemInternal((ImageListItem)items[index]);
}
- ((Bitmap)value).MakeTransparent(owner.transparency_color);
- AddInternal(value);
}
- public int AddStrip(Image value) {
- int image_count;
- int width;
- int height;
- Bitmap image;
- Graphics g;
+ private Image CreateImage(Image value, Color transparentColor)
+ {
+ int imageWidth;
+ int imageHeight;
+ Bitmap bitmap;
+ Graphics graphics;
+ ImageAttributes imageAttributes;
+
+ if (transparentColor.A == 0)
+ imageAttributes = null;
+ else {
+ imageAttributes = new ImageAttributes();
+ imageAttributes.SetColorKey(transparentColor, transparentColor);
+ }
+
+ bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
+ graphics = Graphics.FromImage(bitmap);
+ graphics.DrawImage(value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
+ graphics.Dispose();
+
+ if (imageAttributes != null)
+ imageAttributes.Dispose();
- if (value==null) {
- throw new ArgumentNullException("value", "Cannot add null images");
+ ReduceColorDepth(bitmap);
+ return bitmap;
+ }
+
+ private void RecreateHandle()
+ {
+ if (this.handleCreated) {
+ DestroyHandle();
+ this.handleCreated = true;
+ owner.OnRecreateHandle();
}
+ }
- if ((value.Width % owner.ImageSize.Width) != 0) {
- throw new ArgumentException("Strip is not a multiple of the ImageList with", "value");
+ private unsafe void ReduceColorDepth(Bitmap bitmap)
+ {
+ byte* pixelPtr;
+ byte* lineEndPtr;
+ byte* linePtr;
+ int line;
+ int pixel;
+ int height;
+ int widthBytes;
+ int stride;
+ BitmapData bitmapData;
+ Color[] palette;
+
+ if (this.colorDepth < ColorDepth.Depth32Bit) {
+ bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
+ try {
+ linePtr = (byte*)bitmapData.Scan0;
+ height = bitmapData.Height;
+ widthBytes = bitmapData.Width << 2;
+ stride = bitmapData.Stride;
+
+ if (this.colorDepth < ColorDepth.Depth16Bit) {
+ palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries;
+
+ for (line = 0; line < height; line++) {
+ lineEndPtr = linePtr + widthBytes;
+ for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
+ *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
+ linePtr += stride;
+ }
+ }
+ else if (this.colorDepth < ColorDepth.Depth24Bit) {
+ for (line = 0; line < height; line++) {
+ lineEndPtr = linePtr + widthBytes;
+ for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
+ *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
+ linePtr += stride;
+ }
+ }
+ else {
+ for (line = 0; line < height; line++) {
+ lineEndPtr = linePtr + widthBytes;
+ for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
+ *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
+ linePtr += stride;
+ }
+ }
+ }
+ finally {
+ bitmap.UnlockBits(bitmapData);
+ }
+ }
+ }
+ #endregion // ImageCollection Private Instance Methods
+
+ #region ImageCollection Internal Instance Methods
+ // For use in ImageList
+ internal void DestroyHandle()
+ {
+ if (this.handleCreated) {
+ this.list = new ArrayList();
+ this.count = 0;
+ this.handleCreated = false;
+#if NET_2_0
+ keys = new ArrayList();
+#endif
}
+ }
+
+ // For use in ImageList
+ internal Image GetImage(int index)
+ {
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index");
- // MSDN: The number of images is inferred from the width. A strip is multiple images side-by-side
- width=owner.ImageSize.Width;
- height=owner.ImageSize.Height;
- image_count=value.Width/width;
- for (int i=0; i<image_count; i++) {
- image = new Bitmap(value, width, height);
- g = Graphics.FromImage(image);
+ CreateHandle();
+ return (Image)list[index];
+ }
- g.DrawImage(value, new Rectangle(0, 0, width, height), i*width, 0, width, height, GraphicsUnit.Pixel);
- AddInternal(image);
+ // For use in ImageListStreamer
+ internal Image[] ToArray()
+ {
+ Image[] images;
- g.Dispose();
- image.Dispose();
- }
+ // Handle is created even when the list is empty.
+ CreateHandle();
+ images = new Image[list.Count];
+ list.CopyTo(images);
+ return images;
+ }
+ #endregion // ImageCollection Internal Instance Methods
+
+ #region ImageCollection Public Instance Methods
+ public void Add(Icon value)
+ {
+#if NET_2_0
+ Add(null, value);
+#else
+ AddItem(new ImageListItem(value));
+#endif
+ }
+
+ public void Add(Image value)
+ {
+#if NET_2_0
+ Add(null, value);
+#else
+ AddItem(new ImageListItem(value));
+#endif
+ }
+
+ public int Add(Image value, Color transparentColor)
+ {
+#if NET_2_0
+ return AddItem(null, new ImageListItem(value, transparentColor));
+#else
+ return AddItem(new ImageListItem(value, transparentColor));
+#endif
+ }
+
+#if NET_2_0
+ public void Add(string key, Icon icon)
+ {
+ // Argument has name icon but exceptions use name value.
+ AddItem(key, new ImageListItem(icon));
+ }
+
+ public void Add(string key, Image image)
+ {
+ // Argument has name image but exceptions use name value.
+ AddItem(key, new ImageListItem(image));
+ }
+
+ public void AddRange(Image[] images)
+ {
+ int index;
+
+ if (images == null)
+ throw new ArgumentNullException("images");
+
+ for (index = 0; index < images.Length; index++)
+ Add(images[index]);
+ }
+#endif
+
+ public int AddStrip(Image value)
+ {
+ int width;
+ int imageWidth;
+
+ if (value == null)
+ throw new ArgumentNullException("value");
- // FIXME - is this right? MSDN says to return the index, but we might have multiple...
- return image_count;
+ if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
+ throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
+
+ if (value.Height != this.imageSize.Height)
+ throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
+
+#if NET_2_0
+ return AddItem(null, new ImageListItem(value, width / imageWidth));
+#else
+ return AddItem(new ImageListItem(value, width / imageWidth));
+#endif
}
- public void Clear() {
+ public void Clear()
+ {
list.Clear();
+ if (this.handleCreated)
+ this.count = 0;
+#if NET_2_0
+ keys.Clear();
+#endif
}
- public bool Contains(Image image) {
- return list.Contains(image);
+#if NET_2_0
+ [EditorBrowsable(EditorBrowsableState.Never)]
+#endif
+ public bool Contains(Image image)
+ {
+ throw new NotSupportedException();
}
- public IEnumerator GetEnumerator() {
- return list.GetEnumerator();
+#if NET_2_0
+ public bool ContainsKey(string key)
+ {
+ return IndexOfKey(key) != -1;
}
+#endif
+
+ public IEnumerator GetEnumerator()
+ {
+ Image[] images = new Image[this.Count];
+ int index;
+
+ if (images.Length != 0) {
+ // Handle is created only when there are images.
+ CreateHandle();
- public int IndexOf(Image image) {
- return list.IndexOf(image);
+ for (index = 0; index < images.Length; index++)
+ images[index] = (Image)((Image)list[index]).Clone();
+ }
+
+ return images.GetEnumerator();
}
- public void Remove(Image image) {
- list.Remove(image);
+#if NET_2_0
+ [EditorBrowsable(EditorBrowsableState.Never)]
+#endif
+ public int IndexOf(Image image)
+ {
+ throw new NotSupportedException();
}
- public void RemoveAt(int index) {
- if (index<0 || index>=list.Count) {
- throw new ArgumentOutOfRangeException("index", index, "ImageCollection does not have that many images");
+#if NET_2_0
+ public int IndexOfKey(string key)
+ {
+ int index;
+
+ if (key != null && key.Length != 0) {
+ // When last IndexOfKey was successful and the same key was
+ // assigned to an image with a lower index than the last
+ // result and the key of the last result equals to key
+ // argument the last result is returned.
+
+ if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
+ return this.lastKeyIndex;
+
+ // Duplicate keys are allowed and first match is returned.
+ for (index = 0; index < this.Count; index++)
+ if (CompareKeys((string)keys[index], key))
+ return this.lastKeyIndex = index;
}
+ return this.lastKeyIndex = -1;
+ }
+#endif
+
+#if NET_2_0
+ [EditorBrowsable(EditorBrowsableState.Never)]
+#endif
+ public void Remove(Image image)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void RemoveAt(int index)
+ {
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index");
+
+ CreateHandle();
list.RemoveAt(index);
+#if NET_2_0
+ keys.RemoveAt(index);
+#endif
+ }
+
+#if NET_2_0
+ public void RemoveByKey(string key)
+ {
+ int index;
+
+ if ((index = IndexOfKey(key)) != -1)
+ RemoveAt(index);
}
- #endregion // ImageCollection Public Instance Methods
- #region ImageCollection Interface Properties
+ public void SetKeyName(int index, string name)
+ {
+ // Only SetKeyName throws IndexOutOfRangeException.
+ if (index < 0 || index >= this.Count)
+ throw new IndexOutOfRangeException();
+
+ keys[index] = name;
+ }
+#endif
+ #endregion // ImageCollection Public Instance Methods
+
+ #region ImageCollection Interface Properties
object IList.this[int index] {
get {
- if (index<0 || index>=list.Count) {
- throw new ArgumentOutOfRangeException("index", index, "ImageCollection does not have that many images");
- }
return this[index];
}
set {
- if (!(value is Bitmap)) {
- throw new ArgumentException("Object of type Image required", "value");
- }
+ if (!(value is Image))
+ throw new ArgumentException("value");
- this[index]=(Image)value;
+ this[index] = (Image)value;
}
}
}
}
- bool IList.IsReadOnly {
- get {
- return list.IsReadOnly;
- }
- }
-
bool ICollection.IsSynchronized {
get {
- return list.IsSynchronized;
+ return false;
}
}
object ICollection.SyncRoot {
get {
- return list.SyncRoot;
+ return this;
}
}
- #endregion // ImageCollection Interface Properties
+ #endregion // ImageCollection Interface Properties
- #region ImageCollection Interface Methods
- int IList.Add(object value) {
- if (value == null) {
- throw new ArgumentNullException("value", "Cannot add null images");
- }
+ #region ImageCollection Interface Methods
+ int IList.Add(object value)
+ {
+ int index;
- if (!(value is Bitmap)) {
- throw new ArgumentException("Object of type Image required", "value");
- }
+ if (!(value is Image))
+ throw new ArgumentException("value");
- return list.Add(value);
+ index = this.Count;
+ this.Add((Image)value);
+ return index;
}
- bool IList.Contains(object value) {
- if (!(value is Bitmap)) {
- throw new ArgumentException("Object of type Image required", "value");
- }
-
- return this.Contains((Image) value);
+ bool IList.Contains(object value)
+ {
+ return value is Image ? this.Contains((Image)value) : false;
}
- int IList.IndexOf(object value) {
- if (!(value is Bitmap)) {
- throw new ArgumentException("Object of type Image required", "value");
- }
-
- return this.IndexOf((Image) value);
+ int IList.IndexOf(object value)
+ {
+ return value is Image ? this.IndexOf((Image)value) : -1;
}
- void IList.Insert(int index, object value) {
- if (!(value is Bitmap)) {
- throw new ArgumentException("Object of type Image required", "value");
- }
- list.Insert(index, value);
+ void IList.Insert(int index, object value)
+ {
+ throw new NotSupportedException();
}
- void IList.Remove(object value) {
- if (!(value is Bitmap)) {
- throw new ArgumentException("Object of type Image required", "value");
- }
- list.Remove(value);
+ void IList.Remove(object value)
+ {
+ if (value is Image)
+ this.Remove((Image)value);
}
- void ICollection.CopyTo(Array array, int index) {
- if (list.Count>0) {
- list.CopyTo(array, index);
- }
+ void ICollection.CopyTo(Array array, int index)
+ {
+ int imageIndex;
+
+ for (imageIndex = 0; imageIndex < this.Count; imageIndex++)
+ array.SetValue(this[index], index++);
}
- #endregion // ImageCollection Interface Methods
+ #endregion // ImageCollection Interface Methods
}
- #endregion // Sub-classes
+ #endregion // Sub-classes
#region Public Constructors
- public ImageList() {
- color_depth = ColorDepth.Depth8Bit;
- transparency_color = Color.Transparent;
- size = new Size(16, 16);
- image_collection = new ImageCollection(this);
+ public ImageList()
+ {
+ images = new ImageCollection(this);
+ }
+
+ public ImageList(System.ComponentModel.IContainer container) : this()
+ {
+ container.Add(this);
+ }
+ #endregion // Public Constructors
+
+ #region Private Instance Methods
+ private void OnRecreateHandle()
+ {
+ EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ // MS's TypeDescriptor stuff apparently uses
+ // non-public ShouldSerialize* methods, because it
+ // picks up this behavior even though the methods
+ // aren't public. we can't make them private, though,
+ // without adding compiler warnings. so, make then
+ // internal instead.
+
+ internal bool ShouldSerializeTransparentColor ()
+ {
+ return this.TransparentColor != Color.LightGray;
}
- public ImageList(System.ComponentModel.IContainer container) : this ()
+ internal bool ShouldSerializeColorDepth()
+ {
+ // ColorDepth is serialized in ImageStream when non-empty.
+ // It is serialized even if it has its default value when empty.
+ return images.Empty;
+ }
+
+ internal bool ShouldSerializeImageSize()
{
- color_depth = ColorDepth.Depth8Bit;
- transparency_color = Color.Transparent;
- size = new Size(16, 16);
- container.Add (this);
+ // ImageSize is serialized in ImageStream when non-empty.
+ // It is serialized even if it has its default value when empty.
+#if NET_2_0
+ return images.Empty;
+#else
+ return this.ImageSize != DefaultImageSize;
+#endif
}
- #endregion // Public Constructors
+
+
+ internal void ResetColorDepth ()
+ {
+ this.ColorDepth = DefaultColorDepth;
+ }
+
+#if NET_2_0
+ internal void ResetImageSize ()
+ {
+ this.ImageSize = DefaultImageSize;
+ }
+
+ internal void ResetTransparentColor ()
+ {
+ this.TransparentColor = Color.LightGray;
+ }
+#endif
+ #endregion // Private Instance Methods
#region Public Instance Properties
- [DefaultValue(ColorDepth.Depth8Bit)]
+#if !NET_2_0
+ [DefaultValue(DefaultColorDepth)]
+#endif
public ColorDepth ColorDepth {
get {
- return this.color_depth;
+ return images.ColorDepth;
}
set {
- this.color_depth=value;
+ images.ColorDepth = value;
}
}
- [MonoTODO("Determine if we support HBITMAP handles, this would involve XplatUI")]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IntPtr Handle {
get {
- if (RecreateHandle!=null) RecreateHandle(this, EventArgs.Empty);
- return IntPtr.Zero;
+ return images.Handle;
}
}
- [MonoTODO("Determine if we support HBITMAP handles, this would involve XplatUI")]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool HandleCreated {
get {
- return false;
+ return images.HandleCreated;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ImageCollection Images {
get {
- return this.image_collection;
+ return this.images;
}
}
[Localizable(true)]
public Size ImageSize {
get {
- return this.size;
+ return images.ImageSize;
}
set {
- if (value.Width<1 || value.Width>256 || value.Height<1 || value.Height>256) {
- throw new ArgumentException("ImageSize width and height must be between 1 and 255", "value");
- }
- this.size=value;
+ images.ImageSize = value;
}
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
public ImageListStreamer ImageStream {
get {
- return image_stream;
+ return images.ImageStream;
}
set {
- image_stream = value;
-
- size = image_stream.ImageSize;
- color_depth = image_stream.ImageColorDepth;
- transparency_color = image_stream.BackColor;
+ images.ImageStream = value;
+ }
+ }
- image_collection.Clear ();
+#if NET_2_0
+ [Bindable(true)]
+ [DefaultValue(null)]
+ [Localizable(false)]
+ [TypeConverter(typeof(StringConverter))]
+ public object Tag {
+ get {
+ return this.tag;
+ }
- foreach (Image image in image_stream.Images)
- image_collection.Add (image);
+ set {
+ this.tag = value;
}
}
+#endif
public Color TransparentColor {
get {
- return this.transparency_color;
+ return images.TransparentColor;
}
set {
- this.transparency_color=value;
+ images.TransparentColor = value;
}
}
- #endregion // Public Instance Properties
+ #endregion // Public Instance Properties
#region Public Instance Methods
- public void Draw(Graphics g, Point pt, int index) {
+ public void Draw(Graphics g, Point pt, int index)
+ {
this.Draw(g, pt.X, pt.Y, index);
}
- public void Draw(Graphics g, int x, int y, int index) {
- this.Draw(g, x, y, this.size.Width, this.size.Height, index);
+ public void Draw(Graphics g, int x, int y, int index)
+ {
+ g.DrawImage(images.GetImage(index), x, y);
}
- public void Draw(Graphics g, int x, int y, int width, int height, int index) {
- Image i;
-
- if ((index < 0) || (index >= this.Images.Count)) {
- throw new ArgumentOutOfRangeException("index", index, "ImageList does not contain that many images");
- }
- i = this.Images[index];
- g.DrawImage(i, x, y, width, height);
+ public void Draw(Graphics g, int x, int y, int width, int height, int index)
+ {
+ g.DrawImage(images.GetImage(index), x, y, width, height);
}
- public override string ToString() {\r
- return "ImageList Size "+this.size.Width.ToString()+"x"+this.size.Height.ToString()+", Depth "+this.color_depth.ToString()+", Transparency color "+this.transparency_color.ToString();\r
+ public override string ToString()
+ {
+ return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
}
- #endregion // Public Instance Methods
+ #endregion // Public Instance Methods
- #region Protected Instance Methods
- protected override void Dispose(bool disposing) {\r
- if (image_collection!=null) {\r
- image_collection.Dispose();\r
- }\r
- }\r
+ #region Protected Instance Methods
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ images.DestroyHandle();
- #endregion // Protected Instance Methods
+ base.Dispose(disposing);
+ }
+ #endregion // Protected Instance Methods
#region Events
+ static object RecreateHandleEvent = new object ();
+
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public event EventHandler RecreateHandle;
- #endregion // Events
+ public event EventHandler RecreateHandle {
+ add { Events.AddHandler (RecreateHandleEvent, value); }
+ remove { Events.RemoveHandler (RecreateHandleEvent, value); }
+ }
+ #endregion // Events
}
}