/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : Generic image class #include #ifndef CRYINCLUDE_EDITOR_UTIL_IMAGE_H #define CRYINCLUDE_EDITOR_UTIL_IMAGE_H #pragma once #include "MemoryBlock.h" #include "Util/XmlArchive.h" enum class ImageRotationDegrees { Rotate0, Rotate90, Rotate180, Rotate270 }; /*! * Templated image class. */ template class TImage { public: TImage() : m_data(0) , m_width(0) , m_height(0) , m_bHasAlphaChannel(false) , m_bIsLimitedHDR(false) , m_bIsCubemap(false) , m_bIsSRGB(true) , m_nNumberOfMipmaps(1) , m_format(eTF_Unknown) , m_strDccFilename("") { } virtual ~TImage() {} T& ValueAt(int x, int y) { return m_data[x + y * m_width]; } const T& ValueAt(int x, int y) const { return m_data[x + y * m_width]; } const T& ValueAtSafe(int x, int y) const { static T zero = 0; if (0 <= x && x < m_width && 0 <= y && y < m_height) { return m_data[x + y * m_width]; } return zero; } T* GetData() const { return m_data; } int GetWidth() const { return m_width; } int GetHeight() const { return m_height; } bool HasAlphaChannel() const { return m_bHasAlphaChannel; } bool IsLimitedHDR() const { return m_bIsLimitedHDR; } bool IsCubemap() const { return m_bIsCubemap; } unsigned int GetNumberOfMipMaps() const { return m_nNumberOfMipmaps; } // Returns: // size in bytes int GetSize() const { return m_width * m_height * sizeof(T); } bool IsValid() const { return m_data != 0; } void Attach(T* data, int width, int height) { assert(data); m_memory = new CMemoryBlock(); m_memory->Attach(data, width * height * sizeof(T)); m_data = data; m_width = width; m_height = height; m_strDccFilename = ""; } void Attach(const TImage& img) { assert(img.IsValid()); m_memory = img.m_memory; m_data = (T*)m_memory->GetBuffer(); m_width = img.m_width; m_height = img.m_height; m_strDccFilename = img.m_strDccFilename; } void Detach() { m_memory = 0; m_data = 0; m_width = 0; m_height = 0; m_strDccFilename = ""; } bool Allocate(int width, int height) { if (width < 1) { width = 1; } if (height < 1) { height = 1; } if (m_data && (m_width == width && m_height == height)) { return true; } // New memory block. m_memory = new CMemoryBlock(); m_memory->Allocate(width * height * sizeof(T)); // +width for crash safety. m_data = (T*)m_memory->GetBuffer(); m_width = width; m_height = height; if (!m_data) { return false; } return true; } void Release() { m_memory = 0; m_data = 0; m_width = 0; m_height = 0; m_strDccFilename = ""; } // Copy operator. void Copy(const TImage& img) { if (!img.IsValid()) { return; } if (m_width != img.GetWidth() || m_height != img.GetHeight()) { Allocate(img.GetWidth(), img.GetHeight()); } *m_memory = *img.m_memory; m_data = (T*)m_memory->GetBuffer(); m_strDccFilename = img.m_strDccFilename; } ////////////////////////////////////////////////////////////////////////// void Clear() { Fill(0); } ////////////////////////////////////////////////////////////////////////// void Fill(unsigned char c) { if (IsValid()) { memset(GetData(), c, GetSize()); } } ////////////////////////////////////////////////////////////////////////// void GetSubImage(int x1, int y1, int width, int height, TImage& img) const { int size = width * height; img.Allocate(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { img.ValueAt(x, y) = ValueAtSafe(x1 + x, y1 + y); } } } void SetSubImage(int x1, int y1, const TImage& subImage, float heightOffset, float fClamp) { int width = subImage.GetWidth(); int height = subImage.GetHeight(); FitSubRect(x1, y1, width, height); if (width <= 0 || height <= 0) { return; } if (fClamp < 0.0f) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { ValueAt(x1 + x, y1 + y) = subImage.ValueAt(x, y) + heightOffset; } } } else { T TClamp = fClamp; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { ValueAt(x1 + x, y1 + y) = clamp_tpl(f32(subImage.ValueAt(x, y) + heightOffset), 0.0f, f32(TClamp)); } } } } void SetSubImage(int x1, int y1, const TImage& subImage) { int width = subImage.GetWidth(); int height = subImage.GetHeight(); FitSubRect(x1, y1, width, height); if (width <= 0 || height <= 0) { return; } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { ValueAt(x1 + x, y1 + y) = subImage.ValueAt(x, y); } } } void FitSubRect(int& x1, int& y1, int& width, int& height) { if (x1 < 0) { width = width + x1; x1 = 0; } if (y1 < 0) { height = height + y1; y1 = 0; } if (x1 + width > m_width) { width = m_width - x1; } if (y1 + height > m_height) { height = m_height - y1; } } //! Compress image to memory block. void Compress(CMemoryBlock& mem) const { assert(IsValid()); m_memory->Compress(mem); } //! Uncompress image from memory block. bool Uncompress(const CMemoryBlock& mem) { assert(IsValid()); // New memory block. _smart_ptr temp = new CMemoryBlock(); mem.Uncompress(*temp); bool bValid = (GetSize() == m_memory->GetSize()) || ((GetSize() + m_width * sizeof(T)) == m_memory->GetSize()); if (bValid) { m_memory = temp; m_data = (T*)m_memory->GetBuffer(); } return bValid; //assert( GetSize() == m_memory.GetSize() ); } void SetHasAlphaChannel(bool bHasAlphaChannel) { m_bHasAlphaChannel = bHasAlphaChannel; } void SetIsLimitedHDR(bool bIsLimitedHDR) { m_bIsLimitedHDR = bIsLimitedHDR; } void SetIsCubemap(bool bIsCubemap) { m_bIsCubemap = bIsCubemap; } void SetNumberOfMipMaps(unsigned int nNumberOfMips) { m_nNumberOfMipmaps = nNumberOfMips; } void Serialize(CXmlArchive& ar); void SetFormatDescription(const QString& str) { m_formatDescription = str; }; const QString& GetFormatDescription() const { return m_formatDescription; }; void SetFormat(ETEX_Format format) { m_format = format; } ETEX_Format GetFormat() const { return m_format; } void SetSRGB(bool bEnable) { m_bIsSRGB = bEnable; } bool GetSRGB() const { return m_bIsSRGB; } void SetDccFilename(const QString& str) { m_strDccFilename = str; }; const QString& GetDccFilename() const { return m_strDccFilename; } // RotateOrt() - orthonormal image rotation void RotateOrt(const TImage& img, ImageRotationDegrees degrees) { if (!img.IsValid()) { return; } int width; int height; if (degrees == ImageRotationDegrees::Rotate90 || degrees == ImageRotationDegrees::Rotate270) { width = img.GetHeight(); height = img.GetWidth(); } else { width = img.GetWidth(); height = img.GetHeight(); } if (m_width != width || m_height != height) { Allocate(width, height); } for (int y = 0; y < m_height; y++) { for (int x = 0; x < m_width; x++) { if (degrees == ImageRotationDegrees::Rotate90) { ValueAt(x, y) = img.ValueAt(m_height - y - 1, x); } else if (degrees == ImageRotationDegrees::Rotate180) { ValueAt(x, y) = img.ValueAt(m_width - x - 1, m_height - y - 1); } else if (degrees == ImageRotationDegrees::Rotate270) { ValueAt(x, y) = img.ValueAt(y, m_width - x - 1); } else { ValueAt(x, y) = img.ValueAt(x, y); } } } } ////////////////////////////////////////////////////////////////////////// void ScaleToFit(const TImage& img) { uint32 x, y, u, v; T* destRow, *dest, *src, *sourceRow; if (!img.IsValid()) { return; } const uint32 srcW = img.GetWidth(); const uint32 srcH = img.GetHeight(); const uint32 trgW = GetWidth(); const uint32 trgH = GetHeight(); const uint32 xratio = trgW > 0 ? (srcW << 16) / trgW : 1; const uint32 yratio = trgH > 0 ? (srcH << 16) / trgH : 1; src = img.GetData(); destRow = GetData(); v = 0; for (y = 0; y < trgH; y++) { u = 0; sourceRow = src + (v >> 16) * srcW; dest = destRow; for (x = 0; x < trgW; x++) { *dest++ = sourceRow[u >> 16]; u += xratio; } v += yratio; destRow += trgW; } } private: // Restrict use of copy constructor. TImage(const TImage& img); TImage& operator=(const TImage& img); //! Memory holding image data. _smart_ptr m_memory; T* m_data; int m_width; int m_height; bool m_bHasAlphaChannel; bool m_bIsLimitedHDR; bool m_bIsCubemap; bool m_bIsSRGB; unsigned int m_nNumberOfMipmaps; QString m_formatDescription; QString m_strDccFilename; ETEX_Format m_format; }; template void TImage::Serialize(CXmlArchive& ar) { if (ar.bLoading) { // Loading ar.root->getAttr("ImageWidth", m_width); ar.root->getAttr("ImageHeight", m_height); ar.root->getAttr("Mipmaps", m_nNumberOfMipmaps); ar.root->getAttr("IsCubemap", m_bIsCubemap); bool bIsSRGB; if (ar.root->getAttr("IsSRGB", bIsSRGB)) { m_bIsSRGB = bIsSRGB; } ar.root->getAttr("dccFilename", m_strDccFilename); int format; if (ar.root->getAttr("format", format)) { m_format = (ETEX_Format)format; } else { m_format = eTF_Unknown; } Allocate(m_width, m_height); void* pData = 0; int nDataSize = 0; bool bHaveBlock = ar.pNamedData->GetDataBlock(ar.root->getTag(), pData, nDataSize); if (bHaveBlock && nDataSize == GetSize()) { m_data = (T*)pData; } } else { // Saving. ar.root->setAttr("ImageWidth", m_width); ar.root->setAttr("ImageHeight", m_height); ar.root->setAttr("Mipmaps", m_nNumberOfMipmaps); ar.root->setAttr("IsCubemap", m_bIsCubemap); ar.root->setAttr("IsSRGB", m_bIsSRGB); ar.root->setAttr("format", (int)m_format); ar.root->setAttr("dccFilename", m_strDccFilename); ar.pNamedData->AddDataBlock(ar.root->getTag(), (void*)m_data, GetSize()); } }; ////////////////////////////////////////////////////////////////////////// // Define types of most commonly used images. ////////////////////////////////////////////////////////////////////////// typedef TImage CFloatImage; typedef TImage CByteImage; typedef TImage CWordImage; ////////////////////////////////////////////////////////////////////////// class CImageEx : public TImage < unsigned int > { public: CImageEx() : TImage() { m_bGetHistogramEqualization = false; }; EDITOR_CORE_API bool ConvertToFloatImage(CFloatImage& dstImage); EDITOR_CORE_API void SwapRedAndBlue(); EDITOR_CORE_API void ReverseUpDown(); EDITOR_CORE_API void FillAlpha(unsigned char value = 0xff); // request histogram equalization for HDRs void SetHistogramEqualization(bool bHistogramEqualization) { m_bGetHistogramEqualization = bHistogramEqualization; } bool GetHistogramEqualization() { return m_bGetHistogramEqualization; } private: bool m_bGetHistogramEqualization; }; #endif // CRYINCLUDE_EDITOR_UTIL_IMAGE_H