c语言实现bmp格式的图片创建和读取(仅限24位色格式)

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;
}

转载

猜你喜欢

转载自blog.csdn.net/W_s_j/article/details/113862587