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, 0, 0, Width, Height);
66 g.Dispose();
67 }
68
69 // 5:将图片的数据锁定到系统内存中,用指针操作内存中的这些颜色数据快,实现压缩
70 Rectangle rect = new Rectangle(0, 0, 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}
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, 0, 0, Width, Height);
66 g.Dispose();
67 }
68
69 // 5:将图片的数据锁定到系统内存中,用指针操作内存中的这些颜色数据快,实现压缩
70 Rectangle rect = new Rectangle(0, 0, 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, 0, 0, Width, Height);
18 g.Dispose();
19 }
20
21 Rectangle rect = new Rectangle(0, 0, 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 }
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, 0, 0, Width, Height);
18 g.Dispose();
19 }
20
21 Rectangle rect = new Rectangle(0, 0, 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 }