IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C/C++读写BMP文件 -> 正文阅读

[C++知识库]C/C++读写BMP文件

BMP文件格式

??读写BMP格式的图片需要首先了解BMP图片的存储格式。可以参考维基百科上的介绍。
??BMP文件主要有文件头(File Header)、信息头(DIB Header)、调色板(Color Table)和像素阵列(Pixel Array)组成。大部分情况下我们需要用的就是每个像素的数据。
??可以从图中观察到,文件头中的File Offset to PixelArray可以直接得知Pixel Array的位置。信息头中有图像的宽、高和位深度的信息。像素阵列中的数据是一行一行组织的,每行的长度都是4字节的整数倍,如果一行的像素大小不是4字节的整数倍,还会再后面加Padding。
在这里插入图片描述

BMP读写

??了解了BMP文件的格式后,就可以据此编写相应的读写函数了。我准备写两个函数分别负责读和写。如下面的头文件所示:

/**
 * @file bmpRw.h
 * @author Jiandong Qiu (1335521934@qq.com)
 * @brief 
 * @version 0.1
 * @date 2022-10-07
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#ifndef _BMPRW_H_
#define _BMPRW_H_

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief read bmp file, read data array into pData
 * user should allocate space outsize of function
 * 
 * @param fileName bmp file name
 * @param pData pre-allocated data pointer
 * @return int return 0 for OK
 */
int readBmp(char *fileName, void *pData);

/**
 * @brief write bmp file, user should prepare dstInfo and dstHead before
 * call this function, this function only support dedicated type of bmp file.
 * 
 * @param fileName bmp file name
 * @param pData bmp data
 * @return int return 0 for OK
 */
int writeBmp(char *fileName, void *pData);

#ifdef __cplusplus
}
#endif

#endif

??读BMP文件的函数可以通过指定图片名,将图片中的像素数据读到一个指针指向的地址空间中。用户需要确保这个地址空间足够大。
??写BMP文件的函数同样需要由用户指定文件名,然后将一个指针指向的一块连续的数据写到BMP文件中,考虑到实际使用中图片的分辨率一般不会发生变化,所以这个写BMP文件的函数只支持特定的分辨率大小,文件信息头是由固定的常量给出的。根据实际使用情况需要对源文件中的文件信息头dstInfo进行修改。
??下面是源文件的实现:

/**
 * @file bmpRw.c
 * @author Jiandong Qiu (1335521934@qq.com)
 * @brief 
 * @version 0.1
 * @date 2022-10-07
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include "bmpRw.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

typedef struct __attribute__((packed)) BITMAPFILEHEADER  
{   
    uint16_t bfType;   
    uint32_t bfSize;   
    uint16_t bfReserved1;   
    uint16_t bfReserved2;   
    uint32_t bfOffBits;   
}BITMAPFILEHEADER;   
 
typedef struct __attribute__((packed)) BITMAPINFOHEADER  
{   
    uint32_t biSize;   
    uint32_t biWidth;   
    uint32_t biHeight;   
    uint16_t biPlanes;   
    uint16_t biBitCount;   
    uint32_t biCompression;   
    uint32_t biSizeImage;   
    uint32_t biXPelsPerMeter;   
    uint32_t biYPelsPerMeter;   
    uint32_t biClrUsed;   
    uint32_t biClrImportant;   
}BITMAPINFOHEADER;

const BITMAPFILEHEADER dstHead = {
    .bfType = 19778,
    .bfSize = 766136,
    .bfReserved1 = 0,
    .bfReserved2 = 0,
    .bfOffBits = 54};

const BITMAPINFOHEADER dstInfo = {
    .biSize = 40,
    .biWidth = 639,
    .biHeight = 399,
    .biPlanes = 1,
    .biBitCount = 24,
    .biCompression = 0,
    .biSizeImage = 766082,
    .biXPelsPerMeter = 3779,
    .biYPelsPerMeter = 3779,
    .biClrUsed = 0,
    .biClrImportant = 0};

/**
 * @brief read bmp file, read data array into pData
 * user should allocate space outsize of function
 * 
 * @param fileName bmp file name
 * @param pData pre-allocated data pointer
 * @return int return 0 for OK
 */
