/*
 * "$Id: image-zoom.c,v 1.14 2005/01/03 19:29:57 mike Exp $"
 *
 *   Image zoom routines for the Common UNIX Printing System (CUPS).
 *
 *   Copyright 1993-2005 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Easy Software Products and are protected by Federal
 *   copyright law.  Distribution and use rights are outlined in the file
 *   "LICENSE.txt" which should have been included with this file.  If this
 *   file is missing or damaged please contact Easy Software Products
 *   at:
 *
 *       Attn: CUPS Licensing Information
 *       Easy Software Products
 *       44141 Airport View Drive, Suite 204
 *       Hollywood, Maryland 20636 USA
 *
 *       Voice: (301) 373-9600
 *       EMail: cups-info@cups.org
 *         WWW: http://www.cups.org
 *
 *   This file is subject to the Apple OS-Developed Software exception.
 *
 * Contents:
 *
 *   ImageZoomAlloc() - Allocate a pixel zoom record...
 *   ImageZoomFill()  - Fill a zoom record with image data utilizing bilinear
 *                      interpolation.
 *   ImageZoomQFill() - Fill a zoom record quickly using nearest-neighbor
 *                      sampling.
 *   ImageZoomFree()  - Free a zoom record...
 */

/*
 * Include necessary headers...
 */

#include "image.h"


/*
 * 'ZoomAlloc()' - Allocate a pixel zoom record...
 */

izoom_t *
ImageZoomAlloc(image_t *img,	/* I - Image to zoom */
               int     x0,	/* I - Upper-lefthand corner */
               int     y0,	/* I - ... */
               int     x1,	/* I - Lower-righthand corner */
               int     y1,	/* I - ... */
               int     xsize,	/* I - Final width of image */
               int     ysize,	/* I - Final height of image */
               int     rotated)	/* I - Non-zero if image is rotated 90 degs */
{
  izoom_t	*z;		/* New zoom record */
  int		flip;		/* Flip on X axis? */


  if (xsize > IMAGE_MAX_WIDTH ||
      ysize > IMAGE_MAX_HEIGHT ||
      (x1 - x0) > IMAGE_MAX_WIDTH ||
      (y1 - y0) > IMAGE_MAX_HEIGHT)
    return (NULL);		/* Protect against integer overflow */

  if ((z = (izoom_t *)calloc(1, sizeof(izoom_t))) == NULL)
    return (NULL);

  z->img     = img;
  z->row     = 0;
  z->depth   = ImageGetDepth(img);
  z->rotated = rotated;

  if (xsize < 0)
  {
    flip  = 1;
    xsize = -xsize;
  }
  else
  {
    flip  = 0;
  }

  if (rotated)
  {
    z->xorig   = x1;
    z->yorig   = y0;
    z->width   = y1 - y0 + 1;
    z->height  = x1 - x0 + 1;
    z->xsize   = xsize;
    z->ysize   = ysize;
    z->xmod    = z->width % z->xsize;
    z->xstep   = z->width / z->xsize;
    z->xincr   = 1;
    z->ymod    = z->height % z->ysize;
    z->ystep   = z->height / z->ysize;
    z->yincr   = 1;
    z->instep  = z->xstep * z->depth;
    z->inincr  = z->xincr * z->depth;

    if (z->width < img->ysize)
      z->xmax = z->width;
    else
      z->xmax = z->width - 1;

    if (z->height < img->xsize)
      z->ymax = z->height;
    else
      z->ymax = z->height - 1;
  }
  else
  {
    z->xorig   = x0;
    z->yorig   = y0;
    z->width   = x1 - x0 + 1;
    z->height  = y1 - y0 + 1;
    z->xsize   = xsize;
    z->ysize   = ysize;
    z->xmod    = z->width % z->xsize;
    z->xstep   = z->width / z->xsize;
    z->xincr   = 1;
    z->ymod    = z->height % z->ysize;
    z->ystep   = z->height / z->ysize;
    z->yincr   = 1;
    z->instep  = z->xstep * z->depth;
    z->inincr  = z->xincr * z->depth;

    if (z->width < img->xsize)
      z->xmax = z->width;
    else
      z->xmax = z->width - 1;

    if (z->height < img->ysize)
      z->ymax = z->height;
    else
      z->ymax = z->height - 1;
  }

  if (flip)
  {
    z->instep = -z->instep;
    z->inincr = -z->inincr;
  }

  if ((z->rows[0] = (ib_t *)malloc(z->xsize * z->depth)) == NULL)
  {
    free(z);
    return (NULL);
  }

  if ((z->rows[1] = (ib_t *)malloc(z->xsize * z->depth)) == NULL)
  {
    free(z->rows[0]);
    free(z);
    return (NULL);
  }

  if ((z->in = (ib_t *)malloc(z->width * z->depth)) == NULL)
  {
    free(z->rows[0]);
    free(z->rows[1]);
    free(z);
    return (NULL);
  }

  return (z);
}


