/*->c.g4 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>


#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"

#include "h.fsx"
#include "h.buffer"

#include "h.code"
#include "h.g3"
#include "h.g3dec"
#include "h.g4"



#define NULL_MODE 0
#define PASS_MODE 1
#define HORIZONTAL_MODE 2
#define VERTICAL_V0_MODE 3
#define VERTICAL_VR1_MODE 4
#define VERTICAL_VR2_MODE 5
#define VERTICAL_VR3_MODE 6
#define VERTICAL_VL1_MODE 7
#define VERTICAL_VL2_MODE 8
#define VERTICAL_VL3_MODE 9
#define EXT_MODE_UNCOMPRESSED 10
#define ERROR_MODE 11
#define ERROR_MODE_1 12

#define PASS_CODEWORD           0x1000
#define HORIZONTAL_CODEWORD     0x2000
#define VERTICAL_V0_CODEWORD    0x8000
#define VERTICAL_VR1_CODEWORD   0x6000
#define VERTICAL_VR2_CODEWORD   0x0C00
#define VERTICAL_VR3_CODEWORD   0x0600
#define VERTICAL_VL1_CODEWORD   0x4000
#define VERTICAL_VL2_CODEWORD   0x0800
#define VERTICAL_VL3_CODEWORD   0x0400

#define PASS_CODELEN            4
#define HORIZONTAL_CODELEN      3
#define VERTICAL_V0_CODELEN     1
#define VERTICAL_VR1_CODELEN    3
#define VERTICAL_VR2_CODELEN    6
#define VERTICAL_VR3_CODELEN    7
#define VERTICAL_VL1_CODELEN    3
#define VERTICAL_VL2_CODELEN    6
#define VERTICAL_VL3_CODELEN    7


#define EOL_CODE -1
#define INVALID_CODE -2

#define MSB_FIRST 1
#define LSB_FIRST 2

#define PAD_NONE 1
#define PAD_BYTE 2
#define PAD_NIBBLE 3
#define PAD_ODD_NIBBLE 4



/* 
 *
 NAME
 *      g4sencod.c -- encode group 4 data using nested if statements
 *
 TYPE
 *      C procedures
 *
 SYNOPSIS
 *      char    g4j_encode_black (short runlength);
 *      char    g4j_encode_white (short runlength);
 *      char    g4j_encode_EOFB (void);
 *      char    g4j_encode_new_row (void);
 *      char    g4j_initialize (short image_width, short image_length);
 *
 DESCRIPTION
 *      Routines to encode group 4 images. Consecutive calls to encode
 *      black or white runs will be combined.
 *
 RETURNS
 *
 LEGAL
 *      Copyright 1989, 1990 Michael P. Marking, Post Office Box 8039,
 *      Scottsdale, Arizona 85252-8039. All rights reserved.
 *
 *      License is granted by the copyright holder to distribute and use this
 *      code without payment of royalties or the necessity of notification as
 *      long as this notice (all the text under "LEGAL") is included.
 *
 *      Reference: $Id: g4sencod.c 1.2 90/06/09 18:24:10 marking Exp $
 *
 *      This program is offered without any warranty of any kind. It includes
 *      no warranty of merchantability or fitness for any purpose. Testing and
 *      suitability for any use are the sole responsibility of the user.
 * 
 HISTORY
 *      $Log:   g4sencod.c $
 * Revision 1.2  90/06/09  18:24:10  marking
 * clean up comments for release
 * 
 * Revision 1.1  90/05/01  02:00:00  marking
 * Initial revision
 * 
 *
 NOTES
 *
 PORTABILITY
 *      Tested using Microsoft C 5.1. Some memory models may not work due to
 *      the large encoding arrays.
 *
 *      There is a non-portable use of "global" variables in the file g3g4.h,
 *      about which a minority of compilers will justifiably complain. Certain
 *      variables are declared in g3g4.h without extern keywords. Strictly
 *      speaking, they should be declared extern in all but one module, but
 *      that would require complication of g3g4.h. If it gets past your
 *      compiler and linker, you can probably ignore it.
 *
 SEE ALSO
 *
 INFORMATION
 *      Although there is no support offered with this program, the author will
 *      endeavor to correct errors. Updates will also be made available from
 *      time to time.
 *
 *      Contact: Michael P. Marking, Post Office Box 8039, Scottsdale, Arizona
 *      85252-8039 USA. Replies are not guaranteed to be swift. Beginning
 *      July 1990, e-mail may be sent to uunet!ipel!marking.
 *
 *      Also beginning in July 1990, this code will be archived at the
 *      ipel!phoenix BBS in file g3g4.zoo. The 24-hour telephone number
 *      for 300/1200/2400 is (602)274-0462. When logging in, specify user
 *      "public", system "bbs", and password "public".
 *
 *      This code is also available from the C Users Group in volume 317.
 */


