//*********************************************************************
//
//                 ScanmakerE3 Scanner device
//
//                    version 98.12.31
//
//*********************************************************************

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <math.h>

#include <exec/types.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include <Scanner.h>
#include <Scanner_protos.h>

#include "scsi.h"

char DevName[] = "ScanmakerE3.device";
char DevIdString[] = "ScanmakerE3";

typedef unsigned char ScanCmd[6];

// Scanner specifik defines
#define MAX_BASE_RES      300
#define MAX_EXT_RES       300

// scanning modes
#define SCANMODE_HALFTONE   0
#define SCANMODE_BW         1 
#define SCANMODE_GRAY       2
#define SCANMODE_COLOR      3


#define SCANBUFSIZE     32000

#define MAX_BUSY_RETRY      5


static UBYTE* scanBuffer = NULL;

// Stored scanning parameters
//
static double x0 = 0;                    /* Scanning frame in mm               */
static double y0 = 0;
static double x1 = 215.9;
static double y1 = 297.3;
static UBYTE  resolutionCode = 100;      /* Resolution in % of max resolution  */
static WORD   brightness[3] = {0,0,0};   /* Brightness for red, green and blue */
static WORD   contrast = 0;              /* Contrast                           */
static WORD   shadow = 0;                /* shadow adjust 0..1023 (default 0)  */
static WORD   highlight = 255;           /* highlight adjust 0..1023 (def. 0)  */
static WORD   midtone = 128;             /* midtone adjust 0..1023 (def. 512)  */
static WORD   halftonePattern = 0;       /* halftonePattern 0..255 (def. 0)    */
static WORD   exposureTime = 0;          /* Exposure time                      */
static double gamma = 1.0;               /* gamma value                        */
static UWORD  scanMode = SCANMODE_COLOR; /* see defines above                  */

// Used to make Software corrections of contrast and brightness
static int brightnessValue;
static int contrastValue;

// Actual scanning values
//
static LineFormat lineFormat;
static UWORD      bytesPerLine;

// Parameters used during scanning
//
static BOOL scanning;    /* Scanner is scanning                                */
static int  lineWidth;   /* Length of one scan line including color informat.  */
static int  scanLines;   /* Total number of lines to read from scanner         */
static int  numPlanes;   /* 3 for rgb24, otherwise 1                           */
static int  bufferLen;   /* Length of scanBuffer in number of lines            */
static int  linesRead;   /* Lines already read from scanner                    */
static int  linesInBuf;  /* Number of lines read from scanner in scanBuffer    */
static int  currentLine; /* Next line from scanBuf to return to user           */
static int  nextPlane;   /* next plane number to return to readScanLine        */

//
// Make color correction table brightness and contrast
//

UBYTE corrTab[256];

void makeCorrTab(int bright,int contrast)
{
  int    i,b,c;
  UBYTE* tab;

  tab = corrTab;
  bright = (bright*255)/100;
  c = 100+contrast;
  for( i = 0 ; i < 256 ; i++ )
  {
    b = ((i+bright)*c-50*contrast)/100;
    *(tab++) = b>255?255:(b<0?0:(UBYTE)b);
  }
}

/*------------------------------------------------------------------------*/
static void scannerReady(BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd TEST_UNIT_READY = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    int retry = 0;

    while( sendCommand(TEST_UNIT_READY, 6, NULL, 0, NULL, 0) )
    {
      retry++;
      if (retry == 5)
      {
        *error = SCAN_ERR_READY;
        return;
      }
      Delay(3*50);
    }
  }
}


