1073 lines
28 KiB
C#
1073 lines
28 KiB
C#
|
//
|
||
|
// 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
|
||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||
|
// 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
|
||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
//
|
||
|
|
||
|
// 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
|
||
|
{
|
||
|
[DefaultProperty("Images")]
|
||
|
[Designer("System.Windows.Forms.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)]
|
||
|
[DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
|
||
|
[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;
|
||
|
private object tag;
|
||
|
private readonly ImageCollection images;
|
||
|
#endregion // Private Fields
|
||
|
|
||
|
#region Sub-classes
|
||
|
[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 static class IndexedColorDepths
|
||
|
{
|
||
|
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();
|
||
|
private ArrayList keys = new ArrayList();
|
||
|
private int count;
|
||
|
private bool handleCreated;
|
||
|
private int lastKeyIndex = -1;
|
||
|
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 (this.handleCreated)
|
||
|
DestroyHandle();
|
||
|
else
|
||
|
this.Clear();
|
||
|
}
|
||
|
// Only deserialized ImageListStreamers are used.
|
||
|
else if ((streamImages = value.Images) != null) {
|
||
|
this.list = new ArrayList(streamImages.Length);
|
||
|
this.count = 0;
|
||
|
this.handleCreated = true;
|
||
|
this.keys = new ArrayList(streamImages.Length);
|
||
|
|
||
|
for (index = 0; index < streamImages.Length; index++) {
|
||
|
list.Add((Image)streamImages[index].Clone());
|
||
|
keys.Add(null);
|
||
|
}
|
||
|
|
||
|
// Invalid ColorDepth values are ignored.
|
||
|
if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth))
|
||
|
this.colorDepth = (ColorDepth)value.ColorDepth;
|
||
|
this.imageSize = value.ImageSize;
|
||
|
|
||
|
// Event is raised even when handle was not created yet.
|
||
|
owner.OnRecreateHandle();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For use in ImageList
|
||
|
internal Color TransparentColor {
|
||
|
get {
|
||
|
return this.transparentColor;
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
this.transparentColor = value;
|
||
|
}
|
||
|
}
|
||
|
#endregion // ImageCollection Internal Instance Properties
|
||
|
|
||
|
#region ImageCollection Public Instance Properties
|
||
|
[Browsable(false)]
|
||
|
public int Count {
|
||
|
get {
|
||
|
return this.handleCreated ? list.Count : this.count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool Empty {
|
||
|
get {
|
||
|
return this.Count == 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsReadOnly {
|
||
|
get {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Browsable(false)]
|
||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||
|
public Image this[int index] {
|
||
|
get {
|
||
|
return (Image)GetImage(index).Clone();
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
Image image;
|
||
|
|
||
|
if (index < 0 || index >= this.Count)
|
||
|
throw new ArgumentOutOfRangeException("index");
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Image this[string key] {
|
||
|
get {
|
||
|
int index;
|
||
|
|
||
|
return (index = IndexOfKey(key)) == -1 ? null : this[index];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public StringCollection Keys {
|
||
|
get {
|
||
|
int index;
|
||
|
string key;
|
||
|
StringCollection keyCollection;
|
||
|
|
||
|
// Returns all keys even when there are more keys than
|
||
|
// images. Null keys are returned as empty strings.
|
||
|
|
||
|
keyCollection = new StringCollection();
|
||
|
for (index = 0; index < keys.Count; index++)
|
||
|
keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
|
||
|
|
||
|
return keyCollection;
|
||
|
}
|
||
|
}
|
||
|
#endregion // ImageCollection Public Instance Properties
|
||
|
|
||
|
#region ImageCollection Private Static Methods
|
||
|
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;
|
||
|
}
|
||
|
#endregion // ImageCollection Private Static Methods
|
||
|
|
||
|
#region ImageCollection Private Instance Methods
|
||
|
private int AddItem(string key, ImageListItem item)
|
||
|
{
|
||
|
int itemIndex;
|
||
|
int index;
|
||
|
|
||
|
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 ((item.Flags & ItemFlags.ImageStrip) == 0)
|
||
|
keys.Add(key);
|
||
|
else
|
||
|
for (index = 0; index < item.ImageCount; index++)
|
||
|
keys.Add(null);
|
||
|
|
||
|
return itemIndex;
|
||
|
}
|
||
|
|
||
|
internal event EventHandler Changed;
|
||
|
|
||
|
private int AddItemInternal(ImageListItem item)
|
||
|
{
|
||
|
if (Changed != null)
|
||
|
Changed (this, EventArgs.Empty);
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
|
||
|
ReduceColorDepth(bitmap);
|
||
|
list.Add(bitmap);
|
||
|
}
|
||
|
|
||
|
if (imageAttributes != null)
|
||
|
imageAttributes.Dispose();
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Image CreateImage(Image value, Color transparentColor)
|
||
|
{
|
||
|
int imageWidth;
|
||
|
int imageHeight;
|
||
|
ImageAttributes imageAttributes;
|
||
|
|
||
|
if (transparentColor.A == 0)
|
||
|
imageAttributes = null;
|
||
|
else {
|
||
|
imageAttributes = new ImageAttributes();
|
||
|
imageAttributes.SetColorKey (transparentColor, transparentColor);
|
||
|
}
|
||
|
|
||
|
var bitmap = new Bitmap (imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
|
||
|
using (var graphics = Graphics.FromImage (bitmap))
|
||
|
graphics.DrawImage (value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
|
||
|
|
||
|
if (imageAttributes != null)
|
||
|
imageAttributes.Dispose ();
|
||
|
|
||
|
ReduceColorDepth (bitmap);
|
||
|
return bitmap;
|
||
|
}
|
||
|
|
||
|
private void RecreateHandle()
|
||
|
{
|
||
|
if (this.handleCreated) {
|
||
|
DestroyHandle();
|
||
|
this.handleCreated = true;
|
||
|
owner.OnRecreateHandle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
keys = new ArrayList();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For use in ImageList
|
||
|
internal Image GetImage(int index)
|
||
|
{
|
||
|
if (index < 0 || index >= this.Count)
|
||
|
throw new ArgumentOutOfRangeException("index");
|
||
|
|
||
|
CreateHandle();
|
||
|
return (Image)list[index];
|
||
|
}
|
||
|
|
||
|
// For use in ImageListStreamer
|
||
|
internal Image[] ToArray()
|
||
|
{
|
||
|
Image[] images;
|
||
|
|
||
|
// 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)
|
||
|
{
|
||
|
Add(null, value);
|
||
|
}
|
||
|
|
||
|
public void Add(Image value)
|
||
|
{
|
||
|
Add(null, value);
|
||
|
}
|
||
|
|
||
|
public int Add(Image value, Color transparentColor)
|
||
|
{
|
||
|
return AddItem(null, new ImageListItem(value, transparentColor));
|
||
|
}
|
||
|
|
||
|
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]);
|
||
|
}
|
||
|
|
||
|
public int AddStrip(Image value)
|
||
|
{
|
||
|
int width;
|
||
|
int imageWidth;
|
||
|
|
||
|
if (value == null)
|
||
|
throw new ArgumentNullException("value");
|
||
|
|
||
|
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");
|
||
|
|
||
|
return AddItem(null, new ImageListItem(value, width / imageWidth));
|
||
|
}
|
||
|
|
||
|
public void Clear()
|
||
|
{
|
||
|
list.Clear();
|
||
|
if (!this.handleCreated)
|
||
|
this.count = 0;
|
||
|
keys.Clear();
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||
|
public bool Contains(Image image)
|
||
|
{
|
||
|
throw new NotSupportedException();
|
||
|
}
|
||
|
|
||
|
public bool ContainsKey(string key)
|
||
|
{
|
||
|
return IndexOfKey(key) != -1;
|
||
|
}
|
||
|
|
||
|
public IEnumerator GetEnumerator()
|
||
|
{
|
||
|
Image[] images = new Image[this.Count];
|
||
|
int index;
|
||
|
|
||
|
if (images.Length != 0) {
|
||
|
// Handle is created only when there are images.
|
||
|
CreateHandle();
|
||
|
|
||
|
for (index = 0; index < images.Length; index++)
|
||
|
images[index] = (Image)((Image)list[index]).Clone();
|
||
|
}
|
||
|
|
||
|
return images.GetEnumerator();
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||
|
public int IndexOf(Image image)
|
||
|
{
|
||
|
throw new NotSupportedException();
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||
|
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);
|
||
|
keys.RemoveAt(index);
|
||
|
if (Changed != null)
|
||
|
Changed (this, EventArgs.Empty);
|
||
|
}
|
||
|
|
||
|
public void RemoveByKey(string key)
|
||
|
{
|
||
|
int index;
|
||
|
|
||
|
if ((index = IndexOfKey(key)) != -1)
|
||
|
RemoveAt(index);
|
||
|
}
|
||
|
|
||
|
public void SetKeyName(int index, string name)
|
||
|
{
|
||
|
// Only SetKeyName throws IndexOutOfRangeException.
|
||
|
if (index < 0 || index >= this.Count)
|
||
|
throw new IndexOutOfRangeException();
|
||
|
|
||
|
keys[index] = name;
|
||
|
}
|
||
|
#endregion // ImageCollection Public Instance Methods
|
||
|
|
||
|
#region ImageCollection Interface Properties
|
||
|
object IList.this[int index] {
|
||
|
get {
|
||
|
return this[index];
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
if (!(value is Image))
|
||
|
throw new ArgumentException("value");
|
||
|
|
||
|
this[index] = (Image)value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool IList.IsFixedSize {
|
||
|
get {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ICollection.IsSynchronized {
|
||
|
get {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
object ICollection.SyncRoot {
|
||
|
get {
|
||
|
return this;
|
||
|
}
|
||
|
}
|
||
|
#endregion // ImageCollection Interface Properties
|
||
|
|
||
|
#region ImageCollection Interface Methods
|
||
|
int IList.Add(object value)
|
||
|
{
|
||
|
int index;
|
||
|
|
||
|
if (!(value is Image))
|
||
|
throw new ArgumentException("value");
|
||
|
|
||
|
index = this.Count;
|
||
|
this.Add((Image)value);
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
bool IList.Contains(object image)
|
||
|
{
|
||
|
return image is Image ? this.Contains ((Image) image) : false;
|
||
|
}
|
||
|
|
||
|
int IList.IndexOf (object image)
|
||
|
{
|
||
|
return image is Image ? this.IndexOf ((Image) image) : -1;
|
||
|
}
|
||
|
|
||
|
void IList.Insert(int index, object value)
|
||
|
{
|
||
|
throw new NotSupportedException();
|
||
|
}
|
||
|
|
||
|
void IList.Remove (object image)
|
||
|
{
|
||
|
if (image is Image)
|
||
|
this.Remove ((Image) image);
|
||
|
}
|
||
|
|
||
|
void ICollection.CopyTo(Array dest, int index)
|
||
|
{
|
||
|
for (int imageIndex = 0; imageIndex < this.Count; imageIndex++)
|
||
|
dest.SetValue (this[imageIndex], index++);
|
||
|
}
|
||
|
#endregion // ImageCollection Interface Methods
|
||
|
}
|
||
|
#endregion // Sub-classes
|
||
|
|
||
|
#region Public Constructors
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
{
|
||
|
// ImageSize is serialized in ImageStream when non-empty.
|
||
|
// It is serialized even if it has its default value when empty.
|
||
|
return images.Empty;
|
||
|
}
|
||
|
|
||
|
internal void ResetColorDepth ()
|
||
|
{
|
||
|
this.ColorDepth = DefaultColorDepth;
|
||
|
}
|
||
|
|
||
|
internal void ResetImageSize ()
|
||
|
{
|
||
|
this.ImageSize = DefaultImageSize;
|
||
|
}
|
||
|
|
||
|
internal void ResetTransparentColor ()
|
||
|
{
|
||
|
this.TransparentColor = Color.LightGray;
|
||
|
}
|
||
|
#endregion // Private Instance Methods
|
||
|
|
||
|
#region Public Instance Properties
|
||
|
public ColorDepth ColorDepth {
|
||
|
get {
|
||
|
return images.ColorDepth;
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
images.ColorDepth = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Browsable(false)]
|
||
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||
|
public IntPtr Handle {
|
||
|
get {
|
||
|
return images.Handle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Browsable(false)]
|
||
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||
|
public bool HandleCreated {
|
||
|
get {
|
||
|
return images.HandleCreated;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DefaultValue(null)]
|
||
|
[MergableProperty(false)]
|
||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||
|
public ImageCollection Images {
|
||
|
get {
|
||
|
return this.images;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Localizable(true)]
|
||
|
public Size ImageSize {
|
||
|
get {
|
||
|
return images.ImageSize;
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
images.ImageSize = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Browsable(false)]
|
||
|
[DefaultValue(null)]
|
||
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||
|
public ImageListStreamer ImageStream {
|
||
|
get {
|
||
|
return images.ImageStream;
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
images.ImageStream = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Bindable(true)]
|
||
|
[DefaultValue(null)]
|
||
|
[Localizable(false)]
|
||
|
[TypeConverter(typeof(StringConverter))]
|
||
|
public object Tag {
|
||
|
get {
|
||
|
return this.tag;
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
this.tag = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Color TransparentColor {
|
||
|
get {
|
||
|
return images.TransparentColor;
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
images.TransparentColor = value;
|
||
|
}
|
||
|
}
|
||
|
#endregion // Public Instance Properties
|
||
|
|
||
|
#region Public Instance Methods
|
||
|
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)
|
||
|
{
|
||
|
g.DrawImage(images.GetImage(index), x, y);
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
{
|
||
|
return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
|
||
|
}
|
||
|
#endregion // Public Instance Methods
|
||
|
|
||
|
#region Protected Instance Methods
|
||
|
protected override void Dispose(bool disposing)
|
||
|
{
|
||
|
if (disposing)
|
||
|
images.DestroyHandle();
|
||
|
|
||
|
base.Dispose(disposing);
|
||
|
}
|
||
|
#endregion // Protected Instance Methods
|
||
|
|
||
|
#region Events
|
||
|
static object RecreateHandleEvent = new object ();
|
||
|
|
||
|
[Browsable(false)]
|
||
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||
|
public event EventHandler RecreateHandle {
|
||
|
add { Events.AddHandler (RecreateHandleEvent, value); }
|
||
|
remove { Events.RemoveHandler (RecreateHandleEvent, value); }
|
||
|
}
|
||
|
#endregion // Events
|
||
|
}
|
||
|
}
|