/* ppmtodpp  PPM (raw P6) to Sony DPP-M55 printout data */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#ifdef X68
#include <iocslib.h>
#include <fcntl.h>  /* for setmode() */
int prtGRAM = 0;
#endif

#define NORM255(x) { if(x<0) x=0; else if (x>255) x=255; }

#define NORM(x, y, z) { x = (x * (z-y) / 255) + y ; if(x<y) x=y; else if (x>z) x=z; }

#ifdef X68
#define RMODE "rb"
#define WMODE "wb"
#else
#define RMODE "r"
#define WMODE "w"
#endif

int PICTURE_WIDTH;
int PICTURE_HEIGHT;

#define MAX_WIDTH 480
#define MAX_HEIGHT 640

#define MAX_YELLOW 255
#define MAX_MAGENDA 255
#define MAX_CYAN 255

#define ESC 0x1b

int errflg = 0;
unsigned char R[MAX_WIDTH][MAX_HEIGHT];
unsigned char G[MAX_WIDTH][MAX_HEIGHT];
unsigned char B[MAX_WIDTH][MAX_HEIGHT];

unsigned char tableR[256];
unsigned char tableG[256];
unsigned char tableB[256];

int rotate = 0;
int center = 1;
int verbose = 0;
double gammaR = 1.0;
double gammaG = 1.0;
double gammaB = 1.0;

int norm_max_Y = 204;
int norm_min_Y = 30;
int norm_max_M = 218;
int norm_min_M = 36;
int norm_max_C = 225;
int norm_min_C = 37;

extern int	optind, opterr;
extern char	*optarg;

void
skip_space(infp)
     FILE *infp;
{
  int c;
  c = ' ';
  while(isspace(c)){
    c = fgetc(infp);
  }
  if(c == EOF)
    return;
  ungetc(c, infp);
}

int fgetline(s, size, infp)
     char *s;
     int size;
     FILE *infp;
{
  int i;
  int c;
 fgetretry:
  skip_space(infp);
  for(i = 0 ; i < size; i++){
    c = fgetc(infp);
    if(c == EOF)
      return(EOF);
    if(isspace(c)){
      s[i] = '\0';
      if(c != '\n') skip_space(infp);
      return(i);
    }else if ( c == '#') {
      while(fgetc(infp) != '\n');
      s[i] = '\0';
      if(i == 0) goto fgetretry;
      return(i);
    }else
      s[i] = c;
  }
  return(i);
}

void buildgamma()
{
  int i;
  int v;
  double one_over_gammaR;
  double one_over_gammaG;
  double one_over_gammaB;
  double ind, q;

  one_over_gammaR = 1.0 / gammaR;
  one_over_gammaG = 1.0 / gammaG;
  one_over_gammaB = 1.0 / gammaB;
  for ( i = 0 ; i <= 255; ++i ){
    ind = ( (double) i ) / (double) 255;
    v = ( pow(ind, one_over_gammaR) * (double) 255 ) + 0.5;
    if ( v > 255 ) v = 255;
    tableR[i] = v;
    v = ( pow(ind, one_over_gammaG) * (double) 255 ) + 0.5;
    if ( v > 255 ) v = 255;
    tableG[i] = v;
    v = ( pow(ind, one_over_gammaB) * (double) 255 ) + 0.5;
    if ( v > 255 ) v = 255;
    tableB[i] = v;
  }
}

#ifdef X68
int 
FPUTC(c, stream)
     int c;
     FILE *stream;
{
  if(stream)
    fputc(c, stream);
  else{
    /* put data to LPT */
    while(SNSPRN() ==0);
    OUTLPT(c);
  }
}
#else
#define FPUTC(c, stream) fputc(c, stream)
#endif

