/* This code is ripped from Autotrace-0.29. Small modifications by pts. */
/* input-pnm.ci:
* The pnm reading and writing code was written from scratch by Erik Nygren
* (
[email protected]) based on the specifications in the man pages and
* does not contain any code from the netpbm or pbmplus distributions.
*/
#ifdef __GNUC__
#ifndef __clang__
#pragma implementation
#endif
#endif
/* #include "types.h" */
#include "at_bitmap.h"
/* #include "input-pnm.h" */
/* #include "message.h" */
/* #include "xstd.h" */
/* #include <math.h> -- ceil(); emulated */
#include <stdlib.h> /* atoi(...) */
/**** pts ****/
/* #include <ctype.h> */
#define isspace(c) ((c)=='\0' || (c)==' ' || ((unsigned char)((c)-011)<=(unsigned char)(015-011)))
/* ^^^ not strictly POSIX C locale */
#define isdigit(c) ((unsigned char)(c-'0')<=(unsigned char)('9'-'0'))
#if 0
# define PNMFILE FILE
# define fread_PNMFILE(s,slen,f) fread(s, 1, slen, f)
#else
# define PNMFILE /*Filter::UngetFILED*/GenBuffer::Readable
# define fread_PNMFILE(s,slen,f) f->vi_read(s, slen)
#endif
/* Declare local data types
*/
typedef struct _PNMScanner
{
PNMFILE *fd; /* The file descriptor of the file being read */
char cur; /* The current character in the input stream */
int eof; /* Have we reached end of file? */
} PNMScanner;
typedef struct _PNMInfo
{
unsigned int xres, yres; /* The size of the image */
int maxval; /* For ascii image files, the max value
* which we need to normalize to */
int np; /* Number of image planes (0 for pbm) */
int asciibody; /* 1 if ascii body, 0 if raw body */
/* Routine to use to load the pnm body */
void (* loader) (PNMScanner *, struct _PNMInfo *, unsigned char *);
} PNMInfo;
/* Contains the information needed to write out PNM rows */
typedef struct _PNMRowInfo
{
PNMFILE *fd; /* File descriptor */
char *rowbuf; /* Buffer for writing out rows */
int xres; /* X resolution */
int np; /* Number of planes */
unsigned char *red; /* Colormap red */
unsigned char *grn; /* Colormap green */
unsigned char *blu; /* Colormap blue */
} PNMRowInfo;
/* Save info */
typedef struct
{
int raw; /* raw or ascii */
} PNMSaveVals;
typedef struct
{
int run; /* run */
} PNMSaveInterface;
#define PNM_BUFLEN 512 /* The input buffer size for data returned
* from the scanner. Note that lines
* aren't allowed to be over 256 characters
* by the spec anyways so this shouldn't
* be an issue. */
#define SAVE_COMMENT_STRING "# CREATOR: The GIMP's PNM Filter Version 1.0\n"
/* Declare some local functions.
*/
static void pnm_load_ascii (PNMScanner *scan,
PNMInfo *info,
unsigned char *pixel_rgn);
static void pnm_load_raw (PNMScanner *scan,
PNMInfo *info,
unsigned char *pixel_rgn);
static void pnm_load_rawpbm (PNMScanner *scan,
PNMInfo *info,
unsigned char *pixel_rgn);
static void pnmscanner_destroy (PNMScanner *s);
#if 0
static void pnmscanner_createbuffer (PNMScanner *s,
unsigned int bufsize);
static void pnmscanner_getchar (PNMScanner *s);
static void pnmscanner_getsmalltoken (PNMScanner *s, unsigned char *buf);
#endif
static void pnmscanner_eatwhitespace (PNMScanner *s);
static void pnmscanner_gettoken (PNMScanner *s,
unsigned char *buf,
unsigned int bufsize);
static unsigned pnmscanner_getint(PNMScanner *s);
static PNMScanner * pnmscanner_create (PNMFILE *fd);
#define pnmscanner_eof(s) ((s)->eof)
#define pnmscanner_fd(s) ((s)->fd)
#if 0
/* pnmscanner_getchar ---
* Reads a character from the input stream
*/
static void
pnmscanner_getchar (PNMScanner *s)
{
if (s->inbuf)
{
s->cur = s->inbuf[s->inbufpos++];
if (s->inbufpos >= s->inbufvalidsize)
{
if (s->inbufsize > s->inbufvalidsize)
s->eof = 1;
else
s->inbufvalidsize = fread(s->inbuf, 1, s->inbufsize, s->fd);
s->inbufpos = 0;
}
}
else
s->eof = !fread(&(s->cur), 1, 1, s->fd);
}
#endif
#define pnmscanner_getchar(s) do { s->eof = !fread_PNMFILE(&(s->cur), 1, s->fd); } while(0)
/* pnmscanner_eatwhitespace ---
* Eats up whitespace from the input and returns when done or eof.
* Also deals with comments.
*/
static inline void pnmscanner_eatwhitespace(PNMScanner *s) { /**** pts ****/
while (1) {
if (s->cur=='#') {
do pnmscanner_getchar(s); while (s->cur!='\n');
} else if (!isspace(s->cur)) {
break;
}
pnmscanner_getchar(s);
}
}
static struct struct_pnm_types
{
char name;
int np;
int asciibody;
int maxval;
void (* loader) (PNMScanner *, struct _PNMInfo *, unsigned char *pixel_rgn);
} pnm_types[] =
{
{ '1', 0, 1, 1, pnm_load_ascii }, /* ASCII PBM */
{ '2', 1, 1, 255, pnm_load_ascii }, /* ASCII PGM */
{ '3', 3, 1, 255, pnm_load_ascii }, /* ASCII PPM */
{ '4', 0, 0, 1, pnm_load_rawpbm }, /* RAW PBM */
{ '5', 1, 0, 255, pnm_load_raw }, /* RAW PGM */
{ '6', 3, 0, 255, pnm_load_raw }, /* RAW PPM */
{ 0 , 0, 0, 0, NULL}
};
#if PTS_SAM2P
bitmap_type pnm_load_image (PNMFILE* filename)
#else
bitmap_type pnm_load_image (at_string filename)
#endif
{
char buf[PNM_BUFLEN]; /* buffer for random things like scanning */
PNMInfo *pnminfo;
PNMScanner * volatile scan;
int ctr;
PNMFILE* fd;
bitmap_type bitmap;
#if PTS_SAM2P /**** pts ****/
fd=filename;
#else
/* open the file */
fd = xfopen (filename, "rb");
if (fd == NULL)
{
FATAL("PNM: can't open file\n");
BITMAP_BITS (bitmap) = NULL;
BITMAP_WIDTH (bitmap) = 0;
BITMAP_HEIGHT (bitmap) = 0;
BITMAP_PLANES (bitmap) = 0;
return (bitmap);
}
#endif
/* allocate the necessary structures */
/* pnminfo = (PNMInfo *) malloc (sizeof (PNMInfo)); */
XMALLOCT(pnminfo, PNMInfo*, sizeof(PNMInfo));
scan = NULL;
/* set error handling */
scan = pnmscanner_create(fd);
/* Get magic number */
pnmscanner_gettoken (scan, (unsigned char *)buf, PNM_BUFLEN);
if (pnmscanner_eof(scan))
FATALP ("PNM: premature end of file");
if (buf[0] != 'P' || buf[2])
FATALP ("PNM: is not a valid file");
/* Look up magic number to see what type of PNM this is */
for (ctr=0; pnm_types[ctr].name; ctr++)
if (buf[1] == pnm_types[ctr].name)
{
pnminfo->np = pnm_types[ctr].np;
pnminfo->asciibody = pnm_types[ctr].asciibody;
pnminfo->maxval = pnm_types[ctr].maxval;
pnminfo->loader = pnm_types[ctr].loader;
}
if (!pnminfo->loader)
FATALP ("PNM: file not in a supported format");
pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN);
if (pnmscanner_eof(scan))
FATALP ("PNM: premature end of file");
pnminfo->xres = isdigit(*buf)?atoi(buf):0;
if (pnminfo->xres<=0)
FATALP ("PNM: invalid xres while loading");
pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN);
if (pnmscanner_eof(scan))
FATALP ("PNM: premature end of file");
pnminfo->yres = isdigit(*buf)?atoi(buf):0;
if (pnminfo->yres<=0)
FATALP ("PNM: invalid yres while loading");
if (pnminfo->np != 0) /* pbm's don't have a maxval field */
{
pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN);
if (pnmscanner_eof(scan))
FATALP ("PNM: premature end of file");
pnminfo->maxval = isdigit(*buf)?atoi(buf):0;
if ((pnminfo->maxval<=0)
|| (pnminfo->maxval>255 && !pnminfo->asciibody))
FATALP ("PNM: invalid maxval while loading");
}
BITMAP_WIDTH (bitmap) = (at_dimen_t) pnminfo->xres;
BITMAP_HEIGHT (bitmap) = (at_dimen_t) pnminfo->yres;
BITMAP_PLANES (bitmap) = (pnminfo->np)?(pnminfo->np):1;
/* BITMAP_BITS (bitmap) = (unsigned char *) malloc (pnminfo->yres * pnminfo->xres * BITMAP_PLANES (bitmap)); */
XMALLOCT(BITMAP_BITS (bitmap), unsigned char *, pnminfo->yres * pnminfo->xres * BITMAP_PLANES (bitmap));
pnminfo->loader (scan, pnminfo, BITMAP_BITS (bitmap));
/* vvv Dat: We detect truncation late truncated files will just have garbage :-( */
if (pnmscanner_eof(scan))
FATALP ("PNM: truncated image data");
/* Destroy the scanner */
pnmscanner_destroy (scan);
/* free the structures */
/* free (pnminfo); */
XFREE(pnminfo);
/* close the file */
/* xfclose (fd, filename); */
return (bitmap);
}
static void
pnm_load_ascii (PNMScanner *scan,
PNMInfo *info,
unsigned char *data)
{
register unsigned char *d, *dend;
unsigned u, s;
#if 0 /**** pts ****/
/* Buffer reads to increase performance */
/* !! convert(1) is faster -- maybe buffering helps? */
pnmscanner_createbuffer(scan, 4096);
#endif
d = data;
if (info->np==0) { /* PBM */
dend=d+info->xres*info->yres;
while (d!=dend) {
/* pnmscanner_getsmalltoken(scan, (unsigned char *)buf); */
pnmscanner_eatwhitespace(scan);
*d++=-(scan->cur=='0');
pnmscanner_getchar(scan);
}
} else { /* PGM or PPM */ /**** pts ****/
dend=d+info->xres*info->yres*info->np;
switch (s=info->maxval) {
case 255:
while (d!=dend) {
*d++=pnmscanner_getint(scan); /* Dat: removed isdigit() */
}
break;
case 15:
while (d!=dend) {
*d++ = pnmscanner_getint(scan)*17;
}
break;
case 3:
while (d!=dend) {
*d++ = pnmscanner_getint(scan)*85;
}
break;
case 0: /* avoid division by 0 */
case 1:
while (d!=dend) {
*d++ = -(0==pnmscanner_getint(scan)); /* (*buf=='0')?0xff:0x00; */
}
default:
while (d!=dend) {
u=pnmscanner_getint(scan);
*d++ = (0UL+u*255UL+(s>>1))/s; /* always <= 255 */
}
}
}
}
static void
pnm_load_raw (PNMScanner *scan,
PNMInfo *info,
unsigned char *data)
{
unsigned char *d, *dend;
unsigned s=info->maxval;
slen_t delta, scanlines;
PNMFILE *fd=pnmscanner_fd(scan);
scanlines = info->yres;
d = data;
delta=info->xres * info->np;
dend=d+delta*scanlines;
while (d!=dend) {
if (info->xres*info->np != fread_PNMFILE((char*)d, delta, fd)) return;
d+=delta;
}
d=data;
switch (s=info->maxval) { /**** pts ****/
case 1: case 0:
for (; d!=dend; d++) if (*d!=0) *d=255;
break;
case 3:
while (d!=dend) *d++*=85;
break;
case 15:
while (d!=dend) *d++*=17;
break;
case 255:
break;
default:
for (; d!=dend; d++) *d=(0UL+*d*255UL+(s>>1))/s; /* always <= 255 */
break;
}
}
static void
pnm_load_rawpbm (PNMScanner *scan,
PNMInfo *info,
unsigned char *data)
{
unsigned char *buf;
unsigned char curbyte;
unsigned char *d;
unsigned int x, i;
unsigned int start, end, scanlines;
PNMFILE *fd;
unsigned int rowlen, bufpos;
fd = pnmscanner_fd(scan);
/****pts****/ /* rowlen = (unsigned int)ceil((double)(info->xres)/8.0);*/
rowlen=(info->xres+7)>>3;
/* buf = (unsigned char *)malloc(rowlen*sizeof(unsigned char)); */
XMALLOCT(buf, unsigned char*, rowlen*sizeof(unsigned char));
start = 0;
end = info->yres;
scanlines = end - start;
d = data;
for (i = 0; i < scanlines; i++)
{
if (rowlen != fread_PNMFILE((char*)buf, rowlen, fd))
FATALP ("PNM: error reading file");
bufpos = 0;
curbyte = buf[0];
for (x = 0; x < info->xres; x++)
{
if ((x % 8) == 0) {
curbyte = buf[bufpos++];
/* // if (curbyte!=0) printf("%d <%u>\n", x, curbyte); */
}
/* // if (curbyte!=0) printf("[%u]\n", curbyte); */
d[x] = (curbyte&0x80) ? 0x00 : 0xff;
curbyte <<= 1;
}
d += info->xres;
}
XFREE(buf);
}
/**************** FILE SCANNER UTILITIES **************/
/* pnmscanner_create ---
* Creates a new scanner based on a file descriptor. The
* look ahead buffer is one character initially.
*/
static PNMScanner *
pnmscanner_create (PNMFILE *fd)
{
PNMScanner *s;
XMALLOCT (s, PNMScanner*, sizeof(PNMScanner));
s->fd = fd;
s->eof = !fread_PNMFILE(&(s->cur), 1, s->fd);
return s;
}
/* pnmscanner_destroy ---
* Destroys a scanner and its resources. Doesn't close the fd.
*/
static void
pnmscanner_destroy (PNMScanner *s)
{
XFREE(s);
}
#if 0 /**** pts ****/
/* pnmscanner_createbuffer ---
* Creates a buffer so we can do buffered reads.
*/
static void
pnmscanner_createbuffer (PNMScanner *s,
unsigned int bufsize)
{
/* s->inbuf = (char *)malloc(sizeof(char)*bufsize); */
XMALLOCT(s->inbuf, char*, sizeof(char)*bufsize);
s->inbufsize = bufsize;
s->inbufpos = 0;
s->inbufvalidsize = fread(s->inbuf, 1, bufsize, s->fd);
}
#endif
/* pnmscanner_gettoken ---
* Gets the next token, eating any leading whitespace.
*/
static void pnmscanner_gettoken (PNMScanner *s,
unsigned char *buf,
unsigned int bufsize)
{
unsigned char *bufend=buf+bufsize-1;
pnmscanner_eatwhitespace(s);
while (!pnmscanner_eof(s) && !isspace(s->cur) && s->cur!='#') {
if (buf!=bufend) *buf++=s->cur;
pnmscanner_getchar(s);
}
*buf='\0';
}
static unsigned pnmscanner_getint(PNMScanner *s) {
unsigned ret=0;
pnmscanner_eatwhitespace(s);
while (!pnmscanner_eof(s)) {
if (isdigit(s->cur)) ret=10*ret+s->cur-'0';
else if (isspace(s->cur) || s->cur=='#') break;
pnmscanner_getchar(s);
}
return ret;
}