1. Technology

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/od/graphics/l/aa082801a.htm

was emailed to:

Thanks for sharing About.com with others!

Screen Shuffling with Delphi
Delphi code that divides the current desktop into blocks and then swaps the blocks. It includes an option that lets you adjust the shuffling speed, and the size of the blocks. Great intro to sliding puzzle game or to screen saver development.
 More of this Feature
• Project's CODE

Printer friendly versionPrinter friendly version
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• Graphics programming
• Custom shaped forms
• Zoom - Desktop loupe
• TScreen object
• Changing Screen size
• Screen savers with Delphi

The Idea
Screen shuffling in action I'm sure you've seen it. When the program starts it takes a picture of the current desktop, the picture is then "cut" into a number of rectangular pieces (all of the same size). The piece at some random position is "removed". The main code randomly swaps that piece with the one next to it. Only pieces adjacent to the removed one can be moved into it.

   Implementation
Start a new Delphi project with a blank form. Set the Name property to be 'Shuffler'. Add an Image (Image1) and a Timer (Timer1) component to a form. Image will hold the (scrambled) picture of the Desktop and Timer will fire the drawing procedure. The Interval property of the Timer component determines how often the swapping takes place (the value of 1000 is equal to one second, 2000 is two seconds).
Several global variables are required by the project. Place the next code above the implementation part of the form unit:

var
  //this was added by Delphi
  Shuffler: TShuffler; 

  DesktopBitmap   : TBitmap; 
  gx, gy          : Integer; 
  redRect         : TBitmap; 

  rW, rH          : Integer; 

const
  DELTA = 8; //should be 2^n

• The constant (integer) DELTA value determines in how many pieces (preciselly, rows and columns) the screen shot will be split into. The DELTA number should be 2^n where n is a signed integer number. Bigger DELTA results in smaller pieces. For example if DELTA is 16 and the screen resolution is 1024 x 768 then the screen is divided in 64x48 matrix forming 256 pieces.
DesktopBitmap bitmap holds the captured image of the current Desktop screen - we get this picture by taking the screen shoot.
redRect is a bitmap picture that replaces the "removed" piece of the picture. The redRect is created in the form's OnCreate event.
gx, gy hold the current x and y position (Left, Top) of the redRect inside the scrambled picture.
rW, rH are width and Height of a rectangular part of the picture. For the 1024x768 and DELTA=16 the rW is 64 and rH is 48.

The project starts in the form's OnCreate event handler:

procedure TShuffler.FormCreate(Sender: TObject);
begin
  rW := Screen.Width div DELTA;
  rH := Screen.Height div DELTA;

  redRect:=TBitmap.Create;
  with redRect do begin
    Width := rW;
    Height := rH;
    Canvas.Brush.Color := clRed;
    Canvas.Brush.Style := bssolid;
    Canvas.Rectangle(0,0,rW,rH);
    Canvas.Font.Color := clNavy;
    Canvas.Font.Style := Canvas.Font.Style + [fsBold];
    Canvas.TextOut(2,2,'About');
    Canvas.Font.Style := Canvas.Font.Style - [fsBold];
    Canvas.TextOut(2,17,'Delphi');
    Canvas.TextOut(2,32,'Programming');
  end;

  Timer1.Enabled := False;
  Image1.Align := alClient;
  Visible := False;
  BorderStyle := bsNone;
  Top := 0;
  Left := 0;
  Width := Screen.Width;
  Height := Screen.Height;
  InitScreen;
//  SetWindowPos(Handle,HWND_TOPMOST,0,0,0,0,
                 SWP_NOSIZE + SWP_NOMOVE);
  Visible := True;
  Timer1.Interval := 10; // smaller := faster
  Timer1.Enabled  := True; // start calling DrawScreen
end;

First, the rW and rH values are determined by the DELTA value. As explained, if the screen dimension is 800x600 and DELTA is 8 than the screen picture is "divided" into 8x8 matrix thus forming small 100x75 peaces (rW = 100, rH = 75). In Delphi the TScreen object represents the state (resolution, color depth, etc.) of the screen in which an application runs.

Second, the redRect bitmap is created that will be placed inside the picture so that it replaces the "removed" slide. The redRect is simply a red rectangle with a sample (blue) text inside it. You could use some ready made graphics, some logo or something.

Finally, the code sets the form to be wide and tall as the Screen. The (commented) call to the API SetWindowPos could be used to set the form to be always OnTop, non movable and non resizable. The InitScreen procedure is called. Timer Interval is set and the OnTimer event starts executing the DrawScreen procedure.

InitScreen - Screen shot
The InitScreen procedure, called from the form's OnCreate event handler, is used to take a screen shot of the current Desktop screen, set the starting position of the redRect and to draw the grid. The code that draws the grid is optional.

