DdsLoader.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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 <AzCore/Debug/Trace.h>
  9. #include <AzCore/Math/MathUtils.h>
  10. #include <AzFramework/StringFunc/StringFunc.h>
  11. #include <ImageLoader/ImageLoaders.h>
  12. #include <Atom/ImageProcessing/ImageObject.h>
  13. #include <Processing/PixelFormatInfo.h>
  14. #include <Processing/DDSHeader.h>
  15. #include <Processing/ImageFlags.h>
  16. #include <QString>
  17. namespace ImageProcessingAtom
  18. {
  19. namespace DdsLoader
  20. {
  21. bool IsExtensionSupported(const char* extension)
  22. {
  23. QString ext = QString(extension).toLower();
  24. // This is the list of file extensions supported by this loader
  25. return ext == "dds";
  26. }
  27. // Create an image object from standard dds header
  28. IImageObject* CreateImageFromHeader(DDS_HEADER& header, DDS_HEADER_DXT10& exthead)
  29. {
  30. if ((header.dwCaps & DDS_SURFACE_FLAGS_TEXTURE) != DDS_SURFACE_FLAGS_TEXTURE ||
  31. (header.dwFlags & DDS_HEADER_FLAGS_TEXTURE) != DDS_HEADER_FLAGS_TEXTURE)
  32. {
  33. AZ_Error("Image Processing", false, "This dds file is not a valid texture");
  34. return nullptr;
  35. }
  36. EPixelFormat format = ePixelFormat_Unknown;
  37. uint32_t imageFlags = 0;
  38. uint32_t width = header.dwWidth;
  39. uint32_t height = header.dwHeight;
  40. uint32_t depth = AZStd::max(header.dwDepth, 1u);
  41. uint32_t mips = 1;
  42. if (header.dwFlags & DDS_HEADER_FLAGS_MIPMAP)
  43. {
  44. mips = header.dwMipMapCount;
  45. }
  46. if ((header.dwCaps & DDS_SURFACE_FLAGS_CUBEMAP) && (header.dwCaps_2 & DDS_CUBEMAP))
  47. {
  48. if ((header.dwCaps_2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
  49. {
  50. AZ_Error("Image Processing", false, "Only support cubemap in dds file with all faces");
  51. return nullptr;
  52. }
  53. imageFlags |= EIF_Cubemap;
  54. height *= 6;
  55. }
  56. else if (depth > 1)
  57. {
  58. AZ_Warning("Image Processing", header.dwCaps_2 & DDS_FLAGS_VOLUME,
  59. "Got a 3D image wih depth=%u, but it doesn't claim to be a volume texture. We'll treat it as volume texture.", depth);
  60. imageFlags |= EIF_Volumetexture;
  61. }
  62. // Get pixel format
  63. if (header.ddspf.dwFlags & DDS_FOURCC)
  64. {
  65. // dx10 formats
  66. if (header.ddspf.dwFourCC == FOURCC_DX10)
  67. {
  68. uint32_t dxgiFormat = exthead.dxgiFormat;
  69. //remove the SRGB from dxgi format and add sRGB to image flag
  70. if (dxgiFormat == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
  71. {
  72. dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
  73. }
  74. else if (dxgiFormat == DXGI_FORMAT_BC1_UNORM_SRGB)
  75. {
  76. dxgiFormat = DXGI_FORMAT_BC1_UNORM;
  77. }
  78. else if (dxgiFormat == DXGI_FORMAT_BC2_UNORM_SRGB)
  79. {
  80. dxgiFormat = DXGI_FORMAT_BC2_UNORM;
  81. }
  82. else if (dxgiFormat == DXGI_FORMAT_BC3_UNORM_SRGB)
  83. {
  84. dxgiFormat = DXGI_FORMAT_BC3_UNORM;
  85. }
  86. else if (dxgiFormat == DXGI_FORMAT_BC7_UNORM_SRGB)
  87. {
  88. dxgiFormat = DXGI_FORMAT_BC7_UNORM;
  89. }
  90. //add rgb flag if the dxgiformat was changed (which means it was sRGB format) above
  91. if (dxgiFormat != exthead.dxgiFormat)
  92. {
  93. imageFlags |= EIF_SRGBRead;
  94. }
  95. //check all the pixel formats and find matching one
  96. if (dxgiFormat != DXGI_FORMAT_UNKNOWN)
  97. {
  98. uint32_t i = 0;
  99. for (; i < ePixelFormat_Count; i++)
  100. {
  101. const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo((EPixelFormat)i);
  102. if (static_cast<uint32_t>(info->d3d10Format) == dxgiFormat)
  103. {
  104. format = (EPixelFormat)i;
  105. break;
  106. }
  107. }
  108. if (i == ePixelFormat_Count)
  109. {
  110. AZ_Error("Image Processing", false, "Unhandled d3d10 format: %d", dxgiFormat);
  111. return nullptr;
  112. }
  113. }
  114. }
  115. else if (header.ddspf.dwFourCC == FOURCC_DXT1)
  116. {
  117. format = ePixelFormat_BC1;
  118. }
  119. else if (header.ddspf.dwFourCC == FOURCC_DXT5)
  120. {
  121. format = ePixelFormat_BC3;
  122. }
  123. else if (header.ddspf.dwFourCC == FOURCC_3DCP)
  124. {
  125. format = ePixelFormat_BC4;
  126. }
  127. else if (header.ddspf.dwFourCC == FOURCC_3DC)
  128. {
  129. format = ePixelFormat_BC5;
  130. }
  131. else if (header.ddspf.dwFourCC == DDS_FOURCC_R32F)
  132. {
  133. format = ePixelFormat_R32F;
  134. }
  135. else if (header.ddspf.dwFourCC == DDS_FOURCC_G32R32F)
  136. {
  137. format = ePixelFormat_R32G32F;
  138. }
  139. else if (header.ddspf.dwFourCC == DDS_FOURCC_A32B32G32R32F)
  140. {
  141. format = ePixelFormat_R32G32B32A32F;
  142. }
  143. else if (header.ddspf.dwFourCC == DDS_FOURCC_R16F)
  144. {
  145. format = ePixelFormat_R16F;
  146. }
  147. else if (header.ddspf.dwFourCC == DDS_FOURCC_G16R16F)
  148. {
  149. format = ePixelFormat_R16G16F;
  150. }
  151. else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16F)
  152. {
  153. format = ePixelFormat_R16G16B16A16F;
  154. }
  155. else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16)
  156. {
  157. format = ePixelFormat_R16G16B16A16;
  158. }
  159. }
  160. else
  161. {
  162. if ((header.ddspf.dwFlags == DDS_RGBA || header.ddspf.dwFlags == DDS_RGB))
  163. {
  164. if (header.ddspf.dwRBitMask == 0x00ff0000 || header.ddspf.dwGBitMask == 0x00ff0000 || header.ddspf.dwBBitMask == 0x00ff0000)
  165. {
  166. format = (header.ddspf.dwRGBBitCount == 32) ? ePixelFormat_B8G8R8A8 : ePixelFormat_B8G8R8;
  167. }
  168. else if (header.ddspf.dwBBitMask == 0x00ff0000 || header.ddspf.dwGBitMask == 0x00ff0000 || header.ddspf.dwRBitMask == 0x00ff0000)
  169. {
  170. format = (header.ddspf.dwRGBBitCount == 32) ? ePixelFormat_R8G8B8A8 : ePixelFormat_R8G8B8;
  171. }
  172. }
  173. else if (header.ddspf.dwFlags == DDS_LUMINANCEA && header.ddspf.dwRGBBitCount == 8)
  174. {
  175. format = ePixelFormat_R8G8;
  176. }
  177. else if (header.ddspf.dwFlags == DDS_LUMINANCE && header.ddspf.dwRGBBitCount == 8)
  178. {
  179. format = ePixelFormat_A8;
  180. }
  181. else if ((header.ddspf.dwFlags == DDS_A || header.ddspf.dwFlags == DDS_A_ONLY || header.ddspf.dwFlags == (DDS_A | DDS_A_ONLY)) && header.ddspf.dwRGBBitCount == 8)
  182. {
  183. format = ePixelFormat_A8;
  184. }
  185. }
  186. if (format == ePixelFormat_Unknown)
  187. {
  188. AZ_Error("Image Processing", false, "Unhandled dds pixel format fourCC: %d, flags: %d",
  189. header.ddspf.dwFourCC, header.ddspf.dwFlags);
  190. return nullptr;
  191. }
  192. // Resize to block size for compressed format. This could happened to those 1x1 bc1 dds files
  193. // [GFX TODO] [ATOM-181] We may want to add padding support for bc formats so we can remove this temporary fix
  194. const PixelFormatInfo* const pFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(format);
  195. if (width < pFormatInfo->blockWidth && height < pFormatInfo->blockHeight)
  196. {
  197. width = pFormatInfo->blockWidth;
  198. height = pFormatInfo->blockHeight;
  199. }
  200. IImageObject* newImage = IImageObject::CreateImage(width, height, depth, mips, format);
  201. //set properties
  202. newImage->SetImageFlags(imageFlags);
  203. return newImage;
  204. }
  205. IImageObject* LoadImageFromFile(const AZStd::string& filename)
  206. {
  207. AZ::IO::SystemFileStream fileLoadStream(filename.c_str(), AZ::IO::OpenMode::ModeRead);
  208. if (!fileLoadStream.IsOpen())
  209. {
  210. AZ_Warning("Image Processing", false, "%s: failed to open file %s", __FUNCTION__, filename.c_str());
  211. return nullptr;
  212. }
  213. DDS_FILE_DESC desc;
  214. DDS_HEADER_DXT10 exthead;
  215. fileLoadStream.Read(sizeof(desc), &desc);
  216. if (desc.dwMagic != FOURCC_DDS || !desc.IsValid())
  217. {
  218. AZ_Error("Image Processing", false, "%s: Trying to load a none-DDS file", __FUNCTION__);
  219. return nullptr;
  220. }
  221. if (desc.header.IsDX10Ext())
  222. {
  223. fileLoadStream.Read(sizeof(exthead), &exthead);
  224. }
  225. IImageObject* outImage = CreateImageFromHeader(desc.header, exthead);
  226. if (outImage == nullptr)
  227. {
  228. return nullptr;
  229. }
  230. AZ::u32 faces = 1;
  231. if (outImage->HasImageFlags(EIF_Cubemap))
  232. {
  233. faces = 6;
  234. }
  235. for (AZ::u32 face = 0; face < faces; face++)
  236. {
  237. for (AZ::u32 mip = 0; mip < outImage->GetMipCount(); ++mip)
  238. {
  239. AZ::u32 pitch;
  240. AZ::u8* mem;
  241. outImage->GetImagePointer(mip, mem, pitch);
  242. AZ::u32 faceBufSize = outImage->GetMipBufSize(mip) / faces;
  243. if (fileLoadStream.GetLength() - fileLoadStream.GetCurPos() < faceBufSize)
  244. {
  245. delete outImage;
  246. AZ_Error("Image Processing", false, "DdsLoader: load mip data error");
  247. return nullptr;
  248. }
  249. fileLoadStream.Read(faceBufSize, mem + faceBufSize * face);
  250. }
  251. }
  252. return outImage;
  253. }
  254. }// namespace ImageDDS
  255. } //namespace ImageProcessingAtom