bmp.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
//
#define Bmp_FileHeader_Size 14 // sizeof(Bmp_FileHeader)的值不一定准确
typedef struct{
uint8_t bfType[2]; //文件类型: "BM"/bmp, "BA"/.. , ...
uint32_t bfSize; //整个文件的大小
uint16_t bfReserved1; //保留: 0
uint16_t bfReserved2; //保留: 0
uint32_t bfOffbits; //文件数据从第几个字节开始
}Bmp_FileHeader;
//
#define Bmp_Info_Size 40
typedef struct{
uint32_t biSize; //该段占用字节数
uint32_t biWidth; //图像宽度, 单位像素
int32_t biHeight; //图像高度, 单位像素(数据为正时为倒向)
uint16_t biPlanes; //平面数, 总是为1
uint16_t biBitCount; //单位像素占用比特数: 1, 4, 8, 16, 24, 42
uint32_t biCompression; //图像压缩方式: 0/BI_BGB 不压缩,
// 1/BI_RLE8 8比特游程压缩, 只用于8位位图
// 2/BI_RLE4 4比特游程压缩, 只用于4位位图
// 3/BI_BITFIELDS 比特域, 用于16/32位位图
// 4/BI_JPEG 位图含有jpeg图像, 用于打印机
// 5/BI_PWG 位图含有pwg图像, 用于打印机
uint32_t biSizeImage; //说明图像大小, 为BI_BGB时可能为0
int32_t biXPelsPerMeter;//水平分辨率, 像素/米, 有符号整数
int32_t biYPelsPerMeter;//垂直分辨率, 像素/米, 有符号整数
uint32_t biClrUsed; //位图实际使用彩色表中的颜色索引数(为0表示使用所有)
uint32_t biClrImportant;//图像显示有重要影响的颜色索引数
}Bmp_Info;
//功能: 读取bmp格式图片
//参数: filePath: 传入, 文件地址
// picMaxSize: 传出, 用以返回读取到的图片矩阵的总字节数
// width: 传出, 用以返回图片横向的像素个数
// height: 传出, 用以返回图片纵向的像素个数
// per: 传出, 用以返回图片每像素占用字节数
//返回: 图片矩阵数据的指针(注意指针是函数内分配的内存, 用完需释放)
unsigned char *bmp_get(char *filePath, int *picMaxSize, int *width, int *height, int *per)
{
FILE *fp;
Bmp_FileHeader bf;
Bmp_Info bi;
int perW, perWCount;
int ret;
int i, j, picCount, totalSize;
int overLineBytesNum;
unsigned int overLineBytesSum; // overLineBytesNum : 行结尾补0字节数 //每行字节数规定为4的倍数, 不足将补0, 所以读行的时候注意跳过
unsigned char buffHeader[512], *data, *pic;
//
if(filePath == NULL)
return NULL;
//
if((fp = fopen(filePath, "rb")) < 0)
{
printf("bmp_get : open file %s failed\r\n", filePath);
return NULL;
}
//Bmp_FileHeader
if(fread(buffHeader, 1, Bmp_FileHeader_Size, fp) <= 0)
{
printf("bmp_get : read Bmp_FileHeader failed\r\n");
fclose(fp);
return NULL;
}
bf.bfType[0] = buffHeader[0]; bf.bfType[1] = buffHeader[1];
bf.bfSize = buffHeader[2] + ((buffHeader[3]&0xFF)<<8) + ((buffHeader[4]&0xFF)<<16) + ((buffHeader[5]&0xFF)<<24);
bf.bfOffbits = buffHeader[10] + ((buffHeader[11]&0xFF)<<8) + ((buffHeader[12]&0xFF)<<16) + ((buffHeader[13]&0xFF)<<24);
//printf("bmp_get : bfType/%s, bfSize/%d, bfOffbits/%d\r\n", bf.bfType, bf.bfSize, bf.bfOffbits);
if(bf.bfType[0] != 'B' || bf.bfType[1] != 'M')
{
printf("bmp_get : bmp type err, bfType must be \"BM\"\r\n");
fclose(fp);
return NULL;
}
//Bmp_Info
if(bf.bfOffbits - Bmp_FileHeader_Size < Bmp_Info_Size || fread(buffHeader, 1, Bmp_Info_Size, fp) <= 0)
{
printf("bmp_get : read Bmp_Info failed\r\n");
fclose(fp);
return NULL;
}
bi.biSize = buffHeader[0] + ((buffHeader[1]&0xFF)<<8) + ((buffHeader[2]&0xFF)<<16) + ((buffHeader[3]&0xFF)<<24);
bi.biWidth = buffHeader[4] + ((buffHeader[5]&0xFF)<<8) + ((buffHeader[6]&0xFF)<<16) + ((buffHeader[7]&0xFF)<<24);
bi.biHeight = buffHeader[8] | ((buffHeader[9]&0xFF)<<8) | ((buffHeader[10]&0xFF)<<16) | ((buffHeader[11]&0xFF)<<24);
bi.biPlanes = buffHeader[12] + ((buffHeader[13]&0xFF)<<8);
bi.biBitCount = buffHeader[14] + ((buffHeader[15]&0xFF)<<8);
bi.biCompression = buffHeader[16] + ((buffHeader[17]&0xFF)<<8) + ((buffHeader[18]&0xFF)<<16) + ((buffHeader[19]&0xFF)<<24);
bi.biSizeImage = buffHeader[20] + ((buffHeader[21]&0xFF)<<8) + ((buffHeader[22]&0xFF)<<16) + ((buffHeader[23]&0xFF)<<24);
bi.biXPelsPerMeter = buffHeader[24] | ((buffHeader[25]&0xFF)<<8) | ((buffHeader[26]&0xFF)<<16) | ((buffHeader[27]&0xFF)<<24);
bi.biYPelsPerMeter = buffHeader[28] | ((buffHeader[29]&0xFF)<<8) | ((buffHeader[30]&0xFF)<<16) | ((buffHeader[31]&0xFF)<<24);
bi.biClrUsed = buffHeader[32] + ((buffHeader[33]&0xFF)<<8) + ((buffHeader[34]&0xFF)<<16) + ((buffHeader[35]&0xFF)<<24);
bi.biClrImportant = buffHeader[36] + ((buffHeader[37]&0xFF)<<8) + ((buffHeader[38]&0xFF)<<16) + ((buffHeader[39]&0xFF)<<24);
//perW 每像素字节数
if(bi.biBitCount >= 8)
perW = bi.biBitCount/8;
else
perW = 1;
//计算总字节数
//totalSize = bf.bfSize - bf.bfOffbits;
//计算总字节数
overLineBytesNum = 4- bi.biWidth*(bi.biBitCount/8)%4;
if(overLineBytesNum == 4)
overLineBytesNum = 0;
if(bi.biHeight < 0)
{
totalSize = bi.biWidth*(-bi.biHeight)*(bi.biBitCount/8);
overLineBytesSum = overLineBytesNum*(-bi.biHeight);
}else
{
totalSize = bi.biWidth*bi.biHeight*(bi.biBitCount/8);
overLineBytesSum = overLineBytesNum*bi.biHeight;
}
//printf("bmp_get : biSize/%d, biWidth/%d, biHeight/%d, biPlanes/%d, biBitCount/%d, biCompression/%d, biSizeImage/%d, biXPelsPerMeter/%d, biYPelsPerMeter/%d, biClrUsed/%d, biClrImportant/%d, overLineBytesNum/%d, overLineBytesSum/%d, totalSize/%d\r\n", bi.biSize, bi.biWidth, bi.biHeight, bi.biPlanes, bi.biBitCount, bi.biCompression, bi.biSizeImage, bi.biXPelsPerMeter, bi.biYPelsPerMeter, bi.biClrUsed, bi.biClrImportant, overLineBytesNum, overLineBytesSum, totalSize);
//指针移动到数据起始
if(fseek(fp, bf.bfOffbits, 0) < 0)
{
printf("bmp_get : lseek failed\r\n");
fclose(fp);
return NULL;
}
//分配内存一次读入整张图片
data = (unsigned char *)calloc(1, totalSize + overLineBytesSum + perW); //多1像素的字节, 防止操作不当溢出
if((ret = fread(data, 1, totalSize + overLineBytesSum, fp)) != (totalSize + overLineBytesSum))
{
if(ret <= 0)
{
printf("bmp_get : read data failed\r\n");
free(data);
fclose(fp);
return NULL;
}
}
//close
fclose(fp);
//
pic = (unsigned char *)calloc(1, totalSize);
memset(pic, 0, totalSize);
//根据图片方向拷贝数据
if(bi.biHeight > 0) //倒向 //上下翻转 + 左右翻转 + 像素字节顺序调整
{
for(i = 0, picCount = totalSize; i < totalSize + overLineBytesSum && picCount >= 0; )
{
picCount -= bi.biWidth*perW;
for(j = 0, perWCount = perW - 1; j < bi.biWidth*perW && i < totalSize + overLineBytesSum && picCount >= 0; j++)
{
pic[picCount + perWCount] = data[i++];
if(--perWCount < 0)
perWCount = perW - 1;
if(perWCount == perW - 1)
picCount += perW;
}
picCount -= bi.biWidth*perW;
i += overLineBytesNum;
}
}
else // 正向 //像素字节顺序调整
{
for(i = 0, j = 0, picCount = 0, perWCount = perW - 1; i < totalSize + overLineBytesSum && picCount < totalSize; )
{
pic[picCount + perWCount] = data[i++];
if(--perWCount < 0)
perWCount = perW - 1;
if(perWCount == perW - 1)
picCount += perW;
if(++j == bi.biWidth*perW)
{
j = 0;
i += overLineBytesNum;
}
}
}
//free
free(data);
//返回 宽, 高, 像素字节
if(picMaxSize)
*picMaxSize = totalSize;
if(width)
*width = bi.biWidth;
if(height)
{
if(bi.biHeight > 0)
*height = bi.biHeight;
else
*height = -bi.biHeight;
}
if(per)
*per = perW;
return pic;
}
//功能: 创建bmp格式图片
//参数: filePath: 传入, 文件地址
// data: 传入, 图片矩阵数据的指针
// width: 传入, 图片横向的像素个数
// height: 传入, 图片纵向的像素个数
// per: 传入, 图片每像素占用字节数
//返回: 创建的bmp图片文件的大小, -1表示创建失败
int bmp_create(char *filePath, unsigned char *data, int width, int height, int per)
{
FILE *fp;
int fileSize, fileSize2, count, headSize;
unsigned char *bmpData, *p;
int perWCount;
int i, j, picCount;
int overLineBytesNum;
unsigned int overLineBytesSum = 0;// overLineBytesNum : 行结尾补0字节数 //每行字节数规定为4的倍数, 不足将补0, 所以读行的时候注意跳过
//
if(width < 0)
{
printf("bmp_create : width < 0 , err !!\r\n");
return -1;
}
//
if((fp = fopen(filePath, "wb+")) < 0)
{
printf("bmp_create : create %s err\r\n", filePath);
return -1;
}
//
overLineBytesNum = width*per%4;
if(overLineBytesNum == 4)
overLineBytesNum = 0;
headSize = Bmp_FileHeader_Size + Bmp_Info_Size;
if(height < 0)
{
overLineBytesNum = overLineBytesNum*(-height);
fileSize2 = width*(-height)*per;
}
else
{
overLineBytesNum = overLineBytesNum*height;
fileSize2 = width*height*per;
}
fileSize = headSize + fileSize2;
bmpData = (unsigned char *)calloc(1, fileSize + overLineBytesNum);
//
count = 0;
//
bmpData[count++] = 'B'; //bfType
bmpData[count++] = 'M';
bmpData[count++] = (unsigned char)((fileSize>>0)&0xFF); //bfSize 低位在前
bmpData[count++] = (unsigned char)((fileSize>>8)&0xFF);
bmpData[count++] = (unsigned char)((fileSize>>16)&0xFF);
bmpData[count++] = (unsigned char)((fileSize>>24)&0xFF);
count++; //保留
count++;
count++;
count++;
bmpData[count++] = (unsigned char)((headSize>>0)&0xFF); //bfOffbits 低位在前
bmpData[count++] = (unsigned char)((headSize>>8)&0xFF);
bmpData[count++] = (unsigned char)((headSize>>16)&0xFF);
bmpData[count++] = (unsigned char)((headSize>>24)&0xFF);
bmpData[count++] = (unsigned char)((Bmp_Info_Size>>0)&0xFF); //biSize
bmpData[count++] = (unsigned char)((Bmp_Info_Size>>8)&0xFF);
bmpData[count++] = (unsigned char)((Bmp_Info_Size>>16)&0xFF);
bmpData[count++] = (unsigned char)((Bmp_Info_Size>>24)&0xFF);
bmpData[count++] = (unsigned char)((width>>0)&0xFF); //biWidth
bmpData[count++] = (unsigned char)((width>>8)&0xFF);
bmpData[count++] = (unsigned char)((width>>16)&0xFF);
bmpData[count++] = (unsigned char)((width>>24)&0xFF);
bmpData[count++] = (unsigned char)((height>>0)&0xFF); //biHeight
bmpData[count++] = (unsigned char)((height>>8)&0xFF);
bmpData[count++] = (unsigned char)((height>>16)&0xFF);
bmpData[count++] = (unsigned char)((height>>24)&0xFF);
bmpData[count++] = 0x01; //biPlanes
bmpData[count++] = 0x00;
bmpData[count++] = 24; //biBitCount
bmpData[count++] = 0;
bmpData[count++] = 0; //biCompression
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = (unsigned char)((fileSize2>>0)&0xFF); //biSizeImage
bmpData[count++] = (unsigned char)((fileSize2>>8)&0xFF);
bmpData[count++] = (unsigned char)((fileSize2>>16)&0xFF);
bmpData[count++] = (unsigned char)((fileSize2>>24)&0xFF);
bmpData[count++] = 0; //biXPelsPerMeter
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0; //biYPelsPerMeter
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0; //biClrUsed
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0; //biClrImportant
bmpData[count++] = 0;
bmpData[count++] = 0;
bmpData[count++] = 0;
//
p = &bmpData[count];
if(height >= 0) //倒向 //上下翻转 + 左右翻转 + 像素字节顺序调整
{
for(i = 0, picCount = fileSize2; i < fileSize2 + overLineBytesSum && picCount >= 0; )
{
picCount -= width*per;
for(j = 0, perWCount = per - 1; j < width*per && i < fileSize2 + overLineBytesSum && picCount >= 0; j++)
{
p[i++] = data[picCount + perWCount];
if(--perWCount < 0)
perWCount = per - 1;
if(perWCount == per - 1)
picCount += per;
}
picCount -= width*per;
i += overLineBytesNum;
}
}
else // 正向 //像素字节顺序调整
{
for(i = 0, j = 0, picCount = 0, perWCount = per - 1; i < fileSize2 + overLineBytesSum && picCount < fileSize2; )
{
p[i++] = data[picCount + perWCount];
if(--perWCount < 0)
perWCount = per - 1;
if(perWCount == per - 1)
picCount += per;
if(++j == width*per)
{
j = 0;
i += overLineBytesNum;
}
}
}
//
fileSize = fwrite(bmpData, 1, fileSize, fp);
free(bmpData);
fclose(fp);
//sync();
return fileSize;
}
bmp.h
#ifndef _BMP_H
#define _BMP_H
//bmp 图片数据获取
//filePath : 路径
//picMaxSize : 返回图片数据字节数, 不接收置NULL
//width : 返回图片宽(像素), 不接收置NULL
//height : 返回图片高(像素), 不接收置NULL
//per : 返回图片每像素的字节数, 不接收置NULL
//返回 : 图片数据指针, 已分配内存, 用完记得释放
unsigned char *bmp_get(char *filePath, int *picMaxSize, int *width, int *height, int *per);
//生成 bmp 图片
//filePath : 路径
//data : 原始数据
//width : 宽(像素)
//height : 高(像素)
//per : 每像素字节数
//返回 : 成功创建的 .bmp 文件大小, 小于0则失败
int bmp_create(char *filePath, unsigned char *data, int width, int height, int per);
#endif
main.c
#include <stdio.h>
#include <stdlib.h> //malloc free
#include "bmp.h"
//一张 宽100*高50*像素3字节 的RGB图片
#define BMP_WIDTH 100
#define BMP_HEIGHT 50
#define BMP_PERW 3
//第一个数值表示行数,从上往下数有多少行,自然就表示高度啦;
//第二个数值是列数,自然就表示宽啦
//所以在对坐标[x,y]定点时,应该这样调用 buff[y][x][..]
unsigned char buff[BMP_HEIGHT][BMP_WIDTH][BMP_PERW];
//读取图片用
unsigned char *buff2;
int width, height, perW, picSize;
//小伎俩,在已知图片宽高的情况下,可以利用像下面的类型定义的指针对数据整形,然后就可以方便的像三维数组一样定位数据了
typedef unsigned char MyMap[BMP_WIDTH][BMP_PERW];
MyMap *buff3;
int main(int argc, char *argv[])
{
int x,y;
//---------- 创建一张图片 ----------
//图片左半部涂成红色
for(x = 0; x < BMP_WIDTH/2; x++)
{
for(y = 0; y < BMP_HEIGHT; y++)
{
buff[y][x][0] = 0xff;
buff[y][x][1] = 0;
buff[y][x][2] = 0;
}
}
//图片右半部涂成绿色
for(x = BMP_WIDTH/2; x < BMP_WIDTH; x++)
{
for(y = 0; y < BMP_HEIGHT; y++)
{
buff[y][x][0] = 0;
buff[y][x][1] = 0xff;
buff[y][x][2] = 0;
}
}
//输出图片
bmp_create("test.bmp", (unsigned char *)buff, BMP_WIDTH, BMP_HEIGHT, BMP_PERW);
//---------- 读取一张图片 ----------
buff2 = bmp_get("test.bmp", &picSize, &width, &height, &perW); //后面那几个取址的参数是用来接收图片参数的,不接收的话可以置NULL
if(buff2) //图片读取失败的话会返回NULL
{
printf("bmp get success ! picSize/%d, width/%d, height/%d, perW/%d\r\n", picSize, width, height, perW);
if(width == BMP_WIDTH && height == BMP_HEIGHT && perW == BMP_PERW)
{
buff3 = (MyMap *)buff2; //转换类型,然后像三维数组一样定位图片数据
//图片左上角涂成白色
for(x = 0; x < width/2; x++)
{
for(y = 0; y < height/2; y++)
{
buff3[y][x][0] = 0xff;
buff3[y][x][1] = 0xff;
buff3[y][x][2] = 0xff;
}
}
//图片右下角涂成黑色
for(x = width/2; x < width; x++)
{
for(y = height/2; y < height; y++)
{
buff3[y][x][0] = 0;
buff3[y][x][1] = 0;
buff3[y][x][2] = 0;
}
}
//输出图片
bmp_create("test2.bmp", buff2, width, height, perW);
}
//注意!注意!注意! 用 bmp_get() 获得的数据指针是内存分配而来,用完一定要释放
free(buff2);
}
return 0;
}