03 bmp图片生成及像素修改的源码

bmp图片生成及像素修改的源码

作者将狼才鲸
创建日期2022-10-08

Gitee源码地址
CSDN阅读地址

  • 简介:使用Qt、Gcc(可以是Linux或者MinGW等)编译环境,使用C语言编写。自行生成并保存BMP文件,修改BMP文件中像素。

一、BMP概述

二、编译及使用

  • 在tag为v1.0.0的软件源码下:
  • 如果是在32位Linux系统下,或者Linux系统里已经配置了32位的GCC,或者Windows中的MinGW已经安装了32位的GCC,则直接使用命令 gcc -o demo main.c
  • 编译完成了会生成名为demo,或者名为demo.exe的文件,直接 ./demo 运行或者双击运行,会在源码同级目录下生成picture.bmp文件,直接双击打开这个bmp文件,会看到是一张1280x720的灰色图片。
  • 警告:当前版本源码在64位编译环境下生成的图片在逻辑上一定不对,我并没有也没必要在这种编译环境测试代码。

三、源码内容

/**
 * \brief 创建并用软件生成BMP文件;修改文件中像素内容。
 * \details File format: UTF-8
 *       可以用GCC、Qt、VS编译并运行
 * \author 将狼才鲸
 * \date 2022-10-08
 * \warning 此处我默认你的目标运行环境是32位cpu!一个int类型4个字节;
 *          编译环境是类似于Linux/GCC、Windows这样的小端模式
 * \note 暂时还没有写Makefile文件,如果在Linux或者MinGW下编译直接使用命令
         gcc -o demo main.c,已经能成功生成灰色图片
 */

/**
 * BMP文件格式为文件头(指示文件大小和像素起始位置)+信息头(指示颜色表长度和像素内容长度)+变长颜色表+变长像素内容,BMP存储的内容是小端模式,和GCC中一样。
 */

#include <string.h> /* memset */
#include <stdio.h> /* fopen, fwrite, fclose, fflush, printf */

/**
 * \brief BMP文件头,14字节
 */
typedef struct _bitmap_file_header {
	short int type;	/* 类型,内容为BM这两个字母 */
	int size; 	/* 文件大小,单位字节 */
	short int rsv1;	/* 保留 */
	short int rsv2; /* 保留 */
	int off_bits;	/* 位图像素数据相对于文件头的字节偏移量 */
} __attribute__ ((packed)) BITMAP_FILE_HEADER_T; /* 取消字节对齐(也就是1字节对齐) */

/**
 * \brief BMP信息头,常用40字节,其它不常用的还有12、64、108、124字节
 */
typedef struct _bitmap_info_header {
	int size;	/* 本结构体占的字节数 */
	int width;	/* 位图宽度,单位是像素 */
	int height;	/* 位图高度,单位像素 */
	short int planes;	/* 目标设备级别,值必须为1 */
	short int bit_count;	/* 每个像素的位数;1:双色,4:16色,8:256色,24:真彩色,32:增强真彩色,现在一般是32位色;
                                   当值为1、4、8时默认颜色表中颜色有2、16、256个;当为24、32时没有颜色表*/
	int compression;	/* 位图压缩类型;当前必须为0:不压缩,其它的还有1:BI_RLE8或BI_RLE4 */
	int size_image;	/* 实际位图像素数据所占的字节数 */
	int x_pels_per_meter;	/* 水平分辨率,每米像素数 */
	int y_pels_per_meter;	/* 垂直分辨率,每米像素数 */
	int clr_used;	/* 位图实际使用的颜色表中的颜色数;现在位图基本上都没有颜色表了,都是真彩色 */
	int clr_important;	/* 位图显示过程中重要的颜色数 */
} __attribute__ ((packed)) BITMAP_INFO_HEADER_T;

/**
 * 像素阵列记录顺序是像素从左往右,然后行数从上往下,一行字节数必须是4的倍数,不足的以字节0补齐
 */

/**
 * \brief 颜色表中一个元素的结构体
 */
typedef struct _rgb_quad {
	char rgb_blue;
	char rgb_green;
	char rgb_red;
	char rgb_rsv;	/* 保留,必须为0 */
} __attribute__ ((packed)) RGB_QUAD_T;

#define WIDTH 1280
#define HEIGHT 720
#define BIT_DEPTH 3 /* 3 x 8 = 32bits */
/* 大数组空间需要开辟在函数外面,避免超出芯片的堆栈;多开一点冗余空间方便每行4字节对齐 */
char image_data[(WIDTH + 4) * HEIGHT * BIT_DEPTH];

int main()
{
	/* 手动准备好默认参数 */
	int bit_depth = BIT_DEPTH * 8; /* 现在都是32位图片了 */
	int width = WIDTH;
	int height = HEIGHT;
	int info_header_size = sizeof(BITMAP_INFO_HEADER_T);
	char *file_name = "picture.bmp";

	/** 手动生成一张BMP图片 */
	/* 1. 填写文件头 */
	BITMAP_FILE_HEADER_T file_header;
	memset(&file_header, 0, sizeof(file_header)); /* 内容先清零,这样保留的值就不用手动填,也避免有些值忘记填产生异常 */
	/* 文件大小和像素偏移最后再填,先只填type */
	file_header.type = ('M' << 8) | 'B'; /* GCC默认是小端模式,所以BM这两个字节要处理下 */

	/* 2. 填写信息头 */
	BITMAP_INFO_HEADER_T info_header;
	memset(&info_header, 0, sizeof(info_header));
	info_header.size = info_header_size;
	info_header.width = width;
	info_header.height = height;
	info_header.planes = 1;
	info_header.bit_count = bit_depth;
	info_header.compression = 0;
	info_header.size_image =
		((width % 4) ? ((width / 4) * 4 + 4) : width) * height;
	info_header.x_pels_per_meter = 2800;
	info_header.y_pels_per_meter = 2800;

	/* 3. 补全文件头内容 */
	if (info_header.bit_count >= 24) {
		file_header.size = sizeof(file_header) + sizeof(info_header)
			+ info_header.size_image * info_header.bit_count / 8;
		file_header.off_bits = sizeof(file_header) + sizeof(info_header);
	} else {
		printf("bit depth %d not support!\n", info_header.bit_count);
		return 0;
	}
	
	/* 3. 打开文件并写BMP文件头 */
	FILE *fp = fopen(file_name, "wb+"); /* 替换或者新建文件,用二进制读写 */
	if (fp == NULL) {
		printf("create file %s fail!\n", file_name);
		return 0;
	}
	
	/* 4. 手动填写图片像素,先都弄成灰色 */
	int image_length = info_header.size_image * info_header.bit_count / 8;
	for (int i = 0; i < image_length; i++) {
		/**
		 * \todo 此处先不考虑宽没有4字节对齐,需要补零的情况
		 * \warning 测试时一定先手动保证宽度4字节对齐
		 */
		image_data[i] = 0x7F;
	}
	
	fwrite(&file_header, 1, sizeof(file_header), fp);
	fwrite(&info_header, 1, sizeof(info_header), fp);
	
	if (info_header.bit_count < 24) {
		/**
		 * \todo 如果需要,请补充像素位深度为1、4、8的情况
		 */
		printf("err: please add codes parse bit count %d\n", info_header.bit_count);
	}
	
	/* 第三个参数是每次写几个字节,这里是char数组,所以每次写1个 */
	fwrite(image_data, image_length, 1, fp);
	fflush(fp); /* 把缓存中的内容确定写到文件中,其实这里要fclose了可以不用的 */
	fclose(fp);
	
	return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值