#ifdef X68
int
get_pixel()
{
  int x, y;
  int i, j;
  unsigned char WB[5][16];
  unsigned char WR[5][16];
  unsigned char WG[5][16];
  unsigned char b0, b1, r0, r1, g0, g1;
  struct POINTPTR point;
  int X, Y;

  if(CRTMOD(-1) != 12){
    fprintf(stderr, "CRTMODE is not 65535(512x512) (high) mode.\n");
    return(0);
  }


  y = 0;
  if(verbose)
    fprintf(stderr,"Get pixel y 000");
  for(Y = 0; Y < 512 ; Y = Y + 16){
    x = 0;
    for(X = 0; X < 512 ; X = X + 4){
      for(j = 0; j < 16 ; j++){
	for(i = 0; i < 4 ; i++){
	  point.x = X + i;
	  point.y = Y + j;
	  POINT(&point);
	  b0 = 0xf8 & (point.color << 2);
	  r0 = 0xf8 & (point.color >> 3);
	  g0 = 0xf8 & (point.color >> 8);
	  if(i == 0){
	    WB[i][j] = b0;
	    WR[i][j] = r0;
	    WG[i][j] = g0;
	  }else{
	    WB[i][j] =  ((int) b0 + (int) b1) / 2 ;
	    WR[i][j] =  ((int) r0 + (int) r1) / 2 ;
	    WG[i][j] =  ((int) g0 + (int) g1) / 2 ;
	  }
	  b1 = b0;
	  r1 = r0;
	  g1 = g0;
	}
	WB[4][j] = b0;
	WR[4][j] = r0;
	WG[4][j] = g0;
      }
      for(j = 0 ; j < 15 ; j++){
	for(i = 0; i < 5 ; i++){
	  b1 = ((int) WB[i][j] + (int) WB[i][j + 1]) /2;
	  r1 = ((int) WR[i][j] + (int) WR[i][j + 1]) /2;
	  g1 = ((int) WG[i][j] + (int) WG[i][j + 1]) /2;
	  if(gammaB == 1.0)
	    B[479 - (y + j)][x + i] = b1;
	  else
	    B[479 - (y + j)][x + i] = tableB[b1];
	  if(gammaR == 1.0)
	    R[479 - (y + j)][x + i] = r1;
	  else
	    R[479 - (y + j)][x + i] = tableR[r1];
  	  if(gammaG == 1.0)
	    G[479 - (y + j)][x + i] = g1;
	  else
	    G[479 - (y + j)][x + i] = tableG[g1];
	}
      }
      x = x + 5;
    }
    y = y + 15;
    if(verbose)
      fprintf(stderr,"\b\b\b%03d", Y);
  }
  if(verbose)
    fprintf(stderr, "512\n");
  return(1);
}
#endif

