[comp.sources.unix] v14i086: Sun rasterfile I/O library

rsalz@uunet.uu.net (Rich Salz) (05/10/88)

Submitted-by: Marc Majka <majka@grads.cs.ubc.cdn>
Posting-number: Volume 14, Issue 86
Archive-name: rast

Here is a set of library subroutines which I have found useful for reading
and writing SUN rasterfiles.  The package contains all the usual stuff:
README, Makefile, sources, manuals, and a couple of sample programs.

I have tested and debugged the library to some extent, but local hardware
limitations precluded my from giving them a vigorous workout.  There may
be bugs.  If you find or fix any, please let me know!

---
Marc Majka

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	rast.c
#	rast.h
#	rast.3
#	rdemo.c
#	rsee.c
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
This package contains a library of routines for reading and writing
SUN rasterfiles in a pixel-at-a-time manner.  The routines maintain
a data structure called a RASTER, which contains all the usual file
header information from a rasterfile, along with a file pointer and
a memory cache of a single line (row) of the rasterfile.

Bit stuffing and unpacking is accomplished with pixrect operations,
which are about as fast as possible.

Manifest:

rast.h   - header file to include
rast.c   - library source
rast.3   - manual entry
rdemo.c  - demo program
rsee.c   - program to print rasterfile headers
Makefile - to compile it all

Note that, since the library pixrect operations, you must include
-lpixrect in any programs which use the library.

---
Marc Majka  -  UBC Laboratory for Computational Vision

<majka@vision.ubc.cdn>
<majka@ubc.bitnet>
<majka@ubc-vision.uucp>

SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
all: rast.o rdemo rsee

rast.o: rast.c rast.h
	cc -c rast.c

rdemo: rdemo.c rast.o
	cc -o rdemo rdemo.c rast.o -lpixrect

rsee: rsee.c rast.o
	cc -o rsee rsee.c rast.o -lpixrect
SHAR_EOF
fi # end of overwriting check
if test -f 'rast.c'
then
	echo shar: will not over-write existing file "'rast.c'"
else
cat << \SHAR_EOF > 'rast.c'
#include "rast.h"

RASTER *Ropen(f,m)
char *f;
int m;
{
	RASTER *im, *Ropen_read(), *Ropen_write();

	im = NULL;
	if (m == R) im = Ropen_read(f);
	if (m == W) im = Ropen_write(f);
	return(im);
}

RASTER *Rdopen(f,m)
int f,m;
{
	RASTER *im, *Rdopen_stdin(), *Rdopen_stdout();

	im = NULL;
	if (f == 0 && m == R) im = Rdopen_stdin();
	if (f == 1 && m == W) im = Rdopen_stdout();
	return(im);
}

RASTER *Ropen_read(f)
char *f;
{
	RASTER *im;
	char *malloc();

	im = (RASTER *)malloc(sizeof(RASTER));
	im->fp = fopen(f,"r");
	im->mode = R;
	if (im->fp == NULL) {
		fprintf(stderr,"read open fails for file \"%s\"!\n",f);
		im = NULL;
		return(im);
	}
	return(im);
}

RASTER *Rdopen_stdin()
{
	RASTER *im;
	char *malloc();

	im = (RASTER *)malloc(sizeof(RASTER));
	im->fp = stdin;
	im->mode = R;
	if (im->fp == NULL) {
		fprintf(stderr,"read open fails for stdin!\n");
		im = NULL;
		return(im);
	}
	return(im);
}

RASTER *Ropen_write(f)
char *f;
{
	RASTER *im;
	char *malloc();

	im = (RASTER *)malloc(sizeof(RASTER));
	im->fp = fopen(f,"w");
	if (im->fp == NULL) {
		fprintf(stderr,"write open fails for file \"%s\"!\n",f);
		im = NULL;
		return(im);
	}

	im->mode      = W;
	im->width     = 0;
	im->height    = 0;
	im->depth     = 0;
	im->maplength = 0;
	im->maptype   = RMT_NONE;

	if (im->fp == NULL) im = NULL;
	return(im);
}