/*------------------------------------------------------------------------*/
static void setScanningFrame(BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd SCANNING_FRAME = { 0x04, 0x00, 0x00, 0x00, 0x09, 0x00 };
    UBYTE   frame_data[9];

    int xf0,yf0,xf1,yf1;
    int dpi;
    
    frame_data[0] = 0x00;
    if( scanMode == SCANMODE_HALFTONE )
      frame_data[0] |= 0x01;

    // Unit is pixel
    //
    frame_data[0] |= 0x08;

    dpi = MAX_BASE_RES;
    xf0 = (int)((double)(x0*dpi)/25.4);
    yf0 = (int)((double)(y0*dpi)/25.4);
    xf1 = (int)((double)(x1*dpi)/25.4);
    yf1 = (int)((double)(y1*dpi)/25.4);
    
    frame_data[1] = (UBYTE)(xf0 & 0xff);
    frame_data[2] = (UBYTE)((xf0 & 0xff00) >> 8);
    frame_data[3] = (UBYTE)(yf0 & 0xff);
    frame_data[4] = (UBYTE)((yf0 & 0xff00) >> 8);
    frame_data[5] = (UBYTE)(xf1 & 0xff);
    frame_data[6] = (UBYTE)((xf1 & 0xff00) >> 8);
    frame_data[7] = (UBYTE)(yf1 & 0xff);
    frame_data[8] = (UBYTE)((yf1 & 0xff00) >> 8);

    *error = sendCommand(SCANNING_FRAME, 6, frame_data, 9, NULL, 0);
  }
}


/*------------------------------------------------------------------------*/
static void selectMode(BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd MODE_SELECT = { 0x15, 0x00, 0x00, 0x00, 0x0B, 0x00 };
    UBYTE mode_data[11];

    mode_data[0] = 0x81;
    
    // Set resolution
    mode_data[0] |= 0x02;
    mode_data[1] = resolutionCode;
    mode_data[2] = (exposureTime/3)+7;
    mode_data[3] = (contrast+49)/7;
    mode_data[4] = halftonePattern;
    mode_data[5] = 0x01;
    mode_data[6] = shadow;
    mode_data[7] = highlight;
    mode_data[8] = 0x6C;
    mode_data[9] = 0x00;
    mode_data[10] = midtone;
        
    *error = sendCommand(MODE_SELECT, 6, mode_data, 11, NULL, 0);
  }
}

/*------------------------------------------------------------------------*/
void mode_sense_1(BYTE *error)
{

  if( *error == 0 )
  {
    ScanCmd MODE_SELECT_1 = { 0x16, 0x00, 0x00, 0x00, 0x0a, 0x00 };
    unsigned char mode_data[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    mode_data[1] = (UBYTE)(brightness[0]);

    *error = sendCommand(MODE_SELECT_1, 6, mode_data, 10, NULL, 0);
  }
  if( *error == 0 )
  {
    ScanCmd MODE_SENSE_1 = { 0x19, 0x00, 0x00, 0x00, 0x1e, 0x00 };
    unsigned char mode_data[30] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    mode_data[1] = (UBYTE)(brightness[0]);
    mode_data[2] = (UBYTE)(brightness[1]);
    mode_data[3] = (UBYTE)(brightness[2]);
    *error = sendCommand(MODE_SENSE_1, 6, mode_data, 30, NULL, 0);
  }
}

/*------------------------------------------------------------------------*/
/* int lut_download (char filter, char *gtable, int gt_entries)           */
/* Issue the LOOK-UP-TABLE DOWNLOAD command to the scanner to download    */
/* the gamma table pointed to by <gtable>. <filter> determines the color  */
/* to which the table applies (RED, GREEN, BLUE, or CLEAR; values other   */
/* than CLEAR may only be specified for one-pass color scanners). The     */
/* number of entries is determined by <entries> (256, 1024, 4096 or       */
/* 65536). The entry width is automatically assumed to be one byte if the */
/* number of entries is 256 and two bytes otherwise.                      */
/*------------------------------------------------------------------------*/

void gammaTable(double g, BYTE *error)
{
  if( *error == 0 )
  {
    UBYTE LUT_DOWNLOAD[10] = {0x55,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
    UBYTE gTable[256];
    int i;

    LUT_DOWNLOAD[7] = (UBYTE)((256 & 0xff00) >> 8);
    LUT_DOWNLOAD[8] = (UBYTE)(256 & 0xff);

    if( (g <= 0.0) || (g == 1.0) )
    {
      for( i = 0 ; i < 256 ; i++ )
        gTable[i] = (UBYTE)i;
    }
    else
    {
      for( i = 0 ; i < 256 ; i++ )
        gTable[i] = (UBYTE)(256.0*pow(((double)i)/256.0,1/g));
    }

    *error = sendCommand(LUT_DOWNLOAD,10,gTable,256,NULL,0);
  }
}

/*------------------------------------------------------------------------*/
static void accessory(BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd ACCESSORY = { 0x10, 0x00, 0x00, 0x00, 0x9A, 0x00 };

    *error = sendCommand(ACCESSORY, 6, NULL, 0, NULL, 0);
  }
}

/*------------------------------------------------------------------------*/
static void startScan(BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd START_SCAN = { 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00 };

    switch( scanMode )
    {
      case SCANMODE_HALFTONE:
      case SCANMODE_BW:
        break;
      case SCANMODE_COLOR:
        START_SCAN[4] |= 0x20;  // 1-pass color
      case SCANMODE_GRAY:
        START_SCAN[4] |= 0x40;  // Multibit
        break;
    }
    // if( extendedRes )
    //     START_SCAN[4] |= 0x80;  // extended resolution

    *error = sendCommand(START_SCAN, 6, NULL, 0, NULL, 0);
  }
}


