Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
Listing 11-2: Scaling a bitmap image
    interface

       .

       .

       .

    var

       .

       .

       .

      {this is used as an argument to the Sin function to

       create an oscillating scale factor}

      Angle: Integer;

    implementation

       .

       .

       .

    procedure TfrmDXAppMain.FormCreate(Sender: TObject);

    begin

       .

       .

       .

      {initialize the angle variable}

      Angle := 0;

    end;

       .

       .

       .

    procedure TfrmDXAppMain.FormActivate(Sender: TObject);

    begin

       .

       .

       .

      {create a palette based on this bitmap}

      FPalette := DDLoadPalette(FDirectDraw, ExtractFilePath(ParamStr(0))+

                                ‘Athena.bmp’);

      {attach the palette to the primary surface so that it takes effect}

      DXCheck( FPrimarySurface.SetPalette(FPalette) );

      {load in the bitmap containing the image}

      FImages := DDLoadBitmap(FDirectDraw, ExtractFilePath(ParamStr(0))+

                              ‘Athena.bmp’);

       .

       .

       .

    end;

       .

       .

       .

    procedure TfrmDXAppMain.DrawSurfaces;

    var

      SrfcInfo, ImageInfo: TDDSurfaceDesc2;

      SrcPtr, DestPtr: PByteArray;

      DestX, DestY, SrcX, SrcY: Integer;

      Scale: Double;

    begin

      {erase the last frame of animation}

      ColorFill(FBackBuffer, 0, nil);

      {increment our ‘angle’ variable}

      Inc(Angle);

      {using the angle variable with the Sin function to create a

       cyclic scaling value, compute a scale that oscillates between 0.5 and 1.5}

      Scale := 1+(Sin((Angle)*(PI/180))*0.5);

      try

        {lock the image surface}

        ImageInfo.dwSize := SizeOf(TDDSurfaceDesc2);

        DXCheck( FImages.Lock(nil, ImageInfo, DDLOCK_SURFACEMEMORYPTR or

                              DDLOCK_WAIT, 0) );

        SrcPtr := PByteArray(ImageInfo.lpSurface);

        {lock the destination surface}

        SrfcInfo.dwSize := SizeOf(TDDSurfaceDesc2);

        DXCheck( FBackBuffer.Lock(nil, SrfcInfo, DDLOCK_SURFACEMEMORYPTR or

                                  DDLOCK_WAIT, 0) );

        DestPtr := PByteArray(Integer(SrfcInfo.lpSurface)+100*SrfcInfo.lPitch+100);

        {iterate through the destination pixels, with a maximum scale of 1.5}

        for DestY := 0 to Trunc(ImageInfo.dwHeight * 1.5) do

        begin

          for DestX := 0 to Trunc(ImageInfo.dwWidth * 1.5) do

          begin

            {compute the source pixels based on the scale and our current

             destination pixel}

            SrcY := Trunc(DestY / Scale);

            SrcX := Trunc(DestX / Scale);

            {if the source pixel coordinate is within the source image...}

            if (SrcX > 0) and (SrcX < ImageInfo.dwWidth) and

               (SrcY > 0) and (SrcY < ImageInfo.dwHeight) then

              {...copy this source pixel into the destination}

              DestPtr[DestX] := SrcPtr[(ImageInfo.lPitch*SrcY)+SrcX];

          end;

          {increment to the next line in the destination surface}

          DestPtr := PByteArray(Longint(DestPtr)+SrfcInfo.lPitch);

        end;

      finally

        {unlock both buffers}

        FImages.Unlock(nil);

        FBackBuffer.Unlock(nil);

      end;

    end;
Alternatives/Enhancements Scaling is implemented by DirectDraw; it is emulated by the HEL, and some video cards provide hardware support for scaling. Thus, you do not really need this algorithm to scale a bitmap. However, when you use DirectDraw to perform scaling, you are relying on the implementation present in the current driver or hardware of the end user’s machine. This could potentially lead to differing results from system to system. Using our own techniques, we can control the scaling, ensuring a consistent result on every machine. We could also employ interpolation techniques to determine the color of a scaled pixel from those around it. The resulting image could be of a much higher quality than that capable from DirectDraw, as it will not have the jagged edges produced by the DirectDraw scaling algorithms.
| Tip: To let DirectDraw perform scaling, use the Blt function and specify the desired size for the destination rectangle.
As with most of these bitmap manipulation effects, scaling a bitmap can be very computationally intensive. We could pre-compute the scaled pixel positions relative to an arbitrary starting point for the entire range of scaling values when the application starts. The resulting lookup table would be very fast, but it would probably take up loads and loads of memory, depending on the range of scaling values. It might be possible to provide several bitmaps at specific sizes and scale between them as needed. This might allow us to optimize the scaling routine, but it would require more storage space for bitmaps. However, this would allow us to improve the quality of the images as they get larger. #### Rotation Most top-down, 2-D, sprite-based games feature sprite images that seem to be able to rotate in any direction. This could be accomplished by drawing the sprite image in all possible rotations, but this would be required for all frames of animation and would result in a large number of images for even one sprite. While this is a perfectly acceptable way of implementing rotation, we could use bitmap rotation equations to allow us to rotate images to any arbitrary angle while saving on memory space. Basic Theory You may remember from trigonometry that it is mathematically possible to rotate a point about the origin by an arbitrary angle. Given an original x and y coordinate and an arbitrary angle, a point can be rotated using the following equations:
    New_X := Original_X * Cos(Angle)  Original_Y * Sin(Angle);

    New_Y := Original_X * Sin(Angle) + Original_Y * Cos(Angle);
This new point is rotated about the origin by the specified angle, as illustrated below. Figure 11-3: Rotating a point
| Caution: A common mistake made when implementing these equations is to rotate the x coordinate, and then use this newly rotated value in the rotation equations for the y coordinate. The result will be a highly skewed image which will eventually shrink into nothing if rotations continue. This is why you must store the rotated values into separate variables.
If we treat each pixel in a bitmap as a point, we can use these equations to rotate the pixels in the bitmap. Now, most bitmap rotation algorithms map the pixels from the source bitmap to the destination bitmap, but most of the time, this results in a rotated image that contains holes. Therefore, we should iterate through the pixels in our destination image, determining the pixels we should draw from the source image. If we determine the center of the source image, like so:
    CenterX := (SourceImageWidth / 2);

    CenterY := (SourceImageHeight / 2);
then we can determine which pixels to pull from the source image based on the pixel being drawn in the destination image using these equations:
    SrcX := Trunc(CenterX+(DestX-CenterX)*Cos(Angle)-(DestY-CenterY)*Sin(Angle));

    SrcY := Trunc(CenterY+(DestX-CenterX)*Sin(Angle)+(DestY-CenterY)*Cos(Angle));
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.