#include <iostream>
#include <fstream>
#include <cstdint>
#include <cstring>
#pragma pack(2) // 结构体按字节对齐,每个元素的大小必须是2的整数倍
// bmp文件头
struct BMPHeader
{
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
};
// bmp信息头
struct BMPInfoHeader
{
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
};
#pragma pack()
// 读取bmp图片并返回像素数据
unsigned char *readBMP(const char *filePath, BMPHeader &header, BMPInfoHeader &infoHeader)
{
std::ifstream file(filePath, std::ios::binary); // 以二进制方式打开文件
if (!file.is_open())
{
std::cerr << "Can't open " << filePath << std::endl;
return nullptr;
}
// 读取文件头
file.read(reinterpret_cast<char *>(&header), sizeof(header));
if (header.bfType != 0x4D42)
{
std::cerr << filePath << " is not a BMP file" << std::endl;
return nullptr;
}
// 读取信息头
file.read(reinterpret_cast<char *>(&infoHeader), sizeof(infoHeader));
if (infoHeader.biBitCount != 24)
{ // 只处理24位真彩色图片
std::cerr << filePath << " is not a 24-bit BMP file" << std::endl;
return nullptr;
}
// 读取像素数据
unsigned char *pixels = new unsigned char[infoHeader.biSizeImage];
file.seekg(header.bfOffBits, std::ios::beg);
file.read(reinterpret_cast<char *>(pixels), infoHeader.biSizeImage);
file.close();
return pixels;
}
// 写入bmp图片
bool writeBMP(const char *filePath, BMPHeader &header, BMPInfoHeader &infoHeader, unsigned char *pixels)
{
std::ofstream file(filePath, std::ios::binary); // 以二进制方式打开文件
if (!file.is_open())
{
std::cerr << "Can't create " << filePath << std::endl;
return false;
}
// 写入文件头
file.write(reinterpret_cast<char *>(&header), sizeof(header));
// 写入信息头
file.write(reinterpret_cast<char *>(&infoHeader), sizeof(infoHeader));
// 写入像素数据
file.write(reinterpret_cast<char *>(pixels), infoHeader.biSizeImage);
file.close();
return true;
}
// 在bmp图片的指定位置叠加另一张bmp图片
bool addBMP(const char *bottomFilePath, const char *topFilePath, const char *outputFilePath,
int32_t posX, int32_t posY)
{
// 分别读取两张图片
BMPHeader bottomHeader, topHeader, outputHeader;
BMPInfoHeader bottomInfoHeader, topInfoHeader, outputInfoHeader;
unsigned char *bottomPixels = readBMP(bottomFilePath, bottomHeader, bottomInfoHeader);
unsigned char *topPixels = readBMP(topFilePath, topHeader, topInfoHeader);
if (!bottomPixels || !topPixels)
{
std::cerr << "Read BMP failed" << std::endl;
return false;
}
// 处理图片
int32_t width = bottomInfoHeader.biWidth;
int32_t height = bottomInfoHeader.biHeight;
int32_t bytesPerRow = width * 3;
while (bytesPerRow % 4 != 0)
{ // 每一行的字节数必须是4的整数倍
++bytesPerRow;
}
if (posX + topInfoHeader.biWidth > width || posY + topInfoHeader.biHeight > height)
{
std::cerr << "The position is out of range" << std::endl;
return false;
}
for (int32_t i = 0; i < topInfoHeader.biHeight; ++i)
{
for (int32_t j = 0; j < topInfoHeader.biWidth; ++j)
{
// 计算索引值
int32_t topIndex = (topInfoHeader.biHeight - 1 - i) * topInfoHeader.biWidth * 3 + j * 3;
int32_t bottomIndex = (height - 1 - (posY + i)) * bytesPerRow + (posX + j) * 3;
// 取两张图片中像素值中较大的一个作为输出的像素值
bottomPixels[bottomIndex] = std::max(bottomPixels[bottomIndex], topPixels[topIndex]);
bottomPixels[bottomIndex + 1] = std::max(bottomPixels[bottomIndex + 1], topPixels[topIndex + 1]);
bottomPixels[bottomIndex + 2] = std::max(bottomPixels[bottomIndex + 2], topPixels[topIndex + 2]);
}
}
// 写入输出文件
outputInfoHeader = bottomInfoHeader;
outputHeader = bottomHeader;
bool result = writeBMP(outputFilePath, outputHeader, outputInfoHeader, bottomPixels);
delete[] bottomPixels;
delete[] topPixels;
return result;
}
int main()
{
const char *bottomFilePath = "image.bmp";
const char *topFilePath = "image1.bmp";
const char *outputFilePath = "output.bmp";
int32_t posX = 0.5 * (4096 - 640);
int32_t posY = 2730 - 400;
if (!addBMP(bottomFilePath, topFilePath, outputFilePath, posX, posY))
{
std::cerr << "Add BMP failed" << std::endl;
return -1;
}
std::cout << "Add BMP success" << std::endl;
return 0;
}
实现效果如下: