Raw File
/*----------------------------------------------------------------------------

  I/O functions for Smooth Contours: read PGM or ASC images and curve output
  to PDF or TXT files.

  Copyright (c) 2016 rafael grompone von gioi <grompone@gmail.com>,
                     Gregory Randall <randall@fing.edu.uy>

  Smooth Contours is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.

  ----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>

/*----------------------------------------------------------------------------*/
#ifndef FALSE
#define FALSE 0
#endif /* !FALSE */

#ifndef TRUE
#define TRUE 1
#endif /* !TRUE */

/*----------------------------------------------------------------------------*/
/* fatal error, print a message to standard error and exit
 */
static void error(char * msg)
{
  fprintf(stderr,"error: %s\n",msg);
  exit(EXIT_FAILURE);
}

/*----------------------------------------------------------------------------*/
/* memory allocation, print an error and exit if fail
 */
static void * xmalloc(size_t size)
{
  void * p;
  if( size == 0 ) error("xmalloc input: zero size");
  p = malloc(size);
  if( p == NULL ) error("out of memory");
  return p;
}

/*----------------------------------------------------------------------------*/
/* open file, print an error and exit if fail
 */
static FILE * xfopen(const char * path, const char * mode)
{
  FILE * f = fopen(path,mode);
  if( f == NULL )
    {
      fprintf(stderr,"error: unable to open file '%s'\n",path);
      exit(EXIT_FAILURE);
    }
  return f;
}

/*----------------------------------------------------------------------------*/
/* close file, print an error and exit if fail
 */
static int xfclose(FILE * f)
{
  if( fclose(f) == EOF ) error("unable to close file");
  return 0;
}

/*----------------------------------------------------------------------------*/
/* skip white characters and comments in a PGM file
 */
static void skip_whites_and_comments(FILE * f)
{
  int c;
  do
    {
      while(isspace(c=getc(f))); /* skip spaces */
      if(c=='#') /* skip comments */
        while( c!='\n' && c!='\r' && c!=EOF )
          c=getc(f);
    }
  while( c == '#' || isspace(c) );
  if( c != EOF && ungetc(c,f) == EOF )
    error("unable to 'ungetc' while reading PGM file.");
}

/*----------------------------------------------------------------------------*/
/* read a number in ASCII from a PGM file
 */
static int get_num(FILE * f)
{
  int num,c;

  while(isspace(c=getc(f)));
  if(!isdigit(c)) error("corrupted PGM or PPM file.");
  num = c - '0';
  while( isdigit(c=getc(f)) ) num = 10 * num + c - '0';
  if( c != EOF && ungetc(c,f) == EOF )
    error("unable to 'ungetc' while reading PGM file.");

  return num;
}

/*----------------------------------------------------------------------------*/
/* read a PGM image file
 */
double * read_pgm_image(char * name, int * X, int * Y)
{
  FILE * f;
  int i,n,depth,bin=FALSE;
  double * image;

  /* open file */
  f = xfopen(name,"rb"); /* open to read as a binary file (b option). otherwise,
                            in some systems, it may behave differently */

  /* read header */
  if( getc(f) != 'P' ) error("not a PGM file!");
  if( (n=getc(f)) == '2' ) bin = FALSE;
  else if( n == '5' ) bin = TRUE;
  else error("not a PGM file!");
  skip_whites_and_comments(f);
  *X = get_num(f);               /* X size */
  skip_whites_and_comments(f);
  *Y = get_num(f);               /* Y size */
  skip_whites_and_comments(f);
  depth = get_num(f);            /* pixel depth */
  if( depth < 0 ) error("pixel depth < 0, unrecognized PGM file");
  if( bin && depth > 255 ) error("pixel depth > 255, unrecognized PGM file");
  /* white before data */
  if(!isspace(getc(f))) error("corrupted PGM file.");

  /* get memory */
  image = (double *) xmalloc( *X * *Y * sizeof(double) );

  /* read data */
  for(i=0; i<(*X * *Y); i++)
    image[i] = (double) (bin ? getc(f) : get_num(f));

  /* close file */
  xfclose(f);

  /* return image */
  return image;
}

/*----------------------------------------------------------------------------*/
/* read a 2D ASC format file
 */
double * read_asc_file(char * name, int * X, int * Y)
{
  FILE * f;
  int i,n,Z,C;
  double val;
  double * image;

  /* open file */
  f = xfopen(name,"rb"); /* open to read as a binary file (b option). otherwise,
                            in some systems, it may behave differently */

  /* read header */
  n = fscanf(f,"%d%*c%d%*c%d%*c%d",X,Y,&Z,&C);
  if( n!=4 || *X<=0 || *Y<=0 || Z<=0 || C<=0 ) error("invalid ASC file");

  /* Smooth Contours only handles gray level images */
  if( Z!=1 || C!=1 ) error("only single channel ASC files are handled");

  /* get memory */
  image = (double *) xmalloc( *X * *Y * Z * C * sizeof(double) );

  /* read data */
  for(i=0; i<(*X * *Y * Z * C); i++)
    {
      n = fscanf(f,"%lf",&val);
      if( n!=1 ) error("invalid ASC file");
      image[i] = val;
    }

  /* close file */
  xfclose(f);

  return image;
}

/*----------------------------------------------------------------------------*/
/* read an image from a file in ASC or PGM formats
 */
double * read_image(char * name, int * X, int * Y)
{
  int n = (int) strlen(name);
  char * ext = name+n-4;

  if( n>=4 && ( strcmp(ext,".asc")==0 || strcmp(ext,".ASC")==0) )
    return read_asc_file(name,X,Y);

  return read_pgm_image(name,X,Y);
}

/*----------------------------------------------------------------------------*/
/* write curves into a PDF file. the output is PDF version 1.4 as described in
   "PDF Reference, third edition" by Adobe Systems Incorporated, 2001
 */
void write_curves_pdf( double * x, double * y, int * curve_limits, int M,
                       char * filename, int X, int Y, double width )
{
  FILE * pdf;
  long start1,start2,start3,start4,start5,startxref,stream_len;
  int i,j,k;

  /* check input */
  if( filename == NULL ) error("invalid filename in write_curves_pdf");
  if( M > 0 && ( x == NULL || y == NULL || curve_limits == NULL ) )
    error("invalid curves data in write_curves_pdf");
  if( X <= 0 || Y <= 0 ) error("invalid image size in write_curves_pdf");

  /* open file */
  pdf = xfopen(filename,"wb"); /* open to write as a binary file (b option).
                                  otherwise, in some systems,
                                  it may behave differently */

  /* PDF header */
  fprintf(pdf,"%%PDF-1.4\n");
  /* The following PDF comment contains characters with ASCII codes greater
     than 128. This helps to classify the file as containing 8-bit binary data.
     See "PDF Reference" p.63. */
  fprintf(pdf,"%%%c%c%c%c\n",0xe2,0xe3,0xcf,0xd3);

  /* Catalog, Pages and Page objects */
  start1 = ftell(pdf);
  fprintf(pdf,"1 0 obj\n<</Type /Catalog /Pages 2 0 R>>\n");
  fprintf(pdf,"endobj\n");
  start2 = ftell(pdf);
  fprintf(pdf,"2 0 obj\n<</Type /Pages /Kids [3 0 R] /Count 1 ");
  fprintf(pdf,"/Resources <<>> /MediaBox [0 0 %d %d]>>\nendobj\n",X,Y);
  start3 = ftell(pdf);
  fprintf(pdf,"3 0 obj\n");
  fprintf(pdf,"<</Type /Page /Parent 2 0 R /Contents 4 0 R>>\n");
  fprintf(pdf,"endobj\n");

  /* Contents object - graphic contents */
  start4 = ftell(pdf);
  fprintf(pdf,"4 0 obj\n<</Length 5 0 R>>\n"); /* indirect length in obj 5 */
  fprintf(pdf,"stream\n");
  stream_len = ftell(pdf);
  fprintf(pdf,"%.4f w\n",width); /* set line width */
  for(k=0; k<M; k++) /* write curves */
    {
      /* initate chain */
      i = curve_limits[k];
      fprintf(pdf,"%.4f %.4f m\n", x[i], Y-y[i] ); /* first point */

      /* add remaining points of the curve */
      for(j=i+1; j<curve_limits[k+1]; j++)
        fprintf(pdf,"%.4f %.4f l\n", x[j], Y-y[j] );

      /* if the curve is closed, market as such */
      j = curve_limits[k+1] - 1;
      if( x[i]==x[j] && y[i]==y[j] ) fprintf(pdf,"h\n");

      /* end curve - stroke! */
      fprintf(pdf,"S\n");
    }
  stream_len = ftell(pdf) - stream_len; /* store stream length */
  fprintf(pdf,"\r\nendstream\n"); /* EOL must be CRLF before endstream */
  fprintf(pdf,"endobj\n");

  /* Contents' stream length object - the use of this indirect object
     for the stream length allows to generate the PDF file in a single
     pass, specifying the stream’s length only when its contents have
     been generated. See "PDF Reference" p.40. */
  start5 = ftell(pdf);
  fprintf(pdf,"5 0 obj\n%ld\nendobj\n",stream_len);

  /* PDF Cross-reference table */
  startxref = ftell(pdf);
  fprintf(pdf,"xref\n");
  fprintf(pdf,"0 6\n");
  fprintf(pdf,"0000000000 65535 f\r\n"); /* EOL must be CRLF in xref table */
  fprintf(pdf,"%010ld 00000 n\r\n",start1);
  fprintf(pdf,"%010ld 00000 n\r\n",start2);
  fprintf(pdf,"%010ld 00000 n\r\n",start3);
  fprintf(pdf,"%010ld 00000 n\r\n",start4);
  fprintf(pdf,"%010ld 00000 n\r\n",start5);

  /* PDF trailer */
  fprintf(pdf,"trailer <</Size 6 /Root 1 0 R>>\n");
  fprintf(pdf,"startxref\n");
  fprintf(pdf,"%ld\n",startxref);
  fprintf(pdf,"%%%%EOF\n");

  /* close file */
  xfclose(pdf);
}

/*----------------------------------------------------------------------------*/
/* write curves into a TXT file
 */
void write_curves_txt( double * x, double * y, int * curve_limits, int M,
                       char * filename )
{
  FILE * txt;
  int i,k;

  /* check input */
  if( filename == NULL ) error("invalid filename in write_curves_txt");
  if( M > 0 && ( x == NULL || y == NULL || curve_limits == NULL ) )
    error("invalid curves data in write_curves_txt");

  /* open file */
  txt = xfopen(filename,"wb"); /* open to write as a binary file (b option).
                                  otherwise, in some systems,
                                  it may behave differently */

  /* write curves */
  for(k=0; k<M; k++) /* write curves */
    {
      for(i=curve_limits[k]; i<curve_limits[k+1]; i++)
        fprintf(txt,"%g %g\n",x[i],y[i]);
      fprintf(txt,"-1 -1\n"); /* end of chain */
    }

  /* close file */
  xfclose(txt);
}
/*----------------------------------------------------------------------------*/
back to top