在c中复制bmp

在c中复制bmp,c,copy,bmp,C,Copy,Bmp,背景: 我想将bmp(未压缩的24 RGB)图像从一个文件名复制到另一个文件名。我使用的是TDM-GCC(版本4.9.2,32位,SJLJ)附带代码块的mingw编译器 问题: 该程序适用于黑白图像和简单的彩色图像,但不适用于复杂的彩色图像。请查看附件中的图片。我没有足够的声誉来发布其他图片,所以我尝试发布两张最相关的图片。程序无法复制lenna图像。这种行为的原因是什么 代码: #include <stdio.h> #include <stdlib.h> #inc

背景:
我想将bmp(未压缩的24 RGB)图像从一个文件名复制到另一个文件名。我使用的是TDM-GCC(版本4.9.2,32位,SJLJ)附带代码块的mingw编译器

问题:
该程序适用于黑白图像和简单的彩色图像,但不适用于复杂的彩色图像。请查看附件中的图片。我没有足够的声誉来发布其他图片,所以我尝试发布两张最相关的图片。程序无法复制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;

}
#包括
#包括
#包括
#布拉格语包(1)
/*以下是访问DIB信息的步骤
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字节;
类型定义uint32_t DWORD;
typedef int32t长;
typedef uint16_t字;
类型定义结构
{
字型;
德沃德尺寸;
保留字1;
保留字2;
德沃德;
}位图文件头;
类型定义结构
{
德沃德·比西泽;
长双宽;
长双峰高度;
单词双平面;
单词双比特计数;
德沃德双压缩;
德沃德·比西泽迈格;
长双峰渗透计;
长双渗透计;
德沃德·比克尔鲁斯;
德沃德·比克勒;
}位图信息头;
类型定义结构
{
字节rgbtBlue;
字节rgbtGreen;
字节rgbtRed;
}RGBTRIPLE;
内部主(空)
{
char*infle=“testin.bmp”;
char*outfile=“testout.bmp”;
文件*inptr=fopen(填入“r”);
如果(inptr==NULL)
{
fprintf(stderr,“无法打开%s。\n”,填充);
返回2;
}
文件*outptr=fopen(输出文件,“w”);
if(outptr==NULL)
{
fclose(inptr);
fprintf(stderr,“无法创建%s.\n”,输出文件);
返回3;
}
位图文件头;
fread(&bf,sizeof(BITMAPFILEHEADER),1,inptr);
BitMapInfo头bi;
fread(&bi,sizeof(BitMapInfo标头),1,inptr);
fwrite(&bf,sizeof(BITMAPFILEHEADER),1,outptr);
fwrite(&bi,sizeof(BitMapInfo标头),1,outptr);
int padding=(4-(bi.biWidth*sizeof(RGBTRIPLE))%4)%4;
inti,j,k,biHeight;
对于(i=0,biHeight=abs(bi.biHeight);i
输入图像:

输出图像:


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

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

以文本模式打开文件

这是我从微软的C API中了解到的一种特殊行为,这似乎也适用于mingw TDM-GCC(我很难相信这一点,直到转储的数据让我相信我的怀疑是正确的)

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

  • fopen(infle,“rt”)
    fopen(outfile,“wt”)
    以文本模式打开文件
  • fopen(infle,“rb”)
    fopen(outfile,“wb”)
    以二进制模式打开文件
  • fopen(infle,“r”)
    fopen(outfile,“w”)
    默认为文本模式
在文本模式下,文件读取将所有Microsoft行尾(
“\r\n”
)替换为Unix行尾(
“\n”
),而写入则相反(
“\n”
变为
“\r\n”

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

为了证明这一点,

  • 我下载了您的示例文件(不幸的是以PNG格式上载)
  • 将文件(返回)转换为24位BMP(使用GIMP)
  • 为以下各项创建一个十六进制转储:

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

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

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

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

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

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

    来解决你的问题

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


    进一步阅读:

    在MSC中可能会有问题-关于Windows上的gcc,我不确定。但是,请尝试
    fopen(infle,“rb”)
    fopen(outfile,“wb”)
    ,并报告这是否有任何变化。(充其量,它修复了问题,至少它不会改变任何东西。)这段代码有很多问题。你让它工作只是巧合。您的目标仅仅是复制位图吗?还是提取信息位图信息?如果你只是想复制,有一个更简单的解决方案。谢谢你的全面回答。正如你所概述的,这个问题已经解决了
    FILE *inptr = fopen(infile, "rb");
    
    FILE *outptr = fopen(outfile, "wb");