💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
4.9 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "resource-loader.hpp"
  20. #include "stb/stb_image.h"
  21. #include "resources/image.hpp"
  22. #include <cstring>
  23. #include <stdexcept>
  24. #include <physfs.h>
  25. #include <tinyexr.h>
  26. template <>
  27. image* resource_loader<image>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
  28. {
  29. unsigned char* buffer;
  30. int size;
  31. ::image* image = nullptr;
  32. // Read input stream into buffer
  33. size = static_cast<int>(PHYSFS_fileLength(file));
  34. buffer = new unsigned char[size];
  35. PHYSFS_readBytes(file, buffer, size);
  36. // Select loader according to file extension
  37. if (path.extension() == ".exr")
  38. {
  39. // Load OpenEXR with TinyEXR
  40. int status = TINYEXR_SUCCESS;
  41. const char* error = nullptr;
  42. // Read EXR version
  43. EXRVersion exr_version;
  44. status = ParseEXRVersionFromMemory(&exr_version, buffer, size);
  45. if (status != TINYEXR_SUCCESS)
  46. {
  47. delete[] buffer;
  48. throw std::runtime_error("TinyEXR parse version error (" + std::to_string(status) + "): invalid EXR file");
  49. }
  50. // Check if image is multipart
  51. if (exr_version.multipart)
  52. {
  53. throw std::runtime_error("OpenEXR multipart images not supported");
  54. }
  55. // Read EXR header
  56. EXRHeader exr_header;
  57. InitEXRHeader(&exr_header);
  58. status = ParseEXRHeaderFromMemory(&exr_header, &exr_version, buffer, size, &error);
  59. if (status != TINYEXR_SUCCESS)
  60. {
  61. std::string error_string(error);
  62. FreeEXRErrorMessage(error);
  63. delete[] buffer;
  64. throw std::runtime_error("TinyEXR parse header error (" + std::to_string(status) + "): " + error_string);
  65. }
  66. // Check if image is tiled
  67. if (exr_header.tiled)
  68. {
  69. FreeEXRHeader(&exr_header);
  70. delete[] buffer;
  71. throw std::runtime_error("OpenEXR tiled images not supported");
  72. }
  73. // Read half channels as float
  74. for (int i = 0; i < exr_header.num_channels; ++i)
  75. {
  76. if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF)
  77. {
  78. exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  79. }
  80. }
  81. // Read EXR data
  82. EXRImage exr_image;
  83. InitEXRImage(&exr_image);
  84. status = LoadEXRImageFromMemory(&exr_image, &exr_header, buffer, size, &error);
  85. if (status != TINYEXR_SUCCESS)
  86. {
  87. std::string error_string(error);
  88. FreeEXRErrorMessage(error);
  89. FreeEXRHeader(&exr_header);
  90. delete[] buffer;
  91. throw std::runtime_error("TinyEXR load error (" + std::to_string(status) + "): " + error_string);
  92. }
  93. // Free file buffer
  94. delete[] buffer;
  95. // Create image
  96. image = new ::image();
  97. image->format(sizeof(float), exr_image.num_channels);
  98. image->resize(static_cast<unsigned int>(exr_image.width), static_cast<unsigned int>(exr_image.height));
  99. // Fill image pixels
  100. float* component = static_cast<float*>(image->data());
  101. for (int y = exr_image.height - 1; y >= 0; --y)
  102. {
  103. int row_offset = y * exr_image.width;
  104. for (int x = 0; x < exr_image.width; ++x)
  105. {
  106. int pixel_index = row_offset + x;
  107. for (int c = exr_image.num_channels - 1; c >= 0; --c)
  108. {
  109. *(component++) = reinterpret_cast<float**>(exr_image.images)[c][pixel_index];
  110. }
  111. }
  112. }
  113. // Free EXR data
  114. FreeEXRImage(&exr_image);
  115. FreeEXRHeader(&exr_header);
  116. }
  117. else
  118. {
  119. // Load all other formats with stb_image
  120. // Determine if image is in an HDR format
  121. bool hdr = (stbi_is_hdr_from_memory(buffer, size) != 0);
  122. // Set vertical flip on load in order to upload pixels correctly to OpenGL
  123. stbi_set_flip_vertically_on_load(true);
  124. // Load image data
  125. void* pixels = nullptr;
  126. int width = 0;
  127. int height = 0;
  128. int channels = 0;
  129. if (hdr)
  130. {
  131. pixels = stbi_loadf_from_memory(buffer, size, &width, &height, &channels, 0);
  132. }
  133. else
  134. {
  135. pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0);
  136. }
  137. // Free file buffer
  138. delete[] buffer;
  139. // Check if image was loaded
  140. if (!pixels)
  141. {
  142. throw std::runtime_error("STBI failed to load image from memory.");
  143. }
  144. // Create image
  145. std::size_t component_size = (hdr) ? sizeof(float) : sizeof(unsigned char);
  146. image = new ::image();
  147. image->format(component_size, channels);
  148. image->resize(static_cast<unsigned int>(width), static_cast<unsigned int>(height));
  149. std::memcpy(image->data(), pixels, image->get_size());
  150. // Free loaded image data
  151. stbi_image_free(pixels);
  152. }
  153. return image;
  154. }