To take a screen shoot of the Desktop the GetDC along GetDesktopWindow are used. The BitBt API function is used to transfer the picture of the Desktop to the DesktopBitmap. GetDC(GetDesktopWindow) retrieves a handle of a display device context for the specified window - the window returned by the GetDesktopWindow call. Finally DesktopBitmap is then assigned to Image1 component. Take a look at Delphi help files for more info.

Initial position of the redRect piece is randomly selected. Trunc(Random * DELTA) returns an integer number between 0 and DELTA. Next, the redRect is drawn on the gx, gy position using the CopyRect function of the Canvas object. Again, if you are unfamiliar with drawing algorithms in Delphi - please consider the help system for more info.

Finally, the grid is drawn by using the MoveTo and LineTo. Grid is optional and is used to better differentiate each peace of the picture.

procedure InitScreen;
var i,j:integer;
begin
  //get Desktop bitmap
  DesktopBitmap := TBitmap.Create;
  with DesktopBitmap do begin
    Width := Screen.Width;
    Height := Screen.Height;
  end;
  BitBlt(DesktopBitmap.Canvas.Handle,
         0,0,Screen.Width,Screen.Height,
         GetDC(GetDesktopWindow),0,0,SrcCopy);

  Shuffler.Image1.Picture.Bitmap := DesktopBitmap;

  //initial position of the redRect
  Randomize;
  gx := Trunc(Random * DELTA);
  gy := Trunc(Random * DELTA);

  Shuffler.Image1.Canvas.CopyRect(
    Rect(rW * gx, rH * gy, rW * gx + rW, rH * gy + rH),
    redRect.Canvas,
    Rect(0,0,rW,rH));

  //draw grid
  for i:=0 to DELTA-1 do begin
    Shuffler.Image1.Canvas.MoveTo(rW * i,0);
    Shuffler.Image1.Canvas.LineTo(rW * i,Screen.Height);

    Shuffler.Image1.Canvas.MoveTo(0, rH * i);
    Shuffler.Image1.Canvas.LineTo(Screen.Width, rH * i);
  end;
end;

Draw Screen
The main code is in the DrawScreen procedure. This procedure is called inside the Ontimer event of a Timer component.

procedure DrawScreen;
var
  r1,r2:TRect;
  Direction:integer;
begin
  r1:=Rect(rW * gx , rH * gy,
           rW * gx + rW, rH * gy + rH);

  Direction := Trunc(Random*4);
  case Direction of
   0: gx := Abs((gx + 1) MOD DELTA); //right
   1: gx := Abs((gx - 1) MOD DELTA); //left
   2: gy := Abs((gy + 1) MOD DELTA); //down
   3: gy := Abs((gy - 1) MOD DELTA); //up
  end; //case

  r2 := Rect(rW * gx , rH * gy,
             rW * gx + rW, rH * gy + rH);

  with Shuffler.Image1.Canvas do begin
    CopyRect(r1, Shuffler.Image1.Canvas, r2);
    CopyRect(r2, redRect.Canvas, redRect.Canvas.ClipRect);
  end;
end;

The code may seam strange but is really simple. Since only pieces adjacent to the redRect one can be swapped with it, there are just 4 possible directions. The r1 rectangle holds the current position of the redRect, r2 points to the rectangle with the peace that will be replaced. The CopyRect is used to transfer the "selected" part on the redRect position and again to draw the redRect on it's new position - thus swapping those two peaces.
It would be much eye-frienldy if the swapping is animated - I'll leave this for yopu to implement.

This is how my 640x480 Desktop looks, after several OnTimer events, with DELTA=4. My Screen is usually 1024x768, but I've changed my display properties to take a better screen shoot. Note that you have to hit ALT+F4 in order to stop the program execution. Here is the Project's CODE.

Screen shuffling in action

Note: If you run this application under Delphi 6 the screen will flicker. In Delphi 6 the csOpaque (in TControlStyle) is only set when the Proportional property is False, Stretch is True and Transparent is False. In Delphi 5 (and <) csOpaque is set if Transparent is False and Stretch is True (or the bitmap you want to display is greater or equal size of the Timage) which make sense.
Conclusion. To shuffle the screen without the flicker effect (in D6) you have to set the Stretch property to TRUE - even when you don't want the bitmap to get stretched (if a graphics is of the equal size to the TImage in which it is displayed - as in our case.)

   Final words
You've probably saw something like this as a screen saver. If you would like to make one with this code - go ahead. More info on Screen Saver development can also be found on this site.

For a bit of entertainment - the code is the starting point for the popular slide type puzzle. Those little hand puzzles with tiles numbered 1 to 15 that you must slide into numeric order. All you have to do is to stop the DrawScreen routine after some time to get the puzzled image. The idea of the game is to move the pieces back to reform the picture. In general, add the code that receives user clicks on the shuffled picture. Clicking on a piece next to the redRect should swap that piece with the redRect.
Once you have the game done, you could post it to our Free Source Area.

Of course, if you have any questions or comments on this article please post to the Delphi Programming Forum.

©2014 About.com. All rights reserved.