C语言结构体描述BMP的文件格式

BMP文件的结构其实非常简单,就是两个结构体+一个可选的调色板+位图数据。

第一个结构体是BITMAPFILEHEADER,第二个结构体是BITMAPINFOHEADER。然后就是可选的调色板(RGBQUAD数组)。最后是位图数据。

第一个结构体

这里先讲讲第一个结构体位图文件头结构:BITMAPFILEHEADER 的详细结构。

先看看代码原型:

typedef struct tagBITMAPFILEHEADER
{WORD      bfType;DWORD     bfSize;                       WORD      bfReserved1;        WORD      bfReserved2;        DWORD     bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

其中:

bfType:文件的标识,值必须是0x4D42(也就是“BM”两个字符,注意是Little-Endian)

bfSize:是整个文件的大小

bfReserved1和bfReserved2必须是0.

bfOffBits:是位图数据在文件中的偏移。

第二个结构体

位图信息头:BITMAPINFOHEADER。

typedef struct tagBITMAPINFOHEADER
{        DWORD     biSize;        LONG      biWidth;        LONG      biHeight;        WORD      biPlanes;        WORD      biBitCount;        DWORD     biCompression;        DWORD     biSizeImage;        LONG      biXPelsPerMeter;        LONG      biYPelsPerMeter;        DWORD     biClrUsed;        DWORD     biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

biSize:BITMAPINFOHEADER这个结构体的大小。必须为40(字节)。

biWidth:BMP位图的宽度。

biHeight:BMP位图的宽度。

这里需要说明一下的是,BMP位图数据有两种存储方式,通常是“底到上型”,也就是BMP位图数据的第一行其实是这个图片的最后一行。一种是“顶到下型”,BMP位图数据的第一行就是图片的第一行。

如果位图是“顶到下型”,那么biHeight的值是负的,位图的实际高度其实是(-biHeight),也就是它的相反数。

只有“底到上型”位图支持压缩。“底到上型”是目前最常见的位图类型。

PhotoShop在保存位图文件的时候,你可以设置让它输出“底到上型”或“顶到下型”。“顶到下型”就是“逆行序”。

biPlanes位图的“位面数”。这个值必须为1.

biBitCount位图的“颜色位数”。表示位图数据中,几个二进制位表示一个像素。

颜色位数为1表示单色(其实是“双色”,通常是黑白,也可以是别的两个颜色。)

为2表示四色,也就是四种颜色。为4表示16种颜色。为8表示有256种颜色。

在表示256色以内的颜色数量的时候,这个BMP文件其实是作为使用调色板颜色的图像,因此在BITMAPINFOHEADER结构体的后面还有个调色板。

所谓调色板其实就是RGBQUAD结构体的数组。它的元素个数就是颜色数。

biCompression位图的压缩格式,值可以是BI_RGB(未压缩),BI_RLE8(8位RLE压缩),BI_RLE4(4位RLE压缩),BI_BITFIELDS(表示颜色表中,每个像素有3个DWORD的屏蔽位数据来指示红绿蓝成分。16位和32位位图可以使用这个。),BI_JPEG(位图数据其实是JPG格式的),BI_PNG(位图数据其实是PNG格式的)

当biCompression值为BI_RLE8的时候,biBitCount必须为8,表示这是个8位的256色索引颜色位图(有256个调色板),位图数据按照如下算法压缩:

·位图数据是每两个字节组成一个元素,这样的元素组成了一个数组。

·每个元素,第一个字节表示重复的像素的数量,第二个字节是8位的调色板颜色索引。

·如果元素的第一个字节为0,那么第二个字节有特殊的含义:

0:这行已经结束了。

1:这个位图已经结束了。

2:设置下一个像素的位置,后面的两个无符号字节分别指定了下一个像素的X、Y偏移。注意是无符号字节,意思是说,偏移只能是往后。

3到0xFF:表示后面有一段没有压缩的数据的长度,也就是连续的8位像素颜色索引数据的长度。但是数据必须是2字节对齐的。biCompression值为BI_RLE4的时候,biBitCount必须为4,表示这是个4位的16色索引颜色位图(有16个调色板)。

和BI_RLE8的区别只是每个元素的第二个字节如果表示颜色索引,它表示的是两个像素的颜色索引。第一个字节表示重复的数量,第二个字节表示两个像素的颜色。

举个例,假设调色板第0号色是黑色,第1号色是白色,元素的值是0x05,0x01,那么它表示“黑白黑白黑白黑白黑白”共10个像素出现在1行。

biSizeImage位图数据块的大小。以字节为单位。如果你的位图没有经过压缩,这个值可以是0.

biXPelsPerMeter表示横向的每米的像素数。可以为0.

biYPelsPerMeter表示纵向的每米的像素数。可以为0.

biClrUsed位图实际使用过的调色板的颜色数。如果这个值为0,表示这个位图使用了整个调色板。只有8位以及8位以下的索引颜色位图才需要考虑这个值。对于16位以及16位以上的位图,无视这个值。biClrImportant表示重要的颜色数。如果为0,表示颜色都重要。通常它的值等于biClrUsed,或者等于0.

如果位图是8位以及8位以下的位图,后面是位图的调色板数据。否则没有调色板数据。

调色板就是简单的RGBQUAD数组。

后面就是位图数据了。如果位图是没有经过压缩的,那么位图数据的存储就是一行一行的,每行都是每XX个字节一个像素,取决于位图的位数(biBitCount),每一行都是4字节对齐的!多出来的部分用0补齐。这里必须注意。

因此,每行占用的字节数要按照以下公式计算:(其中的biBitCount指的是位图的位数,也就是BITMAPINFOHEADER的成员)

每行字节数 = ((图像宽度 - 1) * biBitCount / 32 + 1) * 4;

如果你是用VB编写读取位图的话,是这样写的:

每行字节数 = ((图像宽度 - 1) * biBitCount \ 32 + 1) * 4

这里还要注意的是“底到上型”位图和“顶到下型”位图,如果BITMAPINFOHEADER的biHeight成员大于零,那么这个位图就是“底到上型”,否则就是“顶到下型”。“底到上型”的位图,它的行序是从下到上排列的,也就是逆行序。“顶到下型”位图行序是从上到下排列的。

位图颜色表

位图颜色表就比较简单了:

typedef struct tagRGBQUAD
{BYTE      rgbBlue;        BYTE      rgbGreen;        BYTE      rgbRed;        BYTE      rgbReserved;
} RGBQUAD;

红禄蓝三种颜色,然后还有保留一个字节。

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338393.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php mysql 常用语句_PHP mysql基本语句指令

1 /* 选择数据库 2 use test; 3 */ 4 5 /* 显示所有的数据库 6 show databases; 7 */ 8 9 /* 删除表/数据库 10 drop database test1; 11 delete from user1 where id4; 12 */ 13 14 /* 创建表 15 CREATE TABLE user1( 16 id int primary key auto_increment1 /*选择数据库2 us…

C语言Main函数到底有几种,你真的懂吗?

乍一看标题,感觉小编小题大做,但凡学过C语言的聚聚,都知道C程序入口就是main函数,且一套程序里面有且仅有一个。但是很多时候我们看到的main函数却并不是千篇一律,格式竟然会有差别,这究竟是为啥&#xff1…

python大型项目经验_图像分类:13个Kaggle项目的经验总结

来源:数据派THU任何领域的成功都可以归结为一套小规则和基本原则,当它们结合在一起时会产生伟大的结果。机器学习和图像分类也不例外,工程师们可以通过参加像Kaggle这样的竞赛来展示最佳实践。在这篇文章中,我将给你很多资源来学习…

C语言程序main入口函数

一.main()函数是什么样的我们先要搞清楚main()函数有哪几种?查阅C89/C99/C11标准文档,里面明确固定了两种写法:int main(void) { /* ... */ }int main(int argc, char *argv[]) { /* ... */ }除此之外,其他写法应该都是不规范的写…

spring可用于数据层吗_Spring XD用于数据提取

spring可用于数据层吗Spring XD是一个功能强大的工具,它是一组可安装的Spring Boot服务,可以独立运行,在YARN或EC2之上运行。 Spring XD还包括一个管理UI网站和一个用于作业和流管理的命令行工具。 Spring XD是一组功能强大的服务&#xff0c…

go语言mysql操作_使用Go语言操作MySQL数据库的思路与步骤

最近在做注册登录服务时,学习用Go语言操作MySQL数据库实现用户数据的增删改查,现将个人学习心得总结如下,另外附有代码仓库地址,欢迎各位有兴趣的fork。软件环境:Goland、Navicat for MySQL。一、实现思路1&#xff0c…

学习嵌入式C语言的6个层级,你在哪一层?

C语言可以说是一种经典的编程语言,没有C语言就没有今天的各种操作系统。C语言是基础,那么你掌握了多少?新手级别学习目的:过计算机二级,考证,应付期末考试。需要掌握的程度:掌握C语言的基本语法…

intellij idea_IntelliJ IDEA内部设计

intellij ideaIntelliJ IDEA的第一个版本于2001年1月发布,当时它是第一个集成了高级代码导航和代码重构功能的Java IDE之一。 2009年,JetBrains开源了其社区版本 。 从那时起,创建了许多基于它的IDE,例如Google的Android Studio。…

C语言 | 函数执行成功时,return 1 还是return 0?

今天分享的内容是关于函数执行成功,返回0还是1的讨论~基本上,没有人会将大段的C语言代码全部塞入 main() 函数,更好的做法是按照复用率高,耦合性低的原则,尽可能的将代码拆分不同的功能模块,并封装成函数。…

jcache_窥探JCache API(JSR 107)

jcache这篇文章从较高的层次介绍了JCache API,并提供了一个预告片–仅够您(希望)开始对此发痒了;-) 在这篇文章中……。 JCache概述 JCache API,实现 JCache API支持的(Java)平台 快速了解O…

