http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / bumps.c
1 /* -*- mode: C; tab-width: 4 -*-
2  * Bumps, Copyright (c) 2002, 2006 Shane Smit <CodeWeaver@DigitalLoom.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  *
12  * Module: "bumps.c"
13  * Tab Size: 4
14  *
15  * Description:
16  *  This is typical bump-mapping.  The actual bump map is generated by a screen
17  *  grab.  The light source is represented by a spotlight of random color. This
18  *  spotlight randomly traverses the bump map in a sinus pattern.
19  *
20  *  Essentially, it 3D-izes your desktop, based on color intensity.
21  *
22  * Modification History:
23  *  [10/01/99] - Shane Smit: Creation
24  *  [10/08/99] - Shane Smit: Port to C. (Ick)
25  *  [03/08/02] - Shane Smit: New movement code.
26  *  [09/12/02] - Shane Smit: MIT-SHM XImages.
27  *                                                       Thanks to Kennett Galbraith <http://www.Alpha-II.com/>
28  *                                                       for code optimization.
29  */
30
31 #include "bumps.h"
32
33 static void SetPalette(Display *, SBumps *, XWindowAttributes * );
34 static void InitBumpMap(Display *, SBumps *, XWindowAttributes * );
35 static void InitBumpMap_2(Display *, SBumps *);
36 static void SoftenBumpMap( SBumps * );
37
38
39
40
41 /* This function pointer will point to the appropriate PutPixel*() function below. */
42 static void (*MyPutPixel)( int8_ *, uint32_ );
43
44 static void PutPixel32( int8_ *pData, uint32_ pixel )
45 {
46         *(uint32_ *)pData = pixel;
47 }
48
49 static void PutPixel24( int8_ *pData, uint32_ pixel )
50 {
51         pData[ 2 ] = ( pixel & 0x00FF0000 ) >> 16;
52         pData[ 1 ] = ( pixel & 0x0000FF00 ) >> 8;
53         pData[ 0 ] = ( pixel & 0x000000FF );
54 }
55
56 static void PutPixel16( int8_ *pData, uint32_ pixel )
57 {
58         *(uint16_ *)pData = (uint16_)pixel;
59 }
60
61 static void PutPixel8( int8_ *pData, uint32_ pixel )
62 {
63         *(uint8_ *)pData = (uint8_)pixel;
64 }
65
66 /* Creates the light map, which is a circular image... going from black around the edges
67  * to white in the center. */
68 static void CreateSpotLight( SSpotLight *pSpotLight, uint16_ iDiameter, uint16_ nColorCount )
69 {
70         double nDist;
71         int16_ iDistX, iDistY;
72         uint8_ *pLOffset;
73         
74         pSpotLight->nFalloffDiameter = iDiameter;
75         pSpotLight->nFalloffRadius = pSpotLight->nFalloffDiameter / 2;
76         pSpotLight->nLightDiameter = iDiameter / 2;
77         pSpotLight->nLightRadius = pSpotLight->nLightDiameter / 2;
78 #ifdef VERBOSE
79         printf( "%s: Falloff Diameter: %d\n", progclass, pSpotLight->nFalloffDiameter );
80         printf( "%s: Spot Light Diameter: %d\n", progclass, pSpotLight->nLightDiameter );
81 #endif
82
83         pSpotLight->aLightMap = malloc( pSpotLight->nLightDiameter * pSpotLight->nLightDiameter * sizeof(uint8_) );
84
85         pLOffset = pSpotLight->aLightMap;
86         for( iDistY=-pSpotLight->nLightRadius; iDistY<pSpotLight->nLightRadius; ++iDistY )
87         {
88                 for( iDistX=-pSpotLight->nLightRadius; iDistX<pSpotLight->nLightRadius; ++iDistX )
89                 {
90                         nDist = sqrt( pow( iDistX+0.5F, 2 ) + pow( iDistY+0.5F, 2 ) );
91                         if( nDist / pSpotLight->nLightRadius <= 1.0f )
92                                 *pLOffset = (uint8_)(nColorCount - ( ( nDist / pSpotLight->nLightRadius ) * ( nColorCount - 1 ) ));
93                         else
94                                 *pLOffset = 0;
95
96                         ++pLOffset;
97                 }
98         }
99                 
100         /* Initialize movement variables.       */
101         pSpotLight->nAccelX = 0;
102         pSpotLight->nAccelY = 0;
103         pSpotLight->nVelocityX = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
104         pSpotLight->nVelocityY = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
105 }
106
107
108 /* Calculates the position of the spot light on the screen. */
109 static void CalcLightPos( SBumps *pBumps )
110 {
111         SSpotLight *pSpotLight = &pBumps->SpotLight;
112         float nGravity;
113
114         /* X */
115         if( pSpotLight->nXPos < pSpotLight->nFalloffRadius )                                                    nGravity = 1.0f;
116         else if( pSpotLight->nXPos > pBumps->iWinWidth - pSpotLight->nFalloffRadius )   nGravity = -1.0f;
117         else                                                                                                                                                    nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
118                 
119         pSpotLight->nAccelX += nGravity * ( pSpotLight->nAccelMax / 5.0f );
120         if( pSpotLight->nAccelX < -pSpotLight->nAccelMax )              pSpotLight->nAccelX = -pSpotLight->nAccelMax;
121         else if( pSpotLight->nAccelX > pSpotLight->nAccelMax )  pSpotLight->nAccelX = pSpotLight->nAccelMax;
122
123         pSpotLight->nVelocityX += pSpotLight->nAccelX;
124         if( pSpotLight->nVelocityX < -pSpotLight->nVelocityMax )                pSpotLight->nVelocityX = -pSpotLight->nVelocityMax;
125         else if( pSpotLight->nVelocityX > pSpotLight->nVelocityMax )    pSpotLight->nVelocityX = pSpotLight->nVelocityMax;
126
127         pSpotLight->nXPos += pSpotLight->nVelocityX;
128
129         /* Y */
130         if( pSpotLight->nYPos < pSpotLight->nFalloffRadius )                                                            nGravity = 1.0f;
131         else if( pSpotLight->nYPos > pBumps->iWinHeight - pSpotLight->nFalloffRadius )  nGravity = -1.0f;
132         else                                                                                                                                                    nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
133                 
134         pSpotLight->nAccelY += nGravity * ( pSpotLight->nAccelMax / 5.0f );
135         if( pSpotLight->nAccelY < -pSpotLight->nAccelMax )              pSpotLight->nAccelY = -pSpotLight->nAccelMax;
136         else if( pSpotLight->nAccelY > pSpotLight->nAccelMax )  pSpotLight->nAccelY = pSpotLight->nAccelMax;
137
138         pSpotLight->nVelocityY += pSpotLight->nAccelY;
139         if( pSpotLight->nVelocityY < -pSpotLight->nVelocityMax )                pSpotLight->nVelocityY = -pSpotLight->nVelocityMax;
140         else if( pSpotLight->nVelocityY > pSpotLight->nVelocityMax )    pSpotLight->nVelocityY = pSpotLight->nVelocityMax;
141
142         pSpotLight->nYPos += pSpotLight->nVelocityY;
143 }
144
145
146 /* Main initialization function. */
147 static void CreateBumps( SBumps *pBumps, Display *dpy, Window NewWin )
148 {
149         XWindowAttributes XWinAttribs;
150         XGCValues GCValues;
151         int32_ nGCFlags;
152         uint16_ iDiameter;
153
154         /* Make size and velocity a function of window size, so it appears the same at 100x60 as it does in 3200x1200. */
155         XGetWindowAttributes( dpy, NewWin, &XWinAttribs );
156         pBumps->iWinWidth = XWinAttribs.width;
157         pBumps->iWinHeight = XWinAttribs.height;
158         pBumps->SpotLight.nXPos = XWinAttribs.width / 2.0f;
159         pBumps->SpotLight.nYPos = XWinAttribs.height / 2.0f;
160         pBumps->SpotLight.nVelocityMax = ( ( XWinAttribs.width < XWinAttribs.height ) ? XWinAttribs.width : XWinAttribs.height ) / 140.0f;
161         pBumps->SpotLight.nAccelMax = pBumps->SpotLight.nVelocityMax / 10.0f;
162         pBumps->dpy = dpy;
163         pBumps->Win = NewWin;
164     pBumps->screen = XWinAttribs.screen;
165         pBumps->pXImage = NULL;
166         
167         iDiameter = ( ( pBumps->iWinWidth < pBumps->iWinHeight ) ? pBumps->iWinWidth : pBumps->iWinHeight ) / 2;
168
169     /* jwz: sometimes we get tearing if this lands on the wrong bounaary;
170        constraining it to be a multiple of 8 seems to fix it. */
171     iDiameter = ((iDiameter+7)/8)*8;
172
173 #ifdef HAVE_XSHM_EXTENSION
174         pBumps->bUseShm = get_boolean_resource(dpy,  "useSHM", "Boolean" );
175
176         if( pBumps->bUseShm )
177         {
178                 pBumps->pXImage = create_xshm_image( pBumps->dpy, XWinAttribs.visual, XWinAttribs.depth,
179                                                                                          ZPixmap, NULL, &pBumps->XShmInfo, iDiameter, iDiameter );
180                 if( !pBumps->pXImage )
181                 {
182                         fprintf( stderr, "%s: Unable to create XShmImage.\n", progname );
183                         pBumps->bUseShm = False;
184                 }
185         }
186 #endif /* HAVE_XSHM_EXTENSION */
187         if( !pBumps->pXImage )
188         {
189                 pBumps->pXImage = XCreateImage( pBumps->dpy, XWinAttribs.visual, XWinAttribs.depth, 
190                                                                         ZPixmap, 0, NULL, iDiameter, iDiameter, BitmapPad( pBumps->dpy ), 0 );
191                 pBumps->pXImage->data = malloc( pBumps->pXImage->bytes_per_line * pBumps->pXImage->height * sizeof(int8_) );
192         }
193
194         /* For speed, access the XImage data directly using my own PutPixel routine. */
195         switch( pBumps->pXImage->bits_per_pixel )
196         {
197                 case 32:
198                         pBumps->bytesPerPixel = 4;
199                         MyPutPixel = PutPixel32;
200                         break;
201                 
202                 case 24:
203                         pBumps->bytesPerPixel = 3;
204                         MyPutPixel = PutPixel24;
205                         break;
206
207                 case 16:
208                         pBumps->bytesPerPixel = 2;
209                         MyPutPixel = PutPixel16;
210                         break;
211
212                 case 8:
213                         pBumps->bytesPerPixel = 1;
214                         MyPutPixel = PutPixel8;
215                         break;
216
217                 default:
218                         fprintf( stderr, "%s: Unknown XImage depth.", progname );
219 #ifdef HAVE_XSHM_EXTENSION
220                         if( pBumps->bUseShm )
221                                 destroy_xshm_image( pBumps->dpy, pBumps->pXImage, &pBumps->XShmInfo );
222                         else
223 #endif /* HAVE_XSHM_EXTENSION */
224                                 XDestroyImage( pBumps->pXImage );
225                         exit( 1 );
226         }
227         
228         GCValues.function = GXcopy;
229         GCValues.subwindow_mode = IncludeInferiors;
230         nGCFlags = GCFunction;
231         if( use_subwindow_mode_p( XWinAttribs.screen, pBumps->Win ) ) /* See grabscreen.c */
232                 nGCFlags |= GCSubwindowMode;
233         pBumps->GraphicsContext = XCreateGC( pBumps->dpy, pBumps->Win, nGCFlags, &GCValues );
234         
235         SetPalette(dpy, pBumps, &XWinAttribs );
236         CreateSpotLight( &pBumps->SpotLight, iDiameter, pBumps->nColorCount );
237         InitBumpMap(dpy, pBumps, &XWinAttribs );
238 }
239
240
241 /* Creates a specialized phong shade palette. */
242 static void SetPalette(Display *dpy, SBumps *pBumps, XWindowAttributes *pXWinAttribs )
243 {
244         XColor BaseColor;
245         XColor Color;
246         char *sColor;                   /* Spotlight Color */
247         int16_ iColor;
248         
249         sColor = get_string_resource(dpy,  "color", "Color" );
250
251         BaseColor.red = RANDOM() % 0xFFFF; 
252         BaseColor.green = RANDOM() % 0xFFFF;
253         BaseColor.blue = RANDOM() % 0xFFFF;
254         
255         /* Make one color full intesity to avoid dark spotlights.       */
256         switch( RANDOM() % 3 )
257         {
258                 case 0: BaseColor.red   = 0xFFFF;       break;
259                 case 1: BaseColor.green = 0xFFFF;       break;
260                 case 2: BaseColor.blue  = 0xFFFF;       break;
261         }
262
263         if( strcasecmp( sColor, "random" ) && !XParseColor( pBumps->dpy, pXWinAttribs->colormap, sColor, &BaseColor ) )
264                 fprintf( stderr, "%s: color %s not found in database. Choosing random...\n", progname, sColor );
265
266 #ifdef VERBOSE
267         printf( "%s: Spotlight color is <%d,%d,%d> RGB.\n", progclass, BaseColor.red, BaseColor.green, BaseColor.blue );
268 #endif  /*  VERBOSE */
269
270         pBumps->nColorCount = get_integer_resource(dpy,  "colorcount", "Integer" );
271         if( pBumps->nColorCount < 2 )   pBumps->nColorCount = 2;
272         if( pBumps->nColorCount > 128 ) pBumps->nColorCount = 128;
273
274         pBumps->aColors = malloc( pBumps->nColorCount * sizeof(uint32_ ) );
275
276         /* Creates a phong shade:                 / BaseColor  \                               Index/ColorCount 
277          *                                                      PhongShade = | ------------ | Index + ( 65535 - BaseColor )^ 
278          *                                                                                \ ColorCount /                                                                                                */
279         pBumps->nColorCount--;
280         for( iColor=0; iColor<=pBumps->nColorCount; iColor++ )
281         {
282                 Color.red   = (uint16_)( ( ( BaseColor.red   / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.red,   iColor/(double)pBumps->nColorCount ) );
283                 Color.green = (uint16_)( ( ( BaseColor.green / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.green, iColor/(double)pBumps->nColorCount ) );
284                 Color.blue  = (uint16_)( ( ( BaseColor.blue  / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.blue,  iColor/(double)pBumps->nColorCount ) );
285
286                 if( !XAllocColor( pBumps->dpy, pXWinAttribs->colormap, &Color ) )
287                 {
288                         XFreeColors( pBumps->dpy, pXWinAttribs->colormap, pBumps->aColors, iColor, 0 );
289                         free( pBumps->aColors );
290                         pBumps->aColors = malloc( pBumps->nColorCount * sizeof(uint32_) );
291                         pBumps->nColorCount--;
292                         iColor = -1;
293                 }
294                 else
295                         pBumps->aColors[ iColor ] = Color.pixel;
296         }
297         pBumps->nColorCount++;
298
299 #ifdef VERBOSE
300         printf( "%s: Allocated %d colors.\n", progclass, pBumps->nColorCount );
301 #endif  /*  VERBOSE */
302
303         XSetWindowBackground( pBumps->dpy, pBumps->Win, pBumps->aColors[ 0 ] );
304 }
305
306
307 /* Grabs the current contents of the window to use an intensity-based bump map. */
308 static void InitBumpMap(Display *dpy, SBumps *pBumps, XWindowAttributes *pXWinAttribs )
309 {
310         pBumps->xColors = (XColor*)malloc( pBumps->iWinWidth * sizeof(XColor) );
311
312     if (pBumps->source) abort();
313     pBumps->source = XCreatePixmap(pBumps->dpy, pBumps->Win,
314                                    pXWinAttribs->width, pXWinAttribs->height,
315                                    pXWinAttribs->depth);
316   pBumps->img_loader = load_image_async_simple (0, pXWinAttribs->screen,
317                                             pBumps->Win, pBumps->source, 0, 0);
318 }
319
320 static void InitBumpMap_2(Display *dpy, SBumps *pBumps)
321 {
322         XImage *pScreenImage;
323         XColor *pColor;
324         uint8_ nSoften;
325         uint16_ iWidth, iHeight;
326         uint32_ nAverager;
327         uint16_ *pBump;
328         uint16_ maxHeight;
329         double softenMultiplier = 1.0f;
330         BOOL bInvert = (BOOL)get_boolean_resource(dpy,  "invert", "Boolean" );
331     XWindowAttributes XWinAttribs;
332     XGetWindowAttributes( pBumps->dpy, pBumps->Win, &XWinAttribs );
333
334     pBumps->start_time = time ((time_t) 0);
335
336         pScreenImage = XGetImage( pBumps->dpy, pBumps->source, 0, 0, 
337                               pBumps->iWinWidth, pBumps->iWinHeight,
338                               ~0L, ZPixmap );
339 /*    XFreePixmap (pBumps->dpy, pBumps->source);
340     pBumps->source = 0;*/
341
342         XSetWindowBackground( pBumps->dpy, pBumps->Win, pBumps->aColors[ 0 ] );
343         XClearWindow (pBumps->dpy, pBumps->Win);
344         XSync (pBumps->dpy, 0);
345
346         pBumps->aBumpMap = malloc( pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
347         
348         nSoften = get_integer_resource(dpy,  "soften", "Integer" );
349         while( nSoften-- )
350                 softenMultiplier *= 1.0f + ( 1.0f / 3.0f );     /* Softening takes the max height down, so scale up to compensate. */
351         maxHeight = pBumps->SpotLight.nLightRadius * softenMultiplier;
352         nAverager = maxHeight ? ( 3 * 0xFFFF ) / maxHeight : 0;
353
354         pBump = pBumps->aBumpMap;
355         if( bInvert )   /* Funny, it's actually the 'else' that inverts the bump map... */
356         {
357                 for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
358                 {
359                         pColor = pBumps->xColors;
360                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
361                                 (pColor++)->pixel = XGetPixel( pScreenImage, iWidth, iHeight );
362
363                         XQueryColors( pBumps->dpy, XWinAttribs.colormap, pBumps->xColors, pBumps->iWinWidth );
364
365                         pColor = pBumps->xColors;
366                         for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pColor, ++pBump )
367                           *pBump = ( nAverager ? ( pColor->red + pColor->green + pColor->blue ) / nAverager : 0 );
368                 }
369         }
370         else
371         {
372                 for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
373                 {
374                         pColor = pBumps->xColors;
375                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
376                                 (pColor++)->pixel = XGetPixel( pScreenImage, iWidth, iHeight );
377
378                         XQueryColors( pBumps->dpy, XWinAttribs.colormap, pBumps->xColors, pBumps->iWinWidth );
379         
380                         pColor = pBumps->xColors;
381                         for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pColor, ++pBump )
382                           *pBump = ( maxHeight - ( nAverager ? ( pColor->red + pColor->green + pColor->blue ) / nAverager : 0 ) );
383                 }
384         }
385
386         XDestroyImage( pScreenImage );
387
388         nSoften = get_integer_resource(dpy,  "soften", "Integer" );
389 #ifdef VERBOSE
390         if( nSoften )   printf( "%s: Softening Bump Map %d time(s)...\n", progclass, nSoften );
391 #endif
392         while( nSoften-- )
393                 SoftenBumpMap( pBumps );
394
395 /*      free( pBumps->xColors );
396     pBumps->xColors = 0;*/
397 }
398
399 /* Soften the bump map.  This is to avoid pixelated-looking ridges.
400  * |-----|-----|-----|
401  * |  0% |12.5%|  0% |  The adjacent pixels are averaged together
402  * |-----|-----|-----|  first.  Then than value is averaged with
403  * |12.5%| 50% |12.5%|  the pixel is question. This essentially weights
404  * |-----|-----|-----|  each pixel as shown on the left.
405  * |  0% |12.5%|  0% |
406  * |-----|-----|-----|
407  */
408 static void SoftenBumpMap( SBumps *pBumps )
409 {
410         uint16_ *pOffset, *pTOffset;
411         uint32_ nHeight;
412         uint32_ iWidth, iHeight;
413         uint16_ *aTempBuffer = malloc( pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
414
415         pOffset = pBumps->aBumpMap;
416         pTOffset = aTempBuffer;
417         for( iHeight=pBumps->iWinHeight; iHeight; --iHeight )
418         {
419                 for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pOffset, ++pTOffset )
420                 {
421                         if( iHeight==pBumps->iWinHeight || iHeight==1 ||
422                                 iWidth==pBumps->iWinWidth || iWidth==1 )
423                         {
424                                 *pTOffset = 0;
425                                 continue;
426                         }
427
428                         nHeight = pOffset[ -pBumps->iWinWidth ];
429                         nHeight += pOffset[ 1 ];
430                         nHeight += pOffset[ pBumps->iWinWidth ];
431                         nHeight += pOffset[ -1 ];
432                         nHeight >>= 2;
433                         nHeight += pOffset[ 0 ];
434                         nHeight >>= 1;
435                         *pTOffset = nHeight;
436                 }
437         }                                               
438
439         memcpy( pBumps->aBumpMap, aTempBuffer, pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
440         free( aTempBuffer );
441 }
442
443
444 /* This is where we slap down some pixels... */
445 static void Execute( SBumps *pBumps )
446 {
447         int32_ nLightXPos, nLightYPos;
448         int32_ iScreenX, iScreenY;
449         int32_ iLightX, iLightY;
450         uint16_ *pBOffset;
451         int8_ *pDOffset;
452         int32_ nX, nY;
453         uint16_ nColor;
454         int32_ nLightOffsetFar = pBumps->SpotLight.nFalloffDiameter - pBumps->SpotLight.nLightRadius;
455
456         CalcLightPos( pBumps );
457         
458         /* Offset to upper left hand corner. */
459         nLightXPos = pBumps->SpotLight.nXPos - pBumps->SpotLight.nFalloffRadius;
460         nLightYPos = pBumps->SpotLight.nYPos - pBumps->SpotLight.nFalloffRadius;
461         
462         for( iScreenY=nLightYPos, iLightY=-pBumps->SpotLight.nLightRadius; iLightY<nLightOffsetFar; ++iScreenY, ++iLightY )
463         {
464                 if( iScreenY < 0 )                                                      continue;
465                 else if( iScreenY >= pBumps->iWinHeight )       break;
466
467     /* warning: pointer targets in assignment differ in signedness
468        Should pDOffset be a int8?  I can't tell.  -jwz, 22-Jul-2003 */
469                 pDOffset = (int8_ *) &pBumps->pXImage->data[ (iLightY+pBumps->SpotLight.nLightRadius) * pBumps->pXImage->bytes_per_line ];
470                 pBOffset = pBumps->aBumpMap + ( iScreenY * pBumps->iWinWidth ) + nLightXPos;
471                 for( iScreenX=nLightXPos, iLightX=-pBumps->SpotLight.nLightRadius; iLightX<nLightOffsetFar; ++iScreenX, ++iLightX, ++pBOffset, pDOffset+=pBumps->bytesPerPixel )
472                 {
473                         if( iScreenX < 0 )                                                      continue;
474                         else if( iScreenX >= pBumps->iWinWidth )        break;
475                         else if( iScreenY == 0 || iScreenY >= pBumps->iWinHeight-2 ||
476                                          iScreenX == 0 || iScreenX >= pBumps->iWinWidth-2 )
477                         {
478                                 MyPutPixel( pDOffset, pBumps->aColors[ 0 ] );
479                                 continue;
480                         }
481
482                         /* That's right folks, all the magic of bump mapping occurs in these two lines.  (kinda disappointing, isn't it?) */
483                         nX = ( pBOffset[ 1 ] - pBOffset[ 0 ] ) + iLightX;
484                         nY = ( pBOffset[ pBumps->iWinWidth ] - pBOffset[ 0 ] ) + iLightY;
485
486                         if( nX<0 || nX>=pBumps->SpotLight.nLightDiameter
487                          || nY<0 || nY>=pBumps->SpotLight.nLightDiameter )
488                         {
489                                 MyPutPixel( pDOffset, pBumps->aColors[ 0 ] );
490                                 continue;
491                         }
492                                 
493                         nColor = pBumps->SpotLight.aLightMap[ ( nY * pBumps->SpotLight.nLightDiameter ) + nX ];
494                         MyPutPixel( pDOffset, pBumps->aColors[ nColor ] );
495                 }
496         }       
497
498         /* Allow the spotlight to go *slightly* off the screen by clipping the XImage. */
499         iLightX = iLightY = 0;  /* Use these for XImages X and Y now.   */
500         nX = nY = pBumps->SpotLight.nFalloffDiameter;   /* Use these for XImage width and height now.   */
501         if( nLightXPos < 0 )
502         {
503                 iLightX = -nLightXPos;
504                 nX -= iLightX;
505                 nLightXPos = 0;
506         }
507         else if( nLightXPos + nX >= pBumps->iWinWidth )
508         {
509                 nX -= ( nLightXPos + nX ) - pBumps->iWinWidth;
510         }
511         
512         if( nLightYPos < 0 )
513         {
514                 iLightY = -nLightYPos;
515                 nY -= iLightY;
516                 nLightYPos = 0;
517         }
518         else if( nLightYPos + nY >= pBumps->iWinHeight )
519         {
520                 nY -= ( nLightYPos + nY ) - pBumps->iWinHeight;
521         }
522         
523 #ifdef HAVE_XSHM_EXTENSION
524         if( pBumps->bUseShm )
525                 XShmPutImage( pBumps->dpy, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, iLightX, iLightY, nLightXPos, nLightYPos,
526                                           nX, nY, False);
527         else
528 #endif /* HAVE_XSHM_EXTENSION */
529                 XPutImage( pBumps->dpy, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, iLightX, iLightY, nLightXPos, nLightYPos,
530                                    nX, nY );
531 }
532
533
534 static void DestroySpotLight( SSpotLight *pSpotLight ) { free( pSpotLight->aLightMap ); }
535
536 /* Clean up */
537 static void DestroyBumps( SBumps *pBumps )
538 {
539         DestroySpotLight( &pBumps->SpotLight );
540         free( pBumps->aColors );
541         free( pBumps->aBumpMap );
542 #ifdef HAVE_XSHM_EXTENSION
543         if( pBumps->bUseShm )
544                 destroy_xshm_image( pBumps->dpy, pBumps->pXImage, &pBumps->XShmInfo );
545         else
546 #endif /* HAVE_XSHM_EXTENSION */
547                 XDestroyImage( pBumps->pXImage );
548 }
549
550
551 /* All messages to the screensaver are processed here. */
552 static void *
553 bumps_init (Display *dpy, Window Win)
554 {
555         SBumps *Bumps = (SBumps *) calloc (1, sizeof(SBumps));
556
557 #ifdef VERBOSE
558         time_t Time = time( NULL );
559         uint16_ iFrame = 0;
560 #endif  /*  VERBOSE */
561         
562         CreateBumps( Bumps, dpy, Win );
563         Bumps->delay = get_integer_resource(dpy,  "delay", "Integer" );
564     Bumps->duration = get_integer_resource (dpy, "duration", "Seconds");
565     if (Bumps->delay < 0) Bumps->delay = 0;
566     if (Bumps->duration < 1) Bumps->duration = 1;
567     Bumps->start_time = time ((time_t) 0);
568     return Bumps;
569 }
570
571 static unsigned long
572 bumps_draw (Display *dpy, Window window, void *closure)
573 {
574   SBumps *Bumps = (SBumps *) closure;
575
576   if (Bumps->img_loader)   /* still loading */
577     {
578       Bumps->img_loader = load_image_async_simple (Bumps->img_loader, 0, 0, 0, 0, 0);
579       if (! Bumps->img_loader)  /* just finished */
580         InitBumpMap_2(dpy, Bumps);
581       return Bumps->delay;
582     }
583
584   if (!Bumps->img_loader &&
585       Bumps->start_time + Bumps->duration < time ((time_t) 0)) {
586     Bumps->img_loader = load_image_async_simple (0, Bumps->screen,
587                                                  Bumps->Win, Bumps->source, 
588                                                  0, 0);
589   }
590
591   Execute( Bumps );
592
593 #ifdef VERBOSE
594   iFrame++;
595   if( Time - time( NULL ) )
596     {
597       printf( "FPS: %d\n", iFrame );
598       Time = time( NULL );
599       iFrame = 0;
600     }
601 #endif  /*  VERBOSE */
602
603   return Bumps->delay;
604 }
605
606 static void
607 bumps_reshape (Display *dpy, Window window, void *closure, 
608                  unsigned int w, unsigned int h)
609 {
610 }
611
612 static Bool
613 bumps_event (Display *dpy, Window window, void *closure, XEvent *event)
614 {
615   return False;
616 }
617
618 static void
619 bumps_free (Display *dpy, Window window, void *closure)
620 {
621   SBumps *Bumps = (SBumps *) closure;
622   DestroyBumps( Bumps );
623 }
624
625
626 XSCREENSAVER_MODULE ("Bumps", bumps)
627
628 /* vim: ts=4
629  */