繁体   English   中英

位图加载器C ++

[英]Bitmap loader C++

我知道我错过了什么,但我不知道是什么。 这段代码帮助我将图像保存到BMP文件中。 但是当我使用它时,我在图像顶部得到一排黑色像素,图像向右移动。 有任何想法吗 ? 谢谢 !

struct CVImporter::BITMAPFILEHEADER
{
    ushort bfType;
    uint   bfSize;
    uint   bfReserved;
    uint   bfOffBits;
};

struct CVImporter::BITMAPINFOHEADER
{
    uint  biSize;
    int   biWidth;
    int   biHeight;
    short biPlanes;
    short biBitCount;
    uint  biCompression;
    uint  biSizeImage;
    int   biXPelsPerMeter;
    int   biYPelsPerMeter;
    uint  biClrUsed;
    uint  biClrImportant;
};

struct CVImporter::RGBQUAD
{
    uchar rgbBlue;
    uchar rgbGreen;
    uchar rgbRed;
    uchar rgbReserved;
};

struct CVImporter::BITMAPINFO
{
    TBITMAPINFOHEADER bmiHeader;
    TRGBQUAD          bmiColors[256];
};

//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapFileHeader( std::ofstream& stream,
                                   const BITMAPFILEHEADER& header )
{
    WriteToStream( stream, header.bfType );
    WriteToStream( stream, header.bfSize );
    WriteToStream( stream, header.bfReserved );
    WriteToStream( stream, header.bfOffBits );
}

//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapInfoHeader( std::ofstream& stream,
                                   const BITMAPINFOHEADER& infoHeader )
{
    WriteToStream( stream, infoHeader.biSize );
    WriteToStream( stream, infoHeader.biWidth );
    WriteToStream( stream, infoHeader.biHeight );
    WriteToStream( stream, infoHeader.biPlanes );
    WriteToStream( stream, infoHeader.biBitCount );
    WriteToStream( stream, infoHeader.biCompression );
    WriteToStream( stream, infoHeader.biSizeImage );
    WriteToStream( stream, infoHeader.biXPelsPerMeter );
    WriteToStream( stream, infoHeader.biYPelsPerMeter );
    WriteToStream( stream, infoHeader.biClrUsed );
    WriteToStream( stream, infoHeader.biClrImportant );
}

//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapRGBQuad( std::ofstream& stream, const RGBQUAD& quad )
{
    WriteToStream( stream, quad.rgbBlue );
    WriteToStream( stream, quad.rgbGreen );
    WriteToStream( stream, quad.rgbRed );
    WriteToStream( stream, quad.rgbReserved );
}


//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapInfo( std::ofstream& stream,
                             const BITMAPINFO& info )
{
    WriteBitmapInfoHeader( stream, info.bmiHeader );
    for( uint i = 0; i < 256; ++i )
        WriteBitmapRGBQuad( stream, info.bmiColors[i] );
}

