Add PortableCompressedTexture

* Resource that allows saving textures embedded in scenes or standalone.
* Supports only formats that are portable: Lossy, Lossles or BasisUniversal

This is something I wanted to add for a long time. I made it now because @fire
requires it for importing GLTF2 files with embedded textures, but also this
will allow saving Godot scenes as standalone binary files that will run
in all platforms (because textures will load everywhere).

This is ideal when you want to distribute individual standalone assets online
in games that can be built from Godot scenes.
This commit is contained in:
reduz 2022-03-24 18:18:55 +01:00
parent f6ef63635f
commit 45f74ceb85
9 changed files with 482 additions and 8 deletions

View file

@ -2726,6 +2726,7 @@ Vector<uint8_t> (*Image::png_packer)(const Ref<Image> &) = nullptr;
Ref<Image> (*Image::png_unpacker)(const Vector<uint8_t> &) = nullptr;
Vector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = nullptr;
Ref<Image> (*Image::basis_universal_unpacker)(const Vector<uint8_t> &) = nullptr;
Ref<Image> (*Image::basis_universal_unpacker_ptr)(const uint8_t *, int) = nullptr;
void Image::_set_data(const Dictionary &p_data) {
ERR_FAIL_COND(!p_data.has("width"));
@ -3008,7 +3009,7 @@ void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation)
}
}
Image::UsedChannels Image::detect_used_channels(CompressSource p_source) {
Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const {
ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA);
ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA);
bool r = false, g = false, b = false, a = false, c = false;
@ -3613,6 +3614,10 @@ Image::Image(const uint8_t *p_mem_png_jpg, int p_len) {
if (is_empty() && _jpg_mem_loader_func) {
copy_internals_from(_jpg_mem_loader_func(p_mem_png_jpg, p_len));
}
if (is_empty() && _webp_mem_loader_func) {
copy_internals_from(_webp_mem_loader_func(p_mem_png_jpg, p_len));
}
}
Ref<Resource> Image::duplicate(bool p_subresources) const {

View file

@ -147,6 +147,7 @@ public:
static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
static Vector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels);
static Ref<Image> (*basis_universal_unpacker)(const Vector<uint8_t> &p_buffer);
static Ref<Image> (*basis_universal_unpacker_ptr)(const uint8_t *p_data, int p_size);
_FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const;
_FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
@ -379,7 +380,7 @@ public:
virtual Ref<Resource> duplicate(bool p_subresources = false) const override;
UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC);
UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC) const;
void optimize_channels();
Color get_pixelv(const Point2i &p_point) const;

View file

@ -153,7 +153,7 @@
Returns [constant ALPHA_BLEND] if the image has data for alpha values. Returns [constant ALPHA_BIT] if all the alpha values are stored in a single bit. Returns [constant ALPHA_NONE] if no data for alpha values is found.
</description>
</method>
<method name="detect_used_channels">
<method name="detect_used_channels" qualifiers="const">
<return type="int" enum="Image.UsedChannels" />
<argument index="0" name="source" type="int" enum="Image.CompressSource" default="0" />
<description>

