本文介绍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

本文介绍C# BitmapData的更多相关文章

  1. .Net Core 之 图形验证码 本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能。

    本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能. 通过测试的系统: Windows 8.1 64bit Ubuntu Server 16.04 LTS 64 ...

  2. 微软在 .NET 3.5 新增了一个 HashSet 类,在 .NET 4 新增了一个 SortedSet 类,本文介绍他们的特性,并比较他们的异同。

    微软在 .NET 3.5 新增了一个 HashSet 类,在 .NET 4 新增了一个 SortedSet 类,本文介绍他们的特性,并比较他们的异同. .NET Collection 函数库的 Has ...

  3. Routing 为 Magento 2 一个重要的部分,本文介绍基本应用

    Routing 为 Magento 2 一个重要的部分,本文介绍基本应用 Magento 2请求的流程 在Magento 2中,请求URL将如下所示: http://example.com/index ...

  4. Commit message 的写法规范。本文介绍Angular 规范(

    Commit message 的写法规范.本文介绍Angular 规范( http://www.ruanyifeng.com/blog/2016/01/commit_message_change_lo ...

  5. 本文介绍如何使用 Docker Swarm 来部署 Nebula Graph 集群,并部署客户端负载均衡和高可用

    本文作者系:视野金服工程师 | 吴海胜 首发于 Nebula Graph 论坛:https://discuss.nebula-graph.com.cn/t/topic/1388 一.前言 本文介绍如何 ...

  6. 本文介绍使用windows系统自带的远程桌面mstsc连接Centos 7.x远程桌面的基本方法。

    本文介绍使用windows系统自带的远程桌面mstsc连接Centos 7.x远程桌面的基本方法. 一.前言 我希望用windows远程访问centos图形界面.xmanager连接centos远程桌 ...

  7. 【linux命令】:查看当前登录用户的信息,本文介绍3种方法

    作为系统管理员,你可能经常会(在某个时候)需要查看系统中有哪些用户正在活动.有些时候,你甚至需要知道他(她)们正在做什么.本文为我们总结了4种查看系统用户信息(通过编号(ID))的方法. 1. 使用w ...

  8. 没有80端口的备案域名,如何做微信公众平台的开发?本文介绍可以通过任何域名来做开发,www.baidu.com和www.163.com和www.so.com这样的域名都可以

    1.首先做过微信开发的朋友都知道,微信后台需要绑定80端口的备案域名,如果此时手上没有80端口的备案域名就不能进行开发了吗?当然不是 首先在这些地方绑定一个备案域名,国内公司的网址基本上是有备案的如w ...

  9. 部署NetCore项目(本文介绍用用IIS部署)

    首先要下载安装Core Runtim: 然后在IIS中的模块中就可以看到如下内容: 这时环境就搭好了: 下面按照正常的发布流程走就行了,关键看下面(一定要选择无托管代码)

随机推荐

  1. Assembly.Load 详解(c#)

    我们在使用C# 语言的Assembly.Load 来加载托管程序集并使用反射功能时,一般需要先通过Assembly.Load(), Assembly.LoadFrom() 等方法将目标托管程序集加载到 ...

  2. IDEA 热启动,每次更改代码后不用重启服务

    1.ctrl+Shift+Alt+/,选择Registry 2.勾选 compiler.automake.allow.when.app.running(可能不按首字母排序,可以多找找) 3.Setti ...

  3. CentOS-6.4-minimal版中安装JDK_Maven_Subversion以及改动rpm包安装路径

    完整版见https://jadyer.github.io/2013/09/07/centos-config-develop/ /** * @see -------------------------- ...

  4. 【安卓】数据库基于脚本的&quot;增量更新&quot;,每次更新时不需改动java代码、!

    思路: 1.当然是基于SQLiteOpenHelper.onCreate(第一次安装程序时调用).onUpdate(升级程序时调用) 2.用"脚本"(脚本制作详细方法问度娘)做数据 ...

  5. vim 计算器寄存器使用

    我们可能会在vim的使用中,碰到下面的情况 当我正在写一周预算的时候,我想计算下每天我买菜花2.7,每天买两顿,周死晚上出去吃,周六额外买1.5斤14.8一斤的猪肉... 这时候你打算怎么办呢,是不是 ...

  6. Android 学习笔记进阶14之像素操作

    在我们玩的游戏中我们会经常见到一些图像的特效,比如半透明等效果.要实现这种半透明效果其实并不难,需要我们懂得图像像素的操作. 不要怕,其实在Android中Bitmap为我们提供了操作像素的基本方法. ...

  7. 2.CURL命令

    转自:https://blog.csdn.net/ligang2585116/article/details/46548617 curl是一种命令行工具,作用是发出网络请求,然后得到和提取数据,显示在 ...

  8. Cloudera Hue是什么?

     Hue是cdh专门的一套web管理器,它包括3个部分hue ui,hue server,hue db.hue提供所有的cdh组件的shell界面的接口.你可以在hue编写mr,查看修改hdfs的文件 ...

  9. 图片工具GraphicsMagick的安装配置与基本使用

    本文使用GraphicsMagick的版本为1.3.18 (Released March 9, 2013). 1.简介 GraphicsMagick是一个短小精悍的的图片处理工具和库集合.对于Java ...

  10. 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一

    Grabage Collection      GC GC要完毕的三件事情: 哪些内存须要回收? 什么时候回收? 怎样回收? 内存运行时区域的各个部分中: 程序计数器.虚拟机栈.本地方法栈这3个区域随 ...