/**************
 ******##****** Filename:                 >> AAC_SK7S.C <<
 *****####***** Edit Date: 01.08.92
 ****######****
 ***##*##*##*** By: Wolfgang Hofer
 **##**##**##**     Compress one Colomn (for Anim7 short DLTA Chunks)
 ******##******
 ******##****** Compiler: AztecC 5.0
 ******##****** Link:     see makefile
 ************** Computer: AMIGA 500
 **************
 **************
 */


extern int  g_cli;
extern long g_dbug_print;


static long  gs_linebytes;
static long  gs_remain_rows;
static SHORT *gs_curr;
static SHORT *gs_ref;


/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   CountSkipS
   	count how far we could skip from current position
   	(gs_curr)
   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

CountSkipS()

{
  int   ct_skip;
  short row;
  register long  adr;
  register SHORT *curr, *ref;


  row  = gs_remain_rows;
  curr = gs_curr;
  ref  = gs_ref;
  ct_skip = 0;

  while(row--)
  {
    if(*curr == *ref) ct_skip++;
    else              break;

    /* step down one line curr and ref */
    adr  = (long)curr + gs_linebytes;
    curr = (SHORT *)adr;

    adr  = (long)ref + gs_linebytes;
    ref  = (SHORT *)adr;
  }             /* end while(row--) */

  return(ct_skip);
}		/* end CountSkipS */


/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   CountSameS
   	count how much same SHORT words we can find from here.
   	Break the same-run if we find skips larger than 4.
   	(This gives less packing, but improves playback rates)
   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

CountSameS()

{
  int ct_skip;
  int ct_same;
  long  row;
  register long  adr;
  register SHORT *curr, *ref, *prev;


  row = gs_remain_rows;
  prev = NULL;
  curr = gs_curr;
  ref  = gs_ref;
  ct_skip = 0;
  ct_same  = 0;

  while(row--)
  {
    if(*curr == *ref) ct_skip++;
    else              ct_skip = 0;

    /* test if pointer to previous data is valid */
    if(prev)
    {
      if(*prev == *curr)
      {
        if(ct_same) ct_same++;
        else        ct_same = 2;

        /* check break by skip possibility */
        if(ct_skip >= 4)
        {
          return(ct_same - ct_skip);
        }
      }
      else break;
    }

    /* update prev pointer */
    prev = curr;

    /* step down one line curr and ref */
    adr  = (long)curr + gs_linebytes;
    curr = (SHORT *)adr;

    adr  = (long)ref + gs_linebytes;
    ref  = (SHORT *)adr;
  }             /* end while(row--) */

  return(ct_same);

}		/* end CountSameS */


/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   CountUniqS
   	count how far we could skip from here.
   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

CountUniqS()

{
  int   ct_uniq;
  long  row;
  register long  adr;
  register SHORT   *curr, *ref, *prev;


  row = gs_remain_rows;
  prev = NULL;
  curr = gs_curr;
  ref  = gs_ref;
  ct_uniq = 0;

  while(row--)
  {
    if(*curr == *ref) break;

    /* test if pointer to previous data is valid */
    if(prev)
    {
      if(*prev == *curr)
      {
        return(--ct_uniq);
      }
    }

    ct_uniq++;

    /* update prev pointer */
    prev = curr;

    /* step down one line curr and ref */
    adr  = (long)curr + gs_linebytes;
    curr = (SHORT *)adr;

    adr  = (long)ref + gs_linebytes;
    ref  = (SHORT *)adr;
  }             /* end while(row--) */

  return(ct_uniq);
}		/* end CountUniqS */


/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   CompressColumnS
   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

CompressColumnS(f_plane, f_ref_plane, f_rows, f_linebytes, f_dta, f_opc)

