9fac26e6a6
Update package name and hash Add compressor names. Update ImageProcessingAtom unit tests. Removed some unused test assets under ImageProcessingAtom Enalbe ISPC to all the platforms. Better error message with compression/decompression Add temp folder to git ignore added ispccompressor for all platform. valid it with linux Update windows package hashes. Removed AZ_TRAIT_IMAGEPROCESSING_USE_ISPC_TEXTURE_COMPRESSOR Minor refactor with image processing unit tests. Signed-off-by: qingtao <qingtao@amazon.com>
1077 lines
48 KiB
C++
1077 lines
48 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/Asset/AssetManager.h>
|
|
#include <AzCore/Asset/AssetManagerComponent.h>
|
|
#include <AzCore/Memory/Memory.h>
|
|
#include <AzCore/Memory/PoolAllocator.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* GetAppRoot() const override { return nullptr; }
|
|
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;
|
|
|
|
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);
|
|
|
|
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>();
|
|
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());
|
|
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.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);
|
|
|
|
m_jsonRegistrationContext->EnableRemoveReflection();
|
|
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
|
|
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
|
|
m_jsonRegistrationContext->DisableRemoveReflection();
|
|
m_jsonRegistrationContext.reset();
|
|
m_jsonSystemComponent.reset();
|
|
|
|
m_context.reset();
|
|
BuilderSettingManager::DestroyInstance();
|
|
CPixelFormats::DestroyInstance();
|
|
|
|
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_512x512_Normal_Tga, // QImage doesn't support loading this file.
|
|
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_512x512_Normal_Tga] = m_testFileFolder + "512x512_RGB_N.tga";
|
|
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;
|
|
}
|
|
|
|
|
|
bool CompareDDSImage(const QString& imagePath1, const QString& imagePath2, QString& output)
|
|
{
|
|
IImageObjectPtr image1, alphaImage1, image2, alphaImage2;
|
|
|
|
|
|
image1 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(imagePath1.toUtf8().constData()));
|
|
if (image1 && image1->HasImageFlags(EIF_AttachedAlpha))
|
|
{
|
|
if (image1->HasImageFlags(EIF_Splitted))
|
|
{
|
|
alphaImage1 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(QString(imagePath1 + ".a").toUtf8().constData()));
|
|
}
|
|
else
|
|
{
|
|
alphaImage1 = IImageObjectPtr(DdsLoader::LoadAttachedImageFromDdsFileLegacy(imagePath1.toUtf8().constData(), image1));
|
|
}
|
|
}
|
|
|
|
image2 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(imagePath2.toUtf8().constData()));
|
|
if (image2 && image2->HasImageFlags(EIF_AttachedAlpha))
|
|
{
|
|
if (image2->HasImageFlags(EIF_Splitted))
|
|
{
|
|
alphaImage2 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(QString(imagePath2 + ".a").toUtf8().constData()));
|
|
}
|
|
else
|
|
{
|
|
alphaImage2 = IImageObjectPtr(DdsLoader::LoadAttachedImageFromDdsFileLegacy(imagePath2.toUtf8().constData(), image2));
|
|
}
|
|
}
|
|
|
|
if (!image1 && !image2)
|
|
{
|
|
output += "Cannot load both image file! ";
|
|
return false;
|
|
}
|
|
bool isDifferent = false;
|
|
|
|
isDifferent = GetComparisonResult(image1, image2, output);
|
|
|
|
|
|
QFileInfo fi(imagePath1);
|
|
AZStd::string imageName = fi.baseName().toUtf8().constData();
|
|
SaveImageToFile(image1, imageName + "_new");
|
|
SaveImageToFile(image2, imageName + "_old");
|
|
|
|
if (alphaImage1 || alphaImage2)
|
|
{
|
|
isDifferent |= GetComparisonResult(alphaImage1, alphaImage2, output);
|
|
}
|
|
|
|
return isDifferent;
|
|
}
|
|
};
|
|
|
|
// test CPixelFormats related functions
|
|
TEST_F(ImageProcessingTest, TestPixelFormats)
|
|
{
|
|
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
|
|
|
|
//verify names which was used for legacy rc.ini
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC7t") == ePixelFormat_BC7t);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("ETC2A") == ePixelFormat_ETC2a);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("PVRTC4") == ePixelFormat_PVRTC4);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC1") == ePixelFormat_BC1);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("ETC2") == ePixelFormat_ETC2);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC1a") == ePixelFormat_BC1a);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC3") == ePixelFormat_BC3);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC7") == ePixelFormat_BC7);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC5s") == ePixelFormat_BC5s);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("EAC_RG11") == ePixelFormat_EAC_RG11);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC4") == ePixelFormat_BC4);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("EAC_R11") == ePixelFormat_EAC_R11);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("A8R8G8B8") == ePixelFormat_R8G8B8A8);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC6UH") == ePixelFormat_BC6UH);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("R9G9B9E5") == ePixelFormat_R9G9B9E5);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("X8R8G8B8") == ePixelFormat_R8G8B8X8);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("A16B16G16R16F") == ePixelFormat_R16G16B16A16F);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G8R8") == ePixelFormat_R8G8);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G16R16") == ePixelFormat_R16G16);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G16R16F") == ePixelFormat_R16G16F);
|
|
|
|
//some legacy format need to be mapping to new format.
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("DXT1") == ePixelFormat_BC1);
|
|
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("DXT5") == ePixelFormat_BC3);
|
|
|
|
//calculate mipmap count. no cubemap support at this moment
|
|
|
|
//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_PVRTC4, 2, 1, false) == false);
|
|
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 4, 4, false) == false);
|
|
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 16, 16, false) == true);
|
|
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 16, 32, false) == false);
|
|
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 34, 34, false) == false);
|
|
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 256, 256, false) == true);
|
|
|
|
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)
|
|
{
|
|
// exclude astc formats until we add astc compressor to all platforms
|
|
// exclude pvrtc formats (deprecating)
|
|
if (!IsASTCFormat(pixelFormat)
|
|
&& pixelFormat != ePixelFormat_PVRTC2 && pixelFormat != ePixelFormat_PVRTC4
|
|
&& !IsETCFormat(pixelFormat)) // skip ETC since it's very slow
|
|
{
|
|
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;
|
|
}
|
|
|
|
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
|
|
imageToProcess.Set(srcImage);
|
|
imageToProcess.ConvertFormat(pixelFormat);
|
|
|
|
if (!imageToProcess.Get())
|
|
{
|
|
AZ_Warning("test", false, "unsupported format: %s", formatInfo->szName);
|
|
continue;
|
|
}
|
|
ASSERT_TRUE(imageToProcess.Get());
|
|
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
|
|
|
|
// Get compressor name
|
|
ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear;
|
|
ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true);
|
|
|
|
//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);
|
|
|
|
//convert back to an uncompressed format and expect it will be successful
|
|
imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
|
|
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == ePixelFormat_R8G8B8A8);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
SaveImageToFile(process->GetOutputAlphaImage(), "alpha", 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, DISABLED_CompareOutputImage)
|
|
{
|
|
AZStd::string curretTextureFolder = "../TestAssets/TextureAssets/assets_new/textures";
|
|
AZStd::string oldTextureFolder = "../TestAssets/TextureAssets/assets_old/textures";
|
|
bool outputOnlyDifferent = false;
|
|
QDirIterator it(curretTextureFolder.c_str(), QStringList() << "*.dds", QDir::Files, QDirIterator::Subdirectories);
|
|
QFile f("../texture_comparison_output.csv");
|
|
f.open(QIODevice::ReadWrite | QIODevice::Truncate);
|
|
// Write a header for csv file
|
|
f.write("Texture Name, Path, Mip new/old, MipDiff, Format new/old, Flag new/old, MemSize new/old, MemDiff, Error, AlphaMip new/old, AlphaMipDiff, AlphaFormat new/old, AlphaFlag new/old, AlphaMemSize new/old, AlphaMemDiff, AlphaError\r\n");
|
|
int i = 0;
|
|
while (it.hasNext())
|
|
{
|
|
i++;
|
|
it.next();
|
|
|
|
QString fileName = it.fileName();
|
|
QString newFilePath = it.filePath();
|
|
QString sharedPath = QString(newFilePath).remove(curretTextureFolder.c_str());
|
|
QString oldFilePath = QString(oldTextureFolder.c_str()) + sharedPath;
|
|
QString output;
|
|
if (QFile::exists(oldFilePath))
|
|
{
|
|
bool isDifferent = CompareDDSImage(newFilePath, oldFilePath, output);
|
|
if (outputOnlyDifferent && !isDifferent)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
f.write(fileName.toUtf8().constData());
|
|
f.write(",");
|
|
f.write(sharedPath.toUtf8().constData());
|
|
f.write(output.toUtf8().constData());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f.write(fileName.toUtf8().constData());
|
|
f.write(",");
|
|
f.write(sharedPath.toUtf8().constData());
|
|
output += ",No old file for comparison!";
|
|
f.write(output.toUtf8().constData());
|
|
}
|
|
f.write("\r\n");
|
|
}
|
|
f.close();
|
|
}
|
|
|
|
TEST_F(ImageProcessingTest, TextureSettingReflect_SerializingModernDataInAndOut_WritesAndParsesFileAccurately)
|
|
{
|
|
AZStd::string filepath = "test.xml";
|
|
|
|
// Fill-in structure with test data
|
|
TextureSettings fakeTextureSettings;
|
|
fakeTextureSettings.m_preset = AZ::Uuid::CreateRandom();
|
|
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);
|
|
|
|
|