summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/io/image.cpp7
-rw-r--r--core/io/image.h3
-rw-r--r--doc/classes/Array.xml8
-rw-r--r--doc/classes/Image.xml2
-rw-r--r--doc/classes/PortableCompressedTexture2D.xml79
-rw-r--r--editor/editor_node.cpp1
-rw-r--r--modules/basis_universal/register_types.cpp17
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/texture.cpp308
-rw-r--r--scene/resources/texture.h72
10 files changed, 490 insertions, 8 deletions
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 67e6724e71..fad9942017 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -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 {
diff --git a/core/io/image.h b/core/io/image.h
index 39c700565b..7e1e853244 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -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;
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 68cb615209..ddcb249f47 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -513,48 +513,56 @@
<return type="bool" />
<argument index="0" name="right" type="Array" />
<description>
+ Compares the current array against the array passed as argument: Returns [code]false[/code] if the compared arrays are the same object. Returns [code]true[/code] if the compared arrays have different sizes. Returns [code]false[/code] and prints an error if the comparison reaches the highest levels of nesting (100). Returns [code]true[/code] if the contents of the arrays are not equal, [code]false[/code] otherwise.
</description>
</operator>
<operator name="operator +">
<return type="Array" />
<argument index="0" name="right" type="Array" />
<description>
+ This operator is actually provided by the [Variant] class. For further details, see the available documentation for [Variant] or refer to the Godot source code (core/variant/variant_op).
</description>
</operator>
<operator name="operator &lt;">
<return type="bool" />
<argument index="0" name="right" type="Array" />
<description>
+ Performs a comparison for each index between the current array and the array passed as argument, considering the highest common index of both arrays for this comparison: Returns [code]true[/code] on the first occurrence of an element that is less, or [code]false[/code] if the element is greater. Note that depending on the type of data stored, this function will be recursive. If all elements are equal it compares the length of both arrays, it returns [code]false[/code] if the current array has less elements, returns [code]true[/code] otherwise.
</description>
</operator>
<operator name="operator &lt;=">
<return type="bool" />
<argument index="0" name="right" type="Array" />
<description>
+ Performs a comparison for each index between the current array and the array passed as argument, considering the highest common index of both arrays for this comparison: Returns [code]true[/code] on the first occurrence of an element that is less, or [code]false[/code] if the element is greater. Note that depending on the type of data stored, this function will be recursive. If all elements are equal it compares the length of both arrays, it returns [code]true[/code] if the current array has less or the same number of elements, otherwise it returns [code]false[/code].
</description>
</operator>
<operator name="operator ==">
<return type="bool" />
<argument index="0" name="right" type="Array" />
<description>
+ Compares the current array against the array passed as argument: Returns [code]true[/code] if the compared arrays are the same object. Returns [code]false[/code] if the compared arrays have different sizes. Returns [code]true[/code] and prints an error if the comparison reaches the highest levels of nesting (100). Returns [code]false[/code] if the contents of the arrays are not equal, [code]true[/code] otherwise.
</description>
</operator>
<operator name="operator &gt;">
<return type="bool" />
<argument index="0" name="right" type="Array" />
<description>
+ Performs a comparison for each index between the current array and the array passed as argument, considering the highest common index of both arrays for this comparison: Returns [code]true[/code] on the first occurrence of an element that is greater, or [code]false[/code] if the element is less. Note that depending on the type of data stored, this function will be recursive. If all elements are equal it compares the length of both arrays, it returns [code]true[/code] if the array passed as argument has more elements, otherwise it returns [code]false[/code].
</description>
</operator>
<operator name="operator &gt;=">
<return type="bool" />
<argument index="0" name="right" type="Array" />
<description>
+ Performs a comparison for each index between the current array and the array passed as argument, considering the highest common index of both arrays for this comparison: Returns [code]true[/code] on the first occurrence of an element that is greater, or [code]false[/code] if the element is less. Note that depending on the type of data stored, this function will be recursive. If all elements are equal it compares the length of both arrays, it returns [code]true[/code] if the array passed as argument has more or the same number of elements, otherwise it returns [code]false[/code].
</description>
</operator>
<operator name="operator []">
<return type="void" />
<argument index="0" name="index" type="int" />
<description>
+ Returns a reference to the element of type Variant at the specified location.
</description>
</operator>
</operators>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 35868563de..140015babf 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -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>
diff --git a/doc/classes/PortableCompressedTexture2D.xml b/doc/classes/PortableCompressedTexture2D.xml
new file mode 100644
index 0000000000..7b46a0bf07
--- /dev/null
+++ b/doc/classes/PortableCompressedTexture2D.xml
@@ -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>
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index bcdd54edd4..3249477d97 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -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;
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index 8e328a519d..4c2ebe603f 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -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;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 52d6f6e63b..032f43d9b9 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -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);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 3113987fbc..a66d9d9f27 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -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) {
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 1e07b83547..7e194fd21d 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -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);