--- gui.c.orig Thu Jul 28 05:53:01 1994 +++ gui.c Thu Sep 26 03:42:22 1996 @@ -12,10 +12,17 @@ #include #include +#ifdef JPEG +#include +#include +typedef unsigned char uint8; +#endif + #include #include #include #include +#include #include #include #include @@ -24,16 +31,24 @@ #include #include #include +#include #include "Plain.h" #include "gui.h" #include "colour.h" #include "scl.h" +#include "hpscan.icon" + static Widget shell, form, controlform, canvas; static Widget previewb, zoomb, scanb, quitb; static Widget resolutionl, resolutionb, resolutionm, messagel; static Widget filenamel, filenamet; +static Widget intensitylabel, intensityscr, intensityval; +static Widget contrastlabel, contrastscr, contrastval; +#ifdef JPEG +static Widget qualitylabel, qualityscr, qualityval; +#endif static XtAppContext app; Display *display; @@ -43,18 +58,57 @@ int screendepth; static GC image_gc; static Colormap cmap; +static Atom delwin; /* the delete window atom */ -static int canvas_width = 300; +static int canvas_width; static int canvas_height; static int resolution_list[] = {75, 100, 150, 200, 300, 400, 600}; +static int min_intensity, max_intensity, min_contrast, max_contrast; +static int intensity, contrast; + static char *fallback[] = { "*.canvas.translations: #override\\n\ : input() \\n\ : input() \\n\ : input() \\n\ - : input() \\n", + : input() ", + "*.preview.label: Preview", + "*.zoom.label: Zoom", + "*.scan.label: Scan", + "*.quit.label: Quit", + "*.resolution.label: Resolution", + "*.resolutionVal.borderWidth: 0", + "*.message.horizDistance: 40", + "*.message.borderWidth: 0", + "*.filenamelabel.label: Filename:", + "*.filenamelabel.borderWidth: 0", + "*.filename.width: 210", + "*.canvas.width: 300", + "*.intensityLabel.label: Intensity:", + "*.intensityLabel.borderWidth: 0", + "*.intensityVal.borderWidth: 0", + "*.intensityScrollbar.width: 120", + "*.intensityScrollbar.height: 15", + "*.intensityScrollbar.minimumThumb: 8", + "*.intensityScrollbar.topOfThumb: 0.5", + "*.contrastLabel.label: Contrast: ", + "*.contrastLabel.borderWidth: 0", + "*.contrastVal.borderWidth: 0", + "*.contrastScrollbar.width: 120", + "*.contrastScrollbar.height: 15", + "*.contrastScrollbar.minimumThumb: 8", + "*.contrastScrollbar.topOfThumb: 0.5", +#ifdef JPEG + "*.qualityLabel.label: Quality: ", + "*.qualityLabel.borderWidth: 0", + "*.qualityVal.borderWidth: 0", + "*.qualityScrollbar.width: 120", + "*.qualityScrollbar.height: 15", + "*.qualityScrollbar.minimumThumb: 8", + "*.qualityScrollbar.topOfThumb: 0.75", +#endif (char *)0 }; @@ -71,8 +125,16 @@ static void scan(Widget w, XtPointer client, XtPointer call); static void quit(Widget w, XtPointer client, XtPointer call); static void resolution(Widget w, XtPointer client, XtPointer call); +static void movethumb(Widget w, XtPointer client, XtPointer call); +static void scrollthumb(Widget w, XtPointer client, XtPointer call); +#ifdef JPEG +static int writejpg(FILE *outfile, int quality, int height, int width, + uint8 *src); +static int quality; +#endif -static struct raw_image *do_scan(int l, int t, int w, int h, int res,int type); +static struct raw_image *do_scan(int l, int t, int w, int h, int res, + int intensity, int contrast, int type); static XImage *make_image(struct raw_image *raw); static int gamma_correct(int input); static void find_box(struct raw_image *raw); @@ -82,6 +144,14 @@ static void adjust_box(int edge, int x, int y); static void set_message(char *message); +static void actionHook(Widget w, XtPointer cld, String name, XEvent *xev, + String *params, Cardinal *num_params); +static void GlobalProtoHandler(Widget w, XEvent *xev, String *p, Cardinal *n); + +static XtActionsRec actions[] = { {"GlobalProtoHandler", GlobalProtoHandler}}; + +#define TransTableMessage "WM_PROTOCOLS : GlobalProtoHandler()" + static int scanner_to_canvas_x(int x); static int scanner_to_canvas_y(int y); static int canvas_to_scanner_x(int x); @@ -118,19 +188,39 @@ { Widget w; Widget colourmap_widgets[2]; - int i; + int i, lo, hi; char buf[20]; + XtActionHookId ahid; + XWMHints *wmhints; scanner_fd = fd; shell = - XtVaAppInitialize(&app, "Scanner", 0, 0, &argc, argv, fallback, + XtVaAppInitialize(&app, "HPscan", 0, 0, &argc, argv, fallback, (String)0); + ahid = XtAppAddActionHook(app, actionHook, NULL); + display = XtDisplay(shell); screen = XtScreen(shell); + screendepth = DefaultDepthOfScreen(screen); root = RootWindowOfScreen(screen); - + + /* Get various values from the scanner for later processing. */ + GetMinIntensity(scanner_fd, &min_intensity); + GetMaxIntensity(scanner_fd, &max_intensity); + intensity = (max_intensity - min_intensity) / 2 + min_intensity; + GetMinContrast(scanner_fd, &min_contrast); + GetMaxContrast(scanner_fd, &max_contrast); + contrast = (max_contrast - min_contrast) / 2 + min_contrast; + GetMaxXExtentDecipoints(scanner_fd, &scanner_width); + GetMaxYExtentDecipoints(scanner_fd, &scanner_height); + +#ifdef JPEG + /* Set initial JPEG quality */ + quality = 75; +#endif + form = XtVaCreateManagedWidget("form", formWidgetClass, shell, (String)0); @@ -143,14 +233,12 @@ previewb = XtVaCreateManagedWidget("preview", commandWidgetClass, controlform, - XtNlabel, "Preview", (String)0); XtAddCallback(previewb, XtNcallback, preview, (XtPointer)False); zoomb = XtVaCreateManagedWidget("zoom", commandWidgetClass, controlform, XtNfromHoriz, previewb, - XtNlabel, "Zoom", XtNsensitive, False, (String)0); XtAddCallback(zoomb, XtNcallback, preview, (XtPointer)True); @@ -158,7 +246,6 @@ scanb = XtVaCreateManagedWidget("scan", commandWidgetClass, controlform, XtNfromHoriz, zoomb, - XtNlabel, "Scan", XtNsensitive, False, (String)0); XtAddCallback(scanb, XtNcallback, scan, (XtPointer)0); @@ -166,7 +253,6 @@ quitb = XtVaCreateManagedWidget("quit", commandWidgetClass, controlform, XtNfromHoriz, scanb, - XtNlabel, "Quit", (String)0); XtAddCallback(quitb, XtNcallback, quit, (XtPointer)0); @@ -175,7 +261,6 @@ controlform, XtNfromVert, previewb, XtNmenuName, "resolutionmenu", - XtNlabel, "Resolution", (String)0); resolutionm = XtVaCreatePopupShell("resolutionmenu", simpleMenuWidgetClass, @@ -196,7 +281,7 @@ sprintf(buf, "%d dpi", final_resolution); resolutionl = - XtVaCreateManagedWidget("resolution", labelWidgetClass, controlform, + XtVaCreateManagedWidget("resolutionVal", labelWidgetClass, controlform, XtNfromHoriz, resolutionb, XtNfromVert, previewb, XtNlabel, buf, @@ -205,46 +290,130 @@ messagel = XtVaCreateManagedWidget("message", labelWidgetClass, controlform, XtNfromHoriz, resolutionl, - XtNhorizDistance, 40, XtNfromVert, previewb, - /* Scan failed fits in the space */ - XtNlabel, " ", + /* Out of memory! fits in the space */ + XtNlabel, " ", (String)0); filenamel = XtVaCreateManagedWidget("filenamelabel", labelWidgetClass, controlform, XtNfromVert, resolutionb, - XtNlabel, "Filename:", (String)0); filenamet = XtVaCreateManagedWidget("filename", asciiTextWidgetClass, controlform, XtNfromHoriz, filenamel, XtNfromVert, resolutionb, - XtNwidth, canvas_width - 90, /* XXX */ +#ifdef JPEG + XtNstring, "image.jpg", +#else XtNstring, "image.ppm", +#endif XtNeditType, XawtextEdit, (String)0); - /* Create the preview area */ + intensitylabel = + XtVaCreateManagedWidget("intensityLabel", labelWidgetClass, + controlform, + XtNfromVert, filenamel, + (String)0); - GetMaxXExtentDecipoints(scanner_fd, &scanner_width); - GetMaxYExtentDecipoints(scanner_fd, &scanner_height); + sprintf(buf, "%5d", intensity); + intensityval = + XtVaCreateManagedWidget("intensityVal", labelWidgetClass, + controlform, + XtNfromHoriz, intensitylabel, + XtNfromVert, filenamel, + XtNlabel, buf, + (String)0); - canvas_height = canvas_width * ((double)scanner_height / scanner_width); + intensityscr = + XtVaCreateManagedWidget("intensityScrollbar", scrollbarWidgetClass, + controlform, + XtNfromHoriz, intensityval, + XtNfromVert, filenamel, + XtNorientation, XtorientHorizontal, + (String)0); + + XtAddCallback(intensityscr, "jumpProc", movethumb, (XtPointer)&intensity); + XtAddCallback(intensityscr, "scrollProc", scrollthumb, (XtPointer)&intensity); + + contrastlabel = + XtVaCreateManagedWidget("contrastLabel", labelWidgetClass, + controlform, + XtNfromVert, intensitylabel, + (String)0); + + sprintf(buf, "%5d", contrast); + contrastval = + XtVaCreateManagedWidget("contrastVal", labelWidgetClass, + controlform, + XtNfromHoriz, contrastlabel, + XtNfromVert, intensitylabel, + XtNlabel, buf, + (String)0); + + contrastscr = + XtVaCreateManagedWidget("contrastScrollbar", scrollbarWidgetClass, + controlform, + XtNfromHoriz, contrastval, + XtNfromVert, intensitylabel, + XtNorientation, XtorientHorizontal, + (String)0); + + XtAddCallback(contrastscr, "jumpProc", movethumb, (XtPointer)&contrast); + XtAddCallback(contrastscr, "scrollProc", scrollthumb, (XtPointer)&contrast); + +#ifdef JPEG + qualitylabel = + XtVaCreateManagedWidget("qualityLabel", labelWidgetClass, + controlform, + XtNfromVert, contrastlabel, + (String)0); + + sprintf(buf, "%5d", quality); + qualityval = + XtVaCreateManagedWidget("qualityVal", labelWidgetClass, + controlform, + XtNfromHoriz, qualitylabel, + XtNfromVert, contrastlabel, + XtNlabel, buf, + (String)0); + + qualityscr = + XtVaCreateManagedWidget("qualityScrollbar", scrollbarWidgetClass, + controlform, + XtNfromHoriz, qualityval, + XtNfromVert, contrastlabel, + XtNorientation, XtorientHorizontal, + (String)0); + + XtAddCallback(qualityscr, "jumpProc", movethumb, (XtPointer)&quality); + XtAddCallback(qualityscr, "scrollProc", scrollthumb, (XtPointer)&quality); +#endif /* JPEG */ + + /* Create the preview area */ canvas = XtVaCreateManagedWidget("canvas", plainWidgetClass, form, - XtNwidth, canvas_width, - XtNheight, canvas_height, XtNfromVert, controlform, + XtNheight, 1, (String)0); + XtVaGetValues(canvas, XtNwidth, &canvas_width, (String)0); + + /* + * The width value is settable via a resource, the height value is + * derived from it according to the scanner's aspect ratio. + */ + canvas_height = canvas_width * ((double)scanner_height / scanner_width); + XtVaSetValues(canvas, XtNheight, canvas_height, (String)0); + XtAddCallback(canvas, "exposeCallback", redraw, (XtPointer)0); XtAddCallback(canvas, "inputCallback", input, (XtPointer)0); XtVaGetValues(canvas, XtNvisual, &visual, (String)0); XtRealizeWidget(shell); - + /* Set up colour map etc */ colourmap_widgets[0] = canvas; @@ -258,6 +427,25 @@ XSetForeground(display, image_gc, colour_match(0, 0, 0, 0, 0, 0)); XSetBackground(display, image_gc, colour_match(255, 255, 255, 0, 0, 0)); + /* Make the `delete' window manager function happy. */ + delwin = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, XtWindow(shell), &delwin, 1); + XtAppAddActions(app, actions, 1); + XtOverrideTranslations(shell, XtParseTranslationTable(TransTableMessage)); + + /* Now finally, tell the WM about our icon wishes. */ + if((wmhints = XAllocWMHints()) == NULL) + XtError("Out of memory."); + wmhints->flags = IconPixmapHint; + wmhints->icon_pixmap = XCreateBitmapFromData(display, XtWindow(shell), + hpscan_bits, + hpscan_width, hpscan_height); + XmbSetWMProperties(display, XtWindow(shell), + "HPscan", "HPscan", + argv, argc, + NULL, wmhints, NULL); + XFree(wmhints); + XtAppMainLoop(app); } @@ -419,7 +607,13 @@ prev_resolution = (canvas_width * 720) / prev_width; raw = do_scan(prev_left, prev_top, prev_width, prev_height, - prev_resolution, 5); + prev_resolution, intensity, contrast, 5); + if (raw == 0) + { + set_message("Out of memory!"); + return; + } + prev_pixwidth = raw->width; prev_pixheight = raw->height; @@ -440,6 +634,8 @@ XtSetSensitive(zoomb, image != 0); XtSetSensitive(scanb, image != 0); + free(raw->data); + redraw(canvas, 0, 0); } @@ -455,28 +651,35 @@ XtVaGetValues(filenamet, XtNstring, &filename, (String)0); - raw = - do_scan(box_left, box_top, box_right-box_left+1, box_bottom-box_top+1, - final_resolution, 5); - - /* Write it out as a ppm file (nice and easy!) */ + raw = do_scan(box_left, box_top, box_right - box_left + 1, + box_bottom-box_top + 1, final_resolution, + intensity, contrast, 5); if(raw) { f = fopen(filename, "w"); - if(!f) + if (!f) { XBell(display, 0); fprintf(stderr, "can't open %s for writing\n", filename); return; } +#ifdef JPEG + set_message("JPEGing..."); + writejpg(f, quality, raw->height, raw->width, raw->data); +#else + set_message("PPMing..."); + /* Write it out as a ppm file (nice and easy!) */ fprintf(f, "P6\n%d %d\n255\n", raw->width, raw->height); fwrite(raw->data, 1, raw->linebytes * raw->height, f); - +#endif fclose(f); + free(raw->data); + set_message("Done!"); } - + else + set_message("Out of memory!"); } /* @@ -502,17 +705,85 @@ XtVaSetValues(resolutionl, XtNlabel, buf, (String)0); } +/* + * Move the thumb of a scrollbar, update the according value as we go. + */ + +static void movethumb(Widget w, XtPointer client, XtPointer call) +{ + char buf[20]; + float percent = *(float *)call; + int *val_p = (int *)client; + + if (percent > 1.0) + percent = 1.0; + else if (percent < 0.0) + percent = 0.0; + + if (val_p == &intensity) { + *val_p = (max_intensity - min_intensity) * percent + min_intensity; + sprintf(buf, "%5d", *val_p); + XtVaSetValues(intensityval, XtNlabel, buf, (String)0); + } else if (val_p == &contrast) { + *val_p = (max_contrast - min_contrast) * percent + min_contrast; + sprintf(buf, "%5d", *val_p); + XtVaSetValues(contrastval, XtNlabel, buf, (String)0); +#ifdef JPEG + } else if (val_p == &quality) { + *val_p = (int)(100 * percent); + sprintf(buf, "%5d", *val_p); + XtVaSetValues(qualityval, XtNlabel, buf, (String)0); +#endif + } else + XtAppError(app, "unknown client in movethumb()"); +} + +/* + * Almost the same as above, but called by the scrollProc callback. + */ + +static void scrollthumb(Widget w, XtPointer client, XtPointer call) +{ + char buf[20]; + int position = (int)call; + int *val_p = (int *)client; + float shown, topofthumb; + + XtVaGetValues(w, + XtNshown, &shown, + XtNtopOfThumb, &topofthumb, + (String)0); + topofthumb -= 0.0001 * (float)position; + if (topofthumb < 0.0) topofthumb = 0.0; + else if (topofthumb > 1.0) topofthumb = 1.0; + XawScrollbarSetThumb(w, topofthumb, shown); + + if (val_p == &intensity) { + *val_p = (max_intensity - min_intensity) * topofthumb + min_intensity; + sprintf(buf, "%5d", *val_p); + XtVaSetValues(intensityval, + XtNlabel, buf, + (String)0); + } else if (val_p = &contrast) { + *val_p = (max_contrast - min_contrast) * topofthumb + min_contrast; + sprintf(buf, "%5d", *val_p); + XtVaSetValues(contrastval, + XtNlabel, buf, + (String)0); + } else + XtAppError(app, "unknown client in movethumb()"); +} + /* * Perform a scan, returning an XImage (return struct is statically allocated). */ -static struct raw_image *do_scan(int l, int t, int w, int h, int res, int type) +static struct raw_image * +do_scan(int l, int t, int w, int h, int res, int intensity, + int contrast, int type) { static struct raw_image raw; - if(raw.data) - free(raw.data); - SetXPositionDecipoints(scanner_fd, l); SetYPositionDecipoints(scanner_fd, t); SetXExtentDecipoints(scanner_fd, w); @@ -524,14 +795,17 @@ SetXResolution(scanner_fd, res); SetYResolution(scanner_fd, res); + SetIntensity(scanner_fd, intensity); + SetContrast(scanner_fd, contrast); + GetPixelsPerLine(scanner_fd, &raw.width); GetLinesPerScan(scanner_fd, &raw.height); GetBytesPerLine(scanner_fd, &raw.linebytes); raw.type = type; - raw.data = malloc(raw.height * raw.linebytes); + raw.data = malloc(raw.height * raw.width * 4); if(!raw.data) { - fprintf(stderr, "Can't malloc %d bytes\n", raw.height * raw.linebytes); + fprintf(stderr, "Can't malloc %d bytes\n", raw.height * raw.width * 4); return 0; } @@ -555,26 +829,44 @@ XImage *im; unsigned char *newdata, *p; int x, y; + int pad; + switch(screendepth) + { + case 1: + case 8: + pad = 8; break; + case 15: + case 16: + pad = 16; break; + case 24: + case 32: + pad = 32; break; + default: + fprintf(stderr, "Don't know how to handle depth %d\n", screendepth); + return 0; + } + if(!raw) return 0; switch(raw->type) { case 5: /* 24 bit colour */ - newdata = malloc(raw->width * raw->height); + newdata = malloc(raw->width * raw->height * (pad / 8)); if(!newdata) { fprintf(stderr, "Can't malloc %d bytes\n", raw->width*raw->height); return 0; } - im = XCreateImage(display, visual, 8, ZPixmap, 0, newdata, - raw->width, raw->height, 8, raw->width); + im = XCreateImage(display, visual, screendepth, ZPixmap, 0, newdata, + raw->width, raw->height, pad, + raw->width * (pad / 8)); /* Convert to fixed colour map using simple error propagation */ p = raw->data; - for(y=0; yheight; y++) + for (y = 0; y < raw->height; y++) { int r, g, b, r_err = 0, g_err = 0, b_err = 0; @@ -646,7 +938,7 @@ for(y=MARGIN; yheight-MARGIN; y++) { - p = raw->data + (y * raw->width + MARGIN) * 3; + p = raw->data + y * raw->width + MARGIN * 3; for(x=MARGIN; xwidth-MARGIN; x++) { @@ -741,6 +1033,24 @@ XFlush(display); } + +/* + * Window manager legacy. + */ + +static void +actionHook(Widget w, XtPointer cld, String name, XEvent *xev, + String *params, Cardinal *num_params) +{ +} + +static void GlobalProtoHandler(Widget w, XEvent *xev, String *p, Cardinal *n) +{ + if (xev->xclient.data.l[0] == delwin) { + if (w == shell) exit(EXIT_SUCCESS); else XtPopdown(w); + } +} + /* * Functions for converting between a pixel position in the canvas * and a decipoint position on the scanner. @@ -766,3 +1076,35 @@ return prev_top + (y * 720 + prev_resolution/2) / prev_resolution; } +#ifdef JPEG +static int +writejpg(FILE *outfile, int quality, int height, int width, uint8 *src) +{ + char *copybuf = NULL; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; + int i; + uint8 *p; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, outfile); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = src; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + src += (width * 3); + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); +} +#endif