[오픈캡쳐] 영역 지정 캡쳐시 십자선 그리기

오픈 캡쳐에서 아쉬웠던 점은 영역을 지정할 때 화면 전체에 가이드 역할을 하는 십자선이 표시되지 않는다는 점이었다. 십자선이 화면에 표시되도록 하려면 어떻게 해야 할까.


기본적인 아이디어는 다음과 같다.

1. 마우스 포인터가 위치한 곳을 기준으로 십자선을 그린다.
2. 마우스가 이동하면 이전 위치에 있던 십자선을 지운다.
3. 새로 이동한 위치에 새로운 십자선을 그린다.

즉, 그리고 지우는 것을 반복하면 십자선이 마우스 포인터를 따라서 이동하는 효과를 줄 수 있는 것이다. 그리고 지우는 것은 어떻게 구현해야 할까?

여러가지 방법이 있을 수 있다. 십자선을 그리기 전에 화면을 저장하고 마우스 포인터가 있는 곳에 십자선을 그린다. 그리고 이동하면 이전에 저장한 화면을 그리고 새로 십자선을 그린다. 그러나 이 방법은 선 몇 개를  그리기 위해서 화면 전체를 다시 그려야 한다. 더 단순한 방법은 없을까?

그것은 논리 연산의 특성을 이용하는 것이다.

논리 연산에 뭐가 있는가 생각해 보자. and, or, not, nand, nor, xor 이 생각난다. 그 중에서 not 과 xor 은 독특한 특성이 있다. 연산을 두 번 하면 원래 자신의 값이 나온다는 것이다.

not 연산은 부정의 부정은 자기 자신이 나온다는 것이 쉽게 이해된다.

예를 들어, 8비트 값 11010011 을 not 연산을 하면 00101100 이 된다. 이것을 다시 not 연산을 하면 11010011 이 되어서 원래의 값으로 돌아온다.

xor 연산의 특성을 알아보자. (비트간 배타적 논리합) http://ko.wikipedia.org/wiki/XOR

원래의 값 0011 을 0110 과 xor 연산을 해본다.

0011 xor 0110 = 0101

이제 나온값 0101 을 다시 0110 과 xor 연산을 해본다.

0101 xor 0110 = 0011

이제 xor 연산을 두 번 하면 원래의 값을 얻을 수 있다는 것을 알았다. 원래의 값이 나온다는 것은 십자선이 지워지는 효과를 낼 수 있다는 뜻이다.

델파이 에서는 pen mode 라는 것이 있다. 선을 그릴때 어떤 방식으로 그릴지를 정하는 것이다. 이중에서 pmNot, pmXor , pmNotXor 은 같은 자리에 선을 두 번 그리면 원래의 값이 나와서 선이 지워지는 효과를 낸다.

오픈 캡쳐에서는 캡쳐시에 penmode 가 pmNotXor 가 되도록 설정되어 있다. 그럼 이제 해야 되는 일은 뭘까. 이동한 좌표와 이동전 좌표를 찾아서 각각 선을 한번식 그려주면 된다.

CaptureEngine 디렉토리에는 캡쳐시에 필요한 함수와 유저 인터페이스 관련 코드가 있다. 그 중에서 영역 지정 유저 인터페이스 부분은 uCapture.pas 에 있다.

마우스를 이동할 때 십자선이 그려지고 지워지는 일이 반복되므로 OnMouseMove 이벤트 핸들러에 코드를 넣어야 될 것 같지만 그렇게 하면 너무 느리다. 다행히도 오픈 캡쳐는 쓰레드 타이머를 이용해서 고속으로 Zoomviewer 를 업데이트 해서 보여준다. 차라리 Zoomviewer 를 업데이트 해주는 procedure 에 꼽사리 끼는 것이 낫다.

Zoomviewer 를 업데이트 해주는 procedure 는 다음과 같다.

procedure TfrmCapture.tmrZoomThreadTimer(Sender: TObject);


