;                            The Great "Water Effect" Demo!!!
;                            By Tom Kenny
;
;
;  My first full assembler program, this little demo creates a rain-storm (or more 
;  accurately a light-drizzle :) on the user's desktop for a while.  It demonstrates 
;  the Windows GDI and Multi-threading. There is no program window. 
;
;  The droplet effect is done by creating a TILE_SIZE x TILE_SIZE bitmap at a random
;  location on the user's desktop. The wave is simulated by using two arrays of bytes
;  representing water depth (height maps).  The main program loop, creates several of 
;  these tiles at once thus simulating a rainstorm on the desktop.
;
;  The separate tiles are handled using an array of structures (WaterTileStruct) which
;  define the X and Y coordinates of each tile as well as maintaining pointers to their 
;  bitmaps and height maps.  Each tile runs on its' own thread of execution for 
;  NUM_FRAMES then dies on its own (no fuss, no muss).
;
;  This program was written on the MASM32 package (Version 6). 
;

.386 
.model flat,stdcall 
option casemap:none 
include \masm32\include\windows.inc 
include \masm32\include\user32.inc 
include \masm32\include\kernel32.inc 
include \masm32\include\gdi32.inc 
includelib \masm32\lib\user32.lib 
includelib \masm32\lib\kernel32.lib 
includelib \masm32\lib\gdi32.lib 


LPCOLORREF TYPEDEF PTR COLORREF;             Create some TypeDefs........
LPBYTE TYPEDEF PTR BYTE;
PBITMAPINFO  TYPEDEF   PTR   BITMAPINFO  

; Define the structure which keeps our WaterTiles...
WaterTileStruct STRUCT

  IsInUse DWORD 0                     ; Is this structure currently in use?
  DropDepth DWORD 0                   ; How deep is the droplet?
  OffSetX DWORD 0                     ; Offset from the left of the screen
  OffSetY DWORD 0                     ; Offset from the top of the screen
  CaptureBitmap HBITMAP 0             ; Holds the "background" on which the effect is drawn
  WaterBitmap HBITMAP 0               ; The finished frame of animation.
  CaptureBitmapBits LPCOLORREF 0      ; Points to the bits of CaptureBitmap
  WaterBitmapBits LPCOLORREF 0        ; Points to the bits of WaterBitmap
  HeightMapOne LPBYTE 0               ; Points to a frame of water height data
  HeightMapTwo LPBYTE 0               ; Points to the last frame of water height data
   
WaterTileStruct ends 

LPWaterTileStruct TYPEDEF PTR WaterTileStruct   ; typedef a pointer to a WaterTileStruct

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD 
;      
; Our "main" function most of the parameters aren't used, but I kept them for consistency with 
; standards.
;

CreateStorm proto WTStruct:LPWaterTileStruct, StormSize:DWORD, Elements:DWORD
;
; This is where the magic happens!  This function defines a loop which is responsible
; for creating our water tiles. First it searches the array of WaterTiles to find a free 
; structure that we can use to make a new tile.  If it finds one, it generates random
; x and y coordinates, and a random droplet depth.  Then it searches the existing tiles
; to make sure that the new tile's coordinates don't interfere with any already running 
; tiles.  provided there is no interference, the new tile's structure is filled out and
; its own thread of execution is created.  A short Sleep after each tile's creation makes
; for a steady stream of droplets.  Up to MAX_DROPS_AT_ONCE drops can be created at any time.
;

ThreadProc proto WTStruct:LPWaterTileStruct
;
; This is the callback we use when we call CreateThread to create a thread of execution for 
; a tile. All it does is call CreateWaterTile.  It takes a pointer to the tile's watertile 
; structure. 
; 

