summaryrefslogtreecommitdiff
path: root/core/io
diff options
context:
space:
mode:
Diffstat (limited to 'core/io')
-rw-r--r--core/io/file_access_pack.h4
-rw-r--r--core/io/image.cpp52
-rw-r--r--core/io/image.h15
-rw-r--r--core/io/resource.cpp116
-rw-r--r--core/io/resource.h4
-rw-r--r--core/io/resource_format_binary.cpp10
-rw-r--r--core/io/resource_loader.cpp51
7 files changed, 159 insertions, 93 deletions
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 19a0cce796..e656f6b885 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -84,8 +84,8 @@ private:
return (a == p_val.a) && (b == p_val.b);
}
static uint32_t hash(const PathMD5 &p_val) {
- uint32_t h = hash_djb2_one_32(p_val.a);
- return hash_djb2_one_32(p_val.b, h);
+ uint32_t h = hash_murmur3_one_32(p_val.a);
+ return hash_fmix32(hash_murmur3_one_32(p_val.b, h));
}
PathMD5() {}
diff --git a/core/io/image.cpp b/core/io/image.cpp
index dfba45c4e9..f065dac212 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -81,9 +81,15 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
};
SavePNGFunc Image::save_png_func = nullptr;
+SaveJPGFunc Image::save_jpg_func = nullptr;
SaveEXRFunc Image::save_exr_func = nullptr;
SavePNGBufferFunc Image::save_png_buffer_func = nullptr;
+SaveEXRBufferFunc Image::save_exr_buffer_func = nullptr;
+SaveJPGBufferFunc Image::save_jpg_buffer_func = nullptr;
+
+SaveWebPFunc Image::save_webp_func = nullptr;
+SaveWebPBufferFunc Image::save_webp_buffer_func = nullptr;
void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel) {
uint32_t ofs = (p_y * width + p_x) * p_pixel_size;
@@ -2286,6 +2292,14 @@ Error Image::save_png(const String &p_path) const {
return save_png_func(p_path, Ref<Image>((Image *)this));
}
+Error Image::save_jpg(const String &p_path, float p_quality) const {
+ if (save_jpg_func == nullptr) {
+ return ERR_UNAVAILABLE;
+ }
+
+ return save_jpg_func(p_path, Ref<Image>((Image *)this), p_quality);
+}
+
Vector<uint8_t> Image::save_png_to_buffer() const {
if (save_png_buffer_func == nullptr) {
return Vector<uint8_t>();
@@ -2294,6 +2308,14 @@ Vector<uint8_t> Image::save_png_to_buffer() const {
return save_png_buffer_func(Ref<Image>((Image *)this));
}
+Vector<uint8_t> Image::save_jpg_to_buffer(float p_quality) const {
+ if (save_jpg_buffer_func == nullptr) {
+ return Vector<uint8_t>();
+ }
+
+ return save_jpg_buffer_func(Ref<Image>((Image *)this), p_quality);
+}
+
Error Image::save_exr(const String &p_path, bool p_grayscale) const {
if (save_exr_func == nullptr) {
return ERR_UNAVAILABLE;
@@ -2302,6 +2324,31 @@ Error Image::save_exr(const String &p_path, bool p_grayscale) const {
return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale);
}
+Vector<uint8_t> Image::save_exr_to_buffer(bool p_grayscale) const {
+ if (save_exr_buffer_func == nullptr) {
+ return Vector<uint8_t>();
+ }
+ return save_exr_buffer_func(Ref<Image>((Image *)this), p_grayscale);
+}
+
+Error Image::save_webp(const String &p_path, const bool p_lossy, const float p_quality) const {
+ if (save_webp_func == nullptr) {
+ return ERR_UNAVAILABLE;
+ }
+ ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), ERR_INVALID_PARAMETER, "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive).");
+
+ return save_webp_func(p_path, Ref<Image>((Image *)this), p_lossy, p_quality);
+}
+
+Vector<uint8_t> Image::save_webp_to_buffer(const bool p_lossy, const float p_quality) const {
+ if (save_webp_buffer_func == nullptr) {
+ return Vector<uint8_t>();
+ }
+ ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), Vector<uint8_t>(), "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive).");
+
+ return save_webp_buffer_func(Ref<Image>((Image *)this), p_lossy, p_quality);
+}
+
int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) {
int mm;
return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0);
@@ -3138,7 +3185,12 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load", "path"), &Image::load);
ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png);
ClassDB::bind_method(D_METHOD("save_png_to_buffer"), &Image::save_png_to_buffer);
+ ClassDB::bind_method(D_METHOD("save_jpg", "path", "quality"), &Image::save_jpg, DEFVAL(0.75));
+ ClassDB::bind_method(D_METHOD("save_jpg_to_buffer", "quality"), &Image::save_jpg_to_buffer, DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("save_exr_to_buffer", "grayscale"), &Image::save_exr_to_buffer, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("save_webp", "path", "lossy", "quality"), &Image::save_webp, DEFVAL(false), DEFVAL(0.75f));
+ ClassDB::bind_method(D_METHOD("save_webp_to_buffer", "lossy", "quality"), &Image::save_webp_to_buffer, DEFVAL(false), DEFVAL(0.75f));
ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha);
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
diff --git a/core/io/image.h b/core/io/image.h
index 1025554d51..2cad26f3e9 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -45,17 +45,27 @@ class Image;
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
+typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
+typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
+typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
+typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
+typedef Vector<uint8_t> (*SaveEXRBufferFunc)(const Ref<Image> &p_img, bool p_grayscale);
class Image : public Resource {
GDCLASS(Image, Resource);
public:
static SavePNGFunc save_png_func;
+ static SaveJPGFunc save_jpg_func;
static SaveEXRFunc save_exr_func;
static SavePNGBufferFunc save_png_buffer_func;
+ static SaveEXRBufferFunc save_exr_buffer_func;
+ static SaveJPGBufferFunc save_jpg_buffer_func;
+ static SaveWebPFunc save_webp_func;
+ static SaveWebPBufferFunc save_webp_buffer_func;
enum {
MAX_WIDTH = (1 << 24), // force a limit somehow
@@ -281,8 +291,13 @@ public:
Error load(const String &p_path);
Error save_png(const String &p_path) const;
+ Error save_jpg(const String &p_path, float p_quality = 0.75) const;
Vector<uint8_t> save_png_to_buffer() const;
+ Vector<uint8_t> save_jpg_to_buffer(float p_quality = 0.75) const;
+ Vector<uint8_t> save_exr_to_buffer(bool p_grayscale) const;
Error save_exr(const String &p_path, bool p_grayscale) const;
+ Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const;
+ Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const;
void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
create(p_width, p_height, p_use_mipmaps, p_format);
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index ad01eb1083..fec5ca5c7b 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -52,41 +52,36 @@ void Resource::set_path(const String &p_path, bool p_take_over) {
return;
}
+ if (p_path.is_empty()) {
+ p_take_over = false; // Can't take over an empty path
+ }
+
+ ResourceCache::lock.lock();
+
if (!path_cache.is_empty()) {
- ResourceCache::lock.write_lock();
ResourceCache::resources.erase(path_cache);
- ResourceCache::lock.write_unlock();
}
path_cache = "";
- ResourceCache::lock.read_lock();
- bool has_path = ResourceCache::resources.has(p_path);
- ResourceCache::lock.read_unlock();
+ Ref<Resource> existing = ResourceCache::get_ref(p_path);
- if (has_path) {
+ if (existing.is_valid()) {
if (p_take_over) {
- ResourceCache::lock.write_lock();
- Resource **res = ResourceCache::resources.getptr(p_path);
- if (res) {
- (*res)->set_name("");
- }
- ResourceCache::lock.write_unlock();
+ existing->path_cache = String();
+ ResourceCache::resources.erase(p_path);
} else {
- ResourceCache::lock.read_lock();
- bool exists = ResourceCache::resources.has(p_path);
- ResourceCache::lock.read_unlock();
-
- ERR_FAIL_COND_MSG(exists, "Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion).");
+ ResourceCache::lock.unlock();
+ ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion).");
}
}
+
path_cache = p_path;
if (!path_cache.is_empty()) {
- ResourceCache::lock.write_lock();
ResourceCache::resources[path_cache] = this;
- ResourceCache::lock.write_unlock();
}
+ ResourceCache::lock.unlock();
_resource_path_changed();
}
@@ -100,14 +95,14 @@ String Resource::generate_scene_unique_id() {
// If it's not unique it does not matter because the saver will try again.
OS::Date date = OS::get_singleton()->get_date();
OS::Time time = OS::get_singleton()->get_time();
- uint32_t hash = hash_djb2_one_32(OS::get_singleton()->get_ticks_usec());
- hash = hash_djb2_one_32(date.year, hash);
- hash = hash_djb2_one_32(date.month, hash);
- hash = hash_djb2_one_32(date.day, hash);
- hash = hash_djb2_one_32(time.hour, hash);
- hash = hash_djb2_one_32(time.minute, hash);
- hash = hash_djb2_one_32(time.second, hash);
- hash = hash_djb2_one_32(Math::rand(), hash);
+ uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec());
+ hash = hash_murmur3_one_32(date.year, hash);
+ hash = hash_murmur3_one_32(date.month, hash);
+ hash = hash_murmur3_one_32(date.day, hash);
+ hash = hash_murmur3_one_32(time.hour, hash);
+ hash = hash_murmur3_one_32(time.minute, hash);
+ hash = hash_murmur3_one_32(time.second, hash);
+ hash = hash_murmur3_one_32(Math::rand(), hash);
static constexpr uint32_t characters = 5;
static constexpr uint32_t char_count = ('z' - 'a');
@@ -328,7 +323,7 @@ void Resource::notify_change_to_owners() {
#ifdef TOOLS_ENABLED
uint32_t Resource::hash_edited_version() const {
- uint32_t hash = hash_djb2_one_32(get_edited_version());
+ uint32_t hash = hash_murmur3_one_32(get_edited_version());
List<PropertyInfo> plist;
get_property_list(&plist);
@@ -337,7 +332,7 @@ uint32_t Resource::hash_edited_version() const {
if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) {
Ref<Resource> res = get(E.name);
if (res.is_valid()) {
- hash = hash_djb2_one_32(res->hash_edited_version(), hash);
+ hash = hash_murmur3_one_32(res->hash_edited_version(), hash);
}
}
}
@@ -380,7 +375,7 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
return;
}
- ResourceCache::lock.write_lock();
+ ResourceCache::lock.lock();
if (p_remapped) {
ResourceLoader::remapped_list.add(&remapped_list);
@@ -388,7 +383,7 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
ResourceLoader::remapped_list.remove(&remapped_list);
}
- ResourceCache::lock.write_unlock();
+ ResourceCache::lock.unlock();
}
bool Resource::is_translation_remapped() const {
@@ -455,9 +450,9 @@ Resource::Resource() :
Resource::~Resource() {
if (!path_cache.is_empty()) {
- ResourceCache::lock.write_lock();
+ ResourceCache::lock.lock();
ResourceCache::resources.erase(path_cache);
- ResourceCache::lock.write_unlock();
+ ResourceCache::lock.unlock();
}
if (owners.size()) {
WARN_PRINT("Resource is still owned.");
@@ -469,7 +464,7 @@ HashMap<String, Resource *> ResourceCache::resources;
HashMap<String, HashMap<String, String>> ResourceCache::resource_path_cache;
#endif
-RWLock ResourceCache::lock;
+Mutex ResourceCache::lock;
#ifdef TOOLS_ENABLED
RWLock ResourceCache::path_cache_lock;
#endif
@@ -491,46 +486,67 @@ void ResourceCache::reload_externals() {
}
bool ResourceCache::has(const String &p_path) {
- lock.read_lock();
- bool b = resources.has(p_path);
- lock.read_unlock();
+ lock.lock();
+
+ Resource **res = resources.getptr(p_path);
- return b;
+ if (res && (*res)->reference_get_count() == 0) {
+ // This resource is in the process of being deleted, ignore its existence.
+ (*res)->path_cache = String();
+ resources.erase(p_path);
+ res = nullptr;
+ }
+
+ lock.unlock();
+
+ if (!res) {
+ return false;
+ }
+
+ return true;
}
-Resource *ResourceCache::get(const String &p_path) {
- lock.read_lock();
+Ref<Resource> ResourceCache::get_ref(const String &p_path) {
+ Ref<Resource> ref;
+ lock.lock();
Resource **res = resources.getptr(p_path);
- lock.read_unlock();
+ if (res) {
+ ref = Ref<Resource>(*res);
+ }
- if (!res) {
- return nullptr;
+ if (res && !ref.is_valid()) {
+ // This resource is in the process of being deleted, ignore its existence
+ (*res)->path_cache = String();
+ resources.erase(p_path);
+ res = nullptr;
}
- return *res;
+ lock.unlock();
+
+ return ref;
}
void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
- lock.read_lock();
+ lock.lock();
for (KeyValue<String, Resource *> &E : resources) {
p_resources->push_back(Ref<Resource>(E.value));
}
- lock.read_unlock();
+ lock.unlock();
}
int ResourceCache::get_cached_resource_count() {
- lock.read_lock();
+ lock.lock();
int rc = resources.size();
- lock.read_unlock();
+ lock.unlock();
return rc;
}
void ResourceCache::dump(const char *p_file, bool p_short) {
#ifdef DEBUG_ENABLED
- lock.read_lock();
+ lock.lock();
HashMap<String, int> type_count;
@@ -562,7 +578,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
}
}
- lock.read_unlock();
+ lock.unlock();
#else
WARN_PRINT("ResourceCache::dump only with in debug builds.");
#endif
diff --git a/core/io/resource.h b/core/io/resource.h
index a45bc6e1e4..a2cde87990 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -153,7 +153,7 @@ public:
class ResourceCache {
friend class Resource;
friend class ResourceLoader; //need the lock
- static RWLock lock;
+ static Mutex lock;
static HashMap<String, Resource *> resources;
#ifdef TOOLS_ENABLED
static HashMap<String, HashMap<String, String>> resource_path_cache; // Each tscn has a set of resource paths and IDs.
@@ -166,7 +166,7 @@ class ResourceCache {
public:
static void reload_externals();
static bool has(const String &p_path);
- static Resource *get(const String &p_path);
+ static Ref<Resource> get_ref(const String &p_path);
static void dump(const char *p_file = nullptr, bool p_short = false);
static void get_cached_resources(List<Ref<Resource>> *p_resources);
static int get_cached_resource_count();
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 2469e1a4be..b1c50e829c 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -693,7 +693,7 @@ Error ResourceLoaderBinary::load() {
}
if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE && ResourceCache::has(path)) {
- Ref<Resource> cached = ResourceCache::get(path);
+ Ref<Resource> cached = ResourceCache::get_ref(path);
if (cached.is_valid()) {
//already loaded, don't do anything
error = OK;
@@ -717,10 +717,10 @@ Error ResourceLoaderBinary::load() {
if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(path)) {
//use the existing one
- Resource *r = ResourceCache::get(path);
- if (r->get_class() == t) {
- r->reset_state();
- res = Ref<Resource>(r);
+ Ref<Resource> cached = ResourceCache::get_ref(path);
+ if (cached->get_class() == t) {
+ cached->reset_state();
+ res = cached;
}
}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index fb21db1a19..2cd455475c 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -335,23 +335,15 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
thread_load_mutex->unlock();
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Attempted to load a resource already being loaded from this thread, cyclic reference?");
}
- //lock first if possible
- ResourceCache::lock.read_lock();
-
- //get ptr
- Resource **rptr = ResourceCache::resources.getptr(local_path);
-
- if (rptr) {
- Ref<Resource> res(*rptr);
- //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached
- if (res.is_valid()) {
- //referencing is fine
- load_task.resource = res;
- load_task.status = THREAD_LOAD_LOADED;
- load_task.progress = 1.0;
- }
+
+ Ref<Resource> existing = ResourceCache::get_ref(local_path);
+
+ if (existing.is_valid()) {
+ //referencing is fine
+ load_task.resource = existing;
+ load_task.status = THREAD_LOAD_LOADED;
+ load_task.progress = 1.0;
}
- ResourceCache::lock.read_unlock();
}
if (!p_source_resource.is_empty()) {
@@ -530,27 +522,18 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi
}
//Is it cached?
- ResourceCache::lock.read_lock();
-
- Resource **rptr = ResourceCache::resources.getptr(local_path);
- if (rptr) {
- Ref<Resource> res(*rptr);
+ Ref<Resource> existing = ResourceCache::get_ref(local_path);
- //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached
- if (res.is_valid()) {
- ResourceCache::lock.read_unlock();
- thread_load_mutex->unlock();
-
- if (r_error) {
- *r_error = OK;
- }
+ if (existing.is_valid()) {
+ thread_load_mutex->unlock();
- return res; //use cached
+ if (r_error) {
+ *r_error = OK;
}
- }
- ResourceCache::lock.read_unlock();
+ return existing; //use cached
+ }
//load using task (but this thread)
ThreadLoadTask load_task;
@@ -867,7 +850,7 @@ String ResourceLoader::path_remap(const String &p_path) {
}
void ResourceLoader::reload_translation_remaps() {
- ResourceCache::lock.read_lock();
+ ResourceCache::lock.lock();
List<Resource *> to_reload;
SelfList<Resource> *E = remapped_list.first();
@@ -877,7 +860,7 @@ void ResourceLoader::reload_translation_remaps() {
E = E->next();
}
- ResourceCache::lock.read_unlock();
+ ResourceCache::lock.unlock();
//now just make sure to not delete any of these resources while changing locale..
while (to_reload.front()) {