C++/(Gdiplus)Bitmap Array to HBITMAP

일반적으로 개발 포럼에서는 간단하기 때문에 Stream을 사용하는 방법을 많이 답변해주는데 위 방법이 훨~씬 빠르다.


빠른(느린) 이유

핵심 차이점

항목 CreateDIBitmap 방식 Gdiplus::Bitmap::FromStream 방식 성능 영향
처리 레이어 순수 GDI (낮은 레벨) GDI+ (높은 레이어, 추상화됨) GDI+가 느림
포맷 파싱 거의 없음 (직접 구조체 캐스팅) BMP 헤더 + 픽셀 데이터 완전 파싱 + 검증 매우 큼
픽셀 데이터 복사 방식 포인터로 직접 참조 (복사 거의 없음) 내부적으로 전체 픽셀 버퍼 복사 + 변환
색상 변환 / 프리멀티플라이드 거의 안 함 (DIB_RGB_COLORS 그대로) 내부적으로 ARGB → PARGB 변환 가능성 있음 중간~큼
검증 (validation) 없음 GdipImageForceValidation() 계열 호출 (전체 픽셀 강제 materialization) 매우 큼
최종 HBITMAP 생성 방식 CreateDIBitmap = 드라이버에 직접 전달 GDI+ → GDI 변환 (GetHBITMAP 내부에서 BitBlt 수준 복사) 중간

가장 큰 병목 3가지 (실제 지연 대부분 여기서 발생)

  1. GDI+의 강제 픽셀 materialization + 검증
    GDI+는 보안/안정성을 위해 이미지 로드 후 GdipImageForceValidation() 계열을 내부적으로 호출한다.
    전체 픽셀을 실제로 읽고 압축 해제하고 검증하는 작업이 들어간다.
    특히 큰 이미지일수록 이 단계가 치명적이다.

  2. FromStream 자체의 오버헤드
    SHCreateMemStream + FromStream 조합은

    • 스트림 읽기
    • BMP 헤더 파싱
    • 픽셀 포맷 변환 판단
    • 실제 픽셀 데이터 디코딩 + 복사
      를 모두 거친다.
      → CreateDIBitmap은 이 과정 대부분을 건너뛴다 (이미 우리가 직접 파싱했다고 가정).
  3. GetHBITMAP() 내부 복사
    Gdiplus::Bitmap은 기본적으로 PixelFormat32bppPARGB (premultiplied) 형태로 들고 있다. → GetHBITMAP() 호출 시 비-premultiplied HBITMAP을 만들기 위해 픽셀 단위 변환 + 복사가 발생한다. 이 복사는 상당히 무겁다(특히 큰 해상도에서).

많은 게임/실시간 영상 처리 코드에서 GDI+를 피하고 CreateDIBitmap / CreateDIBSection 계열을 선호하는 이유가 바로 이것이다.

필요하면 GDI+를 쓰더라도
- Bitmap을 풀(pool)로 재사용하거나
- CachedBitmap을 활용하거나
- PixelFormat32bppRGB로 강제 생성
하는 식으로 최적화하면 어느 정도 빨라지긴 하다.
하지만 단발성 BMP 메모리 → HBITMAP 변환이라면 거의 항상 CreateDIBitmap 계열이 압승이다.



Show Comments