背景:
我想从一个文件名复制一个bmp(未压缩24 RGB)图像到另一个文件名。我正在使用TDM-GCC(版本4.9.2,32位,SJLJ)中的mingw编译器,它附带了代码块。
问题:
程序适用于黑白图像和简单的彩色图像,但不适用于复杂的彩色图像。请查看所附图片。我没有足够的声誉张贴其他图片,所以我试图张贴2个最相关的。程序无法复制lenna图像。这种行为的原因是什么?
代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#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行结尾(
"\r\n"
)替换所有Microsoft行结尾("\n"
),而写入则相反("\n"
变为"\r\n"
)。如果内容是纯文本,这是合理的,但它可能会损坏二进制内容的输出,其中每当数据流中出现字节
0x0d
(具有任何意义)时,都会插入字节0x0a
。为了证明这一点,
我下载了你的示例文件(不幸的是以PNG格式上传)
已将文件(返回)转换为24位BMP(使用GIMP)
为每一个生成一个十六进制转储:
$ 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'
是回车符之一)顺便说一下,输出文件可能比输入文件长一点(因为插入了CR字节)。BMP查看器可能会忽略这一点,因为BMP头会精确地说明原始图像所需的字节数(多余的字节也会被忽略)。
您可能已经意识到应该将
0d
调用更改为FILE *inptr = fopen(infile, "rb");
和
FILE *outptr = fopen(outfile, "wb");
来解决你的问题。
顺便说一下,*x操作系统(例如Linux)上的C api没有文本模式和二进制模式的区别。相反,只需忽略
'\r'
(这有助于编写可移植代码)。进一步阅读:fopen, fopen_s on cppreference.com
关于c - 在c中复制bmp,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46459517/