[quake3-commits] r1936 - in trunk/code: client renderer

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Thu Mar 17 12:00:46 EDT 2011


Author: thilo
Date: 2011-03-17 12:00:45 -0400 (Thu, 17 Mar 2011)
New Revision: 1936

Modified:
   trunk/code/client/cl_avi.c
   trunk/code/renderer/tr_image_jpg.c
   trunk/code/renderer/tr_init.c
   trunk/code/renderer/tr_local.h
Log:
Fix JPEG compression for screenshots and mjpeg video recording with new JPG library


Modified: trunk/code/client/cl_avi.c
===================================================================
--- trunk/code/client/cl_avi.c	2011-03-17 12:53:47 UTC (rev 1935)
+++ trunk/code/client/cl_avi.c	2011-03-17 16:00:45 UTC (rev 1936)
@@ -368,8 +368,9 @@
   else
     afd.motionJpeg = qfalse;
 
-  afd.cBuffer = Z_Malloc( afd.width * afd.height * 4 );
-  afd.eBuffer = Z_Malloc( afd.width * afd.height * 4 );
+  // 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);
 
   afd.a.rate = dma.speed;
   afd.a.format = WAV_FORMAT_PCM;

Modified: trunk/code/renderer/tr_image_jpg.c
===================================================================
--- trunk/code/renderer/tr_image_jpg.c	2011-03-17 12:53:47 UTC (rev 1935)
+++ trunk/code/renderer/tr_image_jpg.c	2011-03-17 16:00:45 UTC (rev 1936)
@@ -86,10 +86,10 @@
   struct jpeg_error_mgr jerr;
   /* More stuff */
   JSAMPARRAY buffer;		/* Output row buffer */
-  unsigned row_stride;		/* physical row width in output buffer */
-  unsigned pixelcount, memcount;
-  int sindex, dindex;
-  unsigned char *out;
+  unsigned int row_stride;	/* physical row width in output buffer */
+  unsigned int pixelcount, memcount;
+  unsigned int sindex, dindex;
+  byte *out;
   int len;
 	union {
 		byte *b;
@@ -161,7 +161,11 @@
       || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3
     )
   {
-    ri.Error (ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d\n", filename,
+    // Free the memory to make sure we don't leak memory
+    ri.FS_FreeFile (fbuffer.v);
+    jpeg_destroy_decompress(&cinfo);
+  
+    ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d\n", filename,
 		    cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components);
   }
 
@@ -211,7 +215,7 @@
 
   /* Step 7: Finish decompression */
 
-  (void) jpeg_finish_decompress(&cinfo);
+  jpeg_finish_decompress(&cinfo);
   /* We can ignore the return value since suspension is not possible
    * with the stdio data source.
    */
@@ -289,7 +293,15 @@
 static boolean
 empty_output_buffer (j_compress_ptr cinfo)
 {
-  return TRUE;
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+  
+  jpeg_destroy_compress(cinfo);
+  
+  // Make crash fatal or we would probably leak memory.
+  ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes\n",
+           dest->size);
+
+  return FALSE;
 }
 
 /*
@@ -301,14 +313,8 @@
  * for error exit.
  */
 
-static int hackSize;
-
-static void
-term_destination (j_compress_ptr cinfo)
+static void term_destination(j_compress_ptr cinfo)
 {
-  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
-  size_t datacount = dest->size - dest->pub.free_in_buffer;
-  hackSize = datacount;
 }
 
 
@@ -343,143 +349,40 @@
   dest->size = size;
 }
 