View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PortableCompressedTexture2D" inherits="Texture2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Provides a compressed texture for disk and/or VRAM in a way that is portable.
</brief_description>
<description>
This class allows storing compressed textures as self contained (not imported) resources.
For 2D usage (compressed on disk, uncompressed on VRAM), the lossy and lossless modes are recommended. For 3D usage (compressed on VRAM) it depends on the target platform.
If you intend to only use desktop, S3TC or BPTC are recommended. For only mobile, ETC2 is recommended.
For portable, self contained 3D textures that work on both desktop and mobile, Basis Universal is recommended (although it has a small quality cost and longer compression time as a tradeoff).
This resource is intended to be created from code.
</description>
<tutorials>
</tutorials>
<methods>
<method name="create_from_image">
<return type="void" />
<argument index="0" name="image" type="Image" />
<argument index="1" name="compression_mode" type="int" enum="PortableCompressedTexture2D.CompressionMode" />
<argument index="2" name="normal_map" type="bool" default="false" />
<argument index="3" name="lossy_quality" type="float" default="0.8" />
<description>
Initializes the compressed texture from a base image. The compression mode must be provided.
If this image will be used as a normal map, the "normal map" flag is recommended, to ensure optimum quality.
If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WEBP compression quality.
</description>
</method>
<method name="get_compression_mode" qualifiers="const">
<return type="int" enum="PortableCompressedTexture2D.CompressionMode" />
<description>
Return the compression mode used (valid after initialized).
</description>
</method>
<method name="get_format" qualifiers="const">
<return type="int" enum="Image.Format" />
<description>
Return the image format used (valid after initialized).
</description>
</method>
<method name="is_keeping_all_compressed_buffers" qualifiers="static">
<return type="bool" />
<description>
Return whether the flag is overriden for all textures of this type.
</description>
</method>
<method name="set_keep_all_compressed_buffers" qualifiers="static">
<return type="void" />
<argument index="0" name="keep" type="bool" />
<description>
Overrides the flag globally for all textures of this type. This is used primarly by the editor.
</description>
</method>
</methods>
<members>
<member name="_data" type="PackedByteArray" setter="_set_data" getter="_get_data" default="PackedByteArray()">
</member>
<member name="keep_compressed_buffer" type="bool" setter="set_keep_compressed_buffer" getter="is_keeping_compressed_buffer" default="false">
When running on the editor, this class will keep the source compressed data in memory. Otherwise, the source compressed data is lost after loading and the resource can't be re saved.
This flag allows to keep the compressed data in memory if you intend it to persist after loading.
</member>
<member name="size_override" type="Vector2" setter="set_size_override" getter="get_size_override" default="Vector2(0, 0)">
Allow overriding the texture size (for 2D only).
</member>
</members>
<constants>
<constant name="COMPRESSION_MODE_LOSSLESS" value="0" enum="CompressionMode">
</constant>
<constant name="COMPRESSION_MODE_LOSSY" value="1" enum="CompressionMode">
</constant>
<constant name="COMPRESSION_MODE_BASIS_UNIVERSAL" value="2" enum="CompressionMode">
</constant>
<constant name="COMPRESSION_MODE_S3TC" value="3" enum="CompressionMode">
</constant>
<constant name="COMPRESSION_MODE_ETC2" value="4" enum="CompressionMode">
</constant>
<constant name="COMPRESSION_MODE_BPTC" value="5" enum="CompressionMode">
</constant>
</constants>
</class>

View file

