zoukankan      html  css  js  c++  java
  • [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);

    成品图如下:




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

    要是觉得本文还算有点意思就在右下角点个推荐呗~
  • 相关阅读:
    单片机、嵌入式ARM学习网站推荐(多年的积累)
    单片机心得
    printf函数解析
    C语言数组与指针详解
    C语言数组与指针详解
    单片机心得
    单片机、嵌入式ARM学习网站推荐(多年的积累)
    嵌入式开发资料集锦
    poj1941
    poj1723
  • 原文地址:https://www.cnblogs.com/vd630/p/4553985.html
Copyright © 2011-2022 走看看