[C#]LockBits使用笔记

昨天想基于一张图片做个手机锁屏来着,原图如下:
[C#]LockBits使用笔记
主要是嫌白底太丑了,一开始是想画图工具直接油漆桶伺候,然而一浇上去就发现问题了,变成了这样:
[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了,所以这张图的底色其实还是不纯的):
[C#]LockBits使用笔记


既然都走到这一步了,就干脆走得远一点,直接代码做成品了:

 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);

成品图如下:
[C#]LockBits使用笔记



最后想说的是,巴萨梅球王求轻虐十个以内啊!

你可能感兴趣的:(Lock)