/* #define TRACE 1 */

/* implementation limits: Due to the sizes of arrays and variables, and not
   due to any restrictions in the algorithm, the following limits exist:
     maximum number of pixels per row: 65533
     maximum number of rows per image: none
     maximum or minimum k-factor: none
     maximum number of runs per row: 16382 white, 16382 black
   To increase (or decrease) these limits, it will be necessary to play with
   array and variable sizes.  On segmented machines (such as the 8086), a
   different memory model may be necessary.  The algorithm itself has no
   limits on image size or complexity, and the stack requirements are in-
   sensitive to changes in these limits or to image complexity. */

#define EVEN 0
#define ODD 1

static int a0, a1, a2, b0, b1, b2, length_of_current_run;
static unsigned char color, current_row, mode;


#define LINEMAX 2560

static short * even_runs;
static short * odd_runs;


static unsigned int reference_index, coding_index;
static short *reference_line, *coding_line;
static int   column_limit;

  /* Depending as current_row == EVEN or current_row == ODD, the runs of the
     current row are represented in even_runs [] or odd_runs [].  The white
     runs have even subscripts and the black runs have odd subscripts.  The
     values of the array elements are the offsets of the beginnings of the
     corresponding runs from the beginning of the row.  As defined by the
     specification,
        a0 is the reference or starting changing element on the coding line.
                It may be considered the "current position".
        a1 is the next changing element to the right of a0 on the coding line.
        a2 is the next changing element to the right of a1 on the coding line.
        b1 is the first changing element on the reference line to the right of
                a0 and of opposite color to a0.
        b2 is the next changing element to the right of b1 on the reference
                line.
     Furthermore,
        b0 is the "previous" value of b1. 
     */



static int EOL_code=0x0010;
static int EOL_length=12;


os_error * g4j_encode_black(buffer * b,int runlength)
{
 if(!(coding_index & 1)) /* if even number, is white */
 {
  coding_index++; /* move on to black */
  coding_line[coding_index]
      = coding_line [coding_index - 1] + length_of_current_run;
  length_of_current_run = 0;
 }
 length_of_current_run += runlength;
 return(NULL);
}


os_error * g4j_encode_white(buffer * b,int runlength)
{
 if (coding_index & 1) /* if odd number, is black */
 {
  coding_index++; /* move on to white */
  coding_line [coding_index]
      = coding_line [coding_index - 1] + length_of_current_run;
    length_of_current_run = 0;
 }
 length_of_current_run += runlength;
 return(NULL);
}





static void g4j_lineswop(void)
{
  /* now swap rows */
  if (current_row == EVEN)
  {
    current_row = ODD;
    coding_line = odd_runs;
    reference_line = even_runs;
  }
  else /* current_row == ODD */
  {
    current_row = EVEN;
    coding_line = even_runs;
    reference_line = odd_runs;
  }
  coding_index = 0;
  coding_line [coding_index] = 0;
  length_of_current_run = 0;
}


static void g4j_linecheck(void)
{
 if(current_row == EVEN)
 {
  coding_line = even_runs;
  reference_line = odd_runs;
 }
 else
 {
  coding_line = odd_runs; 
  reference_line = even_runs;
 }
}


static void g4j_linefinish(void)
{
 int last_point;

 last_point  = coding_line [coding_index] + length_of_current_run;
 coding_index++;
 coding_line [coding_index] = last_point;
 coding_line [coding_index + 1] = last_point;
 coding_line [coding_index + 2] = last_point;
 coding_line [coding_index + 3] = last_point;
}



