GetDC로 디바이스 컨텍스트 가져와서

주모니터는 가져와지는데 다른 보조모니터는 어떻게 가져오나 해매다가 좋은 참고자료 찾음

 

https://stackoverflow.com/questions/53329673/c-getdc-all-monitors

 

C++ GetDC All Monitors

Basically, I'm making something that imitates a screen melting effect, but I can only get it working on my primary monitor. I've looked up as much as I could and there was only one forum on GetDC f...

stackoverflow.com

 

위 링크에 따르면 GetDC(0) 하면 전체 모니터 DC를 가져온다고 하는데

BitBlt에서 좌표 설정해야 된다고 하더라

 

 

중앙에 위치한 3번 모니터의 시작점은 1920, 0이므로 

 

 

 

 

 

 

BitBlt에서 x1 자리 매개변수 값을 1920으로 고치면 3번 보조모니터의 화면이 나온다.

cv::Mat ADesktopGameModeBase::GetScreenToCVMat()
{
	HDC hScreenDC = GetDC(NULL);
	HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
	int screenWidth = GetDeviceCaps(hScreenDC, HORZRES);
	int screenHeight = GetDeviceCaps(hScreenDC, VERTRES);

	HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, screenWidth, screenHeight);
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
	BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 1920, 0, SRCCOPY);
	SelectObject(hMemoryDC, hOldBitmap);

	cv::Mat matImage(screenHeight, screenWidth, CV_8UC4);
	GetBitmapBits(hBitmap, matImage.total() * matImage.elemSize(), matImage.data);

	DeleteDC(hScreenDC);
	DeleteDC(hMemoryDC);

	DeleteObject(hBitmap);
	DeleteObject(hOldBitmap);


	return matImage;
}

 

 

중앙 보조모니터(3번)

 

 

이제 세 모니터를 띄우는게 가능할것같다.

일단 스크린부터 3개로 늘리자

 

 

배치후 BP_Screen1, 2, 3으로 이름 변경

 

 

 

BP 데스크탑 게임모드베이스에서 기존 코드는 잠깐때고

한번 디스플레이 이름 출력해보면

 

 

순서는 섞였지만 나오긴 나온다.

이 디스플레이 이름 확인해서 넣어주면될듯

 

 

일단 1, 3, 4화면을 utexture2d로 변환하는 코드 만들어보면

 

 

 

 

 

 

 

 

 

기존 코드는 가능한 나두고

모니터 가로세로길이

이미지텍스처스크린1,2,3

cvmat 이미지스크린1,2,3

함수로 ScreensToCVMats, CVMatsToTextures

 

UCLASS()
class HANDDESKTOP_API ADesktopGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	cv::VideoCapture capture;
	cv::Mat image;

	UFUNCTION(BlueprintCallable)
	void ReadFrame();

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* imageTexture;
	void MatToTexture2D(const cv::Mat InMat);

	cv::Mat GetScreenToCVMat();




	int monitorWidth = 1920;
	int monitorHeight = 1080;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* imageTextureScreen1;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* imageTextureScreen2;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* imageTextureScreen3;
	cv::Mat imageScreen1;
	cv::Mat imageScreen2;
	cv::Mat imageScreen3;

	void ScreensToCVMats();
	void CVMatsToTextures();
};

 

 

비긴 플레이에 초기화 코드 추가

#include "DesktopGameModeBase.h"


void ADesktopGameModeBase::BeginPlay()
{
	Super::BeginPlay();

	capture = cv::VideoCapture(0);

	if (!capture.isOpened())
	{
		UE_LOG(LogTemp, Log, TEXT("Open Webcam failed"));
		return;
	}
	else
	{
		UE_LOG(LogTemp, Log, TEXT("Open Webcam Success"));
	}
	imageTexture = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);



	imageScreen1 = cv::Mat(monitorHeight, monitorWidth, CV_8UC4);
	imageScreen2 = cv::Mat(monitorHeight, monitorWidth, CV_8UC4);
	imageScreen3 = cv::Mat(monitorHeight, monitorWidth, CV_8UC4);
	imageTextureScreen1 = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);
	imageTextureScreen2 = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);
	imageTextureScreen3 = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);

}

 

 

리드 프레임 수정

