Files
o3de/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp
T
lumberyard-employee-dm 5fc4551ac0 [LYN-8041] Enable relocation of the Project Game Release Layout (#5380)
* Enable relocation of the Project Game Release Layout

Relocating the Project Game Release Layout to another directory on the file system failed due to the querying of the engine root failing due to the ComponentApplication::m_engineRoot not using the project path stored in the SettingsRegisry if the engine root cannot be detected

Removed the ApplicationRequestBus GetEngineRoot function.
The ComponentApplicationRequestBus has a function of the same name that returns the same path.

Removed the deprecated GetAppRoot function.
The path it returns has no defined value. It was not the engine root or the project root.
Removed unused CFileUtil and CFileUtil_impl functions that were invoking the ApplicationREquestBus GetEngineRoot function.
On the way to update the functions it was discovered that they aren't called

Added a CalculateBranchToken overload that can populate a fixed_string to avoid heap allocations

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Protect against an empty list of artifacts to remove when generating the
engine.pak

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2021-11-09 12:03:52 -06:00

1065 lines
46 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzTest/AzTest.h>
#include <AzTest/Utils.h>
#include <AzQtComponents/Utilities/QtPluginPaths.h>
#include <AzCore/AzCore_Traits_Platform.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Jobs/JobContext.h>
#include <AzCore/Jobs/JobManager.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/RTTI/ReflectionManager.h>
#include <AzCore/Serialization/DataPatch.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <Atom/ImageProcessing/ImageObject.h>
#include <Processing/PixelFormatInfo.h>
#include <Processing/ImageConvert.h>
#include <Processing/ImageToProcess.h>
#include <Processing/ImageAssetProducer.h>
#include <Processing/ImageFlags.h>
#include <ImageLoader/ImageLoaders.h>
#include <Compressors/Compressor.h>
#include <Converters/Cubemap.h>
#include <BuilderSettings/BuilderSettingManager.h>
#include <BuilderSettings/CubemapSettings.h>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/PresetSettings.h>
#include <Editor/EditorCommon.h>
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
#include <Atom/RHI.Reflect/ReflectSystemComponent.h>
#include <QFileInfo>
#include <qdir.h>
#include <QDirIterator>
#include <QIODevice>
#include <array>
#include <utility>
//Enable generate image files for result of some tests.
//This is slow and only useful for debugging. This should be disabled for unit test
//#define DEBUG_OUTPUT_IMAGES
//There are some test functions in this test which are DISABLED. They were mainly for programming tests.
//It's only recommended to enable them for programming test purpose.
#include <AzCore/UnitTest/TestTypes.h>
#include <ImageBuilderComponent.h>
using namespace ImageProcessingAtom;
namespace UnitTest
{
// Expose AZ::AssetManagerComponent::Reflect function for testing
class MyAssetManagerComponent
: public AZ::AssetManagerComponent
{
public:
static void Reflect(ReflectContext* reflection)
{
AZ::AssetManagerComponent::Reflect(reflection);
};
};
class ImageProcessingTest
: public ::testing::Test
, public AllocatorsBase
, public AZ::ComponentApplicationBus::Handler
{
public:
//////////////////////////////////////////////////////////////////////////
// ComponentApplicationMessages.
AZ::ComponentApplication* GetApplication() override { return nullptr; }
void RegisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
void UnregisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
void RegisterEntityAddedEventHandler(AZ::EntityAddedEvent::Handler&) override { }
void RegisterEntityRemovedEventHandler(AZ::EntityRemovedEvent::Handler&) override { }
void RegisterEntityActivatedEventHandler(AZ::EntityActivatedEvent::Handler&) override { }
void RegisterEntityDeactivatedEventHandler(AZ::EntityDeactivatedEvent::Handler&) override { }
void SignalEntityActivated(AZ::Entity*) override { }
void SignalEntityDeactivated(AZ::Entity*) override { }
bool AddEntity(AZ::Entity*) override { return false; }
bool RemoveEntity(AZ::Entity*) override { return false; }
bool DeleteEntity(const AZ::EntityId&) override { return false; }
Entity* FindEntity(const AZ::EntityId&) override { return nullptr; }
AZ::SerializeContext* GetSerializeContext() override { return m_context.get(); }
AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; }
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return m_jsonRegistrationContext.get(); }
const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const AZ::ComponentApplicationRequests::EntityCallback& /*callback*/) override {}
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
//////////////////////////////////////////////////////////////////////////
protected:
AZStd::unique_ptr<AZ::SerializeContext> m_context;
AZStd::unique_ptr<AZ::JsonRegistrationContext> m_jsonRegistrationContext;
AZStd::unique_ptr<AZ::JsonSystemComponent> m_jsonSystemComponent;
AZStd::vector<AZStd::unique_ptr<AZ::Data::AssetHandler>> m_assetHandlers;
AZStd::string m_gemFolder;
AZStd::string m_outputRootFolder;
AZStd::string m_outputFolder;
AZStd::unique_ptr<AZ::JobManager> m_jobManager;
AZStd::unique_ptr<AZ::JobContext> m_jobContext;
void SetUp() override
{
AllocatorsBase::SetupAllocator();
// Adding this handler to allow utility functions access the serialize context
ComponentApplicationBus::Handler::BusConnect();
AZ::Interface<AZ::ComponentApplicationRequests>::Register(this);
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
// AssetManager required to generate image assets
AZ::Data::AssetManager::Descriptor desc;
AZ::Data::AssetManager::Create(desc);
AZ::NameDictionary::Create();
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::ImageMipChainAssetHandler>());
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::StreamingImageAssetHandler>());
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::StreamingImagePoolAssetHandler>());
BuilderSettingManager::CreateInstance();
//prepare reflection
m_context = AZStd::make_unique<AZ::SerializeContext>();
AZ::Name::Reflect(m_context.get());
BuilderPluginComponent::Reflect(m_context.get());
AZ::DataPatch::Reflect(m_context.get());
AZ::RHI::ReflectSystemComponent::Reflect(m_context.get());
AZ::RPI::ImageMipChainAsset::Reflect(m_context.get());
AZ::RPI::ImageAsset::Reflect(m_context.get());
AZ::RPI::StreamingImageAsset::Reflect(m_context.get());
MyAssetManagerComponent::Reflect(m_context.get());
m_jsonRegistrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
m_jsonSystemComponent = AZStd::make_unique<AZ::JsonSystemComponent>();
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
AZ::Name::Reflect(m_jsonRegistrationContext.get());
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
// Setup job context for job system
JobManagerDesc jobManagerDesc;
JobManagerThreadDesc threadDesc;
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
threadDesc.m_cpuId = 0; // Don't set processors IDs on windows
#endif
uint32_t numWorkerThreads = AZStd::thread::hardware_concurrency();
for (unsigned int i = 0; i < numWorkerThreads; ++i)
{
jobManagerDesc.m_workerThreads.push_back(threadDesc);
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
threadDesc.m_cpuId++;
#endif
}
m_jobManager = AZStd::make_unique<JobManager>(jobManagerDesc);
m_jobContext = AZStd::make_unique<JobContext>(*m_jobManager);
JobContext::SetGlobalContext(m_jobContext.get());
// Startup default local FileIO (hits OSAllocator) if not already setup.
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
{
AZ::IO::FileIOBase::SetInstance(aznew AZ::IO::LocalFileIO());
}
//load qt plug-ins for some image file formats support
AzQtComponents::PrepareQtPaths();
m_gemFolder = AZ::Test::GetEngineRootPath() + "/Gems/Atom/Asset/ImageProcessingAtom/";
m_outputFolder = m_gemFolder + AZStd::string("Code/Tests/TestAssets/temp/");
m_defaultSettingFolder = m_gemFolder + AZStd::string("Config/");
m_testFileFolder = m_gemFolder + AZStd::string("Code/Tests/TestAssets/");
InitialImageFilenames();
ImageProcessingAtomEditor::EditorHelper::InitPixelFormatString();
}
void TearDown() override
{
m_gemFolder = AZStd::string();
m_outputFolder = AZStd::string();
m_defaultSettingFolder = AZStd::string();
m_testFileFolder = AZStd::string();
m_imagFileNameMap = AZStd::map<ImageFeature, AZStd::string>();
m_assetHandlers = AZStd::vector<AZStd::unique_ptr<AZ::Data::AssetHandler>>();
delete AZ::IO::FileIOBase::GetInstance();
AZ::IO::FileIOBase::SetInstance(nullptr);
JobContext::SetGlobalContext(nullptr);
m_jobContext = nullptr;
m_jobManager = nullptr;
m_jsonRegistrationContext->EnableRemoveReflection();
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
AZ::Name::Reflect(m_jsonRegistrationContext.get());
m_jsonRegistrationContext->DisableRemoveReflection();
m_jsonRegistrationContext.reset();
m_jsonSystemComponent.reset();
m_context.reset();
BuilderSettingManager::DestroyInstance();
CPixelFormats::DestroyInstance();
AZ::NameDictionary::Destroy();
AZ::Data::AssetManager::Destroy();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(this);
ComponentApplicationBus::Handler::BusDisconnect();
AllocatorsBase::TeardownAllocator();
}
//enum names for Images with specific identification
enum ImageFeature
{
Image_20X16_RGBA8_Png = 0,
Image_32X32_16bit_F_Tif,
Image_32X32_32bit_F_Tif,
Image_200X200_RGB8_Jpg,
Image_512X288_RGB8_Tga,
Image_1024X1024_RGB8_Tif,
Image_UpperCase_Tga,
Image_1024x1024_normal_tiff,
Image_128x128_Transparent_Tga,
Image_237x177_RGB_Jpg,
Image_GreyScale_Png,
Image_Alpha8_64x64_Mip7_Dds,
Image_BGRA_64x64_Mip7_Dds,
Image_Luminance8bpp_66x33_dds,
Image_BGR_64x64_dds,
Image_defaultprobe_cm_1536x256_64bits_tif,
Image_workshop_iblskyboxcm_exr
};
//image file names for testing
AZStd::map<ImageFeature, AZStd::string> m_imagFileNameMap;
AZStd::string m_defaultSettingFolder;
AZStd::string m_testFileFolder;
//initialize image file names for testing
void InitialImageFilenames()
{
m_imagFileNameMap[Image_20X16_RGBA8_Png] = m_testFileFolder + "20x16_32bit.png";
m_imagFileNameMap[Image_32X32_16bit_F_Tif] = m_testFileFolder + "32x32_16bit_f.tif";
m_imagFileNameMap[Image_32X32_32bit_F_Tif] = m_testFileFolder + "32x32_32bit_f.tif";
m_imagFileNameMap[Image_200X200_RGB8_Jpg] = m_testFileFolder + "200x200_24bit.jpg";
m_imagFileNameMap[Image_512X288_RGB8_Tga] = m_testFileFolder + "512x288_24bit.tga";
m_imagFileNameMap[Image_1024X1024_RGB8_Tif] = m_testFileFolder + "1024x1024_24bit.tif";
m_imagFileNameMap[Image_UpperCase_Tga] = m_testFileFolder + "uppercase.TGA";
m_imagFileNameMap[Image_1024x1024_normal_tiff] = m_testFileFolder + "1024x1024_normal.tiff";
m_imagFileNameMap[Image_128x128_Transparent_Tga] = m_testFileFolder + "128x128_RGBA8.tga";
m_imagFileNameMap[Image_237x177_RGB_Jpg] = m_testFileFolder + "237x177_RGB.jpg";
m_imagFileNameMap[Image_GreyScale_Png] = m_testFileFolder + "greyscale.png";
m_imagFileNameMap[Image_Alpha8_64x64_Mip7_Dds] = m_testFileFolder + "Alpha8_64x64_Mip7.dds";
m_imagFileNameMap[Image_BGRA_64x64_Mip7_Dds] = m_testFileFolder + "BGRA_64x64_MIP7.dds";
m_imagFileNameMap[Image_Luminance8bpp_66x33_dds] = m_testFileFolder + "Luminance8bpp_66x33.dds";
m_imagFileNameMap[Image_BGR_64x64_dds] = m_testFileFolder + "RGBA_64x64.dds";
m_imagFileNameMap[Image_defaultprobe_cm_1536x256_64bits_tif] = m_testFileFolder + "defaultProbe_cm.tif";
m_imagFileNameMap[Image_workshop_iblskyboxcm_exr] = m_testFileFolder + "workshop_iblskyboxcm.exr";
}
public:
void SetOutputSubFolder(const char* subFolderName)
{
if (subFolderName)
{
m_outputFolder = m_outputRootFolder + "/" + subFolderName;
}
else
{
m_outputFolder = m_outputRootFolder;
}
}
//helper function to save an image object to a file through QtImage
void SaveImageToFile([[maybe_unused]] const IImageObjectPtr imageObject, [[maybe_unused]] const AZStd::string imageName, [[maybe_unused]] AZ::u32 maxMipCnt = 100)
{
#ifndef DEBUG_OUTPUT_IMAGES
return;
#else
if (imageObject == nullptr)
{
return;
}
// create dir if it doesn't exist
QDir dir;
QDir outputDir(m_outputFolder.c_str());
if (!outputDir.exists())
{
dir.mkpath(m_outputFolder.c_str());
}
//save origin file pixel format so we could use it to generate name later
EPixelFormat originPixelFormat = imageObject->GetPixelFormat();
//convert to RGBA8 before can be exported.
ImageToProcess imageToProcess(imageObject);
imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
IImageObjectPtr finalImage = imageToProcess.Get();
//for each mipmap
for (uint32 mip = 0; mip < finalImage->GetMipCount() && mip < maxMipCnt; mip++)
{
uint8* imageBuf;
uint32 pitch;
finalImage->GetImagePointer(mip, imageBuf, pitch);
uint32 width = finalImage->GetWidth(mip);
uint32 height = finalImage->GetHeight(mip);
uint32 originalSize = imageObject->GetMipBufSize(mip);
//generate file name
char filePath[2048];
azsprintf(filePath, "%s%s_%s_mip%d_%dx%d_%d.png", m_outputFolder.data(), imageName.c_str()
, CPixelFormats::GetInstance().GetPixelFormatInfo(originPixelFormat)->szName
, mip, width, height, originalSize);
QImage qimage(imageBuf, width, height, pitch, QImage::Format_RGBA8888);
qimage.save(filePath);
}
#endif
}
static bool GetComparisonResult(IImageObjectPtr image1, IImageObjectPtr image2, QString& output)
{
bool isImageLoaded = true;
bool isDifferent = false;
if (image1 == nullptr)
{
isImageLoaded = false;
output += ",Image 1 does not exist. ";
}
if (image2 == nullptr)
{
isImageLoaded = false;
output += ",Image 2 does not exist. ";
}
if (!isImageLoaded)
{
return (!image1 && !image2) ? false : true;
}
// Mip
int mip1 = image1->GetMipCount();
int mip2 = image2->GetMipCount();
int mipDiff = abs(mip1 - mip2);
isDifferent |= mipDiff != 0;
// Format
EPixelFormat format1 = image1->GetPixelFormat();
EPixelFormat format2 = image2->GetPixelFormat();
isDifferent |= (format1 != format2);
// Flag
AZ::u32 flag1 = image1->GetImageFlags();
AZ::u32 flag2 = image2->GetImageFlags();
isDifferent |= (flag1 != flag2);
// Size
int memSize1 = image1->GetTextureMemory();
int memSize2 = image2->GetTextureMemory();
int memDiff = abs(memSize1 - memSize2);
isDifferent |= memDiff != 0;
// Error
float error = GetErrorBetweenImages(image1, image2);
static float EPSILON = 0.000001f;
isDifferent |= abs(error) >= EPSILON;
output += QString(",%1/%2,%3,%4/%5,%6/%7,").arg(QString::number(mip1, 'f', 1), QString::number(mip2, 'f', 1), QString::number(mipDiff),
QString(ImageProcessingAtomEditor::EditorHelper::s_PixelFormatString[format1]),
QString(ImageProcessingAtomEditor::EditorHelper::s_PixelFormatString[format2]),
QString::number(flag1, 16), QString::number(flag2, 16));
output += QString("%1/%2,%3,%4").arg(QString(ImageProcessingAtomEditor::EditorHelper::GetFileSizeString(memSize1).c_str()),
QString(ImageProcessingAtomEditor::EditorHelper::GetFileSizeString(memSize2).c_str()),
QString(ImageProcessingAtomEditor::EditorHelper::GetFileSizeString(memDiff).c_str()),
QString::number(error, 'f', 8));
return isDifferent;
}
};
// test CPixelFormats related functions
TEST_F(ImageProcessingTest, TestPixelFormats)
{
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
//for all the non-compressed textures, if there minimum required texture size is 1x1
for (uint32 i = 0; i < ePixelFormat_Count; i++)
{
EPixelFormat pixelFormat = (EPixelFormat)i;
if (pixelFormats.IsPixelFormatUncompressed(pixelFormat))
{
//square, power of 2 sizes for uncompressed format which minimum required size is 1x1
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 128, 128) == 8);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 64, 64) == 7);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 4, 4) == 3);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 2, 2) == 2);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 1, 1) == 1);
//non-square, power of 2 sizes for uncompressed format which minimum required size is 1x1
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 128, 64) == 8);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 128, 32) == 8);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 32, 2) == 6);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 2, 1) == 2);
//Non power of 2 sizes for uncompressed format which minimum required size is 1x1
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 128, 64) == 8);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 128, 32) == 8);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 32, 2) == 6);
ASSERT_TRUE(pixelFormats.ComputeMaxMipCount(pixelFormat, 2, 1) == 2);
}
}
//check function IsImageSizeValid && EvaluateImageDataSize function
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 2, 1, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 16, 16, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 16, 32, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 34, 34, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 256, 256, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_ASTC_4x4, 2, 1, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_ASTC_4x4, 16, 16, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_ASTC_4x4, 16, 32, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_ASTC_4x4, 34, 34, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_ASTC_4x4, 256, 256, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_A8, 2, 1, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_A8, 16, 16, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_A8, 16, 32, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_A8, 34, 34, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_A8, 256, 256, false) == true);
}
TEST_F(ImageProcessingTest, TestCubemapLayouts)
{
{
IImageObjectPtr srcImage(LoadImageFromFile(m_imagFileNameMap[Image_defaultprobe_cm_1536x256_64bits_tif]));
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertCubemapLayout(CubemapLayoutVertical);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) * 6 == imageToProcess.Get()->GetHeight(0));
SaveImageToFile(imageToProcess.Get(), "Vertical", 100);
imageToProcess.ConvertCubemapLayout(CubemapLayoutHorizontalCross);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) * 3 == imageToProcess.Get()->GetHeight(0) * 4);
SaveImageToFile(imageToProcess.Get(), "HorizontalCross", 100);
imageToProcess.ConvertCubemapLayout(CubemapLayoutVerticalCross);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) * 4 == imageToProcess.Get()->GetHeight(0) * 3);
SaveImageToFile(imageToProcess.Get(), "VerticalCross", 100);
imageToProcess.ConvertCubemapLayout(CubemapLayoutHorizontal);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == imageToProcess.Get()->GetHeight(0) * 6);
SaveImageToFile(imageToProcess.Get(), "VerticalHorizontal", 100);
}
}
// test image file loading
TEST_F(ImageProcessingTest, TestImageLoaders)
{
//file extension support for different loader
ASSERT_TRUE(IsExtensionSupported("jpg") == true);
ASSERT_TRUE(IsExtensionSupported("JPG") == true);
ASSERT_TRUE(IsExtensionSupported(".JPG") == false);
ASSERT_TRUE(IsExtensionSupported("tga") == true);
ASSERT_TRUE(IsExtensionSupported("TGA") == true);
ASSERT_TRUE(IsExtensionSupported("tif") == true);
ASSERT_TRUE(IsExtensionSupported("tiff") == true);
IImageObjectPtr img;
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_1024X1024_RGB8_Tif]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetWidth(0) == 1024);
ASSERT_TRUE(img->GetHeight(0) == 1024);
ASSERT_TRUE(img->GetMipCount() == 1);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R8G8B8X8);
//load png
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_20X16_RGBA8_Png]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetWidth(0) == 20);
ASSERT_TRUE(img->GetHeight(0) == 16);
ASSERT_TRUE(img->GetMipCount() == 1);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R8G8B8A8);
//load jpg
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_200X200_RGB8_Jpg]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetWidth(0) == 200);
ASSERT_TRUE(img->GetHeight(0) == 200);
ASSERT_TRUE(img->GetMipCount() == 1);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R8G8B8A8);
//tga
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_512X288_RGB8_Tga]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetWidth(0) == 512);
ASSERT_TRUE(img->GetHeight(0) == 288);
ASSERT_TRUE(img->GetMipCount() == 1);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R8G8B8A8);
//image with upper case extension
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_UpperCase_Tga]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R8G8B8A8);
//16bits float tif
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_32X32_16bit_F_Tif]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R16G16B16A16F);
//32bits float tif
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_32X32_32bit_F_Tif]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_R32G32B32A32F);
// DDS files
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_Alpha8_64x64_Mip7_Dds]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_A8);
ASSERT_TRUE(img->GetMipCount() == 7);
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_BGRA_64x64_Mip7_Dds]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_B8G8R8A8);
ASSERT_TRUE(img->GetMipCount() == 7);
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_Luminance8bpp_66x33_dds]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_A8);
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_BGR_64x64_dds]));
ASSERT_TRUE(img != nullptr);
ASSERT_TRUE(img->GetPixelFormat() == ePixelFormat_B8G8R8);
// Exr file
img = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[Image_workshop_iblskyboxcm_exr]));
ASSERT_TRUE(img != nullptr);
}
TEST_F(ImageProcessingTest, PresetSettingCopyAssignmentOperatorOverload_WithDynamicallyAllocatedSettings_ReturnsTwoSeparateAllocations)
{
PresetSettings presetSetting;
presetSetting.m_mipmapSetting = AZStd::unique_ptr<MipmapSettings>(new MipmapSettings());
presetSetting.m_cubemapSetting = AZStd::unique_ptr<CubemapSettings>(new CubemapSettings());
// Explicit invoke assignment operator by splitting the operation into two lines.
PresetSettings otherPresetSetting;
otherPresetSetting = presetSetting;
EXPECT_NE(otherPresetSetting.m_cubemapSetting, presetSetting.m_cubemapSetting);
EXPECT_NE(otherPresetSetting.m_mipmapSetting, presetSetting.m_mipmapSetting);
}
TEST_F(ImageProcessingTest, PresetSettingCopyConstructor_WithDynamicallyAllocatedSettings_ReturnsTwoSeparateAllocations)
{
PresetSettings presetSetting;
presetSetting.m_mipmapSetting = AZStd::unique_ptr<MipmapSettings>(new MipmapSettings());
presetSetting.m_cubemapSetting = AZStd::unique_ptr<CubemapSettings>(new CubemapSettings());
PresetSettings otherPresetSetting(presetSetting);
EXPECT_NE(otherPresetSetting.m_cubemapSetting, presetSetting.m_cubemapSetting);
EXPECT_NE(otherPresetSetting.m_mipmapSetting, presetSetting.m_mipmapSetting);
}
TEST_F(ImageProcessingTest, PresetSettingEqualityOperatorOverload_WithIdenticalSettings_ReturnsEquivalent)
{
PresetSettings presetSetting;
PresetSettings otherPresetSetting(presetSetting);
EXPECT_TRUE(otherPresetSetting == presetSetting);
}
TEST_F(ImageProcessingTest, PresetSettingEqualityOperatorOverload_WithDifferingDynamicallyAllocatedSettings_ReturnsUnequivalent)
{
PresetSettings presetSetting;
presetSetting.m_mipmapSetting = AZStd::unique_ptr<MipmapSettings>(new MipmapSettings());
presetSetting.m_mipmapSetting->m_type = MipGenType::gaussian;
PresetSettings otherPresetSetting(presetSetting);
otherPresetSetting.m_mipmapSetting = AZStd::unique_ptr<MipmapSettings>(new MipmapSettings());
otherPresetSetting.m_mipmapSetting->m_type = MipGenType::blackmanHarris;
EXPECT_FALSE(otherPresetSetting == presetSetting);
}
//this test is to test image data won't be lost between uncompressed formats (for low to high precision or same precision)
TEST_F(ImageProcessingTest, TestConvertFormatUncompressed)
{
//source image
IImageObjectPtr srcImage(LoadImageFromFile(m_imagFileNameMap[Image_200X200_RGB8_Jpg]));
ImageToProcess imageToProcess(srcImage);
//image pointers to hold precessed images for comparison
IImageObjectPtr dstImage1, dstImage2, dstImage3, dstImage4, dstImage5;
//compare four channels pixel formats
//we will convert to target format then convert back to RGBX8 so they can compare to easy other
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8A8);
dstImage1 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R16G16B16A16);
ASSERT_FALSE(srcImage->CompareImage(imageToProcess.Get())); //this is different than source image
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8A8);
dstImage2 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R16G16B16A16F);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8A8);
dstImage3 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R32G32B32A32F);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8A8);
dstImage4 = imageToProcess.Get();
ASSERT_TRUE(dstImage2->CompareImage(dstImage1));
ASSERT_TRUE(dstImage3->CompareImage(dstImage1));
ASSERT_TRUE(dstImage4->CompareImage(dstImage1));
// three channels formats
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage1 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R9G9B9E5);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage2 = imageToProcess.Get();
ASSERT_TRUE(dstImage2->CompareImage(dstImage1));
//convert image to all one channel formats then convert them back to RGBX8 for comparison
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage1 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R16);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage2 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R16F);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage3 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R32F);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage4 = imageToProcess.Get();
ASSERT_TRUE(dstImage2->CompareImage(dstImage1));
ASSERT_TRUE(dstImage3->CompareImage(dstImage1));
ASSERT_TRUE(dstImage4->CompareImage(dstImage1));
//convert image to all two channels formats then convert them back to RGBX8 for comparison
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage1 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R16G16);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage2 = imageToProcess.Get();
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R16G16F);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
dstImage3 = imageToProcess.Get();
ASSERT_TRUE(dstImage2->CompareImage(dstImage1));
ASSERT_TRUE(dstImage3->CompareImage(dstImage1));
}
TEST_F(ImageProcessingTest, TestConvertFormatCompressed)
{
IImageObjectPtr srcImage;
//images to be tested
static const int imageCount = 4;
ImageFeature images[imageCount] = {
Image_20X16_RGBA8_Png,
Image_237x177_RGB_Jpg,
Image_128x128_Transparent_Tga,
Image_defaultprobe_cm_1536x256_64bits_tif};
// collect all compressed pixel formats
AZStd::vector<EPixelFormat> compressedFormats;
for (uint32 i = 0; i < ePixelFormat_Count; i++)
{
EPixelFormat pixelFormat = (EPixelFormat)i;
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
if (formatInfo->bCompressed)
{
// skip ASTC formats which are tested in TestConvertASTCCompressor
if (!IsASTCFormat(pixelFormat))
{
compressedFormats.push_back(pixelFormat);
}
}
}
for (int imageIdx = 0; imageIdx < imageCount; imageIdx++)
{
//get image's name and it will be used for output file name
QFileInfo fi(m_imagFileNameMap[images[imageIdx]].c_str());
AZStd::string imageName = fi.baseName().toUtf8().constData();
srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[images[imageIdx]]));
ImageToProcess imageToProcess(srcImage);
//test ConvertFormat functions against all the pixel formats
for (EPixelFormat pixelFormat : compressedFormats)
{
//
if (!CPixelFormats::GetInstance().IsImageSizeValid(pixelFormat, srcImage->GetWidth(0), srcImage->GetHeight(0), false))
{
continue;
}
#if defined(AZ_ENABLE_TRACING)
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
#endif
ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear;
ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true);
if (!compressor)
{
AZ_Warning("test", false, "unsupported format: %s", formatInfo->szName);
continue;
}
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormat(pixelFormat);
ASSERT_TRUE(imageToProcess.Get());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
//convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
// Save the image to a file so we can check the visual result
AZStd::string outputName = AZStd::string::format("%s_%s", imageName.c_str(), compressor->GetName());
SaveImageToFile(imageToProcess.Get(), outputName, 1);
}
}
}
TEST_F(ImageProcessingTest, Test_ConvertAllAstc_Success)
{
// Compress/Decompress to all astc formats (LDR)
auto imageIdx = Image_237x177_RGB_Jpg;
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx]));
QFileInfo fi(m_imagFileNameMap[imageIdx].c_str());
AZStd::string imageName = fi.baseName().toUtf8().constData();
for (uint32 i = 0; i < ePixelFormat_Count; i++)
{
EPixelFormat pixelFormat = (EPixelFormat)i;
if (IsASTCFormat(pixelFormat))
{
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertFormat(pixelFormat);
ASSERT_TRUE(imageToProcess.Get());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0));
ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0));
// convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
// save the image to a file so we can check the visual result
AZStd::string outputName = AZStd::string::format("ASTC_%s", imageName.c_str());
SaveImageToFile(imageToProcess.Get(), outputName, 1);
}
}
}
TEST_F(ImageProcessingTest, Test_ConvertHdrToAstc_Success)
{
// Compress/Decompress HDR
auto imageIdx = Image_defaultprobe_cm_1536x256_64bits_tif;
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx]));
EPixelFormat dstFormat = ePixelFormat_ASTC_4x4;
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertFormat(ePixelFormat_ASTC_4x4);
ASSERT_TRUE(imageToProcess.Get());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == dstFormat);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0));
ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0));
//convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
//save the image to a file so we can check the visual result
SaveImageToFile(imageToProcess.Get(), "ASTC_HDR", 1);
}
TEST_F(ImageProcessingTest, Test_AstcNormalPreset_Success)
{
// Normal.preset which uses ASTC as output format
// This test compress a normal texture and its mipmaps
auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder);
ASSERT_TRUE(outcome.IsSuccess());
AZStd::string inputFile;
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
inputFile = m_imagFileNameMap[Image_1024x1024_normal_tiff];
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(inputFile));
ImageConvertProcess* process = CreateImageConvertProcess(inputFile, m_outputFolder, "ios", outProducts, m_context.get());
const PresetSettings* preset = &process->GetInputDesc()->m_presetSetting;
if (process != nullptr)
{
process->ProcessAll();
//get process result
ASSERT_TRUE(process->IsSucceed());
auto outputImage = process->GetOutputImage();
ASSERT_TRUE(outputImage->GetPixelFormat() == preset->m_pixelFormat);
ASSERT_TRUE(outputImage->GetWidth(0) == srcImage->GetWidth(0));
ASSERT_TRUE(outputImage->GetHeight(0) == srcImage->GetHeight(0));
SaveImageToFile(outputImage, "ASTC_Normal", 10);
delete process;
}
}
TEST_F(ImageProcessingTest, DISABLED_TestImageFilter)
{
AZStd::string testImageFile = m_imagFileNameMap[Image_1024X1024_RGB8_Tif];
IImageObjectPtr srcImage, dstImage;
QFileInfo fi(testImageFile.c_str());
AZStd::string imageName = fi.baseName().toUtf8().constData();
//load source image and convert it to RGBA32F
srcImage = IImageObjectPtr(LoadImageFromFile(testImageFile));
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertFormat(ePixelFormat_R32G32B32A32F);
srcImage = imageToProcess.Get();
//create destination image with same size and mipmaps
dstImage = IImageObjectPtr(
IImageObject::CreateImage(srcImage->GetWidth(0), srcImage->GetHeight(0), 3,
ePixelFormat_R32G32B32A32F));
//for each filters
const std::array<std::pair<MipGenType, AZStd::string>, 7> allFilters =
{
{
{MipGenType::point, "point"},
{MipGenType::box, "box" },
{ MipGenType::triangle, "triangle" },
{ MipGenType::quadratic, "Quadratic" },
{ MipGenType::blackmanHarris, "blackmanHarris" },
{ MipGenType::kaiserSinc, "kaiserSinc" }
}
};
for (std::pair<MipGenType, AZStd::string> filter : allFilters)
{
for (uint mip = 0; mip < dstImage->GetMipCount(); mip++)
{
FilterImage(filter.first, MipGenEvalType::sum,
0, 0, imageToProcess.Get(), 0, dstImage, mip, nullptr, nullptr);
}
SaveImageToFile(dstImage, imageName + "_" + filter.second);
}
}
TEST_F(ImageProcessingTest, TestColorSpaceConversion)
{
IImageObjectPtr srcImage(LoadImageFromFile(m_imagFileNameMap[Image_GreyScale_Png]));
ImageToProcess imageToProcess(srcImage);
imageToProcess.GammaToLinearRGBA32F(true);
SaveImageToFile(imageToProcess.Get(), "GammaTolinear_DeGamma", 1);
imageToProcess.LinearToGamma();
SaveImageToFile(imageToProcess.Get(), "LinearToGamma_DeGamma", 1);
}
TEST_F(ImageProcessingTest, VerifyRestrictedPlatform)
{
auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder);
ASSERT_TRUE(outcome.IsSuccess());
PlatformNameList platforms = BuilderSettingManager::Instance()->GetPlatformList();
#ifndef AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS
EXPECT_THAT(platforms, testing::UnorderedPointwise(testing::Eq(), {"pc", "linux", "mac", "ios", "android"}));
#endif //AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS
}
//test image conversion for builder
TEST_F(ImageProcessingTest, TestBuilderImageConvertor)
{
//load builder presets
auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder);
ASSERT_TRUE(outcome.IsSuccess());
AZStd::string inputFile;
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
inputFile = m_imagFileNameMap[Image_128x128_Transparent_Tga];
ImageConvertProcess* process = CreateImageConvertProcess(inputFile, m_outputFolder, "pc", outProducts, m_context.get());
if (process != nullptr)
{
//the process can be stopped if the job is canceled or the worker is shutting down
int step = 0;
while (!process->IsFinished())
{
process->UpdateProcess();
step++;
}
//get process result
ASSERT_TRUE(process->IsSucceed());
SaveImageToFile(process->GetOutputImage(), "rgb", 10);
process->GetAppendOutputProducts(outProducts);
delete process;
}
}
TEST_F(ImageProcessingTest, TestIblSkyboxPreset)
{
//load builder presets
auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder);
ASSERT_TRUE(outcome.IsSuccess());
AZStd::string inputFile;
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
inputFile = m_imagFileNameMap[Image_workshop_iblskyboxcm_exr];
ImageConvertProcess* process = CreateImageConvertProcess(inputFile, m_outputFolder, "pc", outProducts, m_context.get());
if (process != nullptr)
{
process->ProcessAll();
//get process result
ASSERT_TRUE(process->IsSucceed());
auto specularImage = process->GetOutputIBLSpecularCubemap();
auto diffuseImage = process->GetOutputIBLDiffuseCubemap();
ASSERT_TRUE(process->GetOutputImage());
ASSERT_TRUE(specularImage);
ASSERT_TRUE(diffuseImage);
// output converted result if save image is enabled
SaveImageToFile(process->GetOutputImage(), "ibl_skybox", 10);
SaveImageToFile(specularImage, "ibl_specular", 10);
SaveImageToFile(diffuseImage, "ibl_diffuse", 10);
delete process;
}
}
TEST_F(ImageProcessingTest, TextureSettingReflect_SerializingModernDataInAndOut_WritesAndParsesFileAccurately)
{
AZStd::string filepath = "test.xml";
// Fill-in structure with test data
TextureSettings fakeTextureSettings;
fakeTextureSettings.m_preset = "testPreset";
fakeTextureSettings.m_sizeReduceLevel = 0;
fakeTextureSettings.m_suppressEngineReduce = true;
fakeTextureSettings.m_enableMipmap = false;
fakeTextureSettings.m_maintainAlphaCoverage = true;
fakeTextureSettings.m_mipAlphaAdjust = { 0xDEAD, 0xBADBEEF, 0xBADC0DE, 0xFEEFEE, 0xBADF00D, 0xC0FFEE };
fakeTextureSettings.m_mipGenEval = MipGenEvalType::max;
fakeTextureSettings.m_mipGenType = MipGenType::quadratic;
// Write test data to file
auto writeOutcome = TextureSettings::WriteTextureSetting(filepath, fakeTextureSettings, m_context.get());
EXPECT_TRUE(writeOutcome.IsSuccess());
// Parse test data to file
TextureSettings parsedFakeTextureSettings;
auto readOutcome = TextureSettings::LoadTextureSetting(filepath, parsedFakeTextureSettings, m_context.get());
EXPECT_TRUE(readOutcome.IsSuccess());
EXPECT_TRUE(parsedFakeTextureSettings.Equals(fakeTextureSettings, m_context.get()));
// Delete temp data
AZ::IO::FileIOBase::GetInstance()->Remove(filepath.c_str());
}
} // UnitTest
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);