summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/io/dir_access.cpp69
-rw-r--r--core/io/dir_access.h13
-rw-r--r--core/io/image_loader.cpp54
-rw-r--r--core/io/image_loader.h51
-rw-r--r--core/io/json.cpp23
-rw-r--r--core/io/json.h6
-rw-r--r--core/io/stream_peer_gzip.cpp209
-rw-r--r--core/io/stream_peer_gzip.h76
-rw-r--r--core/register_core_types.cpp4
-rw-r--r--doc/classes/DirAccess.xml93
-rw-r--r--doc/classes/FileAccess.xml1
-rw-r--r--doc/classes/ImageFormatLoader.xml19
-rw-r--r--doc/classes/ImageFormatLoaderExtension.xml42
-rw-r--r--doc/classes/StreamPeerGZIP.xml42
-rw-r--r--drivers/png/image_loader_png.cpp2
-rw-r--r--drivers/png/image_loader_png.h2
-rw-r--r--drivers/register_driver_types.cpp9
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_settings_dialog.cpp53
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp2
-rw-r--r--modules/bmp/image_loader_bmp.cpp2
-rw-r--r--modules/bmp/image_loader_bmp.h2
-rw-r--r--modules/bmp/register_types.cpp7
-rw-r--r--modules/hdr/image_loader_hdr.cpp2
-rw-r--r--modules/hdr/image_loader_hdr.h2
-rw-r--r--modules/hdr/register_types.cpp7
-rw-r--r--modules/jpg/image_loader_jpegd.cpp2
-rw-r--r--modules/jpg/image_loader_jpegd.h2
-rw-r--r--modules/jpg/register_types.cpp7
-rw-r--r--modules/svg/image_loader_svg.cpp2
-rw-r--r--modules/svg/image_loader_svg.h2
-rw-r--r--modules/svg/register_types.cpp12
-rw-r--r--modules/tga/image_loader_tga.cpp2
-rw-r--r--modules/tga/image_loader_tga.h2
-rw-r--r--modules/tga/register_types.cpp7
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp2
-rw-r--r--modules/tinyexr/image_loader_tinyexr.h2
-rw-r--r--modules/tinyexr/register_types.cpp7
-rw-r--r--modules/webp/image_loader_webp.cpp2
-rw-r--r--modules/webp/image_loader_webp.h2
-rw-r--r--modules/webp/register_types.cpp11
-rw-r--r--scene/3d/sprite_3d.cpp493
-rw-r--r--scene/3d/sprite_3d.h10
-rw-r--r--scene/main/http_request.cpp125
-rw-r--r--scene/main/http_request.h4
45 files changed, 995 insertions, 495 deletions
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 4454f2a100..f86dfe8057 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -261,6 +261,51 @@ Ref<DirAccess> DirAccess::_open(const String &p_path) {
return da;
}
+int DirAccess::_get_drive_count() {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ return d->get_drive_count();
+}
+
+String DirAccess::get_drive_name(int p_idx) {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ return d->get_drive(p_idx);
+}
+
+Error DirAccess::make_dir_absolute(const String &p_dir) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_dir);
+ return d->make_dir(p_dir);
+}
+
+Error DirAccess::make_dir_recursive_absolute(const String &p_dir) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_dir);
+ return d->make_dir_recursive(p_dir);
+}
+
+bool DirAccess::dir_exists_absolute(const String &p_dir) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_dir);
+ return d->dir_exists(p_dir);
+}
+
+Error DirAccess::copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags) {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ // Support copying from res:// to user:// etc.
+ String from = ProjectSettings::get_singleton()->globalize_path(p_from);
+ String to = ProjectSettings::get_singleton()->globalize_path(p_to);
+ return d->copy(from, to, p_chmod_flags);
+}
+
+Error DirAccess::rename_absolute(const String &p_from, const String &p_to) {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ String from = ProjectSettings::get_singleton()->globalize_path(p_from);
+ String to = ProjectSettings::get_singleton()->globalize_path(p_to);
+ return d->rename(from, to);
+}
+
+Error DirAccess::remove_absolute(const String &p_path) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_path);
+ return d->remove(p_path);
+}
+
Ref<DirAccess> DirAccess::create(AccessType p_access) {
Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr;
if (da.is_valid()) {
@@ -445,10 +490,20 @@ PackedStringArray DirAccess::get_files() {
return _get_contents(false);
}
+PackedStringArray DirAccess::get_files_at(const String &p_path) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ return da->get_files();
+}
+
PackedStringArray DirAccess::get_directories() {
return _get_contents(true);
}
+PackedStringArray DirAccess::get_directories_at(const String &p_path) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ return da->get_directories();
+}
+
PackedStringArray DirAccess::_get_contents(bool p_directories) {
PackedStringArray ret;
@@ -498,20 +553,28 @@ void DirAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("current_is_dir"), &DirAccess::current_is_dir);
ClassDB::bind_method(D_METHOD("list_dir_end"), &DirAccess::list_dir_end);
ClassDB::bind_method(D_METHOD("get_files"), &DirAccess::get_files);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_files_at", "path"), &DirAccess::get_files_at);
ClassDB::bind_method(D_METHOD("get_directories"), &DirAccess::get_directories);
- ClassDB::bind_method(D_METHOD("get_drive_count"), &DirAccess::get_drive_count);
- ClassDB::bind_method(D_METHOD("get_drive", "idx"), &DirAccess::get_drive);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_directories_at", "path"), &DirAccess::get_directories_at);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_count"), &DirAccess::_get_drive_count);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_name", "idx"), &DirAccess::get_drive_name);
ClassDB::bind_method(D_METHOD("get_current_drive"), &DirAccess::get_current_drive);
- ClassDB::bind_method(D_METHOD("change_dir", "todir"), &DirAccess::change_dir);
+ ClassDB::bind_method(D_METHOD("change_dir", "to_dir"), &DirAccess::change_dir);
ClassDB::bind_method(D_METHOD("get_current_dir", "include_drive"), &DirAccess::get_current_dir, DEFVAL(true));
ClassDB::bind_method(D_METHOD("make_dir", "path"), &DirAccess::make_dir);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_absolute", "path"), &DirAccess::make_dir_absolute);
ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &DirAccess::make_dir_recursive);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_recursive_absolute", "path"), &DirAccess::make_dir_recursive_absolute);
ClassDB::bind_method(D_METHOD("file_exists", "path"), &DirAccess::file_exists);
ClassDB::bind_method(D_METHOD("dir_exists", "path"), &DirAccess::dir_exists);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("dir_exists_absolute", "path"), &DirAccess::dir_exists_absolute);
ClassDB::bind_method(D_METHOD("get_space_left"), &DirAccess::get_space_left);
ClassDB::bind_method(D_METHOD("copy", "from", "to", "chmod_flags"), &DirAccess::copy, DEFVAL(-1));
+ ClassDB::bind_static_method("DirAccess", D_METHOD("copy_absolute", "from", "to", "chmod_flags"), &DirAccess::copy_absolute, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("rename", "from", "to"), &DirAccess::rename);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("rename_absolute", "from", "to"), &DirAccess::rename_absolute);
ClassDB::bind_method(D_METHOD("remove", "path"), &DirAccess::remove);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("remove_absolute", "path"), &DirAccess::remove_absolute);
ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational);
ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational);
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index a694f6388f..ee675f1c89 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -136,8 +136,21 @@ public:
static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr);
static Ref<DirAccess> _open(const String &p_path);
+ static int _get_drive_count();
+ static String get_drive_name(int p_idx);
+
+ static Error make_dir_absolute(const String &p_dir);
+ static Error make_dir_recursive_absolute(const String &p_dir);
+ static bool dir_exists_absolute(const String &p_dir);
+
+ static Error copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags = -1);
+ static Error rename_absolute(const String &p_from, const String &p_to);
+ static Error remove_absolute(const String &p_path);
+
PackedStringArray get_files();
+ static PackedStringArray get_files_at(const String &p_path);
PackedStringArray get_directories();
+ static PackedStringArray get_directories_at(const String &p_path);
PackedStringArray _get_contents(bool p_directories);
String _get_next();
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index d09697b951..d6854666c0 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -32,6 +32,12 @@
#include "core/string/print_string.h"
+void ImageFormatLoader::_bind_methods() {
+ BIND_BITFIELD_FLAG(FLAG_NONE);
+ BIND_BITFIELD_FLAG(FLAG_FORCE_LINEAR);
+ BIND_BITFIELD_FLAG(FLAG_CONVERT_COLORS);
+}
+
bool ImageFormatLoader::recognize(const String &p_extension) const {
List<String> extensions;
get_recognized_extensions(&extensions);
@@ -44,7 +50,39 @@ bool ImageFormatLoader::recognize(const String &p_extension) const {
return false;
}
-Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, uint32_t p_flags, float p_scale) {
+Error ImageFormatLoaderExtension::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
+ Error err;
+ if (GDVIRTUAL_CALL(_load_image, p_image, p_fileaccess, p_flags, p_scale, err)) {
+ return err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+void ImageFormatLoaderExtension::get_recognized_extensions(List<String> *p_extension) const {
+ PackedStringArray ext;
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, ext)) {
+ for (int i = 0; i < ext.size(); i++) {
+ p_extension->push_back(ext[i]);
+ }
+ }
+}
+
+void ImageFormatLoaderExtension::add_format_loader() {
+ ImageLoader::add_image_format_loader(this);
+}
+
+void ImageFormatLoaderExtension::remove_format_loader() {
+ ImageLoader::remove_image_format_loader(this);
+}
+
+void ImageFormatLoaderExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_recognized_extensions);
+ GDVIRTUAL_BIND(_load_image, "image", "fileaccess", "flags", "scale");
+ ClassDB::bind_method(D_METHOD("add_format_loader"), &ImageFormatLoaderExtension::add_format_loader);
+ ClassDB::bind_method(D_METHOD("remove_format_loader"), &ImageFormatLoaderExtension::remove_format_loader);
+}
+
+Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object.");
Ref<FileAccess> f = p_custom;
@@ -60,7 +98,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess>
if (!loader[i]->recognize(extension)) {
continue;
}
- Error err = loader[i]->load_image(p_image, f, p_flags, p_scale);
+ Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale);
if (err != OK) {
ERR_PRINT("Error loading image: " + p_file);
}
@@ -79,7 +117,7 @@ void ImageLoader::get_recognized_extensions(List<String> *p_extensions) {
}
}
-ImageFormatLoader *ImageLoader::recognize(const String &p_extension) {
+Ref<ImageFormatLoader> ImageLoader::recognize(const String &p_extension) {
for (int i = 0; i < loader.size(); i++) {
if (loader[i]->recognize(p_extension)) {
return loader[i];
@@ -89,17 +127,17 @@ ImageFormatLoader *ImageLoader::recognize(const String &p_extension) {
return nullptr;
}
-Vector<ImageFormatLoader *> ImageLoader::loader;
+Vector<Ref<ImageFormatLoader>> ImageLoader::loader;
-void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) {
+void ImageLoader::add_image_format_loader(Ref<ImageFormatLoader> p_loader) {
loader.push_back(p_loader);
}
-void ImageLoader::remove_image_format_loader(ImageFormatLoader *p_loader) {
+void ImageLoader::remove_image_format_loader(Ref<ImageFormatLoader> p_loader) {
loader.erase(p_loader);
}
-const Vector<ImageFormatLoader *> &ImageLoader::get_image_format_loaders() {
+const Vector<Ref<ImageFormatLoader>> &ImageLoader::get_image_format_loaders() {
return loader;
}
@@ -152,7 +190,7 @@ Ref<Resource> ResourceFormatLoaderImage::load(const String &p_path, const String
Ref<Image> image;
image.instantiate();
- Error err = ImageLoader::loader[idx]->load_image(image, f);
+ Error err = ImageLoader::loader.write[idx]->load_image(image, f);
if (err != OK) {
if (r_error) {
diff --git a/core/io/image_loader.h b/core/io/image_loader.h
index bf78005e40..f70fdf22aa 100644
--- a/core/io/image_loader.h
+++ b/core/io/image_loader.h
@@ -31,23 +31,23 @@
#ifndef IMAGE_LOADER_H
#define IMAGE_LOADER_H
+#include "core/core_bind.h"
#include "core/io/file_access.h"
#include "core/io/image.h"
#include "core/io/resource_loader.h"
+#include "core/object/gdvirtual.gen.inc"
#include "core/string/ustring.h"
#include "core/templates/list.h"
+#include "core/variant/binder_common.h"
class ImageLoader;
-class ImageFormatLoader {
+class ImageFormatLoader : public RefCounted {
+ GDCLASS(ImageFormatLoader, RefCounted);
+
friend class ImageLoader;
friend class ResourceFormatLoaderImage;
-protected:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags = (uint32_t)FLAG_NONE, float p_scale = 1.0) = 0;
- virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
- bool recognize(const String &p_extension) const;
-
public:
enum LoaderFlags {
FLAG_NONE = 0,
@@ -55,23 +55,50 @@ public:
FLAG_CONVERT_COLORS = 2,
};
+protected:
+ static void _bind_methods();
+
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags = FLAG_NONE, float p_scale = 1.0) = 0;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
+ bool recognize(const String &p_extension) const;
+
+public:
virtual ~ImageFormatLoader() {}
};
+VARIANT_BITFIELD_CAST(ImageFormatLoader::LoaderFlags);
+
+class ImageFormatLoaderExtension : public ImageFormatLoader {
+ GDCLASS(ImageFormatLoaderExtension, ImageFormatLoader);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags = FLAG_NONE, float p_scale = 1.0) override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+
+ void add_format_loader();
+ void remove_format_loader();
+
+ GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions);
+ GDVIRTUAL4R(Error, _load_image, Ref<Image>, Ref<FileAccess>, BitField<ImageFormatLoader::LoaderFlags>, float);
+};
+
class ImageLoader {
- static Vector<ImageFormatLoader *> loader;
+ static Vector<Ref<ImageFormatLoader>> loader;
friend class ResourceFormatLoaderImage;
protected:
public:
- static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), uint32_t p_flags = (uint32_t)ImageFormatLoader::FLAG_NONE, float p_scale = 1.0);
+ static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), BitField<ImageFormatLoader::LoaderFlags> p_flags = ImageFormatLoader::FLAG_NONE, float p_scale = 1.0);
static void get_recognized_extensions(List<String> *p_extensions);
- static ImageFormatLoader *recognize(const String &p_extension);
+ static Ref<ImageFormatLoader> recognize(const String &p_extension);
- static void add_image_format_loader(ImageFormatLoader *p_loader);
- static void remove_image_format_loader(ImageFormatLoader *p_loader);
+ static void add_image_format_loader(Ref<ImageFormatLoader> p_loader);
+ static void remove_image_format_loader(Ref<ImageFormatLoader> p_loader);
- static const Vector<ImageFormatLoader *> &get_image_format_loaders();
+ static const Vector<Ref<ImageFormatLoader>> &get_image_format_loaders();
static void cleanup();
};
diff --git a/core/io/json.cpp b/core/io/json.cpp
index a685fcb718..7e267d35d4 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -56,6 +56,8 @@ String JSON::_make_indent(const String &p_indent, int p_size) {
}
String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision) {
+ ERR_FAIL_COND_V_MSG(p_cur_indent > Variant::MAX_RECURSION_DEPTH, "...", "JSON structure is too deep. Bailing.");
+
String colon = ":";
String end_statement = "";
@@ -357,17 +359,22 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
return ERR_PARSE_ERROR;
}
-Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) {
+Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) {
+ if (p_depth > Variant::MAX_RECURSION_DEPTH) {
+ r_err_str = "JSON structure is too deep. Bailing.";
+ return ERR_OUT_OF_MEMORY;
+ }
+
if (token.type == TK_CURLY_BRACKET_OPEN) {
Dictionary d;
- Error err = _parse_object(d, p_str, index, p_len, line, r_err_str);
+ Error err = _parse_object(d, p_str, index, p_len, line, p_depth + 1, r_err_str);
if (err) {
return err;
}
value = d;
} else if (token.type == TK_BRACKET_OPEN) {
Array a;
- Error err = _parse_array(a, p_str, index, p_len, line, r_err_str);
+ Error err = _parse_array(a, p_str, index, p_len, line, p_depth + 1, r_err_str);
if (err) {
return err;
}
@@ -396,7 +403,7 @@ Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, in
return OK;
}
-Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) {
+Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) {
Token token;
bool need_comma = false;
@@ -421,7 +428,7 @@ Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_
}
Variant v;
- err = _parse_value(v, token, p_str, index, p_len, line, r_err_str);
+ err = _parse_value(v, token, p_str, index, p_len, line, p_depth, r_err_str);
if (err) {
return err;
}
@@ -434,7 +441,7 @@ Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_
return ERR_PARSE_ERROR;
}
-Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) {
+Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) {
bool at_key = true;
String key;
Token token;
@@ -483,7 +490,7 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index,
}
Variant v;
- err = _parse_value(v, token, p_str, index, p_len, line, r_err_str);
+ err = _parse_value(v, token, p_str, index, p_len, line, p_depth, r_err_str);
if (err) {
return err;
}
@@ -514,7 +521,7 @@ Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_st
return err;
}
- err = _parse_value(r_ret, token, str, idx, len, r_err_line, r_err_str);
+ err = _parse_value(r_ret, token, str, idx, len, r_err_line, 0, r_err_str);
// Check if EOF is reached
// or it's a type of the next token.
diff --git a/core/io/json.h b/core/io/json.h
index 208d4ad625..829a5f922b 100644
--- a/core/io/json.h
+++ b/core/io/json.h
@@ -74,9 +74,9 @@ class JSON : public RefCounted {
static String _make_indent(const String &p_indent, int p_size);
static String _stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision = false);
static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str);
- static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
- static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
- static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
+ static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
+ static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
+ static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
protected:
diff --git a/core/io/stream_peer_gzip.cpp b/core/io/stream_peer_gzip.cpp
new file mode 100644
index 0000000000..ca8be2d62e
--- /dev/null
+++ b/core/io/stream_peer_gzip.cpp
@@ -0,0 +1,209 @@
+/*************************************************************************/
+/* stream_peer_gzip.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/io/stream_peer_gzip.h"
+
+#include "core/io/zip_io.h"
+#include <zlib.h>
+
+void StreamPeerGZIP::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("start_compression", "use_deflate", "buffer_size"), &StreamPeerGZIP::start_compression, DEFVAL(false), DEFVAL(65535));
+ ClassDB::bind_method(D_METHOD("start_decompression", "use_deflate", "buffer_size"), &StreamPeerGZIP::start_decompression, DEFVAL(false), DEFVAL(65535));
+ ClassDB::bind_method(D_METHOD("finish"), &StreamPeerGZIP::finish);
+ ClassDB::bind_method(D_METHOD("clear"), &StreamPeerGZIP::clear);
+}
+
+StreamPeerGZIP::StreamPeerGZIP() {
+}
+
+StreamPeerGZIP::~StreamPeerGZIP() {
+ _close();
+}
+
+void StreamPeerGZIP::_close() {
+ if (ctx) {
+ z_stream *strm = (z_stream *)ctx;
+ if (compressing) {
+ deflateEnd(strm);
+ } else {
+ inflateEnd(strm);
+ }
+ memfree(strm);
+ ctx = nullptr;
+ }
+}
+
+void StreamPeerGZIP::clear() {
+ _close();
+ rb.clear();
+ buffer.clear();
+}
+
+Error StreamPeerGZIP::start_compression(bool p_is_deflate, int buffer_size) {
+ return _start(true, p_is_deflate, buffer_size);
+}
+
+Error StreamPeerGZIP::start_decompression(bool p_is_deflate, int buffer_size) {
+ return _start(false, p_is_deflate, buffer_size);
+}
+
+Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size) {
+ ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE);
+ clear();
+ compressing = p_compress;
+ rb.resize(nearest_shift(buffer_size - 1));
+ buffer.resize(1024);
+
+ // Create ctx.
+ ctx = memalloc(sizeof(z_stream));
+ z_stream &strm = *(z_stream *)ctx;
+ strm.next_in = Z_NULL;
+ strm.avail_in = 0;
+ strm.zalloc = zipio_alloc;
+ strm.zfree = zipio_free;
+ strm.opaque = Z_NULL;
+ int window_bits = p_is_deflate ? 15 : (15 + 16);
+ int err = Z_OK;
+ int level = Z_DEFAULT_COMPRESSION;
+ if (compressing) {
+ err = deflateInit2(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);
+ } else {
+ err = inflateInit2(&strm, window_bits);
+ }
+ ERR_FAIL_COND_V(err != Z_OK, FAILED);
+ return OK;
+}
+
+Error StreamPeerGZIP::_process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close) {
+ ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ z_stream &strm = *(z_stream *)ctx;
+ strm.avail_in = p_src_size;
+ strm.avail_out = p_dst_size;
+ strm.next_in = (Bytef *)p_src;
+ strm.next_out = (Bytef *)p_dst;
+ int flush = p_close ? Z_FINISH : Z_NO_FLUSH;
+ if (compressing) {
+ int err = deflate(&strm, flush);
+ ERR_FAIL_COND_V(err != (p_close ? Z_STREAM_END : Z_OK), FAILED);
+ } else {
+ int err = inflate(&strm, flush);
+ ERR_FAIL_COND_V(err != Z_OK && err != Z_STREAM_END, FAILED);
+ }
+ r_out = p_dst_size - strm.avail_out;
+ r_consumed = p_src_size - strm.avail_in;
+ return OK;
+}
+
+Error StreamPeerGZIP::put_data(const uint8_t *p_data, int p_bytes) {
+ int wrote = 0;
+ Error err = put_partial_data(p_data, p_bytes, wrote);
+ if (err != OK) {
+ return err;
+ }
+ ERR_FAIL_COND_V(p_bytes != wrote, ERR_OUT_OF_MEMORY);
+ return OK;
+}
+
+Error StreamPeerGZIP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER);
+
+ // Ensure we have enough space in temporary buffer.
+ if (buffer.size() < p_bytes) {
+ buffer.resize(p_bytes);
+ }
+
+ r_sent = 0;
+ while (r_sent < p_bytes && rb.space_left() > 1024) { // Keep the ring buffer size meaningful.
+ int sent = 0;
+ int to_write = 0;
+ // Compress or decompress
+ Error err = _process(buffer.ptrw(), MIN(buffer.size(), rb.space_left()), p_data + r_sent, p_bytes - r_sent, sent, to_write);
+ if (err != OK) {
+ return err;
+ }
+ // When decompressing, we might need to do another round.
+ r_sent += sent;
+
+ // We can't write more than this buffer is full.
+ if (sent == 0 && to_write == 0) {
+ return OK;
+ }
+ if (to_write) {
+ // Copy to ring buffer.
+ int wrote = rb.write(buffer.ptr(), to_write);
+ ERR_FAIL_COND_V(wrote != to_write, ERR_BUG);
+ }
+ }
+ return OK;
+}
+
+Error StreamPeerGZIP::get_data(uint8_t *p_buffer, int p_bytes) {
+ int received = 0;
+ Error err = get_partial_data(p_buffer, p_bytes, received);
+ if (err != OK) {
+ return err;
+ }
+ ERR_FAIL_COND_V(p_bytes != received, ERR_UNAVAILABLE);
+ return OK;
+}
+
+Error StreamPeerGZIP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+ ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER);
+
+ r_received = MIN(p_bytes, rb.data_left());
+ if (r_received == 0) {
+ return OK;
+ }
+ int received = rb.read(p_buffer, r_received);
+ ERR_FAIL_COND_V(received != r_received, ERR_BUG);
+ return OK;
+}
+
+int StreamPeerGZIP::get_available_bytes() const {
+ return rb.data_left();
+}
+
+Error StreamPeerGZIP::finish() {
+ ERR_FAIL_COND_V(!ctx || !compressing, ERR_UNAVAILABLE);
+ // Ensure we have enough space in temporary buffer.
+ if (buffer.size() < 1024) {
+ buffer.resize(1024); // 1024 should be more than enough.
+ }
+ int consumed = 0;
+ int to_write = 0;
+ Error err = _process(buffer.ptrw(), 1024, nullptr, 0, consumed, to_write, true); // compress
+ if (err != OK) {
+ return err;
+ }
+ int wrote = rb.write(buffer.ptr(), to_write);
+ ERR_FAIL_COND_V(wrote != to_write, ERR_OUT_OF_MEMORY);
+ return OK;
+}
diff --git a/core/io/stream_peer_gzip.h b/core/io/stream_peer_gzip.h
new file mode 100644
index 0000000000..5bafdbca9b
--- /dev/null
+++ b/core/io/stream_peer_gzip.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* stream_peer_gzip.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef STREAM_PEER_GZIP_H
+#define STREAM_PEER_GZIP_H
+
+#include "core/io/stream_peer.h"
+
+#include "core/core_bind.h"
+#include "core/io/compression.h"
+#include "core/templates/ring_buffer.h"
+
+class StreamPeerGZIP : public StreamPeer {
+ GDCLASS(StreamPeerGZIP, StreamPeer);
+
+private:
+ void *ctx = nullptr; // Will hold our z_stream instance.
+ bool compressing = true;
+
+ RingBuffer<uint8_t> rb;
+ Vector<uint8_t> buffer;
+
+ Error _process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close = false);
+ void _close();
+ Error _start(bool p_compress, bool p_is_deflate, int buffer_size = 65535);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Error start_compression(bool p_is_deflate, int buffer_size = 65535);
+ Error start_decompression(bool p_is_deflate, int buffer_size = 65535);
+
+ Error finish();
+ void clear();
+
+ virtual Error put_data(const uint8_t *p_data, int p_bytes) override;
+ virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
+
+ virtual Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
+
+ virtual int get_available_bytes() const override;
+
+ StreamPeerGZIP();
+ ~StreamPeerGZIP();
+};
+
+#endif // STREAM_PEER_GZIP_H
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 0233afe199..bad53ab4e6 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -58,6 +58,7 @@
#include "core/io/resource_format_binary.h"
#include "core/io/resource_importer.h"
#include "core/io/resource_uid.h"
+#include "core/io/stream_peer_gzip.h"
#include "core/io/stream_peer_tls.h"
#include "core/io/tcp_server.h"
#include "core/io/translation_loader_po.h"
@@ -184,6 +185,7 @@ void register_core_types() {
GDREGISTER_ABSTRACT_CLASS(StreamPeer);
GDREGISTER_CLASS(StreamPeerExtension);
GDREGISTER_CLASS(StreamPeerBuffer);
+ GDREGISTER_CLASS(StreamPeerGZIP);
GDREGISTER_CLASS(StreamPeerTCP);
GDREGISTER_CLASS(TCPServer);
@@ -249,6 +251,8 @@ void register_core_types() {
GDREGISTER_CLASS(EncodedObjectAsID);
GDREGISTER_CLASS(RandomNumberGenerator);
+ GDREGISTER_ABSTRACT_CLASS(ImageFormatLoader);
+ GDREGISTER_CLASS(ImageFormatLoaderExtension);
GDREGISTER_ABSTRACT_CLASS(ResourceImporter);
GDREGISTER_CLASS(NativeExtension);
diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml
index de2e32b17b..cb7bf56f11 100644
--- a/doc/classes/DirAccess.xml
+++ b/doc/classes/DirAccess.xml
@@ -6,6 +6,15 @@
<description>
Directory type. It is used to manage directories and their content (not restricted to the project folder).
[DirAccess] can't be instantiated directly. Instead it is created with a static method that takes a path for which it will be opened.
+ Most of the methods have a static alternative that can be used without creating a [DirAccess]. Static methods only support absolute paths (including [code]res://[/code] and [code]user://[/code]).
+ [codeblock]
+ # Standard
+ var dir = Directory.new()
+ dir.open("user://levels")
+ dir.make_dir("world1")
+ # Static
+ Directory.make_dir_absolute("user://levels/world1")
+ [/codeblock]
[b]Note:[/b] Many resources types are imported (e.g. textures or sound files), and their source asset will not be included in the exported game, as only the imported version is used. Use [ResourceLoader] to access imported resources.
Here is an example on how to iterate through the files of a directory:
[codeblocks]
@@ -59,7 +68,7 @@
<methods>
<method name="change_dir">
<return type="int" enum="Error" />
- <param index="0" name="todir" type="String" />
+ <param index="0" name="to_dir" type="String" />
<description>
Changes the currently opened directory to the one passed as an argument. The argument can be relative to the current directory (e.g. [code]newdir[/code] or [code]../newdir[/code]), or an absolute path (e.g. [code]/tmp/newdir[/code] or [code]res://somedir/newdir[/code]).
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
@@ -76,6 +85,15 @@
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
+ <method name="copy_absolute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="from" type="String" />
+ <param index="1" name="to" type="String" />
+ <param index="2" name="chmod_flags" type="int" default="-1" />
+ <description>
+ Static version of [method copy]. Supports only absolute paths.
+ </description>
+ </method>
<method name="current_is_dir" qualifiers="const">
<return type="bool" />
<description>
@@ -87,7 +105,13 @@
<param index="0" name="path" type="String" />
<description>
Returns whether the target directory exists. The argument can be relative to the current directory, or an absolute path.
- If the [DirAccess] is not open, the path is relative to [code]res://[/code].
+ </description>
+ </method>
+ <method name="dir_exists_absolute" qualifiers="static">
+ <return type="bool" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Static version of [method dir_exists]. Supports only absolute paths.
</description>
</method>
<method name="file_exists">
@@ -95,7 +119,7 @@
<param index="0" name="path" type="String" />
<description>
Returns whether the target file exists. The argument can be relative to the current directory, or an absolute path.
- If the [DirAccess] is not open, the path is relative to [code]res://[/code].
+ For a static equivalent, use [method FileAccess.file_exists].
</description>
</method>
<method name="get_current_dir" qualifiers="const">
@@ -108,7 +132,7 @@
<method name="get_current_drive">
<return type="int" />
<description>
- Returns the currently opened directory's drive index. See [method get_drive] to convert returned index to the name of the drive.
+ Returns the currently opened directory's drive index. See [method get_drive_name] to convert returned index to the name of the drive.
</description>
</method>
<method name="get_directories">
@@ -118,17 +142,15 @@
Affected by [member include_hidden] and [member include_navigational].
</description>
</method>
- <method name="get_drive">
- <return type="String" />
- <param index="0" name="idx" type="int" />
+ <method name="get_directories_at" qualifiers="static">
+ <return type="PackedStringArray" />
+ <param index="0" name="path" type="String" />
<description>
- On Windows, returns the name of the drive (partition) passed as an argument (e.g. [code]C:[/code]).
- On macOS, returns the path to the mounted volume passed as an argument.
- On Linux, returns the path to the mounted volume or GTK 3 bookmark passed as an argument.
- On other platforms, or if the requested drive does not exist, the method returns an empty String.
+ Returns a [PackedStringArray] containing filenames of the directory contents, excluding files, at the given [param path]. The array is sorted alphabetically.
+ Use [method get_directories] if you want more control of what gets included.
</description>
</method>
- <method name="get_drive_count">
+ <method name="get_drive_count" qualifiers="static">
<return type="int" />
<description>
On Windows, returns the number of drives (partitions) mounted on the current filesystem.
@@ -137,6 +159,16 @@
On other platforms, the method returns 0.
</description>
</method>
+ <method name="get_drive_name" qualifiers="static">
+ <return type="String" />
+ <param index="0" name="idx" type="int" />
+ <description>
+ On Windows, returns the name of the drive (partition) passed as an argument (e.g. [code]C:[/code]).
+ On macOS, returns the path to the mounted volume passed as an argument.
+ On Linux, returns the path to the mounted volume or GTK 3 bookmark passed as an argument.
+ On other platforms, or if the requested drive does not exist, the method returns an empty String.
+ </description>
+ </method>
<method name="get_files">
<return type="PackedStringArray" />
<description>
@@ -144,6 +176,14 @@
Affected by [member include_hidden].
</description>
</method>
+ <method name="get_files_at" qualifiers="static">
+ <return type="PackedStringArray" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Returns a [PackedStringArray] containing filenames of the directory contents, excluding directories, at the given [param path]. The array is sorted alphabetically.
+ Use [method get_files] if you want more control of what gets included.
+ </description>
+ </method>
<method name="get_next">
<return type="String" />
<description>
@@ -185,6 +225,13 @@
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
+ <method name="make_dir_absolute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Static version of [method make_dir]. Supports only absolute paths.
+ </description>
+ </method>
<method name="make_dir_recursive">
<return type="int" enum="Error" />
<param index="0" name="path" type="String" />
@@ -193,6 +240,13 @@
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
+ <method name="make_dir_recursive_absolute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Static version of [method make_dir_recursive]. Supports only absolute paths.
+ </description>
+ </method>
<method name="open" qualifiers="static">
<return type="DirAccess" />
<param index="0" name="path" type="String" />
@@ -210,6 +264,13 @@
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
+ <method name="remove_absolute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Static version of [method remove]. Supports only absolute paths.
+ </description>
+ </method>
<method name="rename">
<return type="int" enum="Error" />
<param index="0" name="from" type="String" />
@@ -219,6 +280,14 @@
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
+ <method name="rename_absolute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="from" type="String" />
+ <param index="1" name="to" type="String" />
+ <description>
+ Static version of [method rename]. Supports only absolute paths.
+ </description>
+ </method>
</methods>
<members>
<member name="include_hidden" type="bool" setter="set_include_hidden" getter="get_include_hidden">
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index 32f6a1dd3e..adc0f4c3dd 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -77,6 +77,7 @@
<description>
Returns [code]true[/code] if the file exists in the given path.
[b]Note:[/b] Many resources types are imported (e.g. textures or sound files), and their source asset will not be included in the exported game, as only the imported version is used. See [method ResourceLoader.exists] for an alternative approach that takes resource remapping into account.
+ For a non-static, relative equivalent, use [method DirAccess.file_exists].
</description>
</method>
<method name="flush">
diff --git a/doc/classes/ImageFormatLoader.xml b/doc/classes/ImageFormatLoader.xml
new file mode 100644
index 0000000000..c6b1ec922a
--- /dev/null
+++ b/doc/classes/ImageFormatLoader.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ImageFormatLoader" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Base class to add support for specific image formats.
+ </brief_description>
+ <description>
+ The engine supports multiple image formats out of the box (PNG, SVG, JPEG, WebP to name a few), but you can choose to implement support for additional image formats by extending [ImageFormatLoaderExtension].
+ </description>
+ <tutorials>
+ </tutorials>
+ <constants>
+ <constant name="FLAG_NONE" value="0" enum="LoaderFlags" is_bitfield="true">
+ </constant>
+ <constant name="FLAG_FORCE_LINEAR" value="1" enum="LoaderFlags" is_bitfield="true">
+ </constant>
+ <constant name="FLAG_CONVERT_COLORS" value="2" enum="LoaderFlags" is_bitfield="true">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/ImageFormatLoaderExtension.xml b/doc/classes/ImageFormatLoaderExtension.xml
new file mode 100644
index 0000000000..b2a7ebc60f
--- /dev/null
+++ b/doc/classes/ImageFormatLoaderExtension.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ImageFormatLoaderExtension" inherits="ImageFormatLoader" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Base class for creating [ImageFormatLoader] extensions (adding support for extra image formats).
+ </brief_description>
+ <description>
+ The engine supports multiple image formats out of the box (PNG, SVG, JPEG, WebP to name a few), but you can choose to implement support for additional image formats by extending this class.
+ Be sure to respect the documented return types and values. You should create an instance of it, and call [method add_format_loader] to register that loader during the initializaiton phase.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_get_recognized_extensions" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns the list of file extensions for this image format. Files with the given extentions will be treated as image file and loaded using this class.
+ </description>
+ </method>
+ <method name="_load_image" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="image" type="Image" />
+ <param index="1" name="fileaccess" type="FileAccess" />
+ <param index="2" name="flags" type="int" enum="ImageFormatLoader.LoaderFlags" />
+ <param index="3" name="scale" type="float" />
+ <description>
+ Loads the content of [param fileaccess] into the provided [param image].
+ </description>
+ </method>
+ <method name="add_format_loader">
+ <return type="void" />
+ <description>
+ Add this format loader to the engine, allowing it to recognize the file extensions returned by [method _get_recognized_extensions].
+ </description>
+ </method>
+ <method name="remove_format_loader">
+ <return type="void" />
+ <description>
+ Remove this format loader from the engine.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/StreamPeerGZIP.xml b/doc/classes/StreamPeerGZIP.xml
new file mode 100644
index 0000000000..71dd36160d
--- /dev/null
+++ b/doc/classes/StreamPeerGZIP.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="StreamPeerGZIP" inherits="StreamPeer" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Stream peer handling GZIP and deflate compression/decompresison.
+ </brief_description>
+ <description>
+ This class allows to compress or decompress data using GZIP/deflate in a streaming fashion. This is particularly useful when compressing or decompressing files that has to be sent through the network without having to allocate them all in memory.
+ After starting the stream via [method start_compression] (or [method start_decompression]), calling [method StreamPeer.put_partial_data] on this stream will compress (or decompress) the data, writing it to the internal buffer. Calling [method StreamPeer.get_available_bytes] will return the pending bytes in the internal buffer, and [method StreamPeer.get_partial_data] will retrieve the compressed (or decompressed) bytes from it. When the stream is over, you must call [method finish] to ensure the internal buffer is properly flushed (make sure to call [method StreamPeer.get_available_bytes] on last time to check if more data needs to be read after that).
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="clear">
+ <return type="void" />
+ <description>
+ Clears this stream, resetting the internal state.
+ </description>
+ </method>
+ <method name="finish">
+ <return type="int" enum="Error" />
+ <description>
+ Finalizes the stream, compressing or decompressing any buffered chunk left.
+ </description>
+ </method>
+ <method name="start_compression">
+ <return type="int" enum="Error" />
+ <param index="0" name="use_deflate" type="bool" default="false" />
+ <param index="1" name="buffer_size" type="int" default="65535" />
+ <description>
+ Start the stream in compression mode with the given [param buffer_size], if [param use_deflate] is [code]true[/code] uses deflate instead of GZIP.
+ </description>
+ </method>
+ <method name="start_decompression">
+ <return type="int" enum="Error" />
+ <param index="0" name="use_deflate" type="bool" default="false" />
+ <param index="1" name="buffer_size" type="int" default="65535" />
+ <description>
+ Start the stream in decompression mode with the given [param buffer_size], if [param use_deflate] is [code]true[/code] uses deflate instead of GZIP.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp
index 8d2f8a7ed6..165de34c71 100644
--- a/drivers/png/image_loader_png.cpp
+++ b/drivers/png/image_loader_png.cpp
@@ -36,7 +36,7 @@
#include <string.h>
-Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
const uint64_t buffer_size = f->get_length();
Vector<uint8_t> file_buffer;
Error err = file_buffer.resize(buffer_size);
diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h
index 91c3c8925f..a247d77310 100644
--- a/drivers/png/image_loader_png.h
+++ b/drivers/png/image_loader_png.h
@@ -40,7 +40,7 @@ private:
static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size);
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderPNG();
};
diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp
index 504ef9843a..53a7f7aa4f 100644
--- a/drivers/register_driver_types.cpp
+++ b/drivers/register_driver_types.cpp
@@ -34,11 +34,11 @@
#include "drivers/png/image_loader_png.h"
#include "drivers/png/resource_saver_png.h"
-static ImageLoaderPNG *image_loader_png;
+static Ref<ImageLoaderPNG> image_loader_png;
static Ref<ResourceSaverPNG> resource_saver_png;
void register_core_driver_types() {
- image_loader_png = memnew(ImageLoaderPNG);
+ image_loader_png.instantiate();
ImageLoader::add_image_format_loader(image_loader_png);
resource_saver_png.instantiate();
@@ -46,9 +46,8 @@ void register_core_driver_types() {
}
void unregister_core_driver_types() {
- if (image_loader_png) {
- memdelete(image_loader_png);
- }
+ ImageLoader::remove_image_format_loader(image_loader_png);
+ image_loader_png.unref();
ResourceSaver::remove_resource_format_saver(resource_saver_png);
resource_saver_png.unref();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index a2f2b6c625..9ac6fd66ef 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2450,7 +2450,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
}
if (write_movie_file == String()) {
- show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the 'Editor/Movie Writer' category.\nAlternatively, for running single scenes, a 'movie_path' metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
+ show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the Editor > Movie Writer category.\nAlternatively, for running single scenes, a `movie_file` string metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
return;
}
}
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index 8062b6f756..48e746ac36 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -416,45 +416,50 @@ void EditorSettingsDialog::_update_shortcuts() {
List<String> slist;
EditorSettings::get_singleton()->get_shortcut_list(&slist);
+ slist.sort(); // Sort alphabetically.
const EditorPropertyNameProcessor::Style name_style = EditorPropertyNameProcessor::get_settings_style();
const EditorPropertyNameProcessor::Style tooltip_style = EditorPropertyNameProcessor::get_tooltip_style(name_style);
+ // Create all sections first.
for (const String &E : slist) {
Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(E);
- if (!sc->has_meta("original")) {
+ String section_name = E.get_slice("/", 0);
+
+ if (sections.has(section_name)) {
continue;
}
- // Shortcut Section
+ TreeItem *section = shortcuts->create_item(root);
- TreeItem *section;
- String section_name = E.get_slice("/", 0);
+ const String item_name = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, name_style);
+ const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, tooltip_style);
- if (sections.has(section_name)) {
- section = sections[section_name];
- } else {
- section = shortcuts->create_item(root);
-
- const String item_name = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, name_style);
- const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, tooltip_style);
-
- section->set_text(0, item_name);
- section->set_tooltip_text(0, tooltip);
- section->set_selectable(0, false);
- section->set_selectable(1, false);
- section->set_custom_bg_color(0, shortcuts->get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
- section->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
-
- if (collapsed.has(item_name)) {
- section->set_collapsed(collapsed[item_name]);
- }
+ section->set_text(0, item_name);
+ section->set_tooltip_text(0, tooltip);
+ section->set_selectable(0, false);
+ section->set_selectable(1, false);
+ section->set_custom_bg_color(0, shortcuts->get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
+ section->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
- sections[section_name] = section;
+ if (collapsed.has(item_name)) {
+ section->set_collapsed(collapsed[item_name]);
}
- // Shortcut Item
+ sections[section_name] = section;
+ }
+ // Add shortcuts to sections.
+ for (const String &E : slist) {
+ Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(E);
+ if (!sc->has_meta("original")) {
+ continue;
+ }
+
+ String section_name = E.get_slice("/", 0);
+ TreeItem *section = sections[section_name];
+
+ // Shortcut Item
if (!shortcut_filter.is_subsequence_ofn(sc->get_name())) {
continue;
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 228e475083..45b2a5eb14 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -401,7 +401,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
if (all_alternatve_id_zero) {
p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP));
p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, ""));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, "suffix:px"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_"));
// Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does.
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index ae03abca50..cc21ed28e8 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -200,7 +200,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
return err;
}
-Error ImageLoaderBMP::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderBMP::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
bmp_header_s bmp_header;
Error err = ERR_INVALID_DATA;
diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h
index cf8346ecad..0ca54de1dc 100644
--- a/modules/bmp/image_loader_bmp.h
+++ b/modules/bmp/image_loader_bmp.h
@@ -83,7 +83,7 @@ protected:
const bmp_header_s &p_header);
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderBMP();
};
diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp
index 7c4a2085b2..67858e9d46 100644
--- a/modules/bmp/register_types.cpp
+++ b/modules/bmp/register_types.cpp
@@ -32,14 +32,14 @@
#include "image_loader_bmp.h"
-static ImageLoaderBMP *image_loader_bmp = nullptr;
+static Ref<ImageLoaderBMP> image_loader_bmp;
void initialize_bmp_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_bmp = memnew(ImageLoaderBMP);
+ image_loader_bmp.instantiate();
ImageLoader::add_image_format_loader(image_loader_bmp);
}
@@ -48,5 +48,6 @@ void uninitialize_bmp_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_bmp);
+ ImageLoader::remove_image_format_loader(image_loader_bmp);
+ image_loader_bmp.unref();
}
diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp
index e7c6fe592d..6f0bc16a26 100644
--- a/modules/hdr/image_loader_hdr.cpp
+++ b/modules/hdr/image_loader_hdr.cpp
@@ -33,7 +33,7 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
-Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
String header = f->get_token();
ERR_FAIL_COND_V_MSG(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED, "Unsupported header information in HDR: " + header + ".");
diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h
index 1bff05129b..5f817f0ba8 100644
--- a/modules/hdr/image_loader_hdr.h
+++ b/modules/hdr/image_loader_hdr.h
@@ -35,7 +35,7 @@
class ImageLoaderHDR : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderHDR();
};
diff --git a/modules/hdr/register_types.cpp b/modules/hdr/register_types.cpp
index b988bf4587..18b1a73f1c 100644
--- a/modules/hdr/register_types.cpp
+++ b/modules/hdr/register_types.cpp
@@ -32,14 +32,14 @@
#include "image_loader_hdr.h"
-static ImageLoaderHDR *image_loader_hdr = nullptr;
+static Ref<ImageLoaderHDR> image_loader_hdr;
void initialize_hdr_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_hdr = memnew(ImageLoaderHDR);
+ image_loader_hdr.instantiate();
ImageLoader::add_image_format_loader(image_loader_hdr);
}
@@ -48,5 +48,6 @@ void uninitialize_hdr_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_hdr);
+ ImageLoader::remove_image_format_loader(image_loader_hdr);
+ image_loader_hdr.unref();
}
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 3e138bf633..ce20ac9060 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -104,7 +104,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
return OK;
}
-Error ImageLoaderJPG::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderJPG::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h
index caa0461d05..f63db51521 100644
--- a/modules/jpg/image_loader_jpegd.h
+++ b/modules/jpg/image_loader_jpegd.h
@@ -35,7 +35,7 @@
class ImageLoaderJPG : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderJPG();
};
diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp
index b8b48a550f..7da216bbe2 100644
--- a/modules/jpg/register_types.cpp
+++ b/modules/jpg/register_types.cpp
@@ -32,14 +32,14 @@
#include "image_loader_jpegd.h"
-static ImageLoaderJPG *image_loader_jpg = nullptr;
+static Ref<ImageLoaderJPG> image_loader_jpg;
void initialize_jpg_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_jpg = memnew(ImageLoaderJPG);
+ image_loader_jpg.instantiate();
ImageLoader::add_image_format_loader(image_loader_jpg);
}
@@ -48,5 +48,6 @@ void uninitialize_jpg_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_jpg);
+ ImageLoader::remove_image_format_loader(image_loader_jpg);
+ image_loader_jpg.unref();
}
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index cd6081f91b..f43f2784c7 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -142,7 +142,7 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const
p_extensions->push_back("svg");
}
-Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) {
+Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
String svg = p_fileaccess->get_as_utf8_string();
if (p_flags & FLAG_CONVERT_COLORS) {
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index e6f73ab18f..b0b0963c15 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -43,7 +43,7 @@ public:
void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) override;
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
};
diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp
index 5b4d1d31ca..323b1d652a 100644
--- a/modules/svg/register_types.cpp
+++ b/modules/svg/register_types.cpp
@@ -34,7 +34,7 @@
#include <thorvg.h>
-static ImageLoaderSVG *image_loader_svg = nullptr;
+static Ref<ImageLoaderSVG> image_loader_svg;
void initialize_svg_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
@@ -45,7 +45,8 @@ void initialize_svg_module(ModuleInitializationLevel p_level) {
if (tvg::Initializer::init(tvgEngine, 1) != tvg::Result::Success) {
return;
}
- image_loader_svg = memnew(ImageLoaderSVG);
+
+ image_loader_svg.instantiate();
ImageLoader::add_image_format_loader(image_loader_svg);
}
@@ -54,9 +55,12 @@ void uninitialize_svg_module(ModuleInitializationLevel p_level) {
return;
}
- if (!image_loader_svg) {
+ if (image_loader_svg.is_null()) {
+ // It failed to initialize so it was not added.
return;
}
- memdelete(image_loader_svg);
+
+ ImageLoader::remove_image_format_loader(image_loader_svg);
+ image_loader_svg.unref();
tvg::Initializer::term(tvg::CanvasEngine::Sw);
}
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 16d9bf7b93..00ba32eae1 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -230,7 +230,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
return OK;
}
-Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h
index d95c5ff30b..b257ed110d 100644
--- a/modules/tga/image_loader_tga.h
+++ b/modules/tga/image_loader_tga.h
@@ -73,7 +73,7 @@ class ImageLoaderTGA : public ImageFormatLoader {
static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size);
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderTGA();
};
diff --git a/modules/tga/register_types.cpp b/modules/tga/register_types.cpp
index 520ed5f799..3a9d2324e7 100644
--- a/modules/tga/register_types.cpp
+++ b/modules/tga/register_types.cpp
@@ -32,14 +32,14 @@
#include "image_loader_tga.h"
-static ImageLoaderTGA *image_loader_tga = nullptr;
+static Ref<ImageLoaderTGA> image_loader_tga;
void initialize_tga_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_tga = memnew(ImageLoaderTGA);
+ image_loader_tga.instantiate();
ImageLoader::add_image_format_loader(image_loader_tga);
}
@@ -48,5 +48,6 @@ void uninitialize_tga_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_tga);
+ ImageLoader::remove_image_format_loader(image_loader_tga);
+ image_loader_tga.unref();
}
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index 6f61251f9b..5c43bfc8b7 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -37,7 +37,7 @@
#include "thirdparty/tinyexr/tinyexr.h"
-Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h
index 8da2a0d4af..ab34a59da5 100644
--- a/modules/tinyexr/image_loader_tinyexr.h
+++ b/modules/tinyexr/image_loader_tinyexr.h
@@ -35,7 +35,7 @@
class ImageLoaderTinyEXR : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderTinyEXR();
};
diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp
index c5897f37c3..b1a9f18e3b 100644
--- a/modules/tinyexr/register_types.cpp
+++ b/modules/tinyexr/register_types.cpp
@@ -33,14 +33,14 @@
#include "image_loader_tinyexr.h"
#include "image_saver_tinyexr.h"
-static ImageLoaderTinyEXR *image_loader_tinyexr = nullptr;
+static Ref<ImageLoaderTinyEXR> image_loader_tinyexr;
void initialize_tinyexr_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_tinyexr = memnew(ImageLoaderTinyEXR);
+ image_loader_tinyexr.instantiate();
ImageLoader::add_image_format_loader(image_loader_tinyexr);
Image::save_exr_func = save_exr;
@@ -52,7 +52,8 @@ void uninitialize_tinyexr_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_tinyexr);
+ ImageLoader::remove_image_format_loader(image_loader_tinyexr);
+ image_loader_tinyexr.unref();
Image::save_exr_func = nullptr;
}
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index 705ab508ab..dd387db554 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -48,7 +48,7 @@ static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
return img;
}
-Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) {
+Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
index d868ae3f7f..0522e4ef91 100644
--- a/modules/webp/image_loader_webp.h
+++ b/modules/webp/image_loader_webp.h
@@ -35,7 +35,7 @@
class ImageLoaderWebP : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderWebP();
};
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
index 29f633743e..e523f43cfe 100644
--- a/modules/webp/register_types.cpp
+++ b/modules/webp/register_types.cpp
@@ -33,7 +33,7 @@
#include "image_loader_webp.h"
#include "resource_saver_webp.h"
-static ImageLoaderWebP *image_loader_webp = nullptr;
+static Ref<ImageLoaderWebP> image_loader_webp;
static Ref<ResourceSaverWebP> resource_saver_webp;
void initialize_webp_module(ModuleInitializationLevel p_level) {
@@ -41,9 +41,10 @@ void initialize_webp_module(ModuleInitializationLevel p_level) {
return;
}
- image_loader_webp = memnew(ImageLoaderWebP);
- resource_saver_webp.instantiate();
+ image_loader_webp.instantiate();
ImageLoader::add_image_format_loader(image_loader_webp);
+
+ resource_saver_webp.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_webp);
}
@@ -52,7 +53,9 @@ void uninitialize_webp_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_webp);
+ ImageLoader::remove_image_format_loader(image_loader_webp);
+ image_loader_webp.unref();
+
ResourceSaver::remove_resource_format_saver(resource_saver_webp);
resource_saver_webp.unref();
}
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 074c54ba13..4b83bcdfc4 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -87,6 +87,182 @@ void SpriteBase3D::_notification(int p_what) {
}
}
+void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, Rect2 p_src_rect) {
+ ERR_FAIL_COND(p_texture.is_null());
+
+ Rect2 final_rect;
+ Rect2 final_src_rect;
+ if (!p_texture->get_rect_region(p_dst_rect, p_src_rect, final_rect, final_src_rect)) {
+ return;
+ }
+
+ if (final_rect.size.x == 0 || final_rect.size.y == 0) {
+ return;
+ }
+
+ // 2D: 3D plane (axes match exactly when `axis == Vector3::AXIS_Z`):
+ // -X+ -X+
+ // - +
+ // Y +--------+ +--------+ +--------+ Y +--------+
+ // + | +--+ | | | (2) | | - | 0--1 |
+ // | |ab| | (1) | +--+ | (3) | 3--2 | | |ab| |
+ // | |cd| | --> | |ab| | --> | |cd| | <==> | |cd| |
+ // | +--+ | | |cd| | | |ab| | | 3--2 |
+ // | | | +--+ | | 0--1 | | |
+ // +--------+ +--------+ +--------+ +--------+
+
+ // (1) Y-wise shift `final_rect` within `p_dst_rect` so after inverting Y
+ // axis distances between top/bottom borders will be preserved (so for
+ // example AtlasTextures with vertical margins will look the same in 2D/3D).
+ final_rect.position.y = (p_dst_rect.position.y + p_dst_rect.size.y) - ((final_rect.position.y + final_rect.size.y) - p_dst_rect.position.y);
+
+ Color color = _get_color_accum();
+
+ real_t pixel_size = get_pixel_size();
+
+ // (2) Order vertices (0123) bottom-top in 2D / top-bottom in 3D.
+ Vector2 vertices[4] = {
+ (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
+ (final_rect.position + final_rect.size) * pixel_size,
+ (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
+ final_rect.position * pixel_size,
+ };
+
+ Vector2 src_tsize = p_texture->get_size();
+
+ // Properly setup UVs for impostor textures (AtlasTexture).
+ Ref<AtlasTexture> atlas_tex = p_texture;
+ if (atlas_tex != nullptr) {
+ src_tsize[0] = atlas_tex->get_atlas()->get_width();
+ src_tsize[1] = atlas_tex->get_atlas()->get_height();
+ }
+
+ // (3) Assign UVs (abcd) according to the vertices order (bottom-top in 2D / top-bottom in 3D).
+ Vector2 uvs[4] = {
+ final_src_rect.position / src_tsize,
+ (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
+ (final_src_rect.position + final_src_rect.size) / src_tsize,
+ (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
+ };
+
+ if (is_flipped_h()) {
+ SWAP(uvs[0], uvs[1]);
+ SWAP(uvs[2], uvs[3]);
+ }
+
+ if (is_flipped_v()) {
+ SWAP(uvs[0], uvs[3]);
+ SWAP(uvs[1], uvs[2]);
+ }
+
+ Vector3 normal;
+ int axis = get_axis();
+ normal[axis] = 1.0;
+
+ Plane tangent;
+ if (axis == Vector3::AXIS_X) {
+ tangent = Plane(0, 0, -1, 1);
+ } else {
+ tangent = Plane(1, 0, 0, 1);
+ }
+
+ int x_axis = ((axis + 1) % 3);
+ int y_axis = ((axis + 2) % 3);
+
+ if (axis != Vector3::AXIS_Z) {
+ SWAP(x_axis, y_axis);
+
+ for (int i = 0; i < 4; i++) {
+ //uvs[i] = Vector2(1.0,1.0)-uvs[i];
+ //SWAP(vertices[i].x,vertices[i].y);
+ if (axis == Vector3::AXIS_Y) {
+ vertices[i].y = -vertices[i].y;
+ } else if (axis == Vector3::AXIS_X) {
+ vertices[i].x = -vertices[i].x;
+ }
+ }
+ }
+
+ AABB aabb;
+
+ // Everything except position and UV is compressed.
+ uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
+ uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
+
+ uint32_t v_normal;
+ {
+ Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+
+ Vector2 res = n.octahedron_encode();
+ uint32_t value = 0;
+ value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
+ value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
+
+ v_normal = value;
+ }
+ uint32_t v_tangent;
+ {
+ Plane t = tangent;
+ Vector2 res = t.normal.octahedron_tangent_encode(t.d);
+ uint32_t value = 0;
+ value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
+ value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
+
+ v_tangent = value;
+ }
+
+ uint8_t v_color[4] = {
+ uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)),
+ uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)),
+ uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)),
+ uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0))
+ };
+
+ for (int i = 0; i < 4; i++) {
+ Vector3 vtx;
+ vtx[x_axis] = vertices[i][0];
+ vtx[y_axis] = vertices[i][1];
+ if (i == 0) {
+ aabb.position = vtx;
+ aabb.size = Vector3();
+ } else {
+ aabb.expand_to(vtx);
+ }
+
+ float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y };
+ memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8);
+
+ float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z };
+
+ memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
+ memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4);
+ memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4);
+ memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4);
+ }
+
+ RID mesh = get_mesh();
+ RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer);
+ RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer);
+
+ RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb);
+ set_aabb(aabb);
+
+ RID shader_rid;
+ StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid);
+ if (last_shader != shader_rid) {
+ RS::get_singleton()->material_set_shader(get_material(), shader_rid);
+ last_shader = shader_rid;
+ }
+ if (last_texture != p_texture->get_rid()) {
+ RS::get_singleton()->material_set_param(get_material(), "texture_albedo", p_texture->get_rid());
+ last_texture = p_texture->get_rid();
+ }
+ if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
+ RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority());
+ RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material());
+ }
+}
+
void SpriteBase3D::set_centered(bool p_center) {
centered = p_center;
_queue_redraw();
@@ -464,171 +640,17 @@ void Sprite3D::_draw() {
}
Size2 frame_size = base_rect.size / Size2(hframes, vframes);
- Point2 frame_offset = Point2(frame % hframes, frame / hframes);
- frame_offset *= frame_size;
+ Point2 frame_offset = Point2(frame % hframes, frame / hframes) * frame_size;
- Point2 dest_offset = get_offset();
+ Point2 dst_offset = get_offset();
if (is_centered()) {
- dest_offset -= frame_size / 2;
+ dst_offset -= frame_size / 2.0f;
}
Rect2 src_rect(base_rect.position + frame_offset, frame_size);
- Rect2 final_dst_rect(dest_offset, frame_size);
- Rect2 final_rect;
- Rect2 final_src_rect;
- if (!texture->get_rect_region(final_dst_rect, src_rect, final_rect, final_src_rect)) {
- return;
- }
-
- if (final_rect.size.x == 0 || final_rect.size.y == 0) {
- return;
- }
-
- Color color = _get_color_accum();
-
- real_t pixel_size = get_pixel_size();
-
- Vector2 vertices[4] = {
-
- (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
- (final_rect.position + final_rect.size) * pixel_size,
- (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
- final_rect.position * pixel_size,
-
- };
-
- Vector2 src_tsize = tsize;
-
- // Properly setup UVs for impostor textures (AtlasTexture).
- Ref<AtlasTexture> atlas_tex = texture;
- if (atlas_tex != nullptr) {
- src_tsize[0] = atlas_tex->get_atlas()->get_width();
- src_tsize[1] = atlas_tex->get_atlas()->get_height();
- }
-
- Vector2 uvs[4] = {
- final_src_rect.position / src_tsize,
- (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
- (final_src_rect.position + final_src_rect.size) / src_tsize,
- (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
- };
-
- if (is_flipped_h()) {
- SWAP(uvs[0], uvs[1]);
- SWAP(uvs[2], uvs[3]);
- }
-
- if (is_flipped_v()) {
- SWAP(uvs[0], uvs[3]);
- SWAP(uvs[1], uvs[2]);
- }
-
- Vector3 normal;
- int axis = get_axis();
- normal[axis] = 1.0;
-
- Plane tangent;
- if (axis == Vector3::AXIS_X) {
- tangent = Plane(0, 0, -1, 1);
- } else {
- tangent = Plane(1, 0, 0, 1);
- }
-
- int x_axis = ((axis + 1) % 3);
- int y_axis = ((axis + 2) % 3);
-
- if (axis != Vector3::AXIS_Z) {
- SWAP(x_axis, y_axis);
-
- for (int i = 0; i < 4; i++) {
- //uvs[i] = Vector2(1.0,1.0)-uvs[i];
- //SWAP(vertices[i].x,vertices[i].y);
- if (axis == Vector3::AXIS_Y) {
- vertices[i].y = -vertices[i].y;
- } else if (axis == Vector3::AXIS_X) {
- vertices[i].x = -vertices[i].x;
- }
- }
- }
-
- AABB aabb;
-
- // Everything except position and UV is compressed.
- uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
- uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
-
- uint32_t v_normal;
- {
- Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
-
- Vector2 res = n.octahedron_encode();
- uint32_t value = 0;
- value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
- value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
-
- v_normal = value;
- }
- uint32_t v_tangent;
- {
- Plane t = tangent;
- Vector2 res = t.normal.octahedron_tangent_encode(t.d);
- uint32_t value = 0;
- value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
- value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
+ Rect2 dst_rect(dst_offset, frame_size);
- v_tangent = value;
- }
-
- uint8_t v_color[4] = {
- uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)),
- uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)),
- uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)),
- uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0))
- };
-
- for (int i = 0; i < 4; i++) {
- Vector3 vtx;
- vtx[x_axis] = vertices[i][0];
- vtx[y_axis] = vertices[i][1];
- if (i == 0) {
- aabb.position = vtx;
- aabb.size = Vector3();
- } else {
- aabb.expand_to(vtx);
- }
-
- float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y };
- memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8);
-
- float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z };
-
- memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
- memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4);
- memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4);
- memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4);
- }
-
- RID mesh = get_mesh();
- RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer);
- RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer);
-
- RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb);
- set_aabb(aabb);
-
- RID shader_rid;
- StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid);
- if (last_shader != shader_rid) {
- RS::get_singleton()->material_set_shader(get_material(), shader_rid);
- last_shader = shader_rid;
- }
- if (last_texture != texture->get_rid()) {
- RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid());
- last_texture = texture->get_rid();
- }
- if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
- RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority());
- RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material());
- }
+ draw_texture_rect(texture, dst_rect, src_rect);
}
void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) {
@@ -831,158 +853,7 @@ void AnimatedSprite3D::_draw() {
Rect2 dst_rect(ofs, tsize);
- Rect2 final_rect;
- Rect2 final_src_rect;
- if (!texture->get_rect_region(dst_rect, src_rect, final_rect, final_src_rect)) {
- return;
- }
-
- if (final_rect.size.x == 0 || final_rect.size.y == 0) {
- return;
- }
-
- Color color = _get_color_accum();
-
- real_t pixel_size = get_pixel_size();
-
- Vector2 vertices[4] = {
-
- (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
- (final_rect.position + final_rect.size) * pixel_size,
- (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
- final_rect.position * pixel_size,
-
- };
-
- Vector2 src_tsize = tsize;
-
- // Properly setup UVs for impostor textures (AtlasTexture).
- Ref<AtlasTexture> atlas_tex = texture;
- if (atlas_tex != nullptr) {
- src_tsize[0] = atlas_tex->get_atlas()->get_width();
- src_tsize[1] = atlas_tex->get_atlas()->get_height();
- }
-
- Vector2 uvs[4] = {
- final_src_rect.position / src_tsize,
- (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
- (final_src_rect.position + final_src_rect.size) / src_tsize,
- (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
- };
-
- if (is_flipped_h()) {
- SWAP(uvs[0], uvs[1]);
- SWAP(uvs[2], uvs[3]);
- }
- if (is_flipped_v()) {
- SWAP(uvs[0], uvs[3]);
- SWAP(uvs[1], uvs[2]);
- }
-
- Vector3 normal;
- int axis = get_axis();
- normal[axis] = 1.0;
-
- Plane tangent;
- if (axis == Vector3::AXIS_X) {
- tangent = Plane(0, 0, -1, -1);
- } else {
- tangent = Plane(1, 0, 0, -1);
- }
-
- int x_axis = ((axis + 1) % 3);
- int y_axis = ((axis + 2) % 3);
-
- if (axis != Vector3::AXIS_Z) {
- SWAP(x_axis, y_axis);
-
- for (int i = 0; i < 4; i++) {
- //uvs[i] = Vector2(1.0,1.0)-uvs[i];
- //SWAP(vertices[i].x,vertices[i].y);
- if (axis == Vector3::AXIS_Y) {
- vertices[i].y = -vertices[i].y;
- } else if (axis == Vector3::AXIS_X) {
- vertices[i].x = -vertices[i].x;
- }
- }
- }
-
- AABB aabb;
-
- // Everything except position and UV is compressed.
- uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
- uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
-
- uint32_t v_normal;
- {
- Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
-
- Vector2 res = n.octahedron_encode();
- uint32_t value = 0;
- value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
- value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
-
- v_normal = value;
- }
- uint32_t v_tangent;
- {
- Plane t = tangent;
- Vector2 res = t.normal.octahedron_tangent_encode(t.d);
- uint32_t value = 0;
- value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
- value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
- v_tangent = value;
- }
-
- uint8_t v_color[4] = {
- uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)),
- uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)),
- uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)),
- uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0))
- };
-
- for (int i = 0; i < 4; i++) {
- Vector3 vtx;
- vtx[x_axis] = vertices[i][0];
- vtx[y_axis] = vertices[i][1];
- if (i == 0) {
- aabb.position = vtx;
- aabb.size = Vector3();
- } else {
- aabb.expand_to(vtx);
- }
-
- float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y };
- memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8);
-
- float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z };
- memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
- memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4);
- memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4);
- memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4);
- }
-
- RID mesh = get_mesh();
- RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer);
- RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer);
-
- RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb);
- set_aabb(aabb);
-
- RID shader_rid;
- StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid);
- if (last_shader != shader_rid) {
- RS::get_singleton()->material_set_shader(get_material(), shader_rid);
- last_shader = shader_rid;
- }
- if (last_texture != texture->get_rid()) {
- RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid());
- last_texture = texture->get_rid();
- }
- if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
- RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority());
- RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material());
- }
+ draw_texture_rect(texture, dst_rect, src_rect);
}
void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const {
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 781a3f9173..edc48c7b71 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -80,6 +80,9 @@ private:
RID mesh;
RID material;
+ RID last_shader;
+ RID last_texture;
+
bool flags[FLAG_MAX] = {};
AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED;
StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED;
@@ -94,6 +97,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _draw() = 0;
+ void draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, Rect2 p_src_rect);
_FORCE_INLINE_ void set_aabb(const AABB &p_aabb) { aabb = p_aabb; }
_FORCE_INLINE_ RID &get_mesh() { return mesh; }
_FORCE_INLINE_ RID &get_material() { return material; }
@@ -167,9 +171,6 @@ class Sprite3D : public SpriteBase3D {
int vframes = 1;
int hframes = 1;
- RID last_shader;
- RID last_texture;
-
protected:
virtual void _draw() override;
static void _bind_methods();
@@ -225,9 +226,6 @@ class AnimatedSprite3D : public SpriteBase3D {
double _get_frame_duration();
void _reset_timeout();
- RID last_shader;
- RID last_texture;
-
protected:
virtual void _draw() override;
static void _bind_methods();
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index bec378dd91..2c395ec07d 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -32,9 +32,6 @@
#include "core/io/compression.h"
#include "scene/main/timer.h"
-void HTTPRequest::_redirect_request(const String &p_new_url) {
-}
-
Error HTTPRequest::_request() {
return client->connect_to_host(url, port, use_tls, validate_tls);
}
@@ -48,6 +45,7 @@ Error HTTPRequest::_parse_url(const String &p_url) {
body_len = -1;
body.clear();
downloaded.set(0);
+ final_body_size.set(0);
redirections = 0;
String scheme;
@@ -153,7 +151,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust
client->set_blocking_mode(false);
err = _request();
if (err != OK) {
- call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return ERR_CANT_CONNECT;
}
@@ -169,7 +167,7 @@ void HTTPRequest::_thread_func(void *p_userdata) {
Error err = hr->_request();
if (err != OK) {
- hr->call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
+ hr->_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
} else {
while (!hr->thread_request_quit.is_set()) {
bool exit = hr->_update_connection();
@@ -198,6 +196,7 @@ void HTTPRequest::cancel_request() {
}
file.unref();
+ decompressor.unref();
client->close();
body.clear();
got_response = false;
@@ -208,7 +207,7 @@ void HTTPRequest::cancel_request() {
bool HTTPRequest::_handle_response(bool *ret_value) {
if (!client->has_response()) {
- call_deferred(SNAME("_request_done"), RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray());
*ret_value = true;
return true;
}
@@ -219,6 +218,9 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
client->get_response_headers(&rheaders);
response_headers.clear();
downloaded.set(0);
+ final_body_size.set(0);
+ decompressor.unref();
+
for (const String &E : rheaders) {
response_headers.push_back(E);
}
@@ -227,7 +229,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
// Handle redirect.
if (max_redirects >= 0 && redirections >= max_redirects) {
- call_deferred(SNAME("_request_done"), RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
*ret_value = true;
return true;
}
@@ -259,6 +261,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
body_len = -1;
body.clear();
downloaded.set(0);
+ final_body_size.set(0);
redirections = new_redirs;
*ret_value = false;
return true;
@@ -266,13 +269,26 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
}
}
+ // Check if we need to start streaming decompression.
+ String content_encoding;
+ if (accept_gzip) {
+ content_encoding = get_header_value(response_headers, "Content-Encoding").to_lower();
+ }
+ if (content_encoding == "gzip") {
+ decompressor.instantiate();
+ decompressor->start_decompression(false, get_download_chunk_size() * 2);
+ } else if (content_encoding == "deflate") {
+ decompressor.instantiate();
+ decompressor->start_decompression(true, get_download_chunk_size() * 2);
+ }
+
return false;
}
bool HTTPRequest::_update_connection() {
switch (client->get_status()) {
case HTTPClient::STATUS_DISCONNECTED: {
- call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return true; // End it, since it's disconnected.
} break;
case HTTPClient::STATUS_RESOLVING: {
@@ -281,7 +297,7 @@ bool HTTPRequest::_update_connection() {
return false;
} break;
case HTTPClient::STATUS_CANT_RESOLVE: {
- call_deferred(SNAME("_request_done"), RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
@@ -291,7 +307,7 @@ bool HTTPRequest::_update_connection() {
return false;
} break; // Connecting to IP.
case HTTPClient::STATUS_CANT_CONNECT: {
- call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
@@ -306,16 +322,16 @@ bool HTTPRequest::_update_connection() {
return ret_value;
}
- call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
return true;
}
if (body_len < 0) {
// Chunked transfer is done.
- call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body);
+ _defer_done(RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
- call_deferred(SNAME("_request_done"), RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
return true;
// Request might have been done.
} else {
@@ -324,7 +340,7 @@ bool HTTPRequest::_update_connection() {
int size = request_data.size();
Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size);
if (err != OK) {
- call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
}
@@ -347,7 +363,7 @@ bool HTTPRequest::_update_connection() {
}
if (!client->is_response_chunked() && client->get_response_body_length() == 0) {
- call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
return true;
}
@@ -356,14 +372,14 @@ bool HTTPRequest::_update_connection() {
body_len = client->get_response_body_length();
if (body_size_limit >= 0 && body_len > body_size_limit) {
- call_deferred(SNAME("_request_done"), RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
return true;
}
if (!download_to_file.is_empty()) {
file = FileAccess::open(download_to_file, FileAccess::WRITE);
if (file.is_null()) {
- call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray());
return true;
}
}
@@ -375,14 +391,33 @@ bool HTTPRequest::_update_connection() {
}
PackedByteArray chunk = client->read_response_body_chunk();
+ downloaded.add(chunk.size());
+
+ // Decompress chunk if needed.
+ if (decompressor.is_valid()) {
+ Error err = decompressor->put_data(chunk.ptr(), chunk.size());
+ if (err == OK) {
+ chunk.resize(decompressor->get_available_bytes());
+ err = decompressor->get_data(chunk.ptrw(), chunk.size());
+ }
+ if (err != OK) {
+ _defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray());
+ return true;
+ }
+ }
+ final_body_size.add(chunk.size());
+
+ if (body_size_limit >= 0 && final_body_size.get() > body_size_limit) {
+ _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
+ return true;
+ }
if (chunk.size()) {
- downloaded.add(chunk.size());
if (file.is_valid()) {
const uint8_t *r = chunk.ptr();
file->store_buffer(r, chunk.size());
if (file->get_error() != OK) {
- call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
+ _defer_done(RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
return true;
}
} else {
@@ -390,19 +425,14 @@ bool HTTPRequest::_update_connection() {
}
}
- if (body_size_limit >= 0 && downloaded.get() > body_size_limit) {
- call_deferred(SNAME("_request_done"), RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
- return true;
- }
-
if (body_len >= 0) {
if (downloaded.get() == body_len) {
- call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body);
+ _defer_done(RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
} else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) {
// We read till EOF, with no errors. Request is done.
- call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body);
+ _defer_done(RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
@@ -410,11 +440,11 @@ bool HTTPRequest::_update_connection() {
} break; // Request resulted in body: break which must be read.
case HTTPClient::STATUS_CONNECTION_ERROR: {
- call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: {
- call_deferred(SNAME("_request_done"), RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
}
@@ -422,41 +452,13 @@ bool HTTPRequest::_update_connection() {
ERR_FAIL_V(false);
}
+void HTTPRequest::_defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
+ call_deferred(SNAME("_request_done"), p_status, p_code, p_headers, p_data);
+}
+
void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
cancel_request();
- // Determine if the request body is compressed.
- bool is_compressed;
- String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower();
- Compression::Mode mode;
- if (content_encoding == "gzip") {
- mode = Compression::Mode::MODE_GZIP;
- is_compressed = true;
- } else if (content_encoding == "deflate") {
- mode = Compression::Mode::MODE_DEFLATE;
- is_compressed = true;
- } else {
- is_compressed = false;
- }
-
- if (accept_gzip && is_compressed && p_data.size() > 0) {
- // Decompress request body
- PackedByteArray decompressed;
- int result = Compression::decompress_dynamic(&decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode);
- if (result == OK) {
- emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, decompressed);
- return;
- } else if (result == -5) {
- WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit");
- p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED;
- // Just return the raw data if we failed to decompress it.
- } else {
- WARN_PRINT("Failed to decompress HTTP response body");
- p_status = RESULT_BODY_DECOMPRESS_FAILED;
- // Just return the raw data if we failed to decompress it.
- }
- }
-
emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data);
}
@@ -566,7 +568,7 @@ double HTTPRequest::get_timeout() {
void HTTPRequest::_timeout() {
cancel_request();
- call_deferred(SNAME("_request_done"), RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray());
+ _defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray());
}
void HTTPRequest::_bind_methods() {
@@ -594,7 +596,6 @@ void HTTPRequest::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_downloaded_bytes"), &HTTPRequest::get_downloaded_bytes);
ClassDB::bind_method(D_METHOD("get_body_size"), &HTTPRequest::get_body_size);
- ClassDB::bind_method(D_METHOD("_redirect_request"), &HTTPRequest::_redirect_request);
ClassDB::bind_method(D_METHOD("_request_done"), &HTTPRequest::_request_done);
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout);
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 290bacd9d2..80445684b0 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -32,6 +32,7 @@
#define HTTP_REQUEST_H
#include "core/io/http_client.h"
+#include "core/io/stream_peer_gzip.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
#include "scene/main/node.h"
@@ -84,10 +85,12 @@ private:
String download_to_file;
+ Ref<StreamPeerGZIP> decompressor;
Ref<FileAccess> file;
int body_len = -1;
SafeNumeric<int> downloaded;
+ SafeNumeric<int> final_body_size;
int body_size_limit = -1;
int redirections = 0;
@@ -113,6 +116,7 @@ private:
Thread thread;
+ void _defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
static void _thread_func(void *p_userdata);