PDA

View Full Version : Need help taking a screenshot



Niven07
09-26-2013, 05:18 PM
I'm looking for advice from someone who is knowledgable, or could become so, about .bmp files. I'm currently trying to make a function that will take a screenshot and output it as a .bmp file. I almost have it working (it works perfectly if the screen has dimensions that are multiples of 4), but I am having trouble padding the file properly. To my knowledge, the pixel array is supposed to be padded to have row sizes that are multiples of 4 bytes. The code I have so far is:



void ExportScreen()
{
const int WindowWidth = glutGet(GLUT_WINDOW_WIDTH);
const int WindowHeight = glutGet(GLUT_WINDOW_HEIGHT);
const int PixelperMeterH = 1000.f*(float)glutGet(GLUT_SCREEN_HEIGHT)/glutGet(GLUT_SCREEN_HEIGHT_MM);
const int PixelperMeterW = 1000.f*(float)glutGet(GLUT_SCREEN_WIDTH)/glutGet(GLUT_SCREEN_WIDTH_MM);

GLubyte* Pixels = new GLubyte[ 3 * WindowWidth * WindowHeight ];
glReadPixels( 0, 0, WindowWidth, WindowHeight, GL_BGR, GL_UNSIGNED_BYTE, Pixels );

int NumPads = WindowWidth%4;
vector<GLubyte> ImageData( Pixels, Pixels + (3*WindowWidth*WindowHeight) );
vector<int> Indicies;
for ( int i = 0; i < ImageData.size()/3; i++ )
{
if ( i%WindowWidth == WindowWidth - 1 )
{
Indicies.push_back(3*i+3);
}
}
for ( int i = 0; i < Indicies.size(); i++ )
{
for ( int j = 0; j < NumPads; j++ )
{
ImageData.insert( ImageData.begin() + Indicies[i] + NumPads*i + j, 'N' );
}
}

Pixels = new GLubyte[ ImageData.size() ];
for ( int i = 0; i < ImageData.size(); i++ )
Pixels[i] = ImageData[i];

BMP_Header Header;
BMP_DIB_Header DIB_Header;

Header.Ident = 0x4D42;
Header.ByteSize = ( 4 * floor( (24 * WindowWidth + 31)/32.f ) * WindowHeight ) + 54;
Header.Empty = 0;
Header.Useless = 0;
Header.PA_Adress = 54;

DIB_Header.ByteSize = 40;
DIB_Header.PixelWidth = WindowWidth;
DIB_Header.PixelHeight = WindowHeight;
DIB_Header.Planes = 1;
DIB_Header.BBP = 24;
DIB_Header.CompressionMethod = 0;
DIB_Header.ImageSize = Header.ByteSize - 54;
DIB_Header.HorizRes = PixelperMeterW;
DIB_Header.VertRes = PixelperMeterH;
DIB_Header.Colors = 0;
DIB_Header.ImportantColors = 0;

stringstream Temp;
int Current = 0;
while ( 1 )
{
Temp.str("");
Temp<<"Screenshots/Screenshot_"<<Current<<".bmp";
ifstream Check( Temp.str().c_str() );
if ( Check.is_open() )
Current++;
else
break;
}

ofstream ScreenShot( Temp.str().c_str(), ostream::binary );

WriteByte2( ScreenShot, Header.Ident );
WriteByte4( ScreenShot, Header.ByteSize );
WriteByte2( ScreenShot, Header.Empty );
WriteByte2( ScreenShot, Header.Useless );
WriteByte4( ScreenShot, Header.PA_Adress );
WriteByte4( ScreenShot, DIB_Header.ByteSize );
WriteByte4( ScreenShot, DIB_Header.PixelWidth );
WriteByte4( ScreenShot, DIB_Header.PixelHeight );
WriteByte2( ScreenShot, DIB_Header.Planes );
WriteByte2( ScreenShot, DIB_Header.BBP );
WriteByte4( ScreenShot, DIB_Header.CompressionMethod );
WriteByte4( ScreenShot, DIB_Header.ImageSize );
WriteByte4( ScreenShot, DIB_Header.HorizRes );
WriteByte4( ScreenShot, DIB_Header.VertRes );
WriteByte4( ScreenShot, DIB_Header.Colors );
WriteByte4( ScreenShot, DIB_Header.ImportantColors );
ScreenShot.write( (char*)Pixels, ImageData.size() );

WritetoLog("Captured screen successfully");
}

GClements
09-27-2013, 09:54 AM
I'm currently trying to make a function that will take a screenshot and output it as a .bmp file. I almost have it working (it works perfectly if the screen has dimensions that are multiples of 4), but I am having trouble padding the file properly. To my knowledge, the pixel array is supposed to be padded to have row sizes that are multiples of 4 bytes.

Note that OpenGL also pads pixel data to a multiple of 4 bytes by default, although this can be changed with glPixelStorei(GL_PACK_ALIGNMENT). With the default 4-byte alignment, you should just be able to write the data returned by glReadPixels() directly to the file (preceded by the BMP headers), although you'll need to allocate a larger buffer than "width * height * 3", e.g.:



size_t stride = (WindowWidth * 3 + (4-1))/4*4;
GLubyte* Pixels = new GLubyte[ stride * WindowHeight ];

Niven07
09-27-2013, 04:05 PM
Note that OpenGL also pads pixel data to a multiple of 4 bytes by default

Wish I had known that sooner. I'll have to do a little more research on functions next time I try something like this. Also, thank you for the advice. I have the function working correctly now.