/*
 *   Sun Raster image 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:
 *
 *   _cupsImageReadSunRaster() - Read a SunRaster image file.
 *   read_unsigned()      - Read a 32-bit unsigned integer.
 */

/*
 * Include necessary headers...
 */

#include "image-private.h"


#define	RAS_MAGIC	0x59a66a95

	/* Sun supported ras_type's */
#define RT_OLD		0		/* Raw pixrect image in 68000 byte order */
#define RT_STANDARD	1		/* Raw pixrect image in 68000 byte order */
#define RT_BYTE_ENCODED	2		/* Run-length compression of bytes */
#define RT_FORMAT_RGB	3		/* XRGB or RGB instead of XBGR or BGR */
#define RT_EXPERIMENTAL	0xffff		/* Reserved for testing */

	/* Sun registered ras_maptype's */
#define RMT_RAW		2
	/* Sun supported ras_maptype's */
#define RMT_NONE	0		/* ras_maplength is expected to be 0 */
#define RMT_EQUAL_RGB	1		/* red[ras_maplength/3],green[],blue[] */

#define RAS_RLE 0x80

/*
 * NOTES:
 * 	Each line of the image is rounded out to a multiple of 16 bits.
 *   This corresponds to the rounding convention used by the memory pixrect
 *   package (/usr/include/pixrect/memvar.h) of the SunWindows system.
 *	The ras_encoding field (always set to 0 by Sun's supported software)
 *   was renamed to ras_length in release 2.0.  As a result, rasterfiles
 *   of type 0 generated by the old software claim to have 0 length; for
 *   compatibility, code reading rasterfiles must be prepared to compute the
 *   true length from the width, height, and depth fields.
 */

/*
 * Local functions...
 */

static unsigned	read_unsigned(FILE *fp);


/*
 * '_cupsImageReadSunRaster()' - Read a SunRaster image file.
 */

