问题描述
将图标拆分为 Bitmap
部分很容易:
Splitting an icon into its Bitmap
parts is easy:
Bitmap icon16 = new Icon(combinedIcon, new Size(16, 16)).ToBitmap()
但是如何将多个位图
对象合并到一个 Icon
中?
Bitmap icon16, icon32, icon64;
Icon combinedIcon = [...]
我对 Icon
对象的理解不是很普遍.它确实是一组多个图像.加载时,可以将其分成其 Bitmap
部分.但是我看不到创建多图标的任何方法.无法以明显的方式迭代,添加或删除 Bitmap
部分也似乎很奇怪,例如拥有一组位图.
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.
推荐答案
.Net中的 Icon
类非常基础,甚至无法访问实际的所有功能.图标格式.最好将图标构造为字节流,然后将其加载为图标.
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.
我回想了一下格式,实际上它接受png数据作为内部图像.请注意,上述图片的宽度或高度不能超过256个像素,并且文件中的图片数量以两个字节保存,因此它不能超过 Int16.MaxValue
或0xFFFF,或者65535.
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.
代码应如下所示:
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);
}
}
请注意,与其他 System.Drawing
图像格式不同, Icon
类确实不需要流保持打开状态;它只是从流中读取字节并将其留在那里.
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.
如果您希望将图标文件作为字节数组,或者想要将其写入光盘,则当然可以改编此代码以返回字节数组,甚至只是将其写入作为参数提供的流
,而不是在内部创建 MemoryStream
.
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.
这篇关于合并System.Drawing.Bitmap []->图标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!