| #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; |
| } |