IntroductionThis article describes a Delphi project for bitmap rotation.
There are 3 units:
- rotation_unit : procedures for bitmap rotation
- clock_unit : time measurement procedures
exerciserThe form has buttons for loading and saving bitmaps.
Also 3 modes of rotation are selectable
- medium: somewhat slower but destination bitmap is fully covered
- fine: slow, but with soft edges
Moving the mousepointer over the paintbox with leftmousebutton pressed,
causes the picture to rotate in the selected mode.
Below is an example of medium mode rotation
In coarse mode, the source bitmap is scanned pixel by pixel and projected on the destination bitmap.
Therefore, not every pixel of the destination bitmap may be covered.
In medium mode, the pixels of the destination bitmap are scanned and their value is loaded
from the source bitmap.
This insures that all pixels of the destination bitmap are covered.
In fine mode, the scanning is the same as in medium mode, but each pixel is divided in 9 equal parts.
Parts may cover different pixels in the source map, the colors are averaged for the final
color of the destination pixel.
ProgrammingThe programmer has to create both the source and the destination bitmap.
Before a rotation may take place, the rotation_unit has to be informed about the names of the bitmaps.
This is done by a call to
procedure setmaps(sourcemap,destination map)Setmaps sets the pixelformat of both bitmaps to 32 bit.
Also, the dimensions of the destination bitmap are adjusted to accomodate
all possible rotations of the source map.
Hereafter, images may be drawn in the source map and
procedure coarserotate(deg : word) procedure mediumrotate(deg: word) procedure finerotate(deg: word)may be called.
Deg is the rotation angle in degrees from 0..360
Rotation is clockwise, so, for a left rotation of 90 degrees, 270 degrees must be specified.
Do not forget to call the setmaps procedure after loading an image from a file into the source map.
This insures the proper 32 bit format and dimensions of the destination map.
Medium modePicture below shows the coordinate system relative to the bitmaps:
Also, width and height are odd.
The maps are divided into 4 quadrants
2 : left bottom
3 : left top
4 : right top
For quadrants 1 and 4, a row is scanned left to right starting at a certain y position.
For quadrants 2 and 3, a row is scanned right to left starting at a y position.
So, for a certain rotated position (x,y) in the coordinate system, the coordinates of the original
position have to be calculated.
Then these positions are used to calculate the corresponding pixels in the source and destination maps.
Let's observe a rotation of 30 degrees (clockwise) using the above bitmaps
Black pixels of the destination map are scanned, the color is obtained from to corresponding
red pixel of the source map.
Rotation calculationNow we describe how the position on the source map is calculated given position (x,y) of the destination map.
(x,y) is regarded as the vector addition of (x,0) and (0,y), so the x and the y vectors.
These vectors are rotated separately, then the resulting vectors are added to obtain the rotated (x,y) position.
the color for (x,y) on the destination map.
Rotation of the horizontal vector results in OD.
Rotation of the vertical vector results in OC.
OD is the addition of (horizontal) vector OB and (vertical) vector BD.
OC is the addition of (horizontal) vector OA and (vertical) vector AC.
Addition of the horizontal vectors make x', addition of the vertical vectors make y'.
sin( ) and cos( ) functions need the angle in radians.
The call to the rotation procedure supplies the angle in degrees.
Constant deg2rad = p / 180 converts degrees to radians by
Now, for quadrant 1 :
Addressing the pixels in the bitmapsOf coarse, Delphi property pixels[x,y] may be used to change pixelvalues.
However, this way is utterly slow.
Also, the scanline[y] property, which returns the memory pointer of the first pixel in row y, is very slow.
Better is to calculate the pointer to a certain pixel.
Because the bitmaps are in 32 bit format we first define
type PDW = ^dword;PDW is a pointer to a 32 bit unsigned variable.
Address calculations are done with variables in dword ( = cardinal) format.
First, the pointer to pixel [0,0] must be obtained.
For this, scanline is used by the setmaps procedure.
scanline returns the pointer to pixel [0,1].
scanline - scanline gives the pointer difference between two rows.
(note: row 1 pointer is smaller then the row 0 pointer).
In the rotation procedure
trunctx := trunc(tx); //tx is floating point format truncty := trunc(ty); //... ttx := scx + trunctx; //add center tty := scy + truncty; PS := PSBM - tty*Slinestep + (ttx shl 2); //pointer to source pixel pix := PDW(PS)^;pix receives the color value of the source map pixel.
For quadrant 1, the pointer to the destination map is
Ybase1 := PDBM - (dcxy + y)*Dlinestep; PD := Ybase1 + ((dcxy+x) shl 2); PDW(PD)^ := pix;
Fine RotationThis is an addition to medium rotation.
Pixel scanning in both modes is the same, however a pixel is subdivided into 9 subpixels:
This table is generated for a particular rotation angle and holds the x and y offsets per quadrant
of the subpixel relative to the left top.
For each subpixel position on the source map, the RGB colors are summed.
The final color is the summed color divided by 9.
So, this method is basically nine times slower then the medium mode rotation.
For more details I refer to the source code of the rotation project.
DonationsThis software is free to use.
Programming however is time consuming and hard work.
If you make commercial use of my rotation procedures, therefore consider a donation.
Please contact me for details.