basistoktx.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #include <stdexcept>
  2. #include <string>
  3. #include <vector>
  4. #include <assert.h>
  5. #include <stdint.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include "basisu_format.h"
  9. #include "khr_df.h"
  10. #include "ktx2_format.h"
  11. template <typename T>
  12. static void read(const std::string& data, size_t offset, T& result)
  13. {
  14. if (offset + sizeof(T) > data.size())
  15. throw std::out_of_range("read");
  16. memcpy(&result, &data[offset], sizeof(T));
  17. }
  18. template <typename T>
  19. static void write(std::string& data, const T& value)
  20. {
  21. data.append(reinterpret_cast<const char*>(&value), sizeof(value));
  22. }
  23. template <typename T>
  24. static void write(std::string& data, size_t offset, const T& value)
  25. {
  26. if (offset + sizeof(T) > data.size())
  27. throw std::out_of_range("write");
  28. memcpy(&data[offset], &value, sizeof(T));
  29. }
  30. static void createDfd(std::vector<uint32_t>& result, int channels, bool srgb)
  31. {
  32. assert(channels <= 4);
  33. int descriptor_size = KHR_DF_WORD_SAMPLESTART + channels * KHR_DF_WORD_SAMPLEWORDS;
  34. result.clear();
  35. result.resize(1 + descriptor_size);
  36. result[0] = (1 + descriptor_size) * sizeof(uint32_t);
  37. uint32_t* dfd = &result[1];
  38. KHR_DFDSETVAL(dfd, VENDORID, KHR_DF_VENDORID_KHRONOS);
  39. KHR_DFDSETVAL(dfd, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
  40. KHR_DFDSETVAL(dfd, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_1_3);
  41. KHR_DFDSETVAL(dfd, DESCRIPTORBLOCKSIZE, descriptor_size * sizeof(uint32_t));
  42. KHR_DFDSETVAL(dfd, MODEL, KHR_DF_MODEL_RGBSDA);
  43. KHR_DFDSETVAL(dfd, PRIMARIES, KHR_DF_PRIMARIES_BT709);
  44. KHR_DFDSETVAL(dfd, TRANSFER, srgb ? KHR_DF_TRANSFER_SRGB : KHR_DF_TRANSFER_LINEAR);
  45. KHR_DFDSETVAL(dfd, FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT);
  46. static const khr_df_model_channels_e channel_enums[] = {
  47. KHR_DF_CHANNEL_RGBSDA_R,
  48. KHR_DF_CHANNEL_RGBSDA_G,
  49. KHR_DF_CHANNEL_RGBSDA_B,
  50. KHR_DF_CHANNEL_RGBSDA_A,
  51. };
  52. for (int i = 0; i < channels; ++i)
  53. {
  54. KHR_DFDSETSVAL(dfd, i, CHANNELID, channel_enums[i]);
  55. }
  56. }
  57. std::string basisToKtx(const std::string& basis, bool srgb)
  58. {
  59. std::string ktx;
  60. basist::basis_file_header basis_header;
  61. read(basis, 0, basis_header);
  62. assert(basis_header.m_sig == basist::basis_file_header::cBASISSigValue);
  63. assert(basis_header.m_total_slices > 0);
  64. assert(basis_header.m_total_images == 1);
  65. assert(basis_header.m_format == 0);
  66. assert(basis_header.m_flags & basist::cBASISHeaderFlagETC1S);
  67. assert(!(basis_header.m_flags & basist::cBASISHeaderFlagYFlipped));
  68. assert(basis_header.m_tex_type == basist::cBASISTexType2D);
  69. bool has_alpha = (basis_header.m_flags & basist::cBASISHeaderFlagHasAlphaSlices) != 0;
  70. std::vector<basist::basis_slice_desc> slices(basis_header.m_total_slices);
  71. for (size_t i = 0; i < basis_header.m_total_slices; ++i)
  72. read(basis, basis_header.m_slice_desc_file_ofs + i * sizeof(basist::basis_slice_desc), slices[i]);
  73. assert(slices[0].m_level_index == 0);
  74. uint32_t width = slices[0].m_orig_width;
  75. uint32_t height = slices[0].m_orig_height;
  76. uint32_t levels = has_alpha ? uint32_t(slices.size()) / 2 : uint32_t(slices.size());
  77. KTX_header2 ktx_header = {KTX2_IDENTIFIER_REF};
  78. ktx_header.typeSize = 1;
  79. ktx_header.pixelWidth = width;
  80. ktx_header.pixelHeight = height;
  81. ktx_header.layerCount = 0;
  82. ktx_header.faceCount = 1;
  83. ktx_header.levelCount = levels;
  84. ktx_header.supercompressionScheme = KTX_SUPERCOMPRESSION_BASIS;
  85. size_t header_size = sizeof(KTX_header2) + levels * sizeof(ktxLevelIndexEntry);
  86. std::vector<uint32_t> dfd;
  87. createDfd(dfd, has_alpha ? 4 : 3, srgb);
  88. const char* kvp_data[][2] = {
  89. {"KTXwriter", "gltfpack"},
  90. };
  91. std::string kvp;
  92. for (size_t i = 0; i < sizeof(kvp_data) / sizeof(kvp_data[0]); ++i)
  93. {
  94. const char* key = kvp_data[i][0];
  95. const char* value = kvp_data[i][1];
  96. write(kvp, uint32_t(strlen(key) + strlen(value) + 2));
  97. kvp += key;
  98. kvp += '\0';
  99. kvp += value;
  100. kvp += '\0';
  101. if (i + 1 != kvp.size())
  102. kvp.resize((kvp.size() + 3) & ~3);
  103. }
  104. size_t kvp_size = kvp.size();
  105. size_t dfd_size = dfd.size() * sizeof(uint32_t);
  106. size_t bgd_size =
  107. sizeof(ktxBasisGlobalHeader) + sizeof(ktxBasisSliceDesc) * levels +
  108. basis_header.m_endpoint_cb_file_size + basis_header.m_selector_cb_file_size + basis_header.m_tables_file_size;
  109. ktx_header.dataFormatDescriptor.byteOffset = uint32_t(header_size);
  110. ktx_header.dataFormatDescriptor.byteLength = uint32_t(dfd_size);
  111. ktx_header.keyValueData.byteOffset = uint32_t(header_size + dfd_size);
  112. ktx_header.keyValueData.byteLength = uint32_t(kvp_size);
  113. ktx_header.supercompressionGlobalData.byteOffset = (header_size + dfd_size + kvp_size + 7) & ~7;
  114. ktx_header.supercompressionGlobalData.byteLength = bgd_size;
  115. // KTX2 header
  116. write(ktx, ktx_header);
  117. size_t ktx_level_offset = ktx.size();
  118. for (size_t i = 0; i < levels; ++i)
  119. {
  120. ktxLevelIndexEntry le = {}; // This will be patched later
  121. write(ktx, le);
  122. }
  123. // data format descriptor
  124. for (size_t i = 0; i < dfd.size(); ++i)
  125. write(ktx, dfd[i]);
  126. // key/value pair data
  127. ktx += kvp;
  128. ktx.resize((ktx.size() + 7) & ~7);
  129. // supercompression global data
  130. ktxBasisGlobalHeader sgd_header = {};
  131. sgd_header.globalFlags = basis_header.m_flags;
  132. sgd_header.endpointCount = basis_header.m_total_endpoints;
  133. sgd_header.selectorCount = basis_header.m_total_selectors;
  134. sgd_header.endpointsByteLength = basis_header.m_endpoint_cb_file_size;
  135. sgd_header.selectorsByteLength = basis_header.m_selector_cb_file_size;
  136. sgd_header.tablesByteLength = basis_header.m_tables_file_size;
  137. sgd_header.extendedByteLength = basis_header.m_extended_file_size;
  138. write(ktx, sgd_header);
  139. size_t sgd_level_offset = ktx.size();
  140. for (size_t i = 0; i < levels; ++i)
  141. {
  142. ktxBasisSliceDesc sgd_slice = {}; // This will be patched later
  143. write(ktx, sgd_slice);
  144. }
  145. ktx.append(basis.substr(basis_header.m_endpoint_cb_file_ofs, basis_header.m_endpoint_cb_file_size));
  146. ktx.append(basis.substr(basis_header.m_selector_cb_file_ofs, basis_header.m_selector_cb_file_size));
  147. ktx.append(basis.substr(basis_header.m_tables_file_ofs, basis_header.m_tables_file_size));
  148. ktx.append(basis.substr(basis_header.m_extended_file_ofs, basis_header.m_extended_file_size));
  149. ktx.resize((ktx.size() + 7) & ~7);
  150. // mip levels
  151. for (size_t i = 0; i < levels; ++i)
  152. {
  153. size_t slice_index = (levels - i - 1) * (has_alpha + 1);
  154. const basist::basis_slice_desc& slice = slices[slice_index];
  155. const basist::basis_slice_desc* slice_alpha = has_alpha ? &slices[slice_index + 1] : 0;
  156. assert(slice.m_image_index == 0);
  157. assert(slice.m_level_index == levels - i - 1);
  158. size_t file_offset = ktx.size();
  159. ktx.append(basis.substr(slice.m_file_ofs, slice.m_file_size));
  160. if (slice_alpha)
  161. ktx.append(basis.substr(slice_alpha->m_file_ofs, slice_alpha->m_file_size));
  162. ktxLevelIndexEntry le = {};
  163. le.byteOffset = file_offset;
  164. le.byteLength = ktx.size() - file_offset;
  165. le.uncompressedByteLength = 0;
  166. write(ktx, ktx_level_offset + i * sizeof(ktxLevelIndexEntry), le);
  167. ktxBasisSliceDesc sgd_slice = {};
  168. sgd_slice.sliceByteOffset = 0;
  169. sgd_slice.sliceByteLength = slice.m_file_size;
  170. if (slice_alpha)
  171. {
  172. sgd_slice.alphaSliceByteOffset = slice.m_file_size;
  173. sgd_slice.alphaSliceByteLength = slice_alpha->m_file_size;
  174. }
  175. write(ktx, sgd_level_offset + i * sizeof(ktxBasisSliceDesc), sgd_slice);
  176. if (i + 1 != levels)
  177. ktx.resize((ktx.size() + 7) & ~7);
  178. }
  179. return ktx;
  180. }
  181. #ifdef STANDALONE
  182. bool readFile(const char* path, std::string& data)
  183. {
  184. FILE* file = fopen(path, "rb");
  185. if (!file)
  186. return false;
  187. fseek(file, 0, SEEK_END);
  188. long length = ftell(file);
  189. fseek(file, 0, SEEK_SET);
  190. if (length <= 0)
  191. {
  192. fclose(file);
  193. return false;
  194. }
  195. data.resize(length);
  196. size_t result = fread(&data[0], 1, data.size(), file);
  197. fclose(file);
  198. return result == data.size();
  199. }
  200. bool writeFile(const char* path, const std::string& data)
  201. {
  202. FILE* file = fopen(path, "wb");
  203. if (!file)
  204. return false;
  205. size_t result = fwrite(&data[0], 1, data.size(), file);
  206. fclose(file);
  207. return result == data.size();
  208. }
  209. int main(int argc, const char** argv)
  210. {
  211. if (argc < 2)
  212. return 1;
  213. std::string basis;
  214. if (!readFile(argv[1], basis))
  215. return 1;
  216. std::string ktx = basisToKtx(basis, true);
  217. if (!writeFile(argv[2], ktx))
  218. return 1;
  219. return 0;
  220. }
  221. #endif