CreateWaterTile proto WTStruct:LPWaterTileStruct
;
; This function acts as a sort of parent for each tile.  It captures a TILE_SIZE x
; TILE_SIZE portion of the desktop at OffSetX, OffSetY, and then calls the function
; CreateWaterMaps to allocate memory for the two required "height maps".  Then it calls
; the CreateADrop function to place the raindrop in one of the "height maps".  Then
; it calls AnimationLoop to handle the animation of the tile. When the animation is 
; complete, DestroyObjects is called to de-allocate the memory used by the tile's bitmaps
; and height maps.  All of the functions from now on take a pointer to an individual Tile's
; structure (i.e. one of the tiles from the global array of tile structures)..
;

CreateWaterMaps proto WTStruct:LPWaterTileStruct
;
; This just allocates two arrays to handle our "height maps" Since we need one byte for
; each pixel, we need TILE_SIZE x TILE_SIZE bytes for each map.
;

CreateADrop proto WTStruct:LPWaterTileStruct
;
; In order to start the ripples, we need a nice big drop placed square in the middle of the
; tile.  Using the .DropDepth member of the Tile structure, we calculate the drop's depth
; and put the information into the first "Height Map".
;

AnimationLoop proto WTStruct:LPWaterTileStruct
;
; Here is where the actual animation of the tile happens.  This function consists of a loop
; (run once every SLEEP_TIME milliseconds) that calls ProcessWater to produce a new
; water "height map" for the current Tile, then it calls the MakeFrame function to create
; the display bitmap based on the height map.  Finally, PaintWaterTile blits the tile
; to the screen.
;

ProcessWater proto WTStruct:LPWaterTileStruct
;
; This function processes our two "height map" arrays to produce a height map for our
; current animation frame.
;
; The animation of a tile is achieved by maintaining two separate arrays of information
; on the height of the water at each pixel in the tile.  The first array holds the 
; information for the current frame of animation, the second holds the information from
; the last frame.  Between the two frames, we have enough information to create a new
; frame of animation (in the second array).  Then we switch the pointers to the arrays
; around and do it again for the next frame ect.....  

