打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C#中保存GIF文件后透明背景问题的一个解决方法

  以前在用C#做网站保存缩略图的程序中发现,当保存为GIF文件类型时,原来的透明背景变成了黑色,当时由于赶时间,就统一用白色代替了背景,并用Jpeg格式存储,并没有深究。

  近来在网上查阅了许多资料,看到了两种解决方法:一种是在显示时设置透明背景色,GIF文件本身并不改变,另一种是不推荐使用的调用API的方法。将后一种I的VB源码用C#重写后,发现其中的调色板设置太少,转换效果不理想。

  重新到网上搜索关于调色板的资料,发现MSDN上的一篇《对 ASP.NET 图像的颜色量化(Quantization)进行优化》的文章。

http://www.microsoft.com/china/MSDN/library/archives/library/DNAspp/html/colorquant.asp

  将其中基于调色板量化的代码分离出来,透明背景色的图片保存成功。

  完整代码如下:

using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace WindwoodGif
{
public class GifPalette
{
private static ArrayList _cardPalette;
private Color[] _colors;
private Hashtable _colorMap;

public GifPalette(ArrayList palette)
{
_colorMap
= new Hashtable();
_colors
= new Color[palette.Count];
palette.CopyTo(_colors);
}


public GifPalette()
{
ArrayList palette
= SetPalette();
_colorMap
= new Hashtable();
_colors
= new Color[palette.Count];
palette.CopyTo(_colors);
}


public Bitmap Quantize(Image source)
{
int height = source.Height;
int width = source.Width;

Rectangle bounds
= new Rectangle(0, 0, width, height);

Bitmap copy
= new Bitmap(width, height, PixelFormat.Format32bppArgb);
Bitmap output
= new Bitmap(width, height, PixelFormat.Format8bppIndexed);

using (Graphics g = Graphics.FromImage(copy))
{
g.PageUnit
= GraphicsUnit.Pixel;

g.DrawImageUnscaled(source, bounds);
}


BitmapData sourceData
= null;

try
{
sourceData
= copy.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

output.Palette
= this.GetPalette(output.Palette);

SecondPass(sourceData, output, width, height, bounds);
}

finally
{
copy.UnlockBits(sourceData);
}


return output;
}


private ColorPalette GetPalette(ColorPalette palette)
{
for (int index = 0; index < _colors.Length; index++)
palette.Entries[index]
= _colors[index];
return palette;
}


private unsafe void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds)
{
BitmapData outputData
= null;

try
{
outputData
= output.LockBits(bounds, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

byte* pSourceRow = (byte*)sourceData.Scan0.ToPointer();
Int32
* pSourcePixel = (Int32*)pSourceRow;
Int32
* pPreviousPixel = pSourcePixel;

byte* pDestinationRow = (byte*)outputData.Scan0.ToPointer();
byte* pDestinationPixel = pDestinationRow;

byte pixelValue = QuantizePixel((Color32*)pSourcePixel);

*pDestinationPixel = pixelValue;

for (int row = 0; row < height; row++)
{
pSourcePixel
= (Int32*)pSourceRow;

pDestinationPixel
= pDestinationRow;

for (int col = 0; col < width; col++, pSourcePixel++, pDestinationPixel++)
{
if (*pPreviousPixel != *pSourcePixel)
{
pixelValue
= QuantizePixel((Color32*)pSourcePixel);

pPreviousPixel
= pSourcePixel;
}


*pDestinationPixel = pixelValue;
}


pSourceRow
+= sourceData.Stride;

pDestinationRow
+= outputData.Stride;
}

}

finally
{
output.UnlockBits(outputData);
}

}


private unsafe byte QuantizePixel(Color32* pixel)
{
byte colorIndex = 0;
int colorHash = pixel->ARGB;

if (_colorMap.ContainsKey(colorHash))
colorIndex
= (byte)_colorMap[colorHash];
else
{
if (0 == pixel->Alpha)
{
for (int index = 0; index < _colors.Length; index++)
{
if (0 == _colors[index].A)
{
colorIndex
= (byte)index;
break;
}

}

}

else
{
int leastDistance = int.MaxValue;
int red = pixel->Red;
int green = pixel->Green;
int blue = pixel->Blue;

for (int index = 0; index < _colors.Length; index++)
{
Color paletteColor
= _colors[index];

int redDistance = paletteColor.R - red;
int greenDistance = paletteColor.G - green;
int blueDistance = paletteColor.B - blue;

int distance = (redDistance * redDistance) +
(greenDistance
* greenDistance) +
(blueDistance
* blueDistance);

if (distance < leastDistance)
{
colorIndex
= (byte)index;
leastDistance
= distance;

if (0 == distance)
break;
}

}

}


_colorMap.Add(colorHash, colorIndex);
}


return colorIndex;
}


[StructLayout(LayoutKind.Explicit)]
public struct Color32
{
[FieldOffset(
0)]
public byte Blue;

[FieldOffset(
1)]
public byte Green;

[FieldOffset(
2)]
public byte Red;

[FieldOffset(
3)]
public byte Alpha;

[FieldOffset(
0)]
public int ARGB;

public Color Color
{
get { return Color.FromArgb(Alpha, Red, Green, Blue); }
}

}


public static ArrayList SetPalette()
{
if (null == _cardPalette)
{
_cardPalette
= new ArrayList();

Insert the colors into the arraylist
}

return _cardPalette;
}


}

}

  调用方法:

  先将该类添加到项目中,再在合适的地方调用。例:

Bitmap bitmap = new System.Drawing.Bitmap(width, height); // Image类也可
// ......(图形操作代码)
WindwoodGif.GifPalette gifPalette = new WindwoodGif.GifPalette();
bitmap = gifPalette.Quantize(bitmap);
bitmap.Save(SaveFileName, ImageFormat.Gif);

  经测试,这种方法能够实现GIF文件的透明背景存储,在WinForm、WebForm均能使用。由于使用了标准256色调色板,内存开销可能较大,转换时间相对较慢,图像质量也有一定影响。此外,代码中使用了非安全代码(指针),在编译时项目属性中要设置允许不安全代码。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
降低颜色深度及调色板处理
bitmap的图像像素遍历方法
Taking a look at SWT Images
.NET Core 生成二维码
【C#】加快Bitmap的访问速度
c#的图片合成
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服