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(<ime);
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