[WinApi] 엔진 - 이미지 로드, 렌더링

라멘커비·2024년 1월 30일
0

WinApi

목록 보기
7/32
post-thumbnail

엔진

🏝️UEngineResourceManager::LoadImg

  • PlayLevel.cpp
    for AllFileList문 -> FullPath가져와서 LoadImg 실행함

    • Name이 Images(UImage* 맵)에 이미 있으면 로드 X.
    • Load하는 이미지에 대해 SetName, SetPath.
  • PathObject : NameObject를 상속받음. Path를 가짐. 리소스들은 PathObject를 상속받을 것임.

  • Path만 받는 LoadImg, Path랑 Name 둘 다 받는 LoadImg 각각 만듦.

UWindowImage* UEngineResourcesManager::LoadImg(std::string_view _Path)
{
	UEnginePath NewPath = UEnginePath(std::filesystem::path(_Path));
	std::string FileName = NewPath.GetFileName();
	return LoadImg(_Path, FileName);
}

UWindowImage* UEngineResourcesManager::LoadImg(std::string_view _Path, std::string_view _Name)
{
	// 모든 이름은 대문자로
	std::string UpperName = UEngineString::ToUpper(_Name);

	if (true == Images.contains(UpperName))
	{
		MsgBoxAssert(std::string("경로 : ") + std::string(_Path) + "파일명 : " + std::string(_Name) + "이미 로드한 파일을 또 로드하려고 했습니다");
		return nullptr;
	}

	UWindowImage* NewImage = new UWindowImage();
	NewImage->SetName(UpperName);
	NewImage->SetPath(_Path);
	NewImage->Load(GEngine->MainWindow.GetWindowImage());

	Images[UpperName] = NewImage;

	return nullptr;
}

🏝️UWindowImage::Load()

경로와 이름을 이미 알 수 있어서 인자 필요없음. 윈도우 함수 사용할 건데 성공하면 true, 실패하면 false를 리턴하는 게 일반적이라서 얘도 bool 리턴하도록 함.

  • 확장자가 png일 때와 bmp일 때 로드하는 방식이 다르다.
bool UWindowImage::Load(UWindowImage* _Image)
{
	UEnginePath Path = GetEnginePath();

	std::string UpperExt = UEngineString::ToUpper(Path.GetExtension());

	if (".BMP" == UpperExt)
	{
		//HINSTANCE hInst,  이 이미지를 사용할 프로그램을 알려달라는건데. nullptr넣어도 괜찮다.
		//LPCSTR name, // 경로
		//UINT type, // 이미지 타입
		//int cx, // 이미지를 로드할 크기 X 0을 넣으면 전체 크기로 로드
		//int cy, // 이미지를 로드할 크기 Y 0을 넣으면 전체 크기로 로드
		//UINT fuLoad 로드 옵션
		
		// 비트맵을 제어할수 있는 핸들
		HANDLE ImageHandle = LoadImageA(nullptr, Path.GetFullPath().c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		hBitMap = reinterpret_cast<HBITMAP>(ImageHandle);
		ImageType = EWIndowImageType::IMG_BMP;
	}
	else if (".PNG" == UpperExt)
	{
		ULONG_PTR gdiplusToken = 0;

		Gdiplus::GdiplusStartupInput gdistartupinput;
		Gdiplus::GdiplusStartup(&gdiplusToken, &gdistartupinput, nullptr);

		// 멀티바이트 경로를 유니코드 경로로 변경하는 함수
		std::wstring wPath = UEngineString::AnsiToUniCode(Path.GetFullPath());

		Gdiplus::Image* pImage = Gdiplus::Image::FromFile(wPath.c_str());
		Gdiplus::Bitmap* pBitMap = reinterpret_cast<Gdiplus::Bitmap*>(pImage->Clone());

		Gdiplus::Status stat = pBitMap->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &hBitMap);

		if (Gdiplus::Status::Ok != stat)
		{
			MsgBoxAssert("Png 형식 리소스 로드에 실패했습니다.");
		}

		ImageType = EWIndowImageType::IMG_PNG;
	}

	ImageDC = CreateCompatibleDC(_Image->ImageDC);
	HBITMAP OldBitMap = reinterpret_cast<HBITMAP>(SelectObject(ImageDC, hBitMap));	// ImageDC야 너는 BitMap그려야 해.
	DeleteObject(OldBitMap);

	// hBitMap에서 BitMapInfo로 얻어오겠다.
	GetObject(hBitMap, sizeof(BITMAP), &BitMapInfo);


	return true;
}

#pragma comment(lib, "Msimg32.lib")

윈도우에서 이미지를 사용하기 위해서 지원하는 라이브러리.
헤더랑 다르게 라이브러리는 #pragma comment로 추가해서 사용

