组合 System.Drawing.Bitmap[] -> 图标
Combine System.Drawing.Bitmap[] -> Icon
将图标拆分为 Bitmap
个部分很容易:
Bitmap icon16 = new Icon(combinedIcon, new Size(16, 16)).ToBitmap()
但是如何将多个Bitmap
对象合并为一个Icon
?
Bitmap icon16, icon32, icon64;
Icon combinedIcon = [...]
总的来说,我 不清楚 Icon
对象。它确实是一组多个图像。加载时,可以将其分成 Bitmap
个部分。但我没有看到任何创建多图标的方法。不能以明显的方式迭代、添加或删除 Bitmap
部分也很奇怪,比如拥有一组位图。
.Net 中的 Icon
class 非常简陋,甚至无法提供对实际图标格式的所有功能的访问权限。最好将图标构建为字节流,然后将其作为图标加载。
我前一段时间研究过这种格式,它实际上接受 png 数据作为内部图像。请注意,上述图像的宽度或高度不能超过 256 像素,并且文件中的图像数量以两个字节保存,因此不能超过 Int16.MaxValue
,或 0xFFFF,或 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 at index " + i + " is 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
class 不 要求流保持打开状态;它只是从流中读取它的字节并将其保留在那里。
可以找到 png 颜色深度信息 here and here,顺便说一句。
如果你想把图标文件作为字节数组,或者想把它写入光盘,你当然可以将这段代码改编成return字节数组,甚至只是让它写入作为参数给出的 Stream
的内容,而不是在内部创建 MemoryStream
。
将图标拆分为 Bitmap
个部分很容易:
Bitmap icon16 = new Icon(combinedIcon, new Size(16, 16)).ToBitmap()
但是如何将多个Bitmap
对象合并为一个Icon
?
Bitmap icon16, icon32, icon64;
Icon combinedIcon = [...]
总的来说,我 不清楚 Icon
对象。它确实是一组多个图像。加载时,可以将其分成 Bitmap
个部分。但我没有看到任何创建多图标的方法。不能以明显的方式迭代、添加或删除 Bitmap
部分也很奇怪,比如拥有一组位图。
.Net 中的 Icon
class 非常简陋,甚至无法提供对实际图标格式的所有功能的访问权限。最好将图标构建为字节流,然后将其作为图标加载。
我前一段时间研究过这种格式,它实际上接受 png 数据作为内部图像。请注意,上述图像的宽度或高度不能超过 256 像素,并且文件中的图像数量以两个字节保存,因此不能超过 Int16.MaxValue
,或 0xFFFF,或 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 at index " + i + " is 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
class 不 要求流保持打开状态;它只是从流中读取它的字节并将其保留在那里。
可以找到 png 颜色深度信息 here and here,顺便说一句。
如果你想把图标文件作为字节数组,或者想把它写入光盘,你当然可以将这段代码改编成return字节数组,甚至只是让它写入作为参数给出的 Stream
的内容,而不是在内部创建 MemoryStream
。