http://ftp.x.org/contrib/applications/xscreensaver-3.18.tar.gz
[xscreensaver] / hacks / bumps.c
diff --git a/hacks/bumps.c b/hacks/bumps.c
new file mode 100644 (file)
index 0000000..b1dda08
--- /dev/null
@@ -0,0 +1,396 @@
+/* Bumps, Copyright (c) 1999 Shane Smit <blackend@inconnect.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Module: "Bumps.cpp"
+ * Tab Size: 4
+ *
+ * Description:
+ *  This is typical bump-mapping.  The actual bump map is generated by a screen
+ *  grab.  The light source is represented by a spotlight of random color. This
+ *  spotlight randomly traverses the bump map in a sinus pattern.
+ *
+ *  Essentially, it 3D-izes your desktop, based on color intensity.
+ *
+ * Modification History:
+ *  [10/01/99] - Shane Smit: Creation
+ *  [10/08/99] - Shane Smit: Port to C. (Ick)
+ */
+
+
+#include "bumps.h"
+
+
+void CreateSpotLight( SSpotLight *pSpotLight, uint16_ iWinWidth, uint16_ nColorCount )
+{
+       double nDelta;
+       int16_ iHeight, iWidth;
+       
+       pSpotLight->nDiameter = iWinWidth / 3;
+#ifdef VERBOSE
+       printf( "%s: Light Diameter: %d\n", progclass, pSpotLight->nDiameter );
+#endif
+
+       pSpotLight->aLightMap = calloc( pSpotLight->nDiameter * pSpotLight->nDiameter, sizeof(uint8_) );
+       memset( pSpotLight->aLightMap, 0, pSpotLight->nDiameter * pSpotLight->nDiameter );
+
+       /* The falloff max values... 3/4 of the entire lightmap. */
+       pSpotLight->nRadius = (uint16_)(pSpotLight->nDiameter / 2.5F);
+       
+       for( iHeight=-pSpotLight->nRadius; iHeight<pSpotLight->nRadius; iHeight++ )
+               for( iWidth=-pSpotLight->nRadius; iWidth<pSpotLight->nRadius; iWidth++ )
+               {
+                       nDelta = ( nColorCount * 2.5F ) - ( ( sqrt( pow( iWidth+0.5F, 2 ) + pow( iHeight+0.5F, 2 ) ) / pSpotLight->nRadius ) * ( nColorCount * 2.5F ) );
+                       nDelta += nColorCount;
+                       if( nDelta >= ( nColorCount * 2 ) ) nDelta = ( nColorCount * 2 ) - 1;
+                       if( nDelta >= nColorCount )
+                               pSpotLight->aLightMap[ ( ( iHeight + (pSpotLight->nDiameter/2) ) * pSpotLight->nDiameter ) + iWidth + (pSpotLight->nDiameter/2) ] = (uint8_)nDelta;
+               }
+
+       /* The actual lightmap... 1/2 of the entire lightmap. */
+       pSpotLight->nRadius = pSpotLight->nDiameter / 4;
+
+       for( iHeight=-pSpotLight->nRadius; iHeight<pSpotLight->nRadius; iHeight++ )
+               for( iWidth=-pSpotLight->nRadius; iWidth<pSpotLight->nRadius; iWidth++ )
+               {
+                       nDelta = nColorCount - ( ( sqrt( pow( iWidth+0.5F, 2 ) + pow( iHeight+0.5F, 2 ) ) / pSpotLight->nRadius ) * ( nColorCount - 1 ) );
+                       if( nDelta >= 1 )
+                               pSpotLight->aLightMap[ ( ( iHeight + (pSpotLight->nDiameter/2) ) * pSpotLight->nDiameter ) + iWidth + (pSpotLight->nDiameter/2) ] = (uint8_)nDelta;
+               }
+               
+       CreateTables( pSpotLight );
+
+       pSpotLight->nRadius = pSpotLight->nDiameter / 2;        /* Now set the radius back to what it should be. */
+       pSpotLight->nAngleX = RANDOM() % pSpotLight->nDegreeCount;
+       pSpotLight->nAngleY = RANDOM() % pSpotLight->nDegreeCount;
+       pSpotLight->nVelocityX = ( RANDOM() % 3 ) + 1;
+       pSpotLight->nVelocityY = ( RANDOM() % 3 ) + 1;
+}
+
+
+void CreateTables( SSpotLight *pSpotLight )
+{
+       long double nUnit;
+       uint16_ iDegree;
+       
+       pSpotLight->nDegreeCount = get_integer_resource( "degrees", "Integer" );
+       if( pSpotLight->nDegreeCount < 180 ) pSpotLight->nDegreeCount = 180;    /* Less than 180 will show trails in higher resolutions. */
+#ifdef VERBOSE
+       printf( "%s: Using a %d degree circle.\n", progclass, pSpotLight->nDegreeCount );
+#endif
+
+       pSpotLight->aSinTable = calloc( pSpotLight->nDegreeCount, sizeof(double) );
+
+       /* This funtion builds the Sine Lookup Tables. */
+       nUnit = (long double)( PI * 2.0F ) / (long double)( pSpotLight->nDegreeCount );
+
+       for( iDegree=0; iDegree<pSpotLight->nDegreeCount; iDegree++)
+               pSpotLight->aSinTable[ iDegree ] = sin( nUnit * (long double)iDegree );
+}
+
+
+void CalcLightPos( SSpotLight *pSpotLight, uint16_ *pXPos, uint16_ *pYPos )
+{
+       pSpotLight->nVelocityX += ( RANDOM() % 2 ) ? 0.05F : -0.05F;
+       if( pSpotLight->nVelocityX < 1 )                pSpotLight->nVelocityX = 1;
+       else if( pSpotLight->nVelocityX > 3 )   pSpotLight->nVelocityX = 3;
+
+       pSpotLight->nVelocityX += ( RANDOM() % 2 ) ? 0.05F : -0.05F;
+       if( pSpotLight->nVelocityY < 1 )                pSpotLight->nVelocityX = 1;
+       else if( pSpotLight->nVelocityY > 3 )   pSpotLight->nVelocityX = 3;
+       
+    pSpotLight->nAngleX += pSpotLight->nVelocityX;
+    if( pSpotLight->nAngleX >= pSpotLight->nDegreeCount )      pSpotLight->nAngleX -= pSpotLight->nDegreeCount;
+
+    pSpotLight->nAngleY += pSpotLight->nVelocityY;
+    if( pSpotLight->nAngleY >= pSpotLight->nDegreeCount )      pSpotLight->nAngleY -= pSpotLight->nDegreeCount;
+
+    *pXPos = (uint16_)( ( pSpotLight->iWinXCenter - pSpotLight->nRadius ) * pSpotLight->aSinTable[ (uint16_)pSpotLight->nAngleX ] ) + pSpotLight->iWinXCenter;
+    *pYPos = (uint16_)( ( pSpotLight->iWinYCenter - pSpotLight->nRadius ) * pSpotLight->aSinTable[ (uint16_)pSpotLight->nAngleY ] ) + pSpotLight->iWinYCenter;
+
+       /* Offset to upper left hand corner. */
+       *pXPos -= pSpotLight->nRadius;
+       *pYPos -= pSpotLight->nRadius;
+}
+
+
+void CreateBumps( SBumps *pBumps, Display *pNewDisplay, Window NewWin )
+{
+       XWindowAttributes XWinAttribs;
+       XGCValues GCValues;
+       int32_ nGCFlags;
+       uint16_ iWidth, iHeight;
+
+       XGetWindowAttributes( pNewDisplay, NewWin, &XWinAttribs );
+       pBumps->iWinWidth = XWinAttribs.width;
+       pBumps->iWinHeight = XWinAttribs.height;
+       pBumps->SpotLight.iWinXCenter = XWinAttribs.width / 2;
+       pBumps->SpotLight.iWinYCenter = XWinAttribs.height / 2;
+       pBumps->pDisplay = pNewDisplay;
+       pBumps->Win = NewWin;
+
+       pBumps->pXImage = XCreateImage( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
+               pBumps->iWinWidth, pBumps->iWinHeight, BitmapPad( pBumps->pDisplay ), 0 );
+       pBumps->pXImage->data = calloc( pBumps->pXImage->bytes_per_line * pBumps->pXImage->height, sizeof(int8_) );
+
+       GCValues.function = GXcopy;
+       GCValues.subwindow_mode = IncludeInferiors;
+       nGCFlags = GCForeground | GCFunction;
+       if( use_subwindow_mode_p( XWinAttribs.screen, pBumps->Win ) ) /* See grabscreen.c */
+               nGCFlags |= GCSubwindowMode;
+       pBumps->GraphicsContext = XCreateGC( pBumps->pDisplay, pBumps->Win, nGCFlags, &GCValues );
+       
+       SetPalette( pBumps, &XWinAttribs );
+       CreateSpotLight( &pBumps->SpotLight, pBumps->iWinWidth, pBumps->nColorCount );
+       InitBumpMap( pBumps, &XWinAttribs );
+
+       /* Clear the image. */
+  if (pBumps->aXColors[ 0 ].pixel == 0)
+    memset (pBumps->pXImage->data, 0,
+            pBumps->pXImage->bytes_per_line * pBumps->pXImage->height);
+  else
+    for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
+      for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
+        XPutPixel( pBumps->pXImage, iWidth, iHeight,
+                   pBumps->aXColors[ 0 ].pixel );
+  XSetWindowBackground( pBumps->pDisplay, pBumps->Win,
+                        pBumps->aXColors[ 0 ].pixel );
+  XClearWindow (pBumps->pDisplay, pBumps->Win);
+}
+
+
+void SetPalette( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
+{
+       XColor Color;
+       char *sColor;                   /* Spotlight Color */
+       int16_ iColor;
+       uint32_ *aPixels;
+       
+       sColor = get_string_resource( "color", "Color" );
+
+       Color.red = RANDOM() % 0xFFFF; 
+       Color.green = RANDOM() % 0xFFFF;
+       Color.blue = RANDOM() % 0xFFFF;
+
+       if( strcasecmp( sColor, "random" ) && !XParseColor( pBumps->pDisplay, pXWinAttribs->colormap, sColor, &Color ) )
+               fprintf( stderr, "%s: color %s not found in database. Choosing random...\n", progname, sColor );
+
+#ifdef VERBOSE
+       printf( "%s: Spotlight color is <%d,%d,%d> RGB.\n", progclass, Color.red, Color.green, Color.blue );
+#endif  /*  VERBOSE */
+
+       pBumps->nColorCount = get_integer_resource( "colorcount", "Integer" );
+       if( pBumps->nColorCount < 2 )   pBumps->nColorCount = 2;
+       if( pBumps->nColorCount > 128 ) pBumps->nColorCount = 128;
+
+       pBumps->aXColors = calloc( pBumps->nColorCount, sizeof(XColor ) );
+               aPixels  = calloc( pBumps->nColorCount, sizeof(uint32_) );
+
+       /* Creates a phong shade:                 / SpotColor  \                               Index/ColorCount 
+        *                                                      PhongShade = | ------------ | Index + ( 65535 - SpotColor )^ 
+        *                                                                                \ ColorCount /                                                                                                */
+       pBumps->nColorCount--;
+       for( iColor=0; iColor<=pBumps->nColorCount; iColor++ )
+       {
+               pBumps->aXColors[ iColor ].red   = (uint16_)( ( ( Color.red   / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.red,   iColor/(double)pBumps->nColorCount ) );
+               pBumps->aXColors[ iColor ].green = (uint16_)( ( ( Color.green / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.green, iColor/(double)pBumps->nColorCount ) );
+               pBumps->aXColors[ iColor ].blue  = (uint16_)( ( ( Color.blue  / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - Color.blue,  iColor/(double)pBumps->nColorCount ) );
+
+               if( !XAllocColor( pBumps->pDisplay, pXWinAttribs->colormap, &pBumps->aXColors[ iColor ] ) )
+               {
+                       XFreeColors( pBumps->pDisplay, pXWinAttribs->colormap, aPixels, iColor, 0 );
+                       free( pBumps->aXColors );
+                       free(         aPixels );
+                       pBumps->nColorCount--;
+                       pBumps->aXColors = calloc( pBumps->nColorCount, sizeof(XColor) );
+                       aPixels  = calloc( pBumps->nColorCount, sizeof(uint32_) );
+                       iColor = -1;
+               }
+               else
+                       aPixels[ iColor ] = pBumps->aXColors[ iColor ].pixel;
+       }
+       pBumps->nColorCount++;
+
+#ifdef VERBOSE
+       printf( "%s: Allocated %d colors.\n", progclass, pBumps->nColorCount );
+#endif  /*  VERBOSE */
+
+       XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aXColors[ 0 ].pixel );
+}
+
+
+void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
+{
+       XImage *pScreenImage;
+       XColor aColors[ pBumps->iWinWidth ];
+       uint8_ nSoften;
+       uint16_ iWidth, iHeight;
+       BOOL bInvert = (BOOL)get_boolean_resource( "invert", "Boolean" );
+       
+       grab_screen_image( pXWinAttribs->screen, pBumps->Win );
+       pScreenImage = XGetImage( pBumps->pDisplay, pBumps->Win, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap );
+
+        /* jwz: get the grabbed bits off the screen fast */
+        XClearWindow (pBumps->pDisplay, pBumps->Win);
+        XSync (pBumps->pDisplay, 0);
+
+       pBumps->aBumpMap = calloc( pBumps->iWinWidth * pBumps->iWinHeight, sizeof(uint16_) );
+       for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
+       {
+               for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
+                       aColors[ iWidth ].pixel = XGetPixel( pScreenImage, iWidth, iHeight );
+
+               XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
+       
+               if( bInvert )
+                       for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
+                               pBumps->aBumpMap[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = (uint16_)
+                                       ( ( aColors[ iWidth ].red + aColors[ iWidth ].green + aColors[ iWidth ].blue ) / ( 0x2FFFD / (double)pBumps->SpotLight.nDiameter ) );
+               else
+                       for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
+                               pBumps->aBumpMap[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = (uint16_)
+                                       ( pBumps->SpotLight.nDiameter - ( ( aColors[ iWidth ].red + aColors[ iWidth ].green + aColors[ iWidth ].blue ) / ( 0x2FFFD / (double)pBumps->SpotLight.nDiameter ) ) );
+       }
+
+       XDestroyImage( pScreenImage );
+
+       nSoften = get_integer_resource( "soften", "Integer" );
+#ifdef VERBOSE
+       if( nSoften )   printf( "%s: Softening Bump Map %d time(s)...\n", progclass, nSoften );
+#endif
+       while( nSoften-- )
+               SoftenBumpMap( pBumps );
+}
+
+
+void SoftenBumpMap( SBumps *pBumps )
+{
+       uint16_ *pOffset;
+       uint16_ nHeight;
+       uint16_ iWidth, iHeight;
+       uint16_ *pTempBuffer = calloc( pBumps->iWinWidth * pBumps->iWinHeight, sizeof(uint16_) );
+
+       for( iHeight=1; iHeight<pBumps->iWinHeight-1; iHeight++ )
+       {
+               pOffset = pBumps->aBumpMap + ( iHeight * pBumps->iWinWidth );
+               for( iWidth=1; iWidth<pBumps->iWinWidth-1; iWidth++ )
+               {       
+                       nHeight = 0;
+                       nHeight += pOffset[ iWidth ];
+                       nHeight += pOffset[ iWidth - pBumps->iWinWidth ];
+                       nHeight += pOffset[ iWidth + 1 ];
+                       nHeight += pOffset[ iWidth + pBumps->iWinWidth ];
+                       nHeight += pOffset[ iWidth - 1 ];
+                       nHeight /= 5;
+                       pTempBuffer[ ( iHeight * pBumps->iWinWidth ) + iWidth ] = nHeight;
+               }
+       }                                               
+       
+       memcpy( pBumps->aBumpMap, pTempBuffer, pBumps->iWinWidth * pBumps->iWinHeight * 2 );
+       free( pTempBuffer );
+}
+
+
+void Execute( SBumps *pBumps )
+{
+       uint16_ nLightXPos, nLightYPos;
+       uint16_ iWidth, iHeight;
+       uint16_ iLightWidth, iLightHeight;
+       uint16_ *pBOffset;
+       uint8_ *pLOffset;
+       int16_ nX, nY;
+       uint16_ nColor;
+       CalcLightPos( &pBumps->SpotLight, &nLightXPos, &nLightYPos );
+
+       for( iHeight=nLightYPos, iLightHeight=0; iLightHeight<pBumps->SpotLight.nDiameter; iHeight++, iLightHeight++ )
+       {
+               pBOffset = pBumps->aBumpMap + ( iHeight * pBumps->iWinWidth );
+               pLOffset = pBumps->SpotLight.aLightMap + ( iLightHeight * pBumps->SpotLight.nDiameter );
+               for( iWidth=nLightXPos, iLightWidth=0; iLightWidth<pBumps->SpotLight.nDiameter; iWidth++, iLightWidth++ )
+               {
+                       if( pLOffset[ iLightWidth ] )
+                       {                               
+                               nX = pBOffset[ iWidth + 1                 ] - pBOffset[ iWidth ] + iLightWidth;
+                               nY = pBOffset[ iWidth + pBumps->iWinWidth ] - pBOffset[ iWidth ] + iLightHeight;
+
+                               if( nX < 0 )                                    nX = 0;
+                               else if( nX >= pBumps->SpotLight.nDiameter )    nX = pBumps->SpotLight.nDiameter - 1;
+
+                               if( nY < 0 )                                    nY = 0;
+                               else if( nY >= pBumps->SpotLight.nDiameter )    nY = pBumps->SpotLight.nDiameter - 1;
+
+                               nColor = pBumps->SpotLight.aLightMap[ ( nY * pBumps->SpotLight.nDiameter ) + nX ];
+                               if( nColor >= pBumps->nColorCount )
+                                       nColor = 1;
+
+                               if( pLOffset[ iLightWidth ] >= pBumps->nColorCount )
+                                       if( nColor > pLOffset[ iLightWidth ] - pBumps->nColorCount )
+                                               nColor = pLOffset[ iLightWidth ] - pBumps->nColorCount;
+                                               
+                               XPutPixel( pBumps->pXImage, iWidth, iHeight, pBumps->aXColors[ nColor ].pixel );
+                       }
+                       else
+                               XPutPixel( pBumps->pXImage, iWidth, iHeight, pBumps->aXColors[ 0 ].pixel );
+               }
+       }       
+
+       XPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, nLightXPos, nLightYPos, nLightXPos, nLightYPos, pBumps->SpotLight.nDiameter, pBumps->SpotLight.nDiameter );
+       XSync( pBumps->pDisplay, False );
+}
+
+
+void DestroyBumps( SBumps *pBumps )
+{
+       DestroySpotLight( &pBumps->SpotLight );
+       free( pBumps->aXColors );
+       free( pBumps->aBumpMap );
+       XDestroyImage( pBumps->pXImage );
+}
+
+
+/* All messages to the screensaver are processed here. */
+void screenhack( Display *pDisplay, Window Win )
+{
+       SBumps Bumps;
+       uint32_ iDelay;
+#ifdef VERBOSE
+       time_t Time = time( NULL );
+       uint16_ iFrame = 0;
+#endif  /*  VERBOSE */
+       
+       CreateBumps( &Bumps, pDisplay, Win );
+       iDelay = get_integer_resource( "delay", "Integer" );
+
+       while( 1 )
+       {
+               screenhack_handle_events( pDisplay );
+               Execute( &Bumps );
+               usleep( iDelay );
+
+#ifdef VERBOSE
+               iFrame++;
+               if( Time - time( NULL ) )
+               {
+                       printf( "FPS: %d\n", iFrame );
+                       Time = time( NULL );
+                       iFrame = 0;
+               }
+#endif  /*  VERBOSE */
+       }
+
+       DestroyBumps( &Bumps );
+}
+
+/*
+ * End of Module: "Bumps.cpp"
+ */
+
+/* vim: ts=4
+ */