/***************************************************************************
    image.c  -  code for reading and parsing a pbm, pgm or ppm format file 
                also, rgb -> cymk conversion and black separation
                             -------------------
    begin                : Thu Jan 13 2000
    copyright            : (C) 2000 by pnm2ppa project
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/


#include <stdlib.h>
#include <string.h>
#include <math.h>
#define __IMAGE_C__

#include "syslog.h"
#include "global.h"
#include "defaults.h"

#include "debug.h"
#include "image.h"
#include "gamma.h"
#include "dither.h"
#include "lang.h"





int
initImage (image_t *image, FILE *fptr)
{
    char line[1024];
    int colors = 0;
    int k, k1;

    image->fptr = fptr;
    image->version = none;
    image->blackCurLine = 0;
    image->colorCurLine = 0;
    image->bufferCurLine = 0;
    image->unread = 0;
    image->colors = 0;
    
    if (gGammaMode)
      {
	/* produce color calibration page, uses the  default papersize */
	image->version = P6_PPMRAW;
	image->width = gWidth;      
	image->height = gHeight;
      }
    else
      {
	
	if (fgets (line, 1024, fptr) == NULL)
	  return 0;
	line[strlen (line) - 1] = 0;
	
	
	/* we have to handle the six pnm formats .
	 */
	
	if (!strcmp (line, "P1"))
	  {
	    image->version = P1_PBM;
	    if (!gPixmapMode)
	      gColorMode = false ;
	    strcpy(gFormat,"BitMap");
	  }
	if (!strcmp (line, "P2"))
	  {
	    image->version = P2_PGM;
	    if (!gPixmapMode)
	      gColorMode = false ;
	    strcpy(gFormat,"GreyMap");
	  }
	if (!strcmp (line, "P3"))
	  {
	    image->version = P3_PPM;
	    strcpy(gFormat,"PixMap");
	  }
	if (!strcmp (line, "P4"))
	  {
	    image->version = P4_PBMRAW;
	    if (!gPixmapMode)
	      gColorMode = false ;
	    strcpy(gFormat,"BitMap");
	  }
	if (!strcmp (line, "P5"))
	  {
	    image->version = P5_PGMRAW;
	    if (!gPixmapMode)
	      gColorMode = false ;
	    strcpy(gFormat,"GreyMap");
	  }
	if (!strcmp (line, "P6"))
	  {
	    image->version = P6_PPMRAW;
	    strcpy(gFormat,"PixMap");
	  }
	if (image->version == none)
	  {
	  snprintf(syslog_message,message_size,"initImage(): %s", 
		    gMessages[E_IM_BADFORMAT] );
	  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
	    return 0;
	  }
	
	/* initialize switch that is used to terminate further printing 
	 * if EOF is detected while reading the input image
	 */
	
	gTerminate = false ;
	
	do
	  if (fgets (line, 1024, fptr) == NULL)
	    return 0;
	while (line[0] == '#');	
	if (2 != sscanf (line, "%d %d", &image->width, &image->height))
	  return 0;

	/* use the image dimensions read from the pnm innput file to
	 * set gWidth and gHeight, and test that these are accepatable
	 * for the printer model
	 * this will mean that any user input or default papersizes
	 * will only be uses for calibration or gamma correction output
	 * which is not generated by pnm imput 
	 */

	 gWidth = image->width ;
	 gHeight = image->height ;
	 /* test that papersize is acceptable */
	 if   (  (gWidth  > gmaxWidth  )
	      || (gWidth  < gminWidth  )  
	      || (gHeight > gmaxHeight )
	      || (gHeight < gminHeight )
	      )
	   {
	  snprintf(syslog_message,message_size,"initImage(): %s %dx%d @600dpi\n", 
		     gMessages[E_IM_BADPAPERSIZE],  gWidth, gHeight);
	  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
	    return 0;
	   }
	
	switch(image->version)
	  {
	  case P2_PGM:
	  case P3_PPM:
	  case P5_PGMRAW:
	  case P6_PPMRAW:
	    if (fgets (line, 1024, fptr) == NULL)
	      return 0;
	    if (1 != sscanf (line, "%d", &colors))
	      return 0;
	    /* colors should be 255, as the code is currently implemented */
	    if (colors != 255)
	      {
	  snprintf(syslog_message,message_size,"initImage(): %s %d\n", 
		     gMessages[E_IM_BADMAXVALUE], colors);
	  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		return 0;
	      }
	    break;
	  case P1_PBM:
	  case P4_PBMRAW:
	    colors = 1;
	    break;
	  case none:
	  default:
	  snprintf(syslog_message,message_size,"initImage(): %s", 
		   gMessages[E_IM_BADFORMAT]);
	  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
	    return 0;
	    break;
	  }
	
	image->colors = colors ;
	
	DPRINTF( "initImage: width=%d, height=%d, colors = %d\n", 
		 image->width, image->height, image->colors);
	    
    }
    /* initialize buffer */
    image->bufsize = 8192;
    image->bufpos = 0;
    image->buflines = 0;
    image->buftype = empty;
    for (k = 0; k < gMaxPass; k++)
      {
	image->buffer[k] = malloc (image->bufsize);
	if (!image->buffer[k])
	  {
	    for (k1 = 0; k1 < k; k1++)
	      free (image->buffer[k1]);
	    return 0;
	}
      }
    return 1;
    
}

