2016. 5. 19. 13:06ㆍIT-개발/winapi 및 MFC
펌 : http://m.blog.naver.com/mrbullet/130065365590
프로그램 개발 중 우리는 예상치 못한 곳에서 다양한 여러 문제를 만나곤 한다. 프로그램이 멈춰버리는
심각한 문제 같이 꼭 해결해야만 하는 문제도 있지만 조금 피해 가면 되는 일들도 있기 마련이다.
오늘 포스트는 CImage 와 PNG에 관한 글이다. 좀 더 구체적으로 CImage의 LoadFromResource 에서 시작 된다.
많은 장점에도 불구하고 핑 파일은 (보통 피엔지라고 읽지만 정확히는 핑 이라고 한다) 초기에 배경투명화를 지원하지 않는다는
오해(대부분 출력하는 프로그램의 문제이다) 도 받는 등 대접을 제대로 받지 못하다 요즘은 많이들 사용하는 파일이다.
우리는 보통 툴바나 이미지 콤보등 프로그램 UI 구성에 필요한 작은 이미지들은 리소스에 포함시켜 실행파일에 집어넣는 것이
일반적이다. (프로그램을 배포 하면서 툴바나 콤보박스 내에 들어갈 작은 이미지파일 들은 귀찮게 따로 따로 배포할 바보는 없을 것이다).
암튼 고맙게도 일단 CImage는 BMP, JPG, GIF뿐 아니라 PNG 파일도 읽어 준다.
Load 라는 멤버를 통하여 파일을 직접 읽어 들이는 기본 방법외에 CImage가 제공하는 LoadFromResource 멤버를 이용
리소스에 포함되어 있는 이미지 파일들을 불러와 출력하는 테스트를 해보았다.
VC2009 Feature Pack의 예제를 접해본 개발자들이라면 리소스 에디터에서 PNG 타입을 지원 하는 것을 경험 했을 것이다.
그럼에도 불구 하고 아쉽게도 CImage::LoadFromResource는 PNG의 경우만 동작을 제대로 하지 못했다.
분명 개발자 입장에서는 혼돈스럽고 짜증이 나는 일인듯 하다.
물론 이런 경우 그냥 PNG을 사용하지 않고 MaskColor 정해서 bmp등으로 UI를 꾸민다고 문제 될 것은 하나 없다.
그렇다고 프로그램 기능에 영향을 주는 것도 더더욱 아니다.
그런데 괜히 왠지 모르게 약 오르고 자존심 상하고 비겁하게 피해 가는 것 같은 생각을 가지는 사람들이 있다.
나 또한 아직은 이과에 속한다. (이것도 일종의 마음의 병 일듯 하다)
과거에는 시도하는 어떤 한 방법이 생각대로 잘 안될 때 그것을 끝까지 파고들어 끝장을 보는 것이 개발자의 근성이라 여겼지만
팀원의 역활 보다는 팀장의 역할을 더 오래 겪은 지금은 나는 이런 오기나 고집 등이 이제는 그리 좋은 것만은 아니라는 생각이
들기도 하다. 물론 꼭 풀어야 할 문제라면 당연히 밤을 지새워 풀어야 하겠지만 암튼,
결론은
<CImage::Load (_T("XXX.PNG"))는 문제없지만 리소스에 있는 PNG는 CImage::LoadFromResource을 통하여 동작 시킬수 없다>
이다. LoadFromResoure의 버그라고 하기엔 조금 그렇고 암튼 MFC의 과도기적 버전문제에 더 가깝지 않을까 싶은 생각이다.
자료를 조금 찾아보니
http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/b958bfbd-5aee-4ff1-9088-53c8da0f0eb3
http://www.eggheadcafe.com/software/aspnet/34751908/cimage-and-loadfromresour.aspx
언제나 그렇듯 반가운(같은 고민을 한사람들) 사람들이 있다.
위 링크에 보면 한 가지 예제가 있지만 테스트 해보니 동작 하지 않았다. 그래서 좀 더 자료를 찾아 아래와 같이 만들어 보았다.
좀 더 사용하기 편리한 클래스로 바꾸는 건 각자의 몫이다.
BOOL CDemoView::MyLoadFromResource (CImage& img, LPCTSTR pName, LPCTSTR pType, HMODULE hInst)
{
IStream* pStream = NULL;
HRSRC hResource = ::FindResource (hInst, pName, pType); if (!hResource) return FALSE;
DWORD nImageSize = ::SizeofResource (hInst, hResource); if (!nImageSize) return FALSE;
HGLOBAL m_hBuffer = ::GlobalAlloc (GMEM_MOVEABLE, nImageSize);
BYTE* pBytes = (BYTE*)::LoadResource (hInst, hResource);
if (m_hBuffer)
{
void* pSource = ::LockResource (::LoadResource (hInst, hResource));
if (!pSource) return FALSE;
void* pDest = ::GlobalLock (m_hBuffer);
if (pDest)
{
CopyMemory (pDest, pSource, nImageSize);
if (::CreateStreamOnHGlobal (m_hBuffer, FALSE, &pStream) == S_OK)
{
img.Load (pStream); // 여기만 수정 하면
pStream->Release ();
}
::GlobalUnlock (m_hBuffer);
}
::GlobalFree (m_hBuffer);
m_hBuffer = NULL;
}
if (img.IsNull ()) return FALSE;
return TRUE;
}
사용은
MyLoadFromResource (m_img, (LPCTSTR)MAKEINTRESOURCE (IDB_PNG1), _T("PNG"));
이렇게 하면 리소스에 있는 PNG파일을 불러 들이게 된다.
그러나 CImage는 Alpha Composition을 지원하지 않기에 우리가 png를 사용하는 큰 이유 중 하나인 배경의 투명화를
깨끗하게 처리 하지 못하기 때문에 요즘 대부분의 개발자들은 GdiPlus를 이용한다.
위 예제 코드 중 의 img.Load 부분을 Gdiplus::Bitmap::FromStream (pStream)을 이용해서 위 예제를 가지고
아주 조금만 수정 하면 Gdiplus::Bitmap을 지원하는 클래스를 구현 할수 있을 것이다. (해 보라, 정말 간단하다.)
오늘은 CImage 또는 Gdiplus::Bitmap객체들이 리소스에 포함 되어 있는 PNG를 포함한 다양한 이미지 파일을 가져오는 방법을
설명 했다. 결과 화면 첫줄의 PNG 이미지는 위 예제 코드 그대로 CImage를 이용하여 출력한 결과 이고
아래줄 이미지는 Gdiplus::Bitmap을 이용 출력한 결과 이다. 딱 봐도 차이가 확연하다.
아직 Gdiplus를 사용하지 않고 있다면 당장 시작하라고 권하고 싶다.
그럼 이만 ....
.....................
이 Blog 내용이 도움이 된거는 Cimage LoadFromResource 를 통해서 png 이미지를 올리고 그리려고 했으나 Error가 나면서 부터다.
일반적으로 사용하는 BMP, JPG, PNG Format에 상관 없이 1개의 함수로 LoadFromResource 가 제대로 동작하도록 함수를
만들고 싶어서... 다음 과 같이 함수를 만듬.
BOOL ClassName::LoadFromRes(LPWSTR pName, LPCTSTR pType, BOOL bUseGdiplus)
{
BOOL bRet = FALSE;
IStream* pStream = NULL;
// 이미 load된것 있으면 해제
m_ObjImg.Destroy();
if (pType == RT_BITMAP) // Bitmpa 일때, png / jpg 처럼하면 error 나더군요. ㅠㅠ. 그럴이유도 없겠지만
{
m_ObjImg.LoadFromResource(AfxGetInstanceHandle(), (int)pName);
return TRUE;
}
HRSRC hResource = ::FindResource (AfxGetInstanceHandle() , pName, pType);
if (!hResource) return FALSE;
DWORD nImageSize = ::SizeofResource (AfxGetInstanceHandle(), hResource);
if (!nImageSize) return FALSE;
HGLOBAL hBuffer = ::GlobalAlloc (GMEM_MOVEABLE, nImageSize);
if (hBuffer)
{
void* pSource = ::LockResource (::LoadResource(AfxGetInstanceHandle(), hResource));
if (!pSource) return FALSE;
void* pDest = ::GlobalLock (hBuffer);
if (pDest)
{
CopyMemory (pDest, pSource, nImageSize);
if (::CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK)
{
if (bUseGdiplus)
{
// Gdiplus - 이용하는 방식 적용해볼려구요
}
else
{
if (SUCCEEDED(m_ObjImg.Load(pStream))) // Gif / Png 일때
{
SetWindowPos(NULL,0,0,m_ObjImg.GetWidth(), m_ObjImg.GetHeight(), SWP_NOMOVE|SWP_NOZORDER);
bRet = TRUE;
}
}
pStream->Release ();
}
::GlobalUnlock (hBuffer);
}
::GlobalFree (hBuffer);
hBuffer = NULL;
}
if (m_ObjImg.IsNull()) return FALSE;
return bRet;
}
'IT-개발 > winapi 및 MFC' 카테고리의 다른 글
COM DLL만으로 tlb파일 생성및 COM사용하기 (펌) (0) | 2016.08.24 |
---|---|
[Sample] 파일크기 구하기... (0) | 2016.06.16 |
MFC에서 제공하는 api로 Thread 생성하기... (0) | 2016.04.05 |
Document - View 구조 - 액세스가 거부되었습니다 (0) | 2016.04.05 |
[winapi] 단축키 등록 / 사용 (0) | 2016.03.23 |