WindowDC

  • 이미지의 메모리적 권한 HBITMAP
  • 편집 및 출력 권한 HDC (특정 HNITMAP을 어딘가에 그리겠다, 색깔을 바꾸겠다 등)
    • CreateCompatibleDC() : HDC만들어줌

이 두 개가 엮여서 이미지라는 게 된다. 근데 이걸 하나로 묶어놓는 요소가 없음. 그래서 WindowImage라는 클래스에 엮어서 사용하는 거임. DC만 만들면 처음에 1,1짜리 HBITMAP이랑 연결돼있음. 윈도우에서 DC에 꼭 HBITMAP이 연결되어있도록 해놨음.

-> (HBITMAP)SelectObject(ImageDC, BitMap);
: HDC에 HBITMAP 연결시켜줌. 근데 원래 있던 1,1짜리가 남아있게 됨.(leak) 그래서 Delete해줘야함..
(SelectObject를 하면 반환값으로 이전 bitmap을 준다)

	ImageDC = CreateCompatibleDC(_MainDC);
	HBITMAP OldBitMap = (HBITMAP)SelectObject(ImageDC, BitMap);
	DeleteObject(OldBitMap);
  • png 로드하는 기능을 윈도우 기본 라이브러리만으로 지원해주지 않음. Gdiplus를 사용해야 한다. 윈도우가 초기 윈도우의 그래픽 시스템을 개선해서 추가한 라이브러리. Gdiplus헤더의 함수들은 전부다 Gdiplus 네임스페이스 안에 들어있다.

🏝️이미지 Render

이미지 로드하고나서 render

  • UImageRenderer::Render(float _DeltaTime)
void UImageRenderer::Render(float _DeltaTime)
{
	if (nullptr == Image)
	{
		MsgBoxAssert("이미지가 존재하지 않는 랜더러 입니다");
	}

	FTransform ThisTrans = GetTransform();
	FTransform OwnerTrans = GetOwner()->GetTransform();

	// 컴포넌트의 위치는 부모에게서 상대적이기 때문에.
	// 부모의 위치를 더해줘야 한다.
	ThisTrans.AddPosition(OwnerTrans.GetPosition());


	GEngine->MainWindow.GetWindowImage()->BitCopy(Image, ThisTrans);
}
  • UWindowImage::BitCopy(UWindowImage* _CopyImage, FTransform _Trans)
    BitBlt() 사용
void UWindowImage::BitCopy(UWindowImage* _CopyImage, FTransform _Trans)
{
	// HDC hdc, // => 어떤 이미지에
	// int x,   
	// int y,   
	// int cx,  
	// int cy,  
	// HDC hdcSrc,  
	// int x1,  
	// int y1, 
	// DWORD rop

	// 윈도우
	HDC hdc = ImageDC;
	// 이미지
	HDC hdcSrc = _CopyImage->ImageDC;
	BitBlt(
		hdc, 							// HDC hdc,  
		_Trans.iLeft(), 				// int x,    
		_Trans.iTop(), 					// int y,    
		_Trans.GetScale().iX(), 		// int cx,   
		_Trans.GetScale().iY(),			// int cy,  
		hdcSrc,							// HDC hdcSrc, 
		0,								// int x1,  
		0,								// int y1, 
		SRCCOPY							// DWORD rop => 이미지 그대로 고속 복사를 해라.
	);
}

🏝️메모

  • EnginePath::GetFullPath() : Path의 string return

    • Path.string() : std에서는 std::string을 지원해준다. 경로라는 것은 "C:/aaa/bbb/ccc" 문자열에 가깝다. 담당하는 클래스 내부에 경로를 std::string으로 리턴해주는 함수가 있어야 한다.
    • (=> 새로운 자료구조 등을 배웠을 때 내부를 다 알아야하는 것이 아니고 이런식으로 내부 기능을 사용하는 감이 있어야 함)
  • 리소스 이미지 확장자 바꾸려면 이미지 편집 프로그램에서 포맷을 변경하고 저장해야 함.

  • winapi는 png파일을 출력하는 함수를 지원하지 않는다. bmp만 된다.
    png파일을 로드해서 bmp로 변경해서 출력할 것이다.

  • UImage -> UWindowImage로 바꿈, Level2에서 Level1로 옮김. Window에 속한 기능에 가깝기 때문. PathObject 상속받아서 이름과 Path를 가질 수 있는 애가 됨.

  • NameObject Level2에서 Level0으로 옮김.

  • 에디터 모드나 디버그에서는 속력을 따질 필요가 없다.(터지는 마당에 먼 속력) 게임에서 실제 실행되지 않을 내용에서는 속력을 따지지 않는다.


이미지 띄우기 + 방향키 이동

profile
일단 시작해보자

0개의 댓글