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