how to save the opengl scene to a bitmap?

how to save the opengl scene to a bitmap?

I am new to opengl, i want save my current opengl scene to a bitmap. I checked the "red book", and i know i should use "glReadPixels" to read pixels from "pixel buffer",but
i don't know how to swap "frame buffer"  to "pixel buffer". Could you give me some code?
thank you.
bloodbirdAsked:
Who is Participating?
 
ikeworkCommented:
And here is the source-file:
#include <dagl/bitmap24.h>

#if defined( WIN32 )
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>
#endif
#include <GL/gl.h>

using namespace dagl;

#define EFFICIENT_SIZE( x, efficient_alignment ) ((int)(((((int)(x))-1)|(((int)(efficient_alignment))-1))+1))
#define ALIGN_SIZE( x, alignment ) EFFICIENT_SIZE( (x), (alignment) )

uint16 const CBitmap24::ms_ui16Planes = 1;
uint16 const CBitmap24::ms_ui16Bpp = 24;
uint32 const CBitmap24::ms_ui32BitmapFileDataOffset = 0x36;
uint32 const CBitmap24::ms_ui32InfoHeaderSize = 0x28;
myubyte const CBitmap24::ms_unusedColorInfo[ 16 ] =
{

                                        0x12, 0x0B,
    0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


CBitmap24::CBitmap24()
: m_ByteOrder( BO_Unknown ),
  m_ui32Width( 0 ),
  m_ui32Height( 0 ),
  m_ui32DataSize( 0 ),
  m_ui32BitmapFileSize( 0 ),
  m_pData( NULL )
{
}

CBitmap24::~CBitmap24()
{
    Destroy();
}

void CBitmap24::Destroy()
{
    if( m_pData )
    {
        delete[] m_pData;
        m_pData = NULL;

        m_ByteOrder = BO_Unknown;
        m_ui32Width = m_ui32Height = m_ui32DataSize = m_ui32BitmapFileSize = 0;
    }
}

void CBitmap24::ReverseByteOrder()
{
    assert( m_ByteOrder != BO_Unknown );

    GLubyte temp;
    GLubyte *pCol, *pUpper;
    GLubyte const *pColEnd;
    size_t rowDataLen = m_ui32Width * 3;
    GLubyte *pRow = m_pData;
    GLubyte const *const pRowEnd = m_pData + m_ui32DataSize;
    for( ; pRow < pRowEnd; pRow += m_ui32PaddedRowSize )
    {
        pCol = pRow;
        pColEnd = pRow + rowDataLen;
        for( ; pCol < pColEnd; pCol += 3 )
        {
            pUpper = pCol + 2;
            temp = *pCol;
            *pCol = *pUpper;
            *pUpper = temp;
        }
    }
    if( m_ByteOrder == BO_RGB ) m_ByteOrder = BO_BGR;
    else m_ByteOrder = BO_RGB;
}

/*
The pixel data in a BMP file is scan line padded to a 32-bit (4-byte) boundary. 
What this means is that if you have a 1071x363 24-bit image, that each scan line 
(one row of data in the image) consists of 363 pixels each of which requires 
3-bytes (24-bits) to encode. Thus you have 1,089 bytes of data per line. 
The format requires that scan lines be multiples of 4-bytes so 3 null bytes 
(value is zero) are added to the end of the data for each line to make a total 
of 1,092 (4x273) bytes per line.
*/

bool CBitmap24::CreateFromCurrentGLContext()
{
    Destroy();

    GLint viewport[ 4 ];
    glGetIntegerv( GL_VIEWPORT, viewport );

    int const intWidth = viewport[ 2 ];
    int const intHeight = viewport[ 3 ];

    // width must be an even number
    //assert( !( intWidth % 2 ) );

    if( !Init( intWidth, intHeight ) ) return false;

    //glPixelStorei( GL_PACK_ALIGNMENT, 1 );
    glPixelStorei( GL_PACK_ALIGNMENT, 4 );
    //glRasterPos2i( 0, 0 );
    glReadPixels( 0, 0, intWidth, intHeight,
                  GL_RGB, GL_UNSIGNED_BYTE, m_pData );

    m_ByteOrder = BO_RGB;

//  GL_BGR not supported by my version ;(
//  glReadPixels( 0, 0, intWidth, intHeight,
//      GL_BGR, GL_UNSIGNED_BYTE, bitmap.pImage );

    // bitmap needs format bgr...
    ReverseByteOrder();

    assert( glGetError() == GL_NO_ERROR );

    return true;
}

bool CBitmap24::Copy( CBitmap24 const &b )
{
    // needs to be destroyed before !!!
    assert( !m_pData );

    if( b.m_ui32DataSize )
    {
        m_pData = new myubyte[ b.m_ui32DataSize ];
        if( !m_pData ) return false;
        memcpy( m_pData, b.m_pData, b.m_ui32DataSize );
    }

    m_ByteOrder = b.m_ByteOrder;
    m_ui32Width = b.m_ui32Width;
    m_ui32Height = b.m_ui32Height;
    m_ui32PaddedRowSize = b.m_ui32PaddedRowSize;
    m_ui32DataSize = b.m_ui32DataSize;
    m_ui32BitmapFileSize = b.m_ui32BitmapFileSize;

    return true;
}

uint32 CBitmap24::GetPaddedRowSize( uint32 width )
{
    return ALIGN_SIZE( width * 3, 4 );
}

bool CBitmap24::Init( uint32 width, uint32 height )
{
    assert( !m_pData );

    // align line to 4 byte
    uint32 ui32PaddedRowSize = GetPaddedRowSize( width );
    uint32 ui32DataSize = ui32PaddedRowSize * height;

    m_pData = new myubyte[ ui32DataSize ];
    if( !m_pData ) return false;

    m_ui32Width = width;
    m_ui32Height = height;
    m_ui32PaddedRowSize = ui32PaddedRowSize;
    m_ui32DataSize = ui32DataSize;
    m_ui32BitmapFileSize = ms_ui32BitmapFileDataOffset + m_ui32DataSize;

    return true;
}

bool CBitmap24::WriteBitmapFileIntoBuffer(
    myubyte *pBuffer ) const
{
    static const uint32 s_ui32Zero = 0;
    assert( pBuffer );

    memcpy( pBuffer, "BM", 2 );
    pBuffer += 2;
    memcpy( pBuffer, &m_ui32BitmapFileSize, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &s_ui32Zero, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &ms_ui32BitmapFileDataOffset, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &ms_ui32InfoHeaderSize, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &m_ui32Width, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &m_ui32Height, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &ms_ui16Planes, 2 );
    pBuffer += 2;
    memcpy( pBuffer, &ms_ui16Bpp, 2 );
    pBuffer += 2;
    memcpy( pBuffer, &s_ui32Zero, 4 );
    pBuffer += 4;
    memcpy( pBuffer, &m_ui32DataSize, 4 );
    pBuffer += 4;
    memcpy( pBuffer, ms_unusedColorInfo, 16 );
    pBuffer += 16;

    memcpy( pBuffer, m_pData, m_ui32DataSize );

    return true;
}

bool CBitmap24::CreateFromBitmapData( uint32 width, uint32 height, myubyte *pData )
{
    // needs to be destroyed before !!!
    assert( !m_pData );

    if( !Init( width, height ) ) return false;

    memcpy( m_pData, pData, m_ui32DataSize );

    m_ByteOrder = BO_RGB;

    return true;
}

bool CBitmap24::Load( const char *pszFileName )
{
    // needs to be destroyed before !!!
    assert( !m_pData );

    FILE *pFile = fopen( pszFileName, "rb" );
    if( !pFile ) return false;

    bool blnResult = false;
    size_t ui32OffsetData;
    uint32 width, height;
    uint32 file_size;

    if( fseek( pFile, 2, SEEK_CUR ) ) goto close_file;
    if( ( fread( &file_size, 4, 1, pFile ) ) != 1 ) goto close_file;

    if( fseek( pFile, 4, SEEK_CUR ) ) goto close_file;
    if( ( fread( &ui32OffsetData, 4, 1, pFile ) ) != 1 ) goto close_file;

    if( fseek( pFile, 4, SEEK_CUR ) ) goto close_file;
    // read the width
    if( ( fread( &width, 4, 1, pFile ) ) != 1 ) goto close_file;
    // read the height 
    if( ( fread( &height, 4, 1, pFile ) ) != 1 ) goto close_file;

    if( !Init( width, height ) ) goto close_file;
    
    // read bitmap data
    if( fseek( pFile, ui32OffsetData, SEEK_SET ) ) goto close_file;
    if( fread( m_pData, m_ui32DataSize, 1, pFile ) != 1 ) goto close_file; 

    m_ByteOrder = BO_BGR;

    blnResult = true;

close_file:
    fclose( pFile );

    return blnResult;
}

bool CBitmap24::Save( const char *pszFileName ) const
{
    static const uint32 s_ui32Zero = 0;

    assert( m_pData );
    // bmp-format needs data in BGR
    assert( m_ByteOrder == BO_BGR );

    FILE *pFile = fopen( pszFileName, "wb" );
    if( !pFile ) return false;

    fwrite( "BM", 2, 1, pFile );
    fwrite( &m_ui32BitmapFileSize, 4, 1, pFile );
    fwrite( &s_ui32Zero, 4, 1, pFile );
    fwrite( &ms_ui32BitmapFileDataOffset, 4, 1, pFile );
    fwrite( &ms_ui32InfoHeaderSize, 4, 1, pFile );
    fwrite( &m_ui32Width, 4, 1, pFile );
    fwrite( &m_ui32Height, 4, 1, pFile );
    fwrite( &ms_ui16Planes, 2, 1, pFile );
    fwrite( &ms_ui16Bpp, 2, 1, pFile );
    fwrite( &s_ui32Zero, 4, 1, pFile );
    fwrite( &m_ui32DataSize, 4, 1, pFile );
    fwrite( ms_unusedColorInfo, 16, 1, pFile );
    fwrite( m_pData, m_ui32DataSize, 1, pFile );

    fclose( pFile );

    return true;
}

Open in new window

0
 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
Take a look a this thread, just like a point of start http://www.mofeel.net/312-comp-graphics-api-opengl/3919.aspx
0
 
ikeworkCommented:
Hi bloodbird,

Once I wrote a class, that does the job. The usage is as easy as this:

    CBitmap24 bitmap;
    if( !bitmap.CreateFromCurrentGLContext() ) return false;
    if( !bitmap.Save( "c:/y.bmp" ) ) return false;

Hope it helps. Feel free to ask for details :)

