C++读写bmp图并显示图像

预备知识

位图文件头

typedef struct tagBITMAPFILEHEADER {
        WORD bfType;          //文件类型,必须为BM(1-2字节)
        DWORD bfSize;         //文件大小,以字节为单位(3-6字节,低位在前)
        WORD bfReserved1;     //保留字,必须为0(7-8字节)
        WORD bfReserved2;     //保留字,必须为0(9-10字节)
        DWORD bfOffBits;      //位图数据的起始位置,以相对于位图(11-14字节,低位在前)
} BITMAPFILEHEADER;

位图信息头

typedef struct tagBITMAPINFOHEADER{
	    DWORD biSize;        //本结构所占用字节数(15-18字节)
	    LONG biWidth;        //位图的宽度(19-22字节)
	    LONG biHeight;       //位图的高度(23-26字节)
	    WORD biPlanes;       //目标设备的级别,必须为1(27-28字节)
	    WORD biBitCount;     //每个像素所需的位数,必须是1(双色),4(16色),8(256色)16(高彩色)或24(真彩色)之一(29-30字节)
	    DWORD biCompression; //位图压缩类型,必须是0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一(31-34字节)
	    DWORD biSizeImage;   //位图的大小(4字节对齐),以字节为单位(35-38字节)
	    LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
	    LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
	    DWORD biClrUsed;     //位图实际使用的颜色表中的颜色数(47-50字节)
	    DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
}

fopen

FILE * fopen(const char * path,const char * mode);

path是字符串类型的bmp图片路径;mode读取方式,等下回用到"rb",读写打开一个二进制文件,允许读写数据,文件必须存在。

fseek

int fseek(FILE *stream, long offset, int fromwhere);

函数设置文件指针stream的位置,stream指向以fromwhere为基准,偏移offset个字节的位置。

fread

size_t fread ( void *buffer, size_t size, size_t count, FILE *stream);

从stream中读取count个单位大小为size个字节,存在buffer中。

SetPixel

函数功能:该函数将指定坐标处的像素设为指定的颜色。   
函数原型:COLORREF SetPixel(HDC hdc, int X,int Y, COLORREF crColor);
参数:
hdc:设备环境句柄。
X:指定要设置的点的X轴坐标,按逻辑单位表示坐标。   
Y:指定要设置的点的Y轴坐标,按逻辑单位表示坐标。
crColor:指定要用来绘制该点的颜色。
返回值:如果函数执行成功,那么返回值就是函数设置像素的RGB颜色值。这个值可能与crColor指定的颜我色有不同,之所以有时发生这种情况是因为没有找到对指定颜色进行真正匹配造成的;如果函数失败,那么返回值是C1。

代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<Windows.h>
using namespace std;

int main()
{
	//读取图像文件
	FILE* fp = fopen("1.bmp", "rb");//读入的文件
	if (!fp) {
		cout << "文件未打开!\n";
		exit(0);
	}
	long width, height;
	BITMAPFILEHEADER fileHead;//位图文件头
	fread(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);//从fp中读取BITMAPFILEHEADER信息到fileHead中,同时fp的指针移动
	BITMAPINFOHEADER infoHead;//位图信息头
	fread(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);//从fp中读取BITMAPINFOHEADER信息到infoHead中,同时fp的指针移动
	width = infoHead.biWidth;
	height = infoHead.biHeight;

	cout << "width = " << width << endl << "height = " << height << endl;

	char* bmpBuf = new char[width * height * 3];
	fseek(fp, long(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)), 0);//定位到起始位置
	fread(bmpBuf, sizeof(char), width * height * 3, fp);
	fclose(fp);

	//写入文件信息
	FILE* outFile = fopen("show.bmp", "wb");//输出文件
	fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, outFile);
	fwrite(&infoHead, sizeof(BITMAPINFOHEADER), 1, outFile);
	char* tmp = bmpBuf;
	for (int j = 0;j < height;j++)
		for (int i = 0;i < width * 3;i++)
			fwrite(tmp++, 1, 1, outFile);
	fclose(outFile);

	//显示图像
	HWND hwnd = GetForegroundWindow();//窗口句柄
	HDC hdc = GetDC(hwnd);//绘图句柄
	BYTE b, g, r, rgb;
	tmp = bmpBuf;
	for (int j = 0;j < height;j++) {
		for (int i = 0;i < width;i++) {
			b = *tmp++;
			g = *tmp++;
			r = *tmp++;
			SetPixel(hdc, i, 150 + height - j, RGB(r, g, b));
		}
	}
}

运行截图

在这里插入图片描述

思路&解释

  1. 使用分别读取图像的位图文件头和位图信息头的数据,其中位图信息头结构中包含图像的宽、高等信息,因此使用biWidth,biHeight来获取图像的宽和高;
  2. 为bmpBuf分配空间,大小为width * height * 3,因为每一像素由RGB三个分量组成;
  3. 由于使用fread读取了位图文件头和位图信息头,此时fp也随之偏移,偏移长度即为 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) ,因此需要使用fseek函数将fp调整到指向初始位置,然后才能将从fp中读取相应数据到bmpBuf中;
  4. 写文件时使用一个临时变量tmp指向bmpBuf头部,然后依次写入即可,注意每行的数据是 width * 3 个,同样是因为每一像素由RGB三个分量组成;
  5. 显示图像时使用Windows.h中自带的HWND、HDC数据类型和GetForegroundWindow()、GetDC(HWND hwnd)函数获取窗口和绘图句柄,再调用SetPixel(HDC hdc, int x,int y, COLORREF color)函数进行绘图即可。其中,获取rgb数据时,文件中每一像素中的颜色信息按顺序依次为b ,g ,r。

注意

读取的图片应该是 24位bmp图,否则程序可能无法运行。
有些系统默认保存的bmp图可能不是24位的,只需要在windows系统中自带的「画图」软件中打开图片,然后点击 另存为,选择 24位位图 即可。
在这里插入图片描述
在这里插入图片描述

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

friedrichor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值