//-----------------------------------------------------------------------------
//
// ImageLib Sources
// Copyright (C) 2002 by Denton Woods
// Copyright (C) 2002 Nelson Rush.
// Last modified: 05/18/2002
//
// Filename: src-ILUT/src/ilut_x11.c
//
// Description: X11 Pixmap and XImage binding functions (with XShm)
//
//-----------------------------------------------------------------------------

/*
** This file was created by Jesse Maurais Wed April 4, 2007
** Contact: www.jessemaurais@gmail.com
**
**
** This patch to the Devil Tookit binds to the X Windows System Version 11,
** using client-side XImages and server-side Pixmaps, with support for both
** ZPixmaps and the insane XYPixmap format. (Depends on the X server)
**
** If the XShm extension to X11 is present at the time of ./configure then
** support for shared memory XImages and Pixmaps is also compiled in. Note
** that "shared" does not mean Devil and X are sharing the same memory space.
** This is not possible, because both libraries make byte-for-byte copies
** of their data. It means that memory is part of an inter-process memory
** segment (see XShm spec).
**
** TODO
** 1) Assumed the display depth is 24 bits (the most common) but there should
** be a check, and iXConvertImage should handle this properly. I don't think
** this should be difficult to modify from whats here.
** 2) It would be nice to convert from an XImage back to a Devil image for
** saving changes. Would be seful for an interactive image editor.
** 3) Possibly some additional OpenGL bindings for GLX Pbuffers.
**
** FYI
** It was a long night figuring out the under-documented XYPixmap format.
*/


#include "ilut_internal.h"
#ifdef ILUT_USE_X11


int bits;	// bits per pixel
int field;	// bits per channel
int bytes;	// bytes per pixel
int grain;	// bytes per line
int width;	// pixels per line
int height;	// number of lines
ILpal* palette;	// for indexed colors
char* data;	// pointer to pixels



void iXGrabImage( ILimage * img )
{
	bits     = img->Bpp*8;	// bits per pixel
	field    = img->Bpc;	// bits per channel
	bytes    = img->Bpp;	// bytes per pixel
	grain    = img->Bps;	// bytes per line
	width    = img->Width;
	height   = img->Height;
	palette  = &img->Pal;
	data     = (char*)img->Data;
}


Bool iXGrabCurrentImage(void)
{
	ilutCurImage = ilGetCurImage();
	if (!ilutCurImage) {
		return False;
	}
	iXGrabImage(ilutCurImage);
	return True;
}


void iXConvertImage( Display * dpy, XImage * img )
{
	int x,y,z;
	int sX,dX;
	int sY,dY;
 	int sZ,dZ;
	int plane;


	ILimage * tmp;

	switch ( img->byte_order )
	{
	 case LSBFirst:
		tmp = iConvertImage( ilutCurImage,IL_BGR,IL_UNSIGNED_BYTE );
		break;
	 case MSBFirst:
		tmp = iConvertImage( ilutCurImage,IL_RGB,IL_UNSIGNED_BYTE );
		break;
	 default:
		return;
	}

	if ( !tmp ) return;
	iXGrabImage( tmp );


	switch ( img->format )
	{
	 case ZPixmap:

		for ( y = 0; y < height; y ++ )
		{
		 dY = y * img->bytes_per_line;
		 sY = y * grain;

		 for ( x = 0; x < width; x ++ )
		 {
		  dX = x * img->bits_per_pixel / 8;
		  sX = x * bytes;

		  for ( z = 0; z < bytes; z ++ )
		  {
		   img->data[dX+dY+z] = data[sX+sY+z];
		  }
		 }
		}

	 break;

	 case XYPixmap:

		for ( y = 0; y < height; y ++ )
		{
		 sY = y * grain;

		 for ( x = 0; x < width; x ++ )
		 {
		  sX = x * bytes;

		  for ( z = 0; z < bits; z ++ )
		  {
		   sZ = z / 8;
		   dZ = z % 8;

		   if ( data[sY+sX+sZ] & ( 1 << dZ ) )
		   {
		    plane = bits - z - 1;

		    sZ = x % 8;
		    dX = x / 8;
		    dY = y * img->bytes_per_line;
		    dZ = plane * img->bytes_per_line * height;

		    img->data[dZ+dY+dX] |= 1 << sZ;
		   }
		  }
		 }
		}

	 break;

	 default:
		ilSetError( ILUT_NOT_SUPPORTED );
	}

	ilCloseImage( tmp );
}

ILboolean ilutXInit(void) {
	return IL_TRUE;
}







