/* * Imagine Viewer * -------------- * Imagine Viewer is an image viewer program which allows you to * view the following graphics formats in a window on your PC: * * 1. Normal and progressive JPEG files * 2. Any PNG file * 3. Any GIF file * * A window palette provides the best mapping of the file * to the current screen resolution and depth, if the screen * depth is <= 8. Otherwise, the images are loaded as 32-bit * direct colour images. * * Copyright (c) L.Patrick 2000-2002 * * This program uses the JPEG library from the Independent JPEG Group, * version 6b. It also uses the excellent LibPNG and ZLIB libraries. */ #include #include #include #include static char *progname = "Imagine Viewer"; App *app; typedef struct Viewer Viewer; struct Viewer { App * app; Window * win; Bitmap * bmp; Image * img; Palette * pal; Graphics *gb; Graphics *gi; Graphics *gw; char * filename; int percent; int count; int halt; }; Viewer *new_viewer(App *app, Window *win) { Viewer *v; v = zero_alloc(sizeof(struct Viewer)); v->app = app; v->win = win; return v; } void del_viewer(Viewer *v) { if (v->gw) del_graphics(v->gw); if (v->gb) del_graphics(v->gb); if (v->gi) del_graphics(v->gi); if (v->pal) del_palette(v->pal); if (v->bmp) del_bitmap(v->bmp); if (v->img) del_image(v->img); if (v->win) del_window(v->win); if (v->app) del_app(v->app); free(v); } int message_func(ImageReader *r, char *msg) { ask_ok(app, "Error Loading Image File", msg); return 1; } int error_func(ImageReader *r) { Viewer *v = r->user_data; if (v->gw) { del_graphics(v->gw); v->gw = NULL; } if (v->gb) { del_graphics(v->gb); v->gb = NULL; } if (v->gi) { del_graphics(v->gi); v->gi = NULL; } if (v->bmp) { del_bitmap(v->bmp); v->bmp = NULL; } if (v->img) { del_image(v->img); v->img = NULL; } return 1; } int startup_func(ImageReader *r) { Viewer *v = r->user_data; v->gw = NULL; v->gb = NULL; v->gi = NULL; v->percent = -1; v->count = 0; size_window(v->win, rect(0, 0, r->width, r->height)); show_window(v->win); return 1; } int after_dither_func(ImageReader *r) { Viewer *v = r->user_data; /* Set up window palette. */ if (r->required_depth <= 8) { if (v->pal) { set_window_palette(v->win, v->pal); } else if (r->pal) { v->pal = new_palette(r->pal->size, r->pal->element); set_window_palette(v->win, v->pal); } } /* Resize the window to the size of the image (if possible). */ size_window(v->win, rect(0, 0, r->width, r->height)); /* Create bitmap to hold image. */ if (v->bmp) del_bitmap(v->bmp); v->bmp = new_bitmap(v->win, r->width, r->height); /* Fill bitmap with white */ v->gb = get_bitmap_graphics(v->bmp); set_rgb(v->gb, rgb(255, 255, 255)); fill_rect(v->gb, rect(0, 0, r->width, r->height)); /* Create image to hold data. */ if (v->img) del_image(v->img); v->img = new_image(r->width, r->height, r->required_depth); if (r->pal) set_image_cmap(v->img, r->pal->size, r->pal->element); /* Fill image with white */ v->gi = get_image_graphics(v->img); set_rgb(v->gi, rgb(255, 255, 255)); fill_rect(v->gi, rect(0, 0, r->width, r->height)); v->filename = r->filename; v->gw = get_window_graphics(v->win); if (! r->src_pal) { /* erase window before displaying new bmp */ set_rgb(v->gw, rgb(255, 255, 255)); fill_rect(v->gw, rect(0, 0, r->width, r->height)); } return 1; } int progress_func(ImageReader *r) { Viewer *v; int percent; int length; char *buffer; v = r->user_data; percent = (r->rows_done * 100L / r->height); if (percent != v->percent) { v->percent = percent; length = strlen(r->filename) + 80; buffer = alloc(length); if (r->max_stages > 1) { sprintf(buffer, "%s %d/%d: %3d%%", r->filename, r->stage, r->max_stages, percent); } else { sprintf(buffer, "%s %3d%%", r->filename, percent); } set_window_title(v->win, buffer); free(buffer); } while (peek_event(v->app)) if (! do_event(v->app)) break; if (v->halt) return 0; return 1; } int rendering_func(ImageReader *r) { Viewer *v; int y; Rect dr; Point dp; v = r->user_data; y = r->row; dp = pt(0,y); dr = rect(0,y,r->width,1); if (r->required_depth <= 8) { copy_bits(v->gb, dr, v->pal, &r->data8[y]); copy_bits(v->gi, dr, v->pal, &r->data8[y]); } else { copy_rgbs(v->gb, dr, &r->data32[y]); copy_rgbs(v->gi, dr, &r->data32[y]); } copy_rect(v->gw, dp, v->gb, dr); return 1; } int success_func(ImageReader *r) { Viewer *v; v = r->user_data; set_window_title(v->win, r->filename); /* Release graphics objects used in drawing. */ del_graphics(v->gw); del_graphics(v->gb); del_graphics(v->gi); v->gw = NULL; v->gb = NULL; v->gi = NULL; return 1; } /* * Special effects: */ Rect display_area(Window *w, Rect ir) { double hscale, vscale; Rect r; r = get_window_area(w); hscale = r.width * 1.0 / ir.width; vscale = r.height * 1.0 / ir.height; if ((hscale <= 0.2) || (vscale <= 0.2)) hscale = vscale = 0.2; if (hscale < vscale) vscale = hscale; else hscale = vscale; r = rect(0, 0, ir.width * hscale, ir.height * vscale); return r; } void lighten_image(Viewer *v) { int x, y; Rect r; Colour c; Image *i; Graphics *gw, *gb; double factor = 1.1; long max = 255 / factor; if (v->img) { /* modify the image */ i = v->img; if (i->depth == 8) { for (y=0; y < i->cmap_size; y++) { c = i->cmap[y]; if (c.red >= max) c.red = 255; else c.red *= factor; if (c.green >= max) c.green = 255; else c.green *= factor; if (c.blue >= max) c.blue = 255; else c.blue *= factor; i->cmap[y] = c; } } else for (y=0; y < i->height; y++) for (x=0; x <= i->width; x++) { c = i->data32[y][x]; if (c.red >= max) c.red = 255; else c.red *= factor; if (c.green >= max) c.green = 255; else c.green *= factor; if (c.blue >= max) c.blue = 255; else c.blue *= factor; i->data32[y][x] = c; } /* update the bitmap and window */ r = get_image_area(i); r = display_area(v->win, r); if (v->bmp) del_bitmap(v->bmp); v->bmp = new_bitmap(v->win, r.width, r.height); gb = get_bitmap_graphics(v->bmp); draw_image(gb, r, i, get_image_area(i)); gw = get_window_graphics(v->win); copy_rect(gw, pt(0,0), gb, get_bitmap_area(v->bmp)); del_graphics(gw); del_graphics(gb); } } void darken_image(Viewer *v) { int x, y; Rect r; Colour c; Image *i; Graphics *gw, *gb; double factor = 1.1; if (v->img) { /* modify the image */ i = v->img; if (i->depth == 8) { for (y=0; y < i->cmap_size; y++) { c = i->cmap[y]; c.red /= factor; c.green /= factor; c.blue /= factor; i->cmap[y] = c; } } else for (y=0; y < i->height; y++) for (x=0; x <= i->width; x++) { c = i->data32[y][x]; c.red /= factor; c.green /= factor; c.blue /= factor; i->data32[y][x] = c; } /* update the bitmap and window */ r = get_image_area(i); r = display_area(v->win, r); if (v->bmp) del_bitmap(v->bmp); v->bmp = new_bitmap(v->win, r.width, r.height); gb = get_bitmap_graphics(v->bmp); draw_image(gb, r, i, get_image_area(i)); gw = get_window_graphics(v->win); copy_rect(gw, pt(0,0), gb, get_bitmap_area(v->bmp)); del_graphics(gw); del_graphics(gb); } } /* * Utility functions: */ int correct_aspect_ratio(Window *w) { long win_aspect, img_aspect; Rect area; int width, height; ImageReader *r = get_window_data(w); area = get_window_area(w); win_aspect = (area.height * 1024L / area.width); img_aspect = (r->height * 1024L / r->width); if (win_aspect > img_aspect) { /* window width was reduced to fit screen */ height = ((long) area.width * r->height) / r->width; width = area.width; size_window(w, rect(0,0,width,height)); return 0; } else if (win_aspect < img_aspect) { /* window height was reduced to fit screen */ width = ((long) area.height * r->width) / r->height; height = area.height; size_window(w, rect(0,0,width,height)); return 0; } return 1; } void load_a_new_image(Window *w, Viewer *v, ImageReader *r) { char *name; name = ask_file_open(w->app, "Open Image", "Open", v->filename); if (name == NULL) return; /* cancelled */ if (v->filename) del_string(v->filename); v->filename = name; if (v->img) del_image(v->img); v->img = read_image(name, r->required_depth); if (v->bmp) del_bitmap(v->bmp); if (v->img) v->bmp = image_to_bitmap(w, v->img); r->width = v->img->width; r->height = v->img->height; set_window_title(w, v->filename); redraw_window(w); } /* * Responding to user actions: */ void window_mouse_down(Window *w, int buttons, Point p) { ImageReader *r = get_window_data(w); Viewer *v = r->user_data; int i; char *lines[] = { "Open...", "-", "Lighten", "Darken", "-", "About...", "-", "Quit", NULL }; v->halt = 1; if (buttons & RIGHT_BUTTON) { i = pop_up_list(w, NULL, lines, RIGHT_BUTTON, p); if (i == 0) { load_a_new_image(w, v, r); } else if (i == 2) { lighten_image(v); } else if (i == 3) { darken_image(v); } else if (i == 5) { ask_ok(w->app, "About Imagine", "Imagine image viewer\nCopyright 2000-2001 L. Patrick.\nAll rights reserved."); } else if (i == 7) { exit(0); } } } void window_mouse_up(Window *w, int buttons, Point p) { } void window_key_down(Window *w, unsigned long key) { if ((key == 'q') || (key == 'Q') || (key == ESC)) { hide_window(w); exit(0); } } void window_redraw(Window *w, Graphics *g) { Graphics *bg; Rect br; ImageReader *r; Viewer *v; int len; Rect ir; char info[80]; r = get_window_data(w); v = r->user_data; if (v->bmp) { bg = get_bitmap_graphics(v->bmp); br = get_bitmap_area(v->bmp); copy_rect(g, pt(0,0), bg, br); del_graphics(bg); } else { br = get_window_area(w); } if (v->filename) { set_rgb(g, BLUE); len = strlen(v->filename); draw_utf8(g, pt(0,br.height), v->filename, len); } if (v->img == NULL) return; len = sprintf(info, "Width: %d, Height: %d", r->width, r->height); draw_utf8(g, pt(0,br.height+17), info, len); ir = display_area(w, get_image_area(v->img)); len = sprintf(info, "Displayed at: %d x %d", ir.width, ir.height); draw_utf8(g, pt(0,br.height+34), info, len); } void window_resize(Window *w) { Graphics *gb; Rect wr, ir; ImageReader *r; Viewer *v; r = get_window_data(w); v = r->user_data; if (v->img == NULL) return; if (r->state != STOPPED) return; if (v->bmp) del_bitmap(v->bmp); v->bmp = NULL; ir = get_image_area(v->img); wr = display_area(w, ir); v->bmp = new_bitmap(w, wr.width, wr.height); if (v->bmp == NULL) return; gb = get_bitmap_graphics(v->bmp); draw_image(gb, wr, v->img, ir); del_graphics(gb); } void window_close(Window *w) { ImageReader *r = get_window_data(w); Viewer *v = r->user_data; v->halt = 2; hide_window(w); } Palette *create_standard_palette(void) { int i, red, green, blue, grey; Colour elem[256]; Palette *pal; i = 0; /* assign 216 colours in a 6x6x6 colour cube */ for (red=0; red < 256; red += 51) for (green=0; green < 256; green += 51) for (blue=0; blue < 256; blue += 51) { elem[i] = rgb(red, green, blue); i++; } /* add 15 greyscales */ for (grey=16; grey < 255; grey += 16) { elem[i] = rgb(grey, grey, grey); i++; } pal = new_palette(i, elem); return pal; } /* * Initialise the program: */ ImageReader *create_image_reader(Viewer *v, Palette *pal, int depth) { ImageReader *r; r = new_image_reader(); if (pal) { r->src_pal = pal; r->required_depth = 8; r->max_cmap_size = pal->size; } else if (depth <= 8) { r->required_depth = 8; r->max_cmap_size = 256; } else { r->required_depth = 32; } r->width = 300; r->height = 200; r->user_data = v; r->message_func = message_func; r->error_func = error_func; r->startup_func = startup_func; r->after_dither_func = after_dither_func; r->progress_func = progress_func; r->rendering_func = rendering_func; r->success_func = success_func; return r; } int main(int argc, char *argv[]) { Window *win; Palette *pal; ImageReader *r; Viewer *v; Image *img = NULL; int arg; debug_memory(1); app = new_app(argc, argv); win = new_window(app, rect(0,0,300,200), progname, TITLEBAR | RESIZE | CLOSEBOX | MINIMIZE); pal = NULL; /*pal = create_standard_palette();*/ v = new_viewer(app, win); r = create_image_reader(v, pal, 32); set_window_data(win, r); on_window_resize(win, window_resize); on_window_redraw(win, window_redraw); on_window_close(win, window_close); on_window_mouse_down(win, window_mouse_down); on_window_mouse_up(win, window_mouse_up); on_window_key_down(win, window_key_down); if (argc < 2) show_window(win); v->filename = NULL; if (argc == 1) { r->filename = "- standard input -"; r->file = stdin; img = read_image_progressively(r); set_window_title(win, r->filename); } for (arg=1; argv[arg] != NULL; arg++) { if (v->halt == 2) break; if (v->halt) { if (ask_yes_no(app, "Continue?", "Continue displaying images?") == YES) v->halt = 0; else break; } if (r->filename) del_string(r->filename); r->filename = copy_string(argv[arg]); if (img) del_image(img); img = read_image_progressively(r); if (img == NULL) img = read_image(r->filename, 32); if (img == NULL) v->halt = 1; if (v->filename) set_window_title(win, v->filename); /* if (! correct_aspect_ratio(win)) draw_window(win); draw_all(app); */ while (peek_event(app)) do_event(app); if ((! v->halt) && (argv[arg+1] != NULL)) app_delay(app, 1000); while (peek_event(app)) do_event(app); } if (! v->filename) set_window_title(win, progname); draw_window(win); main_loop(app); if (v->bmp) del_bitmap(v->bmp); del_window(win); del_app(app); return 0; }