2
0

LoadTGA.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Image/LoadTGA.h>
  6. #include <Image/Surface.h>
  7. #pragma pack (1)
  8. struct TGAHeader
  9. {
  10. uint8 mIDLength;
  11. uint8 mColorMapType;
  12. uint8 mImageType;
  13. uint16 mColorMapFirstEntryIndex;
  14. uint16 mColorMapLength;
  15. uint8 mColorMapEntrySize;
  16. uint16 mXOrigin;
  17. uint16 mYOrigin;
  18. uint16 mWidth;
  19. uint16 mHeight;
  20. uint8 mPixelDepth;
  21. uint8 mImageDescriptor;
  22. };
  23. #pragma pack ()
  24. Ref<Surface> LoadTGA(istream &inStream)
  25. {
  26. bool loaded = true;
  27. // Read header
  28. TGAHeader header;
  29. inStream.read((char *)&header, sizeof(header));
  30. if (inStream.fail())
  31. return nullptr;
  32. // Get properties
  33. int bytes_per_pixel = (header.mPixelDepth + 7) >> 3;
  34. int scan_width = bytes_per_pixel * header.mWidth;
  35. // Check type
  36. if (header.mImageType < 1 || header.mImageType > 2)
  37. {
  38. Trace("Not a readable TGA");
  39. return nullptr;
  40. }
  41. // Check compression
  42. if ((header.mImageType == 1 && header.mColorMapType != 1) || (header.mImageType == 2 && header.mColorMapType != 0))
  43. {
  44. Trace("Not an uncompressed TGA");
  45. return nullptr;
  46. }
  47. Ref<Surface> surface;
  48. if (header.mPixelDepth == 8)
  49. {
  50. // Determine pixel format
  51. ESurfaceFormat format;
  52. int pixel_size;
  53. switch (header.mColorMapEntrySize)
  54. {
  55. case 15: format = ESurfaceFormat::X1R5G5B5; pixel_size = 2; break;
  56. case 16: format = ESurfaceFormat::X1R5G5B5; pixel_size = 2; break;
  57. case 24: format = ESurfaceFormat::R8G8B8; pixel_size = 3; break;
  58. case 32: format = ESurfaceFormat::A8R8G8B8; pixel_size = 4; break;
  59. default: Trace("Has invalid format"); return nullptr;
  60. }
  61. // Seek to beginning of palette
  62. inStream.seekg(sizeof(TGAHeader) + header.mIDLength);
  63. // Load palette
  64. int pal_bytes = pixel_size * header.mColorMapLength;
  65. uint8 *palette = new uint8 [pal_bytes];
  66. inStream.read((char *)palette, pal_bytes);
  67. loaded = loaded && !inStream.fail();
  68. // Convert pixel data to a surface
  69. surface = new SoftwareSurface(header.mWidth, header.mHeight, format);
  70. surface->Lock(ESurfaceLockMode::Write);
  71. uint8 *scan_line = new uint8 [scan_width];
  72. for (int y = header.mHeight - 1; y >= 0; --y)
  73. {
  74. // Load one scan line
  75. inStream.read((char *)scan_line, scan_width);
  76. loaded = loaded && !inStream.fail();
  77. // Copy one scan line
  78. uint8 *in_pixel = scan_line;
  79. uint8 *out_pixel = (uint8 *)surface->GetScanLine(y);
  80. for (int x = 0; x < header.mWidth; ++x, ++in_pixel, out_pixel += pixel_size)
  81. memcpy(out_pixel, palette + (*in_pixel - header.mColorMapFirstEntryIndex) * pixel_size, pixel_size);
  82. }
  83. surface->UnLock();
  84. // Release temporaries
  85. delete [] palette;
  86. delete [] scan_line;
  87. }
  88. else
  89. {
  90. // Determine pixel format
  91. ESurfaceFormat format;
  92. switch (header.mPixelDepth)
  93. {
  94. case 15: format = ESurfaceFormat::X1R5G5B5; break;
  95. case 16: format = ESurfaceFormat::X1R5G5B5; break;
  96. case 24: format = ESurfaceFormat::R8G8B8; break;
  97. case 32: format = ESurfaceFormat::A8R8G8B8; break;
  98. default: Trace("Invalid format"); return nullptr;
  99. }
  100. // Convert pixel data to a surface
  101. surface = new SoftwareSurface(header.mWidth, header.mHeight, format, scan_width);
  102. surface->Lock(ESurfaceLockMode::Write);
  103. for (int y = header.mHeight - 1; y >= 0; --y)
  104. {
  105. inStream.read((char *)surface->GetScanLine(y), scan_width);
  106. loaded = loaded && !inStream.fail();
  107. }
  108. surface->UnLock();
  109. }
  110. return loaded? surface : Ref<Surface>(nullptr);
  111. }