#line 13 "pbm.c-nw" static char copyright[] = "Copyright NBBI, Den Haag, 1994"; #include #include #include #include #include #include #include #include #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) /* When decoding, the image is drawn every DELTA lines */ #define DELTA 5 typedef struct { Widget win; /* Where to draw the image */ GC gc; /* GC to use when drawing */ unsigned char *image; /* 3 bytes (RGB) per pixel */ int width, height; /* Dimensions of image */ int imagesize; /* Length of image in bytes */ int type; /* 1, 2, 3, 4, 5 or 6 */ int maxval; /* Range of input data */ char buf[2*BUFSIZ+1]; /* Temporary buf for parsing */ int buflen; /* Length of buf */ Bool eof; /* TRUE if this is last buf */ int nrvals; /* # of valid bytes in image */ int line; /* Last line drawn */ } *Info; static Bool has_stdcolormap = FALSE, has_stdgraymap = FALSE; static XscmInfoRec stdcolormap, stdgraymap; typedef struct _Assoc {long id; Info b; struct _Assoc *next;} *Assoc; static Assoc assoclist = NULL; /* store -- store an ID/Buffer combination */ static void store(Info b, long id) { Assoc h; new(h); h->id = id; h->b = b; h->next = assoclist; assoclist = h; } /* delete -- delete an ID/Buffer combination */ static void delete(long id) { Assoc g, h; assert(assoclist); if (assoclist->id == id) { h = assoclist; assoclist = assoclist->next; dispose(h); } else { assert(h->next); for (h = assoclist; h->next->id != id; h = h->next) assert(h->next); g = h->next; h->next = g->next; dispose(g); } } /* find -- find the Buffer associated with an ID */ static Info find(long id) { Assoc h; assert(assoclist); for (h = assoclist; h->id != id; h = h->next) assert(h->next); return h->b; } /* delete_bytes -- delete first len bytes from info->buf */ static void delete_bytes(Info info, int len) { assert(info != NULL && 0 <= len && len <= info->buflen); info->buflen -= len; memmove(info->buf, info->buf + len, info->buflen); } /* append_buf -- append data to info->buf, FALSE if no room */ static Bool append_buf(Info info, const char *buf, size_t nbytes) { assert(info != NULL && errno == 0 && info->buflen >= 0); if (info->buflen + nbytes > sizeof(info->buf)) { errno = ENOMEM; /* No room, probably error */ return FALSE; } else { memcpy(info->buf + info->buflen, buf, nbytes); info->buflen += nbytes; info->buf[info->buflen] = '\0'; /* Needed for P1/P2/P3 */ return TRUE; } } /* parse_num -- parse a number, TRUE if success, else set errno */ static Bool parse_num(Info info, int *num) { int len; assert(info != NULL && errno == 0); if (info->buflen == 0) { if (info->eof) errno = EFORMAT; /* Missing number */ return FALSE; /* Insufficient data */ } if (! isdigit(info->buf[0])) { errno = EFORMAT; /* Number expected */ return FALSE; } (void) sscanf(info->buf, "%d%n", num, &len); if (isspace(info->buf[len]) || info->eof) { delete_bytes(info, len); return TRUE; } else { return FALSE; /* More digits might follow */ } } /* parse_eol -- parse up to and including newline, TRUE if newline found */ static Bool parse_eol(Info info) { int i = 0; for (;;) { if (i == info->buflen) { if (info->eof) { /* Can't expect a newline */ if (i != 0) delete_bytes(info, i); return TRUE; } else { /* Newline might follow */ return FALSE; } } else if (info->buf[i] != '\n') { i++; } else { delete_bytes(info, i + 1); /* Remove including eol */ return TRUE; /* Eol found */ } } } /* parse_white -- parse whitespace and comments, TRUE if non-blank found */ static Bool parse_white(Info info) { int i = 0; assert(info != NULL && info->buflen >= 0); for (;;) { if (i == info->buflen) { if (i != 0) delete_bytes(info, i); /* Remove whitespaxe so far */ return ! info->eof; /* More spaces might follow */ } else if (isspace(info->buf[i])) { i++; } else if (info->buf[i] == '#') { if (i != 0) delete_bytes(info, i); /* Remove whitespace so far */ if (! parse_eol(info)) return FALSE; i = 0; } else { /* Found non-blank */ if (i != 0) delete_bytes(info, i); /* Remove whitespace */ return TRUE; } } } /* parse_type -- parse PBMPM magic number, TRUE if success, else set errno */ static Bool parse_type(Info info) { assert(info != NULL && errno == 0 && info->buflen >= 0); if (info->buflen < 2) return FALSE; /* Wait for more data */ if (info->buf[0] != 'P') {errno = EFORMAT; return FALSE;} switch (info->buf[1]) { case '1': info->type = 1; break; case '2': info->type = 2; break; case '3': info->type = 3; break; case '4': info->type = 4; break; case '5': info->type = 5; break; case '6': info->type = 6; break; default: errno = EFORMAT; return FALSE; } delete_bytes(info, 2); return TRUE; } /* parse_width -- parse width parameter, TRUE if success, else set errno */ static Bool parse_width(Info info) { assert(errno == 0); if (! parse_white(info)) return FALSE; if (! parse_num(info, &info->width)) return FALSE; return TRUE; } /* parse_height -- parse height parameter, TRUE if success, else set errno */ static Bool parse_height(Info info) { assert(errno == 0); if (! parse_white(info)) return FALSE; if (! parse_num(info, &info->height)) return FALSE; return TRUE; } /* parse_maxval -- set or parse maxval parameter, TRUE if success */ static Bool parse_maxval(Info info) { assert(errno == 0); if (info->type == 1 || info->type == 4) { /* P1 & P4 are B&W formats */ info->maxval = 1; } else { if (! parse_white(info)) return FALSE; if (! parse_num(info, &info->maxval)) return FALSE; } return TRUE; } /* parse_P1 -- parse data as 0's and 1's (PBM format) */ static void parse_P1(Info info) { int i = 0; assert(info != NULL && info->buflen >= 0); assert(info->imagesize >= 0 && info->nrvals >= 0); assert(info->nrvals <= info->imagesize); for (;;) { if (i == info->buflen) { delete_bytes(info, i); /* Remove parsed data */ return; } else if (info->nrvals == info->imagesize) { delete_bytes(info, info->buflen); /* Remove rest of data */ /* errno = EFORMAT; */ /* Too much data */ return; } else if (info->buf[i] == '0') { /* White pixel */ info->image[info->nrvals++] = info->image[info->nrvals++] = info->image[info->nrvals++] = 255; } else if (info->buf[i] == '1') { /* Black pixel */ info->image[info->nrvals++] = info->image[info->nrvals++] = info->image[info->nrvals++] = 0; } else if (! isspace(info->buf[i])) { /* Illegal data */ errno = EFORMAT; return; } i++; } } /* parse_P2 -- parse PGM data */ static void parse_P2(Info info) { int val, len, i = 0; assert(info != NULL && info->buflen >= 0); assert(info->imagesize >= 0 && info->nrvals >= 0); assert(info->nrvals <= info->imagesize); for (;;) { if (i == info->buflen) { if (i != 0) delete_bytes(info, i); /* Remove parsed data */ return; /* Wait for more data */ } else if (info->nrvals == info->imagesize) { delete_bytes(info, info->buflen); /* Remove rest of data */ /* errno = EFORMAT; */ /* Too much data */ return; } else if (sscanf(info->buf + i, "%d%n", &val, &len) != 1) { errno = EFORMAT; /* Illegal data */ return; } else if (! isspace(info->buf[i+len]) && ! info->eof) { if (i != 0) delete_bytes(info, i); /* Remove parsed data */ return; /* Wait for more data */ } else if (val > info->maxval) { errno = EFORMAT; /* Illegal pixel value */ return; } else { info->image[info->nrvals++] = info->image[info->nrvals++] = info->image[info->nrvals++] = 255 * val/info->maxval; } i++; } } /* parse_P3 -- parse PPM data */ static void parse_P3(Info info) { int i = 0, val, len; assert(info != NULL && info->buflen >= 0); assert(info->imagesize >= 0 && info->nrvals >= 0); assert(info->nrvals <= info->imagesize); for (;;) { if (i == info->buflen) { if (i != 0) delete_bytes(info, i); return; } else if (info->nrvals == info->imagesize) { delete_bytes(info, info->buflen); /* Remove rest of data */ /* errno = EFORMAT; */ /* Too much data */ return; } else if (sscanf(info->buf + i, "%d%n", &val, &len) != 1) { errno = EFORMAT; /* Illegal data */ return; } else if (! isspace(info->buf[i+len]) && ! info->eof) { if (i != 0) delete_bytes(info, i); /* Remove parsed data */ return; /* Wait for more data */ } else if (val > info->maxval) { errno = EFORMAT; /* Illegal pixel value */ return; } else { info->image[info->nrvals] = 255 * val/info->maxval; info->nrvals++; } i++; } } /* parse_P4 -- parse raw PBM data */ static void parse_P4(Info info) { int i; unsigned char mask; assert(info != NULL && info->buflen >= 0); assert(info->imagesize >= 0 && info->nrvals >= 0); assert(info->nrvals <= info->imagesize); for (i = 0; i < info->buflen; i++) { for (mask = 128; mask; mask >>= 1) { if (info->nrvals == info->imagesize) { delete_bytes(info, info->buflen); /* Remove all of buf */ return; /* End of image reached */ } info->image[info->nrvals++] = info->image[info->nrvals++] = info->image[info->nrvals++] = (info->buf[i] & mask) ? 0 : 255; } } delete_bytes(info, info->buflen); } /* parse_P5 -- parse raw PGM data */ static void parse_P5(Info info) { int i; assert(info != NULL && info->buflen >= 0); assert(info->imagesize >= 0 && info->nrvals >= 0); assert(info->nrvals <= info->imagesize); for (i = 0; i < info->buflen; i++) { if (info->nrvals == info->imagesize) { delete_bytes(info, info->buflen); /* errno = EFORMAT; */ /* Too much data */ return; } info->image[info->nrvals++] = info->image[info->nrvals++] = info->image[info->nrvals++] = (unsigned char) info->buf[i]; } delete_bytes(info, info->buflen); } /* parse_P6 -- parse raw PPM data */ static void parse_P6(Info info) { int i; assert(info != NULL && info->buflen >= 0); assert(info->imagesize >= 0 && info->nrvals >= 0); assert(info->nrvals <= info->imagesize); for (i = 0; i < info->buflen; i++) { if (info->nrvals == info->imagesize) { delete_bytes(info, info->buflen); /* errno = EFORMAT; */ /* Too much data */ return; } info->image[info->nrvals++] = (unsigned char) info->buf[i]; } delete_bytes(info, info->buflen); } /* redraw -- redraw window after an Expose event */ static void redraw(Widget w, XEvent *ev, String *parms, Cardinal *nparms) { Info info; int x, y, rowlen, wd, ht; assert(*nparms == 1); if (sscanf(parms[0], "%ld", &info) != 1) assert(! "wrong arguments"); assert(info); if (! XtIsRealized(info->win) || ! info->image) return; x = ev->xexpose.x; y = ev->xexpose.y; if (x >= info->width || y >= info->height) return; wd = min(ev->xexpose.width, info->width - x); ht = min(ev->xexpose.height, info->height - y); rowlen = 3 * info->width; /* 3 bytes per pixel */ if ((info->type == 3 || info->type == 6 || ! has_stdgraymap) && has_stdcolormap) { XscmDisplay (&stdcolormap, info->image + rowlen * y + 3 * x, rowlen, wd, ht, NULL, NULL, NULL, XtWindow(info->win), info->gc, x, y); } else { assert(has_stdgraymap); XscmDisplay (&stdgraymap, info->image + rowlen * y + 3 * x, rowlen, wd, ht, NULL, NULL, NULL, XtWindow(info->win), info->gc, x, y); } } /* click -- handle a mouse click on the image */ static void click(Widget w, XEvent *ev, String *parms, Cardinal *nparms) { struct {int x, y;} coords; long info; assert(*nparms == 1); if (sscanf(parms[0], "%ld", &info) != 1) assert(! "wrong arguments"); coords.x = ev->xbutton.x; coords.y = ev->xbutton.y; W3Aevent(info, POINT_SELECT, &coords); } /* initPBM -- initialize PBM viewer */ EXPORT Bool initPBM(char ***mime_types, int *nrtypes, float **prefs) { static char *types[] = { "image/x-pbm", "image/x-portable-bitmap", "image/x-pgm", "image/x-gray-bitmap", "image/x-ppm", "image/x-portable-pixmap", "image/x-pnm" "image/x-portable-anymap", }; static float pref[] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; Widget toplevel = W3Atoplevel(); Display *dpy = XtDisplay(toplevel); int screen = XScreenNumberOfScreen(XtScreen(toplevel)); static XtActionsRec actions[] = { {"PBM_expose", redraw}, {"PBM_click", click}}; *mime_types = types; *nrtypes = XtNumber(types); *prefs = pref; XtAppAddActions(XtWidgetToApplicationContext(toplevel), actions, XtNumber(actions)); has_stdcolormap = XscmFindColorSCM(dpy, screen, XSCM_ANY, &stdcolormap); has_stdgraymap = XscmFindGraySCM(dpy, screen, &stdgraymap); if (! has_stdcolormap && ! has_stdgraymap) XtAppWarning(XtWidgetToApplicationContext(W3Atoplevel()), "PBM viewer: Could find neither a standard color map \ nor a standard gray map"); return has_stdcolormap || has_stdgraymap; } EXPORT Bool openPBM(const W3ADocumentInfo doc, W3AWindow win, long id) { XGCValues dummy; Widget board, drawarea; Info info; char s[200]; static char translations[] = ": PBM_expose(%ld)\n\ ,: PBM_click(%ld)"; assert(doc.mime_type != NULL); assert(eq(doc.mime_type, "image/x-ppm") || eq(doc.mime_type, "image/x-pgm") || eq(doc.mime_type, "image/x-pbm")); assert(has_stdcolormap || has_stdgraymap); #if 0 board = XtVaCreateManagedWidget ("pbm-board", xmScrolledWindowWidgetClass, win, XmNscrollingPolicy, XmAUTOMATIC, /* XmNshadowThickness, 0, */ NULL); #if 1 drawarea = XtVaCreateManagedWidget ("pbm", xmDrawingAreaWidgetClass, board, NULL); #else drawarea = XtVaCreateManagedWidget ("pbm", xmPrimitiveWidgetClass, board, NULL); #endif #endif new(info); store(info, id); #if 0 info->win = drawarea; #else info->win = win; #endif info->image = NULL; info->width = -1; info->height = -1; info->maxval = -1; info->type = -1; info->nrvals = -1; info->buflen = 0; info->line = 0; info->eof = FALSE; info->gc = XtGetGC(info->win, 0, &dummy); sprintf(s, translations, (long) info, (long) info); XtOverrideTranslations(info->win, XtParseTranslationTable(s)); return TRUE; } #line 554 "pbm.c-nw" EXPORT int writePBM(long id, const char *buf, size_t nbytes) { Info info = find(id); int line; assert(info && info->win && info->buflen >= 0); errno = 0; if (nbytes == 0) info->eof = TRUE; if (! append_buf(info, buf, nbytes)) return -1; if (info->type < 0 && ! parse_type(info)) return errno ? -1 : nbytes; if (info->width < 0 && ! parse_width(info)) return errno ? -1 : nbytes; if (info->height < 0 && ! parse_height(info)) return errno ? -1 : nbytes; if (info->maxval < 0 && ! parse_maxval(info)) return errno ? -1 : nbytes; if (info->nrvals < 0) { if (! parse_eol(info)) return errno ? -1 : nbytes; info->nrvals = 0; info->imagesize = info->width * info->height * 3; newarray(info->image, info->imagesize); #if 0 XtVaSetValues(XtParent(XtParent(info->win)), XtNwidth, info->width, XtNheight, info->height, NULL); XtVaSetValues(XtParent(info->win), XtNwidth, info->width, XtNheight, info->height, NULL); #endif XtVaSetValues(info->win, XtNwidth, info->width, XtNheight, info->height, NULL); } switch (info->type) { case 1: parse_P1(info); break; /* PBM */ case 2: parse_P2(info); break; /* PGM */ case 3: parse_P3(info); break; /* PPM */ case 4: parse_P4(info); break; /* Raw PBM */ case 5: parse_P5(info); break; /* Raw PGM */ case 6: parse_P6(info); break; /* Raw PPM */ default: assert(! "Cannot happen"); } line = info->nrvals/3/info->width; if (XtIsRealized(info->win) && (line - info->line > DELTA || info->eof)) { if ((info->type == 3 || info->type == 6 || ! has_stdgraymap) && has_stdcolormap) { XscmDisplay (&stdcolormap, info->image + 3 * info->line * info->width, info->width * 3, info->width, line - info->line, NULL, NULL, NULL, XtWindow(info->win), info->gc, 0, info->line); } else { assert(has_stdgraymap); XscmDisplay (&stdgraymap, info->image + 3 * info->line * info->width, info->width * 3, info->width, line - info->line, NULL, NULL, NULL, XtWindow(info->win), info->gc, 0, info->line); } info->line = line; } return errno ? -1 : nbytes; } EXPORT Bool closePBM(long id) { Info info = find(id); assert(info && info->win); XtReleaseGC(info->win, info->gc); #if 0 XtDestroyWidget(XtParent(info->win)); #endif dispose(info->image); dispose(info); return TRUE; } EXPORT void eventPBM(long id, long source, long eventtype, void *params) { /* Doesn't handle events */ } EXPORT Bool infoPBM(long id, W3ADocumentInfo *doc) { /* No modifications */ return TRUE; }