BMP

BMP

BMP文件:

bmp文件主要包含4个部分,分别为文件头,信息头,调色板(大多数没有),图像数据。

一、文件头

文件头描述了该文件的信息,定义如下:

#pragma  pack (push, 1)
struct _file_header {
    unsigned short      fileType;
    unsigned int        fileSize;
    unsigned short      fileReserved1;
    unsigned short      fileReserved2;
    unsigned int        offset;
}; //14个字节
#pragma pack(pop)

结构体的布局必须是紧凑的,也就是不需要做对齐。各字段含义如下:

  1. fileType: BMP文件的标识,一般就是'B', 'M'两个字符,对应16进制为'0x4d42',vFileHeader->fileType = 0x4d42;

  2. fileSize: 整个文件的大小,4个部分的Byte数总和

  3. fileReserved1: 保留字段

  4. fileReserved2: 保留字段

  5. offset: 图像数据的偏移位置,通过fseek到图像数据位置再读取数据

二、信息头

信息头描述了图像的一些基本信息,如下:

struct _info_header {
	unsigned int      infoSize;
	unsigned int      imageWidth;
	int      imageHeight;
	unsigned short      imagePlane;
	unsigned short      imageCount;
	unsigned int      imageCompression;
	unsigned int      imageSize;
	unsigned int      hResolution;
	unsigned int      vResolution;
	unsigned int      clrUsed;
	unsigned int      clrImportant;
}; //40个字节
  • infoSize: 信息头大小,vInfoHeader->infoSize = sizeof(struct _info_header);
  • imageWidth: 图像的宽
  • imageHeight: 图像的高,注意这里是int,有正负之分。如是>0,表示各行的数据是逆向的(第1行数据存放在最后一行);如果<0,表示各行数据是顺序存放的
  • imagePlane: 颜色平面数,总设为1
  • imageCount: 一个像素所占比特数,可以设为8(灰度), 16, 24(RGB), 32(RGBA)
  • imageCompression: 图像压缩类型,可以设为0,不压缩
  • imageSize: 图像数据大小(字节)
  • hResolution: 水平分辨率, 像素/米,可设为0
  • vResolution: 垂直分辨率, 像素/米,可设为0
  • clrUsed: 说明位图使用的颜色索引数
  • clrImportant: 表示对图像显示有重要影响的颜色索引数目

三、图像数据

1. RGB顺序

注意是BGR存放。如果包含alpha通道,存放顺序是BGRA。

2. 每行数据需要做对齐:

每行的图像数据需要做4字节对齐。一行的字节数可以通过如下公式计算:

int line_size = (width * channel + 3) / 4 * 4;

假如width为301, channel为3, 实际数据大小为301*3 = 903字节,需要数据大小(301 * 3 + 3) / 4 * 4 = 904字节。

3. 图像数据的存放顺序:

  1. 顺序存放, 要求height为负数,imageHeight = -480

    第1行数据
    第2行数据
    .
    .
    .
    第n-1行数据
    第n行数据

  2. 逆序存放, 要求height为正数,imageHeight = 480

    第n行数据
    第n-1行数据
    .
    .
    .
    第2行数据
    第1行数据

四、例子

保存bmp文件:

#pragma pack(1)
struct _file_header {
    unsigned short      fileType;
    unsigned int        fileSize;
    unsigned short      fileReserved1;
    unsigned short      fileReserved2;
    unsigned int        offset;
};
#pragma pack(4)

struct _info_header {
    unsigned int      infoSize;
    unsigned int      imageWidth;
    int      imageHeight;
    unsigned short      imagePlane;
    unsigned short      imageCount;
    unsigned int      imageCompression;
    unsigned int      imageSize;
    unsigned int      hResolution;
    unsigned int      vResolution;
    unsigned int      clrUsed;
    unsigned int      clrImportant;
};

