Вы находитесь на странице: 1из 5

unit ScreenRotateScanLine;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtDlgs, StdCtrls, ExtCtrls, Spin;

type
TFormRotateScanLine = class(TForm)
ImageOriginal: TImage;
ButtonLoad: TButton;
LabelImageInfo: TLabel;
ImageRotated: TImage;
LabelRotateTime: TLabel;
SpinEditThetaDegrees: TSpinEdit;
LabelDegreesClockwise: TLabel;
LabelRotationAngle: TLabel;
LabelCenter: TLabel;
SpinEditI: TSpinEdit;
SpinEditJ: TSpinEdit;
LabelI: TLabel;
LabelJ: TLabel;
CheckBoxStretch: TCheckBox;
SpinEditThetaDegreesHundredths: TSpinEdit;
Label1: TLabel;
OpenPictureDialog: TOpenPictureDialog;
ButtonSaveRotated: TButton;
SavePictureDialog: TSavePictureDialog;
procedure ButtonLoadClick(Sender: TObject);
procedure SpinEditRotate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure CheckBoxStretchClick(Sender: TObject);
procedure ButtonSaveRotatedClick(Sender: TObject);

private
{ Private declarations }
BitmapOriginal: TBitmap;
BitmapRotated : TBitmap;
public
PROCEDURE RotateBitmap;
end;

var
FormRotateScanLine: TFormRotateScanLine;

implementation
{$R *.DFM}

USES
{$IFDEF GIF}
GIFImage, // TGIFImage
{$ENDIF}
GraphicsConversionsLibrary, // LoadGraphicsFile
IniFiles, // TIniFile
ImageLib;

CONST
KeywordSetup = 'Setup';
KeywordDirectory = 'Directory';

// Load 24-bit color image from disk


procedure TFormRotateScanLine.ButtonLoadClick(Sender: TObject);
VAR
Filename: STRING;
NewPath : STRING;
IniFile : TIniFile;
begin
IF OpenPictureDialog.Execute
THEN BEGIN
BitmapOriginal.Free;
BitmapOriginal := LoadGraphicsFile(OpenPictureDialog.Filename);

IF BitmapOriginal.PixelFormat <> pf24bit


THEN BitmapOriginal.PixelFormat := pf24bit; // force to 24 bits

// Update INI file for next time


Filename := ChangeFileExt(ParamStr(0), '.INI');
NewPath := ExtractFilePath(OpenPictureDialog.Filename);
OpenPictureDialog.InitialDir := NewPath;
IniFile := TIniFile.Create(Filename);
TRY
Inifile.WriteString(KeywordSetup, KeywordDirectory, NewPath)
FINALLY
IniFile.Free
END;

// Flush INI cache


WritePrivateProfileString(NIL, NIL, NIL, pChar(Filename));

ImageOriginal.Picture.Graphic := BitmapOriginal;
LabelImageInfo.Caption := IntToStr(BitmapOriginal.Width) + ' by ' +
IntToStr(BitmapOriginal.Height) + ' pixels: ' +
OpenPictureDialog.Filename;

SpinEditI.Value := BitmapOriginal.Width DIV 2;


SpinEditJ.Value := BitmapOriginal.Height DIV 2;

// Rotate and display the image


RotateBitmap
END
end;

// Rotate image by angle[degrees] specified in spinbox


procedure TFormRotateScanLine.SpinEditRotate(Sender: TObject);
begin
RotateBitmap
end;

procedure TFormRotateScanLine.FormCreate(Sender: TObject);


VAR
IniFile : TIniFile;
begin
{$IFDEF GIF}
OpenPictureDialog.Filter := OpenPictureDialog.Filter +
'|GIFs|*.GIF';
{$ENDIF}

IniFile := TIniFile.Create(ChangeFileExt(ParamStr(0), '.INI'));


TRY
OpenPictureDialog.InitialDir := Inifile.ReadString(KeywordSetup,
KeywordDirectory,
ExtractFilePath(ParamStr(0)))
FINALLY
IniFile.Free
END;

BitmapOriginal := TBitmap.Create;
BitmapRotated := TBitmap.Create;
end;

procedure TFormRotateScanLine.FormDestroy(Sender: TObject);


begin
BitmapOriginal.Free;
BitmapRotated.Free
end;

