Perl / Win32::GUI::DIBitmap / Win32::Clipboard : 스크린샷 저장 시 저장되는 비트 수에 주의
| 분류: Perl | 최초 작성: 2011-11-24 00:24:41 |
Win32::GuiTest 모듈의 SendKeys 함수를 이용하면, 현재 화면 캡쳐를 매우 간단하게 수행할 수 있습니다. PrtScr (Print Screen) 버튼을 누르면 현재 화면이 캡쳐되는 것은 이미 만인의 상식이 되어버렸으므로, SendKeys 함수를 이용하여 PrtScr 키를 입력하도록 해 버리면 되는 것입니다. 다만 이렇게 캡쳐된 화면은 클립보드로 들어가므로, 적절한 위치에 붙여넣기를 해 주어야 하겠지요.
제 프로그램 중에서 이 기능을 이용하는 프로그램이 두 개 있습니다. 하나는 클립보드의 내용을 선택적으로 저장해 두었다가 나중에 다시 쓸 수 있도록 하는 Clipboard Drawer 이고, 다른 하나는 스크린 캡쳐 & PNG 파일로의 저장을 마우스 클릭 한 번으로 처리하려 만든 PrintScreen & Autosave 입니다. (귀찮다는 이유로 더 귀찮은 짓을 해버리는 기묘한 인간을 보고 계십니다.)
* Clipboard Drawer :
http://www.nightowl.pe.kr/software/clipdrawer* PrintScreen & Autosave :
http://www.nightowl.pe.kr/software/prtscrsave이렇게 클립보드로 들어간 데이터는 특정 CPAN 모듈 (Win32::Clipboard) 을 이용하여 불러온 후 파일로 저장할 수 있습니다. 바로 비트맵 파일로 저장도 가능하고, 만약 (저처럼) 디스크 용량 차지의 문제라던가, 기타 활용을 위해서 PNG 파일로 저장을 원할 수도 있습니다. 바로 비트맵 파일로 저장한다면 굳이 다른 모듈을 쓸 필요도 없이 데이터를 그대로 저장하면 되고, PNG 파일로 압축하여 저장하고자 할 때에는 제가 Win32::GUI 모듈을 이용하여 GUI를 구현하고 있으므로, (굳이 별도로 GD 모듈까지 동원할 필요 없이) Win32::GUI 모듈에 포함되어 있는 Win32::GUI::DIBitmap 모듈을 이용하면 됩니다.
그런데, 무신경하게 코드를 짜고 테스트를 하다가, 좀 미묘한 문제를 만났습니다. 예전에 PrintScreen & Autosave 프로그램을 만들어 테스트할 때도 발생했던가 싶은 문제인데 그동안 잊고 있다가, 최근 Clipboard Drawer 프로그램을 만들면서 똑같은 문제를 맞닥뜨렸습니다. 다음 그림을 보시죠.
아래쪽 이미지가 문제. 전체적으로 투명 픽셀들이 나타나 이미지를 전체적으로 왜곡하고 있다.
위 이미지는 현재 제 바탕화면을 찍은 것입니다. 위쪽 화면은 다른 프로그램을 이용해 저장한 스크린샷이고, 아래쪽 화면은 PrintScreen & Autosave (0.0.2.0) 프로그램으로 제 바탕화면을 찍은 것이죠. 뭔가 다릅니다. 지금 저렇게 보니까 그나마 이미지가 있어 보이는데, 정확히는 아래 그림의 하얗게 보이는 부분을 이미지 뷰어로 읽으면 죄다 투명 픽셀로 처리되어 보이지 않게 되어 버리는 것입니다. 예를 들면 아래와 같이 나온다는 거죠.
Imagine 으로 보이는 그대로를 캡쳐한 것. 투명 픽셀들이 명확히 드러난다.
대체 문제가 뭘까요? 일단 코드를 보시죠. PrintScreen & Autosave 프로그램의 해당 부분의 코드는 다음과 같이 되어 있습니다. 이미 캡쳐된 이미지는 클립보드에 들어와 있다고 가정합니다.
1: use Win32::GUI::DIBitmap;
2: use Win32::Clipboard();
3:
4: my $Filename = GetDateTime(time);
5: my $bitmap = Win32::Clipboard::GetBitmap();
6: my $dib = newFromData Win32::GUI::DIBitmap ($bitmap);
7: undef $bitmap;
8:
9: $dib->SaveToFile("$Filename.png", FIF_PNG ); # 알아서 저장하므로 FIF_PNG는 없어도 된다.
10: undef $dib;
11: undef $Filename;
GetDateTime 서브루틴은 그냥 지금 현재 날짜,시간을 확인하여 파일명으로 사용할 수 있도록 YYYYMMDDHHMMSS 의 형태로 돌려주는 서브루틴이므로 신경쓰지 않으셔도 됩니다. 라인 5에서 클립보드의 데이터를 비트맵으로 읽어오고, 이를 라인 6에서 다시 받아 Win32::GUI::DIBitmap 객체로 되돌립니다. 그리고 이를 라인 9에서 PNG 파일로 자동 변환하여 저장합니다.
코드 자체에는 아무런 문제가 없어 보입니다. (실제로 없습니다. 한번 실행해 보세요. 단 4열의 파일명 정하는 코드는 적절히 수정하시고요. 물론 Win32::GUI 와 Win32::Clipboard 모듈은 CPAN에서 받아서 설치하시고..) 그런데 이 코드로 스크린샷 찍었다 하면 저 위 그림과 같은 난리가 나 버립니다.
도대체 문제가 뭘까 하고 이것저것 찾아보다가, 바로 위 그림의 이미지 뷰어 - Imagine - 덕분에 문제 해결의 힌트를 얻었습니다. 아래 그림은 Imagine 에서 보여주는 상태 표시줄의 파일 정보인데, 위쪽 그림은 정상적으로 보이는 스크린샷 파일의 정보이고, 아래쪽 그림은 문제를 일으키는 스크린샷 파일의 정보입니다.
다른 프로그램으로 저장한 스크린샷은 24bpp, 필자의 프로그램으로 저장한 스크린샷은 32bpp이다.
원인은 24bit와 32bit의 차이였습니다. 즉, Win32::GUI::DIBitmap 모듈이 내부적으로 비트맵 데이터를 처리하는 과정에서 비트수를 늘린 덕에 이런 문제가 발생했다는 것입니다. 비트맵 이미지를 저장하기 전에 데이터를 강제적으로 24비트로 다운시키는 코드를 삽입한 후 문제가 해결되었습니다.
1: use Win32::GUI::DIBitmap;
2: use Win32::Clipboard();
3:
4: my $Filename = GetDateTime(time);
5: my $bitmap = Win32::Clipboard::GetBitmap();
6: my $dib = newFromData Win32::GUI::DIBitmap ($bitmap);
7: undef $bitmap;
8: my $to24bits = $dib->ConvertTo24Bits(); # 24비트로 비트 수 다운그레이드
9: undef $dib;
10:
11: $to24bits->SaveToFile( "$Filename.png", FIF_PNG ); # 알아서 저장하므로 FIF_PNG는 없어도 된다.
12: undef $to24bits;
13: undef $Filename;
정리하자면, 처음에는 비트맵 데이터를 막바로 BMP로 저장하다가, 스샷 하나에 3메가바이트씩 먹어대는 걸 보고 기가 질려서, 늘상 사용하던 모듈을 이용해 PNG 파일로 저장하도록 수정하려다가 겪은 묘한 문제였습니다.
한줄 요약 : Win32::GUI::DIBitmap 모듈로 Win32::Clipboard 모듈에서 읽은 클립보드 비트맵을 처리할 때는 반드시 24비트로 비트수 다운그레이드를 하세요.** 이 글은 네이버 Perl Community & Study 카페에도 등록되어 있습니다.