Fix lossless formats in PortableCompressedTexture2D

Update scene/resources/portable_compressed_texture.cpp

Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
This commit is contained in:
nklbdev 2023-09-06 04:44:18 +05:00
parent 13a0d6e9b2
commit 47d991678d
7 changed files with 47 additions and 10 deletions

View File

@ -3013,6 +3013,7 @@ void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) {
} }
ImageMemLoadFunc Image::_png_mem_loader_func = nullptr; ImageMemLoadFunc Image::_png_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_png_mem_unpacker_func = nullptr;
ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr; ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;

View File

@ -145,6 +145,7 @@ public:
}; };
static ImageMemLoadFunc _png_mem_loader_func; static ImageMemLoadFunc _png_mem_loader_func;
static ImageMemLoadFunc _png_mem_unpacker_func;
static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _jpg_mem_loader_func;
static ImageMemLoadFunc _webp_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func;
static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _tga_mem_loader_func;

View File

@ -66,12 +66,14 @@ Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) {
return img; return img;
} }
Ref<Image> ImageLoaderPNG::unpack_mem_png(const uint8_t *p_png, int p_size) {
ERR_FAIL_COND_V(p_size < 4, Ref<Image>());
ERR_FAIL_COND_V(p_png[0] != 'P' || p_png[1] != 'N' || p_png[2] != 'G' || p_png[3] != ' ', Ref<Image>());
return load_mem_png(&p_png[4], p_size - 4);
}
Ref<Image> ImageLoaderPNG::lossless_unpack_png(const Vector<uint8_t> &p_data) { Ref<Image> ImageLoaderPNG::lossless_unpack_png(const Vector<uint8_t> &p_data) {
const int len = p_data.size(); return unpack_mem_png(p_data.ptr(), p_data.size());
ERR_FAIL_COND_V(len < 4, Ref<Image>());
const uint8_t *r = p_data.ptr();
ERR_FAIL_COND_V(r[0] != 'P' || r[1] != 'N' || r[2] != 'G' || r[3] != ' ', Ref<Image>());
return load_mem_png(&r[4], len - 4);
} }
Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) {
@ -99,6 +101,7 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) {
ImageLoaderPNG::ImageLoaderPNG() { ImageLoaderPNG::ImageLoaderPNG() {
Image::_png_mem_loader_func = load_mem_png; Image::_png_mem_loader_func = load_mem_png;
Image::_png_mem_unpacker_func = unpack_mem_png;
Image::png_unpacker = lossless_unpack_png; Image::png_unpacker = lossless_unpack_png;
Image::png_packer = lossless_pack_png; Image::png_packer = lossless_pack_png;
} }

View File

@ -37,6 +37,7 @@ class ImageLoaderPNG : public ImageFormatLoader {
private: private:
static Vector<uint8_t> lossless_pack_png(const Ref<Image> &p_image); static Vector<uint8_t> lossless_pack_png(const Ref<Image> &p_image);
static Ref<Image> lossless_unpack_png(const Vector<uint8_t> &p_data); static Ref<Image> lossless_unpack_png(const Vector<uint8_t> &p_data);
static Ref<Image> unpack_mem_png(const uint8_t *p_png, int p_size);
static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size);
public: public:

View File

@ -38,6 +38,7 @@
#include "scene/resources/atlas_texture.h" #include "scene/resources/atlas_texture.h"
#include "scene/resources/compressed_texture.h" #include "scene/resources/compressed_texture.h"
#include "scene/resources/image_texture.h" #include "scene/resources/image_texture.h"
#include "scene/resources/portable_compressed_texture.h"
TextureRect *TexturePreview::get_texture_display() { TextureRect *TexturePreview::get_texture_display() {
return texture_display; return texture_display;
@ -158,7 +159,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
} }
bool EditorInspectorPluginTexture::can_handle(Object *p_object) { bool EditorInspectorPluginTexture::can_handle(Object *p_object) {
return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<CompressedTexture2D>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr || Object::cast_to<Image>(p_object) != nullptr; return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<CompressedTexture2D>(p_object) != nullptr || Object::cast_to<PortableCompressedTexture2D>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr || Object::cast_to<Image>(p_object) != nullptr;
} }
void EditorInspectorPluginTexture::parse_begin(Object *p_object) { void EditorInspectorPluginTexture::parse_begin(Object *p_object) {

View File

@ -40,6 +40,7 @@ class CompressedTexture2D : public Texture2D {
public: public:
enum DataFormat { enum DataFormat {
DATA_FORMAT_UNDEFINED,
DATA_FORMAT_IMAGE, DATA_FORMAT_IMAGE,
DATA_FORMAT_PNG, DATA_FORMAT_PNG,
DATA_FORMAT_WEBP, DATA_FORMAT_WEBP,

View File

@ -30,8 +30,10 @@
#include "portable_compressed_texture.h" #include "portable_compressed_texture.h"
#include "core/config/project_settings.h"
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "scene/resources/bit_map.h" #include "scene/resources/bit_map.h"
#include "scene/resources/compressed_texture.h"
void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
if (p_data.size() == 0) { if (p_data.size() == 0) {
@ -41,7 +43,8 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
const uint8_t *data = p_data.ptr(); const uint8_t *data = p_data.ptr();
uint32_t data_size = p_data.size(); uint32_t data_size = p_data.size();
ERR_FAIL_COND(data_size < 20); ERR_FAIL_COND(data_size < 20);
compression_mode = CompressionMode(decode_uint32(data + 0)); compression_mode = CompressionMode(decode_uint16(data));
CompressedTexture2D::DataFormat data_format = CompressedTexture2D::DataFormat(decode_uint16(data + 2));
format = Image::Format(decode_uint32(data + 4)); format = Image::Format(decode_uint32(data + 4));
uint32_t mipmap_count = decode_uint32(data + 8); uint32_t mipmap_count = decode_uint32(data + 8);
size.width = decode_uint32(data + 12); size.width = decode_uint32(data + 12);
@ -56,6 +59,16 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
switch (compression_mode) { switch (compression_mode) {
case COMPRESSION_MODE_LOSSLESS: case COMPRESSION_MODE_LOSSLESS:
case COMPRESSION_MODE_LOSSY: { case COMPRESSION_MODE_LOSSY: {
ImageMemLoadFunc loader_func;
if (data_format == CompressedTexture2D::DATA_FORMAT_UNDEFINED) {
loader_func = nullptr;
} else if (data_format == CompressedTexture2D::DATA_FORMAT_PNG) {
loader_func = Image::_png_mem_unpacker_func;
} else if (data_format == CompressedTexture2D::DATA_FORMAT_WEBP) {
loader_func = Image::_webp_mem_loader_func;
} else {
ERR_FAIL();
}
Vector<uint8_t> image_data; Vector<uint8_t> image_data;
ERR_FAIL_COND(data_size < 4); ERR_FAIL_COND(data_size < 4);
@ -64,7 +77,9 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
data += 4; data += 4;
data_size -= 4; data_size -= 4;
ERR_FAIL_COND(mipsize < data_size); ERR_FAIL_COND(mipsize < data_size);
Ref<Image> img = memnew(Image(data, data_size)); Ref<Image> img = loader_func == nullptr
? memnew(Image(data, data_size))
: Ref<Image>(loader_func(data, data_size));
ERR_FAIL_COND(img->is_empty()); ERR_FAIL_COND(img->is_empty());
if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps. if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps.
img->convert(format); img->convert(format);
@ -99,6 +114,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
} }
image_stored = true; image_stored = true;
size_override = size;
RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height); RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
alpha_cache.unref(); alpha_cache.unref();
@ -122,7 +138,8 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C
Vector<uint8_t> buffer; Vector<uint8_t> buffer;
buffer.resize(20); buffer.resize(20);
encode_uint32(p_compression_mode, buffer.ptrw()); encode_uint16(p_compression_mode, buffer.ptrw());
encode_uint16(CompressedTexture2D::DATA_FORMAT_UNDEFINED, buffer.ptrw() + 2);
encode_uint32(p_image->get_format(), buffer.ptrw() + 4); encode_uint32(p_image->get_format(), buffer.ptrw() + 4);
encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8); encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8);
encode_uint32(p_image->get_width(), buffer.ptrw() + 12); encode_uint32(p_image->get_width(), buffer.ptrw() + 12);
@ -131,12 +148,22 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C
switch (p_compression_mode) { switch (p_compression_mode) {
case COMPRESSION_MODE_LOSSLESS: case COMPRESSION_MODE_LOSSLESS:
case COMPRESSION_MODE_LOSSY: { case COMPRESSION_MODE_LOSSY: {
bool lossless_force_png = GLOBAL_GET("rendering/textures/lossless_compression/force_png") ||
!Image::_webp_mem_loader_func; // WebP module disabled.
bool use_webp = !lossless_force_png && p_image->get_width() <= 16383 && p_image->get_height() <= 16383; // WebP has a size limit.
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
Vector<uint8_t> data; Vector<uint8_t> data;
if (p_compression_mode == COMPRESSION_MODE_LOSSY) { if (p_compression_mode == COMPRESSION_MODE_LOSSY) {
data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
encode_uint16(CompressedTexture2D::DATA_FORMAT_WEBP, buffer.ptrw() + 2);
} else { } else {
data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); if (use_webp) {
data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
encode_uint16(CompressedTexture2D::DATA_FORMAT_WEBP, buffer.ptrw() + 2);
} else {
data = Image::png_packer(p_image->get_image_from_mipmap(i));
encode_uint16(CompressedTexture2D::DATA_FORMAT_PNG, buffer.ptrw() + 2);
}
} }
int data_len = data.size(); int data_len = data.size();
buffer.resize(buffer.size() + 4); buffer.resize(buffer.size() + 4);
@ -145,6 +172,7 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C
} }
} break; } break;
case COMPRESSION_MODE_BASIS_UNIVERSAL: { case COMPRESSION_MODE_BASIS_UNIVERSAL: {
encode_uint16(CompressedTexture2D::DATA_FORMAT_BASIS_UNIVERSAL, buffer.ptrw() + 2);
Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC); Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC);
Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc); Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc);
buffer.append_array(budata); buffer.append_array(budata);
@ -153,6 +181,7 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C
case COMPRESSION_MODE_S3TC: case COMPRESSION_MODE_S3TC:
case COMPRESSION_MODE_ETC2: case COMPRESSION_MODE_ETC2:
case COMPRESSION_MODE_BPTC: { case COMPRESSION_MODE_BPTC: {
encode_uint16(CompressedTexture2D::DATA_FORMAT_IMAGE, buffer.ptrw() + 2);
Ref<Image> copy = p_image->duplicate(); Ref<Image> copy = p_image->duplicate();
switch (p_compression_mode) { switch (p_compression_mode) {
case COMPRESSION_MODE_S3TC: case COMPRESSION_MODE_S3TC: