Merge branch 'main' into Atom/jromnoa/SPEC-6728-fix-material-editor-launcher

main
jromnoa 5 years ago
commit a1b4844f6a

@ -18,7 +18,7 @@ import pytest
import editor_python_test_tools.hydra_test_utils as hydra import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
EDITOR_TIMEOUT = 120 EDITOR_TIMEOUT = 300
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts")

@ -95,32 +95,32 @@ namespace UnitTest
class ImageProcessingTest class ImageProcessingTest
: public ::testing::Test : public ::testing::Test
, public AllocatorsBase , public AllocatorsBase
, public ComponentApplicationBus::Handler , public AZ::ComponentApplicationBus::Handler
{ {
public: public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// ComponentApplicationMessages. // ComponentApplicationMessages.
ComponentApplication* GetApplication() override { return nullptr; } AZ::ComponentApplication* GetApplication() override { return nullptr; }
void RegisterComponentDescriptor(const ComponentDescriptor*) override { } void RegisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
void UnregisterComponentDescriptor(const ComponentDescriptor*) override { } void UnregisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler&) override { } void RegisterEntityAddedEventHandler(AZ::EntityAddedEvent::Handler&) override { }
void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler&) override { } void RegisterEntityRemovedEventHandler(AZ::EntityRemovedEvent::Handler&) override { }
void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler&) override { } void RegisterEntityActivatedEventHandler(AZ::EntityActivatedEvent::Handler&) override { }
void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler&) override { } void RegisterEntityDeactivatedEventHandler(AZ::EntityDeactivatedEvent::Handler&) override { }
void SignalEntityActivated(Entity*) override { } void SignalEntityActivated(AZ::Entity*) override { }
void SignalEntityDeactivated(Entity*) override { } void SignalEntityDeactivated(AZ::Entity*) override { }
bool AddEntity(Entity*) override { return false; } bool AddEntity(AZ::Entity*) override { return false; }
bool RemoveEntity(Entity*) override { return false; } bool RemoveEntity(AZ::Entity*) override { return false; }
bool DeleteEntity(const EntityId&) override { return false; } bool DeleteEntity(const AZ::EntityId&) override { return false; }
Entity* FindEntity(const EntityId&) override { return nullptr; } Entity* FindEntity(const AZ::EntityId&) override { return nullptr; }
SerializeContext* GetSerializeContext() override { return m_context.get(); } AZ::SerializeContext* GetSerializeContext() override { return m_context.get(); }
BehaviorContext* GetBehaviorContext() override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; }
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return m_jsonRegistrationContext.get(); } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return m_jsonRegistrationContext.get(); }
const char* GetAppRoot() const override { return nullptr; } const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
Debug::DrillerManager* GetDrillerManager() override { return nullptr; } AZ::Debug::DrillerManager* GetDrillerManager() override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const AZ::ComponentApplicationRequests::EntityCallback& /*callback*/) override {}
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {} void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

@ -120,13 +120,13 @@ namespace AZ
{ {
// stencil Srg // stencil Srg
// Note: the stencil pass uses a slightly reduced inner AABB to avoid seams // Note: the stencil pass uses a slightly reduced inner AABB to avoid seams
Vector3 innerExtentsReduced = m_innerExtents * m_transform.GetScale() - Vector3(0.1f, 0.1f, 0.1f); Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f);
Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced);
m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil); m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil);
m_stencilSrg->Compile(); m_stencilSrg->Compile();
// blend weight Srg // blend weight Srg
Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents * m_transform.GetScale()); Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents);
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter);
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
@ -149,7 +149,7 @@ namespace AZ
m_renderOuterSrg->Compile(); m_renderOuterSrg->Compile();
// render inner Srg // render inner Srg
Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents * m_transform.GetScale()); Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents);
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner);
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
@ -208,6 +208,12 @@ namespace AZ
void ReflectionProbe::SetTransform(const AZ::Transform& transform) void ReflectionProbe::SetTransform(const AZ::Transform& transform)
{ {
// retrieve previous scale and revert the scale on the inner/outer extents
AZ::Vector3 previousScale = m_transform.GetScale();
m_outerExtents /= previousScale;
m_innerExtents /= previousScale;
// store new transform
m_transform = transform; m_transform = transform;
// avoid scaling the visualization sphere // avoid scaling the visualization sphere
@ -215,22 +221,26 @@ namespace AZ
visualizationTransform.ExtractScale(); visualizationTransform.ExtractScale();
m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, visualizationTransform); m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, visualizationTransform);
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents * m_transform.GetScale() / 2.0f); // update the inner/outer extents with the new scale
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents * m_transform.GetScale() / 2.0f); m_outerExtents *= m_transform.GetScale();
m_innerExtents *= m_transform.GetScale();
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f);
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f);
m_updateSrg = true; m_updateSrg = true;
} }
void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents) void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents)
{ {
m_outerExtents = outerExtents; m_outerExtents = outerExtents * m_transform.GetScale();
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents * m_transform.GetScale() / 2.0f); m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f);
m_updateSrg = true; m_updateSrg = true;
} }
void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents) void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents)
{ {
m_innerExtents = innerExtents; m_innerExtents = innerExtents * m_transform.GetScale();
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents * m_transform.GetScale() / 2.0f); m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f);
m_updateSrg = true; m_updateSrg = true;
} }

@ -78,10 +78,10 @@ namespace AZ
const Vector3& GetPosition() const { return m_transform.GetTranslation(); } const Vector3& GetPosition() const { return m_transform.GetTranslation(); }
void SetTransform(const AZ::Transform& transform); void SetTransform(const AZ::Transform& transform);
AZ::Vector3 GetOuterExtents() const { return m_outerExtents * m_transform.GetScale(); } const AZ::Vector3& GetOuterExtents() const { return m_outerExtents; }
void SetOuterExtents(const AZ::Vector3& outerExtents); void SetOuterExtents(const AZ::Vector3& outerExtents);
AZ::Vector3 GetInnerExtents() const { return m_innerExtents * m_transform.GetScale(); } const AZ::Vector3& GetInnerExtents() const { return m_innerExtents; }
void SetInnerExtents(const AZ::Vector3& innerExtents); void SetInnerExtents(const AZ::Vector3& innerExtents);
const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; } const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; }

@ -7118,7 +7118,9 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
// We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
// while still correctly asserting on mid-frame key press events. // while still correctly asserting on mid-frame key press events.
const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags(); const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
// [GFX TODO] Commented this line until Atom ImGuiPass is refactored (ATOM-15495).
// IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
IM_UNUSED(key_mod_flags); IM_UNUSED(key_mod_flags);
// Recover from errors // Recover from errors

@ -1,7 +0,0 @@
{
"Amazon": {
"AssetProcessor": {
"Settings": {}
}
}
}

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1a1623477f4e2e5ef0440ecfc0c08e775d9b5f12d8ebc6b434e8212bf3445bcd
size 224

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:31b247223c1116d4fc014189a9d8d86d4458b792c1120442673631136fd89520
size 222

@ -1,11 +0,0 @@
<RCC>
<qresource>
<file>refresh-active.png</file>
<file>warning.png</file>
<file>refresh.png</file>
<file>info.png</file>
<file>Backward.png</file>
<file>Forward.png</file>
<file>reset.png</file>
</qresource>
</RCC>

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3fec3caae92b9c9290c4157b3aeca29dcb366edd12719355e1d93dfb9d80cfed
size 825

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d29af2b69b74815c88352f85a7131384482e4f2d26ecf36581b46dede542fccb
size 2973

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:36591b457ee3519b16405d281674fcdd18645e3570bfc960a22b8f142ee99a14
size 436

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0362da38d3804e2911130d138be4fd73040d66c8ac7d1b5f9f60c70037e8ca93
size 1292

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf5e987ea92164dcaa6deb88989aaac962bc092f5745274017f71224f48dbf7a
size 651

@ -1,12 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
add_subdirectory(Code)

@ -1,147 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
ly_add_target(
NAME ImageProcessing.Headers HEADERONLY
NAMESPACE Gem
FILES_CMAKE
imageprocessing_headers_files.cmake
INCLUDE_DIRECTORIES
INTERFACE
Include
)
if (NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME})
set(platform_tools_files)
set(pal_tools_include_files)
set(pal_tools_dirs)
foreach(enabled_platform ${LY_PAL_TOOLS_ENABLED})
string(TOLOWER ${enabled_platform} enabled_platform_lowercase)
ly_get_list_relative_pal_filename(pal_tools_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${enabled_platform})
list(APPEND platform_tools_files ${pal_tools_dir}/pal_tools_${enabled_platform_lowercase}.cmake)
list(APPEND pal_tools_include_files ${pal_tools_dir}/pal_tools_${enabled_platform_lowercase}_files.cmake)
list(APPEND pal_tools_dirs ${pal_tools_dir})
endforeach()
ly_add_target(
NAME ImageProcessing.Static STATIC
NAMESPACE Gem
AUTOMOC
AUTOUIC
FILES_CMAKE
imageprocessing_static_files.cmake
${pal_tools_include_files}
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
PLATFORM_INCLUDE_FILES
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
${platform_tools_files}
INCLUDE_DIRECTORIES
PRIVATE
.
Source
../External
${pal_dir}
${pal_tools_dirs}
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Widgets
3rdParty::etc2comp
3rdParty::PVRTexTool
3rdParty::squish-ccr
3rdParty::zlib
3rdParty::tiff
Legacy::CryCommon
AZ::AzCore
AZ::AssetBuilderSDK
Gem::TextureAtlas
)
ly_add_source_properties(
SOURCES
Source/BuilderSettings/BuilderSettingManager.cpp
Source/Processing/ImageConvert.cpp
PROPERTY COMPILE_DEFINITIONS
VALUES ${LY_PAL_TOOLS_DEFINES}
)
ly_add_target(
NAME ImageProcessing.Editor GEM_MODULE
NAMESPACE Gem
AUTOMOC
AUTORCC
FILES_CMAKE
imageprocessing_files.cmake
PLATFORM_INCLUDE_FILES
${platform_tools_files}
INCLUDE_DIRECTORIES
PRIVATE
.
Source
${pal_dir}
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Widgets
3rdParty::PVRTexTool
3rdParty::squish-ccr
Legacy::CryCommon
AZ::AzCore
AZ::AzToolsFramework
AZ::AssetBuilderSDK
Gem::ImageProcessing.Static
Gem::TextureAtlas
RUNTIME_DEPENDENCIES
3rdParty::ASTCEncoder
Gem::TextureAtlas
)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME ImageProcessing.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
imageprocessing_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Tests
.
Source
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
3rdParty::Qt::Widgets
Legacy::CryCommon
AZ::AssetBuilderSDK
Gem::ImageProcessing.Static
Gem::ImageProcessing.Editor
Gem::TextureAtlas
)
ly_add_googletest(
NAME Gem::ImageProcessing.Tests
)
ly_add_source_properties(
SOURCES Tests/ImageProcessing_Test.cpp
PROPERTY COMPILE_DEFINITIONS
VALUES ${LY_PAL_TOOLS_DEFINES}
)
endif()

@ -1,147 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <ImageProcessing/PixelFormats.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Color.h>
namespace AZ
{
namespace IO
{
class SystemFileStream;
}
}
namespace ImageProcessing
{
class IImageObject;
class TextureSettings;
typedef AZStd::shared_ptr<IImageObject> IImageObjectPtr;
enum class EAlphaContent
{
eAlphaContent_Indeterminate, // the format may have alpha, but can't be calculated
eAlphaContent_Absent, // the format has no alpha
eAlphaContent_OnlyWhite, // alpha contains just white
eAlphaContent_OnlyBlack, // alpha contains just black
eAlphaContent_OnlyBlackAndWhite, // alpha contains just black and white
eAlphaContent_Greyscale // alpha contains grey tones
};
//interface for image object. The image object may have mipmaps.
class IImageObject
{
public:
//static functions
static IImageObject* CreateImage(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat);
virtual ~IImageObject() {};
public:
//creating new image object outof this image object
virtual IImageObject* Clone() const = 0;
// allocate an empty image object with requested format and same properties with current image
virtual IImageObject* AllocateImage(EPixelFormat pixelFormat) const = 0;
virtual IImageObject* AllocateImage() const = 0;
//get pixel format
virtual EPixelFormat GetPixelFormat() const = 0;
virtual AZ::u32 GetPixelCount(AZ::u32 mip) const = 0;
virtual AZ::u32 GetWidth(AZ::u32 mip) const = 0;
virtual AZ::u32 GetHeight(AZ::u32 mip) const = 0;
virtual bool IsCubemap() const = 0;
virtual AZ::u32 GetMipCount() const = 0;
//get pixel data buffer
virtual void GetImagePointer(AZ::u32 mip, AZ::u8*& pMem, AZ::u32& pitch) const = 0;
virtual AZ::u32 GetMipBufSize(AZ::u32 mip) const = 0;
virtual void SetMipData(AZ::u32 mip, AZ::u8* mipBuf, AZ::u32 bufSize, AZ::u32 pitch) = 0;
//get/set image flags
virtual AZ::u32 GetImageFlags() const = 0;
virtual void SetImageFlags(AZ::u32 imageFlags) = 0;
virtual void AddImageFlags(AZ::u32 imageFlags) = 0;
virtual void RemoveImageFlags(AZ::u32 imageFlags) = 0;
virtual bool HasImageFlags(AZ::u32 imageFlags) const = 0;
// image data operations and calculation
// Calculates "(pixel.rgba * scale) + bias"
virtual void ScaleAndBiasChannels(AZ::u32 firstMip, AZ::u32 maxMipCount, const AZ::Vector4& scale, const AZ::Vector4& bias) = 0;
// Calculates "clamp(pixel.rgba, min, max)"
virtual void ClampChannels(AZ::u32 firstMip, AZ::u32 maxMipCount, const AZ::Vector4& min, const AZ::Vector4& max) = 0;
//transfer alpha coverage from source image
virtual void TransferAlphaCoverage(const TextureSettings* textureSetting, const IImageObjectPtr srcImg) = 0;
// Routines to measure and manipulate alpha coverage
virtual float ComputeAlphaCoverageScaleFactor(AZ::u32 mip, float fDesiredCoverage, float fAlphaRef) const = 0;
virtual float ComputeAlphaCoverage(AZ::u32 mip, float fAlphaRef) const = 0;
//helper functions
//compare whether two images are same. return true if they are same.
virtual bool CompareImage(const IImageObjectPtr otherImage) const = 0;
// Writes this image to file used for runtime, overwrites any existing file.
// It may write alpha image as attached image into the same file
// outFilePaths will save filenames finally saved to since the image might be split and saved to multiple files
virtual bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const = 0;
virtual bool SaveImage(AZ::IO::SystemFileStream& out) const = 0;
virtual bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const = 0;
//get total image data size in memory of all mipmaps. Not includs header and flags.
virtual AZ::u32 GetTextureMemory() const = 0;
//identify content of the alpha channel
virtual EAlphaContent GetAlphaContent() const = 0;
//normalize rgb channel for specified mips
virtual void NormalizeVectors(AZ::u32 firstMip, AZ::u32 maxMipCount) = 0;
// use when you convert an image to another one
virtual void CopyPropertiesFrom(const IImageObjectPtr src) = 0;
//swizzle data for source channels to dest channels
virtual void Swizzle(const char channels[4]) = 0;
//get/set properties of the image object
virtual void GetColorRange(AZ::Color& minColor, AZ::Color& maxColor) const = 0;
virtual void SetColorRange(const AZ::Color& minColor, const AZ::Color& maxColor) = 0;
virtual AZ::u32 GetNumPersistentMips() const = 0;
virtual void SetNumPersistentMips(AZ::u32 nMips) = 0;
virtual float GetAverageBrightness() const = 0;
virtual void SetAverageBrightness(float avgBrightness) = 0;
// Derive new roughness from normal variance to preserve the bumpiness of normal map mips and to reduce specular aliasing.
// The derived roughness is combined with the artist authored roughness stored in the alpha channel of the normal map.
// The algorithm is based on the Frequency Domain Normal Mapping implementation presented by Neubelt and Pettineo at Siggraph 2013.
virtual void GlossFromNormals(bool hasAuthoredGloss) = 0;
//convert gloss map from legacy distribution to new one. New World is still using legacy gloss map.
virtual void ConvertLegacyGloss() = 0;
//clear image with color
virtual void ClearColor(float r, float g, float b, float a) = 0;
virtual bool HasPowerOfTwoSizes() const = 0;
};
//loading function to load output dds file to a IImageObject
IImageObject* LoadImageFromDdsFile(const AZStd::string& filename);
IImageObject* LoadImageFromDdsFile(AZ::IO::SystemFileStream& fileLoadStream);
IImageObject* LoadAttachedImageFromDdsFile(const AZStd::string& filename, IImageObjectPtr originImage);
} // namespace ImageProcessing

@ -1,37 +0,0 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <ImageProcessing/ImageObject.h>
namespace ImageProcessing
{
class ImageProcessingRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Loads an image from a source file path
virtual IImageObjectPtr LoadImage(const AZStd::string& filePath) = 0;
// Loads an image from a source file path and converts it to a format suitable for previewing in tools
virtual IImageObjectPtr LoadImagePreview(const AZStd::string& filePath) = 0;
};
using ImageProcessingRequestBus = AZ::EBus<ImageProcessingRequests>;
} // namespace ImageProcessing

@ -1,36 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
class QString;
namespace ImageProcessingEditor
{
class ImageProcessingEditorRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
/////////////////////////////////////////////////////////////////////////
//! Open single texture file
virtual void OpenSourceTextureFile(const AZ::Uuid& textureSourceID) = 0;
};
using ImageProcessingEditorRequestBus = AZ::EBus<ImageProcessingEditorRequests>;
}//namespace ImageProcessingEditor

@ -1,107 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
namespace ImageProcessing
{
enum EPixelFormat : int
{
//unsigned formats
ePixelFormat_R8G8B8A8 = 0,
ePixelFormat_R8G8B8X8,
ePixelFormat_R8G8,
ePixelFormat_R8,
ePixelFormat_A8,
ePixelFormat_R16G16B16A16,
ePixelFormat_R16G16,
ePixelFormat_R16,
//Custom FourCC Formats
// Data in these FourCC formats is custom compressed data and only decodable by certain hardware.
//ASTC formats supported by ios devices with A8 processor. Also supported by Android Extension Pack.
ePixelFormat_ASTC_4x4,
ePixelFormat_ASTC_5x4,
ePixelFormat_ASTC_5x5,
ePixelFormat_ASTC_6x5,
ePixelFormat_ASTC_6x6,
ePixelFormat_ASTC_8x5,
ePixelFormat_ASTC_8x6,
ePixelFormat_ASTC_8x8,
ePixelFormat_ASTC_10x5,
ePixelFormat_ASTC_10x6,
ePixelFormat_ASTC_10x8,
ePixelFormat_ASTC_10x10,
ePixelFormat_ASTC_12x10,
ePixelFormat_ASTC_12x12,
//Formats supported by PowerVR GPU. Mainly for ios devices.
ePixelFormat_PVRTC2, //2bpp
ePixelFormat_PVRTC4, //4bpp
//formats for opengl and opengles 3.0 (android devices)
ePixelFormat_EAC_R11, //one channel unsigned data
ePixelFormat_EAC_RG11, //two channel unsigned data
ePixelFormat_ETC2, //Compresses RGB888 data, it taks 4x4 groups of pixel data and compresses each into a 64-bit
ePixelFormat_ETC2a, //Compresses RGBA8888 data with full alpha support
// Standardized Compressed DXGI Formats (DX10+)
// Data in these compressed formats is hardware decodable on all DX10 chips, and manageable with the DX10-API.
ePixelFormat_BC1, // RGB without alpha, 0.5 byte/px
ePixelFormat_BC1a, // RGB with 1 bit of alpha, 0.5 byte/px.
ePixelFormat_BC3, // RGBA 1 byte/px, color maps with full alpha.
ePixelFormat_BC3t, // BC3 with alpha weighted color
ePixelFormat_BC4, // One color channel, 0.5 byte/px, unsigned
ePixelFormat_BC4s, // BC4, signed
ePixelFormat_BC5, // Two color channels, 1 byte/px, unsigned. Usually use for tangent-space normal maps
ePixelFormat_BC5s, // BC5, signed
ePixelFormat_BC6UH, // RGB, floating-point. Used for HDR images. Decompress to RGB in half floating point
ePixelFormat_BC7, // RGB or RGBA. 1 byte/px. Three color channels (4 to 7 bits per channel) with 0 to 8 bits of alpha
ePixelFormat_BC7t, // BC& with alpha weighted color
// Float formats
// Data in a Float format is floating point data.
ePixelFormat_R9G9B9E5,
ePixelFormat_R32G32B32A32F,
ePixelFormat_R32G32F,
ePixelFormat_R32F,
ePixelFormat_R16G16B16A16F,
ePixelFormat_R16G16F,
ePixelFormat_R16F,
//legacy format. Only used to load old converted dds files.
ePixelFormat_B8G8R8A8, //32bits rgba format
ePixelFormat_R32,
ePixelFormat_Count,
ePixelFormat_Unknown = ePixelFormat_Count
};
inline bool IsASTCFormat(EPixelFormat fmt)
{
return fmt == ePixelFormat_ASTC_4x4 || fmt == ePixelFormat_ASTC_5x4 || fmt == ePixelFormat_ASTC_5x5 ||
fmt == ePixelFormat_ASTC_6x5 || fmt == ePixelFormat_ASTC_6x6 || fmt == ePixelFormat_ASTC_8x5 ||
fmt == ePixelFormat_ASTC_8x6 || fmt == ePixelFormat_ASTC_8x8 || fmt == ePixelFormat_ASTC_10x5 ||
fmt == ePixelFormat_ASTC_10x6 || fmt == ePixelFormat_ASTC_10x8 || fmt == ePixelFormat_ASTC_10x10 ||
fmt == ePixelFormat_ASTC_12x10 || fmt == ePixelFormat_ASTC_12x12;
}
inline bool IsETCFormat(EPixelFormat fmt)
{
return fmt == ePixelFormat_ETC2 || fmt == ePixelFormat_ETC2a || fmt == ePixelFormat_EAC_R11 ||
fmt == ePixelFormat_EAC_RG11;
}
inline bool IsPVRTCFormat(EPixelFormat fmt)
{
return fmt == ePixelFormat_PVRTC2 || fmt == ePixelFormat_PVRTC4;
}
} // namespace ImageProcessing

@ -1,99 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include "AtlasBuilderComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
namespace TextureAtlasBuilder
{
// AZ Components should only initialize their members to null and empty in constructor
// Allocation of data should occur in Init(), once we can guarantee reflection and registration of types
AtlasBuilderComponent::AtlasBuilderComponent()
{
}
// Handle deallocation of your memory allocated in Init()
AtlasBuilderComponent::~AtlasBuilderComponent()
{
}
// Init is where you'll actually allocate memory or create objects
// This ensures that any dependency components will have been been created and serialized
void AtlasBuilderComponent::Init()
{
}
// Activate is where you'd perform registration with other objects and systems.
// All builder classes owned by this component should be registered here
// Any EBuses for the builder classes should also be connected at this point
void AtlasBuilderComponent::Activate()
{
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = "Atlas Worker Builder";
builderDescriptor.m_version = 1;
builderDescriptor.m_patterns.emplace_back(AssetBuilderSDK::AssetBuilderPattern("*.texatlas", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
builderDescriptor.m_busId = azrtti_typeid<AtlasBuilderWorker>();
builderDescriptor.m_createJobFunction = AZStd::bind(&AtlasBuilderWorker::CreateJobs, &m_atlasBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&AtlasBuilderWorker::ProcessJob, &m_atlasBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
m_atlasBuilder.BusConnect(builderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
}
// Disconnects from any EBuses we connected to in Activate()
// Unregisters from objects and systems we register with in Activate()
void AtlasBuilderComponent::Deactivate()
{
m_atlasBuilder.BusDisconnect();
// We don't need to unregister the builder - the AP will handle this for us, because it is managing the lifecycle of this component
}
// Reflect the input and output formats for the serializer
void AtlasBuilderComponent::Reflect(AZ::ReflectContext* context)
{
// components also get Reflect called automatically
// this is your opportunity to perform static reflection or type registration of any types you want the serializer to know about
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<AtlasBuilderComponent, AZ::Component>()
->Version(0)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
;
}
AtlasBuilderInput::Reflect(context);
}
void AtlasBuilderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("Atlas Builder Plugin Service", 0x35974d0d));
}
void AtlasBuilderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("Atlas Builder Plugin Service", 0x35974d0d));
}
void AtlasBuilderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
AZ_UNUSED(required);
}
void AtlasBuilderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
}
}

@ -1,44 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include "AtlasBuilderWorker.h"
namespace TextureAtlasBuilder
{
class AtlasBuilderComponent : public AZ::Component
{
public:
AZ_COMPONENT(AtlasBuilderComponent, "{F49987FB-3375-4417-AB83-97B44C78B335}");
AtlasBuilderComponent();
~AtlasBuilderComponent() override;
void Init() override;
void Activate() override;
void Deactivate() override;
//! Reflect formats for input and output
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
private:
AtlasBuilderWorker m_atlasBuilder;
};
} // namespace TextureAtlasBuilder

@ -1,221 +0,0 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <qimage.h>
#include <TextureAtlas/TextureAtlasBus.h>
namespace TextureAtlasBuilder
{
//! Struct that is used to communicate input commands
struct AtlasBuilderInput
{
AZ_CLASS_ALLOCATOR(AtlasBuilderInput, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(AtlasBuilderInput, "{F54477F9-1BDE-4274-8CC0-8320A3EF4A42}");
bool m_forceSquare;
bool m_forcePowerOf2;
// Includes a white default texture for the UI to use under certain circumstances
bool m_includeWhiteTexture;
int m_maxDimension;
// At least this much padding will surround each texture except on the edges of the atlas
int m_padding;
// Color used in wasted space
AZ::Color m_unusedColor;
// A preset to use for the texture atlas image processing
AZStd::string m_presetName;
AZStd::vector<AZStd::string> m_filePaths;
AtlasBuilderInput():
m_forceSquare(false),
m_forcePowerOf2(false),
m_includeWhiteTexture(true),
m_maxDimension(4096),
m_padding(1),
// Default color should be a non-transparent color that isn't used often in uis
m_unusedColor(.235f, .702f, .443f, 1)
{
}
static void Reflect(AZ::ReflectContext* context);
//! Attempts to read the input from a .texatlas file. "valid" is for reporting exceptions and telling the asset
//! proccesor to fail the job. Supports parsing through a human readable custom parser.
static AtlasBuilderInput ReadFromFile(const AZStd::string& path, const AZStd::string& directory, bool& valid);
//! Resolves any wild cards in paths
static void AddFilesUsingWildCard(AZStd::vector<AZStd::string>& paths, const AZStd::string& insert);
//! Removes anything that matches the wildcard
static void RemoveFilesUsingWildCard(AZStd::vector<AZStd::string>& paths, const AZStd::string& remove);
//! Resolves any folder paths into image file paths
static void AddFolderContents(AZStd::vector<AZStd::string>& paths, const AZStd::string& insert, bool& valid);
//! Resolves remove commands for folders
static void RemoveFolderContents(AZStd::vector<AZStd::string>& paths, const AZStd::string& remove);
};
//! Struct that is used to represent an object with a width and height in pixels
struct ImageDimension
{
int m_width;
int m_height;
ImageDimension(int width, int height)
{
m_width = width;
m_height = height;
}
};
//! Typedef for an ImageDimension paired with an integer
using IndexImageDimension = AZStd::pair<int, ImageDimension>;
//! Typedef for a list of ImageDimensions paired with integers
using ImageDimensionData = AZStd::vector<IndexImageDimension>;
//! Typedef to simplify references to TextureAtlas::AtlasCoordinates
using AtlasCoordinates = TextureAtlasNamespace::AtlasCoordinates;
//! Number of bytes in a pixel
const int bytesPerPixel = 4;
//! The size of the padded sorting units (important for compression)
const int cellSize = 4;
//! Indexes of the products
enum class Product
{
TexatlasidxProduct = 0,
DdsProduct = 1
};
//! An asset builder for texture atlases
class AtlasBuilderWorker : public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
AZ_RTTI(AtlasBuilderWorker, "{79036188-E017-4575-9EC0-8D39CB560EA6}");
AtlasBuilderWorker() = default;
~AtlasBuilderWorker() = default;
//! Asset Builder Callback Functions
//! Called by asset processor to gather information on a job for a ".texatlas" file
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request,
AssetBuilderSDK::CreateJobsResponse& response);
//! Called by asset proccessor when it wants us to execute a job
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request,
AssetBuilderSDK::ProcessJobResponse& response);
//! Returns the job related information used by the builder
static AssetBuilderSDK::JobDescriptor GetJobDescriptor(const AZStd::string& sourceFile, const AtlasBuilderInput& input);
//////////////////////////////////////////////////////////////////////////
//! AssetBuilderSDK::AssetBuilderCommandBus interface
void ShutDown() override; // if you get this you must fail all existing jobs and return.
//////////////////////////////////////////////////////////////////////////
private:
bool m_isShuttingDown = false;
//! This is the main function that takes a set of inputs and attempts to pack them into an atlas of a given
//! size. Returns true if succesful, does not update out on failure.
static bool TryPack(const ImageDimensionData& images,
int targetWidth,
int targetHeight,
int padding,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Removes any overlap between slotList and the given item
static void TrimOverlap(AZStd::vector<AtlasCoordinates>& slotList, AtlasCoordinates item);
//! Uses the proper tightening method based on the input and returns the maximum number of items that were able to be fit
bool TryTightening(AtlasBuilderInput input,
const ImageDimensionData& images,
int smallestWidth,
int smallestHeight,
int targetArea,
int padding,
int& resultWidth,
int& resultHeight,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Finds the tightest square fit achievable by expanding a square area until a valid fit is found
bool TryTighteningSquare(const ImageDimensionData& images,
int lowerBound,
int maxDimension,
int targetArea,
bool powerOfTwo,
int padding,
int& resultWidth,
int& resultHeight,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Finds the tightest fit achievable by starting with the optimal thin solution and attempting to resize to be
//! a better shape
bool TryTighteningOptimal(const ImageDimensionData& images,
int smallestWidth,
int smallestHeight,
int maxDimension,
int targetArea,
bool powerOfTwo,
int padding,
int& resultWidth,
int& resultHeight,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Sorting logic for adding a slot to a sorted list in order to maintain increasing order
static void InsertInOrder(AZStd::vector<AtlasCoordinates>& slotList, AtlasCoordinates item);
//! Misc Logic For Estimating Target Shape
//! Returns the width of the widest element
static int GetWidest(const ImageDimensionData& imageList);
//! Returns the height of the tallest area
static int GetTallest(const ImageDimensionData& imageList);
};
//! Used for sorting ImageDimensions
static bool operator<(ImageDimension a, ImageDimension b);
//! Used to expose the ImageDimension in a pair to AZStd::Sort
static bool operator<(IndexImageDimension a, IndexImageDimension b);
//! Returns true if two coordinate sets overlap
static bool Collides(AtlasCoordinates a, AtlasCoordinates b);
//! Returns true if item collides with any object in list
static bool Collides(AtlasCoordinates item, AZStd::vector<AtlasCoordinates> list);
//! Returns the portion of the second item that overlaps with the first
static AtlasCoordinates GetOverlap(AtlasCoordinates a, AtlasCoordinates b);
//! Performs an operation that copies a pixel to the output
static void SetPixels(AZ::u8* dest, const AZ::u8* source, int destBytes);
//! Checks if we can insert an image into a slot
static bool CanInsert(AtlasCoordinates slot, ImageDimension image, int padding, int farRight, int farBot);
//! Adds the necessary padding to an Atlas Coordinate
static void AddPadding(AtlasCoordinates& slot, int padding, int farRight, int farBot);
}

@ -1,192 +0,0 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/BuilderSettings.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/base.h>
#include <ImageProcessing/ImageObject.h>
class QSettings;
class QString;
namespace AZ
{
template<class T> class EnvironmentVariable;
class SerializeContext;
}
namespace ImageProcessing
{
class BuilderSettingManager
{
public:
AZ_TYPE_INFO(CBuilderSettingManager, "{DAA55241-64FA-4A9B-A37F-C0A36B36D536}");
AZ_CLASS_ALLOCATOR(BuilderSettingManager, AZ::SystemAllocator, 0);
//load builder settings for all platform
//contain builder setting for all platforms
//this manager should be able to get texture setting for a platform
static BuilderSettingManager* Instance();
// life cycle management:
static void CreateInstance();
static void DestroyInstance();
static void Reflect(AZ::ReflectContext* context);
const PresetSettings* GetPreset(const AZ::Uuid presetId, const PlatformName& platform = "");
const BuilderSettings* GetBuilderSetting(const PlatformName& platform);
/**
* Attempts to translate a legacy preset name into Open 3D Engine preset name.
* @param legacy preset name string
* @return A translated preset name. If no translation is available, returns the same value as input argument.
*/
const PresetName TranslateLegacyPresetName(const PresetName& legacyName);
/**
* @return A list of platform supported
*/
const PlatformNameList GetPlatformList();
/**
* @return A map of preset settings based on their filemasks.
* @key filemask string, empty string means no filemask
* @value set of preset setting names supporting the specified filemask
*/
const AZStd::map<FileMask, AZStd::set<PresetName>>& GetPresetFilterMap();
/**
* Find preset id list based on the preset name.
* @param preset name string
* @return a map of preset ids whose name are specified by the input on different platforms
* @key platform name string
* @value uuid of the preset setting
*/
const AZ::Uuid GetPresetIdFromName(const PresetName& presetName);
/**
* Find preset name based on the preset id.
* @param uuid of the preset setting
* @return preset name string
*/
const PresetName GetPresetNameFromId(const AZ::Uuid& presetId);
/**
* Writes preset data to file using AZ::Serialization format.
* @param filepath string to the build settings xml
*/
StringOutcome WriteBuilderSettings(AZStd::string filepath, AZ::SerializeContext* context = nullptr);
/**
* Loads preset data from file using AZ::Serialization format.
* @param filepath string to the build settings xml
*/
StringOutcome LoadBuilderSettings(AZStd::string filepath, AZ::SerializeContext* context = nullptr);
/**
* Overload function. Loads preset data from project setting file if any
* Otherwise, the function will load default setting file inside the gem
*/
StringOutcome LoadBuilderSettings();
/**
* Loads preset data from legacy format found in RC.ini.
* @param filepath string to RC.ini
*/
StringOutcome LoadBuilderSettingsFromRC(AZStd::string& filePath);
/**
* Returns the first u32 generated from a hash of the builder settings
* file that can be used as a version to detect changes to the file.
*/
AZ::u32 BuilderSettingsVersion() const;
/**
* Provides a full path to the adjacent metafile of a given texture/image file.
* @param Filepath string to the texture/image file.
* @param Output filepath string to the adjacent texture/image metafile.
* Will output whichever metafile is present, whether it is legacy or modern format.
* If both are present, modern format is returned.
* If none are present, an empty string is returned.
*/
void MetafilePathFromImagePath(const AZStd::string& imagePath, AZStd::string& metafilePath);
/**
* Find a suitable preset a given image file.
* @param imageFilePath: Filepath string of the image file. The function may load the image from the path for better detection
* @param image: an optional image object which can be used for preset selection if there is no match based file mask.
* @return suggested preset uuid.
*/
AZ::Uuid GetSuggestedPreset(const AZStd::string& imageFilePath, IImageObjectPtr image = nullptr);
bool DoesSupportPlatform(const AZStd::string& platformId);
static const char* s_environmentVariableName;
static AZ::EnvironmentVariable<BuilderSettingManager*> s_globalInstance;
static AZStd::mutex s_instanceMutex;
static const PlatformName s_defaultPlatform;
BuilderSettingManager(){}
private: // functions
AZ_DISABLE_COPY_MOVE(BuilderSettingManager);
StringOutcome ProcessPreset(QString& preset, QSettings& rcINI, PlatformNameVector& all_platforms);
/**
* Clear Builder Settings and any cached maps/lists
*/
void ClearSettings();
/**
* Regenerate Builder Settings and any cached maps/lists
*/
void RegenerateMappings();
private: // variables
//builder settings for each platform
AZStd::map <PlatformName, BuilderSettings> m_builderSettings;
AZStd::map <PresetName, PresetName> m_presetAliases;
/**
* Cached list of presets mapped by their file masks.
* @Key file mask, use empty string to indicate all presets without filtering
* @Value set of preset names that matches the file mask
*/
AZStd::map <FileMask, AZStd::set<PresetName> > m_presetFilterMap;
/**
* A mutex to protect when modifying any map in this manager
*/
AZStd::mutex m_presetMapLock;
//default presets for certian file masks
AZStd::map <FileMask, AZ::Uuid > m_defaultPresetByFileMask;
//default preset for none power of two image
AZ::Uuid m_defaultPresetNonePOT;
//default preset for power of two
AZ::Uuid m_defaultPreset;
//default preset for power of two with alpha
AZ::Uuid m_defaultPresetAlpha;
//generated from hashing the builder settings file
AZ::u32 m_builderSettingsFileVersion;
};
} // namespace ImageProcessing

@ -1,32 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include <BuilderSettings/BuilderSettings.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace ImageProcessing
{
void BuilderSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<BuilderSettings>()
->Version(1)
->Field("GlossScale", &BuilderSettings::m_brdfGlossScale)
->Field("GlossBias", &BuilderSettings::m_brdfGlossBias)
->Field("Streaming", &BuilderSettings::m_enableStreaming)
->Field("Enable", &BuilderSettings::m_enablePlatform)
->Field("Presets", &BuilderSettings::m_presets);
}
}
} // namespace ImageProcessing

@ -1,33 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <BuilderSettings/PresetSettings.h>
namespace ImageProcessing
{
//builder setting for a platform
struct BuilderSettings
{
AZ_TYPE_INFO(BuilderSettings, "{4085AB56-934C-43A6-AF25-4443E1EEB71D}");
AZ_CLASS_ALLOCATOR(BuilderSettings, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
//global settings
float m_brdfGlossScale = 16.0f;
float m_brdfGlossBias = 0.0f;
bool m_enableStreaming = true;
bool m_enablePlatform = true;
AZStd::map<AZ::Uuid, PresetSettings> m_presets;
};
} // namespace ImageProcessing

@ -1,52 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include <BuilderSettings/CubemapSettings.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace ImageProcessing
{
bool CubemapSettings::operator!=(const CubemapSettings& other)
{
return !(*this == other);
}
bool CubemapSettings::operator==(const CubemapSettings& other)
{
return
m_angle == other.m_angle &&
m_mipAngle == other.m_mipAngle &&
m_mipSlope == other.m_mipSlope &&
m_edgeFixup == other.m_edgeFixup &&
m_generateDiff == other.m_generateDiff &&
m_diffuseGenPreset == other.m_diffuseGenPreset;
}
void CubemapSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<CubemapSettings>()
->Version(1)
->Field("Filter", &CubemapSettings::m_filter)
->Field("Angle", &CubemapSettings::m_angle)
->Field("MipAngle", &CubemapSettings::m_mipAngle)
->Field("MipSlope", &CubemapSettings::m_mipSlope)
->Field("EdgeFixup", &CubemapSettings::m_edgeFixup)
->Field("GenerateDiff", &CubemapSettings::m_generateDiff)
->Field("DiffuseProbePreset", &CubemapSettings::m_diffuseGenPreset);
}
}
} // namespace ImageProcessing

@ -1,52 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Memory/Memory.h>
#include <BuilderSettings/ImageProcessingDefines.h>
namespace ImageProcessing
{
//! settings related to cubemap. Part of texture preset setting. only useful when cubemap enabled
struct CubemapSettings
{
AZ_TYPE_INFO(CubemapSettings, "{C6BDEB7B-8E05-4B2D-8F39-8F6275BC84E8}");
AZ_CLASS_ALLOCATOR(CubemapSettings, AZ::SystemAllocator, 0);
bool operator!=(const CubemapSettings& other);
bool operator==(const CubemapSettings& other);
static void Reflect(AZ::ReflectContext* context);
// "cm_ftype", cubemap angular filter type: gaussian, cone, disc, cosine, cosine_power, ggx
CubemapFilterType m_filter;
// "cm_fangle", base filter angle for cubemap filtering(degrees), 0 - disabled
float m_angle;
// "cm_fmipangle", initial mip filter angle for cubemap filtering(degrees), 0 - disabled
float m_mipAngle;
// "cm_fmipslope", mip filter angle multiplier for cubemap filtering, 1 - default"
float m_mipSlope;
// "cm_edgefixup", cubemap edge fix-up width, 0 - disabled
float m_edgeFixup;
// "cm_diff", generate a diffuse illumination light-probe in addition
bool m_generateDiff;
// "cm_diffpreset", the name of the preset to be used for the diffuse probe
AZ::Uuid m_diffuseGenPreset;
};
} // namespace ImageProcessing

@ -1,108 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/base.h>
/**
* Shorthand for checking a condition, and failing if false.
* Works with any function that returns AZ::Outcome<..., AZStd::string>.
* Unlike assert, it is not removed in release builds.
* Ensure all strings are passed with c_str(), as they are passed to AZStd::string::format().
*/
#define AZ_ENSURE_STRING_OUTCOME_CONDITION(cond, ...) if (!(cond)) { return AZ::Failure(AZStd::string::format(__VA_ARGS__)); }
// Similar to above macro, but ensures on an AZ::Outcome. Not removed in release builds.
#define AZ_ENSURE_STRING_OUTCOME(outcome) if (!(outcome.IsSuccess())) { return AZ::Failure(outcome.GetError()); }
namespace ImageProcessing
{
//! Common return type for operations that can fail.
// Empty success string == Success.
// Populated success string == Warning.
// Populated error string == Failure.
using StringOutcome = AZ::Outcome<AZStd::string, AZStd::string>;
#define STRING_OUTCOME_SUCCESS AZ::Success(AZStd::string())
#define STRING_OUTCOME_WARNING(warning) AZ::Success(AZStd::string(warning))
#define STRING_OUTCOME_ERROR(error) AZ::Failure(AZStd::string(error))
// Common typedefs (with dependent forward-declarations)
typedef AZStd::string PlatformName, PresetName, FileMask;
typedef AZStd::vector<PlatformName> PlatformNameVector;
typedef AZStd::list<PlatformName> PlatformNameList;
//! min and max reduce level
static const unsigned int s_MinReduceLevel = 0;
static const unsigned int s_MaxReduceLevel = 5;
static const int s_TotalSupportedImageExtensions = 8;
static const char* s_SupportedImageExtensions[s_TotalSupportedImageExtensions] = {
"*.tif",
"*.tiff",
"*.png",
"*.bmp",
"*.jpg",
"*.jpeg",
"*.tga",
"*.gif"
};
enum class RGBWeight : AZ::u32
{
uniform, // uniform weights (1.0, 1.0, 1.0) (default)
luminance, // luminance-based weights (0.3086, 0.6094, 0.0820)
ciexyz // ciexyz-based weights (0.2126, 0.7152, 0.0722)
};
enum class ColorSpace : AZ::u32
{
linear,
sRGB,
autoSelect,
};
enum class MipGenType : AZ::u32
{
point, //Also called nearest neighbor
box, //Also called 'average'. When shrinking images it will average, and merge the pixels together.
triangle, //Also called linear or Bartlett window
quadratic, //Also called bilinear or Welch window
gaussian, //It remove high frequency noise in a highly controllable way.
blackmanHarris,
kaiserSinc //Good for foliage and tree assets exported from Speedtree.
};
enum class MipGenEvalType : AZ::u32
{
sum,
max,
min
};
//cubemap angular filter type. Only two filter types were used in rc.ini
enum class CubemapFilterType : AZ::u32
{
disc = 0, // same as CP_FILTER_TYPE_DISC in CubemapGen
cone = 1, // same as CP_FILTER_TYPE_CONE
cosine = 2, // same as CP_FILTER_TYPE_COSINE. only used for [EnvironmentProbeHDR_Irradiance]
gaussian = 3, // same as CP_FILTER_TYPE_ANGULAR_GAUSSIAN
cosine_power = 4, // same as CP_FILTER_TYPE_COSINE_POWER
ggx = 5 // same as CP_FILTER_TYPE_GGX. only used for [EnvironmentProbeHDR]
};
} // namespace ImageProcessing

@ -1,66 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include <BuilderSettings/MipmapSettings.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace ImageProcessing
{
bool MipmapSettings::operator!=(const MipmapSettings& other) const
{
return !(*this == other);
}
bool MipmapSettings::operator==(const MipmapSettings& other) const
{
return
m_type == other.m_type &&
m_borderColor == other.m_borderColor &&
m_normalize == other.m_normalize &&
m_streamableMips == other.m_streamableMips;
}
void MipmapSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<MipmapSettings>()
->Version(1)
->Field("MipGenType", &MipmapSettings::m_type)
->Field("BorderColor", &MipmapSettings::m_borderColor)
->Field("Normalize", &MipmapSettings::m_normalize)
->Field("StreamableMips", &MipmapSettings::m_streamableMips);
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<MipmapSettings>("Mipmap Setting", "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MipmapSettings::m_type, "Type", "")
->EnumAttribute(MipGenType::point, "Point")
->EnumAttribute(MipGenType::box, "Average")
->EnumAttribute(MipGenType::triangle, "Linear")
->EnumAttribute(MipGenType::quadratic, "Bilinear")
->EnumAttribute(MipGenType::gaussian, "Gaussian")
->EnumAttribute(MipGenType::blackmanHarris, "BlackmanHarris")
->EnumAttribute(MipGenType::kaiserSinc, "KaiserSinc")
->DataElement(AZ::Edit::UIHandlers::Color, &MipmapSettings::m_borderColor, "Color", "")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &MipmapSettings::m_normalize, "Normalized", "")
->DataElement(AZ::Edit::UIHandlers::SpinBox, &MipmapSettings::m_streamableMips, "Streamable Mips", "")
->Attribute(AZ::Edit::Attributes::Min, 0)
;
}
}
}
} // namespace ImageProcessing

@ -1,39 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <BuilderSettings/ImageProcessingDefines.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Math/Color.h>
namespace ImageProcessing
{
struct MipmapSettings
{
AZ_TYPE_INFO(MipmapSettings, "{9239618E-23A6-43C8-9B87-50528CBFA6FF}");
AZ_CLASS_ALLOCATOR(MipmapSettings, AZ::SystemAllocator, 0);
bool operator!=(const MipmapSettings& other) const;
bool operator==(const MipmapSettings& other) const;
static void Reflect(AZ::ReflectContext* context);
MipGenType m_type = MipGenType::blackmanHarris;
//Unused or duplicated properties. We may want to move same properties from perset setting to here.
AZ::Color m_borderColor;
bool m_normalize;
AZ::u32 m_streamableMips;
};
} // namespace ImageProcessing

@ -1,34 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/PixelFormats.h>
namespace ImageProcessing
{
//! default settings for platform
struct PlatformSetting
{
AZ_TYPE_INFO(PlatformSetting, "{19EF828B-DDFF-4591-AD5A-946801FCC98E}");
AZ_CLASS_ALLOCATOR(PlatformSetting, AZ::SystemAllocator, 0);
//! Platform's name
PlatformName m_name;
//! pixel formats supported for the platform
AZStd::list<EPixelFormat> m_availableFormat;
};
} // namespace ImageProcessing

@ -1,213 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include <BuilderSettings/PresetSettings.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace ImageProcessing
{
PresetSettings::PresetSettings()
: m_uuid(0)
, m_rgbWeight(RGBWeight::uniform)
, m_srcColorSpace(ColorSpace::sRGB)
, m_destColorSpace(ColorSpace::autoSelect)
, m_suppressEngineReduce(false)
, m_pixelFormat(ePixelFormat_R8G8B8A8)
, m_pixelFormatName("R8G8B8A8")
, m_pixelFormatAlpha(ePixelFormat_Unknown)
, m_pixelFormatAlphaName("")
, m_discardAlpha(false)
, m_maxTextureSize(0)
, m_minTextureSize(0)
, m_isPowerOf2(false)
, m_sizeReduceLevel(0)
, m_isColorChart(0)
, m_highPassMip(0)
, m_glossFromNormals(false)
, m_isMipRenormalize(false)
, m_numStreamableMips(100)
, m_isLegacyGloss(false)
{
}
PresetSettings::PresetSettings(const PresetSettings& other)
{
DeepCopyMembers(other);
}
void PresetSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<PresetSettings>()
->Version(1)
->Field("UUID", &PresetSettings::m_uuid)
->Field("Name", &PresetSettings::m_name)
->Field("Description", &PresetSettings::m_description)
->Field("RGB_Weight", &PresetSettings::m_rgbWeight)
->Field("SourceColor", &PresetSettings::m_srcColorSpace)
->Field("DestColor", &PresetSettings::m_destColorSpace)
->Field("FileMasks", &PresetSettings::m_fileMasks)
->Field("SuppressEngineReduce", &PresetSettings::m_suppressEngineReduce)
->Field("PixelFormat", &PresetSettings::m_pixelFormatName)
->Field("PixelFormatAlpha", &PresetSettings::m_pixelFormatAlphaName)
->Field("DiscardAlpha", &PresetSettings::m_discardAlpha)
->Field("MaxTextureSize", &PresetSettings::m_maxTextureSize)
->Field("MinTextureSize", &PresetSettings::m_minTextureSize)
->Field("IsPowerOf2", &PresetSettings::m_isPowerOf2)
->Field("SizeReduceLevel", &PresetSettings::m_sizeReduceLevel)
->Field("IsColorChart", &PresetSettings::m_isColorChart)
->Field("HighPassMip", &PresetSettings::m_highPassMip)
->Field("GlossFromNormal", &PresetSettings::m_glossFromNormals)
->Field("UseLegacyGloss", &PresetSettings::m_isLegacyGloss)
->Field("MipRenormalize", &PresetSettings::m_isMipRenormalize)
->Field("NumberStreamableMips", &PresetSettings::m_numStreamableMips)
->Field("Swizzle", &PresetSettings::m_swizzle)
->Field("CubemapSettings", &PresetSettings::m_cubemapSetting)
->Field("MipMapSetting", &PresetSettings::m_mipmapSetting);
}
}
PresetSettings& PresetSettings::operator= (const PresetSettings& other)
{
DeepCopyMembers(other);
return *this;
}
bool PresetSettings::operator==(const PresetSettings& other) const
{
bool arePointersEqual = true;
///////
// MipMap Settings
//////
// If both pointers are allocated...
if (m_mipmapSetting && other.m_mipmapSetting)
{
// If the allocated values are different...
if (*m_mipmapSetting != *other.m_mipmapSetting)
{
arePointersEqual = false;
}
}
// Otherwise, one or both pointers are un-allocated.
// If only one pointer is allocated (via unequivalency)...
else if (m_mipmapSetting != other.m_mipmapSetting)
{
arePointersEqual = false;
}
///////
// CubeMap Settings
//////
// If both pointers are allocated...
if (m_cubemapSetting && other.m_cubemapSetting)
{
// If the allocated values are different...
if (*m_cubemapSetting != *other.m_cubemapSetting)
{
arePointersEqual = false;
}
}
// Otherwise, one or both pointers are un-allocated.
// If only one pointer is allocated (via unequivalency)...
else if (m_cubemapSetting != other.m_cubemapSetting)
{
arePointersEqual = false;
}
return
arePointersEqual &&
m_uuid == other.m_uuid &&
m_name == other.m_name &&
m_description == other.m_description &&
m_rgbWeight == other.m_rgbWeight &&
m_srcColorSpace == other.m_srcColorSpace &&
m_destColorSpace == other.m_destColorSpace &&
m_fileMasks == other.m_fileMasks &&
m_suppressEngineReduce == other.m_suppressEngineReduce &&
m_pixelFormat == other.m_pixelFormat &&
m_pixelFormatName == other.m_pixelFormatName &&
m_pixelFormatAlpha == other.m_pixelFormatAlpha &&
m_pixelFormatAlphaName == other.m_pixelFormatAlphaName &&
m_discardAlpha == other.m_discardAlpha &&
m_minTextureSize == other.m_minTextureSize &&
m_maxTextureSize == other.m_maxTextureSize &&
m_isPowerOf2 == other.m_isPowerOf2 &&
m_sizeReduceLevel == other.m_sizeReduceLevel &&
m_isColorChart == other.m_isColorChart &&
m_highPassMip == other.m_highPassMip &&
m_glossFromNormals == other.m_glossFromNormals &&
m_isLegacyGloss == other.m_isLegacyGloss &&
m_swizzle == other.m_swizzle &&
m_isMipRenormalize == other.m_isMipRenormalize &&
m_numStreamableMips == other.m_numStreamableMips;
}
void PresetSettings::DeepCopyMembers(const PresetSettings & other)
{
if (this != &other)
{
if(other.m_mipmapSetting)
{
m_mipmapSetting = AZStd::make_unique<MipmapSettings>(*other.m_mipmapSetting);
}
if(other.m_cubemapSetting)
{
m_cubemapSetting = AZStd::make_unique<CubemapSettings>(*other.m_cubemapSetting);
}
m_uuid = other.m_uuid;
m_name = other.m_name;
m_description = other.m_description;
m_rgbWeight = other.m_rgbWeight;
m_srcColorSpace = other.m_srcColorSpace;
m_destColorSpace = other.m_destColorSpace;
m_fileMasks = other.m_fileMasks;
m_suppressEngineReduce = other.m_suppressEngineReduce;
m_pixelFormat = other.m_pixelFormat;
m_pixelFormatAlpha = other.m_pixelFormatAlpha;
m_pixelFormatName = other.m_pixelFormatName;
m_pixelFormatAlphaName = other.m_pixelFormatAlphaName;
m_discardAlpha = other.m_discardAlpha;
m_minTextureSize = other.m_minTextureSize;
m_maxTextureSize = other.m_maxTextureSize;
m_isPowerOf2 = other.m_isPowerOf2;
m_sizeReduceLevel = other.m_sizeReduceLevel;
m_isColorChart = other.m_isColorChart;
m_highPassMip = other.m_highPassMip;
m_glossFromNormals = other.m_glossFromNormals;
m_isLegacyGloss = other.m_isLegacyGloss;
m_swizzle = other.m_swizzle;
m_isMipRenormalize = other.m_isMipRenormalize;
m_numStreamableMips = other.m_numStreamableMips;
}
}
AZ::Vector3 PresetSettings::GetColorWeight()
{
switch (m_rgbWeight)
{
case RGBWeight::uniform:
return AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
case RGBWeight::ciexyz:
return AZ::Vector3(0.2126f, 0.7152f, 0.0722f);
case RGBWeight::luminance:
return AZ::Vector3(0.3086f, 0.6094f, 0.0820f);
default:
AZ_Assert(false, "color weight value need to be added to new rgbWeight enum");
return AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
}
}
} // namespace ImageProcessing

@ -1,120 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/CubemapSettings.h>
#include <BuilderSettings/MipmapSettings.h>
#include <ImageProcessing/PixelFormats.h>
namespace ImageProcessing
{
//settings for texture process preset
class PresetSettings
{
public:
AZ_TYPE_INFO(PresetSettings, "{935BCE3F-9E76-494E-9408-47C5937D7288}");
AZ_CLASS_ALLOCATOR(PresetSettings, AZ::SystemAllocator, 0);
PresetSettings();
PresetSettings(const PresetSettings& other);
PresetSettings& operator= (const PresetSettings& other);
bool operator== (const PresetSettings& other) const;
static void Reflect(AZ::ReflectContext* context);
//unique id for the preset
AZ::Uuid m_uuid;
PresetName m_name;
//a brief description for the usage of this Preset
AZStd::string m_description;
//misc options
// "rgbweights". specify preset for weighting of R,G,B channels (used by compressor)
RGBWeight m_rgbWeight;
ColorSpace m_srcColorSpace;
ColorSpace m_destColorSpace;
// file masks used for helping select default preset and option preset list in texture property dialog
AZStd::vector<FileMask> m_fileMasks;
// "ser". Whether to enable supress reduce resolution (m_sizeReduceLevel) during loading, 0(default)
bool m_suppressEngineReduce;
//pixel format
EPixelFormat m_pixelFormat;
AZStd::string m_pixelFormatName;
//pixel format for image which only contains alpha channel. this is for if we need to save alpha channel into a seperate image
EPixelFormat m_pixelFormatAlpha;
AZStd::string m_pixelFormatAlphaName;
bool m_discardAlpha;
// Resolution related settings
// "maxtexturesize", upper limit of the resolution of generated textures. It should be a power-of-2 number larger than 1
// resulting texture will be downscaled if its width or height larger than this value
// 0 - no upper resolution limit (default)
unsigned int m_maxTextureSize;
// "mintexturesize", lower limit of the resolution of generated textures.It should be a power-of-2 number larger than 1
// resulting texture will be upscaled if its width or height smaller than this value
// 0 - no lower resolution limit (default)
unsigned int m_minTextureSize;
bool m_isPowerOf2;
//"reduce", 0=no size reduce /1=half resolution /2=quarter resolution, etc"
unsigned int m_sizeReduceLevel;
//settings for cubemap generation. it's null if this preset is not for cubemap.
//"cm" equals 1 to enable cubemap in rc.ini
AZStd::unique_ptr<CubemapSettings> m_cubemapSetting;
//settings for mipmap generation. it's null if this preset disable mipmap.
AZStd::unique_ptr<MipmapSettings> m_mipmapSetting;
//some specific settings
// "colorchart". This is to indicate if need to extract color chart from the image and output the color chart data.
// This is very specific usage for cryEngine. Check ColorChart.cpp for better explaination.
bool m_isColorChart;
//"highpass". Defines which mip level is subtracted when applying the high pass filter
//this is only used for terrain asset. we might remove it later since it can be done with source image directly
AZ::u32 m_highPassMip;
//"glossfromnormals". Bake normal variance into smoothness stored in alpha channel
AZ::u32 m_glossFromNormals;
//"mipnormalize". need normalize the rgb
bool m_isMipRenormalize;
//function to get color's rgb weight in vec3 based on m_rgbWeight enum
//this is useful for squisher compression
AZ::Vector3 GetColorWeight();
//numstreamablemips
AZ::u32 m_numStreamableMips;
//legacy options might be removed later
//"glosslegacydist". If the gloss map use legacy distribution. NW is still using legacy dist
bool m_isLegacyGloss;
//"swizzle". need to be 4 character and each character need to be one of "rgba01"
AZStd::string m_swizzle;
private:
void DeepCopyMembers(const PresetSettings& other);
};
} // namespace ImageProcessing

@ -1,589 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include <BuilderSettings/TextureSettings.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/Utils.h>
#include <BuilderSettings/BuilderSettingManager.h>
#include <ImageLoader/ImageLoaders.h>
namespace ImageProcessing
{
const char* TextureSettings::legacyExtensionName = ".exportsettings";
const char* TextureSettings::modernExtensionName = ".imagesettings";
StringOutcome ParseLegacyTextureSettingString(AZStd::string& key, AZStd::string& value, TextureSettings& textureSettingOut)
{
// Parse only the settings we support for TextureSetting
if ("reduce" == key) // Example: reduce=0
{
int reduce = AzFramework::StringFunc::ToInt(value.c_str());
if (reduce >= 0)
{
textureSettingOut.m_sizeReduceLevel = reduce;
}
}
else if ("M" == key) // Example: M=50,50,0,50,50,50
{
textureSettingOut.m_enableMipmap = true;
AZStd::vector<AZStd::string> mipStringValues;
AzFramework::StringFunc::Tokenize(value.c_str(), mipStringValues, ',');
for (size_t mipIndex = 0; mipIndex < mipStringValues.size(); ++mipIndex)
{
AZ::u32 mipValue = AzFramework::StringFunc::ToInt(mipStringValues[mipIndex].c_str());
textureSettingOut.m_mipAlphaAdjust[mipIndex] = mipValue;
}
}
else if ("ser" == key) // Example: ser=1
{
textureSettingOut.m_suppressEngineReduce = (value == "1");
}
else if ("preset" == key) // Example: preset=NormalsWithSmoothness
{
AZ::Uuid presetUuid = BuilderSettingManager::Instance()->GetPresetIdFromName(value);
// There's a chance the preset name still adheres to legacy preset naming convention (from CryEngine).
const PresetName translation = BuilderSettingManager::Instance()->TranslateLegacyPresetName(value);
AZ::Uuid translatedPresetUuid = BuilderSettingManager::Instance()->GetPresetIdFromName(translation);
if (!presetUuid.IsNull())
{
textureSettingOut.m_preset = presetUuid;
}
else if (!translatedPresetUuid.IsNull())
{
textureSettingOut.m_preset = translatedPresetUuid;
}
else
{
AZ_Error("Image processing", false, "Can't find preset %s", value.c_str());
}
}
else if ("mipgentype" == key) // Example: mipgentype=box
{
if ("box" == value || "average" == value)
{
textureSettingOut.m_mipGenType = MipGenType::box;
}
else if ("gauss" == value)
{
textureSettingOut.m_mipGenType = MipGenType::gaussian;
}
else if ("blackman-harris" == value)
{
textureSettingOut.m_mipGenType = MipGenType::blackmanHarris;
}
else if ("kaiser" == value)
{
textureSettingOut.m_mipGenType = MipGenType::kaiserSinc;
}
else if ("point" == value)
{
textureSettingOut.m_mipGenType = MipGenType::point;
}
else if ("quadric" == value)
{
textureSettingOut.m_mipGenType = MipGenType::quadratic;
}
else if ("triangle" == value)
{
textureSettingOut.m_mipGenType = MipGenType::triangle;
}
}
return STRING_OUTCOME_SUCCESS;
}
TextureSettings::TextureSettings()
: m_preset(0)
, m_sizeReduceLevel(0)
, m_suppressEngineReduce(false)
, m_enableMipmap(true)
, m_maintainAlphaCoverage(false)
, m_mipGenEval(MipGenEvalType::sum)
, m_mipGenType(MipGenType::blackmanHarris)
{
const int defaultMipMapValue = 50;
for (int i = 0; i < s_MaxMipMaps; ++i)
{
m_mipAlphaAdjust.push_back(defaultMipMapValue);
}
}
void TextureSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<TextureSettings>()
->Version(1)
->Field("PresetID", &TextureSettings::m_preset)
->Field("SizeReduceLevel", &TextureSettings::m_sizeReduceLevel)
->Field("EngineReduce", &TextureSettings::m_suppressEngineReduce)
->Field("EnableMipmap", &TextureSettings::m_enableMipmap)
->Field("MaintainAlphaCoverage", &TextureSettings::m_maintainAlphaCoverage)
->Field("MipMapAlphaAdjustments", &TextureSettings::m_mipAlphaAdjust)
->Field("MipMapGenEval", &TextureSettings::m_mipGenEval)
->Field("MipMapGenType", &TextureSettings::m_mipGenType)
->Field("PlatformSpecificOverrides", &TextureSettings::m_platfromOverrides)
->Field("OverridingPlatform", &TextureSettings::m_overridingPlatform);
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<TextureSettings>("Texture Setting", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &TextureSettings::m_mipAlphaAdjust, "Alpha Test Bias", "Multiplies the mipmap's alpha with a scale value that is based on alpha coverage. \
Set the mip 0 to mip 5 values to offset the alpha test values and ensure the mipmap's alpha coverage matches the original image. Specify a value from 0 to 100.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->ElementAttribute(AZ::Edit::UIHandlers::Handler, AZ::Edit::UIHandlers::Slider)
->ElementAttribute(AZ::Edit::Attributes::Min, 0)
->ElementAttribute(AZ::Edit::Attributes::Max, 100)
->ElementAttribute(AZ::Edit::Attributes::Step, 1)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TextureSettings::m_mipGenType, "Filter Method", "")
->EnumAttribute(MipGenType::point, "Point")
->EnumAttribute(MipGenType::box, "Average")
->EnumAttribute(MipGenType::triangle, "Linear")
->EnumAttribute(MipGenType::quadratic, "Bilinear")
->EnumAttribute(MipGenType::gaussian, "Gaussian")
->EnumAttribute(MipGenType::blackmanHarris, "BlackmanHarris")
->EnumAttribute(MipGenType::kaiserSinc, "KaiserSinc")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TextureSettings::m_mipGenEval, "Pixel Sampling Type", "")
->EnumAttribute(MipGenEvalType::max, "Max")
->EnumAttribute(MipGenEvalType::min, "Min")
->EnumAttribute(MipGenEvalType::sum, "Sum")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &TextureSettings::m_maintainAlphaCoverage, "Maintain Alpha Coverage", "Select this option to manually adjust Alpha channel mipmaps.")
;
}
}
}
bool TextureSettings::operator!=(const TextureSettings& other) const
{
return !(*this == other);
}
bool TextureSettings::operator==(const TextureSettings& other) const
{
/////////////////////////////
// Compare Alpha Adjust
/////////////////////////////
bool matchingAlphaTestAdjust = true;
for (AZ::u8 curIndex = 0; curIndex < s_MaxMipMaps; ++curIndex)
{
if (m_mipAlphaAdjust[curIndex] != other.m_mipAlphaAdjust[curIndex])
{
matchingAlphaTestAdjust = false;
break;
}
}
return
matchingAlphaTestAdjust &&
m_preset == other.m_preset &&
m_sizeReduceLevel == other.m_sizeReduceLevel &&
m_suppressEngineReduce == other.m_suppressEngineReduce &&
m_maintainAlphaCoverage == other.m_maintainAlphaCoverage &&
m_mipGenEval == other.m_mipGenEval &&
m_mipGenType == other.m_mipGenType;
}
bool TextureSettings::Equals(const TextureSettings& other, AZ::SerializeContext* serializeContext)
{
/////////////////////////////
// Compare Common Settings
/////////////////////////////
if (*this != other)
{
return false;
}
/////////////////////////////
// Compare Overrides
/////////////////////////////
const MultiplatformTextureSettings selfOverrides = GetMultiplatformTextureSetting(*this, serializeContext);
const MultiplatformTextureSettings otherOverrides = GetMultiplatformTextureSetting(other, serializeContext);
auto selfOverridesIter = selfOverrides.begin();
auto otherOverridesIter = otherOverrides.begin();
while (selfOverridesIter != selfOverrides.end() && otherOverridesIter != otherOverrides.end())
{
if (selfOverridesIter->second != otherOverridesIter->second)
{
return false;
}
otherOverridesIter++;
selfOverridesIter++;
}
AZ_Assert(selfOverridesIter == selfOverrides.end() && otherOverridesIter == otherOverrides.end(), "Both iterators must be at the end by now.")
return true;
}
float TextureSettings::ComputeMIPAlphaOffset(AZ::u32 mip) const
{
if (mip / 2 + 1 >= s_MaxMipMaps)
{
return 0;
}
float fVal = static_cast<float>(m_mipAlphaAdjust[s_MaxMipMaps - 1]);
if (mip / 2 + 1 < s_MaxMipMaps)
{
float fInterpolationSlider1 = static_cast<float>(m_mipAlphaAdjust[mip / 2]);
float fInterpolationSlider2 = static_cast<float>(m_mipAlphaAdjust[mip / 2 + 1]);
fVal = fInterpolationSlider1 + (fInterpolationSlider2 - fInterpolationSlider1) * (mip & 1) * 0.5f;
}
return 0.5f - fVal / 100.0f;
}
void TextureSettings::ApplyPreset(AZ::Uuid presetId)
{
const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(presetId);
if (presetSetting != nullptr)
{
m_sizeReduceLevel = presetSetting->m_sizeReduceLevel;
m_suppressEngineReduce = presetSetting->m_suppressEngineReduce;
if (presetSetting->m_mipmapSetting)
{
m_mipGenType = presetSetting->m_mipmapSetting->m_type;
}
m_preset = presetId;
}
else
{
AZ_Error("Image Processing", false, "Cannot set an invalid preset %s!", presetId.ToString<AZStd::string>().c_str());
}
}
StringOutcome TextureSettings::LoadTextureSetting(const AZStd::string& filepath, TextureSettings& textureSettingPtrOut, AZ::SerializeContext* serializeContext /*= nullptr*/)
{
auto loadedTextureSettingPtr = AZStd::unique_ptr<TextureSettings>(AZ::Utils::LoadObjectFromFile<TextureSettings>(filepath, serializeContext));
if (!loadedTextureSettingPtr)
{
return AZ::Failure(AZStd::string());
}
textureSettingPtrOut = *loadedTextureSettingPtr;
return AZ::Success(AZStd::string());
}
StringOutcome TextureSettings::WriteTextureSetting(const AZStd::string& filepath, TextureSettings& textureSetting, AZ::SerializeContext* serializeContext)
{
if(false == AZ::Utils::SaveObjectToFile<TextureSettings>(filepath, AZ::DataStream::StreamType::ST_XML, &textureSetting, serializeContext))
{
return STRING_OUTCOME_ERROR("Failed to write to file: " + filepath);
}
return STRING_OUTCOME_SUCCESS;
}
StringOutcome TextureSettings::LoadLegacyTextureSetting(const AZStd::string& imagePath, const AZStd::string& contentString, TextureSettings& textureSettingOut, AZ::SerializeContext* serializeContext)
{
AZStd::string trimmedContent = contentString;
AzFramework::StringFunc::TrimWhiteSpace(trimmedContent, true /* leading */, true /* trailing */);
if (trimmedContent.empty())
{
return STRING_OUTCOME_ERROR("Empty legacy texture setting!");
}
AZStd::vector<AZStd::string> settings;
AZStd::vector<AZStd::string*> overrideSettings;
// Each setting begins with a forward-slash.
AzFramework::StringFunc::Tokenize(trimmedContent.c_str(), settings, " /"); // requires space character.
// For each setting pair (field & value)...
for (AZStd::string& settingPair : settings)
{
// Split setting pair into key & value.
AZStd::string key, value;
{
AZStd::vector<AZStd::string> key_value;
AzFramework::StringFunc::Tokenize(settingPair.c_str(), key_value, '=');
if (key_value.size() == 2)
{
key = key_value[0];
value = key_value[1];
}
else
{
return STRING_OUTCOME_ERROR("Invalid format found in legacy texture setting: " + settingPair);
}
}
bool containsPlatformOverrides = !value.empty() && (value[0] == '"' && value[value.length() - 1] == '"');
if (containsPlatformOverrides)
{
// Process platform-specific overrides on the next loop.
overrideSettings.push_back(&settingPair);
continue;
}
// Parse the common settings.
ParseLegacyTextureSettingString(key, value, textureSettingOut);
}
// Some setting file won't assign a proper preset for the image, need to assign a suggested one here
if (textureSettingOut.m_preset.IsNull())
{
textureSettingOut.m_preset = BuilderSettingManager::Instance()->GetSuggestedPreset(imagePath);
}
// Store a temporary settings of all platforms intended to have overrides within this preset.
// We will collate all overrides per-platform to generate PatchData at the end.
MultiplatformTextureSettings overrideCache;
// For each platform-specific override setting pair
for (AZStd::string* overrideSettingPair : overrideSettings)
{
// Split setting pair into key & value.
AZStd::string key, value;
{
AZStd::vector<AZStd::string> key_value;
AzFramework::StringFunc::Tokenize(overrideSettingPair->c_str(), key_value, '=');
if (key_value.size() == 2)
{
key = key_value[0];
value = key_value[1];
}
else
{
return STRING_OUTCOME_ERROR("Invalid format found in legacy texture setting: " + *overrideSettingPair);
}
}
// Chop the surrounding quotation marks.
AzFramework::StringFunc::LChop(value, 1);
AzFramework::StringFunc::RChop(value, 1);
// Split the collection of platforms overrides into entries in a vector.
// Layout: { [platform0],[value0],[platform1],[value1],[platform2],[value2] }
AZStd::vector<AZStd::string> override_platform_value;
AzFramework::StringFunc::Tokenize(value.c_str(), override_platform_value, ",:");
if (override_platform_value.size() % 2 != 0)
{
return STRING_OUTCOME_ERROR("Invalid format found in legacy texture setting: " + value);
}
for (int platformIdx = 0, valueIdx = 1;
valueIdx < override_platform_value.size();
platformIdx += 2, valueIdx = platformIdx + 1)
{
PlatformName& overridePlatform = override_platform_value[platformIdx];
AZStd::string& overrideValue = override_platform_value[valueIdx];
// Insert a copy of the base settings we've parsed from the legacy metafile.
overrideCache.insert(AZStd::pair<PlatformName, TextureSettings>(overridePlatform, textureSettingOut));
ParseLegacyTextureSettingString(key, overrideValue, overrideCache[overridePlatform]);
overrideCache[overridePlatform].m_overridingPlatform = overridePlatform;
overrideCache[overridePlatform].m_platfromOverrides.clear();
}
}
// Store the final result in a temp variable to do a whole-sale copy at the end.
TextureSettings finalResult = textureSettingOut;
// Use the override cache to generate a DataPatch per-platform.
for (auto& overridePair : overrideCache)
{
// Every DataPatch is only a diff between a vanilla common-settings (with no platform-specific overrides) and the specified platform's override.
AZ::DataPatch platformOverridePatch;
platformOverridePatch.Create<TextureSettings, TextureSettings>(&textureSettingOut, &overridePair.second,AZ::DataPatch::FlagsMap(), AZ::DataPatch::FlagsMap(), serializeContext);
finalResult.m_platfromOverrides.insert(AZStd::pair<PlatformName, AZ::DataPatch>(overridePair.first, platformOverridePatch));
}
// Fully overwrite output variable. The only difference should be properly filled-out overrides.
textureSettingOut = finalResult;
return STRING_OUTCOME_SUCCESS;
}
StringOutcome TextureSettings::LoadLegacyTextureSettingFromFile(const AZStd::string& imagePath, const AZStd::string& filepath, TextureSettings& textureSettingOut, AZ::SerializeContext* serializeContext)
{
AZStd::string fileContents;
// Perform file I/O to read the contents of the metafile into the string above.
auto fileIoPtr = AZ::IO::FileIOBase::GetInstance();
AZ::IO::HandleType fileHandle;
fileIoPtr->Open(filepath.c_str(), AZ::IO::OpenMode::ModeRead, fileHandle);
AZ::u64 fileSize = 0;
AZ::u64 bytesRead = 0;
fileIoPtr->Size(fileHandle, fileSize);
char* fileString = new char[fileSize + 1];
fileIoPtr->Read(fileHandle, fileString, fileSize, false, &bytesRead);
fileIoPtr->Close(fileHandle);
fileString[bytesRead] = 0;
fileContents = fileString;
delete[] fileString;
return LoadLegacyTextureSetting(imagePath, fileContents, textureSettingOut, serializeContext);
}
MultiplatformTextureSettings TextureSettings::GenerateDefaultMultiplatformTextureSettings(const AZStd::string& imageFilepath)
{
MultiplatformTextureSettings settings;
PlatformNameList platformsList = BuilderSettingManager::Instance()->GetPlatformList();
AZ::Uuid suggestedPreset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilepath);
if (!suggestedPreset.IsNull())
{
for (PlatformName& platform : platformsList)
{
TextureSettings textureSettings;
textureSettings.ApplyPreset(suggestedPreset);
settings.insert(AZStd::pair<PlatformName, TextureSettings>(platform, textureSettings));
}
}
return settings;
}
StringOutcome TextureSettings::GetPlatformSpecificTextureSetting(const PlatformName& platformName, const TextureSettings& baseTextureSettings,
TextureSettings& textureSettingsOut, AZ::SerializeContext* serializeContext)
{
// Obtain the DataPatch (if platform exists)
auto overrideIter = baseTextureSettings.m_platfromOverrides.find(platformName);
if (overrideIter == baseTextureSettings.m_platfromOverrides.end())
{
return STRING_OUTCOME_ERROR(AZStd::string::format("TextureSettings preset [%s] does not have override for platform [%s]",
baseTextureSettings.m_preset.ToString<AZStd::string>().c_str(), platformName.c_str()));
}
AZ::DataPatch& platformOverride = const_cast<AZ::DataPatch&>(overrideIter->second);
// Update settings instance with override values.
if (platformOverride.IsData())
{
// Apply the AZ::DataPatch to obtain a platform-overridden version of the TextureSettings.
AZStd::unique_ptr<TextureSettings> platformSpecificTextureSettings(platformOverride.Apply(&baseTextureSettings, serializeContext));
AZ_Assert(platformSpecificTextureSettings->m_mipAlphaAdjust.size() == s_MaxMipMaps, "Unexpected m_mipAlphaAdjust size.");
// Adjust overrides data to imply 'platformSpecificTextureSettings' *IS* the override.
platformSpecificTextureSettings->m_platfromOverrides.clear();
platformSpecificTextureSettings->m_overridingPlatform = platformName;
textureSettingsOut = *platformSpecificTextureSettings;
}
else
{
textureSettingsOut = baseTextureSettings;
}
return STRING_OUTCOME_SUCCESS;
}
const MultiplatformTextureSettings TextureSettings::GetMultiplatformTextureSetting(const TextureSettings& textureSettings, AZ::SerializeContext* serializeContext)
{
MultiplatformTextureSettings loadedSettingsReturn;
PlatformNameList platformsList = BuilderSettingManager::Instance()->GetPlatformList();
// Generate MultiplatformTextureSettings based on existing available overrides.
for (const PlatformName& curPlatformName : platformsList)
{
// Start with a copy of the base settings
TextureSettings curPlatformOverride = textureSettings;
if (!GetPlatformSpecificTextureSetting(curPlatformName, textureSettings, curPlatformOverride, serializeContext).IsSuccess())
{
// We have failed to obtain an override. Maintain base settings to indicate zero overrides.
// We still want to designate these TextureSettings as an (empty) override.
curPlatformOverride.m_platfromOverrides.clear();
curPlatformOverride.m_overridingPlatform = curPlatformName;
}
// Add as an entry to the multiplatform texture settings
loadedSettingsReturn.insert(AZStd::pair<PlatformName, TextureSettings>(curPlatformName, curPlatformOverride));
}
// return a copy of the results
return loadedSettingsReturn;
}
const MultiplatformTextureSettings TextureSettings::GetMultiplatformTextureSetting(const AZStd::string& imageFilepath, bool& canOverridePreset, AZ::SerializeContext* serializeContext)
{
TextureSettings loadedTextureSetting;
// Attempt to get metadata filepath from image path.
AZStd::string legacyMetadataFilepath = imageFilepath + legacyExtensionName;
AZStd::string modernMetadataFilepath = imageFilepath + modernExtensionName;
bool hasLegacyMetafile = AZ::IO::SystemFile::Exists(legacyMetadataFilepath.c_str());
bool hasModernMetafile = AZ::IO::SystemFile::Exists(modernMetadataFilepath.c_str());
// If the image has an accompanying metadata...
if(hasModernMetafile)
{
// Parse the metadata file.
if (!LoadTextureSetting(modernMetadataFilepath, loadedTextureSetting, serializeContext).IsSuccess())
{
canOverridePreset = true;
return GenerateDefaultMultiplatformTextureSettings(imageFilepath);
}
}
else if (hasLegacyMetafile)
{
if (!LoadLegacyTextureSettingFromFile(imageFilepath, legacyMetadataFilepath, loadedTextureSetting, serializeContext).IsSuccess())
{
canOverridePreset = true;
return GenerateDefaultMultiplatformTextureSettings(imageFilepath);
}
}
else
{
canOverridePreset = true; // RC could override settings if it was loaded from the image so this is set to
// true regardless of whether settings existed in the texture for compatibility.
// Try to load from image file if it has embedded setting file
AZStd::string embeddedString = LoadEmbeddedSettingFromFile(imageFilepath);
if (!LoadLegacyTextureSetting(imageFilepath, embeddedString, loadedTextureSetting, serializeContext).IsSuccess())
{
// If the texture has neither of legacy/modern meta file nor embedded setting, generate data for a new metadata file.
return GenerateDefaultMultiplatformTextureSettings(imageFilepath);
}
}
// Generate MultiplatformTextureSettings based on the loaded texture setting.
return GetMultiplatformTextureSetting(loadedTextureSetting, serializeContext);
}
StringOutcome TextureSettings::ApplySettings(const TextureSettings& settings, const PlatformName& overridePlatform, AZ::SerializeContext* serializeContext)
{
if (overridePlatform.empty())
{
*this = settings;
}
else
{
AZ::DataPatch newOverride;
if (false == newOverride.Create<TextureSettings, TextureSettings>(this, &settings, AZ::DataPatch::FlagsMap(), AZ::DataPatch::FlagsMap(), serializeContext))
{
return STRING_OUTCOME_ERROR("Failed to create TextureSettings platform override data. See AZ_Error log for details.");
}
m_platfromOverrides[overridePlatform] = newOverride;
}
return STRING_OUTCOME_SUCCESS;
}
}

@ -1,171 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <BuilderSettings/ImageProcessingDefines.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/DataPatch.h>
namespace ImageProcessing
{
class TextureSettings;
typedef AZStd::map<PlatformName, TextureSettings> MultiplatformTextureSettings;
class TextureSettings
{
public:
AZ_TYPE_INFO(TextureSettings, "{CC3ED018-7FF7-4233-AAD8-6D3115FD844A}");
AZ_CLASS_ALLOCATOR(TextureSettings, AZ::SystemAllocator, 0);
TextureSettings();
float ComputeMIPAlphaOffset(AZ::u32 mip) const;
void ApplyPreset(AZ::Uuid presetId);
/**
* Performs a comprehensive comparison between two TextureSettings instances.
* @param Reference to the settings which will be compared.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return True, is both instances are equivalent.
*/
bool Equals(const TextureSettings& other, AZ::SerializeContext* serializeContext = nullptr);
/**
* Applies texture settings to the instance (including overrides). Common settings are applied, unless specific platform is specified.
* @param Reference to the settings which will be applied.
* @param Optional. Applies settings as a platform override if a platform is specified.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return Status outcome result.
*/
StringOutcome ApplySettings(const TextureSettings& settings, const PlatformName& overridePlatform = PlatformName(), AZ::SerializeContext* serializeContext = nullptr);
/**
* Gets platform-specific texture settings obtained from the base settings version of a pre-loaded TextureSettings instance.
* @param Name of platform to get the settings from.
* @param Base TextureSettings which we will get overrides from.
* @param Output TextureSettings which will contain the result of the function.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return Status outcome result.
*/
static StringOutcome GetPlatformSpecificTextureSetting(const PlatformName& platformName, const TextureSettings& baseTextureSettings, TextureSettings& textureSettingsOut, AZ::SerializeContext* serializeContext = nullptr);
static void Reflect(AZ::ReflectContext* context);
/**
* Loads base texture settings obtained from ".exportsettings" file (legacy setting)
* @param ImagePath absolute/relative path of the image file.
* @param FilePath absolute/relative path of the ".exportsettings" file.
* @param Output TextureSettings which contain the result of the function, it may contains platform-specific overrides.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return Status outcome result.
*/
static StringOutcome LoadLegacyTextureSettingFromFile(const AZStd::string& imagePath, const AZStd::string& filepath, TextureSettings& textureSettingOut, AZ::SerializeContext* serializeContext = nullptr);
/**
* Loads base texture settings obtained from a legacy setting string
* @param ImagePath absolute/relative path of the image file.
* @param Content string of the legacy setting to be read.
* @param Output TextureSettings which contain the result of the function, it may contains platform-specific overrides.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return Status outcome result.
*/
static StringOutcome LoadLegacyTextureSetting(const AZStd::string& imagePath, const AZStd::string& contentString, TextureSettings& textureSettingOut, AZ::SerializeContext* serializeContext = nullptr);
/**
* Loads base texture settings obtained from ".imagesettings" file (modern setting)
* @param FilePath absolute/relative path of the ".imagesettings" file.
* @param Output TextureSettings which contain the result of the function, it may contains platform-specific overrides.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return Status outcome result.
*/
static StringOutcome LoadTextureSetting(const AZStd::string& filepath, TextureSettings& textureSettingPtrOut, AZ::SerializeContext* serializeContext = nullptr);
/**
* Writes base texture settings to a ".imagesettings" file (modern setting)
* @param FilePath absolute/relative path of the ".imagesettings" file.
* @param TextureSetting to be written on the disk, it may contains platform-specific overrides.
* @param Optional. Serialize context. Will use global context if none is provided.
* @return Status outcome result.
*/
static StringOutcome WriteTextureSetting(const AZStd::string& filepath, TextureSettings& textureSetting, AZ::SerializeContext* serializeContext = nullptr);
// Generates a MultiplatformTextureSettings collection with default texture settings for all
static MultiplatformTextureSettings GenerateDefaultMultiplatformTextureSettings(const AZStd::string& imageFilepath);
/**
* Generates a TextureSetting instance of a particular image file for each supported platform.
* @param filepath - A path to the texture file.
* @param canOverridePreset - Returns whether the preset can be overriden. Will return false if the preset was selecting from a settings file created by the user.
* @param serializeContext - Optional. Serialize context used for reflection/serialization
* @return - The collection of TextureSetting instances. If error occurs, a default MultiplatformTextureSettings is returned (see GenerateDefaultMultiplatformTextureSettings()).
*/
static const MultiplatformTextureSettings GetMultiplatformTextureSetting(const AZStd::string& filepath, bool& canOverridePreset, AZ::SerializeContext* serializeContext = nullptr);
/**
* Generates a TextureSetting instance of a particular image file for each supported platform.
* @param textureSettings - A reference to an already-loaded texture settings.
* @param serializeContext - Optional. Serialize context used for reflection/serialization
* @return - The collection of TextureSetting instances. If error occurs, a default MultiplatformTextureSettings is returned (see GenerateDefaultMultiplatformTextureSettings()).
*/
static const MultiplatformTextureSettings GetMultiplatformTextureSetting(const TextureSettings& textureSettings, AZ::SerializeContext* serializeContext = nullptr);
static const char* legacyExtensionName;
static const char* modernExtensionName;
static const size_t s_MaxMipMaps = 6;
// uuid of selected preset for this texture
AZ::Uuid m_preset;
// texture size reduce level. the value of this variable will override the same variable in PresetSettings
unsigned int m_sizeReduceLevel;
// "ser". Whether to enable supress reduce resolution (m_sizeReduceLevel) during loading, 0(default)
// the value of this variable will override the same variable in PresetSettings
bool m_suppressEngineReduce;
//enable generate mipmap or not
bool m_enableMipmap;
//"mc". not used in rc.ini. experiemental
//maybe relate to http://the-witness.net/news/2010/09/computing-alpha-mipmaps/
bool m_maintainAlphaCoverage;
// "M", adjust mipalpha, 0..50=normal..100. associate with ComputeMIPAlphaOffset function
// only useful if m_maintainAlphaCoverage set to true.
// This data type MUST be an AZStd::vector, even though we treat is as a fixed array. This is due to a limitation
// during AZ::DataPatch serialization, where an element is allocated one by one while extending the container..
AZStd::vector<AZ::u32> m_mipAlphaAdjust;
MipGenEvalType m_mipGenEval;
MipGenType m_mipGenType;
private:
// Platform overrides in form of DataPatch. Each entry is a patch for a specified platform.
// This map is used to generate TextureSettings with overridden values. The map is empty if
// the instance is for platform-specific settings.
AZStd::map<PlatformName, AZ::DataPatch> m_platfromOverrides;
// The platform which these settings override.
// Blank if the instance is for common settings.
PlatformName m_overridingPlatform;
// Comparison operators only compare the base settings, they do not compare overrides.
// For a comprehensive equality comparison, use Equals() function.
bool operator==(const TextureSettings& other) const;
bool operator!=(const TextureSettings& other) const;
};
} // namespace ImageProcessing

@ -1,387 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing/ImageObject.h>
#include <Processing/ImageToProcess.h>
#include <Processing/PixelFormatInfo.h>
#include <Compressors/CTSquisher.h>
namespace ImageProcessing
{
struct CrySquisherCallbackUserData
{
IImageObjectPtr m_pImageObject;
AZ::u8* m_dstMem;
AZ::u32 m_dstOffset;
};
// callbacks for the CryTextureSquisher
void CrySquisherOutputCallback(const CryTextureSquisher::CompressorParameters& compress, const void* data, AZ::u32 size, AZ::u32 oy, AZ::u32 ox)
{
CrySquisherCallbackUserData* const pUserData = (CrySquisherCallbackUserData*)compress.userPtr;
AZ::u32 stride = (compress.width + 3) >> 2;
AZ::u32 blocks = (compress.height + 3) >> 2;
memcpy(pUserData->m_dstMem + size * (stride * oy + ox), data, size);
pUserData->m_dstOffset = size * (stride * blocks);
}
void CrySquisherInputCallback(const CryTextureSquisher::DecompressorParameters& decompress, void* data, AZ::u32 size, AZ::u32 oy, AZ::u32 ox)
{
CrySquisherCallbackUserData* const pUserData = (CrySquisherCallbackUserData*)decompress.userPtr;
AZ::u32 stride = (decompress.width + 3) >> 2;
AZ::u32 blocks = (decompress.height + 3) >> 2;
//assert(CPixelFormats::GetPixelFormatInfo(pUserData->m_pImageObject->GetPixelFormat())->bCompressed);
memcpy(data, pUserData->m_dstMem + size * (stride * oy + ox), size);
pUserData->m_dstOffset = size * (stride * blocks);
}
CryTextureSquisher::ECodingPreset CTSquisher::GetCompressPreset(EPixelFormat compressFmt, EPixelFormat uncompressFmt)
{
CryTextureSquisher::ECodingPreset preset = CryTextureSquisher::eCompressorPreset_Num;
switch (compressFmt)
{
case ePixelFormat_BC1:
preset = CryTextureSquisher::eCompressorPreset_BC1U;
break;
case ePixelFormat_BC1a:
preset = CryTextureSquisher::eCompressorPreset_BC1Ua;
break;
case ePixelFormat_BC3:
preset = CryTextureSquisher::eCompressorPreset_BC3U;
break;
case ePixelFormat_BC3t:
preset = CryTextureSquisher::eCompressorPreset_BC3Ut;
break;
case ePixelFormat_BC4:
preset = (CPixelFormats::GetInstance().IsFormatSingleChannel(uncompressFmt)
? CryTextureSquisher::eCompressorPreset_BC4Ua // a-channel
: CryTextureSquisher::eCompressorPreset_BC4U); // r-channel
break;
case ePixelFormat_BC4s:
preset = (CPixelFormats::GetInstance().IsFormatSingleChannel(uncompressFmt)
? CryTextureSquisher::eCompressorPreset_BC4Sa // a-channel
: CryTextureSquisher::eCompressorPreset_BC4S); // r-channel
break;
case ePixelFormat_BC5:
preset = CryTextureSquisher::eCompressorPreset_BC5Un;
break;
case ePixelFormat_BC5s:
preset = CryTextureSquisher::eCompressorPreset_BC5Sn;
break;
case ePixelFormat_BC6UH:
preset = CryTextureSquisher::eCompressorPreset_BC6UH;
break;
case ePixelFormat_BC7:
preset = CryTextureSquisher::eCompressorPreset_BC7U;
break;
case ePixelFormat_BC7t:
preset = CryTextureSquisher::eCompressorPreset_BC7Ut;
break;
default:
AZ_Assert(false, "%s: Unexpected pixel format (in compressing an image). Inform an RC programmer.", __FUNCTION__);
}
return preset;
}
bool CTSquisher::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_BC1:
case ePixelFormat_BC1a:
case ePixelFormat_BC3:
case ePixelFormat_BC3t:
case ePixelFormat_BC4:
case ePixelFormat_BC4s:
case ePixelFormat_BC5:
case ePixelFormat_BC5s:
case ePixelFormat_BC6UH:
case ePixelFormat_BC7:
case ePixelFormat_BC7t:
return true;
default:
return false;
}
}
bool CTSquisher::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_R8:
case ePixelFormat_A8:
case ePixelFormat_R8G8B8A8:
case ePixelFormat_R8G8B8X8:
case ePixelFormat_R32F:
case ePixelFormat_R32G32B32A32F:
return true;
default:
return false;
}
}
bool CTSquisher::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
{
return true;
}
EPixelFormat CTSquisher::GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt)
{
//special cases
if (compressedfmt == ePixelFormat_BC6UH || compressedfmt == ePixelFormat_BC5 || compressedfmt == ePixelFormat_BC5s)
{
return ePixelFormat_R32G32B32A32F;
}
if (IsUncompressedPixelFormatSupported(uncompressedfmt))
{
return uncompressedfmt;
}
//for fmt dont support, convert to supported uncompressed formats: ePixelFormat_A8, ePixelFormat_R8, ePixelFormat_A8R8G8B8,
// ePixelFormat_X8R8G8B8, ePixelFormat_R32F, ePixelFormat_A32B32G32R32F
switch (uncompressedfmt)
{
case ePixelFormat_R8G8:
case ePixelFormat_R16G16:
return ePixelFormat_R8G8B8X8;
case ePixelFormat_R16:
return ePixelFormat_R8;
case ePixelFormat_R16G16B16A16:
case ePixelFormat_B8G8R8A8:
return ePixelFormat_R8G8B8A8;
case ePixelFormat_R9G9B9E5:
case ePixelFormat_R32G32F:
case ePixelFormat_R16G16B16A16F:
case ePixelFormat_R16G16F:
return ePixelFormat_R32G32B32A32F;
case ePixelFormat_R16F:
return ePixelFormat_R32F;
default:
//this shouldn't happen. but we could handle it with uncompressed data anyway
if (CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(uncompressedfmt))
{
return ePixelFormat_R8G8B8X8;
}
else
{
return ePixelFormat_R8G8B8A8;
}
}
}
IImageObjectPtr CTSquisher::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst)
{
// Decompressing
// the output pixel format could only have one channel or four channels. need to find out more
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be compressed and dst format need to uncompressed.
if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
//clear the dstImage to (0, 0, 0, 1) since some compression format only write to certain channels
dstImage->ClearColor(0, 0, 0, 1);
//for each mipmap
const AZ::u32 mipCount = srcImage->GetMipCount();
for (AZ::u32 dwMip = 0; dwMip < mipCount; ++dwMip)
{
const AZ::u32 dwLocalWidth = srcImage->GetWidth(dwMip);
const AZ::u32 dwLocalHeight = srcImage->GetHeight(dwMip);
AZ::u8* pSrcMem;
AZ::u32 dwSrcPitch;
srcImage->GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
AZ::u8* pDstMem;
AZ::u32 dwDstPitch;
dstImage->GetImagePointer(dwMip, pDstMem, dwDstPitch);
CrySquisherCallbackUserData userData;
userData.m_pImageObject = srcImage;
userData.m_dstOffset = 0;
userData.m_dstMem = pSrcMem;
CryTextureSquisher::DecompressorParameters decompress;
decompress.dstBuffer = pDstMem;
decompress.width = dwLocalWidth;
decompress.height = dwLocalHeight;
decompress.pitch = dwDstPitch;
decompress.dstType = (CPixelFormats::GetInstance().IsFormatFloatingPoint(fmtDst, true) ?
CryTextureSquisher::eBufferType_ufloat : CryTextureSquisher::eBufferType_uint8);
if (CPixelFormats::GetInstance().IsFormatSigned(fmtSrc))
{
decompress.dstType = (decompress.dstType == CryTextureSquisher::eBufferType_ufloat ?
CryTextureSquisher::eBufferType_sfloat : CryTextureSquisher::eBufferType_sint8);
}
decompress.userPtr = &userData;
decompress.userInputFunction = CrySquisherInputCallback;
decompress.preset = GetCompressPreset(fmtSrc, fmtDst);
CryTextureSquisher::Decompress(decompress);
}
// CTsquish operates on native normal vectors when floating-point
// buffers are used. Apply bias and scale when returning a normal-map.
if (fmtSrc == ePixelFormat_BC5 || fmtSrc == ePixelFormat_BC5s)
{
if (fmtDst == ePixelFormat_R32G32B32A32F)
{
//conver from [-1, 1] to [0, 1]. And set alpha to 1.
dstImage->ScaleAndBiasChannels(0, 100,
AZ::Vector4(0.5f, 0.5f, 0.5f, 0.0f),
AZ::Vector4(0.5f, 0.5f, 0.5f, 1.0f));
}
}
return dstImage;
}
///////////////////////////////////////////////////////////////////////////////////
IImageObjectPtr CTSquisher::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst,
const CompressOption *compressOption)
{
// Compressing
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be uncompressed and dst format need to compressed.
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
//passing compress option
ICompressor::EQuality quality = ICompressor::eQuality_Normal;
AZ::Vector3 weights = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
if (compressOption)
{
quality = compressOption->compressQuality;
weights = compressOption->rgbWeight;
}
//do some clamp for float
if (fmtSrc == ePixelFormat_R32G32B32A32F)
{
const uint32 nMips = srcImage->GetMipCount();
// NOTES:
// - all incoming images are unsigned, even normal maps
// - all mipmaps of incoming images can contain out-of-range values from mipmap filtering
// - 3Dc/BC5 is synonymous with "is a normal map" because they are not tagged explicitly as such
if (fmtDst == ePixelFormat_BC5 || fmtDst == ePixelFormat_BC5s)
{
srcImage->ScaleAndBiasChannels(0, nMips,
AZ::Vector4(2.0f, 2.0f, 2.0f, 1.0f),
AZ::Vector4(-1.0f, -1.0f, -1.0f, 0.0f));
srcImage->ClampChannels(0, nMips,
AZ::Vector4(-1.0f, -1.0f, -1.0f, -1.0f),
AZ::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
}
else if (fmtDst == ePixelFormat_BC6UH)
{
srcImage->ClampChannels(0, nMips,
AZ::Vector4(0.0f, 0.0f, 0.0f, 0.0f),
AZ::Vector4(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX));
}
else
{
srcImage->ClampChannels(0, nMips,
AZ::Vector4(0.0f, 0.0f, 0.0f, 0.0f),
AZ::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
}
}
const uint32 mipCount = dstImage->GetMipCount();
for (uint32 dwMip = 0; dwMip < mipCount; ++dwMip)
{
uint32 dwLocalWidth = srcImage->GetWidth(dwMip);
uint32 dwLocalHeight = srcImage->GetHeight(dwMip);
uint8* pSrcMem;
uint32 dwSrcPitch;
srcImage->GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
uint8* pDstMem;
uint32 dwDstPitch;
dstImage->GetImagePointer(dwMip, pDstMem, dwDstPitch);
{
CrySquisherCallbackUserData userData;
userData.m_pImageObject = dstImage;
userData.m_dstOffset = 0;
userData.m_dstMem = pDstMem;
CryTextureSquisher::CompressorParameters compress;
compress.srcBuffer = pSrcMem;
compress.width = dwLocalWidth;
compress.height = dwLocalHeight;
compress.pitch = dwSrcPitch;
compress.srcType = (CPixelFormats::GetInstance().IsFormatFloatingPoint(fmtSrc, true) ?
CryTextureSquisher::eBufferType_ufloat : CryTextureSquisher::eBufferType_uint8);
if (CPixelFormats::GetInstance().IsFormatSigned(fmtDst))
{
compress.srcType = (compress.srcType == CryTextureSquisher::eBufferType_ufloat ?
CryTextureSquisher::eBufferType_sfloat : CryTextureSquisher::eBufferType_sint8);
}
const AZ::Vector3 uniform = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
compress.weights[0] = weights.GetX();
compress.weights[1] = weights.GetY();
compress.weights[2] = weights.GetZ();
compress.perceptual =
(compress.weights[0] != uniform.GetX()) ||
(compress.weights[1] != uniform.GetY()) ||
(compress.weights[2] != uniform.GetZ());
compress.quality =
(quality == eQuality_Preview ? CryTextureSquisher::eQualityProfile_Low :
(quality == eQuality_Fast ? CryTextureSquisher::eQualityProfile_Low :
(quality == eQuality_Slow ? CryTextureSquisher::eQualityProfile_High :
CryTextureSquisher::eQualityProfile_Medium)));
compress.userPtr = &userData;
compress.userOutputFunction = CrySquisherOutputCallback;
compress.preset = GetCompressPreset(fmtDst, fmtSrc);
CryTextureSquisher::Compress(compress);
}
} // for: all mips
return dstImage;
}
} //namespace ImageProcessing

@ -1,38 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Compressors/CryTextureSquisher/CryTextureSquisher.h>
#include <Compressors/Compressor.h>
namespace ImageProcessing
{
//Cry Texture Squisher for all the BC compressions
class CTSquisher : public ICompressor
{
public:
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
static bool DoesSupportDecompress(EPixelFormat fmtDst);
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption *compressOption) override;
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) override;
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) override;
private:
static CryTextureSquisher::ECodingPreset GetCompressPreset(EPixelFormat compressFmt, EPixelFormat uncompressFmt);
};
} // namespace ImageProcessing

@ -1,58 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <Compressors/CTSquisher.h>
#include <Compressors/PVRTC.h>
#include <Compressors/ETC2.h>
namespace ImageProcessing
{
ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, bool isCompressing)
{
if (CTSquisher::IsCompressedPixelFormatSupported(fmt))
{
if (isCompressing || (!isCompressing && CTSquisher::DoesSupportDecompress(fmt)))
{
return ICompressorPtr(new CTSquisher());
}
}
// Both ETC2Compressor and PVRTCCompressor can process ETC formats
// According to Mobile team, Etc2Com is faster than PVRTexLib, so we check with ETC2Compressor before PVRTCCompressor
// Note: with the test I have done, I found out it cost similar time for both Etc2Com and PVRTexLib to compress
// a 2048x2048 test texture to EAC_R11 and EAC_RG11. It was around 7 minutes for EAC_R11 and 14 minutes for EAC_RG11
if (ETC2Compressor::IsCompressedPixelFormatSupported(fmt))
{
if (isCompressing || (!isCompressing && ETC2Compressor::DoesSupportDecompress(fmt)))
{
return ICompressorPtr(new ETC2Compressor());
}
}
if (PVRTCCompressor::IsCompressedPixelFormatSupported(fmt))
{
if (isCompressing || (!isCompressing && PVRTCCompressor::DoesSupportDecompress(fmt)))
{
return ICompressorPtr(new PVRTCCompressor());
}
}
return nullptr;
}
ICompressor::~ICompressor()
{
}
} // namespace ImageProcessing

@ -1,56 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <ImageProcessing/PixelFormats.h>
#include <ImageProcessing/ImageObject.h>
namespace ImageProcessing
{
class ICompressor;
typedef AZStd::shared_ptr<ICompressor> ICompressorPtr;
//the interface base class for any compressors which can decompress/compress image with compressed pixel format
class ICompressor
{
public:
enum EQuality
{
eQuality_Preview, // for the 256x256 preview only
eQuality_Fast,
eQuality_Normal,
eQuality_Slow,
};
//some extra information required for different compressors.
//keep is a simple structure for now.
struct CompressOption
{
EQuality compressQuality = eQuality_Normal;
//required for CTSquisher
AZ::Vector3 rgbWeight = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
};
public:
//compress the source image to desired compressed pixel format
virtual IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption *compressOption) = 0;
virtual IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) = 0;
virtual EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) = 0;
//find compressor for specified compressed pixel format. isCompressing to indicate if it's for compressing or decompressing
static ICompressorPtr FindCompressor(EPixelFormat fmt, bool isCompressing);
virtual ~ICompressor() = 0;
};
} // namespace ImageProcessing

@ -1,297 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <ImageProcessing_precompiled.h>
#include <AzCore/Math/MathUtils.h>
#include "ColorBlockRGBA4x4c.h"
namespace ImageProcessing
{
static_assert(sizeof(int) == 4, "Expected size of int to be 4 bytes!");
void ColorBlockRGBA4x4c::setRGBA8(const void* imgBGRA8, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgBGRA8, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(ColorRGBA8), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA8* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
const uint8* const pSrc = ((const uint8*)imgBGRA8) + (pitch * (y + row)) + (x * sizeof(ColorRGBA8));
ColorRGBA8* const pDst = &m_color[row << 2];
pDst[0].setRGBA(&pSrc[0 * sizeof(ColorRGBA8) / sizeof(*pSrc)]);
pDst[1].setRGBA(&pSrc[1 * sizeof(ColorRGBA8) / sizeof(*pSrc)]);
pDst[2].setRGBA(&pSrc[2 * sizeof(ColorRGBA8) / sizeof(*pSrc)]);
pDst[3].setRGBA(&pSrc[3 * sizeof(ColorRGBA8) / sizeof(*pSrc)]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
const uint8* const pSrc = ((const uint8*)imgBGRA8) + pitch * (y + by);
ColorRGBA8* pDst = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pDst[col].setRGBA(&pSrc[(x + bx) * sizeof(ColorRGBA8) / sizeof(*pSrc)]);
}
}
}
}
void ColorBlockRGBA4x4c::getRGBA8(void* imgRGBA8, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgRGBA8, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(ColorRGBA8), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA8* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
uint8* const pDst = ((uint8*)imgRGBA8) + (pitch * (y + row)) + (x * sizeof(ColorRGBA8));
const ColorRGBA8* const pSrc = &m_color[row << 2];
pSrc[0].getRGBA(&pDst[0 * sizeof(ColorRGBA8) / sizeof(*pDst)]);
pSrc[1].getRGBA(&pDst[1 * sizeof(ColorRGBA8) / sizeof(*pDst)]);
pSrc[2].getRGBA(&pDst[2 * sizeof(ColorRGBA8) / sizeof(*pDst)]);
pSrc[3].getRGBA(&pDst[3 * sizeof(ColorRGBA8) / sizeof(*pDst)]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
uint8* const pDst = ((uint8*)imgRGBA8) + pitch * (y + by);
const ColorRGBA8* const pSrc = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pSrc[col].getRGBA(&pDst[(x + bx) * sizeof(ColorRGBA8) / sizeof(*pSrc)]);
}
}
}
}
void ColorBlockRGBA4x4c::setA8(const void* imgA8, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgA8, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(uint8), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA8* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
const uint8* const pSrc = ((const uint8*)imgA8) + (pitch * (y + row)) + (x * sizeof(uint8));
ColorRGBA8* const pDst = &m_color[row << 2];
pDst[0].setRGBA(0, 0, 0, pSrc[0]);
pDst[1].setRGBA(0, 0, 0, pSrc[1]);
pDst[2].setRGBA(0, 0, 0, pSrc[2]);
pDst[3].setRGBA(0, 0, 0, pSrc[3]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
const uint8* const pSrc = ((const uint8*)imgA8) + pitch * (y + by);
ColorRGBA8* pDst = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pDst[col].setRGBA(0, 0, 0, pSrc[x + bx]);
}
}
}
}
void ColorBlockRGBA4x4c::getA8(void* imgA8, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgA8, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(uint8), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA8* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
uint8* const pDst = ((uint8*)imgA8) + (pitch * (y + row)) + (x * sizeof(uint8));
uint8 r, g, b;
const ColorRGBA8* const pSrc = &m_color[row << 2];
pSrc[0].getRGBA(r, g, b, pDst[0]);
pSrc[1].getRGBA(r, g, b, pDst[1]);
pSrc[2].getRGBA(r, g, b, pDst[2]);
pSrc[3].getRGBA(r, g, b, pDst[3]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
uint8* const pDst = ((uint8*)imgA8) + pitch * (y + by);
uint8 r, g, b;
const ColorRGBA8* const pSrc = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pSrc[col].getRGBA(r, g, b, pDst[x + bx]);
}
}
}
}
bool ColorBlockRGBA4x4c::isSingleColorIgnoringAlpha() const
{
for (unsigned int i = 1; i < COLOR_COUNT; ++i)
{
if ((m_color[0].b != m_color[i].b) ||
(m_color[0].g != m_color[i].g) ||
(m_color[0].r != m_color[i].r))
{
return false;
}
}
return true;
}
} // namespace ImageProcessing

@ -1,62 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "ColorTypes.h"
namespace ImageProcessing
{
// Uncompressed 4x4 color block of 8bit integers.
struct ColorBlockRGBA4x4c
{
ColorBlockRGBA4x4c()
{
}
void setRGBA8(const void* imgBGRA8, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void getRGBA8(void* imgBGRA8, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void setA8(const void* imgA8, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void getA8(void* imgA8, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
bool isSingleColorIgnoringAlpha() const;
const ColorRGBA8* colors() const
{
return m_color;
}
ColorRGBA8* colors()
{
return m_color;
}
ColorRGBA8 color(unsigned int i) const
{
return m_color[i];
}
ColorRGBA8& color(unsigned int i)
{
return m_color[i];
}
private:
static const unsigned int COLOR_COUNT = 4 * 4;
ColorRGBA8 m_color[COLOR_COUNT];
};
} // namespace ImageProcessing

@ -1,282 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <ImageProcessing_precompiled.h>
#include <AzCore/Math/MathUtils.h>
#include "ColorBlockRGBA4x4f.h"
namespace ImageProcessing
{
static_assert(sizeof(int) == 4, "Expected size of int to be 4 bytes!");
void ColorBlockRGBA4x4f::setRGBAf(const void* imgRGBAf, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgRGBAf, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(ColorRGBAf), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorRGBAf* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
const float* const pSrc = (const float*)(((const uint8*)imgRGBAf) + (pitch * (y + row)) + (x * sizeof(ColorRGBAf)));
ColorRGBAf* const pDst = &m_color[row << 2];
pDst[0].setRGBA(&pSrc[0 * sizeof(ColorRGBAf) / sizeof(*pSrc)]);
pDst[1].setRGBA(&pSrc[1 * sizeof(ColorRGBAf) / sizeof(*pSrc)]);
pDst[2].setRGBA(&pSrc[2 * sizeof(ColorRGBAf) / sizeof(*pSrc)]);
pDst[3].setRGBA(&pSrc[3 * sizeof(ColorRGBAf) / sizeof(*pSrc)]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
const float* const pSrc = (const float*)(((const uint8*)imgRGBAf) + pitch * (y + by));
ColorRGBAf* pDst = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pDst[col].setRGBA(&pSrc[(x + bx) * sizeof(ColorRGBAf) / sizeof(*pSrc)]);
}
}
}
}
void ColorBlockRGBA4x4f::getRGBAf(void* imgRGBAf, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgRGBAf, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(ColorRGBAf), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorRGBAf* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
float* const pDst = (float*)(((uint8*)imgRGBAf) + (pitch * (y + row)) + (x * sizeof(ColorRGBAf)));
const ColorRGBAf* const pSrc = &m_color[row << 2];
pSrc[0].getRGBA(&pDst[0 * sizeof(ColorRGBAf) / sizeof(*pDst)]);
pSrc[1].getRGBA(&pDst[1 * sizeof(ColorRGBAf) / sizeof(*pDst)]);
pSrc[2].getRGBA(&pDst[2 * sizeof(ColorRGBAf) / sizeof(*pDst)]);
pSrc[3].getRGBA(&pDst[3 * sizeof(ColorRGBAf) / sizeof(*pDst)]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
float* const pDst = (float*)(((uint8*)imgRGBAf) + pitch * (y + by));
const ColorRGBAf* const pSrc = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pSrc[col].getRGBA(&pDst[(x + bx) * sizeof(ColorRGBAf) / sizeof(*pDst)]);
}
}
}
}
void ColorBlockRGBA4x4f::setAf(const void* imgAf, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgAf, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(float), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorRGBAf* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
const float* const pSrc = (const float*)(((const uint8*)imgAf) + (pitch * (y + row)) + (x * sizeof(float)));
ColorRGBAf* const pDst = &m_color[row << 2];
pDst[0].setRGBA(0, 0, 0, pSrc[0]);
pDst[1].setRGBA(0, 0, 0, pSrc[1]);
pDst[2].setRGBA(0, 0, 0, pSrc[2]);
pDst[3].setRGBA(0, 0, 0, pSrc[3]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
const float* const pSrc = (const float*)(((const uint8*)imgAf) + (pitch * (y + by)));
ColorRGBAf* pDst = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pDst[col].setRGBA(0, 0, 0, pSrc[x + bx]);
}
}
}
}
void ColorBlockRGBA4x4f::getAf(void* imgAf, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgAf, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(float), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorRGBAf* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
float* const pDst = (float*)(((uint8*)imgAf) + (pitch * (y + row)) + (x * sizeof(float)));
float r, g, b;
const ColorRGBAf* const pSrc = &m_color[row << 2];
pSrc[0].getRGBA(r, g, b, pDst[0]);
pSrc[1].getRGBA(r, g, b, pDst[1]);
pSrc[2].getRGBA(r, g, b, pDst[2]);
pSrc[3].getRGBA(r, g, b, pDst[3]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
float* const pDst = (float*)(((uint8*)imgAf) + pitch * (y + by));
float r, g, b;
const ColorRGBAf* const pSrc = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pSrc[col].getRGBA(r, g, b, pDst[x + bx]);
}
}
}
}
} // namespace ImageProcessing

@ -1,59 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "ColorTypes.h"
namespace ImageProcessing
{
// Uncompressed 4x4 color block of single precision floating points.
struct ColorBlockRGBA4x4f
{
ColorBlockRGBA4x4f()
{
}
void setRGBAf(const void* imgARGBf, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void getRGBAf(void* imgARGBf, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void setAf(const void* imgAf, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void getAf(void* imgAf, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
const ColorRGBAf* colors() const
{
return m_color;
}
ColorRGBAf* colors()
{
return m_color;
}
ColorRGBAf color(unsigned int i) const
{
return m_color[i];
}
ColorRGBAf& color(unsigned int i)
{
return m_color[i];
}
private:
static const unsigned int COLOR_COUNT = 4 * 4;
ColorRGBAf m_color[COLOR_COUNT];
};
} // namespace ImageProcessing

@ -1,297 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <ImageProcessing_precompiled.h>
#include <AzCore/Math/MathUtils.h>
#include "ColorBlockRGBA4x4s.h"
namespace ImageProcessing
{
static_assert(sizeof(int) == 4, "Expected size of int to be 4 bytes!");
void ColorBlockRGBA4x4s::setRGBA16(const void* imgBGRA16, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgBGRA16, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(ColorRGBA16), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA16* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
const uint16* const pSrc = (const uint16*)(((const uint8*)imgBGRA16) + (pitch * (y + row)) + (x * sizeof(ColorRGBA16)));
ColorRGBA16* const pDst = &m_color[row << 2];
pDst[0].setRGBA(&pSrc[0 * sizeof(ColorRGBA16) / sizeof(*pSrc)]);
pDst[1].setRGBA(&pSrc[1 * sizeof(ColorRGBA16) / sizeof(*pSrc)]);
pDst[2].setRGBA(&pSrc[2 * sizeof(ColorRGBA16) / sizeof(*pSrc)]);
pDst[3].setRGBA(&pSrc[3 * sizeof(ColorRGBA16) / sizeof(*pSrc)]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
const uint16* const pSrc = (const uint16*)(((const uint8*)imgBGRA16) + pitch * (y + by));
ColorRGBA16* pDst = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pDst[col].setRGBA(&pSrc[(x + bx) * sizeof(ColorRGBA16) / sizeof(*pSrc)]);
}
}
}
}
void ColorBlockRGBA4x4s::getRGBA16(void* imgBGRA16, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgBGRA16, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(ColorRGBA16), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA16* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
uint16* const pDst = (uint16*)(((uint8*)imgBGRA16) + (pitch * (y + row)) + (x * sizeof(ColorRGBA16)));
const ColorRGBA16* const pSrc = &m_color[row << 2];
pSrc[0].getRGBA(&pDst[0 * sizeof(ColorRGBA16) / sizeof(*pDst)]);
pSrc[1].getRGBA(&pDst[1 * sizeof(ColorRGBA16) / sizeof(*pDst)]);
pSrc[2].getRGBA(&pDst[2 * sizeof(ColorRGBA16) / sizeof(*pDst)]);
pSrc[3].getRGBA(&pDst[3 * sizeof(ColorRGBA16) / sizeof(*pDst)]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
uint16* const pDst = (uint16*)(((uint8*)imgBGRA16) + pitch * (y + by));
const ColorRGBA16* const pSrc = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pSrc[col].getRGBA(&pDst[(x + bx) * sizeof(ColorRGBA16) / sizeof(*pSrc)]);
}
}
}
}
void ColorBlockRGBA4x4s::setA16(const void* imgA16, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgA16, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(uint8), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA16* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
const uint16* const pSrc = (const uint16*)(((const uint8*)imgA16) + (pitch * (y + row)) + (x * sizeof(uint16)));
ColorRGBA16* const pDst = &m_color[row << 2];
pDst[0].setRGBA(0, 0, 0, pSrc[0]);
pDst[1].setRGBA(0, 0, 0, pSrc[1]);
pDst[2].setRGBA(0, 0, 0, pSrc[2]);
pDst[3].setRGBA(0, 0, 0, pSrc[3]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
const uint16* const pSrc = (const uint16*)(((const uint8*)imgA16) + pitch * (y + by));
ColorRGBA16* pDst = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pDst[col].setRGBA(0, 0, 0, pSrc[x + bx]);
}
}
}
}
void ColorBlockRGBA4x4s::getA16(void* imgA16, unsigned int const width, unsigned int const height, unsigned int const pitch, unsigned int x, unsigned int y)
{
AZ_Assert(imgA16, "%s: Unexpected image pointer", __FUNCTION__);
AZ_Assert((width & 3) == 0, "%s: Unexpected image width", __FUNCTION__);
AZ_Assert((height & 3) == 0, "%s: Unexpected image height", __FUNCTION__);
AZ_Assert(pitch >= width * sizeof(uint8), "%s: Unexpected image pitch", __FUNCTION__);
AZ_Assert(x < width, "%s: Unexpected pixel position x", __FUNCTION__);
AZ_Assert(y < height, "%s: Unexpected pixel position y", __FUNCTION__);
const unsigned int bw = AZ::GetMin(width - x, 4U);
const unsigned int bh = AZ::GetMin(height - y, 4U);
// note: it's allowed for source data to be not aligned to 4 byte boundary
// (so, we cannot cast source data pointer to ColorBGRA16* in code below)
if ((bw == 4) && (bh == 4))
{
for (unsigned int row = 0; row < 4; ++row)
{
uint16* const pDst = (uint16*)(((uint8*)imgA16) + (pitch * (y + row)) + (x * sizeof(uint16)));
uint16 r, g, b;
const ColorRGBA16* const pSrc = &m_color[row << 2];
pSrc[0].getRGBA(r, g, b, pDst[0]);
pSrc[1].getRGBA(r, g, b, pDst[1]);
pSrc[2].getRGBA(r, g, b, pDst[2]);
pSrc[3].getRGBA(r, g, b, pDst[3]);
}
}
else
{
// Rare case: block is smaller than 4x4.
// Let's repeat pixels in this case.
// It will keep frequency of colors, except the case
// when width and/or height equals 3. But, this case
// is very rare because images usually are "power of 2" sized, and even
// if they are not, nobody will notice that the resulting encoding
// for such block is not ideal.
static unsigned int remainder[] =
{
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for (unsigned int row = 0; row < 4; ++row)
{
const unsigned int by = remainder[(bh - 1) * 4 + row];
uint16* const pDst = (uint16*)(((uint8*)imgA16) + pitch * (y + by));
uint16 r, g, b;
const ColorRGBA16* const pSrc = &m_color[row * 4];
for (unsigned int col = 0; col < 4; ++col)
{
const unsigned int bx = remainder[(bw - 1) * 4 + col];
pSrc[col].getRGBA(r, g, b, pDst[x + bx]);
}
}
}
}
bool ColorBlockRGBA4x4s::isSingleColorIgnoringAlpha() const
{
for (unsigned int i = 1; i < COLOR_COUNT; ++i)
{
if ((m_color[0].b != m_color[i].b) ||
(m_color[0].g != m_color[i].g) ||
(m_color[0].r != m_color[i].r))
{
return false;
}
}
return true;
}
} // namespace ImageProcessing

@ -1,62 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "ColorTypes.h"
namespace ImageProcessing
{
// Uncompressed 4x4 color block of 16bit integers.
struct ColorBlockRGBA4x4s
{
ColorBlockRGBA4x4s()
{
}
void setRGBA16(const void* imgRGBA16, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void getRGBA16(void* imgRGBA16, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void setA16(const void* imgA16, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
void getA16(void* imgA16, unsigned int width, unsigned int height, unsigned int pitch, unsigned int x, unsigned int y);
bool isSingleColorIgnoringAlpha() const;
const ColorRGBA16* colors() const
{
return m_color;
}
ColorRGBA16* colors()
{
return m_color;
}
ColorRGBA16 color(unsigned int i) const
{
return m_color[i];
}
ColorRGBA16& color(unsigned int i)
{
return m_color[i];
}
private:
static const unsigned int COLOR_COUNT = 4 * 4;
ColorRGBA16 m_color[COLOR_COUNT];
};
} // namespace ImageProcessing

@ -1,228 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
namespace ImageProcessing
{
// 32 bit 8888 RGBA color
struct ColorRGBA8
{
ColorRGBA8()
{
}
ColorRGBA8(const ColorRGBA8& a_c)
: u(a_c.u)
{
}
ColorRGBA8(uint8 a_r, uint8 a_g, uint8 a_b, uint8 a_a)
: r(a_r)
, g(a_g)
, b(a_b)
, a(a_a)
{
}
explicit ColorRGBA8(uint32 a_u)
: u(a_u)
{
}
void setRGBA(uint8 a_r, uint8 a_g, uint8 a_b, uint8 a_a)
{
r = a_r;
g = a_g;
b = a_b;
a = a_a;
}
void setRGBA(const uint8* a_pRGBA8)
{
r = a_pRGBA8[0];
g = a_pRGBA8[1];
b = a_pRGBA8[2];
a = a_pRGBA8[3];
}
void getRGBA(uint8& a_r, uint8& a_g, uint8& a_b, uint8& a_a) const
{
a_r = r;
a_g = g;
a_b = b;
a_a = a;
}
void getRGBA(uint8* a_pRGBA8) const
{
a_pRGBA8[0] = r;
a_pRGBA8[1] = g;
a_pRGBA8[2] = b;
a_pRGBA8[3] = a;
}
union
{
struct
{
uint8 r;
uint8 g;
uint8 b;
uint8 a;
};
uint32 u;
};
};
// 64 bit 16161616 RGBA color
struct ColorRGBA16
{
ColorRGBA16()
{
}
ColorRGBA16(const ColorRGBA16& a_c)
: u(a_c.u)
{
}
ColorRGBA16(uint16 a_r, uint16 a_g, uint16 a_b, uint16 a_a)
: r(a_r)
, g(a_g)
, b(a_b)
, a(a_a)
{
}
explicit ColorRGBA16(AZ::u64 a_u)
: u(a_u)
{
}
void setRGBA(uint16 a_r, uint16 a_g, uint16 a_b, uint16 a_a)
{
r = a_r;
g = a_g;
b = a_b;
a = a_a;
}
void setRGBA(const uint16* a_pRGBA16)
{
r = a_pRGBA16[0];
g = a_pRGBA16[1];
b = a_pRGBA16[2];
a = a_pRGBA16[3];
}
void getRGBA(uint16& a_r, uint16& a_g, uint16& a_b, uint16& a_a) const
{
a_r = r;
a_g = g;
a_b = b;
a_a = a;
}
void getRGBA(uint16* a_pRGBA16) const
{
a_pRGBA16[0] = r;
a_pRGBA16[1] = g;
a_pRGBA16[2] = b;
a_pRGBA16[3] = a;
}
union
{
struct
{
uint16 r;
uint16 g;
uint16 b;
uint16 a;
};
AZ::u64 u;
};
};
// 128 bit (4 floats) RGBA color
struct ColorRGBAf
{
ColorRGBAf()
{
}
ColorRGBAf(const ColorRGBAf& a_c)
: r(a_c.r)
, g(a_c.g)
, b(a_c.b)
, a(a_c.a)
{
}
ColorRGBAf(float a_r, float a_g, float a_b, float a_a)
: r(a_r)
, g(a_g)
, b(a_b)
, a(a_a)
{
}
void setRGBA(float a_r, float a_g, float a_b, float a_a)
{
r = a_r;
g = a_g;
b = a_b;
a = a_a;
}
void setRGBA(const float* a_pRGBAf)
{
r = a_pRGBAf[0];
g = a_pRGBAf[1];
b = a_pRGBAf[2];
a = a_pRGBAf[3];
}
void getRGBA(float& a_r, float& a_g, float& a_b, float& a_a) const
{
a_r = r;
a_g = g;
a_b = b;
a_a = a;
}
void getRGBA(float* a_pRGBAf) const
{
a_pRGBAf[0] = r;
a_pRGBAf[1] = g;
a_pRGBAf[2] = b;
a_pRGBAf[3] = a;
}
union
{
struct
{
float r;
float g;
float b;
float a;
};
};
};
static_assert(sizeof(ColorRGBA8) == 4, "Expected size of ColorRGBA8 to be 4 bytes!");
static_assert(sizeof(ColorRGBA16) == 8, "Expected size of ColorRGBA16 to be 4 bytes!");
static_assert(sizeof(ColorRGBAf) == 16, "Expected size of ColorRGBAf to be 4 bytes!");
} // namespace ImageProcessing

@ -1,657 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing_Traits_Platform.h>
#include "ColorBlockRGBA4x4c.h"
#include "ColorBlockRGBA4x4s.h"
#include "ColorBlockRGBA4x4f.h"
#include "CryTextureSquisher.h"
#include <AzCore/std/parallel/mutex.h>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnull-dereference"
# pragma clang diagnostic ignored "-Wsometimes-uninitialized"
# pragma clang diagnostic ignored "-Wshift-negative-value"
#endif
#if AZ_TRAIT_IMAGEPROCESSING_SQUISH_DO_NOT_USE_FASTCALL
#define __fastcall
#define _fastcall
#define __assume(x)
#endif
#include <squish-ccr/squish.h>
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
// number of bytes per block per type
#define BLOCKSIZE_BC1 8
#define BLOCKSIZE_BC2 16
#define BLOCKSIZE_BC3 16
#define BLOCKSIZE_BC4 8
#define BLOCKSIZE_BC5 16
#define BLOCKSIZE_BC6 16
#define BLOCKSIZE_BC7 16
#define BLOCKSIZE_CTX1 8
#define BLOCKSIZE_LIMIT 16
#define PTROFFSET_R 0
#define PTROFFSET_G 1
#define PTROFFSET_B 2
#define PTROFFSET_A 3
namespace ImageProcessing
{
AZStd::mutex s_squishLock;
/* -------------------------------------------------------------------------------------------------------------
* internal presets
*/
static struct ParameterMatrix
{
int flagsBaseline;
int flagsUniform;
int flagsPerceptual;
int flagsQuality[CryTextureSquisher::EQualityProfile::eQualityProfile_Num];
size_t offset;
bool alphaOnly;
} P2P[] =
{
// eCompressorPreset_BC1U,
{
squish::kBtc1 + squish::kExcludeAlphaFromPalette,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC2U,
{
squish::kBtc2,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC3U,
{
squish::kBtc3,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourClusterFit + squish::kAlphaIterativeFit, squish::kColourIterativeClusterFit + squish::kAlphaIterativeFit, squish::kColourIterativeClusterFit + squish::kAlphaIterativeFit },
0, false
},
// eCompressorPreset_BC4U,
{
squish::kBtc4,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_R, false
},
// eCompressorPreset_BC5U,
{
squish::kBtc5,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_R, false
},
// eCompressorPreset_BC6UH,
{
squish::kBtc6,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourRangeFit, squish::kColourRangeFit, squish::kColourRangeFit },
0, false
},
// eCompressorPreset_BC7U,
{
squish::kBtc7,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC4S,
{
squish::kBtc4 + squish::kSignedInternal,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_R, false
},
// eCompressorPreset_BC5S,
{
squish::kBtc5 + squish::kSignedInternal,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_R, false
},
// eCompressorPreset_BC1Un,
{
squish::kBtc1 + squish::kExcludeAlphaFromPalette,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit },
0, false
},
// eCompressorPreset_BC2Un,
{
squish::kBtc2,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit },
0, false
},
// eCompressorPreset_BC3Un,
{
squish::kBtc3,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ squish::kNormalRangeFit, squish::kNormalRangeFit + squish::kAlphaIterativeFit, squish::kNormalRangeFit + squish::kAlphaIterativeFit, squish::kNormalRangeFit + squish::kAlphaIterativeFit },
0, false
},
// eCompressorPreset_BC4Un,
{
squish::kBtc4,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_B, false
},
// eCompressorPreset_BC5Un,
{
squish::kBtc5,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ 0, 0, squish::kNormalIterativeFit, squish::kNormalIterativeFit },
PTROFFSET_R, false
},
// eCompressorPreset_BC6UHn,
{
squish::kBtc6,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit },
0, false
},
// eCompressorPreset_BC7Un,
{
squish::kBtc7,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ squish::kColourRangeFit, squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC4Sn,
{
squish::kBtc4 + squish::kSignedInternal,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_B, false
},
// eCompressorPreset_BC5Sn,
{
squish::kBtc5 + squish::kSignedInternal,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ 0, 0, squish::kNormalIterativeFit, squish::kNormalIterativeFit },
PTROFFSET_R, false
},
// eCompressorPreset_BC1Ua,
{
squish::kBtc1 + squish::kWeightColourByAlpha,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC2Ut,
{
squish::kBtc2 + squish::kWeightColourByAlpha,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC3Ut,
{
squish::kBtc3 + squish::kWeightColourByAlpha,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourClusterFit + squish::kAlphaIterativeFit, squish::kColourIterativeClusterFit + squish::kAlphaIterativeFit, squish::kColourIterativeClusterFit + squish::kAlphaIterativeFit },
0, false
},
// eCompressorPreset_BC4Ua,
{
squish::kBtc4,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_A, true
},
// eCompressorPreset_BC7Ut
{
squish::kBtc7 + squish::kWeightColourByAlpha,
squish::kColourMetricUniform,
squish::kColourMetricPerceptual,
{ squish::kColourRangeFit, squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_BC4Sa,
{
squish::kBtc4 + squish::kSignedInternal,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ 0, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit, squish::kAlphaIterativeFit },
PTROFFSET_A, true
},
// eCompressorPreset_BC7Ug
{
squish::kBtc7,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourClusterFit * 15, squish::kColourClusterFit * 15 },
0, false
},
// eCompressorPreset_CTX1U
{
squish::kCtx1,
squish::kColourMetricUniform,
squish::kColourMetricUniform,
{ squish::kColourRangeFit, squish::kColourClusterFit, squish::kColourIterativeClusterFit, squish::kColourIterativeClusterFit },
0, false
},
// eCompressorPreset_CTX1Un
{
squish::kCtx1,
squish::kColourMetricUnit,
squish::kColourMetricUnit,
{ squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit, squish::kNormalRangeFit },
0, false
},
};
/* -------------------------------------------------------------------------------------------------------------
* compression functions
*/
void CryTextureSquisher::Compress(const CryTextureSquisher::CompressorParameters& compress)
{
const unsigned int w = compress.width;
const unsigned int h = compress.height;
const size_t offset = P2P[compress.preset].offset;
int flags = P2P[compress.preset].flagsBaseline + P2P[compress.preset].flagsQuality[compress.quality] +
(!compress.perceptual ? P2P[compress.preset].flagsUniform : P2P[compress.preset].flagsPerceptual);
const bool bAlphaOnly = P2P[compress.preset].alphaOnly;
squish::sqio::dtp datatype;
switch (compress.srcType)
{
case eBufferType_sint8:
flags += squish::kSignedExternal;
case eBufferType_uint8:
datatype = squish::sqio::dtp::DT_U8;
break;
case eBufferType_sint16:
flags += squish::kSignedExternal;
case eBufferType_uint16:
datatype = squish::sqio::dtp::DT_U16;
break;
case eBufferType_sfloat:
flags += squish::kSignedExternal;
case eBufferType_ufloat:
datatype = squish::sqio::dtp::DT_F23;
break;
default:
__assume(0);
break;
}
if (compress.perceptual && (flags & squish::kColourMetricPerceptual))
{
flags |= squish::kColourMetricCustom;
}
const struct squish::sqio sqio = squish::GetSquishIO(w, h, datatype, flags);
if (compress.perceptual && (flags & squish::kColourMetricPerceptual))
{
s_squishLock.lock();
}
if (compress.perceptual && (flags & squish::kColourMetricPerceptual))
{
squish::SetWeights(sqio.flags, &compress.weights[0]);
}
AZ_Assert(!(h & 3), "%s: Unexpected compress parameter of height", __FUNCTION__);
switch (compress.srcType)
{
// compress an unsigned 8bit texture --------------------------------------------------
// compress a signed 8bit texture -----------------------------------------------------
case eBufferType_uint8:
case eBufferType_sint8:
{
for (unsigned int y = 0U; y < h; y += 4U)
{
ColorBlockRGBA4x4c srcBlock;
uint8 dstBlock[BLOCKSIZE_LIMIT];
uint8* const targetBlock = dstBlock;
const uint8* const sourceRgba = (const uint8*)srcBlock.colors() + offset;
for (unsigned int x = 0U; x < w; x += 4U)
{
if (!bAlphaOnly)
{
srcBlock.setRGBA8(compress.srcBuffer, w, h, compress.pitch, x, y);
}
else
{
srcBlock.setA8(compress.srcBuffer, w, h, compress.pitch, x, y);
}
sqio.encoder(sourceRgba, 0xFFFF, targetBlock, sqio.flags);
if (compress.userOutputFunction)
{
compress.userOutputFunction(compress, targetBlock, sqio.blocksize, y >> 2, x >> 2);
}
}
}
}
break;
// compress an unsigned 16bit texture -------------------------------------------------
// compress a signed 16bit texture ----------------------------------------------------
case eBufferType_uint16:
case eBufferType_sint16:
{
for (unsigned int y = 0U; y < h; y += 4U)
{
ColorBlockRGBA4x4s srcBlock;
uint8 dstBlock[BLOCKSIZE_LIMIT];
uint8* const targetBlock = dstBlock;
const float* const sourceRgba = (const float*)srcBlock.colors() + offset;
for (unsigned int x = 0U; x < w; x += 4U)
{
if (!bAlphaOnly)
{
srcBlock.setRGBA16(compress.srcBuffer, w, h, compress.pitch, x, y);
}
else
{
srcBlock.setA16(compress.srcBuffer, w, h, compress.pitch, x, y);
}
sqio.encoder(sourceRgba, 0xFFFF, targetBlock, sqio.flags);
if (compress.userOutputFunction)
{
compress.userOutputFunction(compress, targetBlock, sqio.blocksize, y >> 2, x >> 2);
}
}
}
}
break;
// compress an unsigned floating point texture ----------------------------------------
// compress a signed floating point texture -------------------------------------------
case eBufferType_ufloat:
case eBufferType_sfloat:
{
for (unsigned int y = 0U; y < h; y += 4U)
{
ColorBlockRGBA4x4f srcBlock;
uint8 dstBlock[BLOCKSIZE_LIMIT];
uint8* const targetBlock = dstBlock;
const float* const sourceRgba = (const float*)srcBlock.colors() + offset;
for (unsigned int x = 0U; x < w; x += 4U)
{
if (!bAlphaOnly)
{
srcBlock.setRGBAf(compress.srcBuffer, w, h, compress.pitch, x, y);
}
else
{
srcBlock.setAf(compress.srcBuffer, w, h, compress.pitch, x, y);
}
sqio.encoder(sourceRgba, 0xFFFF, targetBlock, sqio.flags);
if (compress.userOutputFunction)
{
compress.userOutputFunction(compress, targetBlock, sqio.blocksize, y >> 2, x >> 2);
}
}
}
}
break;
default:
AZ_Assert(false, "%s: Unexpected compress source type", __FUNCTION__);
break;
}
if (compress.perceptual && (flags & squish::kColourMetricPerceptual))
{
s_squishLock.unlock();
}
}
void CryTextureSquisher::Decompress(const DecompressorParameters& decompress)
{
const unsigned int w = decompress.width;
const unsigned int h = decompress.height;
const size_t offset = P2P[decompress.preset].offset;
int flags = P2P[decompress.preset].flagsBaseline +
P2P[decompress.preset].flagsUniform;
const bool bAlphaOnly = P2P[decompress.preset].alphaOnly;
squish::sqio::dtp datatype;
switch (decompress.dstType)
{
case eBufferType_sint8:
flags += squish::kSignedExternal;
case eBufferType_uint8:
datatype = squish::sqio::dtp::DT_U8;
break;
case eBufferType_sint16:
flags += squish::kSignedExternal;
case eBufferType_uint16:
datatype = squish::sqio::dtp::DT_U16;
break;
case eBufferType_sfloat:
flags += squish::kSignedExternal;
case eBufferType_ufloat:
datatype = squish::sqio::dtp::DT_F23;
break;
default:
__assume(0);
break;
}
const struct squish::sqio sqio = squish::GetSquishIO(w, h, datatype, flags);
AZ_Assert(!(h & 3), "%s: Unexpected compress parameter of height", __FUNCTION__);
switch (decompress.dstType)
{
// decompress an unsigned 8bit texture --------------------------------------------------
// decompress a signed 8bit texture -----------------------------------------------------
case eBufferType_uint8:
case eBufferType_sint8:
{
for (unsigned int y = 0U; y < h; y += 4U)
{
uint8 srcBlock[BLOCKSIZE_LIMIT];
ColorBlockRGBA4x4c dstBlock;
uint8* const sourceBlock = srcBlock;
uint8* const targetRgba = (uint8*)dstBlock.colors() + offset;
for (unsigned int x = 0U; x < w; x += 4U)
{
if (decompress.userInputFunction)
{
decompress.userInputFunction(decompress, sourceBlock, sqio.blocksize, y >> 2, x >> 2);
}
if (!bAlphaOnly)
{
dstBlock.setRGBA8(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
sqio.decoder(targetRgba, sourceBlock, sqio.flags);
if (!bAlphaOnly)
{
dstBlock.getRGBA8(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
else
{
dstBlock.getA8(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
}
}
}
break;
// decompress an unsigned 16bit texture -------------------------------------------------
// decompress a signed 16bit texture ----------------------------------------------------
case eBufferType_uint16:
case eBufferType_sint16:
{
for (unsigned int y = 0U; y < h; y += 4U)
{
uint8 srcBlock[BLOCKSIZE_LIMIT];
ColorBlockRGBA4x4s dstBlock;
uint8* const sourceBlock = srcBlock;
uint16* const targetRgba = (uint16*)dstBlock.colors() + offset;
for (unsigned int x = 0U; x < w; x += 4U)
{
if (decompress.userInputFunction)
{
decompress.userInputFunction(decompress, sourceBlock, sqio.blocksize, y >> 2, x >> 2);
}
if (!bAlphaOnly)
{
dstBlock.setRGBA16(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
sqio.decoder(targetRgba, sourceBlock, sqio.flags);
if (!bAlphaOnly)
{
dstBlock.setRGBA16(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
else
{
dstBlock.getA16(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
}
}
}
break;
// decompress an unsigned floating point texture ----------------------------------------
// decompress a signed floating point texture -------------------------------------------
case eBufferType_ufloat:
case eBufferType_sfloat:
{
for (unsigned int y = 0U; y < h; y += 4U)
{
uint8 srcBlock[BLOCKSIZE_LIMIT];
ColorBlockRGBA4x4f dstBlock;
uint8* const sourceBlock = srcBlock;
float* const targetRgba = (float*)dstBlock.colors() + offset;
for (unsigned int x = 0U; x < w; x += 4U)
{
if (decompress.userInputFunction)
{
decompress.userInputFunction(decompress, sourceBlock, sqio.blocksize, y >> 2, x >> 2);
}
if (!bAlphaOnly)
{
dstBlock.setRGBAf(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
sqio.decoder(targetRgba, sourceBlock, sqio.flags);
if (!bAlphaOnly)
{
dstBlock.getRGBAf(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
else
{
dstBlock.getAf(decompress.dstBuffer, w, h, decompress.pitch, x, y);
}
}
}
}
break;
default:
AZ_Assert(false, "%s: Unexpected compress destination type", __FUNCTION__);
break;
}
}
} // namespace ImageProcessing

@ -1,132 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
namespace ImageProcessing
{
class CryTextureSquisher
{
public:
enum EBufferType
{
eBufferType_uint8, // native support: BC1-5/7,CTX1
eBufferType_sint8, // native support: BC4-5
eBufferType_uint16, // native support: BC1-7,CTX1
eBufferType_sint16, // native support: BC4-6
eBufferType_ufloat, // native support: BC1-7,CTX1
eBufferType_sfloat, // native support: BC4-6
};
enum EQualityProfile
{
eQualityProfile_Low = 0,// as-fast-as-possible
eQualityProfile_Medium, // not so bad (nightly builds)
eQualityProfile_High, // relative good (weekly builds)
eQualityProfile_Best, // as-best-as-possible (final build for release)
eQualityProfile_Num
};
enum ECodingPreset
{
eCompressorPreset_BC1U = 0,
eCompressorPreset_BC2U,
eCompressorPreset_BC3U,
eCompressorPreset_BC4U, // r-channel from RGBA
eCompressorPreset_BC5U, // rg-channels from RGBA
eCompressorPreset_BC6UH,
eCompressorPreset_BC7U,
eCompressorPreset_BC4S, // r-channel from RGBA
eCompressorPreset_BC5S, // rg-channels from RGBA
// normal vectors -> unit metric
eCompressorPreset_BC1Un,
eCompressorPreset_BC2Un,
eCompressorPreset_BC3Un,
eCompressorPreset_BC4Un, // z-channel from XYZD
eCompressorPreset_BC5Un, // xy-channels from XYZD, xyz must be a valid unit-vector
eCompressorPreset_BC6UHn,
eCompressorPreset_BC7Un,
eCompressorPreset_BC4Sn, // z-channel from XYZD
eCompressorPreset_BC5Sn, // xy-channels from XYZD, xyz must be a valid unit-vector
// transparency -> weighted alpha
eCompressorPreset_BC1Ua,
eCompressorPreset_BC2Ut,
eCompressorPreset_BC3Ut,
eCompressorPreset_BC4Ua, // a-channel from RGBA
eCompressorPreset_BC7Ut,
eCompressorPreset_BC4Sa, // a-channel from RGBA
// grey-scale -> 12+ bits of precision
eCompressorPreset_BC7Ug,
// special ones
eCompressorPreset_CTX1U, // rg-channels from RGBA
eCompressorPreset_CTX1Un, // xy-channels from XYZD, xyz must be a valid unit-vector
eCompressorPreset_Num
};
struct CompressorParameters
{
// source's parameters
EBufferType srcType;
const void* srcBuffer;
unsigned int width;
unsigned int height;
unsigned int pitch;
// coding preset
ECodingPreset preset;
EQualityProfile quality;
// either if "srgb==1" or if "rgbweights!=uniform"
bool perceptual;
float weights[4];
void* userPtr;
int userInt;
void(*userOutputFunction)(const CompressorParameters& compress, const void* compressedData, unsigned int compressedSize, unsigned int oy, unsigned int ox);
};
struct DecompressorParameters
{
// destination's parameters
EBufferType dstType;
void* dstBuffer;
unsigned int width;
unsigned int height;
unsigned int pitch;
// coding preset
ECodingPreset preset;
void* userPtr;
int userInt;
void(*userInputFunction)(const DecompressorParameters& decompress, void* compressedData, unsigned int compressedSize, unsigned int oy, unsigned int ox);
};
public:
static void Compress(const CompressorParameters& compress);
static void Decompress(const DecompressorParameters& decompress);
};
} //namespace ImageProcessing

@ -1,229 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing/ImageObject.h>
#include <Processing/PixelFormatInfo.h>
#include <Processing/ImageFlags.h>
#include <Converters/PixelOperation.h>
#include <Compressors/ETC2.h>
#include <EtcConfig.h>
#include <Etc.h>
#include <EtcImage.h>
#include <EtcColorFloatRGBA.h>
namespace ImageProcessing
{
//limited to 1 thread because AP requires so. We may change to n when AP allocate n thread to a job in the furture
static const int MAX_COMP_JOBS = 1;
static const int MIN_COMP_JOBS = 1;
static const float ETC_LOW_EFFORT_LEVEL = 25.0f;
static const float ETC_MED_EFFORT_LEVEL = 40.0f;
static const float ETC_HIGH_EFFORT_LEVEL = 80.0f;
//Grab the Etc2Comp specific pixel format enum
static Etc::Image::Format FindEtc2PixelFormat(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_EAC_RG11:
return Etc::Image::Format::RG11;
case ePixelFormat_EAC_R11:
return Etc::Image::Format::R11;
case ePixelFormat_ETC2:
return Etc::Image::Format::RGB8;
case ePixelFormat_ETC2a:
return Etc::Image::Format::RGBA8;
default:
return Etc::Image::Format::FORMATS;
}
}
//Get the errmetric required for the compression
static Etc::ErrorMetric FindErrMetric(Etc::Image::Format fmt)
{
switch (fmt)
{
case Etc::Image::Format::RG11:
return Etc::ErrorMetric::NORMALXYZ;
case Etc::Image::Format::R11:
return Etc::ErrorMetric::NUMERIC;
case Etc::Image::Format::RGB8:
return Etc::ErrorMetric::RGBX;
case Etc::Image::Format::RGBA8:
return Etc::ErrorMetric::RGBA;
default:
return Etc::ErrorMetric::ERROR_METRICS;
}
}
//Convert to sRGB format
static Etc::Image::Format FindGammaEtc2PixelFormat(Etc::Image::Format fmt)
{
switch (fmt)
{
case Etc::Image::Format::RGB8:
return Etc::Image::Format::SRGB8;
case Etc::Image::Format::RGBA8:
return Etc::Image::Format::SRGBA8;
case Etc::Image::Format::RGB8A1:
return Etc::Image::Format::SRGB8A1;
default:
return Etc::Image::Format::FORMATS;
}
}
bool ETC2Compressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
return (FindEtc2PixelFormat(fmt) != Etc::Image::Format::FORMATS);
}
bool ETC2Compressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
{
//for uncompress format
if (fmt == ePixelFormat_R8G8B8A8)
{
return true;
}
return false;
}
EPixelFormat ETC2Compressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, [[maybe_unused]] EPixelFormat uncompressedfmt)
{
return ePixelFormat_R8G8B8A8;
}
bool ETC2Compressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
{
return false;
}
IImageObjectPtr ETC2Compressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst,
const CompressOption *compressOption)
{
const size_t srcPixelSize = 4;
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be uncompressed and dst format need to compressed.
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
//determinate compression quality
ICompressor::EQuality quality = ICompressor::eQuality_Normal;
//get setting from compression option
if (compressOption)
{
quality = compressOption->compressQuality;
}
float qualityEffort = 0.0f;
switch (quality)
{
case eQuality_Preview:
case eQuality_Fast:
{
qualityEffort = ETC_LOW_EFFORT_LEVEL;
break;
}
case eQuality_Normal:
{
qualityEffort = ETC_MED_EFFORT_LEVEL;
break;
}
default:
{
qualityEffort = ETC_HIGH_EFFORT_LEVEL;
}
}
Etc::Image::Format dstEtc2Format = FindEtc2PixelFormat(fmtDst);
if (srcImage->GetImageFlags() & EIF_SRGBRead)
{
dstEtc2Format = FindGammaEtc2PixelFormat(dstEtc2Format);
}
//use to read pixel data from src image
IPixelOperationPtr pixelOp = CreatePixelOperation(fmtSrc);
//get count of bytes per pixel for images
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc)->bitsPerBlock / 8;
const AZ::u32 mipCount = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < mipCount; ++mip)
{
const AZ::u32 width = srcImage->GetWidth(mip);
const AZ::u32 height = srcImage->GetHeight(mip);
// Prepare source data
AZ::u8* srcMem;
AZ::u32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
const AZ::u32 pixelCount = srcImage->GetPixelCount(mip);
Etc::ColorFloatRGBA* rgbaPixels = new Etc::ColorFloatRGBA[pixelCount];
Etc::ColorFloatRGBA* rgbaPixelPtr = rgbaPixels;
float r, g, b, a;
for (AZ::u32 pixelIdx = 0; pixelIdx < pixelCount; pixelIdx++, srcMem += pixelBytes, rgbaPixelPtr++)
{
pixelOp->GetRGBA(srcMem, r, g, b, a);
rgbaPixelPtr->fA = a;
rgbaPixelPtr->fR = r;
rgbaPixelPtr->fG = g;
rgbaPixelPtr->fB = b;
}
//Call into etc2Comp lib to compress. https://medium.com/@duhroach/building-a-blazing-fast-etc2-compressor-307f3e9aad99
Etc::ErrorMetric errMetric = FindErrMetric(dstEtc2Format);
unsigned char* paucEncodingBits;
unsigned int uiEncodingBitsBytes;
unsigned int uiExtendedWidth;
unsigned int uiExtendedHeight;
int iEncodingTime_ms;
Etc::Encode(reinterpret_cast<float*>(rgbaPixels),
width, height,
dstEtc2Format,
errMetric,
qualityEffort,
MIN_COMP_JOBS,
MAX_COMP_JOBS,
&paucEncodingBits, &uiEncodingBitsBytes,
&uiExtendedWidth, &uiExtendedHeight,
&iEncodingTime_ms);
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
memcpy(dstMem, paucEncodingBits, uiEncodingBitsBytes);
delete[] rgbaPixels;
}
return dstImage;
}
IImageObjectPtr ETC2Compressor::DecompressImage(IImageObjectPtr srcImage, [[maybe_unused]] EPixelFormat fmtDst)
{
//etc2Comp doesn't support decompression
//Since PVRTexLib support ETC formats too. It may take over the decompression.
return nullptr;
}
} // namespace ImageProcessing

@ -1,32 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Compressors/Compressor.h>
namespace ImageProcessing
{
class ETC2Compressor : public ICompressor
{
public:
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
static bool DoesSupportDecompress(EPixelFormat fmtDst);
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption *compressOption) override;
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) override;
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) override;
};
} // namespace ImageProcessing

@ -1,389 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing_Traits_Platform.h>
#include <ImageProcessing/ImageObject.h>
#include <Processing/PixelFormatInfo.h>
#include <Processing/ImageFlags.h>
#include <Compressors/PVRTC.h>
#if AZ_TRAIT_IMAGEPROCESSING_PVRTEXLIB_USE_WINDLL_IMPORT
//_WINDLL_IMPORT need to be defined before including PVRTexLib header files to avoid linking error on windows.
#define _WINDLL_IMPORT
// NOMINMAX needs to be defined before including PVRTexLib header files (which include Windows.h)
// so that Windows.h doesn't define min/max. Otherwise, a compile error may arise in Uber builds
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#include <PVRTexture.h>
#include <PVRTextureUtilities.h>
namespace ImageProcessing
{
// Note: PVRTexLib supports ASTC formats, ETC formats, PVRTC formats and BC formats
// We haven't tested the performace to compress BC formats compare to CTSquisher
// For PVRTC formats, we only added PVRTC 1 support for now
// The compression for ePVRTPF_EAC_R11 and ePVRTPF_EAC_RG11 are very slow. It takes 7 and 14 minutes for a 2048x2048 texture.
EPVRTPixelFormat FindPvrPixelFormat(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_ASTC_4x4:
return ePVRTPF_ASTC_4x4;
case ePixelFormat_ASTC_5x4:
return ePVRTPF_ASTC_5x4;
case ePixelFormat_ASTC_5x5:
return ePVRTPF_ASTC_5x5;
case ePixelFormat_ASTC_6x5:
return ePVRTPF_ASTC_6x5;
case ePixelFormat_ASTC_6x6:
return ePVRTPF_ASTC_6x6;
case ePixelFormat_ASTC_8x5:
return ePVRTPF_ASTC_8x5;
case ePixelFormat_ASTC_8x6:
return ePVRTPF_ASTC_8x6;
case ePixelFormat_ASTC_8x8:
return ePVRTPF_ASTC_8x8;
case ePixelFormat_ASTC_10x5:
return ePVRTPF_ASTC_10x5;
case ePixelFormat_ASTC_10x6:
return ePVRTPF_ASTC_10x6;
case ePixelFormat_ASTC_10x8:
return ePVRTPF_ASTC_10x8;
case ePixelFormat_ASTC_10x10:
return ePVRTPF_ASTC_10x10;
case ePixelFormat_ASTC_12x10:
return ePVRTPF_ASTC_12x10;
case ePixelFormat_ASTC_12x12:
return ePVRTPF_ASTC_12x12;
case ePixelFormat_PVRTC2:
return ePVRTPF_PVRTCI_2bpp_RGBA;
case ePixelFormat_PVRTC4:
return ePVRTPF_PVRTCI_4bpp_RGBA;
case ePixelFormat_EAC_R11:
return ePVRTPF_EAC_R11;
case ePixelFormat_EAC_RG11:
return ePVRTPF_EAC_RG11;
case ePixelFormat_ETC2:
return ePVRTPF_ETC2_RGB;
case ePixelFormat_ETC2a:
return ePVRTPF_ETC2_RGBA;
default:
return ePVRTPF_NumCompressedPFs;
}
}
bool PVRTCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
return (FindPvrPixelFormat(fmt) != ePVRTPF_NumCompressedPFs);
}
bool PVRTCCompressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
{
//for uncompress format
if (fmt == ePixelFormat_R8G8B8A8)
{
return true;
}
return false;
}
EPixelFormat PVRTCCompressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, [[maybe_unused]] EPixelFormat uncompressedfmt)
{
return ePixelFormat_R8G8B8A8;
}
bool PVRTCCompressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
{
return true;
}
IImageObjectPtr PVRTCCompressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst,
const CompressOption *compressOption)
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be uncompressed and dst format need to compressed.
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
//determinate compression quality
pvrtexture::ECompressorQuality internalQuality = pvrtexture::eETCFast;
ICompressor::EQuality quality = ICompressor::eQuality_Normal;
AZ::Vector3 uniformWeights = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
AZ::Vector3 weights = uniformWeights;
bool isUniform = true;
//get setting from compression option
if (compressOption)
{
quality = compressOption->compressQuality;
weights = compressOption->rgbWeight;
isUniform = (weights == uniformWeights);
}
if (IsETCFormat(fmtDst))
{
if ((quality <= eQuality_Normal) && isUniform)
{
internalQuality = pvrtexture::eETCFast;
}
else if (quality <= eQuality_Normal)
{
internalQuality = pvrtexture::eETCNormal;
}
else if (isUniform)
{
internalQuality = pvrtexture::eETCSlow;
}
else
{
internalQuality = pvrtexture::eETCSlow;
}
}
else if (IsASTCFormat(fmtDst))
{
if (quality == eQuality_Preview)
{
internalQuality = pvrtexture::eASTCVeryFast;
}
else if (quality == eQuality_Fast)
{
internalQuality = pvrtexture::eASTCFast;
}
else if (quality == eQuality_Normal)
{
internalQuality = pvrtexture::eASTCMedium;
}
else
{
internalQuality = pvrtexture::eASTCThorough;
}
}
else
{
if (quality == eQuality_Preview)
{
internalQuality = pvrtexture::ePVRTCFastest;
}
else if (quality == eQuality_Fast)
{
internalQuality = pvrtexture::ePVRTCFast;
}
else if (quality == eQuality_Normal)
{
internalQuality = pvrtexture::ePVRTCNormal;
}
else
{
internalQuality = pvrtexture::ePVRTCHigh;
}
}
// setup color space
EPVRTColourSpace cspace = ePVRTCSpacelRGB;
if (srcImage->GetImageFlags() & EIF_SRGBRead)
{
cspace = ePVRTCSpacesRGB;
}
//setup src texture for compression
const pvrtexture::PixelType srcPixelType('r', 'g', 'b', 'a', 8, 8, 8, 8);
const AZ::u32 dstMips = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < dstMips; ++mip)
{
const AZ::u32 width = srcImage->GetWidth(mip);
const AZ::u32 height = srcImage->GetHeight(mip);
// Prepare source data
AZ::u8* srcMem;
uint32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
const pvrtexture::CPVRTextureHeader srcHeader(
srcPixelType.PixelTypeID, // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
1, // uint32 u32NumMipMaps=1,
1, // uint32 u32NumArrayMembers=1,
1, // uint32 u32NumFaces=1,
cspace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB,
ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm,
false); // bool bPreMultiplied=false);
pvrtexture::CPVRTexture compressTexture(srcHeader, srcMem);
//compressing
bool isSuccess = false;
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
try
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
{
isSuccess = pvrtexture::Transcode(
compressTexture,
pvrtexture::PixelType(FindPvrPixelFormat(fmtDst)),
ePVRTVarTypeUnsignedByteNorm,
cspace,
internalQuality);
}
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
catch (...)
{
AZ_Error("Image Processing", false, "Unknown exception in PVRTexLib");
return nullptr;
}
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
if (!isSuccess)
{
AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib. You may not have astcenc.exe for compressing ASTC formates");
return nullptr;
}
// Getting compressed data
const void* const compressedData = compressTexture.getDataPtr();
if (!compressedData)
{
AZ_Error("Image Processing", false, "Failed to obtain compressed image data by using PVRTexLib");
return nullptr;
}
const AZ::u32 compressedDataSize = compressTexture.getDataSize();
if (dstImage->GetMipBufSize(mip) != compressedDataSize)
{
AZ_Error("Image Processing", false, "Compressed image data size mismatch while using PVRTexLib");
return nullptr;
}
//save compressed data to dst image
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
memcpy(dstMem, compressedData, compressedDataSize);
}
return dstImage;
}
IImageObjectPtr PVRTCCompressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst)
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat(); //compressed
if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
EPVRTColourSpace colorSpace = ePVRTCSpacelRGB;
if (srcImage->GetImageFlags() & EIF_SRGBRead)
{
colorSpace = ePVRTCSpacesRGB;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
const AZ::u32 mipCount = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < mipCount; ++mip)
{
const AZ::u32 width = srcImage->GetWidth(mip);
const AZ::u32 height = srcImage->GetHeight(mip);
// Preparing source compressed data
const pvrtexture::CPVRTextureHeader compressedHeader(
FindPvrPixelFormat(fmtSrc), // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
1, // uint32 u32NumMipMaps=1,
1, // uint32 u32NumArrayMembers=1,
1, // uint32 u32NumFaces=1,
colorSpace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB,
ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm,
false); // bool bPreMultiplied=false);
const AZ::u32 compressedDataSize = compressedHeader.getDataSize();
if (srcImage->GetMipBufSize(mip) != compressedDataSize)
{
AZ_Error("Image Processing", false, "Decompressed image data size mismatch while using PVRTexLib");
return nullptr;
}
AZ::u8* srcMem;
AZ::u32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
pvrtexture::CPVRTexture cTexture(compressedHeader, srcMem);
// Decompress
bool bOk = false;
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
try
{
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
bOk = pvrtexture::Transcode(
cTexture,
pvrtexture::PVRStandard8PixelType,
ePVRTVarTypeUnsignedByteNorm,
colorSpace,
pvrtexture::ePVRTCHigh);
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
}
catch (...)
{
AZ_Error("Image Processing", false, "Unknown exception in PVRTexLib when decompressing");
return nullptr;
}
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
if (!bOk)
{
AZ_Error("Image Processing", false, "Failed to decompress an image by using PVRTexLib");
return nullptr;
}
// Getting decompressed data
const void* const pDecompressedData = cTexture.getDataPtr();
if (!pDecompressedData)
{
AZ_Error("Image Processing", false, "Failed to obtain decompressed image data by using PVRTexLib");
return nullptr;
}
const AZ::u32 decompressedDataSize = cTexture.getDataSize();
if (dstImage->GetMipBufSize(mip) != decompressedDataSize)
{
AZ_Error("Image Processing", false, "Decompressed image data size mismatch while using PVRTexLib");
return nullptr;
}
//save decompressed image to dst image
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
memcpy(dstMem, pDecompressedData, decompressedDataSize);
}
return dstImage;
}
} //namespace ImageProcessing

@ -1,32 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Compressors/Compressor.h>
namespace ImageProcessing
{
class PVRTCCompressor : public ICompressor
{
public:
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
static bool DoesSupportDecompress(EPixelFormat fmtDst);
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption *compressOption) override;
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) override;
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) override;
};
} // namespace ImageProcessing

@ -1,121 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageConvert.h>
#include <Processing/PixelFormatInfo.h>
#include <Converters/PixelOperation.h>
///////////////////////////////////////////////////////////////////////////////////
//functions for maintaining alpha coverage.
namespace ImageProcessing
{
void CImageObject::TransferAlphaCoverage(const TextureSettings* textureSetting, const IImageObjectPtr srcImg)
{
EPixelFormat srcFmt = srcImg->GetPixelFormat();
//both this image and src image need to be uncompressed
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)
|| !CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcFmt))
{
AZ_Assert(false, "Both source image and dest image need to be uncompressed");
return;
}
const float fAlphaRef = 0.5f; // Seems to give good overall results
const float fDesiredAlphaCoverage = srcImg->ComputeAlphaCoverage(0, fAlphaRef);
//create pixel operation function
IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
//get count of bytes per pixel
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
for (uint32 mip = 0; mip < GetMipCount(); mip++)
{
const float fAlphaOffset = textureSetting->ComputeMIPAlphaOffset(mip);
const float fAlphaScale = ComputeAlphaCoverageScaleFactor(mip, fDesiredAlphaCoverage, fAlphaRef);
AZ::u8* pixelBuf = m_mips[mip]->m_pData;
const AZ::u32 pixelCount = GetPixelCount(mip);
for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
{
float r, g, b, a;
pixelOp->GetRGBA(pixelBuf, r, g, b, a);
a = AZ::GetMin(a * fAlphaScale + fAlphaOffset, 1.0f);
pixelOp->SetRGBA(pixelBuf, r, g, b, a);
}
}
}
float CImageObject::ComputeAlphaCoverageScaleFactor(AZ::u32 mip, float fDesiredCoverage, float fAlphaRef) const
{
float minAlphaRef = 0.0f;
float maxAlphaRef = 1.0f;
float midAlphaRef = 0.5f;
// Find best alpha test reference value using a binary search
for (int i = 0; i < 10; i++)
{
const float currentCoverage = ComputeAlphaCoverage(mip, midAlphaRef);
if (currentCoverage > fDesiredCoverage)
{
minAlphaRef = midAlphaRef;
}
else if (currentCoverage < fDesiredCoverage)
{
maxAlphaRef = midAlphaRef;
}
else
{
break;
}
midAlphaRef = (minAlphaRef + maxAlphaRef) * 0.5f;
}
return fAlphaRef / midAlphaRef;
}
float CImageObject::ComputeAlphaCoverage(AZ::u32 mip, float fAlphaRef) const
{
//This function only works with uncompressed image
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
{
AZ_Assert(false, "This image need to be uncompressed");
return 0;
}
uint32 coverage = 0;
//create pixel operation function
IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
//get count of bytes per pixel
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
AZ::u8* pixelBuf = m_mips[mip]->m_pData;
const AZ::u32 pixelCount = GetPixelCount(mip);
for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
{
float r, g, b, a;
pixelOp->GetRGBA(pixelBuf, r, g, b, a);
coverage += a > fAlphaRef;
}
return (float)coverage / (float)(pixelCount);
}
} // namespace ImageProcessing

@ -1,314 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageToProcess.h>
namespace ImageProcessing
{
const int COLORCHART_IMAGE_WIDTH = 78;
const int COLORCHART_IMAGE_HEIGHT = 66;
// color chart in cry engine is a special image data, with size 78x66, you may see in game screenshot which is defined by a rectangle
// area with a yellow-black dash line boarder
// Create color chart function is to read that block of image data and convert it to a color table then save it to another image
// with size 256x16.
class C3dLutColorChart
{
public:
C3dLutColorChart() {}
~C3dLutColorChart() {};
//generate default color chart data
void GenerateDefault();
//generate color chart data from input image
bool GenerateFromInput(IImageObjectPtr image);
//ouput the color chart data to an image object
IImageObjectPtr GenerateChartImage();
protected:
//extract color chart data from specified location in an image
void ExtractFromImageAt(IImageObjectPtr pImg, AZ::u32 x, AZ::u32 y);
//find color chart location in an image
static bool FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY);
//if there is a color chart at specified location
static bool IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch);
private:
enum EPrimaryShades
{
ePS_Red = 16,
ePS_Green = 16,
ePS_Blue = 16,
ePS_NumColors = ePS_Red * ePS_Green * ePS_Blue
};
struct SColor
{
unsigned char r, g, b, _padding;
};
typedef AZStd::vector<SColor> ColorMapping;
ColorMapping m_mapping;
};
void C3dLutColorChart::GenerateDefault()
{
m_mapping.reserve(ePS_NumColors);
for (int b = 0; b < ePS_Blue; ++b)
{
for (int g = 0; g < ePS_Green; ++g)
{
for (int r = 0; r < ePS_Red; ++r)
{
SColor col;
col.r = 255 * r / (ePS_Red);
col.g = 255 * g / (ePS_Green);
col.b = 255 * b / (ePS_Blue);
int l = 255 - (col.r * 3 + col.g * 6 + col.b) / 10;
col.r = col.g = col.b = (unsigned char)l;
m_mapping.push_back(col);
}
}
}
}
//find color chart location in a image
bool C3dLutColorChart::FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY)
{
const AZ::u32 width = pImg->GetWidth(0);
const AZ::u32 height = pImg->GetHeight(0);
//the origin image is too small to have a color chart
if (width < COLORCHART_IMAGE_WIDTH || height < COLORCHART_IMAGE_HEIGHT)
{
return false;
}
AZ::u8* pData;
AZ::u32 pitch;
pImg->GetImagePointer(0, pData, pitch);
//check all the posible start location on whether there might be a color chart
for (AZ::u32 y = 0; y <= height - COLORCHART_IMAGE_HEIGHT; ++y)
{
for (AZ::u32 x = 0; x <= width - COLORCHART_IMAGE_WIDTH; ++x)
{
if (IsColorChartAt(x, y, pData, pitch))
{
outLocX = x;
outLocY = y;
return true;
}
}
}
return false;
}
bool C3dLutColorChart::GenerateFromInput(IImageObjectPtr image)
{
AZ::u32 outLocX, outLocY;
if (FindColorChart(image, outLocX, outLocY))
{
ExtractFromImageAt(image, outLocX, outLocY);
return true;
}
return false;
}
IImageObjectPtr C3dLutColorChart::GenerateChartImage()
{
const AZ::u32 mipCount = 1;
IImageObjectPtr image( IImageObject::CreateImage(ePS_Red * ePS_Blue, ePS_Green, 1, ePixelFormat_R8G8B8A8));
{
AZ::u8* pData;
AZ::u32 pitch;
image->GetImagePointer(0, pData, pitch);
size_t nSlicePitch = (pitch / ePS_Blue);
AZ::u32 src = 0;
for (int b = 0; b < ePS_Blue; ++b)
{
for (int g = 0; g < ePS_Green; ++g)
{
AZ::u8* p = pData + g * pitch + b * nSlicePitch;
for (int r = 0; r < ePS_Red; ++r)
{
const SColor& c = m_mapping[src];
p[0] = c.r;
p[1] = c.g;
p[2] = c.b;
p[3] = 255;
++src;
p += 4;
}
}
}
}
return image;
}
void C3dLutColorChart::ExtractFromImageAt(IImageObjectPtr image, AZ::u32 x, AZ::u32 y)
{
int ox = x + 1;
int oy = y + 1;
AZ::u8* pData;
AZ::u32 pitch;
image->GetImagePointer(0, pData, pitch);
m_mapping.reserve(ePS_NumColors);
for (int b = 0; b < ePS_Blue; ++b)
{
int px = ox + ePS_Red * (b % 4);
int py = oy + ePS_Green * (b / 4);
for (int g = 0; g < ePS_Green; ++g)
{
for (int r = 0; r < ePS_Red; ++r)
{
AZ::u8* p = pData + pitch * (py + g) + (px + r) * 4;
SColor col;
col.r = p[0];
col.g = p[1];
col.b = p[2];
m_mapping.push_back(col);
}
}
}
}
//check if image data at location x and y could be a color chart
//based on if the boarder is dash lines with two pixel each segement
//the idea and implementation are both coming from CryEngine.
bool C3dLutColorChart::IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch)
{
struct Color
{
private:
int c[3];
public:
Color(AZ::u32 x, AZ::u32 y, void* pPixels, AZ::u32 pitch)
{
const uint8* p = (const uint8*)pPixels + pitch * y + x * 4;
c[0] = p[0];
c[1] = p[1];
c[2] = p[2];
}
bool isSimilar(const Color& a, int maxDiff) const
{
return
abs(a.c[0] - c[0]) <= maxDiff &&
abs(a.c[1] - c[1]) <= maxDiff &&
abs(a.c[2] - c[2]) <= maxDiff;
}
};
const Color colorRef[2] =
{
Color(x, y, pData, pitch),
Color(x + 2, y, pData, pitch)
};
// We require two colors of the border to be at least a bit different
if (colorRef[0].isSimilar(colorRef[1], 15))
{
return false;
}
static const int kMaxDiff = 3;
int refIdx = 0;
//rectangle's top
for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2)
{
if (!colorRef[refIdx].isSimilar(Color(x + i, y, pData, pitch), kMaxDiff) ||
!colorRef[refIdx].isSimilar(Color(x + i + 1, y, pData, pitch), kMaxDiff))
{
return false;
}
refIdx ^= 1;
}
refIdx = 0;
//left
for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2)
{
if (!colorRef[refIdx].isSimilar(Color(x, y + i, pData, pitch), kMaxDiff) ||
!colorRef[refIdx].isSimilar(Color(x, y + i + 1, pData, pitch), kMaxDiff))
{
return false;
}
refIdx ^= 1;
}
refIdx = 0;
//right
for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2)
{
if (!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i, pData, pitch), kMaxDiff) ||
!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i + 1, pData, pitch), kMaxDiff))
{
return false;
}
refIdx ^= 1;
}
refIdx = 0;
//bottom
for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2)
{
if (!colorRef[refIdx].isSimilar(Color(x + i, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff) ||
!colorRef[refIdx].isSimilar(Color(x + i + 1, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff))
{
return false;
}
refIdx ^= 1;
}
return true;
}
void ImageToProcess::CreateColorChart()
{
C3dLutColorChart colorChart;
//get color chart data from source image.
if (!colorChart.GenerateFromInput(m_img))
{
//if load from image failed then generate default color data
colorChart.GenerateDefault();
}
//save color chart data to an image and save as current
m_img = colorChart.GenerateChartImage();
}
}

@ -1,170 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageToProcess.h>
#include <Processing/PixelFormatInfo.h>
#include <Compressors/Compressor.h>
#include <Converters/PixelOperation.h>
///////////////////////////////////////////////////////////////////////////////////
//functions for maintaining alpha coverage.
namespace ImageProcessing
{
void ImageToProcess::ConvertFormat(EPixelFormat fmtDst)
{
//pixel format before convertion
EPixelFormat fmtSrc = Get()->GetPixelFormat();
//return directly if the image already has the desired pixel format
if (fmtDst == fmtSrc)
{
return;
}
uint32 dwWidth, dwHeight, dwMips;
dwWidth = Get()->GetWidth(0);
dwHeight = Get()->GetHeight(0);
dwMips = Get()->GetMipCount();
//if the output image size doesn't work the desired pixel format. set to fallback format
const PixelFormatInfo* dstFmtInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtDst);
if (!CPixelFormats::GetInstance().IsImageSizeValid(fmtDst, dwWidth, dwHeight, true))
{
AZ_Warning("Image Processing", false, "Output pixel format %d doesn't work with output image size %d x %d",
fmtDst, dwWidth, dwHeight);
//fall back to safe texture format
if (dstFmtInfo->nChannels == 1)
{
fmtDst = dstFmtInfo->bHasAlpha ? ePixelFormat_A8 : ePixelFormat_R8;
}
else if (dstFmtInfo->nChannels == 2)
{
fmtDst = ePixelFormat_R8G8;
}
else
{
fmtDst = dstFmtInfo->bHasAlpha ? ePixelFormat_R8G8B8A8 : ePixelFormat_R8G8B8X8;
}
}
//convert src image to uncompressed formats if it's compressed format
bool isSrcUncompressed = CPixelFormats::GetInstance().IsPixelFormatUncompressed(fmtSrc);
bool isDstUncompressed = CPixelFormats::GetInstance().IsPixelFormatUncompressed(fmtDst);
if (isSrcUncompressed && isDstUncompressed)
{//both are uncompressed
ConvertFormatUncompressed(fmtDst);
}
else if (!isSrcUncompressed && !isDstUncompressed)
{ //both are compressed
AZ_Assert(false, "unusual user case. but we can still handle it");
}
else
{ //one fmt is compressed format
//use the compressed format to find right compressor
EPixelFormat compressedFmt = isSrcUncompressed ? fmtDst : fmtSrc;
EPixelFormat uncompressedFmt = isSrcUncompressed ? fmtSrc : fmtDst;
ICompressorPtr compressor = ICompressor::FindCompressor(compressedFmt, isSrcUncompressed);
if (compressor == nullptr)
{
//no avaible compressor for compressed format
AZ_Warning("Image Processing", false, "No avaliable compressor for pixel format %d", compressedFmt);
return;
}
//check if the uncompressed fmt also supported by the compressor
EPixelFormat desiredUncompressedFmt = compressor->GetSuggestedUncompressedFormat(compressedFmt, uncompressedFmt);
if (desiredUncompressedFmt != uncompressedFmt)
{
//we need to do intermedia convertion to convert to the temperory format
ConvertFormat(desiredUncompressedFmt);
ConvertFormat(fmtDst);
}
else
{
IImageObjectPtr dstImage = nullptr;
if (isSrcUncompressed)
{
dstImage = compressor->CompressImage(Get(), fmtDst, &m_compressOption);
}
else
{
dstImage = compressor->DecompressImage(Get(), fmtDst);
}
Set(dstImage);
}
if (Get() == nullptr)
{
AZ_Error("Image Processing", false, "The selected compressor failed to compress this image");
}
}
}
void ImageToProcess::ConvertFormatUncompressed(EPixelFormat fmtTo)
{
IImageObjectPtr srcImage = m_img;
EPixelFormat srcFmt = srcImage->GetPixelFormat();
EPixelFormat dstFmt = fmtTo;
if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcFmt)
&& CPixelFormats::GetInstance().IsPixelFormatUncompressed(dstFmt)))
{
AZ_Assert(false, "both source and dest images' pixel format need to be uncompressed");
return;
}
IImageObjectPtr dstImage(m_img->AllocateImage(fmtTo));
AZ_Assert(srcImage->GetPixelCount(0) == dstImage->GetPixelCount(0), "dest image has different size than source image");
//create pixel operation function for src and dst images
IPixelOperationPtr srcOp = CreatePixelOperation(srcFmt);
IPixelOperationPtr dstOp = CreatePixelOperation(dstFmt);
//get count of bytes per pixel for both src and dst images
uint32 srcPixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(srcFmt)->bitsPerBlock / 8;
uint32 dstPixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(dstFmt)->bitsPerBlock / 8;
const uint32 dwMips = dstImage->GetMipCount();
float r, g, b, a;
for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
{
uint8* srcPixelBuf;
uint32 srcPitch;
srcImage->GetImagePointer(dwMip, srcPixelBuf, srcPitch);
uint8* dstPixelBuf;
uint32 dstPitch;
dstImage->GetImagePointer(dwMip, dstPixelBuf, dstPitch);
const uint32 pixelCount = srcImage->GetPixelCount(dwMip);
for (uint32 i = 0; i < pixelCount; ++i, srcPixelBuf += srcPixelBytes, dstPixelBuf += dstPixelBytes)
{
srcOp->GetRGBA(srcPixelBuf, r, g, b, a);
dstOp->SetRGBA(dstPixelBuf, r, g, b, a);
}
}
m_img = dstImage;
}
} // namespace ImageProcessing

@ -1,620 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageToProcess.h>
#include <Processing/PixelFormatInfo.h>
#include <Processing/ImageConvert.h>
#include <Processing/ImageFlags.h>
#include <Compressors/Compressor.h>
#include <Converters/PixelOperation.h>
#include <Converters/Cubemap.h>
#include <CubeMapGen/CCubeMapProcessor.h>
namespace ImageProcessing
{
CubemapLayoutInfo CubemapLayout::s_layoutList[CubemapLayoutTypeCount];
template <class TInteger>
inline bool IsPowerOfTwo(TInteger x)
{
return (x & (x - 1)) == 0;
}
CubemapLayoutInfo::CubemapLayoutInfo()
: m_type(CubemapLayoutNone)
, m_rows(0)
, m_columns(0)
{
}
void CubemapLayoutInfo::SetFaceInfo(CubemapFace face, AZ::u8 row, AZ::u8 col, CubemapFaceDirection dir)
{
m_faceInfos[face].row = row;
m_faceInfos[face].column = col;
m_faceInfos[face].direction = dir;
}
void CubemapLayout::InitCubemapLayoutInfos()
{
//CubemapLayoutHorizontal
//left , right, front, back, top, bottom;
//NOTE: this layout is widely used in game projects by Jan 2018 since other layouts weren't supported correctly
//but the faces in one has unusual directions compare to other format.
//The direction matters when using it as input for Cubemap generation filter.
//Left: rotated left 90 degree. Right: rotated right 90 degree
//Front: rotated 180 degree. Back: no rotation
//Top: rotate 180 degree. Bottom: no rotation
CubemapLayoutInfo *info = &s_layoutList[CubemapLayoutHorizontal];
info->m_rows = 1;
info->m_columns = 6;
info->m_type = CubemapLayoutHorizontal;
info->SetFaceInfo(FaceLeft, 0, 0, CubemapFaceDirection::DirRotateLeft90);
info->SetFaceInfo(FaceRight, 0, 1, CubemapFaceDirection::DirRotateRight90);
info->SetFaceInfo(FaceFront, 0, 2, CubemapFaceDirection::DirRotate180);
info->SetFaceInfo(FaceBack, 0, 3, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceTop, 0, 4, CubemapFaceDirection::DirRotate180);
info->SetFaceInfo(FaceBottom, 0, 5, CubemapFaceDirection::DirNoRotation);
//CubemapLayoutHorizontalCross
// top
// left front right back
// bottom
info = &s_layoutList[CubemapLayoutHorizontalCross];
info->m_rows = 3;
info->m_columns = 4;
info->m_type = CubemapLayoutHorizontalCross;
info->SetFaceInfo(FaceLeft, 1, 0, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceRight, 1, 2, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceFront, 1, 1, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceBack, 1, 3, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceTop, 0, 1, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceBottom, 2, 1, CubemapFaceDirection::DirNoRotation);
//CubemapLayoutVerticalCross
// top
// left front right
// bottom
// back
info = &s_layoutList[CubemapLayoutVerticalCross];
info->m_rows = 4;
info->m_columns = 3;
info->m_type = CubemapLayoutVerticalCross;
info->SetFaceInfo(FaceLeft, 1, 0, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceRight, 1, 2, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceFront, 1, 1, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceBack, 3, 1, CubemapFaceDirection::DirRotate180);
info->SetFaceInfo(FaceTop, 0, 1, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceBottom, 2, 1, CubemapFaceDirection::DirNoRotation);
//CubemapLayoutVertical
// left
// right
// front
// back
// top
// bottom
info = &s_layoutList[CubemapLayoutVertical];
info->m_rows = 6;
info->m_columns = 1;
info->m_type = CubemapLayoutVertical;
info->SetFaceInfo(FaceLeft, 0, 0, CubemapFaceDirection::DirRotateLeft90);
info->SetFaceInfo(FaceRight, 1, 0, CubemapFaceDirection::DirRotateRight90);
info->SetFaceInfo(FaceFront, 2, 0, CubemapFaceDirection::DirRotate180);
info->SetFaceInfo(FaceBack, 3, 0, CubemapFaceDirection::DirNoRotation);
info->SetFaceInfo(FaceTop, 4, 0, CubemapFaceDirection::DirRotate180);
info->SetFaceInfo(FaceBottom, 5, 0, CubemapFaceDirection::DirNoRotation);
//make sure all types were initialized
for (int i = 0; i < CubemapLayoutTypeCount; i++)
{
AZ_Assert(s_layoutList[i].m_type == i, "layout %d is not initialized", i);
}
}
const float* GetTransformMatrix(CubemapFaceDirection dir, bool isInvert)
{
switch (dir)
{
case CubemapFaceDirection::DirNoRotation:
{
static const float mat[] = { 1, 0, 0, 1 };
return mat;
}
case CubemapFaceDirection::DirRotateLeft90:
{
//thelta = 90 degree
//{cos, -sin, sin, cos}
if (isInvert)
{
return GetTransformMatrix(CubemapFaceDirection::DirRotateRight90, false);
}
static const float mat[] = { 0, -1, 1, 0 };
return mat;
}
case CubemapFaceDirection::DirRotateRight90:
{
//thelta = -90 degree
if (isInvert)
{
return GetTransformMatrix(CubemapFaceDirection::DirRotateLeft90, false);
}
static const float mat[] = { 0, 1, -1, 0 };
return mat;
}
case CubemapFaceDirection::DirRotate180:
{
//thelta = 180 degree
static const float mat[] = { -1, 0, 0, -1 };
return mat;
}
case CubemapFaceDirection::DirMirrorHorizontal:
{
static const float mat[] = { 1, 0, 0, -1 };
return mat;
}
default:
{
AZ_Assert(false, "unimplemented direction matrix");
static const float mat[] = { 1, 0, 0, 1 };
return mat;
}
}
}
void TransformImage(CubemapFaceDirection srcDir, CubemapFaceDirection dstDir, const AZ::u8* srcImageBuf,
AZ::u8* dstImageBuf, AZ::u8 bytePerPixel, AZ::u32 rectSize)
{
//get final matrix to transform dst back to src
const float* m1 = GetTransformMatrix(dstDir, true);
const float* m2 = GetTransformMatrix(srcDir, false);
float mtx[4];
mtx[0] = m1[0] * m2[0] + m1[1] * m2[2];
mtx[1] = m1[0] * m2[1] + m1[1] * m2[3];
mtx[2] = m1[2] * m2[0] + m1[3] * m2[2];
mtx[3] = m1[2] * m2[1] + m1[3] * m2[3];
const float* noRotate = GetTransformMatrix(CubemapFaceDirection::DirNoRotation, false);
if (memcmp(noRotate, mtx, 4 * sizeof(float)) == 0)
{
memcpy(dstImageBuf, srcImageBuf, rectSize*rectSize*bytePerPixel);
return;
}
//for each pixel in dst image, find it's location in src and copy the data from there
float halfSize = rectSize / 2;
for (AZ::u32 row = 0; row < rectSize; row++)
{
for (AZ::u32 col = 0; col < rectSize; col++)
{
//coordinate in image center as origin and right as positive X, up as positive Y
float dstX = col + 0.5f - halfSize;
float dstY = halfSize - row - 0.5f;
float srcX = dstX * mtx[0] + dstY * mtx[1];
float srcY = dstX * mtx[2] + dstY * mtx[3];
AZ::u32 srcCol = srcX + halfSize;
AZ::u32 srcRow = halfSize - srcY;
memcpy(&dstImageBuf[(row*rectSize + col)*bytePerPixel],
&srcImageBuf[(srcRow*rectSize + srcCol)*bytePerPixel], bytePerPixel);
}
}
}
CubemapLayout::CubemapLayout()
: m_info(nullptr)
, m_image(nullptr)
, m_faceSize(256)
{
}
CubemapLayout* CubemapLayout::CreateCubemapLayout(IImageObjectPtr image)
{
//only support uncompressed format.
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(image->GetPixelFormat()))
{
AZ_Assert(false, "CubemapLayout only support uncompressed image");
return nullptr;
}
CubemapLayout* layout = nullptr;
CubemapLayoutInfo* info = GetCubemapLayoutInfo(image);
if (info)
{
layout = new CubemapLayout();
layout->m_info = GetCubemapLayoutInfo(image);
layout->m_image = image;
layout->m_faceSize = image->GetWidth(0)/layout->m_info->m_columns;
}
return layout;
}
CubemapLayoutInfo* CubemapLayout::GetCubemapLayoutInfo(CubemapLayoutType type)
{
if (type == CubemapLayoutNone)
{
return nullptr;
}
//if it's never initialized
if (s_layoutList[0].m_type == CubemapLayoutNone)
{
InitCubemapLayoutInfos();
}
return &s_layoutList[type];
}
CubemapLayoutInfo* CubemapLayout::GetCubemapLayoutInfo(IImageObjectPtr image)
{
//if it's never initialized
if (s_layoutList[0].m_type == CubemapLayoutNone)
{
InitCubemapLayoutInfos();
}
if (image == nullptr)
{
return nullptr;
}
uint32 width, height;
width = image->GetWidth(0);
height = image->GetHeight(0);
CubemapLayoutInfo* info = nullptr;
for (int i = 0; i < CubemapLayoutTypeCount; i++)
{
if (width * s_layoutList[i].m_rows == height*s_layoutList[i].m_columns)
{
info = &s_layoutList[i];
//we require the face size need to be power of two
if (IsPowerOfTwo(width / info->m_columns))
{
return info;
}
else
{
return nullptr;
}
}
}
return nullptr;
}
//public functions to get faces information for associated image
AZ::u32 CubemapLayout::GetFaceSize()
{
return m_faceSize;
}
CubemapLayoutInfo* CubemapLayout::GetLayoutInfo()
{
return m_info;
}
CubemapFaceDirection CubemapLayout::GetFaceDirection(CubemapFace face)
{
return m_info->m_faceInfos[face].direction;
}
void CubemapLayout::GetFaceData(CubemapFace face, void* outBuffer, AZ::u32& outSize)
{
//only valid for uncompressed
AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(m_image->GetPixelFormat())->bitsPerBlock / 8;
AZ::u8* imageBuf;
AZ::u32 dwPitch;
m_image->GetImagePointer(0, imageBuf, dwPitch);
AZ::u8* dstBuf = (AZ::u8*)outBuffer;
AZ::u32 startX = m_info->m_faceInfos[face].column * m_faceSize;
AZ::u32 startY = m_info->m_faceInfos[face].row * m_faceSize;
//face size is same as rows for uncompressed format
for (AZ::u32 y = 0; y < m_faceSize; y++)
{
AZ::u32 scanlineSize = m_faceSize*sizePerPixel;
AZ::u8* srcBuf = &imageBuf[(startY + y) * dwPitch + startX*sizePerPixel];
memcpy(dstBuf, srcBuf, scanlineSize);
dstBuf += scanlineSize;
}
outSize = m_faceSize*m_faceSize*sizePerPixel;
}
void CubemapLayout::SetFaceData(CubemapFace face, void* dataBuffer, [[maybe_unused]] AZ::u32 dataSize)
{
//only valid for uncompressed
AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(m_image->GetPixelFormat())->bitsPerBlock / 8;
AZ::u8* imageBuf;
AZ::u32 dwPitch;
m_image->GetImagePointer(0, imageBuf, dwPitch);
AZ::u8* srcBuf = (AZ::u8*)dataBuffer;
AZ::u32 startX = m_info->m_faceInfos[face].column * m_faceSize;
AZ::u32 startY = m_info->m_faceInfos[face].row * m_faceSize;
//face size is same as rows for uncompressed format
for (AZ::u32 y = 0; y < m_faceSize; y++)
{
AZ::u32 scanlineSize = m_faceSize*sizePerPixel;
AZ::u8* dstBuf = &imageBuf[(startY + y) * dwPitch + startX*sizePerPixel];
memcpy(dstBuf, srcBuf, scanlineSize);
srcBuf += scanlineSize;
}
}
void* CubemapLayout::GetFaceMemBuffer(AZ::u32 mip, CubemapFace face, AZ::u32& outPitch)
{
if (CubemapLayoutVertical != m_info->m_type)
{
AZ_Assert(false, "this should only be used for CubemapLayoutVertical which has continous memory for each face");
return nullptr;
}
AZ::u32 faceSize = m_faceSize >> mip;
AZ::u8* imageBuf;
m_image->GetImagePointer(mip, imageBuf, outPitch);
AZ::u32 startY = m_info->m_faceInfos[face].row * faceSize;
//use startY is same as rows from m_image since the pixel format is uncompressed
return &imageBuf[startY * outPitch];
}
void CubemapLayout::SetToFaceMemBuffer(AZ::u32 mip, CubemapFace face, void* dataBuffer)
{
if (CubemapLayoutVertical != m_info->m_type)
{
AZ_Assert(false, "this should only be used for CubemapLayoutVertical which has continuous memory for each face");
return;
}
AZ::u32 faceSize = m_faceSize >> mip;
AZ::u32 pitch;
AZ::u8* imageBuf;
m_image->GetImagePointer(mip, imageBuf, pitch);
AZ::u32 startY = m_info->m_faceInfos[face].row * faceSize;
//use startY is same as rows from m_image since the pixel format is uncompressed
memcpy(&imageBuf[startY * pitch], dataBuffer, faceSize*pitch);
}
void CubemapLayout::GetRectForFace(AZ::u32 mip, CubemapFace face, QRect& outRect)
{
AZ::u32 faceSize = m_faceSize >> mip;
AZ::u32 startY = m_info->m_faceInfos[face].row * faceSize;
AZ::u32 startX = m_info->m_faceInfos[face].column * faceSize;
outRect.setRect(startX, startY, faceSize, faceSize);
}
bool ImageToProcess::ConvertCubemapLayout(CubemapLayoutType dstLayoutType)
{
const EPixelFormat srcPixelFormat = m_img->GetPixelFormat();
//it need to be uncompressed format
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcPixelFormat))
{
AZ_Assert(false, "Please convert the image to uncompressed pixel format before calling ConvertCubemapLayout");
return false;
}
//check if it's valid cubemap size
CubemapLayoutInfo* layoutInfo = CubemapLayout::GetCubemapLayoutInfo(m_img);
if (layoutInfo == nullptr)
{
AZ_Error("Image Processing", false, "The original image doesn't have a valid size (layout) as cubemap");
return false;
}
//if the source is same as output layout, return directly
if (layoutInfo->m_type == dstLayoutType)
{
return true;
}
CubemapLayoutInfo* dstLayoutInfo = CubemapLayout::GetCubemapLayoutInfo(dstLayoutType);
//create cubemap layout for source image for later operation.
CubemapLayout *srcCubemap = CubemapLayout::CreateCubemapLayout(m_img);
AZ::u32 faceSize = srcCubemap->GetFaceSize();
//create new image with same pixel format and copy prperties from source image
IImageObjectPtr newImage(IImageObject::CreateImage(faceSize * dstLayoutInfo->m_columns,
faceSize*dstLayoutInfo->m_rows, 1, srcPixelFormat));
CubemapLayout *dstCubemap = CubemapLayout::CreateCubemapLayout(newImage);
newImage->CopyPropertiesFrom(newImage);
//copy data from src cube to dst cube for each face
//temp buf for copy over data
AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(srcPixelFormat)->bitsPerBlock/8; //only valid for uncompressed
AZ::u8 *buf = new AZ::u8[faceSize*faceSize*sizePerPixel];
AZ::u8 *tempBuf = new AZ::u8[faceSize*faceSize*sizePerPixel];
for (AZ::u32 faceIdx = 0; faceIdx < FaceCount; faceIdx++)
{
AZ::u32 outSize = 0;
CubemapFace face = (CubemapFace)faceIdx;
srcCubemap->GetFaceData(face, buf, outSize);
CubemapFaceDirection srcDir = srcCubemap->GetFaceDirection(face);
CubemapFaceDirection dstDir = dstCubemap->GetFaceDirection(face);
if (srcDir == dstDir)
{
dstCubemap->SetFaceData(face, buf, outSize);
}
else
{
//transform the image
TransformImage(srcDir, dstDir, buf, tempBuf, sizePerPixel, faceSize);
dstCubemap->SetFaceData(face, tempBuf, outSize);
}
}
//clean up
delete[] buf;
delete[] tempBuf;
delete srcCubemap;
delete dstCubemap;
newImage->AddImageFlags(EIF_Cubemap);
m_img = newImage;
return true;
}
bool ImageConvertProcess::FillCubemapMipmaps()
{
//this function only works with pixel format rgba32f
const EPixelFormat srcPixelFormat = m_image->Get()->GetPixelFormat();
if (srcPixelFormat != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "%s only works with pixel format rgba32f", __FUNCTION__);
return false;
}
//only if the src image has one mip
if (m_image->Get()->GetMipCount() != 1)
{
AZ_Assert(false, "%s called for a mipmapped image. ", __FUNCTION__);
return false;
}
CubemapLayout *srcCubemap = CubemapLayout::CreateCubemapLayout(m_image->Get());
uint32 outWidth;
uint32 outHeight;
uint32 outReduce = 0;
AZ::u32 srcFaceSize = srcCubemap->GetFaceSize();
//get output face size
GetOutputExtent(srcFaceSize, srcFaceSize, outWidth, outHeight, outReduce, &m_textureSetting, &m_presetSetting);
AZ_Assert(outWidth == outHeight, "something wrong with GetOutputExtent function");
//get final cubemap image size
outWidth *= srcCubemap->GetLayoutInfo()->m_columns;
outHeight *= srcCubemap->GetLayoutInfo()->m_rows;
//max mipmap count
uint32 maxMipCount;
if (m_presetSetting.m_mipmapSetting == nullptr || !m_textureSetting.m_enableMipmap)
{
maxMipCount = 1;
}
else
{
//calculate based on face size, and use final export format which may save some low level mip calculation
maxMipCount = CPixelFormats::GetInstance().ComputeMaxMipCount(m_presetSetting.m_pixelFormat, srcFaceSize, srcFaceSize);
//the FilterImage function won't do well with rect size 1. avoiding cubemap with face size 1
if (srcFaceSize >> maxMipCount == 1 && maxMipCount > 1)
{
maxMipCount -= 1;
}
}
//create new new output image with proper face
IImageObjectPtr outImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat));
outImage->CopyPropertiesFrom(m_image->Get());
CubemapLayout *dstCubemap = CubemapLayout::CreateCubemapLayout(outImage);
AZ::u32 outFaceSize = dstCubemap->GetFaceSize();
AZ::u32 dstMipCount = outImage->GetMipCount();
//filter the image for top mip first
for (int iSide = 0; iSide < 6; ++iSide)
{
QRect srcRect;
QRect dstRect;
srcRect.setLeft(0);
srcRect.setRight(srcFaceSize);
srcRect.setTop(iSide * srcFaceSize);
srcRect.setBottom((iSide + 1) * srcFaceSize);
dstRect.setLeft(0);
dstRect.setRight(outFaceSize);
dstRect.setTop(iSide * outFaceSize);
dstRect.setBottom((iSide + 1) * outFaceSize);
FilterImage(m_textureSetting.m_mipGenType, m_textureSetting.m_mipGenEval, 0, 0, m_image->Get(), 0,
outImage, 0, &srcRect, &dstRect);
}
CCubeMapProcessor atiCubemanGen;
//ATI's cubemap generator to filter the image edges to avoid seam problem
// https://gpuopen.com/archive/gamescgi/cubemapgen/
//the thread support was done with windows thread function so it's removed for multi-dev platform support
atiCubemanGen.m_NumFilterThreads = 0;
// input and output cubemap set to have save dimensions,
atiCubemanGen.Init(outFaceSize, outFaceSize, dstMipCount, 4);
// Load the 6 faces of the input cubemap and copy them into the cubemap processor
void* pMem;
uint32 nPitch;
for (int iFace = 0; iFace < 6; ++iFace)
{
pMem = dstCubemap->GetFaceMemBuffer(0, (CubemapFace)iFace, nPitch);
atiCubemanGen.SetInputFaceData(
iFace, // FaceIdx,
CP_VAL_FLOAT32, // SrcType,
4, // SrcNumChannels,
nPitch, // SrcPitch,
pMem, // SrcDataPtr,
1000000.0f, // MaxClamp,
1.0f, // Degamma,
1.0f); // Scale
}
//Filter cubemap
atiCubemanGen.InitiateFiltering(
m_presetSetting.m_cubemapSetting->m_angle, //BaseFilterAngle,
m_presetSetting.m_cubemapSetting->m_mipAngle, //InitialMipAngle,
m_presetSetting.m_cubemapSetting->m_mipSlope, //MipAnglePerLevelScale,
(int)m_presetSetting.m_cubemapSetting->m_filter, //FilterType, CP_FILTER_TYPE_COSINE for diffuse cube
m_presetSetting.m_cubemapSetting->m_edgeFixup > 0? CP_FIXUP_PULL_LINEAR : CP_FIXUP_NONE, //FixupType, CP_FIXUP_PULL_LINEAR if FixupWidth> 0
m_presetSetting.m_cubemapSetting->m_edgeFixup, //FixupWidth,
true, //bUseSolidAngle,
16, //GlossScale,
0, //GlossBias
128); //SampleCountGGX
// Download data into it
for (int iFace = 0; iFace < 6; ++iFace)
{
for (unsigned int dstMip = 0; dstMip < dstMipCount; ++dstMip)
{
pMem = dstCubemap->GetFaceMemBuffer(dstMip, (CubemapFace)iFace, nPitch);
atiCubemanGen.GetOutputFaceData(iFace, dstMip, CP_VAL_FLOAT32, 4, nPitch, pMem, 1.0f, 1.0f);
}
}
delete srcCubemap;
delete dstCubemap;
//set back to image
m_image->Set(outImage);
return true;
}
} // namespace ImageProcessing

@ -1,118 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Processing/ImageToProcess.h>
namespace ImageProcessing
{
// note: O3DE is right hand Z up coordinate
// please don't change the order of the enum since we are using it to match the face id defined in AMD's CubemapGen
// and they are using left hand Y up coordinate
enum CubemapFace
{
FaceLeft = 0,
FaceRight,
FaceFront,
FaceBack,
FaceTop,
FaceBottom,
FaceCount
};
//we are treating the orientation of faces in 4x3 layout as the original direction.
enum class CubemapFaceDirection
{
DirNoRotation = 0,
DirRotateLeft90,
DirRotateRight90,
DirRotate180,
DirMirrorHorizontal
};
//this class contains information to describe a cubemap layout
class CubemapLayoutInfo
{
public:
struct FaceInfo
{
AZ::u8 row;
AZ::u8 column;
CubemapFaceDirection direction;
};
//rows and columns of how cubemap's faces laid
AZ::u8 m_rows;
AZ::u8 m_columns;
//the type of this layout info for
CubemapLayoutType m_type;
//the index of row and column where all the faces located
FaceInfo m_faceInfos[FaceCount];
CubemapLayoutInfo();
void SetFaceInfo(CubemapFace face, AZ::u8 row, AZ::u8 col, CubemapFaceDirection dir);
};
//class to help doing operations with faces for an image as cubemap
class CubemapLayout
{
public:
//create a cubemapLayout object for the image. It can be used later to get image information as a cubemap
static CubemapLayout* CreateCubemapLayout(IImageObjectPtr image);
//get layout info for input layout type
static CubemapLayoutInfo* GetCubemapLayoutInfo(CubemapLayoutType type);
//get layout info for input image based on its size
static CubemapLayoutInfo* GetCubemapLayoutInfo(IImageObjectPtr image);
//public functions to get faces information for associated image
AZ::u32 GetFaceSize();
//get the rect where the face in the image
void GetRectForFace(AZ::u32 mip, CubemapFace face, QRect& outRect);
CubemapLayoutInfo* GetLayoutInfo();
//set/get pixels' data from/to specific face. only works for mip 0
void GetFaceData(CubemapFace face, void* outBuffer, AZ::u32& outSize);
void SetFaceData(CubemapFace face, void* dataBuffer, AZ::u32 dataSize);
//get the face's direction
CubemapFaceDirection GetFaceDirection(CubemapFace face);
//get memory for a face from Image data. only works for CubemapLayoutVertical since its memory for each face is continuous
void* GetFaceMemBuffer(AZ::u32 mip, CubemapFace face, AZ::u32& outPitch);
void SetToFaceMemBuffer(AZ::u32 mip, CubemapFace face, void* dataBuffer);
private:
//information for all supported cubemap layouts
static CubemapLayoutInfo s_layoutList[CubemapLayoutTypeCount];
//the image associated for this CubemapLayout
IImageObjectPtr m_image;
//the layout information of m_image
CubemapLayoutInfo *m_info;
//the size of the cubemap's face (which is square and power of 2).
uint32 m_faceSize;
//private constructor. User should always use CreateCubemapLayout create a layout for an image object
CubemapLayout();
//initialize information of all available cubemap layouts
static void InitCubemapLayoutInfos();
};
}//end namspace ImageProcessing

File diff suppressed because it is too large Load Diff

@ -1,329 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <ImageProcessing_precompiled.h>
#include <math.h>
#include "FIR-Weights.h"
/* ####################################################################################################################
*/
namespace ImageProcessing
{
void calculateFilterRange(unsigned int srcFactor, int& srcFirst, int& srcLast,
unsigned int dstFactor, int dstFirst, int dstLast,
double blurFactor, class IWindowFunction<double>* windowFunction)
{
double s, t, u, scaleFactor; /* scale factors */
double srcRadius, srcCenter; /* window position and size */
#define s0 0
#define s1 srcFactor
#define d0 0
#define d1 dstFactor
/* the mapping from discrete destination coordinates to continuous source coordinates: */
#define MAP(b, scaleFactor, offset) ((b) + (offset)) / (scaleFactor)
/* relation of dstFactor to srcFactor */
s = (double)dstFactor / srcFactor;
t = d0 - s * (s0 - 0.5) - 0.5;
/* compute offsets for MAP */
u = d0 - s * (s0 - 0.5) - t;
/* find scale of filter
* when minifying, scaleFactor = 1/s, but when magnifying, scaleFactor = 1
*/
scaleFactor = (blurFactor == 0.0 ? 1.0 : (blurFactor > 0.0 ? (1.0 + blurFactor) : 1.0 / (1.0 - blurFactor))) * maximum(1., 1. / s);
/* find support radius of scaled filter
* if the window's length is <= 0.5 then we've got point sampling.
*/
srcRadius = maximum(0.5, scaleFactor * windowFunction->getLength());
/* sample the continuous filter, scaled by scaleFactor and
* positioned at continuous source coordinate srcCenter
*/
{
srcCenter = MAP(dstFirst + 0, s, u);
/* find the source coordinate range of this positioned filter window */
srcFirst = int(floor(srcCenter - srcRadius + 0.5));
}
{
srcCenter = MAP(dstLast - 1, s, u);
/* find the source coordinate range of this positioned filter window */
srcLast = int(floor(srcCenter + srcRadius + 0.5));
}
}
template<>
FilterWeights<signed short int>* calculateFilterWeights<signed short int>(unsigned int srcFactor, int srcFirst, int srcLast,
unsigned int dstFactor, int dstFirst, int dstLast, signed short int numRepetitions,
double blurFactor, class IWindowFunction<double>* windowFunction,
bool peaknorm, bool& plusminus)
{
#define WEIGHTBITS 15
#define WEIGHTONE (1 << WEIGHTBITS) /* filter weight of one */
double s, t, u, scaleFactor; /* scale factors */
double srcRadius, srcCenter; /* window position and size */
double sumfWeights, neg, pos, nrmWeights, fWeight; /* window position and size */
int i, i0, i1; /* window position and size */
int dstPosition;
signed short int n;
bool trimZeros = true, stillzero;
int lastnonzero, hWeight, highest;
signed int sumiWeights, iWeight;
signed short int* weightsPtr, *weightsMem;
FilterWeights<signed short int>* weightsObjs;
bool pm, pma = false;
/* pre-calculate filter window solutions for all rows
*/
weightsObjs = new FilterWeights<signed short int>[dstLast - dstFirst];
#define s0 0
#define s1 srcFactor
#define d0 0
#define d1 dstFactor
/* relation of dstFactor to srcFactor */
s = (double)dstFactor / srcFactor;
t = d0 - s * (s0 - 0.5) - 0.5;
/* compute offsets for MAP */
u = d0 - s * (s0 - 0.5) - t;
/* find scale of filter
* when minifying, scaleFactor = 1/s, but when magnifying, scaleFactor = 1
*/
scaleFactor = (blurFactor == 0.0 ? 1.0 : (blurFactor > 0.0 ? (1.0 + blurFactor) : 1.0 / (1.0 - blurFactor))) * maximum(1., 1. / s);
/* find support radius of scaled filter
* if the window's length is <= 0.5 then we've got point sampling.
*/
srcRadius = maximum(0.5, scaleFactor * windowFunction->getLength());
/* sample the continuous filter, scaled by ap->scaleFactor and
* positioned at continuous source coordinate srcCenter, for source coordinates in
* the range [0..len-1], writing the weights into wtab.
* Scale the weights so they sum up to WEIGHTONE, and trim leading and trailing
* zeros if trimZeros is true.
*/
#undef NORMALIZE_SUMMED_PEAK
#define NORMALIZE_MAXXED_PEAK
for (dstPosition = dstFirst, pm = false; dstPosition < dstLast; dstPosition++)
{
srcCenter = MAP(dstPosition, s, u);
/* find the source coordinate range of this positioned filter window */
i0 = int(floor(srcCenter - srcRadius + 0.5));
i1 = int(floor(srcCenter + srcRadius + 0.5));
/* clip against the source-range */
if (i0 < srcFirst)
{
i0 = srcFirst;
}
if (i1 > srcLast)
{
i1 = srcLast;
}
/* this is possible if we hit the final line */
if (i1 <= i0)
{
if (i1 >= srcLast)
{
i0 = i1 - 1;
}
else
{
i1 = i0 + 1;
}
}
AZ_Assert(i0 >= srcFirst, "%s: Invalid source coordinate range!", __FUNCTION__);
AZ_Assert(i1 <= srcLast, "%s: Invalid source coordinate range!", __FUNCTION__);
AZ_Assert(i0 < i1, "%s: Invalid source coordinate range!", __FUNCTION__);
/* find maximum peak to normalize the filter */
for (sumfWeights = 0, pos = 0, neg = 0, i = i0; i < i1; i++)
{
/* evaluate the filter function: */
fWeight = (*windowFunction)((i + 0.5 - srcCenter) / scaleFactor);
#if defined(NORMALIZE_SUMMED_PEAK)
/* get positive and negative summed peaks */
if (fWeight >= 0)
{
pos += fWeight;
}
else
{
neg += fWeight;
}
#elif defined(NORMALIZE_MAXXED_PEAK)
/* get positive and negative maximum peaks */
minmax(fWeight, neg, pos);
#endif
sumfWeights += fWeight;
}
/* the range of source samples to buffer: */
weightsMem = new signed short int[(i1 - i0) * abs(numRepetitions)];
/* set nrmWeights so that sumWeights of windowFunction() is approximately WEIGHTONE
* this needs to be adjusted because the maximum weight-coefficient
* is NOT allowed to leave [-32768,32767]
* a case like {+1.25,-0.25} does produce a sumWeights of 1.0 BUT
* produced a weight much too high (-40000)
*/
#if defined(NORMALIZE_SUMMED_PEAK)
sumfWeights = maximum(-neg, pos);
#elif defined(NORMALIZE_MAXXED_PEAK)
sumfWeights = maximum(sumfWeights, maximum(-neg, pos));
#endif
if (!peaknorm)
{
nrmWeights = (sumfWeights == 0. ? WEIGHTONE : (-neg > pos ? WEIGHTONE - 1 : WEIGHTONE) / sumfWeights);
}
else
{
nrmWeights = (sumfWeights == 0. ? WEIGHTONE : (-neg > pos ? WEIGHTONE - 1 : WEIGHTONE) / maximum(-neg, pos));
}
/* compute the discrete, sampled filter coefficients */
stillzero = trimZeros;
for (sumiWeights = 0, hWeight = -WEIGHTONE, weightsPtr = weightsMem, i = i0; i < i1; i++)
{
/* evaluate the filter function: */
fWeight = (*windowFunction)((i + 0.5 - srcCenter) / scaleFactor);
/* normalize against the peak sumWeights, because the sums are not allowed to leave -32768/32767 */
fWeight = fWeight * nrmWeights;
iWeight = int(round(fWeight));
/* find first nonzero */
if (stillzero && (iWeight == 0))
{
i0++;
}
else
{
AZ_Assert((-fWeight >= -32768.5) && (-fWeight <= 32767.5), "%s:The weight exceeded the maximum weight-coefficient.", __FUNCTION__);
if (!peaknorm)
{
sumiWeights += iWeight;
}
else
{
sumiWeights = maximum(sumiWeights, iWeight);
}
#define sgnextend(n, iWeight) (n & 1 ? (iWeight < 0 ? -1 : 0) : iWeight)
if (numRepetitions < 0)
{
/* add weight to table, interleaved sign */
for (n = 0; n < -numRepetitions; n++)
{
*weightsPtr++ = sgnextend(n, -iWeight);
}
}
else
{
/* add weight to table */
for (n = 0; n < numRepetitions; n++)
{
*weightsPtr++ = -iWeight;
}
}
stillzero = false;
/* find last nonzero */
if (iWeight != 0)
{
lastnonzero = i;
}
/* check for negative values */
if (iWeight < 0)
{
pm = pma = true;
}
/* find most influential value */
if (iWeight >= hWeight)
{
highest = i;
hWeight = iWeight;
}
}
}
if (sumiWeights == 0)
{
i0 = (i0 + i1) >> 1;
i1 = (i0 + 1);
for (n = 0, weightsPtr = weightsMem; n < numRepetitions; n++)
{
*weightsPtr++ = -WEIGHTONE;
}
}
else
{
/* skip leading and trailing zeros */
if (trimZeros)
{
/* set i0 and i1 to the nonzero support of the filter */
i0 = i0;
i1 = i1 = lastnonzero + 1;
}
if (sumiWeights != WEIGHTONE)
{
/* Fudge with the highest value */
i = highest;
/* fudge srcCenter sample */
iWeight = WEIGHTONE - sumiWeights;
for (n = 0, weightsPtr = weightsMem + (i - i0) * numRepetitions; n < numRepetitions; n++)
{
*weightsPtr++ -= iWeight;
}
}
}
/* the new adjusted range of source samples to buffer: */
weightsObjs[dstPosition].first = i0;
weightsObjs[dstPosition].last = i1;
weightsObjs[dstPosition].hasNegativeWeights = pm;
weightsObjs[dstPosition].weights = weightsMem;
}
plusminus = pma;
return weightsObjs;
}
}

@ -1,83 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "FIR-Windows.h"
namespace ImageProcessing
{
/* ####################################################################################################################
*/
template<class DataType>
inline DataType abs (const DataType& ths) { return (ths < 0 ? -ths : ths); }
template<class DataType>
inline void minmax (const DataType& ths, DataType& mn, DataType& mx) { mn = (mn > ths ? ths : mn); mx = (mx < ths ? ths : mx); }
template<class DataType>
inline DataType minimum(const DataType& ths, const DataType& tht) { return (ths < tht ? ths : tht); }
template<class DataType>
inline DataType maximum(const DataType& ths, const DataType& tht) { return (ths > tht ? ths : tht); }
/* ####################################################################################################################
*/
template<class T>
class FilterWeights
{
public:
FilterWeights()
: weights(nullptr)
{
}
~FilterWeights()
{
delete[] weights;
}
public:
// window-position
int first, last;
// do we encounter positive as well as negative weights
bool hasNegativeWeights;
/* weights, summing up to -(1 << 15),
* means weights are given negative
* that enables us to use signed short
* multiplication while occupying 0x8000
*/
T* weights;
};
/* ####################################################################################################################
*/
void calculateFilterRange (unsigned int srcFactor, int& srcFirst, int& srcLast,
unsigned int dstFactor, int dstFirst, int dstLast,
double blurFactor, class IWindowFunction<double>* windowFunction);
template<typename T>
FilterWeights<T>* calculateFilterWeights(unsigned int srcFactor, int srcFirst, int srcLast,
unsigned int dstFactor, int dstFirst, int dstLast, signed short int numRepetitions,
double blurFactor, class IWindowFunction<double>* windowFunction,
bool peaknorm, bool& plusminus);
template<>
FilterWeights<signed short int>* calculateFilterWeights<signed short int>(unsigned int srcFactor, int srcFirst, int srcLast,
unsigned int dstFactor, int dstFirst, int dstLast, signed short int numRepetitions,
double blurFactor, class IWindowFunction<double>* windowFunction,
bool peaknorm, bool& plusminus);
} //end namespace ImageProcessing

File diff suppressed because it is too large Load Diff

@ -1,260 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing/ImageObject.h>
#include <Processing/ImageToProcess.h>
#include <Processing/PixelFormatInfo.h>
#include <ImageProcessing/PixelFormats.h>
#include <Converters/FIR-Weights.h>
#include <Converters/PixelOperation.h>
#include <Processing/ImageFlags.h>
namespace ImageProcessing
{
///////////////////////////////////////////////////////////////////////////////////
// Lookup table for a function 'float fn(float x)'.
// Computed function values are stored in the table for x in [0.0; 1.0].
//
// If passed x is less than xMin (xMin must be >= 0) or greater than 1.0,
// then the original function is called.
// Otherwise, a value from the table (linearly interpolated)
// is returned.
template <int TABLE_SIZE>
class FunctionLookupTable
{
public:
FunctionLookupTable(float(*fn)(float x), float xMin, float maxAllowedDifference)
: m_fn(fn)
, m_xMin(xMin)
, m_fMaxDiff(maxAllowedDifference)
{
}
void Initialize() const
{
m_initialized = true;
AZ_Assert(m_xMin >= 0.0f, "wrong initial data for m_xMin");
for (int i = 0; i <= TABLE_SIZE; ++i)
{
const float x = i / (float)TABLE_SIZE;
const float y = (*m_fn)(x);
m_table[i] = y;
}
}
inline float compute(float x) const
{
if (x < m_xMin || x > 1)
{
return m_fn(x);
}
const float f = x * TABLE_SIZE;
const int i = int(f);
if (!m_initialized)
{
Initialize();
}
if (i >= TABLE_SIZE)
{
return m_table[TABLE_SIZE];
}
const float alpha = f - i;
return (1 - alpha) * m_table[i] + alpha * m_table[i + 1];
}
public:
bool Test(const float maxDifferenceAllowed) const
{
if (int(-0.99f) != 0 ||
int(+0.00f) != 0 ||
int(+0.01f) != 0 ||
int(+0.99f) != 0 ||
int(+1.00f) != 1 ||
int(+1.01f) != 1 ||
int(+1.99f) != 1 ||
int(+2.00f) != 2 ||
int(+2.01f) != 2)
{
return false;
}
if (m_xMin < 0)
{
return false;
}
const int n = 1000000;
for (int i = 0; i <= n; ++i)
{
const float x = 1.1f * (i / (float)n);
const float resOriginal = m_fn(x);
const float resTable = compute(x);
const float difference = resOriginal - resTable;
if (fabs(difference) > maxDifferenceAllowed)
{
return false;
}
}
return true;
}
private:
float(*m_fn)(float x);
float m_xMin;
mutable float m_table[TABLE_SIZE + 1];
mutable bool m_initialized = false;
float m_fMaxDiff = 0.0f;
};
static float GammaToLinear(float x)
{
return (x <= 0.04045f) ? x / 12.92f : powf((x + 0.055f) / 1.055f, 2.4f);
}
static float LinearToGamma(float x)
{
return (x <= 0.0031308f) ? x * 12.92f : 1.055f * powf(x, 1.0f / 2.4f) - 0.055f;
}
static FunctionLookupTable<1024> s_lutGammaToLinear(GammaToLinear, 0.04045f, 0.00001f);
static FunctionLookupTable<1024> s_lutLinearToGamma(LinearToGamma, 0.05f, 0.00001f);
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
bool ImageToProcess::GammaToLinearRGBA32F(bool bDeGamma)
{
// return immediately if there is no need to de-gamma image and the source is in the desired format
EPixelFormat srcFmt = m_img->GetPixelFormat();
if (!bDeGamma && (srcFmt == ePixelFormat_R32G32B32A32F))
{
return true;
}
//convert to 32F first
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcFmt))
{
AZ_Warning("Image Processing", false, "This is not common user case with compressed format input. But it may continue");
ConvertFormat(ePixelFormat_R32G32B32A32F);
}
IImageObjectPtr srcImage = m_img;
EPixelFormat dstFmt = ePixelFormat_R32G32B32A32F;
IImageObjectPtr dstImage(m_img->AllocateImage(dstFmt));
//create pixel operation function for src and dst images
IPixelOperationPtr srcOp = CreatePixelOperation(srcFmt);
IPixelOperationPtr dstOp = CreatePixelOperation(dstFmt);
//get count of bytes per pixel for both src and dst images
uint32 srcPixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(srcFmt)->bitsPerBlock / 8;
uint32 dstPixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(dstFmt)->bitsPerBlock / 8;
const uint32 dwMips = dstImage->GetMipCount();
float r, g, b, a;
for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
{
uint8* srcPixelBuf;
uint32 srcPitch;
srcImage->GetImagePointer(dwMip, srcPixelBuf, srcPitch);
uint8* dstPixelBuf;
uint32 dstPitch;
dstImage->GetImagePointer(dwMip, dstPixelBuf, dstPitch);
const uint32 pixelCount = srcImage->GetPixelCount(dwMip);
for (uint32 i = 0; i < pixelCount; ++i, srcPixelBuf += srcPixelBytes, dstPixelBuf += dstPixelBytes)
{
srcOp->GetRGBA(srcPixelBuf, r, g, b, a);
if (bDeGamma)
{
r = s_lutGammaToLinear.compute(r);
g = s_lutGammaToLinear.compute(g);
b = s_lutGammaToLinear.compute(b);
}
dstOp->SetRGBA(dstPixelBuf, r, g, b, a);
}
}
m_img = dstImage;
if (bDeGamma)
{
m_img->RemoveImageFlags(EIF_SRGBRead);
}
return true;
}
void ImageToProcess::LinearToGamma()
{
if (Get()->HasImageFlags(EIF_SRGBRead))
{
AZ_Assert(false, "%s: input image is already SRGB", __FUNCTION__);
return;
}
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_img->GetPixelFormat()))
{
AZ_Assert(false, "This is not common user case with compressed format input. But it may continue");
ConvertFormat(ePixelFormat_R32G32B32A32F);
}
EPixelFormat srcFmt = m_img->GetPixelFormat();
IImageObjectPtr srcImage = m_img;
IImageObjectPtr dstImage(m_img->AllocateImage(srcFmt));
//create pixel operation function
IPixelOperationPtr pixelOp = CreatePixelOperation(srcFmt);
//get count of bytes per pixel for both src and dst images
uint32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(srcFmt)->bitsPerBlock / 8;
const uint32 dwMips = srcImage->GetMipCount();
float r, g, b, a;
for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
{
uint8* srcPixelBuf;
uint32 srcPitch;
srcImage->GetImagePointer(dwMip, srcPixelBuf, srcPitch);
uint8* dstPixelBuf;
uint32 dstPitch;
dstImage->GetImagePointer(dwMip, dstPixelBuf, dstPitch);
const uint32 pixelCount = srcImage->GetPixelCount(dwMip);
for (uint32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes)
{
pixelOp->GetRGBA(srcPixelBuf, r, g, b, a);
r = s_lutLinearToGamma.compute(r);
g = s_lutLinearToGamma.compute(g);
b = s_lutLinearToGamma.compute(b);
pixelOp->SetRGBA(dstPixelBuf, r, g, b, a);
}
}
m_img = dstImage;
Get()->AddImageFlags(EIF_SRGBRead);
}
} // namespace ImageProcessing

@ -1,110 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageToProcess.h>
#include <Processing/ImageConvert.h>
#include <Processing/PixelFormatInfo.h>
#include <Converters/FIR-Windows.h>
#include <Converters/PixelOperation.h>
namespace ImageProcessing
{
// higher mip level is subtracted by lower mip level when applying the [cheap] high pass filter
void ImageToProcess::CreateHighPass(AZ::u32 dwMipDown)
{
//no need to convert if mip go down 0
if (dwMipDown == 0)
{
return;
}
const EPixelFormat ePixelFormat = m_img->GetPixelFormat();
if (ePixelFormat != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "You need convert the orginal image to ePixelFormat_R32G32B32A32F before call this function");
return;
}
AZ::u32 dwWidth, dwHeight, dwMips;
dwWidth = m_img->GetWidth(0);
dwHeight = m_img->GetHeight(0);
dwMips = m_img->GetMipCount();
if (dwMipDown >= dwMips)
{
AZ_Warning("Image Processing", false, "CreateHighPass can't go down %i MIP levels for high pass as there are not\
enough MIP levels available, going down by %i instead", dwMipDown, dwMips - 1);
dwMipDown = dwMips - 1;
}
IImageObjectPtr newImage(IImageObject::CreateImage(dwWidth, dwHeight, dwMips, ePixelFormat));
newImage->CopyPropertiesFrom(m_img);
IPixelOperationPtr pixelOp = CreatePixelOperation(ePixelFormat);
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(ePixelFormat)->bitsPerBlock / 8;
AZ::u32 dstMips = newImage->GetMipCount();
for (AZ::u32 dstMip = 0; dstMip < dwMipDown; ++dstMip)
{
// linear interpolation
FilterImage(MipGenType::triangle, MipGenEvalType::sum, 0.0f, 0.0f, m_img, dwMipDown, newImage, dstMip, NULL, NULL);
const AZ::u32 pixelCountIn = m_img->GetWidth(dstMip) *m_img->GetHeight(dstMip);
const AZ::u32 pixelCountOut = newImage->GetWidth(dstMip) * newImage->GetHeight(dstMip);
//substraction
AZ::u8* srcPixelBuf;
AZ::u32 srcPitch;
m_img->GetImagePointer(dstMip, srcPixelBuf, srcPitch);
AZ::u8* dstPixelBuf;
AZ::u32 dstPitch;
newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch);
const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip);
for (AZ::u32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes)
{
float r1, g1, b1, a1, r2, g2, b2, a2;
pixelOp->GetRGBA(srcPixelBuf, r1, g1, b1, a1);
pixelOp->GetRGBA(dstPixelBuf, r2, g2, b2, a2);
r2 = AZ::GetClamp<float>(r1 - r2 + 0.5f, 0.0f, 1.0f);
g2 = AZ::GetClamp<float>(g1 - g2 + 0.5f, 0.0f, 1.0f);
b2 = AZ::GetClamp<float>(b1 - b2 + 0.5f, 0.0f, 1.0f);
a2 = AZ::GetClamp<float>(a1 - a2 + 0.5f, 0.0f, 1.0f);
pixelOp->SetRGBA(dstPixelBuf, r2, g2, b2, a2);
}
}
// mips below the chosen highpass mip are grey
for (AZ::u32 dstMip = dwMipDown; dstMip < dstMips; ++dstMip)
{
AZ::u8* dstPixelBuf;
AZ::u32 dstPitch;
newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch);
const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip);
for (AZ::u32 i = 0; i < pixelCount; ++i, dstPixelBuf += pixelBytes)
{
pixelOp->SetRGBA(dstPixelBuf, 0.5f, 0.5f, 0.5f, 1.0f);
}
}
m_img = newImage;
}
} // namespace ImageProcessing

@ -1,81 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing/PixelFormats.h>
#include <ImageProcessing/ImageObject.h>
#include <Converters/PixelOperation.h>
#include <Processing/PixelFormatInfo.h>
#include <Converters/Histogram.h>
///////////////////////////////////////////////////////////////////////////////////
namespace ImageProcessing
{
float GetLuminance(const float& r, const float& g, const float& b)
{
return (r * 0.30f + g * 0.59f + b * 0.11f);
}
bool ComputeLuminanceHistogram(IImageObjectPtr imageObject, Histogram<256>& histogram)
{
EPixelFormat pixelFormat = imageObject->GetPixelFormat();
if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(pixelFormat)))
{
AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
return false;
}
//create pixel operation function
IPixelOperationPtr pixelOp = CreatePixelOperation(pixelFormat);
//setup histogram bin
static const size_t binCount = 256;
Histogram<binCount>::Bins bins;
Histogram<binCount>::clearBins(bins);
//get count of bytes per pixel
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat)->bitsPerBlock / 8;
const AZ::u32 mipCount = imageObject->GetMipCount();
float color[4];
for (uint32 mip = 0; mip < mipCount; ++mip)
{
AZ::u8* pixelBuf;
AZ::u32 pitch;
imageObject->GetImagePointer(mip, pixelBuf, pitch);
const uint32 pixelCount = imageObject->GetPixelCount(mip);
for (uint32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
{
pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
const float luminance = AZ::GetClamp(GetLuminance(color[0], color[1], color[2]), 0.0f, 1.0f);
const float f = luminance * binCount;
if (f <= 0)
{
++bins[0];
}
else
{
const int bin = int(f);
++bins[(bin < binCount) ? bin : binCount - 1];
}
}
}
histogram.set(bins);
return true;
}
} // end namespace ImageProcessing

@ -1,84 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
namespace ImageProcessing
{
template <size_t BIN_COUNT>
class Histogram
{
public:
typedef AZ::u64 Bins[BIN_COUNT];
public:
Histogram()
{
}
static void clearBins(Bins& bins)
{
memset(&bins, 0, sizeof(bins));
}
void set(const Bins& bins)
{
m_bins[0] = bins[0];
m_binsCumulative[0] = bins[0];
double sum = 0.0f;
for (size_t i = 1; i < BIN_COUNT; ++i)
{
m_bins[i] = bins[i];
m_binsCumulative[i] = m_binsCumulative[i - 1] + bins[i];
sum += i * double(bins[i]);
}
const AZ::u64 totalCount = getTotalSampleCount();
m_meanBin = (totalCount <= 0) ? 0.0f : float(sum / totalCount);
}
AZ::u64 getTotalSampleCount() const
{
return m_binsCumulative[BIN_COUNT - 1];
}
float getPercentage(size_t minBin, size_t maxBin) const
{
const AZ::u64 totalCount = getTotalSampleCount();
if ((totalCount <= 0) || (minBin > maxBin) || (maxBin < 0) || (minBin >= BIN_COUNT))
{
return 0.0f;
}
minBin = AZ::GetMax(minBin, size_t(0));
maxBin = AZ::GetMin(maxBin, BIN_COUNT-1);
const AZ::u64 count = m_binsCumulative[maxBin] - ((minBin <= 0) ? 0 : m_binsCumulative[minBin-1]);
return float((double(count) * 100.0) / double(totalCount));
}
float getMeanBin() const
{
return m_meanBin;
}
private:
Bins m_bins;
Bins m_binsCumulative;
float m_meanBin;
};
bool ComputeLuminanceHistogram(IImageObjectPtr imageObject, Histogram<256>& histogram);
}

@ -1,420 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <ImageProcessing_precompiled.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageFlags.h>
namespace ImageProcessing
{
template<const int qBits>
static void AdjustScaleForQuantization(float fBaseValue, float fBaseLine, float& cScale, float& cMinColor, float& cMaxColor)
{
const int qOne = (1 << qBits) - 1;
const int qUpperBits = (8 - qBits);
const int qLowerBits = qBits - qUpperBits;
const int v = int(floor(fBaseValue * qOne));
int v0 = v - (v != 0);
int v1 = v + 0;
int v2 = v + (v != qOne);
v0 = (v0 << qUpperBits) | (v0 >> qLowerBits);
v1 = (v1 << qUpperBits) | (v1 >> qLowerBits);
v2 = (v2 << qUpperBits) | (v2 >> qLowerBits);
const float f0 = v0 / 255.0f;
const float f1 = v1 / 255.0f;
const float f2 = v2 / 255.0f;
float fBaseLock = -1;
if (fabsf(f0 - fBaseValue) < fabsf(fBaseLock - fBaseValue))
{
fBaseLock = f0;
}
if (fabsf(f1 - fBaseValue) < fabsf(fBaseLock - fBaseValue))
{
fBaseLock = f1;
}
if (fabsf(f2 - fBaseValue) < fabsf(fBaseLock - fBaseValue))
{
fBaseLock = f2;
}
float lScale = (1.0f - fBaseLock) / (1.0f - fBaseLine);
float vScale = (1.0f - fBaseValue) / (1.0f - fBaseLine);
float sScale = lScale / vScale;
float csScale = (cScale / sScale);
float csBias = cMinColor - (1.0f - sScale) * (cScale / sScale);
if ((csBias > 0.0f) && ((csScale + csBias) < 1.0f))
{
cMinColor = csBias;
cScale = csScale;
cMaxColor = csScale + csBias;
}
}
///////////////////////////////////////////////////////////////////////////////////
void CImageObject::NormalizeImageRange(EColorNormalization eColorNorm, EAlphaNormalization eAlphaNorm, bool bMaintainBlack, int nExponentBits)
{
if (GetPixelFormat() != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "%s: unsupported source format", __FUNCTION__);
return;
}
uint32 dwWidth, dwHeight, dwMips;
GetExtent(dwWidth, dwHeight, dwMips);
// find image's range, can be negative
float cMinColor[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
float cMaxColor[4] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
{
uint8* pSrcMem;
uint32 dwSrcPitch;
GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
dwHeight = GetHeight(dwMip);
dwWidth = GetWidth(dwMip);
for (uint32 dwY = 0; dwY < dwHeight; ++dwY)
{
const float* pSrcPix = (float*)&pSrcMem[dwY * dwSrcPitch];
for (uint32 dwX = 0; dwX < dwWidth; ++dwX)
{
cMinColor[0] = AZ::GetMin(cMinColor[0], pSrcPix[0]);
cMinColor[1] = AZ::GetMin(cMinColor[1], pSrcPix[1]);
cMinColor[2] = AZ::GetMin(cMinColor[2], pSrcPix[2]);
cMinColor[3] = AZ::GetMin(cMinColor[3], pSrcPix[3]);
cMaxColor[0] = AZ::GetMax(cMaxColor[0], pSrcPix[0]);
cMaxColor[1] = AZ::GetMax(cMaxColor[1], pSrcPix[1]);
cMaxColor[2] = AZ::GetMax(cMaxColor[2], pSrcPix[2]);
cMaxColor[3] = AZ::GetMax(cMaxColor[3], pSrcPix[3]);
pSrcPix += 4;
}
}
}
if (bMaintainBlack)
{
cMinColor[0] = AZ::GetMin(0.f, cMinColor[0]);
cMinColor[1] = AZ::GetMin(0.f, cMinColor[1]);
cMinColor[2] = AZ::GetMin(0.f, cMinColor[2]);
cMinColor[3] = AZ::GetMin(0.f, cMinColor[3]);
}
AZ_Assert(cMaxColor[0] >= cMinColor[0] && cMaxColor[1] >= cMinColor[1] &&
cMaxColor[2] >= cMinColor[2] && cMaxColor[3] >= cMinColor[3], "bad color range");
// some graceful threshold to avoid extreme cases
if (cMaxColor[0] - cMinColor[0] < (3.f / 255))
{
cMinColor[0] = AZ::GetMax(0.f, cMinColor[0] - (2.f / 255));
cMaxColor[0] = AZ::GetMin(1.f, cMaxColor[0] + (2.f / 255));
}
if (cMaxColor[1] - cMinColor[1] < (3.f / 255))
{
cMinColor[1] = AZ::GetMax(0.f, cMinColor[1] - (2.f / 255));
cMaxColor[1] = AZ::GetMin(1.f, cMaxColor[1] + (2.f / 255));
}
if (cMaxColor[2] - cMinColor[2] < (3.f / 255))
{
cMinColor[2] = AZ::GetMax(0.f, cMinColor[2] - (2.f / 255));
cMaxColor[2] = AZ::GetMin(1.f, cMaxColor[2] + (2.f / 255));
}
if (cMaxColor[3] - cMinColor[3] < (3.f / 255))
{
cMinColor[3] = AZ::GetMax(0.f, cMinColor[3] - (2.f / 255));
cMaxColor[3] = AZ::GetMin(1.f, cMaxColor[3] + (2.f / 255));
}
// calculate range to normalize to
const float fMaxExponent = powf(2.0f, (float)nExponentBits) - 1.0f;
const float cUprValue = powf(2.0f, fMaxExponent);
if (eColorNorm == eColorNormalization_PassThrough)
{
cMinColor[0] = cMinColor[1] = cMinColor[2] = 0.f;
cMaxColor[0] = cMaxColor[1] = cMaxColor[2] = 1.f;
}
// don't touch alpha channel if not used
if (eAlphaNorm == eAlphaNormalization_SetToZero)
{
// Store the range explicitly into the structure for read-back.
// The formats which request range expansion don't support alpha.
cMinColor[3] = 0.f;
cMaxColor[3] = cUprValue;
}
else if (eAlphaNorm == eAlphaNormalization_PassThrough)
{
cMinColor[3] = 0.f;
cMaxColor[3] = 1.f;
}
// get the origins of the color model's lattice for the range of values
// these values need to be encoded as precise as possible under quantization
AZ::Vector4 cBaseLines = AZ::Vector4(0.0f, 0.0f, 0.0f, 0.0f);
AZ::Vector4 cScale = AZ::Vector4(cMaxColor[0] - cMinColor[0], cMaxColor[1] - cMinColor[1],
cMaxColor[2] - cMinColor[2], cMaxColor[3] - cMinColor[3]);
#if 0
// NOTE: disabled for now, in the future we can turn this on to force availability
// of value to guarantee for example perfect grey-scales (using YFF)
switch (GetImageFlags() & EIF_Colormodel)
{
case EIF_Colormodel_RGB:
cBaseLines = Vec4(0.0f, 0.0f, 0.0f, 0.0f);
break;
case EIF_Colormodel_CIE:
cBaseLines = Vec4(0.0f, 1.f / 3, 1.f / 3, 0.0f);
break;
case EIF_Colormodel_IRB:
cBaseLines = Vec4(0.0f, 1.f / 2, 1.f / 2, 0.0f);
break;
case EIF_Colormodel_YCC:
case EIF_Colormodel_YFF:
cBaseLines = Vec4(1.f / 2, 0.0f, 1.f / 2, 0.0f);
break;
}
Vec4 cBaseScale = cBaseLines;
cBaseLines = cBaseLines - cMinColor;
cBaseLines = cBaseLines / cScale;
if ((cBaseLines.x > 0.0f) && (cBaseLines.x < 1.0f))
{
AdjustScaleForQuantization<5>(cBaseLines.x, cBaseScale.x, cScale.x, cMinColor.x, cMaxColor.x);
}
if ((cBaseLines.y > 0.0f) && (cBaseLines.y < 1.0f))
{
AdjustScaleForQuantization<6>(cBaseLines.y, cBaseScale.y, cScale.y, cMinColor.y, cMaxColor.y);
}
if ((cBaseLines.z > 0.0f) && (cBaseLines.z < 1.0f))
{
AdjustScaleForQuantization<5>(cBaseLines.z, cBaseScale.z, cScale.z, cMinColor.z, cMaxColor.z);
}
#endif
// normalize the image
AZ::Vector4 vMin = AZ::Vector4(cMinColor[0], cMinColor[1], cMinColor[2], cMinColor[3]);
for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
{
uint8* pSrcMem;
uint32 dwSrcPitch;
GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
dwHeight = GetHeight(dwMip);
dwWidth = GetWidth(dwMip);
for (uint32 dwY = 0; dwY < dwHeight; ++dwY)
{
AZ::Vector4* pSrcPix = (AZ::Vector4*)&pSrcMem[dwY * dwSrcPitch];
for (uint32 dwX = 0; dwX < dwWidth; ++dwX)
{
*pSrcPix = *pSrcPix - vMin;
*pSrcPix = *pSrcPix / cScale;
*pSrcPix = *pSrcPix * cUprValue;
pSrcPix++;
}
}
}
// set up a range
SetColorRange(AZ::Color(cMinColor[0], cMinColor[1], cMinColor[2], cMinColor[3]),
AZ::Color(cMaxColor[0], cMaxColor[1], cMaxColor[2], cMaxColor[3]));
// set up a flag
AddImageFlags(EIF_RenormalizedTexture);
}
void CImageObject::ExpandImageRange([[maybe_unused]] EColorNormalization eColorMode, EAlphaNormalization eAlphaMode, int nExponentBits)
{
AZ_Assert(!((eAlphaMode != eAlphaNormalization_SetToZero) && (nExponentBits != 0)), "%s: Unexpected alpha mode", __FUNCTION__);
if (!HasImageFlags(EIF_RenormalizedTexture))
{
return;
}
if (GetPixelFormat() != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "%s: only supports source format A32B32G32R32F", __FUNCTION__);
return;
}
uint32 dwWidth, dwHeight, dwMips;
GetExtent(dwWidth, dwHeight, dwMips);
// calculate range to normalize to
const float fMaxExponent = powf(2.0f, (float)nExponentBits) - 1.0f;
float cUprValue = powf(2.0f, fMaxExponent);
// find image's range, can be negative
AZ::Color cMinColor = AZ::Color(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX);
AZ::Color cMaxColor = AZ::Color(-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX);
GetColorRange(cMinColor, cMaxColor);
// don't touch alpha channel if not used
if (eAlphaMode == eAlphaNormalization_SetToZero)
{
// Overwrite the range explicitly into the structure.
// The formats which request range expansion don't support alpha.
cUprValue = cMaxColor.GetA();
cMinColor.SetA(1.f);
cMaxColor.SetA(1.f);
}
// expand the image
const AZ::Vector4 cScale = cMaxColor.GetAsVector4() - cMinColor.GetAsVector4();
for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
{
uint8* pSrcMem;
uint32 dwSrcPitch;
GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
dwHeight = GetHeight(dwMip);
dwWidth = GetWidth(dwMip);
for (uint32 dwY = 0; dwY < dwHeight; ++dwY)
{
AZ::Vector4* pSrcPix = (AZ::Vector4*)&pSrcMem[dwY * dwSrcPitch];
for (uint32 dwX = 0; dwX < dwWidth; ++dwX)
{
*pSrcPix = *pSrcPix / cUprValue;
*pSrcPix = *pSrcPix * cScale;
*pSrcPix = *pSrcPix + cMinColor.GetAsVector4();
pSrcPix++;
}
}
}
// set up a range
SetColorRange(AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), AZ::Color(1.0f, 1.0f, 1.0f, 1.0f));
// set up a flag
RemoveImageFlags(EIF_RenormalizedTexture);
}
///////////////////////////////////////////////////////////////////////////////////
void CImageObject::NormalizeVectors(AZ::u32 firstMip, AZ::u32 maxMipCount)
{
if (GetPixelFormat() != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "%s: only supports source format A32B32G32R32F", __FUNCTION__);
return;
}
uint32 lastMip = AZ::GetMin(firstMip + maxMipCount, GetMipCount());
for (uint32 mip = firstMip; mip < lastMip; ++mip)
{
const uint32 pixelCount = GetPixelCount(mip);
uint8* imageMem;
uint32 pitch;
GetImagePointer(mip, imageMem, pitch);
float* pPixels = (float*)imageMem;
for (uint32 i = 0; i < pixelCount; ++i, pPixels += 4)
{
AZ::Vector3 vNormal = AZ::Vector3(pPixels[0] * 2.0f - 1.0f, pPixels[1] * 2.0f - 1.0f, pPixels[2] * 2.0f - 1.0f);
// TODO: every opposing vector addition produces the zero-vector for
// normals on the entire sphere, in that case the forward vector [0,0,1]
// isn't necessarily right and we should look at the adjacent normals
// for a direction
if (vNormal.IsZero())
{
vNormal = AZ::Vector3(1.0f, 0.0f, 0.0f);
}
else
{
vNormal.NormalizeSafe();
}
pPixels[0] = vNormal.GetX() * 0.5f + 0.5f;
pPixels[1] = vNormal.GetY() * 0.5f + 0.5f;
pPixels[2] = vNormal.GetZ() * 0.5f + 0.5f;
}
}
}
///////////////////////////////////////////////////////////////////////////////////
void CImageObject::ScaleAndBiasChannels(AZ::u32 firstMip, AZ::u32 maxMipCount, const AZ::Vector4& scale, const AZ::Vector4& bias)
{
if (GetPixelFormat() != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "%s: only supports source format A32B32G32R32F", __FUNCTION__);
return;
}
const uint32 lastMip = AZ::GetMin(firstMip + maxMipCount, GetMipCount());
for (uint32 mip = firstMip; mip < lastMip; ++mip)
{
const uint32 pixelCount = GetPixelCount(mip);
uint8* imageMem;
uint32 pitch;
GetImagePointer(mip, imageMem, pitch);
float* pPixels = (float*)imageMem;
for (uint32 i = 0; i < pixelCount; ++i, pPixels += 4)
{
pPixels[0] = pPixels[0] * scale.GetX() + bias.GetX();
pPixels[1] = pPixels[1] * scale.GetY() + bias.GetY();
pPixels[2] = pPixels[2] * scale.GetZ() + bias.GetZ();
pPixels[3] = pPixels[3] * scale.GetW() + bias.GetW();
}
}
}
///////////////////////////////////////////////////////////////////////////////////
void CImageObject::ClampChannels(AZ::u32 firstMip, AZ::u32 maxMipCount, const AZ::Vector4& min, const AZ::Vector4& max)
{
if (GetPixelFormat() != ePixelFormat_R32G32B32A32F)
{
AZ_Assert(false, "%s: only supports source format A32B32G32R32F", __FUNCTION__);
return;
}
const uint32 lastMip = AZ::GetMin(firstMip + maxMipCount, GetMipCount());
for (uint32 mip = firstMip; mip < lastMip; ++mip)
{
const uint32 pixelCount = GetPixelCount(mip);
uint8* imageMem;
uint32 pitch;
GetImagePointer(mip, imageMem, pitch);
float* pPixels = (float*)imageMem;
for (uint32 i = 0; i < pixelCount; ++i, pPixels += 4)
{
pPixels[0] = AZ::GetClamp(pPixels[0], float(min.GetX()), float(max.GetX()));
pPixels[1] = AZ::GetClamp(pPixels[1], float(min.GetY()), float(max.GetY()));
pPixels[2] = AZ::GetClamp(pPixels[2], float(min.GetZ()), float(max.GetZ()));
pPixels[3] = AZ::GetClamp(pPixels[3], float(min.GetW()), float(max.GetW()));
}
}
}
} //namespace ImageProcessing

@ -1,487 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <Processing/ImageObjectImpl.h>
#include <Processing/ImageConvert.h>
#include <Processing/PixelFormatInfo.h>
#include <Converters/PixelOperation.h>
///////////////////////////////////////////////////////////////////////////////////
//functions for maintaining alpha coverage.
namespace ImageProcessing
{
//convertion: all data type supported by pixel channel <=> float
float U8ToF32(uint8 in)
{
return in / 255.f;
}
uint8 F32ToU8(float in)
{
return aznumeric_cast<uint8>(round(AZ::GetClamp(in, 0.f, 1.f) * 255));
}
float U16ToF32(uint16 in)
{
return in / 65535.f;
}
uint16 F32ToU16(float in)
{
return aznumeric_cast<uint16>(round(AZ::GetClamp(in, 0.f, 1.f) * 65535.f));
}
float HalfToF32(SHalf in)
{
return in;
}
SHalf F32ToHalf(float in)
{
return SHalf(in);
}
//stucture for RGBE pixel format
struct RgbE
{
static const int RGB9E5_EXPONENT_BITS = 5;
static const int RGB9E5_MANTISSA_BITS = 9;
static const int RGB9E5_EXP_BIAS = 15;
static const int RGB9E5_MAX_VALID_BIASED_EXP = 31;
static const int MAX_RGB9E5_EXP = (RGB9E5_MAX_VALID_BIASED_EXP - RGB9E5_EXP_BIAS);
static const int RGB9E5_MANTISSA_VALUES = (1 << RGB9E5_MANTISSA_BITS);
static const int MAX_RGB9E5_MANTISSA = (RGB9E5_MANTISSA_VALUES - 1);
static float MAX_RGB9E5;
unsigned int r : 9;
unsigned int g : 9;
unsigned int b : 9;
unsigned int e : 5;
static int log2(float x)
{
int bitfield = *((int*)(&x));
bitfield &= ~0x80000000;
return ((bitfield >> 23) - 127);
}
void GetRGBF(float& outR, float& outG, float& outB) const
{
int exponent = e - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS;
float scale = powf(2.0f, aznumeric_cast<float>(exponent));
outR = r * scale;
outG = g * scale;
outB = b * scale;
}
void SetRGBF(const float& inR, const float& inG, const float& inB)
{
float rf = AZStd::GetMax(0.0f, AZStd::GetMin(inR, MAX_RGB9E5));
float gf = AZStd::GetMax(0.0f, AZStd::GetMin(inG, MAX_RGB9E5));
float bf = AZStd::GetMax(0.0f, AZStd::GetMin(inB, MAX_RGB9E5));
float mf = AZStd::GetMax(rf, AZStd::GetMax(gf, bf));
e = AZStd::GetMax(0, log2(mf) + (RGB9E5_EXP_BIAS + 1));
int exponent = e - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS;
float scale = powf(2.0f, aznumeric_cast<float>(exponent));
r = AZStd::GetMin(511, (int)floorf(rf / scale + 0.5f));
g = AZStd::GetMin(511, (int)floorf(gf / scale + 0.5f));
b = AZStd::GetMin(511, (int)floorf(bf / scale + 0.5f));
}
};
float RgbE::MAX_RGB9E5 = (((float)MAX_RGB9E5_MANTISSA) / RGB9E5_MANTISSA_VALUES * (1 << MAX_RGB9E5_EXP));
//ePixelFormat_R8G8B8A8
class PixelOperationR8G8B8A8 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint8* data = buf;
r = U8ToF32(data[0]);
g = U8ToF32(data[1]);
b = U8ToF32(data[2]);
a = U8ToF32(data[3]);
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, const float& a) override
{
uint8* data = buf;
data[0] = F32ToU8(r);
data[1] = F32ToU8(g);
data[2] = F32ToU8(b);
data[3] = F32ToU8(a);
}
};
//ePixelFormat_R8G8B8X8
class PixelOperationR8G8B8X8 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint8* data = buf;
r = U8ToF32(data[0]);
g = U8ToF32(data[1]);
b = U8ToF32(data[2]);
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, [[maybe_unused]] const float& a) override
{
uint8* data = buf;
data[0] = F32ToU8(r);
data[1] = F32ToU8(g);
data[2] = F32ToU8(b);
data[3] = 0xff;
}
};
//ePixelFormat_B8G8R8A8
class PixelOperationB8G8R8A8 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint8* data = buf;
r = U8ToF32(data[2]);
g = U8ToF32(data[1]);
b = U8ToF32(data[0]);
a = U8ToF32(data[3]);
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, const float& a) override
{
uint8* data = buf;
data[0] = F32ToU8(b);
data[1] = F32ToU8(g);
data[2] = F32ToU8(r);
data[3] = F32ToU8(a);
}
};
//ePixelFormat_R8G8
class PixelOperationR8G8 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint8* data = buf;
r = U8ToF32(data[0]);
g = U8ToF32(data[1]);
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
uint8* data = buf;
data[0] = F32ToU8(r);
data[1] = F32ToU8(g);
}
};
//ePixelFormat_R8
class PixelOperationR8 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint8* data = buf;
r = U8ToF32(data[0]);
g = 0.f;
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, [[maybe_unused]] const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
uint8* data = buf;
data[0] = F32ToU8(r);
}
};
//ePixelFormat_A8
class PixelOperationA8 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint8* data = buf;
a = U8ToF32(data[0]);
//save alpha information to rgb too. useful for preview.
r = a;
g = a;
b = a;
}
void SetRGBA(uint8* buf, [[maybe_unused]] const float& r, [[maybe_unused]] const float& g, [[maybe_unused]] const float& b, const float& a) override
{
uint8* data = buf;
data[0] = F32ToU8(a);
}
};
//ePixelFormat_R16G16B16A16
class PixelOperationR16G16B16A16 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint16* data = (uint16*)(buf);
r = U16ToF32(data[0]);
g = U16ToF32(data[1]);
b = U16ToF32(data[2]);
a = U16ToF32(data[3]);
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, const float& a) override
{
uint16* data = (uint16*)(buf);
data[0] = F32ToU16(r);
data[1] = F32ToU16(g);
data[2] = F32ToU16(b);
data[3] = F32ToU16(a);
}
};
//ePixelFormat_R16G16
class PixelOperationR16G16 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint16* data = (uint16*)(buf);
r = U16ToF32(data[0]);
g = U16ToF32(data[1]);
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
uint16* data = (uint16*)(buf);
data[0] = F32ToU16(r);
data[1] = F32ToU16(g);
}
};
//ePixelFormat_R16
class PixelOperationR16 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const uint16* data = (uint16*)(buf);
r = U16ToF32(data[0]);
g = 0.f;
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, [[maybe_unused]] const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
uint16* data = (uint16*)(buf);
data[0] = F32ToU16(r);
}
};
//ePixelFormat_R9G9B9E5
class PixelOperationR9G9B9E5 : public IPixelOperation
{
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const RgbE* data = (RgbE*)(buf);
data->GetRGBF(r, g, b);
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, [[maybe_unused]] const float& a) override
{
RgbE* data = (RgbE*)(buf);
data->SetRGBF(r, g, b);
}
};
//ePixelFormat_R32G32B32A32F
class PixelOperationR32G32B32A32F : public IPixelOperation
{
public:
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const float* data = (float*)(buf);
r = data[0];
g = data[1];
b = data[2];
a = data[3];
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, const float& a) override
{
float* data = (float*)(buf);
data[0] = r;
data[1] = g;
data[2] = b;
data[3] = a;
}
};
//ePixelFormat_R32G32F
class PixelOperationR32G32F : public IPixelOperation
{
public:
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const float* data = (float*)(buf);
r = data[0];
g = data[1];
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
float* data = (float*)(buf);
data[0] = r;
data[1] = g;
}
};
//ePixelFormat_R32F
class PixelOperationR32F : public IPixelOperation
{
public:
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const float* data = (float*)(buf);
r = data[0];
g = 0.f;
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, [[maybe_unused]] const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
float* data = (float*)(buf);
data[0] = r;
}
};
//ePixelFormat_R16G16B16A16F
class PixelOperationR16G16B16A16F : public IPixelOperation
{
public:
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const SHalf* data = (SHalf*)(buf);
r = data[0];
g = data[1];
b = data[2];
a = data[3];
}
void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, const float& a) override
{
SHalf* data = (SHalf*)(buf);
data[0] = SHalf(r);
data[1] = SHalf(g);
data[2] = SHalf(b);
data[3] = SHalf(a);
}
};
//ePixelFormat_R16G16F
class PixelOperationR16G16F : public IPixelOperation
{
public:
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const SHalf* data = (SHalf*)(buf);
r = data[0];
g = data[1];
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
SHalf* data = (SHalf*)(buf);
data[0] = SHalf(r);
data[1] = SHalf(g);
}
};
//ePixelFormat_R16F
class PixelOperationR16F : public IPixelOperation
{
public:
void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) override
{
const SHalf* data = (SHalf*)(buf);
r = data[0];
g = 0.f;
b = 0.f;
a = 1.f;
}
void SetRGBA(uint8* buf, const float& r, [[maybe_unused]] const float& g, [[maybe_unused]] const float& b, [[maybe_unused]] const float& a) override
{
SHalf* data = (SHalf*)(buf);
data[0] = SHalf(r);
}
};
IPixelOperationPtr CreatePixelOperation(EPixelFormat pixelFmt)
{
switch (pixelFmt)
{
case ePixelFormat_R8G8B8A8:
return AZStd::make_shared<PixelOperationR8G8B8A8>();
case ePixelFormat_R8G8B8X8:
return AZStd::make_shared<PixelOperationR8G8B8X8>();
case ePixelFormat_B8G8R8A8:
return AZStd::make_shared<PixelOperationB8G8R8A8>();
case ePixelFormat_R8G8:
return AZStd::make_shared<PixelOperationR8G8>();
case ePixelFormat_R8:
return AZStd::make_shared<PixelOperationR8>();
case ePixelFormat_A8:
return AZStd::make_shared<PixelOperationA8>();
case ePixelFormat_R16G16B16A16:
return AZStd::make_shared<PixelOperationR16G16B16A16>();
case ePixelFormat_R16G16:
return AZStd::make_shared<PixelOperationR16G16>();
case ePixelFormat_R16:
return AZStd::make_shared<PixelOperationR16>();
case ePixelFormat_R9G9B9E5:
return AZStd::make_shared<PixelOperationR9G9B9E5>();
case ePixelFormat_R32G32B32A32F:
return AZStd::make_shared<PixelOperationR32G32B32A32F>();
case ePixelFormat_R32G32F:
return AZStd::make_shared<PixelOperationR32G32F>();
case ePixelFormat_R32F:
return AZStd::make_shared<PixelOperationR32F>();
case ePixelFormat_R16G16B16A16F:
return AZStd::make_shared<PixelOperationR16G16B16A16F>();
case ePixelFormat_R16G16F:
return AZStd::make_shared<PixelOperationR16G16F>();
case ePixelFormat_R16F:
return AZStd::make_shared<PixelOperationR16F>();
default:
AZ_Assert(false, "This function should be only called for uncompressed pixel format");
break;
}
return nullptr;
}
} // namespace ImageProcessing

@ -1,31 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <ImageProcessing/PixelFormats.h>
namespace ImageProcessing
{
class IPixelOperation
{
public:
virtual ~IPixelOperation() {}
virtual void GetRGBA(const uint8* buf, float& r, float& g, float& b, float& a) = 0;
virtual void SetRGBA(uint8* buf, const float& r, const float& g, const float& b, const float& a) = 0;
};
typedef AZStd::shared_ptr<IPixelOperation> IPixelOperationPtr;
IPixelOperationPtr CreatePixelOperation(EPixelFormat pixelFmt);
}// namespace ImageProcessing

@ -1,386 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include <ImageProcessing_Traits_Platform.h>
#include <Editor/EditorCommon.h>
#include <Processing/ImageToProcess.h>
#include <ImageLoader/ImageLoaders.h>
#include <BuilderSettings/TextureSettings.h>
#include <BuilderSettings/PresetSettings.h>
#include <ImageProcessing/PixelFormats.h>
#include <Converters/Cubemap.h>
#include <Processing/ImageConvert.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
bool EditorHelper::s_IsPixelFormatStringInited = false;
const char* EditorHelper::s_PixelFormatString[ImageProcessing::EPixelFormat::ePixelFormat_Count];
void EditorHelper::InitPixelFormatString()
{
if (!s_IsPixelFormatStringInited)
{
s_IsPixelFormatStringInited = true;
}
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
for (int format = 0; format < EPixelFormat::ePixelFormat_Count; format ++)
{
const PixelFormatInfo* info = pixelFormats.GetPixelFormatInfo((EPixelFormat)format);
s_PixelFormatString[(EPixelFormat)format] = "";
if (info)
{
s_PixelFormatString[(EPixelFormat)format] = info->szName;
}
else
{
AZ_Error("Texture Editor", false, "Cannot find name of EPixelFormat %i", format);
}
}
}
const AZStd::string EditorHelper::GetFileSizeString(AZ::u32 fileSizeInBytes)
{
AZStd::string fileSizeStr;
static double kb = 1024.0f;
static double mb = kb * 1024.0;
static double gb = mb * 1024.0;
static AZStd::string byteStr = "B";
static AZStd::string kbStr = "KB";
static AZStd::string mbStr = "MB";
static AZStd::string gbStr = "GB";
#if AZ_TRAIT_IMAGEPROCESSING_USE_BASE10_BYTE_PREFIX
kb = 1000.0;
mb = kb * 1000.0;
gb = mb * 1000.0;
kbStr = "kB";
mbStr = "mB";
gbStr = "gB";
#endif // AZ_TRAIT_IMAGEPROCESSING_USE_BASE10_BYTE_PREFIX
if (fileSizeInBytes < kb)
{
fileSizeStr = AZStd::string::format("%u%s", fileSizeInBytes, byteStr.c_str());
}
else if (fileSizeInBytes < mb)
{
double size = fileSizeInBytes / kb;
fileSizeStr = AZStd::string::format("%.2f%s", size, kbStr.c_str());
}
else if (fileSizeInBytes < gb)
{
double size = fileSizeInBytes / mb;
fileSizeStr = AZStd::string::format("%.2f%s", size, mbStr.c_str());
}
else
{
double size = fileSizeInBytes / gb;
fileSizeStr = AZStd::string::format("%.2f%s", size, gbStr.c_str());
}
return fileSizeStr;
}
const AZStd::string EditorHelper::ToReadablePlatformString(const AZStd::string& platformRawStr)
{
AZStd::string readableString;
AZStd::string platformStrLowerCase = platformRawStr;
AZStd::to_lower(platformStrLowerCase.begin(), platformStrLowerCase.end());
if (platformStrLowerCase == "pc")
{
readableString = "PC";
}
else if (platformStrLowerCase == "es3")
{
readableString = "Android";
}
else if (platformStrLowerCase == "osx_gl")
{
readableString = "macOS";
}
else if (platformStrLowerCase == "provo")
{
readableString = "Provo";
}
else if (platformStrLowerCase == "ios")
{
readableString = "iOS";
}
else
{
return platformRawStr;
}
return readableString;
}
EditorTextureSetting::EditorTextureSetting(const AZ::Uuid& sourceTextureId)
{
const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* fullDetails = AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry::GetSourceByUuid(sourceTextureId);
InitFromPath(fullDetails->GetFullPath());
}
EditorTextureSetting::EditorTextureSetting(const AZStd::string& texturePath)
{
InitFromPath(texturePath);
}
void EditorTextureSetting::InitFromPath(const AZStd::string& texturePath)
{
m_fullPath = texturePath;
AzFramework::StringFunc::Path::GetFullFileName(texturePath.c_str(), m_textureName);
m_img = IImageObjectPtr(LoadImageFromFile(m_fullPath));
if (m_img == nullptr)
{
AZ_Warning("Texture Editor", false, "%s is not a valid texture image.", texturePath.c_str());
return;
}
bool generatedDefaults = false;
m_settingsMap = TextureSettings::GetMultiplatformTextureSetting(m_fullPath, generatedDefaults);
// Get the preset id from one platform. The preset id for each platform should always be same
AZ_Assert(m_settingsMap.size() > 0, "There is no platform information");
AZ::Uuid presetId = m_settingsMap.begin()->second.m_preset;
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetId);
if (!preset)
{
AZ_Warning("Texture Editor", false, "Cannot find preset %s! Will assign a suggested one for the texture.", presetId.ToString<AZStd::string>().c_str());
presetId = BuilderSettingManager::Instance()->GetSuggestedPreset(m_fullPath, m_img);
for (auto& settingIter : m_settingsMap)
{
settingIter.second.ApplyPreset(presetId);
}
}
}
void EditorTextureSetting::SetIsOverrided()
{
for (auto& it : m_settingsMap)
{
m_overrideFromPreset = false;
TextureSettings& textureSetting = it.second;
const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(textureSetting.m_preset);
if (presetSetting != nullptr)
{
if ((textureSetting.m_sizeReduceLevel != presetSetting->m_sizeReduceLevel) ||
(textureSetting.m_suppressEngineReduce != presetSetting->m_suppressEngineReduce) ||
(presetSetting->m_mipmapSetting != nullptr && textureSetting.m_mipGenType != presetSetting->m_mipmapSetting->m_type))
{
m_overrideFromPreset = true;
}
}
else
{
AZ_Error("Texture Editor", false, "Texture Preset %s is not found!", textureSetting.m_preset.ToString<AZStd::string>().c_str());
}
}
}
void EditorTextureSetting::SetToPreset(const AZStd::string& presetName)
{
m_overrideFromPreset = false;
AZ::Uuid presetId = BuilderSettingManager::Instance()->GetPresetIdFromName(presetName);
if (presetId.IsNull())
{
AZ_Error("Texture Editor", false, "Texture Preset %s has no associated UUID.", presetName.c_str());
return;
}
for (auto& settingIter : m_settingsMap)
{
settingIter.second.ApplyPreset(presetId);
}
}
//Get the texture setting on certain platform
TextureSettings& EditorTextureSetting::GetMultiplatformTextureSetting(const AZStd::string& platform)
{
AZ_Assert(m_settingsMap.size() > 0, "Texture Editor", "There is no texture settings for texture %s", m_fullPath.c_str());
PlatformName platformName = platform;
if (platform.empty())
{
platformName = BuilderSettingManager::s_defaultPlatform;
}
if (m_settingsMap.find(platformName) != m_settingsMap.end())
{
return m_settingsMap[platformName];
}
else
{
AZ_Error("Texture Editor", false, "Cannot find texture setting on platform %s", platformName.c_str());
}
return m_settingsMap.begin()->second;
}
bool EditorTextureSetting::GetFinalInfoForTextureOnPlatform(const AZStd::string& platform, AZ::u32 wantedReduce, ResolutionInfo& outResolutionInfo)
{
if (m_settingsMap.find(platform) == m_settingsMap.end())
{
return false;
}
// Copy current texture setting and set to desired reduce
TextureSettings textureSetting = m_settingsMap[platform];
wantedReduce = AZStd::min<int>(AZStd::max<int>(s_MinReduceLevel, wantedReduce), s_MaxReduceLevel);
textureSetting.m_sizeReduceLevel = wantedReduce;
const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(textureSetting.m_preset, platform);
if (presetSetting)
{
EPixelFormat pixelFormat = presetSetting->m_pixelFormat;
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
AZ::u32 inputWidth = m_img->GetWidth(0);
AZ::u32 inputHeight = m_img->GetHeight(0);
// Update input width and height if it's a cubemap
if (presetSetting->m_cubemapSetting != nullptr)
{
CubemapLayout *srcCubemap = CubemapLayout::CreateCubemapLayout(m_img);
if (srcCubemap == nullptr)
{
return false;
}
inputWidth = srcCubemap->GetFaceSize();
inputHeight = inputWidth;
outResolutionInfo.arrayCount = 6;
delete srcCubemap;
}
GetOutputExtent(inputWidth, inputHeight, outResolutionInfo.width, outResolutionInfo.height, outResolutionInfo.reduce, &textureSetting, presetSetting);
AZ::u32 mipMapCount = pixelFormats.ComputeMaxMipCount(pixelFormat, outResolutionInfo.width, outResolutionInfo.height);
outResolutionInfo.mipCount = presetSetting->m_mipmapSetting != nullptr && textureSetting.m_enableMipmap ? mipMapCount : 1;
return true;
}
else
{
return false;
}
}
bool EditorTextureSetting::RefreshMipSetting(bool enableMip)
{
bool enabled = true;
for (auto& it : m_settingsMap)
{
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(it.second.m_preset);
if (enableMip)
{
if (preset && preset->m_mipmapSetting)
{
it.second.m_enableMipmap = true;
it.second.m_mipGenType = preset->m_mipmapSetting->m_type;
}
else
{
it.second.m_enableMipmap = false;
enabled = false;
AZ_Error("Texture Editor", false, "Preset %s does not support mipmap!", preset->m_name.c_str());
}
}
else
{
it.second.m_enableMipmap = false;
enabled = false;
}
}
return enabled;
}
void EditorTextureSetting::PropagateCommonSettings()
{
if (m_settingsMap.size() <= 1)
{
//Only one setting available, no need to propagate
return;
}
TextureSettings& texSetting = GetMultiplatformTextureSetting();
for (auto& it = ++ m_settingsMap.begin(); it != m_settingsMap.end(); ++it)
{
const PlatformName defaultPlatform = BuilderSettingManager::s_defaultPlatform;
if (it->first != defaultPlatform)
{
it->second.m_enableMipmap = texSetting.m_enableMipmap;
it->second.m_maintainAlphaCoverage = texSetting.m_maintainAlphaCoverage;
it->second.m_mipGenEval = texSetting.m_mipGenEval;
it->second.m_mipGenType = texSetting.m_mipGenType;
for (size_t i = 0; i < TextureSettings::s_MaxMipMaps; i++)
{
it->second.m_mipAlphaAdjust[i] = texSetting.m_mipAlphaAdjust[i];
}
}
}
}
AZStd::list<ResolutionInfo> EditorTextureSetting::GetResolutionInfo(AZStd::string platform, AZ::u32& minReduce, AZ::u32& maxReduce)
{
AZStd::list<ResolutionInfo> resolutionInfos;
// Set the min/max reduce to the global value range first
minReduce = s_MaxReduceLevel;
maxReduce = s_MinReduceLevel;
for (AZ::u32 i = s_MinReduceLevel; i <= s_MaxReduceLevel; i++)
{
ResolutionInfo resolutionInfo;
GetFinalInfoForTextureOnPlatform(platform, i, resolutionInfo);
// If actual reduce is lower than desired reduce, it reaches the limit and we can stop try lower resolution
if (i > resolutionInfo.reduce)
{
break;
}
// Finds out the final min/max reduce based on range in different platforms
minReduce = AZStd::min<AZ::u32>(resolutionInfo.reduce, minReduce);
maxReduce = AZStd::max<AZ::u32>(resolutionInfo.reduce, maxReduce);
resolutionInfos.push_back(resolutionInfo);
}
return resolutionInfos;
}
AZStd::list<ResolutionInfo> EditorTextureSetting::GetResolutionInfoForMipmap(AZStd::string platform)
{
AZStd::list<ResolutionInfo> resolutionInfos;
unsigned int baseReduce = m_settingsMap[platform].m_sizeReduceLevel;
ResolutionInfo baseInfo;
GetFinalInfoForTextureOnPlatform(platform, baseReduce, baseInfo);
resolutionInfos.push_back(baseInfo);
for (AZ::u32 i = 1; i < baseInfo.mipCount; i++)
{
ResolutionInfo resolutionInfo = baseInfo;
resolutionInfo.width = AZStd::max<AZ::u32>(baseInfo.width >> i, 1);
resolutionInfo.height = AZStd::max<AZ::u32>(baseInfo.height >> i, 1);
resolutionInfo.reduce = baseInfo.reduce + i;
resolutionInfo.mipCount = 1;
resolutionInfos.push_back(resolutionInfo);
}
return resolutionInfos;
}
} //namespace ImageProcessingEditor

@ -1,105 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <BuilderSettings/BuilderSettingManager.h>
#include <BuilderSettings/TextureSettings.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <Processing/PixelFormatInfo.h>
#include <ImageProcessing/ImageObject.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <QImage>
namespace ImageProcessingEditor
{
class EditorHelper
{
public:
static const char* s_PixelFormatString[ImageProcessing::EPixelFormat::ePixelFormat_Count];
static void InitPixelFormatString();
static const AZStd::string GetFileSizeString(AZ::u32 fileSizeInBytes);
static const AZStd::string ToReadablePlatformString(const AZStd::string& platformRawStr);
private:
static bool s_IsPixelFormatStringInited;
};
struct ResolutionInfo
{
AZ::u32 width = 0;
AZ::u32 height = 0;
AZ::u32 arrayCount = 1;
AZ::u32 reduce = 0;
AZ::u32 mipCount = 0;
};
struct EditorTextureSetting
{
AZStd::string m_textureName = "";
AZStd::string m_fullPath = "";
ImageProcessing::MultiplatformTextureSettings m_settingsMap;
bool m_overrideFromPreset = false;
bool m_modified = false;
ImageProcessing::IImageObjectPtr m_img;
EditorTextureSetting(const AZ::Uuid& sourceTextureId);
EditorTextureSetting(const AZStd::string& texturePath);
~EditorTextureSetting() = default;
void InitFromPath(const AZStd::string& texturePath);
void SetIsOverrided();
void SetToPreset(const AZStd::string& presetName);
//Get the texture setting on certain platform
ImageProcessing::TextureSettings& GetMultiplatformTextureSetting(const AZStd::string& platform = "");
//Gets the final resolution/reduce/mip count for a texture on a certain platform
//@param wantedReduce indicates the reduce level that's preferred
//@return successfully get the value or not
bool GetFinalInfoForTextureOnPlatform(const AZStd::string& platform, AZ::u32 wantedReduce, ResolutionInfo& outResolutionInfo);
//Refresh the mip setting when the mip map setting is enabled/disabled.
//@return whether the mipmap is enabled or not.
bool RefreshMipSetting(bool enableMip);
//Propagate non platform specific settings from the first setting to all the settings stored in m_settingsMap
void PropagateCommonSettings();
//Returns a list of calculated final resolution info based on different base reduce levels
AZStd::list<ResolutionInfo> GetResolutionInfo(AZStd::string platform, AZ::u32& minReduce, AZ::u32& maxReduce);
//Returns a list of calculated final resolution info based on different mipmap levels
AZStd::list<ResolutionInfo> GetResolutionInfoForMipmap(AZStd::string platform);
};
class ImageProcessingEditorInteralNotifications
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
/////////////////////////////////////////////////////////////////////////
//! Used to inform the settings changed across widgets
virtual void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform) = 0;
};
using EditorInternalNotificationBus = AZ::EBus<ImageProcessingEditorInteralNotifications>;
} //namespace ImageProcessingEditor

@ -1,49 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "ImagePopup.h"
#include <Source/Editor/ui_ImagePopup.h>
#include <QLabel>
#include <QPixmap>
namespace ImageProcessingEditor
{
ImagePopup::ImagePopup(QImage previewImage, QWidget* parent /*= nullptr*/)
: QDialog(parent, Qt::Dialog | Qt::FramelessWindowHint | Qt::Popup)
, m_ui(new Ui::ImagePopup)
{
m_ui->setupUi(this);
m_previewImage = previewImage;
if (!m_previewImage.isNull())
{
int height = previewImage.height();
int width = previewImage.width();
this->resize(width, height);
m_ui->imageLabel->resize(width, height);
QPixmap pixmap = QPixmap::fromImage(previewImage);
m_ui->imageLabel->setPixmap(pixmap);
this->setFocusPolicy(Qt::FocusPolicy::NoFocus);
this->setModal(false);
}
}
ImagePopup::~ImagePopup()
{
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_ImagePopup.cpp>

@ -1,44 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzQtComponents/Components/StyledDialog.h>
#endif
namespace Ui
{
class ImagePopup;
}
namespace ImageProcessingEditor
{
class ImagePopup
: public QDialog
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ImagePopup, AZ::SystemAllocator, 0);
explicit ImagePopup(QImage previewImage, QWidget* parent = nullptr);
~ImagePopup();
private:
QScopedPointer<Ui::ImagePopup> m_ui;
QImage m_previewImage;
};
} //namespace ImageProcessingEditor

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImagePopup</class>
<widget class="QDialog" name="ImagePopup">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="imageLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -1,122 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "MipmapSettingWidget.h"
#include <Source/Editor/ui_MipmapSettingWidget.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <QCheckBox>
#include <QSizePolicy>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
MipmapSettingWidget::MipmapSettingWidget(EditorTextureSetting& textureSetting, QWidget* parent /*= nullptr*/)
: QWidget(parent)
, m_ui(new Ui::MipmapSettingWidget)
, m_textureSetting(&textureSetting)
{
m_ui->setupUi(this);
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "Serialization context not available");
m_ui->propertyEditor->SetAutoResizeLabels(true);
m_ui->propertyEditor->Setup(serializeContext, this, true, 250);
m_ui->propertyEditor->ClearInstances();
TextureSettings* instance = &m_textureSetting->GetMultiplatformTextureSetting();
const AZ::Uuid& classId = AZ::SerializeTypeInfo<TextureSettings>::GetUuid(instance);
m_ui->propertyEditor->AddInstance(instance, classId);
m_ui->propertyEditor->InvalidateAll();
m_ui->propertyEditor->ExpandAll();
RefreshUI();
EditorInternalNotificationBus::Handler::BusConnect();
}
MipmapSettingWidget::~MipmapSettingWidget()
{
EditorInternalNotificationBus::Handler::BusDisconnect();
}
void MipmapSettingWidget::RefreshUI()
{
TextureSettings& texSetting = m_textureSetting->GetMultiplatformTextureSetting();
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(texSetting.m_preset);
if (preset == nullptr || preset->m_mipmapSetting == nullptr)
{
m_ui->enableCheckBox->setCheckState(Qt::CheckState::Unchecked);
m_ui->enableCheckBox->setEnabled(false);
m_ui->propertyEditor->hide();
}
else
{
bool showMipMap = texSetting.m_enableMipmap;
m_ui->enableCheckBox->setEnabled(true);
m_ui->enableCheckBox->setCheckState(showMipMap ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
QObject::connect(m_ui->enableCheckBox, &QCheckBox::clicked, this, &MipmapSettingWidget::OnCheckBoxStateChanged);
if (showMipMap)
{
m_ui->propertyEditor->show();
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
else
{
m_ui->propertyEditor->hide();
this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
}
}
m_ui->propertyEditor->InvalidateValues();
}
void MipmapSettingWidget::OnCheckBoxStateChanged(bool checked)
{
bool finalChecked = m_textureSetting->RefreshMipSetting(checked);
if (finalChecked)
{
m_ui->propertyEditor->show();
}
else
{
m_ui->propertyEditor->hide();
}
EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, false, BuilderSettingManager::s_defaultPlatform);
}
void MipmapSettingWidget::AfterPropertyModified(AzToolsFramework::InstanceDataNode* /*pNode*/)
{
//Only the first texture setting reflected is changed, we need to propagate the change to every texture settings.
m_textureSetting->PropagateCommonSettings();
m_ui->propertyEditor->InvalidateValues();
EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, false, BuilderSettingManager::s_defaultPlatform);
}
void MipmapSettingWidget::OnEditorSettingsChanged(bool needRefresh, const AZStd::string& /*platform*/)
{
if (needRefresh)
{
RefreshUI();
}
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_MipmapSettingWidget.cpp>

@ -1,62 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QMainWindow>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <Source/Editor/EditorCommon.h>
#endif
namespace Ui
{
class MipmapSettingWidget;
}
namespace ImageProcessingEditor
{
class MipmapSettingWidget
: public QWidget
, public AzToolsFramework::IPropertyEditorNotify
, protected EditorInternalNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MipmapSettingWidget, AZ::SystemAllocator, 0);
explicit MipmapSettingWidget(EditorTextureSetting& textureSetting, QWidget* parent = nullptr);
~MipmapSettingWidget();
//IPropertyEditorNotify Interface
void BeforePropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SealUndoStack() override {}
public slots:
void OnCheckBoxStateChanged(bool checked);
protected:
////////////////////////////////////////////////////////////////////////
//EditorInternalNotificationBus
void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform);
////////////////////////////////////////////////////////////////////////
private:
void RefreshUI();
QScopedPointer<Ui::MipmapSettingWidget> m_ui;
EditorTextureSetting* m_textureSetting;
};
} //namespace ImageProcessingEditor

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MipmapSettingWidget</class>
<widget class="QWidget" name="MipmapSettingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>583</width>
<height>489</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Mipmap Settings</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="enableCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AzToolsFramework::ReflectedPropertyEditor" name="propertyEditor">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::ReflectedPropertyEditor</class>
<extends>QFrame</extends>
<header location="global">AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

@ -1,120 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "PresetInfoPopup.h"
#include <Source/Editor/ui_PresetInfoPopup.h>
#include <BuilderSettings/PresetSettings.h>
#include <QLabel>
#include <QString>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
// Help functions to convert enum to strings
static const char* RGBWeightToString(RGBWeight weight)
{
static const char* RGBWeightNames[] = { "uniform", "luminance", "ciexyz" };
AZ_Assert(weight <= RGBWeight::ciexyz, "Invalid RGBWeight!");
return RGBWeightNames[(int)weight];
}
static const char* ColorSpaceToString(ColorSpace colorSpace)
{
static const char* colorSpaceNames[] = { "linear", "sRGB", "auto" };
AZ_Assert(colorSpace <= ColorSpace::autoSelect, "Invalid ColorSpace!");
return colorSpaceNames[(int)colorSpace];
}
static const char* MipGenTypeToString(MipGenType mipGenType)
{
static const char* mipGenTypeNames[] = { "point", "average", "linear" , "bilinear" , "gaussian" , "blackmanHarris", "kaiserSinc" };
AZ_Assert(mipGenType <= MipGenType::kaiserSinc, "Invalid MipGenType!");
return mipGenTypeNames[(int)mipGenType];
}
static const char* CubemapFilterTypeToString(CubemapFilterType cubemapFilterType)
{
static const char* cubemapFilterTypeNames[] = { "disc", "cone", "cosine" , "gaussian" , "cosine power" , "ggx" };
AZ_Assert(cubemapFilterType <= CubemapFilterType::ggx, "Invalid CubemapFilterType!");
return cubemapFilterTypeNames[(int)cubemapFilterType];
}
PresetInfoPopup::PresetInfoPopup(const PresetSettings* presetSettings, QWidget* parent /*= nullptr*/)
: AzQtComponents::StyledDialog(parent, Qt::Dialog | Qt::Popup)
, m_ui(new Ui::PresetInfoPopup)
{
m_ui->setupUi(this);
RefreshPresetInfoLabel(presetSettings);
}
PresetInfoPopup::~PresetInfoPopup()
{
}
void PresetInfoPopup::RefreshPresetInfoLabel(const PresetSettings* presetSettings)
{
QString presetInfoText = "";
if (!presetSettings)
{
presetInfoText = "Invalid Preset!";
m_ui->infoLabel->setText(presetInfoText);
return;
}
presetInfoText += QString("UUID: %1\n").arg(presetSettings->m_uuid.ToString<AZStd::string>().c_str());
presetInfoText += QString("Name: %1\n").arg(presetSettings->m_name.c_str());
presetInfoText += QString("RGB Weight: %1\n").arg(RGBWeightToString(presetSettings->m_rgbWeight));
presetInfoText += QString("Source ColorSpace: %1\n").arg(ColorSpaceToString(presetSettings->m_srcColorSpace));
presetInfoText += QString("Destination ColorSpace: %1\n").arg(ColorSpaceToString(presetSettings->m_destColorSpace));
presetInfoText += QString("FileMasks: ");
int i = 0;
for (auto& mask : presetSettings->m_fileMasks)
{
presetInfoText += i > 0 ? ", " : "";
presetInfoText += mask.c_str();
i++;
}
presetInfoText += "\n";
presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False");
presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False");
presetInfoText += QString("Is Power Of 2: %1\n").arg(presetSettings->m_isPowerOf2 ? "True" : "False");
presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False");
presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip);
presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals);
presetInfoText += QString("Use Legacy Gloss: %1\n").arg(presetSettings->m_isLegacyGloss ? "True" : "False");
presetInfoText += QString("Mip Re-normalize: %1\n").arg(presetSettings->m_isMipRenormalize ? "True" : "False");
presetInfoText += QString("Streamable Mips Number: %1\n").arg(presetSettings->m_numStreamableMips);
presetInfoText += QString("Swizzle: %1\n").arg(presetSettings->m_swizzle.c_str());
if (presetSettings->m_cubemapSetting)
{
presetInfoText += QString("[Cubemap Settings]\n");
presetInfoText += QString("Filter: %1\n").arg(CubemapFilterTypeToString(presetSettings->m_cubemapSetting->m_filter));
presetInfoText += QString("Angle: %1\n").arg(presetSettings->m_cubemapSetting->m_angle);
presetInfoText += QString("Mip Angle: %1\n").arg(presetSettings->m_cubemapSetting->m_mipAngle);
presetInfoText += QString("Mip Slope: %1\n").arg(presetSettings->m_cubemapSetting->m_mipSlope);
presetInfoText += QString("Edge Fixup: %1\n").arg(presetSettings->m_cubemapSetting->m_edgeFixup);
presetInfoText += QString("Generate Diff: %1\n").arg(presetSettings->m_cubemapSetting->m_generateDiff ? "True" : "False");
presetInfoText += QString("Diffuse Probe Preset: %1\n").arg(presetSettings->m_cubemapSetting->m_diffuseGenPreset.ToString<AZStd::string>().c_str());
}
if (presetSettings->m_mipmapSetting)
{
presetInfoText += QString("[MipMapSetting]\n");
presetInfoText += QString("Type: %1\n").arg(MipGenTypeToString(presetSettings->m_mipmapSetting->m_type));
}
m_ui->infoLabel->setText(presetInfoText);
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_PresetInfoPopup.cpp>

@ -1,49 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzQtComponents/Components/StyledDialog.h>
#endif
namespace ImageProcessing
{
class PresetSettings;
}
namespace Ui
{
class PresetInfoPopup;
}
namespace ImageProcessingEditor
{
class PresetInfoPopup
: public AzQtComponents::StyledDialog
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(PresetInfoPopup, AZ::SystemAllocator, 0);
explicit PresetInfoPopup(const ImageProcessing::PresetSettings* preset, QWidget* parent = nullptr);
~PresetInfoPopup();
void RefreshPresetInfoLabel(const ImageProcessing::PresetSettings* presetSettings);
private:
QScopedPointer<Ui::PresetInfoPopup> m_ui;
};
} //namespace ImageProcessingEditor

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PresetInfoPopup</class>
<widget class="QDialog" name="PresetInfoPopup">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>400</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Preset Info</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="layout">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="infoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -1,145 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "ResolutionSettingItemWidget.h"
#include <Source/Editor/ui_ResolutionSettingItemWidget.h>
#include <BuilderSettings/PresetSettings.h>
#include <QComboBox>
#include <QSpinBox>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
ResolutionSettingItemWidget::ResolutionSettingItemWidget(ResoultionWidgetType type, QWidget* parent /*= nullptr*/)
: QWidget(parent)
, m_ui(new Ui::ResolutionSettingItemWidget)
{
m_ui->setupUi(this);
m_type = type;
EditorInternalNotificationBus::Handler::BusConnect();
}
ResolutionSettingItemWidget::~ResolutionSettingItemWidget()
{
EditorInternalNotificationBus::Handler::BusDisconnect();
}
void ResolutionSettingItemWidget::Init(AZStd::string platform, EditorTextureSetting* editorTextureSetting)
{
m_platform = platform;
m_editorTextureSetting = editorTextureSetting;
m_textureSetting = &m_editorTextureSetting->m_settingsMap[m_platform];
m_preset = BuilderSettingManager::Instance()->GetPreset(m_textureSetting->m_preset, platform);
SetupResolutionInfo();
RefreshUI();
if (m_type == ResoultionWidgetType::TexturePropety)
{
m_ui->formatLabel->show();
m_ui->formatComboBox->hide();
}
else
{
m_ui->formatLabel->hide();
m_ui->formatComboBox->show();
QObject::connect(m_ui->formatComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ResolutionSettingItemWidget::OnChangeFormat);
}
QObject::connect(m_ui->downResSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ResolutionSettingItemWidget::OnChangeDownRes);
}
void ResolutionSettingItemWidget::RefreshUI()
{
m_ui->platformLabel->setText(EditorHelper::ToReadablePlatformString(m_platform).c_str());
m_ui->downResSpinBox->setRange(m_minReduce, m_maxReduce);
int clampedReduce = AZStd::min<int>(AZStd::max<int>(m_textureSetting->m_sizeReduceLevel, s_MinReduceLevel), s_MaxReduceLevel);
auto it = m_resolutionInfos.begin();
it = AZStd::next(it, clampedReduce);
m_ui->downResSpinBox->setValue(it->reduce);
QString finalResolution;
if (it->arrayCount > 1)
{
finalResolution = QString("%1 x %2 x %3").arg(QString::number(it->width), QString::number(it->height), QString::number(it->arrayCount));
}
else
{
finalResolution = QString("%1 x %2").arg(QString::number(it->width), QString::number(it->height));
}
m_ui->sizeLabel->setText(finalResolution);
QString finalFormat = GetFinalFormat(m_textureSetting->m_preset);
if (m_type == ResoultionWidgetType::TexturePropety)
{
m_ui->formatLabel->setText(finalFormat);
}
else
{
SetupFormatComboBox();
m_ui->formatComboBox->setCurrentText(finalFormat);
}
}
void ResolutionSettingItemWidget::SetupResolutionInfo()
{
m_resolutionInfos = m_editorTextureSetting->GetResolutionInfo(m_platform, m_minReduce, m_maxReduce);
}
void ResolutionSettingItemWidget::OnChangeDownRes(int downRes)
{
if ((unsigned int)downRes >= m_minReduce && (unsigned int)downRes <= m_maxReduce)
{
m_textureSetting->m_sizeReduceLevel = downRes;
RefreshUI();
EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, false, m_platform);
}
}
QString ResolutionSettingItemWidget::GetFinalFormat([[maybe_unused]] const AZ::Uuid& presetId)
{
if (m_preset && m_preset->m_pixelFormat >=0 && m_preset->m_pixelFormat < ePixelFormat_Count)
{
return EditorHelper::s_PixelFormatString[m_preset->m_pixelFormat];
}
return QString();
}
void ResolutionSettingItemWidget::SetupFormatComboBox()
{
m_ui->formatComboBox->clear();
}
void ResolutionSettingItemWidget::OnChangeFormat([[maybe_unused]] int index)
{
bool oldState = m_ui->formatComboBox->blockSignals(true);
m_ui->formatComboBox->blockSignals(oldState);
}
void ResolutionSettingItemWidget::OnEditorSettingsChanged(bool needRefresh, const AZStd::string& /*platform*/)
{
if (needRefresh)
{
m_preset = BuilderSettingManager::Instance()->GetPreset(m_textureSetting->m_preset, m_platform);
SetupResolutionInfo();
RefreshUI();
}
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_ResolutionSettingItemWidget.cpp>

@ -1,81 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <Editor/EditorCommon.h>
#endif
namespace ImageProcessing
{
class PresetSettings;
}
namespace Ui
{
class ResolutionSettingItemWidget;
}
namespace ImageProcessingEditor
{
enum class ResoultionWidgetType
{
TexturePipeline, //Fully editable
TexturePropety, //Only DownRes is editable
};
class ResolutionSettingItemWidget
: public QWidget
, EditorInternalNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ResolutionSettingItemWidget, AZ::SystemAllocator, 0);
explicit ResolutionSettingItemWidget(ResoultionWidgetType type, QWidget* parent = nullptr);
~ResolutionSettingItemWidget();
void Init(AZStd::string platform, EditorTextureSetting* editorTextureSetting);
public slots:
void OnChangeDownRes(int downRes);
void OnChangeFormat(int index);
protected:
////////////////////////////////////////////////////////////////////////
//EditorInternalNotificationBus
void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform);
////////////////////////////////////////////////////////////////////////
private:
void SetupFormatComboBox();
void SetupResolutionInfo();
void RefreshUI();
QString GetFinalFormat(const AZ::Uuid& presetId);
QScopedPointer<Ui::ResolutionSettingItemWidget> m_ui;
ResoultionWidgetType m_type;
AZStd::string m_platform;
ImageProcessing::TextureSettings* m_textureSetting;
EditorTextureSetting* m_editorTextureSetting;
const ImageProcessing::PresetSettings* m_preset;
//Cached list of calculated final resolution info based on different reduce levels
AZStd::list<ResolutionInfo> m_resolutionInfos;
//Final reduce level range
unsigned int m_maxReduce;
unsigned int m_minReduce;
};
} //namespace ImageProcessingEditor

@ -1,205 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ResolutionSettingItemWidget</class>
<widget class="QWidget" name="ResolutionSettingItemWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>20</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>400</width>
<height>20</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="platformLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Provo</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="downResSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="sizeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="formatLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="formatComboBox"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -1,58 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "ResolutionSettingWidget.h"
#include <Source/Editor/ui_ResolutionSettingWidget.h>
#include <QVBoxLayout>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
ResolutionSettingWidget::ResolutionSettingWidget(ResoultionWidgetType type, EditorTextureSetting& textureSetting, QWidget* parent /*= nullptr*/)
: QWidget(parent)
, m_ui(new Ui::ResolutionSettingWidget)
, m_textureSetting(&textureSetting)
{
m_ui->setupUi(this);
m_type = type;
//Put default platform in the first row
ResolutionSettingItemWidget* item = new ResolutionSettingItemWidget(ResoultionWidgetType::TexturePropety, this);
item->Init(BuilderSettingManager::s_defaultPlatform, m_textureSetting);
m_ui->listLayout->addWidget(item);
//Add the other platforms in the list
for (auto& it : m_textureSetting->m_settingsMap)
{
AZStd::string platform = it.first;
if (platform != BuilderSettingManager::s_defaultPlatform)
{
ResolutionSettingItemWidget* item2 = new ResolutionSettingItemWidget(ResoultionWidgetType::TexturePropety, this);
item2->Init(platform, m_textureSetting);
m_ui->listLayout->addWidget(item2);
}
}
// Tooltips
m_ui->downResLabel->setToolTip(QString("Adjust the resolution based on the target platform. \
Use this setting to preserve the resolution of a source file even though it appears smaller in the game. \
Select 0 to preserve the original size or 5 for the maximum reduction."));
}
ResolutionSettingWidget::~ResolutionSettingWidget()
{
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_ResolutionSettingWidget.cpp>

@ -1,44 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <Source/Editor/ResolutionSettingItemWidget.h>
#endif
namespace Ui
{
class ResolutionSettingWidget;
}
namespace ImageProcessingEditor
{
class ResolutionSettingWidget
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ResolutionSettingWidget, AZ::SystemAllocator, 0);
explicit ResolutionSettingWidget(ResoultionWidgetType type, EditorTextureSetting& texureSetting, QWidget* parent = nullptr);
~ResolutionSettingWidget();
private:
QScopedPointer<Ui::ResolutionSettingWidget> m_ui;
ResoultionWidgetType m_type;
EditorTextureSetting* m_textureSetting;
};
} //namespace ImageProcessingEditor

@ -1,177 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ResolutionSettingWidget</class>
<widget class="QWidget" name="ResolutionSettingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>550</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="listLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Platform</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="downResLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>DownRes</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Format</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -1,199 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "TexturePresetSelectionWidget.h"
#include <Source/Editor/ui_TexturePresetSelectionWidget.h>
#include <QCheckBox>
#include <QComboBox>
#include <QPushButton>
#include <AzFramework/StringFunc/StringFunc.h>
#include <BuilderSettings/PresetSettings.h>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
TexturePresetSelectionWidget::TexturePresetSelectionWidget(EditorTextureSetting& textureSetting, QWidget* parent /*= nullptr*/)
: QWidget(parent)
, m_ui(new Ui::TexturePresetSelectionWidget)
, m_textureSetting(&textureSetting)
{
m_ui->setupUi(this);
// Add presets into combo box
m_presetList.clear();
auto& presetFilterMap = BuilderSettingManager::Instance()->GetPresetFilterMap();
AZStd::set<AZStd::string> noFilterPresetList;
// Check if there is any filtered preset list first
for(auto& presetFilter : presetFilterMap)
{
if (presetFilter.first.empty())
{
noFilterPresetList = presetFilter.second;
}
else if (IsMatchingWithFileMask(m_textureSetting->m_textureName, presetFilter.first))
{
for(const AZStd::string& presetName : presetFilter.second)
{
m_presetList.insert(presetName);
}
}
}
// If no filtered preset list available or should list all presets, use non-filter list
if (m_presetList.size() == 0 || m_listAllPresets)
{
m_presetList = noFilterPresetList;
}
foreach (const AZStd::string& presetName, m_presetList)
{
m_ui->presetComboBox->addItem(QString(presetName.c_str()));
}
// Set current preset
const AZ::Uuid& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset;
const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(currPreset);
if (presetSetting)
{
m_ui->presetComboBox->setCurrentText(presetSetting->m_name.c_str());
QObject::connect(m_ui->presetComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &TexturePresetSelectionWidget::OnChangePreset);
// Suppress engine reduction checkbox
m_ui->serCheckBox->setCheckState(m_textureSetting->GetMultiplatformTextureSetting().m_suppressEngineReduce ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
SetCheckBoxReadOnly(m_ui->serCheckBox, presetSetting->m_suppressEngineReduce);
QObject::connect(m_ui->serCheckBox, &QCheckBox::clicked, this, &TexturePresetSelectionWidget::OnCheckBoxStateChanged);
// Set convention label
SetPresetConvention(presetSetting);
}
// Reset btn
QObject::connect(m_ui->resetBtn, &QPushButton::clicked, this, &TexturePresetSelectionWidget::OnRestButton);
// PresetInfo btn
QObject::connect(m_ui->infoBtn, &QPushButton::clicked, this, &TexturePresetSelectionWidget::OnPresetInfoButton);
// Tooltips
m_ui->activeFileConventionLabel->setToolTip(QString("Displays the supported naming convention for the selected preset."));
m_ui->presetComboBox->setToolTip(QString("Choose a preset to update the preview and other properties."));
m_ui->resetBtn->setToolTip(QString("Reset values to current preset defaults."));
m_ui->serCheckBox->setToolTip(QString("Preserves the original size. Use this setting for textures that include text."));
m_ui->infoBtn->setToolTip(QString("Show detail properties of the current preset"));
EditorInternalNotificationBus::Handler::BusConnect();
}
TexturePresetSelectionWidget::~TexturePresetSelectionWidget()
{
EditorInternalNotificationBus::Handler::BusDisconnect();
}
void TexturePresetSelectionWidget::OnCheckBoxStateChanged(bool checked)
{
for (auto& it : m_textureSetting->m_settingsMap)
{
it.second.m_suppressEngineReduce = checked;
}
m_textureSetting->SetIsOverrided();
EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, false, BuilderSettingManager::s_defaultPlatform);
}
void TexturePresetSelectionWidget::OnRestButton()
{
m_textureSetting->SetToPreset(AZStd::string(m_ui->presetComboBox->currentText().toUtf8().data()));
EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, true, BuilderSettingManager::s_defaultPlatform);
}
void TexturePresetSelectionWidget::OnChangePreset(int index)
{
QString text = m_ui->presetComboBox->itemText(index);
m_textureSetting->SetToPreset(AZStd::string(text.toUtf8().data()));
EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, true, BuilderSettingManager::s_defaultPlatform);
}
void ImageProcessingEditor::TexturePresetSelectionWidget::OnPresetInfoButton()
{
const AZ::Uuid& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset;
const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(currPreset);
m_presetPopup.reset(new PresetInfoPopup(presetSetting, this));
m_presetPopup->installEventFilter(this);
m_presetPopup->show();
}
void TexturePresetSelectionWidget::OnEditorSettingsChanged(bool needRefresh, const AZStd::string& /*platform*/)
{
if (needRefresh)
{
bool oldState = m_ui->serCheckBox->blockSignals(true);
m_ui->serCheckBox->setChecked(m_textureSetting->GetMultiplatformTextureSetting().m_suppressEngineReduce);
// If the preset's SER is true, texture setting should not override
const AZ::Uuid& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset;
const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(currPreset);
if (presetSetting)
{
SetCheckBoxReadOnly(m_ui->serCheckBox, presetSetting->m_suppressEngineReduce);
SetPresetConvention(presetSetting);
// If there is preset info dialog open, update the text
if (m_presetPopup && m_presetPopup->isVisible())
{
m_presetPopup->RefreshPresetInfoLabel(presetSetting);
}
}
m_ui->serCheckBox->blockSignals(oldState);
}
}
bool TexturePresetSelectionWidget::IsMatchingWithFileMask(const AZStd::string& filename, const AZStd::string& fileMask)
{
if (fileMask.empty())
{
// Will not match with empty string
return false;
}
else
{
// Extract the file name and compare if it ends with file mask or not
AZStd::string filenameNoExt;
AzFramework::StringFunc::Path::GetFileName(filename.c_str(), filenameNoExt);
return filenameNoExt.length() >= fileMask.length() && filenameNoExt.compare(filenameNoExt.length() - fileMask.length(), fileMask.length(), fileMask) == 0;
}
}
void ImageProcessingEditor::TexturePresetSelectionWidget::SetPresetConvention(const PresetSettings* presetSettings)
{
AZStd::string conventionText = "";
if (presetSettings)
{
int i = 0;
for (const PlatformName& filemask : presetSettings->m_fileMasks)
{
conventionText += i > 0 ? " " + filemask : filemask;
i++;
}
}
m_ui->conventionLabel->setText(QString(conventionText.c_str()));
}
void ImageProcessingEditor::TexturePresetSelectionWidget::SetCheckBoxReadOnly(QCheckBox* checkBox, bool readOnly)
{
checkBox->setAttribute(Qt::WA_TransparentForMouseEvents, readOnly);
checkBox->setFocusPolicy(readOnly ? Qt::NoFocus : Qt::StrongFocus);
checkBox->setEnabled(!readOnly);
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_TexturePresetSelectionWidget.cpp>

@ -1,65 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <Source/BuilderSettings/BuilderSettingManager.h>
#include <Source/Editor/EditorCommon.h>
#include <Editor/PresetInfoPopup.h>
#endif
class QCheckBox;
namespace Ui
{
class TexturePresetSelectionWidget;
}
namespace ImageProcessingEditor
{
class TexturePresetSelectionWidget
: public QWidget
, protected EditorInternalNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(TexturePresetSelectionWidget, AZ::SystemAllocator, 0);
explicit TexturePresetSelectionWidget(EditorTextureSetting& texureSetting, QWidget* parent = nullptr);
~TexturePresetSelectionWidget();
public slots:
void OnCheckBoxStateChanged(bool checked);
void OnRestButton();
void OnChangePreset(int index);
void OnPresetInfoButton();
protected:
////////////////////////////////////////////////////////////////////////
//EditorInternalNotificationBus
void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform);
////////////////////////////////////////////////////////////////////////
private:
QScopedPointer<Ui::TexturePresetSelectionWidget> m_ui;
AZStd::set<AZStd::string> m_presetList;
EditorTextureSetting* m_textureSetting;
QScopedPointer<PresetInfoPopup> m_presetPopup;
bool IsMatchingWithFileMask(const AZStd::string& filename, const AZStd::string& fileMask);
void SetPresetConvention(const ImageProcessing::PresetSettings* presetSettings);
void SetCheckBoxReadOnly(QCheckBox* checkBox, bool readOnly);
bool m_listAllPresets = true;
};
} //namespace ImageProcessingEditor

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TexturePresetSelectionWidget</class>
<widget class="QWidget" name="TexturePresetSelectionWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>624</width>
<height>118</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1">
<widget class="QLabel" name="conventionLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="activeFileConventionLabel">
<property name="text">
<string>Active file conventions</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="titleLabel">
<property name="text">
<string>Texture presets</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="presetComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="activePresetLabel">
<property name="text">
<string>Active preset</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="resetBtn">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normalon>:/Reset.png</normalon>
</iconset>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="serCheckBox">
<property name="text">
<string>Suppress spec reduction</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="infoBtn">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normalon>:/info.png</normalon>
</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../Assets/Editor/Resources.qrc"/>
</resources>
<connections/>
</ui>

@ -1,639 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "TexturePreviewWidget.h"
#include <Processing/PixelFormatInfo.h>
#include <ImageProcessing/ImageObject.h>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QPixmap>
#include <QSize>
#include <QPoint>
#include <QPixmap>
#include <QPainter>
#include <QComboBox>
#include <QColor>
#include <QEvent>
#include <QKeyEvent>
#include <QApplicationStateChangeEvent>
#include <QString>
#include <QMenu>
#include <QWidgetAction>
#include <AzQtComponents/Components/Widgets/PushButton.h>
#include <Source/Editor/ui_TexturePreviewWidget.h>
namespace ImageProcessingEditor
{
using namespace ImageProcessing;
TexturePreviewWidget::TexturePreviewWidget(EditorTextureSetting& texureSetting, QWidget* parent /*= nullptr*/)
: QWidget(parent)
, m_ui(new Ui::TexturePreviewWidget)
, m_textureSetting(&texureSetting)
{
m_ui->setupUi(this);
m_platform = BuilderSettingManager::s_defaultPlatform;
// For now, only provide preview for default platform
m_previewConverter = AZStd::make_unique<ImageProcessing::ImagePreview>(m_textureSetting->m_fullPath, &m_textureSetting->GetMultiplatformTextureSetting());
m_updateTimer = new QTimer(this);
connect(m_updateTimer, &QTimer::timeout, this, &TexturePreviewWidget::UpdatePreview);
m_updateTimer->setSingleShot(false);
m_ui->infoLayer->setAttribute(Qt::WA_NoSystemBackground);
m_ui->mipLevelLabel->setAttribute(Qt::WA_NoSystemBackground);
m_ui->imageSizeLabel->setAttribute(Qt::WA_NoSystemBackground);
m_ui->fileSizeLabel->setAttribute(Qt::WA_NoSystemBackground);
// Setup preview mode combo box
static const QString previewModeString[] = { "RGB",
"R",
"G",
"B",
"Alpha",
"RGBA" };
for (int i = 0; i < (int)PreviewMode::Count; i ++)
{
m_ui->previewComboBox->addItem(previewModeString[i]);
}
QSize size = m_ui->imageLabel->size();
m_imageLabelSize = aznumeric_cast<float>(size.width());
SetUpResolutionInfo();
RefreshUI(true);
QObject::connect(m_ui->previewCheckBox, &QCheckBox::clicked, this, &TexturePreviewWidget::OnTiledChanged);
QObject::connect(m_ui->nextMipBtn, &QPushButton::clicked, this, &TexturePreviewWidget::OnNextMip);
QObject::connect(m_ui->prevMipBtn, &QPushButton::clicked, this, &TexturePreviewWidget::OnPrevMip);
QObject::connect(m_ui->previewComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &TexturePreviewWidget::OnChangePreviewMode);
// Set up Refresh button
m_alwaysRefreshAction = new QAction("Always refresh preview", this);
m_alwaysRefreshAction->setCheckable(true);
m_alwaysRefreshAction->setChecked(m_alwaysRefreshPreview);
QObject::connect(m_alwaysRefreshAction, &QAction::triggered, this, &TexturePreviewWidget::OnAlwaysRefresh);
m_refreshPerClickAction = new QAction("Press to refresh preview", this);
m_refreshPerClickAction->setCheckable(true);
m_refreshPerClickAction->setChecked(!m_alwaysRefreshPreview);
QObject::connect(m_refreshPerClickAction, &QAction::triggered, this, &TexturePreviewWidget::OnRefreshPerClick);
QMenu* menu = new QMenu(this);
menu->addAction(m_alwaysRefreshAction);
menu->addAction(m_refreshPerClickAction);
m_ui->refreshBtn->setMenu(menu);
AzQtComponents::PushButton::applySmallIconStyle(m_ui->refreshBtn);
QObject::connect(m_ui->refreshBtn, &QPushButton::clicked, this, &TexturePreviewWidget::OnRefreshClicked);
m_alwaysRefreshIcon.addFile(QStringLiteral(":/refresh.png"), QSize(), QIcon::Normal, QIcon::On);
m_refreshPerClickIcon.addFile(QStringLiteral(":/refresh-active.png"), QSize(), QIcon::Normal, QIcon::On);
m_ui->refreshBtn->setIcon(m_alwaysRefreshIcon);
m_ui->busyLabel->SetBusyIconSize(16);
SetImageLabelText(QString(), false);
// Tooltips
m_ui->previewComboBox->setToolTip(QString("Preview the texture in different channels."));
m_ui->previewCheckBox->setToolTip(QString("Show or hide a 2x2 tiling of the texture."));
m_ui->hotkeyLabel->setToolTip(QString("Preview different texture states with keyboard shortcuts."));
m_ui->refreshBtn->setToolTip(QString("Provide different ways to refresh the preview. Click on the button to refresh manually."));
EditorInternalNotificationBus::Handler::BusConnect();
}
TexturePreviewWidget::~TexturePreviewWidget()
{
EditorInternalNotificationBus::Handler::BusDisconnect();
}
void TexturePreviewWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
QSize size = m_ui->mainWidget->size();
m_ui->infoLayer->resize(size);
QSize imageSize = m_ui->imageLabel->size();
QPoint center = m_ui->mainWidget->rect().center();
m_ui->imageLabel->move(center - QPoint(imageSize.width() / 2, imageSize.height() / 2));
QSize busyLabelSize = m_ui->busyLabel->size();
m_ui->busyLabel->move(center - QPoint(busyLabelSize.width() + m_ui->imageLabel->sizeHint().width() / 2, busyLabelSize.width() / 2));
}
void TexturePreviewWidget::SetUpResolutionInfo()
{
m_resolutionInfos = m_textureSetting->GetResolutionInfoForMipmap(m_platform);
m_mipCount = (unsigned int)m_resolutionInfos.size();
if (m_currentMipIndex > (int)m_mipCount)
{
m_currentMipIndex = 0;
}
}
void TexturePreviewWidget::OnEditorSettingsChanged([[maybe_unused]] bool needRefresh, const AZStd::string& platform)
{
// Only update the preview if there is any change in current platform
if (platform == m_platform)
{
SetUpResolutionInfo();
RefreshUI(true);
}
}
void TexturePreviewWidget::RefreshUI(bool fullRefresh)
{
m_ui->mipLevelLabel->setText(QString("Mip %1").arg(QString::number(m_currentMipIndex)));
m_ui->previewCheckBox->setCheckState(m_previewTiled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
bool hasNextMip = m_currentMipIndex < (int)m_mipCount - 1;
m_ui->nextMipBtn->setVisible(hasNextMip);
bool hasPrevMip = m_currentMipIndex > 0;
m_ui->prevMipBtn->setVisible(hasPrevMip);
RefreshWarning();
if (m_currentMipIndex < m_resolutionInfos.size())
{
auto it = AZStd::next(m_resolutionInfos.begin(), m_currentMipIndex);
QString finalResolution;
if (it->arrayCount > 1)
{
finalResolution = QString("Image Size: %1 x %2 x %3").arg(QString::number(it->width), QString::number(it->height), QString::number(it->arrayCount));
}
else
{
finalResolution = QString("Image Size: %1 x %2").arg(QString::number(it->width), QString::number(it->height));
}
m_ui->imageSizeLabel->setText(finalResolution);
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(m_textureSetting->GetMultiplatformTextureSetting().m_preset);
if (preset)
{
uint32 size = pixelFormats.EvaluateImageDataSize(preset->m_pixelFormat, it->width, it->height) * it->arrayCount;
AZStd::string fileSizeString = EditorHelper::GetFileSizeString(size);
QString finalFileSize = QString("File Size: %1").arg(fileSizeString.c_str());
m_ui->fileSizeLabel->setText(finalFileSize);
}
if (m_alwaysRefreshPreview)
{
RefreshPreviewImage(fullRefresh ? RefreshMode::Convert : RefreshMode::Mip);
}
}
else
{
AZ_Error("Texture Setting", false, "Cannot find mip reduce level for mip %d", m_currentMipIndex);
}
}
void TexturePreviewWidget::OnNextMip()
{
if (m_currentMipIndex >= (int)m_mipCount - 1)
{
return;
}
m_currentMipIndex ++;
RefreshUI(false);
}
void TexturePreviewWidget::OnPrevMip()
{
if (m_currentMipIndex <= 0)
{
return;
}
m_currentMipIndex--;
RefreshUI(false);
}
void TexturePreviewWidget::UpdatePreview()
{
if (!m_previewConverter->IsDone())
{
float progress = m_previewConverter->GetProgress();
SetImageLabelText(QString("Converting for preview...Progress %1%").arg(QString::number(progress * 100, 'f', 2)));
return;
}
m_updateTimer->stop();
m_previewImageRaw = m_previewConverter->GetOutputImage();
GenerateMipmap(m_currentMipIndex);
GenerateChannelImage(m_previewMode);
PaintPreviewImage();
}
void TexturePreviewWidget::OnAlwaysRefresh()
{
m_alwaysRefreshPreview = true;
m_alwaysRefreshAction->setChecked(true);
m_refreshPerClickAction->setChecked(false);
m_ui->refreshBtn->setIcon(m_alwaysRefreshIcon);
}
void TexturePreviewWidget::OnRefreshPerClick()
{
m_alwaysRefreshPreview = false;
m_alwaysRefreshAction->setChecked(false);
m_refreshPerClickAction->setChecked(true);
m_ui->refreshBtn->setIcon(m_refreshPerClickIcon);
}
void TexturePreviewWidget::OnRefreshClicked()
{
RefreshPreviewImage(RefreshMode::Convert);
}
void TexturePreviewWidget::GenerateMipmap(int mip)
{
// Clear all cached preview images
for (int i = 0; i < (int)PreviewMode::Count; i++)
{
m_previewImages[i] = QImage();
}
if (m_previewImageRaw && (AZ::u32)mip < m_previewImageRaw->GetMipCount() )
{
uint8* imageBuf;
uint32 pitch;
m_previewImageRaw->GetImagePointer(mip, imageBuf, pitch);
const uint32 width = m_previewImageRaw->GetWidth(mip);
const uint32 height = m_previewImageRaw->GetHeight(mip);
m_previewImages[PreviewMode::RGBA] = QImage(imageBuf, width, height, pitch, QImage::Format_RGBA8888);
}
else
{
AZ_Error("Texture Editor", false, "Cannot generate mip preview from an invalid image.");
}
}
void TexturePreviewWidget::GenerateChannelImage(PreviewMode channel)
{
// If there is no preview image generated, ignore this function
if (m_previewImages[PreviewMode::RGBA].isNull())
{
AZ_Error("Texture Editor", false, "Cannot generate channel image from an invalid image.");
return;
}
if (m_previewImages[channel].isNull())
{
// Copy the RGBA image before changing the color
QImage previewImg = m_previewImages[PreviewMode::RGBA].copy();
for (int x = 0; x < previewImg.width(); x++)
{
for (int y = 0; y < previewImg.height(); y++)
{
QRgb pixel = previewImg.pixel(x, y);
int r = qRed(pixel);
int g = qGreen(pixel);
int b = qBlue(pixel);
int a = qAlpha(pixel);
switch (channel)
{
case ImageProcessingEditor::RGB:
pixel = qRgba(r, g, b, 255);
break;
case ImageProcessingEditor::RRR:
pixel = qRgba(r, r, r, 255);
break;
case ImageProcessingEditor::GGG:
pixel = qRgba(g, g, g, 255);
break;
case ImageProcessingEditor::BBB:
pixel = qRgba(b, b, b, 255);
break;
case ImageProcessingEditor::Alpha:
pixel = qRgba(a, a, a, 255);
break;
default:
break;
}
previewImg.setPixel(x, y, pixel);
}
}
// Cache the image in current preview mode
m_previewImages[channel] = previewImg;
}
}
void TexturePreviewWidget::RefreshPreviewImage(RefreshMode mode)
{
// Ignore any none-conversion refresh request when the image is being converted
if (m_updateTimer->isActive() && mode != RefreshMode::Convert)
{
return;
}
switch (mode)
{
case RefreshMode::Convert:
{
// Start conversion in a AZ::Job
m_previewConverter->StartConvert();
// Start the timer to trigger the update function
m_updateTimer->start(s_updateInterval);
SetImageLabelText(QString("Converting for preview...Progress 0.01%"));
}
break;
case RefreshMode::Mip:
{
GenerateMipmap(m_currentMipIndex);
GenerateChannelImage(m_previewMode);
PaintPreviewImage();
}
break;
case RefreshMode::Channel:
{
GenerateChannelImage(m_previewMode);
PaintPreviewImage();
}
break;
default:
PaintPreviewImage();
break;
}
}
void TexturePreviewWidget::PaintPreviewImage()
{
if (m_previewImages[m_previewMode].isNull())
{
SetImageLabelText(QString("Conversion failed, please check console for more information."), false);
return;
}
SetImageLabelText(QString(), false);
// Paint the image on to the image label
QPixmap pixMap = QPixmap::fromImage(m_previewImages[m_previewMode]);
QSize size = m_ui->imageLabel->size();
QPixmap finalPix = pixMap.copy();
finalPix.fill(Qt::transparent);
finalPix = finalPix.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPainter painter(&finalPix);
painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
QRect rect = finalPix.rect();
if (m_previewTiled)
{
pixMap = pixMap.scaled(size / 2, Qt::KeepAspectRatio, Qt::SmoothTransformation);
painter.drawTiledPixmap(rect, pixMap);
}
else
{
painter.drawPixmap(rect, pixMap);
}
// Recenter the image label
float aspectRatio = static_cast<float>(finalPix.width()) / static_cast<float>(finalPix.height());
QSize preferredSize;
if (aspectRatio >= 1.0f)
{
preferredSize = QSize(aznumeric_cast<int>(m_imageLabelSize), aznumeric_cast<int>(m_imageLabelSize / aspectRatio));
}
else
{
preferredSize = QSize(aznumeric_cast<int>(m_imageLabelSize * aspectRatio), aznumeric_cast<int>(m_imageLabelSize));
}
m_ui->imageLabel->resize(preferredSize);
m_ui->imageLabel->setPixmap(finalPix);
QPoint center = m_ui->mainWidget->rect().center();
m_ui->imageLabel->move(center - QPoint(preferredSize.width() / 2, preferredSize.height() / 2));
}
void TexturePreviewWidget::SetImageLabelText(const QString& text, bool busyStatus /*= true*/)
{
// Since setting pixmap will change the label size
// Need to set back to initial size and recenter before displaying text
m_ui->imageLabel->resize(QSize(aznumeric_cast<int>(m_imageLabelSize), aznumeric_cast<int>(m_imageLabelSize)));
QPoint center = m_ui->mainWidget->rect().center();
m_ui->imageLabel->move(center - QPoint(aznumeric_cast<int>(m_imageLabelSize / 2), aznumeric_cast<int>(m_imageLabelSize / 2)));
m_ui->imageLabel->setText(text);
// Set busy label status and position to align with the text
m_ui->busyLabel->SetIsBusy(busyStatus);
QSize size = m_ui->busyLabel->size();
m_ui->busyLabel->move(center - QPoint(size.width() + m_ui->imageLabel->sizeHint().width() / 2, size.width() / 2));
m_ui->busyLabel->setVisible(busyStatus);
}
void TexturePreviewWidget::RefreshWarning()
{
int imageWidth = m_textureSetting->m_img->GetWidth(0);
int imageHeight = m_textureSetting->m_img->GetHeight(0);
AZStd::list<PlatformName> stretchedPlatform;
for (auto& iter: m_textureSetting->m_settingsMap)
{
PlatformName platform = iter.first;
const PresetSettings* presetSettings = BuilderSettingManager::Instance()->GetPreset(iter.second.m_preset, platform);
if (presetSettings)
{
EPixelFormat dstFmt = presetSettings->m_pixelFormat;
if (!CPixelFormats::GetInstance().IsImageSizeValid(dstFmt, imageWidth, imageHeight, false))
{
stretchedPlatform.push_back(EditorHelper::ToReadablePlatformString(platform).c_str());
}
}
}
if (stretchedPlatform.size() > 0)
{
QString warningText = QString("The output image will be stretched on Platform:");
int i = 0;
for (AZStd::string platform: stretchedPlatform)
{
warningText += i > 0 ? ", " : " ";
warningText += platform.c_str();
i ++;
}
m_ui->warningLabel->setText(warningText);
m_ui->warningLabel->setVisible(true);
m_ui->warningIcon->setVisible(true);
}
else
{
m_ui->warningLabel->setVisible(false);
m_ui->warningIcon->setVisible(false);
}
}
void TexturePreviewWidget::OnChangePreviewMode(int index)
{
if (index < (int)PreviewMode::Count)
{
m_previewMode = (PreviewMode)index;
RefreshPreviewImage(RefreshMode::Channel);
}
}
void TexturePreviewWidget::OnTiledChanged(bool checked)
{
m_previewTiled = checked;
RefreshPreviewImage(RefreshMode::Repaint);
}
bool TexturePreviewWidget::OnQtEvent(QEvent * event)
{
if (event->type() == QEvent::KeyPress)
{
const QKeyEvent* ke = static_cast<QKeyEvent*>(event);
if (ke->isAutoRepeat())
{
return false; //ignore repeat key event
}
if (ke->key() == Qt::Key_Space)
{
if (!m_updateTimer->isActive()) // Only popup when image is not converting
{
m_previewPopup.reset(new ImagePopup(m_previewImages[m_previewMode], this));
m_previewPopup->installEventFilter(this);
m_previewPopup->show();
event->accept();
return true;
}
}
else if (ke->key() == Qt::Key_Alt)
{
m_previewMode = PreviewMode::Alpha;
RefreshPreviewImage(RefreshMode::Channel);
event->accept();
return true;
}
else if (ke->key() == Qt::Key_Shift)
{
m_previewMode = PreviewMode::RGBA;
RefreshPreviewImage(RefreshMode::Channel);
event->accept();
return true;
}
}
else if (event->type() == QEvent::KeyRelease)
{
const QKeyEvent* ke = static_cast<QKeyEvent*>(event);
if (ke->isAutoRepeat())
{
return false; //ignore repeat key event
}
if (ke->key() == Qt::Key_Space)
{
if (m_previewPopup)
{
m_previewPopup->hide();
}
event->accept();
return true;
}
else if (ke->key() == Qt::Key_Alt)
{
m_previewMode = (PreviewMode)m_ui->previewComboBox->currentIndex();
RefreshPreviewImage(RefreshMode::Channel);
event->accept();
return true;
}
else if (ke->key() == Qt::Key_Shift)
{
m_previewMode = (PreviewMode)m_ui->previewComboBox->currentIndex();
RefreshPreviewImage(RefreshMode::Channel);
event->accept();
return true;
}
}
else if (event->type() == QEvent::ApplicationStateChange)
{
const QApplicationStateChangeEvent* appEvent = static_cast<QApplicationStateChangeEvent*>(event);
AZ_Warning("Texture Editor", false, "app status change %d", appEvent->applicationState());
if (appEvent->applicationState() != Qt::ApplicationState::ApplicationActive)
{
PreviewMode currPreviewMode = (PreviewMode)m_ui->previewComboBox->currentIndex();
if (m_previewMode != currPreviewMode)
{
m_previewMode = currPreviewMode;
RefreshPreviewImage(RefreshMode::Channel);
event->accept();
return true;
}
}
}
else if (event->type() == QEvent::ShortcutOverride)
{
// since we respond to the following things, let Qt know so that shortcuts don't override us
QKeyEvent* kev = static_cast<QKeyEvent*>(event);
int key = kev->key() | kev->modifiers();
switch (key)
{
case Qt::Key_Space:
case Qt::Key_Alt:
case Qt::Key_Shift:
event->accept();
return true;
break;
default:
break;
}
}
return false;
}
bool TexturePreviewWidget::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::KeyRelease)
{
const QKeyEvent* ke = static_cast<QKeyEvent*>(event);
if (ke->key() == Qt::Key_Space && !ke->isAutoRepeat())
{
if (m_previewPopup)
{
m_previewPopup->hide();
}
return true;
}
}
else if (event->type() == QEvent::ApplicationStateChange)
{
const QApplicationStateChangeEvent* appEvent = static_cast<QApplicationStateChangeEvent*>(event);
if (appEvent->applicationState() != Qt::ApplicationState::ApplicationActive)
{
if (m_previewPopup)
{
m_previewPopup->hide();
}
}
return true;
}
return QWidget::eventFilter(obj, event);
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_TexturePreviewWidget.cpp>

@ -1,121 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <Editor/EditorCommon.h>
#include <Editor/ImagePopup.h>
#include <QEvent>
#include <QTimer>
#include <Processing/ImagePreview.h>
#endif
namespace Ui
{
class TexturePreviewWidget;
}
namespace ImageProcessingEditor
{
enum PreviewMode
{
RGB = 0,
RRR,
GGG,
BBB,
Alpha,
RGBA,
Count
};
enum class RefreshMode
{
Convert, // Convert the whole image from beginning, takes longest time
Mip, // Generate a new mip from from converted image
Channel, // Generate a new channel image from converted image
Repaint,
};
class TexturePreviewWidget
: public QWidget
, protected EditorInternalNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(TexturePreviewWidget, AZ::SystemAllocator, 0);
explicit TexturePreviewWidget(EditorTextureSetting& texureSetting, QWidget* parent = 0);
~TexturePreviewWidget();
bool OnQtEvent(QEvent* event);
public slots:
void OnTiledChanged(bool checked);
void OnPrevMip();
void OnNextMip();
void OnChangePreviewMode(int index);
void UpdatePreview();
void OnAlwaysRefresh();
void OnRefreshPerClick();
void OnRefreshClicked();
protected:
////////////////////////////////////////////////////////////////////////
//EditorInternalNotificationBus
void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform);
////////////////////////////////////////////////////////////////////////
void resizeEvent(QResizeEvent *event) override;
bool eventFilter(QObject* obj, QEvent* event) override;
private:
void SetUpResolutionInfo();
void RefreshUI(bool fullRefresh = false);
void RefreshPreviewImage(RefreshMode mode);
void GenerateMipmap(int mip);
void GenerateChannelImage(PreviewMode channel);
void PaintPreviewImage();
void SetImageLabelText(const QString& text, bool busyStatus = true);
void RefreshWarning();
AZStd::list<ResolutionInfo> m_resolutionInfos;
QScopedPointer<Ui::TexturePreviewWidget> m_ui;
EditorTextureSetting* m_textureSetting;
int m_currentMipIndex = 0;
bool m_previewTiled = false;
float m_imageLabelSize = 0;
AZStd::string m_platform;
unsigned int m_mipCount = 1;
///////////////////////////////////////////
// Preview window
PreviewMode m_previewMode = PreviewMode::RGB;
QScopedPointer<ImagePopup> m_previewPopup;
AZStd::unique_ptr<ImageProcessing::ImagePreview> m_previewConverter;
ImageProcessing::IImageObjectPtr m_previewImageRaw;
QImage m_previewImages[PreviewMode::Count];
QTimer* m_updateTimer;
static const int s_updateInterval = 200;
////////////////////////////////////////////
////////////////////////////////////////////
// Refresh button
bool m_alwaysRefreshPreview = true;
QAction* m_alwaysRefreshAction = nullptr;
QAction* m_refreshPerClickAction = nullptr;
QIcon m_refreshPerClickIcon;
QIcon m_alwaysRefreshIcon;
////////////////////////////////////////////
};
} //namespace ImageProcessingEditor

@ -1,371 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TexturePreviewWidget</class>
<widget class="QWidget" name="TexturePreviewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>672</width>
<height>579</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="previewComboBox"/>
</item>
<item>
<widget class="QCheckBox" name="previewCheckBox">
<property name="text">
<string>Preview tiled</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="hotkeyLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Shift: RGBA | Alt:Alpha | Space: Full-size</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="mainWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<widget class="QFrame" name="infoLayer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>171</width>
<height>185</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="warningLayout">
<item>
<widget class="QLabel" name="warningLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="warningIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="baseSize">
<size>
<width>26</width>
<height>25</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../Assets/Editor/Resources.qrc">:/warning.png</pixmap>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>115</width>
<height>32</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QToolButton" name="nextMipBtn">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normalon>:/Forward.png</normalon>
</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="prevMipBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset>
<normalon>:/Backward.png</normalon>
</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>115</width>
<height>32</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="mipLevelLabel">
<property name="text">
<string>Mip 1</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="imageSizeLabel">
<property name="text">
<string>Image size: 2048 x 2048</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fileSizeLabel">
<property name="text">
<string>File Size: 4,096 KB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="refreshBtn">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QLabel" name="imageLabel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>400</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>2048</width>
<height>2048</height>
</size>
</property>
<property name="text">
<string>image</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="AzQtComponents::StyledBusyLabel" name="busyLabel" native="true">
<property name="geometry">
<rect>
<x>510</x>
<y>80</y>
<width>30</width>
<height>30</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>10</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
<zorder>imageLabel</zorder>
<zorder>infoLayer</zorder>
<zorder>busyLabel</zorder>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::StyledBusyLabel</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/StyledBusyLabel.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../Assets/Editor/Resources.qrc"/>
</resources>
<connections/>
</ui>

@ -1,217 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <ImageProcessing_precompiled.h>
#include "TexturePropertyEditor.h"
#include <Source/Editor/ui_TexturePropertyEditor.h>
#include <Source/BuilderSettings/BuilderSettingManager.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/IO/FileIO.h>
#include <QBoxLayout>
#include <QUrl>
#include <QDesktopServices>
#include <QKeyEvent>
#include <QPushButton>
#include <QCheckBox>
#include <QComboBox>
namespace ImageProcessingEditor
{
TexturePropertyEditor::TexturePropertyEditor(const AZ::Uuid& sourceTextureId, QWidget* parent /*= nullptr*/)
: AzQtComponents::StyledDialog(parent, Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint | Qt::WindowTitleHint)
, m_ui(new Ui::TexturePropertyEditor)
, m_textureSetting(sourceTextureId)
, m_validImage(true)
{
if (m_textureSetting.m_img == nullptr)
{
m_validImage = false;
return;
}
m_ui->setupUi(this);
//Initialize all the format string here
EditorHelper::InitPixelFormatString();
//TexturePreviewWidget will be the widget to preview mipmaps
m_previewWidget.reset(aznew TexturePreviewWidget(m_textureSetting, this));
m_ui->mainLayout->layout()->addWidget(m_previewWidget.data());
//TexturePresetSelectionWidget will be the widget to select the preset for the texture
m_presetSelectionWidget.reset(aznew TexturePresetSelectionWidget(m_textureSetting, this));
m_ui->mainLayout->layout()->addWidget(m_presetSelectionWidget.data());
//ResolutionSettingWidget will be the table section to display mipmap resolution for each platform
m_resolutionSettingWidget.reset(aznew ResolutionSettingWidget(ResoultionWidgetType::TexturePropety, m_textureSetting, this));
m_ui->mainLayout->layout()->addWidget(m_resolutionSettingWidget.data());
//MipmapSettingWidget will be simple ReflectedProperty editor to reflect mipmap settings section
m_mipmapSettingWidget.reset(aznew MipmapSettingWidget(m_textureSetting, this));
m_ui->mainLayout->layout()->addWidget(m_mipmapSettingWidget.data());
// Disable horizontal scroll
m_ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QObject::connect(m_ui->saveBtn, &QPushButton::clicked, this, &TexturePropertyEditor::OnSave);
QObject::connect(m_ui->helpBtn, &QPushButton::clicked, this, &TexturePropertyEditor::OnHelp);
QObject::connect(m_ui->cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
EditorInternalNotificationBus::Handler::BusConnect();
// When checkbox and combobox is focused, they will intercept the space shortcut, need to disable focus on them first
// to get space shortcut pass through
QList<QCheckBox*> checkBoxWidgets = QObject::findChildren<QCheckBox*>();
for (QCheckBox* widget: checkBoxWidgets)
{
widget->setFocusPolicy(Qt::NoFocus);
}
QList<QComboBox*> comboBoxWidgets = QObject::findChildren<QComboBox*>();
for (QComboBox* widget : comboBoxWidgets)
{
widget->setFocusPolicy(Qt::NoFocus);
}
this->setFocusPolicy(Qt::StrongFocus);
}
TexturePropertyEditor::~TexturePropertyEditor()
{
EditorInternalNotificationBus::Handler::BusDisconnect();
}
bool TexturePropertyEditor::HasValidImage()
{
return m_validImage;
}
void TexturePropertyEditor::OnEditorSettingsChanged([[maybe_unused]] bool needRefresh, const AZStd::string& /*platform*/)
{
m_textureSetting.m_modified = true;
}
void TexturePropertyEditor::OnSave()
{
if (!m_validImage)
{
return;
}
bool sourceControlActive = false;
AzToolsFramework::SourceControlConnectionRequestBus::BroadcastResult(sourceControlActive, &AzToolsFramework::SourceControlConnectionRequests::IsActive);
AZStd::string outputPath = m_textureSetting.m_fullPath + ImageProcessing::TextureSettings::modernExtensionName;
if (sourceControlActive)
{
using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus;
bool checkoutResult = false;
ApplicationBus::BroadcastResult(checkoutResult, &ApplicationBus::Events::RequestEditForFileBlocking, outputPath.c_str(), "Checking out .imagesetting file", []([[maybe_unused]] int& current, [[maybe_unused]] int& max) {});
if (checkoutResult)
{
SaveTextureSetting(outputPath);
}
else
{
AZ_Error("Texture Editor", false, "Cannot checkout file '%s' from source control.", outputPath.c_str());
}
}
else
{
const bool fileExisted = AZ::IO::FileIOBase::GetInstance()->Exists(outputPath.c_str());
const bool fileReadOnly = AZ::IO::FileIOBase::GetInstance()->IsReadOnly(outputPath.c_str());
if (!fileExisted || !fileReadOnly)
{
SaveTextureSetting(outputPath);
}
}
}
void TexturePropertyEditor::SaveTextureSetting(AZStd::string outputPath)
{
if (!m_validImage)
{
return;
}
ImageProcessing::TextureSettings& baseSetting = m_textureSetting.GetMultiplatformTextureSetting();
for (auto& it : m_textureSetting.m_settingsMap)
{
baseSetting.ApplySettings(it.second, it.first);
}
ImageProcessing::StringOutcome outcome = ImageProcessing::TextureSettings::WriteTextureSetting(outputPath, baseSetting);
if (outcome.IsSuccess())
{
// Since setting is successfully saved, we can safely delete the legacy setting now
DeleteLegacySetting();
}
else
{
AZ_Error("Texture Editor", false, "Cannot save texture settings!");
}
}
void TexturePropertyEditor::DeleteLegacySetting()
{
AZStd::string legacyFile = m_textureSetting.m_fullPath + ImageProcessing::TextureSettings::legacyExtensionName;
const bool fileExisted = AZ::IO::FileIOBase::GetInstance()->Exists(legacyFile.c_str());
if (fileExisted)
{
bool sourceControlActive = false;
AzToolsFramework::SourceControlConnectionRequestBus::BroadcastResult(sourceControlActive, &AzToolsFramework::SourceControlConnectionRequests::IsActive);
if (sourceControlActive)
{
AzToolsFramework::SourceControlCommandBus::Broadcast(&AzToolsFramework::SourceControlCommandBus::Events::RequestDelete, legacyFile.c_str(),
[](bool success, const AzToolsFramework::SourceControlFileInfo& info)
{
//Deletes the file locally if it's not tracked by source control
if (!success && !info.IsManaged())
{
AZ::IO::FileIOBase::GetInstance()->Remove(info.m_filePath.c_str());
}
});
}
else
{
AZ::IO::FileIOBase::GetInstance()->Remove(legacyFile.c_str());
}
}
}
void TexturePropertyEditor::OnHelp()
{
QString webLink = tr("https://docs.aws.amazon.com/console/lumberyard/texturepipeline");
QDesktopServices::openUrl(QUrl(webLink));
}
bool TexturePropertyEditor::event(QEvent* event)
{
bool needsBlocking = false;
if (m_previewWidget)
{
needsBlocking = m_previewWidget->OnQtEvent(event);
}
return needsBlocking ? true : QWidget::event(event);
}
}//namespace ImageProcessingEditor
#include <Source/Editor/moc_TexturePropertyEditor.cpp>

@ -1,74 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzQtComponents/Components/StyledDialog.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
#include <Source/Editor/EditorCommon.h>
#include <Source/Editor/TexturePresetSelectionWidget.h>
#include <Source/Editor/TexturePreviewWidget.h>
#include <Source/Editor/ResolutionSettingWidget.h>
#include <Source/Editor/MipmapSettingWidget.h>
#endif
namespace Ui
{
class TexturePropertyEditor;
}
namespace ImageProcessingEditor
{
class TexturePropertyEditor
: public AzQtComponents::StyledDialog
, protected EditorInternalNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(TexturePropertyEditor, AZ::SystemAllocator, 0);
explicit TexturePropertyEditor(const AZ::Uuid& sourceTextureId, QWidget* parent = nullptr);
~TexturePropertyEditor();
bool HasValidImage();
protected:
void OnSave();
void OnHelp();
////////////////////////////////////////////////////////////////////////
//EditorInternalNotificationBus
void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform);
////////////////////////////////////////////////////////////////////////
bool event(QEvent* event) override;
private:
QScopedPointer<Ui::TexturePropertyEditor> m_ui;
QScopedPointer<TexturePreviewWidget> m_previewWidget;
QScopedPointer<TexturePresetSelectionWidget> m_presetSelectionWidget;
QScopedPointer<ResolutionSettingWidget> m_resolutionSettingWidget;
QScopedPointer<MipmapSettingWidget> m_mipmapSettingWidget;
EditorTextureSetting m_textureSetting;
bool m_validImage = true;
void SaveTextureSetting(AZStd::string outputPath);
void DeleteLegacySetting();
};
} //namespace ImageProcessingEditor

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TexturePropertyEditor</class>
<widget class="QDialog" name="TexturePropertyEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>580</width>
<height>1100</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>580</width>
<height>800</height>
</size>
</property>
<property name="baseSize">
<size>
<width>580</width>
<height>1200</height>
</size>
</property>
<property name="windowTitle">
<string>Texture Settings Editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>560</width>
<height>1049</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="mainLayout"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QToolButton" name="helpBtn">
<property name="text">
<string>?</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveBtn">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelBtn">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -1,29 +0,0 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/base.h>
typedef AZ::s8 int8;
typedef AZ::s8 sint8;
typedef AZ::u8 uint8;
typedef AZ::s16 int16;
typedef AZ::s16 sint16;
typedef AZ::u16 uint16;
typedef AZ::s32 int32;
typedef AZ::s32 sint32;
typedef AZ::u32 uint32;
typedef float f32;
typedef double f64;

@ -1,346 +0,0 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImageProcessing_precompiled.h"
#include <ImageBuilderComponent.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/Debug/Trace.h>
#include <BuilderSettings/BuilderSettingManager.h>
#include <BuilderSettings/CubemapSettings.h>
#include <Processing/ImageConvert.h>
#include <Processing/PixelFormatInfo.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <QFile>
#include <AzFramework/StringFunc/StringFunc.h>
#include <ImageLoader/ImageLoaders.h>
#include <Processing/ImageConvert.h>
#include <Processing/ImageToProcess.h>
namespace ImageProcessing
{
BuilderPluginComponent::BuilderPluginComponent()
{
// AZ Components should only initialize their members to null and empty in constructor
// after construction, they may be deserialized from file.
}
BuilderPluginComponent::~BuilderPluginComponent()
{
}
void BuilderPluginComponent::Init()
{
}
void BuilderPluginComponent::Activate()
{
// create and initialize BuilderSettingManager once since it's will be used for image conversion
BuilderSettingManager::CreateInstance();
auto outcome = ImageProcessing::BuilderSettingManager::Instance()->LoadBuilderSettings();
AZ_Error("Image Processing", outcome.IsSuccess(), "Failed to load default preset settings!");
// Activate is where you'd perform registration with other objects and systems.
// Since we want to register our builder, we do that here:
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = "Image Worker Builder";
builderDescriptor.m_version = 2;
builderDescriptor.m_analysisFingerprint = AZStd::string::format("%d", ImageProcessing::BuilderSettingManager::Instance()->BuilderSettingsVersion());
for (int i = 0; i < s_TotalSupportedImageExtensions; i++)
{
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(s_SupportedImageExtensions[i], AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
}
//add ".dds" here separately since we only apply copy operation for this type of file. and there won't be export option for dds files.
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.dds", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
builderDescriptor.m_busId = azrtti_typeid<ImageBuilderWorker>();
builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
m_imageBuilder.BusConnect(builderDescriptor.m_busId);
ImageProcessingRequestBus::Handler::BusConnect();
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
}
void BuilderPluginComponent::Deactivate()
{
ImageProcessingRequestBus::Handler::BusDisconnect();
m_imageBuilder.BusDisconnect();
BuilderSettingManager::DestroyInstance();
CPixelFormats::DestroyInstance();
}
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
{
// components also get Reflect called automatically
// this is your opportunity to perform static reflection or type registration of any types you want the serializer to know about
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<BuilderPluginComponent, AZ::Component>()
->Version(0)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
;
}
BuilderSettingManager::Reflect(context);
BuilderSettings::Reflect(context);
PresetSettings::Reflect(context);
CubemapSettings::Reflect(context);
MipmapSettings::Reflect(context);
TextureSettings::Reflect(context);
}
void BuilderPluginComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ImagerBuilderPluginService", 0x6dc0db6e));
}
void BuilderPluginComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ImagerBuilderPluginService", 0x6dc0db6e));
}
IImageObjectPtr BuilderPluginComponent::LoadImage(const AZStd::string& filePath)
{
return IImageObjectPtr(LoadImageFromFile(filePath));
}
IImageObjectPtr BuilderPluginComponent::LoadImagePreview(const AZStd::string& filePath)
{
IImageObjectPtr image(LoadImageFromFile(filePath));
if (image)
{
ImageToProcess imageToProcess(image);
imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
return imageToProcess.Get();
}
return image;
}
void ImageBuilderWorker::ShutDown()
{
// it is important to note that this will be called on a different thread than your process job thread
m_isShuttingDown = true;
}
// this happens early on in the file scanning pass
// this function should consistently always create the same jobs, and should do no checking whether the job is up to date or not - just be consistent.
void ImageBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
// Get the extension of the file
AZStd::string ext;
AzFramework::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), ext, false);
AZStd::to_upper(ext.begin(), ext.end());
// We process the same file for all platforms
for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
{
if (ImageProcessing::BuilderSettingManager::Instance()->DoesSupportPlatform(platformInfo.m_identifier))
{
AssetBuilderSDK::JobDescriptor descriptor;
descriptor.m_jobKey = ext + " Compile";
descriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
descriptor.m_critical = false;
descriptor.m_additionalFingerprintInfo = AZStd::string::format("%d", ImageProcessing::BuilderSettingManager::Instance()->BuilderSettingsVersion());
response.m_createJobOutputs.push_back(descriptor);
}
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
return;
}
// later on, this function will be called for jobs that actually need doing.
// the request will contain the CreateJobResponse you constructed earlier, including any keys and values you placed into the hash table
void ImageBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
// Before we begin, let's make sure we are not meant to abort.
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
AZStd::vector<AZStd::string> productFilepaths;
bool imageProcessingSuccessful = false;
bool needConversion = true;
//if the original file is a dds file then skip conversion
if (AzFramework::StringFunc::Path::IsExtension(request.m_fullPath.c_str(), "dds", false))
{
productFilepaths.push_back(request.m_fullPath);
imageProcessingSuccessful = true;
needConversion = false;
}
// Do conversion and get exported file's path
if (needConversion)
{
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Performing image conversion: %s\n", request.m_fullPath.c_str());
ImageConvertProcess* process = CreateImageConvertProcess(request.m_fullPath, request.m_tempDirPath,
request.m_jobDescription.GetPlatformIdentifier());
if (process != nullptr)
{
//the process can be stopped if the job is cancelled or the worker is shutting down
while (!process->IsFinished() && !m_isShuttingDown && !jobCancelListener.IsCancelled())
{
process->UpdateProcess();
}
//get process result
imageProcessingSuccessful = process->IsSucceed();
process->GetAppendOutputFilePaths(productFilepaths);
delete process;
}
else
{
imageProcessingSuccessful = false;
}
}
if (imageProcessingSuccessful)
{
AZ::Outcome<void, AZStd::string> result = PopulateProducts(request, productFilepaths, response.m_outputProducts);
if (result.IsSuccess())
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
else
{
AZ_Error(AssetBuilderSDK::ErrorWindow, false, result.GetError().c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
}
}
else
{
if (m_isShuttingDown)
{
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
}
else if (jobCancelListener.IsCancelled())
{
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled was requested for job %s.\n", request.m_fullPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
}
else
{
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Unexpected error during processing job %s.\n", request.m_fullPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
}
}
}
AZ::Outcome<void, AZStd::string> ImageBuilderWorker::PopulateProducts(const AssetBuilderSDK::ProcessJobRequest& request, const AZStd::vector<AZStd::string>& productFilepaths, AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts)
{
AssetBuilderSDK::JobProduct* rgbBaseJobProduct = nullptr;
AssetBuilderSDK::JobProduct* diffBaseJobProduct = nullptr;
AssetBuilderSDK::JobProduct* baseJobProduct = nullptr;
AssetBuilderSDK::JobProduct* alphaBaseJobProduct = nullptr;
// Report the image-import result (filepath to one or many '.dds')
// This reserve is critically important to prevent resizing the vector and invalidating the pointers we need to save off
jobProducts.reserve(productFilepaths.size());
for (const auto& product : productFilepaths)
{
AssetBuilderSDK::JobProduct jobProduct(product);
jobProduct.m_dependenciesHandled = true; // Dependencies are handled down below. The base products will have dependencies, lod products won't
jobProducts.push_back(jobProduct);
AZ::u32 lodLevel = AssetBuilderSDK::GetSubID_LOD(jobProduct.m_productSubID);
if(jobProduct.m_productSubID == 0)
{
rgbBaseJobProduct = &jobProducts.back();
}
else if((jobProduct.m_productSubID & AssetBuilderSDK::SUBID_FLAG_DIFF) && lodLevel == 0)
{
diffBaseJobProduct = &jobProducts.back();
}
else if((jobProduct.m_productSubID & AssetBuilderSDK::SUBID_FLAG_ALPHA) && lodLevel == 0)
{
alphaBaseJobProduct = &jobProducts.back();
}
}
//We can have a diff and/or a rgb base. The rgb base always takes precedence when present
baseJobProduct = rgbBaseJobProduct;
if(!baseJobProduct)
{
baseJobProduct = diffBaseJobProduct;
}
for (AssetBuilderSDK::JobProduct& jobProduct : jobProducts)
{
AssetBuilderSDK::ProductDependency productDependency(AZ::Data::AssetId(request.m_sourceFileUUID, jobProduct.m_productSubID), 0);
AZ::u32 lodLevel = AssetBuilderSDK::GetSubID_LOD(jobProduct.m_productSubID);
bool isAlpha = jobProduct.m_productSubID & AssetBuilderSDK::SUBID_FLAG_ALPHA;
if (lodLevel > 0)
{
if (isAlpha)
{
if (alphaBaseJobProduct)
{
// add all alpha mips to the base alpha texture as product dependency
alphaBaseJobProduct->m_dependencies.push_back(productDependency);
}
else
{
return AZ::Failure(AZStd::string::format("Unable to add (%s) file as a product dependency of the base alpha texture file. Base alpha texture file is missing from the products list.\n", jobProduct.m_productFileName.c_str()));
}
}
else
{
if (baseJobProduct)
{
// add all rgb mips to the base rgb texture as product dependency
baseJobProduct->m_dependencies.push_back(productDependency);
}
else
{
return AZ::Failure(AZStd::string::format("Unable to add (%s) file as a product dependency of the base rgb texture file. Base rgb texture file is missing from the products list.\n", jobProduct.m_productFileName.c_str()));
}
}
}
}
// Diffuse (_diff) is required by the base (typically for cubemaps)
if (rgbBaseJobProduct && diffBaseJobProduct)
{
AssetBuilderSDK::ProductDependency productDependency(AZ::Data::AssetId(request.m_sourceFileUUID, diffBaseJobProduct->m_productSubID), 0);
rgbBaseJobProduct->m_dependencies.push_back(productDependency);
}
if (alphaBaseJobProduct && baseJobProduct)
{
// Add the alphaBaseTexture as a product dependency for baseTexture
AssetBuilderSDK::ProductDependency productDependency(AZ::Data::AssetId(request.m_sourceFileUUID, alphaBaseJobProduct->m_productSubID), 0);
baseJobProduct->m_dependencies.push_back(productDependency);
}
return AZ::Success();
}
} // namespace ImageProcessing

@ -1,80 +0,0 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/Outcome/Outcome.h>
#include <ImageProcessing/ImageProcessingBus.h>
namespace ImageProcessing
{
//! Builder to process images
class ImageBuilderWorker
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
AZ_RTTI(ImageBuilderWorker, "{525422DE-05B3-4095-966F-90CD7657A7E1}");
ImageBuilderWorker() = default;
~ImageBuilderWorker() = default;
//! Asset Builder Callback Functions
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
//////////////////////////////////////////////////////////////////////////
//!AssetBuilderSDK::AssetBuilderCommandBus interface
void ShutDown() override; // if you get this you must fail all existing jobs and return.
//////////////////////////////////////////////////////////////////////////
//! Populates the jobProduct vector with all the entries including their product dependencies
AZ::Outcome<void, AZStd::string> PopulateProducts(const AssetBuilderSDK::ProcessJobRequest& request, const AZStd::vector<AZStd::string>& productFilepaths, AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts);
private:
bool m_isShuttingDown = false;
};
//! BuilderPluginComponent is to handle the lifecycle of ImageBuilder module.
class BuilderPluginComponent
: public AZ::Component
, protected ImageProcessingRequestBus::Handler
{
public:
AZ_COMPONENT(BuilderPluginComponent, "{2F12E1BE-D8F6-47A4-AC3E-6C5527C55840}")
static void Reflect(AZ::ReflectContext* context);
BuilderPluginComponent(); // avoid initialization here.
~BuilderPluginComponent() override; // free memory an uninitialize yourself.
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Init() override; // create objects, allocate memory and initialize yourself without reaching out to the outside world
void Activate() override; // reach out to the outside world and connect up to what you need to, register things, etc.
void Deactivate() override; // unregister things, disconnect from the outside world
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
//////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// ImageProcessingRequestBus interface implementation
IImageObjectPtr LoadImage(const AZStd::string& filePath) override;
IImageObjectPtr LoadImagePreview(const AZStd::string& filePath) override;
////////////////////////////////////////////////////////////////////////
private:
ImageBuilderWorker m_imageBuilder;
};
}// namespace ImageProcessing

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save