[오픈캡쳐] 선택영역 지정기능 개선

오픈 캡쳐에서 한가지 불만이었던 점은 선택영역 지정시에 투명부분까지 지정되어 버린다는 것이었다. 투명부분은 선택되지 않도록 하려면 어떻게 해야 할까. 선택영역이 이미지의 범위를 벗어났을 경우 최대, 최소값을 이미지 경계로 정하면 되지 않을까.

오픈 캡쳐는 오픈 소스(Open Source)이지만 소스에 대한 설명은 별로 없다. 약간의 주석만 있을뿐 전체적인 작동에 대한 설명은 없다. 소스에 대해서는 자세히 알 수는 없지만 일부 함수가 어떤 기능을 하는지는 알 수 있었다. UI 부분에서 가장 핵심적인 부분은 uMain.pas 와 uMDI.pas 다.

uMain.pas  메인메뉴와 툴바, 전체적인 UI 담당.
uMDI.pas   이미지를 불러왔을 경우 해당 이미지에 대한 처리 담당.

고치려고 하는 것은 이미지를 불러온 이후의 기능에 대한 것이므로, uMDI.pas 를 수정해야 한다.

마우스를 누르고(Down), 떼고(Up) 끄는(Drag) 동작에 대한 것을 수정해야 한다. 이러한 동작에 대한 것은 이벤트 핸들러(Event Handler)로 지정되어 있다.


FAImage.OnDblClick := AImageDblClick;
FAImage.OnMouseDown := AImageMouseDown;
FAImage.OnMouseMove := AImageMouseMove;
FAImage.OnMouseUp := AImageMouseUp;

이벤트 핸들러는 각각의 이벤트에 대한 처리를 담당한다. FAImage 는 TImgView32 타입의 개체이다.

마우스 처리와 관련된 4개의 이벤트 핸들러 중에서 OnMouseUp 이벤트 핸들러인 AImageMouseUp 을 수정한다. 영역을 지정하기 위해서는 마우스를 누르고 끌기를 한다음 떼야 모든 과정이 끝나기 때문이다. 따라서, 떼는(Up) 동작이후에 영역을 다시 지정하면 된다.

 

if FDown = True then
  begin
    case ToolSel of
      tsDrag:
        begin
          FAImage.Cursor := CUR_DRAG;
          Screen.Cursor := crDefault;
        end;
      tsSelect:
        begin
          FAImage.Bitmap.Canvas.Rectangle(Rect(OldPt, nPt));

          nPt := SetZoomPoint(Point(X, Y));
          if EqualPointXY(OldPt, nPt) = False then
            with CreateBitmapLayer do
            begin
              NormalizePoint(OldPt, nPt);

              if OldPt.X < 0 then OldPt.X := 0;
              if OldPt.Y < 0 then OldPt.Y := 0;
              if nPt.X > FAImage.Bitmap.Width then nPt.X := FAImage.Bitmap.Width;
              if nPt.Y > FAImage.Bitmap.Height then nPt.Y := FAImage.Bitmap.Height;

              Bitmap.Width := nPt.X - OldPt.X;
              Bitmap.Height := nPt.Y - OldPt.Y;

              FAImage.Bitmap.ResetAlpha;

              Bitmap.Draw(0, 0, Rect(OldPt, nPt), FAImage.Bitmap);

              FRBLayer.Location := FloatRect(OldPt.X, OldPt.Y, nPt.X, nPt.Y);

              // ** 이 후 마우스로 드래그 시 영역 삭제 ** //
              Tag := 1;
            end;
        end;

OldPt, nPt 는 상황에 따라 다른 의미를 가지게 된다. NormalizePoint 함수를 통해서 OldPt 는 좌상단, nPt 는 우하단 지점이 된다. Pt 는 현재 마우스 포인터가 있는 지점이다. NormalizePoint 는 OpenInterface.pas 에 다음과 같이 되어 있다. 

// 사용자 캡쳐 시 오른쪽 아래로 드래그로 캡쳐하지 않고 그 외에 다른 방향으로 캡쳐했을 때
// 오른쪽 아래로 드래그한 것과 같은 포인터로 돌려주는함수입니다.
procedure NormalizePoint(var FirstPt ,EndPt: TPoint);
var
 Pt ,Pt1: TPoint;
begin

 if FirstPt.X-EndPt.X > 0 then begin
  Pt.X := FirstPt.X;
  Pt1.X := EndPt.X;
  FirstPt.X := Pt1.X;
  EndPt.X := Pt.X;
 end;

 if FirstPt.Y-EndPt.Y> 0 then begin
  Pt.Y := FirstPt.Y;
  Pt1.Y := EndPt.Y;
  FirstPt.Y := Pt1.Y;
  EndPt.Y := Pt.Y;
 end;
 
end;



화면상의 좌표는 이미지의 좌상단을 (0, 0) 으로 해서 그 보다 왼쪽이거나 위쪽이면 음수가 나온다. 좌상단에서 우하단으로 드래그 해서 영역을 지정할 경우 FirstPt.X - EndPt.X 는 음수가 나온다. 양수가 나왔다면 우하단에서 좌상단, 또는 좌하단에서 우상단으로 드래그 했다는 이야기가 된다. 어떤 방향으로 드래그 했던 간에 무조건 선택영역의 좌상단을 OldPt, 우하단을 nPt 로 바꾸어 주는 함수가 NormalizePoint 이다.

따라서, 선택영역이 음수 좌표일 때 0으로 만들어 주는 조건문 2개와 선택영역이 이미지를 폭(Width)과 높이(Height)를 벗어났을 때 영역을 이미지의 폭과 높이로 만들어주는 조건문 2개가 필요하다.

if OldPt.X < 0 then OldPt.X := 0;
if OldPt.Y < 0 then OldPt.Y := 0;
if nPt.X > FAImage.Bitmap.Width then nPt.X := FAImage.Bitmap.Width;
if nPt.Y > FAImage.Bitmap.Height then nPt.Y := FAImage.Bitmap.Height;



이 코드를 추가한 결과를 확인해 보자.

Before

After