int readBmp(char *fileName, void *pData)
{
    if(!fileName || !pData){
        return -1;
    }
    int i;
    BITMAPFILEHEADER head;
    BITMAPINFOHEADER info;
    size_t nElemSize;
    size_t nPaddingSize;

    FILE *fp = fopen(fileName, "rb");
    if (fp == NULL){
        return -1;
    }

    fread(&head, sizeof(BITMAPFILEHEADER), 1, fp);
    fread(&info, sizeof(BITMAPINFOHEADER), 1, fp);

    nElemSize = info.biBitCount / 8;
    nPaddingSize = ((info.biWidth * nElemSize + 3) & (size_t)-4) - info.biWidth * nElemSize;

    // move fp to data array
    fseek(fp, head.bfOffBits, SEEK_SET);
    for (i = info.biHeight - 1; i >= 0; --i){
        fread(pData + i * info.biWidth * nElemSize, nElemSize, info.biWidth, fp);
        // skip padding
        fseek(fp, nPaddingSize, SEEK_CUR);
    }

    fclose(fp);
    return 0;
}

/**
 * @brief write bmp file, user should prepare dstInfo and dstHead before
 * call this function, this function only support dedicated type of bmp file.
 * 
 * @param fileName bmp file name
 * @param pData bmp data
 * @return int return 0 for OK
 */
int writeBmp(char *fileName, void *pData)
{
    if (!fileName || !pData) {
        return -1;
    }

    int i;
    size_t nElemSize;
    size_t nPaddingSize;
    FILE *fp = fopen(fileName, "wb");
    if (fp == NULL) {
        return -1;
    }

    fwrite(&dstHead, 1, sizeof(BITMAPFILEHEADER), fp);
    fwrite(&dstInfo, 1, sizeof(BITMAPINFOHEADER), fp);

    nElemSize = dstInfo.biBitCount / 8;
    nPaddingSize = ((dstInfo.biWidth * nElemSize + 3) & (size_t)-4) - dstInfo.biWidth * nElemSize;
    uint8_t *pPadding = (uint8_t *)malloc(sizeof(uint8_t) * nPaddingSize);
    if(!pPadding)
        return -1;
    memset(pPadding, 0, sizeof(uint8_t) * nPaddingSize);

    for (i = dstInfo.biHeight - 1; i >= 0; --i) {
        fwrite(pData + i * dstInfo.biWidth * nElemSize, nElemSize, dstInfo.biWidth, fp);
        // add padding
        fwrite(pPadding, 1, nPaddingSize, fp);
    }

    fclose(fp);

    free(pPadding);
    pPadding = NULL;

    return 0;
}

??需要注意的地方:

  • 结构体类型定义需要带上packed属性,这样结构体中的字段才能和真正的BMP文件存储格式对应上;
  • 读写每行像素时,习惯上的第一行,实际上是存在文件中的最后一行,所以在进行读写操作时,循环变量i是从大到小变化;
  • 每行像素有可能会有padding,所以在读写的时候也需要对padding进行处理。
  • 写BMP文件时,要确认文件信息头是否正确。在不知道文件信息头应该是什么样的情况时,可以先读一张同样大小的图片,然后把信息头保存下来。

BMP读写测试

/**
 * @file main.c
 * @author Jiandong Qiu (1335521934@qq.com)
 * @brief 
 * @version 0.1
 * @date 2022-10-07
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include "bmpRw.h"
#include <stdint.h>
#include <stdlib.h>

typedef uint8_t Pixel[3];

#define WIDTH (639)
#define HEIGHT (399)

int main()
{
    Pixel *pData = (Pixel *)malloc(sizeof(Pixel) * WIDTH * HEIGHT);
    if(!pData)
        return -1;
    readBmp("Test.bmp", pData);
    writeBmp("Out.bmp", pData);

    return 0;
}

??最后在windows和Ubuntu下分别对639x399的RGB BMP图片进行读写测试。程序能够读取图片数据并正确写回。

Windows MinGW gcc
在这里插入图片描述
Ubuntu 环境信息
在这里插入图片描述
测试结果:
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:22:11  更:2022-10-08 20:22:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 3:31:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码