C# 如何在ASP.NET中将位图保存为16色灰度GIF或PNG?
在ASP.NET C#中,我试图将位图图像保存为16色非透明灰度图像,保存为PNG或GIF。我假设我必须创建一个调色板,然后以某种方式将调色板附加到图像上,但不确定如何执行此操作C# 如何在ASP.NET中将位图保存为16色灰度GIF或PNG?,c#,asp.net,png,gif,C#,Asp.net,Png,Gif,在ASP.NET C#中,我试图将位图图像保存为16色非透明灰度图像,保存为PNG或GIF。我假设我必须创建一个调色板,然后以某种方式将调色板附加到图像上,但不确定如何执行此操作 源图像是一个24位彩色位图。它被称为量化,而且很复杂。我对这个问题进行了广泛的研究,我的最佳结果是使用八叉树量化和自定义扩散算法 从A到B的最快点是,使用极其简单的API将颜色计数设置为16,并另存为GIF或PNG。应该是大约2行代码,如果你想通过代码隐藏。。。或者,如果querystring位于文件系统上,则可以使用
源图像是一个24位彩色位图。它被称为量化,而且很复杂。我对这个问题进行了广泛的研究,我的最佳结果是使用八叉树量化和自定义扩散算法 从A到B的最快点是,使用极其简单的API将颜色计数设置为16,并另存为GIF或PNG。应该是大约2行代码,如果你想通过代码隐藏。。。或者,如果querystring位于文件系统上,则可以使用它:
image.bmp?format=gif&colors=16
如果图像尚未灰度化,则可以使用模块的ImageAttributes类来执行此操作。生成的GIF将自动具有灰度调色板。工作少,效果好
请记住,您不必将其用作HttpModule—它主要是一个用于调整大小、修改和编码图像的库
如果你想玩你自己的,以下是我开始的内容:
阅读注释并根据我的注释修补指针算术错误
但是,没有抖动,并且在不完全信任的环境中运行原始版本时可能会遇到问题。多年来我做了很多补丁,但我记不清所有补丁。如果你不介意浏览一堆开源代码,另一种可能是下载Paint.Net。我相信它可以转换成灰度,但我可能是错的,因为我已经有一段时间没有使用它了。这其实一点也不难,一旦你有了工具集,我就建立了很多工具集。您需要的是:
- 16色灰度调色板
- 将图像数据与最近颜色匹配的函数(用于获取调色板数据)
- 将这些匹配项转换为4位数据(每个值半字节)的函数
- 将数据写入新的4位图像对象的方法
((bpp*width)+7)/8
现在,如果生成灰度调色板,然后创建一个字节数组,其中包含图像上每个像素的最近调色板值,则所有值都要输入到实际的8位到4位转换函数
我编写了一个函数,将8位数据转换为任意给定的位长度。因此,对于4位图像,这将需要bitsLength=4
BigEndian参数将确定是否切换一个字节内的值。我不确定这里的.Net图像,但我知道很多1BPP格式使用大端位,而我遇到的4BPP格式是以最小半字节开始的
/// <summary>
/// Converts given raw image data for a paletted 8-bit image to lower amount of bits per pixel.
/// </summary>
/// <param name="data8bit">The eight bit per pixel image data</param>
/// <param name="width">The width of the image</param>
/// <param name="height">The height of the image</param>
/// <param name="newBpp">The new amount of bits per pixel</param>
/// <param name="stride">Stride used in the original image data. Will be adjusted to the new stride value.</param>
/// <param name="bigEndian">Values inside a single byte are read from the largest to the smallest bit.</param>
/// <returns>The image data converted to the requested amount of bits per pixel.</returns>
private static Byte[] ConvertFrom8Bit(Byte[] data8bit, Int32 width, Int32 height, Int32 bitsLength, Boolean bigEndian)
{
if (newBpp > 8)
throw new ArgumentException("Cannot convert to bit format greater than 8!", "newBpp");
if (stride < width)
throw new ArgumentException("Stride is too small for the given width!", "stride");
if (data8bit.Length < stride * height)
throw new ArgumentException("Data given data is too small to contain an 8-bit image of the given dimensions", "data8bit");
Int32 parts = 8 / bitsLength;
// Amount of bytes to write per width
Int32 stride = ((bpp * width) + 7) / 8;
// Bit mask for reducing original data to actual bits maximum.
// Should not be needed if data is correct, but eh.
Int32 bitmask = (1 << bitsLength) - 1;
Byte[] dataXbit = new Byte[stride * height];
// Actual conversion porcess.
for (Int32 y = 0; y < height; y++)
{
for (Int32 x = 0; x < width; x++)
{
// This will hit the same byte multiple times
Int32 indexXbit = y * stride + x / parts;
// This will always get a new index
Int32 index8bit = y * width + x;
// Amount of bits to shift the data to get to the current pixel data
Int32 shift = (x % parts) * bitsLength;
// Reversed for big-endian
if (bigEndian)
shift = 8 - shift - bitsLength;
// Get data, reduce to bit rate, shift it and store it.
dataXbit[indexXbit] |= (Byte)((data8bit[index8bit] & bitmask) << shift);
}
}
return dataXbit;
}
查看codeplex.com和code.msdn.com以获取更多免费示例,包括八叉树和其他算法。下载哪种“开源”软件需要69美元@不退款-是的,MSDN上有一些,但它们甚至比codebetter.com更旧、更轻便。如果你读了这篇文章,那就是他的出发点@标记许多开源软件。就像许多linux发行版一样。它是开源的,作为另一个项目的一部分,它是可再发行的,而不是免费的。我讨厌许可证的麻烦,我想其他开发者也一样。减少到灰度不需要量化,而且一点也不复杂。。。您只需生成一个16色的灰色调色板,并将数据与之匹配。
/// <summary>
/// Creates a bitmap based on data, width, height, stride and pixel format.
/// </summary>
/// <param name="sourceData">Byte array of raw source data.</param>
/// <param name="width">Width of the image.</param>
/// <param name="height">Height of the image.</param>
/// <param name="stride">Scanline length inside the data.</param>
/// <param name="pixelFormat">Pixel format.</param>
/// <param name="palette">Color palette.</param>
/// <param name="defaultColor">Default color to fill in on the palette if the given colors don't fully fill it.</param>
/// <returns>The new image.</returns>
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
{
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
Int32 targetStride = targetData.Stride;
Int64 scan0 = targetData.Scan0.ToInt64();
for (Int32 y = 0; y < height; ++y)
Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
newImage.UnlockBits(targetData);
// For indexed images, set the palette.
if ((pixelFormat & PixelFormat.Indexed) != 0 && (palette != null || defaultColor.HasValue))
{
if (palette == null)
palette = new Color[0];
ColorPalette pal = newImage.Palette;
Int32 palLen = pal.Entries.Length;
Int32 paletteLength = palette.Length;
for (Int32 i = 0; i < palLen; ++i)
{
if (i < paletteLength)
pal.Entries[i] = palette[i];
else if (defaultColor.HasValue)
pal.Entries[i] = defaultColor.Value;
else
break;
}
// Palette property getter creates a copy, so the newly filled in palette
// is not actually referenced in the image until you set it again explicitly.
newImage.Palette = pal;
}
return newImage;
}