@ -5852,6 +5852,7 @@ EditorNode::EditorNode() {
EditorPropertyNameProcessor *epnp = memnew(EditorPropertyNameProcessor);
add_child(epnp);
PortableCompressedTexture2D::set_keep_all_compressed_buffers(true);
Input::get_singleton()->set_use_accumulated_input(true);
Resource::_get_local_scene_func = _resource_get_edited_scene;

View file

@ -143,12 +143,11 @@ static Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::
}
#endif // TOOLS_ENABLED
static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
Ref<Image> image;
const uint8_t *r = p_buffer.ptr();
const uint8_t *ptr = r;
int size = p_buffer.size();
const uint8_t *ptr = p_data;
int size = p_size;
basist::transcoder_texture_format format = basist::transcoder_texture_format::cTFTotalTextureFormats;
Image::Format imgfmt = Image::FORMAT_MAX;
@ -259,6 +258,14 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
return image;
}
static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
Ref<Image> image;
const uint8_t *r = p_buffer.ptr();
int size = p_buffer.size();
return basis_universal_unpacker_ptr(r, size);
}
void register_basis_universal_types() {
#ifdef TOOLS_ENABLED
using namespace basisu;
@ -267,6 +274,7 @@ void register_basis_universal_types() {
Image::basis_universal_packer = basis_universal_packer;
#endif
Image::basis_universal_unpacker = basis_universal_unpacker;
Image::basis_universal_unpacker_ptr = basis_universal_unpacker_ptr;
}
void unregister_basis_universal_types() {
@ -274,4 +282,5 @@ void unregister_basis_universal_types() {
Image::basis_universal_packer = nullptr;
#endif
Image::basis_universal_unpacker = nullptr;
Image::basis_universal_unpacker_ptr = nullptr;
}

View file

@ -809,6 +809,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(Texture2D);
GDREGISTER_CLASS(Sky);
GDREGISTER_CLASS(CompressedTexture2D);
GDREGISTER_CLASS(PortableCompressedTexture2D);
GDREGISTER_CLASS(ImageTexture);
GDREGISTER_CLASS(AtlasTexture);
GDREGISTER_CLASS(MeshTexture);

View file

@ -32,12 +32,12 @@
#include "core/core_string_names.h"
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
#include "servers/camera/camera_feed.h"
int Texture2D::get_width() const {
int ret;
if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
@ -338,6 +338,312 @@ ImageTexture::~ImageTexture() {
}
}
/////////////////////
void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
if (p_data.size() == 0) {
return; //nothing to do
}
const uint8_t *data = p_data.ptr();
uint32_t data_size = p_data.size();
ERR_FAIL_COND(data_size < 20);
compression_mode = CompressionMode(decode_uint32(data + 0));
format = Image::Format(decode_uint32(data + 4));
uint32_t mipmap_count = decode_uint32(data + 8);
size.width = decode_uint32(data + 12);
size.height = decode_uint32(data + 16);
mipmaps = mipmap_count > 1;
data += 20;
data_size -= 20;
Ref<Image> image;
switch (compression_mode) {
case COMPRESSION_MODE_LOSSLESS:
case COMPRESSION_MODE_LOSSY: {
Vector<uint8_t> image_data;
ERR_FAIL_COND(data_size < 4);
for (uint32_t i = 0; i < mipmap_count; i++) {
uint32_t mipsize = decode_uint32(data);
data += 4;
data_size -= 4;
ERR_FAIL_COND(mipsize < data_size);
Ref<Image> img = memnew(Image(data, data_size));
ERR_FAIL_COND(img->is_empty());
if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps.
img->convert(format);
}
image_data.append_array(img->get_data());
data += mipsize;
data_size -= mipsize;
}
image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data)));
} break;
case COMPRESSION_MODE_BASIS_UNIVERSAL: {
ERR_FAIL_COND(!Image::basis_universal_unpacker_ptr);
image = Image::basis_universal_unpacker_ptr(data, data_size);
} break;
case COMPRESSION_MODE_S3TC:
case COMPRESSION_MODE_ETC2:
case COMPRESSION_MODE_BPTC: {
image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20))));
} break;
}
ERR_FAIL_COND(image.is_null());
if (texture.is_null()) {
texture = RenderingServer::get_singleton()->texture_2d_create(image);
} else {
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image);
RenderingServer::get_singleton()->texture_replace(texture, new_texture);
}
image_stored = true;
RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
alpha_cache.unref();
if (keep_all_compressed_buffers || keep_compressed_buffer) {
compressed_buffer = p_data;
} else {
compressed_buffer.clear();
}
}
PortableCompressedTexture2D::CompressionMode PortableCompressedTexture2D::get_compression_mode() const {
return compression_mode;
}
Vector<uint8_t> PortableCompressedTexture2D::_get_data() const {
return compressed_buffer;
}
void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map, float p_lossy_quality) {
ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
Vector<uint8_t> buffer;
buffer.resize(20);
encode_uint32(p_compression_mode, buffer.ptrw());
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_width(), buffer.ptrw() + 12);
encode_uint32(p_image->get_height(), buffer.ptrw() + 16);
switch (p_compression_mode) {
case COMPRESSION_MODE_LOSSLESS:
case COMPRESSION_MODE_LOSSY: {
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
Vector<uint8_t> data;
if (p_compression_mode == COMPRESSION_MODE_LOSSY) {
data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
} else {
data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
}
int data_len = data.size();
buffer.resize(buffer.size() + 4);
encode_uint32(data_len, buffer.ptrw() + buffer.size() - 4);
buffer.append_array(data);
}
} break;
case COMPRESSION_MODE_BASIS_UNIVERSAL: {
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);
buffer.append_array(budata);
} break;
case COMPRESSION_MODE_S3TC:
case COMPRESSION_MODE_ETC2:
case COMPRESSION_MODE_BPTC: {
Ref<Image> copy = p_image->duplicate();
switch (p_compression_mode) {
case COMPRESSION_MODE_S3TC:
copy->compress(Image::COMPRESS_S3TC);
break;
case COMPRESSION_MODE_ETC2:
copy->compress(Image::COMPRESS_ETC2);
break;
case COMPRESSION_MODE_BPTC:
copy->compress(Image::COMPRESS_BPTC);
break;
default: {
};
}
buffer.append_array(copy->get_data());
} break;
}
_set_data(buffer);
}
Image::Format PortableCompressedTexture2D::get_format() const {
return format;
}
Ref<Image> PortableCompressedTexture2D::get_image() const {
if (image_stored) {
return RenderingServer::get_singleton()->texture_2d_get(texture);
} else {
return Ref<Image>();
}
}
int PortableCompressedTexture2D::get_width() const {
return size.width;
}
int PortableCompressedTexture2D::get_height() const {
return size.height;
}
RID PortableCompressedTexture2D::get_rid() const {
if (texture.is_null()) {
//we are in trouble, create something temporary
texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
}
return texture;
}
bool PortableCompressedTexture2D::has_alpha() const {
return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
}
void PortableCompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if (size.width == 0 || size.height == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, size), texture, false, p_modulate, p_transpose);
}
void PortableCompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if (size.width == 0 || size.height == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
void PortableCompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if (size.width == 0 || size.height == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
if (!alpha_cache.is_valid()) {
Ref<Image> img = get_image();
if (img.is_valid()) {
if (img->is_compressed()) { //must decompress, if compressed
Ref<Image> decom = img->duplicate();
decom->decompress();
img = decom;
}
alpha_cache.instantiate();
alpha_cache->create_from_image_alpha(img);
}
}
if (alpha_cache.is_valid()) {
int aw = int(alpha_cache->get_size().width);
int ah = int(alpha_cache->get_size().height);
if (aw == 0 || ah == 0) {
return true;
}
int x = p_x * aw / size.width;
int y = p_y * ah / size.height;
x = CLAMP(x, 0, aw);
y = CLAMP(y, 0, ah);
return alpha_cache->get_bit(Point2(x, y));
}
return true;
}
void PortableCompressedTexture2D::set_size_override(const Size2 &p_size) {
size_override = p_size;
RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
}
Size2 PortableCompressedTexture2D::get_size_override() const {
return size_override;
}
void PortableCompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
if (texture.is_valid()) {
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
Resource::set_path(p_path, p_take_over);
}
bool PortableCompressedTexture2D::keep_all_compressed_buffers = false;
void PortableCompressedTexture2D::set_keep_all_compressed_buffers(bool p_keep) {
keep_all_compressed_buffers = p_keep;
}
bool PortableCompressedTexture2D::is_keeping_all_compressed_buffers() {
return keep_all_compressed_buffers;
}
void PortableCompressedTexture2D::set_keep_compressed_buffer(bool p_keep) {
keep_compressed_buffer = p_keep;
if (!p_keep) {
compressed_buffer.clear();
}
}
bool PortableCompressedTexture2D::is_keeping_compressed_buffer() const {
return keep_compressed_buffer;
}
void PortableCompressedTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_from_image", "image", "compression_mode", "normal_map", "lossy_quality"), &PortableCompressedTexture2D::create_from_image, DEFVAL(false), DEFVAL(0.8));
ClassDB::bind_method(D_METHOD("get_format"), &PortableCompressedTexture2D::get_format);
ClassDB::bind_method(D_METHOD("get_compression_mode"), &PortableCompressedTexture2D::get_compression_mode);
ClassDB::bind_method(D_METHOD("set_size_override", "size"), &PortableCompressedTexture2D::set_size_override);
ClassDB::bind_method(D_METHOD("get_size_override"), &PortableCompressedTexture2D::get_size_override);
ClassDB::bind_method(D_METHOD("set_keep_compressed_buffer", "keep"), &PortableCompressedTexture2D::set_keep_compressed_buffer);
ClassDB::bind_method(D_METHOD("is_keeping_compressed_buffer"), &PortableCompressedTexture2D::is_keeping_compressed_buffer);
ClassDB::bind_method(D_METHOD("_set_data", "data"), &PortableCompressedTexture2D::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &PortableCompressedTexture2D::_get_data);
ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("set_keep_all_compressed_buffers", "keep"), &PortableCompressedTexture2D::set_keep_all_compressed_buffers);
ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("is_keeping_all_compressed_buffers"), &PortableCompressedTexture2D::is_keeping_all_compressed_buffers);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_override"), "set_size_override", "get_size_override");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_compressed_buffer"), "set_keep_compressed_buffer", "is_keeping_compressed_buffer");
BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSLESS);
BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSY);
BIND_ENUM_CONSTANT(COMPRESSION_MODE_BASIS_UNIVERSAL);
BIND_ENUM_CONSTANT(COMPRESSION_MODE_S3TC);
BIND_ENUM_CONSTANT(COMPRESSION_MODE_ETC2);
BIND_ENUM_CONSTANT(COMPRESSION_MODE_BPTC);
}
PortableCompressedTexture2D::PortableCompressedTexture2D() {}
PortableCompressedTexture2D::~PortableCompressedTexture2D() {
if (texture.is_valid()) {
RenderingServer::get_singleton()->free(texture);
}
}
//////////////////////////////////////////
Ref<Image> CompressedTexture2D::load_image_from_file(FileAccess *f, int p_size_limit) {

View file

@ -137,6 +137,78 @@ public:
~ImageTexture();
};
class PortableCompressedTexture2D : public Texture2D {
GDCLASS(PortableCompressedTexture2D, Texture2D);
public:
enum CompressionMode {
COMPRESSION_MODE_LOSSLESS,
COMPRESSION_MODE_LOSSY,
COMPRESSION_MODE_BASIS_UNIVERSAL,
COMPRESSION_MODE_S3TC,
COMPRESSION_MODE_ETC2,
COMPRESSION_MODE_BPTC,
};
private:
CompressionMode compression_mode = COMPRESSION_MODE_LOSSLESS;
static bool keep_all_compressed_buffers;
bool keep_compressed_buffer = false;
Vector<uint8_t> compressed_buffer;
Size2 size;
Size2 size_override;
bool mipmaps = false;
Image::Format format = Image::FORMAT_L8;
mutable RID texture;
mutable Ref<BitMap> alpha_cache;
bool image_stored = false;
protected:
Vector<uint8_t> _get_data() const;
void _set_data(const Vector<uint8_t> &p_data);
static void _bind_methods();
public:
CompressionMode get_compression_mode() const;
void create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map = false, float p_lossy_quality = 0.8);
Image::Format get_format() const;
void update(const Ref<Image> &p_image);
Ref<Image> get_image() const override;
int get_width() const override;
int get_height() const override;
virtual RID get_rid() const override;
bool has_alpha() const override;
virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
virtual void set_path(const String &p_path, bool p_take_over = false) override;
void set_size_override(const Size2 &p_size);
Size2 get_size_override() const;
void set_keep_compressed_buffer(bool p_keep);
bool is_keeping_compressed_buffer() const;
static void set_keep_all_compressed_buffers(bool p_keep);
static bool is_keeping_all_compressed_buffers();
PortableCompressedTexture2D();
~PortableCompressedTexture2D();
};
VARIANT_ENUM_CAST(PortableCompressedTexture2D::CompressionMode)
class CompressedTexture2D : public Texture2D {
GDCLASS(CompressedTexture2D, Texture2D);