aboutsummaryrefslogtreecommitdiffstats
path: root/viewer.c
diff options
context:
space:
mode:
Diffstat (limited to 'viewer.c')
-rw-r--r--viewer.c962
1 files changed, 962 insertions, 0 deletions
diff --git a/viewer.c b/viewer.c
new file mode 100644
index 0000000..0bc81f5
--- /dev/null
+++ b/viewer.c
@@ -0,0 +1,962 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/cursorfont.h>
+#include <X11/extensions/XShm.h>
+
+#include "ida.h"
+#include "x11.h"
+#include "dither.h"
+#include "readers.h"
+#include "viewer.h"
+#include "hex.h"
+#include "idaconfig.h"
+
+/* ----------------------------------------------------------------------- */
+
+#define POINTER_NORMAL 0
+#define POINTER_BUSY 1
+#define POINTER_PICK 2
+#define RUBBER_NEW 3
+#define RUBBER_MOVE 4
+#define RUBBER_X1 5
+#define RUBBER_Y1 6
+#define RUBBER_X2 7
+#define RUBBER_Y2 8
+
+#define RUBBER_RANGE 6
+#define RUBBER_INTERVAL 100
+
+#define PROCESS_LINES 16
+
+int debug;
+Cursor ptrs[POINTER_COUNT];
+
+/* ----------------------------------------------------------------------- */
+
+Pixmap image_to_pixmap(struct ida_image *img)
+{
+ unsigned char line[256],*src;
+ XImage *ximage;
+ void *shm;
+ Pixmap pix;
+ GC gc;
+ unsigned int x,y;
+
+ ximage = x11_create_ximage(app_shell, img->i.width, img->i.height, &shm);
+ for (y = 0; y < img->i.height; y++) {
+ src = img->data + 3*y*img->i.width;
+ if (display_type == PSEUDOCOLOR) {
+ dither_line(src, line, y, img->i.width);
+ for (x = 0; x < img->i.width; x++)
+ XPutPixel(ximage, x, y, x11_map[line[x]]);
+ } else {
+ for (x = 0; x < img->i.width; x++, src += 3) {
+ pix = x11_lut_red[src[0]] |
+ x11_lut_green[src[1]] |
+ x11_lut_blue[src[2]];
+ XPutPixel(ximage, x, y, pix);
+ }
+ }
+ }
+ pix = XCreatePixmap(dpy,XtWindow(app_shell),img->i.width, img->i.height,
+ DefaultDepthOfScreen(XtScreen(app_shell)));
+ gc = XCreateGC(dpy, pix, 0, NULL);
+ XPUTIMAGE(dpy, pix, gc, ximage, 0, 0, 0, 0, img->i.width, img->i.height);
+ XFreeGC(dpy, gc);
+ x11_destroy_ximage(app_shell, ximage, shm);
+ return pix;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int viewer_i2s(int zoom, int val)
+{
+ if (0 > zoom)
+ return val/(-zoom+1);
+ if (0 < zoom)
+ return val*(zoom+1);
+ return val;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void
+viewer_renderline(struct ida_viewer *ida, char *scanline)
+{
+ unsigned char *src,*dst,*rgb;
+ unsigned long pix;
+ unsigned int x,s,scrline;
+
+ src = scanline;
+
+ if (0 == ida->zoom) {
+ /* as-is */
+ if (display_type == PSEUDOCOLOR) {
+ dst = ida->dither_line;
+ dither_line(src, dst, ida->line, ida->scrwidth);
+ for (x = 0; x < ida->scrwidth; x++, dst++)
+ XPutPixel(ida->ximage, x, ida->line, x11_map[*dst]);
+ } else {
+ for (x = 0; x < ida->scrwidth; x++, src += 3) {
+ pix = x11_lut_red[src[0]] |
+ x11_lut_green[src[1]] |
+ x11_lut_blue[src[2]];
+ XPutPixel(ida->ximage, x, ida->line, pix);
+ }
+ }
+
+ } else if (ida->zoom < 0) {
+ /* zoom out */
+ s = -ida->zoom+1;
+ if (s-1 != (ida->line % s))
+ return;
+ scrline = ida->line/s;
+ if (display_type == PSEUDOCOLOR) {
+ rgb = ida->rgb_line;
+ for (x = 0; x < ida->scrwidth; x++, rgb += 3, src += 3*s) {
+ rgb[0] = src[0];
+ rgb[1] = src[1];
+ rgb[2] = src[2];
+ }
+ rgb = ida->rgb_line;
+ dst = ida->dither_line;
+ dither_line(rgb, dst, scrline, ida->scrwidth);
+ for (x = 0; x < ida->scrwidth; x++, dst++)
+ XPutPixel(ida->ximage, x, scrline, x11_map[*dst]);
+ } else {
+#if 0
+ /* just drop pixels */
+ for (x = 0; x < ida->scrwidth; x++, src += 3*s) {
+ pix = x11_lut_red[src[0]] |
+ x11_lut_green[src[1]] |
+ x11_lut_blue[src[2]];
+ XPutPixel(ida->ximage, x, scrline, pix);
+ }
+#else
+ /* horizontal interpolation (vertical is much harder ...) */
+ for (x = 0; x < ida->scrwidth; x++, src += 3*s) {
+ int red,green,blue,count,ix;
+ red = 0;
+ green = 0;
+ blue = 0;
+ count = 0;
+ for (ix = 0; ix < 3*s; ix += 3) {
+ red += src[ix+0];
+ green += src[ix+1];
+ blue += src[ix+2];
+ count += 1;
+ }
+ pix = x11_lut_red[red/count] |
+ x11_lut_green[green/count] |
+ x11_lut_blue[blue/count];
+ XPutPixel(ida->ximage, x, scrline, pix);
+ }
+#endif
+ }
+
+ } else {
+ /* zoom in */
+ s = ida->zoom+1;
+ if (display_type == PSEUDOCOLOR) {
+ rgb = ida->rgb_line;
+ for (x = 0; x < ida->scrwidth; rgb += 3) {
+ rgb[0] = src[0];
+ rgb[1] = src[1];
+ rgb[2] = src[2];
+ x++;
+ if (0 == (x%s))
+ src += 3;
+ }
+ for (scrline = ida->line*s; scrline < ida->line*s+s; scrline++) {
+ rgb = ida->rgb_line;
+ dst = ida->dither_line;
+ dither_line(rgb, dst, scrline, ida->scrwidth);
+ for (x = 0; x < ida->scrwidth; x++, dst++)
+ XPutPixel(ida->ximage, x, scrline, x11_map[*dst]);
+ }
+ } else {
+ for (scrline = ida->line*s; scrline < ida->line*s+s; scrline++) {
+ src = scanline;
+ for (x = 0; x < ida->scrwidth; src += 3) {
+ unsigned int i;
+ pix = x11_lut_red[src[0]] |
+ x11_lut_green[src[1]] |
+ x11_lut_blue[src[2]];
+ for (i = 0; i < s; i++, x++)
+ XPutPixel(ida->ximage, x, scrline, pix);
+ }
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void
+viewer_cleanup(struct ida_viewer *ida)
+{
+ if (ida->load_read) {
+ ida->load_done(ida->load_data);
+ ida->load_line = 0;
+ ida->load_read = NULL;
+ ida->load_done = NULL;
+ ida->load_data = NULL;
+ }
+ if (ida->op_work) {
+ ida->op_done(ida->op_data);
+ ida->op_line = 0;
+ ida->op_work = NULL;
+ ida->op_done = NULL;
+ ida->op_data = NULL;
+ if (ida->op_src.data) {
+ if (ida->undo.data) {
+ fprintf(stderr,"have undo buffer /* shouldn't happen */");
+ free(ida->undo.data);
+ }
+ ida->undo = ida->op_src;
+ memset(&ida->op_src,0,sizeof(ida->op_src));
+ }
+ }
+}
+
+static Boolean
+viewer_workproc(XtPointer client_data)
+{
+ struct ida_viewer *ida = client_data;
+ unsigned int start,end;
+ char *scanline;
+
+ start = ida->line;
+ end = ida->line + ida->steps;
+ if (end > ida->img.i.height)
+ end = ida->img.i.height;
+
+ /* image loading */
+ if (ida->load_read) {
+ for (ida->line = start; ida->line < end; ida->line++) {
+ if (ida->load_line > ida->line)
+ continue;
+ scanline = ida->img.data + ida->img.i.width * ida->load_line * 3;
+ ida->load_read(scanline,ida->load_line,ida->load_data);
+ ida->load_line++;
+ }
+ }
+
+ /* image processing */
+ if (ida->op_work && 0 == ida->op_preview) {
+ for (ida->line = start; ida->line < end; ida->line++) {
+ if (ida->op_line > ida->line)
+ continue;
+ scanline = ida->img.data + ida->img.i.width * ida->op_line * 3;
+ ida->op_work(&ida->op_src,&ida->op_rect,
+ scanline,ida->op_line,ida->op_data);
+ ida->op_line++;
+ }
+ }
+
+ /* image rendering */
+ if (ida->op_work && ida->op_preview) {
+ for (ida->line = start; ida->line < end; ida->line++) {
+ ida->op_line = ida->line;
+ ida->op_work(&ida->img,&ida->op_rect,
+ ida->preview_line,ida->line,ida->op_data);
+ viewer_renderline(ida,ida->preview_line);
+ }
+ } else {
+ for (ida->line = start; ida->line < end; ida->line++) {
+ scanline = ida->img.data + ida->img.i.width * ida->line * 3;
+ viewer_renderline(ida,scanline);
+ }
+ }
+
+ /* trigger redraw */
+ XClearArea(XtDisplay(ida->widget), XtWindow(ida->widget),
+ 0, viewer_i2s(ida->zoom,start),
+ ida->scrwidth, viewer_i2s(ida->zoom,ida->steps), True);
+
+ /* all done ? */
+ if (ida->line == ida->img.i.height) {
+ viewer_cleanup(ida);
+ ida->wproc = 0;
+#if 1
+ if (args.testload)
+ XtCallActionProc(ida->widget,"Next",NULL,NULL,0);
+#endif
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void viewer_workstart(struct ida_viewer *ida)
+{
+ /* (re-) start */
+ ida->line = 0;
+ if (!ida->wproc)
+ ida->wproc = XtAppAddWorkProc(app_context,viewer_workproc,ida);
+}
+
+static void viewer_workstop(struct ida_viewer *ida)
+{
+ if (!ida->wproc)
+ return;
+
+ viewer_cleanup(ida);
+ XtRemoveWorkProc(ida->wproc);
+ ida->wproc = 0;
+}
+
+static void viewer_workfinish(struct ida_viewer *ida)
+{
+ char *scanline;
+
+ if (ida->load_read) {
+ for (ida->line = ida->load_line; ida->line < ida->img.i.height;) {
+ scanline = ida->img.data + ida->img.i.width * ida->line * 3;
+ ida->load_read(scanline,ida->load_line,ida->load_data);
+ ida->line++;
+ ida->load_line++;
+ }
+ }
+ if (ida->op_work && 0 == ida->op_preview) {
+ for (ida->line = ida->op_line; ida->line < ida->img.i.height;) {
+ scanline = ida->img.data + ida->img.i.width * ida->line * 3;
+ ida->op_work(&ida->op_src,&ida->op_rect,
+ scanline,ida->op_line,ida->op_data);
+ ida->line++;
+ ida->op_line++;
+ }
+ }
+ viewer_workstop(ida);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void
+viewer_new_view(struct ida_viewer *ida)
+{
+ if (NULL != ida->ximage)
+ x11_destroy_ximage(ida->widget,ida->ximage,ida->ximage_shm);
+ if (NULL != ida->rgb_line)
+ free(ida->rgb_line);
+ if (NULL != ida->dither_line)
+ free(ida->dither_line);
+ if (NULL != ida->preview_line)
+ free(ida->preview_line);
+
+ ida->scrwidth = viewer_i2s(ida->zoom,ida->img.i.width);
+ ida->scrheight = viewer_i2s(ida->zoom,ida->img.i.height);
+ ida->steps = PROCESS_LINES;
+ if (ida->zoom < 0)
+ while ((ida->steps % (-ida->zoom+1)) != 0)
+ ida->steps++;
+
+ ida->rgb_line = malloc(ida->scrwidth*3);
+ ida->dither_line = malloc(ida->scrwidth);
+ ida->preview_line = malloc(ida->img.i.width*3);
+ ida->ximage = x11_create_ximage(ida->widget, ida->scrwidth, ida->scrheight,
+ &ida->ximage_shm);
+ if (NULL == ida->ximage) {
+ ida->zoom--;
+ return viewer_new_view(ida);
+ }
+ XtVaSetValues(ida->widget,
+ XtNwidth, ida->scrwidth,
+ XtNheight, ida->scrheight,
+ NULL);
+ viewer_workstart(ida);
+}
+
+static void
+viewer_timeout(XtPointer client_data, XtIntervalId *id);
+
+static int
+viewer_rubber_draw(struct ida_viewer *ida)
+{
+ XGCValues values;
+ struct ida_rect r = ida->current;
+ int x,y,w,h;
+
+ values.function = GXxor;
+ values.foreground = ida->mask;
+ XChangeGC(dpy,ida->wgc,GCFunction|GCForeground,&values);
+ if (r.x1 < r.x2) {
+ x = viewer_i2s(ida->zoom,r.x1);
+ w = viewer_i2s(ida->zoom,r.x2 - r.x1);
+ } else {
+ x = viewer_i2s(ida->zoom,r.x2);
+ w = viewer_i2s(ida->zoom,r.x1 - r.x2);
+ }
+ if (r.y1 < r.y2) {
+ y = viewer_i2s(ida->zoom,r.y1);
+ h = viewer_i2s(ida->zoom,r.y2 - r.y1);
+ } else {
+ y = viewer_i2s(ida->zoom,r.y2);
+ h = viewer_i2s(ida->zoom,r.y1 - r.y2);
+ }
+ if (0 == h && 0 == w)
+ return 0;
+ if (w)
+ w--;
+ if (h)
+ h--;
+ XDrawRectangle(dpy,XtWindow(ida->widget),ida->wgc,x,y,w,h);
+ return 1;
+}
+
+static void
+viewer_rubber_off(struct ida_viewer *ida)
+{
+ if (ida->marked)
+ viewer_rubber_draw(ida);
+ ida->marked = 0;
+ if (ida->timer)
+ XtRemoveTimeOut(ida->timer);
+ ida->timer = 0;
+}
+
+static void
+viewer_rubber_on(struct ida_viewer *ida)
+{
+ ida->marked = viewer_rubber_draw(ida);
+ if (ida->marked)
+ ida->timer = XtAppAddTimeOut(app_context,RUBBER_INTERVAL,
+ viewer_timeout,ida);
+}
+
+static void
+viewer_timeout(XtPointer client_data, XtIntervalId *id)
+{
+ struct ida_viewer *ida = client_data;
+
+ ida->timer = 0;
+ viewer_rubber_off(ida);
+ ida->mask <<= 1;
+ if ((ida->mask & 0x10) == 0x10)
+ ida->mask |= 0x01;
+ viewer_rubber_on(ida);
+}
+
+static void
+viewer_redraw(Widget widget, XtPointer client_data,
+ XEvent *ev, Boolean *cont)
+{
+ struct ida_viewer *ida = client_data;
+ XExposeEvent *event;
+ XGCValues values;
+
+ if (ev->type != Expose)
+ return;
+ event = (XExposeEvent*)ev;
+
+ if (NULL == ida->ximage)
+ return;
+ if (event->x + event->width > (int)ida->scrwidth)
+ return;
+ if (event->y + event->height > (int)ida->scrheight)
+ return;
+ if (NULL == ida->wgc)
+ ida->wgc = XCreateGC(XtDisplay(widget), XtWindow(widget), 0, NULL);
+
+ viewer_rubber_off(ida);
+ values.function = GXcopy;
+ XChangeGC(dpy,ida->wgc,GCFunction,&values);
+ XPUTIMAGE(XtDisplay(ida->widget), XtWindow(widget),
+ ida->wgc, ida->ximage,
+ event->x, event->y, event->x, event->y,
+ event->width, event->height);
+ viewer_rubber_on(ida);
+}
+
+static int
+viewer_pos2state(struct ida_viewer *ida, int x, int y)
+{
+ int x1,x2,y1,y2;
+
+ if (POINTER_PICK == ida->state)
+ return ida->state;
+
+ x1 = viewer_i2s(ida->zoom,ida->current.x1);
+ x2 = viewer_i2s(ida->zoom,ida->current.x2);
+ y1 = viewer_i2s(ida->zoom,ida->current.y1);
+ y2 = viewer_i2s(ida->zoom,ida->current.y2);
+ if ((x1 < x && x < x2) || (x2 < x && x < x1)) {
+ if (y1-RUBBER_RANGE < y && y < y1+RUBBER_RANGE)
+ return RUBBER_Y1;
+ if (y2-RUBBER_RANGE < y && y < y2+RUBBER_RANGE)
+ return RUBBER_Y2;
+ }
+ if ((y1 < y && y < y2) || (y2 < y && y < y1)) {
+ if (x1-RUBBER_RANGE < x && x < x1+RUBBER_RANGE)
+ return RUBBER_X1;
+ if (x2-RUBBER_RANGE < x && x < x2+RUBBER_RANGE)
+ return RUBBER_X2;
+ }
+ if (((x1 < x && x < x2) || (x2 < x && x < x1)) &&
+ ((y1 < y && y < y2) || (y2 < y && y < y1)))
+ return RUBBER_MOVE;
+ return RUBBER_NEW;
+}
+
+static void
+viewer_mouse(Widget widget, XtPointer client_data,
+ XEvent *ev, Boolean *cont)
+{
+ struct ida_viewer *ida = client_data;
+ int state = POINTER_NORMAL;
+ unsigned char *pix;
+ int x,y;
+
+ viewer_rubber_off(ida);
+
+ switch (ev->type) {
+ case ButtonPress:
+ {
+ XButtonEvent *eb = (XButtonEvent*)ev;
+
+ if (eb->button != Button1)
+ goto out;
+ ida->state = viewer_pos2state(ida,eb->x,eb->y);
+ switch (ida->state) {
+ case POINTER_PICK:
+ x = viewer_i2s(-ida->zoom,eb->x);
+ y = viewer_i2s(-ida->zoom,eb->y);
+ pix = ida->img.data + ida->img.i.width*y*3 + x*3;
+ ida->pick_cb(x,y,pix,ida->pick_data);
+ ida->pick_cb = NULL;
+ ida->pick_data = NULL;
+ ida->state = POINTER_NORMAL;
+ state = POINTER_NORMAL;
+ break;
+ case RUBBER_NEW:
+ ida->mask = 0x33333333;
+ ida->current.x1 = ida->current.x2 = viewer_i2s(-ida->zoom,eb->x);
+ ida->current.y1 = ida->current.y2 = viewer_i2s(-ida->zoom,eb->y);
+ break;
+ case RUBBER_MOVE:
+ ida->last_x = viewer_i2s(-ida->zoom,eb->x);
+ ida->last_y = viewer_i2s(-ida->zoom,eb->y);
+ break;
+ case RUBBER_X1:
+ ida->current.x1 = viewer_i2s(-ida->zoom,eb->x);
+ break;
+ case RUBBER_Y1:
+ ida->current.y1 = viewer_i2s(-ida->zoom,eb->y);
+ break;
+ case RUBBER_X2:
+ ida->current.x2 = viewer_i2s(-ida->zoom,eb->x);
+ break;
+ case RUBBER_Y2:
+ ida->current.y2 = viewer_i2s(-ida->zoom,eb->y);
+ break;
+ }
+ state = ida->state;
+ break;
+ }
+ case MotionNotify:
+ {
+ XMotionEvent *em = (XMotionEvent*)ev;
+
+ if (!(em->state & Button1Mask)) {
+ state = viewer_pos2state(ida,em->x,em->y);
+ goto out;
+ }
+ switch (ida->state) {
+ case RUBBER_NEW:
+ ida->current.x2 = viewer_i2s(-ida->zoom,em->x);
+ ida->current.y2 = viewer_i2s(-ida->zoom,em->y);
+ if (em->state & ShiftMask) {
+ /* square selection */
+ int xlen,ylen;
+ xlen = abs(ida->current.x1 - ida->current.x2);
+ ylen = abs(ida->current.y1 - ida->current.y2);
+ if (ylen > xlen) {
+ if (ida->current.x1 < ida->current.x2)
+ ida->current.x2 -= (xlen - ylen);
+ else
+ ida->current.x2 += (xlen - ylen);
+ } else {
+ if (ida->current.y1 < ida->current.y2)
+ ida->current.y2 -= (ylen - xlen);
+ else
+ ida->current.y2 += (ylen - xlen);
+ }
+ }
+ break;
+ case RUBBER_MOVE:
+ x = viewer_i2s(-ida->zoom,em->x);
+ y = viewer_i2s(-ida->zoom,em->y);
+ ida->current.x1 += (x - ida->last_x);
+ ida->current.x2 += (x - ida->last_x);
+ ida->current.y1 += (y - ida->last_y);
+ ida->current.y2 += (y - ida->last_y);
+ ida->last_x = x;
+ ida->last_y = y;
+ break;
+ case RUBBER_X1:
+ ida->current.x1 = viewer_i2s(-ida->zoom,em->x);
+ break;
+ case RUBBER_Y1:
+ ida->current.y1 = viewer_i2s(-ida->zoom,em->y);
+ break;
+ case RUBBER_X2:
+ ida->current.x2 = viewer_i2s(-ida->zoom,em->x);
+ break;
+ case RUBBER_Y2:
+ ida->current.y2 = viewer_i2s(-ida->zoom,em->y);
+ break;
+ }
+ state = ida->state;
+ break;
+ }
+ case ButtonRelease:
+ {
+ XButtonEvent *eb = (XButtonEvent*)ev;
+
+ if (eb->button != Button1)
+ goto out;
+ ida->state = POINTER_NORMAL;
+ state = ida->state;
+ break;
+ }
+ }
+
+ if (ida->current.x1 < 0)
+ ida->current.x1 = 0;
+ if (ida->current.x1 > ida->img.i.width)
+ ida->current.x1 = ida->img.i.width;
+ if (ida->current.x2 < 0)
+ ida->current.x2 = 0;
+ if (ida->current.x2 > ida->img.i.width)
+ ida->current.x2 = ida->img.i.width;
+ if (ida->current.y1 < 0)
+ ida->current.y1 = 0;
+ if (ida->current.y1 > ida->img.i.height)
+ ida->current.y1 = ida->img.i.height;
+ if (ida->current.y2 < 0)
+ ida->current.y2 = 0;
+ if (ida->current.y2 > ida->img.i.height)
+ ida->current.y2 = ida->img.i.height;
+
+ out:
+ XDefineCursor(dpy, XtWindow(widget), ptrs[state]);
+ viewer_rubber_on(ida);
+}
+
+/* ----------------------------------------------------------------------- */
+/* public stuff */
+
+void viewer_pick(struct ida_viewer *ida, viewer_pick_cb cb, XtPointer data)
+{
+ if (POINTER_NORMAL != ida->state)
+ return;
+ if (debug)
+ fprintf(stderr,"viewer_pick\n");
+ ida->state = POINTER_PICK;
+ ida->pick_cb = cb;
+ ida->pick_data = data;
+}
+
+void viewer_unpick(struct ida_viewer *ida)
+{
+ if (POINTER_PICK != ida->state)
+ return;
+ if (debug)
+ fprintf(stderr,"viewer_unpick\n");
+ ida->state = POINTER_NORMAL;
+ ida->pick_cb = NULL;
+ ida->pick_data = NULL;
+}
+
+void
+viewer_autozoom(struct ida_viewer *ida)
+{
+ if (GET_AUTOZOOM()) {
+ ida->zoom = 0;
+ while (XtScreen(ida->widget)->width < viewer_i2s(ida->zoom,ida->img.i.width) ||
+ XtScreen(ida->widget)->height < viewer_i2s(ida->zoom,ida->img.i.height))
+ ida->zoom--;
+ }
+ viewer_new_view(ida);
+}
+
+void
+viewer_setzoom(struct ida_viewer *ida, int zoom)
+{
+ ida->zoom = zoom;
+ viewer_new_view(ida);
+}
+
+static void
+viewer_op_rect(struct ida_viewer *ida)
+{
+ if (ida->current.x1 == ida->current.x2 &&
+ ida->current.y1 == ida->current.y2) {
+ /* full image */
+ ida->op_rect.x1 = 0;
+ ida->op_rect.x2 = ida->img.i.width;
+ ida->op_rect.y1 = 0;
+ ida->op_rect.y2 = ida->img.i.height;
+ return;
+ } else {
+ /* have selection */
+ if (ida->current.x1 < ida->current.x2) {
+ ida->op_rect.x1 = ida->current.x1;
+ ida->op_rect.x2 = ida->current.x2;
+ } else {
+ ida->op_rect.x1 = ida->current.x2;
+ ida->op_rect.x2 = ida->current.x1;
+ }
+ if (ida->current.y1 < ida->current.y2) {
+ ida->op_rect.y1 = ida->current.y1;
+ ida->op_rect.y2 = ida->current.y2;
+ } else {
+ ida->op_rect.y1 = ida->current.y2;
+ ida->op_rect.y2 = ida->current.y1;
+ }
+ }
+}
+
+int
+viewer_start_op(struct ida_viewer *ida, struct ida_op *op, void *parm)
+{
+ struct ida_image dst;
+
+ ptr_busy();
+ viewer_workfinish(ida);
+ viewer_rubber_off(ida);
+
+ /* try init */
+ viewer_op_rect(ida);
+ if (debug)
+ fprintf(stderr,"viewer_start_op: init %s(%p)\n",op->name,parm);
+ ida->op_data = op->init(&ida->img,&ida->op_rect,&dst.i,parm);
+ ptr_idle();
+ if (NULL == ida->op_data)
+ return -1;
+ dst.data = malloc(dst.i.width * dst.i.height * 3);
+
+ /* prepare background processing */
+ if (ida->undo.data) {
+ free(ida->undo.data);
+ memset(&ida->undo,0,sizeof(ida->undo));
+ }
+ if (ida->op_src.data) {
+ fprintf(stderr,"have op_src buffer /* shouldn't happen */");
+ free(ida->op_src.data);
+ }
+ ida->op_src = ida->img;
+ ida->img = dst;
+ ida->op_line = 0;
+ ida->op_work = op->work;
+ ida->op_done = op->done;
+ ida->op_preview = 0;
+
+ if (ida->op_src.i.width != ida->img.i.width ||
+ ida->op_src.i.height != ida->img.i.height) {
+ memset(&ida->current,0,sizeof(ida->current));
+ viewer_autozoom(ida);
+ } else
+ viewer_new_view(ida);
+ return 0;
+}
+
+int
+viewer_undo(struct ida_viewer *ida)
+{
+ int resize;
+
+ viewer_workfinish(ida);
+ if (NULL == ida->undo.data)
+ return -1;
+ viewer_rubber_off(ida);
+ memset(&ida->current,0,sizeof(ida->current));
+
+ resize = (ida->undo.i.width != ida->img.i.width ||
+ ida->undo.i.height != ida->img.i.height);
+ free(ida->img.data);
+ ida->img = ida->undo;
+ memset(&ida->undo,0,sizeof(ida->undo));
+
+ if (resize)
+ viewer_autozoom(ida);
+ else
+ viewer_new_view(ida);
+ return 0;
+}
+
+int
+viewer_start_preview(struct ida_viewer *ida, struct ida_op *op, void *parm)
+{
+ struct ida_image dst;
+
+ viewer_workfinish(ida);
+
+ /* try init */
+ viewer_op_rect(ida);
+ ida->op_data = op->init(&ida->img,&ida->op_rect,&dst.i,parm);
+ if (NULL == ida->op_data)
+ return -1;
+
+ /* prepare background preview */
+ ida->op_line = 0;
+ ida->op_work = op->work;
+ ida->op_done = op->done;
+ ida->op_preview = 1;
+
+ viewer_workstart(ida);
+ return 0;
+}
+
+int
+viewer_cancel_preview(struct ida_viewer *ida)
+{
+ viewer_workstop(ida);
+ viewer_workstart(ida);
+ return 0;
+}
+
+int
+viewer_loader_start(struct ida_viewer *ida, struct ida_loader *loader,
+ FILE *fp, char *filename, unsigned int page)
+{
+ struct ida_image_info info;
+ void *data;
+
+ /* init loader */
+ ptr_busy();
+ memset(&info,0,sizeof(info));
+ data = loader->init(fp,filename,page,&info,0);
+ ptr_idle();
+ if (NULL == data) {
+ fprintf(stderr,"loading %s [%s] FAILED\n",filename,loader->name);
+ if (fp)
+ hex_display(filename);
+ return -1;
+ }
+
+ /* ok, going to load new image */
+ viewer_workstop(ida);
+ viewer_rubber_off(ida);
+ memset(&ida->current,0,sizeof(ida->current));
+ if (ida->undo.data) {
+ free(ida->undo.data);
+ memset(&ida->undo,0,sizeof(ida->undo));
+ }
+ if (NULL != ida->img.data)
+ free(ida->img.data);
+ ida->file = filename;
+ ida->img.i = info;
+ ida->img.data = malloc(ida->img.i.width * ida->img.i.height * 3);
+
+ /* prepare background loading */
+ ida->load_line = 0;
+ ida->load_read = loader->read;
+ ida->load_done = loader->done;
+ ida->load_data = data;
+
+ viewer_autozoom(ida);
+ return info.npages;
+}
+
+int
+viewer_loadimage(struct ida_viewer *ida, char *filename, unsigned int page)
+{
+ struct list_head *item;
+ struct ida_loader *loader;
+ char blk[512];
+ FILE *fp;
+
+ if (NULL == (fp = fopen(filename, "r"))) {
+ fprintf(stderr,"fopen %s: %s\n",filename,strerror(errno));
+ return -1;
+ }
+ if (debug)
+ fprintf(stderr,"load: %s\n",filename);
+ memset(blk,0,sizeof(blk));
+ fread(blk,1,sizeof(blk),fp);
+ rewind(fp);
+
+ /* pick loader */
+ list_for_each(item,&loaders) {
+ loader = list_entry(item, struct ida_loader, list);
+#if 0
+ if (NULL == loader->magic)
+ break;
+#else
+ if (NULL == loader->magic)
+ continue;
+#endif
+ if (0 == memcmp(blk+loader->moff,loader->magic,loader->mlen))
+ return viewer_loader_start(ida,loader,fp,filename,page);
+ }
+ fprintf(stderr,"%s: unknown format\n",filename);
+ hex_display(filename);
+ fclose(fp);
+ return -1;
+}
+
+int
+viewer_setimage(struct ida_viewer *ida, struct ida_image *img, char *name)
+{
+ /* ok, going to load new image */
+ viewer_workstop(ida);
+ viewer_rubber_off(ida);
+ memset(&ida->current,0,sizeof(ida->current));
+ if (ida->undo.data) {
+ free(ida->undo.data);
+ memset(&ida->undo,0,sizeof(ida->undo));
+ }
+
+ if (NULL != ida->img.data)
+ free(ida->img.data);
+ ida->file = name;
+ ida->img = *img;
+
+ viewer_autozoom(ida);
+ return 0;
+}
+
+struct ida_viewer*
+viewer_init(Widget widget)
+{
+ Colormap cmap = DefaultColormapOfScreen(XtScreen(widget));
+ struct ida_viewer *ida;
+ XColor white,red,dummy;
+ unsigned int i;
+
+ ida = malloc(sizeof(*ida));
+ memset(ida,0,sizeof(*ida));
+ ida->widget = widget;
+ XtAddEventHandler(widget,ExposureMask,False,viewer_redraw,ida);
+ XtAddEventHandler(widget,
+ ButtonPressMask |
+ ButtonReleaseMask |
+ PointerMotionMask,
+ False,viewer_mouse,ida);
+
+ ptrs[POINTER_NORMAL] = XCreateFontCursor(dpy,XC_left_ptr);
+ ptrs[POINTER_BUSY] = XCreateFontCursor(dpy,XC_watch);
+ ptrs[POINTER_PICK] = XCreateFontCursor(dpy,XC_tcross);
+ ptrs[RUBBER_NEW] = XCreateFontCursor(dpy,XC_left_ptr);
+ ptrs[RUBBER_MOVE] = XCreateFontCursor(dpy,XC_fleur);
+ ptrs[RUBBER_X1] = XCreateFontCursor(dpy,XC_sb_h_double_arrow);
+ ptrs[RUBBER_X2] = XCreateFontCursor(dpy,XC_sb_h_double_arrow);
+ ptrs[RUBBER_Y1] = XCreateFontCursor(dpy,XC_sb_v_double_arrow);
+ ptrs[RUBBER_Y2] = XCreateFontCursor(dpy,XC_sb_v_double_arrow);
+ if (XAllocNamedColor(dpy,cmap,"white",&white,&dummy) &&
+ XAllocNamedColor(dpy,cmap,"red",&red,&dummy))
+ for (i = 0; i < sizeof(ptrs)/sizeof(Cursor); i++)
+ XRecolorCursor(dpy,ptrs[i],&red,&white);
+
+ return ida;
+}