Skip to content

Commit

Permalink
Merge pull request #3 from callstack/feat/ktx2-file
Browse files Browse the repository at this point in the history
feat: Implement KTX2File
  • Loading branch information
okwasniewski authored Oct 23, 2024
2 parents 8d64f21 + 1cf622a commit 2d72a2b
Show file tree
Hide file tree
Showing 8 changed files with 1,256 additions and 1 deletion.
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

0 comments on commit 2d72a2b

Please sign in to comment.