/*------------------------------------------------------------------------*/
static void stopScan(BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd STOP_SCAN = { 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00 };

    *error = sendCommand(STOP_SCAN, 6, NULL, 0, NULL, 0);
  }
}

/*------------------------------------------------------------------------*/
static void readScanStatus(int *busy, int *lineWidth, int *linesRemain,BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd GET_SCAN_STATUS = { 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00 };
    UBYTE status_reply[6];
    int retry = 0;

    do
    {
      if( *error = sendCommand(GET_SCAN_STATUS,6,NULL,0,status_reply,6) )
        return;

      *busy        = (int)(status_reply[0] != 0);
      *lineWidth   = (int)(status_reply[1] + 256 * status_reply[2]);
      *linesRemain = (int)(status_reply[3]+256*status_reply[4]+256*256*status_reply[5]);
      if( *busy )
      {
        retry++;
        Delay(3*50);
      }
    }
    while( (*busy) && (retry < MAX_BUSY_RETRY) );
    if( *busy )
      *error = SCAN_ERR_READY;
  }
}


/*----------------------------------------------------------------------*/
/*                                                                      */
/* Read lines from scanner                                              */
/*                                                                      */
/* The number of lines to read must be specified. For color scanning    */
/* the length of one line is three times line width although the lines  */
/* returned is not in correct sequence (r-g-b).                         */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void readScanData(int nlines,UBYTE *buffer,int bsize,BYTE *error)
{
  if( *error == 0 )
  {
    ScanCmd READ_SCANNED_DATA = { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 };

    READ_SCANNED_DATA[2] = (UBYTE)((nlines & 0xff0000) >> 16);
    READ_SCANNED_DATA[3] = (UBYTE)((nlines & 0xff00) >> 8);
    READ_SCANNED_DATA[4] = (UBYTE)(nlines & 0xff);
    *error = sendCommand(READ_SCANNED_DATA,6,NULL,0,buffer,bsize);
  }
}

/*----------------------------------------------------------------------*/
/*                                                                      */
/* Test scanner                                                         */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void testScanner(BYTE *error)
{
  UBYTE    inquiry[96];    /* buffer for INQUIRY data */
  ScanCmd  INQUIRY = { 0x12, 0x00, 0x00, 0x00, 96, 0x00 };
  
  *error = 0;

  if( sendCommand(INQUIRY,6,NULL,0,inquiry,96) || ((inquiry[0] & 0x1f) != 0x06) )
    *error = SCAN_OPNERR_NOT_SCANNER;
  else if( (inquiry[62] != 0x5f) )
    *error = SCAN_OPNERR_UNKNOWN;
  else
    *error = 0;
}

/////////////////////////////////////////////////////////////////////////
//
//  Interface part
//
/////////////////////////////////////////////////////////////////////////

char* modeStrings[] =
{
  "Halftone",
  "Lineart",
  "Gray",
  "Color",
  NULL
};

int modeDepth[] = {1,1,8,24};
        