int					/* O - Read status */
_cupsImageReadSunRaster(
    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		i, x, y,
		bpp,			/* Bytes per pixel */
		scanwidth,
		run_count,
		run_value;
  cups_ib_t	*in,
		*out,
		*scanline,
		*scanptr,
		*p,
		bit;
  unsigned	ras_depth,		/* depth (1, 8, or 24 bits) of pixel */
		ras_type,		/* type of file; see RT_* below */
		ras_maplength;		/* length (bytes) of following map */
  unsigned char	cmap[3][256];		/* colormap */


 /*
  * Read the header; we already know that this is a raster file (cupsImageOpen
  * checks this) so we don't need to check the magic number again.
  */

  fputs("DEBUG: Reading Sun Raster image...\n", stderr);

  read_unsigned(fp); /* Skip magic */
  img->xsize    = read_unsigned(fp);
  img->ysize    = read_unsigned(fp);
  ras_depth     = read_unsigned(fp);
  /* ras_length */read_unsigned(fp);
  ras_type      = read_unsigned(fp);
  /* ras_maptype*/read_unsigned(fp);
  ras_maplength = read_unsigned(fp);

  fprintf(stderr, "DEBUG: ras_width=%d, ras_height=%d, ras_depth=%d, ras_type=%d, ras_maplength=%d\n",
          img->xsize, img->ysize, ras_depth, ras_type, ras_maplength);

  if (ras_maplength > 768 ||
      img->xsize == 0 || img->xsize > CUPS_IMAGE_MAX_WIDTH ||
      img->ysize == 0 || img->ysize > CUPS_IMAGE_MAX_HEIGHT ||
      ras_depth == 0 || ras_depth > 32)
  {
    fputs("DEBUG: Raster image cannot be loaded!\n", stderr);
    fclose(fp);
    return (1);
  }

  if (ras_maplength > 0)
  {
    memset(cmap[0], 255, sizeof(cmap[0]));
    memset(cmap[1], 0, sizeof(cmap[1]));
    memset(cmap[2], 0, sizeof(cmap[2]));

    if (fread(cmap[0], 1, ras_maplength / 3, fp) == 0 && ferror(fp))
      DEBUG_printf(("Error reading file!"));
    if (fread(cmap[1], 1, ras_maplength / 3, fp) == 0 && ferror(fp))
      DEBUG_printf(("Error reading file!"));
    if (fread(cmap[2], 1, ras_maplength / 3, fp) == 0 && ferror(fp))
      DEBUG_printf(("Error reading file!"));
  }

 /*
  * Compute the width of each line and allocate memory as needed...
  */

  scanwidth = (img->xsize * ras_depth + 7) / 8;
  if (scanwidth & 1)
    scanwidth ++;

  if (ras_depth < 24 && ras_maplength == 0)
  {
    img->colorspace = secondary;
    in = malloc(img->xsize + 1);
  }
  else
  {
    img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_RGB : primary;
    in = malloc(img->xsize * 3 + 1);
  }

  if (!in)
  {
    fputs("DEBUG: Unable to allocate memory!\n", stderr);
    fclose(fp);
    return (1);
  }

  bpp = cupsImageGetDepth(img);

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

  if ((scanline = malloc(scanwidth)) == NULL)
  {
    fputs("DEBUG: Unable to allocate memory!\n", stderr);
    fclose(fp);
    free(in);
    free(out);
    return (1);
  }

  run_count = 0;
  run_value = 0;

  fprintf(stderr, "DEBUG: bpp=%d, scanwidth=%d\n", bpp, scanwidth);

  for (y = 0; y < img->ysize; y ++)
  {
    if ((ras_depth != 8 && ras_depth != 24) || ras_maplength > 0)
      p = scanline;
    else
      p = in;

    if (ras_type != RT_BYTE_ENCODED)
    {
      if (fread(p, scanwidth, 1, fp) == 0 && ferror(fp))
	DEBUG_printf(("Error reading file!"));
    }
    else
    {
      for (i = scanwidth; i > 0; i --, p ++)
      {
        if (run_count > 0)
        {
          *p = run_value;
          run_count --;
        }
        else
        {
          run_value = getc(fp);

          if (run_value == RAS_RLE)
          {
            run_count = getc(fp);
            if (run_count == 0)
              *p = RAS_RLE;
            else
              run_value = *p = getc(fp);
          }
          else
            *p = run_value;
        }
      }
    }

    if (ras_depth == 1 && ras_maplength == 0)
    {
     /*
      * 1-bit B&W image...
      */

      for (x = img->xsize, bit = 128, scanptr = scanline, p = in;
           x > 0;
           x --, p ++)
      {
	if (*scanptr & bit)
          *p = 255;
        else
          *p = 0;

	if (bit > 1)
          bit >>= 1;
	else
	{
          bit = 128;
          scanptr ++;
	}
      }
    }
    else if (ras_depth == 1)
    {
     /*
      * 1-bit colormapped image...
      */

      for (x = img->xsize, bit = 128, scanptr = scanline, p = in;
           x > 0;
           x --)
      {
	if (*scanptr & bit)
	{
          *p++ = cmap[0][1];
          *p++ = cmap[1][1];
          *p++ = cmap[2][1];
	}
        else
	{
          *p++ = cmap[0][0];
          *p++ = cmap[1][0];
          *p++ = cmap[2][0];
	}

	if (bit > 1)
          bit >>= 1;
	else
	{
          bit = 128;
          scanptr ++;
	}
      }
    }
    else if (ras_depth == 8 && ras_maplength > 0)
    {
     /*
      * 8-bit colormapped image.
      */

      for (x = img->xsize, scanptr = scanline, p = in;
           x > 0;
           x --)
      {
        *p++ = cmap[0][*scanptr];
        *p++ = cmap[1][*scanptr];
        *p++ = cmap[2][*scanptr++];
      }
    }
    else if (ras_depth == 24 && ras_type != RT_FORMAT_RGB)
    {
     /*
      * Convert BGR to RGB...
      */

      for (x = img->xsize, scanptr = scanline, p = in;
           x > 0;
           x --, scanptr += 3)
      {
        *p++ = scanptr[2];
        *p++ = scanptr[1];
        *p++ = scanptr[0];
      }
    }

    if (ras_depth <= 8 && ras_maplength == 0)
    {
      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);
      }
    }
    else
    {
      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_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);
    }
  }

  free(scanline);
  free(in);
  free(out);

  fclose(fp);

  return (0);
}


/*
 * 'read_unsigned()' - Read a 32-bit unsigned integer.
 */

static unsigned				/* O - Integer from file */
read_unsigned(FILE *fp)			/* I - File to read from */
{
  unsigned	v;			/* Integer from file */


  v = getc(fp);
  v = (v << 8) | getc(fp);
  v = (v << 8) | getc(fp);
  v = (v << 8) | getc(fp);

  return (v);
}

