/*
The IconEx is taken from:
.NET Icon Explorer
by Steve McMahon steve@vbaccelerator.com
http://vbaccelerator.com/home/NET/Utilities/Icon_Extractor/article.asp
http://vbaccelerator.com/home/NET/Utilities/Icon_Extractor/IconExplorer.asp
http://vbaccelerator.com/home/NET/Utilities/Icon_Extractor/IconExplorer_zip_IconEx%5CIconEx_cs.asp
*/
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace vbAccelerator.Components.Win32{
#region IconExException
/// <summary>
/// Exception thrown for unreadable icons
/// </summary>
public class IconExException : Exception
{
public IconExException() : base()
{
}
public IconExException(string message) : base(message)
{
}
public IconExException(string message, Exception innerException) :
base(message, innerException)
{
}
}
#endregion
#region IconEx
/// <summary>
/// Manages a true Windows Icon with multiple images
/// (colour depths and sizes)
/// </summary>
public class IconEx : IDisposable
{
#region Unmanaged Code
[DllImport("kernel32", CharSet = CharSet.Auto)]
private extern static IntPtr LoadLibraryEx(
[MarshalAs(UnmanagedType.LPTStr)]
string lpLibFileName,
IntPtr hFile,
int dwFlags);
[DllImport("kernel32")]
private extern static int FreeLibrary (
IntPtr hLibModule);
[DllImport("kernel32")]
private extern static IntPtr LoadResource (
IntPtr hInstance,
IntPtr hResInfo);
[DllImport("kernel32")]
private extern static IntPtr LockResource(
IntPtr hResData);
[DllImport("kernel32", CharSet = CharSet.Auto)]
private extern static IntPtr FindResource(
IntPtr hInstance,
[MarshalAs(UnmanagedType.LPTStr)]
string lpName,
IntPtr lpType);
[DllImport("kernel32")]
private extern static int SizeofResource(
IntPtr hInstance,
IntPtr hResInfo);
[DllImport("kernel32")]
private extern static int FreeResource (
IntPtr hResData);
#endregion
#region Constants
private const Int16 IMAGE_ICON = 1;
private const int LOAD_LIBRARY_AS_DATAFILE = 0x2;
private const int RT_CURSOR = 1;
private const int RT_BITMAP = 2;
private const int RT_ICON = 3;
private const int DIFFERENCE = 11;
private const int RT_GROUP_CURSOR = RT_CURSOR + DIFFERENCE;
private const int RT_GROUP_ICON = RT_ICON + DIFFERENCE;
#endregion
#region Structures
private struct ICONDIRENTRY
{
public byte width; // Width of the image
public byte height; // Height of the image (times 2)
public byte colorCount; // Number of colors in image (0 if more than 8bpp)
public byte reserved; // Reserved
public Int16 wPlanes; // Color Planes
public Int16 wBitCount; // Bits per pixel
public int dwBytesInRes; // how many bytes in this resource?
public int dwImageOffset;// where in the file is this image
public ICONDIRENTRY(
BinaryReader br)
{
this.width = br.ReadByte();
this.height = br.ReadByte();
this.colorCount = br.ReadByte();
this.reserved = br.ReadByte();
this.wPlanes = br.ReadInt16();
this.wBitCount = br.ReadInt16();
this.dwBytesInRes = br.ReadInt32();
this.dwImageOffset = br.ReadInt32();
}
public void Write(
BinaryWriter br)
{
br.Write(this.width);
br.Write(this.height);
br.Write(this.colorCount);
br.Write(this.reserved);
br.Write(this.wPlanes);
br.Write(this.wBitCount);
br.Write(this.dwBytesInRes);
br.Write(this.dwImageOffset);
}
public override string ToString()
{
return string.Format("Size: ({0},{1}), ColorCount: {2}, Reserved : {3}, Planes: {4}, BitCount {5}, BytesInRes: {6}, ImageOffset {7}",
this.width, this.height, this.colorCount, this.reserved,
this.wPlanes, this.wBitCount,
this.dwBytesInRes, this.dwImageOffset);
}
}
private struct MEMICONDIRENTRY
{
public byte width; // Width of the image
public byte height; // Height of the image (times 2)
public byte colorCount; // Number of colors in image (0 if more than 8bpp)
public byte reserved; // Reserved
public Int16 wPlanes; // Color Planes
public Int16 wBitCount; // Bits per pixel
public int dwBytesInRes; // how many bytes in this resource?
public Int16 nID;// resource id of the image
public MEMICONDIRENTRY(
IntPtr lPtr,
int ofs)
{
this.width = Marshal.ReadByte(lPtr, ofs);
this.height = Marshal.ReadByte(lPtr, ofs + 1);
this.colorCount = Marshal.ReadByte(lPtr, ofs + 2);
this.reserved = Marshal.ReadByte(lPtr, ofs + 3);
this.wPlanes = Marshal.ReadInt16(lPtr, ofs + 4);
this.wBitCount = Marshal.ReadInt16(lPtr, ofs + 6);
this.dwBytesInRes = Marshal.ReadInt32(lPtr, ofs + 8);
this.nID = Marshal.ReadInt16(lPtr, ofs + 12);
}
public override string ToString()
{
return string.Format(
"Size: ({0},{1}), ColorCount: {2}, Planes: {3}, BitCount {4}, BytesInRes: {5}, IconResourceID {6}",
this.width, this.height, this.colorCount,
this.wPlanes , this.wBitCount,
this.dwBytesInRes, this.nID );
}
}
#endregion
#region Member Variables
private IconDeviceImageCollection iconCollection = null;
private string iconFile = null;
private string libraryFile = null;
private int resourceId = -1;
private string resourceName = null;
#endregion
#region Properties
/// <summary>
/// Returns the collection of device images
/// within this icon
/// </summary>
public IconDeviceImageCollection Items
{
get
{
return this.iconCollection;
}
set
{
}
}
/// <summary>
/// Gets the file the icon was loaded from
/// </summary>
public string IconFile
{
get
{
return this.iconFile;
}
}
/// <summary>
/// Gets the library this icon was loaded from
/// or a blank string if the icon was not sourced
/// from a library.
/// </summary>
public string LibraryFile
{
get
{
return this.libraryFile;
}
}
/// <summary>
/// Gets the integer resource id of this icon if it
/// was loaded from a library
/// </summary>
public int ResourceId
{
get
{
return this.resourceId;
}
}
/// <summary>
/// Gets the string resource id of this icon if it
/// was loaded from a library
/// </summary>
public string ResourceName
{
get
{
return this.resourceName;
}
}
#endregion
#region Methods
/// <summary>
/// Loads an icon from the specified file
/// </summary>
/// <param name="iconFile">File containing icon</param>
public void FromFile(
string iconFile)
{
loadFromFile(iconFile);
}
/// <summary>
/// Loads an icon from an executable or library
/// with the specified integer resource id
/// </summary>
/// <param name="libraryFile">Executable or DLL
/// containing icon</param>
/// <param name="resourceId">Integer resource identifier</param>
public void FromLibrary(
string libraryFile,
int resourceId)
{
loadInitialise();
string resourceName = "#" + resourceId.ToString(); //uses "G" String.Format("#{0:N0}", resourceId);
loadFromLibrary(libraryFile, resourceName);
}
/// <summary>
/// Loads an icon from an executable or library
/// with the specified string resource id
/// </summary>
/// <param name="libraryFile">Executable or DLL
/// containing icon</param>
/// <param name="resourceName">String resource identifier</param>
public void FromLibrary(
string libraryFile,
string resourceName
)
{
loadInitialise();
loadFromLibrary(libraryFile, resourceName);
}
/// <summary>
/// Saves the icon to the specified file
/// </summary>
/// <param name="iconFile">File to save to</param>
public void Save(
string iconFile
)
{
// open the file for writing, truncate if exists
FileStream fs = new FileStream(
iconFile,
FileMode.Create,
FileAccess.Write,
FileShare.Read);
BinaryWriter bw = null;
try
{
bw = new BinaryWriter(fs);
// write out the icon header:
writeIconFileHeader(bw);
// write out the icon directory entries:
int iconOffset = 6 + 16 * this.iconCollection.Count;
foreach (IconDeviceImage idi in this.iconCollection)
{
int bytesInRes = idi.IconImageDataBytes();
ICONDIRENTRY ide = new ICONDIRENTRY();
ide.width = (byte)idi.IconSize.Width;
ide.height = (byte)idi.IconSize.Height;
switch (idi.ColorDepth)
{
case ColorDepth.Depth4Bit:
ide.colorCount = 16;
ide.wBitCount = 4;
break;
case ColorDepth.Depth8Bit:
ide.colorCount = 0; //BUG CORRECTED, vasian, old was 255, based on http://www.daubnet.com/formats/ICO.html
ide.wBitCount = 8;
break;
case ColorDepth.Depth16Bit:
ide.colorCount = 0;
ide.wBitCount = 16;
break;
case ColorDepth.Depth24Bit:
ide.colorCount = 0;
ide.wBitCount = 24;
break;
case ColorDepth.Depth32Bit:
ide.colorCount = 0;
ide.wBitCount = 32;
break;
}
ide.wPlanes = 1;
ide.dwBytesInRes = bytesInRes;
ide.dwImageOffset = iconOffset;
ide.Write(bw);
iconOffset += bytesInRes;
}
// write out the icon data:
foreach (IconDeviceImage idi in this.iconCollection)
{
idi.SaveIconBitmapData(bw);
}
}
catch (Exception ex)
{
if (ex is SystemException)
{
throw ex;
}
else
{
throw new IconExException(ex.Message, ex);
}
}
finally
{
if (bw != null)
{
bw.Close();
}
}
}
#endregion
#region Private Implementation
private void loadInitialise()
{
this.iconFile = "";
this.resourceId = -1;
this.libraryFile = "";
this.iconCollection = new IconDeviceImageCollection();
}
// this method is too long, I'm sorry...
private void loadFromLibrary(
string libraryFile,
string resourceName
)
{
string msg = "";
bool failed = false;
IntPtr hGlobal = IntPtr.Zero;
IntPtr hRsrc = IntPtr.Zero;
IntPtr hLibrary = IntPtr.Zero;
IntPtr lPtr = IntPtr.Zero;
try
{
hLibrary = LoadLibraryEx(
libraryFile,
IntPtr.Zero,
LOAD_LIBRARY_AS_DATAFILE);
if (hLibrary != IntPtr.Zero)
{
hRsrc = FindResource(
hLibrary,
resourceName,
(IntPtr)RT_GROUP_ICON);
if (hRsrc != IntPtr.Zero)
{
hGlobal = LoadResource(hLibrary, hRsrc);
if (hGlobal != IntPtr.Zero)
{
lPtr = LockResource(hGlobal);
if (lPtr != IntPtr.Zero)
{
// now we can read the header:
int iconCount = readResourceIconFileHeader(lPtr);
// read the directory:
MEMICONDIRENTRY[] ide = new MEMICONDIRENTRY[iconCount];
int ofs = 6;
for (int iconEntry = 0; iconEntry < iconCount;
iconEntry++)
{
ide[iconEntry] = new MEMICONDIRENTRY(lPtr, ofs);
//Console.WriteLine(ide[iconEntry].ToString());
ofs += 14;
}
FreeResource(hGlobal);
hGlobal = IntPtr.Zero;
// we have the directory, so now can load the icons:
//IconDeviceImage[] icons = new
// IconDeviceImage[iconCount];
ArrayList icons = new ArrayList();
// read the icons:
for (int iconEntry = 0; iconEntry < iconCount;
iconEntry++)
{
// find the specified icon:
string resName = String.Format("#{0:G}", //N0
ide[iconEntry].nID);
hRsrc = FindResource(
hLibrary,
resName,
(IntPtr)RT_ICON);
if (hRsrc == IntPtr.Zero)
{
msg = String.Format(
"Could not find the component icon resource with id {0}",
ide[iconEntry].nID);
failed = true;
break;
}
else
{
// load the resource:
hGlobal = LoadResource(
hLibrary,
hRsrc);
if (hGlobal == IntPtr.Zero)
{
msg = String.Format(
"Could not load the component icon resource with id {0}",
ide[iconEntry].nID);
failed = true;
break;
}
else
{
// check the size:
int resSize = SizeofResource(hLibrary, hRsrc);
if ((resSize > 0) && (resSize ==
ide[iconEntry].dwBytesInRes))
{
// ok
lPtr = LockResource(hGlobal);
byte[] b = new byte[resSize];
Marshal.Copy(lPtr, b, 0, resSize);
try
{
//icons[iconEntry] = new IconDeviceImage(b);
icons.Add(new IconDeviceImage(b));
}
catch{}
}
else
{
msg = String.Format(
"Component icon resource with id {0} is corrupt",
ide[iconEntry].nID);
failed = true;
}
}
}
}
if (!failed)
{
// Add the icons to the collection:
IconDeviceImage[] temp = new IconDeviceImage[icons.Count];
icons.CopyTo(temp);
this.iconCollection = new
IconDeviceImageCollection(temp);
}
}
else
{
msg = "Can't lock resource for reading.";
failed = true;
}
}
else
{
msg = "Can't load resource for reading.";
failed = true;
}
}
else
{
msg = "Can't find resource.";
failed = true;
}
}
else
{
msg = "Can't load library.";
failed = true;
}
}
catch (Exception ex)
{
failed = true;
msg = ex.Message;
}
finally
{
// clear up handles:
if (hGlobal != IntPtr.Zero)
{
FreeResource(hGlobal);
}
if (hLibrary != IntPtr.Zero)
{
FreeLibrary(hLibrary);
}
if (failed)
{
throw new IconExException(msg);
}
}
}
private void loadFromFile(
string iconFile)
{
loadInitialise();
// Open the file
FileStream fs = new FileStream(
iconFile,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
BinaryReader br = new BinaryReader(fs);
try
{
// read the header:
int iconCount = readIconFileHeader(br);
// read the directory:
ICONDIRENTRY[] ide = new ICONDIRENTRY[iconCount];
for (int iconEntry = 0; iconEntry < iconCount; iconEntry++)
{
ide[iconEntry] = new ICONDIRENTRY(br);
}
IconDeviceImage[] icons = new IconDeviceImage[iconCount];
// read the actual icons:
for (int iconEntry = 0; iconEntry < iconCount; iconEntry++)
{
fs.Seek(ide[iconEntry].dwImageOffset, SeekOrigin.Begin);
byte[] b = new byte[ide[iconEntry].dwBytesInRes];
br.Read(b, 0, ide[iconEntry].dwBytesInRes);
icons[iconEntry] = new IconDeviceImage(b);
}
// Add the icons to the collection:
this.iconCollection = new IconDeviceImageCollection(icons);
}
catch (Exception ex)
{
if (ex is SystemException)
{
throw ex;
}
else
{
throw new IconExException("Failed to read icon file.", ex);
}
}
finally
{
br.Close();
}
this.iconFile = iconFile;
}
private int readResourceIconFileHeader(
IntPtr lPtr)
{
int idReserved = Marshal.ReadInt16(lPtr);
int idType = Marshal.ReadInt16(lPtr, 2);
int idCount = Marshal.ReadInt16(lPtr, 4);
if ((idReserved == 0) &&
(idType == IMAGE_ICON) &&
(idCount > 0) &&
(idCount < 1024))
{
return idCount;
}
else
{
throw new IconExException("Invalid Icon File Header");
}
}
private int readIconFileHeader(
BinaryReader br)
{
int idReserved = br.ReadInt16();
int idType = br.ReadInt16();
int idCount = br.ReadInt16();
if ((idReserved == 0) &&
(idType == IMAGE_ICON) &&
(idCount > 0) &&
(idCount < 1024))
{
return idCount;
}
else
{
throw new IconExException("Invalid Icon File Header");
}
}
private void writeIconFileHeader(
BinaryWriter bw)
{
Int16 idReserved = 0;
bw.Write(idReserved);
Int16 idType = IMAGE_ICON;
bw.Write(idType);
Int16 idCount = (Int16)this.Items.Count;
bw.Write(idCount);
}
#endregion
#region Constructor, Dispose
/// <summary>
/// Constructs a new, empty instance of the IconEx
/// object
/// </summary>
public IconEx()
{
}
/// <summary>
/// Constructs an IconEx instance and opens the icon
/// file specified.
/// </summary>
/// <param name="iconFile">Icon file to read</param>
public IconEx(string iconFile)
{
loadFromFile(iconFile);
}
/// <summary>
/// Constructs an IconEx instance and opens the icon
/// from the specified library (Executable or DLL)
/// with the specified integer resource identifier
/// </summary>
/// <param name="libraryFile">Executable or DLL to extract
/// icon from</param>
/// <param name="resourceId">Integer resource Id</param>
public IconEx(
string libraryFile,
int resourceId
)
{
FromLibrary(libraryFile, resourceId);
}
/// <summary>
/// Constructs an IconEx instance and opens the icon
/// from the specified library (Executable or DLL)
/// with the specified string resource identifier
/// </summary>
/// <param name="libraryFile">Executable or DLL to extract
/// icon from</param>
/// <param name="resourceName">String resource Id</param>
public IconEx(
string libraryFile,
string resourceName
)
{
FromLibrary(libraryFile, resourceName);
}
public void Dispose()
{
if (this.iconCollection != null)
{
iconCollection.Dispose();
iconCollection = null;
}
}
#endregion
}
#endregion
#region IconDeviceImageCollection
/// <summary>
/// Manages a read/write collection of icon resources
/// within an Icon file
/// </summary>
public class IconDeviceImageCollection : CollectionBase, IDisposable
{
#region Member Variables
#endregion
#region Methods
/// <summary>
/// Add a new icon device image
/// </summary>
/// <param name="icon">Icon to add</param>
public void Add(IconDeviceImage icon)
{
foreach (IconDeviceImage iconExisting in this.InnerList)
{
if (icon.IconSize.Equals(iconExisting.IconSize) &&
icon.ColorDepth.Equals(iconExisting.ColorDepth))
{
throw new IconExException("An Icon Device Image with the same size and colour depth already exists in this icon");
}
}
this.InnerList.Add(icon);
}
/// <summary>
/// Gets the IconDevice Image at the specified
/// index
/// </summary>
public IconDeviceImage this[int index]
{
get
{
return (IconDeviceImage)this.InnerList[index];
}
}
#endregion
#region Constructor, Dispose
/// <summary>
/// Constructs a new, empty collection of device
/// images.
/// </summary>
public IconDeviceImageCollection()
{
}
/// <summary>
/// Constructs a new collection of device images
/// </summary>
/// <param name="icons">Icons to add</param>
public IconDeviceImageCollection(
IconDeviceImage[] icons
)
{
foreach (IconDeviceImage icon in icons)
{
this.InnerList.Add(icon);
}
}
public void Dispose()
{
if (this.InnerList != null)
{
foreach (IconDeviceImage icon in this.InnerList)
{
if(icon != null)
{
icon.Dispose();
}
}
this.InnerList.Clear();
}
}
#endregion
}
#endregion
#region IconDeviceImage
/// <summary>
/// Manages a single icon device image within an
/// Icon file
/// </summary>
public class IconDeviceImage : IDisposable
{
#region Member Variables
private Size size;
private System.Windows.Forms.ColorDepth colorDepth = ColorDepth.Depth4Bit;
private byte[] data;
private IntPtr hIcon = IntPtr.Zero;
#endregion
#region Unmanaged Code
[DllImport("gdi32")]
private static extern int SetDIBitsToDevice(
IntPtr hdc,
int X, int Y, int dx, int dy,
int SrcX, int SrcY, int Scan, int NumScans,
IntPtr Bits,
IntPtr BitsInfo,
int wUsage);
[DllImport("gdi32")]
public static extern int GetDIBits (
IntPtr hdc,
IntPtr hBitmap,
int nStartScan,
int nNumScans,
IntPtr Bits,
IntPtr BitsInfo,
int wUsage);
private const int DIB_RGB_COLORS = 0; // color table in RGBs
private const int DIB_PAL_COLORS = 1; // color table in palette indices
private const int DIB_PAL_INDICES = 2; // No color table indices into surf palette
private const int DIB_PAL_PHYSINDICES = 2; // No color table indices into surf palette
private const int DIB_PAL_LOGINDICES = 4; // No color table indices into DC palette
// Bitmap compression types:
private const int BI_RGB = 0x0;
private const int BI_RLE4 = 0x2;
private const int BI_RLE8 = 0x1;
[DllImport("gdi32")]
private static extern IntPtr CreateCompatibleDC(
IntPtr hdc);
[DllImport("gdi32", CharSet=CharSet.Auto)]
private static extern IntPtr CreateDC (
[MarshalAs(UnmanagedType.LPTStr)]
string lpDriverName,
IntPtr lpDeviceName,
IntPtr lpOutput,
IntPtr lpInitData);
[DllImport("gdi32")]
private static extern IntPtr CreateCompatibleBitmap(
IntPtr hdc,
int width,
int height);
[DllImport("gdi32")]
private static extern IntPtr SelectObject(
IntPtr hdc, IntPtr hObject);
[DllImport("gdi32")]
private static extern int DeleteObject(
IntPtr hObject);
[DllImport("gdi32")]
private static extern int DeleteDC(
IntPtr hdc);
[DllImport("user32")]
private static extern int DestroyIcon(
IntPtr hIcon);
[DllImport("user32")]
private static extern IntPtr CreateIconIndirect(
ref ICONINFO piconInfo);
private const Int16 IMAGE_ICON = 1;
#endregion
#region Structures
#region ICONINFO. Used by the CreateIconIndirect API function
private struct ICONINFO
{
public int fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hBmMask;
public IntPtr hBmColor;
}
#endregion
#region BITMAPINFOHEADER. This is stored at the start of an icon's data
private struct BITMAPINFOHEADER
{
public int biSize;
public int biWidth;
public int biHeight;
public Int16 biPlanes;
public Int16 biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
public BITMAPINFOHEADER(
Size size,
ColorDepth colorDepth
)
{
this.biSize = 0;
this.biWidth = size.Width;
this.biHeight = size.Height * 2;
this.biPlanes = 1;
this.biCompression = BI_RGB;
this.biSizeImage = 0;
this.biXPelsPerMeter = 0;
this.biYPelsPerMeter = 0;
this.biClrUsed = 0;
this.biClrImportant = 0;
switch (colorDepth)
{
case ColorDepth.Depth4Bit:
this.biBitCount = 4;
break;
case ColorDepth.Depth8Bit:
this.biBitCount = 8;
break;
case ColorDepth.Depth16Bit:
this.biBitCount = 16;
break;
case ColorDepth.Depth24Bit:
this.biBitCount = 24;
break;
case ColorDepth.Depth32Bit:
this.biBitCount = 32;
break;
default:
this.biBitCount = 4;
break;
}
this.biSize = Marshal.SizeOf(this.GetType());
}
public void Write(
BinaryWriter bw)
{
bw.Write(this.biSize);
bw.Write(this.biWidth);
bw.Write(this.biHeight);
bw.Write(this.biPlanes);
bw.Write(this.biBitCount);
bw.Write(this.biCompression);
bw.Write(this.biSizeImage);
bw.Write(this.biXPelsPerMeter);
bw.Write(this.biYPelsPerMeter);
bw.Write(this.biClrUsed);
bw.Write(this.biClrImportant);
}
public BITMAPINFOHEADER(byte[] data)
{
MemoryStream ms = new MemoryStream(data, false);
BinaryReader br = new BinaryReader(ms);
biSize = br.ReadInt32();
biWidth = br.ReadInt32();
biHeight = br.ReadInt32();
biPlanes = br.ReadInt16();
biBitCount = br.ReadInt16();
biCompression = br.ReadInt32();
biSizeImage = br.ReadInt32();
biXPelsPerMeter = br.ReadInt32();
biYPelsPerMeter = br.ReadInt32();
biClrUsed = br.ReadInt32();
biClrImportant = br.ReadInt32();
br.Close();
}
public override string ToString()
{
return string.Format(
"biSize: {0}, biWidth: {1}, biHeight: {2}, biPlanes: {3}, biBitCount: {4},"
+ "biCompression: {5}, biSizeImage: {6}, biXPelsPerMeter: {7}, biYPelsPerMeter {8}, biClrUsed {9},"
+ "biClrImportant {10}",
biSize, biWidth, biHeight, biPlanes, biBitCount,
biCompression, biSizeImage, biXPelsPerMeter, biYPelsPerMeter,
biClrUsed, biClrImportant);
}
}
#endregion
#region RQBQUAD. Used to store colours in a paletised icon (2, 4 or 8 bit)
private struct RGBQUAD
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
public RGBQUAD(
byte r, byte g, byte b, byte alpha
)
{
rgbBlue = b;
rgbGreen = g;
rgbRed = r;
rgbReserved = 0; //alpha;
}
public RGBQUAD(
Color c
)
{
rgbBlue = c.B;
rgbGreen = c.G;
rgbRed = c.R;
rgbReserved = 0; //c.A;
}
public void Write(
BinaryWriter bw)
{
bw.Write(this.rgbBlue);
bw.Write(this.rgbGreen);
bw.Write(this.rgbRed);
bw.Write(this.rgbReserved);
}
public override string ToString()
{
return string.Format(
"rgbBlue: {0}, rgbGreen: {1}, rgbRed: {2}", rgbBlue, rgbGreen, rgbRed);
}
}
#endregion
#endregion
#region Properties
/// <summary>
/// Gets the Icon handle for this device image
/// </summary>
public IntPtr Handle
{
get
{
return this.hIcon;
}
}
/// <summary>
/// Gets the size of this device image
/// </summary>
public Size IconSize
{
get
{
return this.size;
}
}
/// <summary>
/// Gets the colour depth of this device image
/// </summary>
public System.Windows.Forms.ColorDepth ColorDepth
{
get
{
return this.colorDepth;
}
}
/// <summary>
/// Gets/sets the Mask Image of the icon as a bitmap
/// </summary>
public System.Drawing.Bitmap MaskImage
{
get
{
IntPtr junk = IntPtr.Zero;
Bitmap bm = getIconBitmap(true, false, ref junk);
return bm;
}
set
{
setMaskBitsFromBitmap(value);
}
}
/// <summary>
/// Gets/sets the image portion of the icon as a bitmap
/// </summary>
public System.Drawing.Bitmap IconImage
{
get
{
IntPtr junk = IntPtr.Zero;
Bitmap bm = getIconBitmap(false, false, ref junk);
return bm;
}
set
{
setImageBitsFromBitmap(value);
}
}
/// <summary>
/// Gets the device image as a managed icon
/// Note that you should clone the icon if you want to keep it
/// after this class has been disposed.
/// </summary>
public System.Drawing.Icon Icon
{
get
{
System.Drawing.Icon icon =
System.Drawing.Icon.FromHandle(this.hIcon);
return icon;
}
}
#endregion
#region Private Implementation
private void setMaskBitsFromBitmap(
Bitmap bm
)
{
IntPtr hdcc = CreateDC("DISPLAY", IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero);
IntPtr hdc = CreateCompatibleDC(hdcc);
DeleteDC(hdcc);
IntPtr hBmp = bm.GetHbitmap();
BITMAPINFOHEADER bmInfoHdr = new BITMAPINFOHEADER(
this.size, this.colorDepth);
// Now prepare the for GetDIBits call:
RGBQUAD rgbQuad = new RGBQUAD();
int monoBmHdrSize = bmInfoHdr.biSize + Marshal.SizeOf(rgbQuad)* 2;
IntPtr bitsInfo = Marshal.AllocCoTaskMem(
monoBmHdrSize);
Marshal.WriteInt32(bitsInfo, Marshal.SizeOf(bmInfoHdr));
Marshal.WriteInt32(bitsInfo, 4, this.size.Width);
Marshal.WriteInt32(bitsInfo, 8, this.size.Height);
Marshal.WriteInt16(bitsInfo, 12, 1);
Marshal.WriteInt16(bitsInfo, 14, 1);
Marshal.WriteInt32(bitsInfo, 16, BI_RGB);
Marshal.WriteInt32(bitsInfo, 20, 0);
Marshal.WriteInt32(bitsInfo, 24, 0);
Marshal.WriteInt32(bitsInfo, 28, 0);
Marshal.WriteInt32(bitsInfo, 32, 0);
Marshal.WriteInt32(bitsInfo, 36, 0);
// Write the black and white colour indices:
Marshal.WriteInt32(bitsInfo, 40, 0);
Marshal.WriteByte(bitsInfo, 44, 255);
Marshal.WriteByte(bitsInfo, 45, 255);
Marshal.WriteByte(bitsInfo, 46, 255);
Marshal.WriteByte(bitsInfo, 47, 0);
int maskImageBytes = MaskImageSize(bmInfoHdr);
IntPtr bits = Marshal.AllocCoTaskMem(maskImageBytes);
int success = GetDIBits(hdc, hBmp, 0, this.size.Height, bits,
bitsInfo, DIB_RGB_COLORS);
Marshal.Copy(bits, data, MaskImageIndex(bmInfoHdr), maskImageBytes);
// Free memory:
Marshal.FreeCoTaskMem(bits);
Marshal.FreeCoTaskMem(bitsInfo);
DeleteObject(hBmp);
DeleteDC(hdc);
createIcon();
}
private void setImageBitsFromBitmap(
Bitmap bm
)
{
IntPtr hdcc = CreateDC("DISPLAY", IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero);
IntPtr hdc = CreateCompatibleDC(hdcc);
DeleteDC(hdcc);
IntPtr hBmp = bm.GetHbitmap();
BITMAPINFOHEADER bmInfoHdr = new BITMAPINFOHEADER(
this.size, this.colorDepth);
// Now prepare for GetDIBits call:
int xorIndex = XorImageIndex(bmInfoHdr);
int xorImageBytes = XorImageSize(bmInfoHdr);
// Get the BITMAPINFO header into the pointer:
IntPtr bitsInfo = Marshal.AllocCoTaskMem(
xorIndex);
Marshal.Copy(data, 0, bitsInfo, xorIndex);
// fix the height:
Marshal.WriteInt32(bitsInfo, 8, bmInfoHdr.biHeight / 2);
IntPtr bits = Marshal.AllocCoTaskMem(xorImageBytes);
int success = GetDIBits(hdc, hBmp, 0, this.size.Height, bits,
bitsInfo, DIB_RGB_COLORS);
Marshal.Copy(bits, data, xorIndex, xorImageBytes);
// Free memory:
Marshal.FreeCoTaskMem(bits);
Marshal.FreeCoTaskMem(bitsInfo);
DeleteObject(hBmp);
DeleteDC(hdc);
createIcon();
}
private void setDeviceImage(
Size size,
System.Windows.Forms.ColorDepth colorDepth
)
{
this.size = size;
this.colorDepth = colorDepth;
// Initialise the data:
BITMAPINFOHEADER bmInfoHdr = new BITMAPINFOHEADER(
size, colorDepth);
this.data = new byte[
this.MaskImageIndex(bmInfoHdr) + this.MaskImageSize(bmInfoHdr)];
MemoryStream mw = new MemoryStream(this.data, 0, this.data.Length,
true);
BinaryWriter bw = new BinaryWriter(mw);
bmInfoHdr.Write(bw);
// Write the colour indexes if required:
switch (this.colorDepth)
{
case ColorDepth.Depth4Bit:
write16ColorPalette(bw);
break;
case ColorDepth.Depth8Bit:
write256ColorPalette(bw);
break;
}
bw.Close();
}
private void write16ColorPalette(
BinaryWriter bw)
{
// Write out 16 entries containing the
// standard colour palette:
writeColor(bw, Color.Black);
writeColor(bw, Color.White);
writeColor(bw, Color.Red);
writeColor(bw, Color.Green);
writeColor(bw, Color.Blue);
writeColor(bw, Color.Yellow);
writeColor(bw, Color.Magenta);
writeColor(bw, Color.Cyan);
writeColor(bw, Color.Gray);
writeColor(bw, Color.DarkRed);
writeColor(bw, Color.DarkGreen);
writeColor(bw, Color.DarkBlue);
writeColor(bw, Color.Olive);
writeColor(bw, Color.Purple);
writeColor(bw, Color.Teal);
writeColor(bw, Color.DarkGray);
}
private void write256ColorPalette(
BinaryWriter bw)
{
KnownColor kc = KnownColor.ActiveBorder;
Array colors = Enum.GetValues(kc.GetType());
int i = 0;
foreach (KnownColor color in colors)
{
writeColor(bw, Color.FromKnownColor(color));
i++;
if (i > 255)
{
break;
}
}
}
private void writeColor(
BinaryWriter bw,
Color color
)
{
RGBQUAD r = new RGBQUAD(color);
r.Write(bw);
}
private Bitmap getIconBitmap(
bool mask,
bool returnHandle,
ref IntPtr hBmp
)
{
// Bitmap to return
Bitmap bm = null;
// Get bitmap info:
BITMAPINFOHEADER bmInfoHdr = new BITMAPINFOHEADER(data);
if (mask)
{
// extract monochrome mask
IntPtr hdc = CreateCompatibleDC(IntPtr.Zero);
hBmp = CreateCompatibleBitmap(hdc, bmInfoHdr.biWidth,
bmInfoHdr.biHeight / 2);
IntPtr hBmpOld = SelectObject(hdc, hBmp);
// Prepare BitmapInfoHeader for mono bitmap:
RGBQUAD rgbQuad = new RGBQUAD();
int monoBmHdrSize = bmInfoHdr.biSize + Marshal.SizeOf(rgbQuad)* 2;
IntPtr bitsInfo = Marshal.AllocCoTaskMem(
monoBmHdrSize);
Marshal.WriteInt32(bitsInfo, Marshal.SizeOf(bmInfoHdr));
Marshal.WriteInt32(bitsInfo, 4, bmInfoHdr.biWidth);
Marshal.WriteInt32(bitsInfo, 8, bmInfoHdr.biHeight / 2);
Marshal.WriteInt16(bitsInfo, 12, 1);
Marshal.WriteInt16(bitsInfo, 14, 1);
Marshal.WriteInt32(bitsInfo, 16, BI_RGB);
Marshal.WriteInt32(bitsInfo, 20, 0);
Marshal.WriteInt32(bitsInfo, 24, 0);
Marshal.WriteInt32(bitsInfo, 28, 0);
Marshal.WriteInt32(bitsInfo, 32, 0);
Marshal.WriteInt32(bitsInfo, 36, 0);
// Write the black and white colour indices:
Marshal.WriteInt32(bitsInfo, 40, 0);
Marshal.WriteByte(bitsInfo, 44, 255);
Marshal.WriteByte(bitsInfo, 45, 255);
Marshal.WriteByte(bitsInfo, 46, 255);
Marshal.WriteByte(bitsInfo, 47, 0);
// Prepare Mask bits:
int maskImageBytes = MaskImageSize(bmInfoHdr);
IntPtr bits = Marshal.AllocCoTaskMem(maskImageBytes);
Marshal.Copy(data, MaskImageIndex(bmInfoHdr), bits, maskImageBytes);
int success = SetDIBitsToDevice(
hdc,
0, 0, bmInfoHdr.biWidth, bmInfoHdr.biHeight / 2,
0, 0, 0, bmInfoHdr.biHeight / 2,
bits,
bitsInfo,
DIB_RGB_COLORS);
Marshal.FreeCoTaskMem(bits);
Marshal.FreeCoTaskMem(bitsInfo);
SelectObject(hdc, hBmpOld);
DeleteObject(hdc);
}
else
{
// extract colour (XOR) part of image:
// Create bitmap:
IntPtr hdcDesktop = CreateDC("DISPLAY", IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero);
IntPtr hdc = CreateCompatibleDC(hdcDesktop);
hBmp = CreateCompatibleBitmap(hdcDesktop, bmInfoHdr.biWidth,
bmInfoHdr.biHeight / 2);
DeleteDC(hdcDesktop);
IntPtr hBmpOld = SelectObject(hdc, hBmp);
// Find the index of the XOR bytes:
int xorIndex = XorImageIndex(bmInfoHdr);
int xorImageSize = XorImageSize(bmInfoHdr);
// Get Bitmap info header to a pointer:
IntPtr bitsInfo = Marshal.AllocCoTaskMem(xorIndex);
Marshal.Copy(data, 0, bitsInfo, xorIndex);
// fix the height:
Marshal.WriteInt32(bitsInfo, 8, bmInfoHdr.biHeight / 2);
// Get the XOR bits:
IntPtr bits = Marshal.AllocCoTaskMem(xorImageSize);
Marshal.Copy(data, xorIndex, bits, xorImageSize);
int success = SetDIBitsToDevice(
hdc,
0, 0, bmInfoHdr.biWidth, bmInfoHdr.biHeight/2,
0, 0, 0, bmInfoHdr.biHeight/2,
bits,
bitsInfo,
DIB_RGB_COLORS);
Marshal.FreeCoTaskMem(bits);
Marshal.FreeCoTaskMem(bitsInfo);
SelectObject(hdc, hBmpOld);
DeleteObject(hdc);
}
if (!returnHandle)
{
// the bitmap will own the handle and clear
// it up when it is disposed. Otherwise
// need to call DeleteObject on hBmp
// returned.
bm = Bitmap.FromHbitmap(hBmp);
}
return bm;
}
private int MaskImageIndex(
BITMAPINFOHEADER bmInfoHeader
)
{
int maskImageIndex = XorImageIndex(bmInfoHeader);
maskImageIndex += XorImageSize(bmInfoHeader);
return maskImageIndex;
}
private int XorImageSize(
BITMAPINFOHEADER bmInfoHeader
)
{
int imageBytes = (bmInfoHeader.biHeight/ 2 *
WidthBytes(bmInfoHeader.biWidth * bmInfoHeader.biBitCount *
bmInfoHeader.biPlanes));
return imageBytes;
}
private int MaskImageSize(
BITMAPINFOHEADER bmInfoHeader
)
{
int imageBytes = bmInfoHeader.biHeight / 2 *
WidthBytes(bmInfoHeader.biWidth);
return imageBytes;
}
private int WidthBytes(int width)
{
// Returns the width of a row in a DIB Bitmap given the
// number of bits. DIB Bitmap rows always align on a
// DWORD boundary.
int widthBytes = ((width + 31) / 32) * 4;
return widthBytes;
}
private int XorImageIndex(
BITMAPINFOHEADER bmInfoHeader)
{
// Returns the position of the DIB bitmap bits within a
// DIB bitmap array:
RGBQUAD rgbq = new RGBQUAD();
return Marshal.SizeOf(bmInfoHeader) +
dibNumColors(bmInfoHeader) * Marshal.SizeOf(rgbq);
}
private int dibNumColors(
BITMAPINFOHEADER bmInfoHeader)
{
int colorCount = 0;
if (bmInfoHeader.biClrUsed != 0)
{
colorCount = bmInfoHeader.biClrUsed;
}
else
{
switch (bmInfoHeader.biBitCount)
{
case 1:
colorCount = 2;
break;
case 4:
colorCount = 16;
break;
case 8:
colorCount = 256;
break;
}
}
return colorCount;
}
/// <summary>
/// Internal method. Returns the number of bytes in the
/// icon data. Not intended for public use.
/// </summary>
/// <returns>Number of bytes of icon data</returns>
internal int IconImageDataBytes()
{
return this.data.Length;
}
/// <summary>
/// Internal method. Writes the icon bitmap data to
/// the specified BinaryWriter. Not intended for
/// public use.
/// </summary>
/// <param name="bw">BinaryWriter to write to</param>
internal void SaveIconBitmapData(
BinaryWriter bw)
{
bw.Write(this.data, 0, this.data.Length);
}
private void createIcon()
{
if (this.hIcon != IntPtr.Zero)
{
DestroyIcon(this.hIcon);
this.hIcon = IntPtr.Zero;
}
ICONINFO ii = new ICONINFO();
ii.fIcon = IMAGE_ICON;
getIconBitmap(false, true, ref ii.hBmColor);
getIconBitmap(true, true, ref ii.hBmMask);
this.hIcon = CreateIconIndirect(ref ii);
DeleteObject(ii.hBmColor);
DeleteObject(ii.hBmMask);
}
#endregion
#region Constructor, Dispose
/// <summary>
/// Constructs a new IconDeviceImage with the specified
/// size and colour depth.
/// </summary>
/// <param name="size">Size of device image</param>
/// <param name="colorDepth">Colour depth of device image</param>
public IconDeviceImage(
Size size,
System.Windows.Forms.ColorDepth colorDepth
)
{
setDeviceImage(size, colorDepth);
createIcon();
}
/// <summary>
/// Constructs a new IconDeviceImage from a Managed Icon
/// </summary>
/// <param name="icon">Icon to construct from</param>
public IconDeviceImage(
System.Drawing.Icon icon
)
{
// use DrawIconEx to create bitmaps for the
// colour and mask images, then use GetDIBits
// to populate data
}
/// <summary>
/// Constructs a new icon device image from an array of
/// bytes in the Icon file format
/// </summary>
/// <param name="b">Array of bytes</param>
internal IconDeviceImage(
byte[] b
)
{
// store the bytes:
data = new Byte[b.Length];
for (int i = 0; i < b.Length; i++)
{
data[i] = b[i];
}
// Read the BitmapInfoHeader structure to get the
// size and number of bytes:
BITMAPINFOHEADER bmInfoHeader = new BITMAPINFOHEADER(data);
//Console.WriteLine(bmInfoHeader.ToString());
this.size.Width = bmInfoHeader.biWidth;
this.size.Height = bmInfoHeader.biHeight/2;
switch (bmInfoHeader.biBitCount)
{
case 1:
case 4:
this.colorDepth = ColorDepth.Depth4Bit;
break;
case 8:
this.colorDepth = ColorDepth.Depth8Bit;
break;
case 16:
this.colorDepth = ColorDepth.Depth16Bit;
break;
case 24:
this.colorDepth = ColorDepth.Depth24Bit;
break;
case 32:
this.colorDepth = ColorDepth.Depth32Bit;
break;
}
createIcon();
}
/// <summary>
/// Clears up any resources associated with this
/// Icon Device Image.
/// </summary>
public void Dispose()
{
if (this.hIcon != IntPtr.Zero)
{
DestroyIcon(this.hIcon);
this.hIcon = IntPtr.Zero;
}
}
#endregion
}
#endregion
}
|