[C#/WPF] Stream을 사용해 Image를 BitmapImage로 변환할 때 에러 해결 방법

System.Drawing.Image를 System.Windows.Media.Imaging.BitmapImage로


  안녕하세요. 오늘은 아주 기초적이지만 놓치기 쉽다고 생각되는 부분(을 겪은 제 상황)을 알려드리겠습니다.
  방금 한 일은, WPF DataGridCell에 마우스를 올리면 상위 DataGridRow의 Item의 Image를 ToolTip으로 보여주는 작업인데요, 해당 프로그램은 처음에 WinForms 어플리케이션으로 만들었다가 WPF로 컨버전하는 중이기 때문에 System.Drawing.Image를 WPF에서 사용할 수 있는 BitmapImage로 바뀌어야만 했습니다.

  해당 방법은 구글링하면 아주 손쉽게 찾을 수 있었는데, 문제는 본문에 놓친 부분이 댓글에 적혀 있었단 것을 몰랐다는 겁니다. 본문에는 아래와 같이 하라고 쓰여있었습니다.

        public static BitmapImage ToWpfBitmapImage(this Image image)
        {
            if (image == null)
                return null;

            var bimg = new BitmapImage();

            var ms = new MemoryStream();

            image.Save(ms, image.RawFormat);

            bimg.BeginInit();

            bimg.StreamSource = ms;

            bimg.EndInit();

            return bimg;
        }

  네. 하지만 당연하게도 에러를 뿜어냈습니다. 에러가 안 났다면 제가 이 글을 쓰고 있지도 않았겠죠. 뭐 헤더가 손상되었을 수 있습니다 이런 에러를 뿜더라고요.

에러 원인 분석


  에러가 나는 원인을 찾으려고 해당 Exception과 Message를 가지고 검색해도 잘 안 나와서, 그냥 다른 Image to BitmapImage 소스를 찾아보기로 했습니다. 그리고 5분도 채 지나지 않아 제가 참고했던 그 포스트로 돌아오게 되었고, 댓글에 작성자가 달아놓은 아주 중요한 부분을 발견했습니다.

Stream을 되감아야 FormatException 없이 정상 처리가 가능하다.

  네. 그렇습니다. 위 코드는 System.Drawing.ImageSystem.Windows.Media.Imaging.BitmapImage가 초기화하는데 공용으로 사용되는 Stream을 사용한 방식의 변환입니다. 코드를 잘 보면 아래와 같은 순서로 돼있습니다.

  1. 반환할 BitmapImage를 초기화
  2. System.Drawing.Image의 내용을 쓸 MemoryStream을 초기화
  3. MemoryStream에 System.Drawing.Image 객체의 내용을 기록
  4. BitmapImage.BeginInit()
  5. BitmapImage 객체의 StreamSource를 위의 MemoryStream으로 할당
  6. BitmapImage.EndInit() 


  3번과 5번을 강조한 이유를 아시겠나요? 3번과 5번에서 사용되는 MemoryStream은 같은 객체입니다. 그리고 Stream에 기록할 때, 자동으로 Stream의 Position이 기록된 길이만큼 뒤로 밀리게 되죠.


원인 파악 및 해결!


  바로 이것이 원인이었던 겁니다! 멍청하게도, 재작년 회사에서 겪었던 Stream 관련 문제를 기억하지 못하고 오늘 또 같은 실수를 저질렀던 겁니다. 3번에서 Stream의 Position은 이미 저 뒤 끝으로 밀려났고, 5번에서 Stream을 할당해 6번에서 읽으려니 이미 Position이 끝자락이라 더이상 읽을 내용이 없었던 것입니다.

  읽을 내용이 없으니 당연히 헤더도 없겠죠? 그래서 헤더가 손상됐을 수 있다는 내용의 에러를 뿜뿜했던겁니다.

  그렇다면 수정 방법은?


        public static BitmapImage ToWpfBitmapImage(this Image image)
        {
            if (image == null)
                return null;

            var bimg = new BitmapImage();

            var ms = new MemoryStream();

            image.Save(ms, image.RawFormat);

            ms.Seek(0, SeekOrigin.Begin);

            bimg.BeginInit();

            bimg.StreamSource = ms;

            bimg.EndInit();

            return bimg;
        }

  쨘! 굵게 칠한 줄 하나면 됩니다!

  ... 이 문제 가지고 날린 1시간이 너무나 아깝습니다.

댓글

이 블로그의 인기 게시물

C# 남아도는 메모리에도 불구하고 OutOfMemoryException이 발생한다면?

USB를 뒤는 괜찮은데 앞에 꽂으면 인식이 힘들다?

MySQL 데이터 타입과 Java 데이터 타입 비교/매칭