| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <Processing/ImageObjectImpl.h>
- #include <Processing/PixelFormatInfo.h>
- #include <Processing/ImageFlags.h>
- #include <Processing/ImageConvert.h>
- #include <Converters/PixelOperation.h>
- #include <AzCore/std/string/conversions.h>
- #include <AzCore/std/algorithm.h>
- #include <AzCore/IO/SystemFile.h>
- #include <AzCore/IO/GenericStreams.h>
- #include <AzFramework/StringFunc/StringFunc.h>
- // Indicates a 2D texture is a cube-map texture.
- #define DDS_RESOURCE_MISC_TEXTURECUBE 0x4
- namespace ImageProcessingAtom
- {
- using namespace AZ;
- IImageObject* IImageObject::CreateImage(AZ::u32 width, AZ::u32 height,
- AZ::u32 maxMipCount, EPixelFormat pixelFormat)
- {
- return aznew CImageObject(width, height, maxMipCount, pixelFormat);
- }
- IImageObject* IImageObject::CreateImage(AZ::u32 width, AZ::u32 height, AZ::u32 depth, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
- {
- return aznew CImageObject(width, height, depth, maxMipCount, pixelFormat);
- }
- CImageObject::CImageObject(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
- : CImageObject(width, height, 1/*depth*/, maxMipCount, pixelFormat)
- {
- }
- CImageObject::CImageObject(AZ::u32 width, AZ::u32 height, AZ::u32 depth, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
- : m_pixelFormat(pixelFormat)
- , m_colMinARGB(0.0f, 0.0f, 0.0f, 0.0f)
- , m_colMaxARGB(1.0f, 1.0f, 1.0f, 1.0f)
- , m_averageColor(0.0f, 0.0f, 0.0f, 0.0f)
- , m_averageBrightness(0.63f)
- , m_imageFlags(0)
- , m_numPersistentMips(0)
- {
- ResetImage(width, height, depth, maxMipCount, pixelFormat);
- }
- EPixelFormat CImageObject::GetPixelFormat() const
- {
- return m_pixelFormat;
- }
- AZ::u32 CImageObject::GetPixelCount(AZ::u32 mip) const
- {
- AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
- return m_mips[mip]->m_width * m_mips[mip]->m_height * m_mips[mip]->m_depth;
- }
- AZ::u32 CImageObject::GetWidth(AZ::u32 mip) const
- {
- AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
- return m_mips[mip]->m_width;
- }
- AZ::u32 CImageObject::GetHeight(AZ::u32 mip) const
- {
- AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
- return m_mips[mip]->m_height;
- }
- AZ::u32 CImageObject::GetDepth(AZ::u32 mip) const
- {
- AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
- return m_mips[mip]->m_depth;
- }
- AZ::u32 CImageObject::GetMipCount() const
- {
- return (AZ::u32)m_mips.size();
- }
- void CImageObject::ResetImage(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
- {
- ResetImage(width, height, 1 /* depth */, maxMipCount, pixelFormat);
- }
- void CImageObject::ResetImage(AZ::u32 width, AZ::u32 height, AZ::u32 depth, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
- {
- //check input
- AZ_Assert( (width > 0) && (height > 0) && (depth > 0), "image width, height and depth need to be larger than 0. width: %u, height: %u, depth: %u", width, height, depth);
- //clean up mipmaps
- for (AZ::u32 mip = 0; mip < AZ::u32(m_mips.size()); ++mip)
- {
- delete m_mips[mip];
- }
- m_pixelFormat = pixelFormat;
- m_colMinARGB = AZ::Color(0.0f, 0.0f, 0.0f, 0.0f);
- m_colMaxARGB = AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
- m_averageColor = AZ::Color(0.0f, 0.0f, 0.0f, 0.0f);
- m_averageBrightness = 0.0f;
- m_imageFlags = (depth > 1) ? EIF_Volumetexture : 0;
- m_numPersistentMips = 0;
- m_mips.clear();
- const PixelFormatInfo* const pFmt = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat);
- AZ_Assert(pFmt, "can't find pixe format info for %d", m_pixelFormat);
- const AZ::u32 mipCount = AZStd::min<AZ::u32>(maxMipCount,
- CPixelFormats::GetInstance().ComputeMaxMipCount(m_pixelFormat, width, height, depth));
- m_mips.reserve(mipCount);
- for (AZ::u32 mip = 0; mip < mipCount; ++mip)
- {
- MipLevel* const pEntry = aznew MipLevel;
- AZ::u32 localWidth = AZStd::max(width >> mip, 1u);
- AZ::u32 localHeight = AZStd::max(height >> mip, 1u);
- AZ::u32 localDepth = AZStd::max(depth >> mip, 1u);
- pEntry->m_width = localWidth;
- pEntry->m_height = localHeight;
- pEntry->m_depth = localDepth;
- if (pFmt->bCompressed)
- {
- const AZ::u32 blocksInRow = (pEntry->m_width + (pFmt->blockWidth - 1)) / pFmt->blockWidth;
- pEntry->m_pitch = (blocksInRow * pFmt->bitsPerBlock) / 8;
- pEntry->m_rowCount = (localHeight + (pFmt->blockHeight - 1)) / pFmt->blockHeight;
- }
- else
- {
- pEntry->m_pitch = (pEntry->m_width * pFmt->bitsPerBlock) / 8;
- pEntry->m_rowCount = localHeight;
- }
- pEntry->Alloc();
- m_mips.push_back(pEntry);
- }
- }
- bool CImageObject::CompareImage(const IImageObjectPtr otherImage) const
- {
- CImageObject* other = static_cast<CImageObject*>(otherImage.get());
- if (other == nullptr)
- {
- return false;
- }
- if (m_pixelFormat == other->m_pixelFormat
- && m_colMinARGB == other->m_colMinARGB
- && m_colMaxARGB == other->m_colMaxARGB
- && m_averageColor == other->m_averageColor
- && m_averageBrightness == other->m_averageBrightness
- && m_imageFlags == other->m_imageFlags
- && m_numPersistentMips == other->m_numPersistentMips
- && m_mips.size() == other->m_mips.size())
- {
- for (int mip = 0; mip < m_mips.size(); mip++)
- {
- if (!(*m_mips[mip] == *other->m_mips[mip]))
- {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- uint32_t CImageObject::GetTextureMemory() const
- {
- int totalSize = 0;
- for (int mip = 0; mip < m_mips.size(); mip++)
- {
- totalSize += CPixelFormats::GetInstance().EvaluateImageDataSize(m_pixelFormat,
- m_mips[mip]->m_width, m_mips[mip]->m_height) * m_mips[mip]->m_depth;
- }
- return totalSize;
- }
- EAlphaContent CImageObject::GetAlphaContent() const
- {
- if (CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(m_pixelFormat))
- {
- return EAlphaContent::eAlphaContent_Absent;
- }
- if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
- {
- AZ_TracePrintf("Image processing", "GetAlphaContent() was called for compressed format\n");
- return EAlphaContent::eAlphaContent_Indeterminate;
- }
- //go though alpha channel of first mip
- //create pixel operation function to access pixel data
- IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
- //get count of bytes per pixel for images
- AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
- // counts of blacks and white
- uint32_t nBlacks = 0;
- uint32_t nWhites = 0;
- float r, g, b, a;
- AZ::u8* pixelBuf;
- AZ::u32 pitch;
- GetImagePointer(0, pixelBuf, pitch);
- const AZ::u32 pixelCount = GetPixelCount(0);
- for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
- {
- pixelOp->GetRGBA(pixelBuf, r, g, b, a);
- if (a == 0.0f)
- {
- ++nBlacks;
- }
- else if (a == 1.0f)
- {
- ++nWhites;
- }
- else
- {
- return EAlphaContent::eAlphaContent_Greyscale;
- }
- }
- if (nBlacks == 0)
- {
- return EAlphaContent::eAlphaContent_OnlyWhite;
- }
- if (nWhites == 0)
- {
- return EAlphaContent::eAlphaContent_OnlyBlack;
- }
- return EAlphaContent::eAlphaContent_OnlyBlackAndWhite;
- }
- // clone this image-object's contents
- IImageObject* CImageObject::Clone(uint32_t maxMipCount) const
- {
- IImageObject* outImage = AllocateImage(maxMipCount);
- AZ::u32 mips = outImage->GetMipCount();
- for (AZ::u32 mip = 0; mip < mips; ++mip)
- {
- AZ::u8* dstMem;
- AZ::u32 pitch;
- outImage->GetImagePointer(mip, dstMem, pitch);
- memcpy(dstMem, m_mips[mip]->m_pData, GetMipBufSize(mip));
- }
- return outImage;
- }
- void CImageObject::ClearColor(float r, float g, float b, float a)
- {
- //if it's compressed format, return directly
- if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
- {
- AZ_Assert(false, "The %s function only works with uncompressed formats", __FUNCTION__);
- return;
- }
- //create pixel operation function to access pixel data
- IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
- //get count of bytes per pixel for images
- AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
- AZ::u8* pixelBuf;
- AZ::u32 pitch;
- AZ::u32 mips = GetMipCount();
- for (AZ::u32 mip = 0; mip < mips; ++mip)
- {
- GetImagePointer(mip, pixelBuf, pitch);
- const AZ::u32 pixelCount = GetPixelCount(mip);
- for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
- {
- pixelOp->SetRGBA(pixelBuf, r, g, b, a);
- }
- }
- }
- // allocate an empty image with the same properties as the given image and the requested format
- IImageObject* CImageObject::AllocateImage(EPixelFormat pixelFormat, uint32_t maxMipCount) const
- {
- AZ::u32 width = GetWidth(0);
- AZ::u32 height = GetHeight(0);
- AZ::u32 depth = GetDepth(0);
- if (!CPixelFormats::GetInstance().IsImageSizeValid(pixelFormat, width, height, false))
- {
- AZ_Assert(false, "Cann't allocate image with format: %d", pixelFormat);
- return nullptr;
- }
- maxMipCount = AZ::GetMin(maxMipCount, GetMipCount());
- CImageObject* pRet = aznew CImageObject(width, height, depth, maxMipCount, pixelFormat);
- pRet->CopyPropertiesFrom(this);
- return pRet;
- }
- IImageObject* CImageObject::AllocateImage(uint32_t maxMipCount) const
- {
- return AllocateImage(m_pixelFormat, maxMipCount);
- }
- CImageObject::~CImageObject()
- {
- for (size_t i = 0; i < m_mips.size(); ++i)
- {
- delete m_mips[i];
- }
- m_mips.clear();
- }
- float CImageObject::CalculateAverageBrightness() const
- {
- //if it's compressed format, return a default value
- if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
- {
- return 0.5f;
- }
- // Accumulate pixel colors of the top mip
- double avgOverall[3] = { 0.0, 0.0, 0.0 };
- //create pixel operation function to access pixel data
- IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
- //get count of bytes per pixel for images
- AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
- //only calculate mip 0
- AZ::u32 mip = 0;
- float color[4];
- AZ::u8* pixelBuf;
- AZ::u32 pitch;
- GetImagePointer(mip, pixelBuf, pitch);
- const AZ::u32 pixelCount = GetPixelCount(mip);
- for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
- {
- pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
- avgOverall[0] += color[0];
- avgOverall[1] += color[1];
- avgOverall[2] += color[2];
- }
- const double avg = (avgOverall[0] + avgOverall[1] + avgOverall[2]) / (3 * pixelCount);
- return (float)avg;
- }
- bool CImageObject::BuildSurfaceHeader(DDS_HEADER_LEGACY& header) const
- {
- AZ::u32 dwWidth, dwMips, dwHeight, dwDepth;
- GetExtent(dwWidth, dwHeight, dwDepth, dwMips);
- if (dwMips <= 0)
- {
- AZ_Error("Image Processing", false, "%s: dwMips is %u", __FUNCTION__, (unsigned)dwMips);
- return false;
- }
- const EPixelFormat format = GetPixelFormat();
- if ((format < 0) || (format >= ePixelFormat_Count))
- {
- AZ_Error("Image Processing", false, "%s: Bad format %d", __FUNCTION__, (int)format);
- return false;
- }
- const PixelFormatInfo* const pPixelFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(format);
- memset(&header, 0, sizeof(DDS_HEADER_LEGACY));
- header.dwSize = sizeof(DDS_HEADER_LEGACY);
- header.dwHeaderFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
- header.dwWidth = dwWidth;
- header.dwHeight = dwHeight;
- if (dwDepth > 1)
- {
- header.dwDepth = dwDepth;
- header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_CUBEMAP; // DDSCAPS_COMPLEX
- header.dwCubemapFlags |= DDS_FLAGS_VOLUME; // DDSCAPS2_VOLUME
- }
- if (HasImageFlags(EIF_Cubemap))
- {
- AZ_Assert(dwDepth <= 1, "Volumetric Cubemap Textures don't exist!");
- header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_CUBEMAP;
- header.dwCubemapFlags |= DDS_CUBEMAP_ALLFACES;
- //save face size instead of image size.
- header.dwHeight /= 6;
- }
- header.ddspf.dwSize = sizeof(DDS_PIXELFORMAT);
- header.ddspf.dwFlags = DDS_FOURCC;
- header.ddspf.dwFourCC = pPixelFormatInfo->fourCC;
- header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_TEXTURE;
- if (dwMips > 1)
- {
- header.dwHeaderFlags |= DDS_HEADER_FLAGS_MIPMAP;
- header.dwMipMapCount = dwMips;
- header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_MIPMAP;
- }
- // non standardized way to expose some features in the header (same information is in attached chunk but then
- // streaming would need to find this spot in the file)
- // if this is causing problems we need to change it
- header.dwTextureStage = FOURCC_FYRC;
- header.dwReserved1 = GetImageFlags();
- header.bNumPersistentMips = (AZ::u8)GetNumPersistentMips();
- //tile mode for some platform native texture
- if (HasImageFlags(EIF_RestrictedPlatformDNative))
- {
- header.tileMode = eTM_LinearPadded;
- }
- else if (HasImageFlags(EIF_RestrictedPlatformONative))
- {
- header.tileMode = eTM_Optimal;
- }
- // setting up min and max colors
- for (int i = 0; i < 4; i++)
- {
- header.cMinColor[i] = m_colMinARGB.GetElement(i);
- header.cMaxColor[i] = m_colMaxARGB.GetElement(i);
- }
- // set avg brightness
- header.fAvgBrightness = GetAverageBrightness();
- return true;
- }
- bool CImageObject::BuildSurfaceExtendedHeader(DDS_HEADER_DXT10& exthead) const
- {
- const EPixelFormat format = GetPixelFormat();
- const PixelFormatInfo* const pPixelFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(format);
- DXGI_FORMAT dxgiformat = pPixelFormatInfo->d3d10Format;
- // check if we hit a format which can't be stored into a DX10 DDS-file (fe. L8)
- if (dxgiformat == DXGI_FORMAT_UNKNOWN)
- {
- AZ_Error("Image Processing", false, "%s: Format can not be stored in a DDS-file %d", __FUNCTION__, dxgiformat);
- return false;
- }
- //the dxgi format are different for linear space or gamma space
- if (HasImageFlags(EIF_SRGBRead))
- {
- switch (dxgiformat)
- {
- case DXGI_FORMAT_R8G8B8A8_UNORM:
- dxgiformat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
- break;
- case DXGI_FORMAT_BC1_UNORM:
- dxgiformat = DXGI_FORMAT_BC1_UNORM_SRGB;
- break;
- case DXGI_FORMAT_BC2_UNORM:
- dxgiformat = DXGI_FORMAT_BC2_UNORM_SRGB;
- break;
- case DXGI_FORMAT_BC3_UNORM:
- dxgiformat = DXGI_FORMAT_BC3_UNORM_SRGB;
- break;
- case DXGI_FORMAT_BC7_UNORM:
- dxgiformat = DXGI_FORMAT_BC7_UNORM_SRGB;
- break;
- default:
- break;
- }
- }
- else
- {
- switch (dxgiformat)
- {
- case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
- dxgiformat = DXGI_FORMAT_R8G8B8A8_UNORM;
- break;
- case DXGI_FORMAT_BC1_UNORM_SRGB:
- dxgiformat = DXGI_FORMAT_BC1_UNORM;
- break;
- case DXGI_FORMAT_BC2_UNORM_SRGB:
- dxgiformat = DXGI_FORMAT_BC2_UNORM;
- break;
- case DXGI_FORMAT_BC3_UNORM_SRGB:
- dxgiformat = DXGI_FORMAT_BC3_UNORM;
- break;
- case DXGI_FORMAT_BC7_UNORM_SRGB:
- dxgiformat = DXGI_FORMAT_BC7_UNORM;
- break;
- default:
- break;
- }
- }
- memset(&exthead, 0, sizeof(exthead));
- exthead.dxgiFormat = dxgiformat;
- exthead.resourceDimension = 3; //texture2d. not used
- if (HasImageFlags(EIF_Cubemap))
- {
- exthead.miscFlag = DDS_RESOURCE_MISC_TEXTURECUBE;
- exthead.arraySize = 6;
- }
- else
- {
- exthead.miscFlag = 0;
- exthead.arraySize = 1;
- }
- return true;
- }
- void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const
- {
- mipCount = (AZ::u32)m_mips.size();
- width = m_mips[0]->m_width;
- height = m_mips[0]->m_height;
- }
- void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& depth, AZ::u32& mipCount) const
- {
- mipCount = (AZ::u32)m_mips.size();
- width = m_mips[0]->m_width;
- height = m_mips[0]->m_height;
- depth = m_mips[0]->m_depth;
- }
- AZ::u32 CImageObject::GetMipDataSize(const AZ::u32 mip) const
- {
- AZ_Assert(mip < m_mips.size(), "mip %d doesn't exist", mip);
- return m_mips[mip]->GetSize();
- }
- void CImageObject::GetImagePointer(const AZ::u32 mip, AZ::u8*& pMem, AZ::u32& pitch) const
- {
- AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "requested mip doesn't exist");
- pMem = m_mips[mip]->m_pData;
- pitch = m_mips[mip]->m_pitch;
- }
- AZ::u32 CImageObject::GetMipBufSize(AZ::u32 mip) const
- {
- AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "requested mip doesn't exist");
- return m_mips[mip]->GetSize();
- }
- void CImageObject::SetMipData(AZ::u32 mip, AZ::u8* mipBuf, AZ::u32 bufSize, AZ::u32 pitch)
- {
- if (mip >= m_mips.size())
- {
- return;
- }
- uint32_t depth = m_mips[mip]->m_depth;
- if (depth < 1)
- {
- depth = 1;
- m_mips[mip]->m_depth = depth;
- }
- m_mips[mip]->m_pData = mipBuf;
- m_mips[mip]->m_pitch = pitch;
- m_mips[mip]->m_rowCount = bufSize / pitch / depth;
- AZ_Assert(bufSize == m_mips[mip]->m_rowCount * pitch * depth, "Bad pitch size");
- }
- // ARGB
- void CImageObject::GetColorRange(AZ::Color& minColor, AZ::Color& maxColor) const
- {
- minColor = m_colMinARGB;
- maxColor = m_colMaxARGB;
- }
- // ARGB
- void CImageObject::SetColorRange(const AZ::Color& minColor, const AZ::Color& maxColor)
- {
- m_colMinARGB = minColor;
- m_colMaxARGB = maxColor;
- }
- float CImageObject::GetAverageBrightness() const
- {
- return m_averageBrightness;
- }
- void CImageObject::SetAverageBrightness(const float avgBrightness)
- {
- m_averageBrightness = avgBrightness;
- }
- AZ::Color CImageObject::GetAverageColor() const
- {
- return m_averageColor;
- }
- void CImageObject::SetAverageColor(const AZ::Color& averageColor)
- {
- m_averageColor = averageColor;
- }
- AZ::u32 CImageObject::GetImageFlags() const
- {
- return m_imageFlags;
- }
- void CImageObject::SetImageFlags(const AZ::u32 imageFlags)
- {
- m_imageFlags = imageFlags;
- }
- void CImageObject::AddImageFlags(const AZ::u32 imageFlags)
- {
- m_imageFlags |= imageFlags;
- }
- void CImageObject::RemoveImageFlags(const AZ::u32 imageFlags)
- {
- m_imageFlags &= ~imageFlags;
- }
- bool CImageObject::HasImageFlags(const AZ::u32 imageFlags) const
- {
- return (m_imageFlags & imageFlags) != 0;
- }
- AZ::u32 CImageObject::GetNumPersistentMips() const
- {
- return m_numPersistentMips;
- }
- void CImageObject::SetNumPersistentMips(AZ::u32 nMips)
- {
- m_numPersistentMips = nMips;
- }
- bool CImageObject::HasPowerOfTwoSizes() const
- {
- AZ::u32 w, h, d, mips;
- GetExtent(w, h, d, mips);
- return ((w & (w - 1)) == 0) && ((h & (h - 1)) == 0) && ((d & (d - 1)) == 0);
- }
- // use when you convert an image to another one
- void CImageObject::CopyPropertiesFrom(const IImageObjectPtr src)
- {
- const CImageObject* imageObj = static_cast<CImageObject*>(src.get());
- CopyPropertiesFrom(imageObj);
- }
- void CImageObject::CopyPropertiesFrom(const CImageObject* src)
- {
- m_colMinARGB = src->m_colMinARGB;
- m_colMaxARGB = src->m_colMaxARGB;
- m_averageColor = src->m_averageColor;
- m_averageBrightness = src->m_averageBrightness;
- m_imageFlags = src->GetImageFlags();
- }
- void CImageObject::Swizzle(const char channels[4])
- {
- if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)))
- {
- AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
- return;
- }
- const AZ::u8 channelCnt = 4;
- enum Channel_Id
- {
- ChannelR = 0,
- ChannelG,
- ChannelB,
- ChannelA,
- ChannelVal0,
- ChannelVal1,
- ChannelTypeCount
- };
- float values[ChannelTypeCount];
- values[ChannelVal0] = 0.f;
- values[ChannelVal1] = 1.f;
- AZ::u8 channelIndics[channelCnt];
- for (AZ::u8 idx = 0; idx < channelCnt; idx++)
- {
- switch (channels[idx])
- {
- case 'a':
- channelIndics[idx] = ChannelA;
- break;
- case 'r':
- channelIndics[idx] = ChannelR;
- break;
- case 'g':
- channelIndics[idx] = ChannelG;
- break;
- case 'b':
- channelIndics[idx] = ChannelB;
- break;
- case '0':
- channelIndics[idx] = ChannelVal0;
- break;
- case '1':
- channelIndics[idx] = ChannelVal1;
- break;
- default:
- AZ_Assert(false, "%s function only works with channel name \"rgba01\"", __FUNCTION__);
- return;
- }
- }
- //create pixel operation function
- IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
- //get count of bytes per pixel
- uint32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
- const uint32 mips = (uint32)m_mips.size();
- for (uint32 mip = 0; mip < mips; ++mip)
- {
- uint8* pixelBuf = m_mips[mip]->m_pData;
- const uint32 pixelCount = GetPixelCount(mip);
- for (uint32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
- {
- pixelOp->GetRGBA(pixelBuf, values[ChannelR], values[ChannelG], values[ChannelB], values[ChannelA]);
- pixelOp->SetRGBA(pixelBuf, values[channelIndics[0]], values[channelIndics[1]],
- values[channelIndics[2]], values[channelIndics[3]]);
- }
- }
- }
- void CImageObject::GlossFromNormals(bool hasAuthoredGloss)
- {
- if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)))
- {
- AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
- return;
- }
- // 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.
- //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;
- const AZ::u32 mips = (AZ::u32)m_mips.size();
- float color[4];
- for (AZ::u32 mip = 0; mip < mips; ++mip)
- {
- AZ::u8* pixelBuf = m_mips[mip]->m_pData;
- const AZ::u32 pixelCount = GetPixelCount(mip);
- for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
- {
- pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
- // Get length of the averaged normal
- AZ::Vector3 normal(color[0] * 2.0f - 1.0f, color[1] * 2.0f - 1.0f, color[2] * 2.0f - 1.0f);
- float len = AZ::GetMax<float>(normal.GetLength(), 1.0f / (1 << 15));
- float authoredSmoothness = hasAuthoredGloss ? color[3] : 1.0f;
- float finalSmoothness = authoredSmoothness;
- if (len < 1.0f)
- {
- // Convert from smoothness to roughness (needs to match shader code)
- float authoredRoughness = (1.0f - authoredSmoothness) * (1.0f - authoredSmoothness);
- // Derive new roughness based on normal variance
- float kappa = (3.0f * len - len * len * len) / (1.0f - len * len);
- float variance = 1.0f / (2.0f * kappa);
- float finalRoughness = AZ::GetMin(sqrtf(authoredRoughness * authoredRoughness + variance), 1.0f);
- // Convert roughness back to smoothness
- finalSmoothness = 1.0f - sqrtf(finalRoughness);
- }
- pixelOp->SetRGBA(pixelBuf, color[0], color[1], color[2], finalSmoothness);
- }
- }
- }
- } // namespace ImageProcessingAtom
|