【问题标题】:Difficulty writing a Bmp file in C++ manually (using Xcode)在 C++ 中手动编写 Bmp 文件很困难(使用 Xcode)
【发布时间】:2011-03-20 08:36:43
【问题描述】:

我目前正在尝试在 C++ 中创建光线追踪器,但我在编写最后生成的 .bmp 时遇到了困难。我决心手动完成,所以我可以了解更多关于图像文件和编写它们等的信息。但我遇到了一些困难。我对 C++ 很陌生,但使用 Python 已经有一段时间了。

我现在差不多了,我只有一个奇怪的问题。在重要的颜色(我设置为 0)中,一切都是正确的(我设置为 0),其中各种随机字符出现,并且在接下来的几个字节中继续,几个字节进入像素数据。在那之前和之后一切都很好,但我无法解释。我当前的代码正在编辑中:

这是十六进制:

问题区域突出显示

 #include <iostream>
#include <fstream>
#include <math.h>
using namespace std;
//-------------------------Object list
const int renderSize[2] = {254,254};
float sphere1Pos[3] = {0.0,0.0,0.0}; //first sphere at origin to make calculations easier
float sphere1Radius = 10.0;
float sphere1Colour= (255.0,0.0,0.0); 
float light1Pos = (0.0,20.0,0.0); //Above sphere
float light1Intensity = 0.5;
// ------------------------



float dot(float* a,float* b); //Calculates the dot product

struct pixel {

    unsigned char R;
    unsigned char G;
    unsigned char B;
    };

//bmp--------------
struct bitmapMagicNumber {
    unsigned char magicNumber[2];
    };

struct bitmapFileHeader {
    unsigned char fileSize;
    short reserved1;
    long reserved2;
    short offset;
    };

struct bitmapInformationHeader {
    short headerSize;
    short padding;
    short width;
    short height; 
    short planes;
    short bitDepth;
    short compression;
    short imageSize;
    short xPixelsPerMetre;
    short yPixelsPerMetre;
    short colours;
    short importantColours;
    };

void setBitmapMagicNumber(bitmapMagicNumber& magicNum){
    magicNum.magicNumber[0] = 0x42;
    magicNum.magicNumber[1] = 0x4D;
    };

void setBitmapFileHeader(bitmapFileHeader& fileHeader,bitmapInformationHeader& informationHeader,pixel pixelArray) {
    fileHeader.fileSize = 54 + sizeof(pixelArray);
    fileHeader.reserved1 = 0;
    fileHeader.reserved2 = 0;
    fileHeader.offset = 54;
    };

void setBitmapInformationHeader(bitmapInformationHeader& informationHeader){
    informationHeader.headerSize = 40;
    informationHeader.padding=0;
    informationHeader.width = renderSize[0];
    informationHeader.height = renderSize[1];
    informationHeader.planes = 1;
    informationHeader.bitDepth = 24;
    informationHeader.compression = 0;
    informationHeader.imageSize = 0;
    informationHeader.xPixelsPerMetre = 0;
    informationHeader.yPixelsPerMetre = 0 ;
    informationHeader.colours = 0;
    informationHeader.importantColours = 0;
    };

