Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,230 @@
/*
* 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/Math/Color.h>
#include <AzCore/std/algorithm.h>
#include <ImageProcessing_Traits_Platform.h>
//! The following defines and constants are extracted from ImageExtensionHelper.h
//! Please make sure they are always synced with ImageExtensionHelper.h
#define IMAGE_BUIDER_MAKEFOURCC(ch0, ch1, ch2, ch3) \
((AZ::u32)(AZ::u8)(ch0) | ((AZ::u32)(AZ::u8)(ch1) << 8) | \
((AZ::u32)(AZ::u8)(ch2) << 16) | ((AZ::u32)(AZ::u8)(ch3) << 24))
// This header defines constants and structures that are useful when parsing
// DDS files. DDS files were originally designed to use several structures
// and constants that are native to DirectDraw and are defined in ddraw.h,
// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar
// (compatible) constants and structures so that one can use DDS files
// without needing to include ddraw.h.
//Needed to write out DDS files on Mac
#if AZ_TRAIT_IMAGEPROCESSING_DEFINE_DIRECT3D_CONSTANTS
#define DDPF_ALPHAPIXELS 0x00000001 // Texture contains alpha data
#define DDPF_ALPHA 0x00000002 // For alpha channel only uncompressed data
#define DDPF_FOURCC 0x00000004 // Texture contains compressed RGB data
#define DDPF_RGB 0x00000040 // Texture contains uncompressed RGB data
#define DDPF_YUV 0x00000200 // For YUV uncompressed data
#define DDPF_LUMINANCE 0x00020000 // For single channel color uncompressed data
#define DDSCAPS_COMPLEX 0x00000008 // Must be used on any file that contains more than one surface
#define DDSCAPS_MIPMAP 0x00400000 // Should be used for a mipmap
#define DDSCAPS_TEXTURE 0x00001000 // Required
#endif
#define DDS_FOURCC 0x00000004 // DDPF_FOURCC
#define DDS_RGB 0x00000040 // DDPF_RGB
#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE
#define DDS_SIGNED 0x00080000 // DDPF_SIGNED
#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS
#define DDS_LUMINANCEA 0x00020001 // DDS_LUMINANCE | DDPF_ALPHAPIXELS
#define DDS_A 0x00000001 // DDPF_ALPHAPIXELS
#define DDS_A_ONLY 0x00000002 // DDPF_ALPHA
#define DDS_FOURCC_A16B16G16R16 0x00000024 // FOURCC A16B16G16R16
#define DDS_FOURCC_V16U16 0x00000040 // FOURCC V16U16
#define DDS_FOURCC_Q16W16V16U16 0x0000006E // FOURCC Q16W16V16U16
#define DDS_FOURCC_R16F 0x0000006F // FOURCC R16F
#define DDS_FOURCC_G16R16F 0x00000070 // FOURCC G16R16F
#define DDS_FOURCC_A16B16G16R16F 0x00000071 // FOURCC A16B16G16R16F
#define DDS_FOURCC_R32F 0x00000072 // FOURCC R32F
#define DDS_FOURCC_G32R32F 0x00000073 // FOURCC G32R32F
#define DDS_FOURCC_A32B32G32R32F 0x00000074 // FOURCC A32B32G32R32F
#define DDSD_CAPS 0x00000001l // default
#define DDSD_PIXELFORMAT 0x00001000l
#define DDSD_WIDTH 0x00000004l
#define DDSD_HEIGHT 0x00000002l
#define DDSD_LINEARSIZE 0x00080000l
#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT
#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH
#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH
#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE
#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE
#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX
#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
#define DDS_CUBEMAP_ALLFACES (DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX | \
DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY | \
DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ)
#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME
#define DDS_RESF1_NORMALMAP 0x01000000
#define DDS_RESF1_DSDT 0x02000000
namespace ImageProcessing
{
const static AZ::u32 FOURCC_DX10 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', '1', '0');
const static AZ::u32 FOURCC_DDS = IMAGE_BUIDER_MAKEFOURCC('D', 'D', 'S', ' ');
const static AZ::u32 FOURCC_FYRC = IMAGE_BUIDER_MAKEFOURCC('F', 'Y', 'R', 'C');
//The values of each elements in this enum should be same as ITexture ETEX_TileMode enum.
enum DDS_TileMode : AZ::u8
{
eTM_None = 0,
eTM_LinearPadded,
eTM_Optimal,
};
struct DDS_PIXELFORMAT
{
AZ::u32 dwSize;
AZ::u32 dwFlags;
AZ::u32 dwFourCC;
AZ::u32 dwRGBBitCount;
AZ::u32 dwRBitMask;
AZ::u32 dwGBitMask;
AZ::u32 dwBBitMask;
AZ::u32 dwABitMask;
const bool operator == (const DDS_PIXELFORMAT& fmt) const
{
return dwFourCC == fmt.dwFourCC &&
dwFlags == fmt.dwFlags &&
dwRGBBitCount == fmt.dwRGBBitCount &&
dwRBitMask == fmt.dwRBitMask &&
dwGBitMask == fmt.dwGBitMask &&
dwBBitMask == fmt.dwBBitMask &&
dwABitMask == fmt.dwABitMask &&
dwSize == fmt.dwSize;
}
};
struct DDS_HEADER_DXT10
{
// we're unable to use native enums, so we use AZ::u32 instead.
AZ::u32 /*DXGI_FORMAT*/ dxgiFormat;
AZ::u32 /*D3D10_RESOURCE_DIMENSION*/ resourceDimension;
AZ::u32 miscFlag;
AZ::u32 arraySize;
AZ::u32 reserved;
};
struct DDS_HEADER
{
AZ::u32 dwSize;
AZ::u32 dwHeaderFlags;
AZ::u32 dwHeight;
AZ::u32 dwWidth;
AZ::u32 dwPitchOrLinearSize;
AZ::u32 dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwHeaderFlags
AZ::u32 dwMipMapCount;
AZ::u32 dwAlphaBitDepth;
AZ::u32 dwReserved1; // Crytek image flags
float fAvgBrightness; // Average top mip brightness. Could be f16/half
float cMinColor[4];
float cMaxColor[4];
DDS_PIXELFORMAT ddspf;
AZ::u32 dwSurfaceFlags;
AZ::u32 dwCubemapFlags;
AZ::u8 bNumPersistentMips;
AZ::u8 tileMode; //DDS_TileMode
AZ::u8 bReserved2[6];
AZ::u32 dwTextureStage;
inline const bool IsValid() const { return sizeof(*this) == dwSize; }
inline const bool IsDX10Ext() const { return ddspf.dwFourCC == FOURCC_DX10; }
inline const AZ::u32 GetMipCount() const { return AZStd::GetMax(1u, (AZ::u32)dwMipMapCount); }
inline const size_t GetFullHeaderSize() const
{
if (IsDX10Ext())
{
return sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
}
return sizeof(DDS_HEADER);
}
};
// standard description of file header
struct DDS_FILE_DESC
{
AZ::u32 dwMagic;
DDS_HEADER header;
inline const bool IsValid() const { return dwMagic == FOURCC_DDS && header.IsValid(); }
inline const size_t GetFullHeaderSize() const { return sizeof(dwMagic) + header.GetFullHeaderSize(); }
};
// chunk identifier
const static AZ::u32 FOURCC_CExt = IMAGE_BUIDER_MAKEFOURCC('C', 'E', 'x', 't'); // Crytek extension start
const static AZ::u32 FOURCC_CEnd = IMAGE_BUIDER_MAKEFOURCC('C', 'E', 'n', 'd'); // Crytek extension end
const static AZ::u32 FOURCC_AttC = IMAGE_BUIDER_MAKEFOURCC('A', 't', 't', 'C'); // Chunk Attached Channel
//Fourcc for pixel formats which aren't supported by dx10, such as astc formats, etc formats, pvrtc formats
//They are used for dwFourCC of dds header's DDS_PIXELFORMAT to identify non-dx10 pixel formats
const static AZ::u32 FOURCC_EAC_R11 = IMAGE_BUIDER_MAKEFOURCC('E', 'A', 'R', ' ');
const static AZ::u32 FOURCC_EAC_RG11 = IMAGE_BUIDER_MAKEFOURCC('E', 'A', 'R', 'G');
const static AZ::u32 FOURCC_ETC2 = IMAGE_BUIDER_MAKEFOURCC('E', 'T', '2', ' ');
const static AZ::u32 FOURCC_ETC2A = IMAGE_BUIDER_MAKEFOURCC('E', 'T', '2', 'A');
const static AZ::u32 FOURCC_PVRTC2 = IMAGE_BUIDER_MAKEFOURCC('P', 'V', 'R', '2');
const static AZ::u32 FOURCC_PVRTC4 = IMAGE_BUIDER_MAKEFOURCC('P', 'V', 'R', '4');
const static AZ::u32 FOURCC_ASTC_4x4 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '4', '4');
const static AZ::u32 FOURCC_ASTC_5x4 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '5', '4');
const static AZ::u32 FOURCC_ASTC_5x5 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '5', '5');
const static AZ::u32 FOURCC_ASTC_6x5 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '6', '5');
const static AZ::u32 FOURCC_ASTC_6x6 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '6', '6');
const static AZ::u32 FOURCC_ASTC_8x5 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '8', '5');
const static AZ::u32 FOURCC_ASTC_8x6 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '8', '6');
const static AZ::u32 FOURCC_ASTC_10x5 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'A', '5');
const static AZ::u32 FOURCC_ASTC_10x6 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'A', '6');
const static AZ::u32 FOURCC_ASTC_8x8 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '8', '8');
const static AZ::u32 FOURCC_ASTC_10x8 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'A', '8');
const static AZ::u32 FOURCC_ASTC_10x10 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'A', 'A');
const static AZ::u32 FOURCC_ASTC_12x10 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'C', 'A');
const static AZ::u32 FOURCC_ASTC_12x12 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'C', 'C');
//legacy formats names. they are only used for load rc.exe's dds formats
const static AZ::u32 FOURCC_DXT1 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '1');
const static AZ::u32 FOURCC_DXT3 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '3');
const static AZ::u32 FOURCC_DXT5 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '5');
const static AZ::u32 FOURCC_3DCP = IMAGE_BUIDER_MAKEFOURCC('A', 'T', 'I', '1');
const static AZ::u32 FOURCC_3DC = IMAGE_BUIDER_MAKEFOURCC('A', 'T', 'I', '2');
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,182 @@
/*
* 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 <QRect>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/PresetSettings.h>
#include <BuilderSettings/TextureSettings.h>
#include <ImageProcessing/ImageObject.h>
#include <Compressors/Compressor.h>
#include <AzCore/Jobs/Job.h>
#include <AzCore/std/string/string.h>
namespace ImageProcessing
{
class IImageObject;
class ImageToProcess;
//Convert image file with its image export setting and save to specified folder.
//this function can be useful for a cancelable job
class ImageConvertProcess* CreateImageConvertProcess(const AZStd::string& imageFilePath,
const AZStd::string& exportDir, const PlatformName& platformName, AZ::SerializeContext* context = nullptr);
//Convert image file with its image export setting and save to specified folder. it will return when the whole conversion is done.
//Could be used for command mode or test
bool ConvertImageFile(const AZStd::string& imageFilePath, const AZStd::string& exportDir, AZStd::vector<AZStd::string>& outPaths,
const PlatformName& platformName = "", AZ::SerializeContext* context = nullptr);
//image filter function
void FilterImage(MipGenType genType, MipGenEvalType evalType, float blurH, float blurV, const IImageObjectPtr srcImg, int srcMip,
IImageObjectPtr dstImg, int dstMip, QRect* srcRect, QRect* dstRect);
//get compression error for an image converting to certain format
void GetBC1CompressionErrors(IImageObjectPtr originImage, float& errorLinear, float& errorSrgb,
ICompressor::CompressOption option);
float GetErrorBetweenImages(IImageObjectPtr inputImage1, IImageObjectPtr inputImage2);
//Combine image with alpha image if any and output as RGBA8
IImageObjectPtr MergeOutputImageForPreview(IImageObjectPtr image, IImageObjectPtr alphaImage);
//get output image size and mip count based on the texture setting and preset setting
//other helper functions
//Get desired output image size based on the texture settings
void GetOutputExtent(AZ::u32 inputWidth, AZ::u32 inputHeight, AZ::u32& outWidth, AZ::u32& outHeight, AZ::u32& outReduce,
const TextureSettings* textureSettings, const PresetSettings* presetSettings);
class ImageConvertProcess
{
public:
//constructor
ImageConvertProcess(const IImageObjectPtr inputImage, const TextureSettings& textureSetting,
const PresetSettings& presetSetting, bool isPreview, bool isStreaming, bool canOverridePreset,
const AZStd::string& outputPath, const AZStd::string& platformId);
~ImageConvertProcess();
//doing image conversion, this function need to be called repeatly until the process is done
//it could used for a working thread which may need to cancel a process
void UpdateProcess();
//doing all conversion in one step. This function will call UpdateProcess in a while loop until it's done.
void ProcessAll();
//for multi-thread
//get percentage of image convertion progress
float GetProgress();
bool IsFinished();
bool IsSucceed();
//get output images
IImageObjectPtr GetOutputImage();
IImageObjectPtr GetOutputAlphaImage();
IImageObjectPtr GetOutputDiffCubemap();
//get output file paths and append the paths to the outPaths vector.
void GetAppendOutputFilePaths(AZStd::vector<AZStd::string>& outPaths);
private:
enum ConvertStep
{
StepValidateInput = 0,
StepGenerateColorChart,
StepConvertToLinear,
StepSwizzle,
StepOverridePreset,
StepCubemapLayout,
StepPreNormalize,
StepDiffCubemap,
StepMipmap,
StepGlossFromNormal,
StepPostNormalize,
StepCreateHighPass,
StepConvertOutputColorSpace,
StepAlphaImage,
StepConvertPixelFormat,
StepSaveToFile,
StepAll
};
//input image and settings
const IImageObjectPtr m_inputImage;
TextureSettings m_textureSetting;
PresetSettings m_presetSetting;
bool m_canOverridePreset;
bool m_isPreview;
AZStd::string m_outputPath;
AZStd::string m_platformId;
//some global settings from builder setting
bool m_isStreaming;
//for alpha
//to indicate the current alpha chanenl content
EAlphaContent m_alphaContent;
//An image object to hold alpha channel in a seperate image
IImageObjectPtr m_alphaImage;
//for cubemap
//An image object to save output result of diffuse cubemap conversion
IImageObjectPtr m_diffCubemapImage;
//image for processing
ImageToProcess *m_image;
//progress
uint32 m_progressStep;
bool m_isFinished;
bool m_isSucceed;
//all the output products' paths
AZStd::vector<AZStd::string> m_productFilepaths;
//for get processing time
AZStd::sys_time_t m_startTime;
double m_processTime; //in seconds
private:
//validate the input image and settings
bool ValidateInput();
//mipmap generation
bool FillMipmaps();
//mipmap generation for cubemap
bool FillCubemapMipmaps();
//special case: create diffuse cubemap
void CreateDiffuseCubemap();
//convert color space to linear with pixel format rgba32f
bool ConvertToLinear();
//convert to output color space before compression
bool ConvertToOuputColorSpace();
//create alpha image if it's needed
void CreateAlphaImage();
//pixel format convertion/compression
bool ConvertPixelformat();
//save output image to a file
bool SaveOutput();
//if it's converting for cubemap
bool IsConvertToCubemap();
};
}// namespace ImageProcessing
@@ -0,0 +1,148 @@
/*
* 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/ImageConvertJob.h>
#include <Processing/PixelFormatInfo.h>
#include <Processing/ImageToProcess.h>
#include <Converters/PixelOperation.h>
#include <Processing/ImageConvert.h>
#include <BuilderSettings/BuilderSettingManager.h>
namespace ImageProcessing
{
IImageObjectPtr ImageConvertOutput::GetOutputImage(OutputImageType type) const
{
if (type < OutputImageType::Count)
{
return m_outputImage[static_cast<int>(type)];
}
else
{
return IImageObjectPtr();
}
}
void ImageConvertOutput::SetOutputImage(IImageObjectPtr image, OutputImageType type)
{
if (type < OutputImageType::Count)
{
m_outputImage[static_cast<int>(type)] = image;
}
else
{
AZ_Error("ImageProcess", false, "Cannot set output image to %d", type);
}
}
void ImageConvertOutput::SetReady(bool ready)
{
m_outputReady = ready;
}
bool ImageConvertOutput::IsReady() const
{
return m_outputReady;
}
float ImageConvertOutput::GetProgress() const
{
return m_progress;
}
void ImageConvertOutput::SetProgress(float progress)
{
m_progress = progress;
}
void ImageConvertOutput::Reset()
{
for (int i = 0; i < static_cast<int>(OutputImageType::Count); i ++ )
{
m_outputImage[i] = nullptr;
}
m_outputReady = false;
m_progress = 0.0f;
}
ImageConvertJob::ImageConvertJob(IImageObjectPtr image, const TextureSettings* textureSetting,
const PresetSettings* preset, bool isPreview, const AZStd::string& platformId,
ImageConvertOutput* output, bool autoDelete /*= true*/, AZ::JobContext* jobContext /*= nullptr*/)
: AZ::Job(autoDelete, jobContext)
, m_isPreview(isPreview)
, m_isCancelled(false)
, m_output(output)
{
AZ_Assert(m_output, "Needs to have an output destination for image conversion!");
if (image && textureSetting && preset)
{
bool isStreaming = BuilderSettingManager::Instance()->GetBuilderSetting(platformId)->m_enableStreaming;
bool canOverridePreset = false;
m_process = AZStd::make_unique<ImageConvertProcess>(image, *textureSetting, *preset, isPreview, isStreaming, canOverridePreset, "", platformId);
}
}
void ImageConvertJob::Process()
{
if (!m_process)
{
AZ_Error("Image Processing", false, "Cannot start processing, invalid setting or image!");
m_output->SetReady(true);
m_output->SetProgress(1.0f);
return;
}
m_output->SetReady(false);
while (!m_process->IsFinished() && !IsJobCancelled())
{
m_process->UpdateProcess();
if (m_isPreview)
{
m_output->SetProgress(m_process->GetProgress() / static_cast<float>(m_previewProcessStep));
}
else
{
m_output->SetProgress(m_process->GetProgress());
}
}
IImageObjectPtr outputImage = m_process->GetOutputImage();
IImageObjectPtr outputImageAlpha = m_process->GetOutputAlphaImage();
m_output->SetOutputImage(outputImage, ImageConvertOutput::Base);
m_output->SetOutputImage(outputImageAlpha, ImageConvertOutput::Alpha);
if (m_isPreview && !IsJobCancelled())
{
// For preview, combine image output with alpha if any
m_output->SetProgress(1.0f / static_cast<float>(m_previewProcessStep));
IImageObjectPtr combinedImage = MergeOutputImageForPreview(outputImage, outputImageAlpha);
m_output->SetOutputImage(combinedImage, ImageConvertOutput::Preview);
}
m_output->SetReady(true);
m_output->SetProgress(1.0f);
}
void ImageConvertJob::Cancel()
{
m_isCancelled = true;
}
bool ImageConvertJob::IsJobCancelled()
{
return m_isCancelled || IsCancelled();
}
}// namespace ImageProcessing
@@ -0,0 +1,74 @@
/*
* 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/PresetSettings.h>
#include <BuilderSettings/TextureSettings.h>
#include <ImageProcessing/ImageObject.h>
#include <AzCore/Jobs/Job.h>
namespace ImageProcessing
{
class ImageConvertProcess;
class ImageConvertOutput
{
public:
enum OutputImageType
{
Base = 0, // Might contains alpha or not
Alpha, // Separate alpha image
Preview, // Combine base image with alpha if any, format RGBA8
Count
};
IImageObjectPtr GetOutputImage(OutputImageType type) const;
void SetOutputImage(IImageObjectPtr image, OutputImageType type);
void SetReady(bool ready);
bool IsReady() const;
float GetProgress() const;
void SetProgress(float progress);
void Reset();
private:
IImageObjectPtr m_outputImage[OutputImageType::Count];
bool m_outputReady = false;
float m_progress = 0.0f;
};
class ImageConvertJob
: public AZ::Job
{
public:
AZ_CLASS_ALLOCATOR(ImageConvertJob, AZ::ThreadPoolAllocator, 0)
ImageConvertJob(IImageObjectPtr image, const TextureSettings* textureSetting, const PresetSettings* preset
, bool isPreview, const AZStd::string& platformId, ImageConvertOutput* output, bool autoDelete = true
, AZ::JobContext* jobContext = nullptr);
void Process() override;
// Cancel the job itself
void Cancel();
// Whether the job is being cancelled or the whole job group is being cancelled
bool IsJobCancelled();
private:
static const int m_previewProcessStep = 2;
AZStd::unique_ptr<ImageConvertProcess> m_process;
bool m_isPreview;
AZStd::atomic_bool m_isCancelled;
ImageConvertOutput* m_output;
};
}// namespace ImageProcessing
@@ -0,0 +1,43 @@
/*
* 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
//! The following constants are extracted from ImageExtensionHelper.h
//! Please make sure they are always synced with the same constants defined in ImageExtensionHelper.h
namespace ImageProcessing
{
// flags to propagate from the RC to the engine through GetImageFlags()
// 32bit bitmask, numbers should not change as engine relies on them
const static AZ::u32 EIF_Cubemap = 0x1;
const static AZ::u32 EIF_Volumetexture = 0x2;
const static AZ::u32 EIF_Decal = 0x4; // this is usually set through the preset
const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color
const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture
const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use
const static AZ::u32 EIF_AttachedAlpha = 0x400; // info for the engine: it's a texture with attached alpha channel
const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear)
const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized with r_TexResolution
const static AZ::u32 EIF_RenormalizedTexture = 0x10000; // info for the engine: for dds textures that have renormalized color range
const static AZ::u32 EIF_CafeNative = 0x20000; // info for the engine: native Cafe texture format
const static AZ::u32 EIF_RestrictedPlatformONative = 0x40000; // native tiled texture for restrict platform O
const static AZ::u32 EIF_Tiled = 0x80000; // info for the engine: texture has been tiled for the platform
const static AZ::u32 EIF_RestrictedPlatformDNative = 0x100000; // native tiled texture for restrict platform D
const static AZ::u32 EIF_Splitted = 0x200000; // info for the engine: this texture is splitted
const static AZ::u32 EIF_Colormodel = 0x7000000; // info for the engine: bitmask: colormodel used in the texture
const static AZ::u32 EIF_Colormodel_RGB = 0x0000000; // info for the engine: colormodel is RGB (default)
const static AZ::u32 EIF_Colormodel_CIE = 0x1000000; // info for the engine: colormodel is CIE (used for terrain)
const static AZ::u32 EIF_Colormodel_YCC = 0x2000000; // info for the engine: colormodel is Y'CbCr (used for reflectance)
const static AZ::u32 EIF_Colormodel_YFF = 0x3000000; // info for the engine: colormodel is Y'FbFr (used for reflectance)
const static AZ::u32 EIF_Colormodel_IRB = 0x4000000; // info for the engine: colormodel is IRB (used for reflectance)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
/*
* 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/ImageObject.h>
#include <Processing/DDSHeader.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace ImageProcessing
{
// ImageObject allows the abstraction of different kinds of
// images generated during conversion
class CImageObject: public IImageObject
{
public:
AZ_CLASS_ALLOCATOR(CImageObject, AZ::SystemAllocator, 0);
public:
// Constructors
CImageObject(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat);
~CImageObject();
//virtual functions from IImageObject
IImageObject* AllocateImage(EPixelFormat pixelFormat) const override;
IImageObject* AllocateImage() const override;
IImageObject* Clone() const override;
EPixelFormat GetPixelFormat() const override;
AZ::u32 GetPixelCount(AZ::u32 mip) const override;
AZ::u32 GetWidth(AZ::u32 mip) const override;
AZ::u32 GetHeight(AZ::u32 mip) const override;
AZ::u32 GetMipCount() const override;
bool IsCubemap() const override
{
return false;
};
void GetImagePointer(AZ::u32 mip, AZ::u8*& pMem, AZ::u32& pitch) const override;
AZ::u32 GetMipBufSize(AZ::u32 mip) const override;
void SetMipData(AZ::u32 mip, AZ::u8* mipBuf, AZ::u32 bufSize, AZ::u32 pitch) override;
AZ::u32 GetImageFlags() const override;
void SetImageFlags(AZ::u32 imageFlags) override;
void AddImageFlags(AZ::u32 imageFlags) override;
void RemoveImageFlags(AZ::u32 imageFlags) override;
bool HasImageFlags(AZ::u32 imageFlags) const override;
//image data operations and calculations
void ScaleAndBiasChannels(AZ::u32 firstMip, AZ::u32 maxMipCount, const AZ::Vector4& scale, const AZ::Vector4& bias) override;
void ClampChannels(AZ::u32 firstMip, AZ::u32 maxMipCount, const AZ::Vector4& min, const AZ::Vector4& max) override;
void TransferAlphaCoverage(const TextureSettings* textureSetting, const IImageObjectPtr srcImg) override;
float ComputeAlphaCoverageScaleFactor(AZ::u32 mip, float fDesiredCoverage, float fAlphaRef) const override;
float ComputeAlphaCoverage(AZ::u32 firstMip, float fAlphaRef) const override;
bool CompareImage(const IImageObjectPtr otherImage) const override;
bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const override;
bool SaveImage(AZ::IO::SystemFileStream& out) const override;
bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const override;
uint GetTextureMemory() const override;
EAlphaContent GetAlphaContent() const override;
void NormalizeVectors(AZ::u32 firstMip, AZ::u32 maxMipCount) override;
void CopyPropertiesFrom(const IImageObjectPtr src) override;
void Swizzle(const char channels[4]) override;
void GetColorRange(AZ::Color& minColor, AZ::Color& maxColor) const override;
void SetColorRange(const AZ::Color& minColor, const AZ::Color& maxColor) override;
float GetAverageBrightness() const override;
void SetAverageBrightness(float avgBrightness) override;
AZ::u32 GetNumPersistentMips() const override;
void SetNumPersistentMips(AZ::u32 nMips) override;
void GlossFromNormals(bool hasAuthoredGloss) override;
void ConvertLegacyGloss() override;
void ClearColor(float r, float g, float b, float a) override;
//end virtual functions from IImageObject
private:
enum EColorNormalization
{
eColorNormalization_Normalize,
eColorNormalization_PassThrough,
};
enum EAlphaNormalization
{
eAlphaNormalization_SetToZero,
eAlphaNormalization_Normalize,
eAlphaNormalization_PassThrough,
};
private:
class MipLevel
{
public:
AZ_CLASS_ALLOCATOR(MipLevel, AZ::SystemAllocator, 0);
AZ::u32 m_width;
AZ::u32 m_height;
AZ::u32 m_rowCount; // for compressed textures m_rowCount is usually less than m_height
AZ::u32 m_pitch; // row size in bytes
AZ::u8* m_pData;
public:
MipLevel()
: m_width(0)
, m_height(0)
, m_rowCount(0)
, m_pitch(0)
, m_pData(0)
{
}
~MipLevel()
{
delete[] m_pData;
m_pData = 0;
}
void Alloc()
{
AZ_Assert(m_pData == 0, "Mip data must be empty before Allocation!");
m_pData = new AZ::u8[m_pitch * m_rowCount];
}
AZ::u32 GetSize() const
{
AZ_Assert(m_pitch, "Pitch must be greater than zero!");
return m_pitch * m_rowCount;
}
bool operator==(const MipLevel& other)
{
if (m_width == other.m_width && m_height == other.m_height
&& m_rowCount == other.m_rowCount && m_pitch == other.m_pitch)
{
return (memcmp(m_pData, other.m_pData, m_pitch * m_rowCount) == 0);
}
return false;
}
};
private:
EPixelFormat m_pixelFormat;
std::vector<MipLevel*> m_mips; // stores *pointers* to avoid reallocations when elements are erase()'d
AZ::Color m_colMinARGB; // ARGB will be added the properties of the DDS file
AZ::Color m_colMaxARGB; // ARGB will be added the properties of the DDS file
float m_averageBrightness; // will be added to the properties of the DDS file
AZ::u32 m_imageFlags; // combined from CImageExtensionHelper::EIF_Cubemap,...
AZ::u32 m_numPersistentMips; // number of mipmaps won't be splitted
public:
//reset this image object to specified format and size
void ResetImage(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat);
//get mip count and the origin (top mip) size
void GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const;
AZ::u32 GetMipDataSize(AZ::u32 mip) const;
//! calculates the average brightness for a texture
float CalculateAverageBrightness() const;
bool HasPowerOfTwoSizes() const override;
void CopyPropertiesFrom(const CImageObject* src);
// Computes the dynamically used range for the texture and expands it to use the
// full range [0,2^(2^ExponentBits-1)] for better quality.
void NormalizeImageRange(EColorNormalization eColorNorm, EAlphaNormalization eAlphaNorm, bool bMaintainBlack = false, int nExponentBits = 0);
// Brings normalized ranges back to it's original range.
void ExpandImageRange(EColorNormalization eColorNorm, EAlphaNormalization eAlphaNorm, int nExponentBits = 0);
private:
//build image file header from this image object
bool BuildSurfaceHeader(DDS_HEADER& header) const;
bool BuildSurfaceExtendedHeader(DDS_HEADER_DXT10& exthead) const;
};
} // namespace ImageProcessing
@@ -0,0 +1,206 @@
/*
* 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/ImagePreview.h>
#include <BuilderSettings/BuilderSettingManager.h>
#include <ImageLoader/ImageLoaders.h>
#include <Processing/ImageConvert.h>
#include <Processing/PixelFormatInfo.h>
#include <Editor/EditorCommon.h>
#include <Processing/ImageFlags.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace ImageProcessing
{
ImagePreview::ImagePreview(const AZStd::string& inputImageFile, TextureSettings* textureSetting)
: m_imageFileName(inputImageFile)
, m_textureSetting(textureSetting)
, m_presetSetting(nullptr)
, m_inputImage(nullptr)
{
InitializeJobSettings();
}
void ImagePreview::StartConvert()
{
// If there is ongoing job, cancel it
Cancel();
m_output.Reset();
if (m_inputImage == nullptr)
{
// Load input image
m_inputImage = IImageObjectPtr(LoadImageFromFile(m_imageFileName));
}
// Get preset if the setting in texture is changed
if (m_presetSetting == nullptr || m_presetSetting->m_uuid != m_textureSetting->m_preset)
{
m_presetSetting = BuilderSettingManager::Instance()->GetPreset(m_textureSetting->m_preset);
}
const bool isPreview = true;
const bool autoDelete = false;
PlatformName defaultPlatform = BuilderSettingManager::s_defaultPlatform;
m_convertJob = AZStd::make_unique<ImageConvertJob>(m_inputImage, m_textureSetting, m_presetSetting,
isPreview, defaultPlatform, &m_output, autoDelete, m_jobContext.get());
m_convertJob->SetDependent(&m_doneJob);
m_convertJob->Start();
}
bool ImagePreview::IsDone()
{
return m_output.IsReady();
}
float ImagePreview::GetProgress()
{
if (!m_output.IsReady())
{
return m_output.GetProgress();
}
return 1.0f;
}
void ImagePreview::Cancel()
{
if (m_convertJob)
{
m_convertJob->Cancel();
// Block until job completes
m_doneJob.StartAndWaitForCompletion();
AZ_Assert(m_output.IsReady(), "Conversion job is not done yet!");
}
m_convertJob.release();
m_doneJob.Reset(true);
}
IImageObjectPtr ImagePreview::GetOutputImage()
{
return m_output.GetOutputImage(ImageConvertOutput::Preview);
}
ImagePreview::~ImagePreview()
{
Cancel();
// Maintain the releasing order
m_jobManager.release();
m_jobContext.release();
m_jobCancelGroup.release();
}
void ImagePreview::InitializeJobSettings()
{
AZ::JobManagerDesc desc;
AZ::JobManagerThreadDesc threadDesc;
desc.m_workerThreads.push_back(threadDesc);
// Check to ensure these have not already been initialized.
AZ_Error("Image Processing", !m_jobManager && !m_jobCancelGroup && !m_jobContext, "ImagePreview::InitializeJobSettings is being called again after it has already been initialized");
m_jobManager = AZStd::make_unique<AZ::JobManager>(desc);
m_jobCancelGroup = AZStd::make_unique<AZ::JobCancelGroup>();
m_jobContext = AZStd::make_unique<AZ::JobContext>(*m_jobManager, *m_jobCancelGroup);
new (&m_doneJob) AZ::JobCompletion(m_jobContext.get()); //re-initialize with the job context
}
void GetImageInfoString(IImageObjectPtr image, bool isAlpha, AZStd::string& output)
{
if (!image)
{
return;
}
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
EPixelFormat format = image->GetPixelFormat();
const PixelFormatInfo* info = pixelFormats.GetPixelFormatInfo(format);
if (info)
{
output += AZStd::string::format("Format: %s\r\n", info->szName);
}
AZ::u32 mipCount = image->GetMipCount();
output += AZStd::string::format("Mip Count: %d\r\n", mipCount);
AZ::u32 memSize = image->GetTextureMemory();
AZStd::string memSizeString = ImageProcessingEditor::EditorHelper::GetFileSizeString(memSize);
output += AZStd::string::format("Memory Size: %s\r\n", memSizeString.c_str());
if (!isAlpha)
{
if (image->HasImageFlags(EIF_SRGBRead))
{
output += "Color Space: sRGB\r\n";
}
else
{
output += "Color Space: Linear\r\n";
}
if (image->HasImageFlags(EIF_Cubemap))
{
output += "Cubemap\r\n";
}
}
AZ::u32 imageFlag = image->GetImageFlags();
output += AZStd::string::format("Image Flag: 0x%08x\r\n", imageFlag);
}
bool ImagePreview::GetProductTexturePreview(const char* fullProductFileName, QImage& previewImage, AZStd::string& productInfo, AZStd::string& productAlphaInfo)
{
if (!AzFramework::StringFunc::Path::IsExtension(fullProductFileName, "dds", false))
{
return false;
}
IImageObjectPtr originImage = IImageObjectPtr(LoadImageFromDdsFile(fullProductFileName));
IImageObjectPtr alphaImage;
if (originImage && originImage->HasImageFlags(EIF_AttachedAlpha))
{
if (originImage->HasImageFlags(EIF_Splitted))
{
AZStd::string alphaFileName = AZStd::string::format("%s.a", fullProductFileName);
alphaImage = IImageObjectPtr(LoadImageFromDdsFile(alphaFileName));
}
else
{
alphaImage = IImageObjectPtr(LoadAttachedImageFromDdsFile(fullProductFileName, originImage));
}
}
GetImageInfoString(originImage, false, productInfo);
GetImageInfoString(alphaImage, true, productAlphaInfo);
IImageObjectPtr combinedImage = MergeOutputImageForPreview(originImage, alphaImage);
if (combinedImage)
{
AZ::u8* imageBuf;
AZ::u32 pitch;
combinedImage->GetImagePointer(0, imageBuf, pitch);
const AZ::u32 width = originImage->GetWidth(0);
const AZ::u32 height = originImage->GetHeight(0);
QImage result = QImage(imageBuf, width, height, pitch, QImage::Format_RGBA8888);
previewImage = result.copy(); // Return a deep copy here
return true;
}
return false;
}
}// namespace ImageProcessing
@@ -0,0 +1,59 @@
/*
* 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/ImageConvertJob.h>
#include <AzCore/Jobs/JobCompletion.h>
namespace ImageProcessing
{
// The reason to have image preview class, we should keep the source image loaded once,
// we could restart conversion and cancel the old conversion at anytime when setting changed
class ImagePreview
{
public:
ImagePreview(const AZStd::string& inputImageFile, TextureSettings* textureSetting);
~ImagePreview();
void InitializeJobSettings();
void StartConvert();
bool IsDone();
float GetProgress();
void Cancel();
IImageObjectPtr GetOutputImage();
// Output preview image for Asset Browser
static bool GetProductTexturePreview(const char* fullProductFileName, QImage& previewImage, AZStd::string& productInfo, AZStd::string& productAlphaInfo);
private:
AZStd::string m_imageFileName;
IImageObjectPtr m_inputImage;
const TextureSettings* m_textureSetting;
const PresetSettings* m_presetSetting;
IImageObjectPtr m_outputImage;
IImageObjectPtr m_outputAlphaImage;
ImageConvertOutput m_output;
AZStd::unique_ptr<AZ::JobManager> m_jobManager;
AZStd::unique_ptr<AZ::JobCancelGroup> m_jobCancelGroup;
AZStd::unique_ptr<AZ::JobContext> m_jobContext;
AZStd::unique_ptr<ImageConvertJob> m_convertJob;
AZ::JobCompletion m_doneJob;
};
// Get basic image info as string
void GetImageInfoString(IImageObjectPtr image, bool isAlpha, AZStd::string& output);
}// namespace ImageProcessing
@@ -0,0 +1,114 @@
/*
* 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>
#include <Compressors/Compressor.h>
namespace ImageProcessing
{
//cubemap layouts
enum CubemapLayoutType
{
CubemapLayoutHorizontal = 0, //6x1 strip. with rotations.
CubemapLayoutHorizontalCross, //4x3.
CubemapLayoutVerticalCross, //3x4
CubemapLayoutVertical, //1x6 strip. new output format. it's better because the memory is continuous for each face
CubemapLayoutTypeCount,
CubemapLayoutNone = CubemapLayoutTypeCount
};
class ImageToProcess
{
private:
IImageObjectPtr m_img;
ICompressor::CompressOption m_compressOption;
private:
ImageToProcess(const ImageToProcess&);
public:
ImageToProcess(IImageObjectPtr img)
{
m_img = img;
}
~ImageToProcess()
{
}
void Set(IImageObjectPtr img)
{
m_img = img;
}
IImageObjectPtr Get() const
{
return m_img;
}
ICompressor::CompressOption& GetCompressOption()
{
return m_compressOption;
}
void SetCompressOption(const ICompressor::CompressOption& compressOption)
{
m_compressOption = compressOption;
}
public:
// ---------------------------------------------------------------------------------
//! can be used to compress, requires a preset
void ConvertFormat(EPixelFormat fmtTo);
void ConvertFormatUncompressed(EPixelFormat fmtTo);
// ---------------------------------------------------------------------------------
// Arguments:
// bDeGamma - apply de-gamma correction
bool GammaToLinearRGBA32F(bool bDeGamma);
void LinearToGamma();
// ---------------------------------------------------------------------------------
// Resizers for A32B32G32R32F
// Prerequisites: image width is even, ARGB32F only, no mips.
void DownscaleTwiceHorizontally();
// Prerequisites: height is even, ARGB32F only, no mips.
void DownscaleTwiceVertically();
// Prerequisites: width is pow of 2, ARGB32F only, no mips.
void UpscalePow2TwiceHorizontally();
// Prerequisites: height is pow of 2, ARGB32F only, no mips.
void UpscalePow2TwiceVertically();
// ---------------------------------------------------------------------------------
// Tools for A32B32G32R32F
// input needs to be in range 0..1
void AddNormalMap(const IImageObject* pAddBump);
void CreateHighPass(uint32 dwMipDown);
void CreateColorChart();
//convert various original cubemap layouts to new layout
bool ConvertCubemapLayout(CubemapLayoutType newLayout);
};
}// namespace ImageProcessing
@@ -0,0 +1,430 @@
/*
* 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/PixelFormatInfo.h>
#include <Processing/DDSHeader.h>
namespace ImageProcessing
{
CPixelFormats* CPixelFormats::s_instance = nullptr;
CPixelFormats& CPixelFormats::GetInstance()
{
if (s_instance == nullptr)
{
s_instance = new CPixelFormats();
}
return *s_instance;
}
void CPixelFormats::DestroyInstance()
{
delete s_instance;
s_instance = nullptr;
}
PixelFormatInfo::PixelFormatInfo(
int a_bitsPerPixel,
int a_Channels,
bool a_Alpha,
const char* a_szAlpha,
uint32 a_minWidth,
uint32 a_minHeight,
int a_blockWidth,
int a_blockHeight,
int a_bitsPerBlock,
bool a_bSquarePow2,
DXGI_FORMAT a_d3d10Format,
AZ::u32 a_fourCC,
ESampleType a_eSampleType,
const char* a_szName,
const char* a_szDescription,
bool a_bCompressed,
bool a_bSelectable)
: nChannels(a_Channels)
, bHasAlpha(a_Alpha)
, minWidth(a_minWidth)
, minHeight(a_minHeight)
, blockWidth(a_blockWidth)
, blockHeight(a_blockHeight)
, bitsPerBlock(a_bitsPerBlock)
, bSquarePow2(a_bSquarePow2)
, szAlpha(a_szAlpha)
, d3d10Format(a_d3d10Format)
, fourCC(a_fourCC)
, eSampleType(a_eSampleType)
, szName(a_szName)
, szLegacyName(a_szName)
, szDescription(a_szDescription)
, bCompressed(a_bCompressed)
, bSelectable(a_bSelectable)
{
//validate pixel format
//a_bitsPerPixel could be 0 if it's ACTC format since the actual bits per-pixel could be 6.4, 5.12 etc.
if (a_bitsPerPixel)
{
AZ_Assert(a_bitsPerPixel * blockWidth * blockHeight == bitsPerBlock, "PixelFormatInfo: Wrong block setting");
}
AZ_Assert(szName, "szName can't be nullptr");
AZ_Assert(nChannels > 0 && nChannels <= 4, "unreasonable channel count %d", nChannels);
AZ_Assert(a_szDescription, "szDescription can't be nullptr");
AZ_Assert(blockWidth > 0 && blockHeight > 0, "blcok size need to be larger than 0: %d x %d", blockWidth, blockHeight);
AZ_Assert(minWidth > 0 && minHeight > 0, "piexel required mininum image size need to be larger than 0: %d x %d", minWidth, minHeight);
if (!bCompressed)
{
AZ_Assert(blockWidth == 1 && blockHeight == 1, "Uncompressed format shouldn't have block which size > 1");
}
}
CPixelFormats::CPixelFormats()
{
InitPixelFormats();
m_removedLegacyFormats["DXT1"] = ePixelFormat_BC1;
m_removedLegacyFormats["DXT1a"] = ePixelFormat_BC1a;
m_removedLegacyFormats["DXT3"] = ePixelFormat_BC3;
m_removedLegacyFormats["DXT3t"] = ePixelFormat_BC3t;
m_removedLegacyFormats["DXT5"] = ePixelFormat_BC3;
m_removedLegacyFormats["DXT5t"] = ePixelFormat_BC3t;
m_removedLegacyFormats["3DCp"] = ePixelFormat_BC4;
m_removedLegacyFormats["3DC"] = ePixelFormat_BC5;
}
void CPixelFormats::InitPixelFormat(EPixelFormat format, const PixelFormatInfo& formatInfo)
{
AZ_Assert((format >= 0) && (format < ePixelFormat_Count), "Unsupport pixel format: %d", format);
if (m_pixelFormatInfo[format].szName && m_pixelFormatNameMap.find(formatInfo.szName) != m_pixelFormatNameMap.end())
{
// double initialization
AZ_Assert(false, "Pixel format already exist: %s", m_pixelFormatInfo[format].szName);
}
m_pixelFormatNameMap[formatInfo.szName] = format;
m_pixelFormatInfo[format] = formatInfo;
}
void CPixelFormats::InitPixelFormats()
{
// Unsigned Formats
// Data in an unsigned format must be positive. Unsigned formats use combinations of
// (R)ed, (G)reen, (B)lue, (A)lpha, (L)uminance
InitPixelFormat(ePixelFormat_R8G8B8A8, PixelFormatInfo(32, 4, true, "8", 1, 1, 1, 1, 32, false, DXGI_FORMAT_R8G8B8A8_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint8, "R8G8B8A8", "32-bit RGBA pixel format with alpha, using 8 bits per channel", false, true));
InitPixelFormat(ePixelFormat_R8G8B8X8, PixelFormatInfo(32, 4, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_R8G8B8A8_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint8, "R8G8B8X8", "32-bit RGB pixel format, where 8 bits are reserved for each color", false, true));
InitPixelFormat(ePixelFormat_R8G8, PixelFormatInfo(16, 2, false, "0", 1, 1, 1, 1, 16, false, DXGI_FORMAT_R8G8_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint8, "R8G8", "16-bit red/green, using 8 bits per channel", false, false));
InitPixelFormat(ePixelFormat_R8, PixelFormatInfo( 8, 1, false, "0", 1, 1, 1, 1, 8, false, DXGI_FORMAT_R8_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint8, "R8", "8-bit red only", false, false));
InitPixelFormat(ePixelFormat_A8, PixelFormatInfo( 8, 1, true, "8", 1, 1, 1, 1, 8, false, DXGI_FORMAT_A8_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint8, "A8", "8-bit alpha only", false, true));
InitPixelFormat(ePixelFormat_R16G16B16A16, PixelFormatInfo(64, 4, true, "16", 1, 1, 1, 1, 64, false, DXGI_FORMAT_R16G16B16A16_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint16, "R16G16B16A16", "64-bit ARGB pixel format with alpha, using 16 bits per channel", false, false));
InitPixelFormat(ePixelFormat_R16G16, PixelFormatInfo(32, 2, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_R16G16_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint16, "R16G16", "32-bit red/green, using 16 bits per channel", false, false));
InitPixelFormat(ePixelFormat_R16, PixelFormatInfo(16, 1, false, "0", 1, 1, 1, 1, 16, false, DXGI_FORMAT_R16_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint16, "R16", "16-bit red only", false, false));
// Custom FourCC Formats
// Data in these FourCC formats is custom compressed data and only decodable by certain hardware.
InitPixelFormat(ePixelFormat_ASTC_4x4, PixelFormatInfo(0, 4, true, "?", 16, 16, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_4x4, ESampleType::eSampleType_Compressed, "ASTC_4x4", "ASTC 4x4 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_5x4, PixelFormatInfo(0, 4, true, "?", 16, 16, 5, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_5x4, ESampleType::eSampleType_Compressed, "ASTC_5x4", "ASTC 5x4 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_5x5, PixelFormatInfo(0, 4, true, "?", 16, 16, 5, 5, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_5x5, ESampleType::eSampleType_Compressed, "ASTC_5x5", "ASTC 5x5 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_6x5, PixelFormatInfo(0, 4, true, "?", 16, 16, 6, 5, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_6x5, ESampleType::eSampleType_Compressed, "ASTC_6x5", "ASTC 6x5 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_6x6, PixelFormatInfo(0, 4, true, "?", 16, 16, 6, 6, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_6x6, ESampleType::eSampleType_Compressed, "ASTC_6x6", "ASTC 6x6 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_8x5, PixelFormatInfo(0, 4, true, "?", 16, 16, 8, 5, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_8x5, ESampleType::eSampleType_Compressed, "ASTC_8x5", "ASTC 8x5 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_8x6, PixelFormatInfo(0, 4, true, "?", 16, 16, 8, 6, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_8x6, ESampleType::eSampleType_Compressed, "ASTC_8x6", "ASTC 8x6 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_8x8, PixelFormatInfo(0, 4, true, "?", 16, 16, 8, 8, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_8x8, ESampleType::eSampleType_Compressed, "ASTC_8x8", "ASTC 8x8 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_10x5, PixelFormatInfo(0, 4, true, "?", 16, 16, 10, 5, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_10x5, ESampleType::eSampleType_Compressed, "ASTC_10x5", "ASTC 10x5 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_10x6, PixelFormatInfo(0, 4, true, "?", 16, 16, 10, 6, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_10x6, ESampleType::eSampleType_Compressed, "ASTC_10x6", "ASTC 10x6 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_10x8, PixelFormatInfo(0, 4, true, "?", 16, 16, 10, 8, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_10x8, ESampleType::eSampleType_Compressed, "ASTC_10x8", "ASTC 10x8 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_10x10, PixelFormatInfo(0, 4, true, "?", 16, 16, 10, 10, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_10x10, ESampleType::eSampleType_Compressed, "ASTC_10x10", "ASTC 10x10 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_12x10, PixelFormatInfo(0, 4, true, "?", 16, 16, 12, 10, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_12x10, ESampleType::eSampleType_Compressed, "ASTC_12x10", "ASTC 12x10 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_12x12, PixelFormatInfo(0, 4, true, "?", 16, 16, 12, 12, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_12x12, ESampleType::eSampleType_Compressed, "ASTC_12x12", "ASTC 12x12 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_PVRTC2, PixelFormatInfo(2, 4, true, "2", 16, 16, 8, 4, 64, true, DXGI_FORMAT_UNKNOWN, FOURCC_PVRTC2, ESampleType::eSampleType_Compressed, "PVRTC2", "POWERVR 2 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_PVRTC4, PixelFormatInfo(4, 4, true, "2", 8, 8, 4, 4, 64, true, DXGI_FORMAT_UNKNOWN, FOURCC_PVRTC4, ESampleType::eSampleType_Compressed, "PVRTC4", "POWERVR 4 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_EAC_R11, PixelFormatInfo(4, 1, true, "4", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_EAC_R11, ESampleType::eSampleType_Compressed, "EAC_R11", "EAC 4 bpp single channel texture format", true, false));
InitPixelFormat(ePixelFormat_EAC_RG11, PixelFormatInfo(8, 2, false, "0", 4, 4, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_EAC_RG11, ESampleType::eSampleType_Compressed, "EAC_RG11", "EAC 8 bpp dual channel texture format", true, false));
InitPixelFormat(ePixelFormat_ETC2, PixelFormatInfo(4, 3, false, "0", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2, ESampleType::eSampleType_Compressed, "ETC2", "ETC2 RGB 4 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ETC2a, PixelFormatInfo(8, 4, true, "4", 4, 4, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2A, ESampleType::eSampleType_Compressed, "ETC2a", "ETC2 RGBA 8 bpp compressed texture format", true, false));
// Standardized Compressed DXGI Formats (DX10+)
// Data in these compressed formats is hardware decodable on all DX10 chips, and manageable with the DX10-API.
InitPixelFormat(ePixelFormat_BC1, PixelFormatInfo(4, 3, false, "0", 4, 4, 4, 4, 64, false, DXGI_FORMAT_BC1_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC1", "BC1 compressed texture format", true, true));
InitPixelFormat(ePixelFormat_BC1a, PixelFormatInfo(4, 4, true, "1", 4, 4, 4, 4, 64, false, DXGI_FORMAT_BC1_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC1a", "BC1a compressed texture format with transparency", true, true));
InitPixelFormat(ePixelFormat_BC3, PixelFormatInfo(8, 4, true, "3of8", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC3_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC3", "BC3 compressed texture format", true, true));
InitPixelFormat(ePixelFormat_BC3t, PixelFormatInfo(8, 4, true, "3of8", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC3_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC3t", "BC3t compressed texture format with transparency", true, true));
InitPixelFormat(ePixelFormat_BC4, PixelFormatInfo(4, 1, false, "0", 4, 4, 4, 4, 64, false, DXGI_FORMAT_BC4_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC4", "BC4 compressed texture format for single channel maps. 3DCp", true, true));
InitPixelFormat(ePixelFormat_BC4s, PixelFormatInfo(4, 1, false, "0", 4, 4, 4, 4, 64, false, DXGI_FORMAT_BC4_SNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC4s", "BC4 compressed texture format for signed single channel maps", true, true));
InitPixelFormat(ePixelFormat_BC5, PixelFormatInfo(8, 2, false, "0", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC5_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC5", "BC5 compressed texture format for two channel maps or normalmaps. 3DC", true, true));
InitPixelFormat(ePixelFormat_BC5s, PixelFormatInfo(8, 2, false, "0", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC5_SNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC5s", "BC5 compressed texture format for signed two channel maps or normalmaps", true, true));
InitPixelFormat(ePixelFormat_BC6UH, PixelFormatInfo(8, 3, false, "0", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC6H_UF16, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC6UH", "BC6 compressed texture format, unsigned half", true, true));
InitPixelFormat(ePixelFormat_BC7, PixelFormatInfo(8, 4, true, "8", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC7_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC7", "BC7 compressed texture format", true, true));
InitPixelFormat(ePixelFormat_BC7t, PixelFormatInfo(8, 4, true, "8", 4, 4, 4, 4, 128, false, DXGI_FORMAT_BC7_UNORM, FOURCC_DX10, ESampleType::eSampleType_Compressed, "BC7t", "BC7t compressed texture format with transparency", true, true));
// Float formats
// Data in a Float format is floating point data.
InitPixelFormat(ePixelFormat_R9G9B9E5, PixelFormatInfo(32, 3, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, FOURCC_DX10, ESampleType::eSampleType_Compressed, "R9G9B9E5", "32-bit RGB pixel format with shared exponent", false, true));
InitPixelFormat(ePixelFormat_R32G32B32A32F, PixelFormatInfo(128, 4, true, "23", 1, 1, 1, 1, 128, false, DXGI_FORMAT_R32G32B32A32_FLOAT, FOURCC_DX10, ESampleType::eSampleType_Float, "R32G32B32A32F", "four float channels", false, false));
InitPixelFormat(ePixelFormat_R32G32F, PixelFormatInfo(64, 2, false, "0", 1, 1, 1, 1, 64, false, DXGI_FORMAT_R32G32_FLOAT, FOURCC_DX10, ESampleType::eSampleType_Float, "R32G32F", "two float channels", false, false)); // FIXME: This should be eTF_R32G32F, but CryTek did not add that enum to ITexture.h yet
InitPixelFormat(ePixelFormat_R32F, PixelFormatInfo(32, 1, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_R32_FLOAT, FOURCC_DX10, ESampleType::eSampleType_Float, "R32F", "one float channel", false, false));
InitPixelFormat(ePixelFormat_R16G16B16A16F, PixelFormatInfo(64, 4, true, "10", 1, 1, 1, 1, 64, false, DXGI_FORMAT_R16G16B16A16_FLOAT, FOURCC_DX10, ESampleType::eSampleType_Half, "R16G16B16A16F", "four half channels", false, false));
InitPixelFormat(ePixelFormat_R16G16F, PixelFormatInfo(32, 2, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_R16G16_FLOAT, FOURCC_DX10, ESampleType::eSampleType_Half, "R16G16F", "two half channel", false, false));
InitPixelFormat(ePixelFormat_R16F, PixelFormatInfo(16, 1, false, "0", 1, 1, 1, 1, 16, false, DXGI_FORMAT_R16_FLOAT, FOURCC_DX10, ESampleType::eSampleType_Half, "R16F", "one half channel", false, false));
//legacy BGRA8
InitPixelFormat(ePixelFormat_B8G8R8A8, PixelFormatInfo(32, 4, true, "8", 1, 1, 1, 1, 32, false, DXGI_FORMAT_B8G8R8A8_UNORM, FOURCC_DX10, ESampleType::eSampleType_Uint8, "B8G8R8A8", "32-bit BGRA pixel format with alpha, using 8 bits per channel", false, true));
InitPixelFormat(ePixelFormat_R32, PixelFormatInfo(32, 1, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_FORCE_UINT, FOURCC_DX10, ESampleType::eSampleType_Uint32, "R32", "32-bit red only", false, false));
//Set legacy name it can be used for convertion
m_pixelFormatInfo[ePixelFormat_R8G8B8A8].szLegacyName = "A8R8G8B8";
m_pixelFormatInfo[ePixelFormat_R8G8B8X8].szLegacyName = "X8R8G8B8";
m_pixelFormatInfo[ePixelFormat_R8G8].szLegacyName = "G8R8";
m_pixelFormatInfo[ePixelFormat_R16G16B16A16].szLegacyName = "A16B16G16R16";
m_pixelFormatInfo[ePixelFormat_R16G16].szLegacyName = "G16R16";
m_pixelFormatInfo[ePixelFormat_R32G32B32A32F].szLegacyName = "A32B32G32R32F";
m_pixelFormatInfo[ePixelFormat_R32G32F].szLegacyName = "G32R32F";
m_pixelFormatInfo[ePixelFormat_R16G16B16A16F].szLegacyName = "A16B16G16R16F";
m_pixelFormatInfo[ePixelFormat_R16G16F].szLegacyName = "G16R16F";
//validate all pixel formats are proper initialized
for (int i = 0; i < ePixelFormat_Count; ++i)
{
if (m_pixelFormatInfo[i].szName == 0)
{
// Uninitialized entry. Should never happen. But, if it happened: make sure that entries from
// the EPixelFormat enum and InitPixelFormat() calls match.
AZ_Assert(false, "InitPixelFormats error: not all pixel formats have an implementation.");
}
}
}
EPixelFormat CPixelFormats::FindPixelFormatByName(const char* name)
{
if (m_pixelFormatNameMap.find(name) != m_pixelFormatNameMap.end())
{
return m_pixelFormatNameMap[name];
}
return ePixelFormat_Unknown;
}
EPixelFormat CPixelFormats::FindPixelFormatByLegacyName(const char* name)
{
if (m_removedLegacyFormats.find(name) != m_removedLegacyFormats.end())
{
return m_removedLegacyFormats[name];
}
for (int i = 0; i < ePixelFormat_Count; ++i)
{
if (azstricmp(m_pixelFormatInfo[i].szLegacyName, name) == 0)
{
return (EPixelFormat)i;
}
}
return ePixelFormat_Unknown;
}
const PixelFormatInfo* CPixelFormats::GetPixelFormatInfo(EPixelFormat format)
{
AZ_Assert((format >= 0) && (format < ePixelFormat_Count), "Unsupport pixel format: %d", format);
return &m_pixelFormatInfo[format];
}
bool CPixelFormats::IsPixelFormatUncompressed(EPixelFormat format)
{
AZ_Assert((format >= 0) && (format < ePixelFormat_Count), "Unsupport pixel format: %d", format);
return !m_pixelFormatInfo[format].bCompressed;
}
bool CPixelFormats::IsPixelFormatWithoutAlpha(EPixelFormat format)
{
AZ_Assert((format >= 0) && (format < ePixelFormat_Count), "Unsupport pixel format: %d", format);
return !m_pixelFormatInfo[format].bHasAlpha;
}
uint32 CPixelFormats::ComputeMaxMipCount(EPixelFormat format, uint32 width, uint32 height)
{
const PixelFormatInfo* const pFormatInfo = GetPixelFormatInfo(format);
AZ_Assert(pFormatInfo != nullptr, "ComputeMaxMipCount: unsupport pixel format %d", format);
uint32 tmpWidth = width;
uint32 tmpHeight = height;
bool bIgnoreBlockSize = CanImageSizeIgnoreBlockSize(format);
uint32 mipCountW = 0;
while ((tmpWidth >= pFormatInfo->minWidth) && (bIgnoreBlockSize || (tmpWidth % pFormatInfo->blockWidth == 0)))
{
++mipCountW;
tmpWidth >>= 1;
}
uint32 mipCountH = 0;
while ((tmpHeight >= pFormatInfo->minHeight) && (bIgnoreBlockSize || (tmpHeight % pFormatInfo->blockHeight == 0)))
{
++mipCountH;
tmpHeight >>= 1;
}
//for compressed image, use minmum mip out of W and H because any size below won't be compressed properly
//for non-compressed image. use maximum mip count. for example the lowest two mips of 128x64 would be 2x1 and 1x1
const uint32 mipCount = (pFormatInfo->bCompressed)
? AZStd::min<uint32>(mipCountW, mipCountH)
: AZStd::max<uint32>(mipCountW, mipCountH);
// In some cases, user may call this function for image size which is qualified for this pixel format,
// the mipCount could be 0 for those cases. Round it to 1 if it happend.
return AZStd::max<uint32>((uint32)1, mipCount);
}
bool CPixelFormats::CanImageSizeIgnoreBlockSize(EPixelFormat format)
{
// ASTC is a kind of block compression but it doesn't need the image size to be interger mutiples of block size.
// reference: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt
//"For images which are not an integer multiple of the block size, additional texels are added to the edges
// with maximum X and Y.These texels may be any color, as they will not be accessed."
bool bIgnoreBlockSize = IsASTCFormat(format);
return bIgnoreBlockSize;
}
bool CPixelFormats::IsImageSizeValid(EPixelFormat format, uint32 imageWidth, uint32 imageHeight, [[maybe_unused]] bool logWarning)
{
const PixelFormatInfo* const pFormatInfo = GetPixelFormatInfo(format);
AZ_Assert(pFormatInfo != nullptr, "IsImageSizeValid: unsupport pixel format %d", format);
//if the format requires image to be sqaure and power of 2
if (pFormatInfo->bSquarePow2 && ((imageWidth != imageHeight) || (imageWidth & (imageWidth - 1)) != 0))
{
AZ_Warning("ImageBuilder", !logWarning, "Image size need to be square and power of 2 for pixel format %s",
pFormatInfo->szName);
return false;
}
// minimum size required by the pixel format
if (imageWidth < pFormatInfo->minWidth || imageHeight < pFormatInfo->minHeight)
{
AZ_Warning("ImageBuilder", !logWarning, "The image size (%dx%d) is smaller than minimum size (%dx%d) for pixel format %s",
imageWidth, imageHeight, pFormatInfo->minWidth, pFormatInfo->minHeight, pFormatInfo->szName);
return false;
}
//check image size againest block size
if (!CanImageSizeIgnoreBlockSize(format))
{
if (imageWidth % pFormatInfo->blockWidth != 0 || imageHeight % pFormatInfo->blockHeight != 0)
{
AZ_Warning("ImageBuilder", !logWarning, "Image size (%dx%d) need to be integer multiplier of compression block size (%dx%d) for pixel format %s",
imageWidth, imageHeight, pFormatInfo->minWidth, pFormatInfo->minHeight, pFormatInfo->szName);
return false;
}
}
return true;
}
AZ::u32 NextPowOf2(AZ::u32 value)
{
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value++;
return value;
}
void CPixelFormats::GetSuitableImageSize(EPixelFormat format, AZ::u32 imageWidth, AZ::u32 imageHeight,
AZ::u32& outWidth, AZ::u32& outHeight)
{
const PixelFormatInfo* const pFormatInfo = GetPixelFormatInfo(format);
AZ_Assert(pFormatInfo != nullptr, "IsImageSizeValid: unsupport pixel format %d", format);
outWidth = imageWidth;
outHeight = imageHeight;
// minimum size required by the pixel format
if (outWidth < pFormatInfo->minWidth)
{
outWidth = pFormatInfo->minWidth;
}
if (outHeight < pFormatInfo->minHeight)
{
outHeight = pFormatInfo->minHeight;
}
if (pFormatInfo->bSquarePow2 && ((outWidth != outHeight) || (outWidth & (outWidth - 1)) != 0))
{
AZ::u32 sideSide = AZ::GetMax(outWidth, outHeight);
outWidth = NextPowOf2(sideSide);
outHeight = outWidth;
}
//check image size againest block size
//if the format requires square and power of 2. we can skip this step
if (!CanImageSizeIgnoreBlockSize(format) && !pFormatInfo->bSquarePow2)
{
if (outWidth % pFormatInfo->blockWidth != 0)
{
outWidth = ((outWidth + pFormatInfo->blockWidth -1) / pFormatInfo->blockWidth) * pFormatInfo->blockWidth;
}
if (outHeight % pFormatInfo->blockHeight != 0)
{
outHeight = ((outHeight + pFormatInfo->blockHeight - 1) / pFormatInfo->blockHeight) * pFormatInfo->blockHeight;
}
}
}
uint32 CPixelFormats::EvaluateImageDataSize(EPixelFormat format, uint32 imageWidth, uint32 imageHeight)
{
const PixelFormatInfo* const pFormatInfo = GetPixelFormatInfo(format);
AZ_Assert(pFormatInfo != nullptr, "IsImageSizeValid: unsupport pixel format %d", format);
//the image should pass IsImageSizeValid test to be eavluated correctly
if (!IsImageSizeValid(format, imageWidth, imageHeight, false))
{
return 0;
}
// get number of blocks (ceiling round up for block count) and multiply with bits per block. Divided by 8 to get
// final byte size
return (((imageWidth + pFormatInfo->blockWidth -1) / pFormatInfo->blockWidth) *
((imageHeight + pFormatInfo->blockHeight - 1) / pFormatInfo->blockHeight) * pFormatInfo->bitsPerBlock) / 8;
}
bool CPixelFormats::IsFormatSingleChannel(EPixelFormat fmt)
{
return (m_pixelFormatInfo[fmt].nChannels == 1);
}
bool CPixelFormats::IsFormatSigned(EPixelFormat fmt)
{
// all these formats contain signed data, the FP-formats contain scale & biased unsigned data
return (fmt == ePixelFormat_BC4s || fmt == ePixelFormat_BC5s /*|| fmt == ePixelFormat_BC6SH*/);
}
bool CPixelFormats::IsFormatFloatingPoint(EPixelFormat fmt, bool bFullPrecision)
{
// all these formats contain floating point data
if (!bFullPrecision)
{
return ((fmt == ePixelFormat_R16F || fmt == ePixelFormat_R16G16F ||
fmt == ePixelFormat_R16G16B16A16F) || (fmt == ePixelFormat_BC6UH || fmt == ePixelFormat_R9G9B9E5));
}
else
{
return ((fmt == ePixelFormat_R32F || fmt == ePixelFormat_R32G32F || fmt == ePixelFormat_R32G32B32A32F));
}
}
} // namespace ImageProcessing
@@ -0,0 +1,222 @@
/*
* 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 <AzDXGIFormat.h> // DX10+ formats. DXGI_FORMAT
#include <ImageProcessing/PixelFormats.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/string/string.h>
namespace ImageProcessing
{
//The original implementation was from cryhalf's CryConvertFloatToHalf and CryConvertHalfToFloat function
struct SHalf
{
explicit SHalf(float floatValue)
{
AZ::u32 Result;
AZ::u32 intValue = ((AZ::u32*)(&floatValue))[0];
AZ::u32 Sign = (intValue & 0x80000000U) >> 16U;
intValue = intValue & 0x7FFFFFFFU;
if (intValue > 0x47FFEFFFU)
{
// The number is too large to be represented as a half. Saturate to infinity.
Result = 0x7FFFU;
}
else
{
if (intValue < 0x38800000U)
{
// The number is too small to be represented as a normalized half.
// Convert it to a denormalized value.
AZ::u32 Shift = 113U - (intValue >> 23U);
intValue = (0x800000U | (intValue & 0x7FFFFFU)) >> Shift;
}
else
{
// Rebias the exponent to represent the value as a normalized half.
intValue += 0xC8000000U;
}
Result = ((intValue + 0x0FFFU + ((intValue >> 13U) & 1U)) >> 13U) & 0x7FFFU;
}
h = (Result | Sign);
}
operator float() const
{
AZ::u32 Mantissa;
AZ::u32 Exponent;
AZ::u32 Result;
Mantissa = h & 0x03FF;
if ((h & 0x7C00) != 0) // The value is normalized
{
Exponent = ((h >> 10) & 0x1F);
}
else if (Mantissa != 0) // The value is denormalized
{
// Normalize the value in the resulting float
Exponent = 1;
do
{
Exponent--;
Mantissa <<= 1;
} while ((Mantissa & 0x0400) == 0);
Mantissa &= 0x03FF;
}
else // The value is zero
{
Exponent = -112;
}
Result = ((h & 0x8000) << 16) | // Sign
((Exponent + 112) << 23) | // Exponent
(Mantissa << 13); // Mantissa
return *(float*)&Result;
}
private:
AZ::u16 h;
};
enum class ESampleType
{
eSampleType_Uint8,
eSampleType_Uint16,
eSampleType_Uint32,
eSampleType_Half,
eSampleType_Float,
eSampleType_Compressed,
};
struct PixelFormatInfo
{
int nChannels; // channel count per pixel
bool bHasAlpha; // has alpha channel or not
const char* szAlpha; // a string of bits of alpha channel used to show brief of the pixel format
uint32 minWidth; // minimum width required for image using this pixel format
uint32 minHeight; // minimum height required for image using this pixel format
int blockWidth; // width of the block for block based compressing
int blockHeight; // Height of the block for block based compressing
int bitsPerBlock; // bits per pixel before uncompressed
bool bSquarePow2; // whether the pixel format requires image size be square and power of 2.
DXGI_FORMAT d3d10Format; // the mapping d3d10 pixel format
ESampleType eSampleType; // the data type used to present pixel
const char* szLegacyName; // name used for cryEngine
const char* szName; // name for showing in editors
const char* szDescription; // description for showing in editors
bool bCompressed; // if it's a compressed format
bool bSelectable; // shows up in the list of usable destination pixel formats in the dialog window
AZ::u32 fourCC; // fourCC to identify a none d3d10 format
PixelFormatInfo()
: szAlpha(0)
, bitsPerBlock(-1)
, d3d10Format(DXGI_FORMAT_UNKNOWN)
, szName(0)
, szDescription(0)
, fourCC(0)
{
}
PixelFormatInfo(
int a_bitsPerPixel,
int a_Channels,
bool a_Alpha,
const char* a_szAlpha,
uint32 a_minWidth,
uint32 a_minHeight,
int a_blockWidth,
int a_blockHeight,
int a_bitsPerBlock,
bool a_bSquarePow2,
DXGI_FORMAT a_d3d10Format,
AZ::u32 a_fourCC,
ESampleType a_eSampleType,
const char* a_szName,
const char* a_szDescription,
bool a_bCompressed,
bool a_bSelectable);
};
class CPixelFormats
{
public:
//singleton
static CPixelFormats& GetInstance();
static void DestroyInstance();
const PixelFormatInfo* GetPixelFormatInfo(EPixelFormat format);
bool IsPixelFormatWithoutAlpha(EPixelFormat format);
bool IsPixelFormatUncompressed(EPixelFormat format);
//functions seems only used for BC compressions. need re-evaluate later
bool IsFormatSingleChannel(EPixelFormat fmt);
bool IsFormatSigned(EPixelFormat fmt);
bool IsFormatFloatingPoint(EPixelFormat fmt, bool bFullPrecision);
//find the pixel format for name used by Cry's RC.ini
//returns ePixelFormat_Unknown if the name was not found in registed format list
EPixelFormat FindPixelFormatByLegacyName(const char* name);
//find pixel format by its name
EPixelFormat FindPixelFormatByName(const char* name);
//returns maximum lod levels for image which has certain pixel format, width and height.
uint32 ComputeMaxMipCount(EPixelFormat format, uint32 imageWidth, uint32 imageHeight);
//check if the input image size work with the pixel format. Some compression formats have requirements with the input image size.
bool IsImageSizeValid(EPixelFormat format, uint32 imageWidth, uint32 imageHeight, bool logWarning);
//get suitable new size for an image with certain width, height and pixel format
void GetSuitableImageSize(EPixelFormat format, AZ::u32 imageWidth, AZ::u32 imageHeight,
AZ::u32& outWidth, AZ::u32& outHeight);
//check if the image size of the specified pixel format need to be integer mutiple of block size
bool CanImageSizeIgnoreBlockSize(EPixelFormat format);
//eavluate image data size. it doesn't include mips
uint32 EvaluateImageDataSize(EPixelFormat format, uint32 imageWidth, uint32 imageHeight);
private:
CPixelFormats();
void InitPixelFormats();
void InitPixelFormat(EPixelFormat format, const PixelFormatInfo& formatInfo);
private:
static CPixelFormats *s_instance;
PixelFormatInfo m_pixelFormatInfo[ePixelFormat_Count];
//pixel format name to pixel format enum
AZStd::map<AZStd::string, EPixelFormat> m_pixelFormatNameMap;
// some formats from cryEngine were removed. using this name-pixelFormat mapping to look for new format
AZStd::map<AZStd::string, EPixelFormat> m_removedLegacyFormats;
};
template <class TInteger>
bool IsPowerOfTwo(TInteger x);
} // namespace ImageProcessing