
// PNM: declarations and routines
// Version 2.02, 11.Feb.98
// (c) 1997/98 by Stefan Diener

#ifndef STIMP_PNM_INC
#define STIMP_PNM_INC

// We do not need the pseudo modes !
#ifndef NO_PSEUDO_PGM_MODE
#define NO_PSEUDO_PGM_MODE
#endif

#ifndef NO_PSEUDO_PPM_MODE
#define NO_PSEUDO_PPM_MODE
#endif

#include <STIMP/pbm.c>
#include <STIMP/pgm.c>
#include <STIMP/ppm.c>

struct PNM_Info
{
  char type;
  unsigned int height;
  unsigned int width;
  unsigned int maxval;
  union
  {
    unsigned char *gray;
    struct
    {
      unsigned char *red;
      unsigned char *green;
      unsigned char *blue;
    } RGB;
  } DATEN;
};

#define DATA		DATEN.gray
#define redDATA	DATEN.RGB.red
#define greenDATA	DATEN.RGB.green
#define blueDATA	DATEN.RGB.blue

#define TYPE_PNM TYPE_PBM+TYPE_PGM+TYPE_PPM

int ReadPNMFile(char *name, int mask, struct PNM_Info *info)
// read a PNM-file
{
  FILE *datei;
  char tempo[5];
  int type=0, zeile;
  long int i, j, k, elemente, gesamt, anzahl;
  unsigned char *srcR, *srcG, *srcB, *src;
  unsigned char *Puffer, *zeiger, zwisch;

  if (strcmp("-",name)==0) datei=stdin;   // use stdin
  else
  {
    // really open a file
    datei=fopen(name,"rb");
    if (datei==0)
    {
      PrintMessage("File %s not found !", name);
      return 1;
    }
  }

  // set buffer size
  setvbuf(datei,0,_IOFBF,1000);

  // check type
  fgets(tempo, 4, datei);
  correct(tempo);
  if (strcmp(tempo,"P4")==0) type=TYPE_PBM;
  if (strcmp(tempo,"P5")==0) type=TYPE_PGM;
  if (strcmp(tempo,"P6")==0) type=TYPE_PPM;

  if (mask & type)
  {
    info->type=type;

    // read dimensions
    // skip comments !!!
    ReadComment(datei);
    fscanf(datei,"%i %i\n",&info->width,&info->height);
    gesamt=info->width*info->height;

    // read maxval if not a PBM image
    // skip comments !!!
    if (type!=TYPE_PBM)
    {
      ReadComment(datei);
      fscanf(datei,"%i\n",&info->maxval);
    }

    switch (type)
    {
      case TYPE_PBM :   // PBM routine !!!!!!!!!!
        // print it on the screen
        PrintMessage("Reading %s (PBM, %ix%i)", name, info->width, info->height);

        // allocate memory for the picture
        info->DATA=NULL;
        info->DATA=(unsigned char *) malloc(gesamt*sizeof(unsigned char));
        if (info->DATA==NULL)
        {
          PrintMessage("No memory for the picture data left !");
          if (strcmp("-",name)!=0) fclose(datei);
          return 1;
        }

        // need an empty picture
        src=info->DATA;
        for (i=0;i<gesamt;i++) *src++=0;

        // initialize help variables
        src=info->DATA;
        zeile=info->width;
        if (zeile%8) zeile=zeile-(zeile%8)+8;
        zeile=zeile/8;

        // read picture data
        for (i=0; i<info->height; i++)
        {
          for (k=0; k<zeile; k++)
          {
            j=fgetc(datei);
            if (j==EOF)
            {
              PrintMessage("Unexpected end of file !");
              i=info->height;
              k=zeile;
            }
            else
            {
              zwisch=(unsigned char) j;
              for (j=7; j>=0; j--)
              {
                // Bit==0 => white, Bit==1 => black
                if (k*8+j<info->width) src[i*info->width+k*8+j]=((zwisch%2)?0:255);
                zwisch=zwisch>>1;
              }
            }
          }
        }

        // init maxval, even of not really needed
        info->maxval=255;
        break;

       case TYPE_PGM :   // PGM routine !!!!!!!!!!
        // print it on the screen
        PrintMessage("Reading %s (PGM, %ix%i)", name, info->width, info->height);

        // allocate memory for picture data
        info->DATA=NULL;
        info->DATA=(unsigned char *) malloc(gesamt*sizeof(unsigned char));
        if (info->DATA==NULL)
        {
          PrintMessage("No memory for the picture data left !");
          if (strcmp("-",name)!=0) fclose(datei);
          return 1;
        }

        // need an empty picture
        src=info->DATA;
        for (i=0; i<gesamt; i++) *src++=0;

        // read picture data
        i=fread((void *) info->DATA, sizeof(unsigned char), gesamt, datei);
        if (i!=gesamt) PrintMessage("Error while reading the file !");
        break;

      case TYPE_PPM :   // PPM routine !!!!!!!!!!
        // print it on the screen
        PrintMessage("Reading %s (PPM, %ix%i)", name, info->width, info->height);

        // allocate memory for the read buffer
        Puffer=NULL;
        Puffer=(unsigned char *) malloc(PPM_PUFFER_SIZE*sizeof(unsigned char));
        if (Puffer==NULL)
        {
          PrintMessage("No memory for the read buffer left !");
          if (strcmp("-",name)!=0) fclose(datei);
          return 1;
        }

        // allocate memory for red picture data
        info->redDATA=NULL;
        info->redDATA=(unsigned char *) malloc(gesamt*sizeof(unsigned char));
        if (info->redDATA==NULL)
        {
          PrintMessage("No memory for red picture data left !");
          free((void *) Puffer);
          if (strcmp("-",name)!=0) fclose(datei);
          return 1;
        }

        // allocate memory for green picture data
        info->greenDATA=NULL;
        info->greenDATA=(unsigned char *) malloc(gesamt*sizeof(unsigned char));
        if (info->greenDATA==NULL)
        {
          PrintMessage("No memory for green picture data left !");
          free((void *) Puffer);
          free((void *) info->redDATA);
          if (strcmp("-",name)!=0) fclose(datei);
          return 1;
        }

        // allocate memory for blue picture data
        info->blueDATA=NULL;
        info->blueDATA=(unsigned char *) malloc(gesamt*sizeof(unsigned char));
        if (info->blueDATA==NULL)
        {
          PrintMessage("No memory for blue picture data left !");
          if (strcmp("-",name)!=0) fclose(datei);
          free((void *) Puffer);
          free((void *) info->redDATA);
          free((void *) info->greenDATA);
          return 1;
        }

        // need an empty picture
        srcR=info->redDATA;
        srcG=info->greenDATA;
        srcB=info->blueDATA;
        for (i=0; i<gesamt; i++)
        {
           *srcR++=0;
           *srcG++=0;
           *srcB++=0;
        }

        // get pointers
        srcR=info->redDATA;
        srcG=info->greenDATA;
        srcB=info->blueDATA;

        // read picture data
        i=0;
        elemente=PPM_PUFFER_SIZE/3;
        while (i<gesamt)
        {
          anzahl=gesamt-i;
          if (anzahl>elemente) anzahl=elemente;
          j=fread((void *) Puffer, sizeof(unsigned char), anzahl*3, datei);
          if (j!=anzahl*3)
          {
            i=gesamt;
            PrintMessage("Error while reading the file !");
          }
          else
          {
            i+=anzahl;
            zeiger=Puffer;
            for (j=0; j<anzahl; j++)
            {
              *srcR++=*zeiger++;
              *srcG++=*zeiger++;
              *srcB++=*zeiger++;
            }
          }
        }

        // free buffer memory
        free((void *) Puffer);
        break;
    }

    // close file
    if (strcmp("-",name)!=0) fclose(datei);
  }
  else
  {
    if (strcmp("-",name)!=0) fclose(datei);   // really close something
    PrintMessage("Wrong file type: %s !",name);
    return 1;
  }

  // ok
  return 0;
}

