/*
 * "$Id: print-util.c,v 1.6 1997/07/30 20:33:05 mike Exp mike $"
 *
 *   Print plug-in driver utility functions for the GIMP.
 *
 *   Copyright 1997 Michael Sweet (mike@easysw.com)
 *
 *   This program is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU General Public License as published by the Free
 *   Software Foundation; either version 2 of the License, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *   for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Contents:
 *
 *   dither_black()    - Dither grayscale pixels to black.
 *   dither_cmyk()     - Dither RGB pixels to cyan, magenta, yellow, and black.
 *   gray_to_gray()    - Convert grayscale image data to grayscale (brightness
 *                       adjusted).
 *   indexed_to_gray() - Convert indexed image data to grayscale.
 *   indexed_to_rgb()  - Convert indexed image data to RGB.
 *   media_width()     - Get the addressable width of the page.
 *   media_height()    - Get the addressable height of the page.
 *   rgb_to_gray()     - Convert RGB image data to grayscale.
 *   rgb_to_rgb()      - Convert RGB image data to RGB (brightness adjusted).
 *
 * Revision History:
 *
 *   $Log: print-util.c,v $
 *   Revision 1.6  1997/07/30  20:33:05  mike
 *   Final changes for 1.1 release.
 *
 *   Revision 1.5  1997/07/26  18:43:04  mike
 *   Fixed dither_black and dither_cmyk - wasn't clearing extra bits
 *   (caused problems with A3/A4 size output).
 *
 *   Revision 1.5  1997/07/26  18:43:04  mike
 *   Fixed dither_black and dither_cmyk - wasn't clearing extra bits
 *   (caused problems with A3/A4 size output).
 *
 *   Revision 1.4  1997/07/02  18:46:26  mike
 *   Fixed stupid bug in dither_black() - wasn't comparing against gray
 *   pixels (comparing against the first black byte - d'oh!)
 *   Changed 255 in dither matrix to 254 to shade correctly.
 *
 *   Revision 1.4  1997/07/02  18:46:26  mike
 *   Fixed stupid bug in dither_black() - wasn't comparing against gray
 *   pixels (comparing against the first black byte - d'oh!)
 *   Changed 255 in dither matrix to 254 to shade correctly.
 *
 *   Revision 1.3  1997/07/02  13:51:53  mike
 *   Added rgb_to_rgb and gray_to_gray conversion functions.
 *   Standardized calling args to conversion functions.
 *
 *   Revision 1.2  1997/07/01  19:28:44  mike
 *   Updated dither matrix.
 *   Fixed scaling bugs in dither_*() functions.
 *
 *   Revision 1.1  1997/06/19  02:18:15  mike
 *   Initial revision
 */

#include "print.h"


/*
 * RGB to grayscale luminance constants...
 */

#define LUM_RED		31
#define LUM_GREEN	61
#define LUM_BLUE	8


/*
 * Floyd ordered dither buffer - this 16x16 buffer will yield 256 shades.
 *
 * Yes, we could do a blue-noise (error-diffusion) dither in the dither_*()
 * functions, however doing it without washing out the darkest and lightest
 * colors isn't trivial.
 */

static int dither_floyd[16][16] =
{
  { 0, 128, 32, 160, 8, 136, 40, 168, 2, 130, 34, 162, 10, 138, 42, 170 },
  { 192, 64, 224, 96, 200, 72, 232, 104, 194, 66, 226, 98, 202, 74, 234, 106 },
  { 48, 176, 16, 144, 56, 184, 24, 152, 50, 178, 18, 146, 58, 186, 26, 154 },
  { 240, 112, 208, 80, 248, 120, 216, 88, 242, 114, 210, 82, 250, 122, 218, 90 },
  { 12, 140, 44, 172, 4, 132, 36, 164, 14, 142, 46, 174, 6, 134, 38, 166 },
  { 204, 76, 236, 108, 196, 68, 228, 100, 206, 78, 238, 110, 198, 70, 230, 102 },
  { 60, 188, 28, 156, 52, 180, 20, 148, 62, 190, 30, 158, 54, 182, 22, 150 },
  { 252, 124, 220, 92, 244, 116, 212, 84, 254, 126, 222, 94, 246, 118, 214, 86 },
  { 3, 131, 35, 163, 11, 139, 43, 171, 1, 129, 33, 161, 9, 137, 41, 169 },
  { 195, 67, 227, 99, 203, 75, 235, 107, 193, 65, 225, 97, 201, 73, 233, 105 },
  { 51, 179, 19, 147, 59, 187, 27, 155, 49, 177, 17, 145, 57, 185, 25, 153 },
  { 243, 115, 211, 83, 251, 123, 219, 91, 241, 113, 209, 81, 249, 121, 217, 89 },
  { 15, 143, 47, 175, 7, 135, 39, 167, 13, 141, 45, 173, 5, 133, 37, 165 },
  { 207, 79, 239, 111, 199, 71, 231, 103, 205, 77, 237, 109, 197, 69, 229, 101 },
  { 63, 191, 31, 159, 55, 183, 23, 151, 61, 189, 29, 157, 53, 181, 21, 149 },
  { 254, 127, 223, 95, 247, 119, 215, 87, 253, 125, 221, 93, 245, 117, 213, 85 }
};


