http://ftp.x.org/contrib/applications/xscreensaver-3.18.tar.gz
[xscreensaver] / hacks / bumps.c
1 /* Bumps, Copyright (c) 1999 Shane Smit <blackend@inconnect.com>
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.cpp"
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  */
25
26
27 #include "bumps.h"
28
29
30 void CreateSpotLight( SSpotLight *pSpotLight, uint16_ iWinWidth, uint16_ nColorCount )
31 {
32         double nDelta;
33         int16_ iHeight, iWidth;
34         
35         pSpotLight->nDiameter = iWinWidth / 3;
36 #ifdef VERBOSE
37         printf( "%s: Light Diameter: %d\n", progclass, pSpotLight->nDiameter );
38 #endif
39
40         pSpotLight->aLightMap = calloc( pSpotLight->nDiameter * pSpotLight->nDiameter, sizeof(uint8_) );
41         memset( pSpotLight->aLightMap, 0, pSpotLight->nDiameter * pSpotLight->nDiameter );
42
43         /* The falloff max values... 3/4 of the entire lightmap. */
44         pSpotLight->nRadius = (uint16_)(pSpotLight->nDiameter / 2.5F);
45         
46         for( iHeight=-pSpotLight->nRadius; iHeight<pSpotLight->nRadius; iHeight++ )
47                 for( iWidth=-pSpotLight->nRadius; iWidth<pSpotLight->nRadius; iWidth++ )
48                 {
49                         nDelta = ( nColorCount * 2.5F ) - ( ( sqrt( pow( iWidth+0.5F, 2 ) + pow( iHeight+0.5F, 2 ) ) / pSpotLight->nRadius ) * ( nColorCount * 2.5F ) );
50                         nDelta += nColorCount;
51                         if( nDelta >= ( nColorCount * 2 ) ) nDelta = ( nColorCount * 2 ) - 1;
52                         if( nDelta >= nColorCount )
53                                 pSpotLight->aLightMap[ ( ( iHeight + (pSpotLight->nDiameter/2) ) * pSpotLight->nDiameter ) + iWidth + (pSpotLight->nDiameter/2) ] = (uint8_)nDelta;
54                 }
55
56         /* The actual lightmap... 1/2 of the entire lightmap. */
57         pSpotLight->nRadius = pSpotLight->nDiameter / 4;
58
59         for( iHeight=-pSpotLight->nRadius; iHeight<pSpotLight->nRadius; iHeight++ )
60                 for( iWidth=-pSpotLight->nRadius; iWidth<pSpotLight->nRadius; iWidth++ )
61                 {
62                         nDelta = nColorCount - ( ( sqrt( pow( iWidth+0.5F, 2 ) + pow( iHeight+0.5F, 2 ) ) / pSpotLight->nRadius ) * ( nColorCount - 1 ) );
63                         if( nDelta >= 1 )
64                                 pSpotLight->aLightMap[ ( ( iHeight + (pSpotLight->nDiameter/2) ) * pSpotLight->nDiameter ) + iWidth + (pSpotLight->nDiameter/2) ] = (uint8_)nDelta;
65                 }
66                 
67         CreateTables( pSpotLight );
68
69         pSpotLight->nRadius = pSpotLight->nDiameter / 2;        /* Now set the radius back to what it should be. */
70         pSpotLight->nAngleX = RANDOM() % pSpotLight->nDegreeCount;
71         pSpotLight->nAngleY = RANDOM() % pSpotLight->nDegreeCount;
72         pSpotLight->nVelocityX = ( RANDOM() % 3 ) + 1;
73         pSpotLight->nVelocityY = ( RANDOM() % 3 ) + 1;
74 }
75
76
77 void CreateTables( SSpotLight *pSpotLight )
78 {
79         long double nUnit;
80         uint16_ iDegree;
81         
82         pSpotLight->nDegreeCount = get_integer_resource( "degrees", "Integer" );
83         if( pSpotLight->nDegreeCount < 180 ) pSpotLight->nDegreeCount = 180;    /* Less than 180 will show trails in higher resolutions. */
84 #ifdef VERBOSE
85         printf( "%s: Using a %d degree circle.\n", progclass, pSpotLight->nDegreeCount );
86 #endif
87
88         pSpotLight->aSinTable = calloc( pSpotLight->nDegreeCount, sizeof(double) );
89
90         /* This funtion builds the Sine Lookup Tables. */
91         nUnit = (long double)( PI * 2.0F ) / (long double)( pSpotLight->nDegreeCount );
92
93         for( iDegree=0; iDegree<pSpotLight->nDegreeCount; iDegree++)
94                 pSpotLight->aSinTable[ iDegree ] = sin( nUnit * (long double)iDegree );
95 }
96
97
98 void CalcLightPos( SSpotLight *pSpotLight, uint16_ *pXPos, uint16_ *pYPos )
99 {
100         pSpotLight->nVelocityX += ( RANDOM() % 2 ) ? 0.05F : -0.05F;
101         if( pSpotLight->nVelocityX < 1 )                pSpotLight->nVelocityX = 1;
102         else if( pSpotLight->nVelocityX > 3 )   pSpotLight->nVelocityX = 3;
103
104         pSpotLight->nVelocityX += ( RANDOM() % 2 ) ? 0.05F : -0.05F;
105         if( pSpotLight->nVelocityY < 1 )                pSpotLight->nVelocityX = 1;
106         else if( pSpotLight->nVelocityY > 3 )   pSpotLight->nVelocityX = 3;
107         
108     pSpotLight->nAngleX += pSpotLight->nVelocityX;
109     if( pSpotLight->nAngleX >= pSpotLight->nDegreeCount )       pSpotLight->nAngleX -= pSpotLight->nDegreeCount;
110
111     pSpotLight->nAngleY += pSpotLight->nVelocityY;
112     if( pSpotLight->nAngleY >= pSpotLight->nDegreeCount )       pSpotLight->nAngleY -= pSpotLight->nDegreeCount;
113
114     *pXPos = (uint16_)( ( pSpotLight->iWinXCenter - pSpotLight->nRadius ) * pSpotLight->aSinTable[ (uint16_)pSpotLight->nAngleX ] ) + pSpotLight->iWinXCenter;
115     *pYPos = (uint16_)( ( pSpotLight->iWinYCenter - pSpotLight->nRadius ) * pSpotLight->aSinTable[ (uint16_)pSpotLight->nAngleY ] ) + pSpotLight->iWinYCenter;
116
117         /* Offset to upper left hand corner. */
118         *pXPos -= pSpotLight->nRadius;
119         *pYPos -= pSpotLight->nRadius;
120 }
121
122
123 void CreateBumps( SBumps *pBumps, Display *pNewDisplay, Window NewWin )
124 {
125         XWindowAttributes XWinAttribs;
126         XGCValues GCValues;
127         int32_ nGCFlags;
128         uint16_ iWidth, iHeight;
129
130         XGetWindowAttributes( pNewDisplay, NewWin, &XWinAttribs );
131         pBumps->iWinWidth = XWinAttribs.width;
132         pBumps->iWinHeight = XWinAttribs.height;
133         pBumps->SpotLight.iWinXCenter = XWinAttribs.width / 2;
134         pBumps->SpotLight.iWinYCenter = XWinAttribs.height / 2;
135         pBumps->pDisplay = pNewDisplay;
136         pBumps->Win = NewWin;
137
138         pBumps->pXImage = XCreateImage( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
139                 pBumps->iWinWidth, pBumps->iWinHeight, BitmapPad( pBumps->pDisplay ), 0 );
140         pBumps->pXImage->data = calloc( pBumps->pXImage->bytes_per_line * pBumps->pXImage->height, sizeof(int8_) );
141
142         GCValues.function = GXcopy;
143         GCValues.subwindow_mode = IncludeInferiors;
144         nGCFlags = GCForeground | GCFunction;
145         if( use_subwindow_mode_p( XWinAttribs.screen, pBumps->Win ) ) /* See grabscreen.c */
146                 nGCFlags |= GCSubwindowMode;
147         pBumps->GraphicsContext = XCreateGC( pBumps->pDisplay, pBumps->Win, nGCFlags, &GCValues );
148         
149         SetPalette( pBumps, &XWinAttribs );
150         CreateSpotLight( &pBumps->SpotLight, pBumps->iWinWidth, pBumps->nColorCount );
151         InitBumpMap( pBumps, &XWinAttribs );
152
153         /* Clear the image. */
154   if (pBumps->aXColors[ 0 ].pixel == 0)
155     memset (pBumps->pXImage->data, 0,
156             pBumps->pXImage->bytes_per_line * pBumps->pXImage->height);
157   else
158     for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
159       for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
160         XPutPixel( pBumps->pXImage, iWidth, iHeight,
161                    pBumps->aXColors[ 0 ].pixel );
162   XSetWindowBackground( pBumps->pDisplay, pBumps->Win,
163                         pBumps->aXColors[ 0 ].pixel );
164   XClearWindow (pBumps->pDisplay, pBumps->Win);
165 }
166
167
168 void SetPalette( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
169 {
170         XColor Color;
171         char *sColor;                   /* Spotlight Color */
172         int16_ iColor;
173         uint32_ *aPixels;
174         
175         sColor = get_string_resource( "color", "Color" );
176
177         Color.red = RANDOM() % 0xFFFF; 
178         Color.green = RANDOM() % 0xFFFF;
179         Color.blue = RANDOM() % 0xFFFF;
180
181         if( strcasecmp( sColor, "random" ) && !XParseColor( pBumps->pDisplay, pXWinAttribs->colormap, sColor, &Color ) )
182                 fprintf( stderr, "%s: color %s not found in database. Choosing random...\n", progname, sColor );
183
184 #ifdef VERBOSE
185         printf( "%s: Spotlight color is <%d,%d,%d> RGB.\n", progclass, Color.red, Color.green, Color.blue );
186 #endif  /*  VERBOSE */
187
188         pBumps->nColorCount = get_integer_resource( "colorcount", "Integer" );
189         if( pBumps->nColorCount < 2 )   pBumps->nColorCount = 2;
190         if( pBumps->nColorCount > 128 ) pBumps->nColorCount = 128;
191
192         pBumps->aXColors = calloc( pBumps->nColorCount, sizeof(XColor ) );
193                 aPixels  = calloc( pBumps->nColorCount, sizeof(uint32_) );
194
195         /* Creates a phong shade:                 / SpotColor  \                               Index/ColorCount 
196          *                                                      PhongShade = | ------------ | Index + ( 65535 - SpotColor )^ 
197          *                                                                                \ ColorCount /                                                                                                */
198         pBumps->nColorCount--;
199         for( iColor=0; iColor<=pBumps->nColorCount; iColor++ )
200         {
201                 pBumps->aXColors[ iColor ].red   = (uint16_)( ( ( Color.red   / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.red,   iColor/(double)pBumps->nColorCount ) );
202                 pBumps->aXColors[ iColor ].green = (uint16_)( ( ( Color.green / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.green, iColor/(double)pBumps->nColorCount ) );
203                 pBumps->aXColors[ iColor ].blue  = (uint16_)( ( ( Color.blue  / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.blue,  iColor/(double)pBumps->nColorCount ) );
204
205                 if( !XAllocColor( pBumps->pDisplay, pXWinAttribs->colormap, &pBumps->aXColors[ iColor ] ) )
206                 {
207                         XFreeColors( pBumps->pDisplay, pXWinAttribs->colormap, aPixels, iColor, 0 );
208                         free( pBumps->aXColors );
209                         free(         aPixels );
210                         pBumps->nColorCount--;
211                         pBumps->aXColors = calloc( pBumps->nColorCount, sizeof(XColor) );
212                         aPixels  = calloc( pBumps->nColorCount, sizeof(uint32_) );
213                         iColor = -1;
214                 }
215                 else
216                         aPixels[ iColor ] = pBumps->aXColors[ iColor ].pixel;
217         }
218         pBumps->nColorCount++;
219
220 #ifdef VERBOSE
221         printf( "%s: Allocated %d colors.\n", progclass, pBumps->nColorCount );
222 #endif  /*  VERBOSE */
223
224         XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aXColors[ 0 ].pixel );
225 }
226
227
228 void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
229 {
230         XImage *pScreenImage;
231         XColor aColors[ pBumps->iWinWidth ];
232         uint8_ nSoften;
233         uint16_ iWidth, iHeight;
234         BOOL bInvert = (BOOL)get_boolean_resource( "invert", "Boolean" );
235         
236         grab_screen_image( pXWinAttribs->screen, pBumps->Win );
237         pScreenImage = XGetImage( pBumps->pDisplay, pBumps->Win, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap );
238
239         /* jwz: get the grabbed bits off the screen fast */
240         XClearWindow (pBumps->pDisplay, pBumps->Win);
241         XSync (pBumps->pDisplay, 0);
242
243         pBumps->aBumpMap = calloc( pBumps->iWinWidth * pBumps->iWinHeight, sizeof(uint16_) );
244         for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
245         {
246                 for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
247                         aColors[ iWidth ].pixel = XGetPixel( pScreenImage, iWidth, iHeight );
248
249                 XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
250         
251                 if( bInvert )
252                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
253                                 pBumps->aBumpMap[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = (uint16_)
254                                         ( ( aColors[ iWidth ].red + aColors[ iWidth ].green + aColors[ iWidth ].blue ) / ( 0x2FFFD / (double)pBumps->SpotLight.nDiameter ) );
255                 else
256                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
257                                 pBumps->aBumpMap[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = (uint16_)
258                                         ( pBumps->SpotLight.nDiameter - ( ( aColors[ iWidth ].red + aColors[ iWidth ].green + aColors[ iWidth ].blue ) / ( 0x2FFFD / (double)pBumps->SpotLight.nDiameter ) ) );
259         }
260
261         XDestroyImage( pScreenImage );
262
263         nSoften = get_integer_resource( "soften", "Integer" );
264 #ifdef VERBOSE
265         if( nSoften )   printf( "%s: Softening Bump Map %d time(s)...\n", progclass, nSoften );
266 #endif
267         while( nSoften-- )
268                 SoftenBumpMap( pBumps );
269 }
270
271
272 void SoftenBumpMap( SBumps *pBumps )
273 {
274         uint16_ *pOffset;
275         uint16_ nHeight;
276         uint16_ iWidth, iHeight;
277         uint16_ *pTempBuffer = calloc( pBumps->iWinWidth * pBumps->iWinHeight, sizeof(uint16_) );
278
279         for( iHeight=1; iHeight<pBumps->iWinHeight-1; iHeight++ )
280         {
281                 pOffset = pBumps->aBumpMap + ( iHeight * pBumps->iWinWidth );
282                 for( iWidth=1; iWidth<pBumps->iWinWidth-1; iWidth++ )
283                 {       
284                         nHeight = 0;
285                         nHeight += pOffset[ iWidth ];
286                         nHeight += pOffset[ iWidth - pBumps->iWinWidth ];
287                         nHeight += pOffset[ iWidth + 1 ];
288                         nHeight += pOffset[ iWidth + pBumps->iWinWidth ];
289                         nHeight += pOffset[ iWidth - 1 ];
290                         nHeight /= 5;
291                         pTempBuffer[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = nHeight;
292                 }
293         }                                               
294         
295         memcpy( pBumps->aBumpMap, pTempBuffer, pBumps->iWinWidth * pBumps->iWinHeight * 2 );
296         free( pTempBuffer );
297 }
298
299
300 void Execute( SBumps *pBumps )
301 {
302         uint16_ nLightXPos, nLightYPos;
303         uint16_ iWidth, iHeight;
304         uint16_ iLightWidth, iLightHeight;
305         uint16_ *pBOffset;
306         uint8_ *pLOffset;
307         int16_ nX, nY;
308         uint16_ nColor;
309         CalcLightPos( &pBumps->SpotLight, &nLightXPos, &nLightYPos );
310
311         for( iHeight=nLightYPos, iLightHeight=0; iLightHeight<pBumps->SpotLight.nDiameter; iHeight++, iLightHeight++ )
312         {
313                 pBOffset = pBumps->aBumpMap + ( iHeight * pBumps->iWinWidth );
314                 pLOffset = pBumps->SpotLight.aLightMap + ( iLightHeight * pBumps->SpotLight.nDiameter );
315                 for( iWidth=nLightXPos, iLightWidth=0; iLightWidth<pBumps->SpotLight.nDiameter; iWidth++, iLightWidth++ )
316                 {
317                         if( pLOffset[ iLightWidth ] )
318                         {                               
319                                 nX = pBOffset[ iWidth + 1                 ] - pBOffset[ iWidth ] + iLightWidth;
320                                 nY = pBOffset[ iWidth + pBumps->iWinWidth ] - pBOffset[ iWidth ] + iLightHeight;
321
322                                 if( nX < 0 )                                    nX = 0;
323                                 else if( nX >= pBumps->SpotLight.nDiameter )    nX = pBumps->SpotLight.nDiameter - 1;
324
325                                 if( nY < 0 )                                    nY = 0;
326                                 else if( nY >= pBumps->SpotLight.nDiameter )    nY = pBumps->SpotLight.nDiameter - 1;
327
328                                 nColor = pBumps->SpotLight.aLightMap[ ( nY * pBumps->SpotLight.nDiameter ) + nX ];
329                                 if( nColor >= pBumps->nColorCount )
330                                         nColor = 1;
331
332                                 if( pLOffset[ iLightWidth ] >= pBumps->nColorCount )
333                                         if( nColor > pLOffset[ iLightWidth ] - pBumps->nColorCount )
334                                                 nColor = pLOffset[ iLightWidth ] - pBumps->nColorCount;
335                                                 
336                                 XPutPixel( pBumps->pXImage, iWidth, iHeight, pBumps->aXColors[ nColor ].pixel );
337                         }
338                         else
339                                 XPutPixel( pBumps->pXImage, iWidth, iHeight, pBumps->aXColors[ 0 ].pixel );
340                 }
341         }       
342
343         XPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, nLightXPos, nLightYPos, nLightXPos, nLightYPos, pBumps->SpotLight.nDiameter, pBumps->SpotLight.nDiameter );
344         XSync( pBumps->pDisplay, False );
345 }
346
347
348 void DestroyBumps( SBumps *pBumps )
349 {
350         DestroySpotLight( &pBumps->SpotLight );
351         free( pBumps->aXColors );
352         free( pBumps->aBumpMap );
353         XDestroyImage( pBumps->pXImage );
354 }
355
356
357 /* All messages to the screensaver are processed here. */
358 void screenhack( Display *pDisplay, Window Win )
359 {
360         SBumps Bumps;
361         uint32_ iDelay;
362 #ifdef VERBOSE
363         time_t Time = time( NULL );
364         uint16_ iFrame = 0;
365 #endif  /*  VERBOSE */
366         
367         CreateBumps( &Bumps, pDisplay, Win );
368         iDelay = get_integer_resource( "delay", "Integer" );
369
370         while( 1 )
371         {
372                 screenhack_handle_events( pDisplay );
373                 Execute( &Bumps );
374                 usleep( iDelay );
375
376 #ifdef VERBOSE
377                 iFrame++;
378                 if( Time - time( NULL ) )
379                 {
380                         printf( "FPS: %d\n", iFrame );
381                         Time = time( NULL );
382                         iFrame = 0;
383                 }
384 #endif  /*  VERBOSE */
385         }
386
387         DestroyBumps( &Bumps );
388 }
389
390  
391 /*
392  * End of Module: "Bumps.cpp"
393  */
394
395 /* vim: ts=4
396  */