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