/*
 * 'dither_black()' - Dither grayscale pixels to black.
 */

void
dither_black(guchar        *gray,	/* I - Grayscale pixels */
             int           y,		/* I - Current Y coordinate */
             int           src_width,	/* I - Width of input row */
             int           dst_width,	/* I - Width of output row */
             unsigned char *black)	/* O - Black bitmap pixels */
{
  int		x,		/* Current X coordinate */
		xerror,		/* X error count */
		xstep,		/* X step */
		xmod,		/* X error modulus */
		length;		/* Length of output bitmap in bytes */
  unsigned char	bit,		/* Current bit */
		*kptr;		/* Current black pixel */
  int		*dither;	/* Pointer to dither row */


  xstep  = src_width / dst_width;
  xmod   = src_width % dst_width;
  dither = dither_floyd[y & 15];
  length = (dst_width + 7) / 8;

  memset(black, -1, length);

  for (x = 0, bit = 128, kptr = black, xerror = 0;
       x < dst_width;
       x ++)
  {
    if (*gray > dither[x & 15])
      *kptr ^= bit;

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

    gray   += xstep;
    xerror += xmod;
    if (xerror >= dst_width)
    {
      xerror -= dst_width;
      gray   ++;
    };
  };

 /*
  * Clear remaining bits...
  */

  if (bit < 128)
  {
    while (bit > 0)
    {
      *kptr ^= bit;
      bit >>= 1;
    };
  };
}


/*
 * 'dither_cmyk()' - Dither RGB pixels to cyan, magenta, yellow, and black.
 */

void
dither_cmyk(guchar        *rgb,		/* I - RGB pixels */
            int           y,		/* I - Current Y coordinate */
            int           src_width,	/* I - Width of input row */
            int           dst_width,	/* I - Width of output rows */
            unsigned char *cyan,	/* O - Cyan bitmap pixels */
            unsigned char *magenta,	/* O - Magenta bitmap pixels */
            unsigned char *yellow,	/* O - Yellow bitmap pixels */
            unsigned char *black)	/* O - Black bitmap pixels */
{
  int		x,		/* Current X coordinate */
		xerror,		/* X error count */
		xstep,		/* X step */
		xmod,		/* X error modulus */
		length;		/* Length of output bitmap in bytes */
  unsigned char	bit,		/* Current bit */
		*cptr,		/* Current cyan pixel */
		*mptr,		/* Current magenta pixel */
		*yptr,		/* Current yellow pixel */
		*kptr;		/* Current black pixel */
  int		*dither;	/* Pointer to dither row */


  xstep  = 3 * (src_width / dst_width);
  xmod   = src_width % dst_width;
  dither = dither_floyd[y & 15];
  length = (dst_width + 7) / 8;

  memset(cyan, -1, length);
  memset(magenta, -1, length);
  memset(yellow, -1, length);

  for (x = 0, bit = 128, cptr = cyan, mptr = magenta, yptr = yellow, xerror = 0;
       x < dst_width;
       x ++)
  {
    if (rgb[0] > dither[x & 15])
      *cptr ^= bit;
    if (rgb[1] > dither[x & 15])
      *mptr ^= bit;
    if (rgb[2] > dither[x & 15])
      *yptr ^= bit;

    if (bit == 1)
    {
      cptr ++;
      mptr ++;
      yptr ++;
      bit = 128;
    }
    else
      bit >>= 1;

    rgb    += xstep;
    xerror += xmod;
    if (xerror >= dst_width)
    {
      xerror -= dst_width;
      rgb    += 3;
    };
  };

 /*
  * Clear remaining bits...
  */

  if (bit < 128)
  {
    while (bit > 0)
    {
      *cptr ^= bit;
      *mptr ^= bit;
      *yptr ^= bit;
      bit >>= 1;
    };
  };

 /*
  * Make black from CMY if needed...
  */

  if (black != NULL)
  {
    for (x = 0, cptr = cyan, mptr = magenta, yptr = yellow, kptr = black;
	 x < length;
	 x ++, cptr ++, mptr ++, yptr ++, kptr ++)
      if (*kptr = *cptr & *mptr & *yptr)
      {
	*cptr &= ~*kptr;
	*mptr &= ~*kptr;
	*yptr &= ~*kptr;
      };
  };
}


/*
 * 'gray_to_gray()' - Convert grayscale image data to grayscale (brightness
 *                    adjusted).
 */