void ADesktopGameModeBase::ReadFrame()
{
	/*
	if (!capture.isOpened())
	{
		return;
	}
	capture.read(image);

	cv::Mat desktopImage = GetScreenToCVMat();
	MatToTexture2D(desktopImage);
	*/

	ScreensToCVMats();
	CVMatsToTextures();

}

 

모니터 1, 3, 4를 cv::Mat imageScreen1, 2, 3에 저장하는 코드

void ADesktopGameModeBase::ScreensToCVMats()
{
	HDC hScreenDC = GetDC(NULL);
	HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
	int screenWidth = GetDeviceCaps(hScreenDC, HORZRES);
	int screenHeight = GetDeviceCaps(hScreenDC, VERTRES);

	HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, screenWidth, screenHeight);
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

	//screen 1
	BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 0, 0, SRCCOPY);
	GetBitmapBits(hBitmap, imageScreen1.total() * imageScreen1.elemSize(), imageScreen1.data);

	//screen 2
	BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 1920, 0, SRCCOPY);
	GetBitmapBits(hBitmap, imageScreen2.total() * imageScreen2.elemSize(), imageScreen2.data);

	//screen 3
	BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 3840, 0, SRCCOPY);
	GetBitmapBits(hBitmap, imageScreen3.total() * imageScreen3.elemSize(), imageScreen3.data);
	SelectObject(hMemoryDC, hOldBitmap);


	DeleteDC(hScreenDC);
	DeleteDC(hMemoryDC);

	DeleteObject(hBitmap);
	DeleteObject(hOldBitmap);

}

 

 

 

imageScreen1,2,3을 imageTextureScreen1,2,3에 저장하는 코드

이런 식으로 만들 필요는 없는데

이미지텍스처 스크린을 언리얼 프로퍼티로 설정해서 BP에서 사용하려다가 좀 이상하게 됨

void ADesktopGameModeBase::CVMatsToTextures()
{
	for (int i = 0; i < 3; i++)
	{
		if (i == 0)
		{
			FTexture2DMipMap& Mip = imageTextureScreen1->GetPlatformData()->Mips[0];
			void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
			FMemory::Memcpy(Data, imageScreen1.data, imageScreen1.total() * imageScreen1.elemSize());//copy the data
			Mip.BulkData.Unlock();

			imageTextureScreen1->PostEditChange();
			imageTextureScreen1->UpdateResource();
		}
		else if (i == 1)
		{
			FTexture2DMipMap& Mip = imageTextureScreen2->GetPlatformData()->Mips[0];
			void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
			FMemory::Memcpy(Data, imageScreen2.data, imageScreen2.total() * imageScreen2.elemSize());//copy the data
			Mip.BulkData.Unlock();

			imageTextureScreen2->PostEditChange();
			imageTextureScreen2->UpdateResource();
		}
		else if (i == 2)
		{
			FTexture2DMipMap& Mip = imageTextureScreen3->GetPlatformData()->Mips[0];
			void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
			FMemory::Memcpy(Data, imageScreen3.data, imageScreen3.total() * imageScreen3.elemSize());//copy the data
			Mip.BulkData.Unlock();

			imageTextureScreen3->PostEditChange();
			imageTextureScreen3->UpdateResource();
		}


	}


}

 

 

 

데스크탑 게임모드베이스 블루프린트로 들어와

Screen1,2,3(BP_MainWidget) StrScreen1,2,3(포 이치 루프에서 이름으로 찾아내기 위함) 추가

StrScreen1,2,3의 디폴트 값으로 BP_Screen1, BP_Screen2 , BP_Screen3 으로 설정

 

 

 

BP_Screen 모든 액터 가져와 포 이치 루프

아까 정의한 StrScreen1,2,3으로 액터 확인, BP MainWidget 생성후 Screen 1,2,3에 등록

Screen 1, 2, 3(BP_MainWidget)을 루프로 가져온 Screen(BP_Screen)에 등록

 

 

 

 

이벤트 틱에서 각 Screen 변수의 imageWidget과 c++에서 설정한 imageTextureScreen(UProperty)로
SetBrushFromTexture로 텍스쳐 드로잉

 

 

 

전체 스크린에 대해 수행한 결과

 

 

 

 

정리하면 이런형태

 

 

 

 

 

+ Recent posts