问题
Splitting an icon into its Bitmap
parts is easy:
Bitmap icon16 = new Icon(combinedIcon, new Size(16, 16)).ToBitmap()
But how do you merge multiple Bitmap
objects into one Icon
?
Bitmap icon16, icon32, icon64;
Icon combinedIcon = [...]
I'm not that clear about the Icon
object in general. It is indeed a set of multiple images. When loading it, you can take separate it into its Bitmap
parts. But I don't see any method to create a multi-icon. It also seems strange to not being able to iterate, add or remove Bitmap
parts in an obvious fashion, like having a collection of bitmaps.
回答1:
The Icon
class in .Net is very rudimentary, and doesn't even get close to giving access to all features of the actual icon format. It's probably best to construct the icon as byte stream, and then load that as icon.
I looked into the format a while back, and it actually accepts png data as internal images. Do note that said images cannot have a width or height of more than 256 pixels, and the amount of images in the file is saved in two bytes, so it cannot exceed Int16.MaxValue
, or 0xFFFF, or 65535.
The code should look something like this:
public static Icon ConvertImagesToIco(Image[] images)
{
if (images == null)
throw new ArgumentNullException("images");
Int32 imgCount = images.Length;
if (imgCount == 0)
throw new ArgumentException("No images given!", "images");
if (imgCount > 0xFFFF)
throw new ArgumentException("Too many images!", "images");
using (MemoryStream ms = new MemoryStream())
using (BinaryWriter iconWriter = new BinaryWriter(ms))
{
Byte[][] frameBytes = new Byte[imgCount][];
// 0-1 reserved, 0
iconWriter.Write((Int16)0);
// 2-3 image type, 1 = icon, 2 = cursor
iconWriter.Write((Int16)1);
// 4-5 number of images
iconWriter.Write((Int16)imgCount);
// Calculate header size for first image data offset.
Int32 offset = 6 + (16 * imgCount);
for (Int32 i = 0; i < imgCount; ++i)
{
// Get image data
Image curFrame = images[i];
if (curFrame.Width > 256 || curFrame.Height > 256)
throw new ArgumentException("Image too large!", "images");
// for these three, 0 is interpreted as 256,
// so the cast reducing 256 to 0 is no problem.
Byte width = (Byte)curFrame.Width;
Byte height = (Byte)curFrame.Height;
Byte colors = (Byte)curFrame.Palette.Entries.Length;
Int32 bpp;
Byte[] frameData;
using (MemoryStream pngMs = new MemoryStream())
{
curFrame.Save(pngMs, ImageFormat.Png);
frameData = pngMs.ToArray();
}
// Get the colour depth to save in the icon info. This needs to be
// fetched explicitly, since png does not support certain types
// like 16bpp, so it will convert to the nearest valid on save.
Byte colDepth = frameData[24];
Byte colType = frameData[25];
// I think .Net saving only supports colour types 2, 3 and 6 anyway.
switch (colType)
{
case 2: bpp = 3 * colDepth; break; // RGB
case 6: bpp = 4 * colDepth; break; // ARGB
default: bpp = colDepth; break; // Indexed & greyscale
}
frameBytes[i] = frameData;
Int32 imageLen = frameData.Length;
// Write image entry
// 0 image width.
iconWriter.Write(width);
// 1 image height.
iconWriter.Write(height);
// 2 number of colors.
iconWriter.Write(colors);
// 3 reserved
iconWriter.Write((Byte)0);
// 4-5 color planes
iconWriter.Write((Int16)0);
// 6-7 bits per pixel
iconWriter.Write((Int16)bpp);
// 8-11 size of image data
iconWriter.Write(imageLen);
// 12-15 offset of image data
iconWriter.Write(offset);
offset += imageLen;
}
for (Int32 i = 0; i < imgCount; i++)
{
// Write image data
// png data must contain the whole png data file
iconWriter.Write(frameBytes[i]);
}
iconWriter.Flush();
ms.Position = 0;
return new Icon(ms);
}
}
Note that unlike other System.Drawing
image formats, the Icon
class does not require the stream to stay open; it just reads its bytes from the stream and leaves it at that.
The png colour depth info can be found here and here, btw.
If you want to have the icon file as byte array, or want to write it to disc, you can of course adapt this code to return a byte array, or even just to make it write the stuff to a Stream
given as argument rather than creating the MemoryStream
internally.
来源:https://stackoverflow.com/questions/54801185/combine-system-drawing-bitmap-icon