x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Launch Error : ImageWrapperModule was nullptr.

Hello, so I was following this tutorial here to load images from disk outside of the project and I just had to tweak around a bit to get it working for 4.19 as it seems some of the headers were deprecated. The project builds and compiles without any errors and I am able to play in the editor as well but when I try to package and launch it gives me a vague launch failed error. Upon debugging the application I found out the actual error was "ImageWrapperModule was nullptr". I am unable to figure out what could be causing this error, here is the final code I am trying to run.

 // This includes the precompiled header. Change this to whatever is relevant for your project.
 #include "ImageLoader.h"
 #include "VisualizerSample.h"
 #include "Runtime/ImageWrapper/Public/IImageWrapper.h"
 #include "RenderUtils.h"
 #include "Engine/Texture2D.h"
 #include "ModuleManager.h"
 #include "Async.h"
 #include "FileHelper.h"
 #include "Future.h"
 #include "Runtime/ImageWrapper/Public/IImageWrapperModule.h"
 
 // Change the UE_LOG log category name below to whichever log category you want to use.
 #define UIL_LOG(Verbosity, Format, ...)    UE_LOG(LogTemp, Verbosity, Format, __VA_ARGS__)
 
 // Module loading is not allowed outside of the main thread, so we load the ImageWrapper module ahead of time.
 static IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
 
 bool UImageLoader::GetFiles(TArray<FString>& Files, FString RootFolderFullPath, FString Ext)
 {
     if (RootFolderFullPath.Len() < 1) return false;
     FPaths::NormalizeDirectoryName(RootFolderFullPath);
     IFileManager& FileManager = IFileManager::Get();
     if (Ext == "")
     {
         Ext = "*.*";
     }
     else
     {
         Ext = (Ext.Left(1) == ".") ? "*" + Ext : "*." + Ext;
     }
     FString FinalPath = RootFolderFullPath + "/" + Ext;
     FileManager.FindFiles(Files, *FinalPath, true, false);
     return true;
 }
 
 UImageLoader* UImageLoader::LoadImageFromDiskAsync(UObject* Outer, const FString& ImagePath)
 {
     // This simply creates a new ImageLoader object and starts an asynchronous load.
     UImageLoader* Loader = NewObject<UImageLoader>();
     Loader->LoadImageAsync(Outer, ImagePath);
     return Loader;
 }
 
 void UImageLoader::LoadImageAsync(UObject* Outer, const FString& ImagePath)
 {
     // The asynchronous loading operation is represented by a Future, which will contain the result value once the operation is done.
     // We store the Future in this object, so we can retrieve the result value in the completion callback below.
     Future = LoadImageFromDiskAsync(Outer, ImagePath, [this]()
     {
         // This is the same Future object that we assigned above, but later in time.
         // At this point, loading is done and the Future contains a value.
         if (Future.IsValid())
         {
             // Notify listeners about the loaded texture on the game thread.
             AsyncTask(ENamedThreads::GameThread, [this]() { LoadCompleted.Broadcast(Future.Get()); });
         }
     });
 }
 
 TFuture<UTexture2D*> UImageLoader::LoadImageFromDiskAsync(UObject* Outer, const FString& ImagePath, TFunction<void()> CompletionCallback)
 {
     // Run the image loading function asynchronously through a lambda expression, capturing the ImagePath string by value.
     // Run it on the thread pool, so we can load multiple images simultaneously without interrupting other tasks.
     return Async<UTexture2D*>(EAsyncExecution::ThreadPool, [=]() { return LoadImageFromDisk(Outer, ImagePath); }, CompletionCallback);
 }
 
 UTexture2D* UImageLoader::LoadImageFromDisk(UObject* Outer, const FString& ImagePath)
 {
     // Check if the file exists first
     if (!FPaths::FileExists(ImagePath))
     {
         UIL_LOG(Error, TEXT("File not found: %s"), *ImagePath);
         return nullptr;
     }
 
     // Load the compressed byte data from the file
     TArray<uint8> FileData;
     if (!FFileHelper::LoadFileToArray(FileData, *ImagePath))
     {
         UIL_LOG(Error, TEXT("Failed to load file: %s"), *ImagePath);
         return nullptr;
     }
 
     // Detect the image type using the ImageWrapper module
     EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(FileData.GetData(), FileData.Num());
     if (ImageFormat == EImageFormat::Invalid)
     {
         UIL_LOG(Error, TEXT("Unrecognized image file format: %s"), *ImagePath);
         return nullptr;
     }
 
     // Create an image wrapper for the detected image format
     TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat);
     if (!ImageWrapper.IsValid())
     {
         UIL_LOG(Error, TEXT("Failed to create image wrapper for file: %s"), *ImagePath);
         return nullptr;
     }
 
     // Decompress the image data
     const TArray<uint8>* RawData = nullptr;
     ImageWrapper->SetCompressed(FileData.GetData(), FileData.Num());
     ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, RawData);
     if (RawData == nullptr)
     {
         UIL_LOG(Error, TEXT("Failed to decompress image file: %s"), *ImagePath);
         return nullptr;
     }
 
     // Create the texture and upload the uncompressed image data
     FString TextureBaseName = TEXT("Texture_") + FPaths::GetBaseFilename(ImagePath);
     return CreateTexture(Outer, *RawData, ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), EPixelFormat::PF_B8G8R8A8, FName(*TextureBaseName));
 }
 
 UTexture2D* UImageLoader::CreateTexture(UObject* Outer, const TArray<uint8>& PixelData, int32 InSizeX, int32 InSizeY, EPixelFormat InFormat, FName BaseName)
 {
     // Shamelessly copied from UTexture2D::CreateTransient with a few modifications
     if (InSizeX <= 0 || InSizeY <= 0 ||
         (InSizeX % GPixelFormats[InFormat].BlockSizeX) != 0 ||
         (InSizeY % GPixelFormats[InFormat].BlockSizeY) != 0)
     {
         UIL_LOG(Warning, TEXT("Invalid parameters specified for UImageLoader::CreateTexture()"));
         return nullptr;
     }
 
     // Most important difference with UTexture2D::CreateTransient: we provide the new texture with a name and an owner
     FName TextureName = MakeUniqueObjectName(Outer, UTexture2D::StaticClass(), BaseName);
     UTexture2D* NewTexture = NewObject<UTexture2D>(Outer, TextureName, RF_Transient);
 
     NewTexture->PlatformData = new FTexturePlatformData();
     NewTexture->PlatformData->SizeX = InSizeX;
     NewTexture->PlatformData->SizeY = InSizeY;
     NewTexture->PlatformData->PixelFormat = InFormat;
 
     // Allocate first mipmap and upload the pixel data
     int32 NumBlocksX = InSizeX / GPixelFormats[InFormat].BlockSizeX;
     int32 NumBlocksY = InSizeY / GPixelFormats[InFormat].BlockSizeY;
     FTexture2DMipMap* Mip = new(NewTexture->PlatformData->Mips) FTexture2DMipMap();
     Mip->SizeX = InSizeX;
     Mip->SizeY = InSizeY;
     Mip->BulkData.Lock(LOCK_READ_WRITE);
     void* TextureData = Mip->BulkData.Realloc(NumBlocksX * NumBlocksY * GPixelFormats[InFormat].BlockBytes);
     FMemory::Memcpy(TextureData, PixelData.GetData(), PixelData.Num());
     Mip->BulkData.Unlock();
 
     NewTexture->UpdateResource();
     return NewTexture;
 }