char* halfToneStrings[] =
{
  "53-dot screen (53 gray levels)",
  "Horiz. screen (65 gray levels)",
  "Vert. screen (65 gray levels)",
  "Mixed page (33 gray levels)",
  "71-dot screen (29 gray levels)",
  "60-dot #1 (26 gray levels)",
  "60-dot #2 (26 gray levels)",
  "Fine detail #1 (17 gray levels)",
  "Fine detail #2 (17 gray levels)",
  "Slant line (17 gray levels)",
  "Posterizing (10 gray levels)",
  "High Contrast (5 gray levels)",
  NULL
};

int halfToneActive[] = {TRUE,FALSE,FALSE,FALSE};

Range xRange = {DOUBLE_FIX(0.0),DOUBLE_FIX(215.9),DOUBLE_FIX(0.0)};
Range yRange = {DOUBLE_FIX(0.0),DOUBLE_FIX(297.3),DOUBLE_FIX(0.0)};
Range resolutionRange = {3,300,3};
Range brightnessRange = {-100,100,1};
Range contrastRange   = {-42,49,7};
Range exposureRange   = {-18,21,3};
Range gammaRange = {DOUBLE_FIX(0.5),DOUBLE_FIX(4.0),DOUBLE_FIX(0.05)};

struct OptionDescriptor scannerOptions[] = 
{
  {ID_SCANMODE,TYPE_STRING,UNIT_NONE,CONSTRAINT_STRING_LIST,modeStrings,modeDepth},
  {ID_TL_X,TYPE_FIXED,UNIT_MM,CONSTRAINT_RANGE,(char**)&xRange,NULL},
  {ID_TL_Y,TYPE_FIXED,UNIT_MM,CONSTRAINT_RANGE,(char**)&yRange,NULL},
  {ID_BR_X,TYPE_FIXED,UNIT_MM,CONSTRAINT_RANGE,(char**)&xRange,NULL},
  {ID_BR_Y,TYPE_FIXED,UNIT_MM,CONSTRAINT_RANGE,(char**)&yRange,NULL},
  {ID_RESOLUTION,TYPE_INT,UNIT_DPI,CONSTRAINT_RANGE,(char**)&resolutionRange,NULL},
  {ID_HALFTONEPATTERN,TYPE_STRING,UNIT_NONE,CONSTRAINT_STRING_LIST,halfToneStrings,halfToneActive},
  {ID_BRIGHTNESS,TYPE_INT,UNIT_PERCENT,CONSTRAINT_RANGE,(char**)&brightnessRange,NULL},
  {ID_CONTRAST,TYPE_INT,UNIT_PERCENT,CONSTRAINT_RANGE,(char**)&contrastRange,NULL},
  {ID_ANALOGGAMMA,TYPE_FIXED,UNIT_NONE,CONSTRAINT_RANGE,(char**)&gammaRange,NULL},
  {ID_EXPOSURETIME,TYPE_INT,UNIT_PERCENT,CONSTRAINT_RANGE,(char**)&exposureRange,NULL}
};

void closeScanner(void)
{
  if( scanBuffer )
  {
    free(scanBuffer);
    scanBuffer = NULL;
  }
  closeScannerDevice();
}

void openScanner(char* name,int unit,struct ScannerOptions* option,BYTE* status)
{
  scanning = FALSE;
  scanLines = 0;
  linesRead = 0;
  
  if( openScannerDevice(name,unit) )
  {
    testScanner(status);
    if( *status )
    {
      closeScanner();
      return;
    }
    
    if( scanBuffer = (UBYTE*)malloc(SCANBUFSIZE) )
    {
      strcpy(option->so_scannerVendor,"Microtek");
      strcpy(option->so_scannerModel,"Scanmaker E3");
      option->so_driverVersion = 2;
      option->so_driverRevision = 0;
      
      option->so_docWidth = 215.9;
      option->so_docHeight = 297.9;
      
      option->so_flags = 0;
      
      option->so_optionNum = sizeof(scannerOptions)/sizeof(struct OptionDescriptor);
      
      option->so_descriptor = scannerOptions;
      
      *status = SCAN_STATUS_OK;
    }
    else
    {
      closeScanner();
      *status = SCAN_OPNERR_MEMORY; // SCAN_ERR_MEMORY;
    }
  }
  else
    *status = SCAN_OPNERR_DEVICE;   // SCAN_ERR_DEVICEOPEN;
}

void controlOption(struct OptionValue* value,BYTE* status)
{
  if( value->sp_optionID != ID_NONE )
  {
    value->sp_flags &= CONTROL_CALLMASK; // Clear return mask

    if( value->sp_flags & CONTROL_SET )
    {
      switch( value->sp_optionID )
      {
        case ID_SCANMODE:
          scanMode = value->sp_value;
          break;
        case ID_TL_X:            /* upper left corner of scan area     */
          x0 = FIX_DOUBLE(value->sp_value);
          break;
        case ID_TL_Y:            /* upper left corner of scan area     */
          y0 = FIX_DOUBLE(value->sp_value);
          break;
        case ID_BR_X:            /* bottom right corner of scan area   */
          x1 = FIX_DOUBLE(value->sp_value);
          break;
        case ID_BR_Y:            /* bottom right corner of scan area   */
          y1 = FIX_DOUBLE(value->sp_value);
          break;
        case ID_RESOLUTION:      /* scan resolution (combined x and y) */
          if( (value->sp_value < 3) || (value->sp_value > 300) )
            value->sp_flags |= CONTROL_RANGE;
          else
          {
            resolutionCode = (value->sp_value*100)/MAX_BASE_RES;
            if( value->sp_value != (resolutionCode*MAX_BASE_RES)/100 )
              value->sp_flags |= CONTROL_ROUNDED;
          }
          break;
        case ID_HALFTONEPATTERN:
          halftonePattern = value->sp_value;
          break;
        case ID_BRIGHTNESS:
          if( (value->sp_value < -100) || (value->sp_value > 100) )
            value->sp_flags |= CONTROL_RANGE;
          else
            brightnessValue = value->sp_value;
          break;
        case ID_CONTRAST:
          if( (value->sp_value < -42) || (value->sp_value > 49) )
            value->sp_flags |= CONTROL_RANGE;
          else
            contrastValue = ((value->sp_value+42)/7)*7-42;
            if( value->sp_value != contrastValue )
              value->sp_flags |= CONTROL_ROUNDED;
          break;
        case ID_ANALOGGAMMA:
          gamma = FIX_DOUBLE(value->sp_value);
          break;
        case ID_BLACKLEVEL:  /* some times called shadow           */
          shadow = 0;        //value->sp_value;
          break;
        case ID_WHITELEVEL:  /* some times called highlight        */
          highlight = 255;   //value->sp_value;
          break;
        case ID_THRESHOLD:
          midtone = 128;     //value->sp_value;
          break;
        case ID_EXPOSURETIME:
          if( (value->sp_value < -18) || (value->sp_value > 21) )
            value->sp_flags |= CONTROL_RANGE;
          else
            exposureTime = ((value->sp_value+18)/3)*3-18;
            if( value->sp_value != exposureTime )
              value->sp_flags |= CONTROL_ROUNDED;
          break;
        default:
          value->sp_flags |= CONTROL_INVALID;
          break;
      }
    }
        
    if( value->sp_flags & CONTROL_GET )
    {
      switch( value->sp_optionID )
      {
        case ID_SCANMODE:
          value->sp_value = scanMode;
          break;
        case ID_TL_X:            /* upper left corner of scan area     */
          value->sp_value = DOUBLE_FIX(x0);
          break;
        case ID_TL_Y:            /* upper left corner of scan area     */
          value->sp_value = DOUBLE_FIX(y0);
          break;
        case ID_BR_X:            /* bottom right corner of scan area   */
          value->sp_value = DOUBLE_FIX(x1);
          break;
        case ID_BR_Y:            /* bottom right corner of scan area   */
          value->sp_value = DOUBLE_FIX(y1);
          break;
        case ID_RESOLUTION:      /* scan resolution (combined x and y) */
          value->sp_value = (resolutionCode*MAX_BASE_RES)/100;
          break;
        case ID_HALFTONEPATTERN:
          value->sp_value = halftonePattern;
          break;
        case ID_BRIGHTNESS:
          value->sp_value = brightnessValue;
          break;
        case ID_CONTRAST:
          value->sp_value = contrastValue;
          break;
        case ID_ANALOGGAMMA:
          value->sp_value = DOUBLE_FIX(gamma);
          break;
        case ID_EXPOSURETIME:
          value->sp_value = exposureTime;
          break;
        default:
          value->sp_flags |= CONTROL_INVALID;
          break;
      }
    }
  }

  *status = 0;
}

