/*===========================================================================
  Copyright (C) 2000 Craig B. Markwardt
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/***********************************************************************
*                                                               
*   file idilocal4.c
*                                                                 
*   IDI  Device Dependent Routines                                 
*   (version for XWindows release 11)                               
*                                                                    
*
* V 1.00 001129: C. Markwardt - conversions for 24- and 16-bit visuals
*
*   get_plane_offset     : convert bitmask to a shift offset
*   idi_conv32_24        : convert 32-bit RGB to 24-bit RGB
*   idi_conv24_32        : convert 24-bit RGB to 32-bit RGB
*   idi_conv32_16        : convert 32-bit RGB to 16-bit RGB
*   idi_conv16_32        : convert 16-bit RGB to 32-bit RGB
*   idi_order32          : reorder 32-bit RGB
*   idi_putimage         : wrapper for XPutImage
*   idi_getsubimage      : wrapper for XGetSubImage
*   test_swap		 : check, if byte swap is needed
    
 090120		last modif

************************************************************************/

/*

*/

#include    <stdlib.h>

#include    <idi.h>
#include    <idistruct.h>
#include    <x11defs.h>
#include    <proto_idi.h>


/* Determine the shift to apply to 8-bit data to align with "mask" */
int
get_plane_offset(mask, wordsize)
     unsigned int mask, wordsize;
{
  int i;
  for (i=31; i>=0; i--)
    if ( (mask>>i) & 1 ) return (i+1-wordsize);
  return 0;
}
/*

*/

/* Convert 32-bit input to 24-bit output.  The image data sizes are
   the same, hence each line output line will be padded by 25%.  Note
   that this function allocates memory which must be freed by the
   caller. */

unsigned char *idi_conv32_24(image, src)
XImage *image;
unsigned char *src;

{
  unsigned char *dst, *p, *q;
  unsigned int nbytes;
  unsigned int test = 0x000000ff;
  unsigned char *ptest = (unsigned char *)&test;
  int lend;
  int j, i;

  if ( *ptest == 0xff ) lend = 1; else lend = 0;

  /* Allocate a destination image */
  nbytes = image->bytes_per_line*image->height;
  dst = malloc((size_t)nbytes);
  if (dst == 0) 
    return 0;

  /* Zero the new image data */
  q = dst;
  for (i=0; i<nbytes; i++) 
    *q++ = 0;
  
  for (j=0; j<image->height; j++) {
    p = src + image->bytes_per_line*j;
    q = dst + image->bytes_per_line*j;
    /* Note that for 24-bit data, all we have to do is copy the 3
       lsb's and discard the msb, while otherwise preserving the byte
       order. */
    if (lend) {
      /* Little-endian - discard msb at end of each source word */
      for (i=0; i<image->width; i++) {
	*q++ = *p++;   *q++ = *p++;   *q++ = *p++;
	p++;
      } 
    } else {
      /* Big-endian - discard msb at beginning of each source word */
      for (i=0; i<image->width; i++) {
	p++;
	*q++ = *p++;   *q++ = *p++;   *q++ = *p++;
      } 
    }
  }

  return dst;
}
/*

*/

/* Convert 24-bit input to 32-bit output.  The image data sizes are
   the same, hence each input line must be padded by 25%.  Note that
   this function allocates memory which must be freed by the
   caller. */

unsigned char *idi_conv24_32(image, src)
XImage *image;
unsigned char *src;

{
  unsigned char *dst, *p, *q;
  unsigned int nbytes;
  unsigned int test = 0x000000ff;
  unsigned char *ptest = (unsigned char *)&test;
  int lend;
  int j, i;

  /* Test for endian-ness */
  if ( *ptest == 0xff ) lend = 1; else lend = 0;
#if 0
  fprintf(stderr, "%s-endian\n", lend?"little":"big");
  fprintf(stderr, "red_mask   = 0x%08x\n", image->red_mask);
  fprintf(stderr, "green_mask = 0x%08x\n", image->green_mask);
  fprintf(stderr, "blue_mask  = 0x%08x\n", image->blue_mask);
#endif

  /* Allocate a destination image */
  nbytes = image->bytes_per_line*image->height;
  dst = malloc((size_t)nbytes);
  if (dst == 0) 
    return 0;
  
  /* Zero the new image data */
  q = dst;
  for (i=0; i<nbytes; i++) 
    *q++ = 0;
  
  for (j=0; j<image->height; j++) {
    p = src + image->bytes_per_line*j;
    q = dst + image->bytes_per_line*j;
    /* Note that for 24-bit data, all we have to do is copy the 3
       lsb's and discard the msb, while otherwise preserving the byte
       order. */
    if (lend > 0) {
      /* Little-endian - insert msb at end of each dest word */
      for (i=0; i<image->width; i++) {
	*q++ = *p++;   *q++ = *p++;   *q++ = *p++;
	q++;
      } 
    } else {
      /* Big-endian - insert msb at beginning of each dest word */
      for (i=0; i<image->width; i++) {
	q++;
	*q++ = *p++;   *q++ = *p++;   *q++ = *p++;
      } 
    }
  }

  return dst;
}
/*

*/

