|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <draw.h> | 
|  | #include <ctype.h> | 
|  | #include "imagefile.h" | 
|  |  | 
|  | Rawimage *readppm(Biobuf*, Rawimage*); | 
|  |  | 
|  | /* | 
|  | * fetch a non-comment character. | 
|  | */ | 
|  | static | 
|  | int | 
|  | Bgetch(Biobuf *b) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | c = Bgetc(b); | 
|  | if(c == '#') { | 
|  | while((c = Bgetc(b)) != Beof && c != '\n') | 
|  | ; | 
|  | } | 
|  | return c; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * fetch a nonnegative decimal integer. | 
|  | */ | 
|  | static | 
|  | int | 
|  | Bgetint(Biobuf *b) | 
|  | { | 
|  | int c; | 
|  | int i; | 
|  |  | 
|  | while((c = Bgetch(b)) != Beof && !isdigit(c)) | 
|  | ; | 
|  | if(c == Beof) | 
|  | return -1; | 
|  |  | 
|  | i = 0; | 
|  | do { | 
|  | i = i*10 + (c-'0'); | 
|  | } while((c = Bgetch(b)) != Beof && isdigit(c)); | 
|  |  | 
|  | return i; | 
|  | } | 
|  |  | 
|  | static | 
|  | int | 
|  | Bgetdecimalbit(Biobuf *b) | 
|  | { | 
|  | int c; | 
|  | while((c = Bgetch(b)) != Beof && c != '0' && c != '1') | 
|  | ; | 
|  | if(c == Beof) | 
|  | return -1; | 
|  | return c == '1'; | 
|  | } | 
|  |  | 
|  | static int bitc, nbit; | 
|  |  | 
|  | static | 
|  | int | 
|  | Bgetbit(Biobuf *b) | 
|  | { | 
|  | if(nbit == 0) { | 
|  | nbit = 8; | 
|  | bitc = Bgetc(b); | 
|  | if(bitc == -1) | 
|  | return -1; | 
|  | } | 
|  | nbit--; | 
|  | return (bitc >> (nbit-1)) & 0x1; | 
|  | } | 
|  |  | 
|  | static | 
|  | void | 
|  | Bflushbit(Biobuf *b) | 
|  | { | 
|  | USED(b); | 
|  | nbit = 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | Rawimage** | 
|  | readpixmap(int fd, int colorspace) | 
|  | { | 
|  | Rawimage **array, *a; | 
|  | Biobuf b; | 
|  | char buf[ERRMAX]; | 
|  | int i; | 
|  | char *e; | 
|  |  | 
|  | USED(colorspace); | 
|  | if(Binit(&b, fd, OREAD) < 0) | 
|  | return nil; | 
|  |  | 
|  | werrstr(""); | 
|  | e = "out of memory"; | 
|  | if((array = malloc(sizeof *array)) == nil) | 
|  | goto Error; | 
|  | if((array[0] = malloc(sizeof *array[0])) == nil) | 
|  | goto Error; | 
|  | memset(array[0], 0, sizeof *array[0]); | 
|  |  | 
|  | for(i=0; i<3; i++) | 
|  | array[0]->chans[i] = nil; | 
|  |  | 
|  | e = "bad file format"; | 
|  | switch(Bgetc(&b)) { | 
|  | case 'P': | 
|  | Bungetc(&b); | 
|  | a = readppm(&b, array[0]); | 
|  | break; | 
|  | default: | 
|  | a = nil; | 
|  | break; | 
|  | } | 
|  | if(a == nil) | 
|  | goto Error; | 
|  | array[0] = a; | 
|  |  | 
|  | return array; | 
|  |  | 
|  | Error: | 
|  | if(array) | 
|  | free(array[0]); | 
|  | free(array); | 
|  |  | 
|  | errstr(buf, sizeof buf); | 
|  | if(buf[0] == 0) | 
|  | strcpy(buf, e); | 
|  | errstr(buf, sizeof buf); | 
|  |  | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | typedef struct Pix	Pix; | 
|  | struct Pix { | 
|  | char magic; | 
|  | int	maxcol; | 
|  | int	(*fetch)(Biobuf*); | 
|  | int	nchan; | 
|  | int	chandesc; | 
|  | int	invert; | 
|  | void	(*flush)(Biobuf*); | 
|  | }; | 
|  |  | 
|  | static Pix pix[] = { | 
|  | { '1', 1, Bgetdecimalbit, 1, CY, 1, 0 },	/* portable bitmap */ | 
|  | { '4', 1, Bgetbit, 1, CY, 1, Bflushbit },	/* raw portable bitmap */ | 
|  | { '2', 0, Bgetint, 1, CY, 0, 0 },	/* portable greymap */ | 
|  | { '5', 0, Bgetc, 1, CY, 0, 0 },	/* raw portable greymap */ | 
|  | { '3', 0, Bgetint, 3, CRGB, 0, 0 },	/* portable pixmap */ | 
|  | { '6', 0, Bgetc, 3, CRGB, 0, 0 },	/* raw portable pixmap */ | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | Rawimage* | 
|  | readppm(Biobuf *b, Rawimage *a) | 
|  | { | 
|  | int i, ch, wid, ht, r, c; | 
|  | int maxcol, nchan, invert; | 
|  | int (*fetch)(Biobuf*); | 
|  | uchar *rgb[3]; | 
|  | char buf[ERRMAX]; | 
|  | char *e; | 
|  | Pix *p; | 
|  |  | 
|  | e = "bad file format"; | 
|  | if(Bgetc(b) != 'P') | 
|  | goto Error; | 
|  |  | 
|  | c = Bgetc(b); | 
|  | for(p=pix; p->magic; p++) | 
|  | if(p->magic == c) | 
|  | break; | 
|  | if(p->magic == 0) | 
|  | goto Error; | 
|  |  | 
|  |  | 
|  | wid = Bgetint(b); | 
|  | ht = Bgetint(b); | 
|  | if(wid <= 0 || ht <= 0) | 
|  | goto Error; | 
|  | a->r = Rect(0,0,wid,ht); | 
|  |  | 
|  | maxcol = p->maxcol; | 
|  | if(maxcol == 0) { | 
|  | maxcol = Bgetint(b); | 
|  | if(maxcol <= 0) | 
|  | goto Error; | 
|  | } | 
|  |  | 
|  | e = "out of memory"; | 
|  | for(i=0; i<p->nchan; i++) | 
|  | if((rgb[i] = a->chans[i] = malloc(wid*ht)) == nil) | 
|  | goto Error; | 
|  | a->nchans = p->nchan; | 
|  | a->chanlen = wid*ht; | 
|  | a->chandesc = p->chandesc; | 
|  |  | 
|  | e = "error reading file"; | 
|  |  | 
|  | fetch = p->fetch; | 
|  | nchan = p->nchan; | 
|  | invert = p->invert; | 
|  | for(r=0; r<ht; r++) { | 
|  | for(c=0; c<wid; c++) { | 
|  | for(i=0; i<nchan; i++) { | 
|  | if((ch = (*fetch)(b)) < 0) | 
|  | goto Error; | 
|  | if(invert) | 
|  | ch = maxcol - ch; | 
|  | *rgb[i]++ = (ch * 255)/maxcol; | 
|  | } | 
|  | } | 
|  | if(p->flush) | 
|  | (*p->flush)(b); | 
|  | } | 
|  |  | 
|  | return a; | 
|  |  | 
|  | Error: | 
|  | errstr(buf, sizeof buf); | 
|  | if(buf[0] == 0) | 
|  | strcpy(buf, e); | 
|  | errstr(buf, sizeof buf); | 
|  |  | 
|  | for(i=0; i<3; i++) | 
|  | free(a->chans[i]); | 
|  | free(a->cmap); | 
|  | return nil; | 
|  | } |