-void SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer) {
-  /* This struct contains the JPEG compression parameters and pointers to
-   * working space (which is allocated as needed by the JPEG library).
-   * It is possible to have several such structures, representing multiple
-   * compression/decompression processes, in existence at once.  We refer
-   * to any one struct (and its associated working data) as a "JPEG object".
-   */
-  struct jpeg_compress_struct cinfo;
-  /* This struct represents a JPEG error handler.  It is declared separately
-   * because applications often want to supply a specialized error handler
-   * (see the second half of this file for an example).  But here we just
-   * take the easy way out and use the standard error handler, which will
-   * print a message on stderr and call exit() if compression fails.
-   * Note that this struct must live as long as the main JPEG parameter
-   * struct, to avoid dangling-pointer problems.
-   */
-  struct jpeg_error_mgr jerr;
-  /* More stuff */
-  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
-  int row_stride;		/* physical row width in image buffer */
-  unsigned char *out;
-
-  /* Step 1: allocate and initialize JPEG compression object */
-
-  /* We have to set up the error handler first, in case the initialization
-   * step fails.  (Unlikely, but it could happen if you are out of memory.)
-   * This routine fills in the contents of struct jerr, and returns jerr's
-   * address which we place into the link field in cinfo.
-   */
-  cinfo.err = jpeg_std_error(&jerr);
-  /* Now we can initialize the JPEG compression object. */
-  jpeg_create_compress(&cinfo);
-
-  /* Step 2: specify data destination (eg, a file) */
-  /* Note: steps 2 and 3 can be done in either order. */
-
-  /* Here we use the library-supplied code to send compressed data to a
-   * stdio stream.  You can also write your own code to do something else.
-   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
-   * requires it in order to write binary files.
-   */
-  out = ri.Hunk_AllocateTempMemory(image_width*image_height*4);
-  jpegDest(&cinfo, out, image_width*image_height*4);
-
-  /* Step 3: set parameters for compression */
-
-  /* First we supply a description of the input image.
-   * Four fields of the cinfo struct must be filled in:
-   */
-  cinfo.image_width = image_width; 	/* image width and height, in pixels */
-  cinfo.image_height = image_height;
-  cinfo.input_components = 4;		/* # of color components per pixel */
-  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
-  /* Now use the library's routine to set default compression parameters.
-   * (You must set at least cinfo.in_color_space before calling this,
-   * since the defaults depend on the source color space.)
-   */
-  jpeg_set_defaults(&cinfo);
-  /* Now you can set any non-default parameters you wish to.
-   * Here we just illustrate the use of quality (quantization table) scaling:
-   */
-  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
-  /* If quality is set high, disable chroma subsampling */
-  if (quality >= 85) {
-    cinfo.comp_info[0].h_samp_factor = 1;
-    cinfo.comp_info[0].v_samp_factor = 1;
-  }
-
-  /* Step 4: Start compressor */
-
-  /* TRUE ensures that we will write a complete interchange-JPEG file.
-   * Pass TRUE unless you are very sure of what you're doing.
-   */
-  jpeg_start_compress(&cinfo, TRUE);
-
-  /* Step 5: while (scan lines remain to be written) */
-  /*           jpeg_write_scanlines(...); */
-
-  /* Here we use the library's state variable cinfo.next_scanline as the
-   * loop counter, so that we don't have to keep track ourselves.
-   * To keep things simple, we pass one scanline per call; you can pass
-   * more if you wish, though.
-   */
-  row_stride = image_width * 4;	/* JSAMPLEs per row in image_buffer */
-
-  while (cinfo.next_scanline < cinfo.image_height) {
-    /* jpeg_write_scanlines expects an array of pointers to scanlines.
-     * Here the array is only one element long, but you could pass
-     * more than one scanline at a time if that's more convenient.
-     */
-    row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
-    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
-  }
-
-  /* Step 6: Finish compression */
-
-  jpeg_finish_compress(&cinfo);
-  /* After finish_compress, we can close the output file. */
-  ri.FS_WriteFile( filename, out, hackSize );
-
-  ri.Hunk_FreeTempMemory(out);
-
-  /* Step 7: release JPEG compression object */
-
-  /* This is an important step since it will release a good deal of memory. */
-  jpeg_destroy_compress(&cinfo);
-
-  /* And we're done! */
-}
-
 /*
 =================
 SaveJPGToBuffer
+
+Encodes JPEG from image in image_buffer and writes to buffer.
+Expects RGB input data
 =================
 */
-int SaveJPGToBuffer( byte *buffer, int quality,
-    int image_width, int image_height,
-    byte *image_buffer )
+size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
+    int image_width, int image_height, byte *image_buffer)
 {
   struct jpeg_compress_struct cinfo;
   struct jpeg_error_mgr jerr;
   JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  my_dest_ptr dest;
   int row_stride;		/* physical row width in image buffer */
+  size_t outcount;
 
   /* Step 1: allocate and initialize JPEG compression object */
   cinfo.err = jpeg_std_error(&jerr);
+  cinfo.err->error_exit = R_JPGErrorExit;
+  cinfo.err->output_message = R_JPGOutputMessage;
+
   /* Now we can initialize the JPEG compression object. */
   jpeg_create_compress(&cinfo);
 
   /* Step 2: specify data destination (eg, a file) */
   /* Note: steps 2 and 3 can be done in either order. */
-  jpegDest(&cinfo, buffer, image_width*image_height*4);
+  jpegDest(&cinfo, buffer, bufSize);
 
   /* Step 3: set parameters for compression */
   cinfo.image_width = image_width; 	/* image width and height, in pixels */
   cinfo.image_height = image_height;
-  cinfo.input_components = 4;		/* # of color components per pixel */
+  cinfo.input_components = 3;		/* # of color components per pixel */
   cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
 
   jpeg_set_defaults(&cinfo);
@@ -495,23 +398,40 @@
 
   /* Step 5: while (scan lines remain to be written) */
   /*           jpeg_write_scanlines(...); */
-  row_stride = image_width * 4;	/* JSAMPLEs per row in image_buffer */
-
+  row_stride = image_width * cinfo.input_components; /* JSAMPLEs per row in image_buffer */
+  
   while (cinfo.next_scanline < cinfo.image_height) {
     /* jpeg_write_scanlines expects an array of pointers to scanlines.
      * Here the array is only one element long, but you could pass
      * more than one scanline at a time if that's more convenient.
      */
-    row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
+    row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
     (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
   }
 
   /* Step 6: Finish compression */
   jpeg_finish_compress(&cinfo);
-
+  
+  dest = (my_dest_ptr) cinfo.dest;
+  outcount = dest->size - dest->pub.free_in_buffer;
+ 
   /* Step 7: release JPEG compression object */
   jpeg_destroy_compress(&cinfo);
 
   /* And we're done! */
-  return hackSize;
+  return outcount;
 }
+
+void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer)
+{
+  byte *out;
+  size_t bufSize;
+
+  bufSize = image_width * image_height * 3;
+  out = ri.Hunk_AllocateTempMemory(bufSize);
+
+  bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer);
+  ri.FS_WriteFile(filename, out, bufSize);
+
+  ri.Hunk_FreeTempMemory(out);
+}

