/*****************************************************************
 * fljpeg.c: FBM Release 1.2 24-Mar-93 Michael Mauldin
 *
 * Copyright (C) 1993 by Michael Mauldin.  Permission is granted
 * to use this file in whole or in part for any purpose, educational,
 * recreational or commercial, provided that this copyright notice
 * is retained unchanged.  This software is available to all free of
 * charge by anonymous FTP and in the UUNET archives.
 *
 * This code is based on example.c from the JPEG library.
 *
 * fljpeg.c: 
 *
 * CONTENTS
 *	read_jpeg (image, rfile, mstr, mlen)
 *	write_jpeg (image, wfile)
 *
 * EDITLOG
 *	LastEditDate = Mon Jun 25 00:18:04 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/fljpeg.c
 *
 * HISTORY
 * 31-Mar-93  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Created.
 *****************************************************************/

# include <stdio.h>
# include <math.h>
# include "fbm.h"

# define RED 0
# define GRN 1
# define BLU 2

static FBM *fbm_image; /* Global to allow allow access to methods */
static int row;

#ifndef lint
static char *fbmid =
"$FBM fljpeg.c <1.2> 24-Mar-93  (C) 1993 by Michael Mauldin, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

# ifdef DO_JPEG
# include <setjmp.h>
# include <sys/types.h>
# include "jinclude.h"


/*================ Compressor ================*/

void input_init (cinfo)
compress_info_ptr cinfo;
{
  /* The fields are initialized in write_jpeg */
}


/*
 * This function is called repeatedly and must supply the next row of pixels
 * on each call.  The rows MUST be returned in top-to-bottom order if you want
 * your JPEG files to be compatible with everyone else's. 
 */

void get_input_row (cinfo, pixel_row)
compress_info_ptr cinfo;
JSAMPARRAY pixel_row;
/* Read next row of pixels into pixel_row[][] */
{
  /* This example shows how you might read RGB data (3 components)
   * from an input file in which the data is stored 3 bytes per pixel
   * in left-to-right, top-to-bottom order.
   */
  register FILE * infile = cinfo->input_file;
  register JSAMPROW obm;
  register unsigned char *ibm;
  register int k;
  register long col;

  if (row >= fbm_image->hdr.rows) return;

  /* Copy a row from each plane into the appropriate pixel_row */
  for (k=0; k<cinfo->input_components; k++)
  { obm = pixel_row[k];
    ibm = &fbm_image->bm[k * fbm_image->hdr.plnlen + 
			 row * fbm_image->hdr.rowlen];

    for (col = 0; col < cinfo->image_width; col++)
    { *obm++ = (JSAMPLE) *ibm++; }
  }
  
  row++;
}

void input_term (cinfo)
compress_info_ptr cinfo;
/* Finish up at the end of the input */
{
  /* no work to do */
}

/*
 * This routine must determine what output JPEG file format is to be written,
 */

void c_ui_method_selection (cinfo)
compress_info_ptr cinfo;
{
  /* If the input is gray scale, generate a monochrome JPEG file. */
  if (cinfo->in_color_space == CS_GRAYSCALE)
    j_monochrome_default(cinfo);

  /* For now, always select JFIF output format. */
  jselwjfif(cinfo);
}

/****************************************************************
 * write_jpeg: Set up methods and call compressor
 ****************************************************************/

write_jpeg (image, wfile, quality)
FBM *image;
FILE *wfile;
int quality;
{
  struct Compress_info_struct cinfo;
  struct Compress_methods_struct c_methods;
  struct External_methods_struct e_methods;

  /* Global for get_input_row */
  fbm_image = image;

  /* Initialize the system-dependent method pointers. */
  cinfo.methods = &c_methods;	/* links to method structs */
  cinfo.emethods = &e_methods;
  jselerror(&e_methods);	/* select std error/trace message routines */
  jselmemmgr(&e_methods);	/* select std memory allocation routines */

  /*-------- Code that normally goes in input_init --------*/
  cinfo.image_width = image->hdr.cols;		/* width in pixels */
  cinfo.image_height = image->hdr.rows;	/* height in pixels */
  cinfo.input_components = image->hdr.planes;	/* or 1 for grayscale */

  if (cinfo.input_components == 1)
  { cinfo.in_color_space = CS_GRAYSCALE; }
  else
  { cinfo.in_color_space = CS_RGB; }

  cinfo.data_precision = image->hdr.physbits;	/* bits/pixel comp. value */
  
  row = 0;
  /*---------------------------------------------------*/

  /* Here, set up pointers to your own routines for input data handling
   * and post-init parameter selection.
   */
  c_methods.input_init = input_init;		/* now a nop */
  c_methods.get_input_row = get_input_row;
  c_methods.input_term = input_term;
  c_methods.c_ui_method_selection = c_ui_method_selection;

  /* Set up default JPEG parameters in the cinfo data structure. */
  j_c_defaults(&cinfo, quality, TRUE);

  cinfo.input_file = NULL;	/* if no actual input file involved */
  cinfo.output_file = wfile;

  /* Here we go! */
  jpeg_compress(&cinfo);
}

/* ================ Decompressor ================*/

/* These static variables are needed by the error routines. */
static jmp_buf setjmp_buffer;		/* for return to caller */
static external_methods_ptr emethods;	/* for access to message_parm */

/* This routine is used for any and all trace, debug, or error printouts
 * from the JPEG code.  The parameter is a printf format string; up to 8
 * integer data values for the format string have been stored in the
 * message_parm[] field of the external_methods struct.
 */

void trace_message (msgtext)
char *msgtext;
{
  fprintf(stderr, msgtext,
	  emethods->message_parm[0], emethods->message_parm[1],
	  emethods->message_parm[2], emethods->message_parm[3],
	  emethods->message_parm[4], emethods->message_parm[5],
	  emethods->message_parm[6], emethods->message_parm[7]);
  fprintf(stderr, "\n");	/* there is no \n in the format string! */
}

/*
 * The error_exit() routine should not return to its caller.  The default
 * routine calls exit(), but here we assume that we want to return to
 * read_JPEG_file, which has set up a setjmp context for the purpose.
 * You should make sure that the free_all method is called, either within
 * error_exit or after the return to the outer-level routine.
 */

void error_exit (msgtext)
char *msgtext;
{
  trace_message(msgtext);	/* report the error message */
  (*emethods->free_all) ();	/* clean up memory allocation & temp files */
  longjmp(setjmp_buffer, 1);	/* return control to outer routine */
}

/****************************************************************
 * output_init;
 ****************************************************************/


void output_init (cinfo)
decompress_info_ptr cinfo;
{ int rowlen;

  /* Set image size */
  fbm_image->hdr.rows = cinfo->image_height;
  fbm_image->hdr.cols = cinfo->image_width;
  fbm_image->hdr.bits = cinfo->data_precision;
  fbm_image->hdr.physbits = 8;
  fbm_image->hdr.planes = (cinfo->out_color_space == CS_GRAYSCALE) ? 1 : 3;

  /*
   * Check for too many bits of precision.  In the future, we
   * should just truncate to the 8 highest bits in this case.
   */

  if (cinfo->data_precision > 8)
  { char mbuf[256];
    sprintf (mbuf, "error:input has %d bits precision, can only handle 8\n",
	     cinfo->data_precision);
    error_exit (mbuf);
  }

  /* Make sure rowlen is extended to an even number of bytes */
  if ((rowlen = fbm_image->hdr.cols) & 1) rowlen++;
  fbm_image->hdr.rowlen = rowlen;
  fbm_image->hdr.plnlen = rowlen * fbm_image->hdr.rows;

  /* ALways unmapped output */
  fbm_image->hdr.clrlen = 0;

  /* Miscellaneous annotations */
  fbm_image->hdr.aspect = 1.0;
  fbm_image->hdr.title[0] = '\0';
  strcpy (fbm_image->hdr.credits, "via JPEG");
  
  alloc_fbm (fbm_image);

  row = 0;  /* Global variable used in put_pixel_rows */
}

/*
 * This routine is called if and only if you have set cinfo->quantize_colors
 * to TRUE.  We don't need this for FBM.
 */

void put_color_map ()
{
  fprintf(stderr, "put_color_map called: there's a bug here somewhere!\n");
}

/*****************************************************************
 * put_pixel_rows:  Called with 1 or more rows in top to bottom order 
 *****************************************************************/

void put_pixel_rows (cinfo, num_rows, pixel_data)
decompress_info_ptr cinfo;
int num_rows;
JSAMPIMAGE pixel_data;
{ register FILE * outfile = cinfo->output_file;
  register JSAMPROW ibm;
  register unsigned char *obm;
  register int i, j, k;

  for (j = 0; j < num_rows && row < fbm_image->hdr.rows; j++, row++)
  { for (k=0; k < fbm_image->hdr.planes; k++)
    { ibm = pixel_data[k][j];
      obm = &fbm_image->bm[k * fbm_image->hdr.plnlen + 
			   row * fbm_image->hdr.rowlen];
      
      for (i = cinfo->image_width; i > 0; i--, ibm++)
      { *obm++ = GETJSAMPLE (*ibm); }
    }
  }
}

void output_term ()
{
  /* no work to do */
}

/*****************************************************************
 * Now we have overall control and parameter selection routines.
 *****************************************************************/

void
d_ui_method_selection (cinfo)
decompress_info_ptr cinfo;
{
  /* if grayscale input, force grayscale output; */
  /* else leave the output colorspace as set by main routine. */
  if (cinfo->jpeg_color_space == CS_GRAYSCALE)
    cinfo->out_color_space = CS_GRAYSCALE;

  /* select output routines */
  cinfo->methods->output_init = output_init;
  cinfo->methods->put_color_map = put_color_map;
  cinfo->methods->put_pixel_rows = put_pixel_rows;
  cinfo->methods->output_term = output_term;
}

/*****************************************************************
 * OK, here is the main function that actually causes everything to happen.
 * We assume here that all decompression parameters can be default values.
 * The routine returns 1 if successful, 0 if not.
 *****************************************************************/

read_jpeg (image, rfile)
FBM *image;
FILE *rfile;
{ 
  struct Decompress_info_struct cinfo;
  struct Decompress_methods_struct dc_methods;
  struct External_methods_struct e_methods;

  fbm_image = image;

  /* Set input file from caller and specify no output files */
  cinfo.input_file = rfile;
  cinfo.output_file = NULL;	/* if no actual output file involved */

  /* Initialize the system-dependent method pointers. */
  cinfo.methods = &dc_methods;	/* links to method structs */
  cinfo.emethods = &e_methods;

  /* Here we supply our own error handler; compare to use of standard error
   * handler in the previous write_JPEG_file example.
   */
  emethods = &e_methods;	/* save struct addr for possible access */
  e_methods.error_exit = error_exit; /* supply error-exit routine */
  e_methods.trace_message = trace_message; /* supply trace-message routine */
  e_methods.trace_level = 0;	/* default = no tracing */
  e_methods.num_warnings = 0;	/* no warnings emitted yet */
  e_methods.first_warning_level = 0; /* display first corrupt-data warning */
  e_methods.more_warning_level = 3; /* but suppress additional ones */

  /* prepare setjmp context for possible exit from error_exit */
  if (setjmp(setjmp_buffer))
  { fclose(cinfo.input_file);
    return (0);
  }

  jselmemmgr(&e_methods);
  dc_methods.d_ui_method_selection = d_ui_method_selection;
  j_d_defaults(&cinfo, TRUE);
  jselrjfif(&cinfo);

  /* Here we go! */
  jpeg_decompress(&cinfo);

  fclose(cinfo.input_file);

  return (1);
}
# else

/****************************************************************
 * stubs for reading and writing JPEG, since the library isn't loaded
 ****************************************************************/

write_jpeg (image, wfile, quality)
FBM *image;
FILE *wfile;
int quality;
{
  fprintf (stderr, "JPEG support was not compiled into this executable\n");
  exit (1);
}

read_jpeg (image, rfile)
FBM *image;
FILE *rfile;
{ 
  fprintf (stderr, "JPEG support was not compiled into this executable\n");
  exit (1);
}
# endif
