/*
 *	$Source: /u1/Xr/src/Xrlib/Editor/RCS/pixelExt.c,v $
 *	$Header: pixelExt.c,v 1.1 86/12/17 09:06:57 swick Exp $
 */

#ifndef lint
static char *rcsid_pixelExt_c = "$Header: pixelExt.c,v 1.1 86/12/17 09:06:57 swick Exp $";
#endif	lint


#include <Xr/xr-copyright.h>

/* $Header: pixelExt.c,v 1.1 86/12/17 09:06:57 swick Exp $ */
/* Copyright 1986, Hewlett-Packard Company */
/* Copyright 1986, Massachussetts Institute of Technology */


static char rcsid[] = "$Header: pixelExt.c,v 1.1 86/12/17 09:06:57 swick Exp $";
/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        pixelExt.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: 
 **          This module contains the pixel extraction intrinsic
 **          routine.  It is capable of extracting pixel information
 **          from a raster image with a depth of 1 bit, or 1, 2, 3 or
 **          4 bytes.
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	pixelExt.c,v $
 * Revision 1.1  86/12/17  09:06:57  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:30:38  08:30:38  fred ()
 * Final QA Release
 * 
 * Revision 6.0  86/11/10  15:39:11  15:39:11  fred ()
 * QA #2 release
 * 
 * Revision 5.1  86/11/07  14:27:04  14:27:04  fred ()
 * Added new copyright message.
 * 
 * Revision 5.0  86/10/28  08:40:52  08:40:52  fred ()
 * QA #1.1 release
 * 
 * Revision 4.1  86/10/23  09:12:18  09:12:18  fred ()
 * Removed unused variables.
 * 
 * Revision 4.0  86/10/20  12:16:44  12:16:44  fred ()
 * QA #1 release
 * 
 * Revision 3.1  86/10/16  09:24:32  09:24:32  fred ()
 * Performance enhanced: added use of register variables.
 * 
 * Revision 3.0  86/10/02  16:06:46  16:06:46  fred ()
 * Alpha release set to 3.0
 * 
 * Revision 2.1  86/09/22  12:33:08  12:33:08  fred ()
 * Changed xrPixMap structure to xrPixmap structure.
 * 
 * Revision 2.0  86/09/16  08:16:52  08:16:52  fred ()
 * No change; upgraded to revision 2.0 to match other source.
 * 
 * Revision 1.4  86/09/15  06:48:37  06:48:37  fred ()
 * Filled in file headers, and broke up larger routines.
 * 
 * Revision 1.3  86/09/08  15:26:31  15:26:31  fred ()
 * Modified the bit per pixel extraction/setting routine so that
 * it processed the raster data as 16 bit quanities, instead of
 * 8 bit.  Also changed it so that the bits were ordered 15 ... 0
 * instead of 0 ... 15, within a word.
 * 
 * Revision 1.2  86/09/08  06:52:02  06:52:02  fred ()
 * Expanded to allow not only the extraction of pixel data, but
 * also to allow the setting of a pixel within a raster image.
 * 
 * Revision 1.1  86/09/03  14:00:20  14:00:20  fred ()
 * Initial revision
 * 
 *
 *****************************************************************************
 *************************************<+>*************************************/

#include <X/Xlib.h>
#include <Xr/defs.h>
#include <Xr/types.h>
#include <Xr/in_types.h>

extern xrPixelData * MsgNewHandler();
extern xrPixelData * MsgLocationHandler();
extern xrPixelData * MsgValueHandler();



