[comp.graphics] sunraster library for filtered image zoom

ph@miro.Berkeley.EDU (Paul Heckbert) (08/23/89)

# to unpack, cut here and run the following shell archive through sh
# contents: README2 libpic/pic.3 libpic/Makefile libpic/ras.c
# libpic/ras.h libpic/ras_pic.c libpic/pic_all.c libpic/pic_file.c
#
mkdir libpic
echo extracting README2
sed 's/^X//' <<'EOF23218' >README2
XSeveral weeks ago I posted a filtered image zoom program and
Xa library for device-independent picture I/O.
XSeveral people have asked me to post the device library for Sunraster format,
Xso here it is, in ras.c, ras.h, and ras_pic.c, along with the library
Xfiles that need to be changed to add this format to the package: Makefile,
Xpic_file.c, and pic_all.c.  Add this to the source code from my August 11
Xposting and you'll be able to read and write Sunraster format with my zoom
Xprogram.
X
XPlease email me any enhancements, such as new device libraries for libpic;
XI'll collect and redistribute.
X
XPaul Heckbert	22 Aug 1989
XUC Berkeley
Xph@miro.berkeley.edu
EOF23218
echo extracting libpic/pic.3
sed 's/^X//' <<'EOF23219' >libpic/pic.3
X.\" $Header$
X.\" a few macros
X.de Cs		\" code start
X.DS
X.ft B
X.ta 9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n
X..
X.de Ce		\" code end
X.ft R
X.DE
X..
X.de Ss		\" subroutine definition start
X.nf
X.ft B
X.ta 9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n
X..
X.de Sd		\" subroutine documentation
X.ft R
X.fi
X.in +4n
X..
X.de Se		\" subroutine definition&documentation end
X.in -4n
X..
X.de DS
X.nf
X.in +4n
X.sp .5v
X..
X.de DE
X.sp .5v
X.in -4n
X.fi
X..
X.TH PIC 3  "10 August 1989"
X.SH NAME
Xpic \- subroutines for device-independent picture I/O
X.SH OVERVIEW
X\fIpic\fP is a package of subroutines for device-independent frame buffer I/O
Xand format-independent picture file I/O.
XIt is designed to provide a standard subroutine
Xinterface for computer graphics and
Ximage processing applications that manipulate raster images
Xof 8, 24, or 32-bits per pixel.
XApplication programs can be more portable if they
Xdo all their raster graphics through this layer of routines
Xrather than making device or format-dependent calls.
XDevice or file format selection can be made at either compile-time or run-time.
X.PP
XTo use the package, an application program first calls \fIpic_open\fP,
Xwhich opens a picture "device" for reading or writing
Xand returns a pointer that is passed in all subsequent
X\fIpic\fP calls.
XSeveral pictures can be open simultaneously so that one could, for example,
Xbe reading from two picture devices (or files) of different types,
Xand writing to third device simultaneously.
XFollowing the \fIpic_open\fP
Xcall but before pixel I/O the program typically reads or
Xwrites various parameters of the picture.
XThe parameters supported by the \fIpic\fP library at present are:
X.TP .5i
X\fInchan\fP: \fBint nchan;\fP
XNumber of channels in the picture: 1, 3, or 4.
X1 channel means intensity, perhaps color mapped,
X3 channels means RGB, and
X4 channels means RGBA, where A=alpha=opacity.
X.TP .5i
X\fIbox\fP: \fBint xorig, yorig, xsize, ysize;\fP
Xorigin and size of picture, where \fI(xorig,yorig)\fP is the upper left corner
Xof the box and \fI(xsize,ysize)\fP is its size.
X.PP
XAfter these parameters are read or written by the application,
Xit typically reads or writes pixels.
XPixels can be accessed individually or on a scanline basis.
XAll devices should support top-down scanline access,
Xand some may support random access to pixels and scanlines as well.
XThere is one set of routines for reading and writing 1-channel pictures
Xand another set for reading and writing 3 or 4-channel pictures.
XThree-channel pictures ignore (skip over) the \fIa\fP channel.
XIf a picture was opened for reading then only the parameter "get" routines
Xand pixel read routines should be called;
Xif a picture was opened for writing then most device libraries will
Xallow any of the parameter "set" or "get"
Xor pixel read or write routines to be called.
X.PP
XWhen the application is done with a picture, it should call \fIpic_close\fP,
Xwhich does device-dependent cleanup, perhaps flushing output buffers and
Xclosing the device or file stream.
X.PP
XConventions:
XPicture coordinates have the origin at the upper left, with x pointing right
Xand y pointing down.
XThe package currently has no notion of pixel aspect ratio,
Xgamma correction, or color correction.
XIt is an error to set any parameters after pixel writing has
Xcommenced on a picture opened for writing.
XAll pixel coordinates are in the same coordinate system (not window-relative).
XChannels are 8 bits per pixel, with 0=dark and 255=bright.
XThe pixel datatypes are:
X.Cs
Xtypedef unsigned char Pixel1;   /* 1-channel picture */
Xtypedef struct {Pixel1 r, g, b, a;} Pixel1_rgba;   /* 3 or 4-channel picture */
X.Ce
X.SH SUBROUTINES
XThe application should think of a picture as an abstract data type
Xon which the following operations can be performed:
X.Ss
X#include <pic.h>
X.Sd
XShould be included by any application using \fIpic\fP.
X.Se
X
X.Ss
XPic *pic_open(name, mode)
Xchar *name, *mode;
X.Sd
XOpen picture with filename \fIname\fP
Xwith \fImode\fP=\fB"r"\fP for reading or \fB"w"\fP for writing.
XReturns a pointer which must be used in all subsequent operations on the
Xpicture.
XReturns 0 if unsuccessful.
XThis routine uses \fIpic_file_dev\fP to recognize the file's device type.
X.Se
X
X.Ss
Xvoid pic_close(p)
XPic *p;
X.Sd
XClose picture, flushing buffers if necessary.
XThis should be the last operation done on a picture.
X.Se
X
X.Ss
Xvoid pic_catalog()
X.Sd
XPrint a list of the device libraries linked in with the application.
X.Se
X
X.Ss
Xchar *pic_file_dev(file)
Xchar *file;
X.Sd
XDetermine a file's device name by examining its magic number in its first
Xfew bytes, if it is a file, or by recognizing the suffix of its name.
XReturns 0 if device is unrecognized.
XExamples:
X.Cs
Xpic_file_dev("mandrill.dump") == "dump"
Xpic_file_dev("iris") == "iris"
X.Ce
X.in +4n
X.Se
X
X.Ss
Xchar *pic_get_name(p)
Xchar *pic_get_dev(p)
XPic *p;
X.Sd
XThese two routines returns the picture's filename or device name,
Xrespectively.
X.Se
X
X.Ss
Xint pic_get_nchan(p)
Xvoid pic_set_nchan(p, nchan)
XPic *p;
Xint nchan;
X.Sd
XReturns (\fIpic_get_nchan\fP) or sets (\fIpic_set_nchan\fP) the number
Xof channels in the picture: 1, 3, or 4.
X.Se
X
X.Ss
Xvoid pic_get_box(p, &ox, &oy, &sx, &sy)
Xvoid pic_set_box(p, ox, oy, sx, sy)
XPic *p;
Xint ox, oy, sx, sy;
X.Sd
XGet or set the origin \fI(ox,oy)\fP and size \fI(sx,sy)\fP of the device.
XWhen a device with no intrinsic resolution (such as a picture file) is opened
Xfor writing, its origin is undefined (ox==PIC_UNDEFINED) initially,
Xso its box must be set before pixels can be written.
X.Se
X
X.Ss
XWindow *pic_get_window(p, win)
Xvoid pic_set_window(p, win)
XPic *p;
XWindow *w;
X.Sd
XThis is an alternative scheme for getting or setting the box of a picture,
Xredundant with the box routines above.
XThe \fBWindow\fP structure is:
X.Cs
Xtypedef struct {	/* WINDOW: A DISCRETE 2-D RECTANGLE */
X    int x0, y0;		/* xmin and ymin */
X    int x1, y1;		/* xmax and ymax (inclusive) */
X} Window;
X.Ce
X.in +4n
XThe relation between box and window is:
X\fI(x0,y0)=(ox,oy)\fP, \fI(x1,y1)=(ox+sx-1,oy+sy-1)\fP.
X\fIpic_get_window\fP returns its window argument.
X.Se
X
X.Ss
XPixel1 pic_read_pixel(p, x, y)
Xvoid pic_write_pixel(p, x, y, pv)
XPic *p;
Xint x, y;
XPixel1 pv;
X.Sd
XRead or write a pixel from a 1-channel picture.
X\fIpv\fP is the pixel value to write.
X.Se
X
X.Ss
Xvoid pic_read_pixel_rgba(p, x, y, pv)
Xvoid pic_write_pixel_rgba(p, x, y, r, g, b, a)
XPic *p;
Xint x, y;
XPixel1 r, g, b, a;
XPixel1_rgba *pv;
X.Sd
XRead or write a pixel at \fI(x,y)\fP from a 3 or 4-channel picture.
X.Se
X
X.Ss
Xvoid pic_read_row(p, y, x0, nx, buf)
Xvoid pic_write_row(p, y, x0, nx, buf)
XPic *p;
Xint y, x0, nx;
XPixel1 *buf;
X.Sd
XRead or write a portion of the 1-channel scanline at y=\fIy\fP
Xstarting at x=\fIx0\fP,
Xextending \fInx\fP pixels to the right, from or to the array \fIbuf\fP,
Xwhich has room for \fInx\fP 1-channel pixels.
X.Se
X
X.Ss
Xvoid pic_read_row_rgba(p, y, x0, nx, buf)
Xvoid pic_write_row_rgba(p, y, x0, nx, buf)
XPic *p;
Xint y, x0, nx;
XPixel1_rgba *buf;
X.Sd
XRead or write a portion of the 3 or 4-channel scanline at y=\fIy\fP
Xstarting at x=\fIx0\fP,
Xextending \fInx\fP pixels to the right, from or to the array \fIbuf\fP,
Xwhich has room for \fInx\fP 4-channel pixels.
X.Se
X
X.Ss
Xvoid pic_read_block(p, x0, y0, nx, ny, buf)
Xvoid pic_write_block(p, x0, y0, nx, ny, buf)
Xvoid pic_read_block_rgba(p, x0, y0, nx, ny, buf_rgba)
Xvoid pic_write_block_rgba(p, x0, y0, nx, ny, buf_rgba)
XPic *p;
Xint x0, y0, nx, ny;
XPixel1 *buf;
XPixel1_rgba *buf_rgba;
X.Sd
XSimilar to the row routines, but these read or write a rectangular block
Xof pixels with upper left corner \fI(x0,y0)\fP and size \fI(nx,ny)\fP.
XThe buffers are effectively of dimension \fI[ny][nx]\fP.
X.Se
X
X.Ss
Xvoid pic_clear(p, pv)
XPic *p;
XPixel1 pv;
X.Sd
XClear all pixels of the 1-channel picture to pixel value \fIpv\fP.
X.Se
X
X.Ss
Xvoid pic_clear_rgba(p, r, g, b, a)
XPic *p;
XPixel1 r, g, b, a;
X.Sd
XClear all pixels of the 3 or 4-channel picture to pixel value \fI(r,g,b,a)\fP.
X.Se
X
X.Ss
XPic *pic_load(name1, name2)
Xchar *name1, *name2;
X.Sd
XCopy picture from file \fIname1\fP into file \fIname2\fP,
Xand return the descriptor of the latter picture,
Xopened for writing.
X.Se
X
X.Ss
Xvoid pic_save(p, name)
XPic *p;
Xchar *name;
X.Sd
XCopy the picture in \fIp\fP into a new picture in file \fIname\fP.
XPicture \fIp\fP is not closed.
X.Se
X
X.Ss
Xvoid pic_copy(p, q)
XPic *p, *q;
X.Sd
XCopy picture \fIp\fP into picture \fIq\fP.
XNeither one is closed.
X.Se
X.SH LINKING
XThe code within \fIpic\fP consists of two layers: the top layer of
Xdevice-independent code,
Xand the bottom layer of device-dependent "device libraries",
Xwith one library for each device class known.
XAn application using \fIpic\fP has control at compile time of which device
Xlibraries are linked in to its executable file.
XSome programs will be run on just one device, so it is wasteful of disk
Xspace to link in more than the one device library, while other programs
Xneed to read and write a variety of device types, so they will want to link
Xin all device libraries.
XLinking is controlled through the global array \fBpic_list\fP.
XIf the application declares its own \fBpic_list\fP then it has
Xexplicit control of the device libraries linked;
Xotherwise the linker will pick up the default \fBpic_list\fP from
X\fBpic_file.o\fP in \fBlibpic.a\fP and link in all device libraries.
XTo create your own \fBpic_list\fP, put lines similar to the following
Xin your application source code:
X.Cs
Xextern Pic pic_dump, pic_foo;
XPic *pic_list[PIC_LISTMAX] = {&pic_dump, &pic_foo, 0};
X.Ce
XThis will cause the "dump" and "foo" device libraries to be linked in.
XNote: the "0" terminating \fBpic_list\fP is vital.
XThe subroutine \fIpic_catalog()\fP prints \fBpic_list\fP.
X.SH EXAMPLE
XThe following program illustrates the use of the \fIpic\fP package:
X.Cs
X#include <simple.h>
X#include <pic.h>
X
X/* pic_lum: take the luminance of afile and write it into bfile
X * afile is expected to be 3 or 4-channel, bfile is written as 1-channel */
X
Xpic_lum(afile, bfile)
Xchar *afile, *bfile;
X{
X    int x, y, dx, dy;
X    Pic *a, *b;
X    Window win;
X    Pixel1_rgba *rgb;
X    Pixel1 *lum;
X
X    a = pic_open(afile, "r");
X    if (!a) die("can't read %s\en", afile);
X    b = pic_open(bfile, "w");
X    if (!a) die("can't write %s\en", bfile);
X    if (pic_get_nchan(a)<3) die("%s is not 3 channel\en", afile);
X
X    pic_set_nchan(b, 1);
X    pic_set_window(b, pic_get_window(a, &win));
X    dx = win.x1-win.x0+1;
X    dy = win.y1-win.y0+1;
X    printf("%s->%s, res=%dx%d, origin=(%d,%d)\en",
X	afile, bfile, dx, dy, win.x0, win.y0);
X
X    rgb = (Pixel1_rgba *)malloc(dx*sizeof(Pixel1_rgba));
X    lum = (Pixel1 *)malloc(dx*sizeof(Pixel1));
X    for (y=0; y<dy; y++) {
X	pic_read_row_rgba(a, win.y0+y, win.x0, dx, rgb);
X	for (x=0; x<dx; x++)
X	    lum[x] = .30*rgb[x].r + .59*rgb[x].g + .11*rgb[x].b;
X	pic_write_row(b, win.y0+y, win.x0, dx, lum);
X    }
X    free(rgb);
X    free(lum);
X    pic_close(a);
X    pic_close(b);
X}
X
Xstatic die(control, arg)
Xchar *control, *arg;
X{
X    fprintf(stderr, control, arg);
X    exit(1);
X}
X.Ce
X.SH CREATING A DEVICE LIBRARY
XTo add a new device library to \fIpic\fP for a hypothetical file or device
Xtype called "foo",
Xyou would write the following subroutines:
X.Cs
XFoo	*foo_open(file, mode)
Xvoid	foo_close(d)
X
Xchar	*foo_get_name(d)
Xvoid	foo_clear(d, pv)
Xvoid	foo_clear_rgba(d, r, g, b, a)
X
Xvoid	foo_set_nchan(d, nchan)
Xvoid	foo_set_box(d, ox, oy, dx, dy)
Xvoid	foo_write_pixel(d, x, y, pv)
Xvoid	foo_write_pixel_rgba(d, x, y, r, g, b, a)
Xvoid	foo_write_row(d, y, x0, nx, buf)
Xvoid	foo_write_row_rgba(d, y, x0, nx, buf)
X
Xint	foo_get_nchan(d)
Xvoid	foo_get_box(d, ox, oy, dx, dy)
XPixel1	foo_read_pixel(d, x, y)
Xvoid	foo_read_pixel_rgba(d, x, y, pv)
Xvoid	foo_read_row(d, y, x0, nx, buf)
Xvoid	foo_read_row_rgba(d, y, x0, nx, buf)
X.Ce
Xwhere the arguments are identical to those of the \fIpic\fP subroutines,
Xexcept that the first argument to these routines is a pointer to the private
Xdata for this device,
X.Cs
XFoo *d;
X.Ce
XThis private data is a structure of your own design, containing whatever state
Xinformation is needed to perform the above operations.
XIf an operation is difficult on your device then politely punt, e.g.:
X.Cs
XPixel1 foo_read_pixel(p, x, y) Foo *p; int x, y; {
X    fprintf(stderr, "foo_read_pixel: unimplemented\en");
X}
X.Ce
X.PP
XAfter the above routines are written they need to be registered by
Xcollecting their addresses into a global \fBPic_procs\fP structure
Xand creating a prototype \fBPic\fP structure containing the device's name
Xand \fBPic_procs\fP pointer.
XFor our device class "foo", we'd declare:
X.Cs
Xstatic Pic_procs pic_foo_procs = {
X    (char *(*)())foo_open, foo_close,
X    foo_get_name,
X    foo_clear, foo_clear_rgba,
X    foo_set_nchan, foo_set_box,
X    foo_write_pixel, foo_write_pixel_rgba,
X    foo_write_row, foo_write_row_rgba,
X    foo_get_nchan, foo_get_box,
X    foo_read_pixel, foo_read_pixel_rgba,
X    foo_read_row, foo_read_row_rgba,
X};
X
XPic pic_foo = {"foo", &pic_foo_procs};
X.Ce
XBy convention, the device library for device "foo" goes in a source file
Xcalled \fBfoo.c\fP that does not #include pic.h,
Xand the above global structures go in a small file called \fBfoo_pic.c\fP
Xthat does #include pic.h.
X.PP
XThere are three global files that must be modified slightly to register your
Xnew device with the \fIpic\fP library:
XAdd the address of your prototype \fBPic\fP structure to the list of all
Xknown devices in \fBpic_all.c\fP.
XAdd magic number or recognition code to \fIpic_file_dev\fP in \fBpic_file.c\fP.
XModify \fBMakefile\fP.
XRun \fBmake install\fP.
XFor further examples of this process, see the files
X\fBdump.h\fP, \fBdump.c\fP, \fBdump_pic.c\fP.
X.SH AUTHOR
XPaul Heckbert, UC Berkeley, August 1989.
Xph@miro.berkeley.edu
EOF23219
echo extracting libpic/Makefile
sed 's/^X//' <<'EOF23220' >libpic/Makefile
X# $Header: Makefile,v 2.2 88/12/23 20:59:08 ph Locked $
X
XDEST = ..
XL = $(DEST)/lib
XCOPTS = -g $(FLOATOPT)
XIPATH = -I. -I$(DEST)/include
XCFLAGS = $(IPATH) $(COPTS)
X
XSRC = window.c pic.c pic_all.c pic_file.c swap.c \
X	dump.c dump_pic.c \
X	iris.c iris_pic.c \
X	ras.c ras_pic.c
X
XOBJ = window.o pic.o pic_all.o pic_file.o swap.o \
X	dump.o dump_pic.o \
X	iris.o iris_pic.o \
X	ras.c ras_pic.c
X
XHDR = pixel.h window.h pic.h \
X	dump.h iris.h ras.h
X
X.SUFFIXES: .o .s .i .c
X
X.c.i:
X	$(CC) $(CFLAGS) -P $*.c
X	cat -s <$*.i >temp
X	mv temp $*.i
X
Xall: libpic.a
X
Xlibpic.a: $(OBJ)
X	ar cu libpic.a $(OBJ)
X	ranlib libpic.a
X
Xinstall: libpic.a
X	mv libpic.a $L
X#	cp -p $(HDR) $(DEST)/include
X	cp $(HDR) $(DEST)/include
X
Xlibpic.lint: GHOST
X	lint $(IPATH) $(SRC) >libpic.lint
X
Xclean:
X	-rm -f libpic.a *.o *.lint
X
XGHOST:
X
Xpic.o: pic.h pixel.h
Xwindow.o: window.h
Xpic_all.o: pic.h
Xpic_file.o: pic.h
X
Xdump.o: dump.h pixel.h
Xiris.o: iris.h pixel.h
Xras.o: ras.h pixel.h
X
Xdump_pic.o: dump.h pic.h
Xiris_pic.o: iris.h pic.h
Xras_pic.o: ras.h pic.h
EOF23220
echo extracting libpic/ras.c
sed 's/^X//' <<'EOF23221' >libpic/ras.c
X/*
X * ras: subroutine package to read and write Sun raster file format
X *
X * Paul Heckbert	16 Dec 1988
X */
X
Xstatic char rcsid[] = "$Header: ras.c,v 1.3 89/05/19 13:21:10 ph Locked $";
X
X#include <simple.h>
X#include "ras.h"
X#include "dump.h"
X
X#ifdef vax		/* swap bytes in header if little-endian */
X#   define BYTESWAP
X#endif
X
X#define UNDEF PIXEL_UNDEFINED
X
X#define CHECK_HEAD_UNWRITTEN(p, subrname) {	\
X    if ((p)->headwritten) {			\
X	fprintf(stderr, "%s: can't change state once writing commences\n", \
X	    subrname);				\
X	exit(1);				\
X    }						\
X}
X#define CHECK_HEAD_WRITTEN(p, subrname) \
X    if (!(p)->headwritten) ras_write_head(p, subrname); else
X
XRas *ras_open_read(), *ras_open_write();
X
XRas *ras_open(file, mode)
Xchar *file, *mode;
X{
X         if (str_eq(mode, "r")) return ras_open_read(file);
X    else if (str_eq(mode, "w")) return ras_open_write(file);
X    fprintf(stderr, "ras_open: can't do mode %s\n", mode);
X    exit(1); /*NOTREACHED*/
X}
X
Xstatic Ras *ras_open_write(file)
Xchar *file;
X{
X    Dump *p;
X
X    ALLOC(p, Dump, 1);
X    if ((p->fp = fopen(file, "w")) == NULL) {
X	fprintf(stderr, "ras_open_write: can't write %s\n", file);
X	free(p);
X	return 0;
X    }
X    p->h.nchan = 1;
X    p->h.dx = UNDEF;
X    strcpy(p->name, file);
X    p->headsize = sizeof(Ras_head);
X    p->headwritten = 0;
X    p->curx = p->cury = 0;
X    return p;
X}
X
Xstatic void ras_write_head(p, subrname)
XRas *p;
Xchar *subrname;
X{
X    Ras_head h;
X    int v, i;
X    unsigned char cm[3][256];
X
X    if (p->h.dx==UNDEF) {
X	fprintf(stderr, "%s: size of %s is uninitialized\n", p->name);
X	exit(1);
X    }
X    h.magic = RAS_MAGIC;
X    h.dx = p->h.dx;
X    h.dy = p->h.dy;
X    h.nbit = p->h.nchan == 3 ? 24 : 8;
X    h.len = p->h.dx*p->h.dy*p->h.nchan;
X    h.type = RAS_TYPE_STANDARD;
X    h.cmtype = RAS_CM_EQUAL_RGB;
X    h.cmlen = 3*256;
X#   ifdef BYTESWAP
X	headswap(&h);
X#   endif
X    if (fwrite(&h, sizeof(Ras_head), 1, p->fp) != 1) {
X	fprintf(stderr, "%s: write error on %s\n", subrname, p->name);
X	exit(1);
X    }
X    printf("%s: %dx%d %d-chan\n", p->name, p->h.dx, p->h.dy, p->h.nchan);
X    for (i=0; i<3; i++)
X	for (v=0; v<256; v++)
X	    cm[i][v] = v;
X    if (fwrite(cm, 256, 3, p->fp) != 3) {
X	fprintf(stderr, "ras_open_write: write error on colormap of %s\n",
X	    p->name);
X	exit(1);
X    }
X    p->headwritten = 1;
X}
X
Xstatic Ras *ras_open_read(file)
Xchar *file;
X{
X    int badhead;
X    Ras_head h;
X    Dump *p;
X
X    ALLOC_ZERO(p, Dump, 1);
X    if ((p->fp = fopen(file, "r")) == NULL) {
X	fprintf(stderr, "ras_open_read: can't find %s\n", file);
X	free(p);
X	return 0;
X    }
X
X    badhead = fread(&h, sizeof(Ras_head), 1, p->fp) != 1;
X#   ifdef BYTESWAP
X	headswap(&h);
X#   endif
X    if (badhead || h.magic!=RAS_MAGIC) {
X	fprintf(stderr, "ras_open_read: %s is not a ras file\n", file);
X	free(p);
X	return 0;
X    }
X    printf("%s: %dx%d %d-bit, ",
X	file, h.dx, h.dy, h.nbit);
X    printf("len=%d type=%d cmtype=%d cmlen=%d\n",
X	h.len, h.type, h.cmtype, h.cmlen);
X    if (!(h.type==RAS_TYPE_STANDARD && h.nbit==8 &&
X	(h.cmtype==RAS_CM_NONE && h.cmlen==0 || h.cmtype==RAS_CM_EQUAL_RGB))) {
X	    fprintf(stderr,
X		"ras_open_read: don't know type %d, %d bpp, cmtype %d\n",
X		    h.type, h.nbit, h.cmtype);
X	    exit(1);
X    }
X    if (h.len!=h.dx*h.dy)
X	fprintf(stderr, "ras_open_read: surprised length isn't %d\n",
X	    h.dx*h.dy);
X
X    if (h.cmlen>0) {
X	Pixel1 *cm[3], cmb[3*256];
X	int ncolor, v;
X	ncolor = h.cmlen/3;
X	if (fread(cmb, ncolor, 3, p->fp) != 3) {
X	    fprintf(stderr, "ras_open_read: read error on colormap of %s\n",
X		p->name);
X	    exit(1);
X	}
X	cm[0] = &cmb[0];
X	cm[1] = &cmb[ncolor];
X	cm[2] = &cmb[2*ncolor];
X	/* print colormap if it's not an identity */
X	for (v=0; v<ncolor; v++)
X	    if (cm[0][v]!=v || cm[1][v]!=v || cm[2][v]!=v) {
X		for (v=0; v<ncolor; v++)
X		    printf("cm[%3d] = (%3d,%3d,%3d)\n",
X			v, cm[0][v], cm[1][v], cm[2][v]);
X		break;
X	    }
X    }
X    /* else no colormap */
X
X    strcpy(p->name, file);
X    p->h.dx = h.dx;
X    p->h.dy = h.dy;
X    p->h.nchan = h.nbit == 24 ? 3 : 1;
X    p->headsize = sizeof(Ras_head)+h.cmlen;
X    p->headwritten = 1;
X    p->curx = p->cury = 0;
X    return p;
X}
X
X#ifdef BYTESWAP
X
Xstatic headswap(h)
XRas_head *h;
X{
X    swap_long(&h->magic);
X    swap_long(&h->dx);
X    swap_long(&h->dy);
X    swap_long(&h->nbit);
X    swap_long(&h->len);
X    swap_long(&h->type);
X    swap_long(&h->cmtype);
X    swap_long(&h->cmlen);
X}
X
X#endif
X
X/*-------------------- file writing routines --------------------*/
X
Xvoid ras_set_nchan(p, nchan)
XDump *p;
Xint nchan;
X{
X    CHECK_HEAD_UNWRITTEN(p, "ras_set_nchan");
X    if (nchan!=1 && nchan!=3) {
X	fprintf(stderr, "ras_set_nchan: can't handle nchan=%d\n", nchan);
X	exit(1);
X    }
X    p->h.nchan = nchan;
X}
X
Xvoid ras_set_box(p, ox, oy, dx, dy)
XDump *p;
Xint ox, oy, dx, dy;
X{
X    CHECK_HEAD_UNWRITTEN(p, "ras_set_box");
X    /* ignore ox, oy */
X    p->h.dx = dx;
X    p->h.dy = dy;
X}
X
Xvoid ras_write_pixel(p, x, y, pv)
XDump *p;
Xint x, y;
XPixel1 pv;
X{
X    fprintf(stderr, "ras_write_pixel: unimplemented\n");
X}
X
Xvoid ras_write_pixel_rgba(p, x, y, r, g, b, a)
XDump *p;
Xint x, y;
XPixel1 r, g, b, a;
X{
X    fprintf(stderr, "ras_write_pixel_rgba: unimplemented\n");
X}
X
Xvoid ras_write_row(p, y, x0, nx, buf)
Xregister Dump *p;
Xint y, x0, nx;
XPixel1 *buf;
X{
X    CHECK_HEAD_WRITTEN(p, "ras_write_row");
X    if (x0!=p->curx || y!=p->cury)
X	dump_jump_to_pixel(p, x0, y);
X    if (fwrite(buf, nx*sizeof(Pixel1), 1, p->fp) != 1) {
X	fprintf(stderr, "ras_write_row: write error on %s\n", p->name);
X	exit(1);
X    }
X    dump_advance(p, nx);
X}
X
Xvoid ras_write_row_rgba(p, y, x0, nx, buf)
Xregister Dump *p;
Xint y, x0, nx;
Xregister Pixel1_rgba *buf;
X{
X    register int x;
X
X    CHECK_HEAD_WRITTEN(p, "ras_write_row_rgba");
X    if (x0!=p->curx || y!=p->cury)
X	dump_jump_to_pixel(p, x0, y);
X    for (x=nx; --x>=0; buf++) {
X	putc(buf->r, p->fp);
X	putc(buf->g, p->fp);
X	putc(buf->b, p->fp);
X    }
X    dump_advance(p, nx);
X}
EOF23221
echo extracting libpic/ras.h
sed 's/^X//' <<'EOF23222' >libpic/ras.h
X/* ras.h: global definitions for Sun's raster picture file format */
X
X#ifndef RAS_HDR
X#define RAS_HDR
X
X/* $Header: ras.h,v 1.1 88/12/18 20:29:32 ph Locked $ */
X
X/* the following is modified from Sun's /usr/include/rasterfile.h: */
X
Xtypedef struct {	/* RAS FILE HEADER */
X    long magic;		/* magic number */
X    long dx;		/* width in pixels */
X    long dy;		/* height in pixels */
X    long nbit;		/* number of bits per pixel (1, 8, or 24) */
X    long len;		/* length of image data in bytes */
X    long type;		/* type of file; see RAS_TYPE_* below */
X    long cmtype;	/* type of colormap; see RMT_* below */
X    long cmlen;		/* length (bytes) of following map */
X} Ras_head;
X
X#define	RAS_MAGIC	0x59a66a95
X
X/* Sun supported ras types */
X#define RAS_TYPE_OLD		0	/* Raw pixrect image? */
X#define RAS_TYPE_STANDARD	1	/* Raw pixrect image */
X#define RAS_TYPE_BYTE_ENCODED	2	/* Run-length compression of bytes */
X#define RAS_TYPE_EXPERIMENTAL 0xffff	/* Reserved for testing */
X
X/* Sun registered cmtypes */
X#define RAS_CM_RAW		2
X
X/* Sun supported cmtypes */
X#define RAS_CM_NONE		0	/* ras_maplength is expected to be 0 */
X#define RAS_CM_EQUAL_RGB	1	/* red[cmlen/3],green[],blue[] */
X
X/*----------------------------------------------------------------------*/
X
X#include <dump.h>
X
Xtypedef Dump Ras;	/* A RAS IS A DUMP */
X
XRas *ras_open(/* file, mode */);
Xvoid ras_set_box(/* p, ox, oy, dx, dy */);
Xvoid ras_set_nchan(/* p, nchan */);
Xvoid ras_write_pixel(/* p, x, y, pv */);
Xvoid ras_write_pixel_rgba(/* p, x, y, r, g, b, a */);
Xvoid ras_write_row(/* p, y, x0, nx, buf */);
Xvoid ras_write_row_rgba(/* p, y, x0, nx, buf */);
X
X#endif
EOF23222
echo extracting libpic/ras_pic.c
sed 's/^X//' <<'EOF23223' >libpic/ras_pic.c
X/* ras_pic: hooking ras package into pic package */
X
Xstatic char rcsid[] = "$Header: ras_pic.c,v 1.1 88/12/18 20:29:36 ph Locked $";
X
X#include "pic.h"
X#include "ras.h"
X
Xstatic Pic_procs pic_ras_procs = {
X    (char *(*)())ras_open, dump_close,
X    dump_get_name,
X    dump_clear, dump_clear_rgba,
X
X    ras_set_nchan, ras_set_box,
X    ras_write_pixel, ras_write_pixel_rgba,
X    ras_write_row, ras_write_row_rgba,
X
X    dump_get_nchan, dump_get_box,
X    dump_read_pixel, dump_read_pixel_rgba,
X    dump_read_row, dump_read_row_rgba,
X};
X
XPic pic_ras = {"ras", &pic_ras_procs};
EOF23223
echo extracting libpic/pic_all.c
sed 's/^X//' <<'EOF23224' >libpic/pic_all.c
Xstatic char rcsid[] = "$Header: pic_all.c,v 2.2 88/12/23 20:59:02 ph Locked $";
X#include <pic.h>
X
Xextern Pic
X    pic_dump, pic_iris, pic_ras;
X
X/*
X * A pic_list for those programs that want everything.
X * If the application doesn't define space for pic_list then the
X * linker will grab this.
X */
X
XPic *pic_list[PIC_LISTMAX] = {
X    &pic_dump, &pic_iris, &pic_ras,
X0};
EOF23224
echo extracting libpic/pic_file.c
sed 's/^X//' <<'EOF23225' >libpic/pic_file.c
Xstatic char rcsid[] = "$Header: pic_file.c,v 1.1 89/08/22 10:52:19 ph Locked $";
X
X#include <string.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#include <simple.h>
X#include "pic.h"
X
Xtypedef enum {DUNNO, SHORT, LONG} Magic_type;
Xtypedef enum {NA, LITTLE_ENDIAN, BIG_ENDIAN} Magic_byteorder;
X
X#ifdef vax
X#   define MACHINE_BYTEORDER LITTLE_ENDIAN
X#else
X#   define MACHINE_BYTEORDER BIG_ENDIAN
X#endif
X
Xint amg_recog(), dat_recog(), ais_recog(), rad_recog();
X
Xtypedef struct {
X    char *dev;			/* device name */
X    char *suffix;		/* file suffix */
X    long magic;			/* magic number */
X    Magic_type type;		/* type of magic number (DUNNO|SHORT|LONG) */
X    Magic_byteorder byteorder;	/* NA, LITTLE_ENDIAN, or BIG_ENDIAN */
X    int (*recogproc)();		/* procedure to recognize, if needed */
X} Dev_info;
X
Xstatic Dev_info dev[] = {
X /*  DEV      SUFFIX	MAGIC#		TYPE	BYTEORDER	RECOGPROC */
X
X    "dump",   "dump",	0x5088,		SHORT,	BIG_ENDIAN,	0,
X    "iris",   "iris",	0,		DUNNO,	NA,		0,
X    "ras",    "ras",	0x59a66a95,	LONG,	BIG_ENDIAN,	0,
X};
X#define NDEV (sizeof dev / sizeof dev[0])
X
X/*
X * pic_file_dev: given file name, try to determine its device type.
X * First examine the file (if it exists);
X * then try special type-specific recognizers,
X * if those fail look at file suffix.
X * Returns 0 if unrecognized.
X */
X
Xchar *pic_file_dev(file)
Xchar *file;
X{
X    char *suffix;
X    union {
X	unsigned short s;
X	long l;
X    } u, v;
X    Dev_info *d;
X    FILE *fp;
X    struct stat sb;
X
X    /* first try examining the file */
X    if ((fp = fopen(file, "r")) != NULL && fstat(fileno(fp), &sb) == 0 &&
X	(sb.st_mode&S_IFMT) == S_IFREG) {
X	    if (fread(&u, sizeof u, 1, fp) != 1)
X		u.l = 0;			/* no magic number */
X	    fclose(fp);
X	    for (d=dev; d<dev+NDEV; d++) {
X		if (d->byteorder != NA) {	/* check file's magic number */
X		    if (d->type == SHORT) {	/* short magic number */
X			v.s = u.s;
X			/* if file byte order diff. from machine's then swap: */
X			if (d->byteorder != MACHINE_BYTEORDER)
X			    swap_short(&v.s);
X			if (v.s==d->magic) return d->dev;
X		    }
X		    else {			/* long magic number */
X			v.l = u.l;
X			/* if file byte order diff. from machine's then swap: */
X			if (d->byteorder != MACHINE_BYTEORDER)
X			    swap_long(&v.l);
X			if (v.l==d->magic) return d->dev;
X		    }
X		}
X	    }
X    }
X    
X    /* if magic number didn't identify, try type-specific recognizers: */
X    for (d=dev; d<dev+NDEV; d++)
X	if (d->recogproc)		/* call device's recognition proc */
X	    if ((*d->recogproc)(file, d)) return d->dev;
X
X    /* if we couldn't recognize by file contents, try file name */
X    suffix = strrchr(file, '.');
X    if (suffix) suffix++;
X    else {
X	suffix = strrchr(file, '/');
X	suffix = suffix ? suffix+1 : file;
X    }
X    for (d=dev; d<dev+NDEV; d++)
X	if (str_eq(d->suffix, suffix)) return d->dev;
X
X    /* else failure */
X    return 0;
X}
EOF23225
exit