/*
Copyright (c) 2004-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file	dkfigpi.c	PDF image information module.
*/



/**	Inside the dkfigpi module.
*/
#define DKFIGPI_C 1



#include "dkfig.h"




#line 55 "dkfigpi.ctr"




/**	Abbreviation for dkfig_pdf_output_instruction.
*/
typedef dkfig_pdf_output_instruction OI;



/**	Image type: unknown.
*/
#define WHAT_NONE (-1)

/**	Image type: PNG
*/
#define WHAT_PNG 0

/**	Image type: JPEG.
*/
#define WHAT_JPG 1

/**	Image type: NetPBM.
*/
#define WHAT_PBM 2



/**	Image information.
*/
typedef struct {
  int what;		/**< Image type. */
  union {
    int dummy ;		/**< Dummy if no library available at all. */
#if DK_HAVE_ZLIB_H
#if DK_HAVE_PNG_H
    struct {
      png_structp pp;	/**< Read struct. */
      png_infop pi;	/**< Information struct. */
      png_byte **array;	/**< Pixel data. */
    } p;		/**< PNG image information. */
#endif
#endif
#if DK_HAVE_JPEGLIB_H
    struct {
      JSAMPLE **array;		/**< Pixel data. */
      JSAMPROW color_map0;	/**< Color map 0. */
      JSAMPROW color_map1;	/**< Color map 1. */
      JSAMPROW color_map2;	/**< Color map 2. */
      struct jpeg_decompress_struct cinfo;	/**< Decompression struct. */
      struct jpeg_error_mgr jerr;		/**< Error handling. */
      int state;		/**< State of reader. */
      int what_to_do;
    } j;		/**< JPEG image information. */
#endif
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
    struct {
      int format;	/**< NetPBM sub format. */
      xel **array;	/**< Pixel data array. */
      xelval maxxelval;	/**< Maximum pixel value. */
    } n;		/**< NetPBM image information. */
#endif
  } d;			/**< Image information data. */
} image_data;

/**	Abbreviation.
*/
typedef image_data IDATA;



/**	File type suffixes we can handle.
*/
static char *file_suffixes[] = {
  "png", "jpg", "jpeg", "pbm", "pgm", "ppm", "pnm",
  NULL
};



/**	File open mode: read binary.
*/
static char str_mode_read_binary[] = { "rb" };

/**	File open mode: write binary.
*/
static char str_mode_write_binary[] = { "wb" };



/**	Get file type for name by inspecting the suffix.
	@param	n	File name.
	@return	File type WHAT_xxx.
*/
static
int get_file_type DK_P1(char *,n)
{
  int back = -1, cs, x;
  if(n) {
    char *ptr;
    cs = 0;
    ptr = dksf_get_file_type_dot(n);
    if(ptr) {
      ptr++;
      x = dkstr_array_index(file_suffixes, ptr, cs);
      switch(x) {
        case 0: {
	  back = WHAT_PNG;
	} break;
	case 1: case 2: {
	  back = WHAT_JPG;
	} break;
	case 3: case 4: case 5: case 6: {
	  back = WHAT_PBM;
	} break;
      }
    }
  }
  return back;
}



/**	Initialize new image information structure.
	@param	i	Structure to initialize.
*/
static
void null_image DK_P1(IDATA *,i)
{
  DK_MEMRES((void *)i, sizeof(IDATA));
  i->what = -1;
}


#if DK_HAVE_ZLIB_H
#if DK_HAVE_PNG_H



/**	Release memory allocated for PNG pixel data.
	@param	array	Array to release.
	@param	he	Image height (number of rows in array).
*/
static void
release_png_memory DK_P2(png_byte **,array, png_uint_32,he)
{
  png_byte *xptr, **ptr; png_uint_32 y;
  if(array) {
    ptr = array;
    for(y = 0; y < he; y++) {
      xptr = *ptr;
      if(xptr) {
        dkmem_free(xptr);
      }
      *(ptr++) = NULL;
    }
    dkmem_free(array);
  }
}



/**	Allocate memory for PNG pixel data.
	@param	h		Number of rows in image.
	@param	rowbytes	Number of bytes needed for each row.
	@return	Pointer to new array on success, NULL on error.
*/
static png_byte **
allocate_png_memory DK_P2(png_uint_32,h, png_uint_32,rowbytes)
{
  png_byte **back = NULL, **ptr;
  png_uint_32 y; int ok;
  
  back = (png_byte **)dkmem_alloc(sizeof(png_bytep),h);
  if(back) {
    ptr = back;
    for(y = 0; y < h; y++) { *(ptr++) = NULL; }
    ptr = back; ok = 1;
    for(y = 0; y < h; y++) {
      *ptr = (png_bytep)dkmem_alloc(sizeof(png_byte),rowbytes);
      if(!(*ptr)) { ok = 0; }
      ptr++;
    }
    if(!ok) {
      release_png_memory(back, h);
      back = NULL;
    }
  } 
  return back;
}



/**	Read image to memory.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int read_png_image DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0;
  FILE *inputfile;
  unsigned long w;	/* width */
  unsigned long h;	/* height */
  int		b;	/* bit depth (bits per pixel) */
  int		c;	/* color type */
  int		i;	/* interlace */
  int		z;	/* compression type */
  int		f;	/* filter type */
  int		ch;	/* channels */
  int		ne, ns, np;
  unsigned long rowbytes;
  
  (idata->d).p.array = NULL;
  (idata->d).p.pp = NULL;
  (idata->d).p.pi = NULL;
  ne = ns = np = 0;
  inputfile = dkapp_fopen(
    (oi->c)->app, (oi->ci)->inputfilename, str_mode_read_binary
  );
  if(inputfile) {	
    (idata->d).p.pp
    = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if((idata->d).p.pp) {	
      (idata->d).p.pi = png_create_info_struct((idata->d).p.pp);
      if((idata->d).p.pi) {	
#if DK_HAVE_SETJMP_H
	if(setjmp(png_jmpbuf((idata->d).p.pp)) == 0) {
#endif
          png_init_io((idata->d).p.pp, inputfile);
	  png_read_info((idata->d).p.pp, (idata->d).p.pi);
	  png_get_IHDR(
	    (idata->d).p.pp, (idata->d).p.pi,
	    &w, &h, &b, &c, &i, &z, &f
	  );
	  ch = png_get_channels((idata->d).p.pp, (idata->d).p.pi);
          
          
          
          
          
          
          
          
	  if((c == PNG_COLOR_TYPE_PALETTE) && (b <= 8)) {
	    ne = 1;	
	  }
	  if((c == PNG_COLOR_TYPE_GRAY) && (b < 8)) {
	    ne = 1;	
	  }
	  if(png_get_valid((idata->d).p.pp, (idata->d).p.pi, PNG_INFO_tRNS)) {
	    ne = 1;	
	  }
	  if(b > 8) {
	    ns = 1;	
	  } else {
	    if(b < 8) {
	      np = 1;	
	    }
	  }
	  if(ne) { png_set_expand((idata->d).p.pp); }
	  if(ns) { png_set_strip_16((idata->d).p.pp); }
	  if(np) { png_set_packing((idata->d).p.pp); }
	  /*
	  if(png_get_bKGD()) {
	  } else {
	  }
	  */
	  png_read_update_info((idata->d).p.pp, (idata->d).p.pi);
	  ch = png_get_channels((idata->d).p.pp, (idata->d).p.pi);
	  c  = png_get_color_type((idata->d).p.pp, (idata->d).p.pi);
          
          
	  rowbytes = png_get_rowbytes((idata->d).p.pp, (idata->d).p.pi);
	  (idata->d).p.array = allocate_png_memory(h, rowbytes);
	  if((idata->d).p.array) {
	    png_read_image((idata->d).p.pp, (idata->d).p.array);
	    back = 1;
	    (oi->ci)->w = w;
	    (oi->ci)->h = h;
	    (oi->ci)->bpp = b;
	    (oi->ci)->ch = ch;
	  } else {
            dkfig_tool2_simple_error_message(oi->c, 82);
	  }
#if DK_HAVE_SETJMP_H
	} else {
	  dkfig_tool2_combined_error_message(oi->c, 96, 97, (oi->ci)->inputfilename);
	}
#endif
      } else {
	dkfig_tool2_simple_error_message(oi->c, 83);
      }
    } else {
      dkfig_tool2_simple_error_message(oi->c, 82);
    }
    fclose(inputfile); inputfile = NULL;
  } else {
    if((oi->c)->app) {
      dkapp_err_fopenr((oi->c)->app, (oi->ci)->inputfilename);
    }
  } 
  return back;
}

#endif
#endif



#if DK_HAVE_JPEGLIB_H

/**	Flag: Error encountered while reading JPEG file (0=no error).
*/
static int had_error = 0;



/**	Error handling function (do not exit, set error code instead).
	@param	cinfo	JPEG reader structure.
*/
static
void error_exit_replacement DK_P1(j_common_ptr,cinfo)
{
  had_error = 1;
}


/**	Pointer definition.
*/
typedef JSAMPLE *jsptr;



/**	Release memory used for JPEG pixels.
	@param	p	Array pointer.
	@param	h	Number of rows in image (elements in array).
*/
static
void release_jpeg_memory DK_P2(JSAMPLE **,p, JDIMENSION,h)
{
  JSAMPLE *xptr, **ptr; JDIMENSION y;
  
  ptr = p; y = h;
  while(y--) {
    
    if(*ptr) {
      xptr = *ptr;
      dkmem_free(xptr); *ptr = NULL;
      ptr++;
    }
  }
  dkmem_free(p);
  
}



/**	Allocate memory to read JPEG image.
	@param	rl	Row length in bytes.
	@param	h	Number of rows in image.
	@return	Pointer to memory on success, NULL on error.
*/
static JSAMPLE **
allocate_jpeg_memory DK_P2(size_t,rl, size_t,h)
{
  int ok = 1;
  JSAMPLE **back = NULL, **ptr = NULL;
  JDIMENSION y;
  
  back = (JSAMPLE **)dkmem_alloc(sizeof(jsptr),h);
  if(back) {
    ptr = back;
    y = h;
    while(y--) {
      *ptr = (JSAMPLE *)dkmem_alloc(1,rl);
      if(!(*ptr)) {
        ok = 0;
      }
      ptr++;
    }
    if(!ok) {
      release_jpeg_memory(back,h);
      back = NULL;
    }
  } 
  return back;
}



/**	Read JPEG image into memory.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	Pointer to pixel data on success, NULL on error.
*/
static
JSAMPLE **read_jpeg_file DK_P2(OI *,oi, IDATA *,idata)
{
  JSAMPLE **back = NULL, **ptr;
  JDIMENSION w, h, y;
  size_t sz;
  int ok;
  
  sz = (idata->d).j.cinfo.output_width;
  sz = sz * (idata->d).j.cinfo.output_components * sizeof(JSAMPLE);
  back = allocate_jpeg_memory(sz, (idata->d).j.cinfo.output_height);
  if(back) {
    w = (idata->d).j.cinfo.output_width;
    h = (idata->d).j.cinfo.output_height;
    ptr = back; ok = 1;
    while(h && ok) {
      y = jpeg_read_scanlines(&((idata->d).j.cinfo), ptr, h);
      if(!had_error) {
        if(y > 0) {
	  ptr = &(ptr[y]);
	  h = h - y;
	} else {
	  ok = 0;
	}
      } else {
        ok = 0;
      }
    }
    if(ok) {
      
      (oi->ci)->w = (unsigned long)((idata->d).j.cinfo.output_width);
      (oi->ci)->h = (unsigned long)((idata->d).j.cinfo.output_height);
      (oi->ci)->bpp = 8;
      if((idata->d).j.cinfo.quantize_colors) {	/* quantized colors */
        (idata->d).j.what_to_do = 1;
	(idata->d).j.color_map0 = ((idata->d).j.cinfo.colormap)[0];
	if((idata->d).j.cinfo.out_color_space == JCS_GRAYSCALE) {
	  (oi->ci)->ch = 1;
	  (idata->d).j.what_to_do = 2;
	} else {
	  (oi->ci)->ch = 3;
	  (idata->d).j.color_map1 = ((idata->d).j.cinfo.colormap)[1];
	  (idata->d).j.color_map2 = ((idata->d).j.cinfo.colormap)[2];
	}
      } else {	/* colors not quantized */
        if((idata->d).j.cinfo.out_color_space == JCS_GRAYSCALE) {
	  (oi->ci)->ch = 1;
	} else {
	  (oi->ci)->ch = 3;
	}
      }
    } else {
      release_jpeg_memory(back, (idata->d).j.cinfo.output_height);
      back = NULL;
      dkfig_tool2_simple_error_message(oi->c, 80);
      
    }
  } else {
    dkfig_tool2_simple_error_message(oi->c, 81);
  }
  
  return back;
}



/**	Read JPEG image into image information structure.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int read_jpg_image DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0;
  FILE *inputfile;
  
  had_error = 0;
  (idata->d).j.state = 0;
  (idata->d).j.color_map0 = NULL;
  (idata->d).j.color_map1 = NULL;
  (idata->d).j.color_map2 = NULL;
  (idata->d).j.what_to_do = 0;
  (idata->d).j.cinfo.err = jpeg_std_error(&((idata->d).j.jerr));
  (idata->d).j.jerr.error_exit = error_exit_replacement;
  jpeg_create_decompress(&((idata->d).j.cinfo));
  if(!had_error) {
    (idata->d).j.state = 1;
    inputfile = dkapp_fopen((oi->c)->app,(oi->ci)->inputfilename, str_mode_read_binary
    );
    if(inputfile) {
      (idata->d).j.array = NULL;
      jpeg_stdio_src(&((idata->d).j.cinfo), inputfile);
      if(!had_error) {
        (idata->d).j.state = 2;
        jpeg_read_header(&((idata->d).j.cinfo), TRUE);
	if(!had_error) {
          (idata->d).j.state = 3;
          jpeg_start_decompress(&((idata->d).j.cinfo));
	  if(!had_error) {
	    (idata->d).j.state = 4;
	    (oi->ci)->w = (unsigned long)((idata->d).j.cinfo.output_width);
	    (oi->ci)->h = (unsigned long)((idata->d).j.cinfo.output_height);
	    (idata->d).j.array = read_jpeg_file(oi,idata);
	    if((idata->d).j.array) {
	      
	      back = 1;
	      jpeg_finish_decompress(&((idata->d).j.cinfo));
	    } else {	
	      jpeg_abort((j_common_ptr)(&((idata->d).j.cinfo)));
	      dkfig_tool2_simple_error_message(oi->c, 80);
	    }
	  } else {
	    jpeg_abort((j_common_ptr)(&((idata->d).j.cinfo)));
	    dkfig_tool2_simple_error_message(oi->c, 79);
	  }
	} else {
	  jpeg_abort((j_common_ptr)(&((idata->d).j.cinfo)));
	  dkfig_tool2_simple_error_message(oi->c, 78);
	}
      } else {
        jpeg_abort((j_common_ptr)(&((idata->d).j.cinfo)));
	dkfig_tool2_simple_error_message(oi->c, 77);
      }
      fclose(inputfile); inputfile = NULL;
    } else {
      if((oi->c)->app) {
        dkapp_err_fopenr((oi->c)->app, (oi->ci)->inputfilename);
      }
    }
  } else {
    dkfig_tool2_simple_error_message(oi->c, 76);
  } 
  return back;
}

#endif



#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H



/**	Read NetPBM image into image information structure.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int read_pbm_image DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0, cols = 0, rows = 0;
  FILE *inf;
  dkfig_tool_init_netpbm(oi->c);
  (idata->d).n.format = 0;
  (idata->d).n.array = NULL;
  (idata->d).n.maxxelval = 0;
  inf = dkapp_fopen(
    (oi->c)->app, (oi->ci)->inputfilename, str_mode_read_binary
  );
  if(inf) {
    (idata->d).n.array = pnm_readpnm(
      inf, &cols, &rows,
      &((idata->d).n.maxxelval), &((idata->d).n.format)
    );
    if((idata->d).n.array) {
      back = 1;
      (oi->ci)->w = cols;
      (oi->ci)->h = rows;
      (oi->ci)->bpp = 8;
      if(PNM_FORMAT_TYPE((idata->d).n.format) == PPM_TYPE) {
        (oi->ci)->ch = 3;
      } else {
        (oi->ci)->ch = 1;
      }
    } else {
      dkfig_tool2_msg3(oi->c, DK_LOG_LEVEL_ERROR, 71, 61, (oi->ci)->inputfilename);
    }
    fclose(inf); inf = NULL;
  } else {
    if((oi->c)->app) {
      dkapp_err_fopenr((oi->c)->app, (oi->ci)->inputfilename);
    }
  }
  return back;
}

#endif


/**	Release image information structure.
	@param	oi	OI structure.
	@param	idata	Image information structure to release.
*/
static
void release_image DK_P2(OI *,oi, IDATA *,idata)
{
  
  
  switch(idata->what) {
    case WHAT_PNG: {		
#if DK_HAVE_ZLIB_H
#if DK_HAVE_PNG_H
      if((idata->d).p.array) {	
        release_png_memory((idata->d).p.array, (oi->ci)->h);
	(idata->d).p.array = NULL;
      }
      if((idata->d).p.pp) {
        if((idata->d).p.pi) {	
          png_destroy_info_struct((idata->d).p.pp, &((idata->d).p.pi));
	  (idata->d).p.pi = NULL;
        }			
        png_destroy_read_struct(&((idata->d).p.pp), NULL, NULL);
	(idata->d).p.pp = NULL;
      }
#endif
#endif
    } break;
    case WHAT_JPG: {
      
#if DK_HAVE_JPEGLIB_H
      if((idata->d).j.state >= 1) {	
        jpeg_destroy_decompress(&((idata->d).j.cinfo));
      }
      if((idata->d).j.array) {	
        release_jpeg_memory((idata->d).j.array, (oi->ci)->h);
      } else {
        
      }
      (idata->d).j.array = NULL;
#endif
    } break;
    case WHAT_PBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      if((idata->d).n.array) {
        pnm_freearray((idata->d).n.array, (oi->ci)->h);
      }
      (idata->d).n.array = NULL;
#endif
    } break;
  } 
}



/**	Read image into image information structure.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int read_image DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0;
  
  idata->what = get_file_type((oi->ci)->inputfilename);
  switch(idata->what) {
    case WHAT_PNG: {	
#if DK_HAVE_ZLIB_H
#if DK_HAVE_PNG_H
      back = read_png_image(oi, idata);
#endif
#endif
    } break;
    case WHAT_JPG: {	
#if DK_HAVE_JPEGLIB_H
      back = read_jpg_image(oi, idata);
#endif
    } break;
    case WHAT_PBM: {	
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      back = read_pbm_image(oi, idata);
#endif
    } break;
    default: {
      dkfig_tool2_simple_error_message(oi->c, 111);
    } break;
  } 
  return back;
}



/**	Attach a temporary file name for alpha channel.
	@param	oi	OI structure.
	@return	1 on success (no file name needed or file name ok), 0 on error.
*/
static
int attach_filenames DK_P1(OI *,oi)
{
  int back = 0;
  long mpl;
  char *buffer;
  
  mpl = dksf_get_maxpathlen();
  buffer = dk_new(char,mpl);
  if(buffer) {
    if(dkapp_tmpnam((oi->c)->app, buffer, mpl)) {
      (oi->ci)->ofn = dkstr_dup(buffer);
      if((oi->ci)->ofn) {
        switch((oi->ci)->ch) {
	  case 2: case 4: {
	    if(dkapp_tmpnam((oi->c)->app, buffer, mpl)) {
	      (oi->ci)->afn = dkstr_dup(buffer);
	      if((oi->ci)->afn) {
	        back = 1;
	      }
	    }
	    if(oi->d) {
	      (oi->d)->opt2 |= DKFIG_OPT_HAVE_ALPHA_CHANNEL;
	    }
	  } break;
	  default: {
	    (oi->ci)->afn = NULL;
	    back = 1;
	  } break;
	}
      }
    }
    dk_delete(buffer); buffer = NULL;
  }
  
  return back;
}



#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H


/**	Normalize a pixel value.
	@param	v	Original pixel value.
	@param	mx	Original maximum value.
	@param	max	New maximum value.
	@return	Converted value.
*/
static int
normalize_pnm DK_P3(int,v, xelval,mx, int,max)
{
  int back;
  long l1, l2, l3, l4;
  
  back = v;
  if((int)mx != max) {
    l1 = v; l2 = max; l3 = (long)mx;
    l4 = (l1 * l2) / l3;
    back = (int)l4;
  } 
  return back;
}

#endif



/**	Get one byte from image.
	@param	idata	Image information structure.
	@param	x	X position of pixel.
	@param	y	Y position of pixel.
	@param	ch	Number of channels in image.
	@param	ind	Channel to get value for.
	@return	The byte.
*/
static
unsigned char get_byte DK_P5(IDATA *,idata, unsigned long,x, unsigned long,y, int,ch, int,ind)
{
  unsigned char back = 0x00;
  switch(idata->what) {
    case WHAT_PNG: {
#if DK_HAVE_ZLIB_H
#if DK_HAVE_PNG_H
      png_byte *rptr, **array;
      array = (idata->d).p.array;
      rptr = array[y];
      back = rptr[(x * ch) + ind];
#endif
#endif
    } break;
    case WHAT_JPG: {
#if DK_HAVE_JPEGLIB_H
      JSAMPLE *rptr;
      int r;
      rptr = ((idata->d).j.array)[y];
      switch((idata->d).j.what_to_do) {
        case 2: {
	  /* grayscaled mapped */
	  r = rptr[(x * ch) + ind];
	  back = ((idata->d).j.color_map0)[r & 0xFF];
	} break;
	case 1: {
	  /* color mapped */
	  r = rptr[(x * ch) + ind];
	  switch(ind) {
	    case 2: {
	      back = ((idata->d).j.color_map2)[r & 0xFF];
	    } break;
	    case 1: {
	      back = ((idata->d).j.color_map1)[r & 0xFF];
	    } break;
	    default: {
	      back = ((idata->d).j.color_map0)[r & 0xFF];
	    } break;
	  }
	} break;
	default: {
	  back = rptr[(x * ch) + ind];
	} break;
      }
#endif
    } break;
    case WHAT_PBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      xel xe; int r;
      xe = (((idata->d).n.array)[y])[x];
      if(PNM_FORMAT_TYPE((idata->d).n.format) == PPM_TYPE) {
        switch(ind) {
	  case 2:  { r = (int)(PPM_GETB(xe)); } break;
	  case 1:  { r = (int)(PPM_GETG(xe)); } break;
	  default: { r = (int)(PPM_GETR(xe)); } break;
	}
	r = normalize_pnm(r, (idata->d).n.maxxelval, 255);
	back = r & 0xFF;
      } else {
        r = (int)(PNM_GET1(xe));
	r = normalize_pnm(r, (idata->d).n.maxxelval, 255);
	back = r & 0xFF;
      }
#endif
    } break;
  }
  return back;
}



/**	Write information for one pixel to compressed stream.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@param	strm	Output stream (compressing and encoding).
	@param	x	X position of pixel.
	@param	y	Y position of pixel.
	@param	isalpha	Ignored.
*/
static
void handle_position DK_P6(OI *,oi, IDATA *,idata, dk_stream_t *,strm, unsigned long,x, unsigned long,y, int,isalpha)
{
  unsigned char uc[5];
  switch((oi->ci)->ch) {
    case 1: case 2: {
      uc[0] = get_byte(idata,x,y,(oi->ci)->ch, 0);
      uc[1] = '\0';
      dkstream_write(strm,(char *)uc,1);
    } break;
    case 3: case 4: {
      uc[0] = get_byte(idata,x,y,(oi->ci)->ch, 0);
      uc[1] = get_byte(idata,x,y,(oi->ci)->ch, 1);
      uc[2] = get_byte(idata,x,y,(oi->ci)->ch, 2);
      uc[3] = '\0';
      dkstream_write(strm,(char *)uc,3);
    } break;
  }
}



/**	Create compressed an encoded data stream for image.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int write_data_stream DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0;
  unsigned long x, y;
  
  dk_stream_t *pstr, *cstr;
  pstr = dkapp_stream_openfile(
    (oi->c)->app, (oi->ci)->ofn, str_mode_write_binary
  );
  if(pstr) {
    cstr = dkof_open(pstr, 3);
    if(cstr) {
      back = 1;
      dkof_set_crnl(cstr,1);
      if(!dkof_set(cstr,0,DK_OF_TYPE_BUFFERED)) { back = 0; }
      if(!dkof_set(cstr,1,DK_OF_TYPE_ASCII85)) { back = 0; }
      if(!dkof_set(cstr,2,DK_OF_TYPE_FLATE)) { back = 0; }
      if(back) {
        back = 0;
	if(dkof_start_chunk(cstr)) {
	  switch((oi->ci)->flipped) {
	    case 1: {	/* horiz */
	      for(y = 0; y < (oi->ci)->h; y++) {
	        for(x = (oi->ci)->w; x > 0; x--) {
		  handle_position(oi,idata,cstr,(x-1),y,0);
		}
	      }
	    } break;
	    case 2: {	/* diag */
	      for(x = 0; x < (oi->ci)->w; x++) {
	        for(y = 0; y < (oi->ci)->h; y++) {
		  handle_position(oi,idata,cstr,x,y,0);
		}
	      }
	    } break;
	    default: {	/* not flipped */
	      for(y = 0; y < (oi->ci)->h; y++) {
	        for(x = 0; x < (oi->ci)->w; x++) {
		  handle_position(oi,idata,cstr,x,y,0);
		}
	      }
	    } break;
	  }
	  if(dkof_end_chunk(cstr)) {
	    back = 1;
	  }
	}
      }
      dkof_close(cstr); cstr = NULL;
    } else {
      dkfig_tool2_simple_error_message(oi->c, 72);
    }
    (oi->ci)->ol = dkstream_get_bytes_written(pstr);
    dkstream_close(pstr); pstr = NULL;
  } else {
    dkfig_tool2_simple_error_message(oi->c, 72);
  }
  
  return back;
}



/**	Write alpha data to stream.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int write_alpha_stream DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0;
  unsigned long x, y;
  unsigned char uc;
  
  dk_stream_t *pstr, *cstr;
  pstr = dkapp_stream_openfile(
    (oi->c)->app, (oi->ci)->afn, str_mode_write_binary
  );
  if(pstr) {
    cstr = dkof_open(pstr, 3);
    if(cstr) {
      back = 1;
      dkof_set_crnl(cstr,1);
      if(!dkof_set(cstr,0,DK_OF_TYPE_BUFFERED)) { back = 0; }
      if(!dkof_set(cstr,1,DK_OF_TYPE_ASCII85)) { back = 0; }
      if(!dkof_set(cstr,2,DK_OF_TYPE_FLATE)) { back = 0; }
      if(back) {
        back = 0;
	if(dkof_start_chunk(cstr)) {
	  switch((oi->ci)->flipped) {
	    case 1: {	/* horiz */
	      for(y = 0; y < (oi->ci)->h; y++) {
	        for(x = (oi->ci)->w; x > 0; x--) {
		  uc = get_byte(idata,(x - 1),y,(oi->ci)->ch,((oi->ci)->ch - 1));
		  dkstream_write(cstr,(char *)(&uc),1);
		}
	      }
	    } break;
	    case 2: {	/* diag */
	      for(x = 0; x < (oi->ci)->w; x++) {
	        for(y = 0; y < (oi->ci)->h; y++) {
		  uc = get_byte(idata,x,y,(oi->ci)->ch,((oi->ci)->ch - 1));
		  dkstream_write(cstr,(char *)(&uc),1);
		}
	      }
	    } break;
	    default: {	/* not flipped */
	      for(y = 0; y < (oi->ci)->h; y++) {
	        for(x = 0; x < (oi->ci)->w; x++) {
		  uc = get_byte(idata,x,y,(oi->ci)->ch,((oi->ci)->ch - 1));
		  dkstream_write(cstr,(char *)(&uc),1);
		}
	      }
	    } break;
	  }
	  if(dkof_end_chunk(cstr)) {
	    back = 1;
	  }
	}
      }
      dkof_close(cstr); cstr = NULL;
    } else {
      dkfig_tool2_simple_error_message(oi->c, 72);
    }
    (oi->ci)->al = dkstream_get_bytes_written(pstr);
    dkstream_close(pstr); pstr = NULL;
  } else {
    dkfig_tool2_simple_error_message(oi->c, 72);
  }
  
  return back;
}



/**	Write image data.
	@param	oi	OI structure.
	@param	idata	Image information structure.
	@return	1 on success, 0 on error.
*/
static
int write_image DK_P2(OI *,oi, IDATA *,idata)
{
  int back = 0;
  
  if(attach_filenames(oi)) {
    if(write_data_stream(oi, idata)) {
      switch((oi->ci)->ch) {
        case 2: case 4: {
	  if(write_alpha_stream(oi, idata)) {
	    back = 1;
	  }
	} break;
	default: {
	  back = 1;
	} break;
      }
    }
  }
  
  return back;
}



/**	Prepare image for writing to PDF output.
	@param	oi	OI structure.
	@return	1 on success, 0 on error.
*/
static
int prepare_image DK_P1(OI *,oi)
{
  int back = 0;
  image_data idata;
  
  null_image(&idata);
  if(read_image(oi, &idata)) {
    if(write_image(oi, &idata)) {
      back = 1;
    }
  }
  release_image(oi, &idata);
  
  return back;
}

/**	Prepare to show image.
	@param	oi	PDF output instruction structure.
	@return	1 on success, 0 on error.
*/
int
dkfigpi_prepare_image DK_P1(OI *,oi)
{
  int back = 0;
  unsigned long oldlineno;
  if(oi) {
  if(oi->c) {
  if((oi->c)->app) {
    oldlineno = dkapp_get_source_lineno((oi->c)->app);
    dkapp_set_source_lineno((oi->c)->app, (oi->ci)->lineno);
    back = prepare_image(oi);
    if(!back) {
      dkfig_tool2_msg3(
        oi->c, DK_LOG_LEVEL_ERROR, 112, 61, (oi->ci)->inputfilename
      );
    }
    dkapp_set_source_lineno((oi->c)->app, oldlineno);
  }
  }
  }
  return back;
}


