- Fix data alignment issue with glReadPixel call, reported by Guillaume Bougard (#4954)

- Fix data alignment issue in raw AVI recording for weird resolutions (like 1366x768)
This commit is contained in:
Thilo Schulz 2011-04-18 16:06:10 +00:00
parent 5769bed4a3
commit 6a203bc8e9
6 changed files with 170 additions and 66 deletions

View file

@ -351,17 +351,64 @@ FIXME: the statics don't get a reinit between fs_game changes
==============================================================================
*/
/*
==================
RB_ReadPixels
Reads an image but takes care of alignment issues for reading RGB images.
Reads a minimum offset for where the RGB data starts in the image from
integer stored at pointer offset. When the function has returned the actual
offset was written back to address offset. This address will always have an
alignment of packAlign to ensure efficient copying.
Stores the length of padding after a line of pixels to address padlen
Return value must be freed with ri.Hunk_FreeTempMemory()
==================
*/
byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen)
{
byte *buffer, *bufstart;
int padwidth, linelen;
GLint packAlign;
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
linelen = width * 3;
padwidth = PAD(linelen, packAlign);
// Allocate a few more bytes so that we can choose an alignment we like
buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1);
bufstart = (byte *) PAD((intptr_t) buffer + *offset, packAlign);
qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart);
*offset = bufstart - buffer;
*padlen = padwidth - linelen;
return buffer;
}
/*
==================
RB_TakeScreenshot
==================
*/
void RB_TakeScreenshot( int x, int y, int width, int height, char *fileName ) {
byte *buffer;
int i, c, temp;
void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName)
{
byte *allbuf, *buffer;
byte *srcptr, *destptr;
byte *endline, *endmem;
byte temp;
int linelen, padlen;
size_t offset = 18, memcount;
buffer = ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*3+18);
allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen);
buffer = allbuf + offset - 18;
Com_Memset (buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = width & 255;
@ -370,49 +417,62 @@ void RB_TakeScreenshot( int x, int y, int width, int height, char *fileName ) {
buffer[15] = height >> 8;
buffer[16] = 24; // pixel size
qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 );
// swap rgb to bgr and remove padding from line endings
linelen = width * 3;
srcptr = destptr = allbuf + offset;
endmem = srcptr + (linelen + padlen) * height;
while(srcptr < endmem)
{
endline = srcptr + linelen;
// swap rgb to bgr
c = 18 + width * height * 3;
for (i=18 ; i<c ; i+=3) {
temp = buffer[i];
buffer[i] = buffer[i+2];
buffer[i+2] = temp;
while(srcptr < endline)
{
temp = srcptr[0];
*destptr++ = srcptr[2];
*destptr++ = srcptr[1];
*destptr++ = temp;
srcptr += 3;
}
// Skip the pad
srcptr += padlen;
}
memcount = linelen * height;
// gamma correct
if ( glConfig.deviceSupportsGamma ) {
R_GammaCorrect( buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3 );
}
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(allbuf + offset, memcount);
ri.FS_WriteFile( fileName, buffer, c );
ri.FS_WriteFile(fileName, buffer, memcount + 18);
ri.Hunk_FreeTempMemory( buffer );
ri.Hunk_FreeTempMemory(allbuf);
}
/*
==================
RB_TakeScreenshotJPEG
==================
*/
void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName ) {
byte *buffer;
size_t memcount;
*/
memcount = glConfig.vidWidth * glConfig.vidHeight * 3;
void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName)
{
byte *buffer;
size_t offset = 0, memcount;
int padlen;
buffer = ri.Hunk_AllocateTempMemory(memcount);
qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen);
memcount = (width * 3 + padlen) * height;
// gamma correct
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(buffer, memcount);
R_GammaCorrect(buffer + offset, memcount);
ri.FS_WriteFile( fileName, buffer, 1 ); // create path
RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, glConfig.vidWidth, glConfig.vidHeight, buffer);
ri.Hunk_FreeTempMemory( buffer );
RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen);
ri.Hunk_FreeTempMemory(buffer);
}
/*
@ -518,8 +578,10 @@ the menu system, sampled down from full screen distorted images
void R_LevelShot( void ) {
char checkname[MAX_OSPATH];
byte *buffer;
byte *source;
byte *source, *allsource;
byte *src, *dst;
size_t offset = 0;
int padlen;
int x, y;
int r, g, b;
float xScale, yScale;
@ -527,17 +589,16 @@ void R_LevelShot( void ) {
Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName);
source = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight * 3 );
allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen);
source = allsource + offset;
buffer = ri.Hunk_AllocateTempMemory( 128 * 128*3 + 18);
buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18);
Com_Memset (buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = 128;
buffer[14] = 128;
buffer[16] = 24; // pixel size
qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source );
// resample from source
xScale = glConfig.vidWidth / 512.0f;
yScale = glConfig.vidHeight / 384.0f;
@ -546,7 +607,8 @@ void R_LevelShot( void ) {
r = g = b = 0;
for ( yy = 0 ; yy < 3 ; yy++ ) {
for ( xx = 0 ; xx < 4 ; xx++ ) {
src = source + 3 * ( glConfig.vidWidth * (int)( (y*3+yy)*yScale ) + (int)( (x*4+xx)*xScale ) );
src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) +
3 * (int) ((x*4 + xx) * xScale);
r += src[0];
g += src[1];
b += src[2];
@ -566,8 +628,8 @@ void R_LevelShot( void ) {
ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 );
ri.Hunk_FreeTempMemory( buffer );
ri.Hunk_FreeTempMemory( source );
ri.Hunk_FreeTempMemory(buffer);
ri.Hunk_FreeTempMemory(allsource);
ri.Printf( PRINT_ALL, "Wrote %s\n", checkname );
}
@ -700,37 +762,70 @@ RB_TakeVideoFrameCmd
const void *RB_TakeVideoFrameCmd( const void *data )
{
const videoFrameCommand_t *cmd;
size_t memcount;
int i;
byte *cBuf;
size_t memcount, linelen;
int padwidth, avipadwidth, padlen, avipadlen;
GLint packAlign;
cmd = (const videoFrameCommand_t *)data;
qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB,
GL_UNSIGNED_BYTE, cmd->captureBuffer);
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
memcount = cmd->width * cmd->height * 3;
linelen = cmd->width * 3;
// Alignment stuff for glReadPixels
padwidth = PAD(linelen, packAlign);
padlen = padwidth - linelen;
// AVI line padding
avipadwidth = PAD(linelen, AVI_LINE_PADDING);
avipadlen = avipadwidth - linelen;
cBuf = (byte *) PAD((intptr_t) cmd->captureBuffer, packAlign);
qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB,
GL_UNSIGNED_BYTE, cBuf);
memcount = padwidth * cmd->height;
// gamma correct
if( glConfig.deviceSupportsGamma )
R_GammaCorrect(cmd->captureBuffer, memcount);
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(cBuf, memcount);
if( cmd->motionJpeg )
if(cmd->motionJpeg)
{
memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, memcount,
memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height,
r_aviMotionJpegQuality->integer,
cmd->width, cmd->height, cmd->captureBuffer);
cmd->width, cmd->height, cBuf, padlen);
ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount);
}
else
{
for(i = 0; i < memcount; i += 3) // swap R and B
byte *lineend, *memend;
byte *srcptr, *destptr;
srcptr = cBuf;
destptr = cmd->encodeBuffer;
memend = srcptr + memcount;
// swap R and B and remove line paddings
while(srcptr < memend)
{
cmd->encodeBuffer[i] = cmd->captureBuffer[i + 2];
cmd->encodeBuffer[i + 1] = cmd->captureBuffer[i + 1];
cmd->encodeBuffer[i + 2] = cmd->captureBuffer[i];
lineend = srcptr + linelen;
while(srcptr < lineend)
{
*destptr++ = srcptr[2];
*destptr++ = srcptr[1];
*destptr++ = srcptr[0];
srcptr += 3;
}
Com_Memset(destptr, '\0', avipadlen);
destptr += avipadlen;
srcptr += padlen;
}
ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount);
ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height);
}
return (const void *)(cmd + 1);