Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement KTX2File #3

Merged
merged 3 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
428 changes: 428 additions & 0 deletions cpp/KTX2File.cpp

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions cpp/KTX2File.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#pragma once

#include <jsi/jsi.h>
#include "rn_basis_universal/transcoder/basisu.h"
#include "rn_basis_universal/transcoder/basisu_transcoder.h"
#include <vector>
#include <string>
#include <cstdint>

#define KTX2_MAGIC 0xDEADBEE2

namespace facebook::react {

class KTX2File : public jsi::NativeState {
public:
KTX2File(jsi::Runtime &rt, const jsi::ArrayBuffer& buffer);

bool isValid();
void close();
uint32_t getDFDSize();
bool hasKey(std::string key_name);
uint32_t getTotalKeys();
std::string getKey(uint32_t index);
uint32_t getKeyValueSize(std::string key_name);
uint32_t getWidth();
uint32_t getHeight();
uint32_t getFaces();
uint32_t getLayers();
uint32_t getLevels();
uint32_t getFormat();
bool isUASTC();
bool isETC1S();
bool isHDR();
bool getHasAlpha();
uint32_t getDFDColorModel();
uint32_t getDFDColorPrimaries();
uint32_t getDFDTransferFunc();
uint32_t getDFDFlags();
uint32_t getDFDTotalSamples();
uint32_t getDFDChannelID0();
uint32_t getDFDChannelID1();
bool isVideo();
uint32_t getETC1SImageDescImageFlags(uint32_t level_index, uint32_t layer_index, uint32_t face_index);
basist::ktx2_image_level_info getImageLevelInfo(uint32_t level_index, uint32_t layer_index, uint32_t face_index);
basist::ktx2_header getHeader();
uint32_t getImageTranscodedSizeInBytes(uint32_t level_index, uint32_t layer_index, uint32_t face_index, uint32_t format);
uint32_t startTranscoding();
uint32_t transcodeImage(jsi::Runtime& rt,
jsi::Object& destination,
uint32_t level_index,
uint32_t layer_index,
uint32_t face_index,
uint32_t format,
uint32_t get_alpha_for_opaque_formats,
int channel0,
int channel1);
uint32_t getDFD(jsi::Runtime &rt, jsi::Object& destination);

private:
int m_magic;
basist::ktx2_transcoder m_transcoder;
basisu::vector<uint8_t> m_file;
bool m_is_valid;
};
}
150 changes: 150 additions & 0 deletions cpp/react-native-basis-universal.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
#include "react-native-basis-universal.h"
#include "KTX2File.h"
#include <valarray>

#define DEFINE_BASIS_ENCODER_PARAMS_SETTER(func_name, param_name, param_type) \
void ReactNativeBasisUniversal::func_name(jsi::Runtime &rt, jsi::Object handle, param_type flag) { \
auto encoder = tryGetBasisEncoder(rt, handle); \
encoder->m_params.param_name = flag; \
}

#define GENERATE_KTX2_METHOD(RETURN_TYPE, NAME) \
RETURN_TYPE ReactNativeBasisUniversal::NAME(jsi::Runtime &rt, jsi::Object handle) { \
auto ktx2Handle = tryGetKTX2Handle(rt, handle); \
return ktx2Handle->NAME(); \
}

#define GENERATE_KTX2_VOID_METHOD(NAME) \
void ReactNativeBasisUniversal::NAME(jsi::Runtime &rt, jsi::Object handle) { \
auto ktx2Handle = tryGetKTX2Handle(rt, handle); \
ktx2Handle->NAME(); \
}


using namespace basist;
using namespace basisu;
Expand All @@ -26,6 +40,15 @@ std::shared_ptr<BasisEncoder> tryGetBasisEncoder(jsi::Runtime& rt, jsi::Object&
return encoder;
}

std::shared_ptr<KTX2File> tryGetKTX2Handle(jsi::Runtime& rt, jsi::Object& kt2xHandle) {
if (!kt2xHandle.hasNativeState(rt)) {
return nullptr;
}

auto ktx2file = std::dynamic_pointer_cast<KTX2File>(kt2xHandle.getNativeState(rt));
return ktx2file;
}

