3
0

ImageObjectImpl.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  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. #include <Processing/ImageObjectImpl.h>
  9. #include <Processing/PixelFormatInfo.h>
  10. #include <Processing/ImageFlags.h>
  11. #include <Processing/ImageConvert.h>
  12. #include <Converters/PixelOperation.h>
  13. #include <AzCore/std/string/conversions.h>
  14. #include <AzCore/std/algorithm.h>
  15. #include <AzCore/IO/SystemFile.h>
  16. #include <AzCore/IO/GenericStreams.h>
  17. #include <AzFramework/StringFunc/StringFunc.h>
  18. // Indicates a 2D texture is a cube-map texture.
  19. #define DDS_RESOURCE_MISC_TEXTURECUBE 0x4
  20. namespace ImageProcessingAtom
  21. {
  22. using namespace AZ;
  23. IImageObject* IImageObject::CreateImage(AZ::u32 width, AZ::u32 height,
  24. AZ::u32 maxMipCount, EPixelFormat pixelFormat)
  25. {
  26. return aznew CImageObject(width, height, maxMipCount, pixelFormat);
  27. }
  28. IImageObject* IImageObject::CreateImage(AZ::u32 width, AZ::u32 height, AZ::u32 depth, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
  29. {
  30. return aznew CImageObject(width, height, depth, maxMipCount, pixelFormat);
  31. }
  32. CImageObject::CImageObject(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
  33. : CImageObject(width, height, 1/*depth*/, maxMipCount, pixelFormat)
  34. {
  35. }
  36. CImageObject::CImageObject(AZ::u32 width, AZ::u32 height, AZ::u32 depth, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
  37. : m_pixelFormat(pixelFormat)
  38. , m_colMinARGB(0.0f, 0.0f, 0.0f, 0.0f)
  39. , m_colMaxARGB(1.0f, 1.0f, 1.0f, 1.0f)
  40. , m_averageColor(0.0f, 0.0f, 0.0f, 0.0f)
  41. , m_averageBrightness(0.63f)
  42. , m_imageFlags(0)
  43. , m_numPersistentMips(0)
  44. {
  45. ResetImage(width, height, depth, maxMipCount, pixelFormat);
  46. }
  47. EPixelFormat CImageObject::GetPixelFormat() const
  48. {
  49. return m_pixelFormat;
  50. }
  51. AZ::u32 CImageObject::GetPixelCount(AZ::u32 mip) const
  52. {
  53. AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
  54. return m_mips[mip]->m_width * m_mips[mip]->m_height * m_mips[mip]->m_depth;
  55. }
  56. AZ::u32 CImageObject::GetWidth(AZ::u32 mip) const
  57. {
  58. AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
  59. return m_mips[mip]->m_width;
  60. }
  61. AZ::u32 CImageObject::GetHeight(AZ::u32 mip) const
  62. {
  63. AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
  64. return m_mips[mip]->m_height;
  65. }
  66. AZ::u32 CImageObject::GetDepth(AZ::u32 mip) const
  67. {
  68. AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "Mip doesn't exist: %d", mip);
  69. return m_mips[mip]->m_depth;
  70. }
  71. AZ::u32 CImageObject::GetMipCount() const
  72. {
  73. return (AZ::u32)m_mips.size();
  74. }
  75. void CImageObject::ResetImage(AZ::u32 width, AZ::u32 height, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
  76. {
  77. ResetImage(width, height, 1 /* depth */, maxMipCount, pixelFormat);
  78. }
  79. void CImageObject::ResetImage(AZ::u32 width, AZ::u32 height, AZ::u32 depth, AZ::u32 maxMipCount, EPixelFormat pixelFormat)
  80. {
  81. //check input
  82. 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);
  83. //clean up mipmaps
  84. for (AZ::u32 mip = 0; mip < AZ::u32(m_mips.size()); ++mip)
  85. {
  86. delete m_mips[mip];
  87. }
  88. m_pixelFormat = pixelFormat;
  89. m_colMinARGB = AZ::Color(0.0f, 0.0f, 0.0f, 0.0f);
  90. m_colMaxARGB = AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
  91. m_averageColor = AZ::Color(0.0f, 0.0f, 0.0f, 0.0f);
  92. m_averageBrightness = 0.0f;
  93. m_imageFlags = (depth > 1) ? EIF_Volumetexture : 0;
  94. m_numPersistentMips = 0;
  95. m_mips.clear();
  96. const PixelFormatInfo* const pFmt = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat);
  97. AZ_Assert(pFmt, "can't find pixe format info for %d", m_pixelFormat);
  98. const AZ::u32 mipCount = AZStd::min<AZ::u32>(maxMipCount,
  99. CPixelFormats::GetInstance().ComputeMaxMipCount(m_pixelFormat, width, height, depth));
  100. m_mips.reserve(mipCount);
  101. for (AZ::u32 mip = 0; mip < mipCount; ++mip)
  102. {
  103. MipLevel* const pEntry = aznew MipLevel;
  104. AZ::u32 localWidth = AZStd::max(width >> mip, 1u);
  105. AZ::u32 localHeight = AZStd::max(height >> mip, 1u);
  106. AZ::u32 localDepth = AZStd::max(depth >> mip, 1u);
  107. pEntry->m_width = localWidth;
  108. pEntry->m_height = localHeight;
  109. pEntry->m_depth = localDepth;
  110. if (pFmt->bCompressed)
  111. {
  112. const AZ::u32 blocksInRow = (pEntry->m_width + (pFmt->blockWidth - 1)) / pFmt->blockWidth;
  113. pEntry->m_pitch = (blocksInRow * pFmt->bitsPerBlock) / 8;
  114. pEntry->m_rowCount = (localHeight + (pFmt->blockHeight - 1)) / pFmt->blockHeight;
  115. }
  116. else
  117. {
  118. pEntry->m_pitch = (pEntry->m_width * pFmt->bitsPerBlock) / 8;
  119. pEntry->m_rowCount = localHeight;
  120. }
  121. pEntry->Alloc();
  122. m_mips.push_back(pEntry);
  123. }
  124. }
  125. bool CImageObject::CompareImage(const IImageObjectPtr otherImage) const
  126. {
  127. CImageObject* other = static_cast<CImageObject*>(otherImage.get());
  128. if (other == nullptr)
  129. {
  130. return false;
  131. }
  132. if (m_pixelFormat == other->m_pixelFormat
  133. && m_colMinARGB == other->m_colMinARGB
  134. && m_colMaxARGB == other->m_colMaxARGB
  135. && m_averageColor == other->m_averageColor
  136. && m_averageBrightness == other->m_averageBrightness
  137. && m_imageFlags == other->m_imageFlags
  138. && m_numPersistentMips == other->m_numPersistentMips
  139. && m_mips.size() == other->m_mips.size())
  140. {
  141. for (int mip = 0; mip < m_mips.size(); mip++)
  142. {
  143. if (!(*m_mips[mip] == *other->m_mips[mip]))
  144. {
  145. return false;
  146. }
  147. }
  148. return true;
  149. }
  150. return false;
  151. }
  152. uint32_t CImageObject::GetTextureMemory() const
  153. {
  154. int totalSize = 0;
  155. for (int mip = 0; mip < m_mips.size(); mip++)
  156. {
  157. totalSize += CPixelFormats::GetInstance().EvaluateImageDataSize(m_pixelFormat,
  158. m_mips[mip]->m_width, m_mips[mip]->m_height) * m_mips[mip]->m_depth;
  159. }
  160. return totalSize;
  161. }
  162. EAlphaContent CImageObject::GetAlphaContent() const
  163. {
  164. if (CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(m_pixelFormat))
  165. {
  166. return EAlphaContent::eAlphaContent_Absent;
  167. }
  168. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
  169. {
  170. AZ_TracePrintf("Image processing", "GetAlphaContent() was called for compressed format\n");
  171. return EAlphaContent::eAlphaContent_Indeterminate;
  172. }
  173. //go though alpha channel of first mip
  174. //create pixel operation function to access pixel data
  175. IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
  176. //get count of bytes per pixel for images
  177. AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
  178. // counts of blacks and white
  179. uint32_t nBlacks = 0;
  180. uint32_t nWhites = 0;
  181. float r, g, b, a;
  182. AZ::u8* pixelBuf;
  183. AZ::u32 pitch;
  184. GetImagePointer(0, pixelBuf, pitch);
  185. const AZ::u32 pixelCount = GetPixelCount(0);
  186. for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
  187. {
  188. pixelOp->GetRGBA(pixelBuf, r, g, b, a);
  189. if (a == 0.0f)
  190. {
  191. ++nBlacks;
  192. }
  193. else if (a == 1.0f)
  194. {
  195. ++nWhites;
  196. }
  197. else
  198. {
  199. return EAlphaContent::eAlphaContent_Greyscale;
  200. }
  201. }
  202. if (nBlacks == 0)
  203. {
  204. return EAlphaContent::eAlphaContent_OnlyWhite;
  205. }
  206. if (nWhites == 0)
  207. {
  208. return EAlphaContent::eAlphaContent_OnlyBlack;
  209. }
  210. return EAlphaContent::eAlphaContent_OnlyBlackAndWhite;
  211. }
  212. // clone this image-object's contents
  213. IImageObject* CImageObject::Clone(uint32_t maxMipCount) const
  214. {
  215. IImageObject* outImage = AllocateImage(maxMipCount);
  216. AZ::u32 mips = outImage->GetMipCount();
  217. for (AZ::u32 mip = 0; mip < mips; ++mip)
  218. {
  219. AZ::u8* dstMem;
  220. AZ::u32 pitch;
  221. outImage->GetImagePointer(mip, dstMem, pitch);
  222. memcpy(dstMem, m_mips[mip]->m_pData, GetMipBufSize(mip));
  223. }
  224. return outImage;
  225. }
  226. void CImageObject::ClearColor(float r, float g, float b, float a)
  227. {
  228. //if it's compressed format, return directly
  229. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
  230. {
  231. AZ_Assert(false, "The %s function only works with uncompressed formats", __FUNCTION__);
  232. return;
  233. }
  234. //create pixel operation function to access pixel data
  235. IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
  236. //get count of bytes per pixel for images
  237. AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
  238. AZ::u8* pixelBuf;
  239. AZ::u32 pitch;
  240. AZ::u32 mips = GetMipCount();
  241. for (AZ::u32 mip = 0; mip < mips; ++mip)
  242. {
  243. GetImagePointer(mip, pixelBuf, pitch);
  244. const AZ::u32 pixelCount = GetPixelCount(mip);
  245. for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
  246. {
  247. pixelOp->SetRGBA(pixelBuf, r, g, b, a);
  248. }
  249. }
  250. }
  251. // allocate an empty image with the same properties as the given image and the requested format
  252. IImageObject* CImageObject::AllocateImage(EPixelFormat pixelFormat, uint32_t maxMipCount) const
  253. {
  254. AZ::u32 width = GetWidth(0);
  255. AZ::u32 height = GetHeight(0);
  256. AZ::u32 depth = GetDepth(0);
  257. if (!CPixelFormats::GetInstance().IsImageSizeValid(pixelFormat, width, height, false))
  258. {
  259. AZ_Assert(false, "Cann't allocate image with format: %d", pixelFormat);
  260. return nullptr;
  261. }
  262. maxMipCount = AZ::GetMin(maxMipCount, GetMipCount());
  263. CImageObject* pRet = aznew CImageObject(width, height, depth, maxMipCount, pixelFormat);
  264. pRet->CopyPropertiesFrom(this);
  265. return pRet;
  266. }
  267. IImageObject* CImageObject::AllocateImage(uint32_t maxMipCount) const
  268. {
  269. return AllocateImage(m_pixelFormat, maxMipCount);
  270. }
  271. CImageObject::~CImageObject()
  272. {
  273. for (size_t i = 0; i < m_mips.size(); ++i)
  274. {
  275. delete m_mips[i];
  276. }
  277. m_mips.clear();
  278. }
  279. float CImageObject::CalculateAverageBrightness() const
  280. {
  281. //if it's compressed format, return a default value
  282. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
  283. {
  284. return 0.5f;
  285. }
  286. // Accumulate pixel colors of the top mip
  287. double avgOverall[3] = { 0.0, 0.0, 0.0 };
  288. //create pixel operation function to access pixel data
  289. IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
  290. //get count of bytes per pixel for images
  291. AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
  292. //only calculate mip 0
  293. AZ::u32 mip = 0;
  294. float color[4];
  295. AZ::u8* pixelBuf;
  296. AZ::u32 pitch;
  297. GetImagePointer(mip, pixelBuf, pitch);
  298. const AZ::u32 pixelCount = GetPixelCount(mip);
  299. for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
  300. {
  301. pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
  302. avgOverall[0] += color[0];
  303. avgOverall[1] += color[1];
  304. avgOverall[2] += color[2];
  305. }
  306. const double avg = (avgOverall[0] + avgOverall[1] + avgOverall[2]) / (3 * pixelCount);
  307. return (float)avg;
  308. }
  309. bool CImageObject::BuildSurfaceHeader(DDS_HEADER_LEGACY& header) const
  310. {
  311. AZ::u32 dwWidth, dwMips, dwHeight, dwDepth;
  312. GetExtent(dwWidth, dwHeight, dwDepth, dwMips);
  313. if (dwMips <= 0)
  314. {
  315. AZ_Error("Image Processing", false, "%s: dwMips is %u", __FUNCTION__, (unsigned)dwMips);
  316. return false;
  317. }
  318. const EPixelFormat format = GetPixelFormat();
  319. if ((format < 0) || (format >= ePixelFormat_Count))
  320. {
  321. AZ_Error("Image Processing", false, "%s: Bad format %d", __FUNCTION__, (int)format);
  322. return false;
  323. }
  324. const PixelFormatInfo* const pPixelFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(format);
  325. memset(&header, 0, sizeof(DDS_HEADER_LEGACY));
  326. header.dwSize = sizeof(DDS_HEADER_LEGACY);
  327. header.dwHeaderFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
  328. header.dwWidth = dwWidth;
  329. header.dwHeight = dwHeight;
  330. if (dwDepth > 1)
  331. {
  332. header.dwDepth = dwDepth;
  333. header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_CUBEMAP; // DDSCAPS_COMPLEX
  334. header.dwCubemapFlags |= DDS_FLAGS_VOLUME; // DDSCAPS2_VOLUME
  335. }
  336. if (HasImageFlags(EIF_Cubemap))
  337. {
  338. AZ_Assert(dwDepth <= 1, "Volumetric Cubemap Textures don't exist!");
  339. header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_CUBEMAP;
  340. header.dwCubemapFlags |= DDS_CUBEMAP_ALLFACES;
  341. //save face size instead of image size.
  342. header.dwHeight /= 6;
  343. }
  344. header.ddspf.dwSize = sizeof(DDS_PIXELFORMAT);
  345. header.ddspf.dwFlags = DDS_FOURCC;
  346. header.ddspf.dwFourCC = pPixelFormatInfo->fourCC;
  347. header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_TEXTURE;
  348. if (dwMips > 1)
  349. {
  350. header.dwHeaderFlags |= DDS_HEADER_FLAGS_MIPMAP;
  351. header.dwMipMapCount = dwMips;
  352. header.dwSurfaceFlags |= DDS_SURFACE_FLAGS_MIPMAP;
  353. }
  354. // non standardized way to expose some features in the header (same information is in attached chunk but then
  355. // streaming would need to find this spot in the file)
  356. // if this is causing problems we need to change it
  357. header.dwTextureStage = FOURCC_FYRC;
  358. header.dwReserved1 = GetImageFlags();
  359. header.bNumPersistentMips = (AZ::u8)GetNumPersistentMips();
  360. //tile mode for some platform native texture
  361. if (HasImageFlags(EIF_RestrictedPlatformDNative))
  362. {
  363. header.tileMode = eTM_LinearPadded;
  364. }
  365. else if (HasImageFlags(EIF_RestrictedPlatformONative))
  366. {
  367. header.tileMode = eTM_Optimal;
  368. }
  369. // setting up min and max colors
  370. for (int i = 0; i < 4; i++)
  371. {
  372. header.cMinColor[i] = m_colMinARGB.GetElement(i);
  373. header.cMaxColor[i] = m_colMaxARGB.GetElement(i);
  374. }
  375. // set avg brightness
  376. header.fAvgBrightness = GetAverageBrightness();
  377. return true;
  378. }
  379. bool CImageObject::BuildSurfaceExtendedHeader(DDS_HEADER_DXT10& exthead) const
  380. {
  381. const EPixelFormat format = GetPixelFormat();
  382. const PixelFormatInfo* const pPixelFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(format);
  383. DXGI_FORMAT dxgiformat = pPixelFormatInfo->d3d10Format;
  384. // check if we hit a format which can't be stored into a DX10 DDS-file (fe. L8)
  385. if (dxgiformat == DXGI_FORMAT_UNKNOWN)
  386. {
  387. AZ_Error("Image Processing", false, "%s: Format can not be stored in a DDS-file %d", __FUNCTION__, dxgiformat);
  388. return false;
  389. }
  390. //the dxgi format are different for linear space or gamma space
  391. if (HasImageFlags(EIF_SRGBRead))
  392. {
  393. switch (dxgiformat)
  394. {
  395. case DXGI_FORMAT_R8G8B8A8_UNORM:
  396. dxgiformat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
  397. break;
  398. case DXGI_FORMAT_BC1_UNORM:
  399. dxgiformat = DXGI_FORMAT_BC1_UNORM_SRGB;
  400. break;
  401. case DXGI_FORMAT_BC2_UNORM:
  402. dxgiformat = DXGI_FORMAT_BC2_UNORM_SRGB;
  403. break;
  404. case DXGI_FORMAT_BC3_UNORM:
  405. dxgiformat = DXGI_FORMAT_BC3_UNORM_SRGB;
  406. break;
  407. case DXGI_FORMAT_BC7_UNORM:
  408. dxgiformat = DXGI_FORMAT_BC7_UNORM_SRGB;
  409. break;
  410. default:
  411. break;
  412. }
  413. }
  414. else
  415. {
  416. switch (dxgiformat)
  417. {
  418. case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
  419. dxgiformat = DXGI_FORMAT_R8G8B8A8_UNORM;
  420. break;
  421. case DXGI_FORMAT_BC1_UNORM_SRGB:
  422. dxgiformat = DXGI_FORMAT_BC1_UNORM;
  423. break;
  424. case DXGI_FORMAT_BC2_UNORM_SRGB:
  425. dxgiformat = DXGI_FORMAT_BC2_UNORM;
  426. break;
  427. case DXGI_FORMAT_BC3_UNORM_SRGB:
  428. dxgiformat = DXGI_FORMAT_BC3_UNORM;
  429. break;
  430. case DXGI_FORMAT_BC7_UNORM_SRGB:
  431. dxgiformat = DXGI_FORMAT_BC7_UNORM;
  432. break;
  433. default:
  434. break;
  435. }
  436. }
  437. memset(&exthead, 0, sizeof(exthead));
  438. exthead.dxgiFormat = dxgiformat;
  439. exthead.resourceDimension = 3; //texture2d. not used
  440. if (HasImageFlags(EIF_Cubemap))
  441. {
  442. exthead.miscFlag = DDS_RESOURCE_MISC_TEXTURECUBE;
  443. exthead.arraySize = 6;
  444. }
  445. else
  446. {
  447. exthead.miscFlag = 0;
  448. exthead.arraySize = 1;
  449. }
  450. return true;
  451. }
  452. void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const
  453. {
  454. mipCount = (AZ::u32)m_mips.size();
  455. width = m_mips[0]->m_width;
  456. height = m_mips[0]->m_height;
  457. }
  458. void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& depth, AZ::u32& mipCount) const
  459. {
  460. mipCount = (AZ::u32)m_mips.size();
  461. width = m_mips[0]->m_width;
  462. height = m_mips[0]->m_height;
  463. depth = m_mips[0]->m_depth;
  464. }
  465. AZ::u32 CImageObject::GetMipDataSize(const AZ::u32 mip) const
  466. {
  467. AZ_Assert(mip < m_mips.size(), "mip %d doesn't exist", mip);
  468. return m_mips[mip]->GetSize();
  469. }
  470. void CImageObject::GetImagePointer(const AZ::u32 mip, AZ::u8*& pMem, AZ::u32& pitch) const
  471. {
  472. AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "requested mip doesn't exist");
  473. pMem = m_mips[mip]->m_pData;
  474. pitch = m_mips[mip]->m_pitch;
  475. }
  476. AZ::u32 CImageObject::GetMipBufSize(AZ::u32 mip) const
  477. {
  478. AZ_Assert(mip < (AZ::u32)m_mips.size() && m_mips[mip], "requested mip doesn't exist");
  479. return m_mips[mip]->GetSize();
  480. }
  481. void CImageObject::SetMipData(AZ::u32 mip, AZ::u8* mipBuf, AZ::u32 bufSize, AZ::u32 pitch)
  482. {
  483. if (mip >= m_mips.size())
  484. {
  485. return;
  486. }
  487. uint32_t depth = m_mips[mip]->m_depth;
  488. if (depth < 1)
  489. {
  490. depth = 1;
  491. m_mips[mip]->m_depth = depth;
  492. }
  493. m_mips[mip]->m_pData = mipBuf;
  494. m_mips[mip]->m_pitch = pitch;
  495. m_mips[mip]->m_rowCount = bufSize / pitch / depth;
  496. AZ_Assert(bufSize == m_mips[mip]->m_rowCount * pitch * depth, "Bad pitch size");
  497. }
  498. // ARGB
  499. void CImageObject::GetColorRange(AZ::Color& minColor, AZ::Color& maxColor) const
  500. {
  501. minColor = m_colMinARGB;
  502. maxColor = m_colMaxARGB;
  503. }
  504. // ARGB
  505. void CImageObject::SetColorRange(const AZ::Color& minColor, const AZ::Color& maxColor)
  506. {
  507. m_colMinARGB = minColor;
  508. m_colMaxARGB = maxColor;
  509. }
  510. float CImageObject::GetAverageBrightness() const
  511. {
  512. return m_averageBrightness;
  513. }
  514. void CImageObject::SetAverageBrightness(const float avgBrightness)
  515. {
  516. m_averageBrightness = avgBrightness;
  517. }
  518. AZ::Color CImageObject::GetAverageColor() const
  519. {
  520. return m_averageColor;
  521. }
  522. void CImageObject::SetAverageColor(const AZ::Color& averageColor)
  523. {
  524. m_averageColor = averageColor;
  525. }
  526. AZ::u32 CImageObject::GetImageFlags() const
  527. {
  528. return m_imageFlags;
  529. }
  530. void CImageObject::SetImageFlags(const AZ::u32 imageFlags)
  531. {
  532. m_imageFlags = imageFlags;
  533. }
  534. void CImageObject::AddImageFlags(const AZ::u32 imageFlags)
  535. {
  536. m_imageFlags |= imageFlags;
  537. }
  538. void CImageObject::RemoveImageFlags(const AZ::u32 imageFlags)
  539. {
  540. m_imageFlags &= ~imageFlags;
  541. }
  542. bool CImageObject::HasImageFlags(const AZ::u32 imageFlags) const
  543. {
  544. return (m_imageFlags & imageFlags) != 0;
  545. }
  546. AZ::u32 CImageObject::GetNumPersistentMips() const
  547. {
  548. return m_numPersistentMips;
  549. }
  550. void CImageObject::SetNumPersistentMips(AZ::u32 nMips)
  551. {
  552. m_numPersistentMips = nMips;
  553. }
  554. bool CImageObject::HasPowerOfTwoSizes() const
  555. {
  556. AZ::u32 w, h, d, mips;
  557. GetExtent(w, h, d, mips);
  558. return ((w & (w - 1)) == 0) && ((h & (h - 1)) == 0) && ((d & (d - 1)) == 0);
  559. }
  560. // use when you convert an image to another one
  561. void CImageObject::CopyPropertiesFrom(const IImageObjectPtr src)
  562. {
  563. const CImageObject* imageObj = static_cast<CImageObject*>(src.get());
  564. CopyPropertiesFrom(imageObj);
  565. }
  566. void CImageObject::CopyPropertiesFrom(const CImageObject* src)
  567. {
  568. m_colMinARGB = src->m_colMinARGB;
  569. m_colMaxARGB = src->m_colMaxARGB;
  570. m_averageColor = src->m_averageColor;
  571. m_averageBrightness = src->m_averageBrightness;
  572. m_imageFlags = src->GetImageFlags();
  573. }
  574. void CImageObject::Swizzle(const char channels[4])
  575. {
  576. if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)))
  577. {
  578. AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
  579. return;
  580. }
  581. const AZ::u8 channelCnt = 4;
  582. enum Channel_Id
  583. {
  584. ChannelR = 0,
  585. ChannelG,
  586. ChannelB,
  587. ChannelA,
  588. ChannelVal0,
  589. ChannelVal1,
  590. ChannelTypeCount
  591. };
  592. float values[ChannelTypeCount];
  593. values[ChannelVal0] = 0.f;
  594. values[ChannelVal1] = 1.f;
  595. AZ::u8 channelIndics[channelCnt];
  596. for (AZ::u8 idx = 0; idx < channelCnt; idx++)
  597. {
  598. switch (channels[idx])
  599. {
  600. case 'a':
  601. channelIndics[idx] = ChannelA;
  602. break;
  603. case 'r':
  604. channelIndics[idx] = ChannelR;
  605. break;
  606. case 'g':
  607. channelIndics[idx] = ChannelG;
  608. break;
  609. case 'b':
  610. channelIndics[idx] = ChannelB;
  611. break;
  612. case '0':
  613. channelIndics[idx] = ChannelVal0;
  614. break;
  615. case '1':
  616. channelIndics[idx] = ChannelVal1;
  617. break;
  618. default:
  619. AZ_Assert(false, "%s function only works with channel name \"rgba01\"", __FUNCTION__);
  620. return;
  621. }
  622. }
  623. //create pixel operation function
  624. IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
  625. //get count of bytes per pixel
  626. uint32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
  627. const uint32 mips = (uint32)m_mips.size();
  628. for (uint32 mip = 0; mip < mips; ++mip)
  629. {
  630. uint8* pixelBuf = m_mips[mip]->m_pData;
  631. const uint32 pixelCount = GetPixelCount(mip);
  632. for (uint32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
  633. {
  634. pixelOp->GetRGBA(pixelBuf, values[ChannelR], values[ChannelG], values[ChannelB], values[ChannelA]);
  635. pixelOp->SetRGBA(pixelBuf, values[channelIndics[0]], values[channelIndics[1]],
  636. values[channelIndics[2]], values[channelIndics[3]]);
  637. }
  638. }
  639. }
  640. void CImageObject::GlossFromNormals(bool hasAuthoredGloss)
  641. {
  642. if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)))
  643. {
  644. AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
  645. return;
  646. }
  647. // Derive new roughness from normal variance to preserve the bumpiness of normal map mips and to reduce specular aliasing.
  648. // The derived roughness is combined with the artist authored roughness stored in the alpha channel of the normal map.
  649. // The algorithm is based on the Frequency Domain Normal Mapping implementation presented by Neubelt and Pettineo at Siggraph 2013.
  650. //create pixel operation function
  651. IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
  652. //get count of bytes per pixel
  653. AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
  654. const AZ::u32 mips = (AZ::u32)m_mips.size();
  655. float color[4];
  656. for (AZ::u32 mip = 0; mip < mips; ++mip)
  657. {
  658. AZ::u8* pixelBuf = m_mips[mip]->m_pData;
  659. const AZ::u32 pixelCount = GetPixelCount(mip);
  660. for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
  661. {
  662. pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
  663. // Get length of the averaged normal
  664. AZ::Vector3 normal(color[0] * 2.0f - 1.0f, color[1] * 2.0f - 1.0f, color[2] * 2.0f - 1.0f);
  665. float len = AZ::GetMax<float>(normal.GetLength(), 1.0f / (1 << 15));
  666. float authoredSmoothness = hasAuthoredGloss ? color[3] : 1.0f;
  667. float finalSmoothness = authoredSmoothness;
  668. if (len < 1.0f)
  669. {
  670. // Convert from smoothness to roughness (needs to match shader code)
  671. float authoredRoughness = (1.0f - authoredSmoothness) * (1.0f - authoredSmoothness);
  672. // Derive new roughness based on normal variance
  673. float kappa = (3.0f * len - len * len * len) / (1.0f - len * len);
  674. float variance = 1.0f / (2.0f * kappa);
  675. float finalRoughness = AZ::GetMin(sqrtf(authoredRoughness * authoredRoughness + variance), 1.0f);
  676. // Convert roughness back to smoothness
  677. finalSmoothness = 1.0f - sqrtf(finalRoughness);
  678. }
  679. pixelOp->SetRGBA(pixelBuf, color[0], color[1], color[2], finalSmoothness);
  680. }
  681. }
  682. }
  683. } // namespace ImageProcessingAtom