MakeFrame proto WTStruct:LPWaterTileStruct
;
; This function goes through the new height map and depending on the value of each byte
; the corresponding pixel in the destination bitmap is set.  If the value of a given byte
; was zero for instance, then the resulting pixel on the desintation bitmap would be the
; same as the pixel in the source (or background) bitmap.  Non-zero bytes correspond to
; another pixel from the source bitmap (i.e. due to the water's refraction).
;

PaintWaterTile proto WTStruct:LPWaterTileStruct
;
; Nothing too exciting here!  Just creates a DC, and blits the finished bitmap onto the
; screen.
;

DestroyObjects  proto WTStruct:LPWaterTileStruct
;
; Here we de-allocate the memory that was used to store the "height maps" and the source and 
; destination bitmaps.  Also the IsInUse member is set back to false here so that the tile can
; be reused.
;

FillWTStruct proto WTStruct:LPWaterTileStruct, OffSetX:DWORD, OffSetY:DWORD, DropDepth:DWORD
;
; This helper function fills out a Tile Structure for us.  It takes the XOffset, the YOffset
; and the Drop Depth.  It also sets the IsInUse member to true to indicate the structure is 
; in use.
;

SetupBitmapInfoStruct proto BIH:PBITMAPINFO, PicWidth:DWORD, PicHeight:DWORD
;
; Many bitmaps (dibsections actually) are created in this program. Since they are all the same 
; size (TILE_SIZE x TILE_SIZE) we just fill out one BITMAPINFO structure and use it every time we
; need to create a bitmap. It takes a pointer to a BITMAPINFO structure, and the width and height
; of the bitmap.
;
;

TRand proto :DWORD
;
; This is the random number generating function.  The code is based on something I took
; from VC++'s C library.  This function takes one parameter specifying the upper limit
; of the number to be generated.
;


.DATA                                         ; initialized data 
BitmapInfo BITMAPINFO <>                      ; Our BitmapInfo Structure 
WTileStruct  WaterTileStruct {},{},{},{},{}   ; an array of 5 Tile structures...
seed DWORD 1                                  ; for our Random numbers...

.DATA?                                        ; Uninitialized data 
CommandLine LPSTR ?                           ; Can't have too many of these


.CONST
TILE_SIZE DWORD 100         ; Our Tile's (length and width) 
                            ; Unfortunately, I could not use this constant for lea address 
                            ; calculations for some reason.  So I still have "100" in some
                            ; program areas.....


TILE_SMIN DWORD 99          ; TILE_SIZE -1 Its usefull in a loop.
TRAND_MAX DWORD 32767       ; The maximum possible random number

RAND_DROPDEPTH DWORD 100    ; These two constants help us determine a good   
MIN_DROPDEPTH DWORD 27      ; drop depth

MAX_DROPS_AT_ONCE DWORD 5   ; How many drops can we have at once?
STORMLENGTH DWORD 100       ; How many drops should we display in total?
SLEEP_TIME DWORD 120;       ; How much time should we wait between creating Tiles?
NUM_FRAMES DWORD 55         ; How many frames should a tile run for?
SLEEP_TICKS DWORD 10        ; How long should we wait between frames of animation?
INVERSE REAL4 0.166666      ; Helps us to divide by 6 quickly!
NOT_AVAILABLE DWORD -99     ; When all WaterTile structures  in the array are in use

.CODE                                  ; Here comes the CODE segment 
WaterTiles: 

invoke GetCommandLine                  ; get the command line.May be useful later.
mov CommandLine,eax                    

invoke WinMain, NULL,NULL,CommandLine, NULL       ; call the main function 
invoke ExitProcess, eax                           ; quit our program. The exit code is returned 
                                                  ; in eax from WinMain. 

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD 
 
    ; First lets fill out our WaterTileStructure....
    
    invoke CreateStorm, ADDR WTileStruct, STORMLENGTH, MAX_DROPS_AT_ONCE;
    invoke InvalidateRect, NULL, NULL, 1;
                 
    xor eax,eax;                                   All's well..... return 0
    ret 
    
WinMain endp 

;##########################################################################################

CreateStorm proc WTStruct:LPWaterTileStruct, StormSize:DWORD, Elements:DWORD

   LOCAL ScreenX:DWORD
   LOCAL ScreenY:DWORD
   LOCAL ScreenDC:HDC
   LOCAL x:DWORD
   LOCAL y:DWORD
   LOCAL i:DWORD
   LOCAL j:DWORD
   LOCAL FreeElement:DWORD
   LOCAL DropDepth:DWORD
   LOCAL SleepAfter:DWORD
   LOCAL ThreadID:DWORD

   ; Since all tiles are the same size, we only need to set up one bitmap
   ; info structure
   
   invoke SetupBitmapInfoStruct, ADDR BitmapInfo, TILE_SIZE, TILE_SIZE

   xor eax,eax;
   mov i, eax;
   mov ThreadID, eax;   

   StormLoop:
       ; the first thing we need to do is see if there are any available structures...
              
       mov eax, NOT_AVAILABLE;
       mov FreeElement, eax;

       xor eax,eax;
       mov j, eax;

       FreeLoop:

          mov eax, j;
          imul eax, SIZEOF WaterTileStruct;
          mov edx, WTStruct;
          add edx, eax;
          
          mov eax, [edx];
          cmp eax, TRUE;
          je NotFree
          mov eax, j;
          mov FreeElement, eax;         The index of a free structure
             
          NotFree:
          inc j;
          mov eax, j;
          cmp eax, Elements;
          
       jl FreeLoop;
       
       mov eax, FreeElement;
       cmp eax, NOT_AVAILABLE;
       je NoneFree;
 
       inc i;

       invoke GetDC, NULL;
       mov ScreenDC, eax;

       invoke GetDeviceCaps, ScreenDC, HORZRES;
       sub eax, TILE_SIZE;
       mov ScreenX, eax;

       invoke GetDeviceCaps, ScreenDC, VERTRES;
       sub eax, TILE_SIZE;
       mov ScreenY, eax;

       invoke ReleaseDC, NULL, ScreenDC;

       PickCoords:
       
       invoke TRand, ScreenX;
       mov x, eax;

       invoke TRand, ScreenY;
       mov y, eax;

       ; Now for the "ugly" part.  we need to make sure that the x and y coords weve picked
       ; dont interfere with any already running animation tiles.  Basically we need to know whether
       ; our x and y values are both within +/-TILE_SIZE of already running Tiles. So we go through all 
       ; the tiles and if they are in use, we look for interferences.  
       ;
       ; If at any point we see that no interference is possible, we jump past all the other tests.  
       ; If we get past all tests we have an interference, and so we jump back up to pick new coordinates...

;-----------------------------------------------------

       xor eax, eax;
       mov j, eax;
       
       InterferenceLoop:

          mov eax, j;
          imul eax, SIZEOF WaterTileStruct;
          mov edx, WTStruct;
          add edx, eax;
          add edx, 8;
          
          mov esi, [edx];       // esi holds the XOffSet for structure j
          add edx, 4;
          mov edi, [edx];       // edi holds the YOffSet for structure j

          sub esi, TILE_SIZE;
          sub edi, TILE_SIZE;

          cmp x, esi;
          jl Passed
          add esi, TILE_SIZE;
          add esi, TILE_SIZE;
          cmp x, esi;
          jg Passed

          cmp y, edi;
          jl Passed;
          add edi, TILE_SIZE;
          add edi, TILE_SIZE;
          cmp y, edi;
          jg Passed;

          jmp PickCoords            ; We have an inteference so go back and pick new coords
                   
          Passed:                   ; No interference. Increment j and do the next one...
          inc j;
          mov eax, j;
          cmp eax, Elements;
          
       jl InterferenceLoop;
       
;-----------------------------------------------

 
       invoke TRand, RAND_DROPDEPTH                   ; Now get a drop-depth for our droplet
       add eax, MIN_DROPDEPTH;
       mov DropDepth, eax;
  
       mov eax, FreeElement;
       imul eax, SIZEOF WaterTileStruct;
       mov edx, WTStruct;
       add edx, eax;
     
       invoke FillWTStruct, edx, x, y ,DropDepth      ; Fill out our structure

       mov eax, FreeElement;
       imul eax, SIZEOF WaterTileStruct;
       mov edx, WTStruct;
       add edx, eax;


       xor ebx, ebx                                   ; Create a new thread

       invoke CreateThread, ebx, ebx, OFFSET ThreadProc, edx, ebx, ADDR ThreadID 
       invoke CloseHandle, eax                        ; We dont need the Handle anymore so lets close it now...

       NoneFree:                                      ; If we jumped here, there are no free structures
       invoke Sleep, SLEEP_TIME;                      ; try sleeping for a while....
       
       mov eax, i;
       cmp eax, StormSize;
       
   jl StormLoop;
   ret

CreateStorm  endp 

;################################################################################################

ThreadProc proc WTStruct:LPWaterTileStruct

   invoke CreateWaterTile, WTStruct                    ; Create a tile, and off we go.....
   ret
   
ThreadProc endp

;################################################################################################

CreateWaterTile proc WTStruct:LPWaterTileStruct

    LOCAL ScreenDC:HDC
    LOCAL BitmapDC:HDC
    LOCAL CaptureBitmap:HBITMAP
    LOCAL pOldBitmap:HGDIOBJ
    LOCAL BitmapBits:LPVOID 


    invoke CreateCompatibleDC, NULL;                Create a Memory DC
    mov BitmapDC, eax;
  
    invoke GetDC, NULL;                             Get a Hardware DC for our screen...
    mov ScreenDC, eax;
       
    ; create the bitmap
      
    invoke CreateDIBSection, BitmapDC, ADDR BitmapInfo, DIB_RGB_COLORS, ADDR BitmapBits, NULL, NULL
    mov edx, WTStruct;
    mov [edx + 20], eax;
    mov ebx, [BitmapBits];
    mov [edx + 28], ebx;

    invoke CreateDIBSection, BitmapDC, ADDR BitmapInfo, DIB_RGB_COLORS, ADDR BitmapBits, NULL, NULL
    mov edx, WTStruct;
    mov [edx + 16], eax;
    mov ebx, [BitmapBits];
    mov [edx + 24], ebx;

   
    ; Select the bitmap into our memory DC
      
    invoke SelectObject, BitmapDC, eax;  Select the capture bitmap into the memory DC
    mov pOldBitmap, eax;
  
    ;Capture some screen area into the memory DC...

    mov edx, WTStruct;
    mov eax, [edx + 8];
    mov ebx, [edx + 12];

    invoke BitBlt, BitmapDC ,0 ,0 ,TILE_SIZE ,TILE_SIZE ,ScreenDC ,eax ,ebx ,SRCCOPY;
  
    ; Now clean up our GDI stuff....
  
    invoke SelectObject, BitmapDC, pOldBitmap;   Select the bitmap out of the DC and delete it
      
  
    invoke DeleteDC, BitmapDC;                   Delete the DCs....
    invoke ReleaseDC, NULL, ScreenDC;

    invoke CreateWaterMaps, WTStruct;            Create the water masks for this tile..
    invoke CreateADrop, WTStruct;                Place a "drop" on one of the masks

    invoke AnimationLoop, WTStruct;              Now Animate!    
     
    invoke DestroyObjects, WTStruct;             Destroy the watermaps and bitmaps      
    ret

CreateWaterTile endp 

;################################################################################################

CreateWaterMaps proc WTStruct:LPWaterTileStruct

   ; Here is where we allocate the memory for our water masks.....

   mov eax, TILE_SIZE;
   imul eax, eax;
   
   invoke GlobalAlloc, GMEM_FIXED or GMEM_ZEROINIT, eax;
   mov edx, WTStruct;
   mov [edx + 32], eax;

   mov eax, TILE_SIZE;
   imul eax, eax;
   
   invoke GlobalAlloc, GMEM_FIXED or GMEM_ZEROINIT, eax;
   mov edx, WTStruct;
   mov [edx + 36], eax;


   ret
CreateWaterMaps endp 

;################################################################################################

CreateADrop proc WTStruct:LPWaterTileStruct

    LOCAL i:DWORD
    LOCAL j:DWORD
    LOCAL DripRadius:DWORD
    LOCAL Distance:DWORD
    LOCAL DropDepth:DWORD
    LOCAL FinalDepth:DWORD


    mov edx, WTStruct;
    mov edx, [edx + 4];

    mov DropDepth, edx;
    shr edx, 3;
    mov DripRadius, edx;

    mov esi, TILE_SIZE;  
    shr esi, 1;   
		
    mov ebx, esi;         // ebx is both X and Y in the original calculation.
		
    mov edi, esi;         
    add edi, edx;         // edi contains the upper limit of our loops
    sub esi, edx;         // esi contains the lower limit of our loops

    mov j, esi;
    JLoop:

       mov ecx, esi;
	 ILoop:
          mov eax, ecx;
	    sub eax, ebx;
	    imul eax, eax;
	    mov Distance, eax;

	    mov eax, j;
	    sub eax, ebx;
	    imul eax, eax;
	    add Distance, eax;

	    mov eax, DripRadius;
	    imul eax, eax;

	    cmp Distance, eax;
	    jge short NoDrip

	    fild DripRadius;        
	    fld st(0);
	    fild Distance;
	    fsqrt;
	    fsubp st(1), st(0);
	    fxch;
	    fdivp st(1), st(0);
	    fild DropDepth;
	    fmulp st(1), st(0);

	    fistp FinalDepth;
          mov eax, j;
	    imul eax, TILE_SIZE;
	    add eax, ecx;

	    mov edx, WTStruct;
	    mov edx, [edx + 32];
	    add edx, eax;
	    mov eax, FinalDepth;
	    mov [edx], al;

	    NoDrip:
	    inc ecx;
	    cmp ecx, edi;

	 jl short ILoop;

	 inc j;
	 cmp j, edi;

   jl short JLoop;
   invoke Sleep, NULL;
	
   ret
CreateADrop endp 

;################################################################################################

AnimationLoop proc WTStruct:LPWaterTileStruct

  LOCAL Counter:DWORD

  xor eax, eax;
  mov Counter, eax;
  AniLoop:

     inc Counter;
     invoke ProcessWater, WTStruct;
     invoke MakeFrame, WTStruct;
     invoke PaintWaterTile, WTStruct;
     invoke Sleep, SLEEP_TICKS;
     mov ecx, Counter;
     cmp ecx, NUM_FRAMES
     
 jl AniLoop

  ret

AnimationLoop endp

;################################################################################################

ProcessWater proc WTStruct:LPWaterTileStruct

    LOCAL i:DWORD
    LOCAL j:DWORD
    LOCAL YCounter:DWORD
    LOCAL LoopLim:DWORD
    LOCAL StPt:DWORD
    LOCAL TempInt:DWORD

    mov eax, 2;
    mov StPt, eax;

    mov eax, TILE_SIZE;
    sub eax, 2;
    mov LoopLim, eax;

	fld INVERSE;           // For later when we divide by 6...
	mov esi, WTStruct;
	mov edi, WTStruct;
	mov esi, [esi + 32];   // Now esi and edi point to 
	mov edi, [edi + 36];

      mov eax, TILE_SIZE;
      shl eax, 1;
      lea edi, [edi + eax];
		
	mov ecx, StPt;
	mov YCounter, ecx;
	
	YLoop:
		mov ecx, 2;
		XLoop:

	      lea edx, [esi + ecx];
		movsx eax, byte ptr [edx];         // i,j-2

		lea edx, [edx + 100];
		movsx ebx, byte ptr [edx - 1 ];    // i-1,j-1
		add eax, ebx;
		movsx ebx, byte ptr [edx];         // i,j-1
		add eax, ebx;
		movsx ebx, byte ptr [edx + 1];     // i + 1,j-1
		add eax, ebx;

		lea edx, [edx + 100];
		movsx ebx, byte ptr [edx - 2];     // i-2,j
		add eax, ebx;
		movsx ebx, byte ptr [edx - 1];     // i-1,j
		add eax, ebx;
		movsx ebx, byte ptr [edx + 1];     // i+1,j
		add eax, ebx;
		movsx ebx, byte ptr [edx + 2];     // i+2,j
		add eax, ebx;

		lea edx, [edx + 100];
		movsx ebx, byte ptr [edx - 1];     // i-1,j+1
		add eax, ebx;
		movsx ebx, byte ptr [edx];         // i,j + 1
		add eax, ebx;
		movsx ebx, byte ptr [edx + 1];     // i + 1,j + 1
		add eax, ebx;

		lea edx, [edx + 100];
		movsx ebx, byte ptr [edx];         // i,j + 2
		add eax, ebx;

		mov TempInt, eax;			     We need to integer divide by 6, but its quicker to 
		fild TempInt;                      do a floating point multiplication by 1/6....

		fmul st(0), st(1);
		fistp TempInt;

		mov eax, TempInt;

		movsx ebx, byte ptr [edi + ecx];
		sub eax, ebx;
	
		mov ebx, eax;
		shr eax, 4;
		sub ebx, eax;

		cmp ebx, 2;
		jge MakeZero;
		xor ebx, ebx;

		MakeZero:
		mov byte ptr [edi + ecx], bl;   // Nasty partial-register stall?

		inc ecx;
		cmp ecx, LoopLim ;
	jb XLoop;

	add edi, TILE_SIZE;
	add esi, TILE_SIZE;

	mov ecx, YCounter;
	inc ecx;
	mov YCounter, ecx;
	cmp ecx, LoopLim;

   jb YLoop;

   fstp TempInt;          get our float off the stack
   invoke Sleep, NULL;    Surrender control to a sibling thread
   ret
   
ProcessWater endp

;################################################################################################

MakeFrame proc WTStruct:LPWaterTileStruct

   LOCAL CapBits:DWORD      
   LOCAL WatBits:DWORD   
   LOCAL YCounter:DWORD 
   LOCAL Count:DWORD 
       

   xor eax, eax;
   mov YCounter, eax;
   mov Count, eax;
            
   mov ebx, WTStruct;

   mov eax, [ebx + 24];
   mov CapBits, eax;
   mov eax, [ebx + 28];
   mov WatBits, eax;

   mov eax, WTStruct;
   mov esi, [eax + 32];   Make edi and esi point to
   mov edi, [ebx + 36];   the two watermasks

   mov [eax + 32], edi;   Now switch them around to
   mov [eax + 36], esi;   make the animation happen

   mov eax, edi;          switch them again to make it 
   mov edi, esi;          consistant with ProcessWater
   mov esi, eax;          

   mov YCounter, 0;
   YLoop:
   
     xor ecx, ecx;
     XLoop:
        mov eax, ecx;
	  mov edx, Count;
	  test ecx, ecx;
				
	  jz  short NoX;
        cmp ecx, TILE_SMIN;
	  jnb short NoX;

	  movsx edi, byte ptr [esi + edx - 1];
	  movsx ebx, byte ptr [esi + edx + 1];
					
	  sub ebx, edi;
	  add eax, ebx;

    	  NoX:
		mov ebx, YCounter;
		test ebx, ebx;
       	jz  short NoY;
	         cmp ebx, TILE_SMIN;
		jnb short NoY;
  		movsx edx, byte ptr [esi + edx - 100];
		movsx edi, byte ptr [esi + edx + 100];
									
		sub edi, edx;
		add ebx, edx;

		NoY:

  		test eax, eax;           unless xoff and edi are greater than 0, put 0 in them
		jns short NoChangeX;
		xor eax, eax;

		NoChangeX:
				
		test ebx, ebx;
		jns short NoChangeY
		xor ebx, ebx;
		NoChangeY:

		mov edx, TILE_SMIN;      if xoff and ebx are > than TILE_SIZE, put TILE_SMIN in them

		cmp eax, edx;
		jng short NoLimX;
		mov eax, edx;
		NoLimX:


		cmp ebx, edx;
		jng short NoLimY;
		mov ebx, edx;
		NoLimY:
		
		inc edx;                 get some RGB values, ebx is in ebx, TILE_SIZE in edx....
		shl ebx, 2;
		imul edx, ebx;
		lea eax, [edx + eax * 4];
		mov edx, Count;
		add eax, CapBits;

		movsx edx, byte ptr [esi + edx];

		movzx ebx, byte ptr [eax];
		add ebx, edx;
		cmp ebx, 255;
		jnge short NoRLim;
		mov ebx, 255;
		NoRLim:			
		mov edi, ebx;

		movzx ebx, byte ptr [eax + 1];
		add ebx, edx;
		cmp ebx, 255;
		jnge short NoGLim;
		mov ebx, 255;

		NoGLim:			
		shl ebx, 8;
		or edi, ebx;
		movzx ebx, byte ptr [eax + 2];
		add ebx, edx;
		cmp ebx, 255;
		jnge short NoBLim;
		mov ebx, 255;

		NoBLim:			
		shl ebx, 16;
		or edi, ebx;
		mov eax, Count;
		mov edx, WatBits;
		mov [edx + eax * 4], edi;

		inc Count;
		inc ecx;
		cmp ecx, TILE_SIZE ;

	jb XLoop;
			
	mov ecx, YCounter;
	inc ecx;
	mov YCounter, ecx;
	cmp ecx, TILE_SIZE;

   jb YLoop;

   invoke Sleep, NULL;
   ret
   
MakeFrame endp

;################################################################################################

PaintWaterTile proc WTStruct:LPWaterTileStruct
    LOCAL ScreenDC:HDC
    LOCAL BitmapDC:HDC
    LOCAL pOldBitmap:HGDIOBJ
    LOCAL BitmapBits:LPVOID 
    LOCAL Counter:DWORD

      
    invoke CreateCompatibleDC, NULL;                Create a Memory DC
    mov BitmapDC, eax;
  
    invoke GetDC, NULL;                             Get a Hardware DC for our screen...
    mov ScreenDC, eax;
  
        
    ; Select the bitmap into our memory DC
    mov edx, WTStruct
    mov edx, [edx + 20];
      
    invoke SelectObject, BitmapDC, edx;             Select the capture bitmap into the memory DC
    mov pOldBitmap, eax;


    mov edx, WTStruct
    mov eax, [edx + 8];
    mov ebx, [edx + 12];

    ;Blit our watered bitmap onto the screenDC...

    invoke BitBlt, ScreenDC ,eax ,ebx ,TILE_SIZE ,TILE_SIZE ,BitmapDC ,0 ,0 ,SRCCOPY;
       
    ; Now clean up our GDI stuff....
  
    invoke SelectObject, BitmapDC, pOldBitmap;     Select the bitmap out of its DC
      
    invoke DeleteDC, BitmapDC;                     Delete the DCs....
    invoke ReleaseDC, NULL, ScreenDC;
    
    invoke Sleep, NULL;                            Surrender control to a sibling thread
    ret
    
PaintWaterTile endp

;################################################################################################

DestroyObjects proc WTStruct:LPWaterTileStruct

   mov edx, WTStruct;
   mov edx, [edx + 20];
   invoke DeleteObject, edx; 
   
   mov edx, WTStruct;
   mov edx, [edx + 16];
   invoke DeleteObject, edx; 

   mov edx, WTStruct;
   mov edx, [edx + 32];
   invoke GlobalFree, edx;

   mov edx, WTStruct;
   mov edx, [edx + 36];
   invoke GlobalFree, edx;

   mov edx, WTStruct;          Set the IsInUse member to FALSE
   xor eax, eax;
   mov [edx], eax;
  
   ret
DestroyObjects endp

;################################################################################################

FillWTStruct proc WTStruct:LPWaterTileStruct, OffSetX:DWORD, OffSetY:DWORD, DropDepth:DWORD

    mov edx, WTStruct;
    mov eax, 1;
    mov [edx], eax;            Set the IsInUse member to TRUE

    mov eax, DropDepth;        Set the Drop Depth
    mov [edx + 4],  eax;

    mov eax, OffSetX;          Set the X OffSet
    mov [edx + 8],  eax;

    mov eax, OffSetY;          Set the Y Offset
    mov [edx + 12], eax;
    
    ret

FillWTStruct endp 

;################################################################################################

SetupBitmapInfoStruct proc BIH:PBITMAPINFO, PicWidth:DWORD, PicHeight:DWORD

    mov edx, BIH;

    mov ebx, SIZEOF BITMAPINFOHEADER;
    mov [edx], ebx; 

    mov ebx, PicWidth; 
    mov [edx + 4], ebx; 
    mov ebx, PicHeight; 
    mov [edx + 8], ebx; 
     
    mov bx, 1; 
    mov [edx + 12], bx; 

    mov bx, 32;
    mov [edx + 14], bx;
 
    ret

SetupBitmapInfoStruct endp 

;################################################################################################

TRand proc Range:DWORD     ; I got the integer part of this from the C++ libs.  This function 
                           ; returns an integer between 0 and Range instead of 0 and RAND_MAX
    LOCAL TempInt:DWORD
    LOCAL RMax:DWORD

    mov eax, TRAND_MAX;
    mov RMax, eax;
      
    mov  eax, seed;
    imul eax, eax,343FDh;
    add  eax, 269EC3h;
    mov  seed,eax;
    sar  eax,10h;
    and  eax,7FFFh;

    mov TempInt, eax;

    fild TempInt;
    fild RMax;
    fdivp st(1), st(0);
    fild Range;
    fmulp st(1), st(0);
    fistp TempInt; 
 
    mov eax, TempInt;
    ret
    
TRand endp

;################################################################################################

end WaterTiles