int
remallocBuffer (image_t *image, size_t bytes)
{
    int linesize;
    unsigned char *newbuf;
    // size_t newsize;
    int newsize ;
    int k;

    if (image->buftype == bitmap)
	linesize = (image->width + 7) / 8;
    else if (image->buftype == color)
	linesize = (image->width / 2 + 7) / 8 * 3;
    else
	linesize = 0;

    /* if space is adequate, do nothing */
    if (image->bufpos + image->buflines * linesize + bytes < image->bufsize)
	return 0;

    /* if there's an offset, move the image at buffer[k][bufpos] to the
     * the beginning of the buffer, and reset the offset to 0.
     */

    if (image->bufpos != 0)
    {
	DPRINTF("Moving image[k][bufpos] to image[k][0]\n");
	for (k = 0; k < gMaxPass; k++)
	    memmove (image->buffer[k],
		     &image->buffer[k][image->bufpos], image->buflines * linesize);
	image->bufpos = 0;
	if (image->bufpos + image->buflines * linesize + bytes < image->bufsize)
	    return 0;
    }

    // calculate the number of bits and add one to double the needed memory
    // size.
    newsize = pow(2, (int)((log(image->buflines * linesize + bytes)/log(2))+1));

    DPRINTF("resizing memory from %d bytes to %d bytes\n",
	image->bufsize, newsize);

    for (k=0; k < gMaxPass; k++)
    {
	newbuf = malloc(newsize);
	if ( ! newbuf )
	{
	  snprintf(syslog_message,message_size, "remallocBuffer(): %s", 
		    gMessages[E_BADMALLOC]);
	  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		exit (1);
	}
	memcpy(newbuf, image->buffer[k], image->buflines * linesize);
	free(image->buffer[k]);
	image->buffer[k] = newbuf;
    }
    image->bufsize = newsize;

    return 0;
}



