rainxiang

莺莺燕燕翠翠红红处处融融洽洽 风风雨雨花花叶叶年年暮暮朝朝

 

[转]C#中保存GIF文件后透明背景问题的一个解决方法

原文出处
http://blog.csdn.net/ncjmc/archive/2006/08/28/1132879.aspx

以前在用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

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

完整代码如下:

  1public class GifPalette
  2    {
  3        private static ArrayList _cardPalette;
  4        private Color[] _colors;
  5        private Hashtable _colorMap;
  6
  7        public GifPalette(ArrayList palette)
  8        {
  9            _colorMap = new Hashtable();
 10            _colors = new Color[palette.Count];
 11            palette.CopyTo(_colors);
 12        }

 13
 14        public GifPalette()
 15        {
 16            ArrayList palette = SetPalette();
 17            _colorMap = new Hashtable();
 18            _colors = new Color[palette.Count];
 19            palette.CopyTo(_colors);
 20        }

 21
 22        public Bitmap Quantize(Image source)
 23        {
 24            int height = source.Height;
 25            int width = source.Width;
 26
 27            Rectangle bounds = new Rectangle(00, width, height);
 28
 29            Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb);
 30            Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
 31
 32            using (Graphics g = Graphics.FromImage(copy))
 33            {
 34                g.PageUnit = GraphicsUnit.Pixel;
 35
 36                g.DrawImageUnscaled(source, bounds);
 37            }

 38
 39            BitmapData sourceData = null;
 40
 41            try
 42            {
 43                sourceData = copy.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
 44
 45                output.Palette = this.GetPalette(output.Palette);
 46
 47                SecondPass(sourceData, output, width, height, bounds);
 48            }

 49            finally
 50            {
 51                copy.UnlockBits(sourceData);
 52            }

 53
 54            return output;
 55        }

 56
 57        private ColorPalette GetPalette(ColorPalette palette)
 58        {
 59            for (int index = 0; index < _colors.Length; index++)
 60                palette.Entries[index] = _colors[index];
 61            return palette;
 62        }

 63
 64        private unsafe void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds)
 65        {
 66            BitmapData outputData = null;
 67
 68            try
 69            {
 70                outputData = output.LockBits(bounds, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
 71
 72                byte* pSourceRow = (byte*)sourceData.Scan0.ToPointer();
 73                Int32* pSourcePixel = (Int32*)pSourceRow;
 74                Int32* pPreviousPixel = pSourcePixel;
 75
 76                byte* pDestinationRow = (byte*)outputData.Scan0.ToPointer();
 77                byte* pDestinationPixel = pDestinationRow;
 78
 79                byte pixelValue = QuantizePixel((Color32*)pSourcePixel);
 80
 81                *pDestinationPixel = pixelValue;
 82
 83                for (int row = 0; row < height; row++)
 84                {
 85                    pSourcePixel = (Int32*)pSourceRow;
 86
 87                    pDestinationPixel = pDestinationRow;
 88
 89                    for (int col = 0; col < width; col++, pSourcePixel++, pDestinationPixel++)
 90                    {
 91                        if (*pPreviousPixel != *pSourcePixel)
 92                        {
 93                            pixelValue = QuantizePixel((Color32*)pSourcePixel);
 94
 95                            pPreviousPixel = pSourcePixel;
 96                        }

 97
 98                        *pDestinationPixel = pixelValue;
 99                    }

100
101                    pSourceRow += sourceData.Stride;
102
103                    pDestinationRow += outputData.Stride;
104                }

105            }

106            finally
107            {
108                output.UnlockBits(outputData);
109            }

110        }

111
112        private unsafe byte QuantizePixel(Color32* pixel)
113        {
114            byte colorIndex = 0;
115            int colorHash = pixel->ARGB;
116
117            if (_colorMap.ContainsKey(colorHash))
118                colorIndex = (byte)_colorMap[colorHash];
119            else
120            {
121                if (0 == pixel->Alpha)
122                {
123                    for (int index = 0; index < _colors.Length; index++)
124                    {
125                        if (0 == _colors[index].A)
126                        {
127                            colorIndex = (byte)index;
128                            break;
129                        }

130                    }

131                }

132                else
133                {
134                    int leastDistance = int.MaxValue;
135                    int red = pixel->Red;
136                    int green = pixel->Green;
137                    int blue = pixel->Blue;
138
139                    for (int index = 0; index < _colors.Length; index++)
140                    {
141                        Color paletteColor = _colors[index];
142
143                        int redDistance = paletteColor.R - red;
144                        int greenDistance = paletteColor.G - green;
145                        int blueDistance = paletteColor.B - blue;
146
147                        int distance = (redDistance * redDistance) +
148                            (greenDistance * greenDistance) +
149                            (blueDistance * blueDistance);
150
151                        if (distance < leastDistance)
152                        {
153                            colorIndex = (byte)index;
154                            leastDistance = distance;
155
156                            if (0 == distance)
157                                break;
158                        }

159                    }

160                }

161
162                _colorMap.Add(colorHash, colorIndex);
163            }

164
165            return colorIndex;
166        }

167
168        [StructLayout(LayoutKind.Explicit)]
169        public struct Color32
170        {
171            [FieldOffset(0)]
172            public byte Blue;
173
174            [FieldOffset(1)]
175            public byte Green;
176
177            [FieldOffset(2)]
178            public byte Red;
179
180            [FieldOffset(3)]
181            public byte Alpha;
182
183            [FieldOffset(0)]
184            public int ARGB;
185
186            public Color Color
187            {
188                get return Color.FromArgb(Alpha, Red, Green, Blue); }
189            }

190        }

191
192        public static ArrayList SetPalette()
193        {
194            if (null == _cardPalette)
195            {
196                _cardPalette = new ArrayList();
197
198                //Insert the colors into the arraylist
199                Insert the colors into the arraylist
457            }

458            return _cardPalette;
459        }

460
461
462    }

调用方法:

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

        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色调色板,内存开销可能较大,转换时间相对较慢,图像质量也有一定影响。此外,代码中使用了非安全代码(指针),在编译时项目属性中要设置允许不安全代码。


 

 

posted on 2006-12-30 13:05  rainxiang  阅读(1339)  评论(0编辑  收藏  举报

导航