GDI+는 잘 알다시피 전통적인 윈도우 API가 제공하는 GDI에서 더 나아가, 더 향상된 그래픽과 더 깔끔해진 프로그래밍 패러다임을 제공하는 그래픽 API이다. 사실, 닷넷에서는 애초에 기본 그래픽 API가 GDI+이다. (System.Drawing 네임스페이스)
GDI+는 벌써 10년 전, 닷넷 프레임워크가 첫 등장하고 윈도우와 오피스에 XP라는 브랜드가 붙던 시절에 도입되었다. MS가 제공하는 API로는 흔치 않게 C언어 함수도 아니고 그렇다고 DirectX 같은 COM도 아닌, C++ 언어 형태로 되어 있다. 물론, 그래도 실제로 링크를 해 보면 symbol들은 다 C언어 함수 호출 형태로 된다. C++ 클래스는 단순히 C 함수를 호출하는 wrapper인 것이다.
GDI+는 여러 편리한 기능을 많이 제공하지만, 무엇보다도 벡터 그래픽에 안티앨리어싱을 넣기 위해서라도 쓰지 않을 수 없다. 이런 간단한 기능은 그냥 기존 GDI 함수에다가도 옵션을 확장해서 좀 넣어 주지 하는 아쉬움이 있다. 재래식 GDI로도 안티앨리어싱된 텍스트는 얼마든지 찍을 수 있는 것처럼 말이다. (LOGFONT 구조체에 글꼴의 품질을 지정하는 추상화 계층이 있기 때문에, 나중에 추가된 안티앨리어싱 기능도 얼마든지 지정 가능)
재래식 GDI는 열악하던 컴퓨터 환경에서 최대한 장치 독립적인 추상적인 그래픽 계층을 구현하는 게 목표였다. 그래서 래스터 그래픽보다는 벡터 그래픽에, 애니메이션보다는 정적인 그래픽에 초점이 가 있었다. 그 추상화 계층이 확장성 측면에서 편리한 점은 분명 있었지만, 색깔을 하나 바꾸려고 해도 펜이나 브러시를 다시 만들고, 도스 시절의 그래픽 프로그래밍 때는 할 필요가 없었던 GDI 객체 관리를 해야 하니 상당히 불편했다.
그래서 GDI는 게임 그래픽용으로는 적합하지 않은 구석이 있었다. 물론, 지금과 같은 틀을 유지하면서도 JPG/PNG 이미지를 지원하고, 알파 채널 비트맵 Blit이나 별도의 gradient fill 함수를 추가하고, 윈도우 2000처럼 펜이나 브러시의 색깔을 손쉽게 바꿀 수 있는 DC pen/DC brush 같은 기능을 stock object로 넣는 등, 기능 개선이 꾸준히 진행돼 왔다. 하지만 MS 측에서는 이에 만족하지 못하고 이 참에 API를 근본적으로 갈아엎고 싶다는 욕망을 느꼈던 모양이다.
GDI+는 모든 API가 자신만의 별도의 namespace 안에 선언되어 있으며, POINT 같은 간단한 자료형도 자신만의 것을 재정의하여 쓸 정도로 기존 윈도우 API와는 거리를 두고 만들어졌다. 그리고 C++답게 같은 함수도 다양한 overload 버전이 존재하며, 좌표는 정수뿐만이 아니라 실수로도 받기 때문에 편리하다.
사소한 것이다만, 글자를 찍을 때 null-terminated string에 대해서 글자 길이 지정을 생략해도 되는 것 역시 마음에 든다.
전통적으로 윈도우의 GDI 함수들은 글자를 찍는 함수들은 문자열 길이를 반드시 지정해 주게 되어 있다. 왜냐하면 한 null-terminated string을 부분적으로 여러 줄에 걸쳐 찍어야 할 일도 있기 때문이다.
그러니 그런 API 디자인이 수긍은 가지만, 어차피 한 줄밖에 찍을 일이 없는 문자열을 매번 _wcslen 해 주는 것도 귀찮지 않은가. 예전에는 gdi가 아니라 user 계층에 있는 DrawText 같은 고수준 함수나 문자열 길이 지정을 -1로 생략이 가능했던 반면, GDI+는 이 정책이 좀 더 확대되었다.
GDI+는 GDI에 비해서 state machine으로서의 의미가 크게 퇴색했다. 그래서 그리기에 필요한 모든 정보들을 함수 호출 때 매개변수로 일일이 전달해 줘야 하는 경우가 많다. 가령, current position이라는 개념이 없기 때문에 MoveTo와 LineTo 따로가 아니며, SelectOjbect라는 개념도 없어져서 그리기 함수 때 매번 펜이나 브러시에 해당하는 개체를 따로 공급해 줘야 한다.
이런 디자인은 편리한 점도 있지만, 당장 화면에 뭔가를 찍는 드로잉 말고 벡터 path를 기록한다거나 메타파일 같은 걸 만들 때는, 내가 보기에 좀 불편하게 작용하는 점도 있는 것 같다. 가령, GDI에서는 똑같이 HDC이고 여기에다가 BeginPath를 해 주면 그때부터 path 그리기 모드로 GDI가 상태 관리를 하면서 동작한다. 그러던 것이 GDI+에서는 Graphics와 GraphicsPath라고 클래스가 아예 갈라졌다. 두 개체를 상태별로 분리한 건 분명 잘한 디자인이라는 거 인정한다.
하지만 Graphics 말고 GraphicsPath는 어차피 예전 위치에서 계속해서 이어서 그래픽을 기술하는 게 많은 만큼, 재래식 GDI처럼 current position이 있는 게 편리하지 않을까 싶다. 지금 API 체계에서는 직전 위치에 대한 정보를 응용 프로그램이 계속 공급해 줘야 한다.
또한, 복잡한 path를 화면에다 그릴 때, 예전 GDI는 지금 DC가 가지고 있는 펜과 브러시로 윤곽선을 그리고 내부를 채우는 것을 함수 호출 한 번으로 동시에 할 수 있었다. 그러나 GDI+는 선을 그리는 것과 내부를 채우는 것을 따로 해야 한다. path의 경계를 추출하여 래스터라이즈하는 것은 상당히 복잡한 계산이 필요한 작업인데, 동일한 작업이 비효율적으로 중복 적용되는 건 아닌지 우려된다.
즉, 본인은 GDI+에 대해서 참신한 기능은 분명 마음에 든다. 이 글에서 언급된 것 말고도 여러 고급 기능들이 있다. 윈도우 비스타 Aero와 연동하는 일부 드로잉 기능(가령, 클라이언트 영역에도 반투명 Aero 효과를 추가하고, 거기에다 글자를 찍는 것)은 오로지 GDI+로만 접근해야 하는 것도 있다.
하지만 (1) 그냥 재래식 GDI API에다가 옵션을 추가하는 형태로 구현했어도 충분해 보이는 것, (2) GDI+가 바꿔 놓은 API 디자인이 오히려 좀 불편하고 비효율적일 수도 있겠다 싶은 것에 대해 비판적인 안목을 갖고 있다. 속도가 재래식 GDI보다 꽤 느린 건 차치하고라도 말이다.
Posted by 사무엘