/* Convert 32-bit input to 16-bit output.
   The main work is now done in function `wr_lut', here we just
   convert from unsignded int (4 bytes) to unsigned short int (2 bytes). */

unsigned char *idi_conv32_16(image, src, wordsize)
XImage *image;
unsigned char *src;
unsigned int wordsize;

{
unsigned char *dst, *p, *q;
unsigned short int *qi;
unsigned int *pi;
unsigned int word, nbytes;
int j, i;


/* allocate a destination image */

nbytes = image->bytes_per_line*image->height;
dst = malloc((size_t)nbytes);
if (dst == (unsigned char *) 0) return 0;


/* copy/convert from source to dest. image */

p = src;
q = dst;

for (j=0; j<image->height; j++)
   {
   pi = (unsigned int *) p;
   qi = (unsigned short int *) q;

   for (i=0; i<image->width; i++)
      {
      word = *pi++;
      *qi++ = (unsigned short int) word;
      }
   p += image->bytes_per_line;
   q += image->bytes_per_line;
   }

return dst;
}
/*

*/

/* Convert 16-bit input to 32-bit output.  The image data sizes are
   the same, hence each input line must be padded by 50%.  Note that
   this function allocates memory which must be freed by the
   caller. Also, the conversion from 16-bit to 24-bit assumes that the
   low-order bits are zero. */

unsigned char *idi_conv16_32(image, src, wordsize)
XImage *image;
unsigned char *src;
unsigned int wordsize;

{
  unsigned char *dst, *p, *q;
  unsigned short int *pi;
  unsigned int *qi;
  unsigned int nbytes;
  unsigned char r, g, b;
  unsigned int rm, gm, bm;   /* Red green and blue masks */
  unsigned int ro, go, bo;   /* Red green and blue mask offsets */
  unsigned short int word;
  int j, i;

  rm = image->red_mask;
  gm = image->green_mask;
  bm = image->blue_mask;
  ro = get_plane_offset(rm, wordsize);
  go = get_plane_offset(gm, wordsize);
  bo = get_plane_offset(bm, wordsize);
  if (ro < 0 || go < 0 || bo < 0) 
    return 0;

  /* Allocate a destination image */
  nbytes = image->bytes_per_line*image->height;
  dst = malloc((size_t)nbytes);
  if (dst == 0) 
    return 0;
  
  for (i=0, q=dst; i<nbytes; i++) 
    *q++ = 0;
  
  for (j=0; j<image->height; j++) {
    p = src + image->bytes_per_line*j;
    q = dst + image->bytes_per_line*j;
    pi = (unsigned short int *) p;
    qi = (unsigned int *) q;
    /* Note that for 16-bit input data the red element in the 24-bit
       output data will always be in the LSB, given the way that
       RGBord is constructed */
    if (ro > 0) {
      /* bo must be negative */
      for (i=0; i<image->width; i++) {
	word = *pi++;
	r = (word & rm) >>  ro;
	g = (word & gm) >>  go;
	b = (word & bm) << -bo;
	*qi++ = r | (g << 8) | (b << 16);
      } 
    } else {
      /* ro must be negative */
      for (i=0; i<image->width; i++) {
	word = *pi++;
	r = (word & rm) <<  -ro;
	g = (word & gm) >>  go;
	b = (word & bm) >>  bo;
	*qi++ = r | (g << 8) | (b << 16);
      }
    }
  }

  return dst;
}
/*

*/

/* Wrapper for XPutImage.  The arguments are identical.  This function
   takes an XImage either in 32-bit truecolor or 8-bit pseudocolor and
   converts to the appropriate bitsize and depth of the output
   drawable. No error checking is done on the arguments. */

void idi_putimage(display, draw, gc, image, sx, sy, dx, dy, width, height)
Display *display;
Drawable draw;
GC gc;
XImage *image;
int sx, sy, dx, dy;
unsigned int width, height;

