home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1786 / igif.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  27.3 KB  |  1,215 lines

  1. /*
  2.  * igif.c -- display GIF images on the personal iris and other SGI machines.
  3.  *
  4.  * usage:    igif [ -d -e -f -l -t -2 ] { file.gif }
  5.  *
  6.  * If no images are specified, a GIF image is read from standard input.
  7.  * flags:
  8.  *
  9.  *    -d    Dither the images before displaying it.
  10.  *    -e    Erase the entire previous image before going to the next one.
  11.  *    -f    Stay in the foreground; this is useful for 3 line browsers:
  12.  *            foreach f (*.gif)
  13.  *                igif -f $f
  14.  *            end
  15.  *    -l    Force igif to not use lrectwrite.  This may be faster and it
  16.  *        may be necessary for it to work ('though some autodection is done).
  17.  *    -t    Display true colours on colour-mapped displays.  On displays with
  18.  *        only 8 planes, igif will change the colours of images to fit the
  19.  *        standard NeWS colourmap.  This option makes igif display the true
  20.  *        colours; even if that means that other programs will get their
  21.  *        colours (temporarily) stomped on.  It tries hard to be as
  22.  *        inconspicuous as possible, but if you gotta have 256 colours...
  23.  *    -2    Display the images in double height and double width mode.  A
  24.  *        cheap hack, but it saves some eyestrain on those itsy-bitsy
  25.  *        320 x 200 GIFs.
  26.  *
  27.  * When in igif, the left and middle mouse buttons (or the n and p keys)
  28.  * move to the next and previous images respectively.  The right mouse
  29.  * button brings up a menu of the images.  The q key will quit igif.
  30.  * Until all the images are loaded, you may not see the image even if you
  31.  * move to it.  When no numbers appear to the left of the title bar all
  32.  * images should be loaded.
  33.  *
  34.  * Bugs:
  35.  *    If some program has munged the "standard" 8 plane colourmap, the images
  36.  *    won't look too good.  Either run makemap (/usr/sbin/makemap, perhaps)
  37.  *    or use the -t flag.  Igif may itself wipe out the colourmap if you
  38.  *    kill it.
  39.  *    Auto-dection of a missing lrectwrite() may not work on all systems.
  40.  *    I'll be happy to apply a fix if you have one.
  41.  *    Dithering should be done after doubling the image.  It isn't and the
  42.  *    results are not too pretty.
  43.  *
  44.  * TODO list:
  45.  *    - we can't dither interlaced GIFs right away (but we do anyways!)
  46.  *    - support GIF89a fully
  47.  *    - work on displays without RGB but > 8 planes.  Not too hard,
  48.  *        but _I_ can't test it.
  49.  *    - true colour mode should check if a local colourmap is in fact the same.
  50.  *    - set NeWS colourmap on startup?
  51.  *    - display meaninful information if an image is not yet loaded.
  52.  *    - transmogrification into a generalized image viewer which supports
  53.  *        lots of different formats, perhaps in concert with PBM+.
  54.  *    - generalize the magnification (esp. to correct aspect ratio)
  55.  *    - options to perform gamma correction of images
  56.  *    - clean up the code
  57.  *
  58.  * Copyright 1989,1990 by George Phillips
  59.  *
  60.  * Permission to use, copy, modify, and distribute this software and its
  61.  * documentation for any purpose and without fee is hereby granted, provided
  62.  * that the above copyright notice appear in all copies and that both that
  63.  * copyright notice and this permission notice appear in supporting
  64.  * documentation.  This software is provided "as is" without express or
  65.  * implied warranty.
  66.  *
  67.  * The GIF LZW decoder was written by someone else who's copyright notice
  68.  * is contained in decode.c.
  69.  *
  70.  * The Floyd-Steinburg dithering code was based on ppmquant from Jef
  71.  * Poskanzer's PBM+ package.
  72.  *
  73.  * George Phillips <phillips@cs.ubc.ca>
  74.  * Department of Computer Science
  75.  * University of British Columbia
  76.  */
  77.  
  78. #include <stdio.h>
  79. #include <malloc.h>
  80. #include <gl.h>
  81. #include <device.h>
  82.  
  83. #include "errs.h"
  84. #include "mem_image.h"
  85. #include "imgfile.h"
  86. #include "newsmap.h"
  87.  
  88. int bad_code_count;
  89.  
  90. struct imgfile*    imf_list = NULL;
  91. struct imgfile**    imf_ins = &imf_list;
  92. struct mem_image*    cur_img = NULL;
  93.  
  94. int    stay_in_foreground = 0;
  95. int mag = 1;
  96. int use_lrectwrite = 1;
  97. int true_colours = 0;
  98. int dither = 0;
  99. int    erase_all = 0;
  100. int timing = 0;
  101.  
  102. enum disp_mode_type { RGB_LRECT, RGB_WRITE, CMAP_NEWS, CMAP_TRUE };
  103. static enum disp_mode_type disp_mode;
  104. static int win_width;
  105. static int win_height;
  106. int redrawing = 0;
  107. int redraw_line = 0;
  108. struct imgfile* cur_imf = NULL;
  109. long imgmenu;
  110.  
  111. int loading;
  112.  
  113. char* image_title();
  114.  
  115. main(argc, argv)
  116. int        argc;
  117. char*    argv[];
  118. {
  119.     FILE*    fp;
  120.     int        argn;
  121.     int        i;
  122.     int        maxwidth;
  123.     int        maxheight;
  124.     struct imgfile*    imf;
  125.  
  126.     argn = 1;
  127.     for (argn = 1; argn < argc && argv[argn][0] == '-'; argn++) {
  128.         if (!strcmp(argv[argn], "-f"))
  129.             stay_in_foreground = 1;
  130.         else if (!strcmp(argv[argn], "-l"))
  131.             use_lrectwrite = 0;
  132.         else if (!strcmp(argv[argn], "-t"))
  133.             true_colours = 1;
  134.         else if (!strcmp(argv[argn], "-d"))
  135.             dither = 1;
  136.         else if (!strcmp(argv[argn], "-e"))
  137.             erase_all = 1;
  138.         else if (!strcmp(argv[argn], "-2")) {
  139.             mag = 2;
  140.         }
  141.         else if (!strcmp(argv[argn], "-T"))
  142.             timing = 1;
  143.         else {
  144.             fprintf(stderr, "igif: unknown option '%s'\n", argv[argn]);
  145.             exit(1);
  146.         }
  147.     }
  148.  
  149.     if (true_colours && dither) {
  150.         fprintf(stderr, "igif: only one of -t and -d may be specified; -t assumed\n");
  151.         dither = 0;
  152.     }
  153.  
  154.     if (argn == argc)
  155.         setup_file("standard input", stdin);
  156.     else
  157.         for (i = argn; i < argc; i++)
  158.             setup_file(argv[i], (FILE*)NULL);
  159.     
  160.     loading = 0;
  161.     maxwidth = 10;
  162.     maxheight = 10;
  163.     for (imf = imf_list; imf != NULL; imf = imf->next) {
  164.         loading++;
  165.         if (imf->width > maxwidth)
  166.             maxwidth = imf->width;
  167.         if (imf->height > maxheight)
  168.             maxheight = imf->height;
  169.     }
  170.     
  171.     if (loading == 0) {
  172.         fprintf(stderr, "igif: no images to display\n");
  173.         exit(1);
  174.     }
  175.  
  176.     screen_init(maxwidth, maxheight);
  177.     if (dither && (disp_mode == RGB_LRECT || disp_mode == RGB_WRITE))
  178.         dither = 0;
  179.  
  180.     for (imf = imf_list; imf != NULL; imf = imf->next) {
  181.         if (imf->stream != NULL || (fp = fopen(imf->filename, "r")) != NULL) {
  182.             if (cur_imf == NULL) {
  183.                 cur_imf = imf;
  184.                 wintitle(image_title(imf));
  185.             }
  186.             if (imf->stream != NULL)
  187.                 load_gif(imf, imf->stream, imf->width, imf->height);
  188.             else {
  189.                 load_gif(imf, fp, -1, -1);
  190.                 fclose(fp);
  191.             }
  192.             loading--;
  193.             update_loadinfo();
  194.         }
  195.     }
  196.  
  197.     update_loadinfo();
  198.     screen_handle(1);
  199.     exit(0);
  200. }
  201.  
  202. sizeof_gif(name, fp, width, height)
  203. char*    name;
  204. FILE*    fp;
  205. int*    width;
  206. int*    height;
  207. {
  208.     unsigned char buf[10];
  209.  
  210.     if (fread(buf, 10, 1, fp) != 1) {
  211.         fprintf(stderr, "igif: %s is too short\n", name);
  212.         return(0);
  213.     }
  214.     if (strncmp(buf, "GIF", 3)) {
  215.         fprintf(stderr, "igif: %s is not a GIF file\n", name);
  216.         return(0);
  217.     }
  218.     if (strncmp(buf + 3, "87a", 3) && strncmp(buf + 3, "89a", 3)) {
  219.         buf[6] = '\0';
  220.         fprintf(stderr, "igif: %s is an unsupported GIF file version\n", name);
  221.         return(0);
  222.     }
  223.     *width = buf[6] + (buf[7] << 8);
  224.     *height = buf[8] + (buf[9] << 8);
  225.     return(1);
  226. }
  227.  
  228. static int err = 0;
  229. static int    i_y;
  230. static int    pass;
  231.  
  232. #define error(x)    printf("%s at byte %d\n", x, ftell(fp)); return(0)
  233. #define reterr(x)    if (err != 0) { error(x); }
  234. #define reteof()    reterr("Unexpected EOF")
  235. #define skipbyte(x)    getbyte(x); reteof()
  236.  
  237. int* gif2rgb();
  238. struct mem_image* add_image();
  239. void adjust_colourmap();
  240.  
  241. setup_file(filename, stream)
  242. char*    filename;
  243. FILE*    stream;
  244. {
  245.     FILE*    fp;
  246.     int        width, height;
  247.     struct imgfile* imf;
  248.     char* p;
  249.     char* lastslash;
  250.  
  251.     if (stream != NULL)
  252.         fp = stream;
  253.     else if ((fp = fopen(filename, "r")) == NULL) {
  254.         fprintf(stderr, "can't open '%s'\n", filename);
  255.         return;
  256.     }
  257.  
  258.     if (sizeof_gif(filename, fp, &width, &height)) {
  259.         imf = (struct imgfile*)malloc(sizeof(struct imgfile));
  260.         if (imf == NULL)
  261.             no_mem();
  262.         imf->filename = filename;
  263.         imf->stream = stream;
  264.         imf->width = width;
  265.         imf->height = height;
  266.  
  267.         for (p = filename, lastslash = filename - 1; *p; p++)
  268.             if (*p == '/')
  269.                 lastslash = p;
  270.         
  271.         imf->name = lastslash + 1;
  272.         imf->imglist = NULL;
  273.         imf->next = NULL;
  274.         *imf_ins = imf;
  275.         imf_ins = &(imf->next);
  276.     }
  277.     if (stream == NULL)
  278.         fclose(fp);
  279. }
  280.  
  281. load_gif(imf, fp, s_width, s_height)
  282. struct imgfile*    imf;
  283. FILE*    fp;
  284. int        s_width;
  285. int        s_height;
  286. {
  287.     static unsigned char buf[4096];
  288.     int s_control, back;
  289.     int i_top, i_left, i_width, i_control, i_height;
  290.     int ch;
  291.     int    i, j;
  292.     int*    global_colourmap;
  293.     int*    local_colourmap;
  294.     struct mem_image* img;
  295.     struct mem_image** img_ins;
  296.  
  297.     img_ins = &(imf->imglist);
  298.  
  299.     if (s_width == -1) {
  300.         /* read signature */
  301.         if (fread(buf, 3, 1, fp) != 1) {
  302.             error("File too short");
  303.         }
  304.     
  305.         if (strncmp(buf, "GIF", 3)) {
  306.             error("Not a GIF file");
  307.         }
  308.  
  309.         if (fread(buf, 3, 1, fp) != 1) {
  310.             error("File too short");
  311.         }
  312.  
  313.         if (strncmp(buf, "87a", 3) && strncmp(buf, "89a", 3)) {
  314.             buf[3] = '\0';
  315.             printf("unknown version '%s'\n", buf);
  316.             return(0);
  317.         }
  318.     
  319.         /* read screen descriptor */
  320.         s_width = getword(fp); reteof();
  321.         s_height = getword(fp); reteof();
  322.     }
  323.  
  324.     s_control = getbyte(fp); reteof();
  325.     back = getbyte(fp); reteof();
  326.     skipbyte(fp);
  327.  
  328.     if (s_control & 128) { /* global colour map */
  329.         global_colourmap = gif2rgb(fp, 2 << (s_control & 7));
  330.     }
  331.  
  332.     for (;;) {
  333.         ch = getbyte(fp);
  334.         reterr("no terminator");
  335.         switch (ch) {
  336.         case ',':    /* image follows */
  337.             i_left = getword(fp);    reteof();
  338.             i_top = getword(fp);    reteof();
  339.             i_width = getword(fp); reteof();
  340.             i_height = getword(fp); reteof();
  341.             i_control = getbyte(fp); reteof();
  342.  
  343.             if (i_control & 128) { /* local colour map */
  344.                 local_colourmap = gif2rgb(fp, 2 << (i_control & 7));
  345.                 img = add_image(i_width, i_height, 2 << (i_control & 7),
  346.                     s_width, s_height);
  347.                 img->colourmap = local_colourmap;
  348.                 img->maplen = 2 << (i_control & 7);
  349.             }
  350.             else {
  351.                 img = add_image(i_width, i_height, 2 << (s_control & 7),
  352.                     s_width, s_height);
  353.                 img->colourmap = global_colourmap;
  354.                 img->maplen = 2 << (s_control & 7);
  355.             }
  356.             img->x_off += i_left * mag;
  357.             img->y_off += i_top * mag;
  358.             img->gif_interlaced = i_control & 64; 
  359.             img->background = back;
  360.             img->imf = imf;
  361.             *img_ins = img;
  362.             img_ins = &(img->next);
  363.             adjust_colourmap(img);
  364.             if (img->imf == cur_imf && img->imf->imglist == img)
  365.                 paint_background(img);
  366.             bad_code_count = 0;
  367.             i_y = 0;
  368.             pass = 0;
  369.             decoder(fp, img);
  370.             /* ignore rest of blocks used by decoder */
  371.             skipblocks(fp); reterr("Bad block in image");
  372.             break;
  373.         case ';':    /* terminator ... */
  374.             return(0);
  375.         case '!':    /* extension block */
  376.             skipbyte(fp);    /* function code */
  377.             skipblocks(fp); reterr("Bad block in extension block");
  378.             break;
  379.         default:
  380.             /* Supposed to ignore any unknown characters up to the image
  381.              * separator, but I prefer to be tight about these things because
  382.              * there are many corrupt images out there.
  383.              */
  384.             printf("Unknown block type '%c' (%d) at byte %d\n",
  385.                 ch, ch, ftell(fp));
  386.             return(0);
  387.         }
  388.     }
  389. }
  390.  
  391. /*
  392.  * Read the GIF colourmap from the given file and convert it into
  393.  * the format used by lrectwrite.
  394.  *
  395.  * GIF colourmap is a byte stream: rgbrgbrgb
  396.  * lrect format is in 4 byte words: abgr abgr abgr
  397.  * (the a is alpha, which should be zero)
  398.  */
  399. int* gif2rgb(fp, len)
  400. FILE*    fp;
  401. int        len;
  402. {
  403.     register int i;
  404.     int*    rgb;
  405.  
  406.     if ((rgb = (int*)malloc(len * sizeof(int))) == NULL)
  407.         no_mem();
  408.  
  409.     /* very loose about EOF, oh well */
  410.     for (i = 0; i < len; i++) {
  411.         rgb[i] = fgetc(fp);
  412.         rgb[i] |= fgetc(fp) << 8;
  413.         rgb[i] |= fgetc(fp) << 16;
  414.     }
  415.     return(rgb);
  416. }
  417.  
  418. static int pass_width[] = { 8, 8, 4, 2 };
  419. static int pass_start[] = { 0, 4, 2, 1 };
  420.  
  421. char* out_line(img)
  422. struct mem_image*    img;
  423. {
  424.     if (dither)
  425.         floydstein(img, i_y);
  426.  
  427.     if (disp_mode == CMAP_TRUE)
  428.         true_cmap(img, i_y);
  429.  
  430.     if (cur_imf == img->imf) {
  431.         switch (disp_mode) {
  432.         case CMAP_TRUE:
  433.         case CMAP_NEWS:
  434.             cmap_incr_redraw(img, i_y);
  435.             break;
  436.         case RGB_LRECT:
  437.             incr_redraw(img, i_y);
  438.             break;
  439.         case RGB_WRITE:
  440.             nonrect_incr_redraw(img, i_y);
  441.             break;
  442.         }
  443.     }
  444.     screen_handle(0);
  445.  
  446.     if (!img->gif_interlaced)
  447.         return(img->data + ++i_y * img->width);
  448.  
  449.     i_y += pass_width[pass];
  450.     if (i_y >= img->height) {
  451.         pass++;
  452.         i_y = pass_start[pass];
  453.     }
  454.     return(img->data + i_y * img->width);
  455. }
  456.  
  457. int getbyte(fp)
  458. FILE*    fp;
  459. {
  460.     int    ch;
  461.  
  462.     err = 0;
  463.     if ((ch = fgetc(fp)) == EOF) {
  464.         err = 1;
  465.         return(READ_ERROR);
  466.     }
  467.     return(ch & 255);
  468. }
  469.  
  470. int getword(fp)
  471. FILE*    fp;
  472. {
  473.     int    c1, c2;
  474.  
  475.     err = 0;
  476.     if ((c1 = fgetc(fp)) == EOF) {
  477.         err = 1;
  478.         return(0);
  479.     }
  480.     if ((c2 = fgetc(fp)) == EOF) {
  481.         err = 1;
  482.         return(0);
  483.     }
  484.     return(((c2 & 255) << 8) | (c1 & 255));
  485. }
  486.  
  487. skipblocks(fp)
  488. FILE*    fp;
  489. {
  490.     int len;
  491.     static char buf[256];
  492.  
  493.     err = 0;
  494.  
  495.     for (;;) {
  496.         len = getbyte(fp);
  497.         reterr("EOF in blocks");
  498.  
  499.         if (len == 0)
  500.             return(0);
  501.  
  502.         if (fread(buf, len, 1, fp) != 1) {
  503.             puts("EOF in blocks");
  504.             err = 1;
  505.             return(0);
  506.         }
  507.     }
  508. }
  509.  
  510. void set_display_mode();
  511. void save_colourmap();
  512. void set_colourmap();
  513. void toggle_colourmap();
  514. void restore_colourmap();
  515.  
  516. screen_init(maxwidth, maxheight)
  517. int    maxwidth;
  518. int    maxheight;
  519. {
  520.     struct imgfile*    imf;
  521.  
  522.     init_newsmap();
  523.  
  524.     if (stay_in_foreground)
  525.         foreground();
  526.  
  527.     prefsize(win_width = maxwidth * mag, win_height = maxheight * mag);
  528.     if (winopen("igif") < 0) {
  529.         fprintf(stderr, "igif: couldn't open a window.\n");
  530.         exit(1);
  531.     }
  532.     set_display_mode();
  533.  
  534.     if (disp_mode == RGB_LRECT || disp_mode == RGB_WRITE)
  535.         RGBmode();
  536.  
  537.     gconfig();
  538.  
  539.     if (disp_mode == RGB_LRECT)
  540.         rectzoom((float)mag, (float)mag);
  541.  
  542.     if (disp_mode == CMAP_TRUE)
  543.         save_colourmap();
  544.  
  545.     rectf(0, 0, win_width - 1, win_height - 1);
  546.  
  547.     qdevice(REDRAW);
  548.     qdevice(PIECECHANGE);
  549.     qdevice(WINQUIT);
  550.     qdevice(LEFTMOUSE);
  551.     qdevice(MIDDLEMOUSE);
  552.     qdevice(INPUTCHANGE);
  553.  
  554.     qdevice(NKEY);
  555.     qdevice(PKEY);
  556.     qdevice(QKEY);
  557.  
  558.     qdevice(MENUBUTTON);
  559.     imgmenu = newpup();
  560.     addtopup(imgmenu, "images %t");
  561.     for (imf = imf_list; imf != NULL; imf = imf->next)
  562.         addtopup(imgmenu, imf->name);
  563. }
  564.  
  565. screen_handle(block)
  566. int    block;
  567. {
  568.     short    data;
  569.     int        i;
  570.     struct imgfile*    imf;
  571.  
  572.     if (block)
  573.         update_loadinfo();
  574.  
  575.     do {
  576.         while (qtest()) {
  577.             switch (qread(&data)) {
  578.             case MENUBUTTON:
  579.                 if ((i = dopup(imgmenu)) <= 0)
  580.                     break;
  581.                 for (cur_imf = imf_list; i > 1; i--)
  582.                     cur_imf = cur_imf->next;
  583.                 new_imf();
  584.                 break;
  585.             case PKEY:
  586.             case MIDDLEMOUSE:
  587.                 if (data == 0)
  588.                     break;
  589.                 for (imf = imf_list; imf != NULL; imf = imf->next)
  590.                     if (imf->next == cur_imf ||
  591.                     (imf->next == NULL && cur_imf == imf_list))
  592.                         break;
  593.  
  594.                 cur_imf = imf;
  595.                 new_imf();
  596.                 break;
  597.             case NKEY:
  598.             case LEFTMOUSE:
  599.                 /* no need to redraw if we have only one image */
  600.                 if (imf_list->next == NULL || data == 0)
  601.                     break;
  602.                 if ((cur_imf = cur_imf->next) == NULL)
  603.                     cur_imf = imf_list;
  604.                 new_imf();
  605.                 break;
  606.             case REDRAW:
  607.             case PIECECHANGE:
  608.                 if (cur_imf != NULL) {
  609.                     cur_img = cur_imf->imglist;
  610.                     if (cur_img != NULL) {
  611.                         redrawing = 1;
  612.                         redraw_line = 0;
  613.                         paint_background(cur_img);
  614.                     }
  615.                     else
  616.                         redrawing = 0;
  617.                 }
  618.                 break;
  619.             case QKEY:
  620.             case WINQUIT:
  621.                 if (disp_mode == CMAP_TRUE)
  622.                     restore_colourmap();
  623.                 exit(0);
  624.             case INPUTCHANGE:
  625.                 if (disp_mode == CMAP_TRUE && cur_imf->imglist != NULL)
  626.                     toggle_colourmap(cur_imf->imglist);
  627.                 break;
  628.             default:
  629.                 break;
  630.             }
  631.         }
  632.         if (redrawing) {
  633.             switch (disp_mode) {
  634.             case CMAP_TRUE:
  635.             case CMAP_NEWS:
  636.                 redraw_line = cmap_incr_redraw(cur_img, redraw_line);
  637.                 break;
  638.             case RGB_LRECT:
  639.                 redraw_line = incr_redraw(cur_img, redraw_line);
  640.                 break;
  641.             case RGB_WRITE:
  642.                 redraw_line = nonrect_incr_redraw(cur_img, redraw_line);
  643.                 break;
  644.             }
  645.  
  646.             if (redraw_line >= cur_img->height) {
  647.                 cur_img = cur_img->next;
  648.                 if (cur_img == NULL)
  649.                     redrawing = 0;
  650.                 else
  651.                     redraw_line = 0;
  652.             }
  653.         }
  654.     } while (block);
  655. }
  656.  
  657. new_imf()
  658. {
  659.     wintitle(image_title(cur_imf));
  660.     cur_img = cur_imf->imglist;
  661.     if (cur_img != NULL) {
  662.         if (disp_mode == CMAP_TRUE)
  663.             set_colourmap(cur_img);
  664.         paint_background(cur_img);
  665.         redrawing = 1;
  666.         redraw_line = 0;
  667.     }
  668.     else
  669.         redrawing = 0;
  670. }
  671.  
  672. update_loadinfo()
  673. {
  674.     if (cur_imf != NULL)
  675.         wintitle(image_title(cur_imf));
  676. }
  677.  
  678. no_mem()
  679. {
  680.     fprintf(stderr, "out of memory\n");
  681.     exit(1);
  682. }
  683.  
  684.  
  685. struct mem_image* add_image(width, height, depth, scr_width, scr_height)
  686. int        width;
  687. int        height;
  688. int        depth;
  689. int        scr_width;
  690. int        scr_height;
  691. {
  692.     struct mem_image*    img;
  693.  
  694.     if ((img = (struct mem_image*)malloc(sizeof(struct mem_image))) == NULL)
  695.         no_mem();
  696.     
  697.     img->width = width;
  698.     img->height = height;
  699.     img->depth = depth;
  700.     img->colourmap = NULL;
  701.     img->mapmap = NULL;
  702.     img->gif_interlaced = 0;
  703.     img->seq = -1;
  704.  
  705.     img->x_off = (win_width - scr_width * mag) / 2;
  706.     img->y_off = (win_height - scr_height * mag) / 2;
  707.     /*img->state = LOADING;*/
  708.  
  709.     if ((img->data = malloc(width * height)) == NULL)
  710.         no_mem();
  711.  
  712.     bzero(img->data, width * height);
  713.  
  714.     img->next = NULL;
  715.     
  716.     return(img);
  717. }
  718.  
  719. char* image_title(imf)
  720. struct imgfile*    imf;
  721. {
  722.     static char title[256];
  723.     char    lbuf[32];
  724.  
  725.     sprintf(lbuf, "%d ", loading);
  726.  
  727.     sprintf(title, "%s%s",
  728.         loading > 0 ? lbuf : "",
  729.         imf->name);
  730.  
  731.     return(title);
  732. }
  733.  
  734.  
  735. paint_background(img)
  736. struct mem_image* img;
  737. {
  738.     int    xleft, xright, ytop, ybottom;
  739.  
  740.     switch (disp_mode) {
  741.     case RGB_LRECT:
  742.         RGBcolor(img->colourmap[img->background] & 255,
  743.                  (img->colourmap[img->background] >> 8) & 255,
  744.                  (img->colourmap[img->background] >> 16) & 255);
  745.         break;
  746.     case RGB_WRITE:
  747.         RGBcolor(*((char*)img->colourmap + img->background),
  748.                  *((char*)img->colourmap + img->maplen + img->background),
  749.                  *((char*)img->colourmap + img->maplen * 2 + img->background));
  750.         break;
  751.     case CMAP_NEWS:
  752.         color(rgb2newsmap(img->colourmap[img->background] & 255,
  753.                           (img->colourmap[img->background] >> 8) & 255,
  754.                           (img->colourmap[img->background] >> 16) & 255));
  755.         break;
  756.     case CMAP_TRUE:
  757.         if (img->mapmap[img->background] >= 0)
  758.             color(img->mapmap[img->background]);
  759.         else {
  760.             int    m;
  761.  
  762.             /* we haven't mapped any colours yet so collision isn't possible */
  763.             m = rgb2newsmap(img->colourmap[img->background] & 255,
  764.                             (img->colourmap[img->background] >> 8) & 255,
  765.                             (img->colourmap[img->background] >> 16) & 255);
  766.             img->mapmap[img->background] = m;
  767.             color(m);
  768.         }
  769.     }
  770.  
  771.     if (erase_all) {
  772.         rectf(0, 0, win_width, win_height);
  773.         return;
  774.     }
  775.  
  776.     if (img->imf->width >= win_width && img->imf->height >= win_height)
  777.         return;
  778.  
  779.     xleft = (win_width - img->imf->width * mag) / 2;
  780.     xright = win_width - img->imf->width * mag - xleft;
  781.     ybottom = (win_height - img->imf->height * mag) / 2;
  782.     ytop = win_height - img->imf->height * mag - ybottom;
  783.  
  784.     rectf(0, 0, win_width - 1, ybottom);
  785.     rectf(0, 0, xleft, win_height - 1);
  786.     rectf(0, win_height - ytop, win_width - 1, win_height - 1);
  787.     rectf(win_width - xright, 0, win_width - 1, win_height - 1);
  788. }
  789.  
  790. int incr_redraw(img, line)
  791. register struct mem_image*    img;
  792. register int                line;
  793. {
  794.     static int argb_buf[XMAXSCREEN];
  795.     register char* p;
  796.     register int* argb;
  797.  
  798.     p = img->data + line * img->width + img->width - 1;
  799.     argb = argb_buf + img->width - 1;
  800.  
  801.     while (argb >= argb_buf)
  802.         *argb-- = img->colourmap[*p--];
  803.  
  804.     lrectwrite(img->x_off,
  805.             (win_height - (line + 1) * mag) - img->y_off,
  806.             img->width /* * mag */ - 1 + img->x_off,
  807.             (win_height - (line + 1) * mag) - img->y_off,
  808.             argb_buf);
  809.  
  810.     return(line + 1);
  811. }
  812.  
  813.  
  814. /* This is based on code From: moss@BRL.MIL ("Gary S. Moss", VLD/VMB) */
  815.  
  816. int nonrect_incr_redraw(img, line)
  817. register struct mem_image*    img;
  818. register int                line;
  819. {
  820.     static unsigned char Red_pixels[XMAXSCREEN];
  821.     static unsigned char Green_pixels[XMAXSCREEN];
  822.     static unsigned char Blue_pixels[XMAXSCREEN];
  823.     register short i;
  824.     register char* p;
  825.     register char* red_map;
  826.     register char* green_map;
  827.     register char* blue_map;
  828.  
  829.     p = img->data + line * img->width;
  830.  
  831.     red_map = (char*)img->colourmap;
  832.     green_map = (char*)img->colourmap + img->maplen;
  833.     blue_map = (char*)img->colourmap + img->maplen * 2;
  834.  
  835.     for (i = 0; i < img->width * mag; i++, p++)  {
  836.         Red_pixels[i] = red_map[*p];
  837.         Green_pixels[i] = green_map[*p];
  838.         Blue_pixels[i] = blue_map[*p];
  839.         if (mag == 2) {
  840.             Red_pixels[++i] = red_map[*p];
  841.             Green_pixels[i] = green_map[*p];
  842.             Blue_pixels[i] = blue_map[*p];
  843.         }
  844.     }
  845.     
  846.     cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off);
  847.     writeRGB(img->width * mag, Red_pixels, Green_pixels, Blue_pixels);
  848.     if (mag == 2) {
  849.         cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off + 1);
  850.         writeRGB(img->width * mag, Red_pixels, Green_pixels, Blue_pixels);
  851.     }
  852.  
  853.     return(line + 1);
  854. }
  855.  
  856. int cmap_incr_redraw(img, line)
  857. register struct mem_image*    img;
  858. register int                line;
  859. {
  860.     static unsigned short cmap[XMAXSCREEN];
  861.     register short i;
  862.     register char* p;
  863.  
  864.     p = img->data + line * img->width;
  865.  
  866.     for (i = 0; i < img->width * mag; i++, p++)  {
  867.         cmap[i] = img->mapmap[*p];
  868.         if (mag == 2)
  869.             cmap[++i] = img->mapmap[*p];
  870.     }
  871.     
  872.     cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off);
  873.     writepixels(img->width * mag, cmap);
  874.     if (mag == 2) {
  875.         cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off + 1);
  876.         writepixels(img->width * mag, cmap);
  877.     }
  878.  
  879.     return(line + 1);
  880. }
  881.  
  882. /* auto-dectection code From:    <cditi!caw@uunet.uucp> */
  883.  
  884. void set_display_mode()
  885. {
  886.     char buf[32];
  887.  
  888.     gversion(buf);
  889.  
  890.     if (getplanes() <= 8)
  891.         disp_mode = true_colours ? CMAP_TRUE : CMAP_NEWS;
  892.     else if (strncmp(buf, "GL4D-", 5) || !use_lrectwrite)
  893.         disp_mode = RGB_WRITE;
  894.     else
  895.         disp_mode = RGB_LRECT;
  896. }
  897.  
  898. struct map_entry {
  899.     int    orig_index;
  900.     int    abgr;
  901.     int    screen_index;
  902.     int    used;
  903. };
  904.  
  905. static int colour_used[256];
  906.  
  907. int map_ent_cmp(a, b)
  908. struct map_entry*    a;
  909. struct map_entry*    b;
  910. {
  911.     if (!a->used)
  912.         return(1);
  913.     if (!b->used)
  914.         return(-1);
  915.     return(a->abgr - b->abgr);
  916. }
  917.  
  918. static int abs(x)
  919. {
  920.     if (x < 0) return(-x);
  921.     return(x);
  922. }
  923.  
  924. /*
  925.  * Depending on how the images will be displayed, we may wish to
  926.  * change the colour maps in our images into a more convenient form.
  927.  * Lrectwrite() is the default, but writeRGB would rather have 3
  928.  * tables.  For screens with colour maps, we can either map the
  929.  * images colour map into the screen colour map or load the screen
  930.  * colour map with the image colour map.  In the latter case we'll
  931.  * want to compress the colourmap as much as possible to minimize
  932.  * on-screen weirdness.  It would also be nice to put the image
  933.  * colours into screen map entries that won't change a lot when
  934.  * they are re-loaded.  We could also change the image data to
  935.  * avoid a colourmap to colourmap conversion, but it probably isn't
  936.  * worth it.
  937.  *
  938.  * This should really be 3 or 4 separate functions :-(
  939.  */
  940. void adjust_colourmap(img)
  941. struct mem_image*    img;
  942. {
  943.     int i, r, g, b;
  944.     int mindiff, best;
  945.     unsigned char* red;
  946.     unsigned char* green;
  947.     unsigned char* blue;
  948.     int new;
  949.     char* p;
  950.     int    workspace[256];
  951.     struct map_entry map[256];
  952.  
  953.     if (disp_mode == RGB_LRECT)
  954.         return;
  955.  
  956.     if (disp_mode == RGB_WRITE) {
  957.         /* do it for writeRGB */
  958.         for (i = 0; i < img->maplen; i++)
  959.             workspace[i] = img->colourmap[i];
  960.         red = (unsigned char*)img->colourmap;
  961.         green = (unsigned char*)img->colourmap + img->maplen;
  962.         blue = (unsigned char*)img->colourmap + img->maplen * 2;
  963.         for (i = 0; i < img->maplen; i++) {
  964.             *red++ = workspace[i] & 255;
  965.             *green++ = (workspace[i] >> 8) & 255;
  966.             *blue++ = (workspace[i] >> 16) & 255;
  967.         }
  968.         return;
  969.     }
  970.  
  971.     /*img->mapmap = (int*)malloc(img->maplen * sizeof(int));*/
  972.     /* Don't necessarily need 256, but ... */
  973.     img->mapmap = (int*)malloc(256 * sizeof(int));
  974.     if (img->mapmap == NULL)
  975.         no_mem();
  976.  
  977.     /* colourmapped display */
  978.     if (disp_mode == CMAP_NEWS) {
  979.         if (!dither) {
  980.             for (i = 0; i < img->maplen; i++) {
  981.                 img->mapmap[i] = rgb2newsmap(
  982.                     r = img->colourmap[i] & 255,
  983.                     g = (img->colourmap[i] >> 8) & 255,
  984.                     b = (img->colourmap[i] >> 16) & 255
  985.                 );
  986.             }
  987.         }
  988.         else {
  989.             /* we're going to change all the indicies anyways... */
  990.             for (i = 0; i < 256; i++)
  991.                 img->mapmap[i] = i;
  992.             init_floydstein(img);
  993.         }
  994.         return;
  995.     }
  996.  
  997.     /* disp_mode == CMAP_TRUE */
  998.  
  999.     /* A little kludge here.  The basic idea is that all the images
  1000.      * which use a global colourmap should have the same mapmap.  What
  1001.      * is not so pretty is that maybe the first image didn't use the
  1002.      * global map.
  1003.      */
  1004.     if (img->imf->imglist != img &&
  1005.         img->imf->imglist->colourmap == img->colourmap) {
  1006.         int qq;
  1007.  
  1008.         img->mapmap = img->imf->imglist->mapmap;
  1009.         for (i = 0; i < 256; i++)
  1010.             colour_used[i] = 0;
  1011.  
  1012.         for (i = 0; i < 256; i++)
  1013.             if (img->mapmap[i] >= 0)
  1014.                 colour_used[img->mapmap[i]] = 1;
  1015.         return;
  1016.     }
  1017.  
  1018.     for (i = 0; i < 256; i++) {
  1019.         img->mapmap[i] = -1;
  1020.         colour_used[i] = 0;
  1021.     }
  1022.  
  1023. #ifdef OLD
  1024.     /* disp_mode == CMAP_TRUE */
  1025.     /* ok, let's crank */
  1026.     for (i = 0; i < img->maplen; i++) {
  1027.         map[i].orig_index = i;
  1028.         map[i].abgr = img->colourmap[i];
  1029.         map[i].screen_index = -1;
  1030.         map[i].used = 0;
  1031.     }
  1032.     
  1033.     for (p = img->data, i = img->width * img->height; i > 0; i--)
  1034.         map[*p++].used = 1;
  1035.     
  1036.     qsort(map, img->maplen, sizeof(struct map_entry), map_ent_cmp);
  1037.  
  1038.     for (i = 1; i < img->maplen; i++) {
  1039.         if (!map[i].used)
  1040.             break;            /* 'cause all the unused ones are at the end */
  1041.         if (map[i].abgr == map[i - 1].abgr)
  1042.             map[i].used = -1;
  1043.     }
  1044.  
  1045.     for (i = 0; i < 256; i++)
  1046.         workspace[i] = 0;    /* i.e., we haven't used this screen colour */
  1047.     
  1048.     for (i = 0; i < img->maplen; i++) {
  1049.         if (!map[i].used)
  1050.             break;
  1051.         if (map[i].used == -1)
  1052.             continue;
  1053.         new = rgb2newsmap(map[i].abgr & 255,
  1054.             (map[i].abgr >> 8) & 255,
  1055.             (map[i].abgr >> 16) & 255
  1056.         );
  1057.         if (workspace[new] == 0) {
  1058.             workspace[new] = 1;
  1059.             map[i].screen_index = new;
  1060.         }
  1061.     }
  1062.  
  1063.     new = 0;
  1064.     for (i = 0; i < img->maplen; i++)
  1065.         if (map[i].used && map[i].screen_index == -1)
  1066.             new++;
  1067.     printf("%d hard colours to map\n", new);
  1068.  
  1069. #ifdef SIMPFIT
  1070.     /*
  1071.      * allocate entries for the guys who collided; I should do some sort of
  1072.      * best fitting, but for now just start from the top.  The only best
  1073.      * fit algorithm I can think of is rather expensive.
  1074.      */
  1075.     new = 255;
  1076.     for (i = 0; i < img->maplen; i++) {
  1077.         if (!map[i].used)
  1078.             break;
  1079.         if (map[i].screen_index != -1)
  1080.             continue;
  1081.         while (workspace[new])
  1082.             new--;
  1083.         workspace[new] = 1;
  1084.         map[i].screen_index = new;
  1085.     }
  1086. #endif
  1087.  
  1088.     for (i = 0; i < img->maplen; i++) {
  1089.         int d;
  1090.         short r,g,b;
  1091.         if (!map[i].used)
  1092.             break;
  1093.         if (map[i].screen_index != -1)
  1094.             continue;
  1095.         best = 0;
  1096.         mindiff = 10000;
  1097.         for (new = 0; new < 256; new++) {
  1098.             if (workspace[new])
  1099.                 continue;
  1100.             getmcolor(new, &r, &g, &b);
  1101.             d = abs(r - ((map[i].abgr & 255))) +
  1102.                 abs(g - ((map[i].abgr >> 8) & 255)) +
  1103.                 abs(b - ((map[i].abgr >> 16) & 255));
  1104.             if (d < mindiff) {
  1105.                 best = new;
  1106.                 mindiff = d;
  1107.             }
  1108.         }
  1109.         workspace[best] = 1;
  1110.         map[i].screen_index = best;
  1111.     }
  1112.  
  1113.     /* finally, re-define the image's colourmap.  Don't forget about those
  1114.      * duplicate entries.
  1115.      */
  1116.     for (i = 0; i < img->maplen; i++)
  1117.         img->mapmap[i] = -1;
  1118.  
  1119.     for (i = 0; i < img->maplen; i++) {
  1120.         if (!map[i].used)
  1121.             break;
  1122.         if (map[i].used == -1)
  1123.             img->mapmap[map[i].orig_index] = 
  1124.                 img->mapmap[map[i - 1].orig_index];
  1125.         else
  1126.             img->mapmap[map[i].orig_index] = map[i].screen_index;
  1127.     }
  1128. #endif
  1129. }
  1130.  
  1131. static short orig_colourmap[256 * 3];
  1132. static int orig_cmap_inst = 1;
  1133.  
  1134. true_cmap(img, line)
  1135. struct mem_image*    img;
  1136. int                    line;
  1137. {
  1138.     register unsigned char* p;
  1139.     register int            i;
  1140.     register int            try;
  1141.     short                    r,g,b;
  1142.  
  1143.     for (p = img->data + img->width * line, i = img->width; i >= 0; p++, i--) {
  1144.         if (img->mapmap[*p] >= 0)
  1145.             continue;
  1146.         try = rgb2newsmap(r = img->colourmap[*p] & 255,
  1147.             g = (img->colourmap[*p] >> 8) & 255,
  1148.             b = (img->colourmap[*p] >> 16) & 255
  1149.         );
  1150.         if (colour_used[try]) {
  1151.             int        mindiff, d, j;
  1152.  
  1153.             try = 255;
  1154.             mindiff = 10000;
  1155.             for (j = 255; j >= 0; j--) {
  1156.                 if (colour_used[j])
  1157.                     continue;
  1158.                 if ((d = abs(r - orig_colourmap[j * 3]) +
  1159.                          abs(g - orig_colourmap[j * 3 + 1]) +
  1160.                          abs(b - orig_colourmap[j * 3 + 2])) < mindiff) {
  1161.                     try = j;
  1162.                     mindiff = d;
  1163.                 }
  1164.             }
  1165.         }
  1166.         img->mapmap[*p] = try;
  1167.         colour_used[try] = 1;
  1168.         if (!orig_cmap_inst && img->imf == cur_imf)
  1169.             mapcolor(try, r, g, b);
  1170.     }
  1171. }
  1172.  
  1173. void save_colourmap()
  1174. {
  1175.     short*    p;
  1176.     int        i;
  1177.  
  1178.     for (i = 0, p = orig_colourmap; i < 256; i++, p += 3)
  1179.         getmcolor(i, p, p + 1, p + 2);
  1180. }
  1181.  
  1182. void set_colourmap(img)
  1183. struct mem_image*    img;
  1184. {
  1185.     int    i;
  1186.  
  1187.     for (i = 0; i < img->maplen; i++)
  1188.         if (img->mapmap[i] >= 0)
  1189.             mapcolor(img->mapmap[i],
  1190.                 img->colourmap[i] & 255,
  1191.                 (img->colourmap[i] >> 8) & 255,
  1192.                 (img->colourmap[i] >> 16) & 255
  1193.             );
  1194.     orig_cmap_inst = 0;
  1195. }
  1196.  
  1197. void toggle_colourmap(img)
  1198. struct mem_image*    img;
  1199. {
  1200.     if (orig_cmap_inst)
  1201.         set_colourmap(img);
  1202.     else
  1203.         restore_colourmap();
  1204. }
  1205.  
  1206. void restore_colourmap()
  1207. {
  1208.     short*    p;
  1209.     int        i;
  1210.  
  1211.     for (i = 0, p = orig_colourmap; i < 256; i++, p += 3)
  1212.         mapcolor(i, p[0], p[1], p[2]);
  1213.     orig_cmap_inst = 1;
  1214. }
  1215.