【问题标题】:Copying a bmp in c在c中复制bmp
【发布时间】:2018-03-09 15:05:45
【问题描述】:

背景:
我想将 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;

}

输入图像:

输出图像:

【问题讨论】:

  • 这将是 MSC 中的一个问题 - 关于 Windows 上的 gcc 我不确定。但是,请尝试fopen(infile, "rb")fopen(outfile, "wb") 并报告这是否会改变任何内容。 (充其量,它解决了问题,至少它不会改变任何东西。)
  • 这段代码有很多问题。你只是巧合地让它工作。您的目标仅仅是复制位图吗?还是提取信息位图信息?如果你只是想复制,有一个更简单的解决方案。

标签: c copy bmp


【解决方案1】:

我对所提供的数据做了一点改动,我很确定发生了什么:

FILE *inptr = fopen(infile, "r");

FILE *outptr = fopen(outfile, "w");

以文本模式打开文件。

这是我从 Microsoft 的 C API 中了解到的一种特殊行为,似乎这也适用于 mingw TDM-GCC(我一直难以相信,直到倾倒的数据让我相信我的怀疑是正确的)。

微软C API中的文件I/O区分文本模式和二进制模式:

  • fopen(infile, "rt")fopen(outfile, "wt") 以文本模式打开文件。
  • fopen(infile, "rb")fopen(outfile, "wb") 以二进制模式打开文件。
  • fopen(infile, "r")fopen(outfile, "w") 默认为文本模式。

在文本模式下,文件读取将所有 Microsoft 行尾 ("\r\n") 替换为 Unix 行尾 ("\n"),而写入则相反("\n" 变为 "\r\n")。

如果内容是纯文本,这是合理的,但它可能会破坏二进制内容的输出,其中每当数据流中出现字节 0x0a(任何含义)时都会插入字节 0x0d

为了证明这一点,

  1. 我下载了您的示例文件(很遗憾以 PNG 格式上传)
  2. 将文件(返回)转换为 24 位 BMP(使用 GIMP)
  3. 为每个生成一个十六进制转储:

    $ hexdump -C IkW6FbN.bmp >IkW6FbN.bmp.txt
    
    $ hexdump -C jnxpTwE.bmp >jnxpTwE.bmp.txt
    
  4. 最后将IkW6FbN.bmp.txtjnxpTwE.bmp.txt加载到WinMerge中进行比较。

如快照所示,输入和输出文件的前 14037 (0x36d5) 个字节具有相同的内容。然后,输入文件“意外地”包含三个字节0a 0a 0a,而输出文件却包含0d 0a 0d 0a 0d 0a。因此,相应的原始像素(以及所有后续像素)都被破坏了。

(您可能已经猜到了,0a 是换行符'\n' 的十六进制值,0d 是回车符'\r' 之一。)

顺便说一句。输出文件可能比输入文件长一点(由于插入的 CR 字节)。这可能会被 BMP 查看器忽略,因为 BMP 标头准确地说明了原始图像需要多少字节(并且简单地忽略额外的字节)。

您可能已经认识到,您应该将 fopen() 调用更改为

FILE *inptr = fopen(infile, "rb");

FILE *outptr = fopen(outfile, "wb");

解决您的问题。

顺便说一句。 *x 操作系统(例如 Linux)上的 C API 没有文本和二进制模式的这种区别。相反,b 被简单地忽略(这有助于编写可移植代码)。

延伸阅读:fopen, fopen_s on cppreference.com

【讨论】:

  • 感谢您的全面回答。正如您所概述的,问题已得到解决。
猜你喜欢
  • 2012-10-24
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
  • 2012-06-23
  • 2014-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多