{
  unsigned char *dst = 0;
  unsigned char *src = (unsigned char *)image->data;
  int wordsize = 8;

  /* Convert from a standard size, either 8-bit or 32-bit, to the
     native pixel size */
  switch (image->bits_per_pixel) {
    /* 32-bit true-color, and 8-bit pseudo color are output immediately */
  case 8:
  case 32:
    break;
  case 16:
    dst = idi_conv32_16(image, src, wordsize);  /* 32 bits to 16 bits */
    break;
  case 24:
    dst = idi_conv32_24(image, src);            /* 32 bits to 24 bits */
    break;
  }

  /* Temporarily insert the transformed data into the XImage, Put the
     image, and then restore the original data */
  if (dst) image->data = (char *) dst;
  XPutImage(display, draw, gc, image, sx, sy, dx, dy, width, height);
  image->data = (char *) src;
  
  if (dst) free(dst);
  return;
}

/*

*/

void idi_order32(no,image, src)
int  no;
XImage *image;
unsigned char *src;

{
int  i, ii, j, ioff;

register unsigned char  *p, *q, rr, gg, bb;



ii = image->bytes_per_line;
ioff = 0;


/* work on RGBord = 1 */

if (Xworkst[no].flag24[2] == 1) 	/* special swap flag */
   {
   for (j=0; j<image->height; j++)
      {
      p = q = src + ioff;

      for (i=0; i<image->width; i++)
         {
         rr = *p++;
         gg = *p++;
         bb = *p++;
         p++;

         *q++ = bb;
         *q++ = gg;
         *q++ = rr;
         q++;
         }

      ioff += ii;
      }
   }

else
   {
   for (j=0; j<image->height; j++)
      {
      p = q = src + ioff;

      for (i=0; i<image->width; i++)
         {
         p++;
         rr = *p++;
         gg = *p++;
         bb = *p++;

         q++;
         *q++ = bb;
         *q++ = gg;
         *q++ = rr;
         }

      ioff += ii;
      }
   }
}
/*

*/

/* Wrapper for XGetSubImage.  The arguments are identical.  This
   function reads an XImage from the drawable, in the native bitsize
   and depth of the drawable, and then converts to either an 8-bit
   pseudocolor or 32-bit RGB image.  No error checking is done on the
   arguments. */

XImage 
*idi_getsubimage(display,draw,x,y,width,height,plane_mask,format,dimage,no)
Display *display;
Drawable draw;
int x, y;
unsigned int width, height;
unsigned long plane_mask;
int format;
XImage *dimage;
int no;

{
XImage *image;

unsigned char *dst;



/* Do the actual XGetSubImage, which will return in the native
   depth and bit_per_pixel of the drawable */

image =
   XGetSubImage(display,draw,x,y,width,height,plane_mask,format,dimage,0,0);


/* Convert to a standard size, either 8-bit or 32-bit */
switch (image->bits_per_pixel) 
   {
  case 16: 				/* 16 bits to 32 bits */
   dst = idi_conv16_32(image,(unsigned char *) image->data, 8);
   break;				/* wordsize = 8 */

  case 24:          			/* 24 bits to 32 bits */
   dst = idi_conv24_32(image,(unsigned char *) image->data); 
   break;

  case 8:				/* 8-bit pseudo color, nothing to do */
   return image;

  case 32:				/* 32-bit true-color, reorder */
   if (Xworkst[no].RGBord == 1)
      idi_order32(no,image,(unsigned char *) image->data);
   return image;

  default:
   return image;
  }

/* Now copy the "destination" image back into the original "source" */
    
if (dst != (unsigned char *)0) 
   {
   unsigned int nbytes, i;
   register unsigned char *p, *q;
 
   nbytes = image->bytes_per_line * image->height;
   p = dst; 
   q = (unsigned char *) image->data;
   for (i=0; i<nbytes; i++) *q++ = *p++;

   free(dst);
   }

return image;
}
/*

*/

/* test_swap returns 1, if byte swapping needed, else 0 */

int test_swap(dspno)
int  dspno;
				/* `test_swap()' is copied from Xtest.c */
{
int   nn, mm;
int  *endian;
unsigned char hilo[4];



/* find byte order at Xhost */

nn = XImageByteOrder(mydisp[ididev[dspno].screen]);
if (nn == LSBFirst)
   nn = 0;                              /* least signif. byte first */
else
   nn = 1;                              /* most signif. byte first */


/* get byte order locally */

endian = (int *) &hilo[0];
*endian = 0x1256abef;
if (hilo[0] == 0x12)

   /* the most significant byte (0x12) is stored first,
      we have a big-endian architecture */
   mm = 1;

else

   /* the most significant byte (0x12) is stored last,
      we have a little-endian architecture */
   mm = 0;


if (mm == nn)
   return 0;
else
   return 1;
}