//-----------------------------------------------------------------------------
//
void
CVImporter::LoadBitmapFile( const CLString& fileName,
                            CVBitmap::Ptr bm ) throw(IOException)
{
    if( bm.IsNull() ) throw( IOException("Pointer should not be null", CL_ORIGIN) );

    // Verify the extension of the file
    CLString ext("");
    CLFileSystem::GetExtension( fileName, ext );
    if( ext != "bmp" )
        throw( IOException("Bad file extension (should be bmp)", CL_ORIGIN) );

    uint bytesPerPixel {3};
    uint imgDataSize = (bm->GetWidth()*bytesPerPixel)*bm->GetHeight();

    BITMAPFILEHEADER bmFile;
    BITMAPINFO bmInfo;

    bmFile.bfType = (ushort)0x4D42;
    bmFile.bfSize = imgDataSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO);
    bmFile.bfReserved = 0;
    bmFile.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO);

    bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmInfo.bmiHeader.biWidth = (ulong)bm->GetWidth();
    bmInfo.bmiHeader.biHeight = (ulong)bm->GetHeight();
    bmInfo.bmiHeader.biPlanes = 1;
    bmInfo.bmiHeader.biBitCount = (ushort) 8*bytesPerPixel;
    bmInfo.bmiHeader.biCompression = 0; //BI_RGB;
    bmInfo.bmiHeader.biSizeImage = imgDataSize;
    bmInfo.bmiHeader.biXPelsPerMeter = 0;
    bmInfo.bmiHeader.biYPelsPerMeter = 0;
    bmInfo.bmiHeader.biClrUsed = 256;
    bmInfo.bmiHeader.biClrImportant = 256;

    for( uint i = 0; i < 256; ++i )
    {
        bmInfo.bmiColors[i].rgbBlue = i;
        bmInfo.bmiColors[i].rgbGreen = i;
        bmInfo.bmiColors[i].rgbRed = i;
        bmInfo.bmiColors[i].rgbReserved = 0;
    }

    std::ofstream stream( fileName.c_str(), std::ios::binary );
    WriteBitmapFileHeader( stream, bmFile );
    WriteBitmapInfo( stream, bmInfo );

    uint padding = (4 - ((3 * bm->GetWidth()) % 4)) % 4;
    char padding_data[4] {0, 0, 0, 0};

    for( uint i = 0; i < bm->GetHeight(); ++i )
    {
        uchar* data_ptr = bm->GetBits() + ((bm->GetHeight()-i)*bm->GetWidthStep());
        stream.write( reinterpret_cast<char*>(data_ptr), sizeof(char) *
                      bm->GetByteSize() * bm->GetWidth() );

        stream.write( padding_data, padding );
    }

    stream.close();
}


//-----------------------------------------------------------------------------
//
template<typename T>
inline void
CVImporter::WriteToStream( std::ofstream& stream, const T& t )
{
    stream.write( reinterpret_cast<const char*>(&t), sizeof(T) );
}

在此输入图像描述

您的代码有两个直接问题。

首先,结构成员通常在4字节存储器地址上对齐(SO: 结构填充和结构填充 )。 这意味着所有charshortint将占用4个字节,而前两个字节在内存中只有几个未使用的字节。 这通常是一件好事,因为当处理器可以从对齐的内存中读取时,内存访问 - 读取和写入 - 通常更快。 但是,如果您的结构由不同大小的成员组成,则在读取或写入文件时必须小心。 在读取时,您的一些数据可能会消失在“未使用”的字节中,在写入时,您会将这些未使用的字节保存到文件中。

你说你已经尝试过报告的大小值而不是sizeof ,但这只能部分解决它。 正确的大小将写入您的文件,但它仍然是错误的数据 - 因为您仍在编写填充字节。

解决方案是告诉编译器您希望在结构成员之间自动添加填充。 不同的编译器有不同的方法,你没有提到你的,但我上面提到的SO问题包含了一些例子。 如果所有其他方法都失败了,请在编译器的手册中查找。

另一个策略是手动读取和写入每个结构成员而不是整个结构,但这在您的情况下几乎没有任何优势。

这应该解决了右边几列被包裹在左边的问题。 你的BMP阅读器似乎很宽容,因为很多值最终都是“错误的”,但最终它仍然开始从错误的起始位置显示图像。


幸运的是,问题#2更容易。 从您的代码:

for( uint i = 0; i < bm->GetHeight(); ++i )
{
    uchar* data_ptr = bm->GetBits() + ((bm->GetHeight()-i)*bm->GetWidthStep());
    ...

(你忘了包含函数GetWidthStep但我猜它会返回单个位图行的长度。)你的意思是首先抓住指向最后一行开头的指针,然后抓住它上面的行,依此类推,直到第0行。 但是,你走了一条线!

如果i=0 ,则将指针计算为start + (height-i) * width ,因此它是start + height * width 这指向图像后面的“线”, height * width 你可以看到,如果你在精神上填写'身高'的实际值。

所以你要抓住第1行到height指针,而你应该使用0height-1 请改用:

uchar* data_ptr = bm->GetBits() + ((bm->GetHeight()-1-i)*bm->GetWidthStep());

- 注意GetHeight()之后的-1 - 让它工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM