Skip to content
Open
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
3 changes: 1 addition & 2 deletions basisu_tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1632,8 +1632,7 @@ static bool compress_mode(command_line_params &opts)
if (params.m_tex_type == basist::basis_texture_type::cBASISTexTypeVideoFrames)
{
// Create KTXanimData key value entry
// TODO: Move this to basisu_comp.h
basist::ktx2_transcoder::key_value kv;
basisu::key_value kv;

const char* pAD = "KTXanimData";
kv.m_key.resize(strlen(pAD) + 1);
Expand Down
43 changes: 26 additions & 17 deletions encoder/basisu_comp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4585,18 +4585,18 @@ namespace basisu
}

// Key values
basist::ktx2_transcoder::key_value_vec key_values(m_params.m_ktx2_key_values);
basisu::key_value_vec key_values(m_params.m_ktx2_key_values);

basist::ktx2_add_key_value(key_values, "KTXwriter", fmt_string("Basis Universal {}", BASISU_LIB_VERSION_STRING));
basisu::add_key_value(key_values, "KTXwriter", fmt_string("Basis Universal {}", BASISU_LIB_VERSION_STRING));

if (m_params.m_hdr)
{
if (m_upconverted_any_ldr_images)
{
basist::ktx2_add_key_value(key_values, "LDRUpconversionMultiplier", fmt_string("{}", m_ldr_to_hdr_upconversion_nit_multiplier));
basisu::add_key_value(key_values, "LDRUpconversionMultiplier", fmt_string("{}", m_ldr_to_hdr_upconversion_nit_multiplier));

if (m_params.m_ldr_hdr_upconversion_srgb_to_linear)
basist::ktx2_add_key_value(key_values, "LDRUpconversionSRGBToLinear", "1");
basisu::add_key_value(key_values, "LDRUpconversionSRGBToLinear", "1");
}

// Always write the scale to simplify testing.
Expand Down Expand Up @@ -4919,7 +4919,8 @@ namespace basisu
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats,
int quality_level, int effort_level)
int quality_level, int effort_level,
const basisu::key_value_vec* pKeyValues)
{
assert((pSource_images != nullptr) || (pSource_images_hdr != nullptr));
assert(!((pSource_images != nullptr) && (pSource_images_hdr != nullptr)));
Expand Down Expand Up @@ -5135,6 +5136,8 @@ namespace basisu
// Set KTX2 specific parameters.
if ((flags_and_quality & cFlagKTX2UASTCSuperCompression) && (comp_params.m_uastc))
comp_params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD;
if (pKeyValues)
comp_params.m_ktx2_key_values = *pKeyValues;
}

comp_params.m_compute_stats = (pStats != nullptr);
Expand Down Expand Up @@ -5217,47 +5220,52 @@ namespace basisu
const basisu::vector<image>& source_images,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats)
image_stats* pStats,
const basisu::key_value_vec* pKeyValues)
{
return basis_compress_internal(mode, &source_images, nullptr, flags_and_quality, uastc_rdo_or_dct_quality, pSize, pStats, -1, -1);
return basis_compress_internal(mode, &source_images, nullptr, flags_and_quality, uastc_rdo_or_dct_quality, pSize, pStats, -1, -1, pKeyValues);
}

void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<image>& source_images,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats)
image_stats* pStats,
const basisu::key_value_vec* pKeyValues)
{
return basis_compress_internal(mode, &source_images, nullptr, flags_and_quality, 0.0f, pSize, pStats, quality_level, effort_level);
return basis_compress_internal(mode, &source_images, nullptr, flags_and_quality, 0.0f, pSize, pStats, quality_level, effort_level, pKeyValues);
}

void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats)
image_stats* pStats,
const basisu::key_value_vec* pKeyValues)
{
return basis_compress_internal(mode, nullptr, &source_images_hdr, flags_and_quality, uastc_rdo_or_dct_quality, pSize, pStats, -1, -1);
return basis_compress_internal(mode, nullptr, &source_images_hdr, flags_and_quality, uastc_rdo_or_dct_quality, pSize, pStats, -1, -1, pKeyValues);
}

void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats)
image_stats* pStats,
const basisu::key_value_vec* pKeyValues)
{
return basis_compress_internal(mode, nullptr, &source_images_hdr, flags_and_quality, 0.0f, pSize, pStats, quality_level, effort_level);
return basis_compress_internal(mode, nullptr, &source_images_hdr, flags_and_quality, 0.0f, pSize, pStats, quality_level, effort_level, pKeyValues);
}

void* basis_compress(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats)
image_stats* pStats,
const basisu::key_value_vec* pKeyValues)
{
if (!pitch_in_pixels)
pitch_in_pixels = width;
Expand All @@ -5283,15 +5291,16 @@ namespace basisu
for (uint32_t y = 0; y < height; y++)
memcpy(source_image[0].get_ptr() + y * width, (const color_rgba*)pImageRGBA + y * pitch_in_pixels, width * sizeof(color_rgba));

return basis_compress(mode, source_image, flags_and_quality, uastc_rdo_or_dct_quality, pSize, pStats);
return basis_compress(mode, source_image, flags_and_quality, uastc_rdo_or_dct_quality, pSize, pStats, pKeyValues);
}

void* basis_compress2(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats)
image_stats* pStats,
const basisu::key_value_vec* pKeyValues)
{
if (!pitch_in_pixels)
pitch_in_pixels = width;
Expand All @@ -5317,7 +5326,7 @@ namespace basisu
for (uint32_t y = 0; y < height; y++)
memcpy(source_image[0].get_ptr() + y * width, (const color_rgba*)pImageRGBA + y * pitch_in_pixels, width * sizeof(color_rgba));

return basis_compress2(mode, source_image, flags_and_quality, quality_level, effort_level, pSize, pStats);
return basis_compress2(mode, source_image, flags_and_quality, quality_level, effort_level, pSize, pStats, pKeyValues);
}

void basis_free_data(void* p)
Expand Down
23 changes: 15 additions & 8 deletions encoder/basisu_comp.h
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ namespace basisu
// Internally, the compressor always creates a .basis file then it converts that losslessly to KTX2.
bool_param<false> m_create_ktx2_file;
basist::ktx2_supercompression m_ktx2_uastc_supercompression;
basist::ktx2_transcoder::key_value_vec m_ktx2_key_values;
basisu::key_value_vec m_ktx2_key_values;
param<int> m_ktx2_zstd_supercompression_level;

// Note: The default for this parameter (which used to be "m_ktx2_srgb_transfer_func") used to be false, now setting this to true and renaming to m_ktx2_and_basis_srgb_transfer_function.
Expand Down Expand Up @@ -990,7 +990,8 @@ namespace basisu
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats,
int quality_level = -1, int effort_level = -1);
int quality_level = -1, int effort_level = -1,
const basisu::key_value_vec* pKeyValues = nullptr);

// This function accepts an array of source images.
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
Expand All @@ -1003,7 +1004,8 @@ namespace basisu
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
image_stats* pStats = nullptr,
const basisu::key_value_vec* pKeyValues = nullptr);

// HDR-only version.
// Important: The returned block MUST be manually freed using basis_free_data().
Expand All @@ -1012,7 +1014,8 @@ namespace basisu
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
image_stats* pStats = nullptr,
const basisu::key_value_vec* pKeyValues = nullptr);

// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
// Important: The returned block MUST be manually freed using basis_free_data().
Expand All @@ -1021,7 +1024,8 @@ namespace basisu
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
image_stats* pStats = nullptr,
const basisu::key_value_vec* pKeyValues = nullptr);

// basis_compress2() variants accept the new unified quality_level and effort_level parameters instead of the old flags/float uastc_rdo_or_dct_quality parameter.
// quality_level must be [0,100], effort_level [0,10].
Expand All @@ -1030,21 +1034,24 @@ namespace basisu
const basisu::vector<image>& source_images,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
image_stats* pStats = nullptr,
const basisu::key_value_vec* pKeyValues = nullptr);

void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
image_stats* pStats = nullptr,
const basisu::key_value_vec* pKeyValues = nullptr);

void* basis_compress2(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
image_stats* pStats = nullptr,
const basisu::key_value_vec* pKeyValues = nullptr);

// Frees the dynamically allocated file data returned by basis_compress().
// This MUST be called on the pointer returned by basis_compress() when you're done with it.
Expand Down
48 changes: 48 additions & 0 deletions transcoder/basisu.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,54 @@ namespace basisu
}
};

// Key value field data.
struct key_value
{
// The key field is UTF8 and always zero terminated.
// In memory we always append a zero terminator to the key.
basisu::uint8_vec m_key;

// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.
// In memory we always append a zero terminator to the value.
basisu::uint8_vec m_value;

bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; }


};
typedef basisu::vector<key_value> key_value_vec;

// Replaces if the key already exists
inline void add_key_value(key_value_vec& key_values, const std::string& key, const std::string& val)
{
assert(key.size());

key_value* p = nullptr;

// Try to find an existing key
for (size_t i = 0; i < key_values.size(); i++)
{
if (strcmp((const char*)key_values[i].m_key.data(), key.c_str()) == 0)
{
p = &key_values[i];
break;
}
}

if (!p)
p = key_values.enlarge(1);

p->m_key.resize(0);
p->m_value.resize(0);

p->m_key.resize(key.size() + 1);
memcpy(p->m_key.data(), key.c_str(), key.size());

p->m_value.resize(val.size() + 1);
if (val.size())
memcpy(p->m_value.data(), val.c_str(), val.size());
}

enum eZero { cZero };
enum eNoClamp { cNoClamp };

Expand Down
50 changes: 2 additions & 48 deletions transcoder/basisu_transcoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1209,24 +1209,9 @@ namespace basist
ktx2_df_channel_id get_dfd_channel_id0() const { return m_dfd_chan0; }
ktx2_df_channel_id get_dfd_channel_id1() const { return m_dfd_chan1; }

// Key value field data.
struct key_value
{
// The key field is UTF8 and always zero terminated.
// In memory we always append a zero terminator to the key.
basisu::uint8_vec m_key;

// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.
// In memory we always append a zero terminator to the value.
basisu::uint8_vec m_value;

bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; }
};
typedef basisu::vector<key_value> key_value_vec;

// Returns the array of key-value entries. This may be empty. Valid after init().
// The order of key values fields in this array exactly matches the order they were stored in the file. The keys are supposed to be sorted by their Unicode code points.
const key_value_vec& get_key_values() const { return m_key_values; }
const basisu::key_value_vec& get_key_values() const { return m_key_values; }

const basisu::uint8_vec *find_key(const std::string& key_name) const;

Expand Down Expand Up @@ -1278,7 +1263,7 @@ namespace basist
ktx2_header m_header;
basisu::vector<ktx2_level_index> m_levels;
basisu::uint8_vec m_dfd;
key_value_vec m_key_values;
basisu::key_value_vec m_key_values;

ktx2_etc1s_global_data_header m_etc1s_header;
basisu::vector<ktx2_etc1s_image_desc> m_etc1s_image_descs;
Expand Down Expand Up @@ -1315,37 +1300,6 @@ namespace basist
bool read_key_values();
};

// Replaces if the key already exists
inline void ktx2_add_key_value(ktx2_transcoder::key_value_vec& key_values, const std::string& key, const std::string& val)
{
assert(key.size());

basist::ktx2_transcoder::key_value* p = nullptr;

// Try to find an existing key
for (size_t i = 0; i < key_values.size(); i++)
{
if (strcmp((const char*)key_values[i].m_key.data(), key.c_str()) == 0)
{
p = &key_values[i];
break;
}
}

if (!p)
p = key_values.enlarge(1);

p->m_key.resize(0);
p->m_value.resize(0);

p->m_key.resize(key.size() + 1);
memcpy(p->m_key.data(), key.c_str(), key.size());

p->m_value.resize(val.size() + 1);
if (val.size())
memcpy(p->m_value.data(), val.c_str(), val.size());
}

#endif // BASISD_SUPPORT_KTX2

// Returns true if the transcoder was compiled with KTX2 support.
Expand Down