이번에는 그림을 회전하고 반전해서 저장하는 기법을 알아봅시다.
Graphics32 에서는 그림을 회전시키는 방법을 4가지로 제공합니다.
1. 90도 회전 procedure Rotate90(Dst: TBitmap32);
2. 180도 회전 procedure Rotate180(Dst: TBitmap32);
3. 270도 회전 procedure Rotate270(Dst: TBitmap32);
4. AffineTransformation 회전 procedure Rotate(Cx, Cy, Alpha: Single);
4번은 임의의 각으로 회전이 가능합니다.
이번 강좌에서는 90도 회전과 270도 회전만 이용합니다.
반전 방법은 수직 반전(FlipVert) 수평반전(FlipHorz) 의 두 가지 방법을 제공합니다.
1. procedure FlipHorz(Dst: TBitmap32);
2. procedure FlipVert(Dst: TBitmap32);
그럼 이 기능을 어떻게 구현할 것인가. 기본 아이디어는 다음과 같습니다.
1. 회전기능을 가지는 폼을 화면에 보여준다.
2. 폼 생성시 임시 TBitmap32 를 생성한다.
3. 임시로 생성한 TBitmap32 를 회전시킨 후 ImgPreview 에 복사한다.
4. ImgPreview 에 회전시킨 이미지가 보여진다.
회전기능을 위한 폼은 다음과 같습니다.
Rotate 메뉴의 이벤트 핸들러는 다음과 같습니다.
void __fastcall TMainForm::Rotate1Click(TObject *Sender)
{
UndoBitmap32->Assign(ImgView321->Bitmap); //이전의 이미지를 저장
RotateForm->ImgPreview->Bitmap->Assign(MainForm->ImgView321->Bitmap);
RotateForm->ShowModal();
if(RotateForm->ModalResult == mrOk) //OK 버튼을 누르면
{
ImgView321->Bitmap->Assign(RotateForm->ImgPreview->Bitmap);
}
}
UndoBitmap32 는 회전을 실행하기 전에 현재의 이미지를 저장하는 것입니다.
RotateForm 에서 미리보기 기능을 제공하는 ImgView32 컴포넌트의 Bitmap 에 MainForm 의
ImgView32 의 Bitmap을 복사합니다. ShowModal 하면 ImgPreview 에 나타납니다.
마찬가지 원리로 OK 버튼을 누르면 앞에서와 반대로 ImgView321 의 Bitmap에 ImgPreview 의
Bitmap 을 복사합니다.
그럼 , Undo 는 어떻게 구현할까요?
TBitmap32* UndoBitmap32;
UndoBitmap32 = new TBitmap32;
void __fastcall TMainForm::Undo1Click(TObject *Sender)
{
ImgView321->Bitmap->Assign(UndoBitmap32);
}
TBitmap32 객체를 하나 만들고, 회전을 시키기 전에 Bitmap 을 복사한 후 Undo 를 클릭하면
UndoBitmap32 의 Bitmap 을 ImgView321 의 Bitmap 에 복사하는 것입니다.
복잡해 보이지만 의외로 쉽게 구현이 됩니다. Undo 가 여러번 되도록 할 수도 있겠지만 일단은
간단하게 구현하는 것이 목표입니다.
그럼 , 가장 중요한 Rotate 기능은 어떻게 구현할까요?
한가지 조심해야 할 것이 있습니다. 저도 꽤나 고민했던 부분입니다. Bitmap 을 그냥 돌리면
안됩니다.
TBitmap32* ImgTemp;
ImgTemp = new TBitmap32();
void __fastcall TRotateForm::btnRotatePlusClick(TObject *Sender)
{
ImgTemp->Assign(ImgPreview->Bitmap);
ImgTemp->Rotate90(ImgPreview->Bitmap);
}
임시로 사용할 TBitmap32 객체를 만들고 ImgPreview 의 Bitmap 을 복사하고, ImgTemp 를
Rotate 시켜서 ImgPreview 의 Bitmap 에 보냅니다. 처음에는 ImgPreview 의 Bitmap 을 Rotate 시키면
될 줄 알았는데 원하는 대로 동작하지 않더군요. 예제가 많으면 참고라도 하겠는데 생각보다
많지가 않습니다. 직접 해보면서 익히는 수밖에 없습니다.
그럼 , jpg 저장은 어떻게 할까요?
이것또한 문제가 많습니다. Graphics32 는 왜 32일까요? 그것은 PixelFormat 을 32bit 로 처리하기 때문입니다.
무조건 32bit 로 처리하므로 효율이 좋아서 속도가 향상됩니다. 하지만 jpg 에서는 32bit 를 지원하지
않습니다. 빌더에서 제공하는 Jpeg 유닛에서는 8bit, 24bit 를 지원합니다. 따라서 TBitmap32 의
이미지를 Jpeg 객체에 직접 Assign 하면 에러가 납니다. Graphics32 에서는 PixelFormat 을
바꿀 수가 없습니다. 이것을 가지고 또 고민을 많이 했습니다. jpg 로 저장하는 예제가 없어서
직접 해보면서 할 수 밖에 없습니다. 뉴스그룹을 찾아서 간신히 힌트를 얻는데 성공했습니다.
뉴스그룹에서도 Tutorial 이 없는것에 불만이 많더군요. 결론은 직접해보라는 것뿐...
해결 방법은 TBitmap 객체를 만들어서 TBitmap32 객체의 이미지를 복사하는 것입니다.
TBitmap 의 이미지는 Jpeg 객체에 Assign 할 수 있습니다. 그 뒤에는 SaveToFile 하면 됩니다.
jpg 저장에 대해서 다음을 참고해 보세요.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_faq&no=47
void __fastcall TMainForm::Save1Click(TObject *Sender)
{
if(ImgView321->Bitmap->Empty() == true) return; //그림이 없으면 저장하지 않음
Graphics::TBitmap* ImgTemp = new Graphics::TBitmap();
TJPEGImage* jp = new TJPEGImage();
try
{
ImgTemp->Assign(ImgView321->Bitmap);
jp->CompressionQuality = 100; //Quality 100%
jp->Assign(ImgTemp);
jp->SaveToFile(ImgFileName);
}
__finally
{
delete jp;
delete ImgTemp;
}
}
완성되면 다음과 같이 됩니다.