Implemented keyboard controls and basic mouse handling.
[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     XDestroyImage (ximage);
138     ximage = NULL;
139   }
140
141   if (ximage == NULL)
142   {
143     char *data;
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     data = (char *) malloc (img->width * img->height * depth_g / 8);
153     ximage->data = data;
154     if (ximage->data == NULL)
155     {
156       XDestroyImage (ximage);
157       ximage = NULL;
158       return (-1);
159     }
160   }
161
162   copy_rgb_to_zpixmap (ximage, img);
163   XPutImage(disp_g, window_g, gc, ximage, 0, 0,
164       0, 0, ximage->width, ximage->height);
165
166   return (0);
167 } /* int draw_window */
168
169 #define ZOOM_SPEED 1.03
170
171 static void zoom_in (double *fov)
172 {
173   *fov = *fov / ZOOM_SPEED;
174   if (*fov < 10.5)
175     *fov = 10.5;
176 } /* void zoom_in */
177
178 static void zoom_out (double *fov)
179 {
180   *fov = *fov * ZOOM_SPEED;
181   if (*fov > 165.0)
182     *fov = 165.0;
183 } /* void zoom_out */
184
185 int main( int argc, char** argv )
186 {
187   XSizeHints hint;
188   unsigned long color_fg, color_bg;
189   int done;
190   static Atom                   proto_atom= None, delete_atom= None;
191
192   long event_mask;
193
194   double fov = 70.0;
195   double yaw = 0.0;
196   double pitch = 0.0;
197
198   /* Detect mouse movement direction */
199   int pos_x_new = 0;
200   int pos_y_new = 0;
201   int pos_x_old;
202   int pos_y_old;
203
204   ui_image_t *pano;
205   ui_image_t *view;
206
207
208   // Set up display
209   disp_g   = XOpenDisplay("");
210   screen_g = DefaultScreen(disp_g);
211   depth_g  = DefaultDepth(disp_g, screen_g);
212
213   color_bg = WhitePixel(disp_g,screen_g);
214   color_fg = BlackPixel(disp_g,screen_g);
215
216   // Load panoramic image       
217   if( argc < 2 )
218   {
219     PrintError("No image file supplied");
220     exit(0);
221   }
222
223   if( strcmp( argv[1], "-h" ) == 0 ) /* FIXME: Use getopt */
224   {
225     PrintError( "%s\n %s\nUsage: PTViewer Imagefile", "PTViewer", LONGVERSION );
226     exit(0);
227   }
228
229   pano = ui_create_file (argv[1]);
230   if (pano == NULL)
231   {
232     PrintError ("Unable to read pano file.");
233     exit (0);
234   }
235
236   printf ("Panorama `%s', %ux%u pixels, loaded.\n",
237       argv[1], pano->width, pano->height);
238
239   view = ui_create (DEF_VIEW_WIDTH, DEF_VIEW_HEIGHT); /* FIXME: Possibly chose another size */
240   if (view == NULL)
241   {
242     PrintError ("Unable to create view.");
243     exit (0);
244   }
245
246   hint.width  = view->width;
247   hint.height = view->height;
248   hint.flags = PSize;
249
250   window_g = XCreateSimpleWindow(disp_g,
251       DefaultRootWindow(disp_g),
252       0, 0, /* Position */
253       view->width, view->height,
254       5,
255       color_fg, color_bg);
256
257   XSetStandardProperties(disp_g,
258       window_g,
259       text,
260       text,
261       None,
262       argv,
263       argc,
264       &hint);
265   gc = XCreateGC(disp_g,window_g,0,0);
266   XSetBackground(disp_g,gc,color_bg);
267   XSetForeground(disp_g,gc,color_fg);
268
269   event_mask = ButtonPressMask | ButtonReleaseMask | Button1MotionMask
270     | KeyPressMask | KeyReleaseMask | StructureNotifyMask
271     | EnterWindowMask | LeaveWindowMask| ExposureMask;
272
273   XSelectInput(disp_g, window_g, event_mask);
274   XMapRaised(disp_g,window_g);
275
276   proto_atom    = XInternAtom(disp_g, "WM_PROTOCOLS", False);
277   delete_atom = XInternAtom(disp_g, "WM_DELETE_WINDOW", False);
278   if ((proto_atom != None) && (delete_atom != None))
279     XChangeProperty(disp_g, window_g, proto_atom, XA_ATOM, 32,
280         PropModeReplace, (unsigned char *)&delete_atom,
281         1);
282   visual_g  = DefaultVisual(disp_g, screen_g);
283
284   pl_extract_view (view, pano, pitch, yaw, fov);
285   draw_window (view);
286
287   done = 0;
288   while (done == 0)
289   {
290     int isChanged = 0;
291     XEvent event;
292
293     XMaskEvent (disp_g, event_mask, &event);
294
295     switch (event.type)
296     {
297       case Expose:
298         draw_window (view);
299         break;
300
301       case ConfigureNotify:
302         {
303           XConfigureEvent *cev = (XConfigureEvent *) &event;
304           printf ("XConfigureEvent received: width = %i; height = %i;\n",
305               cev->width, cev->height);
306
307           ui_destroy (view);
308           view = ui_create (cev->width, cev->height);
309           isChanged = 1;
310         }
311         break;
312
313       case KeyPress:
314         {
315           XKeyEvent *kev = (XKeyEvent *) &event;
316           KeySym ks = XLookupKeysym (kev, 0);
317
318           switch (ks)
319           {
320             case XK_Shift_L:
321             case XK_Shift_R:
322               zoom_in (&fov);
323               isChanged = 1;
324               break;
325
326             case XK_Control_L:
327             case XK_Control_R:
328               zoom_out (&fov);
329               isChanged = 1;
330               break;
331
332             case XK_Up:
333               pitch += 5.0;
334               if (pitch > 90.0)
335                 pitch = 90.0;
336               isChanged = 1;
337               break;
338
339             case XK_Down:
340               pitch -= 5.0;
341               if (pitch < -90.0)
342                 pitch = -90.0;
343               isChanged = 1;
344               break;
345
346             case XK_Right:
347               yaw += 5.0;
348               if (yaw > 180.0)
349                 yaw -= 360.0;
350               isChanged = 1;
351               break;
352
353             case XK_Left:
354               yaw -= 5.0;
355               if (yaw <= -180.0)
356                 yaw += 360.0;
357               isChanged = 1;
358               break;
359           }
360         }
361         break;
362
363       case KeyRelease:
364         printf ("Ignoring KeyRelease event.\n");
365         break;
366
367       case ButtonPress:
368       case ButtonRelease:
369         {
370           XButtonEvent *bev = (XButtonEvent *) &event;
371           pos_x_old = pos_x_new = bev->x;
372           pos_y_old = pos_y_new = bev->y;
373         }
374         break;
375
376       case MotionNotify:
377         {
378           XMotionEvent *mev = (XMotionEvent *) &event;
379
380           pos_x_old = pos_x_new;
381           pos_y_old = pos_y_new;
382
383           do
384           {
385             pos_x_new = mev->x;
386             pos_y_new = mev->y;
387           }
388           while (XCheckTypedEvent (disp_g, MotionNotify, &event));
389
390           printf ("XMotionEvent received: [%u,%u] -> [%u,%u]\n",
391               pos_x_old, pos_y_old, pos_x_new, pos_y_new);
392
393           if (mev->state & Button1Mask)
394           {
395             double yaw_diff = (pos_x_new - pos_x_old) / 24.0;
396             if (yaw_diff > 90.0)
397               yaw_diff = 90.0;
398             else if (yaw_diff < -90.0)
399               yaw_diff = -90.0;
400
401             yaw += yaw_diff;
402             if (yaw > 180.0)
403               yaw -= 360.0;
404             else if (yaw <= -180.0)
405               yaw += 360.0;
406
407             pitch -= (pos_y_new - pos_y_old) / 24.0;
408             if (pitch > 90.0)
409               pitch = 90.0;
410             else if (pitch < -90.0)
411               pitch = -90.0;
412
413             isChanged = 1;
414           }
415         }
416         break;
417
418       default:
419         printf ("Unhandled event type: 0x%02x\n",
420             event.type);
421     } /* switch (event.type) */
422
423     if (isChanged != 0)
424     {
425       pl_extract_view (view, pano, pitch, yaw, fov);
426       draw_window (view);
427     }
428   } /* while (done == 0) */
429
430   XFreeGC(disp_g,gc);
431   XDestroyWindow(disp_g, window_g);
432   XCloseDisplay(disp_g);
433
434   return (0);
435 } /* int main */
436         
437
438
439
440
441
442 // Error reporting
443
444 void  PrintError( char* fmt, ...)
445 {
446         va_list ap;
447         char message[257];
448         
449         va_start(ap, fmt);
450         vsprintf(message, fmt, ap);
451         va_end(ap);
452         
453         printf("%s\n", message);
454 }
455
456 void**  mymalloc( long numBytes )                                       // Memory allocation, use Handles
457 {
458         char **mem;
459         
460         mem = (char**)malloc( sizeof(char*) );                  // Allocate memory for pointer
461         if(mem == NULL)
462                 return (void**)NULL;
463         else
464         {
465                 (*mem) = (char*) malloc( numBytes );            // Allocate numBytes
466                 if( *mem == NULL )
467                 {
468                         free( mem );
469                         return (void**)NULL;
470                 }
471                 else
472                         return (void**)mem;
473         }
474 }
475
476 void    myfree( void** Hdl )                                            // free Memory, use Handles
477 {
478         free( (char*) *Hdl );
479         free( (char**) Hdl );
480 }               
481
482
483 void SetImageDefaults(Image *im)
484 {
485         im->data                        = NULL;
486         im->bytesPerLine        = 0;
487         im->width                       = 0;
488         im->height                      = 0;
489         im->dataSize            = 0;
490         im->bitsPerPixel        = 0;
491         im->format                      = 0;
492         im->dataformat          = _RGB;
493         im->hfov                        = 0.0;
494         im->yaw                         = 0.0;
495         im->pitch                       = 0.0;
496         im->roll                        = 0.0;
497 //      SetCorrectDefaults( &(im->cP) );
498         *(im->name)                     = 0;
499 }
500
501 // expand image from 3 to 4 bits per pixel. No pad bytes allowed.
502 // Memory must be allocated
503 void ThreeToFourBPP( Image *im )
504 {
505         register int x,y,c1,c2;
506
507         if( im->bitsPerPixel == 32 || im->bitsPerPixel == 64) // Nothing to do
508                 return;
509         
510         
511         
512         if( im->bitsPerPixel == 24 )    // Convert to 4byte / pixel
513         {
514                 for( y = im->height-1; y>=0; y--)
515                 {
516                         for( x= im->width-1; x>=0; x--)
517                         {
518                                 c1 = (y * im->width + x) * 4;
519                                 c2 = y * im->bytesPerLine + x * 3;
520                                 (*(im->data))[c1++] = UCHAR_MAX;
521                                 (*(im->data))[c1++] = (*(im->data))[c2++];
522                                 (*(im->data))[c1++] = (*(im->data))[c2++];
523                                 (*(im->data))[c1++] = (*(im->data))[c2++];
524                         }
525                 }
526                 im->bitsPerPixel = 32;
527                 im->bytesPerLine = im->width * 4;
528         }
529         else if( im->bitsPerPixel == 48 ) // Convert to 8byte / pixel
530         {
531                 for( y = im->height-1; y>=0; y--)
532                 {
533                         for( x= im->width-1; x>=0; x--)
534                         {
535                                 c1 = (y * im->width + x) * 4;
536                                 c2 = y * im->bytesPerLine/2 + x * 3;
537                                 ((USHORT*)(*(im->data)))[c1++] = USHRT_MAX;
538                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
539                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
540                                 ((USHORT*)(*(im->data)))[c1++] = ((USHORT*)(*(im->data)))[c2++];
541                         }
542                 }
543                 im->bitsPerPixel = 64;
544                 im->bytesPerLine = im->width * 8;
545         }
546         im->dataSize = im->height * im->bytesPerLine;
547 }
548
549 /*
550  * vim: set shiftwidth=2 tabstop=8 softtabstop=2 :
551  */