void writeBitmap(bitmapMagicNumber& magicNum, bitmapFileHeader& fileHeader,bitmapInformationHeader& informationHeader,pixel pixelArray){
    ofstream out("test.bmp",ios::out|ios::binary);

    //file header
    out.write((char*) &magicNum,2);
    out.write((char*) &fileHeader.fileSize,sizeof(fileHeader.fileSize));
    if (sizeof(fileHeader.fileSize)<3){
        out.write((char*) &informationHeader.padding,1);
        }
    out.write((char*) &informationHeader.padding,1);
    out.write((char*) &fileHeader.reserved1,2);
    out.write((char*) &fileHeader.reserved2,2);
    out.write((char*) &fileHeader.offset,sizeof(fileHeader.offset));
    out.write((char*) &informationHeader.padding,1);
    out.write((char*) &informationHeader.padding,1);


    //information header
    out.write((char*) &informationHeader.headerSize,sizeof(informationHeader.headerSize));
    out.write((char*) &informationHeader.padding,1);
    out.write((char*) &informationHeader.padding,1);



    out.write((char*) &informationHeader.width,sizeof(informationHeader.width));
    if (sizeof(informationHeader.width)<4){
        out.write((char*) &informationHeader.padding,1);
        }
    if (sizeof(informationHeader.width)<3){
        out.write((char*) &informationHeader.padding,1);
        }
    if (sizeof(informationHeader.width)<2){
        out.write((char*) &informationHeader.padding,1);
        }

    out.write((char*) &informationHeader.height,sizeof(informationHeader.height));
    if (sizeof(informationHeader.height)<4){
        out.write((char*) &informationHeader.padding,1);
        }
    if (sizeof(informationHeader.height)<3){
        out.write((char*) &informationHeader.padding,1);
        }
    if (sizeof(informationHeader.height)<2){
        out.write((char*) &informationHeader.padding,1);
        }   

    out.write((char*) &informationHeader.planes,sizeof(informationHeader.planes));
    out.write((char*) &informationHeader.bitDepth,sizeof(informationHeader.bitDepth));
    out.write((char*) &informationHeader.compression,4);
    out.write((char*) &informationHeader.imageSize,4);
    out.write((char*) &informationHeader.xPixelsPerMetre,4);
    out.write((char*) &informationHeader.yPixelsPerMetre,4);
    out.write((char*) &informationHeader.colours,4);
    out.write((char*) &informationHeader.importantColours,4);

    //pixel data
    for (int y=0; y < renderSize[1]; y++) {
        for (int x=0; x< renderSize[0]; x++) {
            out.write((char*) &pixelArray[x][y],sizeof(pixel));
        }
    }

    out.close();


}


// end bmp-----------

int main() {

pixel pixelArray[renderSize[0]][renderSize[1]];

    for (int y=0; y < renderSize[1]; y++) {
        for (int x=0; x< renderSize[0]; x++) {
            float rayPos[3] = {x,y, -1000.0};
            float rayDir[3] = {0.0,0.0,-1.0};   
            bool intersect;

            //for each object in scene, see if intersects. (for now there is only one object to make things easier)

            //-------sphere ray intersection....
            float distance[3];
            distance[0]= rayPos[0]-sphere1Pos[0];
            distance[1]= rayPos[1]-sphere1Pos[1];
            distance[2]= rayPos[2]-sphere1Pos[2];
            float a = dot(rayDir, rayDir);
            float b = 2 * dot(rayDir, distance);
            float c = dot(distance, distance) - (sphere1Radius * sphere1Radius);

            float disc = b * b - 4 * a * c;

            if (disc < 0)
                intersect=false;
            else
                intersect=true;


            //--------------------

            if (intersect==true){
                pixelArray[x][y].R = 0;
                pixelArray[x][y].G = 0;
                pixelArray[x][y].B = 0;
                }

            else {
                pixelArray[x][y].R = 0;
                pixelArray[x][y].G = 0;
                pixelArray[x][y].B = 0;
                }



            // trace to lights (as long as another object is not in the way) 

        }

    }
    //write .bmp
    bitmapMagicNumber magicNum;
    bitmapFileHeader fileHeader;
    bitmapInformationHeader informationHeader;

    setBitmapMagicNumber(magicNum);
    setBitmapFileHeader(fileHeader,informationHeader, pixelArray[renderSize[0]][renderSize[1]]);
    setBitmapInformationHeader(informationHeader);

    writeBitmap(magicNum,fileHeader,informationHeader, pixelArray[renderSize[0]][renderSize[1]]);
}

//calculate dot product
float dot(float* a,float* b)
{
float dp = 0.0;
for (int i=0;i<3;i++)
    dp += a[i] * b[i];
return dp;
}

