#include <emscripten/bind.h>
#include <ktx.h>
#include <iostream>
using namespace emscripten;
#define ktxTexture2(t) reinterpret_cast<ktxTexture2*>(m_ptr.get())
namespace ktx
{
class texture
{
public:
texture(texture&) = delete;
texture(texture&& other) = default;
static texture createFromMemory(const emscripten::val& data)
{
std::vector<uint8_t> bytes{};
bytes.resize(data["byteLength"].as<size_t>());
emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"];
emscripten::val memoryView = data["constructor"].new_(memory, reinterpret_cast<uintptr_t>(bytes.data()), data["length"].as<uint32_t>());
memoryView.call<void>("set", data);
ktxTexture* ptr = nullptr;
KTX_error_code result = ktxTexture_CreateFromMemory(
bytes.data(),
bytes.size(),
KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,
&ptr);
if (result != KTX_SUCCESS)
{
std::cout << "ERROR: Failed to create from memory: " << ktxErrorString(result) << std::endl;
return texture(nullptr);
}
return texture(ptr);
}
uint32_t baseWidth() const
{
return m_ptr->baseWidth;
}
uint32_t baseHeight() const
{
return m_ptr->baseHeight;
}
bool needsTranscoding() const
{
return ktxTexture_NeedsTranscoding(m_ptr.get());
}
bool isPremultiplied() const
{
return (m_ptr->classId == ktxTexture2_c
&& ktxTexture2_GetPremultipliedAlpha(ktxTexture2(m_ptr.get())));
}
uint32_t numComponents() const
{
if (m_ptr->classId != ktxTexture2_c)
{
std::cout << "ERROR: numComponents is only supported for KTX2" << std::endl;
return 0;
}
return ktxTexture2_GetNumComponents(ktxTexture2(m_ptr.get()));
}
enum ktxSupercmpScheme supercompressionScheme() const
{
if (m_ptr->classId == ktxTexture1_c)
return KTX_SS_NONE;
else
return ktxTexture2(m_ptr.get())->supercompressionScheme;
}
uint32_t vkFormat() const
{
if (m_ptr->classId != ktxTexture2_c)
{
std::cout << "ERROR: vkFormat is only supported for KTX2" << std::endl;
return 0;
}
return ktxTexture2(m_ptr.get())->vkFormat;
}
struct ktxOrientation orientation() const
{
return m_ptr->orientation;
}
ktx_error_code_e transcodeBasis(const val& targetFormat, const val& decodeFlags)
{
if (m_ptr->classId != ktxTexture2_c)
{
std::cout << "ERROR: transcodeBasis is only supported for KTX2" << std::endl;
return KTX_INVALID_VALUE;
}
KTX_error_code result = ktxTexture2_TranscodeBasis(
ktxTexture2(m_ptr.get()),
targetFormat.as<ktx_texture_transcode_fmt_e>(),
decodeFlags.as<ktx_transcode_flags>());
if (result != KTX_SUCCESS)
{
std::cout << "ERROR: Failed to transcode: " << ktxErrorString(result) << std::endl;
}
return result;
}
emscripten::val glUpload()
{
GLuint texname = 0;
GLenum target = 0;
GLenum error = 0;
KTX_error_code result = ktxTexture_GLUpload(m_ptr.get(), &texname, &target, &error);
if (result != KTX_SUCCESS)
{
std::cout << "ERROR: Failed to GL upload: " << ktxErrorString(result) << std::endl;
}
val ret = val::object();
val texture = val::module_property("GL")["textures"][texname];
ret.set("texture", texture);
ret.set("target", target);
ret.set("error", error);
return ret;
}
private:
texture(ktxTexture* ptr)
: m_ptr{ ptr, &destroy }
{
}
static void destroy(ktxTexture* ptr)
{
ktxTexture_Destroy(ptr);
}
std::unique_ptr<ktxTexture, decltype(&destroy)> m_ptr;
};
}
EMSCRIPTEN_BINDINGS(ktx)
{
enum_<ktx_error_code_e>("ErrorCode")
.value("SUCCESS", KTX_SUCCESS)
.value("FILE_DATA_ERROR", KTX_FILE_DATA_ERROR)
.value("FILE_ISPIPE", KTX_FILE_ISPIPE)
.value("FILE_OPEN_FAILED", KTX_FILE_OPEN_FAILED)
.value("FILE_OVERFLOW", KTX_FILE_OVERFLOW)
.value("FILE_READ_ERROR", KTX_FILE_READ_ERROR)
.value("FILE_SEEK_ERROR", KTX_FILE_SEEK_ERROR)
.value("FILE_UNEXPECTED_ERROR", KTX_FILE_UNEXPECTED_EOF)
.value("FILE_WRITE_ERROR", KTX_FILE_WRITE_ERROR)
.value("GL_ERROR", KTX_GL_ERROR)
.value("INVALID_OPERATION", KTX_INVALID_OPERATION)
.value("INVALID_VALUE", KTX_INVALID_VALUE)
.value("NOT_FOUND", KTX_NOT_FOUND)
.value("OUT_OF_MEMORY", KTX_OUT_OF_MEMORY)
.value("TRANSCODE_FAILED", KTX_TRANSCODE_FAILED)
.value("UNKNOWN_FILE_FORMAT", KTX_UNKNOWN_FILE_FORMAT)
.value("UNSUPPORTED_TEXTURE_TYPE", KTX_UNSUPPORTED_TEXTURE_TYPE)
.value("UNSUPPORTED_FEATURE", KTX_UNSUPPORTED_FEATURE)
.value("LIBRARY_NOT_LINKED", KTX_LIBRARY_NOT_LINKED)
;
enum_<ktx_texture_transcode_fmt_e>("TranscodeTarget")
.value("ETC1_RGB", KTX_TTF_ETC1_RGB)
.value("BC1_RGB", KTX_TTF_BC1_RGB)
.value("BC4_R", KTX_TTF_BC4_R)
.value("BC5_RG", KTX_TTF_BC5_RG)
.value("BC3_RGBA", KTX_TTF_BC3_RGBA)
.value("BC1_OR_3", KTX_TTF_BC1_OR_3)
.value("PVRTC1_4_RGB", KTX_TTF_PVRTC1_4_RGB)
.value("PVRTC1_4_RGBA", KTX_TTF_PVRTC1_4_RGBA)
.value("BC7_RGBA", KTX_TTF_BC7_RGBA)
.value("BC7_M6_RGB", KTX_TTF_BC7_M6_RGB)
.value("BC7_M5_RGBA", KTX_TTF_BC7_M5_RGBA)
.value("ETC2_RGBA", KTX_TTF_ETC2_RGBA)
.value("ASTC_4x4_RGBA", KTX_TTF_ASTC_4x4_RGBA)
.value("RGBA32", KTX_TTF_RGBA32)
.value("RGB565", KTX_TTF_RGB565)
.value("BGR565", KTX_TTF_BGR565)
.value("RGBA4444", KTX_TTF_RGBA4444)
.value("PVRTC2_4_RGB", KTX_TTF_PVRTC2_4_RGB)
.value("PVRTC2_4_RGBA", KTX_TTF_PVRTC2_4_RGBA)
.value("ETC", KTX_TTF_ETC)
.value("EAC_R11", KTX_TTF_ETC2_EAC_R11)
.value("EAC_RG11", KTX_TTF_ETC2_EAC_RG11)
;
enum_<ktx_transcode_flag_bits_e>("TranscodeFlagBits")
.value("TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS)
;
enum_<ktxOrientationX>("OrientationX")
.value("LEFT", KTX_ORIENT_X_LEFT)
.value("RIGHT", KTX_ORIENT_X_RIGHT)
;
enum_<ktxOrientationY>("OrientationY")
.value("UP", KTX_ORIENT_Y_UP)
.value("DOWN", KTX_ORIENT_Y_DOWN)
;
enum_<ktxOrientationZ>("OrientationZ")
.value("IN", KTX_ORIENT_Z_IN)
.value("OUT", KTX_ORIENT_Z_OUT)
;
enum_<ktxSupercmpScheme>("SupercmpScheme")
.value("NONE", KTX_SS_NONE)
.value("BASIS_LZ", KTX_SS_BASIS_LZ)
.value("ZSTD", KTX_SS_ZSTD)
;
value_object<ktxOrientation>("Orientation")
.field("x", &ktxOrientation::x)
.field("y", &ktxOrientation::y)
.field("z", &ktxOrientation::z)
;
class_<ktx::texture>("ktxTexture")
.constructor(&ktx::texture::createFromMemory)
.property("baseWidth", &ktx::texture::baseWidth)
.property("baseHeight", &ktx::texture::baseHeight)
.property("isPremultiplied", &ktx::texture::isPremultiplied)
.property("needsTranscoding", &ktx::texture::needsTranscoding)
.property("numComponents", &ktx::texture::numComponents)
.property("orientation", &ktx::texture::orientation)
.property("supercompressScheme", &ktx::texture::supercompressionScheme)
.property("vkFormat", &ktx::texture::vkFormat)
.function("transcodeBasis", &ktx::texture::transcodeBasis)
.function("glUpload", &ktx::texture::glUpload)
;
}