文章摘要: 本文主要描述了BMP/JPG解码及显示的示例。
操作系统: Debian


BMP数据结构:

// BMP文件头(54字节),其中文件头14字节,图像信息40字节
#pragma pack(1) //设置内存按照1个字节对齐
typedef struct
{
    char bfType[2];           // 00 文件类型: 固定为BM
    uint32_t bfSize;          // 02 文件大小:单位字节
    uint16_t bfReserved1;     // 06 保留字段
    uint16_t bfReserved2;     // 08 保留字段
    uint32_t bfOffBits;       // 10 数据偏移
    uint32_t biSize;          // 14 信息结构长度
    uint32_t biWidth;         // 18 图像宽度
    uint32_t biHeight;        // 22 图像高度
    uint16_t biPlanes;        // 26 颜色面板数(总是为1)
    uint16_t biBitCount;      // 28 像素位数
    uint32_t biCompression;   // 30 压缩格式
    uint32_t biSizeImage;     // 34 图像内容大小
    uint32_t biXPelsPerMeter; // 38 水平分辨率 像素/米
    uint32_t biYPelsPerMeter; // 42 竖直分辨率 像素/米
    uint32_t biClrUsed;       // 46 调色板数量
    uint32_t biClrImportant;  // 50 关键色彩数
} BITMAPINFOHEADER, bmp_info_t;
#pragma pack()

BMP文件解析:

bmp_info_t* bmp_info = (void*)&buff[0];
// 正常情况是读出54个字节
fread(buff, 54, 1, fp);
dump_data(buff, 54);
printf("文件类型: %c%c\r\n",     bmp_info->bfType[0], bmp_info->bfType[1]);
printf("文件大小: %d 字节\r\n",  bmp_info->bfSize);
printf("数据偏移: %d\r\n",       bmp_info->bfOffBits);
printf("信息长度: %d\r\n",       bmp_info->biSize);
printf("图像尺寸: %dx%d\r\n",    bmp_info->biWidth, bmp_info->biHeight);
printf("像素位数: %d bits\r\n",  bmp_info->biBitCount);
printf("压缩格式: %d\r\n",       bmp_info->biCompression);
printf("图像内容: %d 字节\r\n",  bmp_info->biSizeImage);    
printf("分 辨 率: %d,%d\r\n",    bmp_info->biXPelsPerMeter, bmp_info->biXPelsPerMeter);
printf("调色板数: %d\r\n\r\n",   bmp_info->biClrUsed);
// 如果文件头大于54个字节,需要读出其他数据
if(bmp_info->bfOffBits > 54)
{
    fread(buff, bmp_info->bfOffBits - 54, 1, fp);
}
// 申请读取一行数据需要的内存    
int line_size = bmp_info->biWidth * bmp_info->biBitCount / 8;

// BMP文件和每行长度必须以4字节对齐,不足部分填充0,所以必须全部读出来  
// 否则显示会错位 
line_size  = (line_size + 3 )/ 4 * 4;    
     
char *pline = malloc(line_size);
if (pline == NULL)
{
    perror("内存分配失败");
    exit(-2);
}  
//----------------------------
int xcnt;
int ycnt;
int offset; // 偏移
char *p2;
char *p3;

// 屏幕和图像尺寸取最小值
xcnt = (bmp_info->biWidth  < vinfo.xres)?bmp_info->biWidth :vinfo.xres;
ycnt = (bmp_info->biHeight < vinfo.yres)?bmp_info->biHeight:vinfo.yres;
while (ycnt--)
{    
    // 从文件中读一行数据出来    
    fread((char *)pline, 1, line_size, fp);     
    // 计算偏移值
    offset = (ycnt) * finfo.line_length; 
    p2 = (char *)(pfb + offset);
    p3 = pline;        
    for (j = 0; j < xcnt; j++)
    {
        *p2++ = *p3++;    // 先按24bit显示
        *p2++ = *p3++;
        *p2++ = *p3++;
        *p2++ = 0;
    }
}

JPG图像解码:
需要添加libjpeg-dev支持;
编译选项:-ljpeg