/*************************************<->*************************************
 *
 *  _XrPixelExtract (instance, message, data)
 *
 *     xrPixelData * instance;
 *     INT32         message;
 *     INT8        * data;
 *
 *   Description:
 *   -----------
 *     This intrinsic extracts a single pixel of information from a
 *     specified raster image, or sets a single pixel of information
 *     within a raster image; the image can have a depth of 1 bit,
 *     or 1, 2, 3 or 4 bytes.  When the instance is first created, using
 *     the MSG_NEW message, the extraction location is set to (0,0), and
 *     an instance pointer is returned to the application; this instance
 *     pointer must be used for all subsequent requests made in regard to
 *     that particular raster image.  Using the MSG_LOCATION message,
 *     an application can change this extraction location to by any point
 *     within the bounds of the raster image.  The extraction location is
 *     automatically incremented after each MSG_GETVALUE or MSG_SETVALUE
 *     request, and will wrap back to (0,0), when the end of the raster
 *     image is reached.  When an extraction instance is no longer needed,
 *     it should be freed up, using the MSG_FREE message.
 *
 *
 *   Inputs:
 *   ------
 *     instance = For all messages except MSG_NEW, this must specify
 *                the instance which is to be affected by the message.
 *                For MSG_NEW, this should be set to NULL.
 *
 *     message  = This specifies the action to be performed by this
 *                intrinsic.
 *
 *     data     = This points to a message specific data structure.
 * 
 *   Outputs:
 *   -------
 *     All messages return the instance pointer upon successful
 *         completion; NULL is returned upon failure, and the global
 *         xrErrno will be set.
 *
 *   Procedures Called
 *   -----------------
 *   MsgNewHandler()
 *   MsgLocationHandler()
 *   MsgValueHandler()
 *
 *************************************<->***********************************/

xrPixelData *
_XrPixelExtract (instance, message, data)

   register xrPixelData * instance;
            INT32         message;
            INT8        * data;

{
   switch (message)
   {
      case MSG_NEW:
      {
         /*
          * Create a new extraction instance, and initialize its
          * instance structure; return a pointer to this structure
          * back to the application program.
          *
          * The 'data' parameter is interpreted as a pointer to an
          * instance of the 'xrPixmap' structure, with the 'depth',
          * 'height', 'width' and 'raster' fields filled out to describe
          * the pixmap which is to have its data extracted.
          */
         return ((xrPixelData *) MsgNewHandler (data));
      }

      case MSG_FREE:
      {
         /*
          * Destroy an active extraction instance; this involves
          * simply freeing up the memory occupied by its instance
          * structure.  The only parameter of interest is the
          * the instance pointer; 'data' is ignored.
          */

         if (instance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrPixelData *) NULL);
         }

         (*xrFree) (instance);
         return (instance);
      }

      case MSG_LOCATION:
      {
         /*
          * Set up a new extraction location within the raster
          * image.  If the point is outside the range of the
          * image's dimensions, then the request will fail. The
          * 'data' parameter is interpreted as a pointer to a POINT
          * structure, containing the new x and y extraction point.
          */
         return ((xrPixelData *) MsgLocationHandler (instance, data));
      }

      case MSG_SETVALUE:
      case MSG_GETVALUE:
      {
         /*
          * MSG_GETVALUE:
          *    Extract the next pixel value from the raster image,
          *    and return it in the 32 bit integer value pointed
          *    to by the 'data' parameter.  
          *
          * MSG_SETVALUE:
          *    Set the pixel specified as the current extraction
          *    location, to the value specified by the 'data' parameter.
          *
          * The extraction location will then be incremented by 1.  
          * If the last column in a row is reached, then the extraction 
          * location will be set to the start of the next row; if, however, 
          * this moves us past the last row, then the extraction point
          * will wrap back to (0,0).
          */
         return ((xrPixelData *) MsgValueHandler (instance, message, data));
      }
   }  /* end of switch */

   xrErrno = XrINVALIDMSG;
   return ((xrPixelData *) NULL);
}  /* end of _XrPixelExtract() */