RASTER *Rdopen_stdout()
{
	RASTER *im;
	char *malloc();

	im = (RASTER *)malloc(sizeof(RASTER));
	im->fp = stdout;
	if (im->fp == NULL) {
		fprintf(stderr,"write open fails for stdout!\n");
		im = NULL;
		return(im);
	}

	im->mode      = W;
	im->width     = 0;
	im->height    = 0;
	im->depth     = 0;
	im->maplength = 0;
	im->maptype   = RMT_NONE;

	return(im);
}

Rgetheader(im)
RASTER *im;
{
	char *malloc();
	int i, rc, ll, extra;
	struct rasterfile rhead;

	rc = fread(&rhead,(sizeof (struct rasterfile)),1,im->fp);
	
	if (rc <= 0) {
		fprintf(stderr,"Rgetheader failed!\n");
		return(0);
	}
	if (rhead.ras_magic != RAS_MAGIC) return(0);

	im->height    = rhead.ras_height;
	im->width     = rhead.ras_width;
	im->depth     = rhead.ras_depth;
	im->length    = rhead.ras_length;
	im->type      = rhead.ras_type;
	im->maptype   = rhead.ras_maptype;
	im->maplength = rhead.ras_maplength;

	extra = (16 - ((im->width * im->depth) % 16)) % 16;
	ll = ((im->width * im->depth) + extra) / 8;

	im->linelen   = ll;
	im->cache     = malloc(ll);
	im->c_pixel   = im->width + 1;
	im->c_pr      = mem_point(im->width,1,im->depth,im->cache);
	if (im->maplength > 0) {
		im->map = malloc(im->maplength);
		for (i = 0; i < im->maplength; i++) im->map[i] = getc(im->fp);
	}
	return(1);
}

Rputheader(im)
RASTER *im;
{
	char *malloc();
	int i, rc, ll, extra;
	struct rasterfile rhead;

	extra = (16 - ((im->width * im->depth) % 16)) % 16;
	ll = ((im->width * im->depth) + extra) / 8;

	if (im->width == 0) {
		fprintf(stderr,"rasterfile width is zero!\n");
		return(0);
	}
	if (im->height == 0) {
		fprintf(stderr,"rasterfile height is zero!\n");
		return(0);
	}
	if (im->depth == 0) {
		fprintf(stderr,"rasterfile depth is zero!\n");
		return(0);
	}

	rhead.ras_magic     = RAS_MAGIC;
	rhead.ras_width     = im->width;
	rhead.ras_height    = im->height;
	rhead.ras_depth     = im->depth;
	rhead.ras_length    = ll * im->height;
	rhead.ras_type      = RT_STANDARD;
	rhead.ras_maptype   = im->maptype;
	rhead.ras_maplength = im->maplength;

	im->linelen   = ll;
	im->cache     = malloc(ll);
	im->c_pixel   = 0;
	im->c_pr      = mem_point(im->width,1,im->depth,im->cache);

	rc = fwrite(&rhead,(sizeof (struct rasterfile)),1,im->fp);
	
	if (rc <= 0) {
		fprintf(stderr,"Rputheader failed!\n");
		return(0);
	}

	if (im->maptype != RMT_NONE && im->maplength > 0)
		fwrite(im->map,im->maplength,1,im->fp);
	return(1);
}

Rgetpix(im)
RASTER *im;
{
	int pix;
	
	if (im->mode != R) {
		fprintf(stderr,"attempt to read rasterfile opened for write!\n");
		return(-1);
	}

	if (im->c_pixel >= im->width) {
		fread(im->cache,im->linelen,1,im->fp);
		im->c_pixel = 0;
	}

	pix = pr_get(im->c_pr,im->c_pixel,0);
	im->c_pixel++;
	return(pix);
}

Rputpix(im,pix)
RASTER *im;
int pix;
{
	if (im->mode != W) {
		fprintf(stderr,"attempt to write rasterfile opened for read!\n");
		return(-1);
	}

	pr_put(im->c_pr,im->c_pixel,0,pix);
	im->c_pixel++;
	
	if (im->c_pixel >= im->width) {
		fwrite(im->cache,im->linelen,1,im->fp);
		im->c_pixel = 0;
	}
}

Rclose(im)
RASTER *im;
{
	if (im->mode == W) fflush(im->fp);
	fclose(im->fp);
}

Rinfo(im)
RASTER *im;
{
	fprintf(stderr,"  height    = %d\n",im->height);
	fprintf(stderr,"  width     = %d\n",im->width);
	fprintf(stderr,"  depth     = %d\n",im->depth);
	fprintf(stderr,"  length    = %d\n",im->length);
	fprintf(stderr,"  type      = %d\n",im->type);
	fprintf(stderr,"  maptype   = %d\n",im->maptype);
	fprintf(stderr,"  maplength = %d\n",im->maplength);
}

Rinitmap(im,t,l)
RASTER *im;
int t,l;
{
	char *malloc();

	im->maptype   = t;
	im->maplength = l;
	im->map       = malloc(l);
}

Rputmap(im,i,r,g,b)
RASTER *im;
int i,r,g,b;
{
	int x;
	
	switch (im->maptype) {
		case RMT_NONE: 
			fprintf(stderr,"can't put in a null map!\n");
			break;
		case RMT_RAW:
			im->map[i] = r;
			break;
		case RMT_EQUAL_RGB:
			x = i * 3;
			im->map[x++] = r;
			im->map[x++] = g;
			im->map[x]   = b;
			break;
		default:
			fprintf(stderr,"unknown map type in rasterfile!\n");
	}
}

Rgetmap(im,i,r,g,b)
RASTER *im;
int i,*r,*g,*b;
{
	int x;
	
	switch (im->maptype) {
		case RMT_NONE: 
			fprintf(stderr,"can't get from a null map!\n");
			break;
		case RMT_RAW:
			*r = im->map[i];
			break;
		case RMT_EQUAL_RGB:
			x = i * 3;
			*r = im->map[x++];
			*g = im->map[x++];
			*b = im->map[x];
			break;
		default:
			fprintf(stderr,"unknown map type in rasterfile!\n");
	}
}