redis 启动加载mysql_Redis分析系列:启动加载过程

从本篇文章开始(命名为Redis分析系列),将会通过分析Redis的源代码(以Redis 2.2.0 RC1为准),来对它的内部实现做一些探讨。本文主要介绍Redis启动加载过程,总体上可以分为如下几步:1. 初始化全局服务器配置2. 加载配置文件(如果指定…

c 文件怎么进行读取和写入操作?

C >>和<<读写文本文件&#xff1a;fstream 或者 ifstream 类负责实现对文件的读取&#xff0c;它们内部都对 >> 输出流运算符做了重载&#xff1b;同样&#xff0c;fstream 和 ofstream 类负责实现对文件的写入&#xff0c;它们的内部也都对 << 输出流…

mysql+误操作怎么恢复_Mysql误操作恢复流程

一、开启binlog。show variables like log_bin;#vim /etc/my.cnf在[mysqld]中加入log-bin mysql-binlog-bin /usr/local/mysql/log/mysql-bin.log重启mysql服务#service mysqld stop#service mysqld start二、数据写入建库create database …

drools6.5_Drools 6.2.0.Final发布

drools6.5我们很高兴地宣布最新&#xff0c;最出色的Drools 6.2.0.Final版本。 特别是此发行版更加注重改进的可用性和功能&#xff0c;这些功能使项目更易于使用&#xff08;和采用&#xff09;。 新功能包括对工作台UI的大量改进&#xff0c;对社交活动和插件管理的支持以及…

c程序编写x的y次方的方法

c程序怎么编写x的y次方?C语言pow()函数&#xff1a;求x的y次方&#xff08;次幂&#xff09;头文件&#xff1a;#include pow() 函数用来求 x 的 y 次幂&#xff08;次方&#xff09;&#xff0c;其原型为&#xff1a;double pow(double x, double y);pow()用来计算以x 为底的…

8条嵌入式C语言编程小知识总结

1. 流水线被指令填满时才能发挥最大效能&#xff0c;即每时钟周期完成一条指令的执行(仅指单周期指令)。如果程序发生跳转&#xff0c;流水线会被清空&#xff0c;这将需要几个时钟才能使流水线再次填满。因此&#xff0c;尽量少的使用跳转指令可以提高程序执行效率&#xff0c…

c语言函数的三种调用方式是什么?

函数的三种调用方式&#xff1a;1、函数作为表达式中的一项出现在表达式中&#xff0c;例“zmax(x,y)”&#xff1b;2、函数作为一个单独的语句&#xff0c;例“printf("%d",a)”&#xff1b;3、函数作为调用另一个函数时的实参&#xff0c;例“printf("%d"…

弱口令扫描工具mysql ftp_基于端口的弱口令检测工具--iscan

iscan: 基于端口的弱口令检测工具亲手打造了一款基于端口的弱口令检测工具&#xff0c;使用python进行编写&#xff0c;主要可以用于渗透测试中常见服务端口弱口令的检测。目前支持以下服务&#xff1a;系统弱口令&#xff1a;ftp、ssh、telnet、ipc$数据库弱口令&#xff1a;m…

javafx 剪切板_JavaFX技巧18:路径剪切

javafx 剪切板我最近注意到&#xff0c;我致力于ControlsFX项目的PopOver控件无法正确剪切其内容。 当我为FlexCalendarFX框架开发手风琴弹出窗口时&#xff0c;这一点变得显而易见。 每当最后一个标题窗格扩展时&#xff0c;其底角不再是圆角而是正方形。 在标题窗格中放置一个…

嵌入式开发中C语言编程要点简述!

在嵌入式Linux的C语言开发中&#xff0c;C语言的基本编程依然是最重要的内容。除此之外&#xff0c;与一般的C语言编程相比&#xff0c;嵌入式Linux的C语言编程有以下一些要点&#xff1a;1、库函数与系统调用在进行C语言编程的时候&#xff0c;使用库函数是不可避免的。关于使…