/*************************************<->*************************************
 *
 *  xrPixelData *
 *  MsgNewHandler (pixmapInfo)
 *
 *     xrPixmap * pixmapInfo;
 *
 *   Description:
 *   -----------
 *     This routine is responsible for creating a new pixel extraction
 *     instance.  It will allocate a structure to hold all of the
 *     extraction information, initialize it, and then return a pointer
 *     to this structure back to the calling application; this pointer
 *     will serve as a unique handle for that particular extraction
 *     instance.  A MSG_NEW request will fail if the instance data is
 *     not supplied, or an invalid raster depth is supplied, or the
 *     system is out of memory.
 *
 *
 *   Inputs:
 *   ------
 *     pixmapInfo = This is a pointer to an X-ray pixmap structure,
 *                  containing the description of the raster image
 *                  which is to have an extraction instance created.
 *                  This includes the raster data, the height and
 *                  width of the data, and the depth of the raster data.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer for the new
 *          extraction instance will be returned.  If the request
 *          fails, then 'NULL' is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
xrPixelData *
MsgNewHandler (pixmapInfo)

   register xrPixmap * pixmapInfo;

{
   register xrPixelData * instance;
            INT8          depth;

   if (pixmapInfo == NULL)
   {
      xrErrno = XrINVALIDPTR;
      return ((xrPixelData *) NULL);
   }

   /* Check for an invalid raster depth specification */
   depth = pixmapInfo->depth;
   if ((depth != XrBIT1) && (depth != XrBYTE1) && (depth != XrBYTE2) &&
       (depth != XrBYTE3) && (depth != XrBYTE4))
   {
      xrErrno = XrINVALIDDEPTH;
      return ((xrPixelData *) NULL);
   }

   /* Allocate an instance structure, and initialize it */
   if ((instance = (xrPixelData *) (*xrMalloc) (sizeof (xrPixelData)))
                    == NULL)
   {
      xrErrno = XrOUTOFMEM;
      return ((xrPixelData *) NULL);
   }

   instance->height = pixmapInfo->height;
   instance->width = pixmapInfo->width;
   instance->depth = depth;
   instance->data = (UINT8 *) pixmapInfo->raster;
   instance->extractionLoc.x = 0;
   instance->extractionLoc.y = 0;

   return (instance);
}


/*************************************<->*************************************
 *
 *  xrPixelData *
 *  MsgLocationHandler (instance, location)
 *
 *     xrPixelData * instance;
 *     POINT       * location;
 *
 *   Description:
 *   -----------
 *     This routine modifies the point at which a pixel value is extracted,
 *     for a MSG_GETVALUE request, or set, for a MSG_SETVALUE request.  The
 *     value is in the form (x,y), where 'x' is the column number, and 'y'
 *     is the row number.  This request will fail if the location is outside
 *     the range of the raster image.
 *
 *
 *   Inputs:
 *   ------
 *     instance = This handle indicates which extraction instance is to
 *                have its extraction location modified.  This is a
 *                pointer to the extraction data structure, allocated 
 *                during the MSG_NEW request.
 *
 *     location = This is a pointer to a POINT structure, containing the
 *                new extraction location, specified as an (x,y) pair.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer will be returned;
 *          otherwise, a value of 'NULL' is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyPt()  [calc.c]
 *
 *************************************<->***********************************/

static
xrPixelData *
MsgLocationHandler (instance, location)

   register xrPixelData * instance;
   register POINT       * location;

{
   if (instance == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrPixelData *) NULL);
   }
   else if (location == NULL)
   {
      xrErrno = XrINVALIDPTR;
      return ((xrPixelData *) NULL);
   }

   /*
    * Verify the new extraction point falls within the
    * bounds of the raster image.
    */
   if ((location->x >= instance->width) ||
       (location->y >= instance->height) ||
       (location->x < 0) || (location->y < 0))
   {
      xrErrno = XrPARMOUTOFRANGE;
      return ((xrPixelData *) NULL);
   }

   XrCopyPt (location, &instance->extractionLoc);
   return (instance);
}