/*
 * 'ImageZoomFill()' - Fill a zoom record with image data utilizing bilinear
 *                     interpolation.
 */

void
ImageZoomFill(izoom_t *z,	/* I - Zoom record to fill */
              int     iy)	/* I - Zoom image row */
{
  ib_t	*r,			/* Row pointer */
	*inptr;			/* Pixel pointer */
  int	xerr0,			/* X error counter */
	xerr1;			/* ... */
  int	ix,
	x,
	count,
	z_depth,
	z_xstep,
	z_xincr,
	z_instep,
	z_inincr,
	z_xmax,
	z_xmod,
	z_xsize;


  if (iy > z->ymax)
    iy = z->ymax;

  z->row ^= 1;

  z_depth  = z->depth;
  z_xsize  = z->xsize;
  z_xmax   = z->xmax;
  z_xmod   = z->xmod;
  z_xstep  = z->xstep;
  z_xincr  = z->xincr;
  z_instep = z->instep;
  z_inincr = z->inincr;

  if (z->rotated)
    ImageGetCol(z->img, z->xorig - iy, z->yorig, z->width, z->in);
  else
    ImageGetRow(z->img, z->xorig, z->yorig + iy, z->width, z->in);

  if (z_inincr < 0)
    inptr = z->in + (z->width - 1) * z_depth;
  else
    inptr = z->in;

  for (x = z_xsize, xerr0 = z_xsize, xerr1 = 0, ix = 0, r = z->rows[z->row];
       x > 0;
       x --)
  {
    if (ix < z_xmax)
    {
      for (count = 0; count < z_depth; count ++)
        *r++ = (inptr[count] * xerr0 + inptr[z_depth + count] * xerr1) / z_xsize;
    }
    else
    {
      for (count = 0; count < z_depth; count ++)
        *r++ = inptr[count];
    }

    ix    += z_xstep;
    inptr += z_instep;
    xerr0 -= z_xmod;
    xerr1 += z_xmod;

    if (xerr0 <= 0)
    {
      xerr0 += z_xsize;
      xerr1 -= z_xsize;
      ix    += z_xincr;
      inptr += z_inincr;
    }
  }
}


/*
 * 'ImageZoomQFill()' - Fill a zoom record quickly using nearest-neighbor sampling.
 */

void
ImageZoomQFill(izoom_t *z,	/* I - Zoom record to fill */
               int     iy)	/* I - Zoom image row */
{
  ib_t	*r,			/* Row pointer */
	*inptr;			/* Pixel pointer */
  int	xerr0;			/* X error counter */
  int	ix,
	x,
	count,
	z_depth,
	z_xstep,
	z_xincr,
	z_instep,
	z_inincr,
	z_xmod,
	z_xsize;


  if (iy > z->ymax)
    iy = z->ymax;

  z->row ^= 1;

  z_depth  = z->depth;
  z_xsize  = z->xsize;
  z_xmod   = z->xmod;
  z_xstep  = z->xstep;
  z_xincr  = z->xincr;
  z_instep = z->instep;
  z_inincr = z->inincr;

  if (z->rotated)
    ImageGetCol(z->img, z->xorig - iy, z->yorig, z->width, z->in);
  else
    ImageGetRow(z->img, z->xorig, z->yorig + iy, z->width, z->in);

  if (z_inincr < 0)
    inptr = z->in + (z->width - 1) * z_depth;
  else
    inptr = z->in;

  for (x = z_xsize, xerr0 = z_xsize, ix = 0, r = z->rows[z->row];
       x > 0;
       x --)
  {
    for (count = 0; count < z_depth; count ++)
      *r++ = inptr[count];

    ix    += z_xstep;
    inptr += z_instep;
    xerr0 -= z_xmod;

    if (xerr0 <= 0)
    {
      xerr0 += z_xsize;
      ix    += z_xincr;
      inptr += z_inincr;
    }
  }
}


/*
 * 'ImageZoomFree()' - Free a zoom record...
 */

void
ImageZoomFree(izoom_t *z)	/* I - Zoom record to free */
{
  free(z->rows[0]);
  free(z->rows[1]);
  free(z->in);
  free(z);
}


/*
 * End of "$Id: image-zoom.c,v 1.14 2005/01/03 19:29:57 mike Exp $".
 */