int
read_ppm(infilename, skiphead)
     char *infilename;
     int skiphead;
{
  FILE *infp;
  unsigned char lbuf[256];
  unsigned char magic[2] = { 'P', '6' };
  int r, g, b;
  int x, y;
  int X, Y;
  int offsetx = 0;
  int offsety = 0;

  infp = stdin;
  if (infilename) {
    infp = fopen(infilename, RMODE);
    if (infp == NULL){
      fprintf(stderr, "can't open infile(%s).\n", infilename);
      errflg ++;
      goto cleanup;
    }
  }

#ifdef X68
  if(infp == stdin)
    setmode(fileno(stdin), O_BINARY);
#endif

  if(!skiphead){
    if(fgetline(lbuf, 3, infp) < 0 ){
      fprintf(stderr, "can't read header.\n");
      errflg ++;
      goto cleanup;
    }

    if(memcmp(lbuf, magic, sizeof(magic)) != 0){
      fprintf(stderr, "this file(%s) is not PPM(P6) format.\n", infilename);
      errflg ++;
      goto cleanup;
    }

    if(fgetline(lbuf, sizeof(lbuf), infp) < 0 ){
      fprintf(stderr, "can't read width data.\n");
      errflg ++;
      goto cleanup;
    }

    PICTURE_WIDTH = atoi(lbuf);
    if(PICTURE_WIDTH == 0){
      fprintf(stderr, "can't get width data.\n");
      errflg ++;
      goto cleanup;
    }

    if(fgetline(lbuf, sizeof(lbuf), infp) < 0 ){
      fprintf(stderr, "can't read height data.\n");
      errflg ++;
      goto cleanup;
    }

    PICTURE_HEIGHT = atoi(lbuf);
    if(PICTURE_HEIGHT == 0){
      fprintf(stderr, "can't get height data.\n");
      errflg ++;
      goto cleanup;
    }

    if(PICTURE_WIDTH > PICTURE_HEIGHT){
      PICTURE_HEIGHT = PICTURE_WIDTH;
      PICTURE_WIDTH = atoi(lbuf);
      rotate = 1;
    }

    if((PICTURE_WIDTH > MAX_WIDTH)  || (PICTURE_HEIGHT > MAX_HEIGHT)){
      fprintf(stderr, "picture image is too large. (over 480x640)\n");
      errflg ++;
      goto cleanup;
    }

    if(center){
      offsetx = (MAX_WIDTH - PICTURE_WIDTH)/2;
      offsety = (MAX_HEIGHT - PICTURE_HEIGHT)/2;
    }

    if(fgetline(lbuf, sizeof(lbuf), infp) < 0 ){
      fprintf(stderr, "can't read color depth data.\n");
      errflg ++;
      goto cleanup;
    }

    if(strcmp((char *)lbuf, "255") != 0){
      fprintf(stderr, "this picture's depth is not 255.\n");
      errflg ++;
      goto cleanup;
    }

  }

  if(verbose) fprintf(stderr, "loading 000");
  
  if(rotate)
    for(y = (PICTURE_WIDTH -1)  ; y >= 0 ; y--){
      if(verbose)
	fprintf(stderr, "\b\b\b%03d", y);
      for( x = 0 ; x < PICTURE_HEIGHT ; x++){
	X = offsetx + x;
	Y = offsety + y;
	r = fgetc(infp);
	g = fgetc(infp);
	b = fgetc(infp);
	
	if((r < 0) || (g < 0) || (b < 0)) {
	  perror("fgetc");
	  errflg ++;
	  goto cleanup;
	}
	if(gammaR == 1.0)
	  R[Y][X] = r;
	else
	  R[Y][X] = tableR[r];
	if(gammaG == 1.0)
	  G[Y][X] = g;
	else
	  G[Y][X] = tableG[g];
	if(gammaB == 1.0)
	  B[Y][X] = b;
	else
	  B[Y][X] = tableB[b];
      }
    }
  else 
    for(y = 0 ; y < PICTURE_HEIGHT ; y++){
      if(verbose)
	fprintf(stderr, "\b\b\b%03d", y);
      for( x = 0 ; x < PICTURE_WIDTH ; x++){
	X = offsetx + x;
	Y = offsety + y;
	r = fgetc(infp);
	g = fgetc(infp);
	b = fgetc(infp);

	if((r < 0) || (g < 0) || (b < 0)) {
	  perror("fgetc");
	  errflg ++;
	  goto cleanup;
	}
	if(gammaR == 1.0)
	  R[X][Y] = r;
	else
	  R[X][Y] = tableR[r];
	if(gammaG == 1.0)
	  G[X][Y] = g;
	else
	  G[X][Y] = tableG[g];
	if(gammaB == 1.0)
	  B[X][Y] = b;
	else
	  B[X][Y] = tableB[b];
      }
    }
  if(verbose) fprintf(stderr, "\b\b\bEnd.\n");

 cleanup:
  if (infp != stdin)
    fclose(infp);
  if(errflg)
    return(0);
  return(1);
}

