C# 画4位或8位图(将图片压缩成4位或8位)

C# 画4位或8位图(将图片压缩成4位或8位)

原帖

http://www.cnblogs.com/wdh1983/archive/2009/10/15/1584048.html

 

画4位图同画8位图的思路有些类似。难点是C#里没有4位的数据类型。所以要想办法用已有的数据类型(比如byte)去操作4位的数据类型。
对于用C#画八位图的方法我上一篇文章有说过,这里就不再多说了(地址: http://www.cnblogs.com/wdh1983/archive/2009/01/20/1378468.html   文章内容虽没什么技术含量,但请转载时给个原址链接)。
废话就不说了,直接上代码
  1 using  System;
  2 using  System.Collections.Generic;
  3 using  System.Drawing; // 此次需要引入System.Drawing.dll
  4 using  System.Drawing.Imaging;
  5
  6 namespace  ConsoleApplication_Draw
  7 {
  8    class Program
  9    {
 10        static void Main(string[] args)
 11        {
 12            Bitmap old_Image = new Bitmap(@"C:/Documents and Settings/weidonghai/桌面/test.bmp");
 13            Bitmap new_Image = pictureZip(old_Image);
 14            new_Image.Save(@"C:/Documents and Settings/weidonghai/桌面/test_1.bmp");
 15        }

 16
 17        /// <summary>
 18        /// 图片压缩,压缩后的位数由传入参数中的调色板颜色个数决定,如果颜色数大于256种,将返回原图
 19        /// </summary>
 20        /// <param name="image">将要压缩的元素图片</param>
 21        /// <returns>经压缩函数处理后的图片</returns>

 22        private static Bitmap pictureZip(Bitmap image_old)
 23        {
 24            int Width = image_old.Width;
 25            int Height = image_old.Height;
 26
 27            // 1:首先分析原图颜色数,并创建颜色列表,以便后面创建调色板
 28            List<Color> listColor = new List<Color>();
 29            for (int y = 0; y < Height; y++)
 30            {
 31                for (int x = 0; x < Width; x++)
 32                {
 33                    Color c = image_old.GetPixel(x, y);
 34                    if (!listColor.Contains(c))
 35                    {
 36                        listColor.Add(c);
 37                    }

 38                }

 39            }

 40
 41            // 2:当颜色数大于256时,就没法压缩了,所以返回原图
 42            //    由于8位图最多只能包含256种颜色,当颜色数在( 16,256 ] 这个范围内,可压缩成8位图
 43            //    4位图道理同上
 44            Bitmap bitmap_new;
 45            if (listColor.Count > 256)
 46                return image_old;
 47            else if (listColor.Count > 16)
 48                bitmap_new = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
 49            else
 50                bitmap_new = new Bitmap(Width, Height, PixelFormat.Format4bppIndexed);
 51
 52            // 3:根据颜色列表创建压缩图片的调色板
 53            ColorPalette ImagePal = bitmap_new.Palette;
 54            for (int i = 0; i < listColor.Count; i++)
 55            {
 56                ImagePal.Entries[i] = listColor[i];
 57            }

 58            bitmap_new.Palette = ImagePal;
 59
 60            // 4:因为原图可能不是32位的,这里要将原图变为32位的,以方便后面的压缩
 61            Bitmap bitmap_oldCopy = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
 62            {
 63                Graphics g = Graphics.FromImage(bitmap_oldCopy);
 64                g.PageUnit = GraphicsUnit.Pixel;
 65                g.DrawImage(image_old, 00, Width, Height);
 66                g.Dispose();
 67            }

 68            
 69            // 5:将图片的数据锁定到系统内存中,用指针操作内存中的这些颜色数据快,实现压缩
 70            Rectangle rect = new Rectangle(00, Width, Height);
 71            BitmapData bitmapData_new;
 72            if (ImagePal.Entries.Length > 16)
 73                bitmapData_new = bitmap_new.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
 74            else
 75                bitmapData_new = bitmap_new.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format4bppIndexed);
 76            BitmapData bitmapData_old = bitmap_oldCopy.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
 77
 78            IntPtr pixels = bitmapData_new.Scan0;
 79            unsafe
 80            {
 81                byte* pBits;
 82                if (bitmapData_new.Stride > 0)
 83                    pBits = (byte*)pixels.ToPointer();
 84                else
 85                    pBits = (byte*)pixels.ToPointer() + bitmapData_new.Stride * (Height - 1);
 86                uint stride = (uint)Math.Abs(bitmapData_new.Stride);
 87
 88                byte* p = (byte*)bitmapData_old.Scan0;
 89                int offset = bitmapData_old.Stride - Width * 4;
 90
 91                // 6:把内存中的数据看成是一个二维数组,遍历,一个点一个点的压缩
 92                if (ImagePal.Entries.Length > 16)
 93                {
 94                    for (uint row = 0; row < Height; ++row)
 95                    {
 96                        for (uint col = 0; col < Width; ++col)
 97                        {
 98                            Color pixel = Color.FromArgb(p[3], p[2], p[1], p[0]);
 99                            //获取新图对应点的指针
100                            byte* p8bppPixel = pBits + row * stride + col;
101                            //将原图这个点的颜色所对应的颜色索引值赋给指针
102                            *p8bppPixel = (byte)listColor.IndexOf(pixel);
103                            p += 4;
104                        }

105                        p += offset;
106                    }

107
108                }

109                else
110                {
111                    //四位图与八位图的不同之处是,要用一个byte同时操作两个四位数据
112                    int _width = 0;
113                    if (Width % 2 == 0) _width = Width / 2;
114                    else _width = Width / 2 + 1;
115                    for (uint row = 0; row < Height; row++)
116                    {
117                        for (uint col = 0; col < _width; col++)
118                        {
119                            Color pixel_1 = Color.FromArgb(p[3], p[2], p[1], p[0]);
120                            Color pixel_2 = Color.FromArgb(p[7], p[6], p[5], p[4]);
121                            byte index1 = (byte)listColor.IndexOf(pixel_1);
122                            byte index2 = (byte)listColor.IndexOf(pixel_2);
123
124                            byte* p8bppPixel = pBits + row * stride + col;
125                            if (col != _width - 1)
126                            {   //非每行最后一个元素
127                                *p8bppPixel = (byte)(index1 * 16 + index2);
128                                p += 8;
129                            }

130                            else
131                            {   //每行最后一个元素
132                                if (Width % 2 == 0)
133                                {   //当图片宽度为偶数时
134                                    *p8bppPixel = (byte)(index1 * 16 + index2);
135                                    p += 8;
136                                }

137                                else
138                                {   //当图片宽度为奇数时,这里是最特殊的地方
139                                    *p8bppPixel = (byte)(*p8bppPixel % 16 + index1 * 16);
140                                    p += 4;
141                                }

142                            }

143                        }

144                        p += offset;
145                    }

146                }

147                bitmap_oldCopy.UnlockBits(bitmapData_old);
148            }

149            bitmap_oldCopy.Dispose();
150            bitmap_new.UnlockBits(bitmapData_new);
151            return bitmap_new;
152        }

153    }