【问题讨论】:

  • “我对 C++ 还很陌生,但使用 Python 已经有一段时间了。” C++ 与 Python 完全不同。如果您还没有一本好的 C++ 书籍,请考虑从The Definitive C++ Book Guide and List 获取一本介绍性 C++ 书籍。
  • 我有一个并且已经阅读了,我只是觉得我的经验不足值得一提,因为这可能是我的一些错误的原因。
  • @Elephantic:太好了!我只是想确定一下。 :-) 很多人试图在没有一本好书的情况下学习,但结果通常并不好。
  • 不要使用 &lt;pre&gt;&lt;code&gt; - 选择代码并按 010 按钮或缩进至少 4 个空格。
  • 啊,好的。我试图按下按钮然后粘贴代码。感谢您修复它:)

标签: c++ image bmp


【解决方案1】:

虽然这可能不是您唯一的问题,但您几乎可以肯定有data alignment 问题。

以你的bitmapFileHeader为例,假设long有四字节对齐,short有两字节对齐,magicNumberfileSize之间会有两个字节的未命名填充(有大多数其他数据结构中的类似问题)。

作为一种解决方案,您可以将标题和其他结构表示为char 的数组(没有填充),并将相关数据复制到数组中的正确位置。

您的编译器可能会提供一种“打包”数据结构的方法,使它们不对齐,这也可以解决您的问题,但这样做是完全不可移植的。

【讨论】:

  • 好的,谢谢,我会调查的。但是你知道在将像素数组传递给 setBitmapFileHeader 时如何解决我的问题吗?它阻止了我编译程序,我不知道该怎么做。
  • @Elephantic: renderSize 必须是 const 才能用作数组的维度。 &amp;pixelArray 产生一个指向数组的指针,而不是指向第一个元素的指针(指针值相同,但它们的类型不同)。如果您放弃&amp;,它应该可以解决您的问题。请注意,您的大小计算也有错误(您使用了两次renderSize[1]),并且在技术上不允许像访问一维数组一样访问二维数组。
  • 谢谢 :) 编译时错误在以下行: setBitmapFileHeader(fileHeader,informationHeader,pixelArray);返回:“错误:从 'pixel (*)[(((ling unsigned int)(((int)renderSize[1]) - 1)) + 1u)]' 转换为非标量类型 'pixel' 请求”我的问题是我真的不知道这里的类型应该放什么:“void setBitmapFileHeader(bitmapFileHeader& fileHeader,bitmapInformationHeader& informationHeader,pixel pixelArray) {”我以为它会是像素,但这不起作用。
  • @Elephantic:必须是pixel pixelArray[][renderSize[1]
  • 感谢您的帮助 :)
【解决方案2】:

我总是以 .ppm 格式输出。格式再简单不过了:

FILE * out = fopen("out.ppm", "wb");
fprintf(out, "P6 %d %d 255\n", HEIGHT, WIDTH);

for(int i=0; i<HEIGHT; i++)
  for(int j=0; j<WIDTH; j++)
  {
    color c = get_pixel_color(i,j)
    putc(c.red, out);
    putc(c.green, out);
    putc(c.blue, out);
  }

fclose(out);

...其中红色、绿色和蓝色是整数,但必须取 0 到 255 之间的值。ppm 标题中的 255 表示颜色通道的最大值为 255。

大多数图像编辑器都会读取 .ppm:gimp、photoshop 等。我一直很喜欢它,因为我能记住我脑海中的格式,而且我不必写任何不用的东西...当我真的需要 .bmp 或 .jpg 等时,我使用 imagemagick convert 将我的 ppm 转换为该格式。

【讨论】:

  • 事后看来,这可能是我从一开始就应该做的,但我现在已经放弃了!
【解决方案3】:

This code 具有在 Windows 和其他平台上读取/写入 BMP 的功能。看看吧。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-09
    • 2013-07-11
    相关资源
    最近更新 更多