/*************************************<->*************************************
 *
 *  xrPixelData *
 *  MsgValueHandler (instance, message, value)
 *
 *     xrPixelData * instance;
 *     INT32         message;
 *     UINT32      * value;
 *
 *   Description:
 *   -----------
 *     This routine allows an application to either extract the next
 *     pixel value, or set a pixel value within the raster image.  In
 *     either case, the current extraction location is used to determine
 *     where to grab/set the pixel value, and this location will be
 *     automatically incremented afterwards; if the end of the image is
 *     reached, then the extraction location will wrap back to (0,0).
 *
 *
 *   Inputs:
 *   ------
 *     instance = This is a handle, describing which pixel extraction
 *                instance is to be used.  It is a pointer to the
 *                extraction data created by the MSG_NEW request.
 *
 *     message  = This must be either MSG_GETVALUE or MSG_SETVALUE.
 *
 *     value    = If the message is MSG_GETVALUE, then this is a pointer
 *                to an unsigned 32 bit integer value, into which the
 *                next pixel value will be placed.  If the message is
 *                MSG_SETVALUE, then this is an unsigned 32 bit value
 *                which will be stored into the raster image, at the
 *                current extraction location.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer will be returned;
 *          in addition, if this was a MSG_GETVALUE request, then the
 *          next pixel value will be returned by means of the unsigned
 *          32 bit integer value pointed to by the 'value' parameter.  If
 *          this request fails, then 'NULL' is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyPt()  [calc.c]
 *
 *************************************<->***********************************/

static
xrPixelData *
MsgValueHandler (instance, message, value)

   register xrPixelData * instance;
            INT32         message;
            UINT32      * value;

{
   INT8     depth;

   if (instance == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrPixelData *) NULL);
   }
   else if ((message == MSG_GETVALUE) && (value == NULL))
   {
      xrErrno = XrINVALIDPTR;
      return ((xrPixelData *) NULL);
   }

   depth = instance->depth;

   if (depth == XrBIT1)
   {
      /*
       * Bit extraction requires a different algorithm than that used
       * to extract bytes of data.  Using the extraction location, we 
       * must first determine which word of data the desired bit resides 
       * in, and then we must construct a mask, used to extract out that
       * single bit from the word, or to set/clear that bit.
       */
      UINT16 * wordPtr;         /* Ptr to word of interest */
      UINT16   extractionMask;  /* Mask to extract bit of interest */

      wordPtr = (UINT16 *)instance->data + 
             (instance->extractionLoc.x >> 4) +
             (instance->extractionLoc.y * ((instance->width + 15) >> 4));

      extractionMask = 1 << (instance->extractionLoc.x & 15);

      if (message == MSG_GETVALUE)
         *value = (*wordPtr & extractionMask) ? 1 : 0;
      else
      {
         if (value)
            *wordPtr |= extractionMask;
         else
            *wordPtr &= ~extractionMask;
      }
   }
   else
   {
      /*
       * Byte extraction is easy, since we only need to determine the
       * number of bytes to extract (based upon the depth of the raster 
       * image), and then we can extract the data.
       */
      register INT32    valueLen;    /* Number of bytes to extract */
      register UINT8  * valuePtr;    /* Ptr to bytes of interest */
      register UINT32   pixelValue;  /* Value being extracted */
               INT32   setValue;
               INT32   i;

      valueLen = depth >> 3;
      valuePtr = instance->data +
         (instance->extractionLoc.x * valueLen) +
         (instance->extractionLoc.y * (valueLen * instance->width));

      if (message == MSG_GETVALUE)
      {
         for (i = 0, pixelValue = 0; i < valueLen; i++)
            pixelValue = (pixelValue << 8) + *valuePtr++;

         *value = pixelValue;
      }
      else
      {
         for (i = 0, setValue = (INT32) value; i < valueLen; i++)
         {
            *(valuePtr + valueLen - 1 - i) = setValue & 0xFF;
            setValue = setValue >> 4;
         }
      }
   }

   /* Update the extraction location; wrap, if necessary */
   instance->extractionLoc.x++;

   if (instance->extractionLoc.x >= instance->width)
   {
      if (instance->extractionLoc.y >= (instance->height - 1))
         XrCopyPt (&xrZeroPt, &instance->extractionLoc);
      else
      {
         instance->extractionLoc.x = 0;
         instance->extractionLoc.y++;
      }
   }

   return (instance);
}
