在c中复制bmp [英] Copying a bmp in c

查看:98
本文介绍了在c中复制bmp的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Background:
I want to copy a bmp (uncompressed 24 RGB) image from one file name to another. I am using the mingw compiler from TDM-GCC (version 4.9.2, 32 bit, SJLJ) that comes with codeblocks.

问题:
该程序适用于黑白图像和简单的彩色图像,但不适用于复杂的彩色图像.请检查所附的图像.我没有足够的声誉来发布其他图片,因此我尝试发布了两个最相关的图片.该程序无法复制天线图像.造成这种现象的原因是什么?

Problem:
Program works for black and white images and simple color images but not complicated color images. Please review attached images. I did not have enough reputation to post the other images so I tried posting the 2 most relevant. The program is unable to copy the lenna image. What is the cause for this behavior?

代码:

 #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;

}

输入图片:

输出图像:

推荐答案

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

I fiddled a little bit with the data provided, and I'm quite sure what happened:

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

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

以文本模式打开文件.

这是我从Microsoft的C API中了解到的一种特殊行为,看来这也适用于mingw TDM-GCC(我一直难以相信,直到转储的数据使我确信自己的怀疑是对的为止.

This is a special behavior I know from Microsoft's C API and it seems that this applies to mingw TDM-GCC as well (which I struggled to believe until the dumped data convinced me that my suspicion was right).

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

The file I/O in Microsoft's C API distinguishs between text mode and binary mode:

  • fopen(infile, "rt")fopen(outfile, "wt")以文本模式打开文件.
  • fopen(infile, "rb")fopen(outfile, "wb")以二进制模式打开文件.
  • fopen(infile, "r")fopen(outfile, "w")默认为文本模式.
  • fopen(infile, "rt") and fopen(outfile, "wt") open the files in text mode.
  • fopen(infile, "rb") and fopen(outfile, "wb") open the files in binary mode.
  • fopen(infile, "r") and fopen(outfile, "w") default to text mode.

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

In text mode the file reading replaces all Microsoft line-endings ("\r\n") by Unix line-endings ("\n") as well as the writing does the opposite ("\n" becomes "\r\n").

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

This is reasonable if the contents is plain text but it probably corrupts output of binary contents where a byte 0x0d is inserted whenever a byte 0x0a (with any meaning) occurs in the data stream.

为了证明这一点,

  1. 我下载了您的示例文件(不幸的是以PNG格式上传)
  2. 使用GIMP将文件转换回24位BMP
  3. 对每个对象进行了十六进制转储:

  1. I downloaded your sample files (unfortunately uploaded in PNG format)
  2. converted the files (back) to 24 bit BMP (using GIMP)
  3. made a hex-dump for each:

$ hexdump -C IkW6FbN.bmp >IkW6FbN.bmp.txt

$ hexdump -C jnxpTwE.bmp >jnxpTwE.bmp.txt

  • ,最后将IkW6FbN.bmp.txtjnxpTwE.bmp.txt加载到 WinMerge 中进行比较.

  • and finally loaded IkW6FbN.bmp.txt and jnxpTwE.bmp.txt into WinMerge for comparison.

    如快照所示,输入和输出文件的前14037(0x36d5)个字节具有相同的内容.然后,输入文件包含偶然地"三个字节0a 0a 0a,而输出文件则包含0d 0a 0d 0a 0d 0a.因此,各个原始像素(及其后的所有像素)均已损坏.

    As the snapshot illustrates, the input and output file have identical contents for the first 14037 (0x36d5) bytes. Then, the input file contains "accidentally" three bytes 0a 0a 0a where the output file has instead 0d 0a 0d 0a 0d 0a. Thus, the respective original pixels (and all following) are corrupted.

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

    (As you might already guess, 0a is the hexadecimal value of the line-feed character '\n', 0d the one of the carriage-return '\r'.)

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

    Btw. the output file is probably a little bit longer than the input file (due to the inserted CR bytes). This might be ignored by a BMP viewer as the BMP header states exactly how many bytes are needed for the raw image (and the extra bytes are simply ignored).

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

    As you already might've recognized you should change the fopen() calls to

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

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

    解决您的问题.

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

    Btw. the C APIs on *x OSes (e.g. Linux) doesn't have such a distinction of text and binary mode. Instead, the b is simply ignored (which is helpful to write portable code).

    进一步阅读: fopen,cppreference.com上的fopen_s

    这篇关于在c中复制bmp的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