여기에는 마우스 포인터의 현재 위치를 저장하는 MousePoint 와 이전의 위치를 기억하는 tmrPt 가 있다. MousePoint 와 tmrPt 가 달라졌을 때  즉, 위치가 변했을 때 십자선을 지우고 그리는 코드를 넣으면 된다. 십자선을 그리는 방법은 단순하다. 마우스 포인터의 x 좌표와 화면의 y 좌표의 0 으로 이동후 (MoveTo) 마우스 포인터의 x 좌표와 화면의 y좌표의 아래쪽 끝으로 선을 그리면(LineTo) 된다. 초기에는 듀얼 모니터를 생각하지 않고 좌표를 설정했는데 오픈 캡쳐 개발자인 정룡옥님께서 수정해 주셨다. 다음은 수정된 코드이다.


 if EqualPoint(MousePoint, tmrPt) = False then begin
  BitmapWidth := (MousePoint.X + 24) - (MousePoint.X - 25);
  BitmapHeight := BitmapWidth;
 

  with FImgZoom do begin
   BeginUpdate;
   Bitmap.Clear(clWhite);
   if (UserCaptureType = ctUserCapture) and (ShapeType <> stPolygon) and (ShapeType <> stPolyline) then

   begin
    if FDown = False then
    with imgBackBuffer.Canvas do
    begin
     if (tmrPt.X <> -1) and (tmrPt.Y <> -1) then begin
      //캡쳐 화면에서 이전 위치의 십자선을 지운다. (XOR 연산의 특성을 이용)
      MoveTo(tmrPt.X, 0);
      LineTo(tmrPt.X, Screen.DesktopHeight);
      MoveTo(0, tmrPt.Y);
      LineTo(Screen.DesktopWidth, tmrPt.Y);
     end;


     //캡쳐 화면에 십자선을 그린다.  (현재 PenMode = pmNotXor)

     MoveTo(MousePoint.X, 0);
     LineTo(MousePoint.X, Screen.DesktopHeight);
     MoveTo(0, MousePoint.Y);
     LineTo(Screen.DesktopWidth, MousePoint.Y);
    end;
   end;




그럼, 여기에서 끝인가? 여기에서 클릭해서 끌기(Drag)를 하면 화면에 십자선이 그려진 채로 영역을 지정하는 사각형이 나온다. 그럼 모양이 보기 좋지 않으므로 OnMouseDown 이벤트 핸들러에서 이전의 위치 tmrPt 에 선을 다시 그려주는 코드를 넣으면 된다. 


 if (UserCaptureType = ctUserCapture) and (ShapeType <> stPolygon) and (ShapeType <> stPolyline) then
    begin
      with imgBackBuffer.Canvas do
      begin
       //캡쳐 화면에서 이전 위치의 십자선을 지운다. (XOR 연산의 특성을 이용)
       MoveTo(tmrPt.X, 0);
       LineTo(tmrPt.X, Screen.DesktopHeight);
       MoveTo(0, tmrPt.Y);
       LineTo(Screen.DesktopWidth, tmrPt.Y);
      end;
    end;




그런데 문제가 또 있다. 다른 캡쳐 프로그램은 클릭하면 영역 지정 모드가 되어서 십자선이 사라지고 사각형 영역이 표시되어 마우스 끌기(Drag) 하면서 영역을 지정한다. 처음에는 이것 어떻게 해결하지 못해서 OnClick, OnDblClick 이벤트 핸들러에 까지 같은 코드를 넣는 삽질을 했다. 그래도 만족스럽지가 않아서 고민을 하고 있던중 OnMouseUp 이벤트 핸들러에 FDown = False; 가 있는 것을 발견했다. 마우스 버튼을 떼면 (Up) 버튼이 눌러진 상태를 표시하는 FDown 값이 False 로 변하는 것이다. MouseUp 해도 상태를 유지시키려면 문장을 삭제하면 된다.

개발자인 정룡옥님께 물어보니까 오래전에 무슨 목적을 가지고 삽입한 코드인데 지금은 기억나지 않는다고... 흔쾌히 코드를 수정해 주셔서 모든 문제가 해결되었다.