Modified: trunk/code/renderer/tr_init.c
===================================================================
--- trunk/code/renderer/tr_init.c	2011-03-17 12:53:47 UTC (rev 1935)
+++ trunk/code/renderer/tr_init.c	2011-03-17 16:00:45 UTC (rev 1936)
@@ -397,18 +397,20 @@
 */  
 void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName ) {
 	byte		*buffer;
+	size_t memcount;
 
-	buffer = ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4);
+	memcount = glConfig.vidWidth * glConfig.vidHeight * 3;
 
-	qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); 
+	buffer = ri.Hunk_AllocateTempMemory(memcount);
 
+	qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
+
 	// gamma correct
-	if ( glConfig.deviceSupportsGamma ) {
-		R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 );
-	}
+	if(glConfig.deviceSupportsGamma)
+		R_GammaCorrect(buffer, memcount);
 
 	ri.FS_WriteFile( fileName, buffer, 1 );		// create path
-	SaveJPG( fileName, r_screenshotJpegQuality->integer, glConfig.vidWidth, glConfig.vidHeight, buffer);
+	RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, glConfig.vidWidth, glConfig.vidHeight, buffer);
 
 	ri.Hunk_FreeTempMemory( buffer );
 }
@@ -698,36 +700,37 @@
 const void *RB_TakeVideoFrameCmd( const void *data )
 {
 	const videoFrameCommand_t	*cmd;
-	int												frameSize;
-	int												i;
+	size_t				memcount;
+	int				i;
 	
 	cmd = (const videoFrameCommand_t *)data;
 	
-	qglReadPixels( 0, 0, cmd->width, cmd->height, GL_RGBA,
-			GL_UNSIGNED_BYTE, cmd->captureBuffer );
+	qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB,
+		GL_UNSIGNED_BYTE, cmd->captureBuffer);
 
+	memcount = cmd->width * cmd->height * 3;
+
 	// gamma correct
 	if( glConfig.deviceSupportsGamma )
-		R_GammaCorrect( cmd->captureBuffer, cmd->width * cmd->height * 4 );
+		R_GammaCorrect(cmd->captureBuffer, memcount);
 
 	if( cmd->motionJpeg )
 	{
-		frameSize = SaveJPGToBuffer( cmd->encodeBuffer, r_aviMotionJpegQuality->integer,
-				cmd->width, cmd->height, cmd->captureBuffer );
-		ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize );
+		memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, memcount,
+			r_aviMotionJpegQuality->integer,
+			cmd->width, cmd->height, cmd->captureBuffer);
+		ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount);
 	}
 	else
 	{
-		frameSize = cmd->width * cmd->height;
-
-		for( i = 0; i < frameSize; i++)    // Pack to 24bpp and swap R and B
+		for(i = 0; i < memcount; i += 3)    // swap R and B
 		{
-			cmd->encodeBuffer[ i*3 ]     = cmd->captureBuffer[ i*4 + 2 ];
-			cmd->encodeBuffer[ i*3 + 1 ] = cmd->captureBuffer[ i*4 + 1 ];
-			cmd->encodeBuffer[ i*3 + 2 ] = cmd->captureBuffer[ i*4 ];
+			cmd->encodeBuffer[i]     = cmd->captureBuffer[i + 2];
+			cmd->encodeBuffer[i + 1] = cmd->captureBuffer[i + 1];
+			cmd->encodeBuffer[i + 2] = cmd->captureBuffer[i];
 		}
 
-		ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize * 3 );
+		ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount);
 	}
 
 	return (const void *)(cmd + 1);	

Modified: trunk/code/renderer/tr_local.h
===================================================================
--- trunk/code/renderer/tr_local.h	2011-03-17 12:53:47 UTC (rev 1935)
+++ trunk/code/renderer/tr_local.h	2011-03-17 16:00:45 UTC (rev 1936)
@@ -1695,10 +1695,9 @@
 					  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 SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer);
-int SaveJPGToBuffer( byte *buffer, int quality,
-		int image_width, int image_height,
-		byte *image_buffer );
+void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer);
+size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
+		int image_width, int image_height, byte *image_buffer);
 void RE_TakeVideoFrame( int width, int height,
 		byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg );
 



More information about the quake3-commits mailing list