void
gray_to_gray(guchar *grayin,	/* I - RGB pixels */
             guchar *grayout,	/* O - RGB pixels */
             int    width,	/* I - Width of row */
             int    bpp,	/* I - Bytes-per-pixel in grayin */
             guchar *lut,	/* I - Brightness lookup table */
             guchar *cmap)	/* I - Colormap (unused) */
{
  while (width > 0)
  {
    *grayout = lut[*grayin];

    grayout ++;
    grayin  += bpp;
    width --;
  };
}


/*
 * 'indexed_to_gray()' - Convert indexed image data to grayscale.
 */

void
indexed_to_gray(guchar *indexed,	/* I - Indexed pixels */
                guchar *gray,		/* O - Grayscale pixels */
        	int    width,		/* I - Width of row */
        	int    bpp,		/* I - Bytes-per-pixel in indexed */
                guchar *lut,		/* I - Brightness lookup table */
                guchar *cmap)		/* I - Colormap */
{
  int		i;			/* Looping var */
  unsigned char	gray_cmap[256];		/* Grayscale colormap */


  for (i = 0; i < 256; i ++, cmap += 3)
    gray_cmap[i] = lut[(cmap[0] * LUM_RED + cmap[1] * LUM_GREEN + cmap[2] * LUM_BLUE) / 100];

  while (width > 0)
  {
    *gray = gray_cmap[*indexed];
    gray ++;
    indexed += bpp;
    width --;
  };
}


/*
 * 'indexed_to_rgb()' - Convert indexed image data to RGB.
 */

void
indexed_to_rgb(guchar *indexed,		/* I - Indexed pixels */
               guchar *rgb,		/* O - RGB pixels */
               int    width,		/* I - Width of row */
               int    bpp,		/* I - Bytes-per-pixel in indexed */
               guchar *lut,		/* I - Brightness lookup table */
               guchar *cmap)		/* I - Colormap */
{
  while (width > 0)
  {
    rgb[0] = lut[cmap[*indexed * 3 + 0]];
    rgb[1] = lut[cmap[*indexed * 3 + 1]];
    rgb[2] = lut[cmap[*indexed * 3 + 2]];
    rgb += 3;
    indexed += bpp;
    width --;
  };
}


/*
 * 'media_width()' - Get the addressable width of the page.
 *
 * This function assumes a standard left/right margin of 0.25".
 */

int
media_width(int media_size,		/* I - Media size code */
            int dpi)			/* I - Resolution in dots-per-inch */
{
  switch (media_size)
  {
    case MEDIA_LETTER :
    case MEDIA_LEGAL :
        return (8 * dpi);

    case MEDIA_TABLOID :
        return ((int)(10.5 * dpi + 0.5));

    case MEDIA_A4 :
        return ((int)(7.77 * dpi + 0.5));

    case MEDIA_A3 :
        return ((int)(11.09 * dpi + 0.5));

    default :
        return (0);
  };
}


/*
 * 'media_height()' - Get the addressable height of the page.
 *
 * This function assumes a standard top/bottom margin of 0.5".
 */

int
media_height(int media_size,		/* I - Media size code */
             int dpi)			/* I - Resolution in dots-per-inch */
{
  switch (media_size)
  {
    case MEDIA_LETTER :
        return (10 * dpi);

    case MEDIA_LEGAL :
        return (13 * dpi);

    case MEDIA_TABLOID :
        return (16 * dpi);

    case MEDIA_A4 :
        return ((int)(10.69 * dpi + 0.5));

    case MEDIA_A3 :
        return ((int)(15.54 * dpi + 0.5));

    default :
        return (0);
  };
}


/*
 * 'rgb_to_gray()' - Convert RGB image data to grayscale.
 */

void
rgb_to_gray(guchar *rgb,		/* I - RGB pixels */
            guchar *gray,		/* O - Grayscale pixels */
            int    width,		/* I - Width of row */
            int    bpp,			/* I - Bytes-per-pixel in RGB */
            guchar *lut,		/* I - Brightness lookup table */
            guchar *cmap)		/* I - Colormap (unused) */
{
  while (width > 0)
  {
    *gray = lut[(rgb[0] * LUM_RED + rgb[1] * LUM_GREEN + rgb[2] * LUM_BLUE) / 100];
    gray ++;
    rgb += bpp;
    width --;
  };
}


/*
 * 'rgb_to_rgb()' - Convert RGB image data to RGB (brightness adjusted).
 */

void
rgb_to_rgb(guchar *rgbin,	/* I - RGB pixels */
           guchar *rgbout,	/* O - RGB pixels */
           int    width,	/* I - Width of row */
           int    bpp,		/* I - Bytes-per-pixel in RGB */
           guchar *lut,		/* I - Brightness lookup table */
           guchar *cmap)	/* I - Colormap (unused) */
{
  while (width > 0)
  {
    rgbout[0] = lut[rgbin[0]];
    rgbout[1] = lut[rgbin[1]];
    rgbout[2] = lut[rgbin[2]];

    rgbout += 3;
    rgbin  += bpp;
    width --;
  };
}


/*
 * End of "$Id: print-util.c,v 1.6 1997/07/30 20:33:05 mike Exp mike $".
 */
