55cc106b3ca2a3be059f48bf28ccecda838335da
[libopano.git] / src / viewer.c
1 /* Panorama_Tools       -       Generate, Edit and Convert Panoramic Images
2    Copyright (C) 1998,1999,2000 - Helmut Dersch  der@fh-furtwangen.de
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /*------------------------------------------------------------*/
19
20 #include <X11/X.h>
21 #include <X11/Xlib.h>
22 #include <X11/Xutil.h>
23 #include <X11/cursorfont.h>
24 #include <X11/keysym.h>
25 #include <X11/Xatom.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include "filter.h"
29
30 void    PV_ExtractStill( TrformStr *TrPtr );
31 void    PV_SetInvMakeParams( struct fDesc *stack, struct MakeParams *mp, Image *im , Image *pn );
32 int     SetUpAtan();
33 int     SetUpSqrt();
34 int     SetUpMweights();
35
36 void    DrawWindow();
37 void    DrawView(int InterPolator);
38
39 int                                     panning = FALSE;
40 int                                     zooming_in = FALSE;
41 int                                     zooming_out = FALSE;
42
43 #define VIEW_WIDTH      400
44 #define VIEW_HEIGHT 300
45
46
47 char text[] = "PTViewer";
48
49 Image                   view, pano;
50 Display                 *disp;
51 Window                  win;
52 int                     screen;
53 XImage                  *ximage = NULL;
54 GC                              gc;
55 Visual                  *visual;
56 unsigned int    depth;
57 int                     oldposx, oldposy;
58
59
60 int main( int argc, char** argv )
61 {
62         KeySym key;
63         XSizeHints hint;
64         unsigned long fg, bg;
65         int done;
66         union 
67         {
68         XEvent              event;
69         XAnyEvent           any;
70         XButtonEvent        button;
71         XKeyEvent           key;
72         XConfigureEvent     configure;
73         XExposeEvent        expose;
74         XMotionEvent        motion;
75         XResizeRequestEvent resize;
76         XClientMessageEvent message;
77          } event;
78         int newposx, newposy;
79         static Atom             proto_atom= None, delete_atom= None;
80
81
82         // Set up display
83         
84
85
86         disp            = XOpenDisplay("");
87         screen          = DefaultScreen(disp);
88         depth           = DefaultDepth(disp, screen);
89         
90         if( depth != 24 )
91         {
92                 PrintError("Depth = %d, must be 24 pixels", (int)depth);
93                 exit(0);
94         }
95         bg = WhitePixel(disp,screen);
96         fg = BlackPixel(disp,screen);
97
98         // Load panoramic image         
99         if( argc < 2 )
100         {
101                 PrintError("No image file supplied");
102                 exit(0);
103         }
104         
105         if( strcmp( argv[1], "-h" ) == 0 )
106         {
107                 PrintError( "%s\n %s\nUsage: PTViewer Imagefile", "PTViewer", LONGVERSION );
108                 exit(0);
109         }
110         if( readJPEG( &pano, (fullPath*)argv[1] ) != 0 )
111         {
112                 PrintError("Could not read panoramic image");
113                 exit(0);
114         }
115         pano.hfov               = 360.0;
116         pano.format     = _equirectangular;
117         
118         // Initialize Look-up tables
119         
120         if( SetUpAtan() || SetUpSqrt() || SetUpMweights() )
121         {
122                 PrintError("Could not set up LU Tables");
123                 exit(0);
124         }
125
126         // Set up viewer window
127
128         SetImageDefaults( &view );
129
130         view.hfov                       = 70.0;
131         view.width                      = VIEW_WIDTH;
132         view.height                     = VIEW_HEIGHT;
133         view.bitsPerPixel       = 32;
134         view.bytesPerLine       = view.width * view.bitsPerPixel / 8;
135         view.dataSize           = view.bytesPerLine * view.height;
136         view.format                     = 1;
137         view.data                       = (unsigned char**)mymalloc( view.dataSize );
138         if(view.data == NULL)
139         {
140                 PrintError("Not enough memory");
141                 exit(0);
142         }
143
144
145         hint.x                  = 200;
146         hint.y                  = 200;
147         hint.width              = view.width;
148         hint.height     = view.height;
149         hint.flags=PPosition | PSize;
150
151         win = XCreateSimpleWindow(disp,
152                 DefaultRootWindow(disp),
153                 hint.x,
154                 hint.y,
155                 hint.width,
156                 hint.height,
157                 5,
158                 fg,
159                 bg);
160
161         XSetStandardProperties(disp,
162                 win,
163                 text,
164                 text,
165                 None,
166                 argv,
167                 argc,
168                 &hint);
169         gc = XCreateGC(disp,win,0,0);
170         XSetBackground(disp,gc,bg);
171         XSetForeground(disp,gc,fg);
172
173         XSelectInput(disp,
174                 win,
175                 ButtonPressMask | ButtonReleaseMask | Button1MotionMask | KeyPressMask | KeyReleaseMask |
176         StructureNotifyMask | EnterWindowMask | LeaveWindowMask| ExposureMask);
177         XMapRaised(disp,win);
178
179     proto_atom  = XInternAtom(disp, "WM_PROTOCOLS", False);
180     delete_atom = XInternAtom(disp, "WM_DELETE_WINDOW", False);
181     if ((proto_atom != None) && (delete_atom != None))
182                 XChangeProperty(disp, win, proto_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&delete_atom, 1);
183  
184
185
186         visual  = DefaultVisual(disp, screen);
187
188         ximage = XCreateImage(disp, visual, depth, ZPixmap, 0,
189                                           NULL, view.width, view.height,
190                                           8, 0);
191     memset( *(view.data), 0, view.dataSize);
192     ximage->data = (char *)*(view.data);
193     ximage->byte_order= MSBFirst;
194
195         
196
197         done = 0;
198         while( done==0)
199         {
200                 int isChanged = 0;
201                 
202                 // No XNextEvent 
203                                 
204                 if( XCheckTypedEvent(disp, Expose, (XEvent*)&event) ) DrawWindow();
205
206                 if( XCheckTypedEvent(disp, KeyPress, (XEvent*)&event) )
207                 {
208                 char buf[128];
209                 KeySym ks;
210                 XComposeStatus status;
211                 int nkey = XLookupString(&event.key,buf,128,&ks,&status);
212                                                 
213                 if( nkey == 0 )
214                 {
215                         switch(ks)
216                         {
217                                 case XK_Shift_L:
218                                         case XK_Shift_R: zooming_in = TRUE;
219                                         break;
220                                 
221                                 case XK_Control_L:
222                                         case XK_Control_R: zooming_out = TRUE;
223                                 break;
224                         }
225                 }
226                 else if( nkey == 1 && buf[0] == 'q')
227                         done = 1;
228                                                 
229                 goto _EventLoop;
230         }
231
232                 if( XCheckTypedEvent(disp, KeyRelease, (XEvent*)&event) ) 
233                 {
234                 char buf[128];
235                 KeySym ks;
236                 XComposeStatus status;
237
238                 if (XLookupString(&event.key,buf,128,&ks,&status) == 0)
239                 {
240                         switch(ks)
241                         {
242                                 case XK_Shift_L:
243                         case XK_Shift_R: zooming_in = FALSE;
244                                                                 view.format = 1;
245                                                                 DrawWindow();
246                                                                 break;
247                                 case XK_Control_L: 
248                         case XK_Control_R: zooming_out = FALSE;
249                                                                 view.format = 1;
250                                                                 DrawWindow();
251                                                                 break;
252                         }
253                 }
254                 goto _EventLoop;
255                 }
256  
257                 if( XCheckTypedEvent(disp, MotionNotify, (XEvent*)&event) )
258                 { 
259                         newposx = event.button.x;
260             newposy = event.button.y;
261                 while (XCheckTypedEvent(disp, MotionNotify, (XEvent*)&event) == True) 
262                 {
263                                 newposx= event.button.x;
264                                 newposy= event.button.y;
265                 }
266                 }
267                                         
268                 if( XCheckTypedEvent(disp, ButtonPress, (XEvent*)&event) )
269                 {
270                 if (event.button.button == 1) 
271                 {
272                         panning = TRUE;
273                                 oldposx = newposx = event.button.x;
274                                 oldposy = newposy = event.button.y;
275                         }
276                 }
277
278                 if( XCheckTypedEvent(disp, ButtonRelease, (XEvent*)&event) )
279                 {
280                 if (event.button.button == 1) 
281                 {
282                                 panning = FALSE;
283                                 view.format = 1;
284                                 DrawWindow();
285                         }
286                 }
287                 
288                 if( XCheckTypedEvent(disp, DestroyNotify, (XEvent*)&event) )
289                         done = 1;
290                 
291                 if( XCheckTypedEvent(disp,ClientMessage , (XEvent*)&event) )
292                 {
293                         if ((event.message.window == win) && (event.message.data.l[0] == delete_atom)) 
294                                 done = 1;
295                 }
296
297                 
298 _EventLoop:                     
299                 if( panning )
300                 {
301                                 double yaw, pitch;
302                                                                                 
303                                 yaw = view.yaw + (newposx - oldposx)/20.0;
304                                 NORM_ANGLE( yaw );
305                                                                                 
306                                 pitch = view.pitch - (newposy - oldposy)/20.0;
307                                 if( pitch > 90.0 ) pitch = 90.0;
308                                 if( pitch < -90.0 ) pitch = -90.0;
309                                                                         
310                                 if( pitch != view.pitch || yaw != view.yaw )
311                                 {
312                                         view.pitch      = pitch;
313                                         view.yaw        = yaw;
314                                         isChanged = 1;
315                                 }
316                 }
317                 if( zooming_in && view.hfov > 10.5)
318                 {
319                         view.hfov /= 1.03;
320                         isChanged = 1;
321                 }
322                 if( zooming_out && view.hfov < 165.0)
323                 {
324                         view.hfov *= 1.03;
325                         isChanged = 1;
326                 }
327                 if( zooming_in || zooming_out || panning )
328                 {
329                         if( isChanged )
330                         {
331                                 view.format = 1;
332                                 DrawWindow();
333                         }
334                 }
335
336
337         }
338         XFreeGC(disp,gc);
339         XDestroyWindow(disp, win);
340         XCloseDisplay(disp);
341 }
342         
343
344
345
346
347
348 // Error reporting
349
350 void  PrintError( char* fmt, ...)
351 {
352         va_list ap;
353         char message[257];
354         
355         va_start(ap, fmt);
356         vsprintf(message, fmt, ap);
357         va_end(ap);
358         
359         printf("%s\n", message);
360 }
361
362 void**  mymalloc( long numBytes )                                       // Memory allocation, use Handles
363 {
364         char **mem;
365         
366         mem = (char**)malloc( sizeof(char*) );                  // Allocate memory for pointer
367         if(mem == NULL)
368                 return (void**)NULL;
369         else
370         {
371                 (*mem) = (char*) malloc( numBytes );            // Allocate numBytes
372                 if( *mem == NULL )
373                 {
374                         free( mem );
375                         return (void**)NULL;
376                 }
377                 else
378                         return (void**)mem;
379         }
380 }
381
382 void    myfree( void** Hdl )                                            // free Memory, use Handles
383 {
384         free( (char*) *Hdl );
385         free( (char**) Hdl );
386 }               
387
388
389 void SetImageDefaults(Image *im)
390 {
391         im->data                        = NULL;
392         im->bytesPerLine        = 0;
393         im->width                       = 0;
394         im->height                      = 0;
395         im->dataSize            = 0;
396         im->bitsPerPixel        = 0;
397         im->format                      = 0;
398         im->dataformat          = _RGB;
399         im->hfov                        = 0.0;
400         im->yaw                         = 0.0;
401         im->pitch                       = 0.0;
402         im->roll                        = 0.0;
403 //      SetCorrectDefaults( &(im->cP) );
404         *(im->name)                     = 0;
405 }
406
407 // expand image from 3 to 4 bits per pixel. No pad bytes allowed.
408 // Memory must be allocated
409 void ThreeToFourBPP( Image *im )
410 {
411         register int x,y,c1,c2;
412
413         if( im->bitsPerPixel == 32 || im->bitsPerPixel == 64) // Nothing to do
414                 return;
415         
416         
417         
418         if( im->bitsPerPixel == 24 )    // Convert to 4byte / pixel
419         {
420                 for( y = im->height-1; y>=0; y--)
421                 {
422                         for( x= im->width-1; x>=0; x--)
423                         {
424                                 c1 = (y * im->width + x) * 4;
425                                 c2 = y * im->bytesPerLine + x * 3;
426                                 (*(im->data))[c1++] = UCHAR_MAX;
427                                 (*(im->data))[c1++] = (*(im->data))[c2++];
428                                 (*(im->data))[c1++] = (*(im->data))[c2++];
429                                 (*(im->data))[c1++] = (*(im->data))[c2++];
430                         }
431                 }
432                 im->bitsPerPixel = 32;
433                 im->bytesPerLine = im->width * 4;
434         }
435         else if( im->bitsPerPixel == 48 ) // Convert to 8byte / pixel
436         {
437                 for( y = im->height-1; y>=0; y--)
438                 {
439                         for( x= im->width-1; x>=0; x--)
440                         {
441                                 c1 = (y * im->width + x) * 4;
442                                 c2 = y * im->bytesPerLine/2 + x * 3;
443                                 ((USHORT*)(*(im->data)))[c1++] = USHRT_MAX;
444                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
445                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
446                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
447                         }
448                 }
449                 im->bitsPerPixel = 64;
450                 im->bytesPerLine = im->width * 8;
451         }
452         im->dataSize = im->height * im->bytesPerLine;
453 }
454
455
456
457 void DrawWindow()
458 {
459         XWindowAttributes xa;
460
461         XGetWindowAttributes( disp, win, &xa );
462
463         if( xa.width != view.width || xa.height != view.height )
464         {
465                 myfree((void**)view.data);
466                 view.width                      = xa.width;
467                 view.height             = xa.height;
468         view.bytesPerLine       = view.width * view.bitsPerPixel / 8;
469         view.dataSize           = view.bytesPerLine * view.height;
470         view.format             = 1;
471         view.data               = (unsigned char**)mymalloc( view.dataSize );
472         if(view.data == NULL)
473         {
474              PrintError("Not enough memory");
475              exit(0);
476         }
477
478         ximage = XCreateImage(disp, visual, depth, ZPixmap, 0,
479                                           NULL, view.width, view.height,
480                                           8, 0);
481         ximage->data = (char *)*(view.data);
482         ximage->byte_order= MSBFirst;
483         }
484         if( view.format )
485         {
486                 if( panning || zooming_in || zooming_out)
487                         DrawView(_nn);
488                 else
489                         DrawView(_bilinear);
490         }
491                         
492         XPutImage(disp, win, gc, ximage, 0, 0, 0, 0, view.width, view.height);
493 }
494
495 void DrawView(int InterPolator)
496 {
497         TrformStr                               Tr;
498                         
499         Tr.interpolator                 = InterPolator;
500
501         Tr.src                                  = &pano;
502         Tr.dest                                 = &view;        
503         
504         PV_ExtractStill( &Tr );
505         
506         view.format = 0;
507         return;
508         
509 }
510