Home Contents Search

Perpetual Newbie
Error Message Issue #23 skins QTime Perpetual Newbie Perpetual Newbie Perpetual Newbie Perpetual Newbie How Convert User Registration rabbit hole CASE & Classes skins Premium 5 Premium 6 Premium Domains Premium 2 Premium 3 Premium 4 Rare domains cities_realestate Similar   Websites education_sites entertainment_sites games misc_sites LLLL.com Site Acronym 2 Acronym 4 LLLLL.com LLLLL.com 2 LLLLL.com 3 Acronym 5 Acronym 6 Acronym 7 Acronym 8 Acronym 9 Acronym 10 Acronym 3 Brandable sites Pin Yin sites service_sites technology Acronym sites Payment Options About Our Office

Spin on a Dime� With NT!

By Robert Vivrette - RobertV@mail.com

This article originally appeared in the Janurary 1999 issue of Delphi Informant Magazine and is being reprinted here with permission from Informant Communications.

Many of you are no-doubt familiar with the BitBlt Windows API function. For those of you who don�t, it stands for �Bit Block Transfer� and is simply one of the functions Windows uses to paint images on the screen. It�s close-relative is the StretchBlt function which performs a similar function, but allows you to stretch or shrink the image you are drawing. Delphi wraps each of these commands in far simpler to use mechanisms� namely the Draw and StretchDraw methods.

In addition to a few other relatives (including MaskBlt for masking operations, and PatBlt for painting an area with a pattern), Windows also includes a little-known function called PlgBlt. Part of the reason why it is not in many peoples graphics vocabulary is that it is currently an WinNT/Win2K-only function (so is MaskBlt by the way). This means that users of Win31, Win95 and Win98 are out of luck here. It won�t work.

However, if you use WinNT/Win2K and have to do some fancy footwork with graphics, you will find PlgBlt to be an incredibly powerful tool.

In a nutshell, PlgBlt is pretty much the same as a StretchBlt except that the destination of the painting doesn�t need to be rectilinear� hence the �Plg� in its name (which stands for parallelogram). All PlgBlt needs to perform this magic is 3 points on a canvas� (sounds like a song!)

Now wait a minute, you say� why only 3 points? Well, as it turns out, it really does use 4 points to make this parallelogram. However, it doesn�t trust you to generate the 4th one. If you were to place 3 dots on a piece of paper in a specific order, there would be only one spot where you could place the fourth and still have a parallelogram. Note here that the sequence of those first three points is important in making this determination.

As a demonstration on the use of PlgBlt, I decided to write a quick little application that would allow the user to spin a bitmap in real-time. As you can see from the listing below, there is no rocket science here, but the capability PlgBlt adds is undeniably cool.

Even if you are using a non WinNT/Win2K development environment, there are a couple of interesting techniques that are in use in this code that you may find useful anyway. Read On!

unit PlgBltU;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls, Math;

type
  TForm1 = class(TForm)
    Img: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
  private
    P          : Array[0..3] of TPoint;
    OAng       : Array[0..3] of Double;
    OverHandle : Integer;
    BkBmp      : TBitmap;
    MidPt      : TPoint;
    Ang,R      : Double;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  Pt : Integer;
begin
  BkBmp := TBitmap.Create;
  BkBmp.Width := Width;
  BkBmp.Height := Height;
  P[0] := Img.BoundsRect.TopLeft;
  P[3] := Img.BoundsRect.BottomRight;
  P[1] := P[0]; Inc(P[1].X,Img.Width);
  P[2] := P[3]; Dec(P[2].X,Img.Width);
  with Img do MidPt := Point(Left+Width div 2,Top + Height div 2);
  with Img do R := SqRt(Sqr(Width div 2)+Sqr(Height div 2));
  for Pt := 0 to 3 do with P[Pt] do
    OAng[Pt]:= ArcTan2(Y-MidPt.Y,X-MidPt.X)+Pi;
  OverHandle := -1;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  BkBmp.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  Pt : Integer;
begin
  with BkBmp.Canvas do
    begin
      Brush.Color := clBtnFace;
      FillRect(ClipRect);
      if PlgBlt(Handle,P,Img.Canvas.Handle,0,0,Img.Width,Img.Height,0,0,0) then
        begin
          Brush.Color := clBlack;
          for Pt := 0 to 3 do with P[Pt] do
            FillRect(Rect(X-3,Y-3,X+3,Y+3));
        end
      else
        TextOut(0,0,'PlgBlt currently supported only on WinNT');
    end;
  Canvas.Draw(0,0,BkBmp);
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
var
  Pt      : Integer;
  TmpRect : TRect;
begin
  if ssLeft in Shift then
    begin
      if OverHandle = -1 then exit;
      Ang := ArcTan2(Y-MidPt.Y,X-MidPt.X)-OAng[OverHandle]+Pi;
      for Pt := 0 to 3 do
        P[Pt] := Point(MidPt.X-Round(R*Cos(Ang+OAng[Pt])),
                       MidPt.Y-Round(R*Sin(Ang+OAng[Pt])));
      Paint;
    end
  else
    begin
      OverHandle := -1;
      for Pt := 0 to 3 do
        begin
          with P[Pt] do TmpRect := Rect(X-3,Y-3,X+3,Y+3);
          if PtInRect(TmpRect,Point(X,Y)) then
            begin
              Cursor := crHandPoint;
              OverHandle := Pt;
            end;
        end;
      if OverHandle = -1 then Cursor := crDefault;
    end;