Rgetmappedpix(im,r,g,b)
RASTER *im;
int *r,*g,*b;
{
	int pix, x;

	if (im->mode != R) {
		fprintf(stderr,"attempt to read rasterfile opened for write!\n");
		return(-1);
	}
	
	if (im->c_pixel >= im->width) {
		fread(im->cache,im->linelen,1,im->fp);
		im->c_pixel = 0;
	}

	pix = pr_get(im->c_pr,im->c_pixel,0);
	im->c_pixel++;
	
	switch (im->maptype) {
		case RMT_RAW: 
			*r = im->map[pix];
			break;
		case RMT_EQUAL_RGB:
			x = pix * 3;
			*r = im->map[x++];
			*g = im->map[x++];
			*b = im->map[x];
			break;
		default:
			*r = pix;
			break;
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'rast.h'
then
	echo shar: will not over-write existing file "'rast.h'"
else
cat << \SHAR_EOF > 'rast.h'
#include <stdio.h>
#include <pixrect/pixrect_hs.h>

#define R 0
#define W 1

typedef struct RASTER_STRUCT {
	int height;				/* number of rows							*/
	int width;				/* number of columns						*/
	int depth;				/* bits per pixel							*/
	int length;				/* bytes of data following header			*/
	int type;				/* rasterfile type							*/
	int maptype;			/* type of lookup table						*/
	int maplength;			/* length of lookup table					*/
	char *map;				/* lookup table								*/
	int linelen;			/* bytes in a row (padded to even 16 bits)	*/
	FILE *fp;				/* file pointer								*/
	int mode;				/* read (R) or write (W) mode flag			*/
	char *cache;			/* cache of 1 row							*/
	int c_pixel;			/* current pixel in row cache				*/
	struct pixrect *c_pr;	/* make cache look like a pixrect			*/
} RASTER;
SHAR_EOF
fi # end of overwriting check
if test -f 'rast.3'
then
	echo shar: will not over-write existing file "'rast.3'"
else
cat << \SHAR_EOF > 'rast.3'
.TH RAST 3
.SH NAME
rast - SUN rasterfile interface
.SH SYNOPSIS
.B #include \*(lqrast.h\*(rq
.sp
.B RASTER *Ropen(filename,mode)
.br
.B char *filename;
.br
.B int mode;
.sp
.B RASTER Rdopen(filedes,mode)
.br
.B int filedes, mode;
.sp
.B Rgetheader(raster)
.br
.B RASTER *raster;
.sp
.B Rputheader(raster)
.br
.B RASTER *raster;
.sp
.B Rgetpix(raster)
.br
.B RASTER *raster;
.sp
.B Rputpix(raster,pixel)
.br
.B RASTER *raster;
.br
.B int pixel;
.sp
.B Rgetmappedpix(raster,value)
.br
.B RASTER *raster;
.br
.B int *value;
.sp
.B Rgetmappedpix(raster,red,green,blue)
.br
.B RASTER *raster;
.br
.B int *red, *green, *blue;
.sp
.B Rinitmap(raster,type,length)
.br
.B RASTER *raster;
.br
.B int type, length;
.sp
.B Rputmap(raster,index,value)
.br
.B RASTER *raster;
.br
.B int index, value;
.sp
.B Rputmap(raster,index,red,green,blue)
.br
.B RASTER *raster;
.br
.B int index, red, green, blue;
.sp
.B Rgetmap(raster,index,value)
.br
.B RASTER *raster;
.br
.B int index, *value;
.sp
.B Rgetmap(raster,index,red,green,blue)
.br
.B RASTER *raster;
.br
.B int index, *red, *green, *blue;
.sp
.B Rclose(raster)
.br
.B RASTER *raster;
.sp
.SH DESCRIPTION
This library provides pixel I/O for SUN rasterfiles. 
.sp
.I Ropen
opens a SUN rasterfile for reading or writing, and returns a RASTER
structure.  The mode should be R for read, W for write.  R and W are
defined in the file rast.h.
.I Rdopen
opens a stream, specified by a UNIX file descriptor.  The RASTER structure
is defined below.
.sp
After a rasterfile has been opened to read, data from the rasterfile
header is fetched and placed in the RASTER structure with a call to
.I Rgetheader.
A rasterfile opened for output must have (at least) it's width, height, and
depth set before the header may be written with
.I Rputheader.
If the header is to include a map, the map must be initialized with a call to 
.I Rinitmap.
Map values may be set directly in the RASTER structure, or they my be set
using 
.I Rputmap.
The map type must be one defined in the include file <rasterfile.h>.
.sp
For convenience, map values may be retrieved with
.I Rgetmap.
.sp
Each call to
.I Rgetpix
will return a raw pixel value from the rasterfile. 
.I Rgetmappedpix
returns the result of putting the raw pixel value through the rasterfile map.
.I Rputpix
writes pixel values to the rasterfile.
.sp
A rasterfile is closed with 
.I Rclose.
.SH "RASTER STRUCTURE"
The data structure maintained by these routines is defined in the file rast.h.
.sp
.nf
typedef struct RASTER_STRUCT {
    int height;             /* number of rows                           */
    int width;              /* number of columns                        */
    int depth;              /* bits per pixel                           */
    int length;             /* bytes of data following header           */
    int type;               /* rasterfile type                          */
    int maptype;            /* type of lookup table                     */
    int maplength;          /* length of lookup table                   */
    char *map;              /* lookup table                             */
    int linelen;            /* bytes in a row (padded to even 16 bits)  */
    FILE *fp;               /* file pointer                             */
    int mode;               /* read (R) or write (W) mode flag          */
    char *cache;            /* cache of 1 row                           */
    int c_pixel;            /* current pixel in row cache               */
    struct pixrect *c_pr;   /* make cache look like a pixrect           */
} RASTER;
.fi
.sp
The height, width, depth, length, type, maptype, and maplength fields
have exactly the same meaning as they do in rasterfiles.  The map field
is used to store the rasterfile map data.
.sp
The linelen field gives the number of bytes in a single line (row)
in the rasterfile.  This is different from the width because each line
is padded with zero bits to make it an integer multiple of 16 bits in length.
.sp
The UNIX file pointer is kept in the fp field, and a read/write flag is 
kept in the mode field.
.sp
Pixels are stored in the cache field, which holds one line (row) of the
raster. The cache is maintained by 
.I Rgetpix
and
.I Rputpix.
The current pixel (column) within the cache is recorded by in the c_pixel
field.  The cache is efficiently accessed by turning it into a memory pixrect
and using 
.I pr_get
and
.I pr_put
to do the necessary bit stuffing.  The memory pixrect is kept in the c_pr
field.  Because of this, programs using this library must be compiled with
-lpixrect.
.SH AUTHOR
Marc Majka
SHAR_EOF
fi # end of overwriting check
if test -f 'rdemo.c'
then
	echo shar: will not over-write existing file "'rdemo.c'"
else
cat << \SHAR_EOF > 'rdemo.c'
/***********************************************************************

   rdemo - reads a rasterfile, computes the average of the colour
   values (if applicable) of the pixels, and writes a new rasterfile
   with the average.  An inverting colour map is added.

***********************************************************************/

#include <stdio.h>
#include "rast.h"

main(argc,argv)
int argc;
char *argv[];
{
	int r, g, b, avg, row, col, i, pix, ifile, ofile;
	RASTER *irast, *orast, *Ropen(), *Rdopen();

	ifile = 0;
	ofile = 0;

	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch (argv[i][1]) {
				case 'i': ifile = ++i; break;
				case 'o': ofile = ++i; break;
				default:  usage();
			}
		}
	}

	if (ifile) irast = Ropen(argv[ifile],R);
	else irast = Rdopen(0,R);
	if (irast == NULL) {
		fprintf(stderr,"rdemo: input rasterfile open failed!\n");
		exit(1);
	}

	if (ofile) orast = Ropen(argv[ofile],W);
	else orast = Rdopen(1,W);
	if (irast == NULL) {
		fprintf(stderr,"rdemo: output rasterfile open failed!\n");
		exit(1);
	}

	if (!Rgetheader(irast)) {
		fprintf(stderr,"input file is not a raster file!\n");
		exit(1);
	}

	orast->height = irast->height;
	orast->width  = irast->width;
	orast->depth  = irast->depth;

	Rinitmap(orast,RMT_EQUAL_RGB,256*3);
	for (i = 0; i < 256; i++) Rputmap(orast,255-i,i,i,i);
	
	Rputheader(orast);

	if (irast->maptype == RMT_EQUAL_RGB) {
		for (row = 0; row < irast->height; row++) 
			for (col = 0; col < irast->width; col++) {
				Rgetmappedpix(irast,&r,&g,&b);
				avg = (r + g + b) / 3;
				Rputpix(orast,avg);
			}
	}
	else if (irast->maptype == RMT_RAW) {
		for (row = 0; row < irast->height; row++) 
			for (col = 0; col < irast->width; col++) {
				Rgetmappedpix(irast,&pix);
				Rputpix(orast,pix);
			}
	}
	else if (irast->maptype == RMT_NONE) {
		for (row = 0; row < irast->height; row++) 
			for (col = 0; col < irast->width; col++) {
				pix = Rgetpix(irast);
				Rputpix(orast,pix);
			}
	}
	else {
		fprintf(stderr,"rdemo: unknown map type in input rasterfile!\n");
		exit(1);
	}
	
	Rclose(irast);
	Rclose(orast);
}

usage()
{
	fprintf(stderr,"usage: rdemo [-i file] [-o file]\n");
	fprintf(stderr,"       -i  input rasterfile [default is stdin]\n");
	fprintf(stderr,"       -o  output rasterfile [default is stdout]\n");
	exit(0);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'rsee.c'
then
	echo shar: will not over-write existing file "'rsee.c'"
else
cat << \SHAR_EOF > 'rsee.c'
#include <stdio.h>
#include "rast.h"

main(argc,argv)
int argc;
char *argv[];
{
	RASTER *irast, *Ropen();
	int i, rc;

	for (i = 1; i < argc; i++) {
		irast = Ropen(argv[i],R);
		if (irast == NULL)
			fprintf(stderr,"rasterfile open failed for \"%s\"!\n",argv[i]);
		else {
			rc = Rgetheader(irast);
			fprintf(stderr,"%s:\n",argv[i]);
			if (rc) Rinfo(irast);
			else fprintf(stderr,"not a rasterfile!\n");
			fprintf(stderr,"\n");
			Rclose(irast);
		}
	}
	exit(0);
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0


-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.