os_error * g4j_encode_new_row(buffer * b)
{
 g4j_linefinish();

  /* encode the current row */


  #if defined (TRACE)
    if (trace_flag)    
    {
      int j = 0, a;
      printf (" coding_line = < ");
      while ((a = coding_line [j]) < column_limit)
      {
        printf ("%hd@%hd ", (short) (coding_line [j + 1] - a), a);
        j++;
      }
      printf ("> ");
      j = 0;
      printf ("\nreference_line = < ");
      while ((a = reference_line [j]) < column_limit)
      {
        printf ("%hd@%hd ", (short) (reference_line [j + 1] - a), a);
        j++;
      }
      printf ("> ");
    };
  #endif 

  reference_index = 0;
  coding_index = 0;
  a1 = coding_line [0];
  a2 = coding_line [1];
  if (a2 == 0 && reference_line [1] == 0)
    /* if the first (white) run is null as was in previous row */
  {
    encode_word(b,VERTICAL_V0_CODEWORD, VERTICAL_V0_CODELEN);
    coding_index++;
    a1 = coding_line [1];
    a2 = coding_line [2];
    #if defined (TRACE)
      if (trace_flag) printf ("V0 ");
    #endif
  }
  while (a1 < column_limit)
  {
    int effective_a0;
    a0 = a1;
    a1 = a2;
    a2 = coding_line [coding_index + 2];
    if (reference_index > 3) reference_index -= 3;
    else reference_index = 0;
    if (coding_index) effective_a0 = a0;
    else effective_a0 = -1;
    while (effective_a0 >= (b1 = reference_line [reference_index]))
      reference_index++;
      /* now the reference index points to the first changing point beyond a0,
         but it may or may not be the same color as a0 */
    if (! ((coding_index ^ reference_index) & 1)) /* if same colors */
    {
      reference_index++; /* now different color */
      b1 = reference_line [reference_index];
    }
    b2 = reference_line [reference_index + 1];
    #if defined (TRACE)
      if (trace_flag)
        printf ("\na0=%hd a1=%hd a2=%hd b1=%hd b2=%hd rx=%hd cx=%hd ",
          a0, a1, a2, b1, b2, reference_index, coding_index);
    #endif
    while (b2 < a1) /* PASS */
    {
      #if defined (TRACE)
        short old_a0 = a0;
      #endif
      encode_word(b,PASS_CODEWORD, PASS_CODELEN);
      a0 = b2;
      reference_index += 2;
      b1 = reference_line [reference_index];
      b2 = reference_line [reference_index + 1];
      #if defined (TRACE)
        if (trace_flag) printf ("PASS k%hd ", (short) (a0 - old_a0));
      #endif
    }
    if (a1 == b1) /* V0 */
    {
      encode_word(b,VERTICAL_V0_CODEWORD, VERTICAL_V0_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("V0 ");
      #endif
    }
    else if (a1 == b1 + 1) /* VR1 */
    {
      encode_word(b,VERTICAL_VR1_CODEWORD, VERTICAL_VR1_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("VR1 ");
      #endif
    }
    else if (a1 == b1 + 2) /* VR2 */
    {
      encode_word(b,VERTICAL_VR2_CODEWORD, VERTICAL_VR2_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("VR2 ");
      #endif
    }
    else if (a1 == b1 + 3) /* VR3 */
    {
      encode_word(b,VERTICAL_VR3_CODEWORD, VERTICAL_VR3_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("VR3 ");
      #endif
    }
    else if (a1 == b1 - 1) /* VL1 */
    {
      encode_word(b,VERTICAL_VL1_CODEWORD, VERTICAL_VL1_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("VL1 ");
      #endif
    }
    else if (a1 == b1 - 2) /* VL2 */
    {
      encode_word(b,VERTICAL_VL2_CODEWORD, VERTICAL_VL2_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("VL2 ");
      #endif
    }
    else if (a1 == b1 - 3) /* VL3 */
    {
      encode_word(b,VERTICAL_VL3_CODEWORD, VERTICAL_VL3_CODELEN);
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("VL3 ");
      #endif
    }
    else /* HORIZONTAL */
    {
      int first_run, second_run;
      encode_word(b,HORIZONTAL_CODEWORD, HORIZONTAL_CODELEN);
      first_run = a1 - a0;
      second_run = a2 - a1;
      if(coding_index & 1) /* if BLACK, WHITE */
      {

        g3j_encode_black(b,first_run);
        g3j_encode_white(b,second_run);
        #if defined (TRACE)
          if (trace_flag) printf ("B%hd W%hd ", first_run, second_run);
        #endif
      }
      else /* WHITE, BLACK */
      {
        g3j_encode_white(b,first_run);
        g3j_encode_black(b,second_run);
        #if defined (TRACE)
          if (trace_flag) printf ("W%hd B%hd ", first_run, second_run);
        #endif
      }
      coding_index++;
      a0 = a1;
      a1 = a2;
      a2 = coding_line [coding_index + 2];
      coding_index++;
      #if defined (TRACE)
        if (trace_flag) printf ("HORIZ ");
      #endif
    }
  }

 g4j_lineswop();

 return (0);
}



os_error * g4j_encode_EOFB(buffer * b)
{
 encode_word(b,EOL_code,EOL_length);
 encode_word(b,EOL_code,EOL_length);
 return (0);
}



os_error * g4j_finit(void)
{
 if(even_runs) flex_free((flex_ptr)&even_runs);
 if(odd_runs)  flex_free((flex_ptr)&odd_runs);

 return(NULL);
}



os_error * g4j_initialize(putfn putf,int width)
{
 os_error * err;

 g3j_initialize(putf);
 
 err=NULL;

 if(!even_runs) 
        err=flex_alloce((flex_ptr)&even_runs,(width+8)*sizeof(*even_runs));
 if(!err && !odd_runs)
        err=flex_alloce((flex_ptr)&odd_runs, (width+8)*sizeof(*odd_runs));

 if(!err)
 {
  color=WHITE;
  current_row=ODD;
  coding_line=odd_runs;
  reference_line=even_runs;
  coding_index=0;
  coding_line[0]=0;
  length_of_current_run=0;
  column_limit=width;
  reference_line[0]=0;
  reference_line[1]=column_limit;
  reference_line[2]=column_limit;
  reference_line[3]=column_limit;
 }
 else g4j_finit();

 return(err);
}


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

static int G3EOL_code=0x0018;   /* EOL + 1 */
static int G3EOL_length=13;
static int G4EOL_code=0x0010;   /* EOL + 0 */
static int G4EOL_length=13;


os_error * g4k_putline(codestr * cx,int pad)
{
 os_error * err;
 int        i;
 int        count;
 int        width;
 int        x;
 int        run;
 int        runminus;
 int        runlimit;

 g4j_linecheck();

 count=g3outcount+pad;
 width=cx->width;
 x=0;
 runlimit=cx->run;
 runminus=runlimit-1;

 if(!runlimit) err=g4j_encode_white(cx->bf,width);
 else
 for(i=0;i<runlimit;i++)
 {
  run=cx->runs[i++];
  x+=run;
  if(x>width)
  {
   run-=(x-width);
   x=width;
  }
  else
  if(i>=runlimit && x<width) run+=(width-x);

  err=g4j_encode_white(cx->bf,run);

  if(i<runlimit)
  {
   run=cx->runs[i];
   x+=run;
   if(x>width)
   {
    run-=(x-width);
    x=width;
   }

   err=g4j_encode_black(cx->bf,run);

   if(i==runminus && x<width) 
   {
    run=width-x;
    g4j_encode_white(cx->bf,run);
   }
  }
 }



 if(cx->kcount++<cx->K)
 {
  /* debugputc('D');debugputc('2');debugputc('D'); */


  err=encode_word(cx->bf,G4EOL_code,G4EOL_length);  /* 2D line */
  err=g4j_encode_new_row(cx->bf);
 }
 else
 {
  /* debugputc('D');debugputc('1');debugputc('D'); */


  g4j_linefinish();

  err=encode_word(cx->bf,G3EOL_code,G3EOL_length);   /* 1D line */
  for(i=0;i<cx->run;i++)
  {
   err=g3j_encode_white(cx->bf,cx->runs[i]);
   i++;
   if(i<cx->run)
   {
    err=g3j_encode_black(cx->bf,cx->runs[i]);
   }
  }
  cx->kcount=0;

  g4j_lineswop();
 }

 g3j_padnull(cx->bf,count-g3outcount);

 cx->line++;


 return(err);
}


os_error * g4k_puteop(codestr * cx)
{
 os_error * err;
 int        i;

 for(i=0;i<6;i++)
 {
  err=encode_word(cx->bf,G3EOL_code,G3EOL_length);
  if(err) break;
 }

 err=g3j_encode_pad(cx->bf,PAD_BYTE);

 return(err);
}



os_error * g4k_init(codestr * cx)
{
 os_error * err;

 cx->K=3;
 cx->kcount=0;

 err=g4j_initialize(cx->putbyte,cx->width);

 return(err);
}


os_error * g4k_finit(codestr * cx)
{
 os_error * err;

 err=g4j_finit();

 return(err);

 cx=cx;
}