ReactNativeBasisUniversal::ReactNativeBasisUniversal(std::shared_ptr<CallInvoker> jsInvoker)
: NativeBasisUniversalCxxSpecJSI(jsInvoker) {}

Expand Down Expand Up @@ -271,4 +294,131 @@ DEFINE_BASIS_ENCODER_PARAMS_SETTER(setComputeStats, m_compute_stats, bool);
DEFINE_BASIS_ENCODER_PARAMS_SETTER(setCreateKTX2File, m_create_ktx2_file, bool);
DEFINE_BASIS_ENCODER_PARAMS_SETTER(setDebug, m_debug, bool);
DEFINE_BASIS_ENCODER_PARAMS_SETTER(setHDR, m_hdr, bool);


// KTX2 File

jsi::Object ReactNativeBasisUniversal::createKTX2FileHandle(jsi::Runtime &rt, jsi::Object data) {
jsi::Object basisObject{rt};
basisObject.setNativeState(rt, std::make_shared<KTX2File>(rt, data.getArrayBuffer(rt)));
return basisObject;
}

GENERATE_KTX2_METHOD(bool, isValid);
GENERATE_KTX2_METHOD(int, getDFDSize);
GENERATE_KTX2_VOID_METHOD(close);
GENERATE_KTX2_METHOD(int, getTotalKeys);
GENERATE_KTX2_METHOD(int, getWidth);
GENERATE_KTX2_METHOD(int, getHeight);
GENERATE_KTX2_METHOD(int, getFaces);
GENERATE_KTX2_METHOD(int, getLayers);
GENERATE_KTX2_METHOD(int, getLevels);
GENERATE_KTX2_METHOD(int, getFormat);
GENERATE_KTX2_METHOD(bool, isUASTC);
GENERATE_KTX2_METHOD(bool, isHDR);
GENERATE_KTX2_METHOD(bool, isETC1S);
GENERATE_KTX2_METHOD(bool, getHasAlpha);
GENERATE_KTX2_METHOD(int, getDFDColorModel);
GENERATE_KTX2_METHOD(int, getDFDColorPrimaries);
GENERATE_KTX2_METHOD(int, getDFDTransferFunc);
GENERATE_KTX2_METHOD(int, getDFDFlags);
GENERATE_KTX2_METHOD(int, getDFDTotalSamples);
GENERATE_KTX2_METHOD(int, getDFDChannelID0);
GENERATE_KTX2_METHOD(int, getDFDChannelID1);
GENERATE_KTX2_METHOD(bool, isVideo);
GENERATE_KTX2_METHOD(bool, startTranscoding);

int ReactNativeBasisUniversal::getDFD(jsi::Runtime &rt, jsi::Object handle, jsi::Object destination) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->getDFD(rt, destination);
}

jsi::Object ReactNativeBasisUniversal::getHeader(jsi::Runtime &rt, jsi::Object handle) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
auto nativeHeader = ktx2Handle->getHeader();
return Bridging<KTX2Header>::toJs(rt, {
.dfdByteLength = nativeHeader.m_dfd_byte_length,
.dfdByteOffset = nativeHeader.m_dfd_byte_offset,
.faceCount = nativeHeader.m_face_count,
.layerCount = nativeHeader.m_layer_count,
.levelCount = nativeHeader.m_level_count,
.vkFormat = nativeHeader.m_vk_format,
.typeSize = nativeHeader.m_type_size,
.pixelWidth = nativeHeader.m_pixel_width,
.pixelHeight = nativeHeader.m_pixel_height,
.pixelDepth = nativeHeader.m_pixel_depth,
.supercompressionScheme = nativeHeader.m_supercompression_scheme,
.kvdByteLength = nativeHeader.m_kvd_byte_length,
.kvdByteOffset = nativeHeader.m_kvd_byte_offset,
.sgdByteLength = nativeHeader.m_sgd_byte_length,
.sgdByteOffset = nativeHeader.m_sgd_byte_offset
}, this->jsInvoker_);
}

bool ReactNativeBasisUniversal::hasKey(jsi::Runtime &rt, jsi::Object handle, jsi::String key) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->hasKey(key.utf8(rt));
}


