zoukankan      html  css  js  c++  java
  • 使用C#的BitmapData

    【文章摘要】最近要转开发平台,正研究C#。C#好是好,不过处理图片时一个像素一个像素的操作像素不是一般的慢。最近要转开发平台,正研究C#。C#好是好,不过处理图片时一个像素一个像素的操作像素不是一般的慢。其实Delphi也一样,但好在Delphi的Bitmap类提供了ScanLines,可以一行一行的读图,效率比较高。C#应该也有类似的东东。经过一番搜索,终于发现了BitmapData类。

    先看个例子,这是对一张位图的每个像素按FF取补,然后输出到一个新图(代码有点啰嗦,不过应该可以说明问题了)。

    int h = m_Bmp.Height; 
    int w = m_Bmp.Width; 
    
    Bitmap bmpOut = new Bitmap(w, h, PixelFormat.Format24bppRgb); 
    
    BitmapData dataIn = m_Bmp.LockBits(new Rectangle(0,0,w,h),ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb); 
    BitmapData dataOut = bmpOut.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
    
    unsafe 
    { 
        byte* pIn = (byte*)(dataIn.Scan0.ToPointer()); 
        byte * pOut = (byte*)(dataOut.Scan0.ToPointer()); 
    
        for (int y = 0; y < dataIn.Height; y++) 
        { 
            for (int x = 0; x < dataIn.Width; x++) 
            { 
    
                pOut[0] = (byte)(255 - pIn[0]); 
                pOut[1] = (byte)(255 - pIn[1]); 
                pOut[2] = (byte)(255 - pIn[2]); 
    
                pIn += 3; 
                pOut += 3; 
            } 
    
            pIn += dataIn.Stride - dataIn.Width * 3; 
            pOut += dataOut.Stride - dataOut.Width * 3; 
        } 
    } 
    
    bmpOut.UnlockBits(dataOut); 
    m_Bmp.UnlockBits(dataIn); 

    貌似比Delphi复杂得多,难道我真的天生对指针过敏?还是Delphi的比较好理解,就是扫描每一行,然后对当前像素点的三个分量做处理,非常方便。而且C#代码中的Stride是个什么东东?

    查找了不少资料,现在我是这么理解的:

    假设有一张图片宽度为6,因为是Format24bppRgb格式(每像素3字节。在以下的讨论中,除非特别说明,否则Bitmap都被认为是24位RGB)的,显然,每一行需要6*3=18个字节存储。对于Bitmap就是如此。但对于BitmapData,虽然BitmapData.Width还是等于Bitmap.Width,但大概是出于显示性能的考虑,每行的实际的字节数将变成大于等于它的那个离它最近的4的整倍数,此时的实际字节数就是Stride。就此例而言,18不是4的整倍数,而比18大的离18最近的4的倍数是20,所以这个BitmapData.Stride = 20。显然,当宽度本身就是4的倍数时,BitmapData.Stride = Bitmap.Width * 3。

    画个图可能更好理解。R、G、B 分别代表3个原色分量字节,BGR就表示一个像素。为了看起来方便我在每个像素之间插了个空格,实际上是没有的。X表示补足4的倍数而自动插入的字节。为了符合人类的阅读习惯我分行了,其实在计算机内存中应该看成连续的一大段。

    Scan0    

    --------------Stride----------------   

    --------------Width------------                     注:Width是图片(BGR作为一个单位)宽度

    BGR BGR BGR BGR BGR BGR XX

    BGR BGR BGR BGR BGR BGR XX

    .

    .

    .

    现在应该很好理解了。首先用 BitmapData.Scan0找到第0个像素的第0个分量的地址。这个地址指向的是个byte类型,所以当时定义为byte* pIn。 行扫描时,在当前指针位置(不妨看成当前像素的第0个颜色分量)连续取出三个值(3个原色分量。注意,0 1 2代表的次序是B G R。在取指针指向的值时,貌似p[n]和p += n再取p[0]是等价的),然后下移3个位置(pIn += 3,看成指到下一个像素的第0个颜色分量)。做过Bitmap.Width次操作后,就到达了Bitmap.Width * 3的位置,应该要跳过图中标记为X的字节了(共有Stride - Width * 3个字节),代码中就是 pIn += dataIn.Stride - dataIn.Width * 3;

    跳过以后指针就到达下行的第0个像素了。按照此算法,一共需要做Bitmap.Height次行扫描(代码就是 for (int y = 0; y < dataIn.Height; y++))。

    另外,因为使用了unsafe,所以编译的时候需要设置“允许不安全的代码”。

    让图像旁变成红色:

    // Create a new bitmap.
    Bitmap bmp = (Bitmap)this.pictureBox1.Image;
    
    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
    
    // Get the address of the first line.
    IntPtr ptr = bmpData.Scan0;
    
    // Declare an array to hold the bytes of the bitmap. 
    int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
    byte[] rgbValues = new byte[bytes];
    
    // Copy the RGB values into the array.
    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    
    // Set every third value to 255. A 24bpp bitmap will look red.   
    for (int counter = 2; counter < rgbValues.Length; counter += 3)
        rgbValues[counter] = 255;
    
    // Copy the RGB values back to the bitmap
    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
    
    // Unlock the bits.
    bmp.UnlockBits(bmpData);
    this.pictureBox1.Image = bmp;
  • 相关阅读:
    SERV-U处于“域正离线”怎么办?
    在wampserver3.0.6中配置虚拟主机(设置二级域名)
    解决Win7系统新建选项中无记事本问题
    解决Windows Server 2008 R2安装WAMPSERVER3.0.6问题总结
    php实现定时任务的思路
    https配置for apache
    jquery传值
    有趣的em
    自我感觉良好的配搭
    正则基础整理
  • 原文地址:https://www.cnblogs.com/AndyGe/p/3011642.html
Copyright © 2011-2022 走看看