1、了解bmp图片
2、此网站可以查看bmp图片的二进制信息
3、QT建立工程(MainWindow)和(Widget)都可以
1、了解bmp图片格式后,首先我们需要建立两个结构体BITMAPFILEHEADER 和BITMAPINFOHEADER 存储该bmp图片的信息(宽、高等)
2、用类将bmp的操作进行封装:bmpGet.h 代码如下:
#ifndef BMPGET_H #define BMPGET_H #include <QFile> #include <qDebug> #include <QRgb> #include <QImage> typedef char CHAR; typedef short SHORT; typedef int BOOL;//4 typedef float FLOAT; typedef uint LONG;//4 typedef quint8 BYTE;//1 typedef quint16 WORD;//2 typedef quint32 DWORD;//4 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; //40字节 typedef struct tagBITMAPFILEHEADER { WORD bfType; //文件类型:表示bmp的固定值 DWORD bfSize; //文件大小 WORD bfReserved1; //保留字:0 WORD bfReserved2; //保留字:0 DWORD bfOffBits; //从文件头到位图数据的偏移量 } BITMAPFILEHEADER; //14个字节 //封装 class bmpGet { public: BITMAPFILEHEADER headBmp; //位图头信息 BITMAPINFOHEADER tailBmp; //位图中间信息 QString bmpPath; //文件路径 char *img_R = NULL; //new char[1024 * 1024]; char *img_G = NULL; //new char[1024 * 1024]; char *img_B = NULL; //new char[1024 * 1024]; char *img_data = NULL; int bmpWidth; int bmpHeight; int lineByte; QImage img; public: bool readRgb(); //读数据 bool separateRGB(); //分离RGB void printImageBmp(int offX, int offY); //保存IMG bmpGet(QString bmpPath); ~bmpGet(); }; //--------------------->x //| //| //| //| //| //| //↓ //y #endif // BMPGET_H
相应的.cpp文件:在readRgb()这个方法里面尤其要注意设置小端排序
#include "bmpget.h" bmpGet::bmpGet(QString bmpPath):bmpPath(bmpPath) { img_R = new char[1024 * 1024]; img_G = new char[1024 * 1024]; img_B = new char[1024 * 1024]; img_data = new char[1024 * 1024 * 3]; }; bool bmpGet::readRgb() { QFile bmpFile(this->bmpPath); if(!bmpFile.open(QIODevice::ReadOnly)) qDebug()<<"error"; //debug QDataStream in(&bmpFile); //read barry stream;建立二进制的流文件对象,从流里面读取二进制数据。如果需要,seek函数可以改变流的起始位置。 in.setByteOrder(QDataStream::LittleEndian); //小端排序:这一步非常重要 in>>this->headBmp.bfType; in>>this->headBmp.bfSize; in>>this->headBmp.bfReserved1; in>>this->headBmp.bfReserved2; in>>this->headBmp.bfOffBits; in>>this->tailBmp.biSize; in>>this->tailBmp.biWidth; in>>this->tailBmp.biHeight; in>>this->tailBmp.biPlanes; in>>this->tailBmp.biBitCount; in>>this->tailBmp.biCompression; in>>this->tailBmp.biSizeImage; in>>this->tailBmp.biXPelsPerMeter; in>>this->tailBmp.biYPelsPerMeter; in>>this->tailBmp.biClrUsed; in>>this->tailBmp.biClrImportant; this->lineByte = (this->tailBmp.biWidth * this->tailBmp.biBitCount / 8 + 3) / 4 * 4; //4字节补齐,不补齐会出现图像偏差的问题 char *tempByteData=new char[this->lineByte * this->tailBmp.biHeight]; if ((unsigned int)in.readRawData(tempByteData,(this->lineByte * this->tailBmp.biHeight))<(unsigned int)this->lineByte * this->tailBmp.biHeight) { qDebug() << "get bmp err"; } memcpy(this->img_data,tempByteData,this->lineByte * this->tailBmp.biHeight); //not strycpy this->bmpHeight =this->tailBmp.biHeight; this->bmpWidth =this->tailBmp.biWidth; delete [] tempByteData;//不要忘记释放内存 bmpFile.close(); return true; } bool bmpGet::separateRGB() { char *temp=this->img_data; int t=0; for (int i = 0; i < this->bmpHeight; i++) { for (int k = 0; k < this->bmpWidth; k++) { this->img_R[t] = temp[3 * k + 0]; this->img_G[t] = temp[3 * k + 1]; this->img_B[t] = temp[3 * k + 2]; t++; } temp += lineByte;//跳过lineByte个地址而不是this->bmpWidth个地址:这可以查查bmp图片的位图数据的格式对齐:要不然输出的图像是倾斜的。 } return true; } void bmpGet::printImageBmp(int offX, int offY) { this->readRgb(); this->separateRGB(); unsigned char R, G, B = 0; QImage img(this->bmpWidth+offX,this->bmpHeight+offY,QImage::Format_RGB32); for (int i = 0 ; i < this->bmpHeight; i++) for (int k = 0; k < this->bmpWidth; k++) { R = (unsigned char)this->img_R[i * this->bmpWidth + k]; G = (unsigned char)this->img_G[i * this->bmpWidth + k]; B = (unsigned char)this->img_B[i * this->bmpWidth + k]; img.setPixel(k + offX, this->bmpHeight - i - 1+offY, qRgb(B,G,R));//为什么是this->bmpHeight - i - 1+offY,因为bmp位图像素数据是图片从下往上,从左往右一一排序的。同时要注意img的坐标,分别是向右和向左为xy轴。 } this->img=img; } bmpGet::~bmpGet()//在构造函数里面new 就需要在析构函数里面delete { delete [] img_R; delete [] img_G; delete [] img_B; delete [] img_data; }
3、界面文件调用封装的类:widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> //#include <QWheelEvent>通过鼠标缩放用的头文件(建立鼠标滚轮角度事件) #include <QFile> #include <qDebug> #include "bmpget.h" QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); void showBmp(int offX, int offY); ~Widget(); private: QImage img; Ui::Widget *ui; }; #endif // WIDGET_H
widget.cpp
#include "widget.h" #include "ui_widget.h" #include "bmpget.h" #include <QMessageBox> #include <fstream> #include<iostream> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); showBmp(0,0); } void Widget::showBmp(int offX, int offY) { bmpGet bmp("C:\\Users\\32652\\Pictures\\2.bmp");//这里改成自己放bmp图片的位置。 bmp.printImageBmp(offX,offY); ui->label->setPixmap(QPixmap::fromImage(bmp.img)); } Widget::~Widget() { delete ui; }
4、要记得在ui里面托一个组件:QLabel
4、总的工程结构
运行效果:
最后!不懂的可以私信我。同时,后续我会优化UI界面和添加各种图片缩放算法的示例。