jsi::String ReactNativeBasisUniversal::getKey(jsi::Runtime &rt, jsi::Object handle, int index) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
auto result = ktx2Handle->getKey(index);
return jsi::String::createFromUtf8(rt, result);
}

int ReactNativeBasisUniversal::getKeyValueSize(jsi::Runtime &rt, jsi::Object handle, jsi::String key) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->getKeyValueSize(key.utf8(rt));
}

jsi::Object ReactNativeBasisUniversal::getImageLevelInfo(jsi::Runtime &rt, jsi::Object handle, int level, int layerIndex, int faceIndex) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
auto nativeInfo = ktx2Handle->getImageLevelInfo(level, layerIndex, faceIndex);
return Bridging<KTX2ImageLevelInfo>::toJs(rt, {
.levelIndex = nativeInfo.m_level_index,
.layerIndex = nativeInfo.m_layer_index,
.faceIndex = nativeInfo.m_face_index,
.origWidth = nativeInfo.m_orig_width,
.origHeight = nativeInfo.m_orig_height,
.width = nativeInfo.m_width,
.height = nativeInfo.m_height,
.numBlocksX = nativeInfo.m_num_blocks_x,
.numBlocksY = nativeInfo.m_num_blocks_y,
.totalBlocks = nativeInfo.m_total_blocks,
.alphaFlag = nativeInfo.m_alpha_flag,
.iframeFlag = nativeInfo.m_iframe_flag
}, this->jsInvoker_);
}

int ReactNativeBasisUniversal::getImageTranscodedSizeInBytes(jsi::Runtime &rt, jsi::Object handle, int levelIndex, int layerIndex, int faceIndex, int format) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->getImageTranscodedSizeInBytes(levelIndex, layerIndex, faceIndex, format);
}

// TODO: Used in IREngine
int ReactNativeBasisUniversal::transcodeImage(jsi::Runtime &rt, jsi::Object handle, jsi::Object dst, int levelIndex, int layerIndex, int faceIndex, int format, int getAlphaForOpaqueFormats, int channel0, int channel1) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->transcodeImage(rt,
dst,
levelIndex,
layerIndex,
faceIndex,
format,
getAlphaForOpaqueFormats,
channel0,
channel1);
}


int ReactNativeBasisUniversal::getETC1SImageDescImageFlags(jsi::Runtime &rt, jsi::Object handle, int levelIndex, int layerIndex, int faceIndex) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->getETC1SImageDescImageFlags(levelIndex, layerIndex, faceIndex);
}

int ReactNativeBasisUniversal::getKeyValue(jsi::Runtime &rt, jsi::Object handle, jsi::Object destination) {
// TODO: Not implemented (Not used in IREngine)
return 0;
}

}
82 changes: 82 additions & 0 deletions cpp/react-native-basis-universal.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,48 @@

