basistoktx.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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. // KTX Specification: 2. File Structure
  11. struct Ktx2Header
  12. {
  13. uint8_t identifier[12];
  14. uint32_t vkFormat;
  15. uint32_t typeSize;
  16. uint32_t pixelWidth;
  17. uint32_t pixelHeight;
  18. uint32_t pixelDepth;
  19. uint32_t layerCount;
  20. uint32_t faceCount;
  21. uint32_t levelCount;
  22. uint32_t supercompressionScheme;
  23. uint32_t dfdByteOffset;
  24. uint32_t dfdByteLength;
  25. uint32_t kvdByteOffset;
  26. uint32_t kvdByteLength;
  27. uint64_t sgdByteOffset;
  28. uint64_t sgdByteLength;
  29. };
  30. struct Ktx2LevelIndex
  31. {
  32. uint64_t byteOffset;
  33. uint64_t byteLength;
  34. uint64_t uncompressedByteLength;
  35. };
  36. enum
  37. {
  38. Ktx2SupercompressionSchemeBasis = 1,
  39. };
  40. // KTX Specification: 3.1. identifier
  41. static const uint8_t Ktx2FileIdentifier[12] = {
  42. 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A, //
  43. };
  44. // KTX Specification: 3.12.2. Basis Universal Global Data
  45. struct Ktx2BasisGlobalHeader
  46. {
  47. uint32_t globalFlags;
  48. uint16_t endpointCount;
  49. uint16_t selectorCount;
  50. uint32_t endpointsByteLength;
  51. uint32_t selectorsByteLength;
  52. uint32_t tablesByteLength;
  53. uint32_t extendedByteLength;
  54. };
  55. struct Ktx2BasisImageDesc
  56. {
  57. uint32_t imageFlags;
  58. uint32_t rgbSliceByteOffset;
  59. uint32_t rgbSliceByteLength;
  60. uint32_t alphaSliceByteOffset;
  61. uint32_t alphaSliceByteLength;
  62. };
  63. template <typename T>
  64. static void read(const std::string& data, size_t offset, T& result)
  65. {
  66. if (offset + sizeof(T) > data.size())
  67. throw std::out_of_range("read");
  68. memcpy(&result, &data[offset], sizeof(T));
  69. }
  70. template <typename T>
  71. static void write(std::string& data, const T& value)
  72. {
  73. data.append(reinterpret_cast<const char*>(&value), sizeof(value));
  74. }
  75. template <typename T>
  76. static void write(std::string& data, size_t offset, const T& value)
  77. {
  78. if (offset + sizeof(T) > data.size())
  79. throw std::out_of_range("write");
  80. memcpy(&data[offset], &value, sizeof(T));
  81. }
  82. static void createDfd(std::vector<uint32_t>& result, int channels, bool srgb)
  83. {
  84. assert(channels <= 4);
  85. int descriptor_size = KHR_DF_WORD_SAMPLESTART + channels * KHR_DF_WORD_SAMPLEWORDS;
  86. result.clear();
  87. result.resize(1 + descriptor_size);
  88. result[0] = (1 + descriptor_size) * sizeof(uint32_t);
  89. uint32_t* dfd = &result[1];
  90. KHR_DFDSETVAL(dfd, VENDORID, KHR_DF_VENDORID_KHRONOS);
  91. KHR_DFDSETVAL(dfd, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
  92. KHR_DFDSETVAL(dfd, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_1_3);
  93. KHR_DFDSETVAL(dfd, DESCRIPTORBLOCKSIZE, descriptor_size * sizeof(uint32_t));
  94. KHR_DFDSETVAL(dfd, MODEL, KHR_DF_MODEL_RGBSDA);
  95. KHR_DFDSETVAL(dfd, PRIMARIES, KHR_DF_PRIMARIES_BT709);
  96. KHR_DFDSETVAL(dfd, TRANSFER, srgb ? KHR_DF_TRANSFER_SRGB : KHR_DF_TRANSFER_LINEAR);
  97. KHR_DFDSETVAL(dfd, FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT);
  98. static const khr_df_model_channels_e channel_enums[] = {
  99. KHR_DF_CHANNEL_RGBSDA_R,
  100. KHR_DF_CHANNEL_RGBSDA_G,
  101. KHR_DF_CHANNEL_RGBSDA_B,
  102. KHR_DF_CHANNEL_RGBSDA_A,
  103. };
  104. for (int i = 0; i < channels; ++i)
  105. {
  106. uint32_t qualifiers = (srgb && i == 3) ? KHR_DF_SAMPLE_DATATYPE_LINEAR : 0;
  107. KHR_DFDSETSVAL(dfd, i, CHANNELID, channel_enums[i]);
  108. KHR_DFDSETSVAL(dfd, i, QUALIFIERS, qualifiers);
  109. KHR_DFDSETSVAL(dfd, i, SAMPLELOWER, 0);
  110. KHR_DFDSETSVAL(dfd, i, SAMPLEUPPER, ~0u);
  111. }
  112. }
  113. std::string basisToKtx(const std::string& basis, bool srgb)
  114. {
  115. std::string ktx;
  116. basist::basis_file_header basis_header;
  117. read(basis, 0, basis_header);
  118. assert(basis_header.m_sig == basist::basis_file_header::cBASISSigValue);
  119. assert(basis_header.m_total_slices > 0);
  120. assert(basis_header.m_total_images == 1);
  121. assert(basis_header.m_format == 0);
  122. assert(basis_header.m_flags & basist::cBASISHeaderFlagETC1S);
  123. assert(!(basis_header.m_flags & basist::cBASISHeaderFlagYFlipped));
  124. assert(basis_header.m_tex_type == basist::cBASISTexType2D);
  125. bool has_alpha = (basis_header.m_flags & basist::cBASISHeaderFlagHasAlphaSlices) != 0;
  126. std::vector<basist::basis_slice_desc> slices(basis_header.m_total_slices);
  127. for (size_t i = 0; i < basis_header.m_total_slices; ++i)
  128. read(basis, basis_header.m_slice_desc_file_ofs + i * sizeof(basist::basis_slice_desc), slices[i]);
  129. assert(slices[0].m_level_index == 0);
  130. uint32_t width = slices[0].m_orig_width;
  131. uint32_t height = slices[0].m_orig_height;
  132. uint32_t levels = has_alpha ? uint32_t(slices.size()) / 2 : uint32_t(slices.size());
  133. Ktx2Header ktx_header = {};
  134. memcpy(ktx_header.identifier, Ktx2FileIdentifier, sizeof(Ktx2FileIdentifier));
  135. ktx_header.typeSize = 1;
  136. ktx_header.pixelWidth = width;
  137. ktx_header.pixelHeight = height;
  138. ktx_header.layerCount = 0;
  139. ktx_header.faceCount = 1;
  140. ktx_header.levelCount = levels;
  141. ktx_header.supercompressionScheme = Ktx2SupercompressionSchemeBasis;
  142. size_t header_size = sizeof(Ktx2Header) + levels * sizeof(Ktx2LevelIndex);
  143. std::vector<uint32_t> dfd;
  144. createDfd(dfd, has_alpha ? 4 : 3, srgb);
  145. const char* kvp_data[][2] = {
  146. {"KTXwriter", "gltfpack"},
  147. };
  148. std::string kvp;
  149. for (size_t i = 0; i < sizeof(kvp_data) / sizeof(kvp_data[0]); ++i)
  150. {
  151. const char* key = kvp_data[i][0];
  152. const char* value = kvp_data[i][1];
  153. write(kvp, uint32_t(strlen(key) + strlen(value) + 2));
  154. kvp += key;
  155. kvp += '\0';
  156. kvp += value;
  157. kvp += '\0';
  158. kvp.resize((kvp.size() + 3) & ~3);
  159. }
  160. size_t kvp_size = kvp.size();
  161. size_t dfd_size = dfd.size() * sizeof(uint32_t);
  162. size_t bgd_size =
  163. sizeof(Ktx2BasisGlobalHeader) + sizeof(Ktx2BasisImageDesc) * levels +
  164. basis_header.m_endpoint_cb_file_size + basis_header.m_selector_cb_file_size + basis_header.m_tables_file_size;
  165. ktx_header.dfdByteOffset = uint32_t(header_size);
  166. ktx_header.dfdByteLength = uint32_t(dfd_size);
  167. ktx_header.kvdByteOffset = uint32_t(header_size + dfd_size);
  168. ktx_header.kvdByteLength = uint32_t(kvp_size);
  169. ktx_header.sgdByteOffset = (header_size + dfd_size + kvp_size + 7) & ~7;
  170. ktx_header.sgdByteLength = bgd_size;
  171. // KTX2 header
  172. write(ktx, ktx_header);
  173. size_t ktx_level_offset = ktx.size();
  174. for (size_t i = 0; i < levels; ++i)
  175. {
  176. Ktx2LevelIndex le = {}; // This will be patched later
  177. write(ktx, le);
  178. }
  179. // data format descriptor
  180. for (size_t i = 0; i < dfd.size(); ++i)
  181. write(ktx, dfd[i]);
  182. // key/value pair data
  183. ktx += kvp;
  184. ktx.resize((ktx.size() + 7) & ~7);
  185. // supercompression global data
  186. Ktx2BasisGlobalHeader sgd_header = {};
  187. sgd_header.globalFlags = basis_header.m_flags;
  188. sgd_header.endpointCount = uint16_t(basis_header.m_total_endpoints);
  189. sgd_header.selectorCount = uint16_t(basis_header.m_total_selectors);
  190. sgd_header.endpointsByteLength = basis_header.m_endpoint_cb_file_size;
  191. sgd_header.selectorsByteLength = basis_header.m_selector_cb_file_size;
  192. sgd_header.tablesByteLength = basis_header.m_tables_file_size;
  193. sgd_header.extendedByteLength = basis_header.m_extended_file_size;
  194. write(ktx, sgd_header);
  195. size_t sgd_level_offset = ktx.size();
  196. for (size_t i = 0; i < levels; ++i)
  197. {
  198. Ktx2BasisImageDesc sgd_image = {}; // This will be patched later
  199. write(ktx, sgd_image);
  200. }
  201. ktx.append(basis.substr(basis_header.m_endpoint_cb_file_ofs, basis_header.m_endpoint_cb_file_size));
  202. ktx.append(basis.substr(basis_header.m_selector_cb_file_ofs, basis_header.m_selector_cb_file_size));
  203. ktx.append(basis.substr(basis_header.m_tables_file_ofs, basis_header.m_tables_file_size));
  204. ktx.append(basis.substr(basis_header.m_extended_file_ofs, basis_header.m_extended_file_size));
  205. ktx.resize((ktx.size() + 7) & ~7);
  206. // mip levels
  207. for (size_t i = 0; i < levels; ++i)
  208. {
  209. size_t level_index = levels - i - 1;
  210. size_t slice_index = level_index * (has_alpha + 1);
  211. const basist::basis_slice_desc& slice = slices[slice_index];
  212. const basist::basis_slice_desc* slice_alpha = has_alpha ? &slices[slice_index + 1] : 0;
  213. assert(slice.m_image_index == 0);
  214. assert(slice.m_level_index == level_index);
  215. size_t file_offset = ktx.size();
  216. ktx.append(basis.substr(slice.m_file_ofs, slice.m_file_size));
  217. if (slice_alpha)
  218. ktx.append(basis.substr(slice_alpha->m_file_ofs, slice_alpha->m_file_size));
  219. Ktx2LevelIndex le = {};
  220. le.byteOffset = file_offset;
  221. le.byteLength = ktx.size() - file_offset;
  222. le.uncompressedByteLength = 0;
  223. write(ktx, ktx_level_offset + level_index * sizeof(Ktx2LevelIndex), le);
  224. Ktx2BasisImageDesc sgd_image = {};
  225. sgd_image.rgbSliceByteOffset = 0;
  226. sgd_image.rgbSliceByteLength = slice.m_file_size;
  227. if (slice_alpha)
  228. {
  229. sgd_image.alphaSliceByteOffset = slice.m_file_size;
  230. sgd_image.alphaSliceByteLength = slice_alpha->m_file_size;
  231. }
  232. write(ktx, sgd_level_offset + level_index * sizeof(Ktx2BasisImageDesc), sgd_image);
  233. ktx.resize((ktx.size() + 7) & ~7);
  234. }
  235. return ktx;
  236. }
  237. #ifdef STANDALONE
  238. bool readFile(const char* path, std::string& data)
  239. {
  240. FILE* file = fopen(path, "rb");
  241. if (!file)
  242. return false;
  243. fseek(file, 0, SEEK_END);
  244. long length = ftell(file);
  245. fseek(file, 0, SEEK_SET);
  246. if (length <= 0)
  247. {
  248. fclose(file);
  249. return false;
  250. }
  251. data.resize(length);
  252. size_t result = fread(&data[0], 1, data.size(), file);
  253. fclose(file);
  254. return result == data.size();
  255. }
  256. bool writeFile(const char* path, const std::string& data)
  257. {
  258. FILE* file = fopen(path, "wb");
  259. if (!file)
  260. return false;
  261. size_t result = fwrite(&data[0], 1, data.size(), file);
  262. fclose(file);
  263. return result == data.size();
  264. }
  265. int main(int argc, const char** argv)
  266. {
  267. if (argc < 2)
  268. return 1;
  269. std::string basis;
  270. if (!readFile(argv[1], basis))
  271. return 1;
  272. std::string ktx = basisToKtx(basis, true);
  273. if (!writeFile(argv[2], ktx))
  274. return 1;
  275. return 0;
  276. }
  277. #endif