Advertisement
Guest User

Untitled

a guest
Nov 15th, 2019
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.58 KB | None | 0 0
  1. // This includes the precompiled header. Change this to whatever is relevant for your project.
  2.  
  3. #include "ImageLoader.h"
  4. #include "Async/Async.h"
  5. #include "Engine/Texture2D.h"
  6. #include "IImageWrapper.h"
  7. #include "IImageWrapperModule.h"
  8. #include "RenderUtils.h"
  9.  
  10. // Change the UE_LOG log category name below to whichever log category you want to use.
  11. #define UIL_LOG( Verbosity, Format, ... ) UE_LOG( LogTemp, Verbosity, Format, __VA_ARGS__ )
  12.  
  13. // Module loading is not allowed outside of the main thread, so we load the ImageWrapper module ahead of time.
  14. static IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked< IImageWrapperModule >( TEXT( "ImageWrapper" ) );
  15.  
  16. UImageLoader* UImageLoader::LoadImageFromDiskAsync( UObject* Outer, const FString& ImagePath )
  17. {
  18.     // This simply creates a new ImageLoader object and starts an asynchronous load.
  19.     UImageLoader* Loader = NewObject< UImageLoader >();
  20.     Loader->LoadImageAsync( Outer, ImagePath );
  21.     return Loader;
  22. }
  23.  
  24. void UImageLoader::LoadImageAsync( UObject* Outer, const FString& ImagePath )
  25. {
  26.     // The asynchronous loading operation is represented by a Future, which will contain the result value once the operation is done.
  27.     // We store the Future in this object, so we can retrieve the result value in the completion callback below.
  28.     Future = LoadImageFromDiskAsync( Outer, ImagePath, [ this ]() {
  29.         // This is the same Future object that we assigned above, but later in time.
  30.         // At this point, loading is done and the Future contains a value.
  31.         if( Future.IsValid() )
  32.         {
  33.             // Notify listeners about the loaded texture on the game thread.
  34.             AsyncTask( ENamedThreads::GameThread, [ this ]() { LoadCompleted.Broadcast( Future.Get() ); } );
  35.         }
  36.     } );
  37. }
  38.  
  39. TFuture< UTexture2D* > UImageLoader::LoadImageFromDiskAsync( UObject* Outer, const FString& ImagePath, TFunction< void() > CompletionCallback )
  40. {
  41.     // Run the image loading function asynchronously through a lambda expression, capturing the ImagePath string by value.
  42.     // Run it on the thread pool, so we can load multiple images simultaneously without interrupting other tasks.
  43.     return Async(
  44.         EAsyncExecution::ThreadPool, [ = ]() { return LoadImageFromDisk( Outer, ImagePath ); }, MoveTemp( CompletionCallback ) );
  45. }
  46.  
  47. UTexture2D* UImageLoader::LoadImageFromDisk( UObject* Outer, const FString& ImagePath )
  48. {
  49.     // Check if the file exists first
  50.     if( !FPaths::FileExists( ImagePath ) )
  51.     {
  52.         UIL_LOG( Error, TEXT( "File not found: %s" ), *ImagePath );
  53.         return nullptr;
  54.     }
  55.  
  56.     // Load the compressed byte data from the file
  57.     TArray< uint8 > FileData;
  58.     if( !FFileHelper::LoadFileToArray( FileData, *ImagePath ) )
  59.     {
  60.         UIL_LOG( Error, TEXT( "Failed to load file: %s" ), *ImagePath );
  61.         return nullptr;
  62.     }
  63.  
  64.     // Detect the image type using the ImageWrapper module
  65.     EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat( FileData.GetData(), FileData.Num() );
  66.     if( ImageFormat == EImageFormat::Invalid )
  67.     {
  68.         UIL_LOG( Error, TEXT( "Unrecognized image file format: %s" ), *ImagePath );
  69.         return nullptr;
  70.     }
  71.  
  72.     // Create an image wrapper for the detected image format
  73.     TSharedPtr< IImageWrapper > ImageWrapper = ImageWrapperModule.CreateImageWrapper( ImageFormat );
  74.     if( !ImageWrapper.IsValid() )
  75.     {
  76.         UIL_LOG( Error, TEXT( "Failed to create image wrapper for file: %s" ), *ImagePath );
  77.         return nullptr;
  78.     }
  79.  
  80.     // Decompress the image data
  81.     const TArray< uint8 >* RawData = nullptr;
  82.     ImageWrapper->SetCompressed( FileData.GetData(), FileData.Num() );
  83.     ImageWrapper->GetRaw( ERGBFormat::BGRA, 8, RawData );
  84.     if( RawData == nullptr )
  85.     {
  86.         UIL_LOG( Error, TEXT( "Failed to decompress image file: %s" ), *ImagePath );
  87.         return nullptr;
  88.     }
  89.  
  90.     // Create the texture and upload the uncompressed image data
  91.     FString TextureBaseName = TEXT( "Texture_" ) + FPaths::GetBaseFilename( ImagePath );
  92.     return CreateTexture( Outer, *RawData, ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), EPixelFormat::PF_B8G8R8A8, FName( *TextureBaseName ) );
  93. }
  94.  
  95. UTexture2D* UImageLoader::CreateTexture( UObject* Outer, const TArray< uint8 >& PixelData, int32 InSizeX, int32 InSizeY, EPixelFormat InFormat, FName BaseName )
  96. {
  97.     // Shamelessly copied from UTexture2D::CreateTransient with a few modifications
  98.     if( InSizeX <= 0 || InSizeY <= 0 || ( InSizeX % GPixelFormats[ InFormat ].BlockSizeX ) != 0 || ( InSizeY % GPixelFormats[ InFormat ].BlockSizeY ) != 0 )
  99.     {
  100.         UIL_LOG( Warning, TEXT( "Invalid parameters specified for UImageLoader::CreateTexture()" ) );
  101.         return nullptr;
  102.     }
  103.  
  104.     // Most important difference with UTexture2D::CreateTransient: we provide the new texture with a name and an owner
  105.     FName TextureName = MakeUniqueObjectName( Outer, UTexture2D::StaticClass(), BaseName );
  106.     UTexture2D* NewTexture = NewObject< UTexture2D >( Outer, TextureName, RF_Transient );
  107.  
  108.     NewTexture->PlatformData = new FTexturePlatformData();
  109.     NewTexture->PlatformData->SizeX = InSizeX;
  110.     NewTexture->PlatformData->SizeY = InSizeY;
  111.     NewTexture->PlatformData->PixelFormat = InFormat;
  112.  
  113.     // Allocate first mipmap and upload the pixel data
  114.     int32 NumBlocksX = InSizeX / GPixelFormats[ InFormat ].BlockSizeX;
  115.     int32 NumBlocksY = InSizeY / GPixelFormats[ InFormat ].BlockSizeY;
  116.     FTexture2DMipMap* Mip = new( NewTexture->PlatformData->Mips ) FTexture2DMipMap();
  117.     Mip->SizeX = InSizeX;
  118.     Mip->SizeY = InSizeY;
  119.     Mip->BulkData.Lock( LOCK_READ_WRITE );
  120.     void* TextureData = Mip->BulkData.Realloc( NumBlocksX * NumBlocksY * GPixelFormats[ InFormat ].BlockBytes );
  121.     FMemory::Memcpy( TextureData, PixelData.GetData(), PixelData.Num() );
  122.     Mip->BulkData.Unlock();
  123.  
  124.     NewTexture->UpdateResource();
  125.     return NewTexture;
  126. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement