File: common\stereo.c

    1 /*
    2     STEREO.C a module to view 3D images.
    3     Written in Borland 'C++' by Paul de Leeuw.
    4     From an idea in "New Scientist" 9 October 1993 pages 26 - 29.
    5 
    6     Change History:
    7       11 June 94 - Modified to reuse existing Fractint arrays        TW
    8       11 July 94 - Added depth parameter                             PDL
    9       14 July 94 - Added grayscale option and did general cleanup    TW
   10       19 July 94 - Fixed negative depth                              PDL
   11       19 July 94 - Added calibration bars, get_min_max()             TW
   12       24 Sep  94 - Added image save/restore, color cycle, and save   TW
   13       28 Sep  94 - Added image map                                   TW
   14       20 Mar  95 - Fixed endless loop bug with bad depth values      TW
   15       23 Mar  95 - Allow arbitrary dimension image maps              TW
   16 
   17       (TW is Tim Wegner, PDL is Paul de Leeuw)
   18 */
   19 
   20 #include <string.h>
   21 #include <time.h>
   22 
   23   /* see Fractint.c for a description of the "include"  hierarchy */
   24 #include "port.h"
   25 #include "prototyp.h"
   26 #include "helpdefs.h"
   27 
   28 char stereomapname[FILE_MAX_DIR+1] = {""};
   29 int AutoStereo_depth = 100;
   30 double AutoStereo_width = 10;
   31 char grayflag = 0;              /* flag to use gray value rather than color
   32                                  * number */
   33 char calibrate = 1;             /* add calibration bars to image */
   34 char image_map = 0;
   35 
   36 /* this structure permits variables to be temporarily static and visible
   37    to routines in this file without permanently hogging memory */
   38 
   39 static struct static_vars
   40 {
   41    long avg;
   42    long avgct;
   43    long depth;
   44    int barheight;
   45    int ground;
   46    int maxcc;
   47    int maxc;
   48    int minc;
   49    int reverse;
   50    int sep;
   51    double width;
   52    int x1;
   53    int x2;
   54    int xcen;
   55    int y;
   56    int y1;
   57    int y2;
   58    int ycen;
   59    BYTE *savedac;
   60 } *pv;
   61 
   62 #define AVG         (pv->avg)
   63 #define AVGCT       (pv->avgct)
   64 #define DEPTH       (pv->depth)
   65 #define BARHEIGHT   (pv->barheight)
   66 #define GROUND      (pv->ground)
   67 #define MAXCC       (pv->maxcc)
   68 #define MAXC        (pv->maxc)
   69 #define MINC        (pv->minc)
   70 #define REVERSE     (pv->reverse)
   71 #define SEP         (pv->sep)
   72 #define WIDTH       (pv->width)
   73 #define X1          (pv->x1)
   74 #define X2          (pv->x2)
   75 #define Y           (pv->y)
   76 #define Y1          (pv->y1)
   77 #define Y2          (pv->y2)
   78 #define XCEN        (pv->xcen)
   79 #define YCEN        (pv->ycen)
   80 
   81 /*
   82    The getdepth() function allows using the grayscale value of the color
   83    as DEPTH, rather than the color number. Maybe I got a little too
   84    sophisticated trying to avoid a divide, so the comment tells what all
   85    the multiplies and shifts are trying to do. The result should be from
   86    0 to 255.
   87 */
   88 
   89 typedef BYTE (*DACBOX)[256][3];
   90 #define dac   (*((DACBOX)(pv->savedac)))
   91 
   92 static int getdepth(int xd, int yd)
   93 {
   94    int pal;
   95    pal = getcolor(xd, yd);
   96    if (grayflag)
   97    {
   98       /* effectively (30*R + 59*G + 11*B)/100 scaled 0 to 255 */
   99       pal = ((int) dac[pal][0] * 77 +
  100              (int) dac[pal][1] * 151 +
  101              (int) dac[pal][2] * 28);
  102       pal >>= 6;
  103    }
  104    return (pal);
  105 }
  106 
  107 /*
  108    Get min and max DEPTH value in picture
  109 */
  110 
  111 static int get_min_max(void)
  112 {
  113    int xd, yd, ldepth;
  114    MINC = colors;
  115    MAXC = 0;
  116    for(yd = 0; yd < ydots; yd++)
  117    {
  118       if (keypressed())
  119          return (1);
  120       if(yd == 20)
  121          showtempmsg("Getting min and max");
  122       for(xd = 0; xd < xdots; xd++)
  123       {
  124          ldepth = getdepth(xd,yd);
  125          if ( ldepth < MINC)
  126             MINC = ldepth;
  127          if (ldepth > MAXC)
  128             MAXC = ldepth;
  129       }
  130    }
  131    cleartempmsg();
  132    return(0);
  133 }
  134 
  135 void toggle_bars(int *bars, int barwidth, int far *colour)
  136 {
  137    int i, j, ct;
  138    find_special_colors();
  139    ct = 0;
  140    for (i = XCEN; i < (XCEN) + barwidth; i++)
  141       for (j = YCEN; j < (YCEN) + BARHEIGHT; j++)
  142       {
  143          if(*bars)
  144          {
  145             putcolor(i + (int)(AVG), j , color_bright);
  146             putcolor(i - (int)(AVG), j , color_bright);
  147          }
  148          else
  149          {
  150             putcolor(i + (int)(AVG), j, colour[ct++]);
  151             putcolor(i - (int)(AVG), j, colour[ct++]);
  152          }
  153       }
  154    *bars = 1 - *bars;
  155 }
  156 
  157 int outline_stereo(BYTE * pixels, int linelen)
  158 {
  159    int i, j, x, s;
  160    int far *same;
  161    int far *colour;
  162    if((Y) >= ydots)
  163       return(1);
  164    same   = (int far *)MK_FP(extraseg,0);
  165    colour = &same[ydots];
  166 
  167    for (x = 0; x < xdots; ++x)
  168       same[x] = x;
  169    for (x = 0; x < xdots; ++x)
  170    {
  171       if(REVERSE)
  172          SEP = GROUND - (int) (DEPTH * (getdepth(x, Y) - MINC) / MAXCC);
  173       else
  174          SEP = GROUND - (int) (DEPTH * (MAXCC - (getdepth(x, Y) - MINC)) / MAXCC);
  175       SEP =  (int)((SEP * 10.0) / WIDTH);        /* adjust for media WIDTH */
  176 
  177       /* get average value under calibration bars */
  178       if(X1 <= x && x <= X2 && Y1 <= Y && Y <= Y2)
  179       {
  180          AVG += SEP;
  181          (AVGCT)++;
  182       }
  183       i = x - (SEP + (SEP & Y & 1)) / 2;
  184       j = i + SEP;
  185       if (0 <= i && j < xdots)
  186       {
  187          /* there are cases where next never terminates so we timeout */
  188          int ct = 0;
  189          for (s = same[i]; s != i && s != j && ct++ < xdots; s = same[i])
  190          {
  191             if (s > j)
  192             {
  193                same[i] = j;
  194                i = j;
  195                j = s;
  196             }
  197             else
  198                i = s;
  199          }
  200          same[i] = j;
  201       }
  202    }
  203    for (x = xdots - 1; x >= 0; x--)
  204    {
  205       if (same[x] == x)
  206          /* colour[x] = rand()%colors; */
  207          colour[x] = (int)pixels[x%linelen];
  208       else
  209          colour[x] = colour[same[x]];
  210       putcolor(x, Y, colour[x]);
  211    }
  212    (Y)++;
  213    return(0);
  214 }
  215 
  216 
  217 /**************************************************************************
  218         Convert current image into Auto Stereo Picture
  219 **************************************************************************/
  220 
  221 int do_AutoStereo(void)
  222 {
  223    struct static_vars v;
  224    BYTE savedacbox[256*3];
  225    int oldhelpmode, ret=0;
  226    int i, j, done;
  227    int bars, ct, kbdchar, barwidth;
  228    time_t ltime;
  229    unsigned char *buf = (unsigned char *)decoderline;
  230    /* following two lines re-use existing arrays in Fractint */
  231    int far *same;
  232    int far *colour;
  233    same   = (int far *)MK_FP(extraseg,0);
  234    colour = &same[ydots];
  235 
  236    pv = &v;   /* set static vars to stack structure */
  237    pv->savedac = savedacbox;
  238 
  239    /* Use the current time to randomize the random number sequence. */
  240    time(&ltime);
  241    srand((unsigned int)ltime);
  242 
  243    oldhelpmode = helpmode;
  244    helpmode = RDSKEYS;
  245    savegraphics();                      /* save graphics image */
  246    memcpy(savedacbox, dacbox, 256 * 3);  /* save colors */
  247 
  248    if(xdots > OLDMAXPIXELS)
  249    {
  250       static FCODE msg[] = 
  251          {"Stereo not allowed with resolution > 2048 pixels wide"};
  252       stopmsg(0,msg);
  253       buzzer(1);
  254       ret = 1;
  255       goto exit_stereo;
  256    }
  257 
  258    /* empircally determined adjustment to make WIDTH scale correctly */
  259    WIDTH = AutoStereo_width*.67;
  260    if(WIDTH < 1)
  261       WIDTH = 1;
  262    GROUND = xdots / 8;
  263    if(AutoStereo_depth < 0)
  264       REVERSE = 1;
  265    else
  266       REVERSE = 0;
  267    DEPTH = ((long) xdots * (long) AutoStereo_depth) / 4000L;
  268    DEPTH = labs(DEPTH) + 1;
  269    if(get_min_max())
  270    {
  271       buzzer(1);
  272       ret = 1;
  273       goto exit_stereo;
  274    }
  275    MAXCC = MAXC - MINC + 1;
  276    AVG = AVGCT = 0L;
  277    barwidth  = 1 + xdots / 200;
  278    BARHEIGHT = 1 + ydots / 20;
  279    XCEN = xdots/2;
  280    if(calibrate > 1)
  281       YCEN = BARHEIGHT/2;
  282    else
  283       YCEN = ydots/2;
  284 
  285    /* box to average for calibration bars */
  286    X1 = XCEN - xdots/16;
  287    X2 = XCEN + xdots/16;
  288    Y1 = YCEN - BARHEIGHT/2;
  289    Y2 = YCEN + BARHEIGHT/2;
  290 
  291    Y = 0;
  292    if(image_map)
  293    {
  294       outln = outline_stereo;
  295       while((Y) < ydots)
  296          if(gifview())
  297          {
  298             ret = 1;
  299             goto exit_stereo;
  300          }
  301    }
  302    else
  303    {
  304       while(Y < ydots)
  305       {
  306           if(keypressed())
  307           {
  308              ret = 1;
  309              goto exit_stereo;
  310           }
  311           for(i=0;i<xdots;i++)
  312              buf[i] = (unsigned char)(rand()%colors);
  313           outline_stereo(buf,xdots);
  314       }
  315    }
  316 
  317    find_special_colors();
  318    AVG /= AVGCT;
  319    AVG /= 2;
  320    ct = 0;
  321    for (i = XCEN; i < XCEN + barwidth; i++)
  322       for (j = YCEN; j < YCEN + BARHEIGHT; j++)
  323       {
  324          colour[ct++] = getcolor(i + (int)(AVG), j);
  325          colour[ct++] = getcolor(i - (int)(AVG), j);
  326       }
  327    if(calibrate)
  328       bars = 1;
  329    else
  330       bars = 0;
  331    toggle_bars(&bars, barwidth, colour);
  332    done = 0;
  333    while(done==0)
  334    {
  335       while(keypressed()==0); /* to trap F1 key */
  336       kbdchar = getakey();
  337       switch(kbdchar)
  338       {
  339          case ENTER:   /* toggle bars */
  340          case SPACE:
  341             toggle_bars(&bars, barwidth, colour);
  342             break;
  343          case 'c':
  344          case '+':
  345          case '-':
  346             rotate((kbdchar == 'c') ? 0 : ((kbdchar == '+') ? 1 : -1));
  347             break;
  348          case 's':
  349          case 'S':
  350             diskisactive = 1;           /* flag for disk-video routines */
  351             savetodisk(savename);
  352             diskisactive = 0;
  353             break;
  354          default:
  355             if(kbdchar == 27)   /* if ESC avoid returning to menu */
  356                kbdchar = 255;
  357             ungetakey(kbdchar);
  358             buzzer(0);
  359             done = 1;
  360             break;
  361        }
  362    }
  363 
  364    exit_stereo:
  365    helpmode = oldhelpmode;
  366    restoregraphics();
  367    memcpy(dacbox, savedacbox, 256 * 3);
  368    spindac(0,1);
  369    return (ret);
  370 }
  371