viewer.c: Removed debugging output and fixed indentation.
[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 <stdint.h>
29 #include "filter.h"
30
31 void    PV_ExtractStill( TrformStr *TrPtr );
32 void    PV_SetInvMakeParams( struct fDesc *stack, struct MakeParams *mp, Image *im , Image *pn );
33 int     SetUpAtan();
34 int     SetUpSqrt();
35 int     SetUpMweights();
36
37 void    DrawWindow();
38 void    DrawView(int InterPolator);
39
40 int readJPEG ( Image *im, fullPath *sfile );
41
42 int                                     panning = FALSE;
43 int                                     zooming_in = FALSE;
44 int                                     zooming_out = FALSE;
45
46 #define VIEW_WIDTH      400
47 #define VIEW_HEIGHT 300
48
49
50 char text[] = "PTViewer";
51
52 Image                   view, pano;
53 Display                 *disp;
54 Window                  win;
55 int                     screen;
56 XImage                  *ximage = NULL;
57 GC                              gc;
58 Visual                  *visual;
59 unsigned int    depth;
60 int                     oldposx, oldposy;
61
62
63 int main( int argc, char** argv )
64 {
65         XSizeHints hint;
66         unsigned long fg, bg;
67         int done;
68         union 
69         {
70         XEvent              event;
71         XAnyEvent           any;
72         XButtonEvent        button;
73         XKeyEvent           key;
74         XConfigureEvent     configure;
75         XExposeEvent        expose;
76         XMotionEvent        motion;
77         XResizeRequestEvent resize;
78         XClientMessageEvent message;
79          } event;
80         int newposx, newposy;
81         static Atom             proto_atom= None, delete_atom= None;
82
83
84         // Set up display
85         
86
87
88         disp            = XOpenDisplay("");
89         screen          = DefaultScreen(disp);
90         depth           = DefaultDepth(disp, screen);
91         
92         if( depth != 24 )
93         {
94                 PrintError("Depth = %d, must be 24 pixels", (int)depth);
95         }
96         bg = WhitePixel(disp,screen);
97         fg = BlackPixel(disp,screen);
98
99         // Load panoramic image         
100         if( argc < 2 )
101         {
102                 PrintError("No image file supplied");
103                 exit(0);
104         }
105         
106         if( strcmp( argv[1], "-h" ) == 0 )
107         {
108                 PrintError( "%s\n %s\nUsage: PTViewer Imagefile", "PTViewer", LONGVERSION );
109                 exit(0);
110         }
111         if( readJPEG( &pano, (fullPath*)argv[1] ) != 0 )
112         {
113                 PrintError("Could not read panoramic image");
114                 exit(0);
115         }
116         pano.hfov               = 360.0;
117         pano.format     = _equirectangular;
118         
119         // Initialize Look-up tables
120         
121         if( SetUpAtan() || SetUpSqrt() || SetUpMweights() )
122         {
123                 PrintError("Could not set up LU Tables");
124                 exit(0);
125         }
126
127         // Set up viewer window
128
129         SetImageDefaults( &view );
130
131         view.hfov                       = 70.0;
132         view.width                      = VIEW_WIDTH;
133         view.height                     = VIEW_HEIGHT;
134         view.bitsPerPixel       = 32;
135         view.bytesPerLine       = view.width * view.bitsPerPixel / 8;
136         view.dataSize           = view.bytesPerLine * view.height;
137         view.format                     = 1;
138         view.data                       = (unsigned char**)mymalloc( view.dataSize );
139         if(view.data == NULL)
140         {
141                 PrintError("Not enough memory");
142                 exit(0);
143         }
144
145
146         hint.x                  = 200;
147         hint.y                  = 200;
148         hint.width              = view.width;
149         hint.height     = view.height;
150         hint.flags=PPosition | PSize;
151
152         win = XCreateSimpleWindow(disp,
153                 DefaultRootWindow(disp),
154                 hint.x,
155                 hint.y,
156                 hint.width,
157                 hint.height,
158                 5,
159                 fg,
160                 bg);
161
162         XSetStandardProperties(disp,
163                 win,
164                 text,
165                 text,
166                 None,
167                 argv,
168                 argc,
169                 &hint);
170         gc = XCreateGC(disp,win,0,0);
171         XSetBackground(disp,gc,bg);
172         XSetForeground(disp,gc,fg);
173
174         XSelectInput(disp,
175                 win,
176                 ButtonPressMask | ButtonReleaseMask | Button1MotionMask | KeyPressMask | KeyReleaseMask |
177         StructureNotifyMask | EnterWindowMask | LeaveWindowMask| ExposureMask);
178         XMapRaised(disp,win);
179
180     proto_atom  = XInternAtom(disp, "WM_PROTOCOLS", False);
181     delete_atom = XInternAtom(disp, "WM_DELETE_WINDOW", False);
182     if ((proto_atom != None) && (delete_atom != None))
183                 XChangeProperty(disp, win, proto_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&delete_atom, 1);
184  
185
186
187     visual  = DefaultVisual(disp, screen);
188
189     ximage = XCreateImage(disp, visual, depth, ZPixmap, 0,
190                     NULL, view.width, view.height,
191                     8, 0);
192     memset( *(view.data), 0, view.dataSize);
193     ximage->data = (char *)*(view.data);
194     ximage->byte_order= MSBFirst;
195
196
197
198         done = 0;
199         while( done==0)
200         {
201                 int isChanged = 0;
202                 
203                 // No XNextEvent 
204                                 
205                 if( XCheckTypedEvent(disp, Expose, (XEvent*)&event) ) DrawWindow();
206
207                 if( XCheckTypedEvent(disp, KeyPress, (XEvent*)&event) )
208                 {
209                 char buf[128];
210                 KeySym ks;
211                 XComposeStatus status;
212                 int nkey = XLookupString(&event.key,buf,128,&ks,&status);
213                                                 
214                 if( nkey == 0 )
215                 {
216                         switch(ks)
217                         {
218                                 case XK_Shift_L:
219                                         case XK_Shift_R: zooming_in = TRUE;
220                                         break;
221                                 
222                                 case XK_Control_L:
223                                         case XK_Control_R: zooming_out = TRUE;
224                                 break;
225                         }
226                 }
227                 else if( nkey == 1 && buf[0] == 'q')
228                         done = 1;
229                                                 
230                 goto _EventLoop;
231         }
232
233                 if( XCheckTypedEvent(disp, KeyRelease, (XEvent*)&event) ) 
234                 {
235                 char buf[128];
236                 KeySym ks;
237                 XComposeStatus status;
238
239                 if (XLookupString(&event.key,buf,128,&ks,&status) == 0)
240                 {
241                         switch(ks)
242                         {
243                                 case XK_Shift_L:
244                         case XK_Shift_R: zooming_in = FALSE;
245                                                                 view.format = 1;
246                                                                 DrawWindow();
247                                                                 break;
248                                 case XK_Control_L: 
249                         case XK_Control_R: zooming_out = FALSE;
250                                                                 view.format = 1;
251                                                                 DrawWindow();
252                                                                 break;
253                         }
254                 }
255                 goto _EventLoop;
256                 }
257  
258                 if( XCheckTypedEvent(disp, MotionNotify, (XEvent*)&event) )
259                 { 
260                         newposx = event.button.x;
261             newposy = event.button.y;
262                 while (XCheckTypedEvent(disp, MotionNotify, (XEvent*)&event) == True) 
263                 {
264                                 newposx= event.button.x;
265                                 newposy= event.button.y;
266                 }
267                 }
268                                         
269                 if( XCheckTypedEvent(disp, ButtonPress, (XEvent*)&event) )
270                 {
271                 if (event.button.button == 1) 
272                 {
273                         panning = TRUE;
274                                 oldposx = newposx = event.button.x;
275                                 oldposy = newposy = event.button.y;
276                         }
277                 }
278
279                 if( XCheckTypedEvent(disp, ButtonRelease, (XEvent*)&event) )
280                 {
281                 if (event.button.button == 1) 
282                 {
283                                 panning = FALSE;
284                                 view.format = 1;
285                                 DrawWindow();
286                         }
287                 }
288                 
289                 if( XCheckTypedEvent(disp, DestroyNotify, (XEvent*)&event) )
290                         done = 1;
291                 
292                 if( XCheckTypedEvent(disp,ClientMessage , (XEvent*)&event) )
293                 {
294                         if ((event.message.window == win) && (event.message.data.l[0] == delete_atom)) 
295                                 done = 1;
296                 }
297
298                 
299 _EventLoop:                     
300                 if( panning )
301                 {
302                                 double yaw, pitch;
303                                                                                 
304                                 yaw = view.yaw + (newposx - oldposx)/20.0;
305                                 NORM_ANGLE( yaw );
306                                                                                 
307                                 pitch = view.pitch - (newposy - oldposy)/20.0;
308                                 if( pitch > 90.0 ) pitch = 90.0;
309                                 if( pitch < -90.0 ) pitch = -90.0;
310                                                                         
311                                 if( pitch != view.pitch || yaw != view.yaw )
312                                 {
313                                         view.pitch      = pitch;
314                                         view.yaw        = yaw;
315                                         isChanged = 1;
316                                 }
317                 }
318                 if( zooming_in && view.hfov > 10.5)
319                 {
320                         view.hfov /= 1.03;
321                         isChanged = 1;
322                 }
323                 if( zooming_out && view.hfov < 165.0)
324                 {
325                         view.hfov *= 1.03;
326                         isChanged = 1;
327                 }
328                 if( zooming_in || zooming_out || panning )
329                 {
330                         if( isChanged )
331                         {
332                                 view.format = 1;
333                                 DrawWindow();
334                         }
335                 }
336
337
338         }
339         XFreeGC(disp,gc);
340         XDestroyWindow(disp, win);
341         XCloseDisplay(disp);
342
343         return (0);
344 }
345         
346
347
348
349
350
351 // Error reporting
352
353 void  PrintError( char* fmt, ...)
354 {
355         va_list ap;
356         char message[257];
357         
358         va_start(ap, fmt);
359         vsprintf(message, fmt, ap);
360         va_end(ap);
361         
362         printf("%s\n", message);
363 }
364
365 void**  mymalloc( long numBytes )                                       // Memory allocation, use Handles
366 {
367         char **mem;
368         
369         mem = (char**)malloc( sizeof(char*) );                  // Allocate memory for pointer
370         if(mem == NULL)
371                 return (void**)NULL;
372         else
373         {
374                 (*mem) = (char*) malloc( numBytes );            // Allocate numBytes
375                 if( *mem == NULL )
376                 {
377                         free( mem );
378                         return (void**)NULL;
379                 }
380                 else
381                         return (void**)mem;
382         }
383 }
384
385 void    myfree( void** Hdl )                                            // free Memory, use Handles
386 {
387         free( (char*) *Hdl );
388         free( (char**) Hdl );
389 }               
390
391
392 void SetImageDefaults(Image *im)
393 {
394         im->data                        = NULL;
395         im->bytesPerLine        = 0;
396         im->width                       = 0;
397         im->height                      = 0;
398         im->dataSize            = 0;
399         im->bitsPerPixel        = 0;
400         im->format                      = 0;
401         im->dataformat          = _RGB;
402         im->hfov                        = 0.0;
403         im->yaw                         = 0.0;
404         im->pitch                       = 0.0;
405         im->roll                        = 0.0;
406 //      SetCorrectDefaults( &(im->cP) );
407         *(im->name)                     = 0;
408 }
409
410 // expand image from 3 to 4 bits per pixel. No pad bytes allowed.
411 // Memory must be allocated
412 void ThreeToFourBPP( Image *im )
413 {
414         register int x,y,c1,c2;
415
416         if( im->bitsPerPixel == 32 || im->bitsPerPixel == 64) // Nothing to do
417                 return;
418         
419         
420         
421         if( im->bitsPerPixel == 24 )    // Convert to 4byte / pixel
422         {
423                 for( y = im->height-1; y>=0; y--)
424                 {
425                         for( x= im->width-1; x>=0; x--)
426                         {
427                                 c1 = (y * im->width + x) * 4;
428                                 c2 = y * im->bytesPerLine + x * 3;
429                                 (*(im->data))[c1++] = UCHAR_MAX;
430                                 (*(im->data))[c1++] = (*(im->data))[c2++];
431                                 (*(im->data))[c1++] = (*(im->data))[c2++];
432                                 (*(im->data))[c1++] = (*(im->data))[c2++];
433                         }
434                 }
435                 im->bitsPerPixel = 32;
436                 im->bytesPerLine = im->width * 4;
437         }
438         else if( im->bitsPerPixel == 48 ) // Convert to 8byte / pixel
439         {
440                 for( y = im->height-1; y>=0; y--)
441                 {
442                         for( x= im->width-1; x>=0; x--)
443                         {
444                                 c1 = (y * im->width + x) * 4;
445                                 c2 = y * im->bytesPerLine/2 + x * 3;
446                                 ((USHORT*)(*(im->data)))[c1++] = USHRT_MAX;
447                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
448                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
449                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
450                         }
451                 }
452                 im->bitsPerPixel = 64;
453                 im->bytesPerLine = im->width * 8;
454         }
455         im->dataSize = im->height * im->bytesPerLine;
456 }
457
458 static int copy_rgb_to_zpixmap (XImage *dest, const Image *src)
459 {
460         uint32_t *src_data;
461
462         uint32_t src_r;
463         uint32_t src_g;
464         uint32_t src_b;
465
466         uint32_t dest_r;
467         uint32_t dest_g;
468         uint32_t dest_b;
469         uint32_t dest_pixel_value;
470
471         uint32_t x;
472         uint32_t y;
473         uint32_t pixel;
474
475         uint32_t dest_r_maxval;
476         uint32_t dest_g_maxval;
477         uint32_t dest_b_maxval;
478
479         uint32_t dest_r_offset;
480         uint32_t dest_g_offset;
481         uint32_t dest_b_offset;
482
483         dest_r_offset = 0;
484         dest_g_offset = 0;
485         dest_b_offset = 0;
486         dest_r_maxval = dest->red_mask;
487         dest_g_maxval = dest->green_mask;
488         dest_b_maxval = dest->blue_mask;
489         for (x = 0; x < dest->depth; x++)
490         {
491                 if ((dest_r_maxval & 0x01) == 0)
492                 {
493                         dest_r_offset++;
494                         dest_r_maxval >>= 1;
495                 }
496                 if ((dest_g_maxval & 0x01) == 0)
497                 {
498                         dest_g_offset++;
499                         dest_g_maxval >>= 1;
500                 }
501                 if ((dest_b_maxval & 0x01) == 0)
502                 {
503                         dest_b_offset++;
504                         dest_b_maxval >>= 1;
505                 }
506         }
507
508         src_data = (uint32_t *) *src->data;
509
510         pixel = 0;
511         for (y = 0; y < dest->height; y++)
512         {
513                 for (x = 0; x < dest->width; x++, pixel++)
514                 {
515                         int32_t bytenum;
516
517                         src_r = (src_data[pixel] >>  8) & 0xFF;
518                         src_g = (src_data[pixel] >> 16) & 0xFF;
519                         src_b = (src_data[pixel] >> 24) & 0xFF;
520
521                         dest_r = dest_r_maxval * src_r / 0xFF;
522                         dest_g = dest_g_maxval * src_g / 0xFF;
523                         dest_b = dest_b_maxval * src_b / 0xFF;
524
525                         dest_pixel_value = 0
526                                 | ((dest_r << dest_r_offset) & dest->red_mask)
527                                 | ((dest_g << dest_g_offset) & dest->green_mask)
528                                 | ((dest_b << dest_b_offset) & dest->blue_mask);
529
530                         for (bytenum = 0; bytenum < (dest->depth / 8); bytenum++)
531                         {
532                                 dest->data[(pixel * dest->bits_per_pixel / 8) + bytenum] = 
533                                         (dest_pixel_value >> (dest->bits_per_pixel - (8 * (bytenum + 1)))) & 0xFF;
534                         }
535                 }
536         }
537
538         return (0);
539 } /* int copy_rgb_to_zpixmap */
540
541 void DrawWindow()
542 {
543         XWindowAttributes xa;
544
545         XGetWindowAttributes( disp, win, &xa );
546
547         if( xa.width != view.width || xa.height != view.height )
548         {
549                 myfree((void**)view.data);
550                 view.width                      = xa.width;
551                 view.height             = xa.height;
552                 view.bytesPerLine       = view.width * view.bitsPerPixel / 8;
553                 view.dataSize           = view.bytesPerLine * view.height;
554                 view.format             = 1;
555                 view.data               = (unsigned char**)mymalloc( view.dataSize );
556                 if(view.data == NULL)
557                 {
558                         PrintError("Not enough memory");
559                         exit(0);
560                 }
561
562                 ximage = XCreateImage(disp, visual, depth, ZPixmap, 0,
563                                 NULL, view.width, view.height,
564                                 8, 0);
565                 ximage->data = (char *)*(view.data);
566                 ximage->byte_order= MSBFirst;
567         }
568         if( view.format )
569         {
570                 if( panning || zooming_in || zooming_out)
571                         DrawView(_nn);
572                 else
573                         DrawView(_bilinear);
574         }
575
576         ximage->data = (char *) malloc (view.dataSize);
577         if (ximage->data == NULL)
578                 return;
579         memcpy (ximage->data, *(view.data), view.dataSize);
580
581         copy_rgb_to_zpixmap (ximage, &view);
582
583         XPutImage(disp, win, gc, ximage, 0, 0, 0, 0, view.width, view.height);
584
585         free (ximage->data);
586         ximage->data = NULL;
587 }
588
589 void DrawView(int InterPolator)
590 {
591         TrformStr                               Tr;
592                         
593         Tr.interpolator                 = InterPolator;
594
595         Tr.src                                  = &pano;
596         Tr.dest                                 = &view;        
597         
598         PV_ExtractStill( &Tr );
599         
600         view.format = 0;
601         return;
602         
603 }
604