/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */
/*
 * pbm.c - interface with PBP/PGM/PPM file formats.
 *
 * Author:      Raul Rivero
 *              Mathematics Dept.
 *              University of Oviedo
 * Date:        Mon Feb 3 1992
 * Copyright (c) 1992, Raul Rivero
 *
 */

#include <lug.h>
#include <lugfnts.h>

extern int LUGverbose;

read_pbm_file( name, bitmap )
char *name;
bitmap_hdr *bitmap;
{
  FILE *handle;

  /* Open the file descriptor */
  if ( name != NULL )
    handle = Fopen( name, "rb" );
  else handle = stdin;

  /* Read the bitmap */
  read_pbm( handle, bitmap );
  rm_compress();

  /* Close the file */
  Fclose( handle );
}

read_pbm(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  int type;
  int totalsize;

  /* Is really a PBM file */
  if ( fgetc(handle) != 'P' )
    error( 5 );

  /* Get its type */
  switch( type = fgetc(handle) - '0' ) {
    case 1:
    case 4:
                image->depth = 1;
                image->cmap = (byte *) Malloc( 6 );
                image->cmap[3] = image->cmap[4] = image->cmap[5] = 255;
                break;
    case 2:
    case 5:
                image->depth = 8;
                image->cmap = create_bw_pallete();
                break;
    case 3:
    case 6:
                image->depth = 24;
                break;
    default:
                fprintf( stderr, "Unknow PBM file type (%d)\n", type );
                error( 99 );
  }

  /* Get the sizes and depth */
  image->colors = ( 1 << image->depth );
  image->magic = LUGUSED;
  skip_pbm( handle );
  fscanf( handle, "%d", &(image->xsize) );
  fscanf( handle, "%d", &(image->ysize) );
  totalsize = image->xsize * image->ysize;

  /* Ok, we have it all.  Allocate memory */
  image->r = (byte *) Malloc( totalsize );
  if ( image->depth > 8 ) {
    image->g = (byte *) Malloc( totalsize );
    image->b = (byte *) Malloc( totalsize );
  }

  if ( type < 4 ) {
    /* An ASCII file */
    fprintf( stderr, "The PBM/PGM/PPM ascii file format type is not supported.\n" );
    error( 99 );
  }else {
    register int i, j;
    byte *line, *ptr;
    byte *rb, *gb, *bb;

    switch ( type ) {
      case 4:
                line = image->r;
                ptr  = line + totalsize;
                
                skip_pbm( handle );
                /* One byte has 8 pixels, so ... */
                while ( line < ptr ) {
                  i = fgetc( handle );
                  for ( j = 7; j >= 0; j-- )
                    *line++ =  i & ( 1 << j );
                }
                break;
      case 5:
                /* Skip the max. value */
                fscanf( handle, "%d", &type );
                skip_pbm( handle );

                /* Read the raw data info */
                Fread( image->r, totalsize, 1, handle );
                break;
      case 6:
                /* Skip the max. value */
                fscanf( handle, "%d", &type );
                skip_pbm( handle );

                /* Set pointers */
                line = (byte *) Malloc( 3 * image->xsize );
                rb = image->r;
                gb = image->g;
                bb = image->b;
                /* Read each line and split RGB */
                for ( i = 0; i < image->ysize; i++ ) {
                  Fread( line, 3, image->xsize, handle );
                  j = image->xsize, ptr = line;
                  while ( j-- ) {
                    *rb++ = *ptr++;
                    *gb++ = *ptr++;
                    *bb++ = *ptr++;
                  }
                }

                /* Free memory */
                free( line );
                break;
    }
  }
  return 0;
}

write_pbm_file( name, image )
char *name;
bitmap_hdr *image;
{
  FILE *handle;

  /* Open the file descriptor */
  if ( name != NULL )
    handle = Fopen( name, "wb" );
  else handle = stdout;

  /* Write the bitmap */
  write_pbm( handle, image );

  /* Close the file */
  Fclose( handle );
}

write_pbm(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  int totalsize = image->xsize * image->ysize;
  bitmap_hdr aux_bitmap;
  byte *r, *g, *b, *end;
  register int i, j;

  if ( image->magic != LUGUSED )
    error( 19 );

  switch( image->depth ) {
    case 1:     /** B&W images **/
                /*
                 * Write the file header: P4 ( b&w / binary format )
                 * and the image sizes.
                 */
                fprintf( handle, "P4\n" );
                fprintf( handle, "# PBM image generated by The LUG Library\n");
                fprintf( handle, "%d %d\n", image->xsize, image->ysize );

                /* Set pointers */
                r = image->r;
                end = r + image->xsize * image->ysize;

                /*
                 * Now pack all the image. We can build a
                 * single byte from 8 pixels.
                 */
                while ( r < end ) {
                  for ( i = 7, j = 0; i >= 0 && r < end; i-- )
                    if ( *r++ )
                      j |= ( 1 << i );
                  fputc( j, handle );
                }
                break;
    case 32:    /** Futures releases **/
    case 24:    /** True color images **/
                /*
                 * Write the file header: P6 ( true color / binary format ),
                 * the image sizes and the max. value.
                 */
                fprintf( handle, "P6\n" );
                fprintf( handle, "# PBM image generated by The LUG Library\n");
                fprintf( handle, "%d %d 255\n", image->xsize, image->ysize );

                /* Set pointers */
                r = image->r;
                g = image->g;
                b = image->b;
                while ( totalsize-- ) {
                  fputc( *r++, handle );
                  fputc( *g++, handle );
                  fputc( *b++, handle );
                }
                break;
    default:    /** Mapped images ( ... ok, only gray mapped ) **/
                /*
                 * Write the file header: P5 ( gray map / binary format ),
                 * the image sizes and the max. value.
                 */
                fprintf( handle, "P5\n" );
                fprintf( handle, "# PGM image generated by The LUG Library\n");
                fprintf( handle, "%d %d 255\n", image->xsize, image->ysize );

                /*
                 * Now we need a raster level information, so convert
                 * our image ... and write it !.
                 */
                to_raw_bw( image, &aux_bitmap );
                Fwrite( aux_bitmap.r, totalsize, 1, handle );

                /* Free memory */
                freebitmap( &aux_bitmap );

                break;
  }
  return 0;
}

/*
 * PBM files suport coments, so if a line begin with
 * a '#' char then ...
 */
skip_pbm( handle )
FILE *handle;
{
  int c;

  while ( !feof( handle ) ) {
    /* Skip until next line */
    while( fgetc( handle ) != '\n' );
    /*
     * If the first char is a '#' then we have
     * a comment ( so we re-begin ), else return.
     */
    if ( (c = fgetc( handle )) != '#' ) {
      ungetc( c, handle );
      return;
    }
  }

  /*
   * Hmmm ???, if all the pbm image was comments I suppose
   * an error, and you ?.
   */
  error( 6 );
}
