You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-09 12:50:35 +00:00
BasisU: Update to 1.50.0 and add HDR support
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
|
||||
#include "image_compress_basisu.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
#include <transcoder/basisu_transcoder.h>
|
||||
@@ -46,9 +48,48 @@ void basis_universal_init() {
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
template <typename T>
|
||||
inline void _basisu_pad_mipmap(const uint8_t *p_image_mip_data, Vector<uint8_t> &r_mip_data_padded, int p_next_width, int p_next_height, int p_width, int p_height, int64_t p_size) {
|
||||
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
|
||||
const T *mip_src_data = reinterpret_cast<const T *>(p_image_mip_data);
|
||||
|
||||
// Reserve space in the padded buffer.
|
||||
r_mip_data_padded.resize(p_next_width * p_next_height * sizeof(T));
|
||||
T *data_padded_ptr = reinterpret_cast<T *>(r_mip_data_padded.ptrw());
|
||||
|
||||
// Pad mipmap to the nearest block by smearing.
|
||||
int x = 0, y = 0;
|
||||
for (y = 0; y < p_height; y++) {
|
||||
for (x = 0; x < p_width; x++) {
|
||||
data_padded_ptr[p_next_width * y + x] = mip_src_data[p_width * y + x];
|
||||
}
|
||||
|
||||
// First, smear in x.
|
||||
for (; x < p_next_width; x++) {
|
||||
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Then, smear in y.
|
||||
for (; y < p_next_height; y++) {
|
||||
for (x = 0; x < p_next_width; x++) {
|
||||
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - p_next_width];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
|
||||
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
|
||||
|
||||
Ref<Image> image = p_image->duplicate();
|
||||
image->convert(Image::FORMAT_RGBA8);
|
||||
bool is_hdr = false;
|
||||
|
||||
if (image->get_format() <= Image::FORMAT_RGB565) {
|
||||
image->convert(Image::FORMAT_RGBA8);
|
||||
} else if (image->get_format() <= Image::FORMAT_RGBE9995) {
|
||||
image->convert(Image::FORMAT_RGBAF);
|
||||
is_hdr = true;
|
||||
}
|
||||
|
||||
basisu::basis_compressor_params params;
|
||||
|
||||
@@ -74,32 +115,42 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
|
||||
basisu::job_pool job_pool(OS::get_singleton()->get_processor_count());
|
||||
params.m_pJob_pool = &job_pool;
|
||||
|
||||
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
|
||||
switch (p_channels) {
|
||||
case Image::USED_CHANNELS_L: {
|
||||
decompress_format = BASIS_DECOMPRESS_RGB;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_LA: {
|
||||
params.m_force_alpha = true;
|
||||
decompress_format = BASIS_DECOMPRESS_RGBA;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_R: {
|
||||
decompress_format = BASIS_DECOMPRESS_R;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_RG: {
|
||||
params.m_force_alpha = true;
|
||||
image->convert_rg_to_ra_rgba8();
|
||||
decompress_format = BASIS_DECOMPRESS_RG;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_RGB: {
|
||||
decompress_format = BASIS_DECOMPRESS_RGB;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_RGBA: {
|
||||
params.m_force_alpha = true;
|
||||
decompress_format = BASIS_DECOMPRESS_RGBA;
|
||||
} break;
|
||||
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_MAX;
|
||||
|
||||
if (is_hdr) {
|
||||
decompress_format = BASIS_DECOMPRESS_HDR_RGB;
|
||||
params.m_hdr = true;
|
||||
params.m_uastc_hdr_options.set_quality_level(0);
|
||||
|
||||
} else {
|
||||
switch (p_channels) {
|
||||
case Image::USED_CHANNELS_L: {
|
||||
decompress_format = BASIS_DECOMPRESS_RGB;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_LA: {
|
||||
params.m_force_alpha = true;
|
||||
decompress_format = BASIS_DECOMPRESS_RGBA;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_R: {
|
||||
decompress_format = BASIS_DECOMPRESS_R;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_RG: {
|
||||
params.m_force_alpha = true;
|
||||
image->convert_rg_to_ra_rgba8();
|
||||
decompress_format = BASIS_DECOMPRESS_RG;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_RGB: {
|
||||
decompress_format = BASIS_DECOMPRESS_RGB;
|
||||
} break;
|
||||
case Image::USED_CHANNELS_RGBA: {
|
||||
params.m_force_alpha = true;
|
||||
decompress_format = BASIS_DECOMPRESS_RGBA;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(decompress_format == BASIS_DECOMPRESS_MAX, Vector<uint8_t>());
|
||||
|
||||
// Copy the source image data with mipmaps into BasisU.
|
||||
{
|
||||
const int orig_width = image->get_width();
|
||||
@@ -113,9 +164,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
|
||||
|
||||
Vector<uint8_t> image_data = image->get_data();
|
||||
basisu::vector<basisu::image> basisu_mipmaps;
|
||||
basisu::vector<basisu::imagef> basisu_mipmaps_hdr;
|
||||
|
||||
// Buffer for storing padded mipmap data.
|
||||
Vector<uint32_t> mip_data_padded;
|
||||
Vector<uint8_t> mip_data_padded;
|
||||
|
||||
for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
|
||||
int64_t ofs, size;
|
||||
@@ -126,31 +178,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
|
||||
|
||||
// Pad the mipmap's data if its resolution isn't divisible by 4.
|
||||
if (image->has_mipmaps() && !is_res_div_4 && (width > 2 && height > 2) && (width != next_width || height != next_height)) {
|
||||
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
|
||||
const uint32_t *mip_src_data = reinterpret_cast<const uint32_t *>(image_mip_data);
|
||||
|
||||
// Reserve space in the padded buffer.
|
||||
mip_data_padded.resize(next_width * next_height);
|
||||
uint32_t *data_padded_ptr = mip_data_padded.ptrw();
|
||||
|
||||
// Pad mipmap to the nearest block by smearing.
|
||||
int x = 0, y = 0;
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
data_padded_ptr[next_width * y + x] = mip_src_data[width * y + x];
|
||||
}
|
||||
|
||||
// First, smear in x.
|
||||
for (; x < next_width; x++) {
|
||||
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Then, smear in y.
|
||||
for (; y < next_height; y++) {
|
||||
for (x = 0; x < next_width; x++) {
|
||||
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - next_width];
|
||||
}
|
||||
if (is_hdr) {
|
||||
_basisu_pad_mipmap<BasisRGBAF>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
|
||||
} else {
|
||||
_basisu_pad_mipmap<uint32_t>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
|
||||
}
|
||||
|
||||
// Override the image_mip_data pointer with our temporary Vector.
|
||||
@@ -159,7 +190,7 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
|
||||
// Override the mipmap's properties.
|
||||
width = next_width;
|
||||
height = next_height;
|
||||
size = mip_data_padded.size() * 4;
|
||||
size = mip_data_padded.size();
|
||||
}
|
||||
|
||||
// Get the next mipmap's resolution.
|
||||
@@ -167,44 +198,61 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
|
||||
next_height /= 2;
|
||||
|
||||
// Copy the source mipmap's data to a BasisU image.
|
||||
basisu::image basisu_image(width, height);
|
||||
memcpy(basisu_image.get_ptr(), image_mip_data, size);
|
||||
if (is_hdr) {
|
||||
basisu::imagef basisu_image(width, height);
|
||||
memcpy(reinterpret_cast<uint8_t *>(basisu_image.get_ptr()), image_mip_data, size);
|
||||
|
||||
if (i == 0) {
|
||||
params.m_source_images_hdr.push_back(basisu_image);
|
||||
} else {
|
||||
basisu_mipmaps_hdr.push_back(basisu_image);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
params.m_source_images.push_back(basisu_image);
|
||||
} else {
|
||||
basisu_mipmaps.push_back(basisu_image);
|
||||
basisu::image basisu_image(width, height);
|
||||
memcpy(basisu_image.get_ptr(), image_mip_data, size);
|
||||
|
||||
if (i == 0) {
|
||||
params.m_source_images.push_back(basisu_image);
|
||||
} else {
|
||||
basisu_mipmaps.push_back(basisu_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.m_source_mipmap_images.push_back(basisu_mipmaps);
|
||||
if (is_hdr) {
|
||||
params.m_source_mipmap_images_hdr.push_back(basisu_mipmaps_hdr);
|
||||
} else {
|
||||
params.m_source_mipmap_images.push_back(basisu_mipmaps);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the image data.
|
||||
Vector<uint8_t> basisu_data;
|
||||
|
||||
basisu::basis_compressor compressor;
|
||||
compressor.init(params);
|
||||
|
||||
int basisu_err = compressor.process();
|
||||
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, basisu_data);
|
||||
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, Vector<uint8_t>());
|
||||
|
||||
const basisu::uint8_vec &basisu_out = compressor.get_output_basis_file();
|
||||
basisu_data.resize(basisu_out.size() + 4);
|
||||
const basisu::uint8_vec &basisu_encoded = compressor.get_output_basis_file();
|
||||
|
||||
// Copy the encoded data to the buffer.
|
||||
{
|
||||
uint8_t *wb = basisu_data.ptrw();
|
||||
*(uint32_t *)wb = decompress_format;
|
||||
Vector<uint8_t> basisu_data;
|
||||
basisu_data.resize(basisu_encoded.size() + 4);
|
||||
uint8_t *basisu_data_ptr = basisu_data.ptrw();
|
||||
|
||||
memcpy(wb + 4, basisu_out.get_ptr(), basisu_out.size());
|
||||
}
|
||||
// Copy the encoded BasisU data into the output buffer.
|
||||
*(uint32_t *)basisu_data_ptr = decompress_format;
|
||||
memcpy(basisu_data_ptr + 4, basisu_encoded.get_ptr(), basisu_encoded.size());
|
||||
|
||||
print_verbose(vformat("BasisU: Encoding a %dx%d image with %d mipmaps took %d ms.", p_image->get_width(), p_image->get_height(), p_image->get_mipmap_count(), OS::get_singleton()->get_ticks_msec() - start_time));
|
||||
|
||||
return basisu_data;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
|
||||
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
|
||||
|
||||
Ref<Image> image;
|
||||
ERR_FAIL_NULL_V_MSG(p_data, image, "Cannot unpack invalid BasisUniversal data.");
|
||||
|
||||
@@ -320,6 +368,23 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
|
||||
}
|
||||
|
||||
} break;
|
||||
case BASIS_DECOMPRESS_HDR_RGB: {
|
||||
if (bptc_supported) {
|
||||
basisu_format = basist::transcoder_texture_format::cTFBC6H;
|
||||
image_format = Image::FORMAT_BPTC_RGBFU;
|
||||
} else if (astc_supported) {
|
||||
basisu_format = basist::transcoder_texture_format::cTFASTC_HDR_4x4_RGBA;
|
||||
image_format = Image::FORMAT_ASTC_4x4_HDR;
|
||||
} else {
|
||||
// No supported VRAM compression formats, decompress.
|
||||
basisu_format = basist::transcoder_texture_format::cTFRGB_9E5;
|
||||
image_format = Image::FORMAT_RGBE9995;
|
||||
}
|
||||
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL_V(image);
|
||||
} break;
|
||||
}
|
||||
|
||||
src_ptr += 4;
|
||||
@@ -371,6 +436,9 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
|
||||
}
|
||||
}
|
||||
|
||||
print_verbose(vformat("BasisU: Transcoding a %dx%d image with %d mipmaps into %s took %d ms.",
|
||||
image->get_width(), image->get_height(), image->get_mipmap_count(), Image::get_format_name(image_format), OS::get_singleton()->get_ticks_msec() - start_time));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user