背景:
我想从一个文件名复制一个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;

}

输入图像:
c - 在c中复制bmp-LMLPHP
输出图像:
c - 在c中复制bmp-LMLPHP

最佳答案

我稍微修改了一下提供的数据,我很确定发生了什么:

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.txtjnxpTwE.bmp.txt加载到WinMerge中进行比较。
c - 在c中复制bmp-LMLPHP
如快照所示,输入和输出文件对于前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/

10-15 02:16