viewer.c: Fix a segfault when resizing the window.
[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
30 #include "filter.h"
31 #include "panolib.h"
32 #include "utils_image.h"
33
34 #define DEF_VIEW_WIDTH  400
35 #define DEF_VIEW_HEIGHT 300
36
37 char text[] = "PTViewer";
38
39 Display         *disp_g;
40 Window          window_g;
41 int             screen_g;
42 GC              gc;
43 Visual          *visual_g;
44 unsigned int    depth_g;
45
46
47 static int copy_rgb_to_zpixmap (XImage *dest, const ui_image_t *src)
48 {
49         uint32_t *src_data;
50
51         uint32_t src_r;
52         uint32_t src_g;
53         uint32_t src_b;
54
55         uint32_t dest_r;
56         uint32_t dest_g;
57         uint32_t dest_b;
58         uint32_t dest_pixel_value;
59
60         uint32_t x;
61         uint32_t y;
62         uint32_t pixel;
63
64         uint32_t dest_r_maxval;
65         uint32_t dest_g_maxval;
66         uint32_t dest_b_maxval;
67
68         uint32_t dest_r_offset;
69         uint32_t dest_g_offset;
70         uint32_t dest_b_offset;
71
72         dest_r_offset = 0;
73         dest_g_offset = 0;
74         dest_b_offset = 0;
75         dest_r_maxval = dest->red_mask;
76         dest_g_maxval = dest->green_mask;
77         dest_b_maxval = dest->blue_mask;
78         for (x = 0; x < dest->depth; x++)
79         {
80                 if ((dest_r_maxval & 0x01) == 0)
81                 {
82                         dest_r_offset++;
83                         dest_r_maxval >>= 1;
84                 }
85                 if ((dest_g_maxval & 0x01) == 0)
86                 {
87                         dest_g_offset++;
88                         dest_g_maxval >>= 1;
89                 }
90                 if ((dest_b_maxval & 0x01) == 0)
91                 {
92                         dest_b_offset++;
93                         dest_b_maxval >>= 1;
94                 }
95         }
96
97         src_data = (uint32_t *) *src->data;
98
99         pixel = 0;
100         for (y = 0; y < dest->height; y++)
101         {
102                 for (x = 0; x < dest->width; x++, pixel++)
103                 {
104                         int32_t bytenum;
105
106                         src_r = src->data[0][pixel];
107                         src_g = src->data[1][pixel];
108                         src_b = src->data[2][pixel];
109
110                         dest_r = dest_r_maxval * src_r / 255;
111                         dest_g = dest_g_maxval * src_g / 255;
112                         dest_b = dest_b_maxval * src_b / 255;
113
114                         dest_pixel_value = 0
115                                 | ((dest_r << dest_r_offset) & dest->red_mask)
116                                 | ((dest_g << dest_g_offset) & dest->green_mask)
117                                 | ((dest_b << dest_b_offset) & dest->blue_mask);
118
119                         for (bytenum = 0; bytenum < (dest->depth / 8); bytenum++)
120                         {
121                                 dest->data[(pixel * dest->bits_per_pixel / 8) + bytenum] = 
122                                         (dest_pixel_value >> (dest->bits_per_pixel - (8 * (bytenum + 1)))) & 0xFF;
123                         }
124                 }
125         }
126
127         return (0);
128 } /* int copy_rgb_to_zpixmap */
129
130 static int draw_window (const ui_image_t *img)
131 {
132   static XImage *ximage = NULL;
133
134   if ((ximage != NULL) && ((img->width != ximage->width) || (img->height != ximage->height)))
135   {
136     free (ximage->data);
137     ximage->data = NULL;
138     XDestroyImage (ximage);
139     ximage = NULL;
140   }
141
142   if (ximage == NULL)
143   {
144     ximage = XCreateImage(disp_g, visual_g, depth_g, ZPixmap, 0,
145         NULL, img->width, img->height,
146         8, 0);
147     if (ximage == NULL)
148       return (-1);
149
150     ximage->byte_order= MSBFirst;
151
152     ximage->data = (char *) malloc (img->width * img->height * depth_g / 8);
153     if (ximage->data == NULL)
154     {
155       XDestroyImage (ximage);
156       ximage = NULL;
157       return (-1);
158     }
159   }
160
161   copy_rgb_to_zpixmap (ximage, img);
162   XPutImage(disp_g, window_g, gc, ximage, 0, 0,
163       0, 0, ximage->width, ximage->height);
164
165   return (0);
166 } /* int draw_window */
167
168 #define ZOOM_SPEED 1.03
169
170 static void zoom_in (double *fov)
171 {
172   *fov = *fov / ZOOM_SPEED;
173   if (*fov < 10.5)
174     *fov = 10.5;
175 } /* void zoom_in */
176
177 static void zoom_out (double *fov)
178 {
179   *fov = *fov * ZOOM_SPEED;
180   if (*fov > 165.0)
181     *fov = 165.0;
182 } /* void zoom_out */
183
184 int main( int argc, char** argv )
185 {
186   unsigned long color_fg, color_bg;
187   int done;
188   static Atom                   proto_atom= None, delete_atom= None;
189
190   long event_mask;
191
192   double fov = 70.0;
193   double yaw = 0.0;
194   double pitch = 0.0;
195
196   /* Detect mouse movement direction */
197   unsigned int btn_current_pressed = 0;
198   int btn_reference_pos_x = 0;
199   int btn_reference_pos_y = 0;
200   int btn_current_pos_x = 0;
201   int btn_current_pos_y = 0;
202
203   ui_image_t *pano;
204   ui_image_t *view;
205
206
207   // Set up display
208   disp_g   = XOpenDisplay("");
209   screen_g = DefaultScreen(disp_g);
210   depth_g  = DefaultDepth(disp_g, screen_g);
211
212   color_bg = WhitePixel(disp_g,screen_g);
213   color_fg = BlackPixel(disp_g,screen_g);
214
215   // Load panoramic image       
216   if( argc < 2 )
217   {
218     PrintError("No image file supplied");
219     exit(0);
220   }
221
222   if( strcmp( argv[1], "-h" ) == 0 ) /* FIXME: Use getopt */
223   {
224     PrintError( "%s\n %s\nUsage: PTViewer Imagefile", "PTViewer", LONGVERSION );
225     exit(0);
226   }
227
228   pano = ui_create_file (argv[1]);
229   if (pano == NULL)
230   {
231     PrintError ("Unable to read pano file.");
232     exit (0);
233   }
234
235   printf ("Panorama `%s', %ux%u pixels, loaded.\n",
236       argv[1], pano->width, pano->height);
237
238   view = ui_create (DEF_VIEW_WIDTH, DEF_VIEW_HEIGHT); /* FIXME: Possibly chose another size */
239   if (view == NULL)
240   {
241     PrintError ("Unable to create view.");
242     exit (0);
243   }
244
245   window_g = XCreateSimpleWindow(disp_g,
246       DefaultRootWindow(disp_g),
247       0, 0, /* Position */
248       view->width, view->height,
249       5,
250       color_fg, color_bg);
251
252   {
253     XSizeHints hint;
254
255     memset (&hint, '\0', sizeof (hint));
256     hint.width  = view->width;
257     hint.height = view->height;
258     hint.flags = PSize;
259
260     XSetStandardProperties(disp_g,
261         window_g,
262         text,
263         text,
264         None,
265         argv,
266         argc,
267         &hint);
268   }
269
270   gc = XCreateGC(disp_g,window_g,0,0);
271   XSetBackground(disp_g,gc,color_bg);
272   XSetForeground(disp_g,gc,color_fg);
273
274   event_mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
275     | KeyPressMask | KeyReleaseMask | StructureNotifyMask
276     | EnterWindowMask | LeaveWindowMask| ExposureMask;
277
278   XSelectInput(disp_g, window_g, event_mask);
279   XMapRaised(disp_g,window_g);
280
281   proto_atom    = XInternAtom(disp_g, "WM_PROTOCOLS", False);
282   delete_atom = XInternAtom(disp_g, "WM_DELETE_WINDOW", False);
283   if ((proto_atom != None) && (delete_atom != None))
284     XChangeProperty(disp_g, window_g, proto_atom, XA_ATOM, 32,
285         PropModeReplace, (unsigned char *)&delete_atom,
286         1);
287   visual_g  = DefaultVisual(disp_g, screen_g);
288
289   pl_extract_view (view, pano, pitch, yaw, fov);
290   draw_window (view);
291
292   done = 0;
293   while (done == 0)
294   {
295     int isChanged = 0;
296     XEvent event;
297
298     memset (&event, '\0', sizeof (event));
299
300     if (btn_current_pressed == 0)
301     {
302       /* No button is pressed -> block */
303       XMaskEvent (disp_g, event_mask, &event);
304     }
305     else
306     {
307       /* A button is pressed -> do not block */
308       XCheckMaskEvent (disp_g, event_mask, &event);
309     }
310
311     switch (event.type)
312     {
313       case 0: /* XCheckMaskEvent didn't return an event */
314         break;
315
316       case Expose:
317         {
318           XExposeEvent *eev = (XExposeEvent *) &event;
319
320           while (eev->count != 0)
321           {
322             if (!XCheckTypedEvent (disp_g, Expose, &event))
323             {
324               eev = NULL;
325               break;
326             }
327             /* do nothing */
328           }
329
330           if (eev != NULL)
331             isChanged = 1;
332         }
333         break;
334
335       case EnterNotify:
336         break;
337
338       case LeaveNotify:
339         break;
340
341       case ConfigureNotify:
342         {
343           XConfigureEvent *cev = (XConfigureEvent *) &event;
344           printf ("XConfigureEvent received: width = %i; height = %i;\n",
345               cev->width, cev->height);
346
347           ui_destroy (view);
348           view = ui_create (cev->width, cev->height);
349           isChanged = 1;
350         }
351         break;
352
353       case KeyPress:
354         {
355           XKeyEvent *kev = (XKeyEvent *) &event;
356           char buffer[64];
357           int buffer_len = sizeof (buffer);
358           KeySym ks;
359
360           buffer_len = XLookupString(kev, buffer, sizeof (buffer), &ks, NULL);
361
362           if (buffer_len >= sizeof (buffer))
363             buffer_len = sizeof (buffer) - 1;
364           buffer[buffer_len] = '\0';
365
366           switch (ks)
367           {
368             case XK_Shift_L:
369             case XK_Shift_R:
370               zoom_in (&fov);
371               isChanged = 1;
372               break;
373
374             case XK_Control_L:
375             case XK_Control_R:
376               zoom_out (&fov);
377               isChanged = 1;
378               break;
379
380             case XK_Up:
381               pitch += 5.0;
382               if (pitch > 90.0)
383                 pitch = 90.0;
384               isChanged = 1;
385               break;
386
387             case XK_Down:
388               pitch -= 5.0;
389               if (pitch < -90.0)
390                 pitch = -90.0;
391               isChanged = 1;
392               break;
393
394             case XK_Right:
395               yaw += 5.0;
396               if (yaw > 180.0)
397                 yaw -= 360.0;
398               isChanged = 1;
399               break;
400
401             case XK_Left:
402               yaw -= 5.0;
403               if (yaw <= -180.0)
404                 yaw += 360.0;
405               isChanged = 1;
406               break;
407
408             case XK_q:
409               done++;
410               break;
411
412             default:
413               printf ("Unhandeled KeySym 0x%02llx, buffer = %s\n", (uint64_t) ks, buffer);
414           }
415         }
416         break;
417
418 #if 0
419       case KeyRelease:
420         printf ("Ignoring KeyRelease event.\n");
421         break;
422 #endif
423
424       case ButtonPress:
425       /* case ButtonRelease: */
426         {
427           XButtonEvent *bev = (XButtonEvent *) &event;
428
429           btn_current_pressed = bev->button;
430           btn_current_pos_x = btn_reference_pos_x = bev->x;
431           btn_current_pos_y = btn_reference_pos_y = bev->y;
432         }
433         break;
434
435       case ButtonRelease:
436         btn_current_pressed = 0;
437         break;
438
439       case MotionNotify:
440         {
441           XMotionEvent *mev = (XMotionEvent *) &event;
442
443           do
444           {
445             btn_current_pos_x = mev->x;
446             btn_current_pos_y = mev->y;
447           }
448           while (XCheckTypedEvent (disp_g, MotionNotify, &event));
449         }
450         break;
451
452       default:
453         printf ("Unhandled event type: 0x%02x\n",
454             event.type);
455     } /* switch (event.type) */
456
457     /*
458      * Check for pressed buttons and change view accordingly
459      */
460     if (btn_current_pressed == Button1)
461     {
462       double yaw_diff = (btn_current_pos_x - btn_reference_pos_x) / 24.0;
463       if (yaw_diff > 90.0)
464         yaw_diff = 90.0;
465       else if (yaw_diff < -90.0)
466         yaw_diff = -90.0;
467
468       yaw += yaw_diff;
469       if (yaw > 180.0)
470         yaw -= 360.0;
471       else if (yaw <= -180.0)
472         yaw += 360.0;
473
474       pitch -= (btn_current_pos_y - btn_reference_pos_y) / 24.0;
475       if (pitch > 90.0)
476         pitch = 90.0;
477       else if (pitch < -90.0)
478         pitch = -90.0;
479
480       isChanged = 1;
481     }
482     else if (btn_current_pressed == Button3)
483     {
484       double zoom_diff = (btn_current_pos_y - btn_reference_pos_y) / 64.0;
485       double zoom_ratio = pow (ZOOM_SPEED, zoom_diff);
486
487       fov *= zoom_ratio;
488       if (fov < 10.5)
489         fov = 10.5;
490       else if (fov > 165.0)
491         fov = 165.0;
492
493       isChanged = 1;
494     }
495
496     if (isChanged != 0)
497     {
498       pl_extract_view (view, pano, pitch, yaw, fov);
499       draw_window (view);
500     }
501   } /* while (done == 0) */
502
503   XFreeGC(disp_g,gc);
504   XDestroyWindow(disp_g, window_g);
505   XCloseDisplay(disp_g);
506
507   return (0);
508 } /* int main */
509         
510
511
512
513
514
515 // Error reporting
516
517 void  PrintError( char* fmt, ...)
518 {
519         va_list ap;
520         char message[257];
521         
522         va_start(ap, fmt);
523         vsprintf(message, fmt, ap);
524         va_end(ap);
525         
526         printf("%s\n", message);
527 }
528
529 void**  mymalloc( long numBytes )                                       // Memory allocation, use Handles
530 {
531         char **mem;
532         
533         mem = (char**)malloc( sizeof(char*) );                  // Allocate memory for pointer
534         if(mem == NULL)
535                 return (void**)NULL;
536         else
537         {
538                 (*mem) = (char*) malloc( numBytes );            // Allocate numBytes
539                 if( *mem == NULL )
540                 {
541                         free( mem );
542                         return (void**)NULL;
543                 }
544                 else
545                         return (void**)mem;
546         }
547 }
548
549 void    myfree( void** Hdl )                                            // free Memory, use Handles
550 {
551         free( (char*) *Hdl );
552         free( (char**) Hdl );
553 }               
554
555
556 void SetImageDefaults(Image *im)
557 {
558         im->data                        = NULL;
559         im->bytesPerLine        = 0;
560         im->width                       = 0;
561         im->height                      = 0;
562         im->dataSize            = 0;
563         im->bitsPerPixel        = 0;
564         im->format                      = 0;
565         im->dataformat          = _RGB;
566         im->hfov                        = 0.0;
567         im->yaw                         = 0.0;
568         im->pitch                       = 0.0;
569         im->roll                        = 0.0;
570 //      SetCorrectDefaults( &(im->cP) );
571         *(im->name)                     = 0;
572 }
573
574 // expand image from 3 to 4 bits per pixel. No pad bytes allowed.
575 // Memory must be allocated
576 void ThreeToFourBPP( Image *im )
577 {
578         register int x,y,c1,c2;
579
580         if( im->bitsPerPixel == 32 || im->bitsPerPixel == 64) // Nothing to do
581                 return;
582         
583         
584         
585         if( im->bitsPerPixel == 24 )    // Convert to 4byte / pixel
586         {
587                 for( y = im->height-1; y>=0; y--)
588                 {
589                         for( x= im->width-1; x>=0; x--)
590                         {
591                                 c1 = (y * im->width + x) * 4;
592                                 c2 = y * im->bytesPerLine + x * 3;
593                                 (*(im->data))[c1++] = UCHAR_MAX;
594                                 (*(im->data))[c1++] = (*(im->data))[c2++];
595                                 (*(im->data))[c1++] = (*(im->data))[c2++];
596                                 (*(im->data))[c1++] = (*(im->data))[c2++];
597                         }
598                 }
599                 im->bitsPerPixel = 32;
600                 im->bytesPerLine = im->width * 4;
601         }
602         else if( im->bitsPerPixel == 48 ) // Convert to 8byte / pixel
603         {
604                 for( y = im->height-1; y>=0; y--)
605                 {
606                         for( x= im->width-1; x>=0; x--)
607                         {
608                                 c1 = (y * im->width + x) * 4;
609                                 c2 = y * im->bytesPerLine/2 + x * 3;
610                                 ((USHORT*)(*(im->data)))[c1++] = USHRT_MAX;
611                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
612                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
613                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
614                         }
615                 }
616                 im->bitsPerPixel = 64;
617                 im->bytesPerLine = im->width * 8;
618         }
619         im->dataSize = im->height * im->bytesPerLine;
620 }
621
622 /*
623  * vim: set shiftwidth=2 tabstop=8 softtabstop=2 :
624  */