使用C#的BitmapData

转载 2012年03月29日 14:08:59
我在前两篇图片处理的文章里几乎都用BitmapData来做图片处理的,那么这个东东究竟是个什么玩意儿呢?
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---------|  |
BGR BGR BGR BGR BGR BGR XX
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,所以编译的时候需要设置“允许不安全的代码”。


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1478611

相关文章推荐

使用C#的BitmapData

  • 2014年10月19日 12:37
  • 29KB
  • 下载

编程实例 使用C#的BitmapData

本文介绍C# BitmapData,对于C# BitmapData,虽然BitmapData.Width还是等于Bitmap.Width,但大概是出于显示性能的考虑。   最近要转开发平台,正...

BitmapData.draw方法

  • 2008年11月20日 18:00
  • 36KB
  • 下载

Bitmap序列化(将BitmapData保存为原生Binary/ByteArray),AS3保存图像的方法

当应用程序需要将位图图像保存到本地或发送到服务端时, 通常的方法是在发送数据前将图像通过PNG或JPEG编码。如果只是想保存位图图像,只要序列化BitmapData即可,将图像转换为JPEG/PNG是...

rtmp 视频流在截图时出现沙箱问题 “Security sandbox violation: BitmapData.draw”

原文:http://www.shell-tips.com/2009/08/30/flash-how-to-fix-the-security-sandbox-violation-bitmapdata-d...
  • kvgnt
  • kvgnt
  • 2011年08月20日 15:21
  • 1829

Flex中利用ByteArray与BitmapData互相转换实现图片的二进制保存与复原

本文为转载,原文地址:http://hjy2099.javaeye.com/blog/260329近日在项目当中需要将图片保存到共享对象当中,开始用了俩天的时间做了对象的序列化,并以BitmapDat...

BitmapData类

使用C#进行图像处理的几种方法本文讨论了C#图像处理中Bitmap类、BitmapData类和unsafe代码的使用以及字节对齐问题。 Bitmap类命名空间:System.Drawing封装 GDI...

Flex 利用bitmap和bitmapData改变图片为黑白画

Flex中ByteArray与BitmapData互相转换实现图片的二进制保存与复原

package com.kyit.lg..util { import flash.display.Bitmap; import flash.display.BitmapData; impo...
  • GIS__
  • GIS__
  • 2011年08月15日 16:54
  • 7713

细说BitmapData

如果你是刚接触as编程或者现在只接触flex开发,或许你会对BitmapData不太了解,也或许你会对它不屑一顾。 但作为一个资深的as开发者,无论是游戏开发还是web应用。 BitmapDat...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用C#的BitmapData
举报原因:
原因补充:

(最多只允许输入30个字)