[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.Image와 System.Windows.Media.Imaging.BitmapImage가 초기화하는데 공용으로 사용되는 Stream을 사용한 방식의 변환입니다. 코드를 잘 보면 아래와 같은 순서로 돼있습니다.
- 반환할 BitmapImage를 초기화
- System.Drawing.Image의 내용을 쓸 MemoryStream을 초기화
- MemoryStream에 System.Drawing.Image 객체의 내용을 기록
- BitmapImage.BeginInit()
- BitmapImage 객체의 StreamSource를 위의 MemoryStream으로 할당
- 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시간이 너무나 아깝습니다.
댓글
댓글 쓰기