Files
2025-05-18 13:04:45 +08:00

1544 lines
45 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TextureHelper.h"
#include "Async/ParallelFor.h"
#include "ClearQuad.h"
#include "Data/RawBuffer.h"
#include "Engine/Texture.h"
#include "Engine/TextureRenderTarget2D.h"
#include "TextureGraphEngineGameInstance.h"
#include "Modules/ModuleManager.h"
#include "RHICommandList.h"
#include "RHIDefinitions.h"
#include "RenderUtils.h"
#include "RendererInterface.h"
#include "Rendering/Texture2DResource.h"
#include "Tex.h"
#include "Data/TiledBlob.h"
#include "CanvasTypes.h"
#include "Engine/Canvas.h"
#include "Misc/App.h"
#include "TextureResource.h"
#include "SceneUtils.h"
#include "Logging/MessageLog.h"
#include "Engine/TextureRenderTarget2D.h"
#include "ImageUtils.h"
#include "OneColorShader.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "Engine/Texture2D.h"
#include <Serialization/BufferArchive.h>
#include <IImageWrapper.h>
#include <IImageWrapperModule.h>
#include <ImageWriteBlueprintLibrary.h>
#include "TextureSet.h"
#include "Helper/ColorUtil.h"
#include "RHIGPUReadback.h"
#include "Data/Blobber.h"
#include "TextureGraphEngine.h"
#include "Curves/CurveLinearColorAtlas.h"
#include "Engine/TextureLightProfile.h"
#include "Model/Mix/MixSettings.h"
#if WITH_EDITOR
#include "Editor.h"
#endif
TiledBlobPtr TextureHelper::GTransparent;
TiledBlobPtr TextureHelper::GBlack;
TiledBlobPtr TextureHelper::GWhite;
TiledBlobPtr TextureHelper::GGray;
TiledBlobPtr TextureHelper::GRed;
TiledBlobPtr TextureHelper::GGreen;
TiledBlobPtr TextureHelper::GBlue;
TiledBlobPtr TextureHelper::GYellow;
TiledBlobPtr TextureHelper::GMagenta;
TiledBlobPtr TextureHelper::GDefaultNormal;
TiledBlobPtr TextureHelper::GWhiteMask;
TiledBlobPtr TextureHelper::GBlackMask;
const uint32 s_maxSolidTextureSize = 1;
cti::continuable<bool> TextureHelper::InitSolidTexture(TiledBlobPtr* BlobObj, FLinearColor clr, FString Name, TexDescriptor Desc)
{
#if WITH_EDITOR
if (!GEditor)
return cti::make_ready_continuable(false);
Desc.Width = s_maxSolidTextureSize;
Desc.Height = s_maxSolidTextureSize;
Desc.ClearColor = clr;
Desc.Name = Name;
Tex* TexObj = new Tex(Desc);
return TexObj->ToSingleBlob(std::make_shared<CHash>(TexObj->GetDescriptor().HashValue(), true), false, true, true)
.then([TexObj, BlobObj](TiledBlobRef Result)
{
check(Result);
Result->GetTile(0, 0)->GetBufferRef()->SetDeviceTransferChain({ DeviceType::FX }, true);
*BlobObj = Result;
Util::OnGameThread([=]()
{
delete TexObj;
});
return true;
})
.fail([TexObj, BlobObj](std::exception_ptr e)
{
*BlobObj = nullptr;
Util::OnGameThread([=]()
{
delete TexObj;
});
throw e;
return false;
});
#else
return cti::make_ready_continuable(false);
#endif
}
void TextureHelper::InitStockTextures()
{
#if WITH_EDITOR
// No need to run during command-let execution
if (!GEditor)
{
return;
}
check(IsInGameThread());
UE_LOG(LogTemp, Log, TEXT("Init stock textures ..."));
/// If we've already go the stuff, then we don't need to worry about it
if (GBlack)
{
return;
}
TextureType Type = TextureHelper::TextureContentToTextureType(TextureContent::Albedo);
TexDescriptor AlbedoDesc(TextureSet::GDesc[(int32)Type]);
// TODO: For now, just switching the white texture srgb value to linear.
// Need better solution later.
TexDescriptor LinearDesc(TextureSet::GDesc[(int32)Type]);
LinearDesc.bIsSRGB = false;
std::vector<cti::continuable<bool>> Promises;
// TODO: Need these values to be linear.
Promises.push_back(InitSolidTexture(&GTransparent, FLinearColor::Transparent, TEXT("TRANSPARENT"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GBlack, FLinearColor::Black, TEXT("BLACK"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GWhite, FLinearColor::White, TEXT("WHITE"), LinearDesc));
Promises.push_back(InitSolidTexture(&GGray, FLinearColor::Gray, TEXT("GRAY"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GRed, FLinearColor::Red, TEXT("RED"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GGreen, FLinearColor::Green, TEXT("GREEN"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GBlue, FLinearColor::Blue, TEXT("BLUE"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GYellow, FLinearColor::Yellow, TEXT("YELLOW"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GMagenta, FLinearColor(1, 0, 1, 1), TEXT("MAGENTA"), AlbedoDesc));
Promises.push_back(InitSolidTexture(&GDefaultNormal, ColorUtil::DefaultNormal(), TEXT("DefaultNormal"), AlbedoDesc));
TexDescriptor MaskDesc(1, 1, PF_G8, false, false, false);
MaskDesc.NumChannels = 1;
Promises.push_back(InitSolidTexture(&GWhiteMask, FLinearColor::White, TEXT("WhiteMask"), MaskDesc));
Promises.push_back(InitSolidTexture(&GBlackMask, FLinearColor::Black, TEXT("BlackMask"), MaskDesc));
cti::when_all(Promises.begin(), Promises.end()).apply(cti::transforms::wait());
UE_LOG(LogTemp, Log, TEXT("Stock textures init complete!"));
#endif
}
void TextureHelper::FreeStockTextures()
{
check(IsInGameThread());
GTransparent = nullptr;
GBlack = nullptr;
GWhite = nullptr;
GGray = nullptr;
GRed = nullptr;
GGreen = nullptr;
GBlue = nullptr;
GYellow = nullptr;
GDefaultNormal = nullptr;
}
uint32 TextureHelper::GetChannelsFromPixelFormat(EPixelFormat InPixelFormat)
{
switch (InPixelFormat)
{
case PF_Unknown:
case PF_A32B32G32R32F:
case PF_B8G8R8A8:
case PF_FloatRGBA:
case PF_A2B10G10R10:
case PF_A16B16G16R16:
case PF_R16G16B16A16_UINT:
case PF_R16G16B16A16_SINT:
case PF_R32G32B32A32_UINT:
case PF_R8G8B8A8_UINT:
case PF_R8G8B8A8_SNORM:
case PF_R16G16B16A16_UNORM:
case PF_R16G16B16A16_SNORM:
case PF_R8G8B8A8:
case PF_A8R8G8B8:
return 4;
case PF_FloatR11G11B10:
case PF_FloatRGB:
case PF_R32G32B32F:
case PF_ETC2_RGB:
case PF_ATC_RGB:
case PF_R5G6B5_UNORM:
return 3;
case PF_G16R16:
case PF_G16R16F:
case PF_G16R16F_FILTER:
case PF_G32R32F:
case PF_DepthStencil:
case PF_V8U8:
case PF_R8G8:
case PF_R32G32_UINT:
//case PF_BC5:
return 2;
case PF_G8:
case PF_G16:
case PF_ShadowDepth:
case PF_R32_FLOAT:
case PF_D24:
case PF_R16F:
case PF_R16F_FILTER:
case PF_A8:
case PF_R32_UINT:
case PF_R32_SINT:
case PF_A1:
case PF_R16_UINT:
case PF_R16_SINT:
return 1;
default:
return 4;
}
}
uint32 TextureHelper::GetBppFromPixelFormat(EPixelFormat InPixelFormat)
{
switch (InPixelFormat)
{
case PF_Unknown:
case PF_A32B32G32R32F:
return 4 * sizeof(float) * 8;
case PF_R32G32B32F:
return 3 * sizeof(float) * 8;
case PF_B8G8R8A8:
return 4 * sizeof(char) * 8;
case PF_A2B10G10R10:
return 32;
case PF_A16B16G16R16:
return 4 * 16;
case PF_R16G16B16A16_UINT:
return 4 * 16;
case PF_R16G16B16A16_SINT:
return 4 * 16;
case PF_R32G32B32A32_UINT:
return 4 * 32;
case PF_R8G8B8A8_UINT:
return 4 * 8;
case PF_R8G8B8A8_SNORM:
return 4 * 8;
case PF_R16G16B16A16_UNORM:
return 4 * 16;
case PF_R16G16B16A16_SNORM:
return 4 * 16;
case PF_R8G8B8A8:
return 4 * 8;
case PF_A8R8G8B8:
return 4 * 8 ;
case PF_FloatR11G11B10:
return 32;
case PF_FloatRGB: //Float RBA is 16 bit
return 3 * 16;
case PF_FloatRGBA:
return 4 * 16;
case PF_G16R16:
return 2 * 16;
case PF_G16R16F:
return 2 * 16;
case PF_G16R16F_FILTER:
return 2 * 16;
case PF_G32R32F:
return 2 * 32;
case PF_DepthStencil:
return 24;
case PF_V8U8:
return 2 * 8;
case PF_R8G8:
return 2 * 8;
case PF_R32G32_UINT:
return 2 * 32;
case PF_R8:
case PF_G8:
case PF_A8:
return 8;
case PF_G16:
return 16;
case PF_ShadowDepth:
return 16;
case PF_R32_FLOAT:
return 32;
case PF_D24:
return 24;
case PF_R16F:
return 16;
case PF_R16F_FILTER:
return 16;
case PF_R32_UINT:
return 32;
case PF_R32_SINT:
return 32;
case PF_A1:
return 1;
case PF_R16_UINT:
return 16;
case PF_R16_SINT:
return 16;
default:
return 0;
}
}
EPixelFormat TextureHelper::GetPixelFormatFromRenderTargetFormat(ETextureRenderTargetFormat RTFormat)
{
return ::GetPixelFormatFromRenderTargetFormat(RTFormat);
}
bool TextureHelper::IsFloatRT(UTextureRenderTarget2D* RenderTarget)
{
check(RenderTarget);
return
RenderTarget->RenderTargetFormat == ETextureRenderTargetFormat::RTF_R32f ||
RenderTarget->RenderTargetFormat == ETextureRenderTargetFormat::RTF_RG32f ||
RenderTarget->RenderTargetFormat == ETextureRenderTargetFormat::RTF_RGBA32f;
}
ETextureRenderTargetFormat TextureHelper::GetRenderTargetFormatFromPixelFormat(EPixelFormat InPixelFormat)
{
switch (InPixelFormat)
{
case PF_G8:
return ETextureRenderTargetFormat::RTF_R8;
case PF_R8G8:
return ETextureRenderTargetFormat::RTF_RG8;
case PF_B8G8R8A8:
return ETextureRenderTargetFormat::RTF_RGBA8;
case PF_R16F:
return ETextureRenderTargetFormat::RTF_R16f;
case PF_G16R16F:
return ETextureRenderTargetFormat::RTF_RG16f;
case PF_R32_FLOAT:
return ETextureRenderTargetFormat::RTF_R32f;
case PF_G32R32F:
return ETextureRenderTargetFormat::RTF_RG32f;
case PF_A32B32G32R32F:
return ETextureRenderTargetFormat::RTF_RGBA32f;
case PF_A2B10G10R10:
return ETextureRenderTargetFormat::RTF_RGB10A2;
case PF_R32G32B32F:
return ETextureRenderTargetFormat::RTF_RGBA32f;
case PF_A16B16G16R16:
case PF_FloatRGBA:
return ETextureRenderTargetFormat::RTF_RGBA16f;
default:
break;
}
return ETextureRenderTargetFormat::RTF_RGBA8;
}
ETextureSourceFormat TextureHelper::GetSourceFormat(ETG_TextureFormat TGTextureFormat)
{
switch (TGTextureFormat)
{
case ETG_TextureFormat::RGBA16F:
return ETextureSourceFormat::TSF_RGBA16F;
case ETG_TextureFormat::BGRA8:
return ETextureSourceFormat::TSF_BGRA8;
case ETG_TextureFormat::G8:
return ETextureSourceFormat::TSF_G8;
case ETG_TextureFormat::R16F:
return ETextureSourceFormat::TSF_R16F;
case ETG_TextureFormat::R32F:
return ETextureSourceFormat::TSF_R32F;
case ETG_TextureFormat::RGBA32F:
return ETextureSourceFormat::TSF_RGBA32F;
default:
return ETextureSourceFormat::TSF_BGRA8;
}
}
bool TextureHelper::GetBufferFormatAndChannelsFromTGTextureFormat(ETG_TextureFormat Format, BufferFormat& OutBufferFormat, uint32& OutBufferChannels)
{
switch (Format)
{
case ETG_TextureFormat::Auto:
OutBufferFormat = BufferFormat::Auto;
OutBufferChannels = 0;
break;
case ETG_TextureFormat::RGBA16F:
OutBufferFormat = BufferFormat::Half;
OutBufferChannels = 4;
break;
case ETG_TextureFormat::BGRA8:
OutBufferFormat = BufferFormat::Byte;
OutBufferChannels = 4;
break;
case ETG_TextureFormat::G8:
OutBufferFormat = BufferFormat::Byte;
OutBufferChannels = 1;
break;
case ETG_TextureFormat::R16F:
OutBufferFormat = BufferFormat::Half;
OutBufferChannels = 1;
break;
case ETG_TextureFormat::R32F:
OutBufferFormat = BufferFormat::Float;
OutBufferChannels = 1;
break;
case ETG_TextureFormat::RGBA32F:
OutBufferFormat = BufferFormat::Float;
OutBufferChannels = 4;
break;
default:
return false;
}
return true;
}
ETG_TextureFormat TextureHelper::GetTGTextureFormatFromChannelsAndFormat(uint32 ItemsPerPoint, BufferFormat Format)
{
ETG_TextureFormat TextureFormat = ETG_TextureFormat::Auto;
if(ItemsPerPoint == 0 || ItemsPerPoint == 3 || ItemsPerPoint == 4)
{
// not using 3 channels at the moment, falling back to use 4
switch (Format)
{
default:
case BufferFormat::Auto:
case BufferFormat::Byte:
TextureFormat = ETG_TextureFormat::BGRA8;
break;
case BufferFormat::Half:
TextureFormat = ETG_TextureFormat::RGBA16F;
break;
case BufferFormat::Float:
TextureFormat = ETG_TextureFormat::RGBA32F;
break;
}
}
else if (ItemsPerPoint == 1 || ItemsPerPoint == 2)
{
// not using 2 channels for now, falling back to one channel
switch (Format)
{
default:
case BufferFormat::Auto:
case BufferFormat::Byte:
TextureFormat = ETG_TextureFormat::G8;
break;
case BufferFormat::Half:
TextureFormat = ETG_TextureFormat::R16F;
break;
case BufferFormat::Float:
TextureFormat = ETG_TextureFormat::R32F;
break;
}
}
return TextureFormat;
}
uint32 TextureHelper::GetNumChannelsFromTGTextureFormat(ETG_TextureFormat TextureFormat)
{
switch(TextureFormat)
{
default:
case ETG_TextureFormat::Auto:
return 0;
case ETG_TextureFormat::G8:
case ETG_TextureFormat::R16F:
case ETG_TextureFormat::R32F:
return 1;
case ETG_TextureFormat::BGRA8:
case ETG_TextureFormat::RGBA16F:
case ETG_TextureFormat::RGBA32F:
return 4;
}
}
FString TextureHelper::GetChannelsTextFromItemsPerPoint(const int32 InItemsPerPoint)
{
FString Channel = "R";
switch (InItemsPerPoint)
{
case 2:
Channel = "RG";
break;
case 3:
Channel = "RGB";
break;
case 4:
Channel = "RGBA";
break;
default:
Channel = "R";
break;
}
return Channel;
}
void TextureHelper::ClearRT(FRHICommandList& RHI, UTextureRenderTarget2D* RenderTarget, FLinearColor Color /* = FLinearColor::Transparent */)
{
UE_LOG(LogTexture, VeryVerbose, TEXT("Clear RT: %s"), *RenderTarget->GetName());
FTextureRenderTarget2DResource* RTRes = (FTextureRenderTarget2DResource*)RenderTarget->GetRenderTargetResource();
// Silently ignore this
if (!RTRes)
return;
FRHIRenderPassInfo RenderPassInfo(RTRes->GetRenderTargetTexture(), ERenderTargetActions::DontLoad_Store);
TransitionRenderPassTargets(RHI, RenderPassInfo);
RHI.BeginRenderPass(RenderPassInfo, TEXT("ClearRT"));
DrawClearQuad(RHI, Color);
RHI.EndRenderPass();
}
void TextureHelper::ClearRT(UTextureRenderTarget2D* RenderTarget, FLinearColor Color /* = FLinearColor::Transparent */)
{
ENQUEUE_RENDER_COMMAND(ClearRTCommand)([RenderTarget, Color](FRHICommandList& RHI)
{
TextureHelper::ClearRT(RHI, RenderTarget, Color);
});
}
const char* TextureHelper::TextureTypeToString(TextureType Type)
{
switch (Type)
{
case TextureType::Diffuse: return "Diffuse";
case TextureType::Specular: return "Specular";
case TextureType::Albedo: return "Albedo";
case TextureType::Metalness: return "Metalness";
case TextureType::Normal: return "Normal";
case TextureType::Displacement: return "Displacement";
case TextureType::Opacity: return "Opacity";
case TextureType::Roughness: return "Roughness";
case TextureType::AO: return "AO";
case TextureType::Curvature: return "Curvature";
case TextureType::Preview: return "Preview";
default: break;
}
return "";
}
const char* TextureHelper::TextureTypeToMegascansType(TextureType Type)
{
switch (Type)
{
case TextureType::Diffuse: return "diffuse";
case TextureType::Specular: return "specular";
case TextureType::Albedo: return "albedo";
case TextureType::Metalness: return "metalness";
case TextureType::Normal: return "normal";
case TextureType::Displacement: return "displacement";
case TextureType::Opacity: return "opacity";
case TextureType::Roughness: return "roughness";
case TextureType::AO: return "ao";
case TextureType::Curvature: return "curvature";
case TextureType::Preview: return "preview";
default: break;
}
return "";
}
TextureType TextureHelper::TextureContentToTextureType(TextureContent Content)
{
switch (Content)
{
case TextureContent::Diffuse:
return TextureType::Diffuse;
case TextureContent::Specular:
return TextureType::Specular;
case TextureContent::AO:
return TextureType::AO;
case TextureContent::Normal:
return TextureType::Normal;
case TextureContent::Displacement:
return TextureType::Displacement;
case TextureContent::Roughness:
return TextureType::Roughness;
case TextureContent::Metalness:
return TextureType::Metalness;
case TextureContent::Albedo:
return TextureType::Albedo;
case TextureContent::Opacity:
return TextureType::Opacity;
case TextureContent::Curvature:
return TextureType::Curvature;
case TextureContent::Preview:
return TextureType::Preview;
}
return TextureType::Unknown;
}
TextureContent TextureHelper::TextureTypeToTextureContent(TextureType Type)
{
switch (Type)
{
case TextureType::Diffuse:
return TextureContent::Diffuse;
case TextureType::Specular:
return TextureContent::Specular;
case TextureType::Normal:
return TextureContent::Normal;
case TextureType::Displacement:
return TextureContent::Displacement;
case TextureType::Roughness:
return TextureContent::Roughness;
case TextureType::AO:
return TextureContent::AO;
case TextureType::Metalness:
return TextureContent::Metalness;
case TextureType::Albedo:
return TextureContent::Albedo;
case TextureType::Opacity:
return TextureContent::Opacity;
case TextureType::Curvature:
return TextureContent::Curvature;
case TextureType::Preview:
return TextureContent::Preview;
}
return TextureContent::None;
}
MaskType TextureHelper::TextureContentToMaskType(TextureContent Content)
{
switch (Content)
{
case TextureContent::PaintMask:
return MaskType::PaintMask;
case TextureContent::SolidMask:
return MaskType::SolidMask;
case TextureContent::ImageMask:
return MaskType::ImageMask;
case TextureContent::NoiseMask:
return MaskType::NoiseMask;
case TextureContent::PatternMask:
return MaskType::PatternMask;
case TextureContent::NormalMask:
return MaskType::NormalMask;
case TextureContent::CurvatureMask:
return MaskType::CurvatureMask;
case TextureContent::PositionGradient:
return MaskType::PositionGradient;
}
return MaskType::MasksTypeCount;
}
TextureContent TextureHelper::MaskTypeToTextureContent(MaskType Type)
{
switch (Type)
{
case MaskType::PaintMask:
return TextureContent::PaintMask;
case MaskType::SolidMask:
return TextureContent::SolidMask;
case MaskType::ImageMask:
return TextureContent::ImageMask;
case MaskType::NoiseMask:
return TextureContent::NoiseMask;
case MaskType::PatternMask:
return TextureContent::PatternMask;
case MaskType::NormalMask:
return TextureContent::NormalMask;
case MaskType::CurvatureMask:
return TextureContent::CurvatureMask;
case MaskType::PositionGradient:
return TextureContent::PositionGradient;
}
return TextureContent::None;
}
MaskModifierType TextureHelper::TextureContentToMaskModifier(TextureContent Content)
{
switch (Content)
{
case TextureContent::BrightnessMaskModifier:
return MaskModifierType::BrightnessMaskModifier;
case TextureContent::ClampMaskModifier:
return MaskModifierType::ClampMaskModifier;
case TextureContent::InvertMaskModifier:
return MaskModifierType::InvertMaskModifier;
case TextureContent::NormalizeMaskModifier:
return MaskModifierType::NormalizeMaskModifier;
case TextureContent::GradientRemapMaskModifier:
return MaskModifierType::GradientRemapMaskModifier;
case TextureContent::PosterizeMaskModifier:
return MaskModifierType::PosterizeMaskModifier;
case TextureContent::ScatterMaskModifier:
return MaskModifierType::ScatterMaskModifier;
}
return MaskModifierType::MaskModifiersTypeCount;
}
TextureContent TextureHelper::MaskModifierToTextureContent(MaskModifierType Type)
{
switch (Type)
{
case MaskModifierType::BrightnessMaskModifier:
return TextureContent::BrightnessMaskModifier;
case MaskModifierType::InvertMaskModifier:
return TextureContent::InvertMaskModifier;
case MaskModifierType::NormalizeMaskModifier:
return TextureContent::NormalizeMaskModifier;
case MaskModifierType::GradientRemapMaskModifier:
return TextureContent::GradientRemapMaskModifier;
case MaskModifierType::PosterizeMaskModifier:
return TextureContent::PosterizeMaskModifier;
case MaskModifierType::ScatterMaskModifier:
return TextureContent::ScatterMaskModifier;
}
return TextureContent::None;
}
TextureType TextureHelper::TextureStringToType(const FString& TypeString)
{
for (int i = 0; i < (int)TextureType::Count; i++)
{
TextureType Type = (TextureType)i;
FString CurrentTypeString = TextureHelper::TextureTypeToString(Type);
if (CurrentTypeString.Equals(TypeString))
{
return Type;
}
}
return TextureType::Count;
}
RawBufferPtr TextureHelper::RawFromRT(UTextureRenderTarget2D* RenderTarget, const BufferDescriptor& Desc)
{
//check(IsInRenderingThread()); //Can be from any thread
return RawFromResource(FTextureRHIRef(((FTexture2DResource*)RenderTarget->GetResource())->GetTexture2DRHI()), Desc);
}
RawBufferPtr TextureHelper::RawFromTexture(UTexture2D* Texture, const BufferDescriptor& Desc)
{
check(IsInRenderingThread());
uint8* RawData = nullptr;
size_t DataSize = 0;
//For editor we are using the source texture until we support the compressed Formats
#if WITH_EDITOR
if (Texture->Source.IsValid())
{
// Possibly the data was stored in TextureSource rather than PlatformData
// The difference is the source data (residing in Utexture) is editor only and can be saved to disk
// This is mostly used for thumbnail, where thumbnail data can exist across sessions (e.g UAsset)
// platform data, as the name suggests, is used during cook to convert to platform specific texture.
// This is legal because we might not be cooking our data.
TArray64<uint8> MipDataSrc;
Texture->Source.GetMipData(MipDataSrc, 0);
DataSize = MipDataSrc.Num();
RawData = new uint8[DataSize];
FMemory::Memcpy(RawData, MipDataSrc.GetData(), DataSize);
}
#endif
if (!RawData)
{
const uint8* MipData = static_cast<uint8*>(Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY));
DataSize = Texture->GetPlatformData()->Mips[0].BulkData.GetBulkDataSize();
if (DataSize != 0)
{
size_t DescSize = Desc.Size();
check(DataSize == DescSize);
RawData = new uint8[DataSize];
// Bulk data was already allocated for the correct size when we called CreateTransient above
FMemory::Memcpy(RawData, MipData, DataSize);
Texture->GetPlatformData()->Mips[0].BulkData.Unlock();
}
}
return std::make_shared<RawBuffer>(RawData, DataSize, Desc);
}
size_t TextureHelper::RoundUpTo(size_t Size, size_t DesiredRounding)
{
size_t RoundedSize = Size;
size_t Remainder = RoundedSize % DesiredRounding;
if (Remainder != 0)
{
RoundedSize = Size + DesiredRounding - Remainder;
}
return RoundedSize;
}
RawBufferPtr TextureHelper::RawFromResource(const FTextureRHIRef& ResourceRHI, const BufferDescriptor& Desc)
{
FRHICommandListImmediate& RHICmdList = FRHICommandListImmediate::Get();
try
{
RawBufferPtr RawObj;
const EPixelFormat PixelFormat = Desc.PixelFormat();
/// These must be the same!
check(TextureHelper::GetBppFromPixelFormat(ResourceRHI->GetFormat()) == TextureHelper::GetBppFromPixelFormat(PixelFormat));
const uint32 BitsPerPixel = TextureHelper::GetBppFromPixelFormat(PixelFormat);
check(BitsPerPixel % 8 == 0);
const uint32 BytesPerPixel = BitsPerPixel / 8;
const TUniquePtr<FRHIGPUTextureReadback> TextureReadback = MakeUnique<FRHIGPUTextureReadback>(TEXT("RawFromResourceTextureReadback"));
RHICmdList.FlushResources();
RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly);
RHICmdList.Transition(FRHITransitionInfo(ResourceRHI, ERHIAccess::Unknown, ERHIAccess::CopySrc));
TextureReadback->EnqueueCopy(RHICmdList, ResourceRHI);
RHICmdList.Transition(FRHITransitionInfo(ResourceRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
RHICmdList.BlockUntilGPUIdle();
//check(TextureReadback->IsReady());
{
const size_t DstDataLength = Desc.Width * Desc.Height * BytesPerPixel;
uint8* const DstData = new uint8[DstDataLength];
int32 OutBufferRowPitchInPixels, OutBufferHeight;
const uint8* SrcData = static_cast<const uint8*>(TextureReadback->Lock(OutBufferRowPitchInPixels, &OutBufferHeight));
check(SrcData);
check(static_cast<uint32>(OutBufferRowPitchInPixels) >= Desc.Width && static_cast<uint32>(OutBufferHeight) >= Desc.Height);
if (OutBufferRowPitchInPixels == Desc.Width)
{
// If pitch and width are the same, we can just copy the entire buffer.
FMemory::Memcpy(DstData, SrcData, DstDataLength);
}
else
{
// If pitch and width are NOT the same, we need to copy the valid pixels row by row.
uint8* RowDstPtr = DstData;
const size_t RowDstLength = Desc.Width * BytesPerPixel;
const uint8* RowSrcPtr = SrcData;
const size_t RowSrcLength = OutBufferRowPitchInPixels * BytesPerPixel;
for (uint32 Row = 0; Row < Desc.Height; ++Row)
{
FMemory::Memcpy(RowDstPtr, RowSrcPtr, RowDstLength);
RowDstPtr += RowDstLength;
RowSrcPtr += RowSrcLength;
}
}
TextureReadback->Unlock();
RawObj = std::make_shared<RawBuffer>(DstData, DstDataLength, Desc);
}
return RawObj;
}
catch (const std::exception&)
{
check(0);
}
uint8* DstData = new uint8[1];
return std::make_shared<RawBuffer>(DstData, 0, Desc, nullptr);
}
void TextureHelper::RawFromRT_Tiled(UTextureRenderTarget2D* RenderTarget, const BufferDescriptor& Desc, size_t TileSizeX, size_t TileSizeY, RawBufferPtrTiles& Tiles)
{
check(IsInRenderingThread());
FTextureRHIRef ResourceRHI = ((FTextureRenderTarget2DResource*)RenderTarget->GetResource())->GetTextureRHI();
return RawFromResource_Tiled(ResourceRHI, Desc, TileSizeX, TileSizeY, Tiles);
}
void TextureHelper::RawFromTexture_Tiled(UTexture2D* Texture, const BufferDescriptor& Desc, size_t TileSizeX, size_t TileSizeY, RawBufferPtrTiles& Tiles)
{
check(IsInRenderingThread());
FTextureRHIRef ResourceRHI = ((FTexture2DResource*)Texture->GetResource())->GetTexture2DRHI();
return RawFromResource_Tiled(ResourceRHI, Desc, TileSizeX, TileSizeY, Tiles);
}
void TextureHelper::RawFromResource_Tiled(FTextureRHIRef ResourceRHI, const BufferDescriptor& Desc, size_t TileSizeX, size_t TileSizeY, RawBufferPtrTiles& Tiles)
{
/// If any of the dimensions are less than the tile size requested then we don't tile at all
if (Desc.Width < TileSizeX && Desc.Height < TileSizeY)
{
auto raw = RawFromResource(ResourceRHI, Desc);
Tiles.Resize(1, 1);
Tiles[0][0] = raw;
return;
}
uint32 stride;
const uint8* SrcData = (const uint8*)RHILockTexture2D(ResourceRHI, 0, RLM_ReadOnly, stride, false);
size_t srcDataLength = Desc.Size();
RawFromMem_Tiled(SrcData, srcDataLength, Desc, TileSizeX, TileSizeY, Tiles);
RHIUnlockTexture2D(ResourceRHI, 0, false);
}
BufferFormat TextureHelper::FindOptimalSupportedFormat(BufferFormat SrcFormat)
{
if (SrcFormat == BufferFormat::Short)
return BufferFormat::Float;
return SrcFormat;
}
typedef std::function <void(uint8* Dst, size_t DstLength, const BufferDescriptor& DstDesc, const uint8* Src, size_t SrcLength, const BufferDescriptor& SrcDesc)> DataConverter;
static DataConverter GDataConverters[static_cast<int>(BufferFormat::Count)][static_cast<int>(BufferFormat::Count)] = {};
void DefaultDataConverter(uint8* Dst, size_t DstLength, const BufferDescriptor& DstDesc, const uint8* Src, size_t SrcLength, const BufferDescriptor& SrcDesc)
{
check(SrcLength == DstLength);
FMemory::Memcpy(Dst, Src, SrcLength);
}
void ShortToHalf_DataConverter(uint8* Dst, size_t DstLength, const BufferDescriptor& DstDesc, const uint8* Src, size_t SrcLength, const BufferDescriptor& SrcDesc)
{
check(SrcDesc.Format == BufferFormat::Short);
check(DstDesc.Format == BufferFormat::Half);
const size_t DstFormatSize = DstDesc.FormatSize();
const size_t SrcFormatSize = SrcDesc.FormatSize();
const size_t DstCount = DstLength / DstFormatSize;
const size_t SrcCount = SrcLength / SrcFormatSize;
check(DstCount == SrcCount);
for (size_t PixelIndex = 0; PixelIndex < DstCount; PixelIndex++)
{
FFloat16* DstPixel = reinterpret_cast<FFloat16*>(Dst + PixelIndex * DstFormatSize);
const uint16_t* SrcPixel = reinterpret_cast<const uint16_t*>(Src + PixelIndex * SrcFormatSize);
float SrcPixelF = float(*SrcPixel) / 65536.0f;
*DstPixel = SrcPixelF;
}
}
void ShortToFloat_DataConverter(uint8* Dst, size_t DstLength, const BufferDescriptor& DstDesc, const uint8* Src, size_t SrcLength, const BufferDescriptor& SrcDesc)
{
check(SrcDesc.Format == BufferFormat::Short);
check(DstDesc.Format == BufferFormat::Float);
const size_t DstFormatSize = DstDesc.FormatSize();
const size_t SrcFormatSize = SrcDesc.FormatSize();
const size_t DstCount = DstLength / DstFormatSize;
const size_t SrcCount = SrcLength / SrcFormatSize;
check(DstCount == SrcCount);
for (size_t PixelIndex = 0; PixelIndex < DstCount; PixelIndex++)
{
float* DstPixel = reinterpret_cast<float*>(Dst + PixelIndex * DstFormatSize);
const uint16_t* SrcPixel = reinterpret_cast<const uint16_t*>(Src + PixelIndex * SrcFormatSize);
*DstPixel = float(*SrcPixel) / 65536.0f;
}
}
DataConverter GetDataConverter(BufferFormat SrcFormat, BufferFormat DstFormat)
{
check(static_cast<int32>(SrcFormat) >= 0 && SrcFormat < BufferFormat::Count);
check(static_cast<int32>(DstFormat) >= 0 && DstFormat < BufferFormat::Count);
/// Initialise the array
if (!GDataConverters[0][0])
{
for (int32 fx = 0; fx < static_cast<int32>(BufferFormat::Count); fx++)
{
for (int32 fy = 0; fy < static_cast<int32>(BufferFormat::Count); fy++)
{
GDataConverters[fx][fy] = std::bind(&DefaultDataConverter, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
}
}
GDataConverters[(int)BufferFormat::Short][(int)BufferFormat::Half] = std::bind(&ShortToHalf_DataConverter, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
GDataConverters[(int)BufferFormat::Short][(int)BufferFormat::Float] = std::bind(&ShortToFloat_DataConverter, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
}
return GDataConverters[(int)SrcFormat][(int)DstFormat];
}
void TextureHelper::RawFromMem_Tiled(const uint8* SrcData, size_t SrcDataLength, const BufferDescriptor& SrcDesc, size_t TileSizeX, size_t TileSizeY, RawBufferPtrTiles& RawTiles)
{
int SourceWidth = SrcDesc.Width;
int SourceHeight = SrcDesc.Height;
int TileWidth = TileSizeX;
int TileHeight = TileSizeY;
BufferDescriptor DstTileDesc = SrcDesc;
DstTileDesc.Width = TileSizeX;
DstTileDesc.Height = TileSizeY;
DstTileDesc.Format = FindOptimalSupportedFormat(SrcDesc.Format);
size_t NumCols = SrcDesc.Width / TileSizeX;
size_t NumRows = SrcDesc.Height / TileSizeY;
T_Tiles<uint8*> Tiles(NumRows, NumCols);
size_t SrcFormatSize = SrcDesc.FormatSize();
size_t SrcTilePitch = TileWidth * SrcFormatSize * SrcDesc.ItemsPerPoint;
check(SrcDataLength % SrcDesc.Height == 0);
size_t SrcPitch = SrcDataLength / SrcDesc.Height;
size_t DstFormatSize = DstTileDesc.FormatSize();
size_t DstTilePitch = TileWidth * DstFormatSize * DstTileDesc.ItemsPerPoint;
size_t ActualTileSize = DstTilePitch * TileSizeY;
size_t TileSize = ActualTileSize; // DataUtil::GetOptimalHashingSize(ActualTileSize);
RawTiles.Resize(NumRows, NumCols);
for (size_t RowIndex = 0; RowIndex < NumRows; RowIndex++)
{
for (size_t ColIndex = 0; ColIndex < NumCols; ColIndex++)
{
uint8* DstData = new uint8[TileSize];
memset(DstData, 0, TileSize);
Tiles[RowIndex][ColIndex] = DstData;
BufferDescriptor ThisTileDesc = DstTileDesc;
ThisTileDesc.Name = TextureHelper::CreateTileName(SrcDesc.Name, ColIndex, RowIndex);
}
}
auto Converter = GetDataConverter(SrcDesc.Format, DstTileDesc.Format);
check(Converter != nullptr);
ParallelFor(SourceHeight, [&](int32 Y)
{
int RowIndex = FMath::Abs(Y / TileHeight);
for (int ColIndex = 0; ColIndex < NumCols; ColIndex++)
{
uint8* Tile = Tiles[RowIndex][ColIndex];
uint8* Dst = Tile + ((Y % TileHeight) * DstTilePitch);
const uint8* Src = SrcData + (ColIndex * SrcTilePitch) + (Y * SrcPitch);
Converter(Dst, DstTilePitch, DstTileDesc, Src, SrcTilePitch, SrcDesc);
}
});
for (size_t RowId = 0; RowId < NumRows; RowId++)
{
for (size_t ColId = 0; ColId < NumCols; ColId++)
{
BufferDescriptor ThisTileDesc = DstTileDesc;
ThisTileDesc.Name = TextureHelper::CreateTileName(SrcDesc.Name, RowId, ColId);
/// Only TileSize use it for actual hashing calculation
HashType TileHashValue = DataUtil::Hash(Tiles[RowId][ColId], TileSize);
CHashPtr TileHash = std::make_shared<CHash>(TileHashValue, true);
RawTiles[RowId][ColId] = std::make_shared<RawBuffer>(Tiles[RowId][ColId], ActualTileSize, ThisTileDesc, TileHash);
}
}
}
RawBufferPtr TextureHelper::CombineRaw_Tiles(const RawBufferPtrTiles& Tiles, CHashPtr HashValue /* = nullptr */, bool bIsTransient /* = false */)
{
uint8* Data = nullptr;
size_t DataLength = 0;
try
{
check(Tiles.Rows() && Tiles.Cols());
size_t NumRows = Tiles.Rows();
size_t NumCols = Tiles.Cols();
RawBufferPtr Tile0 = Tiles[0][0];
BufferDescriptor TileDesc = Tile0->GetDescriptor();
BufferDescriptor Desc = Tile0->GetDescriptor();
Desc.Width = NumRows * TileDesc.Width;
Desc.Height = NumCols * TileDesc.Height;
Desc.bIsTransient = bIsTransient;
size_t TotalTiles = NumRows * NumCols;
/// This is just cecking that the tiles are the same dimensions
for (size_t RowIndex = 0; RowIndex < NumRows; RowIndex++)
{
for (size_t ColIndex = 0; ColIndex < NumCols; ColIndex++)
{
RawBufferPtr Tile = Tiles[RowIndex][ColIndex];
/// We can currently only combine homogeneous tiles together
check(Tile->Width() == TileDesc.Width);
check(Tile->Height() == TileDesc.Height);
//desc.width += tile->Width();
//desc.height += tile->Height();
}
}
size_t TilePitch = TileDesc.Pitch();
size_t DstPitch = Desc.Pitch();
Data = new uint8 [DstPitch * Desc.Height];
T_Tiles<CHashPtr> TileHashes(0, 0);
if (HashValue == nullptr || HashValue->IsNull())
TileHashes.Resize(NumRows, NumCols);
ParallelFor(TotalTiles, [&](int32 TileIndex)
{
int32 RowIndex = (int32)(TileIndex / NumRows);
int32 ColIndex = TileIndex - (RowIndex * NumRows);
RawBufferPtr Tile = Tiles[RowIndex][ColIndex];
const uint8* SrcBase = Tile->GetData();
int32 DstOffset = ColIndex * DstPitch * Tile->Height() + RowIndex * TilePitch;
uint8* DstBase = Data + DstOffset;
//UE_LOG(LogData, Log, TEXT("Combining slice: [%d, %d] - [Tile Base: 0x%x] => %d "), xi, yi, srcBase, dstOffset);
for (size_t TileRow = 0; TileRow < Tile->Height(); TileRow++)
memcpy(DstBase + (TileRow * DstPitch), SrcBase + (TileRow * TilePitch), TilePitch);
if (HashValue == nullptr || HashValue->IsNull())
{
CHashPtr TileHash = Tile->Hash();
TileHashes[RowIndex][ColIndex] = TileHash;
}
});
if (HashValue == nullptr || HashValue->IsNull())
HashValue = CHash::ConstructFromSources(TileHashes.Tiles());
DataLength = Desc.Size();
return std::make_shared<RawBuffer>(Data, DataLength, Desc, HashValue);
}
catch (const std::exception& Ex)
{
delete[] Data;
Data = nullptr;
throw Ex;
}
catch (...)
{
/// Make sure we delete the data
delete[] Data;
Data = nullptr;
}
return nullptr;
}
void sRGBToLinear(uint8* Value, float BitDepth)
{
bool IsHalf = BitDepth == 16;
if (IsHalf)
{
uint16_t* HalfVal = (uint16_t*)Value;
uint16_t LinearVal = std::pow(((*HalfVal + 0.055) / 1.055), 2.4); //enhanced formula taken from http://entropymine.com/imageworsener/srgbformula/
*Value = LinearVal;
}
else
{
float* ValFloat = (float*)Value;
float LinearVal = std::pow(((*ValFloat + 0.055) /1.055), 2.4); //enhanced formula taken from http://entropymine.com/imageworsener/srgbformula/
*ValFloat = LinearVal;
}
}
FLinearColor TextureHelper::GetPixelValueFromRaw(RawBufferPtr RawObj, int32 Width, int32 Height, int32 X, int32 Y)
{
check(RawObj);
const uint8* Data = RawObj->GetData();
check(Data);
auto PixelIndex = X + (Y * Width);
return RawObj->GetAsLinearColor(PixelIndex);
}
AsyncBool TextureHelper::ExportRaw(RawBufferPtr RawObj, const FString& CompletePath)
{
return cti::make_continuable<bool>([RawObj, CompletePath](auto Promise) mutable
{
check((!TextureGraphEngine::IsDestroying()));
FText PathError;
FPaths::ValidatePath(CompletePath, &PathError);
if (CompletePath.IsEmpty())
{
UE_LOG(LogTexture, Warning, TEXT("Provide path is empty!"));
Promise.set_value(false);
}
else if (!PathError.IsEmpty())
{
UE_LOG(LogTexture, Warning, TEXT("Invalid file path provided: %s"), *(PathError.ToString()));
Promise.set_value(false);
}
bool bSuccess = false;
if (RawObj->IsPadded())
{
const EPixelFormat PixelFormat = BufferDescriptor::BufferPixelFormat(RawObj->GetDescriptor().Format, RawObj->GetDescriptor().ItemsPerPoint);
const int32 NumBlocksX = RawObj->Width() / GPixelFormats[PixelFormat].BlockSizeX;
const int32 NumBlocksY = RawObj->Height() / GPixelFormats[PixelFormat].BlockSizeY;
const uint32 DestStride = NumBlocksX * GPixelFormats[PixelFormat].BlockBytes;
const int64 DestSize = DestStride * NumBlocksY;
uint8* DstData = new uint8 [DestSize];
RawObj->CopyUnpaddedBytes(DstData);
// overwrite the RawBufferPtr to now use the newly created one without padding
RawObj = std::make_shared<RawBuffer>(DstData, DestSize, RawObj->GetDescriptor());
}
const uint8* RawDataPtr = RawObj->GetData();
if (RawDataPtr == nullptr)
Promise.set_value(false);
EPixelFormat PixelFormat = BufferDescriptor::BufferPixelFormat(RawObj->GetDescriptor().Format, RawObj->GetDescriptor().ItemsPerPoint);
EImageFormat ImageFormat = EImageFormat::EXR;
if (PixelFormat == PF_B8G8R8A8 || PixelFormat == PF_R8G8B8A8 || PixelFormat == PF_G8 || PixelFormat == PF_R8)
ImageFormat = EImageFormat::PNG;
bool bIsPng = ImageFormat == EImageFormat::PNG;
FString OutputPath = CompletePath;
if (FPaths::GetExtension(OutputPath).IsEmpty())
OutputPath += bIsPng ? ".png" : ".exr";
FArchive* FileArchive = IFileManager::Get().CreateFileWriter(*OutputPath);
if (FileArchive)
{
IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(TEXT("ImageWrapper"));
ERGBFormat RGBFormat = ERGBFormat::BGRA;
bool FlipGreenAndRed = false;
bool ConvertToRGBA = false;
bool ExtractOneComponentToGrayF = false;
int32 ComponentSentToGrey = 0;
int32 BitDepth = TextureHelper::GetBppFromPixelFormat(PixelFormat) / RawObj->GetDescriptor().ItemsPerPoint;
// in the case we recognize metadata in the descriptor, enforce the export format
if (RawObj->GetDescriptor().HasMetadata(RawBufferMetadataDefs::G_LAYER_MASK))
{
RGBFormat = ERGBFormat::GrayF;
ExtractOneComponentToGrayF = true;
ComponentSentToGrey = 1; // export Green component
}
// Else export the raw buffer matching the export file and format as close as possible to the pixel format
else
{
if (PixelFormat == PF_B8G8R8A8)
{
const uint8 ClearColor = RawObj->GetDescriptor().DefaultValue.ToFColor(false).A;
size_t Count = RawObj->GetLength() / 4;
ParallelFor(Count, [&](size_t pixel)
{
uint8* PA = const_cast<uint8*>(RawDataPtr) + (pixel * 4) + 3;
*PA = ClearColor;
});
}
else if (RawObj->GetDescriptor().ItemsPerPoint == 1)
{
FlipGreenAndRed = true;
if (PixelFormat == PF_R32_FLOAT)
RGBFormat = ERGBFormat::GrayF;
else
RGBFormat = ERGBFormat::Gray;
}
else if (RawObj->GetDescriptor().ItemsPerPoint == 2)
{
ConvertToRGBA = true;
RGBFormat = ERGBFormat::RGBAF;
}
else
{
RGBFormat = ERGBFormat::RGBAF;
}
}
// If texels are sRGB and we are supposed to save in exr format then convert to linear color space
if (RawObj->GetDescriptor().bIsSRGB && ImageFormat == EImageFormat::EXR)
{
// Adjustment for linear images
ParallelFor(RawObj->Width() * RawObj->Height(), [&](int32 pixel)
{
int32 ItemsPerPoint = RawObj->GetDescriptor().ItemsPerPoint;
uint8* Location = const_cast<uint8*>(RawDataPtr) + pixel * (BitDepth / 8) * ItemsPerPoint;
uint8* Red = Location;
sRGBToLinear(Red, BitDepth);
if (ItemsPerPoint >= 2)
{
uint8* Green = Location + (1 * BitDepth / 8);
sRGBToLinear(Green, BitDepth);
}
if (ItemsPerPoint >= 3)
{
uint8* Blue = Location + (2 * BitDepth / 8);
sRGBToLinear(Blue, BitDepth);
}
});
}
if (ExtractOneComponentToGrayF)
{
int32 ItemsPerPoint = RawObj->GetDescriptor().ItemsPerPoint;
int32 ByteDepth = BitDepth / 8;
uint64 NumPixels = (RawObj->GetLength() / (ItemsPerPoint * ByteDepth));
const float* SrcData = (float*)RawDataPtr;
int32 DestPixelByteSize = ByteDepth;
float* Greys = new float[NumPixels]; //for 4 components
float AlphaValue = 1.0;
ParallelFor(NumPixels, [&](size_t PixelNum)
{
const float* SrcLocation = SrcData + PixelNum * (ItemsPerPoint);
float srcV = SrcLocation[ComponentSentToGrey];
Greys[PixelNum] = srcV;
});
RawDataPtr = (uint8*) Greys;
RawObj = std::make_shared<RawBuffer>(RawDataPtr, NumPixels * ByteDepth, BufferDescriptor{ RawObj->Width(), RawObj->Height(), 1 }, nullptr, false);
}
if (ConvertToRGBA)
{
int32 ItemsPerPoint = RawObj->GetDescriptor().ItemsPerPoint;
static constexpr int32 FullColorPoint = 4;
uint64 DataSize = (RawObj->GetLength() / ItemsPerPoint) * FullColorPoint; //RGBA
uint8* NewRaw = new uint8[DataSize];
static constexpr float Zero = 0;
static constexpr float One = 1;
ParallelFor(RawObj->Width() * RawObj->Height(), [&](int32 Pixel)
{
uint8* SrcLocation = const_cast<uint8*>(RawDataPtr) + Pixel * (BitDepth / 8) * ItemsPerPoint;
uint8* SrcRed = SrcLocation;
uint8* DstRed = NewRaw + Pixel * (BitDepth / 8) * FullColorPoint;
FMemory::Memcpy(DstRed, SrcRed, BitDepth / 8);
uint8* SrcGreen = nullptr;
uint8* SrcBlue = nullptr;
uint8* SrcAlpha = nullptr;
uint8* DstGreen = DstRed + (1 * BitDepth / 8);
uint8* DstBlue = DstRed + (2 * BitDepth / 8);
uint8* DstAlpha = DstRed + (3 * BitDepth / 8);
SrcGreen = SrcLocation + (1 * BitDepth / 8);
FMemory::Memcpy(DstGreen, SrcGreen, BitDepth / 8);
FMemory::Memcpy(DstBlue, &Zero, BitDepth / 8);
FMemory::Memcpy(DstAlpha, &One, BitDepth / 8);
});
RawObj = std::make_shared<RawBuffer>(NewRaw, DataSize, BufferDescriptor{RawObj->Width(), RawObj->Height(), 4}, nullptr, false );
RawDataPtr = NewRaw;
}
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat);
ImageWrapper->SetRaw(RawDataPtr, RawObj->GetLength(), RawObj->Width(), RawObj->Height(), RGBFormat, BitDepth);
const TArray64<uint8>& CompressedData = ImageWrapper->GetCompressed();
if (FlipGreenAndRed)
{
*((char*)CompressedData.GetData() + 28) = 'R';
}
FileArchive->Serialize((void*)CompressedData.GetData(), CompressedData.GetAllocatedSize());
bSuccess = true;
delete FileArchive;
FileArchive = nullptr;
}
check((!TextureGraphEngine::IsDestroying()));
Promise.set_value(bSuccess);
});
}
bool TextureHelper::CanSupportTexture(UTexture* Tex)
{
bool CanSupport = false;
if (Tex != nullptr)
{
UTexture2D* Texture2D = Cast<UTexture2D>(Tex);
UTextureLightProfile* LightProfileTexture = Cast<UTextureLightProfile>(Tex);
UCurveLinearColorAtlas* CurveAtlas = Cast<UCurveLinearColorAtlas>(Tex);
if (Texture2D != nullptr && !LightProfileTexture && !CurveAtlas/*&& !Texture2D->VirtualTextureStreaming*/)
{
CanSupport = true;
}
}
return CanSupport;
}
bool TextureHelper::CanSplitToTiles(int Width, int Height, int TilesX, int TilesY)
{
bool bSplitToTiles = true;
bool bSingleTile = (TilesX == 1 && TilesY == 1);
//Height and width both must be big enough to support tiling
bool bSizeNotBigEnough = (Width <= TilesX || Height <= TilesY);
if (bSingleTile || bSizeNotBigEnough)
{
bSplitToTiles = false;
}
return bSplitToTiles;
}
bool TextureHelper::GetPixelFormatFromTextureSourceFormat(ETextureSourceFormat SourceFormat, EPixelFormat &OutPixelFormat, uint32 &OutNumChannels)
{
switch (SourceFormat)
{
// Currently supported formats :
case ETextureSourceFormat::TSF_RGBA16F:
OutPixelFormat = PF_FloatRGBA;
OutNumChannels = 4;
break;
case ETextureSourceFormat::TSF_RGBA32F:
case ETextureSourceFormat::TSF_RGBA16:
case ETextureSourceFormat::TSF_BGRA8:
case ETextureSourceFormat::TSF_BGRE8:
case ETextureSourceFormat::TSF_R32F:
case ETextureSourceFormat::TSF_R16F:
case ETextureSourceFormat::TSF_G16:
case ETextureSourceFormat::TSF_G8:
// Commenting as 'TSF_RGBA8': Legacy ETextureSourceFormat not supported,
// use BGRA8 Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
//case ETextureSourceFormat::TSF_RGBA8:
//case ETextureSourceFormat::TSF_RGBE8:
OutPixelFormat = GTextureSourceFormats[SourceFormat].PixelFormat;
OutNumChannels = GTextureSourceFormats[SourceFormat].NumComponents;
break;
case ETextureSourceFormat::TSF_Invalid:
default:
return false;
}
return true;
}
ETextureSourceFormat TextureHelper::GetTextureSourceFormat(BufferFormat Format, uint32 ItemsPerPoint)
{
ETextureSourceFormat SourceFormat = ETextureSourceFormat::TSF_Invalid;
if (ItemsPerPoint == 1)
{
switch (Format)
{
case BufferFormat::Byte:
return ETextureSourceFormat::TSF_G8;
case BufferFormat::Float:
return ETextureSourceFormat::TSF_R32F;
case BufferFormat::Half:
return ETextureSourceFormat::TSF_R16F;
}
}
else if (ItemsPerPoint == 4)
{
switch (Format)
{
case BufferFormat::Byte:
return ETextureSourceFormat::TSF_BGRA8;
case BufferFormat::Float:
return ETextureSourceFormat::TSF_RGBA32F;
case BufferFormat::Half:
return ETextureSourceFormat::TSF_RGBA16F;
case BufferFormat::Short:
return ETextureSourceFormat::TSF_RGBA16;
}
}
return SourceFormat;
}