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;
여기에서는 출력 픽셀을 입력 이미지로 매핑시키게 됩니다. 이것을 사용해 이미지의 회전을 부드럽게 할 수 있습니다.
원점 (0, 0)을 기준으로 점(x, y)를 Theta 각도만큼 회전시키려 한다면, 결과값(x', y')은 다음과 같습니다.
x' = x * cos(theta) + y * sin(theta)
y' = -x * sin(theta) + y * cos(theta)
theta에 대해 반대방향으로 회전 할 경우의 각도는 -theta입니다. 즉, 출력 픽셀을 입력 위치로 매핑시키기 위해서는 회전 된 반대 방향으로 같은 각도만큼 돌리면 될 것이며, 이렇게 하기 위해서는 -theta를 사용하면 됩니다. 만약, 출력 픽셀이 (x', y')이라면 그에 대한 입력 픽셀(x, y)는 다음과 같습니다.
x = x' * Cos(-theta) + y' * Sin(-theta)
y = -x' * Sin(-theta) + y' * Cos(-theta)
여기에서 Sin(-theta)는 -Sin(theta)이고 Cos(-theta)는 Cos(theta)와 같으므로,
x = x' * Cos(theta) - y' * Sin(theta)
y = x' * Sin(theta) + y' * Cos(theta)
의 공식을 사용해 프로그램에서 이미지를 회전시킬 수 가 있습니다.
출력 이미지의 각 픽셀에 대해서, 프로그램은 출력 픽셀에 매핑되는 입력 이미지내의 점을 찾기 위해 위의 공식을 사용합니다.
이 코드에서는 위에 설명했던것과는 조금 다르게 이미지 왼쪽 상단 모서리가 아닌, 이미지의 중심을 기준으로 해서 회전을 시킵니다. 그렇기 때문에, 계산이 진행되기 전에 출력 픽셀의 위치로부터 (to_cx, to_cy)값이 빼졌고, 결과에 (from_cx, from_cy)가 더해졌습니다.
마지막으로, 회전을 할 경우에 고려해야할 사항이 하나 더 있습니다.
회전이 되면 원본 이미지보다 바깥쪽으로 모서리가 벗어날 수 있어서 짤리게 됩니다. 180' 회전이 아니면, 거의 항상 그렇죠? 그래서, GetRotateSize라는 프로시져를 이용해 출력될 이미지의 높이와 너비를 계산하게 됩니다. 아래에 그 공식이 나와 있습니다.
참고로 아래의 코드는 박상윤씨라구 잘생긴 분이 한거랍니다. ^^
(박상윤씨가 이글 보면 술 한잔 사주~~~)
xc:= trunc(bi^.bmiHeader.biWidth/2 ); // 이미지의 중앙을 원점으로 잡는다.
yc:= trunc(bi^.bmiHeader.biHeight/2);
radian:=degree*3.141592/180; //각도를 라디안으로 변화
for i:=-yc+1 to yc do begin
for j:=-xc+1 to xc do begin //중심이 중앙이므로 -에서 +까지 처리한다.
//+1을 한이유는 배열이 1부터이기때문
v:=(i-py); //이동 변환
u:=(j-px);
y:=(u*sin(radian)+v*cos(radian))/zy; //확대축소 및 회전 변환
x:=(u*cos(radian)-v*Sin(radian))/zx;
if y>0 then //실수 좌표에서 정수 좌표로 변환하여 차이 p,q를 구함
m:=trunc(y) //양수 일 때 와 음수 일 때 틀려지는 것은 정수점을
else //구하는 조건이 x,y를 넘지 않는 정수이기 때문이다.
m:=trunc(y-1); //음수의 경우 넘지 않는 다는 의미는 -1만큼 이동하여
if x>0 then //정수를 구해야 한다는 것을 의미한다.
n:=trunc(x)
else
n:=trunc(x-1);
q:=y-m; //실수좌표와 정수 좌표의 차이계산
p:=x-n;
if((m>=-yc+1)and(m<=yc) )and ((n>=-xc+1)and(n<=xc)) //원하는 이미지크기
then begin //만큼만 계산함
d:=trunc((1.0-q)*((1.0-p)*data[n+xc,m+yc]+p*data[n+1+xc,m+yc])
+q*((1.0-p)*data[n+xc,m+1+yc]+p*data[n+1+xc,m+1+yc]));
end //선형 보간법에 의한 데이터계산
else
d:=255
아래 예제를 기술하시고 마우스로 이미지를 강제로 회전시키면
모든 각도로 회전됩니다.
참고 하시고, 님도 돈 많이 버세요.
그럼~ 항상 즐거운 프로그래밍 하시길~~
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.