Image.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. // Description : Generic image class
  9. #include <ITexture.h>
  10. #ifndef CRYINCLUDE_EDITOR_UTIL_IMAGE_H
  11. #define CRYINCLUDE_EDITOR_UTIL_IMAGE_H
  12. #pragma once
  13. #include "MemoryBlock.h"
  14. #include "Util/XmlArchive.h"
  15. enum class ImageRotationDegrees
  16. {
  17. Rotate0,
  18. Rotate90,
  19. Rotate180,
  20. Rotate270
  21. };
  22. /*!
  23. * Templated image class.
  24. */
  25. template <class T>
  26. class TImage
  27. {
  28. public:
  29. TImage()
  30. : m_data(0)
  31. , m_width(0)
  32. , m_height(0)
  33. , m_bHasAlphaChannel(false)
  34. , m_bIsLimitedHDR(false)
  35. , m_bIsCubemap(false)
  36. , m_bIsSRGB(true)
  37. , m_nNumberOfMipmaps(1)
  38. , m_format(eTF_Unknown)
  39. , m_strDccFilename("")
  40. {
  41. }
  42. virtual ~TImage() {}
  43. T& ValueAt(int x, int y) { return m_data[x + y * m_width]; }
  44. const T& ValueAt(int x, int y) const { return m_data[x + y * m_width]; }
  45. const T& ValueAtSafe(int x, int y) const
  46. {
  47. static T zero = 0;
  48. if (0 <= x && x < m_width && 0 <= y && y < m_height)
  49. {
  50. return m_data[x + y * m_width];
  51. }
  52. return zero;
  53. }
  54. T* GetData() const { return m_data; }
  55. int GetWidth() const { return m_width; }
  56. int GetHeight() const { return m_height; }
  57. bool HasAlphaChannel() const { return m_bHasAlphaChannel; }
  58. bool IsLimitedHDR() const { return m_bIsLimitedHDR; }
  59. bool IsCubemap() const { return m_bIsCubemap; }
  60. unsigned int GetNumberOfMipMaps() const { return m_nNumberOfMipmaps; }
  61. // Returns:
  62. // size in bytes
  63. int GetSize() const { return m_width * m_height * sizeof(T); }
  64. bool IsValid() const { return m_data != 0; }
  65. void Attach(T* data, int width, int height)
  66. {
  67. assert(data);
  68. m_memory = new CMemoryBlock();
  69. m_memory->Attach(data, width * height * sizeof(T));
  70. m_data = data;
  71. m_width = width;
  72. m_height = height;
  73. m_strDccFilename = "";
  74. }
  75. void Attach(const TImage<T>& img)
  76. {
  77. assert(img.IsValid());
  78. m_memory = img.m_memory;
  79. m_data = (T*)m_memory->GetBuffer();
  80. m_width = img.m_width;
  81. m_height = img.m_height;
  82. m_strDccFilename = img.m_strDccFilename;
  83. }
  84. void Detach()
  85. {
  86. m_memory = 0;
  87. m_data = 0;
  88. m_width = 0;
  89. m_height = 0;
  90. m_strDccFilename = "";
  91. }
  92. bool Allocate(int width, int height)
  93. {
  94. if (width < 1)
  95. {
  96. width = 1;
  97. }
  98. if (height < 1)
  99. {
  100. height = 1;
  101. }
  102. if (m_data && (m_width == width && m_height == height))
  103. {
  104. return true;
  105. }
  106. // New memory block.
  107. m_memory = new CMemoryBlock();
  108. m_memory->Allocate(width * height * sizeof(T)); // +width for crash safety.
  109. m_data = (T*)m_memory->GetBuffer();
  110. m_width = width;
  111. m_height = height;
  112. if (!m_data)
  113. {
  114. return false;
  115. }
  116. return true;
  117. }
  118. void Release()
  119. {
  120. m_memory = 0;
  121. m_data = 0;
  122. m_width = 0;
  123. m_height = 0;
  124. m_strDccFilename = "";
  125. }
  126. // Copy operator.
  127. void Copy(const TImage<T>& img)
  128. {
  129. if (!img.IsValid())
  130. {
  131. return;
  132. }
  133. if (m_width != img.GetWidth() || m_height != img.GetHeight())
  134. {
  135. Allocate(img.GetWidth(), img.GetHeight());
  136. }
  137. *m_memory = *img.m_memory;
  138. m_data = (T*)m_memory->GetBuffer();
  139. m_strDccFilename = img.m_strDccFilename;
  140. }
  141. //////////////////////////////////////////////////////////////////////////
  142. void Clear()
  143. {
  144. Fill(0);
  145. }
  146. //////////////////////////////////////////////////////////////////////////
  147. void Fill(unsigned char c)
  148. {
  149. if (IsValid())
  150. {
  151. memset(GetData(), c, GetSize());
  152. }
  153. }
  154. //////////////////////////////////////////////////////////////////////////
  155. void GetSubImage(int x1, int y1, int width, int height, TImage<T>& img) const
  156. {
  157. img.Allocate(width, height);
  158. for (int y = 0; y < height; y++)
  159. {
  160. for (int x = 0; x < width; x++)
  161. {
  162. img.ValueAt(x, y) = ValueAtSafe(x1 + x, y1 + y);
  163. }
  164. }
  165. }
  166. void SetSubImage(int x1, int y1, const TImage<T>& subImage, float heightOffset, float fClamp)
  167. {
  168. int width = subImage.GetWidth();
  169. int height = subImage.GetHeight();
  170. FitSubRect(x1, y1, width, height);
  171. if (width <= 0 || height <= 0)
  172. {
  173. return;
  174. }
  175. if (fClamp < 0.0f)
  176. {
  177. for (int y = 0; y < height; y++)
  178. {
  179. for (int x = 0; x < width; x++)
  180. {
  181. ValueAt(x1 + x, y1 + y) = subImage.ValueAt(x, y) + heightOffset;
  182. }
  183. }
  184. }
  185. else
  186. {
  187. T TClamp = fClamp;
  188. for (int y = 0; y < height; y++)
  189. {
  190. for (int x = 0; x < width; x++)
  191. {
  192. ValueAt(x1 + x, y1 + y) = clamp_tpl(f32(subImage.ValueAt(x, y) + heightOffset), 0.0f, f32(TClamp));
  193. }
  194. }
  195. }
  196. }
  197. void SetSubImage(int x1, int y1, const TImage<T>& subImage)
  198. {
  199. int width = subImage.GetWidth();
  200. int height = subImage.GetHeight();
  201. FitSubRect(x1, y1, width, height);
  202. if (width <= 0 || height <= 0)
  203. {
  204. return;
  205. }
  206. for (int y = 0; y < height; y++)
  207. {
  208. for (int x = 0; x < width; x++)
  209. {
  210. ValueAt(x1 + x, y1 + y) = subImage.ValueAt(x, y);
  211. }
  212. }
  213. }
  214. void FitSubRect(int& x1, int& y1, int& width, int& height)
  215. {
  216. if (x1 < 0)
  217. {
  218. width = width + x1;
  219. x1 = 0;
  220. }
  221. if (y1 < 0)
  222. {
  223. height = height + y1;
  224. y1 = 0;
  225. }
  226. if (x1 + width > m_width)
  227. {
  228. width = m_width - x1;
  229. }
  230. if (y1 + height > m_height)
  231. {
  232. height = m_height - y1;
  233. }
  234. }
  235. //! Compress image to memory block.
  236. void Compress(CMemoryBlock& mem) const
  237. {
  238. assert(IsValid());
  239. m_memory->Compress(mem);
  240. }
  241. //! Uncompress image from memory block.
  242. bool Uncompress(const CMemoryBlock& mem)
  243. {
  244. assert(IsValid());
  245. // New memory block.
  246. _smart_ptr<CMemoryBlock> temp = new CMemoryBlock();
  247. mem.Uncompress(*temp);
  248. bool bValid = (GetSize() == m_memory->GetSize())
  249. || ((GetSize() + m_width * sizeof(T)) == m_memory->GetSize());
  250. if (bValid)
  251. {
  252. m_memory = temp;
  253. m_data = (T*)m_memory->GetBuffer();
  254. }
  255. return bValid;
  256. //assert( GetSize() == m_memory.GetSize() );
  257. }
  258. void SetHasAlphaChannel(bool bHasAlphaChannel) { m_bHasAlphaChannel = bHasAlphaChannel; }
  259. void SetIsLimitedHDR(bool bIsLimitedHDR) { m_bIsLimitedHDR = bIsLimitedHDR; }
  260. void SetIsCubemap(bool bIsCubemap) { m_bIsCubemap = bIsCubemap; }
  261. void SetNumberOfMipMaps(unsigned int nNumberOfMips) { m_nNumberOfMipmaps = nNumberOfMips; }
  262. void Serialize(CXmlArchive& ar);
  263. void SetFormatDescription(const QString& str) { m_formatDescription = str; };
  264. const QString& GetFormatDescription() const { return m_formatDescription; };
  265. void SetFormat(ETEX_Format format) { m_format = format; }
  266. ETEX_Format GetFormat() const { return m_format; }
  267. void SetSRGB(bool bEnable) { m_bIsSRGB = bEnable; }
  268. bool GetSRGB() const { return m_bIsSRGB; }
  269. void SetDccFilename(const QString& str) { m_strDccFilename = str; };
  270. const QString& GetDccFilename() const { return m_strDccFilename; }
  271. // RotateOrt() - orthonormal image rotation
  272. void RotateOrt(const TImage<T>& img, ImageRotationDegrees degrees)
  273. {
  274. if (!img.IsValid())
  275. {
  276. return;
  277. }
  278. int width;
  279. int height;
  280. if (degrees == ImageRotationDegrees::Rotate90 || degrees == ImageRotationDegrees::Rotate270)
  281. {
  282. width = img.GetHeight();
  283. height = img.GetWidth();
  284. }
  285. else
  286. {
  287. width = img.GetWidth();
  288. height = img.GetHeight();
  289. }
  290. if (m_width != width || m_height != height)
  291. {
  292. Allocate(width, height);
  293. }
  294. for (int y = 0; y < m_height; y++)
  295. {
  296. for (int x = 0; x < m_width; x++)
  297. {
  298. if (degrees == ImageRotationDegrees::Rotate90)
  299. {
  300. ValueAt(x, y) = img.ValueAt(m_height - y - 1, x);
  301. }
  302. else if (degrees == ImageRotationDegrees::Rotate180)
  303. {
  304. ValueAt(x, y) = img.ValueAt(m_width - x - 1, m_height - y - 1);
  305. }
  306. else if (degrees == ImageRotationDegrees::Rotate270)
  307. {
  308. ValueAt(x, y) = img.ValueAt(y, m_width - x - 1);
  309. }
  310. else
  311. {
  312. ValueAt(x, y) = img.ValueAt(x, y);
  313. }
  314. }
  315. }
  316. }
  317. //////////////////////////////////////////////////////////////////////////
  318. void ScaleToFit(const TImage<T>& img)
  319. {
  320. uint32 x, y, u, v;
  321. T* destRow, *dest, *src, *sourceRow;
  322. if (!img.IsValid())
  323. {
  324. return;
  325. }
  326. const uint32 srcW = img.GetWidth();
  327. const uint32 srcH = img.GetHeight();
  328. const uint32 trgW = GetWidth();
  329. const uint32 trgH = GetHeight();
  330. const uint32 xratio = trgW > 0 ? (srcW << 16) / trgW : 1;
  331. const uint32 yratio = trgH > 0 ? (srcH << 16) / trgH : 1;
  332. src = img.GetData();
  333. destRow = GetData();
  334. v = 0;
  335. for (y = 0; y < trgH; y++)
  336. {
  337. u = 0;
  338. sourceRow = src + (v >> 16) * srcW;
  339. dest = destRow;
  340. for (x = 0; x < trgW; x++)
  341. {
  342. *dest++ = sourceRow[u >> 16];
  343. u += xratio;
  344. }
  345. v += yratio;
  346. destRow += trgW;
  347. }
  348. }
  349. private:
  350. // Restrict use of copy constructor.
  351. TImage(const TImage<T>& img);
  352. TImage<T>& operator=(const TImage<T>& img);
  353. //! Memory holding image data.
  354. _smart_ptr<CMemoryBlock> m_memory;
  355. T* m_data;
  356. int m_width;
  357. int m_height;
  358. bool m_bHasAlphaChannel;
  359. bool m_bIsLimitedHDR;
  360. bool m_bIsCubemap;
  361. bool m_bIsSRGB;
  362. unsigned int m_nNumberOfMipmaps;
  363. QString m_formatDescription;
  364. QString m_strDccFilename;
  365. ETEX_Format m_format;
  366. };
  367. template <class T>
  368. void TImage<T>::Serialize(CXmlArchive& ar)
  369. {
  370. if (ar.bLoading)
  371. {
  372. // Loading
  373. ar.root->getAttr("ImageWidth", m_width);
  374. ar.root->getAttr("ImageHeight", m_height);
  375. ar.root->getAttr("Mipmaps", m_nNumberOfMipmaps);
  376. ar.root->getAttr("IsCubemap", m_bIsCubemap);
  377. bool bIsSRGB;
  378. if (ar.root->getAttr("IsSRGB", bIsSRGB))
  379. {
  380. m_bIsSRGB = bIsSRGB;
  381. }
  382. ar.root->getAttr("dccFilename", m_strDccFilename);
  383. int format;
  384. if (ar.root->getAttr("format", format))
  385. {
  386. m_format = (ETEX_Format)format;
  387. }
  388. else
  389. {
  390. m_format = eTF_Unknown;
  391. }
  392. Allocate(m_width, m_height);
  393. void* pData = 0;
  394. int nDataSize = 0;
  395. bool bHaveBlock = ar.pNamedData->GetDataBlock(ar.root->getTag(), pData, nDataSize);
  396. if (bHaveBlock && nDataSize == GetSize())
  397. {
  398. m_data = (T*)pData;
  399. }
  400. }
  401. else
  402. {
  403. // Saving.
  404. ar.root->setAttr("ImageWidth", m_width);
  405. ar.root->setAttr("ImageHeight", m_height);
  406. ar.root->setAttr("Mipmaps", m_nNumberOfMipmaps);
  407. ar.root->setAttr("IsCubemap", m_bIsCubemap);
  408. ar.root->setAttr("IsSRGB", m_bIsSRGB);
  409. ar.root->setAttr("format", (int)m_format);
  410. ar.root->setAttr("dccFilename", m_strDccFilename);
  411. ar.pNamedData->AddDataBlock(ar.root->getTag(), (void*)m_data, GetSize());
  412. }
  413. };
  414. //////////////////////////////////////////////////////////////////////////
  415. // Define types of most commonly used images.
  416. //////////////////////////////////////////////////////////////////////////
  417. typedef TImage<float> CFloatImage;
  418. typedef TImage<unsigned char> CByteImage;
  419. typedef TImage<unsigned short> CWordImage;
  420. //////////////////////////////////////////////////////////////////////////
  421. class CImageEx
  422. : public TImage < unsigned int >
  423. {
  424. public:
  425. CImageEx()
  426. : TImage() { m_bGetHistogramEqualization = false; };
  427. EDITOR_CORE_API bool ConvertToFloatImage(CFloatImage& dstImage);
  428. EDITOR_CORE_API void SwapRedAndBlue();
  429. EDITOR_CORE_API void ReverseUpDown();
  430. EDITOR_CORE_API void FillAlpha(unsigned char value = 0xff);
  431. // request histogram equalization for HDRs
  432. void SetHistogramEqualization(bool bHistogramEqualization) { m_bGetHistogramEqualization = bHistogramEqualization; }
  433. bool GetHistogramEqualization() { return m_bGetHistogramEqualization; }
  434. private:
  435. bool m_bGetHistogramEqualization;
  436. };
  437. #endif // CRYINCLUDE_EDITOR_UTIL_IMAGE_H