void
convert_black_data (image_t *image, unsigned char *line, unsigned char *black)
{
  /* Lets document this - please correct anything incorrect here!
     (duncan, Feb 12 2000, updated July 2000)

     -  The entries in line[] are written by one of the
        functions FS_Color_Dither(), HT_Color_Dither(), HT_Black_Dither()
        in  dither.c

     -  The entries in line[] represent the ( image->width ) pixels on a line.
        Each pixel is represented by three consecutive entries
              (line[i] , line [i + 1] ,line [i + 2] ) , 
	where  i = 0, 3, 6 , ... ,  < (image->width * 3) 
        The three entries correspond to Cyan, Magenta, and Yellow ink.

     -  entries in line[] take values  1, 2,...,(gMaxPass + 1) , 0  or  255,
        where gMaxPass is the maximum number of passes of the color print head.
	* gMaxPass is only used in Color mode (gColorMode=true)
        * In normal color mode (gEcomode=false, gFastmode=false) gMaxPass = 4
        * In  Economy mode (gEcoMode = true) gMaxPass = 1 
	* In Fast Color mode (gFastMode = true) gMaxPass = 1

     -  values line [] =  1, 2, ..., gMaxPass  correspond to
        gMaxPass, gMaxPass-1, ..., 1 drops of ink of a given color (C, M,or Y).
     -  the value line [] =  0 also means  gMaxPass drops of ink
     -  the value line [] > gMaxPass  (usually = gMaxPass+1 or 255) 
        means 0 drops of color ink.

     -  an entry (0, 0, 0) corresponds to a black drop, which can
        either be a drop of black ink, or gMaxPass drops each of C,M and Y ink,
        If this is implemented with a drop of black ink, convert_black_data()
        changes (0,0,0) to (255,255,255), so it won't get repeated  using
        colored ink by the following call to convert_color_data().

     -  whitespace is coded by (255,255,255).

    Color data is only printed on even lines.   

    Black data is printed on even and odd lines, 
    except in Black-and-White Economy mode (gColorMode=false, 
    gEcoMode=true), when only even lines of black are printed.  
    If the odd line is not printed, its black data is added to 
    the next even line.
  */


    int i, j, k;
    unsigned char mask = 0x80;
    unsigned char *pos = black;
    int blackness ;
    
    unsigned char curr_stuff= 0 ; // 0 = white, 1 = not white (current pixel) 
    unsigned char remove_black = 1, have_color = 0;
    unsigned char first_black=0 ,  second_black=0 ;  //controls black ink density

    blackness = gBlackness ;
    j = image->bufferCurLine ;

    /* black ink quantity control (duncan) */
    /* this should be extended to control color  ink printing
       of "black" pixels */
    if ( blackness == 1 )
      {
	/* one black ink drop, on even lines only */
	if ( (j % 2) ) 
	  {
	    first_black = 0   ; /* odd lines */
	    second_black =  0;
	  }
	else
	  {
	    first_black =  0 ; /* even lines */
	    second_black = 1 ;
	  }
      }
    else if ( blackness == 2 )
      /* two black ink drops  */
      {
	if ( (j % 2) ) 
	  {
	    first_black = 1   ; /* odd lines */
	    second_black =  0;
	  }
	else
	  {
	    // first_black =  0 ; /* even lines */
	    // second_black = 1 ;
	    /* treat odd and even lines the same, to avoid artifacts?*/ 
	    first_black =  1 ; /* even lines */
	    second_black = 0 ;
	  }
      }
    else if ( blackness == 3 )
      /* two drops on even lines, one on odd lines  */
      {
	first_black = 1;
      if ( j % 2 )
	  second_black = 0;  /* odd lines */
      else
	  second_black = 1 ; /* even lines */
	}
    else if (blackness == 4 )
      /* two black ink drops, all lines */
	{
	  first_black =  1 ;
	  second_black = 1 ;
	}
    else
      /* no black ink */
	{
	  first_black =  0  ;
	  second_black = 0  ;
	}
    
    /* In Pixmap mode  black ink cartridge is disabled (this should
       be renamed to (say) NoBlack mode ) */

    if (gPixmapMode)
      memset (pos, 0x00, (image->width + 7) / 8);
    else
      {
	if (gColorMode)
	  {
	    /* This is the most delicate situation, we (may) have both color 
	     *   and black on the same line; 
	     *  we want to:
	     *   - Print half of the black ink to avoid "wet" effect
	     *   - get a good "covering" black in pixmaps (neither too dense, 
	     *      neither too sparse...)  
	     */

	    curr_stuff=0;
	    
	    for (i = 0; i < image->width * 3; i += 6)
	      {
		if (mask == 0x80)
		  *pos = 0;

		if (curr_stuff==0)
		  {
		    /* this is a test to see if the current non-whitespace
		     *  pixel is part of a region of black delineated by
		     * whitespace (have_color=0)
		     * or is in a region containing some
		     * colored pixels (in this case have_color=1) 
		     */
		    if (! (line[i]>gMaxPass &&   //if line is not white
			   line[i+1]> gMaxPass && 
			   line[i+2]>gMaxPass) )
		      {
			have_color=0;
			k=i;
			while ( (!(have_color)) &&
				(k<image->width * 3) &&
				(! (line[k]>gMaxPass &&
				    line[k+1]> gMaxPass && 
				    line[k+2]>gMaxPass) ))
			  { 
			    if ((line[k] && line[k]<=gMaxPass) ||
				(line[k+1] && line[k+1]<=gMaxPass) ||
				(line[k+2] && line[k+2]<=gMaxPass))
			      have_color=1;   
			    
			    k+=6;
			  }
			if (have_color) 
			  remove_black=0;
			else
			  remove_black=1;
			curr_stuff=1;
		      }
		  }

		if ( !(line[i] || line[i + 1] || line[i + 2]))
		  {
		    /* pixel is black; there are two possible
		     * behaviors, depending on whether remove_black
		     * is set by Drugo's new code above.
		     * these behaviors may need some further
		     * adjustments.
		     */
		    if (remove_black)
		      {
			/* in this case  the black region is 
			 * identified as  delineated by whitespace
			 * (probably =  text?).   
			 * So print with black ink, 
			 * and no colored ink. To avoid  using
			 * too much black ink (wets paper)
			 * first_black and second_black
			 *  are used as controls on amount
			 * of black ink.
			 */
			if  (first_black && gBlack ) 
			  *pos |= mask;   /* one drop of black ink */
			mask >>= 1;
			if (mask == 0)
			  {
			    pos++;
			    mask = 0x80;
			  }
			if  (second_black && gBlack ) 
			  *pos |= mask;   /* one  drop of black ink */ 
			/*  suppress use of colored ink on this pixel */
			line[i] = line[i + 1] = line[i + 2] = 255;
			line[i + 3] = line[i + 4] = line[i + 5] = 255;
		      }
		    else
		      {
			/* this black region is bounded by a colored
			 * region, and is probably part of an image.
			 * so: print half strength black ink, and use
			 * some colored ink.
			 *
			 * note: in  EcoMode, only even lines are processed.
			 * in normal mode, skip even lines.
			 * j is the line number.
			 */
			if ( (j % 2 || gEcoMode ) && gBlack ) 
			  *pos |= mask;/* a drop of black, every second line */
			mask >>= 1;
			if (mask == 0)
			  {
			    pos++;
			    mask = 0x80;
			  }
			/* reduce amount  of colored ink to avoid wetting:
			 * try  resetting line[] to gMaxPass-1 
			 * gives 2  passes  of the color head in high quality,
			 * (gMaxPass=4, line[] = 3),
			 * 1 pass in ecoMode (gMaxPass = 1, line[] = 0)
			 */
			line[i] = line[i + 1] = line[i + 2] = gMaxPass-1;
			line[i + 3] = line[i + 4] = line[i + 5] = gMaxPass-1;
		      }
		  }
		else 
		  {
		    /* pixel is not black */
		    mask >>= 1;
		    if (mask == 0)
		      {
			pos++;
			mask = 0x80;
		      }
		    if (line[i] > gMaxPass && 
			line[i + 1] > gMaxPass && 
			line[i + 2] > gMaxPass)
		      {
			/* pixel is whitespace */
		      curr_stuff=0;
		      }
		  }
				
		mask >>= 1;
		if (mask == 0)
		  {
		    pos++;
		    mask = 0x80;
		  }
	      }
	  }
	else
	  {
	    /* this next part is for printing in black and white only.
	     * if !gColorMode,  the line contains only black or white, so 
	     * we can print safely 1 drop out of 2 without any problem 
	     */

	    *pos = 0;

	    for (i = 0 ; i < image->width * 3; i += 6)
	      {
		if (mask == 0x80)
		  *pos = 0;

		if ((!line[i] && !line[i + 1] && !line[i + 2]))
		  {
		    if  (first_black && gBlack) 
		      *pos |= mask;   /* one drop of black ink */
			mask >>= 1;
			if (mask == 0)
			  {
			    pos++;
			    mask = 0x80;
			  }
			if  (second_black && gBlack) 
			  *pos |= mask;   /* one  drop of black ink */ 
		  }
		else 
		  {
		    /* pixel is not black */
		    mask >>= 1;
		    if (mask == 0)
		      {
			pos++;
			mask = 0x80;
		      }
		  }
		mask >>= 1;
		if (mask == 0)
		  {
		    pos++;
		    mask = 0x80;
		  }
	      }
	    memset (line, 0xff, image->width * 3);
	  }
	
      }
}


void
convert_color_data (image_t *image, unsigned char *line,
		    unsigned char *color[], int c_off)
{
    int i, k, j;
    unsigned char mask;
    unsigned char *pos;

    j = 1;
    for (k = 0; k < gMaxPass; k++)
    {
	mask = 0x80;
	pos = &color[gMaxPass - j][c_off];
	j++;

	memset (pos, 0x00, (image->width / 2 + 7) / 8 * 3);

	for (i = 0; i < image->width * 3; i += 6)
	{

	    if ((line[i] < gMaxPass - (k) + 1) &&  gCyan)
		pos[0] |= mask;
	    if ((line[i + 1] < gMaxPass - (k) + 1) && gMagenta)
		pos[1] |= mask;
	    if ((line[i + 2] < gMaxPass - (k) + 1) && gYellow)
		pos[2] |= mask;

	    mask >>= 1;
	    if (mask == 0)
	    {
		pos += 3;
		mask = 0x80;
	    }
	}
    }
}

/* Drugo */

/* read a line of data from the file and convert to the necessary format */
void
read_line (image_t * image, unsigned char *black[], int b_off,
	   unsigned char *color[], int c_off)
{
  static unsigned char *line, *oldline;  /* take care to free these! */
  int i, j, k ;
  int fread_eof = 0;
  static int have_oldline = 0;

  line = malloc (image->width * 3);
    
  if (!line)
    {
      /* make this nicer... propagate upwards */
      snprintf(syslog_message,message_size, "read_line(): %s", 
	       gMessages[E_BADMALLOC]);
      wrap_syslog (LOG_CRIT,"%s",syslog_message); 
      if (have_oldline)
	free (oldline);
       exit (1);
    }
    

  /* All the six pnm input formats 
   * (pbm, pbmraw, pgm, pgmraw, pbm, pbmraw ) 
   * can be read from the input, and  converted to 
   * ppmraw format for further processing.   
   */


  if (!gGammaMode && !gTerminate)
    {
      switch (image->version)
	{
	  /*  for completeness, the non-raw pnm formats are  supported */
	case P1_PBM:
	  for ( i = 0 ; i < image->width * 3  ; )
	    switch( getc( image->fptr ) )
	      {
	      case EOF:
		snprintf(syslog_message,message_size, "read_line(): %s PBM\n", 
			 gMessages[E_IM_EOFREADLINE]);
		wrap_syslog (LOG_CRIT,"%s",syslog_message); 

		/* input data is truncated; set gTerminate mode
		 * and replace lost part of image with whitespace
		 * until print run ends.
		 */
		gTerminate = true ;
		break;
	      case '1':
		line[i] = line[i + 1]= line[i + 2] = 0;
		i += 3 ;
		  break;
	      case '0':
		line[i] = line[i + 1]= line[i + 2] = 255;
		i += 3 ;
		  break;
	      default :
		break;
	      }
	  break;
	case P2_PGM:
	  {
	    /* This is included for completeness, 
	     * but this implementation s PAINFULLY slow !
	     * is there a better way?
	     * (YES, USE THE PGMRAW FORMAT ONLY !)
	     */
	    unsigned int value ;
	    for ( i = 0 ; i < image->width * 3  ; i += 3 )
	      {
		switch  (fscanf(image->fptr, "%u ", &value ) )
		  {
		  case '1':
		    break ;
		  case EOF:
		    snprintf(syslog_message,message_size,"read_line(): %s PGM\n",
			     gMessages[E_IM_EOFREADLINE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 

		/* input data is truncated; set gTerminate mode
		 * and replace lost part of image with whitespace
		 * until print run ends.
		 */
		gTerminate = true ;
		    break;
		  default:
		    snprintf(syslog_message,message_size,"read_line(): %s", 
			     gMessages[E_IM_BADREADLINE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		    free (line);
		    if (have_oldline)
		      free (oldline);
		    exit (1);
		    break;
		  }
		if(value <= 255)
		  line[i] = line[i + 1] = line[i + 2] = value ;
		else
		  {
		    snprintf(syslog_message,message_size,"read_line(): %s", 
			     gMessages[E_IM_BADIMAGE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		    free (line);
		    if (have_oldline)
		      free (oldline);
		    exit (1);
		  }
	      }
	  }
	  break;
	case P3_PPM:
	  {
	    /* This is included for completeness, 
	     * but this implementation is PAINFULLY slow !
	     * is there a better way?
	     * (YES, USE THE PPMRAW FORMAT ONLY !)
	     */
	    unsigned int value ;
	    for ( i = 0 ; i < image->width * 3  ; i ++   )
	      {
		switch  (fscanf(image->fptr, "%u ", &value ) )
		  {
		  case '1':
		    break ;
		  case EOF:
		    snprintf(syslog_message,message_size,"read_line(): %s PPM\n", 
			     gMessages[E_IM_EOFREADLINE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 

		    /* input data is truncated; set gTerminate mode
		     * and replace lost part of image with whitespace
		     * until print run ends.
		     */
		    gTerminate = true ;
		    break;
		  default:
		    snprintf(syslog_message,message_size,"read_line(): %s", 
			     gMessages[E_IM_BADREADLINE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		    free (line);
		    if (have_oldline)
		      free (oldline);
		    exit (1);
		    break;
		  }
		if(value <= 255)
		  line[i] = value ;
		else
		  {
		    snprintf(syslog_message,message_size,"read_line() %s", 
			     gMessages[E_IM_BADIMAGE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		    free (line);
		    if (have_oldline)
		      free (oldline);
		    exit (1);
		  }
	      }
	  }
	  break;
	case P4_PBMRAW:
	  /* read pbmraw format and convert to ppmraw  format */
	  {
	      int pbm_width, count, index ;
	      char* pbmraw_data;
	      unsigned char mask[8] = 
	      { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
	      pbm_width = ( image->width + 7 ) / 8;
	      
	      if (! (pbmraw_data = malloc(pbm_width)) )
		{
		    snprintf(syslog_message,message_size,"read_line(): %s", 
			     gMessages[E_BADMALLOC]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		  if (have_oldline)
		    free (oldline);
		  free (line);
		  exit (1);
	       }
	      
	      count = fread(pbmraw_data, 1 , pbm_width, image->fptr);
	      if (pbm_width == count)
		{
		  /* success */
		  /* pbm raw format: 0=white, 1=black;
		   * bits are stored eight per byte, high bit
		   * first, low bit last. 
		   */
		  index = 0;
		  for ( i = 0 ; i < image->width ;  i ++  )
		    {
		      k = i % 8 ;
		      j = i / 8 ;
		      if ( pbmraw_data[j] & mask[k] )
			line[index] = line[index + 1] = line[index + 2] = 0 ;
		     else
		       line[index] = line[index + 1] = line[index + 2] = 255; ;
		     index += 3;
		    }
		  free(pbmraw_data);
		}
	      else
		{
		  /* error reading line */
		  free(pbmraw_data);
		  fread_eof=feof(image->fptr);
		  clearerr(image->fptr);
		  if (fread_eof)
		    {
		    snprintf(syslog_message,message_size,"read_line(): %s PBMRAW\n", 
			     gMessages[E_IM_EOFREADLINE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 

		      /* input data is truncated; set gTerminate mode
		       * and replace lost part of image with whitespace
		       * until print run ends.
		       */
		      gTerminate = true ;
		    }
		  else
		    {
		    snprintf(syslog_message,message_size,"read_line(): %s", 
			     gMessages[E_IM_BADREADLINE]);
		    wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		      free (line);
		      if (have_oldline)
			free (oldline);
		      exit (1);
		    }
	       }
	  }
	  
	  break;
	case P5_PGMRAW:
	  
	  /* read pgmraw format and convert to ppmraw  format */
	  {
	    int  index ;
	    char* pgm_data;
	    
	    pgm_data = malloc(image->width);
	    if (!pgm_data)
	      {
		snprintf(syslog_message,message_size,"read_line(): %s", 
			 gMessages[E_BADMALLOC]);
		wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		free (line);
		if (have_oldline)
		  free (oldline);
		exit (1);
	      }
	    if ( 1 == fread (pgm_data, image->width , 1, image->fptr) )
	      {
		 /* success */
		index = 0 ;
		for ( i = 0 ; i < image->width ;  i ++  )
		  {
		    line[index] = line[index + 1] = line[index + 2]
		      = pgm_data[i] ;
		    index += 3;
		  }
		free(pgm_data);
	      }
	     else
	       {
		 /* error reading line */
		 free(pgm_data);
		 fread_eof=feof(image->fptr);
		 clearerr(image->fptr);
		 if (fread_eof)
		   {
		     snprintf(syslog_message,message_size,"read_line:() %s PGMRAW\n", 
			      gMessages[E_IM_EOFREADLINE]);
		     wrap_syslog (LOG_CRIT,"%s",syslog_message); 

		     /* input data is truncated; set gTerminate mode
		      * and replace lost part of image with whitespace
		      * until print run ends.
		      */
		     gTerminate = true ;
		   }
		 else
		   {
		     snprintf(syslog_message,message_size,"read_line(): %s", 
			      gMessages[E_IM_BADREADLINE]);
		     wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		     free (line);
		     if (have_oldline)
		       free (oldline);
		     exit (1);
		   }
	       }
	  }
	  break;
	case P6_PPMRAW:
	  if (1 !=  fread (line, image->width * 3, 1, image->fptr) )
	    {
	      fread_eof=feof(image->fptr);
	      clearerr(image->fptr);
	      if (fread_eof)
		{
		  snprintf(syslog_message,message_size,"read_line(): %s PPMRAW\n", 
			   gMessages[E_IM_EOFREADLINE]);
		  wrap_syslog (LOG_CRIT,"%s",syslog_message); 

		  /* input data is truncated; set gTerminate mode
		   * and replace lost part of image with whitespace
		   * until print run ends.
		   */
		  gTerminate = true ;
		}
	      else
		{
		  snprintf(syslog_message,message_size,"read_line(): %s", 
			   gMessages[E_IM_BADREADLINE]);
		  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
		  free (line);
		  if (have_oldline)
		    free (oldline);
		  exit (1);
		}
	    }
	  break;
	case none:
	default:
	  snprintf(syslog_message,message_size,"read_line(): %s", 
		   gMessages[E_IM_BADFORMAT]);
	  wrap_syslog (LOG_CRIT,"%s",syslog_message); 
	  free (line);
	  if (have_oldline)
	    free (oldline);
	  exit (1);
	  break;
	}	
    }
  else  if (gGammaMode) 
    ProduceGamma (image, line ) ;  /* generate color calibration page */
  
  if ( gTerminate )
    {
      /* this becomes true if EOF was obtained when a line was read .
       *  The input image is incomplete .
       * Print only previously-read complete lines.
       * unread lines are replaced with blanks for the rest of the print run
       */
      int index ;
      index = 0 ;
      for ( i = 0 ; i < image->width ;  i ++  )
	{
	  line[index] = line[index + 1] = line[index + 2] = 255; ;
	  index += 3;
	}
    }


  
  /* process ALL lines to avoid artifacts 
     (odd lines are only processed for black in *_Dither() functions )
     hopefully, this minimal processing is quite fast */
  
  if (gColorMode)
    {
      if (gFastMode)
	HT_Color_Dither (image, line);
      else
	FS_Color_Dither (image, line);
    }
  else
    HT_Black_Dither (image, line);
  
  /* if the current line is odd, we only need to return black data */
  if (image->bufferCurLine % 2 == 1)
    {
      if  ( (!gEcoMode || gColorMode) && !gPixmapMode  )
	{
	  convert_black_data (image, line, &black[0][b_off]);
 	}
      else
	{
	  /* in B&W EcoMode, or in PixmapMode,
	   *  don't print the odd line, but store  it
	   * (unless this is the last line!)
	   */
	  if ( image->bufferCurLine + 1 < image->height )
	    {
	      memset(&black[0] [b_off], 0x00, (image->width +7 ) / 8 );
	      oldline = malloc (image->width * 3);
	      memcpy(oldline, line, image->width * 3 );
	      have_oldline = 1;
	    }
	}
      
      
    }
  else
    {
      /* return both black and color data on even lines */
      /* note that convert_color_data must be called AFTER convert_black_data */
      if ( have_oldline )
	{
	  /* add any black from previous unprinted odd line 
	     to next even line */
	  for ( i = 0; i < image->width * 3; i += 3 )
	    {
	      if ( !oldline[i] && !oldline[i + 1] && !oldline[i + 2] )
		{
		  line[i] = line[i + 1] = line[i + 2] = 0 ;
		}
	    }
	  free(oldline);  
	  have_oldline = 0;
	}
      
      
      
      convert_black_data (image, line, &black[0][b_off]);
      if (gColorMode)
	convert_color_data (image, line, color, c_off);
    }
  
  
  free (line);
  
  image->bufferCurLine++;
  
}

/* Drugo */


/* buffer a black line or two and return color data */

void
buffer_black_line (image_t *image, unsigned char *data[], int offset)
{
    int lines, linesize = (image->width + 7) / 8;

    /* if the current line is odd, we need to read 2 black lines */
    if (image->bufferCurLine % 2 == 1)
	lines = 2;
    else
	lines = 1;		//mocho 1

    remallocBuffer (image, linesize * lines);

    /* read_line into buffer and 'data' */
    read_line (image, image->buffer,
	       image->bufpos + image->buflines * linesize, data, offset);
    image->buflines++;

    /* if we have to read 2 lines, then we haven't gotten any color data yet */
    if (lines == 2)
    {
	read_line (image, image->buffer,
		   image->bufpos + image->buflines * linesize, data, offset);
	image->buflines++;
    }

    image->buftype = bitmap;
}

/* buffer a color line and return black data */
void
buffer_color_line (image_t *image, unsigned char *data[], int offset)
{
    int linesize = (image->width / 2 + 7) / 8 * 3;	

    /* if the current line is odd, we don't need to buffer any color data */
    if (image->bufferCurLine % 2 == 1)
    {
	read_line (image, data, offset, NULL, 0);
	return;
    }

    /* we need to buffer 1 color line */
    if (gColorMode)
	remallocBuffer (image, linesize);

    read_line (image, data, offset, image->buffer,
	       image->bufpos + image->buflines * linesize);
    image->buflines++;

    image->buftype = color;
}

/* Reads a single line of black image data into data which must be at least
   (image->width+7)/8 bytes of storage */
int
im_black_readline (image_t *image, unsigned char *data[], int offset)
{

    if (image->blackCurLine >= image->height)
      return 0;

    if (image->unread)
    {
	memcpy (data[0] + offset, image->revdata, (image->width + 7) / 8);
	image->blackCurLine++;
	image->unread = 0;
	free (image->revdata);
	return 1;
    }
    
    /* if there are buffered black lines, use one of them */
    /* if there aren't buffered black lines, process the next line and */
    /*   buffer a color line */
    if (image->buftype == bitmap)
      {
	memcpy (data[0] + offset,
		&image->buffer[0][image->bufpos], (image->width + 7) / 8);
	image->bufpos += (image->width + 7) / 8;
	if (--image->buflines == 0)
	  {
	    image->bufpos = 0;
	    image->buftype = empty;
	  }
      }
	else
	  buffer_color_line (image, data, offset);
    image->blackCurLine++;
    return 1;
    
}

/* Reads a single line of color image data into data which must be at least
   (image->width/2+7)/8*3 bytes of storage */
int
im_color_readline (image_t *image, unsigned char *data[], int offset)
{
    int k;

    if (image->colorCurLine >= image->height)
    {
	DPRINTF ("im_color_readline returning 0 immediately!\n");
	return 0;
    }
    
    /* if there are buffered color lines, use one of them */
    /* if there aren't buffered color lines, process the next line and */
    /*   buffer a black line */
    if (image->buftype == color)
      {
	for (k = 0; k < gMaxPass; k++)
	  memcpy (data[k] + offset,
		  &image->buffer[k][image->bufpos], 3 * ((image->width / 2 + 7) / 8));	//mocho changes /2
	image->bufpos += 3 * ((image->width / 2 + 7) / 8);	//mocho changes /2
	image->buflines--;
	if (image->buflines == 0)
	  {
	    image->bufpos = 0;
	    image->buftype = empty;
	  }
      }
    else
      buffer_black_line (image, data, offset);
    image->colorCurLine += 2;
    return 1;
}

/* push a black line back into the buffer; we read too much! */
void
im_unreadline (image_t * image, void *data)
{
    /* can only store one line in the unread buffer */
    if (image->unread)
	return;

    image->unread = 1;
    image->revdata = malloc ((image->width + 7) / 8);
    memcpy (image->revdata, data, (image->width + 7) / 8);
    image->blackCurLine--;
}