// 指定图片的路径就可以调用这个jpg的解析
// output_width:      图像的输出宽度
// output_height:    图像的输出高度
// output_component: 每个像素的分量数,也即字节数(3或4字节)
int jpg_display(char *path)
{
    // 1. 分配并初始化一个jpeg解压对象
    struct jpeg_decompress_struct dinfo;
    struct jpeg_error_mgr jerr;        // 错误变量
    dinfo.err = jpeg_std_error(&jerr); //
    jpeg_create_decompress(&dinfo);    // 初始化

    // 2. 打开JPG图像文件
    FILE *infile;
    infile = fopen(path, "r");
    if (infile == NULL)
    {
        perror("fopen error");
        return -1;
    }
    jpeg_stdio_src(&dinfo, infile); //为解压对象dinfo指定要解压的文件

    // 3. 获取图像信息
    jpeg_read_header(&dinfo, TRUE);    
    //-------------------------------------------------
    // 4. 设置jpeg解压缩对象dinfo的一些参数,可采用默认参数
    //-------------------------------------------------
    // 5. 解码文件头
    jpeg_start_decompress(&dinfo);
    printf("图像信息: %dx%d %d\r\n", dinfo.output_width, dinfo.output_height, dinfo.output_components);
    //-----------------------------------------------------
    // 6. 读取一行或者多行扫描线数据并处理
    // 分配内存以便读入数据
    unsigned char *line_buff = malloc(dinfo.output_width * dinfo.output_components);
    if(line_buff == NULL)
    {
        perror("malloc failed");
        exit(1);
    }    
    while (dinfo.output_scanline < dinfo.output_height)
    {
       // 读取一行数据并解码(output_scanlinee表示当前行数,会自动增加)
       jpeg_read_scanlines(&dinfo, &line_buff, 1);
       // 此处加入数据后续处理 
       // BMP的顺序为BGR, 显示器顺序为BGRA, JPG的顺序为RGB      
       // 如果直接输出为图片,则显示为上下翻转且颜色也不对(BMP本身的问题)       
    }
    
    // 完成解压过程
    jpeg_finish_decompress(&dinfo);

    // 释放内容
    jpeg_destroy_decompress(&dinfo);
    return 0;
}

调整图像(输出BMP格式文件):

int sp;
int offset;        
unsigned char *file_buff = malloc(line_size * dinfo.output_height);
unsigned char *line_buff = malloc(line_size);
if((file_buff == NULL) || (line_buff == NULL))
{
    exit(1);
}
while (dinfo.output_scanline < dinfo.output_height)
{        
    offset = (dinfo.output_height - dinfo.output_scanline - 1) * line_size;
    unsigned char *p = file_buff + offset;
    jpeg_read_scanlines(&dinfo, &line_buff, 1);
    sp = 0;
    for (int i = 0; i < dinfo.output_width; i++)
    {
        *p++ = line_buff[sp+2];
        *p++ = line_buff[sp+1];
        *p++ = line_buff[sp];
        sp  += dinfo.output_components;
    }        
} 
   
FILE* bmp_fp = bmp_file_init(&dinfo);
fwrite(file_buff,1,dinfo.output_width*dinfo.output_components*dinfo.output_height,bmp_fp);
fclose(bmp_fp);

BMP文件头处理:

FILE* bmp_file_init(struct jpeg_decompress_struct* j)
{
    FILE *fp;
    bmp_info_t bmp_info;
    long int img_size = j->output_width * j->output_height * j->output_components;

    bmp_info.bfType[0] = 'B';
    bmp_info.bfType[1] = 'M';
    bmp_info.bfSize = img_size + 54;      // 文件大小
    bmp_info.bfReserved1 = 0;             // 06 保留
    bmp_info.bfReserved2 = 0;             // 08 保留
    bmp_info.bfOffBits = 54;              // 10 数据偏移(一般都为54)
    bmp_info.biSize = 40;                 // 14 信息结构长度
    bmp_info.biWidth = j->output_width;   // 18 图像宽度
    bmp_info.biHeight = j->output_height; // 22 图像高度
    bmp_info.biPlanes = 1;                // 26 颜色面板数(总是为1)
    bmp_info.biBitCount = 24;             // 28 像素位数
    bmp_info.biCompression = 0;           // 30 压缩格式
    bmp_info.biSizeImage = img_size;      // 34 图像内容大小
    bmp_info.biXPelsPerMeter = 1024;      // 38 水平分辨率 像素/米
    bmp_info.biYPelsPerMeter = 1024;      // 42 竖直分辨率 像素/米
    bmp_info.biClrUsed = 0;               // 46 调色板数量
    bmp_info.biClrImportant = 0;          // 50 关键色彩数

    fp = fopen("output.bmp", "wb");
    if(fp == NULL)
    {
        perror("Open Bmp File Failed");
        exit(-1);
    }
    fwrite(&bmp_info,1,54,fp);
    
    return fp;
}