最近要转开发平台正研究C#C#好是好不过处理图片时一个像素一个像素的操作像素不是一般的慢其实Delphi也一样但好在Delphi的Bitmap类提供了ScanLines可以一行一行的读图效率比较高C#应该也有类似的东东经过一番搜索终于发现了BitmapData类
先看个例子这是对一张位图的每个像素按FF取补然后输出到一个新图(代码有点啰嗦不过应该可以说明问题了)
int h = m_BmpHeight;
int w = m_BmpWidth;
Bitmap bmpOut = new Bitmap(w h PixelFormatFormatbppRgb);
BitmapData dataIn = m_BmpLockBits(new Rectangle(wh)ImageLockModeReadOnlyPixelFormatFormatbppRgb);
BitmapData dataOut = bmpOutLockBits(new Rectangle( w h) ImageLockModeReadWrite PixelFormatFormatbppRgb);
unsafe
{
byte* pIn = (byte*)(dataInScanToPointer());
byte * pOut = (byte*)(dataOutScanToPointer());
for (int y = ; y < dataInHeight; y++)
{
for (int x = ; x < dataInWidth; x++)
{
pOut[] = (byte)( pIn[]);
pOut[] = (byte)( pIn[]);
pOut[] = (byte)( pIn[]);
pIn += ;
pOut += ;
}
pIn += dataInStride dataInWidth * ;
pOut += dataOutStride dataOutWidth * ;
}
}
bmpOutUnlockBits(dataOut);
m_BmpUnlockBits(dataIn);
貌似比Delphi复杂得多难道我真的天生对指针过敏?还是Delphi的比较好理解就是扫描每一行然后对当前像素点的三个分量做处理非常方便而且C#代码中的Stride是个什么东东?
查找了不少资料现在我是这么理解的假设有一张图片宽度为因为是FormatbppRgb格式(每像素字节在以下的讨论中除非特别说明否则Bitmap都被认为是位RGB)的显然每一行需要*=个字节存储对于Bitmap就是如此但对于BitmapData虽然BitmapDataWidth还是等于BitmapWidth但大概是出于显示性能的考虑每行的实际的字节数将变成大于等于它的那个离它最近的的整倍数此时的实际字节数就是Stride就此例而言不是的整倍数而比大的离最近的的倍数是所以这个BitmapDataStride = 显然当宽度本身就是的倍数时BitmapDataStride = BitmapWidth * 画个图可能更好理解
RGB 分别代表个原色分量字节BGR就表示一个像素为了看起来方便我在每个像素之间插了个空格实际上是没有的X表示补足的倍数而自动插入的字节为了符合人类的阅读习惯我分行了其实在计算机内存中应该看成连续的一大段
Scan
Stride
Width 注Width是图片(BGR作为一个单位)宽度
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
现在应该很好理解了首先用 BitmapDataScan找到第个像素的第个分量的地址这个地址指向的是个byte类型所以当时定义为byte* pIn
行扫描时在当前指针位置(不妨看成当前像素的第个颜色分量)连续取出三个值(个原色分量注意 代表的次序是B G R在取指针指向的值时貌似p[n]和p += n再取p[]是等价的)然后下移个位置(pIn += 看成指到下一个像素的第个颜色分量)做过BitmapWidth次操作后就到达了BitmapWidth * 的位置应该要跳过图中标记为X的字节了(共有Stride Width * 个字节)代码中就是 pIn += dataInStride dataInWidth * ;
跳过以后指针就到达下行的第个像素了按照此算法一共需要做BitmapHeight次行扫描(代码就是 for (int y = ; y < dataInHeight; y++))
另外因为使用了unsafe所以编译的时候需要设置允许不安全的代码