9d8ca2e8a52062447c1fb688fe79844efde52683
[xscreensaver] / hacks / glx / glslideshow.c
1 /*
2  * glslideshow - takes a snapshot of the screen and smoothly scans around
3  *               in it
4  *
5  * Copyright (c) 2002, 2003 Mike Oliphant (oliphant@gtk.org)
6  *
7  * Framework based on flipscreen3d
8  *   Copyright (C) 2001 Ben Buxton (bb@cactii.net)
9  *
10  * Smooth transitions between multiple files added by
11  * Jamie Zawinski <jwz@jwz.org>
12  *
13  * Permission to use, copy, modify, distribute, and sell this software and its
14  * documentation for any purpose is hereby granted without fee, provided that
15  * the above copyright notice appear in all copies and that both that
16  * copyright notice and this permission notice appear in supporting
17  * documentation.  No representations are made about the suitability of this
18  * software for any purpose.  It is provided "as is" without express or
19  * implied warranty.
20  *
21  */
22
23 #include <X11/Intrinsic.h>
24
25
26 # define PROGCLASS "GLSlideshow"
27 # define HACK_INIT init_slideshow
28 # define HACK_DRAW draw_slideshow
29 # define HACK_RESHAPE reshape_slideshow
30 # define slideshow_opts xlockmore_opts
31
32 # define DEF_FADE     "True"
33 # define DEF_DURATION "30"
34 # define DEF_ZOOM     "75"
35
36 #define DEFAULTS  "*delay:       20000          \n" \
37                   "*fade:       " DEF_FADE     "\n" \
38                   "*duration:   " DEF_DURATION "\n" \
39                   "*zoom:       " DEF_ZOOM     "\n" \
40                   "*wireframe:   False          \n" \
41                   "*showFPS:     False          \n" \
42                   "*fpsSolid:    True           \n" \
43                   "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n"
44
45 # include "xlockmore.h"
46
47 #define RRAND(range) (random()%(range))
48 #undef countof
49 #define countof(x) (sizeof((x))/sizeof((*x)))
50
51 #ifdef USE_GL
52
53 #include <GL/glu.h>
54 #include <math.h>
55 #include <sys/time.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include "grab-ximage.h"
59
60
61 #define QW 12.4   /* arbitrary size of the textured quads we render */
62 #define QH 12.4
63 #define QX -6.2
64 #define QY  6.2
65
66 #define NQUADS 2  /* sometimes we draw 2 at once */
67
68 typedef struct {
69   GLXContext *glx_context;
70   Window window;
71
72   int tw, th;                   /* texture width, height */
73   GLfloat max_tx, max_ty;
74
75   GLfloat qw, qh;               /* q? are for the quad we'll draw */
76   GLfloat qx[NQUADS], qy[NQUADS], qz[NQUADS];
77   GLfloat dx[NQUADS], dy[NQUADS], dz[NQUADS];
78
79   GLuint texids[NQUADS];        /* two textures: current img, incoming img */
80
81   time_t start_time;            /* when we started displaying this image */
82
83   int curr_screen;
84   int curr_texid_index;
85   int in_transition;            /* true while we're drawing overlapping imgs */
86   int in_file_transition;       /* ...plus loading a new image */
87   int frames;                   /* how many frames we've drawn in this pan */
88
89 } slideshow_state;
90
91 static slideshow_state *sss = NULL;
92
93
94 /* Command-line arguments
95  */
96 int fade;     /* If true, transitions between pans (and between images) will
97                  be translucent; otherwise, they will be jump-cuts. */
98 int duration; /* how many seconds until loading a new image */
99 int zoom;     /* how far in to zoom when panning, in percent of image size:
100                  that is, 75 means "when zoomed all the way in, 75% of the
101                  image will be on screen."  */
102
103
104 /* blah, apparently other magic numbers elsewhere in the file also
105    affect this...   can't just change these to speed up / slow down...
106  */
107 static int frames_per_pan  = 300;
108 static int frames_per_fade = 100;
109
110
111 static XrmOptionDescRec opts[] = {
112   {"+fade",     ".slideshow.fade",     XrmoptionNoArg, (caddr_t) "False" },
113   {"-fade",     ".slideshow.fade",     XrmoptionNoArg, (caddr_t) "True" },
114   {"-duration", ".slideshow.duration", XrmoptionSepArg, 0},
115   {"-zoom",     ".slideshow.zoom",     XrmoptionSepArg, 0}
116 };
117
118 static argtype vars[] = {
119   {(caddr_t *) &fade,     "fade",     "Fade",     DEF_FADE,     t_Bool},
120   {(caddr_t *) &duration, "duration", "Duration", DEF_DURATION, t_Int},
121   {(caddr_t *) &zoom,     "zoom",     "Zoom",     DEF_ZOOM,     t_Int}
122 };
123
124 ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
125
126
127 /* draw the texture mapped quad.
128    `screen' specifies which of the independently-moving images to draw.
129  */
130 static void
131 showscreen (ModeInfo *mi, int wire, int screen, int texid_index)
132 {
133   slideshow_state *ss = &sss[MI_SCREEN(mi)];
134   static GLfloat r = 1, g = 1, b = 1, a = 1;
135   GLfloat qxw, qyh;
136   GLfloat x, y, w, h;
137
138   if (screen >= NQUADS) abort();
139   qxw = ss->qx[screen] + ss->qw;
140   qyh = ss->qy[screen] - ss->qh;
141   x = ss->qx[screen];
142   y = ss->qy[screen];
143   w = qxw;
144   h = qyh;
145
146   ss->qx[screen] += ss->dx[screen];
147   ss->qy[screen] -= ss->dy[screen];
148   ss->qz[screen] += ss->dz[screen];
149
150   glTranslatef(0, 0, ss->qz[screen]);
151
152   if (ss->in_transition) {
153     a = 1 - (ss->frames/100.0);
154
155     if (screen != ss->curr_screen) {
156       a = 1-a;
157     }
158   }
159   else {
160     a = 1;
161   }
162
163   glColor4f(r, g, b, a);
164
165   if(!wire) {
166     glEnable(GL_TEXTURE_2D);
167     glEnable(GL_BLEND);
168     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
169     glDepthMask(GL_FALSE);
170     glBindTexture (GL_TEXTURE_2D, ss->texids[texid_index]);
171   }
172
173   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
174
175   glNormal3f(0, 0, 1);
176
177   glTexCoord2f(0, ss->max_ty);
178   glVertex3f(x, y, 0);
179
180   glTexCoord2f(ss->max_tx, ss->max_ty);
181   glVertex3f(w, y, 0);
182
183   glTexCoord2f(ss->max_tx, 0);
184   glVertex3f(w, h, 0);
185
186   glTexCoord2f(0, 0);
187   glVertex3f(x, h, 0);
188
189   glEnd();
190
191   if (wire) {
192     GLfloat i;
193     int k = 10;
194     glBegin(GL_LINES);
195     for (i = x; i < w; i += ((w-x)/k))
196       {
197         glVertex3f(i, y, 0);
198         glVertex3f(i, h, 0);
199       }
200     for (i = y; i >= h; i -= ((y-h)/k))
201       {
202         glVertex3f(x, i, 0);
203         glVertex3f(w, i, 0);
204       }
205     glVertex3f(x, y, 0);
206     glVertex3f(w, h, 0);
207     glVertex3f(x, h, 0);
208     glVertex3f(w, y, 0);
209     glEnd();
210   }
211
212   glDisable(GL_TEXTURE_2D);
213   glDepthMask(GL_TRUE);
214   
215   glBegin(GL_LINE_LOOP);
216   glVertex3f(x, y, 0);
217   glVertex3f(x, h, 0);
218   glVertex3f(w, h, 0);
219   glVertex3f(w, y, 0);
220   glEnd();
221   glDisable(GL_BLEND);
222
223   glTranslatef(0, 0, -ss->qz[screen]);
224 }
225
226
227 static void
228 display (ModeInfo *mi)
229 {
230   slideshow_state *ss = &sss[MI_SCREEN(mi)];
231   int wire = MI_IS_WIREFRAME(mi);
232   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
233   glLoadIdentity();
234   gluLookAt(0, 0, 15,
235             0, 0, 0,
236             0, 1, 0);
237   glPushMatrix();
238
239   showscreen (mi, wire, ss->curr_screen, ss->curr_texid_index);
240
241   if (ss->in_transition)
242     showscreen (mi, wire, 1-ss->curr_screen,
243                 (ss->in_file_transition
244                  ? 1 - ss->curr_texid_index
245                  : ss->curr_texid_index));
246
247   glPopMatrix();
248   glFlush();
249 }
250
251 void
252 reshape_slideshow (ModeInfo *mi, int width, int height)
253 {
254   glViewport(0,0,(GLint)width, (GLint) height);
255   glMatrixMode(GL_PROJECTION);
256   glLoadIdentity();
257   gluPerspective(45, 1, 2.0, 85);
258   glMatrixMode(GL_MODELVIEW);
259 }
260
261 static void
262 reset (ModeInfo *mi, int screen)
263 {
264   slideshow_state *ss = &sss[MI_SCREEN(mi)];
265   ss->frames = 0;
266
267   if (screen >= NQUADS) abort();
268   ss->dz[screen] = (-.02+(RRAND(400)/10000.0)) * (GLfloat) zoom/100.0;
269
270   if (ss->dz[screen] < 0.0) {
271     ss->qz[screen] = 6.0 + RRAND(300)/100.0;
272   }
273   else {
274     ss->qz[screen] = 1.0 + RRAND(300)/100.0;
275   }
276
277   ss->qz[screen] *= (GLfloat) zoom/100.0;
278
279   ss->dx[screen] = -.02 + RRAND(400)/10000.0;
280   ss->dy[screen] =- .01 + RRAND(200)/10000.0;
281
282   ss->dx[screen] *= ss->qz[screen]/12.0;
283   ss->dy[screen] *= ss->qz[screen]/12.0;
284
285   ss->qx[screen] = QX - ss->dx[screen] * 40.0 * ss->qz[screen];
286   ss->qy[screen] = QY + ss->dy[screen] * 40.0 * ss->qz[screen];  
287 }
288
289
290 static void
291 getSnapshot (ModeInfo *mi, int into_texid)
292 {
293   slideshow_state *ss = &sss[MI_SCREEN(mi)];
294   XImage *ximage;
295   int status;
296   
297   if(MI_IS_WIREFRAME(mi)) return;
298
299   ss->qw = QW;
300   ss->qh = QH;
301
302   ximage = screen_to_ximage (mi->xgwa.screen, mi->window);
303
304   ss->tw = mi->xgwa.width;
305   ss->th = mi->xgwa.height;
306 /*  ss->tw = ximage->width; */
307 /*  ss->th = ximage->height; */
308   
309   ss->qw *= (GLfloat) ss->tw / MI_WIDTH(mi);
310   ss->qh *= (GLfloat) ss->th / MI_HEIGHT(mi);
311   
312   ss->max_tx = (GLfloat) ss->tw / (GLfloat) ximage->width;
313   ss->max_ty = (GLfloat) ss->th / (GLfloat) ximage->height;
314
315   glBindTexture (GL_TEXTURE_2D, into_texid);
316
317   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
318   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
319                   GL_LINEAR_MIPMAP_LINEAR);
320   
321   clear_gl_error();
322   status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
323                              ximage->width, ximage->height,
324                              GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
325   
326   if(!status && glGetError())
327    /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway.
328       We could just call check_gl_error(), but that would exit. */
329     status = -1;
330
331   if(status) {
332     const char *s = gluErrorString (status);
333
334     fprintf(stderr, "%s: error mipmapping %dx%d texture: %s\n",
335             progname, ximage->width, ximage->height,
336             (s ? s : "(unknown)"));
337     fprintf(stderr, "%s: turning on -wireframe.\n", progname);
338     MI_IS_WIREFRAME(mi) = 1;
339     clear_gl_error();
340   }
341
342   check_gl_error("mipmapping");  /* should get a return code instead of a
343                                     GL error, but just in case... */
344   
345   free(ximage->data);
346   ximage->data = 0;
347   XDestroyImage(ximage);
348
349   ss->start_time = time ((time_t *) 0);
350 }
351
352 void
353 init_slideshow (ModeInfo *mi)
354 {
355   int screen = MI_SCREEN(mi);
356   slideshow_state *ss;
357   
358   if(sss == NULL) {
359     if((sss = (slideshow_state *)
360         calloc(MI_NUM_SCREENS(mi), sizeof(slideshow_state))) == NULL)
361       return;
362   }
363
364   ss = &sss[screen];
365   ss->window = MI_WINDOW(mi);
366   ss->qw = QW;
367   ss->qh = QH;
368
369   if((ss->glx_context = init_GL(mi)) != NULL) {
370     reshape_slideshow(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
371   } else {
372     MI_CLEARWINDOW(mi);
373   }
374
375   glClearColor(0.0,0.0,0.0,0.0);
376   
377   if(! MI_IS_WIREFRAME(mi)) {
378     glShadeModel(GL_SMOOTH);
379     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
380     glEnable(GL_DEPTH_TEST);
381     glEnable(GL_CULL_FACE);
382     glCullFace(GL_FRONT);
383     glDisable(GL_LIGHTING);
384
385     glGenTextures (1, &ss->texids[0]);  /* texture for image A */
386     glGenTextures (1, &ss->texids[1]);  /* texture for image B */
387   }
388   
389   reset(mi, ss->curr_screen);
390   ss->curr_texid_index = 0;
391   getSnapshot(mi, ss->texids[ss->curr_texid_index]);
392 }
393
394 void
395 draw_slideshow (ModeInfo *mi)
396 {
397   slideshow_state *ss = &sss[MI_SCREEN(mi)];
398   Window w = MI_WINDOW(mi);
399   Display *disp = MI_DISPLAY(mi);
400
401   if(!ss->glx_context) return;
402
403   glXMakeCurrent(disp, w, *(ss->glx_context));
404   
405   if (ss->frames == frames_per_pan) {
406
407     time_t now = time ((time_t *) 0);
408
409     if(fade) {
410       ss->in_transition = 1;
411       reset (mi, 1 - ss->curr_screen);
412
413       if (ss->start_time + duration <= now) {
414         ss->in_file_transition = 1;
415         getSnapshot(mi, ss->texids[1 - ss->curr_texid_index]);
416       }
417
418     } else {
419       reset(mi, ss->curr_screen);
420
421       if (ss->start_time + duration <= now)
422         getSnapshot(mi, ss->texids[ss->curr_texid_index]);
423     }
424   }
425
426   if (fade && ss->in_transition && ss->frames == frames_per_fade) {
427     ss->in_transition = 0;
428     ss->curr_screen = 1 - ss->curr_screen;
429
430     if (ss->in_file_transition) {
431       ss->in_file_transition = 0;
432       ss->curr_texid_index = 1 - ss->curr_texid_index;
433     }
434   }
435
436   display(mi);
437   
438   ss->frames++;
439
440   if(mi->fps_p) do_fps(mi);
441
442   glFinish(); 
443   glXSwapBuffers(disp, w);
444 }
445
446 void
447 release_slideshow (ModeInfo *mi)
448 {
449   if(sss != NULL) {
450     (void) free((void *) sss);
451     sss = NULL;
452   }
453
454   FreeAllGL(MI);
455 }
456
457 #endif