Product Version: UE 4.19
Tags:
more ▼

asked Aug 21 '18 at 08:47 AM in C++ Programming

avatar image

Jenova723
26 2 3

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

2 answers: sort voted first

This is working for me.

 // This includes the precompiled header. Change this to whatever is relevant for your project.
 #include "Project.h"
 
 #include "ImageLoader.h"
 #include "Runtime/ImageWrapper/Public/IImageWrapper.h"//#include "IImageWrapper.h"
 #include "Runtime/ImageWrapper/Public/IImageWrapperModule.h"
 #include "Runtime/RenderCore/Public/RenderUtils.h"
 #include "Engine/Texture2D.h"
 
 // Change the UE_LOG log category name below to whichever log category you want to use.
 
 #define UIL_LOG(Verbosity, Format, ...) UE_LOG(LogTemp, Verbosity, Format, __VA_ARGS__)
 
 // Module loading is not allowed outside of the main thread, so we load the ImageWrapper module ahead of time. 
 //static IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(TEXT("ImageWrapper"));
 static IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
 UImageLoader* UImageLoader::LoadImageFromDiskAsync(UObject* Outer, const FString& ImagePath) { // This simply creates a new ImageLoader object and starts an asynchronous load. 
 UImageLoader* Loader = NewObject<UImageLoader>(); 
 Loader->LoadImageAsync(Outer, ImagePath); 
 return Loader; }
 void UImageLoader::LoadImageAsync(UObject* Outer, const FString& ImagePath) { // The asynchronous loading operation is represented by a Future, which will contain the result value once the operation is done. // We store the Future in this object, so we can retrieve the result value in the completion callback below. 
 Future = LoadImageFromDiskAsync(Outer, ImagePath, [this]() { // This is the same Future object that we assigned above, but later in time. // At this point, loading is done and the Future contains a value. 
 if (Future.IsValid()) { // Notify listeners about the loaded texture on the game thread. 
 AsyncTask(ENamedThreads::GameThread, [this]() { LoadCompleted.Broadcast(Future.Get()); }); } }); }
 
 TFuture<UTexture2D*> UImageLoader::LoadImageFromDiskAsync(UObject* Outer, const FString& ImagePath, TFunction<void()> CompletionCallback) { // Run the image loading function asynchronously through a lambda expression, capturing the ImagePath string by value. // Run it on the thread pool, so we can load multiple images simultaneously without interrupting other tasks. 
 return Async<UTexture2D*>(EAsyncExecution::ThreadPool, [=]() { return LoadImageFromDisk(Outer, ImagePath); }, CompletionCallback); }
 
 UTexture2D* UImageLoader::LoadImageFromDisk(UObject* Outer, const FString& ImagePath) { // Check if the file exists first 
 if (!FPaths::FileExists(ImagePath)) { UIL_LOG(Error, TEXT("File not found: %s"), *ImagePath); 
 return nullptr; }
 
 // Load the compressed byte data from the file 
 TArray<uint8> FileData; 
 if (!FFileHelper::LoadFileToArray(FileData, *ImagePath)) { UIL_LOG(Error, TEXT("Failed to load file: %s"), *ImagePath); 
 return nullptr; }
 
 // Detect the image type using the ImageWrapper module 
 EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(FileData.GetData(), FileData.Num()); 
 if (ImageFormat == EImageFormat::Invalid) { 
 UIL_LOG(Error, TEXT("Unrecognized image file format: %s"), *ImagePath); 
 return nullptr; }
 
 // Create an image wrapper for the detected image format 
 TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); 
 if (!ImageWrapper.IsValid()) { UIL_LOG(Error, TEXT("Failed to create image wrapper for file: %s"), *ImagePath); 
 return nullptr; }
 
 // Decompress the image data 
 const TArray<uint8>* RawData = nullptr; 
 ImageWrapper->SetCompressed(FileData.GetData(), FileData.Num()); 
 ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, RawData); 
 if (RawData == nullptr) { UIL_LOG(Error, TEXT("Failed to decompress image file: %s"), *ImagePath); 
 return nullptr; }
 
 // Create the texture and upload the uncompressed image data 
 FString TextureBaseName = TEXT("Texture_") + FPaths::GetBaseFilename(ImagePath); 
 return CreateTexture(Outer, *RawData, ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), EPixelFormat::PF_B8G8R8A8, FName(*TextureBaseName)); }
 
 UTexture2D* UImageLoader::CreateTexture(UObject* Outer, const TArray<uint8>& PixelData, int32 InSizeX, int32 InSizeY, EPixelFormat InFormat, FName BaseName) { // Shamelessly copied from UTexture2D::CreateTransient with a few modifications 
 if (InSizeX <= 0 || InSizeY <= 0 || (InSizeX % GPixelFormats[InFormat].BlockSizeX) != 0 || (InSizeY % GPixelFormats[InFormat].BlockSizeY) != 0) { UIL_LOG(Warning, TEXT("Invalid parameters specified for UImageLoader::CreateTexture()")); 
 return nullptr; }
 
 // Most important difference with UTexture2D::CreateTransient: we provide the new texture with a name and an owner 
 FName TextureName = MakeUniqueObjectName(Outer, UTexture2D::StaticClass(), BaseName); 
 UTexture2D* NewTexture = NewObject<UTexture2D>(Outer, TextureName, RF_Transient);
 
 NewTexture->PlatformData = new FTexturePlatformData(); 
 NewTexture->PlatformData->SizeX = InSizeX; 
 NewTexture->PlatformData->SizeY = InSizeY; 
 NewTexture->PlatformData->PixelFormat = InFormat;
 
 // Allocate first mipmap and upload the pixel data 
 int32 NumBlocksX = InSizeX / GPixelFormats[InFormat].BlockSizeX; 
 int32 NumBlocksY = InSizeY / GPixelFormats[InFormat].BlockSizeY; 
 FTexture2DMipMap* Mip = new(NewTexture->PlatformData->Mips) FTexture2DMipMap(); 
 Mip->SizeX = InSizeX; 
 Mip->SizeY = InSizeY; 
 Mip->BulkData.Lock(LOCK_READ_WRITE); 
 void* TextureData = Mip->BulkData.Realloc(NumBlocksX * NumBlocksY * GPixelFormats[InFormat].BlockBytes); 
 FMemory::Memcpy(TextureData, PixelData.GetData(), PixelData.Num()); 
 Mip->BulkData.Unlock();
 
 NewTexture->UpdateResource(); 
 return NewTexture; 
 }


more ▼

answered Oct 30 '18 at 06:33 PM

avatar image

BestWarrior
34 3 3 6

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

I ran into the same issue, but the above fix did not work for me.

Apparently making this call: IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper"));

as a static variable failed. I moved the call to the function call where it was used and the crash went away. Apparently doing the call that early doesn't work in subsequent versions of Unreal.

more ▼

answered Apr 22 '19 at 04:52 PM

avatar image

Michal Todorovic
1 1

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question