// // System.Drawing.Icon.cs // // Authors: // Dennis Hayes (dennish@Raytek.com) // Andreas Nahr (ClassDevelopment@A-SoftTech.com) // Sanjay Gupta (gsanjay@novell.com) // Peter Dennis Bartok (pbartok@novell.com) // Sebastien Pouliot // // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com // Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com) // // 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. // using System.ComponentModel; using System.Drawing.Imaging; using System.IO; using System.Runtime.Serialization; using System.Runtime.InteropServices; using System.Security.Permissions; namespace System.Drawing { #if !NET_2_0 [ComVisible (false)] #endif [Serializable] [Editor ("System.Drawing.Design.IconEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))] [TypeConverter(typeof(IconConverter))] public sealed class Icon : MarshalByRefObject, ISerializable, ICloneable, IDisposable { [StructLayout(LayoutKind.Sequential)] internal struct IconDirEntry { internal byte width; // Width of icon internal byte height; // Height of icon internal byte colorCount; // colors in icon internal byte reserved; // Reserved internal ushort planes; // Color Planes internal ushort bitCount; // Bits per pixel internal uint bytesInRes; // bytes in resource internal uint imageOffset; // position in file }; [StructLayout(LayoutKind.Sequential)] internal struct IconDir { internal ushort idReserved; // Reserved internal ushort idType; // resource type (1 for icons) internal ushort idCount; // how many images? internal IconDirEntry [] idEntries; // the entries for each image }; [StructLayout(LayoutKind.Sequential)] internal struct BitmapInfoHeader { internal uint biSize; internal int biWidth; internal int biHeight; internal ushort biPlanes; internal ushort biBitCount; internal uint biCompression; internal uint biSizeImage; internal int biXPelsPerMeter; internal int biYPelsPerMeter; internal uint biClrUsed; internal uint biClrImportant; }; [StructLayout(LayoutKind.Sequential)] internal struct IconImage { internal BitmapInfoHeader iconHeader; //image header internal uint [] iconColors; //colors table internal byte [] iconXOR; // bits for XOR mask internal byte [] iconAND; //bits for AND mask }; private Size iconSize; private IntPtr handle = IntPtr.Zero; private IconDir iconDir; private ushort id; private IconImage [] imageData; private bool undisposable; private bool disposed; private Bitmap bitmap; private Icon () { } private Icon (IntPtr handle) { this.handle = handle; if (GDIPlus.RunningOnUnix ()) { bitmap = Bitmap.FromHicon (handle); iconSize = new Size (bitmap.Width, bitmap.Height); // FIXME: we need to convert the bitmap into an icon } else { IconInfo ii; GDIPlus.GetIconInfo (handle, out ii); if (!ii.IsIcon) throw new NotImplementedException (Locale.GetText ("Handle doesn't represent an ICON.")); // If this structure defines an icon, the hot spot is always in the center of the icon iconSize = new Size (ii.xHotspot * 2, ii.yHotspot * 2); // FIXME: we need to convert the bitmap(s) into an icon } undisposable = true; } public Icon (Icon original, int width, int height) : this (original, new Size (width, height)) { } public Icon (Icon original, Size size) { if (original == null) throw new ArgumentException ("original"); iconSize = size; iconDir = original.iconDir; imageData = original.imageData; id = UInt16.MaxValue; int count = iconDir.idCount; for (ushort i=0; i < count; i++) { IconDirEntry ide = iconDir.idEntries [i]; if ((ide.height == size.Height) || (ide.width == size.Width)) { id = i; break; } } // if a perfect match isn't found we look for the biggest icon *smaller* than specified if (id == UInt16.MaxValue) { int requested = Math.Min (size.Height, size.Width); IconDirEntry best = iconDir.idEntries [0]; for (ushort i=1; i < count; i++) { IconDirEntry ide = iconDir.idEntries [i]; if ((ide.height < requested) || (ide.width < requested)) { if ((ide.height > best.height) || (ide.width > best.width)) id = i; } } } // last one, if nothing better can be found if (id == UInt16.MaxValue) id = (ushort) (count - 1); iconSize.Height = iconDir.idEntries [id].height; iconSize.Width = iconDir.idEntries [id].width; if (original.bitmap != null) bitmap = (Bitmap)original.bitmap.Clone (); } public Icon (Stream stream) : this (stream, 32, 32) { } public Icon (Stream stream, int width, int height) { InitFromStreamWithSize (stream, width, height); } public Icon (string fileName) : this (new FileStream (fileName, FileMode.Open), 32, 32) { } public Icon (Type type, string resource) { if (resource == null) throw new ArgumentException ("resource"); using (Stream s = type.Assembly.GetManifestResourceStream (type, resource)) { if (s == null) { string msg = Locale.GetText ("Resource '{0}' was not found.", resource); throw new FileNotFoundException (msg); } InitFromStreamWithSize (s, 32, 32); // 32x32 is default } } private Icon (SerializationInfo info, StreamingContext context) { MemoryStream dataStream = null; int width=0; int height=0; foreach (SerializationEntry serEnum in info) { if (String.Compare(serEnum.Name, "IconData", true) == 0) { dataStream = new MemoryStream ((byte []) serEnum.Value); } if (String.Compare(serEnum.Name, "IconSize", true) == 0) { Size iconSize = (Size) serEnum.Value; width = iconSize.Width; height = iconSize.Height; } } if ((dataStream != null) && (width == height)) { dataStream.Seek (0, SeekOrigin.Begin); InitFromStreamWithSize (dataStream, width, height); } } internal Icon (string resourceName, bool undisposable) { using (Stream s = typeof (Icon).Assembly.GetManifestResourceStream (resourceName)) { if (s == null) { string msg = Locale.GetText ("Resource '{0}' was not found.", resourceName); throw new FileNotFoundException (msg); } InitFromStreamWithSize (s, 32, 32); // 32x32 is default } this.undisposable = true; } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { MemoryStream ms = new MemoryStream (); Save (ms); info.AddValue ("IconSize", this.Size, typeof (Size)); info.AddValue ("IconData", ms.ToArray ()); } #if NET_2_0 public Icon (Stream stream, Size size) : this (stream, size.Width, size.Height) { } public Icon (string fileName, int width, int height) : this (new FileStream (fileName, FileMode.Open), width, height) { } public Icon (string fileName, Size size) : this (new FileStream (fileName, FileMode.Open), size) { } [MonoLimitation ("The same icon, SystemIcons.WinLogo, is returned for all file types.")] public static Icon ExtractAssociatedIcon (string filePath) { if ((filePath == null) || (filePath.Length == 0)) throw new ArgumentException (Locale.GetText ("Null or empty path."), "filePath"); if (!File.Exists (filePath)) throw new FileNotFoundException (Locale.GetText ("Couldn't find specified file."), filePath); return SystemIcons.WinLogo; } #endif public void Dispose () { // SystemIcons requires this if (undisposable) return; if (!disposed) { if (bitmap != null) { bitmap.Dispose (); bitmap = null; } GC.SuppressFinalize (this); } disposed = true; } public object Clone () { return new Icon (this, this.Width, this.Height); } [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] public static Icon FromHandle (IntPtr handle) { if (handle == IntPtr.Zero) throw new ArgumentException ("handle"); return new Icon (handle); } public void Save (Stream outputStream) { if (iconDir.idEntries!=null){ BinaryWriter bw = new BinaryWriter (outputStream); //write icondir bw.Write (iconDir.idReserved); bw.Write (iconDir.idType); ushort count = iconDir.idCount; bw.Write (count); //now write iconDirEntries for (int i=0; i<(int)count; i++){ IconDirEntry ide = iconDir.idEntries [i]; bw.Write (ide.width); bw.Write (ide.height); bw.Write (ide.colorCount); bw.Write (ide.reserved); bw.Write (ide.planes); bw.Write (ide.bitCount); bw.Write (ide.bytesInRes); bw.Write (ide.imageOffset); } //now write iconImage data for (int i=0; i<(int)count; i++){ BitmapInfoHeader bih = imageData [i].iconHeader; bw.Write (bih.biSize); bw.Write (bih.biWidth); bw.Write (bih.biHeight); bw.Write (bih.biPlanes); bw.Write (bih.biBitCount); bw.Write (bih.biCompression); bw.Write (bih.biSizeImage); bw.Write (bih.biXPelsPerMeter); bw.Write (bih.biYPelsPerMeter); bw.Write (bih.biClrUsed); bw.Write (bih.biClrImportant); //now write color table int colCount = imageData [i].iconColors.Length; for (int j=0; j"; } [Browsable (false)] public IntPtr Handle { get { // note: this handle doesn't survive the lifespan of the icon instance if (!disposed && (handle == IntPtr.Zero)) { if (GDIPlus.RunningOnUnix ()) { handle = GetInternalBitmap ().NativeObject; } else { // FIXME - construct a real (win32) icon and return it's handle } } return handle; } } [Browsable (false)] public int Height { get { return iconSize.Height; } } public Size Size { get { return iconSize; } } [Browsable (false)] public int Width { get { return iconSize.Width; } } ~Icon () { Dispose (); } private void InitFromStreamWithSize (Stream stream, int width, int height) { //read the icon header if (stream == null || stream.Length == 0) throw new System.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream"); BinaryReader reader = new BinaryReader (stream); //iconDir = new IconDir (); iconDir.idReserved = reader.ReadUInt16(); if (iconDir.idReserved != 0) //must be 0 throw new System.ArgumentException ("Invalid Argument", "stream"); iconDir.idType = reader.ReadUInt16(); if (iconDir.idType != 1) //must be 1 throw new System.ArgumentException ("Invalid Argument", "stream"); ushort dirEntryCount = reader.ReadUInt16(); iconDir.idCount = dirEntryCount; iconDir.idEntries = new IconDirEntry [dirEntryCount]; imageData = new IconImage [dirEntryCount]; bool sizeObtained = false; //now read in the IconDirEntry structures for (int i=0; i= largestSize) { largestSize = iconDir.idEntries [j].bytesInRes; this.id = (ushort) j; this.iconSize.Height = iconDir.idEntries [j].height; this.iconSize.Width = iconDir.idEntries [j].width; } } } //now read in the icon data for (int j = 0; j>5)<<2); //Determine the XOR array Size int xorSize = numBytesPerLine * iconHeight; iidata.iconXOR = new byte [xorSize]; int nread = bihReader.Read (iidata.iconXOR, 0, xorSize); if (nread != xorSize) throw new Exception ("Short file"); //Determine the AND array size numBytesPerLine = (int)((((bih.biWidth) + 31) & ~31) >> 3); int andSize = numBytesPerLine * iconHeight; iidata.iconAND = new byte [andSize]; nread = bihReader.Read (iidata.iconAND, 0, andSize); if (nread != andSize) throw new Exception ("Short file"); imageData [j] = iidata; bihReader.Close(); } reader.Close(); } } }