1 /* -*- mode: C; tab-width: 4 -*-
2 * Bumps, Copyright (c) 2002 Shane Smit <CodeWeaver@DigitalLoom.org>
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
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.
20 * Essentially, it 3D-izes your desktop, based on color intensity.
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.
33 /* This function pointer will point to the appropriate PutPixel*() function below. */
34 void (*MyPutPixel)( int8_ *, uint32_ );
36 void PutPixel32( int8_ *pData, uint32_ pixel )
38 *(uint32_ *)pData = pixel;
41 void PutPixel24( int8_ *pData, uint32_ pixel )
43 pData[ 2 ] = ( pixel & 0x00FF0000 ) >> 16;
44 pData[ 1 ] = ( pixel & 0x0000FF00 ) >> 8;
45 pData[ 0 ] = ( pixel & 0x000000FF );
48 void PutPixel16( int8_ *pData, uint32_ pixel )
50 *(uint16_ *)pData = (uint16_)pixel;
53 void PutPixel8( int8_ *pData, uint32_ pixel )
55 *(uint8_ *)pData = (uint8_)pixel;
58 /* Creates the light map, which is a circular image... going from black around the edges
59 * to white in the center. */
60 void CreateSpotLight( SSpotLight *pSpotLight, uint16_ iDiameter, uint16_ nColorCount )
63 int16_ iDistX, iDistY;
66 pSpotLight->nFalloffDiameter = iDiameter;
67 pSpotLight->nFalloffRadius = pSpotLight->nFalloffDiameter / 2;
68 pSpotLight->nLightDiameter = iDiameter / 2;
69 pSpotLight->nLightRadius = pSpotLight->nLightDiameter / 2;
71 printf( "%s: Falloff Diameter: %d\n", progclass, pSpotLight->nFalloffDiameter );
72 printf( "%s: Spot Light Diameter: %d\n", progclass, pSpotLight->nLightDiameter );
75 pSpotLight->aLightMap = malloc( pSpotLight->nLightDiameter * pSpotLight->nLightDiameter * sizeof(uint8_) );
77 pLOffset = pSpotLight->aLightMap;
78 for( iDistY=-pSpotLight->nLightRadius; iDistY<pSpotLight->nLightRadius; ++iDistY )
80 for( iDistX=-pSpotLight->nLightRadius; iDistX<pSpotLight->nLightRadius; ++iDistX )
82 nDist = sqrt( pow( iDistX+0.5F, 2 ) + pow( iDistY+0.5F, 2 ) );
83 if( nDist / pSpotLight->nLightRadius <= 1.0f )
84 *pLOffset = (uint8_)(nColorCount - ( ( nDist / pSpotLight->nLightRadius ) * ( nColorCount - 1 ) ));
92 /* Initialize movement variables. */
93 pSpotLight->nAccelX = 0;
94 pSpotLight->nAccelY = 0;
95 pSpotLight->nVelocityX = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
96 pSpotLight->nVelocityY = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
100 /* Calculates the position of the spot light on the screen. */
101 void CalcLightPos( SBumps *pBumps )
103 SSpotLight *pSpotLight = &pBumps->SpotLight;
107 if( pSpotLight->nXPos < pSpotLight->nFalloffRadius ) nGravity = 1.0f;
108 else if( pSpotLight->nXPos > pBumps->iWinWidth - pSpotLight->nFalloffRadius ) nGravity = -1.0f;
109 else nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
111 pSpotLight->nAccelX += nGravity * ( pSpotLight->nAccelMax / 5.0f );
112 if( pSpotLight->nAccelX < -pSpotLight->nAccelMax ) pSpotLight->nAccelX = -pSpotLight->nAccelMax;
113 else if( pSpotLight->nAccelX > pSpotLight->nAccelMax ) pSpotLight->nAccelX = pSpotLight->nAccelMax;
115 pSpotLight->nVelocityX += pSpotLight->nAccelX;
116 if( pSpotLight->nVelocityX < -pSpotLight->nVelocityMax ) pSpotLight->nVelocityX = -pSpotLight->nVelocityMax;
117 else if( pSpotLight->nVelocityX > pSpotLight->nVelocityMax ) pSpotLight->nVelocityX = pSpotLight->nVelocityMax;
119 pSpotLight->nXPos += pSpotLight->nVelocityX;
122 if( pSpotLight->nYPos < pSpotLight->nFalloffRadius ) nGravity = 1.0f;
123 else if( pSpotLight->nYPos > pBumps->iWinHeight - pSpotLight->nFalloffRadius ) nGravity = -1.0f;
124 else nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
126 pSpotLight->nAccelY += nGravity * ( pSpotLight->nAccelMax / 5.0f );
127 if( pSpotLight->nAccelY < -pSpotLight->nAccelMax ) pSpotLight->nAccelY = -pSpotLight->nAccelMax;
128 else if( pSpotLight->nAccelY > pSpotLight->nAccelMax ) pSpotLight->nAccelY = pSpotLight->nAccelMax;
130 pSpotLight->nVelocityY += pSpotLight->nAccelY;
131 if( pSpotLight->nVelocityY < -pSpotLight->nVelocityMax ) pSpotLight->nVelocityY = -pSpotLight->nVelocityMax;
132 else if( pSpotLight->nVelocityY > pSpotLight->nVelocityMax ) pSpotLight->nVelocityY = pSpotLight->nVelocityMax;
134 pSpotLight->nYPos += pSpotLight->nVelocityY;
138 /* Main initialization function. */
139 void CreateBumps( SBumps *pBumps, Display *pNewDisplay, Window NewWin )
141 XWindowAttributes XWinAttribs;
146 /* Make size and velocity a function of window size, so it appears the same at 100x60 as it does in 3200x1200. */
147 XGetWindowAttributes( pNewDisplay, NewWin, &XWinAttribs );
148 pBumps->iWinWidth = XWinAttribs.width;
149 pBumps->iWinHeight = XWinAttribs.height;
150 pBumps->SpotLight.nXPos = XWinAttribs.width / 2.0f;
151 pBumps->SpotLight.nYPos = XWinAttribs.height / 2.0f;
152 pBumps->SpotLight.nVelocityMax = ( ( XWinAttribs.width < XWinAttribs.height ) ? XWinAttribs.width : XWinAttribs.height ) / 140.0f;
153 pBumps->SpotLight.nAccelMax = pBumps->SpotLight.nVelocityMax / 10.0f;
154 pBumps->pDisplay = pNewDisplay;
155 pBumps->Win = NewWin;
156 pBumps->pXImage = NULL;
158 iDiameter = ( ( pBumps->iWinWidth < pBumps->iWinHeight ) ? pBumps->iWinWidth : pBumps->iWinHeight ) / 2;
160 #ifdef HAVE_XSHM_EXTENSION
161 pBumps->bUseShm = get_boolean_resource( "useSHM", "Boolean" );
163 if( pBumps->bUseShm )
165 pBumps->pXImage = create_xshm_image( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth,
166 ZPixmap, NULL, &pBumps->XShmInfo, iDiameter, iDiameter );
167 if( !pBumps->pXImage )
169 fprintf( stderr, "%s: Unable to create XShmImage.\n", progname );
170 pBumps->bUseShm = False;
173 #endif /* HAVE_XSHM_EXTENSION */
174 if( !pBumps->pXImage )
176 pBumps->pXImage = XCreateImage( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth,
177 ZPixmap, 0, NULL, iDiameter, iDiameter, BitmapPad( pBumps->pDisplay ), 0 );
178 pBumps->pXImage->data = malloc( pBumps->pXImage->bytes_per_line * pBumps->pXImage->height * sizeof(int8_) );
181 /* For speed, access the XImage data directly using my own PutPixel routine. */
182 switch( pBumps->pXImage->bits_per_pixel )
185 pBumps->bytesPerPixel = 4;
186 MyPutPixel = PutPixel32;
190 pBumps->bytesPerPixel = 3;
191 MyPutPixel = PutPixel24;
195 pBumps->bytesPerPixel = 2;
196 MyPutPixel = PutPixel16;
200 pBumps->bytesPerPixel = 1;
201 MyPutPixel = PutPixel8;
205 fprintf( stderr, "%s: Unknown XImage depth.", progname );
206 #ifdef HAVE_XSHM_EXTENSION
207 if( pBumps->bUseShm )
208 destroy_xshm_image( pBumps->pDisplay, pBumps->pXImage, &pBumps->XShmInfo );
210 #endif /* HAVE_XSHM_EXTENSION */
211 XDestroyImage( pBumps->pXImage );
215 GCValues.function = GXcopy;
216 GCValues.subwindow_mode = IncludeInferiors;
217 nGCFlags = GCForeground | GCFunction;
218 if( use_subwindow_mode_p( XWinAttribs.screen, pBumps->Win ) ) /* See grabscreen.c */
219 nGCFlags |= GCSubwindowMode;
220 pBumps->GraphicsContext = XCreateGC( pBumps->pDisplay, pBumps->Win, nGCFlags, &GCValues );
222 SetPalette( pBumps, &XWinAttribs );
223 CreateSpotLight( &pBumps->SpotLight, iDiameter, pBumps->nColorCount );
224 InitBumpMap( pBumps, &XWinAttribs );
226 XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aColors[ 0 ] );
227 XClearWindow (pBumps->pDisplay, pBumps->Win);
231 /* Creates a specialized phong shade palette. */
232 void SetPalette( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
236 char *sColor; /* Spotlight Color */
239 sColor = get_string_resource( "color", "Color" );
241 BaseColor.red = RANDOM() % 0xFFFF;
242 BaseColor.green = RANDOM() % 0xFFFF;
243 BaseColor.blue = RANDOM() % 0xFFFF;
245 /* Make one color full intesity to avoid dark spotlights. */
246 switch( RANDOM() % 3 )
248 case 0: BaseColor.red = 0xFFFF; break;
249 case 1: BaseColor.green = 0xFFFF; break;
250 case 2: BaseColor.blue = 0xFFFF; break;
253 if( strcasecmp( sColor, "random" ) && !XParseColor( pBumps->pDisplay, pXWinAttribs->colormap, sColor, &BaseColor ) )
254 fprintf( stderr, "%s: color %s not found in database. Choosing random...\n", progname, sColor );
257 printf( "%s: Spotlight color is <%d,%d,%d> RGB.\n", progclass, BaseColor.red, BaseColor.green, BaseColor.blue );
260 pBumps->nColorCount = get_integer_resource( "colorcount", "Integer" );
261 if( pBumps->nColorCount < 2 ) pBumps->nColorCount = 2;
262 if( pBumps->nColorCount > 128 ) pBumps->nColorCount = 128;
264 pBumps->aColors = malloc( pBumps->nColorCount * sizeof(uint32_ ) );
266 /* Creates a phong shade: / BaseColor \ Index/ColorCount
267 * PhongShade = | ------------ | Index + ( 65535 - BaseColor )^
269 pBumps->nColorCount--;
270 for( iColor=0; iColor<=pBumps->nColorCount; iColor++ )
272 Color.red = (uint16_)( ( ( BaseColor.red / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.red, iColor/(double)pBumps->nColorCount ) );
273 Color.green = (uint16_)( ( ( BaseColor.green / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.green, iColor/(double)pBumps->nColorCount ) );
274 Color.blue = (uint16_)( ( ( BaseColor.blue / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.blue, iColor/(double)pBumps->nColorCount ) );
276 if( !XAllocColor( pBumps->pDisplay, pXWinAttribs->colormap, &Color ) )
278 XFreeColors( pBumps->pDisplay, pXWinAttribs->colormap, pBumps->aColors, iColor, 0 );
279 free( pBumps->aColors );
280 pBumps->aColors = malloc( pBumps->nColorCount * sizeof(uint32_) );
281 pBumps->nColorCount--;
285 pBumps->aColors[ iColor ] = Color.pixel;
287 pBumps->nColorCount++;
290 printf( "%s: Allocated %d colors.\n", progclass, pBumps->nColorCount );
293 XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aColors[ 0 ] );
297 /* Grabs the current contents of the window to use an intensity-based bump map. */
298 void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
300 XImage *pScreenImage;
301 XColor *aColors, *pColor;
303 uint16_ iWidth, iHeight;
307 double softenMultiplier = 1.0f;
308 BOOL bInvert = (BOOL)get_boolean_resource( "invert", "Boolean" );
311 aColors = (XColor*)malloc( pBumps->iWinWidth * sizeof(XColor) );
313 p = XCreatePixmap(pBumps->pDisplay, pBumps->Win,
314 pXWinAttribs->width, pXWinAttribs->height,
315 pXWinAttribs->depth);
316 load_random_image (pXWinAttribs->screen, pBumps->Win, p);
318 pScreenImage = XGetImage( pBumps->pDisplay, p, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap );
319 XFreePixmap (pBumps->pDisplay, p);
321 /* jwz: get the grabbed bits off the screen fast */
322 XClearWindow (pBumps->pDisplay, pBumps->Win);
323 XSync (pBumps->pDisplay, 0);
325 pBumps->aBumpMap = malloc( pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
327 nSoften = get_integer_resource( "soften", "Integer" );
329 softenMultiplier *= 1.0f + ( 1.0f / 3.0f ); /* Softening takes the max height down, so scale up to compensate. */
330 maxHeight = pBumps->SpotLight.nLightRadius * softenMultiplier;
331 nAverager = ( 3 * 0xFFFF ) / maxHeight;
333 pBump = pBumps->aBumpMap;
334 if( bInvert ) /* Funny, it's actually the 'else' that inverts the bump map... */
336 for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
339 for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
340 (pColor++)->pixel = XGetPixel( pScreenImage, iWidth, iHeight );
342 XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
345 for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pColor, ++pBump )
346 *pBump = ( ( pColor->red + pColor->green + pColor->blue ) / nAverager );
351 for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
354 for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
355 (pColor++)->pixel = XGetPixel( pScreenImage, iWidth, iHeight );
357 XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
360 for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pColor, ++pBump )
361 *pBump = ( maxHeight - ( ( pColor->red + pColor->green + pColor->blue ) / nAverager ) );
365 XDestroyImage( pScreenImage );
367 nSoften = get_integer_resource( "soften", "Integer" );
369 if( nSoften ) printf( "%s: Softening Bump Map %d time(s)...\n", progclass, nSoften );
372 SoftenBumpMap( pBumps );
377 /* Soften the bump map. This is to avoid pixellated-looking ridges.
378 * |-----|-----|-----|
379 * | 0% |12.5%| 0% | The adjacent pixels are averaged together
380 * |-----|-----|-----| first. Then than value is averaged with
381 * |12.5%| 50% |12.5%| the pixel is question. This essentially weights
382 * |-----|-----|-----| each pixel as shown on the left.
384 * |-----|-----|-----|
386 void SoftenBumpMap( SBumps *pBumps )
388 uint16_ *pOffset, *pTOffset;
390 uint32_ iWidth, iHeight;
391 uint16_ *aTempBuffer = malloc( pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
393 pOffset = pBumps->aBumpMap;
394 pTOffset = aTempBuffer;
395 for( iHeight=pBumps->iWinHeight; iHeight; --iHeight )
397 for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pOffset, ++pTOffset )
399 if( iHeight==pBumps->iWinHeight || iHeight==1 ||
400 iWidth==pBumps->iWinWidth || iWidth==1 )
406 nHeight = pOffset[ -pBumps->iWinWidth ];
407 nHeight += pOffset[ 1 ];
408 nHeight += pOffset[ pBumps->iWinWidth ];
409 nHeight += pOffset[ -1 ];
411 nHeight += pOffset[ 0 ];
417 memcpy( pBumps->aBumpMap, aTempBuffer, pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
422 /* This is where we slap down some pixels... */
423 void Execute( SBumps *pBumps )
425 int32_ nLightXPos, nLightYPos;
426 int32_ iScreenX, iScreenY;
427 int32_ iLightX, iLightY;
432 int32_ nLightOffsetFar = pBumps->SpotLight.nFalloffDiameter - pBumps->SpotLight.nLightRadius;
434 CalcLightPos( pBumps );
436 /* Offset to upper left hand corner. */
437 nLightXPos = pBumps->SpotLight.nXPos - pBumps->SpotLight.nFalloffRadius;
438 nLightYPos = pBumps->SpotLight.nYPos - pBumps->SpotLight.nFalloffRadius;
440 for( iScreenY=nLightYPos, iLightY=-pBumps->SpotLight.nLightRadius; iLightY<nLightOffsetFar; ++iScreenY, ++iLightY )
442 if( iScreenY < 0 ) continue;
443 else if( iScreenY >= pBumps->iWinHeight ) break;
445 pDOffset = &pBumps->pXImage->data[ (iLightY+pBumps->SpotLight.nLightRadius) * pBumps->pXImage->bytes_per_line ];
446 pBOffset = pBumps->aBumpMap + ( iScreenY * pBumps->iWinWidth ) + nLightXPos;
447 for( iScreenX=nLightXPos, iLightX=-pBumps->SpotLight.nLightRadius; iLightX<nLightOffsetFar; ++iScreenX, ++iLightX, ++pBOffset, pDOffset+=pBumps->bytesPerPixel )
449 if( iScreenX < 0 ) continue;
450 else if( iScreenX >= pBumps->iWinWidth ) break;
451 else if( iScreenY == 0 || iScreenY >= pBumps->iWinHeight-2 ||
452 iScreenX == 0 || iScreenX >= pBumps->iWinWidth-2 )
454 MyPutPixel( pDOffset, pBumps->aColors[ 0 ] );
458 /* That's right folks, all the magic of bump mapping occurs in these two lines. (kinda disappointing, isn't it?) */
459 nX = ( pBOffset[ 1 ] - pBOffset[ 0 ] ) + iLightX;
460 nY = ( pBOffset[ pBumps->iWinWidth ] - pBOffset[ 0 ] ) + iLightY;
462 if( nX<0 || nX>=pBumps->SpotLight.nLightDiameter
463 || nY<0 || nY>=pBumps->SpotLight.nLightDiameter )
465 MyPutPixel( pDOffset, pBumps->aColors[ 0 ] );
469 nColor = pBumps->SpotLight.aLightMap[ ( nY * pBumps->SpotLight.nLightDiameter ) + nX ];
470 MyPutPixel( pDOffset, pBumps->aColors[ nColor ] );
474 /* Allow the spotlight to go *slightly* off the screen by clipping the XImage. */
475 iLightX = iLightY = 0; /* Use these for XImages X and Y now. */
476 nX = nY = pBumps->SpotLight.nFalloffDiameter; /* Use these for XImage width and height now. */
479 iLightX = -nLightXPos;
483 else if( nLightXPos + nX >= pBumps->iWinWidth )
485 nX -= ( nLightXPos + nX ) - pBumps->iWinWidth;
490 iLightY = -nLightYPos;
494 else if( nLightYPos + nY >= pBumps->iWinHeight )
496 nY -= ( nLightYPos + nY ) - pBumps->iWinHeight;
499 #ifdef HAVE_XSHM_EXTENSION
500 if( pBumps->bUseShm )
501 XShmPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, iLightX, iLightY, nLightXPos, nLightYPos,
504 #endif /* HAVE_XSHM_EXTENSION */
505 XPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, iLightX, iLightY, nLightXPos, nLightYPos,
508 XSync( pBumps->pDisplay, False );
513 void DestroyBumps( SBumps *pBumps )
515 DestroySpotLight( &pBumps->SpotLight );
516 free( pBumps->aColors );
517 free( pBumps->aBumpMap );
518 #ifdef HAVE_XSHM_EXTENSION
519 if( pBumps->bUseShm )
520 destroy_xshm_image( pBumps->pDisplay, pBumps->pXImage, &pBumps->XShmInfo );
522 #endif /* HAVE_XSHM_EXTENSION */
523 XDestroyImage( pBumps->pXImage );
527 /* All messages to the screensaver are processed here. */
528 void screenhack( Display *pDisplay, Window Win )
533 time_t Time = time( NULL );
537 CreateBumps( &Bumps, pDisplay, Win );
538 iDelay = get_integer_resource( "delay", "Integer" );
542 screenhack_handle_events( pDisplay );
548 if( Time - time( NULL ) )
550 printf( "FPS: %d\n", iFrame );
557 DestroyBumps( &Bumps );