long  f_plane;
long  f_ref_plane;
long  f_rows;
long  f_linebytes;
UBYTE **f_dta;
UBYTE **f_opc;
{
  register long  handled_rows;
  register short skip, same, uniq;

  short ct_ops;
  long  row;
  long  offset;
  short i;

  long  adr;
  UBYTE  *opc;
  SHORT  *uni_dta, *dta;


  gs_linebytes = f_linebytes;

  dta  = (SHORT *)*f_dta;
  opc  = *f_opc;
  /* reserve 1 byte for opc count */
  opc++;

  /* test if we can skip the whole column */
  gs_ref  = (SHORT *)f_ref_plane;
  gs_curr = (SHORT *)f_plane;

  gs_remain_rows = f_rows;
  skip = (short)CountSkipS();
  if(skip == f_rows)
  {
     /* skip the whole column
      * by setting opcount to zero
      */

      **f_opc = 0;
      (*f_opc)++;
      return(0);
  }


  /* now try to compress */
  gs_ref  = (SHORT *)f_ref_plane;
  gs_curr = (SHORT *)f_plane;

  ct_ops = 0;
  gs_remain_rows = f_rows;

  while(gs_remain_rows > 0)
  {
    same = 0;
    skip = 0;
    uniq = 0;

    /* --------------------------
     * decide which opcode to use
     * (these terrible goto's
     * speed up the compression a bit)
     * --------------------------
     */


    if(ct_ops > 245)
    {
      /* it seems that we run out of ops very soon (max 255)
       * this emergency case will happen if data
       * is of a sequence like this:
       *    (skip1, uniq1, skip1, uniq1, .......  )
       * now use the last 10 ops as uniq
       * for upto 1270 (10 x 127) Pixelrows
       */
      uniq = gs_remain_rows;
      goto FlushUNIQ;
    }

    skip = (short)CountSkipS();
    if(skip > 2)    goto FlushSKIP;

    same = (short)CountSameS();
    if(skip)
    {
      /* test if the very short skips (1 or 2)
       * can be included into a following bigger same-run
       * On larger skips we do no include, because
       * the playback of skips-ops is faster than same-ops
       */

      if(same > skip)      goto FlushSAME;

      goto FlushSKIP;
    }

    if(same == 1)
    {
      uniq  = 1;
      goto FlushUNIQ;
    }

    if(same)        goto FlushSAME;
    if(skip)        goto FlushSKIP;

    uniq = (short)CountUniqS();
    if(uniq == 0)
    {
      if(g_cli) printf("Fatal Error\n");
      return;
    }
    goto FlushUNIQ;


    /* --------------------------
     * flush opcode/data to the
     * correspunding buffers
     * --------------------------
     */

FlushSKIP:
        handled_rows = skip;
	if ((gs_remain_rows - handled_rows) <= 0)
	{
          /* if the last opc is just a skip
           * we can supress this useless dummy
           */
          if(g_dbug_print) printf("omitted_skp(%d) ", (long)skip);
	}
	else
	{
	  while(skip > 0)
	  {
	    ct_ops++;

	    if(skip > 127)
	    {
              *(opc++) = 127;   /* Hi bit clear */
              skip -= 127;

              if(g_dbug_print) printf("skp(127) ");
	    }
	    else
	    {
              *(opc++) = skip;   /* Hi bit clear */
              if(g_dbug_print) printf("skp(%d) ", (long)skip);
              skip = 0;
            }
          }	/* end while */
        }

        goto AfterFLUSH;

FlushSAME:
        handled_rows = same;
	while(same > 0)
	{
          *(dta++) = *gs_curr;	/* same requires one data entry */

	  ct_ops++;
          *(opc++) = 0; 	/* 0 is the 'same' opcode */

          if(same > 255)
	  {
            *(opc++) = 255;   /* use max same count of 255 */
            if(g_dbug_print) printf("sam(255) ");
            same -= 255;
	  }
	  else
	  {
            *(opc++) = same;   /* same count follows the opc */
            if(g_dbug_print) printf("sam(%d) ", (long)same);
            same = 0;
          }
        }	/* end while */

        goto AfterFLUSH;

FlushUNIQ:
        handled_rows = uniq;
        /* uniq opcode(s) require [n] data entries */
        uni_dta = gs_curr;
	for(i = 0; i < uniq; i++)
	{
	  *(dta++) = *uni_dta;
	  /* step down one line gs_curr and gs_ref */
	  adr  = (long)uni_dta + f_linebytes;
	  uni_dta = (SHORT *)adr;
	}

	while(uniq > 0)
	{
	  ct_ops++;

	  if(uniq > 127)
	  {
            *(opc++) = (0x80 | 127);   /* uniq ops got Hi bit set */
            if(g_dbug_print) printf("uni(127) ");
            uniq -= 127;
	  }
	  else
	  {
            *(opc++) = 0x80 | uniq;   /* Hi bit set */
            if(g_dbug_print) printf("uni(%d) ", (long)uniq);
            uniq = 0;
          }
        }	/* end while */

AfterFLUSH:

    /* advance Bitplane Pointers */
    offset = f_linebytes * handled_rows;

    /* step down one line gs_curr and gs_ref */
    adr  = (long)gs_curr + offset;
    gs_curr = (SHORT *)adr;

    adr  = (long)gs_ref + offset;
    gs_ref  = (SHORT *)adr;

    gs_remain_rows -= handled_rows;

  }             /* end while(gs_remain_rows) */




  if(g_dbug_print) printf(" ops[%d]\n", (long)ct_ops);

  if((g_cli) &&
     ((g_dbug_print == 4) || (gs_remain_rows < 0)) )
  {

    /* debug report of column data */
    gs_ref  = (SHORT *)f_ref_plane;
    gs_curr = (SHORT *)f_plane;

    if (gs_remain_rows < 0) printf("\nPACKING ERROR\n");
    printf("\n curr_column  (ref_column)\n");

    for(row=0; row < f_rows; row++)
    {
      printf("%3d %8x \t (%8x)\n", (long)row, (long)(*gs_curr), (long)(*gs_ref));

      /* step down one line gs_curr and gs_ref */
      adr  = (long)gs_curr + f_linebytes;
      gs_curr = (SHORT *)adr;

      adr  = (long)gs_ref + f_linebytes;
      gs_ref  = (SHORT *)adr;
    }

  }

  **f_opc = ct_ops;  /* set count of ops */

  /* advance passed opc and dta pointers */
  *f_opc  = opc;
  *f_dta  = (UBYTE *)dta;

  return(1);    /* column has changes */
}               /* end CompressColumnS */
