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