在c中复制bmp
背景:
我想将bmp(未压缩的24 RGB)图像从一个文件名复制到另一个文件名。 我正在使用来自TDM-GCC(版本4.9.2,32位,SJLJ)的mingw编译器,它带有代码块。
问题:
程序适用于黑白图像和简单的彩色图像,但不适用于复杂的彩色图像。 请查看附件图片。 我没有足够的声誉来发布其他图片,所以我尝试发布最相关的2。 该程序无法复制lenna图像。 这种行为的原因是什么?
码:
#include #include #include #pragma pack(1) /* The following is to access the DIB information https://msdn.microsoft.com/en-us/library/cc230309.aspx https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx */ typedef uint8_t BYTE; typedef uint32_t DWORD; typedef int32_t LONG; typedef uint16_t WORD; typedef struct { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; }BITMAPFILEHEADER; typedef struct { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; }BITMAPINFOHEADER; typedef struct { BYTE rgbtBlue; BYTE rgbtGreen; BYTE rgbtRed; }RGBTRIPLE; int main(void) { char *infile = "testin.bmp"; char *outfile = "testout.bmp"; FILE *inptr = fopen(infile, "r"); if (inptr == NULL) { fprintf(stderr, "Could not open %s.\n", infile); return 2; } FILE *outptr = fopen(outfile, "w"); if (outptr == NULL) { fclose(inptr); fprintf(stderr, "Could not create %s.\n", outfile); return 3; } BITMAPFILEHEADER bf; fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); BITMAPINFOHEADER bi; fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr); fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr); int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; int i, j, k, biHeight; for(i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) { for(j = 0; j < bi.biWidth; j++) { RGBTRIPLE triple; fread(&triple, sizeof(RGBTRIPLE), 1, inptr); fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr); } } fseek(inptr, padding, SEEK_CUR); for(k = 0; k < padding; k++) { fputc(0x00, outptr); } fclose(inptr); fclose(outptr); return 0; }
输入图像:
输出图像:
我提供了一些提供的数据,我很确定发生了什么:
FILE *inptr = fopen(infile, "r");
和
FILE *outptr = fopen(outfile, "w");
以文本模式打开文件。
这是我从微软的C API中所知道的一种特殊行为,它似乎也适用于mingw TDM-GCC(我倾向于相信,直到倾销的数据使我确信我的怀疑是正确的)。
Microsoft C API中的文件I / O区分文本模式和二进制模式:
-
fopen(infile, "rt")
和fopen(outfile, "wt")
以文本模式打开文件。 -
fopen(infile, "rb")
和fopen(outfile, "wb")
以二进制模式打开文件。 -
fopen(infile, "r")
和fopen(outfile, "w")
默认为文本模式。
在文本模式下,文件读取用Unix行结尾( "\n"
)替换所有Microsoft行结尾( "\r\n"
)以及写入相反( "\n"
变为"\r\n"
)。
如果内容是纯文本,这是合理的,但它可能破坏二进制内容的输出,只要在数据流中出现字节0x0a
(具有任何含义),就会插入字节0x0d
。
为了certificate这一点,
- 我下载了您的示例文件(不幸的是以PNG格式上传)
- 将文件(返回)转换为24位BMP(使用GIMP)
-
为每个做了一个hex-dump:
$ hexdump -C IkW6FbN.bmp >IkW6FbN.bmp.txt $ hexdump -C jnxpTwE.bmp >jnxpTwE.bmp.txt
-
最后将
IkW6FbN.bmp.txt
和jnxpTwE.bmp.txt
加载到WinMerge中进行比较。
如快照所示,输入和输出文件具有与前14037(0x36d5)字节相同的内容。 然后,输入文件包含“意外”三个字节0a 0a 0a
,其中输出文件具有0d 0a 0d 0a 0d 0a
。 因此,各个原始像素(以及所有后续像素)被破坏。
(正如您可能已经猜到的那样, 0a
是换行符'\n'
的hex值, 0d
是回车符'\r'
。)
顺便说一句。 输出文件可能比输入文件稍长一些(由于插入的CR字节)。 BMP查看器可能会忽略这一点,因为BMP标头确切地说明了原始图像需要多少字节(并且简单地忽略了额外的字节)。
正如您已经认识到的那样,您应该将fopen()
调用更改为
FILE *inptr = fopen(infile, "rb");
和
FILE *outptr = fopen(outfile, "wb");
解决你的问题。
顺便说一句。 * x OS(例如Linux)上的C API没有文本和二进制模式的区别。 相反, b
被简单地忽略(这有助于编写可移植代码)。
进一步阅读: fopen,fopen_s在cppreference.com上