CTSquisher.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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 <Atom/ImageProcessing/ImageObject.h>
  9. #include <Processing/ImageToProcess.h>
  10. #include <Processing/PixelFormatInfo.h>
  11. #include <Compressors/CTSquisher.h>
  12. namespace ImageProcessingAtom
  13. {
  14. struct CrySquisherCallbackUserData
  15. {
  16. IImageObjectPtr m_pImageObject;
  17. AZ::u8* m_dstMem;
  18. AZ::u32 m_dstOffset;
  19. };
  20. // callbacks for the CryTextureSquisher
  21. void CrySquisherOutputCallback(const CryTextureSquisher::CompressorParameters& compress, const void* data, AZ::u32 size, AZ::u32 oy, AZ::u32 ox)
  22. {
  23. CrySquisherCallbackUserData* const pUserData = (CrySquisherCallbackUserData*)compress.userPtr;
  24. AZ::u32 stride = (compress.width + 3) >> 2;
  25. AZ::u32 blocks = (compress.height + 3) >> 2;
  26. memcpy(pUserData->m_dstMem + size * (stride * oy + ox), data, size);
  27. pUserData->m_dstOffset = size * (stride * blocks);
  28. }
  29. void CrySquisherInputCallback(const CryTextureSquisher::DecompressorParameters& decompress, void* data, AZ::u32 size, AZ::u32 oy, AZ::u32 ox)
  30. {
  31. CrySquisherCallbackUserData* const pUserData = (CrySquisherCallbackUserData*)decompress.userPtr;
  32. AZ::u32 stride = (decompress.width + 3) >> 2;
  33. AZ::u32 blocks = (decompress.height + 3) >> 2;
  34. //assert(CPixelFormats::GetPixelFormatInfo(pUserData->m_pImageObject->GetPixelFormat())->bCompressed);
  35. memcpy(data, pUserData->m_dstMem + size * (stride * oy + ox), size);
  36. pUserData->m_dstOffset = size * (stride * blocks);
  37. }
  38. CryTextureSquisher::ECodingPreset CTSquisher::GetCompressPreset(EPixelFormat compressFmt, EPixelFormat uncompressFmt)
  39. {
  40. CryTextureSquisher::ECodingPreset preset = CryTextureSquisher::eCompressorPreset_Num;
  41. switch (compressFmt)
  42. {
  43. case ePixelFormat_BC1:
  44. preset = CryTextureSquisher::eCompressorPreset_BC1U;
  45. break;
  46. case ePixelFormat_BC1a:
  47. preset = CryTextureSquisher::eCompressorPreset_BC1Ua;
  48. break;
  49. case ePixelFormat_BC3:
  50. preset = CryTextureSquisher::eCompressorPreset_BC3U;
  51. break;
  52. case ePixelFormat_BC3t:
  53. preset = CryTextureSquisher::eCompressorPreset_BC3Ut;
  54. break;
  55. case ePixelFormat_BC4:
  56. preset = (CPixelFormats::GetInstance().IsFormatSingleChannel(uncompressFmt)
  57. ? CryTextureSquisher::eCompressorPreset_BC4Ua // a-channel
  58. : CryTextureSquisher::eCompressorPreset_BC4U); // r-channel
  59. break;
  60. case ePixelFormat_BC4s:
  61. preset = (CPixelFormats::GetInstance().IsFormatSingleChannel(uncompressFmt)
  62. ? CryTextureSquisher::eCompressorPreset_BC4Sa // a-channel
  63. : CryTextureSquisher::eCompressorPreset_BC4S); // r-channel
  64. break;
  65. case ePixelFormat_BC5:
  66. preset = CryTextureSquisher::eCompressorPreset_BC5Un;
  67. break;
  68. case ePixelFormat_BC5s:
  69. preset = CryTextureSquisher::eCompressorPreset_BC5Sn;
  70. break;
  71. case ePixelFormat_BC6UH:
  72. preset = CryTextureSquisher::eCompressorPreset_BC6UH;
  73. break;
  74. case ePixelFormat_BC7:
  75. preset = CryTextureSquisher::eCompressorPreset_BC7U;
  76. break;
  77. case ePixelFormat_BC7t:
  78. preset = CryTextureSquisher::eCompressorPreset_BC7Ut;
  79. break;
  80. default:
  81. AZ_Assert(false, "%s: Unexpected pixel format (in compressing an image). Inform an RC programmer.", __FUNCTION__);
  82. }
  83. return preset;
  84. }
  85. bool CTSquisher::IsCompressedPixelFormatSupported(EPixelFormat fmt)
  86. {
  87. switch (fmt)
  88. {
  89. case ePixelFormat_BC1:
  90. case ePixelFormat_BC1a:
  91. case ePixelFormat_BC3:
  92. case ePixelFormat_BC3t:
  93. case ePixelFormat_BC4:
  94. case ePixelFormat_BC4s:
  95. case ePixelFormat_BC5:
  96. case ePixelFormat_BC5s:
  97. case ePixelFormat_BC6UH:
  98. case ePixelFormat_BC7:
  99. case ePixelFormat_BC7t:
  100. return true;
  101. default:
  102. return false;
  103. }
  104. }
  105. bool CTSquisher::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
  106. {
  107. switch (fmt)
  108. {
  109. case ePixelFormat_R8:
  110. case ePixelFormat_A8:
  111. case ePixelFormat_R8G8B8A8:
  112. case ePixelFormat_R8G8B8X8:
  113. case ePixelFormat_R32F:
  114. case ePixelFormat_R32G32B32A32F:
  115. return true;
  116. default:
  117. return false;
  118. }
  119. }
  120. bool CTSquisher::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
  121. {
  122. return true;
  123. }
  124. ColorSpace CTSquisher::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const
  125. {
  126. return ColorSpace::autoSelect;
  127. }
  128. const char* CTSquisher::GetName() const
  129. {
  130. return "CTSquisher";
  131. }
  132. EPixelFormat CTSquisher::GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const
  133. {
  134. //special cases
  135. if (compressedfmt == ePixelFormat_BC6UH || compressedfmt == ePixelFormat_BC5 || compressedfmt == ePixelFormat_BC5s)
  136. {
  137. return ePixelFormat_R32G32B32A32F;
  138. }
  139. if (IsUncompressedPixelFormatSupported(uncompressedfmt))
  140. {
  141. return uncompressedfmt;
  142. }
  143. //for fmt dont support, convert to supported uncompressed formats: ePixelFormat_A8, ePixelFormat_R8, ePixelFormat_A8R8G8B8,
  144. // ePixelFormat_X8R8G8B8, ePixelFormat_R32F, ePixelFormat_A32B32G32R32F
  145. switch (uncompressedfmt)
  146. {
  147. case ePixelFormat_R8G8:
  148. case ePixelFormat_R16G16:
  149. case ePixelFormat_R8G8B8:
  150. case ePixelFormat_B8G8R8:
  151. return ePixelFormat_R8G8B8X8;
  152. case ePixelFormat_R16:
  153. return ePixelFormat_R8;
  154. case ePixelFormat_R16G16B16A16:
  155. case ePixelFormat_B8G8R8A8:
  156. return ePixelFormat_R8G8B8A8;
  157. case ePixelFormat_R9G9B9E5:
  158. case ePixelFormat_R32G32F:
  159. case ePixelFormat_R16G16B16A16F:
  160. case ePixelFormat_R16G16F:
  161. return ePixelFormat_R32G32B32A32F;
  162. case ePixelFormat_R16F:
  163. return ePixelFormat_R32F;
  164. default:
  165. //this shouldn't happen. but we could handle it with uncompressed data anyway
  166. if (CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(uncompressedfmt))
  167. {
  168. return ePixelFormat_R8G8B8X8;
  169. }
  170. else
  171. {
  172. return ePixelFormat_R8G8B8A8;
  173. }
  174. }
  175. }
  176. IImageObjectPtr CTSquisher::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const
  177. {
  178. // Decompressing
  179. // the output pixel format could only have one channel or four channels. need to find out more
  180. EPixelFormat fmtSrc = srcImage->GetPixelFormat();
  181. //src format need to be compressed and dst format need to uncompressed.
  182. if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst))
  183. {
  184. return nullptr;
  185. }
  186. IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
  187. //clear the dstImage to (0, 0, 0, 1) since some compression format only write to certain channels
  188. dstImage->ClearColor(0, 0, 0, 1);
  189. //for each mipmap
  190. const AZ::u32 mipCount = srcImage->GetMipCount();
  191. for (AZ::u32 dwMip = 0; dwMip < mipCount; ++dwMip)
  192. {
  193. const AZ::u32 dwLocalWidth = srcImage->GetWidth(dwMip);
  194. const AZ::u32 dwLocalHeight = srcImage->GetHeight(dwMip);
  195. AZ::u8* pSrcMem;
  196. AZ::u32 dwSrcPitch;
  197. srcImage->GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
  198. AZ::u8* pDstMem;
  199. AZ::u32 dwDstPitch;
  200. dstImage->GetImagePointer(dwMip, pDstMem, dwDstPitch);
  201. CrySquisherCallbackUserData userData;
  202. userData.m_pImageObject = srcImage;
  203. userData.m_dstOffset = 0;
  204. userData.m_dstMem = pSrcMem;
  205. CryTextureSquisher::DecompressorParameters decompress;
  206. decompress.dstBuffer = pDstMem;
  207. decompress.width = dwLocalWidth;
  208. decompress.height = dwLocalHeight;
  209. decompress.pitch = dwDstPitch;
  210. decompress.dstType = (CPixelFormats::GetInstance().IsFormatFloatingPoint(fmtDst, true) ?
  211. CryTextureSquisher::eBufferType_ufloat : CryTextureSquisher::eBufferType_uint8);
  212. if (CPixelFormats::GetInstance().IsFormatSigned(fmtSrc))
  213. {
  214. decompress.dstType = (decompress.dstType == CryTextureSquisher::eBufferType_ufloat ?
  215. CryTextureSquisher::eBufferType_sfloat : CryTextureSquisher::eBufferType_sint8);
  216. }
  217. decompress.userPtr = &userData;
  218. decompress.userInputFunction = CrySquisherInputCallback;
  219. decompress.preset = GetCompressPreset(fmtSrc, fmtDst);
  220. CryTextureSquisher::Decompress(decompress);
  221. }
  222. // CTsquish operates on native normal vectors when floating-point
  223. // buffers are used. Apply bias and scale when returning a normal-map.
  224. if (fmtSrc == ePixelFormat_BC5 || fmtSrc == ePixelFormat_BC5s)
  225. {
  226. if (fmtDst == ePixelFormat_R32G32B32A32F)
  227. {
  228. //conver from [-1, 1] to [0, 1]. And set alpha to 1.
  229. dstImage->ScaleAndBiasChannels(0, 100,
  230. AZ::Vector4(0.5f, 0.5f, 0.5f, 0.0f),
  231. AZ::Vector4(0.5f, 0.5f, 0.5f, 1.0f));
  232. }
  233. }
  234. return dstImage;
  235. }
  236. ///////////////////////////////////////////////////////////////////////////////////
  237. IImageObjectPtr CTSquisher::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst,
  238. const CompressOption* compressOption) const
  239. {
  240. // Compressing
  241. EPixelFormat fmtSrc = srcImage->GetPixelFormat();
  242. //src format need to be uncompressed and dst format need to compressed.
  243. if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
  244. {
  245. return nullptr;
  246. }
  247. IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
  248. //passing compress option
  249. ICompressor::EQuality quality = ICompressor::eQuality_Normal;
  250. AZ::Vector3 weights = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
  251. if (compressOption)
  252. {
  253. quality = compressOption->compressQuality;
  254. weights = compressOption->rgbWeight;
  255. }
  256. //do some clamp for float
  257. if (fmtSrc == ePixelFormat_R32G32B32A32F)
  258. {
  259. const uint32 nMips = srcImage->GetMipCount();
  260. // NOTES:
  261. // - all incoming images are unsigned, even normal maps
  262. // - all mipmaps of incoming images can contain out-of-range values from mipmap filtering
  263. // - 3Dc/BC5 is synonymous with "is a normal map" because they are not tagged explicitly as such
  264. if (fmtDst == ePixelFormat_BC5 || fmtDst == ePixelFormat_BC5s)
  265. {
  266. srcImage->ScaleAndBiasChannels(0, nMips,
  267. AZ::Vector4(2.0f, 2.0f, 2.0f, 1.0f),
  268. AZ::Vector4(-1.0f, -1.0f, -1.0f, 0.0f));
  269. srcImage->ClampChannels(0, nMips,
  270. AZ::Vector4(-1.0f, -1.0f, -1.0f, -1.0f),
  271. AZ::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  272. }
  273. else if (fmtDst == ePixelFormat_BC6UH)
  274. {
  275. srcImage->ClampChannels(0, nMips,
  276. AZ::Vector4(0.0f, 0.0f, 0.0f, 0.0f),
  277. AZ::Vector4(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX));
  278. }
  279. else
  280. {
  281. srcImage->ClampChannels(0, nMips,
  282. AZ::Vector4(0.0f, 0.0f, 0.0f, 0.0f),
  283. AZ::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  284. }
  285. }
  286. const uint32 mipCount = dstImage->GetMipCount();
  287. for (uint32 dwMip = 0; dwMip < mipCount; ++dwMip)
  288. {
  289. uint32 dwLocalWidth = srcImage->GetWidth(dwMip);
  290. uint32 dwLocalHeight = srcImage->GetHeight(dwMip);
  291. uint8* pSrcMem;
  292. uint32 dwSrcPitch;
  293. srcImage->GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
  294. uint8* pDstMem;
  295. uint32 dwDstPitch;
  296. dstImage->GetImagePointer(dwMip, pDstMem, dwDstPitch);
  297. {
  298. CrySquisherCallbackUserData userData;
  299. userData.m_pImageObject = dstImage;
  300. userData.m_dstOffset = 0;
  301. userData.m_dstMem = pDstMem;
  302. CryTextureSquisher::CompressorParameters compress;
  303. compress.srcBuffer = pSrcMem;
  304. compress.width = dwLocalWidth;
  305. compress.height = dwLocalHeight;
  306. compress.pitch = dwSrcPitch;
  307. compress.srcType = (CPixelFormats::GetInstance().IsFormatFloatingPoint(fmtSrc, true) ?
  308. CryTextureSquisher::eBufferType_ufloat : CryTextureSquisher::eBufferType_uint8);
  309. if (CPixelFormats::GetInstance().IsFormatSigned(fmtDst))
  310. {
  311. compress.srcType = (compress.srcType == CryTextureSquisher::eBufferType_ufloat ?
  312. CryTextureSquisher::eBufferType_sfloat : CryTextureSquisher::eBufferType_sint8);
  313. }
  314. const AZ::Vector3 uniform = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
  315. compress.weights[0] = weights.GetX();
  316. compress.weights[1] = weights.GetY();
  317. compress.weights[2] = weights.GetZ();
  318. compress.perceptual =
  319. (compress.weights[0] != uniform.GetX()) ||
  320. (compress.weights[1] != uniform.GetY()) ||
  321. (compress.weights[2] != uniform.GetZ());
  322. compress.quality =
  323. (quality == eQuality_Preview ? CryTextureSquisher::eQualityProfile_Low :
  324. (quality == eQuality_Fast ? CryTextureSquisher::eQualityProfile_Low :
  325. (quality == eQuality_Slow ? CryTextureSquisher::eQualityProfile_High :
  326. CryTextureSquisher::eQualityProfile_Medium)));
  327. compress.userPtr = &userData;
  328. compress.userOutputFunction = CrySquisherOutputCallback;
  329. compress.preset = GetCompressPreset(fmtDst, fmtSrc);
  330. CryTextureSquisher::Compress(compress);
  331. }
  332. } // for: all mips
  333. return dstImage;
  334. }
  335. }; //namespace ImageProcessingAtom