/*
 * negative.c - Does a simple negative in Imagemaster
 */

#include <stdio.h>

/*
 * Each individual buffer has the following struture for use in a
 * Public Interface Module. Note that the "mask" pointer is usually
 * NULL; it is only there when the buffer is non-rectangular, for
 * instance when the user has cut out a clip using freehand.
 * if it is there, you are well advised to use it. There is data
 * available in the buffer where the mask is zero, but it is
 * hidden from the user and processing in it is a waste of time.
 * Although all three RGB pointers should be there, you should
 * still check them for safety's sake.
 */
struct jackinbuff
  {
    unsigned char *red;     /* points to red component of buffer     */
    unsigned char *green;   /* points to green component of buffer   */
    unsigned char *blue;    /* points to blue component of buffer    */
    unsigned char *mask;    /* points to local mask, IF there is one */
    unsigned short x;       /* X size of buffer (pixels per line)    */
    unsigned short y;       /* Y size of buffer (pixels per column)  */
  };

/*
 * This is the main "handle" you get from the ARexx "jackin" command.
 * Note that any, or ALL of the buffer pointers may be NULL, and you
 * MUST check them for validity before using them or you may blow up
 * the system. Macros may be invoked without a single buffer available.
 */
struct jackina
  {
    struct jackinbuff *primary;     /* current working buffer               */
    struct jackinbuff *secondary;   /* compositing buffer                   */
    struct jackinbuff *undo;        /* undo buffer                          */
    struct jackinbuff *blend;       /* blend buffer                         */
    struct jackinbuff *brush;       /* brush buffer                         */
    unsigned char     *mask;        /* working mask (use WITH local masks)  */
    char              jack[4];      /* ID for sanity chk: 'J' 'A' 'C' 'K'   */
    long              showscr;      /* points to control panel screen       */
    unsigned char     pname[4];     /* 'IM'00 'IP'00 or 'IMFC' - sanity chk */
    short             ver[2];       /* version:revision numbers             */
  };

int main(argc,argv)
  int argc;
  char *argv[];
  {
  unsigned int x,y,xw,yw,offset,yoffset;
  unsigned char rr,gg,bb;
  unsigned char *mask;
  unsigned char *lmask;
  unsigned char *rbu;
  unsigned char *gbu;
  unsigned char *bbu;
  short cancel;
  struct jackina *jackins;
  
    if (argc <= 1)
      {
        imes("NEGATIVE <pointer to IM jackin structure>",
             "No pointer argument supplied!"); /* ARexx problem? */
        return(1);
      }
    else
      {
        sscanf(argv[1],"%x",(int*)&jackins);
      }

    if (jackins == 0)
      {
        imes("NEGATIVE : Argument is ZERO!",0); /* ARexx problem? */
        return(1);
      }
      
    if (jackins->jack[0] != 'J' ||
        jackins->jack[1] != 'A' ||
        jackins->jack[2] != 'C' ||
        jackins->jack[3] != 'K' )
      {
        imes("NEGATIVE : Problem with information", /* VERY scary! */
             "passed from image processor!");
        return(1);
      }
    if (beginvbars(jackins->showscr)) /* open communications to panel */
      {
        imes("Unable to open Progress Communication!",0); /* scary! */
        return(1);
      }
    /*
     * Up to this point, if there is a problem, "imes()" will send its
     * output to the STDOUT stream; we were not certain that we were
     * successfully linked to the image processor until now. From now on,
     * messages from "imes()" will appear upon the user's control panel.
     */
    if (jackins->primary == NULL) /* if no primary buffer */
      {
        imes("NEGATIVE : Primary buffer is NULL!",0); /* ok, but silly */
        return(1);
      }
    mask = jackins->mask;
    if (jackins->mask == NULL) /* if no mask available */
      {
        imes("NEGATIVE : No area selection mask found!",0); /* scary! */
        return(1);
      }
    xw = jackins->primary->x;
    yw = jackins->primary->y;
    if (xw == 0 || yw == 0) /* if X:Y size of primary is insane */
      {
        imes("NEGATIVE : Primary buffer is Empty!",0); /* scary! */
        return(1);
      }
    rbu   = jackins->primary->red;
    gbu   = jackins->primary->green;
    bbu   = jackins->primary->blue;
    lmask = jackins->primary->mask; /* this can be NULL - we check later */
    if ((rbu==NULL) || (gbu==NULL) || (bbu==NULL)) /* any pointer missing? */
      {
        imes("NEGATIVE : Primary buffer is corrupted!",0); /* scary! */
        return(1);
      }
    /*
     * Actual operation is performed here
     */
    if (progressaset(yw,"-Negative-")) /* open progress bar */
      {
        imes("Cannot open progress bar!",0);
        return(1);
      }
    cancel = 0;
    for (y=0; y<yw; y++) /* for each scan line of the image */
      {
        yoffset = y * xw; /* make address offset - saves lots of multiplies */
        for (x=0; x<xw; x++) /* for each pixel in the scan line */
          {
            offset = yoffset + x;
            if (lmask) /* if there is a local mask */
              {
                if (mask[offset] & lmask[offset]) /* selected & selectable? */
                  {
                    rr = *(rbu+offset); /* get the pixel RGB data */
                    gg = *(gbu+offset);
                    bb = *(bbu+offset);
                    *(rbu+offset) = 255 - rr;  /* perform color negative */
                    *(gbu+offset) = 255 - gg;
                    *(bbu+offset) = 255 - bb;
                  }
              }
            else /* there was no local mask - just use main mask */
              {
                if (mask[offset]) /* selected? */
                  {
                    rr = *(rbu+offset); /* get the pixel RGB data */
                    gg = *(gbu+offset);
                    bb = *(bbu+offset);
                    *(rbu+offset) = 255 - rr;  /* perform color negative */
                    *(gbu+offset) = 255 - gg;
                    *(bbu+offset) = 255 - bb;
                  }
              }
          }
        if (progressupd(y)) /* show progress, look for press of CLOSE gadget */
          {
            if (y != yw) /* if we're not already done */
              {
                cancel = 1; /* indicate user forced exit */
                y = yw;     /* force structured exit from processing loop */
              }
          }
      }
    progressend(); /* close progress bar */
    if (cancel)    /* ack the press of close gadget */
      {
        imes("Operation Cancelled at User's Request",0);
        return(1); /* return not ok - operation incomplete */
      }
    endvbars(); /* close ALL references to image processor screen */
    return(0);
  }