static int writeBMP(FILE *desFile, unsigned char *inputBuf,
        unsigned int width, unsigned int height, int cn)
{
    if (cn != 1 && cn != 4) {
        printf("only support channel 1 and 4\n");
        return -1;
    }

    if (cn == 1) {
        struct _file_header *vFileHeader = NULL;
        struct _info_header *vInfoHeader = NULL;
        unsigned char *palleteData = NULL;
        unsigned char *imageData = NULL;
        unsigned char *bmpBuffer = NULL;

        int w = (width + 3) / 4 * 4;

        unsigned int imgSize = w * height;
        unsigned int headerSize = 1024 + sizeof(struct _file_header)
            + sizeof(struct _info_header);
        unsigned int fileSize = headerSize + imgSize;

        unsigned int i, val, length = 1 << 8;

        bmpBuffer = (unsigned char *)malloc(fileSize);
        if (!bmpBuffer) {
            printf("invalid buffer\n");
            return -1;
        }

        vFileHeader = (struct _file_header *)bmpBuffer;
        vInfoHeader = (struct _info_header *)(bmpBuffer
                + sizeof(struct _file_header));
        palleteData = bmpBuffer + sizeof(struct _file_header)
            + sizeof(struct _info_header);
        imageData = bmpBuffer + headerSize;

        // assign vFileHeader & vInfoHeader
        vFileHeader->fileType = 0x4d42;
        vFileHeader->fileSize = (unsigned int)fileSize;
        vFileHeader->fileReserved1 = 0;
        vFileHeader->fileReserved2 = 0;
        vFileHeader->offset = headerSize;

        //vFileHeader->fileOffset1 = (unsigned short)
        //    ((headerSize << 16) >> 16);
        //vFileHeader->fileOffset2 = (unsigned short)(headerSize >> 16);

        vInfoHeader->infoSize = sizeof(struct _info_header);
        vInfoHeader->imageWidth = width;
        vInfoHeader->imageHeight = -(int)height;
        vInfoHeader->imagePlane = 1;
        vInfoHeader->imageCount = 8;
        vInfoHeader->imageCompression = 0;
        vInfoHeader->imageSize = 0;
        vInfoHeader->hResolution = 0;
        vInfoHeader->vResolution = 0;
        vInfoHeader->clrUsed = 0;
        vInfoHeader->clrImportant = 0;

        for (i = 0; i < length; i++) {
            val = (i * 255/(length - 1)) ^ 0;
            palleteData[0] = (unsigned char)val;
            palleteData[1] = (unsigned char)val;
            palleteData[2] = (unsigned char)val;
            palleteData[3] = (unsigned char)0;
            palleteData += 4;
        }


        for (i = 0; i < height; ++i)
            memcpy(imageData + i * w, inputBuf + width * i, width);

        // write desFile
        if (desFile == NULL) {
            if (bmpBuffer != NULL) {
                free(bmpBuffer);
                bmpBuffer = NULL;
            }

            printf("desFile is NULL\n");
            return -1;
        }

        fwrite(bmpBuffer, fileSize, 1, desFile);
        if (bmpBuffer != NULL) {
            free(bmpBuffer);
            bmpBuffer = NULL;
        }

    } else if (cn == 4) {
        struct _file_header *vFileHeader = NULL;
        struct _info_header *vInfoHeader = NULL;
        unsigned char *imageData = NULL;
        unsigned char *bmpBuffer = NULL;

        unsigned int imgSize = width * height;
        unsigned int fileSize = sizeof(struct _file_header)
            + sizeof(struct _info_header) + imgSize * 4;

        bmpBuffer = (unsigned char *)malloc(fileSize);
        if (bmpBuffer == NULL) {
            printf("malloc bmpBuffer failed\n");
            return -1;
        }

        vFileHeader = (struct _file_header *)bmpBuffer;
        vInfoHeader = (struct _info_header *)(bmpBuffer
                + sizeof(struct _file_header));
        imageData = bmpBuffer + sizeof(struct _file_header)
            + sizeof(struct _info_header);

        // assign vFileHeader & vInfoHeader
        vFileHeader->fileType = 0x4d42;
        vFileHeader->fileSize = (unsigned int)fileSize;
        vFileHeader->fileReserved1 = 0;
        vFileHeader->fileReserved2 = 0;
        vFileHeader->offset = sizeof(struct _file_header)
            + sizeof(struct _info_header);

        vInfoHeader->infoSize = sizeof(struct _info_header);
        vInfoHeader->imageWidth = width;
        vInfoHeader->imageHeight = -((int)height);
        vInfoHeader->imagePlane = 1;
        vInfoHeader->imageCount = 32;
        vInfoHeader->imageCompression = 0;
        vInfoHeader->imageSize = imgSize * 4;
        vInfoHeader->hResolution = 0;
        vInfoHeader->vResolution = 0;
        vInfoHeader->clrUsed = 0;
        vInfoHeader->clrImportant = 0;

        // assign image data
        memcpy(imageData, inputBuf, imgSize * 4);

        // write desFile
        if (desFile == NULL) {
            if (bmpBuffer != NULL) {
                free(bmpBuffer);
                bmpBuffer = NULL;
            }

            printf("desFile is NULL\n");
            return -1;
        }

        fwrite(bmpBuffer, fileSize, 1, desFile);
        if (bmpBuffer != NULL) {
            free(bmpBuffer);
            bmpBuffer = NULL;
        }
    }

    return 0;
}