void
ppm_to_dpp(outfilename)
     char *outfilename;
{
  FILE *fp;
  int x, y;

  int i;
  int YMC;

  fp = stdout;
  if (outfilename) {
    fp = fopen(outfilename, WMODE);
    if (fp == NULL){
      fprintf(stderr, "can't open outfile(%s).\n", outfilename);
      errflg ++;
      if (fp != stdout)
	fclose(fp);
      return;
    }
  }

#ifdef X68
  if(fp == stdout){
    fp = NULL;
    /* do printer initialize */
    if(INIT_PRN(0xff, 0xff) == 0)
      fprintf(stderr, "init prn fail\n");
  }
#endif

  /* Header */
  FPUTC(ESC, fp);FPUTC('C', fp);
  FPUTC(ESC, fp);FPUTC('M', fp);FPUTC('S', fp);
  FPUTC(0xcc, fp);FPUTC(0xda, fp);FPUTC(0xe1, fp);
  FPUTC(ESC, fp);FPUTC('N', fp);FPUTC(0x01, fp);
  FPUTC(ESC, fp);FPUTC('A', fp);FPUTC(0x00, fp);
  FPUTC(ESC, fp);FPUTC('D', fp);FPUTC(0x00, fp);
  FPUTC(ESC, fp);FPUTC('Z', fp);FPUTC(0x00, fp);FPUTC(0x90, fp);
  FPUTC(0x00, fp);FPUTC(0x90, fp);

  /* Yellow */
  i = 0;
  if(verbose) fprintf(stderr, "Yellow 000");
  for(y = 0 ; y < MAX_HEIGHT ; y++){
    if(verbose)
      fprintf(stderr, "\b\b\b%03d", y);
    if(y % 32 == 0 ){
      FPUTC(ESC, fp);FPUTC('W', fp);FPUTC('Y', fp);
      FPUTC((i >> 8) &  0xff, fp);FPUTC( i & 0xff, fp);
      FPUTC(0x00, fp);FPUTC(0x00, fp);
      FPUTC((i >> 8) & 0xff, fp);FPUTC( (i & 0xff) + 0x1f, fp);
      FPUTC(0x01, fp);FPUTC(0xdf, fp);
      i = i + 0x20;
    }
    for( x = 0 ; x < MAX_WIDTH ; x++){
      YMC = MAX_YELLOW - B[x][y];
      NORM(YMC, norm_min_Y, norm_max_Y);
      FPUTC(YMC, fp);
    }
  }
  FPUTC(ESC, fp);FPUTC('P', fp);FPUTC('Y', fp);FPUTC('S', fp);
  if(verbose) fprintf(stderr, "\b\b\b%03d\n", MAX_HEIGHT);

  /* Magenda */
  i = 0;
  if(verbose) fprintf(stderr, "Magenda 000");
  for(y = 0 ; y < MAX_HEIGHT ; y++){
    if(verbose)
      fprintf(stderr, "\b\b\b%03d", y);
    if(y % 32 == 0 ){
      FPUTC(ESC, fp);FPUTC('W', fp);FPUTC('M', fp);
      FPUTC((i >> 8) &  0xff, fp);FPUTC( i & 0xff, fp);
      FPUTC(0x00, fp);FPUTC(0x00, fp);
      FPUTC((i >> 8) & 0xff, fp);FPUTC( (i & 0xff) + 0x1f, fp);
      FPUTC(0x01, fp);FPUTC(0xdf, fp);
      i = i + 0x20;
    }
    for( x = 0 ; x < MAX_WIDTH ; x++){
      YMC = MAX_MAGENDA - G[x][y];
      NORM(YMC, norm_min_M, norm_max_M);
      FPUTC(YMC, fp);
    }
  }
  FPUTC(ESC, fp);FPUTC('P', fp);FPUTC('M', fp);FPUTC('S', fp);
  if(verbose) fprintf(stderr, "\b\b\b%03d\n", MAX_HEIGHT);
  
  /* Cyan */
  i = 0;
  if(verbose) fprintf(stderr, "Cyan 000");
  for(y = 0 ; y < MAX_HEIGHT ; y++){
    if(verbose)
      fprintf(stderr, "\b\b\b%03d", y);
    if(y % 32 == 0 ){
      FPUTC(ESC, fp);FPUTC('W', fp);FPUTC('C', fp);
      FPUTC((i >> 8) &  0xff, fp);FPUTC( i & 0xff, fp);
      FPUTC(0x00, fp);FPUTC(0x00, fp);
      FPUTC((i >> 8) & 0xff, fp);FPUTC( (i & 0xff) + 0x1f, fp);
      FPUTC(0x01, fp);FPUTC(0xdf, fp);
      i = i + 0x20;
    }
    for( x = 0 ; x < MAX_WIDTH ; x++){
      YMC = MAX_CYAN - R[x][y];
      NORM(YMC, norm_min_C, norm_max_C);
      FPUTC(YMC, fp);
    }
  }
  FPUTC(ESC, fp);FPUTC('P', fp);FPUTC('C', fp);FPUTC('S', fp);
  if(verbose) fprintf(stderr, "\b\b\b%03d\n", MAX_HEIGHT);

  if((fp != NULL) && (fp != stdout))
    fclose(fp);
  
  return;
}

void init()
{
  int x, y;
  for(x = 0 ; x < MAX_WIDTH ; x++)
    for(y = 0 ; y < MAX_HEIGHT ; y++){
      R[x][y] = 0xff;
      G[x][y] = 0xff;
      B[x][y] = 0xff;
    }
  if(!((gammaR == 1.0) && (gammaG == 1.0) && (gammaB == 1.0)))
    buildgamma();
  NORM255(norm_min_Y);
  NORM255(norm_min_M);
  NORM255(norm_min_C);
  NORM255(norm_max_Y);
  NORM255(norm_max_M);
  NORM255(norm_max_C);
  if(norm_min_Y > norm_max_Y){
    fprintf(stderr, "wrong value is setted at -Y or -y option.\n");
    exit(-2);
  }
  if(norm_min_M > norm_max_M){
    fprintf(stderr, "wrong value is setted at -M or -m option.\n");
    exit(-2);
  }
  if(norm_min_C > norm_max_C){
    fprintf(stderr, "wrong value is setted at -C or -c option.\n");
    exit(-2);
  }
	  
}

void usage()
{
  static	char	*usagestr[] =  {
    "ppmtodpp (Ver 0.04) (c)1997 ken-ichi HAYASHI\n",
    "ppmtodpp [options] ppmfile\n",
    "\t -n           : disable centering.\n",
    "\t -o filename  : output filename.(YMC data for DPP-M55)\n",
    "\t -r value     : gamma value for RED. (dark < 1.0 < light)\n",
    "\t -g value     : gamma value for GREEN. (dark < 1.0 < light)\n",
    "\t -b value     : gamma value for BLUE. (dark < 1.0 < light)\n",
    "\t -y value     : minimum value of Yellow. (default 30)\n",
    "\t -Y value     : maximum value of Yellow. (default 204)\n",
    "\t -m value     : minimum value of Magenda. (default 36)\n",
    "\t -M value     : maximum value of Magenda. (default 218)\n",
    "\t -c value     : minimum value of Cyan. (default 37)\n",
    "\t -C value     : maximum value of Cyan. (default 225)\n",
#ifdef X68
    "\t -X           : print graphics(G-RAM) .\n",
#endif
    "\t -h           : show this usage.\n",
    (char *)NULL,
  };
  char	**p;

  p = usagestr;
  while (*p)
    fprintf(stderr, *p++);
}

void
main(argc, argv)
     int	argc;
     char	**argv;
{
  char c;
  char *outfilename = NULL;

  while ((c = getopt( argc, argv, "hno:vr:g:b:Y:M:C:y:m:c:X")) != -1){
    switch(c){
    case 'n':
      center = 0;
      break;
    case 'r':
      gammaR = atof(optarg);
      break;
    case 'g':
      gammaG = atof(optarg);
      break;
    case 'b':
      gammaB = atof(optarg);
      break;
    case 'Y':
      norm_max_Y = atoi(optarg);
      break;
    case 'M':
      norm_max_M = atoi(optarg);
      break;
    case 'C':
      norm_max_C = atoi(optarg);
      break;
    case 'y':
      norm_min_Y = atoi(optarg);
      break;
    case 'm':
      norm_min_M = atoi(optarg);
      break;
    case 'c':
      norm_min_C = atoi(optarg);
      break;
    case 'o':
      outfilename = optarg;
      break;
    case 'v':
      verbose = 1;
      break;
    case 'X':
      prtGRAM = 1;
      break;
    case 'h':
    default:
      usage();
      exit(-1);
    }
  }
  init();
#ifdef X68
  if(prtGRAM){
    if(get_pixel())
      ppm_to_dpp(outfilename);
    if(errflg)
      exit(1);
    exit(0);
  }
    
#endif
  if(optind == argc){
    if(read_ppm(NULL, 0))
      ppm_to_dpp(outfilename);
  }else
    while(optind <argc){
      if(read_ppm(argv[optind++], 0))
	ppm_to_dpp(outfilename);
    }
  if(errflg)
    exit(1);
  exit(0);
}
