Import an Image from Explorer in Runtime

Hey friends!

Guess what? After hours of banging my head against the keyboard, I finally cracked the code conundrum that was haunting me. I scoured the internet, dove deep into research, and voila! I’ve got this neat piece of code that lets you pop open a “File Dialogue,” pick an image file, and smoothly import it into your game as a Texture2D. And the best part? It works like a charm in runtime!

Check it out:

.h

UCLASS()
class MYPROJECT_API YOURCLASSNAME : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

	UFUNCTION(BlueprintCallable)
		static UTexture2D* OpenFileDialogueAndLoadImage();
};

.cpp clean

UTexture2D* YOURCLASSNAME::OpenFileDialogueAndLoadImage()
{

	IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();

	if (DesktopPlatform)
	{
		
		TArray<FString> OutFilenames;
		bool bOpened = DesktopPlatform->OpenFileDialog
		(
			nullptr,
			TEXT("Choose Image"),
			FPaths::ProjectContentDir(),
			TEXT(""),
			TEXT("Image Files (*.png; *.jpg| *.png;*.jpg)"),
			EFileDialogFlags::None,
			OutFilenames
		);

		if (bOpened && OutFilenames.Num() > 0) 
		{

			FString SelectedImagePath = OutFilenames[0]; 

			TArray <uint8> ImageData; 

			if (FFileHelper::LoadFileToArray(ImageData, *SelectedImagePath))
			{
				IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));

				EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); 

				TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat);

				if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num()))
				{
				
					TArray64<uint8> RawData;

					if(ImageWrapper->GetRaw(ERGBFormat::RGBA, 8, RawData))
					{
						TArray64<uint8>* RawDataProcessed = &RawData;
						UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_R8G8B8A8);
						                                                                                                                    
						if (Texture)
						{
							
FTexture2DMipMap& Mip = Texture->GetPlatformData()->Mips[0];// 
							void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE); 

							FMemory::Memcpy(Data, RawDataProcessed->GetData(), RawDataProcessed->Num());
							Mip.BulkData.Unlock(); 

							Texture->GetPlatformData()->SetNumSlices(1); 

							Texture->NeverStream = true; 

							Texture->UpdateResource();

							return Texture;
	}
	}		
	}
	}
	}
	}
	return nullptr;
}

.cpp with my comments

UTexture2D* YOURCLASSHERE::OpenFileDialogueAndLoadImage()
{
	//Here we initialize a pointer to the Desktop Platform Module, so we can open the file dialog. 
	//Dont forget to include "DesktopPlatform" in the Build.cs
	IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();

	if (DesktopPlatform)
	{
		
		TArray<FString> OutFilenames; //This stores the file path
		bool bOpened = DesktopPlatform->OpenFileDialog // File Dialogue so we can choose a file
		(
			nullptr,
			TEXT("Choose Image"),
			FPaths::ProjectContentDir(),
			TEXT(""),
			TEXT("Image Files (*.png; *.jpg| *.png;*.jpg)"),
			EFileDialogFlags::None,
			OutFilenames
		);

		if (bOpened && OutFilenames.Num() > 0) //if we managed to open the dialogue and we chose at least one file
		{

			FString SelectedImagePath = OutFilenames[0]; 

			TArray <uint8> ImageData; // this will be our image

			if (FFileHelper::LoadFileToArray(ImageData, *SelectedImagePath)) //here we load the data
			{
				IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));//Get a reference to the wrapper module 
				EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); // Here i wanted to get the Image format, JPG, PNG and so on so that we could create an appropriate wrapper for it 
				TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); // here we create an image wrapper for the given format 
				
				if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num()))
				{
				
					TArray64<uint8> RawData;
					if(ImageWrapper->GetRaw(ERGBFormat::RGBA, 8, RawData))// we want to take the raw data from the wrapper and put it in the raw data variable
					{
						TArray64<uint8>* RawDataProcessed = &RawData;
						UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_R8G8B8A8);// we want to make a transient Texture object we can modify in code
						                                                                                                                    // we also get the widht and height from the wrapper and set the pixel format
						if (Texture)
						{
							FTexture2DMipMap& Mip = Texture->GetPlatformData()->Mips[0];// we get platform specific data for the texture and select the firt mipmap
							void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE); //bulk data contains well... the bulk data of the mipmap and we want to write to it, we lock it so nothing accesses it now
							FMemory::Memcpy(Data, RawDataProcessed->GetData(), RawDataProcessed->Num()); // this copies memory data from one place to another, so we copy the image data 
							Mip.BulkData.Unlock(); // we unlock it so it can be accessed again

							Texture->GetPlatformData()->SetNumSlices(1); //we dont really need more but you can set it to more
							Texture->NeverStream = true; //if you want it streamed

							Texture->UpdateResource(); //update 
							return Texture;
						}
					}

				
				}
			}

		



		}
	}
	return nullptr;
}

Pics for proof!

Screenshot_1

Screenshot_2

Screenshot_3

Hope you all have a great day!
Cheers.

5 Likes

very useful insight

Hello ,
I don’t know if you are still up on you UE account but -
I’m trying to do exactly the same module as you , and i would have loved to see what were your includes to make it working :slight_smile:
Have a nice day.

Charles -

Rectification , found them by myself ! haha sorry !

Few comments:

  1. The FileDialog done that way will stop the current game thread, so if you have something running in the background it will stop. In my case my client times out as the server doesn’t receive anymore the keepalive. If you need this then you have to wrap the file open into an AsyncTask
  2. For the image loading into a texture, there is a much simpler method already provided by UE
    FImageUtils::ImportFileAsTexture2D

Cheers.