154}

实际上,上面的压缩方法性能是比较低的,以前那个文章中有说过一个改进方法。但效果一般。
本文原创地址: http://www.cnblogs.com/wdh1983/archive/2009/10/15/1584048.html
如果你要压缩的图片是你自己用程序画的,比如你要画一个行情的折线图,或行情的K图,那么这里还有一个更好的压缩方法。
就是你在画图前肯定是知道自己会用到多少种颜色的,那么你先根据这些颜色建好调色板。另外,在你画图的时候,不要用原始的颜色之间话,比如红色,不要用Color.Red,用Color.FromArgb(X, X, X, index),其中X为任意(0,255),index为红色在你建好的那个调色板中的索引值。
压缩代码如下:
 1          private   static  Bitmap pictureZipPlus(Bitmap image_old, ColorPalette ImagePal)
 2          {
 3            int Width = image_old.Width;
 4            int Height = image_old.Height;
 5
 6            Bitmap bitmap_new;
 7            if (ImagePal.Entries.Length > 16)
 8                bitmap_new = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
 9            else
10                bitmap_new = new Bitmap(Width, Height, PixelFormat.Format4bppIndexed);
11            bitmap_new.Palette = ImagePal;
12
13            Bitmap bitmap_oldCopy = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
14            {
15                Graphics g = Graphics.FromImage(bitmap_oldCopy);
16                g.PageUnit = GraphicsUnit.Pixel;
17                g.DrawImage(image_old, 00, Width, Height);
18                g.Dispose();
19            }

20
21            Rectangle rect = new Rectangle(00, Width, Height);
22            BitmapData bitmapData_new;
23            if (ImagePal.Entries.Length > 16)
24                bitmapData_new = bitmap_new.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
25            else
26                bitmapData_new = bitmap_new.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format4bppIndexed);
27            IntPtr pixels = bitmapData_new.Scan0;
28            BitmapData bitmapData_old = bitmap_oldCopy.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
29
30            unsafe
31            {
32                byte* pBits;
33                if (bitmapData_new.Stride > 0)
34                    pBits = (byte*)pixels.ToPointer();
35                else
36                    pBits = (byte*)pixels.ToPointer() + bitmapData_new.Stride * (Height - 1);
37                uint stride = (uint)Math.Abs(bitmapData_new.Stride);
38
39                byte* p = (byte*)bitmapData_old.Scan0;
40                int offset = bitmapData_old.Stride - Width * 4;
41
42                if (ImagePal.Entries.Length > 16)
43                {
44                    for (uint row = 0; row < Height; ++row)
45                    {
46                        for (uint col = 0; col < Width; ++col)
47                        {
48                            byte* p8bppPixel = pBits + row * stride + col;
49                            *p8bppPixel = p[0];
50                            p += 4;
51                        }

52                        p += offset;
53                    }

54
55                }

56                else
57                {
58                    int _width = 0;
59                    if (Width % 2 == 0) _width = Width / 2;
60                    else _width = Width / 2 + 1;
61                    for (uint row = 0; row < Height; row++)
62                    {
63                        for (uint col = 0; col < _width; col++)
64                        {
65                            byte* p8bppPixel = pBits + row * stride + col;
66                            if (col != _width - 1)
67                            {
68                                *p8bppPixel = (byte)(p[0* 16 + p[4]);
69                                p += 8;
70                            }

71                            else
72                            {
73                                if (Width % 2 == 0)
74                                {
75                                    *p8bppPixel = (byte)(p[0* 16 + p[4]);
76                                    p += 8;
77                                }

78                                else
79                                {
80                                    *p8bppPixel = (byte)(*p8bppPixel % 16 + p[0* 16);
81                                    p += 4;
82                                }

83                            }

84                        }

85                        p += offset;
86                    }

87                }

88                bitmap_oldCopy.UnlockBits(bitmapData_old);
89            }

90            bitmap_oldCopy.Dispose();
91            bitmap_new.UnlockBits(bitmapData_new);
92            return bitmap_new;
93        }
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值