[Eclipse RCP] SWT에서의 GC, Drawable, Canvas 사용

addPaintListener()

Control 객체가 그 객체의 paintEvent를 통하여 그림을 그릴 수 있지만, 이보다는 Control 클래스를 상속받아 그래픽 작업을 위해 설계된 특별한 클래스인 Canvas 클래스를 사용하여 그림을 그리는 것이 더욱 좋습니다. Canvas 클래스를 생성한 후 addPaintListener()를 통하여 paintListener를 추가하거나, 사용자가 직접 정의한 Control을 상속받음으로써 그림을 그릴 수 있습니다. Canvas 클래스는 그림을 그릴 때 사용할 수 있는 다양한 종류의 스타일 비트를 가지고 있습니다.

SWT.NO_BACKGROUND

Canvas의 기본적인 동작은 다음과 같습니다. 객체의 할당영역을 그리기 앞서, 객체에 할당된 전체 영역을 현재 배경색으로 칠합니다. 이때 화면이 깜박이는 현상을 볼 수 있는데 이는 paintEvent가 배경색을 칠하고, 그 위에 Canvas의 요소들을 덧칠하는 순간을 우리가 보는 것입니다. 이 현상을 방지하기 위한 방법 중 하나는, Canvas를 생성할 때 SWT.NO_BACKGROUND 스타일 비트를 사용하여 배경이 그려지지 않게 하는것입니다. 단, 이 비트를 사용할 경우, 프로그램은 영역 전체에 대하여 모든 픽셀을 직접 그려야 합니다.

SWT.NO_REDRAW_RESIZE

위젯의 사이즈가 변경될 때에도 paintEvent가 발생합니다. 이 역시 화면을 깜박거리게 할 수 있는데, 왜냐하면 사이즈가 변경될 때마다 해당 영역에 대해 모든 요소들을 다시 그려야 하기 때문입니다. 이는 SWT.NO_REDRAW_RESIZE 스타일 비트를 사용하여 완화시킬 수 있으며, 이는 컨트롤의 사이즈가 변경되더라도 paintEvent가 발생하지 않는다는 것을 의미합니다. 이는 컨트롤이 불필요하게 다시 그려지는 것을 방지한다는 의미를 갖으며, 만약 크기가 증가된다고 한다면, paintEvent의 GC는 이 영역을 제외한 부분만을 다시 그리게 됩니다. 단 사각형 모양이 Canvas가 존재할 경우 사각형의 오른쪽 하단에 L이 거꾸로 된 형태로 그려질 수 있습니다.

SWT.NO_REDRAW_RESIZE 스타일 비트는 새로 그려지는 부분에 대해서 적절히 처리해줄 경우, 고정사이즈의 그림이 GC에 그려질 때 나타나는 깜박임을 감소시킬 수 있습니다. 하지만 잘못 쓰였을 경우, SWT.NO_REDRAW_RESIZE는 치즈(cheese)라는 효과를 불러올 수 있습니다. 치즈는 다시 그려야 할 상황에 제대로 다시 그려지지 않는 부분이 있는 경우를 지칭합니다. paintEvent가 클라이언트 전 영역을 다시 그려야 하는 다음의 예시를 확인해 보세요. 화면이 작아질 때에는 paintEvent가 발생하지 않기 때문에 그림이 다시 그려지지 않습니다. SWT.NO_REDRAW_RESIZE 상태에서 화면이 커질 경우, paintEvent는 새로 그려야 하는 부분에 대해서만 영역을 다시 그립니다. 그래서 기존에 그려졌던 부분이 지워지지 않았기 때문에 치즈 현상이 발생하게 됩니다. 또한 Canvas 사이즈가 증가할 때 GC는 필요한 부분만 다시 그리기 때문에 치즈 현상이 발생하게 됩니다.

1
2
3
4
5
6
7
8
9
10
shell.setLayout(new FillLayout());
final Canvas canvas = new Canvas(shell,SWT.NO_REDRAW_RESIZE);

canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
Rectangle clientArea = canvas.getClientArea();
e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
e.gc.fillOval(0,0,clientArea.width,clientArea.height);
}
});

SWT.NONE

이 문제를 해결하는 방법은 SWT.NONE 스타일 비트를 사용하여 GC가 크기가 커질 때 커진 부분만 새로 그리는것이 아닌, 모든 부분을 새로 그리도록 하고, 동시에 paintEvent가 셸 사이즈가 작아졌을 때에도 발생하게 하여, Canvas의 전체 부분이 다시 그려지게 하는 것입니다.

1
final Canvas canvas = new Canvas(shell,SWT.NONE);

SWT.NO_MERGE_PAINTS

각각의 SWT 위젯에 대해, 만약 하나이상의 영역이 다시 그려져야 할 경우, 운영체제는 다시 그려야 할 영역을 하나의 영역으로 묶어서 paintEvent를 발생하여 효율적인 처리를 가능하게 합니다. Canvas의 SWT.NO_MERGE_PAINTS 스타일 비트는 각각의 위젯에서 다시 그릴 영역을 하나로 묶지 않고, 각각의 영역에 대해 일일이 paintEvent를 호출하는 방식으로 변경합니다.

정리

NO_BACKGROUND, NO_REDRAW_RESIZE, NO_MERGE_PAINTS 스타일 비트는 Composite와 이 하부 클래스인 Canvas, Shell, Group등에서 사용할 수 있습니다, SWT에서 허용하긴 하지만, Composite 클래스의 Javadoc에서는 스타일 비트에 대해서 다음과 같이 기술합니다. Canvas 이외의 Composite의 하위클래스에서의 사용은 정의되지 않았다. 그러므로 Canvas 클래스가 그림 그리는데에 가장 적합한 컨트롤이라 할 수 있습니다.

깜박임을 방지하는 또 다른 방법은 더블 버퍼링을 사용하여 그림 그리기를 한 번에 처리하는 것입니다. 더블 버퍼링은 paintEvent에서 제공하는 GC기 아닌 GC에 그림을 미리 그린 후, 이것을 제공되는 GC에 복사하는 기술입니다. 이를 위하여, Canvas의 영역과 동일한 사이즈의 Image 객체를 만들고, GC(Image)를 통하여 이 객체에 그림을 그립니다. 그려진 그림(Image)은 drawImage(Image, int, int) 메서드를 호출함으로써 paintEvent의 GC로 그려지게 됩니다. 이 기술을 사용할 때 주의할 점은 몇몇 운영체제는 이미 더블 버퍼링을 자체적으로 구현하고 있으므로, 결국 트리플 버퍼링이 이루어질 수 있다는 점입니다.

Share