end;

end.


As you can see, there are only 4 methods in use here. Our objective for this demo is to place a TImage on a form, add a graphic to it, and when the application is run, allow the user to grab the corners and rotate the image. We start by creating a new form, placing a TImage in the center and loading the TImage with a bitmap.
The first thing that happens is the FormCreate of course. Here we do a few setup-type things. There is a TBitmap that we have declared earlier that we are going to use to prevent flicker when we rotate the image. More on that a little later. Here in the FormCreate however, we must setup that bitmap to be the width and height of the form.

Next, we setup an array of points. There are four points in this array (numbered 0 to 3) and we will be using them for two purposes: The first is to pass the array into the PlgBlt command (remember, it will ignore the last point); and the second purpose is to provide locations for drawing handles on the image (so we can see where to grab when rotating it). All I do here in the FormCreate is place the points in their proper locations� one at each corner of the image.

Next we calculate a MidPt. This TPoint variable will hold the rotational center of the image and is an average of the 4 corners of the image. The next variable defined is �R� which will hold the distance (radius) from one corner of the image to the MidPt. This is used in the rotation calculations later on. Next, we fill an array of 4 real numbers (Double�s actually) with the initial angle that each corner of the image has in relation to the midpoint. This is also used in our rotation calculations. Lastly, we set the OverHandle variable to a known value. This variable is used to track the handle number that the mouse is over. When it is �1, it is not over a handle.

The FormDestroy method simply cleans up a little by deleting our background bitmap we created in the FormCreate.

The FormPaint is where PlgBlt comes into play. Remember I said earlier that we were going to use the background bitmap to avoid flicker. Well, the way this is done is that all drawing is done on this bitmap first. When it is all done, then the entire contents are plastered on the form�s canvas with a single call to its Draw method. Flicker occurs when you have multiple (and conflicting) draw actions occurring in the same space. Since the background bitmap is an in-memory TBitmap we can draw on it all we want and the user won�t see it until it is transferred to the form. Since it is a single drawing event, there is no flicker.

Back to the FormPaint� First, we clear the background bitmap using FillRect. Then we do our call to PlgBlt. The parameters it expects are as follows (partially excerpted from the Win32 API Help):

hdcDest - Identifies the destination device context. This is the handle to the destination canvas (BkBmp.Canvas.Handle)
lpPoint � This is our array of points used to identify the first three corners of the destination parallelogram. The upper-left corner of the source rectangle is mapped to the first point in this array, the upper-right corner to the second point in this array, and the lower-left corner to the third point. As I mentioned earlier, the lower-right corner is calculated for you. It doesn�t hurt that we are actually passing in all four points, the PlgBlt function is ignoring the last one.
hdcSrc - Identifies the source device context.  This is the handle to the Images canvas (Img.Canvas.Handle).
nXSrc - Specifies the x-coordinate, of the upper-left corner of the source rectangle.
nYSrc - Specifies the y-coordinate, of the upper-left corner of the source rectangle.
nWidth - Specifies the width, of the source rectangle.
nHeight - Specifies the height, of the source rectangle.
The last three parameters (hbmMask, xMask, and yMask) have to do with an optional bitmap that can be used to mask colors. We aren�t using these in this example, so we set them all to zero.

If the call to PlgBlt is successful, then we draw the 4 handles on the image, using the same array of points we passed into PlgBlt. If the call is not successful, it generally is a sign that we are not running on a WinNT platform, so we make an appropriate mention using TextOut. After all this is done, the background bitmap is transferred all at once to the form using its Draw method.

The last method we have in this demo is the mouse management for working with the handles on the corners of the image. When the mouse is moved, we first look to see if the left mouse button is down. If so, we also check to make sure that OverHandle contains the number of a handle that has been grabbed. Assuming this is also true, we determine the angle from where the mouse is to the mid-point of the image (calculated earlier). We use this angle to re-calculate the locations of each of the four handles on the image.

If the left mouse button is not down, we make sure the OverHandle variable is reset and then we use PtInRect to determine if the mouse is over any of the four handles. If so, we store that handle�s number in OverHandle, and switch the cursor to a hand pointer.

That is pretty much it for this demonstration. Notice that there is no code to rotate the image as such. The PlgBlt routine handles that all for us.

When you run the application, you will see something like what is shown in Figure 1.


Figure 1

Grabbing one of the handles and spinning it a little counter-clockwise would result in what is shown in Figure 2.


Figure 2

These static pictures of course really don�t do the demo much justice. Suffice it to say that the rotation is perfectly smooth and very fast, thanks in part to the amazing power of PlgBlt.

Copyright © 2007 undu.com                    Powered by Engineer Partner The One Stop Outsource