读bmp:

void *read_bmp_alloc(const char *path,
        uint8_t *mem, int *bmpWidth, int *bmpHeight)
{
    FILE *fp = fopen(path, "rb");
    printf("fp : %p\n", fp);
    printf("open %s\n", path);
    struct _file_header fh;
    struct _info_header ih;
    int ret;
    int w;
    int h;
    int c;
    uint8_t *buf = NULL;
    int i, j, k;
    int j2;
    int size;

    ret = fread(&fh, 1, sizeof(fh), fp);
    ret = fread(&ih, 1, sizeof(ih), fp);
    w = *bmpWidth = ih.imageWidth;
    h = *bmpHeight = abs(ih.imageHeight);
    c = ih.imageCount/8;

    if (c != 3 && c != 1 && c != 4) {
        printf("only support rgb bmp!\n");
        ret = -1;
        goto exit;
    }

    size = get_file_size(path);
    buf = (uint8_t *)malloc(size - fh.offset);
    fseek(fp, fh.offset, SEEK_SET);
    if (!buf) {
        printf("out of memory!\n");
        ret = -1;
        goto exit;
    }
    fread(buf, 1, size - fh.offset, fp);


    mem = (uint8_t *)malloc(w * h * 3);
    if (!mem)
        goto exit;

    uint8_t *p = (uint8_t *)mem;
    uint8_t *b = (uint8_t *)buf;

    if (c == 1) {
        int wid_size = (w + 3) / 4 * 4;
        for (j = 0; j < h; ++j) {
            j2 = j;
            if (ih.imageHeight > 0)
                j2 = h - 1 - j;
            for (k = 0; k < w; ++k) {
                for (int i = 0; i < 3; ++i) {
                    p[i * h * w + j2 * w + k] = b[k];
                }
            }
            b += wid_size;
        }
    } else if (c == 3) {
        int wid_size = (w * 3 + 3) / 4 * 4;

        for (j = 0; j < h; ++j) {
            j2 = j;
            if (ih.imageHeight > 0)
                j2 = h - 1 - j;
            for (k = 0; k < w; ++k) {
                for (int i = 0; i < 3; ++i) {
                    p[i * h * w + j2 * w + k]
                        = b[k * c + i];
                }
            }
            b += wid_size;
        }
    } else if (c == 4) {
        for (j = 0; j < h; ++j) {
            j2 = j;
            if (ih.imageHeight > 0)
                j2 = h - 1 - j;  //处理行顺序
                
            for (k = 0; k < w; ++k) {
                for (i = 0; i < 3; ++i) {
                    p[i * h * w + j2 * w + k]
                        = b[k * c + i];
                }
            }
            b += w*4;  //处理对齐
        }
    }

exit:
    if (fp)
        fclose(fp);
    if (buf)
        free(buf);
    return p;
}
posted @ 2022-01-07 14:04  bairuiworld  阅读(683)  评论(0编辑  收藏  举报