PROCEDURE TFormRotateScanLine.RotateBitmap;
VAR
cosTheta : DOUBLE;
Delta : DWORD; // D3/D4 compatibility
i : INTEGER;
iRotationAxis : INTEGER;
iOriginal : INTEGER;
iPrime : INTEGER;
iPrimeRotated : INTEGER;
j : INTEGER;
jRotationAxis : INTEGER;
jOriginal : INTEGER;
jPrime : INTEGER;
jPrimeRotated : INTEGER;
RowOriginal : pRGBArray;
RowRotated : pRGBArray;
sinTheta : DOUBLE;
StartTime : DWORD;
Theta : DOUBLE; // radians
begin
// The size of BitmapRotated is the same as BitmapOriginal. PixelFormat
// must also match since 24-bit GBR triplets are assumed in ScanLine.
BitmapRotated.Width := BitmapOriginal.Width;
BitmapRotated.Height := BitmapOriginal.Height;
BitmapRotated.PixelFormat := pf24bit;

// "Start" the clock


StartTime := GetTickCount;

// Axis of rotation is normally center of image


TRY
iRotationAxis := SpinEditI.Value
EXCEPT
iRotationAxis := 0
END;

TRY
jRotationAxis := SpinEditJ.Value
EXCEPT
jRotationAxis := 0
END;

// Convert degrees to radians. Use minus sign to force clockwise rotation.


Theta := -(SpinEditThetaDegrees.Value +
SpinEditThetaDegreesHundredths.Value/100) *
PI / 180;
sinTheta := SIN(Theta);
cosTheta := COS(Theta);

// Step through each row of rotated image.


FOR j := BitmapRotated.Height-1 DOWNTO 0 DO
BEGIN
RowRotated := BitmapRotated.Scanline[j];

// Assume the bitmap has an even number of pixels in both dimensions and
// the axis of rotation is to be the exact middle of the image -- so this
// axis of rotation is not at the middle of any pixel.

// The transformation (i,j) to (iPrime, jPrime) puts the center of each


// pixel at odd-numbered coordinates. The left and right sides of each
// pixel (as well as the top and bottom) then have even-numbered coordinates.

// The point (iRotationAxis, jRotationAxis) identifies the axis of rotation.

// For a 640 x 480 pixel image, the center point is (320, 240). Pixels
// numbered (index i) 0..319 are left of this point along the "X" axis and
// pixels numbered 320..639 are right of this point. Likewise, vertically
// pixels are numbered (index j) 0..239 above the axis of rotation and
// 240..479 below the axis of rotation.

// The subtraction (i, j) - (iRotationAxis, jRotationAxis) moves the axis of


// rotation from (i, j) to (iRotationAxis, jRotationAxis), which is the
// center of the bitmap in this implementation.

jPrime := 2*(j - jRotationAxis) + 1;

FOR i := BitmapRotated.Width-1 DOWNTO 0 DO


BEGIN

iPrime := 2*(i - iRotationAxis) + 1;

// Rotate (iPrime, jPrime) to location of desired pixel


// Note: There is negligible difference between floating point and
// scaled integer arithmetic here, so keep the math simple (and readable).
iPrimeRotated := ROUND(iPrime * CosTheta - jPrime * sinTheta);
jPrimeRotated := ROUND(iPrime * sinTheta + jPrime * cosTheta);

// Transform back to pixel coordinates of image, including translation


// of origin from axis of rotation to origin of image.
iOriginal := (iPrimeRotated - 1) DIV 2 + iRotationAxis;
jOriginal := (jPrimeRotated - 1) DIV 2 + jRotationAxis;
// Make sure (iOriginal, jOriginal) is in BitmapOriginal. If not,
// assign blue color to corner points.
IF (iOriginal >= 0) AND (iOriginal <= BitmapOriginal.Width-1) AND
(jOriginal >= 0) AND (jOriginal <= BitmapOriginal.Height-1)
THEN BEGIN
// Assign pixel from rotated space to current pixel in BitmapRotated
RowOriginal := BitmapOriginal.Scanline[jOriginal];
RowRotated[i] := RowOriginal[iOriginal]
END
ELSE BEGIN
RowRotated[i].rgbtBlue := 255; // assign "corner" color
RowRotated[i].rgbtGreen := 0;
RowRotated[i].rgbtRed := 0
END

END
END;
Delta := GetTickCount - StartTime; // "stop" the clock
LabelRotateTime.Caption := 'Rotation Time = ' + IntToStr(Delta) + ' ms';

ImageRotated.Picture.Graphic := BitmapRotated

END {RotateImage};

procedure TFormRotateScanLine.CheckBoxStretchClick(Sender: TObject);


begin
ImageOriginal.Stretch := CheckBoxStretch.Checked;
ImageRotated.Stretch := CheckBoxStretch.Checked
end;

procedure TFormRotateScanLine.ButtonSaveRotatedClick(Sender: TObject);


begin
IF SavePictureDialog.Execute
THEN BitmapRotated.SaveToFile(SavePictureDialog.Filename)
end;

end.

Вам также может понравиться