/*
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted,
** provided that the above copyright notice appear in all copies and that
** both that copyright notice and this permission notice appear in
** supporting documentation.
**
** This file is provided AS IS with no warranties of any kind.  The author
** shall have no liability with respect to the infringement of copyrights,
** trade secrets or any patents by this file or any part thereof.  In no
** event will the author be liable for any lost revenue or profits or
** other special, indirect and consequential damages.
**
** Revision History:
**
** 20030828	1.0		acd		First Release.
**							Changed to get dimensions from window.
*/

#include <X11/Intrinsic.h>
#include <stdio.h>
#include <stdlib.h>
#include <jpeglib.h>
#include <X11/xpm.h>

#define	RGB_SIZE	3	/* one byte each for r, g and b */

/*
** function to grab image data from screen and create a jpeg from it.
** each time it's called the image name will increment giving a sequence.
**
** uses libjpeg and needs -ljpeg during linking.
**
** use -geometry {w}x{h} on command line to set grab size.
** pal vcd is 352x288. use 702x576 and scale down for anti-aliasing.
** convert to movie using something like this:
**		mencoder -mf on -ovc lavc -lavcopts vcodec=mpeg4 grab\*.jpg
*/
void
grab_frame(Display *display, Window window)
{
	static int					my_grabcount = 0;
	char						grabname[100];
	FILE						*outfile;
	XImage						*grabimage;
	unsigned char				*row_pointer;
	unsigned char				*end;
	int							i;
	unsigned char				*my_buffer = NULL;
	struct jpeg_compress_struct	cinfo;
	struct jpeg_error_mgr		jerr;
	XWindowAttributes			xgwa;

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);
	sprintf(grabname, "grab%05d.jpg", my_grabcount);
	if ((outfile = fopen(grabname, "wb")) == NULL)
	{
		fprintf(stderr, "can't open %s\n", grabname);
		exit(-1);
	}
	jpeg_stdio_dest(&cinfo, outfile);

	/* get window dimensions */
	XGetWindowAttributes (display, window, &xgwa);

	cinfo.image_width = xgwa.width;
	cinfo.image_height = xgwa.height;
	cinfo.input_components = RGB_SIZE; /* # of color components per pixel */
	cinfo.in_color_space = JCS_RGB; /* colorspace of input image */

	/* buffer for line - rgb * width */
	my_buffer = (unsigned char *)malloc(xgwa.width * RGB_SIZE);
	if (my_buffer == NULL)
	{
		fprintf(stderr, "Cannot allocate memory for grab buffer\n");
		exit(-1);
	}

	jpeg_set_defaults(&cinfo);
	jpeg_set_quality (&cinfo, 100, TRUE);

	jpeg_start_compress(&cinfo, TRUE);

	grabimage = XGetImage(display, window, 0, 0, xgwa.width, xgwa.height, ~0L, ZPixmap);
	row_pointer = grabimage->data;

	end = row_pointer + (grabimage->bytes_per_line * xgwa.height);

	while (row_pointer < end)
	{

		/* 
		** XGetImage gives us back a 32 bit value (rgba) but libjpeg only
		** wants 24 bits (rgb) so compact the current scanline into my_buffer
		** by ignoring every 4th byte
		*/

		int	j = 0;

		for (i = 0 ; i < grabimage->bytes_per_line ; i += 4) {
			/* acd XXX sneaky byte swapping here, red becomes blue otherwise */
			/* almost definately an endian thing */
			my_buffer[j++] = row_pointer[i + 2];
			my_buffer[j++] = row_pointer[i + 1];
			my_buffer[j++] = row_pointer[i + 0];
		}

		/*printf("scanline:%d\n", cinfo.next_scanline);*/
		jpeg_write_scanlines(&cinfo, &my_buffer, 1);
		row_pointer += grabimage->bytes_per_line;
	}

	free(grabimage->data);
	grabimage->data = NULL;
	XDestroyImage(grabimage);

	jpeg_finish_compress(&cinfo);

	jpeg_destroy_compress(&cinfo);

	my_grabcount++;
	if ((my_grabcount % 1000) == 0)
	{
		printf("Grabbed [%d]\n", my_grabcount);
	}

	if (my_buffer != NULL)
	{
		free(my_buffer);
		my_buffer = NULL;
	}
	fclose(outfile);
}

