TgaLoader.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 <AzCore/Casting/numeric_cast.h>
  11. #include <ImageLoader/ImageLoaders.h>
  12. #include <Atom/ImageProcessing/ImageObject.h>
  13. #include <Processing/PixelFormatInfo.h>
  14. #include <QString>
  15. namespace ImageProcessingAtom
  16. {
  17. namespace TgaLoader
  18. {
  19. bool IsExtensionSupported(const char* extension)
  20. {
  21. // This is the list of file extensions supported by this loader
  22. QString ext = QString(extension).toLower();
  23. return ext == "tga";
  24. }
  25. // https://www.opennet.ru/docs/formats/targa.pdf
  26. // http://www.paulbourke.net/dataformats/tga/
  27. // https://forum.qt.io/topic/74712/qimage-from-tga-with-alpha/11
  28. // https://forum.qt.io/topic/101971/qimage-and-tga-support-in-c/5
  29. enum class ImageTypeCode : uint32_t
  30. {
  31. // No image data included.
  32. NoImageData = 0,
  33. // Uncompressed, color-mapped images.
  34. ColorMapped = 1,
  35. // Uncompressed, RGB images.
  36. RGB = 2,
  37. // Uncompressed, black and white images.
  38. BlackAndWhite = 3,
  39. // Runlength encoded color-mapped images.
  40. ColorMappedRLE = 9,
  41. // Runlength encoded RGB images.
  42. RGBRLE = 10,
  43. // Runlength encoded black and white images.
  44. BlackAndWhiteRLE = 11
  45. };
  46. enum class ImagePixelSize : uint32_t
  47. {
  48. Targa8 = 8,
  49. Targa16 = 16,
  50. Targa24 = 24,
  51. Targa32 = 32,
  52. };
  53. enum class ImageOrigin : uint32_t
  54. {
  55. BottomLeft = 0,
  56. BottomRight = 1,
  57. TopLeft = 2,
  58. TopRight = 3
  59. };
  60. const static uint32_t TgaHeaderSize = 18;
  61. struct TgaHeader
  62. {
  63. // Note: the layout and bits size defined in this structure strictly match tga header.
  64. uint64_t m_idLength : 8; // Identification size
  65. uint64_t m_colorMapType : 8;
  66. uint64_t m_dataTypeCode : 8;
  67. // Color Map Specification.
  68. uint64_t m_colorMapOrigin : 16;
  69. uint64_t m_colorMapLength : 16; // total number of color map entries
  70. uint64_t m_colorMapEntrySize : 8; // bits per color map entry
  71. // Image Specification.
  72. uint64_t m_xOrigin : 16;
  73. uint64_t m_yOrigin : 16;
  74. uint64_t m_width : 16;
  75. uint64_t m_height : 16;
  76. uint32_t m_bitsPerPixel : 8; // bits per pixel of the image data
  77. uint32_t m_imageDescriptor : 8;
  78. uint32_t GetBitsPerImageData() const
  79. {
  80. return aznumeric_cast<uint32_t>(m_bitsPerPixel);
  81. }
  82. uint32_t GetBytesPerImageData() const
  83. {
  84. return GetBitsPerImageData() / 8;
  85. }
  86. uint32_t GetImageBytesSize() const
  87. {
  88. uint32_t bytesPerPixel = GetBytesPerImageData();
  89. uint32_t width = aznumeric_cast<uint32_t>(m_width);
  90. uint32_t height = aznumeric_cast<uint32_t>(m_height);
  91. uint32_t imageBytesSize = width * height * bytesPerPixel;
  92. return imageBytesSize;
  93. }
  94. uint32_t GetColorMapBytesSize() const
  95. {
  96. uint32_t entryCount = aznumeric_cast<uint32_t>(m_colorMapLength);
  97. uint32_t entrySize = aznumeric_cast<uint32_t>(m_colorMapEntrySize)/ 8;
  98. return entryCount * entrySize;
  99. }
  100. uint32_t GetBitsPerPixel() const
  101. {
  102. if (m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMapped) || m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMappedRLE))
  103. {
  104. return m_colorMapEntrySize;
  105. }
  106. return m_bitsPerPixel;
  107. }
  108. ImageOrigin GetImageOrigin() const
  109. {
  110. return (ImageOrigin)((m_imageDescriptor & 0x30)>>4);
  111. }
  112. };
  113. static_assert(sizeof(TgaHeader) >= TgaHeaderSize, "TgaHeader structure's size should be at least 18 bytes");
  114. IImageObject* CreateNewImage(const TgaHeader& tgaHeader, const AZStd::vector<uint8>& imageData, const AZStd::vector<uint8>& colorMapData);
  115. // Read image data
  116. bool ReadImageData(const TgaHeader& tgaHeader, AZ::IO::SystemFileStream& imageFileStream, AZStd::vector<uint8>& imageData)
  117. {
  118. uint32_t bytesPerImagePixel = tgaHeader.GetBytesPerImageData();
  119. uint32_t imageBytesSize = tgaHeader.GetImageBytesSize();
  120. bool isRLE = tgaHeader.m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::RGBRLE) || tgaHeader.m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMappedRLE);
  121. imageData.resize_no_construct(imageBytesSize);
  122. if (!isRLE)
  123. {
  124. if (imageFileStream.Read(imageBytesSize, imageData.data()) != imageBytesSize)
  125. {
  126. return false;
  127. }
  128. }
  129. else
  130. {
  131. uint8 chunkHeader;
  132. uint8 byteData[5];
  133. uint32_t imageDataOffset = 0;
  134. do
  135. {
  136. if (imageFileStream.Read(sizeof(chunkHeader), reinterpret_cast<char*>(&chunkHeader)) != sizeof(chunkHeader))
  137. {
  138. return false;
  139. }
  140. if (chunkHeader >> 7) // repeat count
  141. {
  142. // the lower 7 bits is the repeat count - 1
  143. uint8 repeatCount = (chunkHeader&0x7f) + 1;
  144. // The followed data is repeating repeatCount times
  145. if (imageFileStream.Read(bytesPerImagePixel, reinterpret_cast<char*>(byteData)) != bytesPerImagePixel)
  146. {
  147. return false;
  148. }
  149. for (uint8 count = 0; count < repeatCount; count++)
  150. {
  151. memcpy(imageData.data() + imageDataOffset, byteData, bytesPerImagePixel);
  152. imageDataOffset += bytesPerImagePixel;
  153. }
  154. }
  155. else // data count
  156. {
  157. uint8 dataCount = chunkHeader + 1;
  158. // The followed number of data need to be read.
  159. uint32_t readDataBytes = bytesPerImagePixel * dataCount;
  160. if (imageFileStream.Read(readDataBytes, imageData.data() + imageDataOffset) != readDataBytes)
  161. {
  162. return false;
  163. }
  164. imageDataOffset += readDataBytes;
  165. }
  166. } while (imageDataOffset < imageBytesSize);
  167. }
  168. return true;
  169. }
  170. IImageObject* LoadImageFromFile(const AZStd::string& filename)
  171. {
  172. // open the file
  173. AZ::IO::SystemFileStream imageFileStream(filename.c_str(), AZ::IO::OpenMode::ModeRead);
  174. if (!imageFileStream.IsOpen())
  175. {
  176. AZ_Warning("Image Processing", false, "TgaLoader: failed to open file %s", filename.c_str());
  177. return nullptr;
  178. }
  179. // read in the header
  180. TgaHeader tgaHeader;
  181. if (imageFileStream.Read(TgaHeaderSize, &tgaHeader) != TgaHeaderSize)
  182. {
  183. AZ_Warning("Image Processing", false, "TgaLoader: failed to read file header %s", filename.c_str());
  184. return nullptr;
  185. }
  186. // only support rgb or colormapped formats
  187. if (tgaHeader.m_dataTypeCode != static_cast<uint64_t>(ImageTypeCode::ColorMapped)
  188. && tgaHeader.m_dataTypeCode != static_cast<uint64_t>(ImageTypeCode::RGB)
  189. && tgaHeader.m_dataTypeCode != static_cast<uint64_t>(ImageTypeCode::ColorMappedRLE)
  190. && tgaHeader.m_dataTypeCode != static_cast<uint64_t>(ImageTypeCode::RGBRLE))
  191. {
  192. AZ_Warning("Image Processing", false, "TgaLoader: unsupported type code [%u] of TGA file %s. Only support RGB(RLE) or color mapped (RLE) tga images",
  193. tgaHeader.m_dataTypeCode, filename.c_str());
  194. return nullptr;
  195. }
  196. // only support 24bits or 32 bits pixel format
  197. uint32_t pixelBits = tgaHeader.GetBitsPerPixel();
  198. if (pixelBits != static_cast<uint32_t>(ImagePixelSize::Targa24) && pixelBits != static_cast<uint32_t>(ImagePixelSize::Targa32))
  199. {
  200. AZ_Warning("Image Processing", false, "TgaLoader: unsupported pixel size [%u] of TGA file %s. Only support 24bits or 32bits color",
  201. pixelBits, filename.c_str());
  202. return nullptr;
  203. }
  204. // validate image data pixel size for color mapped
  205. if ( (tgaHeader.m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMapped) || tgaHeader.m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMappedRLE))
  206. && tgaHeader.GetBytesPerImageData() > 2)
  207. {
  208. AZ_Warning("Image Processing", false, "TgaLoader: invalid image pixel size [%u] for color mapped image of TGA file %s. It should be 1 or 2",
  209. tgaHeader.GetBytesPerImageData(), filename.c_str());
  210. return nullptr;
  211. }
  212. // Skip image identification data if there are any
  213. imageFileStream.Seek(tgaHeader.m_idLength, AZ::IO::GenericStream::SeekMode::ST_SEEK_CUR);
  214. // Read color map if there is one
  215. AZStd::vector<uint8> colorMap;
  216. if (tgaHeader.m_colorMapType != 0) // color map exists
  217. {
  218. uint32_t colorMapSize = tgaHeader.GetColorMapBytesSize();
  219. colorMap.resize_no_construct(colorMapSize);
  220. imageFileStream.Read(colorMapSize, colorMap.data());
  221. }
  222. // Read image data
  223. AZStd::vector<uint8> imageData;
  224. bool success = ReadImageData(tgaHeader, imageFileStream, imageData);
  225. if (!success)
  226. {
  227. AZ_Warning("Image Processing", false, "TgaLoader: failed to read read image data from file %s", filename.c_str());
  228. return nullptr;
  229. }
  230. return CreateNewImage(tgaHeader, imageData, colorMap);
  231. }
  232. IImageObject* CreateNewImage(const TgaHeader& tgaHeader, const AZStd::vector<uint8>& imageData, const AZStd::vector<uint8>& colorMapData)
  233. {
  234. uint32_t bytesPerPixel = tgaHeader.GetBitsPerPixel()/8;
  235. EPixelFormat pixelFormat = ePixelFormat_R8G8B8A8;
  236. if (bytesPerPixel == 3)
  237. {
  238. pixelFormat = ePixelFormat_R8G8B8;
  239. }
  240. IImageObject* pImage = IImageObject::CreateImage(tgaHeader.m_width, tgaHeader.m_height, 1, pixelFormat);
  241. // copy data from pixelContent to the ImageObject
  242. uint8* pDst;
  243. uint32 dwPitch;
  244. pImage->GetImagePointer(0, pDst, dwPitch);
  245. const uint32 pixelCount = pImage->GetPixelCount(0);
  246. bool useColorMap = tgaHeader.m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMapped) || tgaHeader.m_dataTypeCode == static_cast<uint64_t>(ImageTypeCode::ColorMappedRLE);
  247. ImageOrigin imageOrigin = tgaHeader.GetImageOrigin();
  248. // lambda function to find the index of image data based the origin
  249. auto GetImagePixelIndex = [tgaHeader] (ImageOrigin imageOrigin, uint32 dstIndex)
  250. {
  251. uint32 reIndex = dstIndex;
  252. uint32 imageWidth = aznumeric_cast<uint32>(tgaHeader.m_width);
  253. uint32 imageHeight = aznumeric_cast<uint32>(tgaHeader.m_height);
  254. switch (imageOrigin)
  255. {
  256. case ImageOrigin::BottomLeft:
  257. {
  258. auto x = dstIndex % imageWidth;
  259. auto y = imageHeight - 1 - dstIndex/imageWidth;
  260. reIndex = y * imageWidth + x;
  261. break;
  262. }
  263. case ImageOrigin::BottomRight:
  264. {
  265. auto x = imageWidth - 1 - dstIndex % imageWidth;
  266. auto y = imageHeight - 1 - dstIndex/imageWidth;
  267. reIndex = y * imageWidth + x;
  268. break;
  269. }
  270. case ImageOrigin::TopRight:
  271. {
  272. auto x = imageWidth - 1 - dstIndex % imageWidth;
  273. auto y = dstIndex/imageWidth;
  274. reIndex = y * imageWidth + x;
  275. break;
  276. }
  277. }
  278. return reIndex;
  279. };
  280. for (uint32 i = 0; i < pixelCount; ++i)
  281. {
  282. uint32 srcIndex = GetImagePixelIndex(imageOrigin, i);
  283. if (useColorMap)
  284. {
  285. uint32_t colorMapIndex;
  286. if (tgaHeader.GetBytesPerImageData() == 1)
  287. {
  288. colorMapIndex = imageData[srcIndex];
  289. }
  290. else
  291. { // 2 bytes
  292. colorMapIndex = ((uint16*)imageData.data())[srcIndex];
  293. }
  294. memcpy(pDst + i*bytesPerPixel, colorMapData.data() + colorMapIndex*bytesPerPixel, bytesPerPixel);
  295. }
  296. else
  297. {
  298. memcpy(pDst + i*bytesPerPixel, imageData.data() + srcIndex*bytesPerPixel, bytesPerPixel);
  299. }
  300. // swap R and B
  301. AZStd::swap(pDst[i*bytesPerPixel], pDst[i*bytesPerPixel + 2]);
  302. }
  303. return pImage;
  304. }
  305. }// namespace TgaLoader
  306. } //namespace ImageProcessingAtom