[C#]LockBits使用筆記


昨天想基於一張圖片做個手機鎖屏來着,原圖如下:

主要是嫌白底太丑了,一開始是想畫圖工具直接油漆桶伺候,然而一澆上去就發現問題了,變成了這樣:

看來得手工處理一下把底色統一了,原圖分辨率挺高的,SetPixel顯然會太慢,所以只能LockBits咯。


LockBits的使用方法和參數什么的都可以百度和MSDN,不多說,直接貼一個BitmapWrapper先:

 1 unsafe class BitmapWrapper
 2 {
 3     private readonly Bitmap bmp;
 4     private readonly BitmapData bmpData;
 5 
 6     private readonly byte* scan0;
 7     private readonly int byteCount;
 8 
 9     public BitmapWrapper(Bitmap bitmap)
10     {
11         bmp = bitmap;
12         bmpData = bmp.LockBits(
13             new Rectangle(0, 0, bmp.Width, bmp.Height),
14             ImageLockMode.ReadWrite,
15             bmp.PixelFormat);
16 
17         scan0 = (byte*) bmpData.Scan0;
18         // byteCount = bmpData.Stride / bmpData.Width;
19         byteCount = bmpData.PixelFormat.ToString().IndexOf("32") > 0 ? 4 : 3;
20     }
21     public Bitmap UnWrapper()
22     {
23         bmp.UnlockBits(bmpData);
24         return bmp;
25     }
26     public void SetPixel(Point point, Color color)
27     {
28         int offset = (point.X - 1) * byteCount + (point.Y - 1) * bmpData.Stride;
29         scan0[offset] = color.B;
30         scan0[offset + 1] = color.G;
31         scan0[offset + 2] = color.R;
32         if (byteCount == 4)
33             scan0[offset + 3] = color.A;
34     }
35     public Color GetPixel(Point point)
36     {
37         int offset = (point.X - 1) * byteCount + (point.Y - 1) * bmpData.Stride;
38         Color color = Color.FromArgb(
39             scan0[offset + 2],
40             scan0[offset + 1],
41             scan0[offset]
42         );
43         if (byteCount == 4)
44             color = Color.FromArgb(scan0[offset + 3], color);
45         return color;
46     }
47 }



注意代碼里頭有一句注掉了,那里是我出現第一個問題的地方。。。
本來是想計算每一像素占的字節數,那就拿每行的字節數除每一行的像素數咯,於是就錯了。。。
MSDN查BitmapData.Stride可以看到備注里面的一句話:

跨距是單行像素(一個掃描行)的寬度,舍入為一個 4 字節的邊界。

所以跨距其實應該是等於這樣的:Stride = byteCount * Width + ((byteCount * Width) % 4) == 0 ? 0 : (4 - (byteCount * Width) % 4)
於是不知道該怎么反解byteCount,所以用了19行的那個方法,暫時忽略其他情況吧。。。


第二個問題是發生在存取RGB三個byte值的時候。
因為每個像素的RGB三個值是從高位到低位放置的,所以SetPixel里面應該是這樣:

scan0[offset] = color.B;
scan0[offset + 1] = color.G;
scan0[offset + 2] = color.R;

而不是這樣:

scan0[offset] = color.R;
scan0[offset + 1] = color.G;
scan0[offset + 2] = color.B;



第三個問題發生在保存圖片的時候。。。本來是這么寫的:

bmp.Save("Juven.bmp");

打開圖片再用油漆桶,發現還是和原來差不多,底色里面仍然參雜了高度接近純白的灰色斑點。
因為Save不管你文件擴展名是什么的啊!通通默認Jpeg啊!一壓縮就前功盡棄了!所以應該改成這樣:

bmp.Save(@"Juven.bmp", ImageFormat.Bmp);

這樣就對了,油漆桶后的效果如下(上傳前轉回jpg了,所以這張圖的底色其實還是不純的):



既然都走到這一步了,就干脆走得遠一點,直接代碼做成品了:

 1 Bitmap bmp = new Bitmap(src);
 2 BitmapWrapper wrapper = new BitmapWrapper(bmp);
 3 
 4 byte r, g, b;
 5 for (int y = 1; y <= bmp.Height; y++)
 6 {
 7     for (int x = 1; x <= bmp.Width; x++)
 8     {
 9         Point point = new Point(x, y);
10         Color cr = wrapper.GetPixel(point);
11         if (cr.R + cr.G + cr.B >= 30)
12         {
13             if (x < 200)
14             {
15                 r = 34;
16                 g = 177;
17                 b = 76;
18             }
19             else if (x > 395)
20             {
21                 r = 237;
22                 g = 28;
23                 b = 36;
24             }
25             else
26                 r = g = b = 255;
27             wrapper.SetPixel(point, Color.FromArgb(r, g, b));
28         }
29         else break;
30     }
31 
32     for (int x = bmp.Width; x > 0; x--)
33     {
34         Point point = new Point(x, y);
35         Color cr = wrapper.GetPixel(point);
36         if (cr.R + cr.G + cr.B >= 30)
37         {
38             if (x < 200)
39             {
40                 r = 34;
41                 g = 177;
42                 b = 76;
43             }
44             else if (x > 400)
45             {
46                 r = 237;
47                 g = 28;
48                 b = 36;
49             }
50             else
51                 r = g = b = 255;
52             wrapper.SetPixel(point, Color.FromArgb(r, g, b));
53         }
54         else break;
55     }
56 }
57 wrapper.UnWrapper();
58 bmp.Save(target);

成品圖如下:




最后想說的是,巴薩梅球王求輕虐十個以內啊!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM