/*
 * Module: pbmtools
 *
 * Functions for reading/writing .pbm files and pixel
 * manipulation.
 *
 * Author:
 *   Stuart Inglis (singlis@internz.co.nz)
 *   (c) 1998 
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "marklist.h"
#include "pbmtools.h"
#include "utils.h"



void pbm_putpixel_range(Pixel **bitmap,int c,int r,int val, int cols, int rows)
{
  if(((c)>=0)&&((c)<cols)&&((r)>=0)&&((r)<rows)) 
    pbm_putpixel(bitmap,c,r,val); 
  else 
    fprintf(stderr,"putpitxel (%d %d) outside (%d %d)\n",c,r,cols,rows);
}

int pbm_getpixel_range(Pixel **bitmap,int c, int r, int cols, int rows)
{
  if(((c)>=0)&&((c)<cols)&&((r)>=0)&&((r)<rows)) 
    return pbm_getpixel(bitmap,c,r);
  else {
    fprintf(stderr,"getpixel (%d %d) outside (%d %d)\n",c,r,cols,rows);
    return 0;
  }
}





/*
Returns 1 if *fp points to a PBM (magic is P1 or P4) file.
leaves the fp in it's original position.
usage: if(pbm_isapbmfile(fp)) process(fp);
*/
int pbm_isapbmfile(FILE *fp)
{
  unsigned short magic;
  magic=magic_getshort(fp);

  return ((magic==MAGIC_P4)||(magic==MAGIC_P1));
}


/*
Frees the memory used in the bitmap structure.
usage: pbm_free(&bitmap);
Sets bitmap to be NULL on return.
*/
void pbm_free(Pixel ***bitmap,int rows)
{
/*  int r;*/
  if(rows==0) rows++;

  FREE_2D(*bitmap,rows);

/*  for(r=0;r<rows;r++)
		free((*bitmap)[r]);
  free(*bitmap);*/
  (*bitmap)=NULL;
}


/*
This function allocates the bitmap.
usage: bitmap=pbm_alloc(cols,rows);
*/
Pixel **pbm_alloc(int cols, int rows)
{
  Pixel **p;
  int newc=(cols+PBITS)/PBITS; 
  
  if(newc==0) newc++;
  if(rows==0) rows++;

  if(rows<0){fprintf(stderr,"error: rows=%d\n",rows);}

  CALLOC_2D(p,rows,newc,Pixel);
  
  return p;
}



Pixel **pbm_copy(Pixel **bitmap, int cols, int rows)
{
        Pixel ** p;
	int newc=(cols+PBITS)/PBITS;
	int x,y;

	if((cols==0)||(rows==0)) return NULL;

	p=pbm_alloc(cols,rows);
	for(x=0;x<newc;x++)
	  for(y=0;y<rows;y++)
	    p[y][x]=bitmap[y][x];
	return p;
}

	

void pbm_clear(Pixel **bitmap, int cols, int rows)
{
    int newc=(cols+PBITS)/PBITS;
    int x,y;
    
    for(x=0;x<newc;x++)
	for(y=0;y<rows;y++)
	    bitmap[y][x]=0;
}

	


/*
Reads in a pbm file and returns a pointer to the allocated bitmap, 
in the P4 format.
Assumes fp was opened and keeps it that way.
usage: bitmap=pbm_readfile(fp,&cols,&rows);
*/
Pixel **pbm_readfile(FILE *fp,int *cols, int *rows, short *resolution)
{
	Pixel **p=NULL;
	char *s=NULL;
	unsigned char *buf;
	int r,c,ii=0;
	int p1=0,k;
	int arg;
	
	*resolution=0;

	do {
	  if(s!=NULL)
	    free(s);
	  s=fgoodgets(fp);
	} while(s[0]=='#');

	if(strcmp(s,"P1")==0) p1=1;
	else if(strcmp(s,"P4")==0) p1=0;
	else error("pbm_readfile","only pbm files are supported","");

	free(s);s=NULL;

	do {
	  if(s!=NULL){
	    free(s);s=NULL;
	  }
	  s=fgoodgets(fp);
	  if(s){
	    if(strncmp(s,"# resolution: ",14)==0){
	      ii=sscanf(s,"%*s %*s %hd",resolution);
	      if(ii!=1) *resolution=0;
	    }
	  }
	} while(s[0]=='#');

	arg=sscanf(s,"%d %d",cols,rows);
	free(s);s=NULL;

	if(arg==1){
	  s=fgoodgets(fp);
	  arg=sscanf(s,"%d",rows);
	  if(arg!=1) error("pbm_readfile","bizzare pbm format","");
	  free(s);s=NULL;
	}

      	p=pbm_alloc(*cols,*rows);
	buf=(unsigned char*)malloc((*cols+2)*sizeof(unsigned char));
	if(!buf) error("pbm_readfile","out of memory","");

	if(p1){
	  for(r=0;r<*rows;r++)
	    for(c=0;c<*cols;c++){
	      fscanf(fp,"%d",&ii);
	      pbm_putpixel(p,c,r,ii);
	    }
	}
	else {
	  for(r=0;r<*rows;r++){
	    c=0;
	    k=fread(buf,1,(*cols+7)/8,fp);
	    if(k!=(*cols+7)/8){
		fprintf(stderr,"*warning* file too short!\n");
	    }
	    for(c=0;c<*cols;c++){
	      if(!(c%8)) ii=buf[c/8];
	      if(ii<0) error("pbm_readfile","file reading error","");
	      if(ii&128) pbm_putpixel(p,c,r,1);
	      ii<<=1;
	    }
	  }
	} /* p4 type */
	free(buf);
	return p;
}





/*
Writes a pbm file given a file pointer and bitmap. Assumes that
fp is already open, and keeps in that way.
usage: pbm_writefile(fp,bitmap,cols,rows);
*/
void pbm_writefile(FILE *fp,Pixel **bitmap,int cols, int rows, int resolution)
{
  int r,c,ii=0,j;
  unsigned char *buf;

  buf=(unsigned char*)malloc(((cols+14)/8)*sizeof(unsigned char));

  fprintf(fp,"%s\n","P4");
  if(resolution)
    fprintf(fp,"# resolution: %d\n",resolution);
  fprintf(fp,"%d %d\n",cols,rows);

  if((cols>0) && (rows>=0))
    for(r=0;r<rows;r++){
      for(c=0;c<cols;c+=8){
	ii=0;
	for(j=0;j<8;j++) 
	  if(c+j<cols){		    
	    if(pbm_getpixel(bitmap,c+j,r)) ii|=(128>>j);
	  }
	buf[c/8]=ii;
      }
      fwrite(buf,1,(cols+7)/8,fp);
    }
  free(buf);
  if(ferror(fp)){
    fprintf(stderr,"pbm_writefile: error occured while writing file.\n");
    return ;
  }
}



/*
Writes a pbm file given a file pointer and bitmap. Assumes that
fp is already open, and keeps in that way.
usage: pbm_writefile(fp,bitmap,cols,rows);
*/
void pbm_writefile_xy(FILE *fp,Pixel **bitmap, int x, int y,int cols, int rows, int resolution)
{
  int r,c,ii=0,j;
  unsigned char *buf;

  buf=(unsigned char*)malloc(((cols+14)/8)*sizeof(unsigned char));

  fprintf(fp,"%s\n","P4");
  if(resolution)
    fprintf(fp,"# resolution: %d\n",resolution);
  fprintf(fp,"%d %d\n",cols,rows);

  if((cols>0) && (rows>=0))
    for(r=0;r<rows;r++){
      for(c=0;c<cols;c+=8){
	ii=0;
	for(j=0;j<8;j++) 
	  if(c+j<cols){		    
	    if(pbm_getpixel(bitmap,c+j,r)) ii|=(128>>j);
	  }
	buf[c/8]=ii;
      }
      fwrite(buf,1,(cols+7)/8,fp);
    }
  free(buf);
  if(ferror(fp)){
    fprintf(stderr,"pbm_writefile: error occured while writing file.\n");
    return ;
  }
}






/* works best is cols is >= rows, and iterate x faster than y */
int **pgm_alloc(int cols, int rows)
{
  int **p;
/*  int i;*/

  CALLOC_2D(p,rows,cols,int);
  
  return p;
}


void pgm_free(int ***image,int rows)
{
/*  int r;*/

  FREE_2D(*image,rows);

/*
  for(r=0;r<rows;r++)
		free((*image)[r]);
  free(*image);
*/
  (*image)=NULL;
}



int ** pgm_read(FILE *fp, int *nc, int *nr, int *maxval)
{
  int i,j;
  char *s=NULL;
  int ii=0;
  int arg;
  int **pp;
  int p2=0;

  do {
    if(s) free(s);
    s=fgoodgets(fp);
  } while(s[0]=='#');
  
  if((strcmp(s,"P5")!=0) && (strcmp(s,"P2")!=0))
    error("pgm_read","only pgm P5/P2 files are supported","");

  if(strcmp(s,"P2")==0)
    p2=1;

  free(s);s=NULL;
  
  do {
    if(s) free(s);
    s=fgoodgets(fp);
  } while(s[0]=='#');
  
  arg=sscanf(s,"%d %d",nc,nr);
  free(s);s=NULL;

  do {
    if(s) free(s);
    s=fgoodgets(fp);
  } while(s[0]=='#');
  
  arg=sscanf(s,"%d",maxval);
  free(s);s=NULL;

  pp=pgm_alloc(*nc,*nr);

  for(j=0;j<*nr;j++)
    for(i=0;i<*nc;i++){
      if(p2==0)
	ii=fgetc(fp);
      else
	fscanf(fp,"%d",&ii);
      pp[j][i]=ii;
    }
  return pp;
}
      


void pgm_write(FILE *fp, int **image, int nc, int nr, int maxval)
{
  int x, y, c;
  int p2;


  if(maxval<=255) { /* go into P5 mode */
    p2=0;
    fprintf(fp, "P5\n");
    fprintf(fp, "%d %d\n%d\n", nc, nr, maxval);
  } else {
    p2=1;
    fprintf(fp, "P2\n");
    fprintf(fp, "%d %d\n%d\n", nc, nr, maxval);
  }

  c = 0;
  for (y = 0; y < nr; y++){
    for (x = 0; x <nc; x++){
      if(p2==1)
	fprintf(fp, "%d ", pgm_getpixel(image,x,y));
      else
	fputc(pgm_getpixel(image,x,y),fp);
    }
    if(p2==1)
      fprintf(fp,"\n");
  }
  fclose(fp);
}
