测试通过的C代码:平台无关的RGB保存为BMP

根据工作要求,得到视频数据。那么怎么证明视频数据是对的?转为BMP最简单了。这样一个想法,折腾了吾很久,找到了快10种代码,结果都不能用。后来终于找到了一个可用的(代码是部分的),看到保存出来的BMP图片,终于松了一口气。

个人发现关键地方有:

1、文件头。

2、文件头的对齐。

3、每行数据的对齐。

取之于网络,用之于网络,吾将代码公布出来:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef unsigned char  	BYTE;
typedef unsigned short  WORD;
typedef unsigned int  	DWORD;
typedef int  		LONG;

#define BITS_PER_PIXCEL 24

/** 必须对齐,所以用这个来对齐 */
#pragma pack(1)

typedef struct
{
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
} BMP_FILE_HEADER;
 
typedef struct{
    DWORD      biSize;
    LONG       biWidth;
    LONG       biHeight;
    WORD       biPlanes;
    WORD       biBitCount;
    DWORD      biCompression;
    DWORD      biSizeImage;
    LONG       biXPelsPerMeter;
    LONG       biYPelsPerMeter;
    DWORD      biClrUsed;
    DWORD      biClrImportant;
} BMP_INFO_HEADER;

#pragma pack()


int rgbaToBmpFile(char *pFileName, unsigned char* pRgbaData, int nWidth , int nHeight)
{
    BMP_FILE_HEADER bmpHeader;
    BMP_INFO_HEADER bmpInfo;

    FILE* fp         = NULL;
    char* pBmpSource = NULL;
    char* pBmpData   = NULL;

    int i = 0, j=0;

    //4字节对齐。每一行必须是4的倍数。
    int bytesPerLine = (nWidth*BITS_PER_PIXCEL+31)/32*4;
    int pixcelBytes  = bytesPerLine*nHeight;

    bmpHeader.bfType        = 0x4D42;
    bmpHeader.bfReserved1   = 0;
    bmpHeader.bfReserved2   = 0;
    bmpHeader.bfOffBits     = sizeof(BMP_FILE_HEADER) + sizeof(BMP_INFO_HEADER);
    bmpHeader.bfSize        = bmpHeader.bfOffBits     + pixcelBytes;
  
    bmpInfo.biSize          = sizeof(BMP_INFO_HEADER);
    bmpInfo.biWidth         = nWidth;
    /** 这样图片才不会倒置 */
    bmpInfo.biHeight        = -nHeight; 
    bmpInfo.biPlanes        = 1;
    bmpInfo.biBitCount      = BITS_PER_PIXCEL;
    bmpInfo.biCompression   = 0;
    bmpInfo.biSizeImage     = pixcelBytes;
    bmpInfo.biXPelsPerMeter = 100;
    bmpInfo.biYPelsPerMeter = 100;
    bmpInfo.biClrUsed       = 0;
    bmpInfo.biClrImportant  = 0;
 

    /** 先在内存中进行转换,完成之后,写到文件中。*/
    pBmpSource = malloc(pixcelBytes);
    if (!pBmpSource)
    {
        return -1;
    }

    /** 打开文件 */
    fp = fopen(pFileName,"wb+");
    if (!fp)
    {
        return -1;
    }

    fwrite(&bmpHeader, sizeof(BMP_FILE_HEADER), 1, fp);
    fwrite(&bmpInfo,   sizeof(BMP_INFO_HEADER), 1, fp);
    /** 此处根据传递的颜色格式进行处理。这里的处理逻辑是:
        传入的颜色格式是RGBA。
        写到文件的格式BGR(否则会红蓝颠倒)*/
    pBmpData = pBmpSource;
    for (i=0; i<nHeight; i++)
    {
        for (j=0; j<nWidth; j++)
        {
           pBmpData[0] = pRgbaData[2];
           pBmpData[1] = pRgbaData[1];
           pBmpData[2] = pRgbaData[0];
	   pRgbaData += 4;
           pBmpData  += 3;
        }
        //必须4字节对齐,否则会显示错误。
        pBmpData +=(bytesPerLine - nWidth*3);
    }
    fwrite(pBmpSource, pixcelBytes, 1, fp);

    /** 清理资源。 */
    fclose(fp);  
    free(pBmpSource);

    return 0;
}

如果要剪切一部分呢?代码如下:


int clipRgbaToBmpFile(char *pFileName, unsigned char* pRgbaData, int nWidth , int nHeight,
    int nClipLeft, int nClipTop, int nClipWidth, int nClipHeight)
{
    char* pClipSource     = NULL;
    char* pClipData       = NULL;
    int pixcelBytes       = nClipWidth*nClipHeight*4;
    int i = 0;

    pClipSource = malloc(pixcelBytes);
    if (!pClipSource)
    {
        return -1;
    }

    //移动到制定位置
    pRgbaData += nClipTop * nWidth * 4;
    pRgbaData += nClipLeft * 4;

    pClipData = pClipSource;
    for (i=0; i<nClipHeight; i++)
    {
        memcpy(pClipData, pRgbaData, nClipWidth*4);
        pRgbaData += nWidth    * 4;
        pClipData += nClipWidth* 4;
    }

    rgbaToBmpFile(pFileName, pClipSource, nClipWidth, nClipHeight);

    //释放资源
    free(pClipSource);

    return 0;
}

祝愿大家身体健康,工作顺利。

猜你喜欢

转载自blog.csdn.net/quantum7/article/details/82114750