void startScanning(struct ScanInformation* inform,BYTE* status)
{
  *status = 0;
  
  scannerReady(status);
  selectMode(status);
  mode_sense_1(status);
  setScanningFrame(status);
  gammaTable(gamma,status);
  accessory(status);

  if( *status == 0 )
  {
    UWORD imageWidth;
    UWORD imageHeight;
    UWORD imageDepth;
    int   busy;
      
    makeCorrTab(brightnessValue,contrastValue);

    startScan(status);
    readScanStatus(&busy,&lineWidth,&scanLines,status);

    if( lineWidth <= 0 )
      *status = SCAN_ERR_MISC;

    if( *status == 0 )
    {
      switch( scanMode )
      {
        case SCANMODE_HALFTONE:
        case SCANMODE_BW:
          bytesPerLine = lineWidth;
          imageWidth = lineWidth*8;
          imageHeight = scanLines;
          imageDepth = 1;
          numPlanes = 1;
          lineFormat = FORMAT_BW;
          break;
        case SCANMODE_GRAY:
          bytesPerLine = lineWidth;
          imageWidth = bytesPerLine;
          imageHeight = scanLines;
          imageDepth = 8;
          numPlanes = 1;
          lineFormat = FORMAT_GRAY;
          break;
        case SCANMODE_COLOR:
          bytesPerLine = lineWidth-2;
          imageWidth = bytesPerLine;
          imageHeight = scanLines;
          imageDepth = 24;
          numPlanes = 3;
          lineFormat = FORMAT_RGB_RANDOM;
          break;

      }
      bufferLen = SCANBUFSIZE/lineWidth/numPlanes;
      linesRead = 0;
      linesInBuf = 0;
      currentLine = 0;
      nextPlane = 0;
      
      inform->sv_xResolution  = (double)(resolutionCode*MAX_BASE_RES)/100.0;
      inform->sv_yResolution  = (double)(resolutionCode*MAX_BASE_RES)/100.0;
      inform->sv_lineFormat   = lineFormat; 
      inform->sv_imageWidth   = imageWidth;
      inform->sv_imageHeight  = imageHeight;
      inform->sv_imageDepth   = imageDepth;
      inform->sv_bytesPerLine = bytesPerLine;
      inform->sv_Flags        = 0;
     
      scanning = TRUE;
    }
  }
  else
    *status = SCAN_ERR_PARAMETER;
}

void stopScanning(void)
{
  BYTE status = 0;
  
  if( scanLines != linesRead )
    stopScan(&status);
  scanning = FALSE;
}

void readScanLine(struct ScanLine* line,BYTE* status)
{
  if( !scanning )
    *status = SCAN_ERR_NOTSCANNING;
  else if( (scanLines == linesRead) && (currentLine == linesInBuf) )
    *status = SCAN_STATUS_EOF;
  else
  {
    UBYTE* bptr;
    UBYTE* data;
    int    i;
    
    if( currentLine == linesInBuf )
    {
      if( bufferLen < scanLines-linesRead )
        linesInBuf = bufferLen;
      else
        linesInBuf = scanLines-linesRead;
        
      readScanData(linesInBuf,scanBuffer,linesInBuf*lineWidth*numPlanes,status);
      
      if( *status )
        return;
        
      linesRead += linesInBuf;
      currentLine = 0;
    }
    
    bptr = scanBuffer + (numPlanes*currentLine+nextPlane)*lineWidth;
    
    if( lineFormat == FORMAT_RGB_RANDOM )
    {
      line->sl_color = (bptr[1] == 'R') ? FORMAT_RED : ((bptr[1] == 'G') ? FORMAT_GREEN : FORMAT_BLUE);
      bptr += 2;
    }
    else
      line->sl_color = lineFormat;

    for( data = line->sl_data, i = 0 ; i < bytesPerLine ; i++ )
      *(data++) = corrTab[*(bptr++)];
    
    if( ++nextPlane == numPlanes )
    {
      nextPlane = 0;
      currentLine++;
    }
  }
}

