本文介绍C# BitmapData,对于C# BitmapData,虽然BitmapData.Width还是等于Bitmap.Width,但大概是出于显示性能的考虑。   最近要转开发平台,正研究C#。C#好是好,不过处理图片时一个像素一个像素的操作像素不是一般的慢。其实Delphi也一样,但好在Delphi的Bitmap类提供了ScanLines,可以一行一行的读图,效率比较高。C#应该也有类似的东东。经过一番搜索,终于发现了C# BitmapData类。  先看个例子,这是对一张位图的每个像素按FF取补,然后输出到一个新图(代码有点啰嗦,不过应该可以说明问题了)。

1.  int h = m_Bmp.Height;   2. int w = m_Bmp.Width;   3.    4.  Bitmap bmpOut = new Bitmap(w, h, PixelFormat.Format24bppRgb);   5.    6. BitmapData dataIn = m_Bmp.LockBits(new  Rectangle(0,0,w,h),  ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);   7. BitmapData dataOut = bmpOut.LockBits(new Rectangle(0, 0,  w, h),   ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);   8.    9. unsafe   10. {    11. byte* pIn = (byte*)(dataIn.Scan0.ToPointer());   12. byte * pOut = (byte*)(dataOut.Scan0.ToPointer());   13.    14. for (int y = 0; y < dataIn.Height; y++)   15. {    16. for (int x = 0; x < dataIn.Width; x++)   17. {   18.    19. pOut[0] = (byte)(255 - pIn[0]);   20. pOut[1] = (byte)(255 - pIn[1]);   21. pOut[2] = (byte)(255 - pIn[2]);   22.    23. pIn += 3;   24. pOut += 3;   25. }   26.    27. pIn += dataIn.Stride - dataIn.Width * 3;   28. pOut += dataOut.Stride - dataOut.Width * 3;   29. }   30. }   31.

32. bmpOut.UnlockBits(dataOut);   33. m_Bmp.UnlockBits(dataIn);

貌似比Delphi复杂得多,难道我真的天生对指针过敏?还是Delphi的比较好理解,就是扫描每一行,然后对当前像素点的三个分量做处理,非常方便。而且C#代码中的Stride是个什么东东?  查找了不少资料,现在我是这么理解的:假设有一张图片宽度为6,因为是Format24bppRgb格式(每像素3字节。在以下的讨论中,除非特别说明,否则Bitmap都被认为是24位RGB)的,显然,每一行需要6*3=18个字节存储。对于Bitmap就是如此。但对于C# 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的倍数而自动插入的字节。为了符合人类的阅读习惯我分行了,其实在计算机内存中应该看成连续的一大段。  .----------------------------%4==0---------------------------. .-------------------------------Stride-------------------------. .--------------------------3*With-----------------------. [R][G][B][R][G][B][R][G][B][R][G][B][R][G][B]......[X][X] [R][G][B][R][G][B][R][G][B][R][G][B][R][G][B]......[X][X] [R][G][B][R][G][B][R][G][B][R][G][B][R][G][B]......[X][X] [R][G][B][R][G][B][R][G][B][R][G][B][R][G][B]......[X][X] ... .... .... ... .... ....  [R][G][B][R][G][B][R][G][B][R][G][B][R][G][B]......[X][X]  现在应该很好理解了。  首先用 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,所以编译的时候需要设置“允许不安全的代码”。以上介绍C# BitmapData

最新文章

  1. simpleCart的简单使用及配置
  2. OnClientClick的用法
  3. PHPMailer邮件类使用错误分析
  4. [C/CPP系列知识] 在C中使用没有声明的函数时将发生什么 What happens when a function is called before its declaration in C
  5. .NET 认识
  6. linux 输入子系统(3)----事件处理(input_handler层)
  7. 【Android】Intent中使用Extra传递数据
  8. Unity3D-第一视角射击游戏
  9. JavaEE(19) - Web层和EJB的整合(Session Bean)
  10. 浏览器扩展系列————异步可插入协议(pluggable protocol)的实现
  11. DAX/PowerBI系列 - 写在前面
  12. 网站waf检测
  13. Centos安装jdk8
  14. C# Dispose模式详细分析
  15. python——几种截图对比方式!
  16. GO语言的进阶之路-go的程序结构以及包简介
  17. strcpy和memcpy的差别
  18. Android Studio快速集成讯飞SDK实现文字朗读功能
  19. RestTemplate 发送 get 请求使用误区 多值为null
  20. centos配置ruby开发环境(转 )

热门文章

  1. Windows server 2016 解决“无法完成域加入,原因是试图加入的域的SID与本计算机的SID相同。”
  2. ArcGIS Engine中的重点类库介绍
  3. android 二次按返回键退出client
  4. SQLite详解,案例,手册
  5. c++位运算符介绍
  6. Spark scheduler
  7. SQL查询结果排序
  8. windows安装memcached
  9. enable&amp;&amp;builtin---shell内部命令
  10. windows查看硬件信息系统信息