XImage * ILAPIENTRY ilutXCreateImage( Display * dpy )
{
	Visual * vis;
	XImage * img;
	char * buffer;

	if (!iXGrabCurrentImage()) {
		return NULL;
	}

	buffer = malloc( width * height * 4 );
	if (!buffer) {
		return NULL;
	}

	vis = CopyFromParent;
	img = XCreateImage( dpy,vis, 24,ZPixmap,0,buffer,width,height,8,0 );
	if (!img) {
		free(buffer);
		return NULL;
	}

	iXConvertImage( dpy,img );

	return img;
}



Pixmap ILAPIENTRY ilutXCreatePixmap( Display * dpy, Drawable draw )
{
	XImage * img;
	GC gc;
	Pixmap pix;

	img = ilutXCreateImage( dpy );
	if (!img) {
		return None;
	}

	gc = DefaultGC(dpy,DefaultScreen(dpy));
	if (!gc) {
		XDestroyImage( img );
		return None;
	}

	pix = XCreatePixmap( dpy,draw, width,height,24 );
	if (!pix ) {
		XDestroyImage( img );
		return None;
	}

	XPutImage( dpy,pix,gc,img, 0,0,0,0,width,height );

	XDestroyImage( img );
	
	return pix;
}



XImage * ILAPIENTRY ilutXLoadImage( Display * dpy, char * filename )
{
	iBindImageTemp();
	if (!ilLoadImage(filename)) {
		return NULL;
	}
	return ilutXCreateImage( dpy );
}



Pixmap ILAPIENTRY ilutXLoadPixmap( Display * dpy, Drawable draw, char * filename )
{
	iBindImageTemp();
	if (!ilLoadImage(filename)) {
		return None;
	}
	return ilutXCreatePixmap( dpy,draw );
}






#ifdef ILUT_USE_XSHM
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>


XImage * ILAPIENTRY ilutXShmCreateImage( Display * dpy, XShmSegmentInfo * info )
{
	Visual * vis;
	XImage * img;

	// Get server supported format

	int size,format = XShmPixmapFormat( dpy );

	// Grab the current image

	if (!iXGrabCurrentImage()) {
		return NULL;
	}

	// Create a shared image

	vis = CopyFromParent;
	img = XShmCreateImage( dpy,vis, 24,format,NULL,info,width,height );

	if (!img) {
		return NULL;
	}

	// Create shared memory

	size = img->bytes_per_line * img->height;

	info->shmid = shmget( IPC_PRIVATE, size, IPC_CREAT | 0666 );
	info->shmaddr = img->data = shmat( info->shmid, 0, 0 );
	info->readOnly = False;

	// Attach to server

	XShmAttach( dpy,info );

	// Copy image pixels to shared memory

	iXConvertImage( dpy,img );

	return img;
}



void ILAPIENTRY ilutXShmDestroyImage( Display * dpy, XImage * img, XShmSegmentInfo * info )
{
	XShmDetach( dpy,info );
	XDestroyImage( img );
	XFlush( dpy );

	shmdt( info->shmaddr );
	shmctl( info->shmid, IPC_RMID, 0 );
}



Pixmap ILAPIENTRY ilutXShmCreatePixmap( Display * dpy, Drawable draw, XShmSegmentInfo * info )
{
	Pixmap pix;
	XImage*img;

	// Create a dumby image

	img = ilutXShmCreateImage( dpy,info );
	if (!img) {
		return None;
	}

	// Use the same memory segment in the pixmap

	pix = XShmCreatePixmap( dpy,draw, info->shmaddr,info,width,height,24 );
	if (!pix) {
		ilutXShmDestroyImage( dpy,img,info );
		return None;
	}

	// Riddance to the image

	XDestroyImage( img );

	return pix;
}



void ILAPIENTRY ilutXShmFreePixmap( Display * dpy, Pixmap pix, XShmSegmentInfo * info )
{
	XShmDetach( dpy,info );
	XFreePixmap( dpy,pix );
	XFlush( dpy );

	shmdt( info->shmaddr );
	shmctl( info->shmid, IPC_RMID, 0 );
}



XImage * ILAPIENTRY ilutXShmLoadImage( Display * dpy, char* filename, XShmSegmentInfo * info )
{
	iBindImageTemp();
	if (!ilLoadImage(filename)) {
		return NULL;
	}
	return ilutXShmCreateImage( dpy,info );
}



Pixmap ILAPIENTRY ilutXShmLoadPixmap( Display * dpy, Drawable draw, char* filename, XShmSegmentInfo * info )
{
	iBindImageTemp();
	if (!ilLoadImage(filename)) {
		return None;
	}
	return ilutXShmCreatePixmap( dpy,draw,info );
}





#endif//ILUT_USE_XSHM


#endif//ILUT_USE_X11