namespace facebook::react {

using KTX2Header = NativeBasisUniversalKTX2Header<
/* vkFormat */ uint32_t,
/* typeSize */ uint32_t,
/* pixelWidth */ uint32_t,
/* pixelHeight */ uint32_t,
/* pixelDepth */ uint32_t,
/* layerCount */ uint32_t,
/* faceCount */ uint32_t,
/* levelCount */ uint32_t,
/* supercompressionScheme */ uint32_t,
/* dfdByteOffset */ uint32_t,
/* dfdByteLength */ uint32_t,
/* kvdByteOffset */ uint32_t,
/* kvdByteLength */ uint32_t,
/* sgdByteOffset */ uint32_t,
/* sgdByteLength */ uint32_t
>;

template <>
struct Bridging<KTX2Header>
: NativeBasisUniversalKTX2HeaderBridging<KTX2Header> {};


using KTX2ImageLevelInfo = NativeBasisUniversalKTX2ImageLevelInfo<
/* levelIndex */ uint32_t,
/* layerIndex */ uint32_t,
/* faceIndex */ uint32_t,
/* origWidth */ uint32_t,
/* origHeight */ uint32_t,
/* width */ uint32_t,
/* height: */ uint32_t,
/* numBlocksX */ uint32_t,
/* numBlocksY */ uint32_t,
/* totalBlocks */ uint32_t,
/* alphaFlag */ bool,
/* iframeFlag */ bool
>;

template <>
struct Bridging<KTX2ImageLevelInfo>
: NativeBasisUniversalKTX2ImageLevelInfoBridging<KTX2ImageLevelInfo> {};

using namespace facebook;

class ReactNativeBasisUniversal : public NativeBasisUniversalCxxSpecJSI {
Expand All @@ -21,6 +63,8 @@ class ReactNativeBasisUniversal : public NativeBasisUniversalCxxSpecJSI {
constexpr static auto kModuleName = "BasisUniversal";

void initializeBasis(jsi::Runtime &rt) override;

// Basis Encoder
jsi::Object createBasisHandle(jsi::Runtime &rt) override;
void setCreateKTX2File(jsi::Runtime &rt, jsi::Object handle, bool flag) override;
void setDebug(jsi::Runtime &rt, jsi::Object handle, bool flag) override;
Expand Down Expand Up @@ -51,6 +95,44 @@ class ReactNativeBasisUniversal : public NativeBasisUniversalCxxSpecJSI {
void setSelectorRDOThresh(jsi::Runtime &rt, jsi::Object handle, double threshold) override;
void setEndpointRDOThresh(jsi::Runtime &rt, jsi::Object handle, double threshold) override;

// KTX2 File
jsi::Object createKTX2FileHandle(jsi::Runtime &rt, jsi::Object data) override;
bool isValid(jsi::Runtime &rt, jsi::Object handle) override;
void close(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDSize(jsi::Runtime &rt, jsi::Object handle) override;
int getDFD(jsi::Runtime &rt, jsi::Object handle, jsi::Object destination) override;
jsi::Object getHeader(jsi::Runtime &rt, jsi::Object handle) override;
bool hasKey(jsi::Runtime &rt, jsi::Object handle, jsi::String key) override;
int getTotalKeys(jsi::Runtime &rt, jsi::Object handle) override;
jsi::String getKey(jsi::Runtime &rt, jsi::Object handle, int index) override;
int getKeyValueSize(jsi::Runtime &rt, jsi::Object handle, jsi::String key) override;
int getKeyValue(jsi::Runtime &rt, jsi::Object handle, jsi::Object destination) override;
int getWidth(jsi::Runtime &rt, jsi::Object handle) override;
int getHeight(jsi::Runtime &rt, jsi::Object handle) override;
int getFaces(jsi::Runtime &rt, jsi::Object handle) override;
int getLayers(jsi::Runtime &rt, jsi::Object handle) override;
int getLevels(jsi::Runtime &rt, jsi::Object handle) override;
int getFormat(jsi::Runtime &rt, jsi::Object handle) override;
bool isUASTC(jsi::Runtime &rt, jsi::Object handle) override;
bool isHDR(jsi::Runtime &rt, jsi::Object handle) override;
bool isETC1S(jsi::Runtime &rt, jsi::Object handle) override;
bool getHasAlpha(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDColorModel(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDColorPrimaries(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDTransferFunc(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDFlags(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDTotalSamples(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDChannelID0(jsi::Runtime &rt, jsi::Object handle) override;
int getDFDChannelID1(jsi::Runtime &rt, jsi::Object handle) override;
bool isVideo(jsi::Runtime &rt, jsi::Object handle) override;
int getETC1SImageDescImageFlags(jsi::Runtime &rt, jsi::Object handle, int levelIndex, int layerIndex, int faceIndex) override;
jsi::Object getImageLevelInfo(jsi::Runtime &rt, jsi::Object handle, int level, int layerIndex, int faceIndex) override;
int getImageTranscodedSizeInBytes(jsi::Runtime &rt, jsi::Object handle, int levelIndex, int layerIndex, int faceIndex, int format) override;
bool startTranscoding(jsi::Runtime &rt, jsi::Object handle) override;
int transcodeImage(jsi::Runtime &rt, jsi::Object handle, jsi::Object dst, int levelIndex, int layerIndex, int faceIndex, int format, int getAlphaForOpaqueFormats, int channel0, int channel1) override;



private:
bool basis_initialized_flag;
};
Expand Down
Loading
Loading