5fc4551ac0
* 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>
1065 lines
46 KiB
C++
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);
|
|
|
|
|