Below is the header, just add appropriate typedefs for the used types
#ifndef DAGL_BITMAP24BGR_INCLUDED
#define DAGL_BITMAP24BGR_INCLUDED

/*

http://web.uccs.edu/wbahn/ECE1021/STATIC/REFERENCES/bmpfileformat.htm

The pixel data in a BMP file is scan line padded to a 32-bit (4-byte) boundary. 
What this means is that if you have a 1071x363 24-bit image, that each scan line 
(one row of data in the image) consists of 363 pixels each of which requires 
3-bytes (24-bits) to encode. Thus you have 1,089 bytes of data per line. 
The format requires that scan lines be multiples of 4-bytes so 3 null bytes 
(value is zero) are added to the end of the data for each line to make a total 
of 1,092 (4x273) bytes per line.
*/

#include <cassert>

namespace dagl
{

    class CBitmap24
    {
    public:
        CBitmap24();
        virtual ~CBitmap24();

        enum ByteOrder
        {
            BO_Unknown = -1,
            BO_RGB = 0,
            BO_BGR
        };

        virtual void    Destroy();
        bool            Init( uint32 width, uint32 height );

        bool            CreateFromBitmapData( uint32 width, uint32 height, myubyte *pData );

        bool            CreateFromCurrentGLContext();
        void            ReverseByteOrder();

        bool            Load( const char *pszFileName );

        bool            Save( const char *pszFileName ) const;
        bool            WriteBitmapFileIntoBuffer( myubyte *pBuffer ) const;

        ByteOrder       GetByteOrder() const { return m_ByteOrder; }
        uint32          GetBitmapFileSize() const;
        uint32          GetWidth() const { return m_ui32Width; }
        uint32          GetHeight() const { return m_ui32Height; }
        uint32          GetDataSize() const { return m_ui32DataSize; }
        uint32          GetPaddedRowSize() const { return m_ui32PaddedRowSize; }
        bool            IsEmpty() const { return (bool)( m_pData == NULL ); }
        myubyte const*  GetDataPtr() const { return m_pData; }

        virtual bool    Copy( CBitmap24 const &b );

        static uint32   GetPaddedRowSize( uint32 width );

    protected:
        myubyte*        GetDataPtr() { return m_pData; }
        void            SetByteOrder( ByteOrder b ) { assert( b == BO_RGB || b == BO_BGR ); m_ByteOrder = b; }

    private:
        ByteOrder   m_ByteOrder;
        // number of pixel in x
        uint32      m_ui32Width;
        // number of pixel in y
        uint32      m_ui32Height;
        // one row (in x) is padded to 4 byte
        uint32      m_ui32PaddedRowSize;
        uint32      m_ui32DataSize;
        uint32      m_ui32BitmapFileSize;
        myubyte     *m_pData;

        static uint16 const ms_ui16Planes;
        static uint16 const ms_ui16Bpp;
        static uint32 const ms_ui32BitmapFileDataOffset;
        static uint32 const ms_ui32InfoHeaderSize;
        static myubyte const ms_unusedColorInfo[ 16 ];
    };

    inline
    uint32 CBitmap24::GetBitmapFileSize() const
    {
        assert( m_pData );
        return m_ui32BitmapFileSize;
    }

}

#endif // #ifndef DAGL_BITMAP24BGR_INCLUDED

Open in new window

0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.