From a387252e93f1773acdfa0daa7c60992fcccf81b9 Mon Sep 17 00:00:00 2001 From: Richard Allen Date: Mon, 27 Jun 2011 19:59:12 +0000 Subject: [PATCH] IOQ3 commit 1944 --- reaction/code/client/cl_avi.c | 14 +- reaction/code/qcommon/q_shared.h | 3 +- reaction/code/qcommon/qcommon.h | 3 + reaction/code/renderer/tr_image_jpg.c | 8 +- reaction/code/renderer/tr_init.c | 205 +++++++++++++++++++------- reaction/code/renderer/tr_local.h | 5 +- 6 files changed, 171 insertions(+), 67 deletions(-) diff --git a/reaction/code/client/cl_avi.c b/reaction/code/client/cl_avi.c index 528ac607..dd7bd9bc 100644 --- a/reaction/code/client/cl_avi.c +++ b/reaction/code/client/cl_avi.c @@ -368,9 +368,13 @@ qboolean CL_OpenAVIForWriting( const char *fileName ) else afd.motionJpeg = qfalse; - // Buffers only need to store RGB pixels - afd.cBuffer = Z_Malloc(afd.width * afd.height * 3); - afd.eBuffer = Z_Malloc(afd.width * afd.height * 3); + // Buffers only need to store RGB pixels. + // Allocate a bit more space for the capture buffer to account for possible + // padding at the end of pixel lines, and padding for alignment + #define MAX_PACK_LEN 16 + afd.cBuffer = Z_Malloc((afd.width * 3 + MAX_PACK_LEN - 1) * afd.height + MAX_PACK_LEN - 1); + // raw avi files have pixel lines start on 4-byte boundaries + afd.eBuffer = Z_Malloc(PAD(afd.width * 3, AVI_LINE_PADDING) * afd.height); afd.a.rate = dma.speed; afd.a.format = WAV_FORMAT_PCM; @@ -468,7 +472,7 @@ void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size ) { int chunkOffset = afd.fileSize - afd.moviOffset - 8; int chunkSize = 8 + size; - int paddingSize = PAD( size, 2 ) - size; + int paddingSize = PADLEN(size, 2); byte padding[ 4 ] = { 0 }; if( !afd.fileOpen ) @@ -542,7 +546,7 @@ void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size ) { int chunkOffset = afd.fileSize - afd.moviOffset - 8; int chunkSize = 8 + bytesInBuffer; - int paddingSize = PAD( bytesInBuffer, 2 ) - bytesInBuffer; + int paddingSize = PADLEN(bytesInBuffer, 2); byte padding[ 4 ] = { 0 }; bufIndex = 0; diff --git a/reaction/code/qcommon/q_shared.h b/reaction/code/qcommon/q_shared.h index 0f537ea2..b582e7cb 100644 --- a/reaction/code/qcommon/q_shared.h +++ b/reaction/code/qcommon/q_shared.h @@ -175,7 +175,8 @@ typedef int sfxHandle_t; typedef int fileHandle_t; typedef int clipHandle_t; -#define PAD(x,y) (((x)+(y)-1) & ~((y)-1)) +#define PAD(x,y) (((x)+(y)-1) & ~((y)-1)) +#define PADLEN(x,y) (PAD((x), (y)) - (x)) #ifdef __GNUC__ #define QALIGN(x) __attribute__((aligned(x))) diff --git a/reaction/code/qcommon/qcommon.h b/reaction/code/qcommon/qcommon.h index 1e4da5d2..e9e89200 100644 --- a/reaction/code/qcommon/qcommon.h +++ b/reaction/code/qcommon/qcommon.h @@ -1008,6 +1008,9 @@ void S_ClearSoundBuffer( void ); void SCR_DebugGraph (float value, int color); // FIXME: move logging to common? +// AVI files have the start of pixel lines 4 byte-aligned +#define AVI_LINE_PADDING 4 + // // server interface // diff --git a/reaction/code/renderer/tr_image_jpg.c b/reaction/code/renderer/tr_image_jpg.c index 501f54e2..a7a19172 100644 --- a/reaction/code/renderer/tr_image_jpg.c +++ b/reaction/code/renderer/tr_image_jpg.c @@ -359,7 +359,7 @@ Expects RGB input data ================= */ size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, - int image_width, int image_height, byte *image_buffer) + int image_width, int image_height, byte *image_buffer, int padding) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -399,7 +399,7 @@ size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ - row_stride = image_width * cinfo.input_components; /* JSAMPLEs per row in image_buffer */ + row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */ while (cinfo.next_scanline < cinfo.image_height) { /* jpeg_write_scanlines expects an array of pointers to scanlines. @@ -423,7 +423,7 @@ size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, return outcount; } -void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer) +void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding) { byte *out; size_t bufSize; @@ -431,7 +431,7 @@ void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, bufSize = image_width * image_height * 3; out = ri.Hunk_AllocateTempMemory(bufSize); - bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer); + bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding); ri.FS_WriteFile(filename, out, bufSize); ri.Hunk_FreeTempMemory(out); diff --git a/reaction/code/renderer/tr_init.c b/reaction/code/renderer/tr_init.c index 658cf10a..08eb73f6 100644 --- a/reaction/code/renderer/tr_init.c +++ b/reaction/code/renderer/tr_init.c @@ -367,16 +367,63 @@ 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; - - buffer = ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*3+18); +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; + + allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen); + buffer = allbuf + offset - 18; Com_Memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type @@ -386,24 +433,39 @@ 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 ; iinteger, glConfig.vidWidth, glConfig.vidHeight, buffer); - - ri.Hunk_FreeTempMemory( buffer ); + RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen); + ri.Hunk_FreeTempMemory(buffer); } /* @@ -537,8 +596,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; @@ -546,17 +607,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; @@ -565,7 +625,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]; @@ -585,8 +646,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 ); } @@ -719,42 +780,76 @@ 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; + fbo_t *fbo; cmd = (const videoFrameCommand_t *)data; fbo = R_FBO_Bind(NULL); - - qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, - GL_UNSIGNED_BYTE, cmd->captureBuffer); - memcount = cmd->width * cmd->height * 3; + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + 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; R_FBO_Bind(fbo); // 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); diff --git a/reaction/code/renderer/tr_local.h b/reaction/code/renderer/tr_local.h index 989b8170..a1a9321e 100644 --- a/reaction/code/renderer/tr_local.h +++ b/reaction/code/renderer/tr_local.h @@ -2556,9 +2556,10 @@ void RE_StretchPic ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void RE_BeginFrame( stereoFrame_t stereoFrame ); void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); -void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer); +void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, + unsigned char *image_buffer, int padding); size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, - int image_width, int image_height, byte *image_buffer); + int image_width, int image_height, byte *image_buffer, int padding); void RE_TakeVideoFrame( int width, int height, byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg );