/*
 *   Portable Any Map file routines for CUPS.
 *
 *   Copyright 2007-2011 by Apple Inc.
 *   Copyright 1993-2007 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Apple Inc. and are protected by Federal copyright
 *   law.  Distribution and use rights are outlined in the file "COPYING"
 *   which should have been included with this file.
 *
 * Contents:
 *
 *   _cupsImageReadPNM() - Read a PNM image file.
 */

/*
 * Include necessary headers...
 */

#include "image-private.h"


/*
 * '_cupsImageReadPNM()' - Read a PNM image file.
 */

int					/* O - Read status */
_cupsImageReadPNM(
    cups_image_t    *img,		/* IO - cupsImage */
    FILE            *fp,		/* I - cupsImage file */
    cups_icspace_t  primary,		/* I - Primary choice for colorspace */
    cups_icspace_t  secondary,		/* I - Secondary choice for colorspace */
    int             saturation,		/* I - Color saturation (%) */
    int             hue,		/* I - Color hue (degrees) */
    const cups_ib_t *lut)		/* I - Lookup table for gamma/brightness */
{
  int		x, y;			/* Looping vars */
  int		bpp;			/* Bytes per pixel */
  cups_ib_t	*in,			/* Input pixels */
		*inptr,			/* Current input pixel */
		*out,			/* Output pixels */
		*outptr,		/* Current output pixel */
		bit;			/* Bit in input line */
  char		line[255],		/* Input line */
		*lineptr;		/* Pointer in line */
  int		format,			/* Format of PNM file */
		val,			/* Pixel value */
		maxval;			/* Maximum pixel value */


 /*
  * Read the file header in the format:
  *
  *   Pformat
  *   # comment1
  *   # comment2
  *   ...
  *   # commentN
  *   width
  *   height
  *   max sample
  */

  if ((lineptr = fgets(line, sizeof(line), fp)) == NULL)
  {
    fputs("DEBUG: Bad PNM header!\n", stderr);
    fclose(fp);
    return (1);
  }

  lineptr ++;

  format = atoi(lineptr);
  while (isdigit(*lineptr & 255))
    lineptr ++;

  while (lineptr != NULL && img->xsize == 0)
  {
    if (*lineptr == '\0' || *lineptr == '#')
      lineptr = fgets(line, sizeof(line), fp);
    else if (isdigit(*lineptr & 255))
    {
      img->xsize = atoi(lineptr);
      while (isdigit(*lineptr & 255))
	lineptr ++;
    }
    else
      lineptr ++;
  }

  while (lineptr != NULL && img->ysize == 0)
  {
    if (*lineptr == '\0' || *lineptr == '#')
      lineptr = fgets(line, sizeof(line), fp);
    else if (isdigit(*lineptr & 255))
    {
      img->ysize = atoi(lineptr);
      while (isdigit(*lineptr & 255))
	lineptr ++;
    }
    else
      lineptr ++;
  }

  if (format != 1 && format != 4)
  {
    maxval = 0;

    while (lineptr != NULL && maxval == 0)
    {
      if (*lineptr == '\0' || *lineptr == '#')
	lineptr = fgets(line, sizeof(line), fp);
      else if (isdigit(*lineptr & 255))
      {
	maxval = atoi(lineptr);
	while (isdigit(*lineptr & 255))
	  lineptr ++;
      }
      else
	lineptr ++;
    }
  }
  else
    maxval = 1;

  if (img->xsize == 0 || img->xsize > CUPS_IMAGE_MAX_WIDTH ||
      img->ysize == 0 || img->ysize > CUPS_IMAGE_MAX_HEIGHT)
  {
    fprintf(stderr, "DEBUG: Bad PNM dimensions %dx%d!\n",
            img->xsize, img->ysize);
    fclose(fp);
    return (1);
  }

  if (maxval == 0)
  {
    fprintf(stderr, "DEBUG: Bad PNM max value %d!\n", maxval);
    fclose(fp);
    return (1);
  }

  if (format == 1 || format == 2 || format == 4 || format == 5)
    img->colorspace = secondary;
  else
    img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_RGB : primary;

  cupsImageSetMaxTiles(img, 0);

  bpp = cupsImageGetDepth(img);

  if ((in = malloc(img->xsize * 3)) == NULL)
  {
    fputs("DEBUG: Unable to allocate memory!\n", stderr);
    fclose(fp);
    return (1);
  }

  if ((out = malloc(img->xsize * bpp)) == NULL)
  {
    fputs("DEBUG: Unable to allocate memory!\n", stderr);
    fclose(fp);
    free(in);
    return (1);
  }

 /*
  * Read the image file...
  */

  for (y = 0; y < img->ysize; y ++)
  {
    switch (format)
    {
      case 1 :
          for (x = img->xsize, inptr = in; x > 0; x --, inptr ++)
            if (fscanf(fp, "%d", &val) == 1)
              *inptr = val ? 0 : 255;
          break;

      case 2 :
          for (x = img->xsize, inptr = in; x > 0; x --, inptr ++)
            if (fscanf(fp, "%d", &val) == 1)
              *inptr = 255 * val / maxval;
          break;

      case 3 :
          for (x = img->xsize, inptr = in; x > 0; x --, inptr += 3)
          {
            if (fscanf(fp, "%d", &val) == 1)
              inptr[0] = 255 * val / maxval;
            if (fscanf(fp, "%d", &val) == 1)
              inptr[1] = 255 * val / maxval;
            if (fscanf(fp, "%d", &val) == 1)
              inptr[2] = 255 * val / maxval;
          }
          break;

      case 4 :
          if (fread(out, (img->xsize + 7) / 8, 1, fp) == 0 && ferror(fp))
	    DEBUG_printf(("Error reading file!"));
          for (x = img->xsize, inptr = in, outptr = out, bit = 128;
               x > 0;
               x --, inptr ++)
          {
            if (*outptr & bit)
              *inptr = 0;
            else
              *inptr = 255;

            if (bit > 1)
              bit >>= 1;
            else
            {
              bit = 128;
              outptr ++;
            }
          }
          break;

      case 5 :
          if (fread(in, img->xsize, 1, fp) == 0 && ferror(fp))
	    DEBUG_printf(("Error reading file!"));
          break;

      case 6 :
          if (fread(in, img->xsize, 3, fp) == 0 && ferror(fp))
	    DEBUG_printf(("Error reading file!"));
          break;
    }

    switch (format)
    {
      case 1 :
      case 2 :
      case 4 :
      case 5 :
          if (img->colorspace == CUPS_IMAGE_WHITE)
	  {
	    if (lut)
	      cupsImageLut(in, img->xsize, lut);

            _cupsImagePutRow(img, 0, y, img->xsize, in);
	  }
	  else
	  {
	    switch (img->colorspace)
	    {
              default :
		  break;

	      case CUPS_IMAGE_RGB :
		  cupsImageWhiteToRGB(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_BLACK :
		  cupsImageWhiteToBlack(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_CMY :
		  cupsImageWhiteToCMY(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_CMYK :
		  cupsImageWhiteToCMYK(in, out, img->xsize);
		  break;
	    }

	    if (lut)
	      cupsImageLut(out, img->xsize * bpp, lut);

            _cupsImagePutRow(img, 0, y, img->xsize, out);
	  }
	  break;

      default :
	  if ((saturation != 100 || hue != 0) && bpp > 1)
	    cupsImageRGBAdjust(in, img->xsize, saturation, hue);

	  switch (img->colorspace)
	  {
            default :
		break;

	    case CUPS_IMAGE_WHITE :
		cupsImageRGBToWhite(in, out, img->xsize);
		break;
	    case CUPS_IMAGE_RGB :
		cupsImageRGBToRGB(in, out, img->xsize);
		break;
	    case CUPS_IMAGE_BLACK :
		cupsImageRGBToBlack(in, out, img->xsize);
		break;
	    case CUPS_IMAGE_CMY :
		cupsImageRGBToCMY(in, out, img->xsize);
		break;
	    case CUPS_IMAGE_CMYK :
		cupsImageRGBToCMYK(in, out, img->xsize);
		break;
	  }

	  if (lut)
	    cupsImageLut(out, img->xsize * bpp, lut);

          _cupsImagePutRow(img, 0, y, img->xsize, out);
  	  break;
    }
  }

  free(in);
  free(out);

  fclose(fp);

  return (0);
}

