[quake3-commits] r1944 - in trunk/code: client qcommon renderer
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Mon Apr 18 12:06:11 EDT 2011
Author: thilo
Date: 2011-04-18 12:06:10 -0400 (Mon, 18 Apr 2011)
New Revision: 1944
Modified:
trunk/code/client/cl_avi.c
trunk/code/qcommon/q_shared.h
trunk/code/qcommon/qcommon.h
trunk/code/renderer/tr_image_jpg.c
trunk/code/renderer/tr_init.c
trunk/code/renderer/tr_local.h
Log:
- 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)
Modified: trunk/code/client/cl_avi.c
===================================================================
--- trunk/code/client/cl_avi.c 2011-04-18 00:39:15 UTC (rev 1943)
+++ trunk/code/client/cl_avi.c 2011-04-18 16:06:10 UTC (rev 1944)
@@ -368,9 +368,13 @@
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 @@
{
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 @@
{
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;
Modified: trunk/code/qcommon/q_shared.h
===================================================================
--- trunk/code/qcommon/q_shared.h 2011-04-18 00:39:15 UTC (rev 1943)
+++ trunk/code/qcommon/q_shared.h 2011-04-18 16:06:10 UTC (rev 1944)
@@ -177,7 +177,8 @@
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)))
Modified: trunk/code/qcommon/qcommon.h
===================================================================
--- trunk/code/qcommon/qcommon.h 2011-04-18 00:39:15 UTC (rev 1943)
+++ trunk/code/qcommon/qcommon.h 2011-04-18 16:06:10 UTC (rev 1944)
@@ -1008,6 +1008,9 @@
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
//
Modified: trunk/code/renderer/tr_image_jpg.c
===================================================================
--- trunk/code/renderer/tr_image_jpg.c 2011-04-18 00:39:15 UTC (rev 1943)
+++ trunk/code/renderer/tr_image_jpg.c 2011-04-18 16:06:10 UTC (rev 1944)
@@ -358,7 +358,7 @@
=================
*/
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;
@@ -398,7 +398,7 @@
/* 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.
@@ -422,7 +422,7 @@
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;
@@ -430,7 +430,7 @@
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);
Modified: trunk/code/renderer/tr_init.c
===================================================================
--- trunk/code/renderer/tr_init.c 2011-04-18 00:39:15 UTC (rev 1943)
+++ trunk/code/renderer/tr_init.c 2011-04-18 16:06:10 UTC (rev 1944)
@@ -353,15 +353,62 @@
/*
==================
+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 @@
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);
+ buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen);
+ memcount = (width * 3 + padlen) * height;
- qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
-
// 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 @@
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 @@
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 @@
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 @@
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 @@
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;
+ 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, cmd->captureBuffer);
+ GL_UNSIGNED_BYTE, cBuf);
- memcount = cmd->width * cmd->height * 3;
+ 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);
Modified: trunk/code/renderer/tr_local.h
===================================================================
--- trunk/code/renderer/tr_local.h 2011-04-18 00:39:15 UTC (rev 1943)
+++ trunk/code/renderer/tr_local.h 2011-04-18 16:06:10 UTC (rev 1944)
@@ -1695,9 +1695,10 @@
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 );
More information about the quake3-commits
mailing list