int WritePNMFile(char *name, struct PNM_Info *info)
// write a PNM-file
{
  int error=0;
  struct PBM_Info infoB;
  struct PGM_Info infoG;
  struct PPM_Info infoP;

  switch (info->type)
  {
    case TYPE_PBM: infoB.width=info->width;
                              infoB.height=info->height;
                              infoB.Data=info->DATA;
                              error=WritePBMFile(name, &infoB);
                              break;

    case TYPE_PGM: infoG.width=info->width;
                              infoG.height=info->height;
                              infoG.maxval=info->maxval;
                              infoG.Data=info->DATA;
                              error=WritePGMFile(name, &infoG);
                              break;

    case TYPE_PPM: infoP.width=info->width;
                              infoP.height=info->height;
                              infoP.maxval=info->maxval;
                              infoP.redData=info->redDATA;
                              infoP.greenData=info->greenDATA;
                              infoP.blueData=info->blueDATA;
                              error=WritePPMFile(name, &infoP);
                              break;
  }

  return error;
}

int CreatePNMArray(int y, int x, int typ, struct PNM_Info *info)
// allocate memory for a PNM-picture
{
  int error=0;
  struct PBM_Info infoB;
  struct PGM_Info infoG;
  struct PPM_Info infoP;

  switch (typ)
  {
    case TYPE_PBM: error=CreatePBMArray(y, x, &infoB);
                              break;

    case TYPE_PGM: error=CreatePGMArray(y, x, &infoG);
                              break;

    case TYPE_PPM: error=CreatePPMArray(y, x, &infoP);
                              break;

    default: error=1;
                 break;
  }

  if (error==0)
  {
    info->type=typ;

    switch (typ)
    {
      case TYPE_PBM: info->height=infoB.height;
                                info->width=infoB.width;
                                info->maxval=255;
                                info->DATA=infoB.Data;
                                break;

      case TYPE_PGM: info->height=infoG.height;
                                info->width=infoG.width;
                                info->maxval=infoG.maxval;
                                info->DATA=infoG.Data;
                                break;

      case TYPE_PPM: info->height=infoP.height;
                                info->width=infoP.width;
                                info->maxval=infoP.maxval;
                                info->redDATA=infoP.redData;
                                info->greenDATA=infoP.greenData;
                                info->blueDATA=infoP.blueData;
                                break;
    }
  }

  return error;
}

void FreePNMArray(struct PNM_Info *info)
// free the memory of a PNM-picture
{
  struct PBM_Info infoB;
  struct PGM_Info infoG;
  struct PPM_Info infoP;

  switch (info->type)
  {
    case TYPE_PBM: infoB.width=info->width;
                              infoB.height=info->height;
                              infoB.Data=info->DATA;
                              FreePBMArray(&infoB);
                              break;

    case TYPE_PGM: infoG.width=info->width;
                              infoG.height=info->height;
                              infoG.maxval=info->maxval;
                              infoG.Data=info->DATA;
                              FreePGMArray(&infoG);
                              break;

    case TYPE_PPM: infoP.width=info->width;
                              infoP.height=info->height;
                              infoP.maxval=info->maxval;
                              infoP.redData=info->redDATA;
                              infoP.greenData=info->greenDATA;
                              infoP.blueData=info->blueDATA;
                              FreePPMArray(&infoP);
                              break;
  }
}

#endif
