http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.02.tar.gz
[xscreensaver] / hacks / bumps.c
1 /* Bumps, Copyright (c) 2002 Shane Smit <CodeWeaver@DigitalLoom.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Module: "bumps.c"
12  * Tab Size: 4
13  *
14  * Description:
15  *  This is typical bump-mapping.  The actual bump map is generated by a screen
16  *  grab.  The light source is represented by a spotlight of random color. This
17  *  spotlight randomly traverses the bump map in a sinus pattern.
18  *
19  *  Essentially, it 3D-izes your desktop, based on color intensity.
20  *
21  * Modification History:
22  *  [10/01/99] - Shane Smit: Creation
23  *  [10/08/99] - Shane Smit: Port to C. (Ick)
24  *  [03/08/02] - Shane Smit: New movement code.
25  */
26
27
28 #include "bumps.h"
29
30
31 void CreateSpotLight( SSpotLight *pSpotLight, uint16_ iDiameter, uint16_ nColorCount )
32 {
33         double nDelta;
34         int16_ iHeight, iWidth;
35         
36         pSpotLight->nDiameter = iDiameter;
37 #ifdef VERBOSE
38         printf( "%s: Light Diameter: %d\n", progclass, pSpotLight->nDiameter );
39 #endif
40
41         pSpotLight->aLightMap = calloc( pSpotLight->nDiameter * pSpotLight->nDiameter, sizeof(uint8_) );
42         memset( pSpotLight->aLightMap, 0, pSpotLight->nDiameter * pSpotLight->nDiameter );
43
44         /* The falloff max values... 3/4 of the entire lightmap. */
45         pSpotLight->nRadius = (uint16_)(pSpotLight->nDiameter / 2.5F);
46         
47         for( iHeight=-pSpotLight->nRadius; iHeight<pSpotLight->nRadius; iHeight++ )
48                 for( iWidth=-pSpotLight->nRadius; iWidth<pSpotLight->nRadius; iWidth++ )
49                 {
50                         nDelta = ( nColorCount * 2.5F ) - ( ( sqrt( pow( iWidth+0.5F, 2 ) + pow( iHeight+0.5F, 2 ) ) / pSpotLight->nRadius ) * ( nColorCount * 2.5F ) );
51                         nDelta += nColorCount;
52                         if( nDelta >= ( nColorCount * 2 ) ) nDelta = ( nColorCount * 2 ) - 1;
53                         if( nDelta >= nColorCount )
54                                 pSpotLight->aLightMap[ ( ( iHeight + (pSpotLight->nDiameter/2) ) * pSpotLight->nDiameter ) + iWidth + (pSpotLight->nDiameter/2) ] = (uint8_)nDelta;
55                 }
56
57         /* The actual lightmap... Use 1/4 to save time within the loop. */
58         pSpotLight->nRadius = pSpotLight->nDiameter / 4;
59
60         for( iHeight=-pSpotLight->nRadius; iHeight<pSpotLight->nRadius; iHeight++ )
61                 for( iWidth=-pSpotLight->nRadius; iWidth<pSpotLight->nRadius; iWidth++ )
62                 {
63                         nDelta = nColorCount - ( ( sqrt( pow( iWidth+0.5F, 2 ) + pow( iHeight+0.5F, 2 ) ) / pSpotLight->nRadius ) * ( nColorCount - 1 ) );
64                         if( nDelta >= 1 )
65                                 pSpotLight->aLightMap[ ( ( iHeight + (pSpotLight->nDiameter/2) ) * pSpotLight->nDiameter ) + iWidth + (pSpotLight->nDiameter/2) ] = (uint8_)nDelta;
66                 }
67                 
68         pSpotLight->nRadius = pSpotLight->nDiameter / 2;        /* Now set the radius back to what it should be. */
69         pSpotLight->nAccelX = 0;
70         pSpotLight->nAccelY = 0;
71         pSpotLight->nVelocityX = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
72         pSpotLight->nVelocityY = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
73 }
74
75
76 void CalcLightPos( SBumps *pBumps )
77 {
78         SSpotLight *pSpotLight = &pBumps->SpotLight;
79         float nGravity;
80
81         /* X */
82         if( pSpotLight->nXPos < pSpotLight->nDiameter * 1 )                                                                     nGravity = 1.0f;
83         else if( pSpotLight->nXPos > pBumps->iWinWidth - ( pSpotLight->nDiameter * 1 ) )        nGravity = -1.0f;
84         else                                                                                                                                                            nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
85                 
86         pSpotLight->nAccelX += nGravity * ( pSpotLight->nAccelMax / 10.0f );
87         if( pSpotLight->nAccelX < -pSpotLight->nAccelMax )              pSpotLight->nAccelX = -pSpotLight->nAccelMax;
88         else if( pSpotLight->nAccelX > pSpotLight->nAccelMax )  pSpotLight->nAccelX = pSpotLight->nAccelMax;
89
90         pSpotLight->nVelocityX += pSpotLight->nAccelX;
91         if( pSpotLight->nVelocityX < -pSpotLight->nVelocityMax )                pSpotLight->nVelocityX = -pSpotLight->nVelocityMax;
92         else if( pSpotLight->nVelocityX > pSpotLight->nVelocityMax )    pSpotLight->nVelocityX = pSpotLight->nVelocityMax;
93
94         pSpotLight->nXPos += pSpotLight->nVelocityX;
95
96         /* Y */
97         if( pSpotLight->nYPos < pSpotLight->nDiameter * 1 )                                                                     nGravity = 1.0f;
98         else if( pSpotLight->nYPos > pBumps->iWinHeight - ( pSpotLight->nDiameter * 1 ) )       nGravity = -1.0f;
99         else                                                                                                                                                            nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
100                 
101         pSpotLight->nAccelY += nGravity * ( pSpotLight->nAccelMax / 10.0f );
102         if( pSpotLight->nAccelY < -pSpotLight->nAccelMax )              pSpotLight->nAccelY = -pSpotLight->nAccelMax;
103         else if( pSpotLight->nAccelY > pSpotLight->nAccelMax )  pSpotLight->nAccelY = pSpotLight->nAccelMax;
104
105         pSpotLight->nVelocityY += pSpotLight->nAccelY;
106         if( pSpotLight->nVelocityY < -pSpotLight->nVelocityMax )                pSpotLight->nVelocityY = -pSpotLight->nVelocityMax;
107         else if( pSpotLight->nVelocityY > pSpotLight->nVelocityMax )    pSpotLight->nVelocityY = pSpotLight->nVelocityMax;
108
109         pSpotLight->nYPos += pSpotLight->nVelocityY;
110 }
111
112
113 void CreateBumps( SBumps *pBumps, Display *pNewDisplay, Window NewWin )
114 {
115         XWindowAttributes XWinAttribs;
116         XGCValues GCValues;
117         int32_ nGCFlags;
118         uint16_ iWidth, iHeight;
119         uint16_ iDiameter;
120         
121         XGetWindowAttributes( pNewDisplay, NewWin, &XWinAttribs );
122         pBumps->iWinWidth = XWinAttribs.width;
123         pBumps->iWinHeight = XWinAttribs.height;
124         pBumps->SpotLight.nXPos = XWinAttribs.width / 2.0f;
125         pBumps->SpotLight.nYPos = XWinAttribs.height / 2.0f;
126         pBumps->SpotLight.nVelocityMax = ( ( XWinAttribs.width < XWinAttribs.height ) ? XWinAttribs.width : XWinAttribs.height ) / 128.0f;
127         pBumps->SpotLight.nAccelMax = pBumps->SpotLight.nVelocityMax / 10.0f;
128         pBumps->pDisplay = pNewDisplay;
129         pBumps->Win = NewWin;
130
131         pBumps->pXImage = XCreateImage( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
132                 pBumps->iWinWidth, pBumps->iWinHeight, BitmapPad( pBumps->pDisplay ), 0 );
133         pBumps->pXImage->data = calloc( pBumps->pXImage->bytes_per_line * pBumps->pXImage->height, sizeof(int8_) );
134
135         GCValues.function = GXcopy;
136         GCValues.subwindow_mode = IncludeInferiors;
137         nGCFlags = GCForeground | GCFunction;
138         if( use_subwindow_mode_p( XWinAttribs.screen, pBumps->Win ) ) /* See grabscreen.c */
139                 nGCFlags |= GCSubwindowMode;
140         pBumps->GraphicsContext = XCreateGC( pBumps->pDisplay, pBumps->Win, nGCFlags, &GCValues );
141         
142         SetPalette( pBumps, &XWinAttribs );
143         iDiameter = ( ( pBumps->iWinWidth < pBumps->iWinHeight ) ? pBumps->iWinWidth : pBumps->iWinHeight ) / 3;
144         CreateSpotLight( &pBumps->SpotLight, iDiameter, pBumps->nColorCount );
145         InitBumpMap( pBumps, &XWinAttribs );
146
147         /* Clear the image. */
148   if (pBumps->aXColors[ 0 ].pixel == 0)
149     memset (pBumps->pXImage->data, 0,
150             pBumps->pXImage->bytes_per_line * pBumps->pXImage->height);
151   else
152     for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
153       for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
154         XPutPixel( pBumps->pXImage, iWidth, iHeight,
155                    pBumps->aXColors[ 0 ].pixel );
156   XSetWindowBackground( pBumps->pDisplay, pBumps->Win,
157                         pBumps->aXColors[ 0 ].pixel );
158   XClearWindow (pBumps->pDisplay, pBumps->Win);
159 }
160
161
162 void SetPalette( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
163 {
164         XColor Color;
165         char *sColor;                   /* Spotlight Color */
166         int16_ iColor;
167         uint32_ *aPixels;
168         
169         sColor = get_string_resource( "color", "Color" );
170
171         Color.red = RANDOM() % 0xFFFF; 
172         Color.green = RANDOM() % 0xFFFF;
173         Color.blue = RANDOM() % 0xFFFF;
174         
175         /* Make one color full intesity to avoid dark spotlights.       */
176         switch( RANDOM() % 3 )
177         {
178                 case 0: Color.red       = 0xFFFF;       break;
179                 case 1: Color.green     = 0xFFFF;       break;
180                 case 2: Color.blue      = 0xFFFF;       break;
181         }
182
183         if( strcasecmp( sColor, "random" ) && !XParseColor( pBumps->pDisplay, pXWinAttribs->colormap, sColor, &Color ) )
184                 fprintf( stderr, "%s: color %s not found in database. Choosing random...\n", progname, sColor );
185
186 #ifdef VERBOSE
187         printf( "%s: Spotlight color is <%d,%d,%d> RGB.\n", progclass, Color.red, Color.green, Color.blue );
188 #endif  /*  VERBOSE */
189
190         pBumps->nColorCount = get_integer_resource( "colorcount", "Integer" );
191         if( pBumps->nColorCount < 2 )   pBumps->nColorCount = 2;
192         if( pBumps->nColorCount > 128 ) pBumps->nColorCount = 128;
193
194         pBumps->aXColors = calloc( pBumps->nColorCount, sizeof(XColor ) );
195                 aPixels  = calloc( pBumps->nColorCount, sizeof(uint32_) );
196
197         /* Creates a phong shade:                 / SpotColor  \                               Index/ColorCount 
198          *                                                      PhongShade = | ------------ | Index + ( 65535 - SpotColor )^ 
199          *                                                                                \ ColorCount /                                                                                                */
200         pBumps->nColorCount--;
201         for( iColor=0; iColor<=pBumps->nColorCount; iColor++ )
202         {
203                 pBumps->aXColors[ iColor ].red   = (uint16_)( ( ( Color.red   / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.red,   iColor/(double)pBumps->nColorCount ) );
204                 pBumps->aXColors[ iColor ].green = (uint16_)( ( ( Color.green / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.green, iColor/(double)pBumps->nColorCount ) );
205                 pBumps->aXColors[ iColor ].blue  = (uint16_)( ( ( Color.blue  / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.blue,  iColor/(double)pBumps->nColorCount ) );
206
207                 if( !XAllocColor( pBumps->pDisplay, pXWinAttribs->colormap, &pBumps->aXColors[ iColor ] ) )
208                 {
209                         XFreeColors( pBumps->pDisplay, pXWinAttribs->colormap, aPixels, iColor, 0 );
210                         free( pBumps->aXColors );
211                         free(         aPixels );
212                         pBumps->nColorCount--;
213                         pBumps->aXColors = calloc( pBumps->nColorCount, sizeof(XColor) );
214                         aPixels  = calloc( pBumps->nColorCount, sizeof(uint32_) );
215                         iColor = -1;
216                 }
217                 else
218                         aPixels[ iColor ] = pBumps->aXColors[ iColor ].pixel;
219         }
220         pBumps->nColorCount++;
221
222 #ifdef VERBOSE
223         printf( "%s: Allocated %d colors.\n", progclass, pBumps->nColorCount );
224 #endif  /*  VERBOSE */
225
226         XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aXColors[ 0 ].pixel );
227 }
228
229
230 void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
231 {
232         XImage *pScreenImage;
233         XColor *aColors;
234         uint8_ nSoften;
235         uint16_ iWidth, iHeight;
236         BOOL bInvert = (BOOL)get_boolean_resource( "invert", "Boolean" );
237         
238         aColors = (XColor*)calloc( pBumps->iWinWidth, sizeof(XColor) );
239         grab_screen_image( pXWinAttribs->screen, pBumps->Win );
240         pScreenImage = XGetImage( pBumps->pDisplay, pBumps->Win, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap );
241
242         /* jwz: get the grabbed bits off the screen fast */
243         XClearWindow (pBumps->pDisplay, pBumps->Win);
244         XSync (pBumps->pDisplay, 0);
245
246         pBumps->aBumpMap = calloc( pBumps->iWinWidth * pBumps->iWinHeight, sizeof(uint16_) );
247         for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
248         {
249                 for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
250                         aColors[ iWidth ].pixel = XGetPixel( pScreenImage, iWidth, iHeight );
251
252                 XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
253         
254                 if( bInvert )
255                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
256                                 pBumps->aBumpMap[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = (uint16_)
257                                         ( ( aColors[ iWidth ].red + aColors[ iWidth ].green + aColors[ iWidth ].blue ) / ( 0x2FFFD / (double)pBumps->SpotLight.nDiameter ) );
258                 else
259                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
260                                 pBumps->aBumpMap[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = (uint16_)
261                                         ( pBumps->SpotLight.nDiameter - ( ( aColors[ iWidth ].red + aColors[ iWidth ].green + aColors[ iWidth ].blue ) / ( 0x2FFFD / (double)pBumps->SpotLight.nDiameter ) ) );
262         }
263
264         XDestroyImage( pScreenImage );
265
266         nSoften = get_integer_resource( "soften", "Integer" );
267 #ifdef VERBOSE
268         if( nSoften )   printf( "%s: Softening Bump Map %d time(s)...\n", progclass, nSoften );
269 #endif
270         while( nSoften-- )
271                 SoftenBumpMap( pBumps );
272         free( aColors );
273 }
274
275
276 void SoftenBumpMap( SBumps *pBumps )
277 {
278         uint16_ *pOffset;
279         uint16_ nHeight;
280         uint16_ iWidth, iHeight;
281         uint16_ *pTempBuffer = calloc( pBumps->iWinWidth * pBumps->iWinHeight, sizeof(uint16_) );
282
283         for( iHeight=1; iHeight<pBumps->iWinHeight-1; iHeight++ )
284         {
285                 pOffset = pBumps->aBumpMap + ( iHeight * pBumps->iWinWidth );
286                 for( iWidth=1; iWidth<pBumps->iWinWidth-1; iWidth++ )
287                 {       
288                         nHeight = 0;
289                         nHeight += pOffset[ iWidth ];
290                         nHeight += pOffset[ iWidth - pBumps->iWinWidth ];
291                         nHeight += pOffset[ iWidth + 1 ];
292                         nHeight += pOffset[ iWidth + pBumps->iWinWidth ];
293                         nHeight += pOffset[ iWidth - 1 ];
294                         nHeight /= 5;
295                         pTempBuffer[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = nHeight;
296                 }
297         }                                               
298         
299         memcpy( pBumps->aBumpMap, pTempBuffer, pBumps->iWinWidth * pBumps->iWinHeight * 2 );
300         free( pTempBuffer );
301 }
302
303
304 void Execute( SBumps *pBumps )
305 {
306         uint16_ nLightXPos, nLightYPos;
307         uint16_ iWidth, iHeight;
308         uint16_ iLightWidth, iLightHeight;
309         uint16_ *pBOffset;
310         uint8_ *pLOffset;
311         int16_ nX, nY;
312         uint16_ nColor;
313         CalcLightPos( pBumps );
314         
315         /* Offset to upper left hand corner. */
316         nLightXPos = pBumps->SpotLight.nXPos - pBumps->SpotLight.nRadius;
317         nLightYPos = pBumps->SpotLight.nYPos - pBumps->SpotLight.nRadius;
318
319         for( iHeight=nLightYPos, iLightHeight=0; iLightHeight<pBumps->SpotLight.nDiameter; iHeight++, iLightHeight++ )
320         {
321                 pBOffset = pBumps->aBumpMap + ( iHeight * pBumps->iWinWidth );
322                 pLOffset = pBumps->SpotLight.aLightMap + ( iLightHeight * pBumps->SpotLight.nDiameter );
323                 for( iWidth=nLightXPos, iLightWidth=0; iLightWidth<pBumps->SpotLight.nDiameter; iWidth++, iLightWidth++ )
324                 {
325                         if( pLOffset[ iLightWidth ] )
326                         {                               
327                                 nX = pBOffset[ iWidth + 1                 ] - pBOffset[ iWidth ] + iLightWidth;
328                                 nY = pBOffset[ iWidth + pBumps->iWinWidth ] - pBOffset[ iWidth ] + iLightHeight;
329
330                                 if( nX < 0 )                                    nX = 0;
331                                 else if( nX >= pBumps->SpotLight.nDiameter )    nX = pBumps->SpotLight.nDiameter - 1;
332
333                                 if( nY < 0 )                                    nY = 0;
334                                 else if( nY >= pBumps->SpotLight.nDiameter )    nY = pBumps->SpotLight.nDiameter - 1;
335
336                                 nColor = pBumps->SpotLight.aLightMap[ ( nY * pBumps->SpotLight.nDiameter ) + nX ];
337                                 if( nColor >= pBumps->nColorCount )
338                                         nColor = 1;
339
340                                 if( pLOffset[ iLightWidth ] >= pBumps->nColorCount )
341                                         if( nColor > pLOffset[ iLightWidth ] - pBumps->nColorCount )
342                                                 nColor = pLOffset[ iLightWidth ] - pBumps->nColorCount;
343                                                 
344                                 XPutPixel( pBumps->pXImage, iWidth, iHeight, pBumps->aXColors[ nColor ].pixel );
345                         }
346                         else
347                                 XPutPixel( pBumps->pXImage, iWidth, iHeight, pBumps->aXColors[ 0 ].pixel );
348                 }
349         }       
350
351         XPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, nLightXPos, nLightYPos, nLightXPos, nLightYPos, pBumps->SpotLight.nDiameter, pBumps->SpotLight.nDiameter );
352         XSync( pBumps->pDisplay, False );
353 }
354
355
356 void DestroyBumps( SBumps *pBumps )
357 {
358         DestroySpotLight( &pBumps->SpotLight );
359         free( pBumps->aXColors );
360         free( pBumps->aBumpMap );
361         XDestroyImage( pBumps->pXImage );
362 }
363
364
365 /* All messages to the screensaver are processed here. */
366 void screenhack( Display *pDisplay, Window Win )
367 {
368         SBumps Bumps;
369         uint32_ iDelay;
370 #ifdef VERBOSE
371         time_t Time = time( NULL );
372         uint16_ iFrame = 0;
373 #endif  /*  VERBOSE */
374         
375         CreateBumps( &Bumps, pDisplay, Win );
376         iDelay = get_integer_resource( "delay", "Integer" );
377
378         while( 1 )
379         {
380                 screenhack_handle_events( pDisplay );
381                 Execute( &Bumps );
382                 usleep( iDelay );
383
384 #ifdef VERBOSE
385                 iFrame++;
386                 if( Time - time( NULL ) )
387                 {
388                         printf( "FPS: %d\n", iFrame );
389                         Time = time( NULL );
390                         iFrame = 0;
391                 }
392 #endif  /*  VERBOSE */
393         }
394
395         DestroyBumps( &Bumps );
396 }
397
398  
399 /* vim: ts=4
400  */