diff options
220 files changed, 2869 insertions, 4522 deletions
diff --git a/.gitignore b/.gitignore index 1a83e4707e..b939bb16d5 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,7 @@ gmon.out # Jetbrains IDEs .idea/ +.fleet/ # Kate *.kate-swp diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 9ac5cc9398..1ce50bc186 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -201,18 +201,18 @@ typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_ typedef uint64_t (*GDNativeExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance); typedef struct { - uint32_t type; + GDNativeVariantType type; const char *name; const char *class_name; - uint32_t hint; + uint32_t hint; // Bitfield of `PropertyHint` (defined in `extension_api.json`) const char *hint_string; - uint32_t usage; + uint32_t usage; // Bitfield of `PropertyUsageFlags` (defined in `extension_api.json`) } GDNativePropertyInfo; typedef struct { const char *name; GDNativePropertyInfo return_value; - uint32_t flags; // From GDNativeExtensionClassMethodFlags + uint32_t flags; // Bitfield of `GDNativeExtensionClassMethodFlags` int32_t id; GDNativePropertyInfo *arguments; uint32_t argument_count; @@ -234,6 +234,8 @@ typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtension typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); typedef struct { + GDNativeBool is_virtual; + GDNativeBool is_abstract; GDNativeExtensionClassSet set_func; GDNativeExtensionClassGet get_func; GDNativeExtensionClassGetPropertyList get_property_list_func; @@ -292,7 +294,7 @@ typedef struct { void *method_userdata; GDNativeExtensionClassMethodCall call_func; GDNativeExtensionClassMethodPtrCall ptrcall_func; - uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */ + uint32_t method_flags; // Bitfield of `GDNativeExtensionClassMethodFlags` uint32_t argument_count; GDNativeBool has_return_value; GDNativeExtensionClassMethodGetArgumentType get_argument_type_func; diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index 6418da2235..cc019584a5 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -152,6 +152,8 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr extension->native_extension.parent_class_name = parent_class_name; extension->native_extension.class_name = class_name; extension->native_extension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR; + extension->native_extension.is_virtual = p_extension_funcs->is_virtual; + extension->native_extension.is_abstract = p_extension_funcs->is_abstract; extension->native_extension.set = p_extension_funcs->set_func; extension->native_extension.get = p_extension_funcs->get_func; extension->native_extension.get_property_list = p_extension_funcs->get_property_list_func; diff --git a/core/io/image.cpp b/core/io/image.cpp index 4be624a5a8..3450eb7abd 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -982,7 +982,7 @@ void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { } void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { - ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first."); + ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use set_data() first."); ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; @@ -1017,7 +1017,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; if (interpolate_mipmaps) { - dst2.create(p_width, p_height, false, format); + dst2.initialize_data(p_width, p_height, false, format); } bool had_mipmaps = mipmaps; @@ -2016,9 +2016,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con uint8_t* wr = imgdata.ptrw(); memcpy(wr.ptr(), ptr, size); wr = uint8_t*(); - Ref<Image> im; - im.instantiate(); - im->create(w, h, false, format, imgdata); + Ref<Image> im = Image::create_from_data(w, h, false, format, imgdata); im->save_png("res://mipmap_" + itos(i) + ".png"); } #endif @@ -2051,7 +2049,25 @@ Vector<uint8_t> Image::get_data() const { return data; } -void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { +Ref<Image> Image::create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { + Ref<Image> image; + image.instantiate(); + image->initialize_data(p_width, p_height, p_use_mipmaps, p_format); + return image; +} + +Ref<Image> Image::create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { + Ref<Image> image; + image.instantiate(); + image->initialize_data(p_width, p_height, p_use_mipmaps, p_format, p_data); + return image; +} + +void Image::set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { + initialize_data(p_width, p_height, p_use_mipmaps, p_format, p_data); +} + +void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, @@ -2077,7 +2093,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma format = p_format; } -void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { +void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, @@ -2115,7 +2131,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma mipmaps = p_use_mipmaps; } -void Image::create(const char **p_xpm) { +void Image::initialize_data(const char **p_xpm) { int size_width = 0; int size_height = 0; int pixelchars = 0; @@ -2230,7 +2246,7 @@ void Image::create(const char **p_xpm) { } if (line == colormap_size) { status = READING_PIXELS; - create(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8); + initialize_data(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8); data_write = data.ptrw(); pixel_size = has_alpha ? 4 : 3; } @@ -2559,7 +2575,7 @@ Image::Image(const char **p_xpm) { mipmaps = false; format = FORMAT_L8; - create(p_xpm); + initialize_data(p_xpm); } Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { @@ -2568,7 +2584,7 @@ Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { mipmaps = p_use_mipmaps; format = FORMAT_L8; - create(p_width, p_height, p_use_mipmaps, p_format); + initialize_data(p_width, p_height, p_use_mipmaps, p_format); } Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { @@ -2577,7 +2593,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const V mipmaps = p_mipmaps; format = FORMAT_L8; - create(p_width, p_height, p_mipmaps, p_format, p_data); + initialize_data(p_width, p_height, p_mipmaps, p_format, p_data); } Rect2i Image::get_used_rect() const { @@ -2931,7 +2947,7 @@ void Image::_set_data(const Dictionary &p_data) { ERR_FAIL_COND(ddformat == FORMAT_MAX); - create(dwidth, dheight, dmipmaps, ddformat, ddata); + initialize_data(dwidth, dheight, dmipmaps, ddformat, ddata); } Dictionary Image::_get_data() const { @@ -3294,8 +3310,9 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps); - ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty); - ClassDB::bind_method(D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data); + ClassDB::bind_static_method("Image", D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty); + ClassDB::bind_static_method("Image", D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data); + ClassDB::bind_method(D_METHOD("set_data", "width", "height", "use_mipmaps", "format", "data"), &Image::set_data); ClassDB::bind_method(D_METHOD("is_empty"), &Image::is_empty); @@ -3460,9 +3477,7 @@ Ref<Image> Image::rgbe_to_srgb() { ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>()); - Ref<Image> new_image; - new_image.instantiate(); - new_image->create(width, height, false, Image::FORMAT_RGB8); + Ref<Image> new_image = create_empty(width, height, false, Image::FORMAT_RGB8); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { diff --git a/core/io/image.h b/core/io/image.h index fd264a7a38..194ee05bc7 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -279,12 +279,12 @@ public: void normalize(); //for normal maps /** - * Create a new image of a given size and format. Current image will be lost + * Creates new internal image data of a given size and format. Current image will be lost. */ - void create(int p_width, int p_height, bool p_use_mipmaps, Format p_format); - void create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format); + void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + void initialize_data(const char **p_xpm); - void create(const char **p_xpm); /** * returns true when the image is empty (0,0) in size */ @@ -303,13 +303,9 @@ public: 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); - } - - void create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - create(p_width, p_height, p_use_mipmaps, p_format, p_data); - } + static Ref<Image> create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format); + static Ref<Image> create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + void set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); /** * create an empty image diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index e573e8de19..200e5f5e83 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -37,11 +37,17 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) { String fname; fname.parse_utf8(p_fname); + int file_access_mode = 0; if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - (*fa) = FileAccess::open(fname, FileAccess::WRITE); - } else { - (*fa) = FileAccess::open(fname, FileAccess::READ); + file_access_mode |= FileAccess::WRITE; } + if (mode & ZLIB_FILEFUNC_MODE_READ) { + file_access_mode |= FileAccess::READ; + } + if (mode & ZLIB_FILEFUNC_MODE_CREATE) { + file_access_mode |= FileAccess::WRITE_READ; + } + (*fa) = FileAccess::open(fname, file_access_mode); if (fa->is_null()) { return nullptr; diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 996f4f4d67..561970d2ee 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -62,6 +62,7 @@ subject to the following restrictions: #include "core/math/aabb.h" #include "core/math/math_defs.h" #include "core/os/memory.h" +#include "core/templates/oa_hash_map.h" #include "core/templates/paged_allocator.h" #include <string.h> @@ -2252,19 +2253,62 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3 r_mesh.vertices = ch.vertices; + // Tag which face each edge belongs to + LocalVector<int32_t> edge_faces; + edge_faces.resize(ch.edges.size()); + + for (uint32_t i = 0; i < ch.edges.size(); i++) { + edge_faces[i] = -1; + } + + for (uint32_t i = 0; i < ch.faces.size(); i++) { + const Edge *e_start = &ch.edges[ch.faces[i]]; + const Edge *e = e_start; + do { + int64_t ofs = e - ch.edges.ptr(); + edge_faces[ofs] = i; + + e = e->get_next_edge_of_face(); + } while (e != e_start); + } + // Copy the edges over. There's two "half-edges" for every edge, so we pick only one of them. r_mesh.edges.resize(ch.edges.size() / 2); + OAHashMap<uint64_t, int32_t> edge_map; + edge_map.reserve(ch.edges.size() * 4); // The higher the capacity, the faster the insert + uint32_t edges_copied = 0; for (uint32_t i = 0; i < ch.edges.size(); i++) { + ERR_CONTINUE(edge_faces[i] == -1); // Sanity check + uint32_t a = (&ch.edges[i])->get_source_vertex(); uint32_t b = (&ch.edges[i])->get_target_vertex(); if (a < b) { // Copy only the "canonical" edge. For the reverse edge, this will be false. ERR_BREAK(edges_copied >= (uint32_t)r_mesh.edges.size()); - r_mesh.edges.write[edges_copied].a = a; - r_mesh.edges.write[edges_copied].b = b; + r_mesh.edges[edges_copied].vertex_a = a; + r_mesh.edges[edges_copied].vertex_b = b; + r_mesh.edges[edges_copied].face_a = edge_faces[i]; + r_mesh.edges[edges_copied].face_b = -1; + + uint64_t key = a; + key <<= 32; + key |= b; + edge_map.insert(key, edges_copied); + edges_copied++; + } else { + uint64_t key = b; + key <<= 32; + key |= a; + int32_t index; + if (!edge_map.lookup(key, index)) { + ERR_PRINT("Invalid edge"); + } else { + r_mesh.edges[index].face_b = edge_faces[i]; + } } } + if (edges_copied != (uint32_t)r_mesh.edges.size()) { ERR_PRINT("Invalid edge count."); } @@ -2273,7 +2317,7 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3 for (uint32_t i = 0; i < ch.faces.size(); i++) { const Edge *e_start = &ch.edges[ch.faces[i]]; const Edge *e = e_start; - Geometry3D::MeshData::Face &face = r_mesh.faces.write[i]; + Geometry3D::MeshData::Face &face = r_mesh.faces[i]; do { face.indices.push_back(e->get_target_vertex()); @@ -2284,8 +2328,8 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3 // reverse indices: Godot wants clockwise, but this is counter-clockwise if (face.indices.size() > 2) { // reverse all but the first index. - int *indices = face.indices.ptrw(); - for (int c = 0; c < (face.indices.size() - 1) / 2; c++) { + int *indices = face.indices.ptr(); + for (uint32_t c = 0; c < (face.indices.size() - 1) / 2; c++) { SWAP(indices[c + 1], indices[face.indices.size() - 1 - c]); } } diff --git a/core/math/convex_hull.h b/core/math/convex_hull.h index cc41a794bd..ab6671a7d0 100644 --- a/core/math/convex_hull.h +++ b/core/math/convex_hull.h @@ -62,6 +62,10 @@ public: friend class ConvexHullComputer; public: + int32_t get_next_relative() const { + return next; + } + int32_t get_source_vertex() const { return (this + reverse)->target_vertex; } @@ -86,7 +90,7 @@ public: }; // Vertices of the output hull - Vector<Vector3> vertices; + LocalVector<Vector3> vertices; // Edges of the output hull LocalVector<Edge> edges; diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index c5871358ed..548b9e4620 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -141,21 +141,21 @@ real_t Geometry3D::get_closest_distance_between_segments(const Vector3 &p_p0, co void Geometry3D::MeshData::optimize_vertices() { HashMap<int, int> vtx_remap; - for (int i = 0; i < faces.size(); i++) { - for (int j = 0; j < faces[i].indices.size(); j++) { + for (uint32_t i = 0; i < faces.size(); i++) { + for (uint32_t j = 0; j < faces[i].indices.size(); j++) { int idx = faces[i].indices[j]; if (!vtx_remap.has(idx)) { int ni = vtx_remap.size(); vtx_remap[idx] = ni; } - faces.write[i].indices.write[j] = vtx_remap[idx]; + faces[i].indices[j] = vtx_remap[idx]; } } - for (int i = 0; i < edges.size(); i++) { - int a = edges[i].a; - int b = edges[i].b; + for (uint32_t i = 0; i < edges.size(); i++) { + int a = edges[i].vertex_a; + int b = edges[i].vertex_b; if (!vtx_remap.has(a)) { int ni = vtx_remap.size(); @@ -166,16 +166,16 @@ void Geometry3D::MeshData::optimize_vertices() { vtx_remap[b] = ni; } - edges.write[i].a = vtx_remap[a]; - edges.write[i].b = vtx_remap[b]; + edges[i].vertex_a = vtx_remap[a]; + edges[i].vertex_b = vtx_remap[b]; } - Vector<Vector3> new_vertices; + LocalVector<Vector3> new_vertices; new_vertices.resize(vtx_remap.size()); - for (int i = 0; i < vertices.size(); i++) { + for (uint32_t i = 0; i < vertices.size(); i++) { if (vtx_remap.has(i)) { - new_vertices.write[vtx_remap[i]] = vertices[i]; + new_vertices[vtx_remap[i]] = vertices[i]; } } vertices = new_vertices; @@ -751,7 +751,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes Vector3 center = p.center(); // make a quad clockwise - Vector<Vector3> vertices = { + LocalVector<Vector3> vertices = { center - up * subplane_size + right * subplane_size, center - up * subplane_size - right * subplane_size, center + up * subplane_size - right * subplane_size, @@ -763,7 +763,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes continue; } - Vector<Vector3> new_vertices; + LocalVector<Vector3> new_vertices; Plane clip = p_planes[j]; if (clip.normal.dot(p.normal) > 0.95f) { @@ -774,7 +774,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes break; } - for (int k = 0; k < vertices.size(); k++) { + for (uint32_t k = 0; k < vertices.size(); k++) { int k_n = (k + 1) % vertices.size(); Vector3 edge0_A = vertices[k]; @@ -816,9 +816,9 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes MeshData::Face face; // Add face indices. - for (int j = 0; j < vertices.size(); j++) { + for (uint32_t j = 0; j < vertices.size(); j++) { int idx = -1; - for (int k = 0; k < mesh.vertices.size(); k++) { + for (uint32_t k = 0; k < mesh.vertices.size(); k++) { if (mesh.vertices[k].distance_to(vertices[j]) < 0.001f) { idx = k; break; @@ -837,28 +837,34 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes // Add edge. - for (int j = 0; j < face.indices.size(); j++) { + for (uint32_t j = 0; j < face.indices.size(); j++) { int a = face.indices[j]; int b = face.indices[(j + 1) % face.indices.size()]; bool found = false; - for (int k = 0; k < mesh.edges.size(); k++) { - if (mesh.edges[k].a == a && mesh.edges[k].b == b) { + int found_idx = -1; + for (uint32_t k = 0; k < mesh.edges.size(); k++) { + if (mesh.edges[k].vertex_a == a && mesh.edges[k].vertex_b == b) { found = true; + found_idx = k; break; } - if (mesh.edges[k].b == a && mesh.edges[k].a == b) { + if (mesh.edges[k].vertex_b == a && mesh.edges[k].vertex_a == b) { found = true; + found_idx = k; break; } } if (found) { + mesh.edges[found_idx].face_b = j; continue; } MeshData::Edge edge; - edge.a = a; - edge.b = b; + edge.vertex_a = a; + edge.vertex_b = b; + edge.face_a = j; + edge.face_b = -1; mesh.edges.push_back(edge); } } diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index e5ace9db72..4b4c173a1e 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -33,6 +33,7 @@ #include "core/math/face3.h" #include "core/object/object.h" +#include "core/templates/local_vector.h" #include "core/templates/vector.h" class Geometry3D { @@ -539,18 +540,19 @@ public: struct MeshData { struct Face { Plane plane; - Vector<int> indices; + LocalVector<int> indices; }; - Vector<Face> faces; + LocalVector<Face> faces; struct Edge { - int a, b; + int vertex_a, vertex_b; + int face_a, face_b; }; - Vector<Edge> edges; + LocalVector<Edge> edges; - Vector<Vector3> vertices; + LocalVector<Vector3> vertices; void optimize_vertices(); }; diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index c7727a44a1..c194e1cc21 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -369,7 +369,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { Geometry3D::MeshData::Face &f = E->get(); - for (int i = 0; i < f.indices.size(); i++) { + for (uint32_t i = 0; i < f.indices.size(); i++) { int a = E->get().indices[i]; int b = E->get().indices[(i + 1) % f.indices.size()]; Edge e(a, b); @@ -436,17 +436,24 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ r_mesh.faces.clear(); r_mesh.faces.resize(ret_faces.size()); + HashMap<List<Geometry3D::MeshData::Face>::Element *, int> face_indices; + int idx = 0; - for (const Geometry3D::MeshData::Face &E : ret_faces) { - r_mesh.faces.write[idx++] = E; + for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { + face_indices[E] = idx; + r_mesh.faces[idx++] = E->get(); } r_mesh.edges.resize(ret_edges.size()); idx = 0; for (const KeyValue<Edge, RetFaceConnect> &E : ret_edges) { Geometry3D::MeshData::Edge e; - e.a = E.key.vertices[0]; - e.b = E.key.vertices[1]; - r_mesh.edges.write[idx++] = e; + e.vertex_a = E.key.vertices[0]; + e.vertex_b = E.key.vertices[1]; + ERR_CONTINUE(!face_indices.has(E.value.left)); + ERR_CONTINUE(!face_indices.has(E.value.right)); + e.face_a = face_indices[E.value.left]; + e.face_b = face_indices[E.value.right]; + r_mesh.edges[idx++] = e; } r_mesh.vertices = p_points; diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h index 9352bae0a6..bf67154cd6 100644 --- a/core/math/random_number_generator.h +++ b/core/math/random_number_generator.h @@ -57,7 +57,7 @@ public: _FORCE_INLINE_ real_t randfn(real_t p_mean = 0.0, real_t p_deviation = 1.0) { return randbase.randfn(p_mean, p_deviation); } _FORCE_INLINE_ int randi_range(int p_from, int p_to) { return randbase.random(p_from, p_to); } - RandomNumberGenerator() {} + RandomNumberGenerator() { randbase.randomize(); } }; #endif // RANDOM_NUMBER_GENERATOR_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index ca56add2ab..41585943b3 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1528,7 +1528,10 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION; c.native_extension = p_extension; c.name = p_extension->class_name; - c.creation_func = parent->creation_func; + c.is_virtual = p_extension->is_virtual; + if (!p_extension->is_abstract) { + c.creation_func = parent->creation_func; + } c.inherits = parent->name; c.class_ptr = parent->class_ptr; c.inherits_ptr = parent; diff --git a/core/object/object.cpp b/core/object/object.cpp index 368cba740d..540b9a8f19 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1856,6 +1856,46 @@ void ObjectDB::debug_objects(DebugFunc p_func) { } void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0) { + if (p_function == "connect" || p_function == "is_connected" || p_function == "disconnect" || p_function == "emit_signal" || p_function == "has_signal") { + List<MethodInfo> signals; + get_signal_list(&signals); + for (const MethodInfo &E : signals) { + r_options->push_back(E.name.quote()); + } + } else if (p_function == "call" || p_function == "call_deferred" || p_function == "callv" || p_function == "has_method") { + List<MethodInfo> methods; + get_method_list(&methods); + for (const MethodInfo &E : methods) { + if (E.name.begins_with("_") && !(E.flags & METHOD_FLAG_VIRTUAL)) { + continue; + } + r_options->push_back(E.name.quote()); + } + } else if (p_function == "set" || p_function == "set_deferred" || p_function == "get") { + List<PropertyInfo> properties; + get_property_list(&properties); + for (const PropertyInfo &E : properties) { + if (E.usage & PROPERTY_USAGE_DEFAULT && !(E.usage & PROPERTY_USAGE_INTERNAL)) { + r_options->push_back(E.name.quote()); + } + } + } else if (p_function == "set_meta" || p_function == "get_meta" || p_function == "has_meta" || p_function == "remove_meta") { + for (const KeyValue<StringName, Variant> &K : metadata) { + r_options->push_back(String(K.key).quote()); + } + } + } else if (p_idx == 2) { + if (p_function == "connect") { + // Ideally, the constants should be inferred by the parameter. + // But a parameter's PropertyInfo does not store the enum they come from, so this will do for now. + List<StringName> constants; + ClassDB::get_enum_constants("Object", "ConnectFlags", &constants); + for (const StringName &E : constants) { + r_options->push_back(String(E)); + } + } + } } SpinLock ObjectDB::spin_lock; diff --git a/core/object/object.h b/core/object/object.h index 5ba5453b31..359ab0f211 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -295,6 +295,8 @@ struct ObjectNativeExtension { StringName parent_class_name; StringName class_name; bool editor_class = false; + bool is_virtual = false; + bool is_abstract = false; GDNativeExtensionClassSet set; GDNativeExtensionClassGet get; GDNativeExtensionClassGetPropertyList get_property_list; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 872c8357ae..671b06f0ed 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3667,32 +3667,43 @@ bool String::is_network_share_path() const { String String::simplify_path() const { String s = *this; String drive; - if (s.begins_with("local://")) { - drive = "local://"; - s = s.substr(8); - } else if (s.begins_with("res://")) { - drive = "res://"; - s = s.substr(6); - } else if (s.begins_with("user://")) { - drive = "user://"; - s = s.substr(7); - } else if (s.begins_with("uid://")) { - drive = "uid://"; - s = s.substr(6); - } else if (is_network_share_path()) { - drive = s.substr(0, 2); - s = s.substr(2, s.length() - 2); - } else if (s.begins_with("/") || s.begins_with("\\")) { - drive = s.substr(0, 1); - s = s.substr(1, s.length() - 1); - } else { - int p = s.find(":/"); - if (p == -1) { - p = s.find(":\\"); + + // Check if we have a special path (like res://) or a protocol identifier. + int p = s.find("://"); + bool found = false; + if (p > 0) { + bool only_chars = true; + for (int i = 0; i < p; i++) { + if (!is_ascii_char(s[i])) { + only_chars = false; + break; + } + } + if (only_chars) { + found = true; + drive = s.substr(0, p + 3); + s = s.substr(p + 3); } - if (p != -1 && p < s.find("/")) { - drive = s.substr(0, p + 2); - s = s.substr(p + 2); + } + if (!found) { + if (is_network_share_path()) { + // Network path, beginning with // or \\. + drive = s.substr(0, 2); + s = s.substr(2); + } else if (s.begins_with("/") || s.begins_with("\\")) { + // Absolute path. + drive = s.substr(0, 1); + s = s.substr(1); + } else { + // Windows-style drive path, like C:/ or C:\. + p = s.find(":/"); + if (p == -1) { + p = s.find(":\\"); + } + if (p != -1 && p < s.find("/")) { + drive = s.substr(0, p + 2); + s = s.substr(p + 2); + } } } diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h index 6171c8c88f..427a337aab 100644 --- a/core/variant/container_type_validate.h +++ b/core/variant/container_type_validate.h @@ -79,9 +79,12 @@ struct ContainerTypeValidate { return true; } - ERR_FAIL_COND_V_MSG(type != p_variant.get_type(), false, "Attempted to " + String(p_operation) + " a variable of type '" + Variant::get_type_name(p_variant.get_type()) + "' into a " + where + " of type '" + Variant::get_type_name(type) + "'."); if (type != p_variant.get_type()) { - return false; + if (p_variant.get_type() == Variant::NIL && type == Variant::OBJECT) { + return true; + } + + ERR_FAIL_V_MSG(false, "Attempted to " + String(p_operation) + " a variable of type '" + Variant::get_type_name(p_variant.get_type()) + "' into a " + where + " of type '" + Variant::get_type_name(type) + "'."); } if (type != Variant::OBJECT) { @@ -90,7 +93,7 @@ struct ContainerTypeValidate { #ifdef DEBUG_ENABLED ObjectID object_id = p_variant; if (object_id == ObjectID()) { - return true; //fine its null; + return true; // This is fine, it's null. } Object *object = ObjectDB::get_instance(object_id); ERR_FAIL_COND_V_MSG(object == nullptr, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + "."); @@ -101,7 +104,7 @@ struct ContainerTypeValidate { } #endif if (class_name == StringName()) { - return true; //all good, no class type requested + return true; // All good, no class type requested. } StringName obj_class = object->get_class_name(); @@ -110,12 +113,12 @@ struct ContainerTypeValidate { } if (script.is_null()) { - return true; //all good + return true; // All good, no script requested. } Ref<Script> other_script = object->get_script(); - //check base script.. + // Check base script.. ERR_FAIL_COND_V_MSG(other_script.is_null(), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'."); ERR_FAIL_COND_V_MSG(!other_script->inherits_script(script), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'."); diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index 629675132a..47e1f85c97 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -68,9 +68,6 @@ <member name="shortcut" type="Shortcut" setter="set_shortcut" getter="get_shortcut"> [Shortcut] associated to the button. </member> - <member name="shortcut_context" type="Node" setter="set_shortcut_context" getter="get_shortcut_context"> - The [Node] which must be a parent of the focused GUI [Control] for the shortcut to be activated. If [code]null[/code], the shortcut can be activated when any control is focused (a global shortcut). This allows shortcuts to be accepted only when the user has a certain area of the GUI focused. - </member> <member name="shortcut_in_tooltip" type="bool" setter="set_shortcut_in_tooltip" getter="is_shortcut_in_tooltip_enabled"> If [code]true[/code], the button will add information about its shortcut in the tooltip. </member> diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index 50c0860d1f..7c1b19b961 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -5,6 +5,7 @@ </brief_description> <description> Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [CanvasLayer] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [CanvasLayer] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below). + Embedded [Window]s are placed in layer 1024. CanvasItems in layer 1025 or above appear in front of embedded windows, CanvasItems in layer 1023 or below appear behind embedded windows. </description> <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 9c7cb7b089..7968b03c4b 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1051,6 +1051,9 @@ [b]Note:[/b] This property is mainly intended to be used for animation purposes. Text inside the Control will look pixelated or blurry when the Control is scaled. To support multiple resolutions in your project, use an appropriate viewport stretch mode as described in the [url=$DOCS_URL/tutorials/viewports/multiple_resolutions.html]documentation[/url] instead of scaling Controls individually. [b]Note:[/b] If the Control node is a child of a [Container] node, the scale will be reset to [code]Vector2(1, 1)[/code] when the scene is instantiated. To set the Control's scale when it's instantiated, wait for one frame using [code]await get_tree().process_frame[/code] then set its [member scale] property. </member> + <member name="shortcut_context" type="Node" setter="set_shortcut_context" getter="get_shortcut_context"> + The [Node] which must be a parent of the focused [Control] for the shortcut to be activated. If [code]null[/code], the shortcut can be activated when any control is focused (a global shortcut). This allows shortcuts to be accepted only when the user has a certain area of the GUI focused. + </member> <member name="size" type="Vector2" setter="_set_size" getter="get_size" default="Vector2(0, 0)"> The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically. </member> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 243a28e73d..33b6a786ae 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -301,6 +301,7 @@ <member name="volumetric_fog_density" type="float" setter="set_volumetric_fog_density" getter="get_volumetric_fog_density" default="0.05"> The base [i]exponential[/i] density of the volumetric fog. Set this to the lowest density you want to have globally. [FogVolume]s can be used to add to or subtract from this density in specific areas. Fog rendering is exponential as in real life. A value of [code]0.0[/code] disables global volumetric fog while allowing [FogVolume]s to display volumetric fog in specific areas. + To make volumetric fog work as a volumetric [i]lighting[/i] solution, set [member volumetric_fog_density] to the lowest non-zero value ([code]0.0001[/code]) then increase lights' [member Light3D.light_volumetric_fog_energy] to values between [code]10000[/code] and [code]100000[/code] to compensate for the very low density. </member> <member name="volumetric_fog_detail_spread" type="float" setter="set_volumetric_fog_detail_spread" getter="get_volumetric_fog_detail_spread" default="2.0"> The distribution of size down the length of the froxel buffer. A higher value compresses the froxels closer to the camera and places more detail closer to the camera. diff --git a/doc/classes/FogMaterial.xml b/doc/classes/FogMaterial.xml index 7428d6169a..aab6be4212 100644 --- a/doc/classes/FogMaterial.xml +++ b/doc/classes/FogMaterial.xml @@ -15,6 +15,7 @@ </member> <member name="density" type="float" setter="set_density" getter="get_density" default="1.0"> The density of the [FogVolume]. Denser objects are more opaque, but may suffer from under-sampling artifacts that look like stripes. Negative values can be used to subtract fog from other [FogVolume]s or global volumetric fog. + [b]Note:[/b] Due to limited precision, [member density] values between [code]-0.001[/code] and [code]0.001[/code] (exclusive) act like [code]0.0[/code]. This does not apply to [member Environment.volumetric_fog_density]. </member> <member name="density_texture" type="Texture3D" setter="set_density_texture" getter="get_density_texture"> The 3D texture that is used to scale the [member density] of the [FogVolume]. This can be used to vary fog density within the [FogVolume] with any kind of static pattern. For animated effects, consider using a custom [url=$DOCS_URL/tutorials/shaders/shader_reference/fog_shader.html]fog shader[/url]. diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 510f14ec54..eb5b3dad91 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -111,8 +111,8 @@ Copies [param src] image to this image. </description> </method> - <method name="create"> - <return type="void" /> + <method name="create" qualifiers="static"> + <return type="Image" /> <param index="0" name="width" type="int" /> <param index="1" name="height" type="int" /> <param index="2" name="use_mipmaps" type="bool" /> @@ -121,8 +121,8 @@ Creates an empty image of given size and format. See [enum Format] constants. If [param use_mipmaps] is [code]true[/code] then generate mipmaps for this image. See the [method generate_mipmaps]. </description> </method> - <method name="create_from_data"> - <return type="void" /> + <method name="create_from_data" qualifiers="static"> + <return type="Image" /> <param index="0" name="width" type="int" /> <param index="1" name="height" type="int" /> <param index="2" name="use_mipmaps" type="bool" /> @@ -454,6 +454,17 @@ Saves the image as a WebP (Web Picture) file to a byte array. By default it will save lossless. If [param lossy] is true, the image will be saved lossy, using the [param quality] setting between 0.0 and 1.0 (inclusive). </description> </method> + <method name="set_data"> + <return type="void" /> + <param index="0" name="width" type="int" /> + <param index="1" name="height" type="int" /> + <param index="2" name="use_mipmaps" type="bool" /> + <param index="3" name="format" type="int" enum="Image.Format" /> + <param index="4" name="data" type="PackedByteArray" /> + <description> + Overwrites data of an existing [Image]. Non-static equivalent of [method create_from_data]. + </description> + </method> <method name="set_pixel"> <return type="void" /> <param index="0" name="x" type="int" /> diff --git a/doc/classes/MenuBar.xml b/doc/classes/MenuBar.xml index 3ef0572e9f..e8505937ff 100644 --- a/doc/classes/MenuBar.xml +++ b/doc/classes/MenuBar.xml @@ -106,9 +106,6 @@ <member name="prefer_global_menu" type="bool" setter="set_prefer_global_menu" getter="is_prefer_global_menu" default="true"> If [code]true[/code], [MenuBar] will use system global menu when supported. </member> - <member name="shortcut_context" type="Node" setter="set_shortcut_context" getter="get_shortcut_context"> - The [Node] which must be a parent of the focused GUI [Control] for the shortcut to be activated. If [code]null[/code], the shortcut can be activated when any control is focused (a global shortcut). This allows shortcuts to be accepted only when the user has a certain area of the GUI focused. - </member> <member name="start_index" type="int" setter="set_start_index" getter="get_start_index" default="-1"> Position in the global menu to insert first [MenuBar] item at. </member> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index bceb285584..620895d8d8 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -529,9 +529,9 @@ <method name="move_child"> <return type="void" /> <param index="0" name="child_node" type="Node" /> - <param index="1" name="to_position" type="int" /> + <param index="1" name="to_index" type="int" /> <description> - Moves a child node to a different position (order) among the other children. Since calls, signals, etc are performed by tree order, changing the order of children nodes may be useful. If [param to_position] is negative, the index will be counted from the end. + Moves a child node to a different index (order) among the other children. Since calls, signals, etc. are performed by tree order, changing the order of children nodes may be useful. If [param to_index] is negative, the index will be counted from the end. [b]Note:[/b] Internal children can only be moved within their expected "internal range" (see [code]internal[/code] parameter in [method add_child]). </description> </method> diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index a41207e9b3..807a94ab24 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -259,6 +259,10 @@ <member name="spread" type="float" setter="set_spread" getter="get_spread" default="45.0"> Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. </member> + <member name="sub_emitter_amount_at_collision" type="int" setter="set_sub_emitter_amount_at_collision" getter="get_sub_emitter_amount_at_collision"> + Sub particle amount on collision. + Maximum amount set in the sub particles emitter. + </member> <member name="sub_emitter_amount_at_end" type="int" setter="set_sub_emitter_amount_at_end" getter="get_sub_emitter_amount_at_end"> </member> <member name="sub_emitter_frequency" type="float" setter="set_sub_emitter_frequency" getter="get_sub_emitter_frequency"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 0dc24345e9..a8080c70c6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -413,6 +413,9 @@ <member name="debug/gdscript/warnings/standalone_ternary" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that has no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement. </member> + <member name="debug/gdscript/warnings/static_called_on_instance" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly. + </member> <member name="debug/gdscript/warnings/treat_warnings_as_errors" type="bool" setter="" getter="" default="false"> If [code]true[/code], all warnings will be reported as if they are errors. </member> diff --git a/doc/classes/PropertyTweener.xml b/doc/classes/PropertyTweener.xml index 75d9c919aa..78ead65cdd 100644 --- a/doc/classes/PropertyTweener.xml +++ b/doc/classes/PropertyTweener.xml @@ -27,7 +27,7 @@ Sets a custom initial value to the [PropertyTweener]. Example: [codeblock] var tween = get_tree().create_tween() - tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100) #this will move the node from position (100, 100) to (200, 100) + tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100)) #this will move the node from position (100, 100) to (200, 100) [/codeblock] </description> </method> diff --git a/doc/classes/RandomNumberGenerator.xml b/doc/classes/RandomNumberGenerator.xml index b8a290381f..a6254788ce 100644 --- a/doc/classes/RandomNumberGenerator.xml +++ b/doc/classes/RandomNumberGenerator.xml @@ -10,10 +10,9 @@ [codeblock] var rng = RandomNumberGenerator.new() func _ready(): - rng.randomize() var my_random_number = rng.randf_range(-10.0, 10.0) [/codeblock] - [b]Note:[/b] The default values of [member seed] and [member state] properties are pseudo-random, and changes when calling [method randomize]. The [code]0[/code] value documented here is a placeholder, and not the actual default seed. + [b]Note:[/b] The default values of [member seed] and [member state] properties are pseudo-random, and change when calling [method randomize]. The [code]0[/code] value documented here is a placeholder, and not the actual default seed. </description> <tutorials> <link title="Random number generation">$DOCS_URL/tutorials/math/random_number_generation.html</link> diff --git a/doc/classes/XROrigin3D.xml b/doc/classes/XROrigin3D.xml index 7acee097e7..506d0fce41 100644 --- a/doc/classes/XROrigin3D.xml +++ b/doc/classes/XROrigin3D.xml @@ -13,6 +13,9 @@ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <members> + <member name="current" type="bool" setter="set_current" getter="is_current" default="false"> + Is this XROrigin3D node the current origin used by the [XRServer]? + </member> <member name="world_scale" type="float" setter="set_world_scale" getter="get_world_scale" default="1.0"> Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 real world meter. [b]Note:[/b] This method is a passthrough to the [XRServer] itself. diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index de0181f887..92c29a4264 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -114,7 +114,7 @@ CopyEffects::~CopyEffects() { copy.shader.version_free(copy.shader_version); } -void CopyEffects::copy_to_rect(const Rect2i &p_rect) { +void CopyEffects::copy_to_rect(const Rect2 &p_rect) { copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); glBindVertexArray(quad_array); diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index b863e76579..7f16b4e0f3 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -61,7 +61,7 @@ public: ~CopyEffects(); // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. - void copy_to_rect(const Rect2i &p_rect); + void copy_to_rect(const Rect2 &p_rect); void copy_screen(); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); void set_color(const Color &p_color, const Rect2i &p_region); diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index c635e18132..b407670098 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -115,7 +115,7 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans p_mat4[15] = 1; } -void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { +void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); @@ -130,7 +130,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ if (syncStatus == GL_UNSIGNALED) { // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway. if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { +#ifndef WEB_ENABLED + // On web, we do nothing as the glSubBufferData will force a sync anyway and WebGL does not like waiting. glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms +#endif } else { // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use // Allocate a new buffer and use that. @@ -144,9 +147,173 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } } - // TODO: Setup Directional Lights + //setup directional lights if exist + + uint32_t light_count = 0; + uint32_t directional_light_count = 0; + { + Light *l = p_directional_light_list; + uint32_t index = 0; + + while (l) { + if (index == data.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + + Vector2 canvas_light_dir = l->xform_cache.columns[1].normalized(); + + state.light_uniforms[index].position[0] = -canvas_light_dir.x; + state.light_uniforms[index].position[1] = -canvas_light_dir.y; + + //_update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height; //0..1 here + + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + /* + if (state.shadow_fb.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } + */ + + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + /* + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + */ + + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + light_count = index; + directional_light_count = light_count; + state.using_directional_lights = directional_light_count > 0; + } - // TODO: Setup lights + //setup lights if exist + + { + Light *l = p_light_list; + uint32_t index = light_count; + + while (l) { + if (index == data.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + Transform2D to_light_xform = (p_canvas_transform * l->light_shader_xform).affine_inverse(); + + Vector2 canvas_light_pos = p_canvas_transform.xform(l->xform.get_origin()); //convert light position to canvas coordinates, as all computation is done in canvas coords to avoid precision loss + state.light_uniforms[index].position[0] = canvas_light_pos.x; + state.light_uniforms[index].position[1] = canvas_light_pos.y; + + _update_transform_2d_to_mat2x4(to_light_xform, state.light_uniforms[index].matrix); + _update_transform_2d_to_mat2x4(l->xform_cache.affine_inverse(), state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height * (p_canvas_transform.columns[0].length() + p_canvas_transform.columns[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + /* + if (state.shadow_fb.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } + */ + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + /* + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + */ + + if (clight->texture.is_valid()) { + Rect2 atlas_rect = GLES3::TextureStorage::get_singleton()->texture_atlas_get_texture_rect(clight->texture); + state.light_uniforms[index].atlas_rect[0] = atlas_rect.position.x; + state.light_uniforms[index].atlas_rect[1] = atlas_rect.position.y; + state.light_uniforms[index].atlas_rect[2] = atlas_rect.size.width; + state.light_uniforms[index].atlas_rect[3] = atlas_rect.size.height; + + } else { + state.light_uniforms[index].atlas_rect[0] = 0; + state.light_uniforms[index].atlas_rect[1] = 0; + state.light_uniforms[index].atlas_rect[2] = 0; + state.light_uniforms[index].atlas_rect[3] = 0; + } + + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + light_count = index; + } + + if (light_count > 0) { + glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].light_ubo); + +#ifdef WEB_ENABLED + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, state.light_uniforms); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.light_uniforms, sizeof(LightUniform) * light_count); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + + GLuint texture_atlas = texture_storage->texture_atlas_get_texture(); + if (texture_atlas == 0) { + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); + texture_atlas = tex->tex_id; + } + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 2); + glBindTexture(GL_TEXTURE_2D, texture_atlas); + } { //update canvas state uniform buffer @@ -175,13 +342,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; - // TODO: temporary, this should be set at the top of this function glViewport(0, 0, render_target_size.x, render_target_size.y); state_buffer.time = state.time; state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; - state_buffer.directional_light_count = 0; //directional_light_count; + state_buffer.directional_light_count = directional_light_count; Vector2 canvas_scale = p_canvas_transform.get_scale(); @@ -200,7 +366,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_state_buffer); + glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].state_ubo); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); GLuint global_buffer = material_storage->global_shader_parameters_get_uniform_buffer(); @@ -442,7 +608,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data; CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant; - _bind_material(material_data, variant); + uint64_t specialization = 0; + specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled); + _bind_material(material_data, variant, specialization); GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode; @@ -554,6 +722,38 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran bool skipping = false; + // TODO: consider making lights a per-batch property and then baking light operations in the shader for better performance. + uint32_t lights[4] = { 0, 0, 0, 0 }; + + uint16_t light_count = 0; + + { + Light *light = p_lights; + + while (light) { + if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + uint32_t light_index = light->render_index_cache; + lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); + + light_count++; + + if (light_count == data.max_lights_per_item) { + break; + } + } + light = light->next_ptr; + } + + base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + } + + bool lights_disabled = light_count == 0 && !state.using_directional_lights; + + if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled; + } + const Item::Command *c = p_item->commands; while (c) { if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { @@ -580,6 +780,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran state.instance_data_array[r_index].pad[0] = 0.0; state.instance_data_array[r_index].pad[1] = 0.0; + state.instance_data_array[r_index].lights[0] = lights[0]; + state.instance_data_array[r_index].lights[1] = lights[1]; + state.instance_data_array[r_index].lights[2] = lights[2]; + state.instance_data_array[r_index].lights[3] = lights[3]; + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config Color blend_color; @@ -808,7 +1013,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran _add_to_batch(r_index, r_batch_broken); if (primitive->point_count == 4) { - // Reset base data + // Reset base data. _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; @@ -816,12 +1021,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config for (uint32_t j = 0; j < 3; j++) { - //second half of triangle - state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x; - state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y; - state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x; - state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y; - Color col = primitive->colors[j + 1] * base_color; + int offset = j == 0 ? 0 : 1; + // Second triangle in the quad. Uses vertices 0, 2, 3. + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + offset].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + offset].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + offset].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + offset].y; + Color col = primitive->colors[j + offset] * base_color; state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } @@ -1140,25 +1346,41 @@ void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) _align_instance_data_buffer(r_index); } -void RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant) { +void RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization) { if (p_material_data) { if (p_material_data->shader_data->version.is_valid() && p_material_data->shader_data->valid) { // Bind uniform buffer and textures p_material_data->bind_uniforms(); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant, p_specialization); } else { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } } else { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } } RID RasterizerCanvasGLES3::light_create() { - return RID(); + CanvasLight canvas_light; + return canvas_light_owner.make_rid(canvas_light); } void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl); + if (cl->texture == p_texture) { + return; + } + if (cl->texture.is_valid()) { + texture_storage->texture_remove_from_texture_atlas(cl->texture); + } + cl->texture = p_texture; + + if (cl->texture.is_valid()) { + texture_storage->texture_add_to_texture_atlas(cl->texture); + } } void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) { @@ -1187,6 +1409,14 @@ void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) { } bool RasterizerCanvasGLES3::free(RID p_rid) { + if (canvas_light_owner.owns(p_rid)) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND_V(!cl, false); + canvas_light_owner.free(p_rid); + } else { + return false; + } + return true; } @@ -1357,7 +1587,7 @@ void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasIte state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; } - if (!normal_map) { + if (normal_map) { state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; } else { state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED; @@ -1567,13 +1797,23 @@ void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) { // In theory allocations can reach as high as number of windows * 3 frames // because OpenGL can start rendering subsequent frames before finishing the current one void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { - GLuint new_buffer; - glGenBuffers(1, &new_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, new_buffer); - glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_DYNAMIC_DRAW); + GLuint new_buffers[3]; + glGenBuffers(3, new_buffers); + // Batch UBO. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); + glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + // Light uniform buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); + // State buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + state.current_buffer = (state.current_buffer + 1); DataBuffer db; - db.ubo = new_buffer; + db.ubo = new_buffers[0]; + db.light_ubo = new_buffers[1]; + db.state_ubo = new_buffers[2]; db.last_frame_used = RSG::rasterizer->get_frame_number(); state.canvas_instance_data_buffers.insert(state.current_buffer, db); state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); @@ -1755,12 +1995,21 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { state.canvas_instance_batches.reserve(200); for (int i = 0; i < 3; i++) { - GLuint new_buffer; - glGenBuffers(1, &new_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, new_buffer); - glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_DYNAMIC_DRAW); + GLuint new_buffers[3]; + glGenBuffers(3, new_buffers); + // Batch UBO. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); + glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + // Light uniform buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); + // State buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); DataBuffer db; - db.ubo = new_buffer; + db.ubo = new_buffers[0]; + db.light_ubo = new_buffers[1]; + db.state_ubo = new_buffers[2]; db.last_frame_used = 0; db.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); state.canvas_instance_data_buffers[i] = db; @@ -1768,6 +2017,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { glBindBuffer(GL_UNIFORM_BUFFER, 0); state.instance_data_array = memnew_arr(InstanceData, data.max_instances_per_ubo); + state.light_uniforms = memnew_arr(LightUniform, data.max_lights_per_render); { const uint32_t no_of_instances = data.max_instances_per_batch; @@ -1794,14 +2044,9 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { delete[] indices; } - glGenBuffers(1, &state.canvas_state_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - String global_defines; global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now - global_defines += "#define MAX_LIGHTS " + itos(data.max_instances_per_batch) + "\n"; + global_defines += "#define MAX_LIGHTS " + itos(data.max_lights_per_render) + "\n"; global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n"; GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines); @@ -1854,7 +2099,8 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { glDeleteVertexArrays(1, &data.canvas_quad_array); GLES3::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture); - memfree(state.instance_data_array); + memdelete_arr(state.instance_data_array); + memdelete_arr(state.light_uniforms); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 15c2ca5710..65e5f14838 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -96,6 +96,33 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { DEFAULT_MAX_LIGHTS_PER_RENDER = 256, }; + /******************/ + /**** LIGHTING ****/ + /******************/ + + struct CanvasLight { + RID texture; + }; + + RID_Owner<CanvasLight> canvas_light_owner; + + struct LightUniform { + float matrix[8]; //light to texture coordinate matrix + float shadow_matrix[8]; //light to shadow coordinate matrix + float color[4]; + + uint8_t shadow_color[4]; + uint32_t flags; //index to light texture + float shadow_pixel_size; + float height; + + float position[2]; + float shadow_z_far_inv; + float shadow_y_ofs; + + float atlas_rect[4]; + }; + public: enum { BASE_UNIFORM_LOCATION = 0, @@ -184,8 +211,8 @@ public: RID canvas_shader_default_version; - uint32_t max_lights_per_render; - uint32_t max_lights_per_item; + uint32_t max_lights_per_render = 256; + uint32_t max_lights_per_item = 16; uint32_t max_instances_per_batch = 512; uint32_t max_instances_per_ubo = 16384; uint32_t max_instance_buffer_size = 16384 * 128; @@ -212,16 +239,22 @@ public: const Item::Command *command = nullptr; Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch. uint32_t primitive_points = 0; + + bool lights_disabled = false; }; + // DataBuffer contains our per-frame data. I.e. the resources that are updated each frame. + // We track them and ensure that they don't get reused until at least 2 frames have passed + // to avoid the GPU stalling to wait for a resource to become available. struct DataBuffer { GLuint ubo = 0; + GLuint light_ubo = 0; + GLuint state_ubo = 0; uint64_t last_frame_used = -3; GLsync fence = GLsync(); }; struct State { - GLuint canvas_state_buffer; LocalVector<DataBuffer> canvas_instance_data_buffers; LocalVector<Batch> canvas_instance_batches; uint32_t current_buffer = 0; @@ -230,6 +263,10 @@ public: InstanceData *instance_data_array = nullptr; + LightUniform *light_uniforms = nullptr; + + bool using_directional_lights = false; + RID current_tex = RID(); RS::CanvasItemTextureFilter current_filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; RS::CanvasItemTextureRepeat current_repeat_mode = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; @@ -282,7 +319,7 @@ public: void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer = false); void _record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch); void _render_batch(Light *p_lights, uint32_t p_index); - void _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant); + void _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); void _new_batch(bool &r_batch_broken, uint32_t &r_index); void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 4c71edc24c..d6486801e7 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1166,12 +1166,13 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - //lod - Vector3 lod_support_min = inst->transformed_aabb.get_support(-p_render_data->lod_camera_plane.normal); - Vector3 lod_support_max = inst->transformed_aabb.get_support(p_render_data->lod_camera_plane.normal); + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - float distance_min = p_render_data->lod_camera_plane.distance_to(lod_support_min); - float distance_max = p_render_data->lod_camera_plane.distance_to(lod_support_max); + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->cam_transform.origin.distance_to(lod_support_max); float distance = 0.0; @@ -1650,7 +1651,6 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ // this should be the same for all cameras.. render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); - render_data.lod_camera_plane = Plane(-p_camera_data->main_transform.basis.get_column(Vector3::AXIS_Z), p_camera_data->main_transform.get_origin()); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { render_data.screen_mesh_lod_threshold = 0.0; diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index d11dc14080..3895620228 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -118,7 +118,6 @@ struct RenderDataGLES3 { int reflection_probe_pass = 0; float lod_distance_multiplier = 0.0; - Plane lod_camera_plane = Plane(); float screen_mesh_lod_threshold = 0.0; uint32_t directional_light_count = 0; diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index a177112476..23db41802e 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -211,7 +211,9 @@ void main() { #include "canvas_uniforms_inc.glsl" #include "stdlib_inc.glsl" -//uniform sampler2D atlas_texture; //texunit:-2 +#ifndef DISABLE_LIGHTING +uniform sampler2D atlas_texture; //texunit:-2 +#endif // DISABLE_LIGHTING //uniform sampler2D shadow_atlas_texture; //texunit:-3 uniform sampler2D screen_texture; //texunit:-4 uniform sampler2D sdf_texture; //texunit:-5 @@ -243,6 +245,77 @@ layout(std140) uniform MaterialUniforms{ #endif #GLOBALS +#ifndef DISABLE_LIGHTING +#ifdef LIGHT_CODE_USED + +vec4 light_compute( + vec3 light_vertex, + vec3 light_position, + vec3 normal, + vec4 light_color, + float light_energy, + vec4 specular_shininess, + inout vec4 shadow_modulate, + vec2 screen_uv, + vec2 uv, + vec4 color, bool is_directional) { + vec4 light = vec4(0.0); + vec3 light_direction = vec3(0.0); + + if (is_directional) { + light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z)); + light_position = vec3(0.0); + } else { + light_direction = normalize(light_position - light_vertex); + } + +#CODE : LIGHT + + return light; +} + +#endif + +vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) { + float cNdotL = max(0.0, dot(normal, light_vec)); + + if (specular_shininess_used) { + //blinn + vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough + vec3 half_vec = normalize(view + light_vec); + + float cNdotV = max(dot(normal, view), 0.0); + float cNdotH = max(dot(normal, half_vec), 0.0); + float cVdotH = max(dot(view, half_vec), 0.0); + float cLdotH = max(dot(light_vec, half_vec), 0.0); + float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); + + return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL; + } else { + return light_color * base_color * cNdotL; + } +} + +void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { + uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK; + + switch (blend_mode) { + case LIGHT_FLAGS_BLEND_MODE_ADD: { + color.rgb += light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_SUB: { + color.rgb -= light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_MIX: { + color.rgb = mix(color.rgb, light_color.rgb, light_color.a); + } break; + } +} + +#endif #ifdef USE_NINEPATCH @@ -353,7 +426,8 @@ void main() { color *= texture(color_texture, uv); } - bool using_light = false; + uint light_count = (draw_data[draw_data_instance].flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights + bool using_light = light_count > 0u || directional_light_count > 0u; vec3 normal; @@ -414,11 +488,105 @@ void main() { #endif } + if (normal_used) { + //convert by item transform + normal.xy = mat2(normalize(draw_data[draw_data_instance].world_x), normalize(draw_data[draw_data_instance].world_y)) * normal.xy; + //convert by canvas transform + normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz); + } + + vec4 base_color = color; + #ifdef MODE_LIGHT_ONLY color = vec4(0.0); #else color *= canvas_modulation; #endif +#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED) + + // Directional Lights + + for (uint i = 0u; i < directional_light_count; i++) { + uint light_base = i; + + vec2 direction = light_array[light_base].position; + vec4 light_color = light_array[light_base].color; + +#ifdef LIGHT_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + light_color = light_compute(light_vertex, vec3(direction, light_array[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true); +#else + + if (normal_used) { + vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array[light_base].height)); + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; + } +#endif + + light_blend_compute(light_base, light_color, color.rgb); + } + + // Positional Lights + + for (uint i = 0u; i < MAX_LIGHTS_PER_ITEM; i++) { + if (i >= light_count) { + break; + } + uint light_base; + if (i < 8u) { + if (i < 4u) { + light_base = draw_data[draw_data_instance].lights[0]; + } else { + light_base = draw_data[draw_data_instance].lights[1]; + } + } else { + if (i < 12u) { + light_base = draw_data[draw_data_instance].lights[2]; + } else { + light_base = draw_data[draw_data_instance].lights[3]; + } + } + light_base >>= (i & 3u) * 8u; + light_base &= uint(0xFF); + + vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy; + vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); + vec4 light_base_color = light_array[light_base].color; + +#ifdef LIGHT_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + vec3 light_position = vec3(light_array[light_base].position, light_array[light_base].height); + + light_color.rgb *= light_base_color.rgb; + light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false); +#else + + light_color.rgb *= light_base_color.rgb * light_base_color.a; + + if (normal_used) { + vec3 light_pos = vec3(light_array[light_base].position, light_array[light_base].height); + vec3 pos = light_vertex; + vec3 light_vec = normalize(light_pos - pos); + + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; + } +#endif + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + light_color.a = 0.0; + } + + light_blend_compute(light_base, light_color, color.rgb); + } +#endif + frag_color = color; } diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index 6b65e09cbf..dd5ebecb1a 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -82,6 +82,7 @@ layout(std140) uniform CanvasData { //ubo:0 uint pad2; }; +#ifndef DISABLE_LIGHTING #define LIGHT_FLAGS_BLEND_MASK uint(3 << 16) #define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16) #define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16) @@ -94,6 +95,27 @@ layout(std140) uniform CanvasData { //ubo:0 #define LIGHT_FLAGS_SHADOW_PCF5 uint(1 << 22) #define LIGHT_FLAGS_SHADOW_PCF13 uint(2 << 22) +struct Light { + mat2x4 texture_matrix; //light to texture coordinate matrix (transposed) + mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed) + vec4 color; + + uint shadow_color; // packed + uint flags; //index to light texture + float shadow_pixel_size; + float height; + + vec2 position; + float shadow_zfar_inv; + float shadow_y_ofs; + + vec4 atlas_rect; +}; + +layout(std140) uniform LightData { //ubo:2 + Light light_array[MAX_LIGHTS]; +}; +#endif // DISABLE_LIGHTING layout(std140) uniform DrawDataInstances { //ubo:3 DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index ca2fc7e36d..796ba79c2e 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -2,7 +2,7 @@ #[modes] mode_default = #define MODE_SIMPLE_COPY -mode_copy_section = #define USE_COPY_SECTION +mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -25,8 +25,7 @@ void main() { gl_Position = vec4(vertex_attrib, 1.0, 1.0); #ifdef USE_COPY_SECTION - gl_Position.xy = (copy_section.xy + (uv_interp.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0; - uv_interp = copy_section.xy + uv_interp * copy_section.zw; + gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0; #endif } diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 442bd69b55..554b4165fa 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -59,9 +59,7 @@ TextureStorage::TextureStorage() { { //create default textures { // White Textures - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(1, 1, 1, 1)); image->generate_mipmaps(); @@ -90,9 +88,7 @@ TextureStorage::TextureStorage() { } { // black - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 1)); image->generate_mipmaps(); @@ -116,9 +112,7 @@ TextureStorage::TextureStorage() { } { // transparent black - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 0)); image->generate_mipmaps(); @@ -127,9 +121,7 @@ TextureStorage::TextureStorage() { } { - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0.5, 0.5, 1, 1)); image->generate_mipmaps(); @@ -138,9 +130,7 @@ TextureStorage::TextureStorage() { } { - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(1.0, 0.5, 1, 1)); image->generate_mipmaps(); @@ -197,6 +187,22 @@ TextureStorage::TextureStorage() { glBindTexture(GL_TEXTURE_2D, 0); + { // Atlas Texture initialize. + uint8_t pixel_data[4 * 4 * 4]; + for (int i = 0; i < 16; i++) { + pixel_data[i * 4 + 0] = 0; + pixel_data[i * 4 + 1] = 0; + pixel_data[i * 4 + 2] = 0; + pixel_data[i * 4 + 3] = 255; + } + + glGenTextures(1, &texture_atlas.texture); + glBindTexture(GL_TEXTURE_2D, texture_atlas.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data); + } + + glBindTexture(GL_TEXTURE_2D, 0); + #ifdef GLES_OVER_GL glEnable(GL_PROGRAM_POINT_SIZE); #endif @@ -207,6 +213,11 @@ TextureStorage::~TextureStorage() { for (int i = 0; i < DEFAULT_GL_TEXTURE_MAX; i++) { texture_free(default_gl_textures[i]); } + + glDeleteTextures(1, &texture_atlas.texture); + texture_atlas.texture = 0; + glDeleteFramebuffers(1, &texture_atlas.framebuffer); + texture_atlas.framebuffer = 0; } //TODO, move back to storage @@ -653,7 +664,7 @@ void TextureStorage::texture_free(RID p_texture) { } } - //decal_atlas_remove_texture(p_texture); + texture_atlas_remove_texture(p_texture); for (int i = 0; i < t->proxies.size(); i++) { Texture *p = texture_owner.get_or_null(t->proxies[i]); @@ -724,9 +735,7 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); texture_2d_initialize(p_texture, image); @@ -735,9 +744,7 @@ void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -756,9 +763,7 @@ void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, Re void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -812,9 +817,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); + Ref<Image> image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (texture->format != texture->real_format) { image->convert(texture->format); @@ -875,7 +878,7 @@ void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) { //delete last, so proxies can be updated texture_owner.free(p_by_texture); - //decal_atlas_mark_dirty_on_texture(p_texture); + texture_atlas_mark_dirty_on_texture(p_texture); } void TextureStorage::texture_set_size_override(RID p_texture, int p_width, int p_height) { @@ -1143,6 +1146,217 @@ RID TextureStorage::texture_create_radiance_cubemap(RID p_source, int p_resoluti return RID(); } +/* TEXTURE ATLAS API */ + +void TextureStorage::texture_add_to_texture_atlas(RID p_texture) { + if (!texture_atlas.textures.has(p_texture)) { + TextureAtlas::Texture t; + t.users = 1; + texture_atlas.textures[p_texture] = t; + texture_atlas.dirty = true; + } else { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + t->users++; + } +} + +void TextureStorage::texture_remove_from_texture_atlas(RID p_texture) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + ERR_FAIL_COND(!t); + t->users--; + if (t->users == 0) { + texture_atlas.textures.erase(p_texture); + // Do not mark it dirty, there is no need to since it remains working. + } +} + +void TextureStorage::texture_atlas_mark_dirty_on_texture(RID p_texture) { + if (texture_atlas.textures.has(p_texture)) { + texture_atlas.dirty = true; // Mark it dirty since it was most likely modified. + } +} + +void TextureStorage::texture_atlas_remove_texture(RID p_texture) { + if (texture_atlas.textures.has(p_texture)) { + texture_atlas.textures.erase(p_texture); + // There is not much a point of making it dirty, texture can be removed next time the atlas is updated. + } +} + +GLuint TextureStorage::texture_atlas_get_texture() const { + return texture_atlas.texture; +} + +void TextureStorage::update_texture_atlas() { + CopyEffects *copy_effects = CopyEffects::get_singleton(); + ERR_FAIL_NULL(copy_effects); + + if (!texture_atlas.dirty) { + return; //nothing to do + } + + texture_atlas.dirty = false; + + if (texture_atlas.texture != 0) { + glDeleteTextures(1, &texture_atlas.texture); + texture_atlas.texture = 0; + glDeleteFramebuffers(1, &texture_atlas.framebuffer); + texture_atlas.framebuffer = 0; + } + + const int border = 2; + + if (texture_atlas.textures.size()) { + //generate atlas + Vector<TextureAtlas::SortItem> itemsv; + itemsv.resize(texture_atlas.textures.size()); + int base_size = 8; + + int idx = 0; + + for (const KeyValue<RID, TextureAtlas::Texture> &E : texture_atlas.textures) { + TextureAtlas::SortItem &si = itemsv.write[idx]; + + Texture *src_tex = get_texture(E.key); + + si.size.width = (src_tex->width / border) + 1; + si.size.height = (src_tex->height / border) + 1; + si.pixel_size = Size2i(src_tex->width, src_tex->height); + + if (base_size < si.size.width) { + base_size = nearest_power_of_2_templated(si.size.width); + } + + si.texture = E.key; + idx++; + } + + //sort items by size + itemsv.sort(); + + //attempt to create atlas + int item_count = itemsv.size(); + TextureAtlas::SortItem *items = itemsv.ptrw(); + + int atlas_height = 0; + + while (true) { + Vector<int> v_offsetsv; + v_offsetsv.resize(base_size); + + int *v_offsets = v_offsetsv.ptrw(); + memset(v_offsets, 0, sizeof(int) * base_size); + + int max_height = 0; + + for (int i = 0; i < item_count; i++) { + //best fit + TextureAtlas::SortItem &si = items[i]; + int best_idx = -1; + int best_height = 0x7FFFFFFF; + for (int j = 0; j <= base_size - si.size.width; j++) { + int height = 0; + for (int k = 0; k < si.size.width; k++) { + int h = v_offsets[k + j]; + if (h > height) { + height = h; + if (height > best_height) { + break; //already bad + } + } + } + + if (height < best_height) { + best_height = height; + best_idx = j; + } + } + + //update + for (int k = 0; k < si.size.width; k++) { + v_offsets[k + best_idx] = best_height + si.size.height; + } + + si.pos.x = best_idx; + si.pos.y = best_height; + + if (si.pos.y + si.size.height > max_height) { + max_height = si.pos.y + si.size.height; + } + } + + if (max_height <= base_size * 2) { + atlas_height = max_height; + break; //good ratio, break; + } + + base_size *= 2; + } + + texture_atlas.size.width = base_size * border; + texture_atlas.size.height = nearest_power_of_2_templated(atlas_height * border); + + for (int i = 0; i < item_count; i++) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(items[i].texture); + t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2); + t->uv_rect.size = items[i].pixel_size; + + t->uv_rect.position /= Size2(texture_atlas.size); + t->uv_rect.size /= Size2(texture_atlas.size); + } + } else { + texture_atlas.size.width = 4; + texture_atlas.size.height = 4; + } + + { // Atlas Texture initialize. + // TODO validate texture atlas size with maximum texture size + glGenTextures(1, &texture_atlas.texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_atlas.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture_atlas.size.width, texture_atlas.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + + glGenFramebuffers(1, &texture_atlas.framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, texture_atlas.framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_atlas.texture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &texture_atlas.framebuffer); + texture_atlas.framebuffer = 0; + glDeleteTextures(1, &texture_atlas.texture); + texture_atlas.texture = 0; + WARN_PRINT("Could not create texture atlas, status: " + get_framebuffer_error(status)); + return; + } + glViewport(0, 0, texture_atlas.size.width, texture_atlas.size.height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glDisable(GL_BLEND); + + if (texture_atlas.textures.size()) { + for (const KeyValue<RID, TextureAtlas::Texture> &E : texture_atlas.textures) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(E.key); + Texture *src_tex = get_texture(E.key); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, src_tex->tex_id); + copy_effects->copy_to_rect(t->uv_rect); + } + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + /* DECAL API */ RID TextureStorage::decal_allocate() { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 7e083e48e8..39a74236e5 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -371,6 +371,38 @@ private: Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool p_force_decompress) const; + /* TEXTURE ATLAS API */ + + struct TextureAtlas { + struct Texture { + int users; + Rect2 uv_rect; + }; + + struct SortItem { + RID texture; + Size2i pixel_size; + Size2i size; + Point2i pos; + + bool operator<(const SortItem &p_item) const { + //sort larger to smaller + if (size.height == p_item.size.height) { + return size.width > p_item.size.width; + } else { + return size.height > p_item.size.height; + } + } + }; + + HashMap<RID, Texture> textures; + bool dirty = true; + + GLuint texture = 0; + GLuint framebuffer = 0; + Size2i size; + } texture_atlas; + /* Render Target API */ mutable RID_Owner<RenderTarget> render_target_owner; @@ -473,6 +505,25 @@ public: void texture_bind(RID p_texture, uint32_t p_texture_no); RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const; + /* TEXTURE ATLAS API */ + + void update_texture_atlas(); + + GLuint texture_atlas_get_texture() const; + _FORCE_INLINE_ Rect2 texture_atlas_get_texture_rect(RID p_texture) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + if (!t) { + return Rect2(); + } + + return t->uv_rect; + } + + void texture_add_to_texture_atlas(RID p_texture); + void texture_remove_from_texture_atlas(RID p_texture); + void texture_atlas_mark_dirty_on_texture(RID p_texture); + void texture_atlas_remove_texture(RID p_texture); + /* DECAL API */ virtual RID decal_allocate() override; diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 16bacf1829..6e91f38050 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -302,6 +302,7 @@ void Utilities::update_dirty_resources() { MaterialStorage::get_singleton()->_update_queued_materials(); //MeshStorage::get_singleton()->_update_dirty_skeletons(); MeshStorage::get_singleton()->_update_dirty_multimeshes(); + TextureStorage::get_singleton()->update_texture_atlas(); } void Utilities::set_debug_generate_wireframes(bool p_generate) { diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index bc4bb3782b..79641464d8 100644 --- a/drivers/png/png_driver_common.cpp +++ b/drivers/png/png_driver_common.cpp @@ -119,7 +119,7 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT); //print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height)); - p_image->create(png_img.width, png_img.height, false, dest_format, buffer); + p_image->set_data(png_img.width, png_img.height, false, dest_format, buffer); return OK; } diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index fb90b776cf..d6636606d2 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -127,6 +127,11 @@ static bool default_capture_device_changed = false; #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif +#if defined(__GNUC__) +// Workaround GCC warning from -Wcast-function-type. +#define GetProcAddress (void *)GetProcAddress +#endif + class CMMNotificationClient : public IMMNotificationClient { LONG _cRef = 1; IMMDeviceEnumerator *_pEnumerator = nullptr; @@ -201,6 +206,20 @@ public: static CMMNotificationClient notif_client; +typedef const char *(CDECL *PWineGetVersionPtr)(void); + +bool AudioDriverWASAPI::is_running_on_wine() { + HMODULE nt_lib = LoadLibraryW(L"ntdll.dll"); + if (!nt_lib) { + return false; + } + + PWineGetVersionPtr wine_get_version = (PWineGetVersionPtr)GetProcAddress(nt_lib, "wine_get_version"); + FreeLibrary(nt_lib); + + return (bool)wine_get_version; +} + Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = nullptr; @@ -285,6 +304,10 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + if (using_audio_client_3 && is_running_on_wine()) { + using_audio_client_3 = false; + print_verbose("WASAPI: Wine detected, falling back to IAudioClient interface"); + } if (using_audio_client_3) { hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); if (hr != S_OK) { diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index c30a54c042..e9f2794e97 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -79,6 +79,8 @@ class AudioDriverWASAPI : public AudioDriver { SafeFlag exit_thread; + static bool is_running_on_wine(); + static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample); static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 2105a101d8..de7f5c8b88 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -37,6 +37,7 @@ #include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" #include "plugins/script_editor_plugin.h" +#include "scene/resources/packed_scene.h" static Node *_find_first_script(Node *p_root, Node *p_node) { if (p_node != p_root && p_node->get_owner() != p_root) { @@ -733,9 +734,11 @@ void ConnectionsDock::_disconnect_all() { while (child) { Connection connection = child->get_metadata(0); - ConnectDialog::ConnectionData cd = connection; - undo_redo->add_do_method(selected_node, "disconnect", cd.signal, cd.get_callable()); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.binds, cd.flags); + if (!_is_connection_inherited(connection)) { + ConnectDialog::ConnectionData cd = connection; + undo_redo->add_do_method(selected_node, "disconnect", cd.signal, cd.get_callable()); + undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.binds, cd.flags); + } child = child->get_next(); } @@ -780,6 +783,26 @@ bool ConnectionsDock::_is_item_signal(TreeItem &p_item) { return (p_item.get_parent() == tree->get_root() || p_item.get_parent()->get_parent() == tree->get_root()); } +bool ConnectionsDock::_is_connection_inherited(Connection &p_connection) { + Node *scene_root = EditorNode::get_singleton()->get_edited_scene(); + Ref<PackedScene> scn = ResourceLoader::load(scene_root->get_scene_file_path()); + ERR_FAIL_NULL_V(scn, false); + + Ref<SceneState> state = scn->get_state(); + ERR_FAIL_NULL_V(state, false); + + Node *source = Object::cast_to<Node>(p_connection.signal.get_object()); + Node *target = Object::cast_to<Node>(p_connection.callable.get_object()); + + const NodePath source_path = scene_root->get_path_to(source); + const NodePath target_path = scene_root->get_path_to(target); + const StringName signal_name = p_connection.signal.get_name(); + const StringName method_name = p_connection.callable.get_method(); + + // If it cannot be found in PackedScene, this connection was inherited. + return !state->has_connection(source_path, signal_name, target_path, method_name, true); +} + /* * Open connection dialog with TreeItem data to CREATE a brand-new connection. */ @@ -866,6 +889,19 @@ void ConnectionsDock::_handle_signal_menu_option(int p_option) { } } +void ConnectionsDock::_signal_menu_about_to_popup() { + TreeItem *signal_item = tree->get_selected(); + + bool disable_disconnect_all = true; + for (int i = 0; i < signal_item->get_child_count(); i++) { + if (!signal_item->get_child(i)->has_meta("_inherited_connection")) { + disable_disconnect_all = false; + } + } + + signal_menu->set_item_disabled(slot_menu->get_item_index(DISCONNECT_ALL), disable_disconnect_all); +} + void ConnectionsDock::_handle_slot_menu_option(int p_option) { TreeItem *item = tree->get_selected(); @@ -888,6 +924,13 @@ void ConnectionsDock::_handle_slot_menu_option(int p_option) { } } +void ConnectionsDock::_slot_menu_about_to_popup() { + bool connection_is_inherited = tree->get_selected()->has_meta("_inherited_connection"); + + slot_menu->set_item_disabled(slot_menu->get_item_index(EDIT), connection_is_inherited); + slot_menu->set_item_disabled(slot_menu->get_item_index(DISCONNECT), connection_is_inherited); +} + void ConnectionsDock::_rmb_pressed(Vector2 p_position, MouseButton p_button) { if (p_button != MouseButton::RIGHT) { return; @@ -1001,7 +1044,7 @@ void ConnectionsDock::update_tree() { name = base; } - if (!icon.is_valid()) { + if (icon.is_null()) { icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons")); } @@ -1133,6 +1176,12 @@ void ConnectionsDock::update_tree() { connection_item->set_text(0, path); connection_item->set_metadata(0, connection); connection_item->set_icon(0, get_theme_icon(SNAME("Slot"), SNAME("EditorIcons"))); + + if (_is_connection_inherited(connection)) { + // The scene inherits this connection. + connection_item->set_custom_color(0, get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + connection_item->set_meta("_inherited_connection", true); + } } } @@ -1185,6 +1234,7 @@ ConnectionsDock::ConnectionsDock() { signal_menu = memnew(PopupMenu); add_child(signal_menu); signal_menu->connect("id_pressed", callable_mp(this, &ConnectionsDock::_handle_signal_menu_option)); + signal_menu->connect("about_to_popup", callable_mp(this, &ConnectionsDock::_signal_menu_about_to_popup)); signal_menu->add_item(TTR("Connect..."), CONNECT); signal_menu->add_item(TTR("Disconnect All"), DISCONNECT_ALL); signal_menu->add_item(TTR("Copy Name"), COPY_NAME); @@ -1192,6 +1242,7 @@ ConnectionsDock::ConnectionsDock() { slot_menu = memnew(PopupMenu); add_child(slot_menu); slot_menu->connect("id_pressed", callable_mp(this, &ConnectionsDock::_handle_slot_menu_option)); + slot_menu->connect("about_to_popup", callable_mp(this, &ConnectionsDock::_slot_menu_about_to_popup)); slot_menu->add_item(TTR("Edit..."), EDIT); slot_menu->add_item(TTR("Go to Method"), GO_TO_SCRIPT); slot_menu->add_item(TTR("Disconnect"), DISCONNECT); diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 16a60306aa..126a0ca828 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -212,13 +212,16 @@ class ConnectionsDock : public VBoxContainer { void _tree_item_selected(); void _tree_item_activated(); bool _is_item_signal(TreeItem &p_item); + bool _is_connection_inherited(Connection &p_connection); void _open_connection_dialog(TreeItem &p_item); void _open_connection_dialog(ConnectDialog::ConnectionData p_cd); void _go_to_script(TreeItem &p_item); void _handle_signal_menu_option(int p_option); + void _signal_menu_about_to_popup(); void _handle_slot_menu_option(int p_option); + void _slot_menu_about_to_popup(); void _rmb_pressed(Vector2 p_position, MouseButton p_button); void _close(); diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index a882275375..dfa2b8e70f 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -308,9 +308,7 @@ void EditorProfiler::_update_plot() { } } - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, graph_image); + Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image); if (reset_texture) { if (graph_texture.is_null()) { diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 8e7135f1c5..8552c8e9ef 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -298,9 +298,7 @@ void EditorVisualProfiler::_update_plot() { } } - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, graph_image); + Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image); if (reset_texture) { if (graph_texture.is_null()) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index bf50efc4f9..8ee82888f3 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1618,7 +1618,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { // which would result in an invalid texture. if (c3d == 0 && c2d == 0) { img.instantiate(); - img->create(1, 1, false, Image::FORMAT_RGB8); + img->initialize_data(1, 1, false, Image::FORMAT_RGB8); } else if (c3d < c2d) { Ref<ViewportTexture> viewport_texture = scene_root->get_texture(); if (viewport_texture->get_width() > 0 && viewport_texture->get_height() > 0) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 155802d16c..899fa69be1 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -90,7 +90,9 @@ void EditorPropertyText::update_property() { String s = get_edited_object()->get(get_edited_property()); updating = true; if (text->get_text() != s) { + int caret = text->get_caret_column(); text->set_text(s); + text->set_caret_column(caret); } text->set_editable(!is_read_only()); updating = false; @@ -760,8 +762,13 @@ void EditorPropertyEnum::_option_selected(int p_which) { } void EditorPropertyEnum::update_property() { - int64_t which = get_edited_object()->get(get_edited_property()); + Variant current = get_edited_object()->get(get_edited_property()); + if (current.get_type() == Variant::NIL) { + options->select(-1); + return; + } + int64_t which = current; for (int i = 0; i < options->get_item_count(); i++) { if (which == (int64_t)options->get_item_metadata(i)) { options->select(i); diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 5edb6d877c..1cdfceebc8 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -143,7 +143,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventKey> k = p_event; - if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept")) { + if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept", true)) { _focus_entered(); } } diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp index 88e99d9b30..8e820f41ee 100644 --- a/editor/editor_zoom_widget.cpp +++ b/editor/editor_zoom_widget.cpp @@ -161,13 +161,19 @@ void EditorZoomWidget::_bind_methods() { ADD_SIGNAL(MethodInfo("zoom_changed", PropertyInfo(Variant::FLOAT, "zoom"))); } +void EditorZoomWidget::set_shortcut_context(Node *p_node) const { + zoom_minus->set_shortcut_context(p_node); + zoom_plus->set_shortcut_context(p_node); + zoom_reset->set_shortcut_context(p_node); +} + EditorZoomWidget::EditorZoomWidget() { // Zoom buttons zoom_minus = memnew(Button); zoom_minus->set_flat(true); add_child(zoom_minus); zoom_minus->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_minus)); - zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom Out"), KeyModifierMask::CMD_OR_CTRL | Key::MINUS)); + zoom_minus->set_shortcut(ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_minus", TTR("Zoom Out"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::MINUS), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_SUBTRACT) })); zoom_minus->set_shortcut_context(this); zoom_minus->set_focus_mode(FOCUS_NONE); @@ -189,7 +195,7 @@ EditorZoomWidget::EditorZoomWidget() { zoom_plus->set_flat(true); add_child(zoom_plus); zoom_plus->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_plus)); - zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom In"), KeyModifierMask::CMD_OR_CTRL | Key::EQUAL)); // Usually direct access key for PLUS + zoom_plus->set_shortcut(ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_plus", TTR("Zoom In"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::EQUAL), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_ADD) })); zoom_plus->set_shortcut_context(this); zoom_plus->set_focus_mode(FOCUS_NONE); diff --git a/editor/editor_zoom_widget.h b/editor/editor_zoom_widget.h index 4690a57a2b..995b9e1808 100644 --- a/editor/editor_zoom_widget.h +++ b/editor/editor_zoom_widget.h @@ -57,6 +57,8 @@ public: float get_zoom(); void set_zoom(float p_zoom); void set_zoom_by_increments(int p_increment_count, bool p_integer_only = false); + // Sets the shortcut context for the zoom buttons. By default their context is this EditorZoomWidget control. + void set_shortcut_context(Node *p_node) const; }; #endif // EDITOR_ZOOM_WIDGET_H diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp index 14e482432a..e4c35a5b81 100644 --- a/editor/event_listener_line_edit.cpp +++ b/editor/event_listener_line_edit.cpp @@ -59,8 +59,9 @@ void EventListenerLineEdit::gui_input(const Ref<InputEvent> &p_event) { // First event will be an event which is used to focus this control - i.e. a mouse click, or a tab press. // Ignore the first one so that clicking into the LineEdit does not override the current event. // Ignore is reset to true when the control is unfocused. - if (ignore) { - ignore = false; + // This class also specially handles grab_focus() calls. + if (ignore_next_event) { + ignore_next_event = false; return; } @@ -85,7 +86,7 @@ void EventListenerLineEdit::_on_focus() { } void EventListenerLineEdit::_on_unfocus() { - ignore = true; + ignore_next_event = true; set_placeholder(TTR("Filter by event...")); } @@ -109,6 +110,12 @@ int EventListenerLineEdit::get_allowed_input_types() const { return allowed_input_types; } +void EventListenerLineEdit::grab_focus() { + // If we grab focus through code, we don't need to ignore the first event! + ignore_next_event = false; + Control::grab_focus(); +} + void EventListenerLineEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/editor/event_listener_line_edit.h b/editor/event_listener_line_edit.h index 5b415e8a2d..c4cd5e4511 100644 --- a/editor/event_listener_line_edit.h +++ b/editor/event_listener_line_edit.h @@ -44,7 +44,7 @@ class EventListenerLineEdit : public LineEdit { GDCLASS(EventListenerLineEdit, LineEdit) int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; - bool ignore = true; + bool ignore_next_event = true; bool share_keycodes = false; Ref<InputEvent> event; @@ -67,6 +67,8 @@ public: void set_allowed_input_types(int input_types); int get_allowed_input_types() const; + void grab_focus(); + public: EventListenerLineEdit(); }; diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 88dc7bd5cd..5db79b98d1 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -144,6 +144,7 @@ public: }; virtual Ref<EditorExportPreset> create_preset(); + virtual bool is_executable(const String &p_path) const { return false; } virtual void clear_messages() { messages.clear(); } virtual void add_message(ExportMessageType p_type, const String &p_category, const String &p_message) { diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 8538414523..c5b61e9b03 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -185,10 +185,12 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> String src_path = ProjectSettings::get_singleton()->globalize_path(so_files[i].path); String target_path; if (so_files[i].target.is_empty()) { - target_path = p_path.get_base_dir().path_join(src_path.get_file()); + target_path = p_path.get_base_dir(); } else { - target_path = p_path.get_base_dir().path_join(so_files[i].target).path_join(src_path.get_file()); + target_path = p_path.get_base_dir().path_join(so_files[i].target); + da->make_dir_recursive(target_path); } + target_path = target_path.path_join(src_path.get_file()); if (da->dir_exists(src_path)) { err = da->make_dir_recursive(target_path); diff --git a/editor/icons/Line.svg b/editor/icons/Line.svg new file mode 100644 index 0000000000..c7b1f8a701 --- /dev/null +++ b/editor/icons/Line.svg @@ -0,0 +1 @@ +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 8-8" fill="none" stroke="#ffffff" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 9dafa47c1c..e1df78e741 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -186,9 +186,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons int mm_d = MAX(1, d >> 1); for (int i = 0; i < mm_d; i++) { - Ref<Image> mm; - mm.instantiate(); - mm->create(mm_w, mm_h, false, p_images[0]->get_format()); + Ref<Image> mm = Image::create_empty(mm_w, mm_h, false, p_images[0]->get_format()); Vector3 pos; pos.z = float(i) * float(d) / float(mm_d) + 0.5; for (int x = 0; x < mm_w; x++) { diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index 9171f04f42..bf22a9377e 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -273,9 +273,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file EditorAtlasPacker::chart_pack(charts, atlas_width, atlas_height); //blit the atlas - Ref<Image> new_atlas; - new_atlas.instantiate(); - new_atlas->create(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); + Ref<Image> new_atlas = Image::create_empty(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); for (int i = 0; i < pack_data_files.size(); i++) { PackData &pack_data = pack_data_files.write[i]; diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index ed9a5898fb..c577c61db7 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -533,6 +533,10 @@ String InputEventConfigurationDialog::_get_device_string(int p_device) const { void InputEventConfigurationDialog::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_VISIBILITY_CHANGED: { + event_listener->grab_focus(); + } break; + case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { input_list_search->set_right_icon(input_list_search->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index a48990779b..b4737976d8 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -128,14 +128,11 @@ void AnimationPlayerEditor::_notification(int p_what) { { Ref<Image> autoplay_img = autoplay_icon->get_image(); Ref<Image> reset_img = reset_icon->get_image(); - Ref<Image> autoplay_reset_img; Size2 icon_size = autoplay_img->get_size(); - autoplay_reset_img.instantiate(); - autoplay_reset_img->create(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format()); + Ref<Image> autoplay_reset_img = Image::create_empty(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format()); autoplay_reset_img->blit_rect(autoplay_img, Rect2i(Point2i(), icon_size), Point2i()); autoplay_reset_img->blit_rect(reset_img, Rect2i(Point2i(), icon_size), Point2i(icon_size.x, 0)); - autoplay_reset_icon.instantiate(); - autoplay_reset_icon->set_image(autoplay_reset_img); + autoplay_reset_icon = ImageTexture::create_from_image(autoplay_reset_img); } stop->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 4c792d0dca..fe33a91a41 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5060,6 +5060,7 @@ CanvasItemEditor::CanvasItemEditor() { zoom_widget = memnew(EditorZoomWidget); controls_vb->add_child(zoom_widget); zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); + zoom_widget->set_shortcut_context(this); zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom)); panner.instantiate(); @@ -5454,6 +5455,15 @@ void CanvasItemEditorPlugin::set_state(const Dictionary &p_state) { canvas_item_editor->set_state(p_state); } +void CanvasItemEditorPlugin::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + connect("scene_changed", callable_mp((CanvasItem *)canvas_item_editor->get_viewport_control(), &CanvasItem::queue_redraw).unbind(1)); + connect("scene_closed", callable_mp((CanvasItem *)canvas_item_editor->get_viewport_control(), &CanvasItem::queue_redraw).unbind(1)); + } break; + } +} + CanvasItemEditorPlugin::CanvasItemEditorPlugin() { canvas_item_editor = memnew(CanvasItemEditor); canvas_item_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index b731d3cc7d..3ade048e4b 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -563,6 +563,9 @@ class CanvasItemEditorPlugin : public EditorPlugin { CanvasItemEditor *canvas_item_editor = nullptr; +protected: + void _notification(int p_what); + public: virtual String get_name() const override { return "2D"; } bool has_main_screen() const override { return true; } diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 8d1df0b32c..434e6a92e3 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -805,7 +805,7 @@ Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, cons img_ref.instantiate(); Image &im = **img_ref; - im.create(thumbnail_size, thumbnail_size / 2, false, Image::FORMAT_RGBA8); + im.initialize_data(thumbnail_size, thumbnail_size / 2, false, Image::FORMAT_RGBA8); Color bg_color(0.1, 0.1, 0.1, 1.0); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 59b8f31720..836f76ac4f 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -210,9 +210,7 @@ Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, } } - Ref<Image> img; - img.instantiate(); - img->create(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data); + Ref<Image> img = Image::create_from_data(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data); if (img->is_compressed()) { if (img->decompress() != OK) { @@ -483,10 +481,8 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, int line = 0; int col = 0; - Ref<Image> img; - img.instantiate(); int thumbnail_size = MAX(p_size.x, p_size.y); - img->create(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8); Color bg_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/background_color"); Color keyword_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/keyword_color"); @@ -660,9 +656,7 @@ Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_f //post_process_preview(img); - Ref<Image> image; - image.instantiate(); - image->create(w, h, false, Image::FORMAT_RGB8, img); + Ref<Image> image = Image::create_from_data(w, h, false, Image::FORMAT_RGB8, img); return ImageTexture::create_from_image(image); } diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index 1b2afe2151..891b9efa4f 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -299,7 +299,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGF, texdata); + img->set_data(w, h, false, Image::FORMAT_RGF, texdata); pm->set_emission_point_texture(ImageTexture::create_from_image(img)); pm->set_emission_point_count(vpc); @@ -315,7 +315,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, colordata); + img->set_data(w, h, false, Image::FORMAT_RGBA8, colordata); pm->set_emission_color_texture(ImageTexture::create_from_image(img)); } @@ -335,7 +335,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGF, normdata); + img->set_data(w, h, false, Image::FORMAT_RGF, normdata); pm->set_emission_normal_texture(ImageTexture::create_from_image(img)); } else { diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 7194cd9d27..26af5e3f2d 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -4751,9 +4751,9 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (err == OK) { Vector<Vector3> points2; points2.resize(md.edges.size() * 2); - for (int i = 0; i < md.edges.size(); i++) { - points2.write[i * 2 + 0] = md.vertices[md.edges[i].a]; - points2.write[i * 2 + 1] = md.vertices[md.edges[i].b]; + for (uint32_t i = 0; i < md.edges.size(); i++) { + points2.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a]; + points2.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b]; } p_gizmo->add_lines(points2, material); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index cfece173d6..5dac66d3e1 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -414,16 +414,16 @@ void SpriteFramesEditor::_notification(int p_what) { load_sheet->set_icon(get_theme_icon(SNAME("SpriteSheet"), SNAME("EditorIcons"))); copy->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); paste->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons"))); - empty->set_icon(get_theme_icon(SNAME("InsertBefore"), SNAME("EditorIcons"))); - empty2->set_icon(get_theme_icon(SNAME("InsertAfter"), SNAME("EditorIcons"))); + empty_before->set_icon(get_theme_icon(SNAME("InsertBefore"), SNAME("EditorIcons"))); + empty_after->set_icon(get_theme_icon(SNAME("InsertAfter"), SNAME("EditorIcons"))); move_up->set_icon(get_theme_icon(SNAME("MoveLeft"), SNAME("EditorIcons"))); move_down->set_icon(get_theme_icon(SNAME("MoveRight"), SNAME("EditorIcons"))); - _delete->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + delete_frame->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); zoom_out->set_icon(get_theme_icon(SNAME("ZoomLess"), SNAME("EditorIcons"))); zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons"))); zoom_in->set_icon(get_theme_icon(SNAME("ZoomMore"), SNAME("EditorIcons"))); - new_anim->set_icon(get_theme_icon(SNAME("New"), SNAME("EditorIcons"))); - remove_anim->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + add_anim->set_icon(get_theme_icon(SNAME("New"), SNAME("EditorIcons"))); + delete_anim->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); anim_search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); split_sheet_zoom_out->set_icon(get_theme_icon(SNAME("ZoomLess"), SNAME("EditorIcons"))); split_sheet_zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons"))); @@ -1016,19 +1016,19 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { hide(); } - new_anim->set_disabled(read_only); - remove_anim->set_disabled(read_only); + add_anim->set_disabled(read_only); + delete_anim->set_disabled(read_only); anim_speed->set_editable(!read_only); anim_loop->set_disabled(read_only); load->set_disabled(read_only); load_sheet->set_disabled(read_only); copy->set_disabled(read_only); paste->set_disabled(read_only); - empty->set_disabled(read_only); - empty2->set_disabled(read_only); + empty_before->set_disabled(read_only); + empty_after->set_disabled(read_only); move_up->set_disabled(read_only); move_down->set_disabled(read_only); - _delete->set_disabled(read_only); + delete_frame->set_disabled(read_only); } void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { @@ -1187,18 +1187,16 @@ SpriteFramesEditor::SpriteFramesEditor() { HBoxContainer *hbc_animlist = memnew(HBoxContainer); sub_vb->add_child(hbc_animlist); - new_anim = memnew(Button); - new_anim->set_flat(true); - new_anim->set_tooltip_text(TTR("New Animation")); - hbc_animlist->add_child(new_anim); - new_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_add)); + add_anim = memnew(Button); + add_anim->set_flat(true); + hbc_animlist->add_child(add_anim); + add_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_add)); - remove_anim = memnew(Button); - remove_anim->set_flat(true); - remove_anim->set_tooltip_text(TTR("Remove Animation")); - hbc_animlist->add_child(remove_anim); - remove_anim->set_disabled(true); - remove_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); + delete_anim = memnew(Button); + delete_anim->set_flat(true); + hbc_animlist->add_child(delete_anim); + delete_anim->set_disabled(true); + delete_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); anim_search_box = memnew(LineEdit); hbc_animlist->add_child(anim_search_box); @@ -1215,6 +1213,11 @@ SpriteFramesEditor::SpriteFramesEditor() { animations->connect("item_edited", callable_mp(this, &SpriteFramesEditor::_animation_name_edited)); animations->set_allow_reselect(true); + add_anim->set_shortcut_context(animations); + add_anim->set_shortcut(ED_SHORTCUT("sprite_frames/new_animation", TTR("Add Animation"), KeyModifierMask::CMD_OR_CTRL | Key::N)); + delete_anim->set_shortcut_context(animations); + delete_anim->set_shortcut(ED_SHORTCUT("sprite_frames/delete_animation", TTR("Delete Animation"), Key::KEY_DELETE)); + HBoxContainer *hbc_anim_speed = memnew(HBoxContainer); hbc_anim_speed->add_child(memnew(Label(TTR("Speed:")))); vbc_animlist->add_child(hbc_anim_speed); @@ -1244,54 +1247,45 @@ SpriteFramesEditor::SpriteFramesEditor() { load = memnew(Button); load->set_flat(true); - load->set_tooltip_text(TTR("Add a Texture from File")); hbc->add_child(load); load_sheet = memnew(Button); load_sheet->set_flat(true); - load_sheet->set_tooltip_text(TTR("Add Frames from a Sprite Sheet")); hbc->add_child(load_sheet); hbc->add_child(memnew(VSeparator)); copy = memnew(Button); copy->set_flat(true); - copy->set_tooltip_text(TTR("Copy")); hbc->add_child(copy); paste = memnew(Button); paste->set_flat(true); - paste->set_tooltip_text(TTR("Paste")); hbc->add_child(paste); hbc->add_child(memnew(VSeparator)); - empty = memnew(Button); - empty->set_flat(true); - empty->set_tooltip_text(TTR("Insert Empty (Before)")); - hbc->add_child(empty); + empty_before = memnew(Button); + empty_before->set_flat(true); + hbc->add_child(empty_before); - empty2 = memnew(Button); - empty2->set_flat(true); - empty2->set_tooltip_text(TTR("Insert Empty (After)")); - hbc->add_child(empty2); + empty_after = memnew(Button); + empty_after->set_flat(true); + hbc->add_child(empty_after); hbc->add_child(memnew(VSeparator)); move_up = memnew(Button); move_up->set_flat(true); - move_up->set_tooltip_text(TTR("Move (Before)")); hbc->add_child(move_up); move_down = memnew(Button); move_down->set_flat(true); - move_down->set_tooltip_text(TTR("Move (After)")); hbc->add_child(move_down); - _delete = memnew(Button); - _delete->set_flat(true); - _delete->set_tooltip_text(TTR("Delete")); - hbc->add_child(_delete); + delete_frame = memnew(Button); + delete_frame->set_flat(true); + hbc->add_child(delete_frame); hbc->add_spacer(); @@ -1333,13 +1327,40 @@ SpriteFramesEditor::SpriteFramesEditor() { load->connect("pressed", callable_mp(this, &SpriteFramesEditor::_load_pressed)); load_sheet->connect("pressed", callable_mp(this, &SpriteFramesEditor::_open_sprite_sheet)); - _delete->connect("pressed", callable_mp(this, &SpriteFramesEditor::_delete_pressed)); + delete_frame->connect("pressed", callable_mp(this, &SpriteFramesEditor::_delete_pressed)); copy->connect("pressed", callable_mp(this, &SpriteFramesEditor::_copy_pressed)); paste->connect("pressed", callable_mp(this, &SpriteFramesEditor::_paste_pressed)); - empty->connect("pressed", callable_mp(this, &SpriteFramesEditor::_empty_pressed)); - empty2->connect("pressed", callable_mp(this, &SpriteFramesEditor::_empty2_pressed)); + empty_before->connect("pressed", callable_mp(this, &SpriteFramesEditor::_empty_pressed)); + empty_after->connect("pressed", callable_mp(this, &SpriteFramesEditor::_empty2_pressed)); move_up->connect("pressed", callable_mp(this, &SpriteFramesEditor::_up_pressed)); move_down->connect("pressed", callable_mp(this, &SpriteFramesEditor::_down_pressed)); + + load->set_shortcut_context(tree); + load->set_shortcut(ED_SHORTCUT("sprite_frames/load_from_file", TTR("Add frame from file"), KeyModifierMask::CMD_OR_CTRL | Key::O)); + load_sheet->set_shortcut_context(tree); + load_sheet->set_shortcut(ED_SHORTCUT("sprite_frames/load_from_sheet", TTR("Add frames from sprite sheet"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::O)); + delete_frame->set_shortcut_context(tree); + delete_frame->set_shortcut(ED_SHORTCUT("sprite_frames/delete", TTR("Delete Frame"), Key::KEY_DELETE)); + copy->set_shortcut_context(tree); + copy->set_shortcut(ED_SHORTCUT("sprite_frames/copy", TTR("Copy Frame"), KeyModifierMask::CMD_OR_CTRL | Key::C)); + paste->set_shortcut_context(tree); + paste->set_shortcut(ED_SHORTCUT("sprite_frames/paste", TTR("Paste Frame"), KeyModifierMask::CMD_OR_CTRL | Key::V)); + empty_before->set_shortcut_context(tree); + empty_before->set_shortcut(ED_SHORTCUT("sprite_frames/empty_before", TTR("Insert Empty (Before Selected)"), KeyModifierMask::ALT | Key::LEFT)); + empty_after->set_shortcut_context(tree); + empty_after->set_shortcut(ED_SHORTCUT("sprite_frames/empty_after", TTR("Insert Empty (After Selected)"), KeyModifierMask::ALT | Key::RIGHT)); + move_up->set_shortcut_context(tree); + move_up->set_shortcut(ED_SHORTCUT("sprite_frames/move_left", TTR("Move Frame Left"), KeyModifierMask::CMD_OR_CTRL | Key::LEFT)); + move_down->set_shortcut_context(tree); + move_down->set_shortcut(ED_SHORTCUT("sprite_frames/move_right", TTR("Move Frame Right"), KeyModifierMask::CMD_OR_CTRL | Key::RIGHT)); + + zoom_out->set_shortcut_context(tree); + zoom_out->set_shortcut(ED_SHORTCUT_ARRAY("sprite_frames/zoom_out", TTR("Zoom Out"), + { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::MINUS), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_SUBTRACT) })); + zoom_in->set_shortcut_context(tree); + zoom_in->set_shortcut(ED_SHORTCUT_ARRAY("sprite_frames/zoom_in", TTR("Zoom In"), + { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::EQUAL), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_ADD) })); + file->connect("files_selected", callable_mp(this, &SpriteFramesEditor::_file_load_request).bind(-1)); loading_scene = false; sel = -1; diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 092f556c63..5fc3fe4481 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -61,11 +61,11 @@ class SpriteFramesEditor : public HSplitContainer { Button *load = nullptr; Button *load_sheet = nullptr; - Button *_delete = nullptr; + Button *delete_frame = nullptr; Button *copy = nullptr; Button *paste = nullptr; - Button *empty = nullptr; - Button *empty2 = nullptr; + Button *empty_before = nullptr; + Button *empty_after = nullptr; Button *move_up = nullptr; Button *move_down = nullptr; Button *zoom_out = nullptr; @@ -75,8 +75,8 @@ class SpriteFramesEditor : public HSplitContainer { bool loading_scene; int sel; - Button *new_anim = nullptr; - Button *remove_anim = nullptr; + Button *add_anim = nullptr; + Button *delete_anim = nullptr; LineEdit *anim_search_box = nullptr; Tree *animations = nullptr; diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index d7e08db954..167c6d169b 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -47,9 +47,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla merged_mapping.clear(); if (p_atlas_sources.size() >= 2) { - Ref<Image> output_image; - output_image.instantiate(); - output_image->create(1, 1, false, Image::FORMAT_RGBA8); + Ref<Image> output_image = Image::create_empty(1, 1, false, Image::FORMAT_RGBA8); // Compute the new texture region size. Vector2i new_texture_region_size; diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index c823487279..502c34459a 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -533,11 +533,11 @@ TileAtlasView::TileAtlasView() { panel->set_v_size_flags(SIZE_EXPAND_FILL); add_child(panel); - // Scrollingsc zoom_widget = memnew(EditorZoomWidget); add_child(zoom_widget); zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1)); + zoom_widget->set_shortcut_context(this); button_center_view = memnew(Button); button_center_view->set_icon(get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons"))); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 73ca8b6176..17b9035121 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -831,6 +831,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { editor_zoom_widget = memnew(EditorZoomWidget); editor_zoom_widget->set_position(Vector2(5, 5)); editor_zoom_widget->connect("zoom_changed", callable_mp(this, &GenericTilePolygonEditor::_zoom_changed).unbind(1)); + editor_zoom_widget->set_shortcut_context(this); root->add_child(editor_zoom_widget); button_center_view = memnew(Button); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index dd16d4ffea..cbc95ef6d6 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -460,7 +460,7 @@ void TileMapEditorTilesPlugin::_update_theme() { source_sort_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Sort"), SNAME("EditorIcons"))); select_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons"))); paint_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons"))); + line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Line"), SNAME("EditorIcons"))); rect_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons"))); bucket_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons"))); @@ -3277,7 +3277,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { void TileMapEditorTerrainsPlugin::_update_theme() { paint_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - line_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons"))); + line_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Line"), SNAME("EditorIcons"))); rect_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons"))); bucket_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons"))); diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 364c3b7246..6fdc9a80e8 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -66,7 +66,9 @@ void TilesEditorPlugin::_thread() { pattern_preview_sem.wait(); pattern_preview_mutex.lock(); - if (pattern_preview_queue.size()) { + if (pattern_preview_queue.size() == 0) { + pattern_preview_mutex.unlock(); + } else { QueueItem item = pattern_preview_queue.front()->get(); pattern_preview_queue.pop_front(); pattern_preview_mutex.unlock(); @@ -130,8 +132,6 @@ void TilesEditorPlugin::_thread() { item.callback.callp(args_ptr, 2, r, error); viewport->queue_delete(); - } else { - pattern_preview_mutex.unlock(); } } } diff --git a/main/main.cpp b/main/main.cpp index 0f73bc7d1e..bb6ab11362 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2129,7 +2129,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } else { // Create a 1×1 transparent image. This will effectively hide the splash image. boot_logo.instantiate(); - boot_logo->create(1, 1, false, Image::FORMAT_RGBA8); + boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8); boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0)); } diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index e80d453df7..155f7809b0 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -253,7 +253,7 @@ static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size }; image.instantiate(); - image->create(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); + image->set_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); return image; } diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index cc21ed28e8..681b3e74bc 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -156,7 +156,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, if (p_color_buffer == nullptr || color_table_size == 0) { // regular pixels - p_image->create(width, height, false, Image::FORMAT_RGBA8, data); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, data); } else { // data is in indexed format, extend it @@ -194,7 +194,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, dest += 4; } - p_image->create(width, height, false, Image::FORMAT_RGBA8, extended_data); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, extended_data); } } return err; diff --git a/modules/camera/camera_macos.mm b/modules/camera/camera_macos.mm index 0b9696a3e9..0e61dde8e9 100644 --- a/modules/camera/camera_macos.mm +++ b/modules/camera/camera_macos.mm @@ -158,7 +158,7 @@ memcpy(w, dataY, new_width * new_height); img[0].instantiate(); - img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]); + img[0]->set_data(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]); } { @@ -177,7 +177,7 @@ ///TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion img[1].instantiate(); - img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); + img[1]->set_data(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); } // set our texture... diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 3322ff0a1b..89705e4ee0 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -230,7 +230,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann h = MAX(h / 2, 1); } - p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } void image_decompress_cvtt(Image *p_image) { @@ -339,5 +339,5 @@ void image_decompress_cvtt(Image *p_image) { w >>= 1; h >>= 1; } - p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp index a0dbd07b10..2b9221bc43 100644 --- a/modules/denoise/lightmap_denoiser.cpp +++ b/modules/denoise/lightmap_denoiser.cpp @@ -51,7 +51,7 @@ Ref<Image> LightmapDenoiserOIDN::denoise_image(const Ref<Image> &p_image) { return p_image; } - img->create(img->get_width(), img->get_height(), false, img->get_format(), data); + img->set_data(img->get_width(), img->get_height(), false, img->get_format(), data); return img; } diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index 7d5557d197..3d66b27556 100644 --- a/modules/etcpak/image_compress_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -233,7 +233,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua } // Replace original image with compressed one. - r_img->create(width, height, mipmaps, target_format, dest_data); + r_img->set_data(width, height, mipmaps, target_format, dest_data); print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index e1beb2f374..6fbdec863f 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1041,7 +1041,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { } } -void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) { +void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root) { ERR_FAIL_COND_MSG(p_node == nullptr, "Trying to resolve type of a null node."); switch (p_node->type) { @@ -1114,7 +1114,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) { case GDScriptParser::Node::SUBSCRIPT: case GDScriptParser::Node::TERNARY_OPERATOR: case GDScriptParser::Node::UNARY_OPERATOR: - reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), true); + reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), p_is_root); break; case GDScriptParser::Node::BREAK: case GDScriptParser::Node::BREAKPOINT: @@ -1411,7 +1411,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else? p_for->variable->set_datatype(variable_type); } else if (p_for->list) { - resolve_node(p_for->list); + resolve_node(p_for->list, false); if (p_for->list->datatype.has_container_element_type()) { variable_type = p_for->list->datatype.get_container_element_type(); variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; @@ -1439,7 +1439,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) { - resolve_node(p_while->condition); + resolve_node(p_while->condition, false); resolve_suite(p_while->loop); p_while->set_datatype(p_while->loop->get_datatype()); @@ -1824,7 +1824,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre reduce_binary_op(static_cast<GDScriptParser::BinaryOpNode *>(p_expression)); break; case GDScriptParser::Node::CALL: - reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), p_is_root); + reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), false, p_is_root); break; case GDScriptParser::Node::CAST: reduce_cast(static_cast<GDScriptParser::CastNode *>(p_expression)); @@ -2548,6 +2548,16 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a mark_lambda_use_self(); } +#ifdef DEBUG_ENABLED + if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) { + parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); + } + + if (is_static && !base_type.is_meta_type && !(callee_type != GDScriptParser::Node::SUBSCRIPT && parser->current_function != nullptr && parser->current_function->is_static)) { + parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, base_type.to_string()); + } +#endif // DEBUG_ENABLED + call_type = return_type; } else { bool found = false; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 3966b81b6e..217a856ce0 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -63,7 +63,7 @@ class GDScriptAnalyzer { void resolve_class_body(GDScriptParser::ClassNode *p_class); void resolve_function_signature(GDScriptParser::FunctionNode *p_function); void resolve_function_body(GDScriptParser::FunctionNode *p_function); - void resolve_node(GDScriptParser::Node *p_node); + void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true); void resolve_suite(GDScriptParser::SuiteNode *p_suite); void resolve_if(GDScriptParser::IfNode *p_if); void resolve_for(GDScriptParser::ForNode *p_for); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 1cae7bdfac..a0c107aa53 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -155,6 +155,10 @@ String GDScriptWarning::get_message() const { case INT_ASSIGNED_TO_ENUM: { return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type."; } + case STATIC_CALLED_ON_INSTANCE: { + CHECK_SYMBOLS(2); + return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]); + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -215,6 +219,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "EMPTY_FILE", "SHADOWED_GLOBAL_IDENTIFIER", "INT_ASSIGNED_TO_ENUM", + "STATIC_CALLED_ON_INSTANCE", }; static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index a639e7b44e..7e4e975510 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -78,6 +78,7 @@ public: EMPTY_FILE, // A script file is empty. SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable. INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting. + STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. WARNING_MAX, }; diff --git a/modules/gdscript/tests/scripts/parser/features/class.gd b/modules/gdscript/tests/scripts/parser/features/class.gd index 6652f85ad9..af24b32322 100644 --- a/modules/gdscript/tests/scripts/parser/features/class.gd +++ b/modules/gdscript/tests/scripts/parser/features/class.gd @@ -21,5 +21,5 @@ func test(): assert(test_sub.number == 25) # From Test. assert(test_sub.other_string == "bye") # From TestSub. - TestConstructor.new() - TestConstructor.new(500) + var _test_constructor = TestConstructor.new() + _test_constructor = TestConstructor.new(500) diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out index d73c5eb7cd..13f759dd46 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out +++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out @@ -1 +1,5 @@ GDTEST_OK +>> WARNING +>> Line: 6 +>> RETURN_VALUE_DISCARDED +>> The function 'i_return_int()' returns a value, but this value is never used. diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd new file mode 100644 index 0000000000..29d8501b78 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd @@ -0,0 +1,11 @@ +class Player: + var x = 3 + +func test(): + # These should not emit a warning. + var _player = Player.new() + print(String.num_uint64(8589934592)) # 2 ^ 33 + + # This should emit a warning. + var some_string = String() + print(some_string.num_uint64(8589934592)) # 2 ^ 33 diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out new file mode 100644 index 0000000000..3933a35178 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out @@ -0,0 +1,7 @@ +GDTEST_OK +>> WARNING +>> Line: 11 +>> STATIC_CALLED_ON_INSTANCE +>> The function 'num_uint64()' is a static function but was called from an instance. Instead, it should be directly called from the type: 'String.num_uint64()'. +8589934592 +8589934592 diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index 4d1aa89ac9..8e48066623 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -9,6 +9,25 @@ <tutorials> <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> </tutorials> + <methods> + <method name="get_additional_data"> + <return type="Variant" /> + <param index="0" name="extension_name" type="StringName" /> + <description> + Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + </description> + </method> + <method name="set_additional_data"> + <return type="void" /> + <param index="0" name="extension_name" type="StringName" /> + <param index="1" name="additional_data" type="Variant" /> + <description> + Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + </description> + </method> + </methods> <members> <member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1"> </member> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 56f3a70631..d0740cf7ca 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -20,6 +20,14 @@ <description> </description> </method> + <method name="get_additional_data"> + <return type="Variant" /> + <param index="0" name="extension_name" type="StringName" /> + <description> + Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + </description> + </method> <method name="get_animation_player"> <return type="AnimationPlayer" /> <param index="0" name="idx" type="int" /> @@ -120,6 +128,15 @@ <description> </description> </method> + <method name="set_additional_data"> + <return type="void" /> + <param index="0" name="extension_name" type="StringName" /> + <param index="1" name="additional_data" type="Variant" /> + <description> + Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + </description> + </method> <method name="set_animations"> <return type="void" /> <param index="0" name="animations" type="GLTFAnimation[]" /> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 6700b6de0a..cb148463a7 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3484,7 +3484,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { height = albedo_texture->get_height(); width = albedo_texture->get_width(); } - orm_image->create(width, height, false, Image::FORMAT_RGBA8); + orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8); if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) { ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS); } @@ -3860,13 +3860,11 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Re if (r_spec_gloss->diffuse_img.is_null()) { return; } - Ref<Image> rm_img; - rm_img.instantiate(); bool has_roughness = false; bool has_metal = false; p_material->set_roughness(1.0f); p_material->set_metallic(1.0f); - rm_img->create(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); + Ref<Image> rm_img = Image::create_empty(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); r_spec_gloss->spec_gloss_img->decompress(); if (r_spec_gloss->diffuse_img.is_valid()) { r_spec_gloss->diffuse_img->decompress(); diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 60192c67e6..ac5665e396 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -87,6 +87,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations); ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations); ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node); + ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFState::get_additional_data); + ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int @@ -358,3 +360,11 @@ String GLTFState::get_base_path() { void GLTFState::set_base_path(String p_base_path) { base_path = p_base_path; } + +Variant GLTFState::get_additional_data(const StringName &p_extension_name) { + return additional_data[p_extension_name]; +} + +void GLTFState::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) { + additional_data[p_extension_name] = p_additional_data; +} diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index afe7e82010..e24017b0fd 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -97,6 +97,7 @@ class GLTFState : public Resource { HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton; HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin; + Dictionary additional_data; protected: static void _bind_methods(); @@ -191,6 +192,9 @@ public: AnimationPlayer *get_animation_player(int idx); + Variant get_additional_data(const StringName &p_extension_name); + void set_additional_data(const StringName &p_extension_name, Variant p_additional_data); + //void set_scene_nodes(RBMap<GLTFNodeIndex, Node *> p_scene_nodes) { // this->scene_nodes = p_scene_nodes; //} diff --git a/modules/gltf/structures/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp index 86280603fa..6fd36f93b7 100644 --- a/modules/gltf/structures/gltf_node.cpp +++ b/modules/gltf/structures/gltf_node.cpp @@ -57,6 +57,8 @@ void GLTFNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children); ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light); ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light); + ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFNode::get_additional_data); + ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFNode::set_additional_data); ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); // int @@ -176,3 +178,11 @@ GLTFLightIndex GLTFNode::get_light() { void GLTFNode::set_light(GLTFLightIndex p_light) { light = p_light; } + +Variant GLTFNode::get_additional_data(const StringName &p_extension_name) { + return additional_data[p_extension_name]; +} + +void GLTFNode::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) { + additional_data[p_extension_name] = p_additional_data; +} diff --git a/modules/gltf/structures/gltf_node.h b/modules/gltf/structures/gltf_node.h index 1a57ea32e2..90a4fa99ed 100644 --- a/modules/gltf/structures/gltf_node.h +++ b/modules/gltf/structures/gltf_node.h @@ -53,6 +53,7 @@ private: Vector3 scale = Vector3(1, 1, 1); Vector<int> children; GLTFLightIndex light = -1; + Dictionary additional_data; protected: static void _bind_methods(); @@ -96,6 +97,9 @@ public: GLTFLightIndex get_light(); void set_light(GLTFLightIndex p_light); + + Variant get_additional_data(const StringName &p_extension_name); + void set_additional_data(const StringName &p_extension_name, Variant p_additional_data); }; #endif // GLTF_NODE_H diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index 6f0bc16a26..457b263e16 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -140,7 +140,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField } } - p_image->create(width, height, false, Image::FORMAT_RGBE9995, imgdata); + p_image->set_data(width, height, false, Image::FORMAT_RGBE9995, imgdata); return OK; } diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index ce20ac9060..d465467cf9 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -99,7 +99,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p fmt = Image::FORMAT_RGB8; } - p_image->create(image_width, image_height, false, fmt, data); + p_image->set_data(image_width, image_height, false, fmt, data); return OK; } diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 8785f327db..9111827c1b 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -251,15 +251,11 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ } for (int i = 0; i < atlas_slices; i++) { - Ref<Image> albedo; - albedo.instantiate(); - albedo->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8); + Ref<Image> albedo = Image::create_empty(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8); albedo->set_as_black(); albedo_images.write[i] = albedo; - Ref<Image> emission; - emission.instantiate(); - emission->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH); + Ref<Image> emission = Image::create_empty(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH); emission->set_as_black(); emission_images.write[i] = emission; } @@ -478,9 +474,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i grid_usage.write[j] = count > 0 ? 255 : 0; } - Ref<Image> img; - img.instantiate(); - img->create(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage); + Ref<Image> img = Image::create_from_data(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage); img->save_png("res://grid_layer_" + itos(1000 + i).substr(1, 3) + ".png"); } #endif @@ -660,9 +654,7 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade #ifdef DEBUG_TEXTURES for (int i = 0; i < atlas_slices; i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->convert(Image::FORMAT_RGBA8); img->save_png("res://5_dilated_" + itos(i) + ".png"); } @@ -778,7 +770,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d panorama_tex->convert(Image::FORMAT_RGBAF); } else { panorama_tex.instantiate(); - panorama_tex->create(8, 8, false, Image::FORMAT_RGBAF); + panorama_tex->initialize_data(8, 8, false, Image::FORMAT_RGBAF); panorama_tex->fill(Color(0, 0, 0, 1)); } @@ -953,13 +945,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices; i++) { Vector<uint8_t> s = rd->texture_get_data(position_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s); img->save_exr("res://1_position_" + itos(i) + ".exr", false); s = rd->texture_get_data(normal_tex, i); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + img->set_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://1_normal_" + itos(i) + ".exr", false); } #endif @@ -1182,9 +1172,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices; i++) { Vector<uint8_t> s = rd->texture_get_data(light_source_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false); } #endif @@ -1415,14 +1403,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d #if 0 for (int i = 0; i < probe_positions.size(); i++) { - Ref<Image> img; - img.instantiate(); - img->create(6, 4, false, Image::FORMAT_RGB8); + Ref<Image> img = Image::create_empty(6, 4, false, Image::FORMAT_RGB8); for (int j = 0; j < 6; j++) { Vector<uint8_t> s = rd->texture_get_data(lightprobe_tex, i * 6 + j); - Ref<Image> img2; - img2.instantiate(); - img2->create(2, 2, false, Image::FORMAT_RGBAF, s); + Ref<Image> img2 = Image::create_from_data(2, 2, false, Image::FORMAT_RGBAF, s); img2->convert(Image::FORMAT_RGB8); img->blit_rect(img2, Rect2i(0, 0, 2, 2), Point2i((j % 3) * 2, (j / 3) * 2)); } @@ -1449,9 +1433,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d if (denoiser.is_valid()) { for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); Ref<Image> denoised = denoiser->denoise_image(img); if (denoised != img) { @@ -1484,9 +1466,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false); } #endif @@ -1640,9 +1620,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://5_blendseams" + itos(i) + ".exr", false); } #endif @@ -1652,9 +1630,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->convert(Image::FORMAT_RGBH); //remove alpha bake_textures.push_back(img); } @@ -1667,9 +1643,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d #ifdef DEBUG_TEXTURES { - Ref<Image> img2; - img2.instantiate(); - img2->create(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data); + Ref<Image> img2 = Image::create_from_data(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data); img2->save_exr("res://6_lightprobes.exr", false); } #endif diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 0d2bea2363..745a8b73f8 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -17,6 +17,8 @@ namespace GodotTools.Export { public partial class ExportPlugin : EditorExportPlugin { + private List<string> _tempFolders = new List<string>(); + public void RegisterExportSettings() { // TODO: These would be better as export preset options, but that doesn't seem to be supported yet @@ -111,62 +113,78 @@ namespace GodotTools.Export string buildConfig = isDebug ? "ExportDebug" : "ExportRelease"; - // TODO: This works for now, as we only implemented support for x86 family desktop so far, but it needs to be fixed - string arch = features.Contains("x86_64") ? "x86_64" : "x86"; - - string ridOS = DetermineRuntimeIdentifierOS(platform); - string ridArch = DetermineRuntimeIdentifierArch(arch); - string runtimeIdentifier = $"{ridOS}-{ridArch}"; - - // Create temporary publish output directory - - string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet", - $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}"); - - if (!Directory.Exists(publishOutputTempDir)) - Directory.CreateDirectory(publishOutputTempDir); - - // Execute dotnet publish - - if (!BuildManager.PublishProjectBlocking(buildConfig, platform, - runtimeIdentifier, publishOutputTempDir)) + var archs = new List<string>(); + if (features.Contains("x86_64")) { - throw new InvalidOperationException("Failed to build project."); + archs.Add("x86_64"); } - - string soExt = ridOS switch + else if (features.Contains("x86_32")) { - OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll", - OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib", - _ => "so" - }; - - if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll")) - // NativeAOT shared library output - && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}"))) + archs.Add("x86_32"); + } + else if (features.Contains("arm64")) { - throw new NotSupportedException( - "Publish succeeded but project assembly not found in the output directory"); + archs.Add("arm64"); } - - // Copy all files from the dotnet publish output directory to - // a data directory next to the Godot output executable. - - string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject()); - - if (Directory.Exists(outputDataDir)) - Directory.Delete(outputDataDir, recursive: true); // Clean first - - Directory.CreateDirectory(outputDataDir); - - foreach (string dir in Directory.GetDirectories(publishOutputTempDir, "*", SearchOption.AllDirectories)) + else if (features.Contains("universal")) { - Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(publishOutputTempDir.Length + 1))); + if (platform == OS.Platforms.MacOS) + { + archs.Add("x86_64"); + archs.Add("arm64"); + } } - foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories)) + foreach (var arch in archs) { - File.Copy(file, Path.Combine(outputDataDir, file.Substring(publishOutputTempDir.Length + 1))); + string ridOS = DetermineRuntimeIdentifierOS(platform); + string ridArch = DetermineRuntimeIdentifierArch(arch); + string runtimeIdentifier = $"{ridOS}-{ridArch}"; + string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}"; + if (platform == OS.Platforms.MacOS) + { + projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName); + } + + // Create temporary publish output directory + + string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet", + $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}"); + + _tempFolders.Add(publishOutputTempDir); + + if (!Directory.Exists(publishOutputTempDir)) + Directory.CreateDirectory(publishOutputTempDir); + + // Execute dotnet publish + + if (!BuildManager.PublishProjectBlocking(buildConfig, platform, + runtimeIdentifier, publishOutputTempDir)) + { + throw new InvalidOperationException("Failed to build project."); + } + + string soExt = ridOS switch + { + OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll", + OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib", + _ => "so" + }; + + if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll")) + // NativeAOT shared library output + && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}"))) + { + throw new NotSupportedException( + "Publish succeeded but project assembly not found in the output directory"); + } + + // Add to the exported project shared object list. + + foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories)) + { + AddSharedObject(file, tags: null, projectDataDirName); + } } } @@ -198,6 +216,12 @@ namespace GodotTools.Export if (Directory.Exists(aotTempDir)) Directory.Delete(aotTempDir, recursive: true); + foreach (string folder in _tempFolders) + { + Directory.Delete(folder, recursive: true); + } + _tempFolders.Clear(); + // TODO: The following is just a workaround until the export plugins can be made to abort with errors // We check for empty as well, because it's set to empty after hot-reloading diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index d29e0d69ab..3be8dd87c0 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -3119,9 +3119,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() { for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) { StringName enum_proxy_cname = E.key; String enum_proxy_name = enum_proxy_cname.operator String(); - if (itype.find_property_by_proxy_name(enum_proxy_cname)) { - // We have several conflicts between enums and PascalCase properties, - // so we append 'Enum' to the enum name in those cases. + if (itype.find_property_by_proxy_name(enum_proxy_name) || itype.find_method_by_proxy_name(enum_proxy_name) || itype.find_signal_by_proxy_name(enum_proxy_name)) { + // In case the enum name conflicts with other PascalCase members, + // we append 'Enum' to the enum name in those cases. + // We have several conflicts between enums and PascalCase properties. enum_proxy_name += "Enum"; enum_proxy_cname = StringName(enum_proxy_name); } @@ -3170,7 +3171,15 @@ bool BindingsGenerator::_populate_object_type_interfaces() { int64_t *value = class_info->constant_map.getptr(StringName(constant_name)); ERR_FAIL_NULL_V(value, false); - ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); + String constant_proxy_name = snake_to_pascal_case(constant_name, true); + + if (itype.find_property_by_proxy_name(constant_proxy_name) || itype.find_method_by_proxy_name(constant_proxy_name) || itype.find_signal_by_proxy_name(constant_proxy_name)) { + // In case the constant name conflicts with other PascalCase members, + // we append 'Constant' to the constant name in those cases. + constant_proxy_name += "Constant"; + } + + ConstantInterface iconstant(constant_name, constant_proxy_name, *value); iconstant.const_doc = nullptr; for (int i = 0; i < itype.class_doc->constants.size(); i++) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 3c75d18943..9b3969d453 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -76,6 +76,11 @@ namespace Godot internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) { + if (@delegate is null) + { + return false; + } + if (@delegate is MulticastDelegate multicastDelegate) { bool someDelegatesSerialized = false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 13070c8033..664b2e0f34 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -292,6 +292,18 @@ namespace Godot } /// <summary> + /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and + /// a <paramref name="point"/> on the plane. + /// </summary> + /// <param name="normal">The normal of the plane, must be normalized.</param> + /// <param name="point">The point on the plane.</param> + public Plane(Vector3 normal, Vector3 point) + { + _normal = normal; + D = _normal.Dot(point); + } + + /// <summary> /// Constructs a <see cref="Plane"/> from the three points, given in clockwise order. /// </summary> /// <param name="v1">The first point.</param> diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index c7e47d2718..185a7e60cf 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -94,138 +94,63 @@ String _get_mono_user_dir() { class _GodotSharpDirs { public: - String res_data_dir; String res_metadata_dir; - String res_config_dir; - String res_temp_dir; - String res_temp_assemblies_base_dir; String res_temp_assemblies_dir; String mono_user_dir; - String mono_logs_dir; - - String api_assemblies_base_dir; String api_assemblies_dir; #ifdef TOOLS_ENABLED - String mono_solutions_dir; String build_logs_dir; - String data_editor_tools_dir; -#else - // Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'. - // Only defined on export templates. Used when exporting assemblies outside of PCKs. - String data_game_assemblies_dir; -#endif - - String data_mono_etc_dir; - String data_mono_lib_dir; - -#ifdef WINDOWS_ENABLED - String data_mono_bin_dir; #endif private: _GodotSharpDirs() { - res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono"); + String res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono"); res_metadata_dir = res_data_dir.path_join("metadata"); - res_config_dir = res_data_dir.path_join("etc").path_join("mono"); // TODO use paths from csproj - res_temp_dir = res_data_dir.path_join("temp"); - res_temp_assemblies_base_dir = res_temp_dir.path_join("bin"); - res_temp_assemblies_dir = res_temp_assemblies_base_dir.path_join(_get_expected_build_config()); - - api_assemblies_base_dir = res_data_dir.path_join("assemblies"); + res_temp_assemblies_dir = res_data_dir.path_join("temp").path_join("bin").path_join(_get_expected_build_config()); #ifdef WEB_ENABLED mono_user_dir = "user://"; #else mono_user_dir = _get_mono_user_dir(); #endif - mono_logs_dir = mono_user_dir.path_join("mono_logs"); - -#ifdef TOOLS_ENABLED - mono_solutions_dir = mono_user_dir.path_join("solutions"); - build_logs_dir = mono_user_dir.path_join("build_logs"); - - String base_path = ProjectSettings::get_singleton()->globalize_path("res://"); -#endif String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir(); + String res_dir = OS::get_singleton()->get_bundle_resource_dir(); #ifdef TOOLS_ENABLED - String data_dir_root = exe_dir.path_join("GodotSharp"); data_editor_tools_dir = data_dir_root.path_join("Tools"); - api_assemblies_base_dir = data_dir_root.path_join("Api"); - - String data_mono_root_dir = data_dir_root.path_join("Mono"); - data_mono_etc_dir = data_mono_root_dir.path_join("etc"); - -#ifdef ANDROID_ENABLED - data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir(); -#else - data_mono_lib_dir = data_mono_root_dir.path_join("lib"); -#endif - -#ifdef WINDOWS_ENABLED - data_mono_bin_dir = data_mono_root_dir.path_join("bin"); -#endif - + String api_assemblies_base_dir = data_dir_root.path_join("Api"); + build_logs_dir = mono_user_dir.path_join("build_logs"); #ifdef MACOS_ENABLED if (!DirAccess::exists(data_editor_tools_dir)) { - data_editor_tools_dir = exe_dir.path_join("../Resources/GodotSharp/Tools"); + data_editor_tools_dir = res_dir.path_join("GodotSharp").path_join("Tools"); } - if (!DirAccess::exists(api_assemblies_base_dir)) { - api_assemblies_base_dir = exe_dir.path_join("../Resources/GodotSharp/Api"); - } - - if (!DirAccess::exists(data_mono_root_dir)) { - data_mono_etc_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/etc"); - data_mono_lib_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/lib"); + api_assemblies_base_dir = res_dir.path_join("GodotSharp").path_join("Api"); } #endif - -#else - + api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config()); +#else // TOOLS_ENABLED + String arch = Engine::get_singleton()->get_architecture_name(); String appname = ProjectSettings::get_singleton()->get("application/config/name"); String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); - String data_dir_root = exe_dir.path_join("data_" + appname_safe); + String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch); if (!DirAccess::exists(data_dir_root)) { - data_dir_root = exe_dir.path_join("data_Godot"); + data_dir_root = exe_dir.path_join("data_Godot_" + arch); } - - String data_mono_root_dir = data_dir_root.path_join("Mono"); - data_mono_etc_dir = data_mono_root_dir.path_join("etc"); - -#ifdef ANDROID_ENABLED - data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir(); -#else - data_mono_lib_dir = data_mono_root_dir.path_join("lib"); - data_game_assemblies_dir = data_dir_root.path_join("Assemblies"); -#endif - -#ifdef WINDOWS_ENABLED - data_mono_bin_dir = data_mono_root_dir.path_join("bin"); -#endif - #ifdef MACOS_ENABLED - if (!DirAccess::exists(data_mono_root_dir)) { - data_mono_etc_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/etc"); - data_mono_lib_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/lib"); + if (!DirAccess::exists(data_dir_root)) { + data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch); } - - if (!DirAccess::exists(data_game_assemblies_dir)) { - data_game_assemblies_dir = exe_dir.path_join("../Resources/GodotSharp/Assemblies"); + if (!DirAccess::exists(data_dir_root)) { + data_dir_root = res_dir.path_join("data_Godot_" + arch); } #endif - -#endif - -#ifdef TOOLS_ENABLED - api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config()); -#else api_assemblies_dir = data_dir_root; #endif } @@ -237,26 +162,10 @@ public: } }; -String get_res_data_dir() { - return _GodotSharpDirs::get_singleton().res_data_dir; -} - String get_res_metadata_dir() { return _GodotSharpDirs::get_singleton().res_metadata_dir; } -String get_res_config_dir() { - return _GodotSharpDirs::get_singleton().res_config_dir; -} - -String get_res_temp_dir() { - return _GodotSharpDirs::get_singleton().res_temp_dir; -} - -String get_res_temp_assemblies_base_dir() { - return _GodotSharpDirs::get_singleton().res_temp_assemblies_base_dir; -} - String get_res_temp_assemblies_dir() { return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir; } @@ -265,23 +174,11 @@ String get_api_assemblies_dir() { return _GodotSharpDirs::get_singleton().api_assemblies_dir; } -String get_api_assemblies_base_dir() { - return _GodotSharpDirs::get_singleton().api_assemblies_base_dir; -} - String get_mono_user_dir() { return _GodotSharpDirs::get_singleton().mono_user_dir; } -String get_mono_logs_dir() { - return _GodotSharpDirs::get_singleton().mono_logs_dir; -} - #ifdef TOOLS_ENABLED -String get_mono_solutions_dir() { - return _GodotSharpDirs::get_singleton().mono_solutions_dir; -} - String get_build_logs_dir() { return _GodotSharpDirs::get_singleton().build_logs_dir; } @@ -289,23 +186,6 @@ String get_build_logs_dir() { String get_data_editor_tools_dir() { return _GodotSharpDirs::get_singleton().data_editor_tools_dir; } -#else -String get_data_game_assemblies_dir() { - return _GodotSharpDirs::get_singleton().data_game_assemblies_dir; -} #endif -String get_data_mono_etc_dir() { - return _GodotSharpDirs::get_singleton().data_mono_etc_dir; -} - -String get_data_mono_lib_dir() { - return _GodotSharpDirs::get_singleton().data_mono_lib_dir; -} - -#ifdef WINDOWS_ENABLED -String get_data_mono_bin_dir() { - return _GodotSharpDirs::get_singleton().data_mono_bin_dir; -} -#endif } // namespace GodotSharpDirs diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h index 03e62ffd82..cdfb8e4787 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -35,34 +35,18 @@ namespace GodotSharpDirs { -String get_res_data_dir(); String get_res_metadata_dir(); -String get_res_config_dir(); -String get_res_temp_dir(); -String get_res_temp_assemblies_base_dir(); String get_res_temp_assemblies_dir(); String get_api_assemblies_dir(); -String get_api_assemblies_base_dir(); String get_mono_user_dir(); -String get_mono_logs_dir(); #ifdef TOOLS_ENABLED -String get_mono_solutions_dir(); String get_build_logs_dir(); - String get_data_editor_tools_dir(); -#else -String get_data_game_assemblies_dir(); #endif -String get_data_mono_etc_dir(); -String get_data_mono_lib_dir(); - -#ifdef WINDOWS_ENABLED -String get_data_mono_bin_dir(); -#endif } // namespace GodotSharpDirs #endif // GODOTSHARP_DIRS_H diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index f989fc45a5..f0d3e329ce 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -266,10 +266,10 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans if (err == OK) { PackedVector3Array faces; - for (int j = 0; j < md.faces.size(); ++j) { - Geometry3D::MeshData::Face face = md.faces[j]; + for (uint32_t j = 0; j < md.faces.size(); ++j) { + const Geometry3D::MeshData::Face &face = md.faces[j]; - for (int k = 2; k < face.indices.size(); ++k) { + for (uint32_t k = 2; k < face.indices.size(); ++k) { faces.push_back(md.vertices[face.indices[0]]); faces.push_back(md.vertices[face.indices[k - 1]]); faces.push_back(md.vertices[face.indices[k]]); @@ -392,10 +392,10 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans if (err == OK) { PackedVector3Array faces; - for (int j = 0; j < md.faces.size(); ++j) { - Geometry3D::MeshData::Face face = md.faces[j]; + for (uint32_t j = 0; j < md.faces.size(); ++j) { + const Geometry3D::MeshData::Face &face = md.faces[j]; - for (int k = 2; k < face.indices.size(); ++k) { + for (uint32_t k = 2; k < face.indices.size(); ++k) { faces.push_back(md.vertices[face.indices[0]]); faces.push_back(md.vertices[face.indices[k - 1]]); faces.push_back(md.vertices[face.indices[k]]); diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 23d60c4866..8c785d7591 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -176,9 +176,7 @@ Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradi int width = p_image->get_width(); int height = p_image->get_height(); - Ref<Image> new_image; - new_image.instantiate(); - new_image->create(width, height, false, Image::FORMAT_RGBA8); + Ref<Image> new_image = Image::create_empty(width, height, false, Image::FORMAT_RGBA8); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { diff --git a/modules/squish/image_decompress_squish.cpp b/modules/squish/image_decompress_squish.cpp index 3a810e5259..2abd9a0c69 100644 --- a/modules/squish/image_decompress_squish.cpp +++ b/modules/squish/image_decompress_squish.cpp @@ -70,7 +70,7 @@ void image_decompress_squish(Image *p_image) { h >>= 1; } - p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); if (p_image->get_format() == Image::FORMAT_DXT5_RA_AS_RG) { p_image->convert_ra_rgba8_to_rg(); diff --git a/modules/svg/SCsub b/modules/svg/SCsub index 93262f4f87..ae3e1bdedb 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -11,6 +11,16 @@ thirdparty_obj = [] thirdparty_dir = "#thirdparty/thorvg/" thirdparty_sources = [ + "src/lib/sw_engine/tvgSwFill.cpp", + "src/lib/sw_engine/tvgSwImage.cpp", + "src/lib/sw_engine/tvgSwMath.cpp", + "src/lib/sw_engine/tvgSwMemPool.cpp", + "src/lib/sw_engine/tvgSwRaster.cpp", + "src/lib/sw_engine/tvgSwRenderer.cpp", + "src/lib/sw_engine/tvgSwRle.cpp", + "src/lib/sw_engine/tvgSwShape.cpp", + "src/lib/sw_engine/tvgSwStroke.cpp", + "src/lib/tvgAccessor.cpp", "src/lib/tvgBezier.cpp", "src/lib/tvgCanvas.cpp", "src/lib/tvgFill.cpp", @@ -28,27 +38,18 @@ thirdparty_sources = [ "src/lib/tvgShape.cpp", "src/lib/tvgSwCanvas.cpp", "src/lib/tvgTaskScheduler.cpp", + "src/loaders/external_png/tvgPngLoader.cpp", + "src/loaders/jpg/tvgJpgd.cpp", + "src/loaders/jpg/tvgJpgLoader.cpp", "src/loaders/raw/tvgRawLoader.cpp", - "src/loaders/svg/tvgXmlParser.cpp", - "src/loaders/svg/tvgSvgUtil.cpp", - "src/loaders/svg/tvgSvgSceneBuilder.cpp", - "src/loaders/svg/tvgSvgPath.cpp", - "src/loaders/svg/tvgSvgLoader.cpp", "src/loaders/svg/tvgSvgCssStyle.cpp", + "src/loaders/svg/tvgSvgLoader.cpp", + "src/loaders/svg/tvgSvgPath.cpp", + "src/loaders/svg/tvgSvgSceneBuilder.cpp", + "src/loaders/svg/tvgSvgUtil.cpp", + "src/loaders/svg/tvgXmlParser.cpp", "src/loaders/tvg/tvgTvgBinInterpreter.cpp", "src/loaders/tvg/tvgTvgLoader.cpp", - "src/loaders/jpg/tvgJpgLoader.cpp", - "src/loaders/jpg/tvgJpgd.cpp", - "src/loaders/external_png/tvgPngLoader.cpp", - "src/lib/sw_engine/tvgSwFill.cpp", - "src/lib/sw_engine/tvgSwImage.cpp", - "src/lib/sw_engine/tvgSwMath.cpp", - "src/lib/sw_engine/tvgSwMemPool.cpp", - "src/lib/sw_engine/tvgSwRaster.cpp", - "src/lib/sw_engine/tvgSwRenderer.cpp", - "src/lib/sw_engine/tvgSwRle.cpp", - "src/lib/sw_engine/tvgSwShape.cpp", - "src/lib/sw_engine/tvgSwStroke.cpp", "src/savers/tvg/tvgTvgSaver.cpp", ] @@ -62,14 +63,18 @@ env_thirdparty.Prepend( CPPPATH=[ thirdparty_dir + "src/lib", thirdparty_dir + "src/lib/sw_engine", + thirdparty_dir + "src/loaders/external_png", + thirdparty_dir + "src/loaders/jpg", thirdparty_dir + "src/loaders/raw", thirdparty_dir + "src/loaders/svg", - thirdparty_dir + "src/loaders/jpg", - thirdparty_dir + "src/loaders/png", thirdparty_dir + "src/loaders/tvg", thirdparty_dir + "src/savers/tvg", ] ) +# Also requires libpng headers +if env["builtin_libpng"]: + env_thirdparty.Prepend(CPPPATH=["#thirdparty/libpng"]) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.modules_sources += thirdparty_obj diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index f43f2784c7..b8c412a201 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -135,7 +135,7 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin res = sw_canvas->clear(true); memfree(buffer); - p_image->create(width, height, false, Image::FORMAT_RGBA8, image); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, image); } void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const { diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index c9b0fa7dd5..eb398f77c7 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2494,9 +2494,7 @@ void TextServerAdvanced::_font_set_texture_image(const RID &p_font_rid, const Ve tex.texture_h = p_image->get_height(); tex.format = p_image->get_format(); - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2515,11 +2513,7 @@ Ref<Image> TextServerAdvanced::_font_get_texture_image(const RID &p_font_rid, co ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); - - return img; + return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); } void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { @@ -2853,9 +2847,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2901,9 +2893,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -3248,9 +3238,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -3340,9 +3328,7 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 518c877baa..13c997c981 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -1588,9 +1588,7 @@ void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Ve tex.texture_h = p_image->get_height(); tex.format = p_image->get_format(); - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -1609,11 +1607,7 @@ Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, co ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); - - return img; + return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); } void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { @@ -1933,9 +1927,7 @@ RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -1981,9 +1973,7 @@ Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, co if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2310,9 +2300,7 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2402,9 +2390,7 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index aed95294e7..a6fc650414 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -246,7 +246,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff } } - p_image->create(width, height, false, Image::FORMAT_RGBA8, image_data); + p_image->initialize_data(width, height, false, Image::FORMAT_RGBA8, image_data); return OK; } diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 1284412cd8..69fb079970 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -336,9 +336,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { size.x = w; size.y = h; - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(w, h, false, Image::FORMAT_RGBA8); texture->set_image(img); } else { diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 5c43bfc8b7..c5aa110fe6 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -280,7 +280,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitF } } - p_image->create(exr_image.width, exr_image.height, false, format, imgdata); + p_image->set_data(exr_image.width, exr_image.height, false, format, imgdata); FreeEXRHeader(&exr_header); FreeEXRImage(&exr_image); diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp index 8657a98853..049c1c3a32 100644 --- a/modules/webp/webp_common.cpp +++ b/modules/webp/webp_common.cpp @@ -183,7 +183,7 @@ Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image."); - p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image); + p_image->set_data(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image); return OK; } diff --git a/modules/websocket/editor/editor_debugger_server_websocket.cpp b/modules/websocket/editor/editor_debugger_server_websocket.cpp index 1c4ebd0f55..48bfbaa14e 100644 --- a/modules/websocket/editor/editor_debugger_server_websocket.cpp +++ b/modules/websocket/editor/editor_debugger_server_websocket.cpp @@ -40,7 +40,13 @@ void EditorDebuggerServerWebSocket::poll() { if (pending_peer.is_null() && tcp_server->is_connection_available()) { - Ref<WebSocketPeer> peer; + Ref<WebSocketPeer> peer = Ref<WebSocketPeer>(WebSocketPeer::create()); + ERR_FAIL_COND(peer.is_null()); // Bug. + + Vector<String> ws_protocols; + ws_protocols.push_back("binary"); // Compatibility for emscripten TCP-to-WebSocket. + peer->set_supported_protocols(ws_protocols); + Error err = peer->accept_stream(tcp_server->take_connection()); if (err == OK) { pending_timer = OS::get_singleton()->get_ticks_msec(); diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 5f3cb76852..3bd132bc73 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -120,7 +120,7 @@ Error EMWSPeer::_send(const uint8_t *p_buffer, int p_buffer_size, bool p_binary) } Error EMWSPeer::send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) { - return _send(p_buffer, p_buffer_size, p_mode == WRITE_MODE_TEXT); + return _send(p_buffer, p_buffer_size, p_mode == WRITE_MODE_BINARY); } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 691b031bbd..c55a651ab0 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -31,11 +31,14 @@ #include "register_types.h" #include "core/config/project_settings.h" +#include "core/debugger/engine_debugger.h" #include "core/error/error_macros.h" #include "websocket_multiplayer_peer.h" #include "websocket_peer.h" +#include "remote_debugger_peer_websocket.h" + #ifdef WEB_ENABLED #include "emws_peer.h" #else @@ -55,7 +58,7 @@ static void _editor_init_callback() { #endif void initialize_websocket_module(ModuleInitializationLevel p_level) { - if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { + if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) { #ifdef WEB_ENABLED EMWSPeer::initialize(); #else @@ -64,6 +67,9 @@ void initialize_websocket_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(WebSocketMultiplayerPeer); ClassDB::register_custom_instance_class<WebSocketPeer>(); + + EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create); + EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create); } #ifdef TOOLS_ENABLED @@ -74,7 +80,7 @@ void initialize_websocket_module(ModuleInitializationLevel p_level) { } void uninitialize_websocket_module(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + if (p_level != MODULE_INITIALIZATION_LEVEL_CORE) { return; } #ifndef WEB_ENABLED diff --git a/modules/websocket/remote_debugger_peer_websocket.cpp b/modules/websocket/remote_debugger_peer_websocket.cpp index fc4f51b59b..58adb76208 100644 --- a/modules/websocket/remote_debugger_peer_websocket.cpp +++ b/modules/websocket/remote_debugger_peer_websocket.cpp @@ -33,12 +33,13 @@ #include "core/config/project_settings.h" Error RemoteDebuggerPeerWebSocket::connect_to_host(const String &p_uri) { + ws_peer = Ref<WebSocketPeer>(WebSocketPeer::create()); + ERR_FAIL_COND_V(ws_peer.is_null(), ERR_BUG); + Vector<String> protocols; protocols.push_back("binary"); // Compatibility for emscripten TCP-to-WebSocket. - ws_peer = Ref<WebSocketPeer>(WebSocketPeer::create()); ws_peer->set_supported_protocols(protocols); - ws_peer->set_max_queued_packets(max_queued_messages); ws_peer->set_inbound_buffer_size((1 << 23) - 1); ws_peer->set_outbound_buffer_size((1 << 23) - 1); @@ -81,6 +82,7 @@ void RemoteDebuggerPeerWebSocket::poll() { } int RemoteDebuggerPeerWebSocket::get_max_message_size() const { + ERR_FAIL_COND_V(ws_peer.is_null(), 0); return ws_peer->get_max_packet_size(); } diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 00b96ec587..c314ebd049 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -61,6 +61,7 @@ void WebSocketMultiplayerPeer::_clear() { tls_key.unref(); if (current_packet.data != nullptr) { memfree(current_packet.data); + current_packet.data = nullptr; } for (Packet &E : incoming_packets) { diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 4c8e661f67..4930b178ec 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -104,6 +104,8 @@ void WSLPeer::Resolver::try_next_candidate(Ref<StreamPeerTCP> &p_tcp) { p_tcp->set_no_delay(true); ip_candidates.clear(); return; + } else if (status == StreamPeerTCP::STATUS_CONNECTING) { + return; // Keep connecting. } else { p_tcp->disconnect_from_host(); } diff --git a/modules/zip/SCsub b/modules/zip/SCsub new file mode 100644 index 0000000000..b7710123fd --- /dev/null +++ b/modules/zip/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_zip = env_modules.Clone() + +# Module files +env_zip.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/zip/config.py b/modules/zip/config.py new file mode 100644 index 0000000000..96cd2fc5bd --- /dev/null +++ b/modules/zip/config.py @@ -0,0 +1,17 @@ +def can_build(env, platform): + return env["minizip"] + + +def configure(env): + pass + + +def get_doc_classes(): + return [ + "ZIPReader", + "ZIPPacker", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/zip/doc_classes/ZIPPacker.xml b/modules/zip/doc_classes/ZIPPacker.xml new file mode 100644 index 0000000000..95d7ef50f9 --- /dev/null +++ b/modules/zip/doc_classes/ZIPPacker.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ZIPPacker" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Allows the creation of zip files. + </brief_description> + <description> + This class implements a writer that allows storing the multiple blobs in a zip archive. + [codeblock] + func write_zip_file(): + var writer := ZIPPacker.new() + var err := writer.open("user://archive.zip") + if err != OK: + return err + writer.start_file("hello.txt") + writer.write_file("Hello World".to_utf8_buffer()) + writer.close_file() + + writer.close() + return OK + [/codeblock] + </description> + <tutorials> + </tutorials> + <methods> + <method name="close"> + <return type="int" enum="Error" /> + <description> + Closes the underlying resources used by this instance. + </description> + </method> + <method name="close_file"> + <return type="int" enum="Error" /> + <description> + Stops writing to a file within the archive. + It will fail if there is no open file. + </description> + </method> + <method name="open"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <param index="1" name="append" type="int" enum="ZIPPacker.ZipAppend" default="0" /> + <description> + Opens a zip file for writing at the given path using the specified write mode. + This must be called before everything else. + </description> + </method> + <method name="start_file"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <description> + Starts writing to a file within the archive. Only one file can be written at the same time. + Must be called after [method open]. + </description> + </method> + <method name="write_file"> + <return type="int" enum="Error" /> + <param index="0" name="data" type="PackedByteArray" /> + <description> + Write the given [param data] to the file. + Needs to be called after [method start_file]. + </description> + </method> + </methods> + <constants> + <constant name="APPEND_CREATE" value="0" enum="ZipAppend"> + </constant> + <constant name="APPEND_CREATEAFTER" value="1" enum="ZipAppend"> + </constant> + <constant name="APPEND_ADDINZIP" value="2" enum="ZipAppend"> + </constant> + </constants> +</class> diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml new file mode 100644 index 0000000000..717116a531 --- /dev/null +++ b/modules/zip/doc_classes/ZIPReader.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ZIPReader" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Allows reading the content of a zip file. + </brief_description> + <description> + This class implements a reader that can extract the content of individual files inside a zip archive. + [codeblock] + func read_zip_file(): + var reader := ZIPReader.new() + var err := reader.open("user://archive.zip") + if err == OK: + return PackedByteArray() + var res := reader.read_file("hello.txt") + reader.close() + return res + [/codeblock] + </description> + <tutorials> + </tutorials> + <methods> + <method name="close"> + <return type="int" enum="Error" /> + <description> + Closes the underlying resources used by this instance. + </description> + </method> + <method name="get_files"> + <return type="PackedStringArray" /> + <description> + Returns the list of names of all files in the loaded archive. + Must be called after [method open]. + </description> + </method> + <method name="open"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <description> + Opens the zip archive at the given [param path] and reads its file index. + </description> + </method> + <method name="read_file"> + <return type="PackedByteArray" /> + <param index="0" name="path" type="String" /> + <param index="1" name="case_sensitive" type="bool" default="true" /> + <description> + Loads the whole content of a file in the loaded zip archive into memory and returns it. + Must be called after [method open]. + </description> + </method> + </methods> +</class> diff --git a/modules/zip/register_types.cpp b/modules/zip/register_types.cpp new file mode 100644 index 0000000000..20fb484cfe --- /dev/null +++ b/modules/zip/register_types.cpp @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* register_types.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 "register_types.h" + +#include "core/object/class_db.h" +#include "zip_packer.h" +#include "zip_reader.h" + +void initialize_zip_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + GDREGISTER_CLASS(ZIPPacker); + GDREGISTER_CLASS(ZIPReader); +} + +void uninitialize_zip_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} diff --git a/modules/zip/register_types.h b/modules/zip/register_types.h new file mode 100644 index 0000000000..2640be12b8 --- /dev/null +++ b/modules/zip/register_types.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* register_types.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 ZIP_REGISTER_TYPES_H +#define ZIP_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_zip_module(ModuleInitializationLevel p_level); +void uninitialize_zip_module(ModuleInitializationLevel p_level); + +#endif // ZIP_REGISTER_TYPES_H diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp new file mode 100644 index 0000000000..c37fc0945e --- /dev/null +++ b/modules/zip/zip_packer.cpp @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* zip_packer.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 "zip_packer.h" + +#include "core/io/zip_io.h" +#include "core/os/os.h" + +Error ZIPPacker::open(String p_path, ZipAppend p_append) { + if (fa.is_valid()) { + close(); + } + + zlib_filefunc_def io = zipio_create_io(&fa); + zf = zipOpen2(p_path.utf8().get_data(), p_append, NULL, &io); + return zf != NULL ? OK : FAILED; +} + +Error ZIPPacker::close() { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker cannot be closed because it is not open."); + + return zipClose(zf, NULL) == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::start_file(String p_path) { + ERR_FAIL_COND_V_MSG(zf != NULL, FAILED, "ZIPPacker is already in use."); + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use."); + + zip_fileinfo zipfi; + + OS::DateTime time = OS::get_singleton()->get_datetime(); + + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = time.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = time.month - 1; + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = time.year; + zipfi.dosDate = 0; + zipfi.external_fa = 0; + zipfi.internal_fa = 0; + + int ret = zipOpenNewFileInZip(zf, p_path.utf8().get_data(), &zipfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + return ret == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::write_file(Vector<uint8_t> p_data) { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use."); + + return zipWriteInFileInZip(zf, p_data.ptr(), p_data.size()) == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::close_file() { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use."); + + Error err = zipCloseFileInZip(zf) == ZIP_OK ? OK : FAILED; + if (err == OK) { + zf = NULL; + } + return err; +} + +void ZIPPacker::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path", "append"), &ZIPPacker::open, DEFVAL(Variant(APPEND_CREATE))); + ClassDB::bind_method(D_METHOD("start_file", "path"), &ZIPPacker::start_file); + ClassDB::bind_method(D_METHOD("write_file", "data"), &ZIPPacker::write_file); + ClassDB::bind_method(D_METHOD("close_file"), &ZIPPacker::close_file); + ClassDB::bind_method(D_METHOD("close"), &ZIPPacker::close); + + BIND_ENUM_CONSTANT(APPEND_CREATE); + BIND_ENUM_CONSTANT(APPEND_CREATEAFTER); + BIND_ENUM_CONSTANT(APPEND_ADDINZIP); +} + +ZIPPacker::ZIPPacker() {} + +ZIPPacker::~ZIPPacker() { + if (fa.is_valid()) { + close(); + } +} diff --git a/modules/zip/zip_packer.h b/modules/zip/zip_packer.h new file mode 100644 index 0000000000..23e96b5ad2 --- /dev/null +++ b/modules/zip/zip_packer.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* zip_packer.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 ZIP_PACKER_H +#define ZIP_PACKER_H + +#include "core/io/file_access.h" +#include "core/object/ref_counted.h" + +#include "thirdparty/minizip/zip.h" + +class ZIPPacker : public RefCounted { + GDCLASS(ZIPPacker, RefCounted); + + Ref<FileAccess> fa; + zipFile zf; + +protected: + static void _bind_methods(); + +public: + enum ZipAppend { + APPEND_CREATE = 0, + APPEND_CREATEAFTER = 1, + APPEND_ADDINZIP = 2, + }; + + Error open(String p_path, ZipAppend p_append); + Error close(); + + Error start_file(String p_path); + Error write_file(Vector<uint8_t> p_data); + Error close_file(); + + ZIPPacker(); + ~ZIPPacker(); +}; + +VARIANT_ENUM_CAST(ZIPPacker::ZipAppend) + +#endif // ZIP_PACKER_H diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp new file mode 100644 index 0000000000..f35b947cef --- /dev/null +++ b/modules/zip/zip_reader.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* zip_reader.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 "zip_reader.h" + +#include "core/error/error_macros.h" +#include "core/io/zip_io.h" + +Error ZIPReader::open(String p_path) { + if (fa.is_valid()) { + close(); + } + + zlib_filefunc_def io = zipio_create_io(&fa); + uzf = unzOpen2(p_path.utf8().get_data(), &io); + return uzf != NULL ? OK : FAILED; +} + +Error ZIPReader::close() { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPReader cannot be closed because it is not open."); + + return unzClose(uzf) == UNZ_OK ? OK : FAILED; +} + +PackedStringArray ZIPReader::get_files() { + ERR_FAIL_COND_V_MSG(fa.is_null(), PackedStringArray(), "ZIPReader must be opened before use."); + + List<String> s; + + if (unzGoToFirstFile(uzf) != UNZ_OK) { + return PackedStringArray(); + } + + do { + unz_file_info64 file_info; + char filename[256]; // Note filename is a path ! + int err = unzGetCurrentFileInfo64(uzf, &file_info, filename, sizeof(filename), NULL, 0, NULL, 0); + if (err == UNZ_OK) { + s.push_back(filename); + } else { + // Assume filename buffer was too small + char *long_filename_buff = (char *)memalloc(file_info.size_filename); + int err2 = unzGetCurrentFileInfo64(uzf, NULL, long_filename_buff, sizeof(long_filename_buff), NULL, 0, NULL, 0); + if (err2 == UNZ_OK) { + s.push_back(long_filename_buff); + memfree(long_filename_buff); + } + } + } while (unzGoToNextFile(uzf) == UNZ_OK); + + PackedStringArray arr; + arr.resize(s.size()); + int idx = 0; + for (const List<String>::Element *E = s.front(); E; E = E->next()) { + arr.set(idx++, E->get()); + } + return arr; +} + +PackedByteArray ZIPReader::read_file(String p_path, bool p_case_sensitive) { + ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "ZIPReader must be opened before use."); + + int cs = p_case_sensitive ? 1 : 2; + if (unzLocateFile(uzf, p_path.utf8().get_data(), cs) != UNZ_OK) { + ERR_FAIL_V_MSG(PackedByteArray(), "File does not exist in zip archive: " + p_path); + } + if (unzOpenCurrentFile(uzf) != UNZ_OK) { + ERR_FAIL_V_MSG(PackedByteArray(), "Could not open file within zip archive."); + } + + unz_file_info info; + unzGetCurrentFileInfo(uzf, &info, NULL, 0, NULL, 0, NULL, 0); + PackedByteArray data; + data.resize(info.uncompressed_size); + + uint8_t *w = data.ptrw(); + unzReadCurrentFile(uzf, &w[0], info.uncompressed_size); + + unzCloseCurrentFile(uzf); + return data; +} + +ZIPReader::ZIPReader() {} + +ZIPReader::~ZIPReader() { + if (fa.is_valid()) { + close(); + } +} + +void ZIPReader::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path"), &ZIPReader::open); + ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close); + ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files); + ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true))); +} diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h new file mode 100644 index 0000000000..fbc2fc0409 --- /dev/null +++ b/modules/zip/zip_reader.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* zip_reader.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 ZIP_READER_H +#define ZIP_READER_H + +#include "core/io/file_access.h" +#include "core/object/ref_counted.h" + +#include "thirdparty/minizip/unzip.h" + +class ZIPReader : public RefCounted { + GDCLASS(ZIPReader, RefCounted) + + Ref<FileAccess> fa; + unzFile uzf; + +protected: + static void _bind_methods(); + +public: + Error open(String p_path); + Error close(); + + PackedStringArray get_files(); + PackedByteArray read_file(String p_path, bool p_case_sensitive); + + ZIPReader(); + ~ZIPReader(); +}; + +#endif // ZIP_READER_H diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index ef3b79b630..6426f95b42 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1551,7 +1551,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R print_verbose("Creating splash background color image."); splash_bg_color_image.instantiate(); - splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); + splash_bg_color_image->initialize_data(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); splash_bg_color_image->fill(bg_color); String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); @@ -2901,20 +2901,22 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP _load_image_data(splash_bg_color_image, data); } - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - if (file == launcher_icons[i].export_path) { - _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + if (file.ends_with(".png") && file.contains("mipmap")) { + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + if (file == launcher_icons[i].export_path) { + _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + } } - } - if (foreground.is_valid() && !foreground->is_empty()) { - if (file == launcher_adaptive_icon_foregrounds[i].export_path) { - _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + if (foreground.is_valid() && !foreground->is_empty()) { + if (file == launcher_adaptive_icon_foregrounds[i].export_path) { + _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + } } - } - if (background.is_valid() && !background->is_empty()) { - if (file == launcher_adaptive_icon_backgrounds[i].export_path) { - _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + if (background.is_valid() && !background->is_empty()) { + if (file == launcher_adaptive_icon_backgrounds[i].export_path) { + _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + } } } } diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 74a57dc614..83b2012d3e 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -740,8 +740,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp img->resize(info.width, info.width / aspect_ratio); } } - Ref<Image> new_img = memnew(Image); - new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> new_img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); new_img->fill(boot_bg_color); _blend_and_rotate(new_img, img, false); err = new_img->save_png(p_dest_dir + info.export_name); @@ -755,8 +754,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp } } else { // Generate loading screen from the splash screen - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); img->fill(boot_bg_color); Ref<Image> img_bs; diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index e0963f42ce..e78467beff 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -1729,6 +1729,18 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) { usleep(10000); } + + // Keep rendering context window size in sync +#if defined(VULKAN_ENABLED) + if (context_vulkan) { + context_vulkan->window_resize(p_window, xwa.width, xwa.height); + } +#endif +#if defined(GLES3_ENABLED) + if (gl_manager) { + gl_manager->window_resize(p_window, xwa.width, xwa.height); + } +#endif } Size2i DisplayServerX11::window_get_size(WindowID p_window) const { diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 070830c486..f5f64f9663 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -31,6 +31,8 @@ #include "export_plugin.h" #include "codesign.h" +#include "lipo.h" +#include "macho.h" #include "core/string/translation.h" #include "editor/editor_node.h" @@ -754,6 +756,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres if (extensions_to_sign.is_empty()) { extensions_to_sign.push_back("dylib"); extensions_to_sign.push_back("framework"); + extensions_to_sign.push_back(""); } Error dir_access_error; @@ -778,6 +781,10 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres if (code_sign_error != OK) { return code_sign_error; } + if (is_executable(current_file_path)) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(current_file_path, 0755); + } } else if (dir_access->current_is_dir()) { Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) }; if (code_sign_error != OK) { @@ -799,6 +806,14 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access const String &p_in_app_path, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, bool p_should_error_on_non_code_sign) { + static Vector<String> extensions_to_sign; + + if (extensions_to_sign.is_empty()) { + extensions_to_sign.push_back("dylib"); + extensions_to_sign.push_back("framework"); + extensions_to_sign.push_back(""); + } + Error err{ OK }; if (dir_access->dir_exists(p_src_path)) { #ifndef UNIX_ENABLED @@ -818,7 +833,13 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access // If it is a directory, find and sign all dynamic libraries. err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign); } else { - err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); + if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) { + err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); + } + if (is_executable(p_in_app_path)) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(p_in_app_path, 0755); + } } } return err; @@ -877,6 +898,17 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str return OK; } +bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const { + Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path)); + uint16_t magic = fb->get_16(); + return (magic == 0x2123); +} + +bool EditorExportPlatformMacOS::is_executable(const String &p_path) const { + return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path); +} + Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); if (f.is_null()) { @@ -1158,11 +1190,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p // Now process our template. bool found_binary = false; - Vector<String> dylibs_found; while (ret == UNZ_OK && err == OK) { - bool is_execute = false; - // Get filename. unz_file_info info; char fname[16384]; @@ -1219,7 +1248,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p continue; // skip } found_binary = true; - is_execute = true; file = "Contents/MacOS/" + pkg_name; } @@ -1251,25 +1279,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } if (data.size() > 0) { - if (file.find("/data.mono.macos.release_debug." + architecture + "/") != -1) { - if (!p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.macos.release_debug." + architecture + "/", "/GodotSharp/"); - } - if (file.find("/data.mono.macos.release." + architecture + "/") != -1) { - if (p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.macos.release." + architecture + "/", "/GodotSharp/"); - } - - if (file.ends_with(".dylib")) { - dylibs_found.push_back(file); - } - print_verbose("ADDING: " + file + " size: " + itos(data.size())); // Write it into our application bundle. @@ -1285,7 +1294,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p if (f.is_valid()) { f->store_buffer(data.ptr(), data.size()); f.unref(); - if (is_execute) { + if (is_executable(file)) { // chmod with 0755 if the file is executable. FileAccess::set_unix_permissions(file, 0755); } @@ -1324,12 +1333,35 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p return ERR_SKIP; } + // See if we can code sign our new package. + bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0); + bool ad_hoc = false; + int codesign_tool = p_preset->get("codesign/codesign"); + switch (codesign_tool) { + case 1: { // built-in ad-hoc + ad_hoc = true; + } break; + case 2: { // "rcodesign" + ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); + } break; +#ifdef MACOS_ENABLED + case 3: { // "codesign" + ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-"); + } break; +#endif + default: { + }; + } + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; Vector<SharedObject> shared_objects; err = save_pack(p_preset, p_debug, pack_path, &shared_objects); - // See if we can code sign our new package. - bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0); + bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation"); + if (!shared_objects.is_empty() && sign_enabled && ad_hoc && !lib_validation) { + add_message(EXPORT_MESSAGE_INFO, TTR("Entitlements Modified"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries.")); + lib_validation = true; + } String ent_path = p_preset->get("codesign/entitlements/custom_file"); String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements"); @@ -1365,7 +1397,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { + if (lib_validation) { ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); ent_f->store_line("<true/>"); } @@ -1495,32 +1527,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - bool ad_hoc = false; - int codesign_tool = p_preset->get("codesign/codesign"); - switch (codesign_tool) { - case 1: { // built-in ad-hoc - ad_hoc = true; - } break; - case 2: { // "rcodesign" - ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); - } break; -#ifdef MACOS_ENABLED - case 3: { // "codesign" - ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-"); - } break; -#endif - default: { - }; - } - - if (err == OK) { - bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation"); - if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries.")); - err = ERR_CANT_CREATE; - } - } - if (err == OK) { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { @@ -1529,8 +1535,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(); err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true); } else { - String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target).path_join(src_path.get_file()); - err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, false); + String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target); + tmp_app_dir->make_dir_recursive(path_in_app); + err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, false); } if (err != OK) { break; @@ -1546,14 +1553,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - if (sign_enabled) { - for (int i = 0; i < dylibs_found.size(); i++) { - if (err == OK) { - err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path, false); - } - } - } - if (err == OK && sign_enabled) { if (ep.step(TTR("Code signing bundle"), 2)) { return ERR_SKIP; @@ -1683,8 +1682,6 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri } else if (da->current_is_dir()) { _zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name); } else { - bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers") || f.ends_with(".command"); - OS::DateTime dt = OS::get_singleton()->get_datetime(); zip_fileinfo zipfi; @@ -1698,7 +1695,7 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri // 0100000: regular file type // 0000755: permissions rwxr-xr-x // 0000644: permissions rw-r--r-- - uint32_t _mode = (is_executable ? 0100755 : 0100644); + uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644); zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); zipfi.internal_fa = 0; diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h index 87790129d3..b6ad587caa 100644 --- a/platform/macos/export/export_plugin.h +++ b/platform/macos/export/export_plugin.h @@ -97,6 +97,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { return true; } + bool is_shbang(const String &p_path) const; protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; @@ -108,6 +109,7 @@ public: virtual String get_os_name() const override { return "macOS"; } virtual Ref<Texture2D> get_logo() const override { return logo; } + virtual bool is_executable(const String &p_path) const override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { List<String> list; if (use_dmg()) { diff --git a/platform/macos/export/lipo.cpp b/platform/macos/export/lipo.cpp index 82baf18c52..76d4eee418 100644 --- a/platform/macos/export/lipo.cpp +++ b/platform/macos/export/lipo.cpp @@ -28,12 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "modules/modules_enabled.gen.h" // For regex. - #include "lipo.h" -#ifdef MODULE_REGEX_ENABLED - bool LipO::is_lipo(const String &p_path) { Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path)); @@ -232,5 +228,3 @@ void LipO::close() { LipO::~LipO() { close(); } - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/macos/export/lipo.h b/platform/macos/export/lipo.h index 6378f9899c..bd8b7f6f2c 100644 --- a/platform/macos/export/lipo.h +++ b/platform/macos/export/lipo.h @@ -35,12 +35,9 @@ #include "core/io/file_access.h" #include "core/object/ref_counted.h" -#include "modules/modules_enabled.gen.h" // For regex. #include "macho.h" -#ifdef MODULE_REGEX_ENABLED - class LipO : public RefCounted { struct FatArch { uint32_t cputype; @@ -71,6 +68,4 @@ public: ~LipO(); }; -#endif // MODULE_REGEX_ENABLED - #endif // MACOS_LIPO_H diff --git a/platform/macos/export/macho.cpp b/platform/macos/export/macho.cpp index 97289e408d..642d99e098 100644 --- a/platform/macos/export/macho.cpp +++ b/platform/macos/export/macho.cpp @@ -28,12 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "modules/modules_enabled.gen.h" // For regex. - #include "macho.h" -#ifdef MODULE_REGEX_ENABLED - uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) { uint32_t salign = p_max; if (p_vmaddr != 0) { @@ -544,5 +540,3 @@ bool MachO::set_signature_size(uint64_t p_size) { } return true; } - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/macos/export/macho.h b/platform/macos/export/macho.h index 0d42a7013f..0c954e66b1 100644 --- a/platform/macos/export/macho.h +++ b/platform/macos/export/macho.h @@ -37,9 +37,6 @@ #include "core/crypto/crypto_core.h" #include "core/io/file_access.h" #include "core/object/ref_counted.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#ifdef MODULE_REGEX_ENABLED class MachO : public RefCounted { struct MachHeader { @@ -210,6 +207,4 @@ public: bool set_signature_size(uint64_t p_size); }; -#endif // MODULE_REGEX_ENABLED - #endif // MACOS_MACHO_H diff --git a/platform/macos/export/plist.cpp b/platform/macos/export/plist.cpp index 36de9dd34b..cad014e65b 100644 --- a/platform/macos/export/plist.cpp +++ b/platform/macos/export/plist.cpp @@ -28,12 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "modules/modules_enabled.gen.h" // For regex. - #include "plist.h" -#ifdef MODULE_REGEX_ENABLED - Ref<PListNode> PListNode::new_array() { Ref<PListNode> node = memnew(PListNode()); ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); @@ -566,5 +562,3 @@ String PList::save_text() const { Ref<PListNode> PList::get_root() { return root; } - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/macos/export/plist.h b/platform/macos/export/plist.h index b3c51a9635..97331a3629 100644 --- a/platform/macos/export/plist.h +++ b/platform/macos/export/plist.h @@ -35,9 +35,6 @@ #include "core/crypto/crypto_core.h" #include "core/io/file_access.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#ifdef MODULE_REGEX_ENABLED class PListNode; @@ -111,6 +108,4 @@ public: ~PListNode() {} }; -#endif // MODULE_REGEX_ENABLED - #endif // MACOS_PLIST_H diff --git a/platform/web/SCsub b/platform/web/SCsub index 013b734be2..cb00fa9f5b 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -2,6 +2,20 @@ Import("env") +# The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run" +if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS: + from serve import serve + import os + + port = os.environ.get("GODOT_WEB_TEST_PORT", 8060) + try: + port = int(port) + except Exception: + print("GODOT_WEB_TEST_PORT must be a valid integer") + sys.exit(255) + serve(env.Dir("#bin/.web_zip").abspath, port, "run" in COMMAND_LINE_TARGETS) + sys.exit(0) + web_files = [ "audio_driver_web.cpp", "display_server_web.cpp", diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index c263ee094b..07c53e51d0 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -37,9 +37,6 @@ #include "platform/web/display_server_web.h" #include "modules/modules_enabled.gen.h" // For websocket. -#ifdef MODULE_WEBSOCKET_ENABLED -#include "modules/websocket/remote_debugger_peer_websocket.h" -#endif #include <dlfcn.h> #include <emscripten.h> @@ -56,11 +53,6 @@ void OS_Web::alert(const String &p_alert, const String &p_title) { void OS_Web::initialize() { OS_Unix::initialize_core(); DisplayServerWeb::register_web_driver(); - -#ifdef MODULE_WEBSOCKET_ENABLED - EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create); - EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create); -#endif } void OS_Web::resume_audio() { diff --git a/platform/web/serve.py b/platform/web/serve.py index 14e87e9ea1..6a3efcc463 100755 --- a/platform/web/serve.py +++ b/platform/web/serve.py @@ -24,6 +24,17 @@ def shell_open(url): subprocess.call([opener, url]) +def serve(root, port, run_browser): + os.chdir(root) + + if run_browser: + # Open the served page in the user's default browser. + print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).") + shell_open(f"http://127.0.0.1:{port}") + + test(CORSRequestHandler, HTTPServer, port=port) + + if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int) @@ -41,12 +52,4 @@ if __name__ == "__main__": # so that the script can be run from any location. os.chdir(Path(__file__).resolve().parent) - if args.root: - os.chdir(args.root) - - if args.browser: - # Open the served page in the user's default browser. - print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).") - shell_open(f"http://127.0.0.1:{args.port}") - - test(CORSRequestHandler, HTTPServer, port=args.port) + serve(args.root, args.port, args.browser) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 5e4ba4a9e3..5ca064e523 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -331,8 +331,10 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const { if (hr != S_OK) { return Vector<String>(); } + BSTR resource_name = SysAllocString(L"root\\CIMV2"); + hr = wbemLocator->ConnectServer(resource_name, NULL, NULL, NULL, 0, NULL, NULL, &wbemServices); + SysFreeString(resource_name); - hr = wbemLocator->ConnectServer(L"root\\CIMV2", NULL, NULL, 0, NULL, 0, 0, &wbemServices); SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices` if (hr != S_OK) { SAFE_RELEASE(wbemServices) diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index d57e6bd21c..2c5df48b75 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -490,9 +490,7 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { params.thickness = th; _compute_sdf(¶ms); - Ref<Image> ret; - ret.instantiate(); - ret->create(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, cells_data); + Ref<Image> ret = Image::create_from_data(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, cells_data); ret->convert(Image::FORMAT_RH); //convert to half, save space ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 3ea1de57cd..1b5427c80d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -145,10 +145,7 @@ Array LightmapGIData::_get_light_textures_data() const { for (int i = 0; i < texture_count; i++) { int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; - Ref<Image> texture_image; - texture_image.instantiate(); - - texture_image->create(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); + Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); for (int j = 0; j < texture_slice_count; j++) { texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); @@ -818,7 +815,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } md.albedo_on_uv2.instantiate(); - md.albedo_on_uv2->create(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); + md.albedo_on_uv2->set_data(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); } md.emission_on_uv2 = images[RS::BAKE_CHANNEL_EMISSION]; @@ -1054,7 +1051,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } break; case ENVIRONMENT_MODE_CUSTOM_COLOR: { environment_image.instantiate(); - environment_image->create(128, 64, false, Image::FORMAT_RGBAF); + environment_image->initialize_data(128, 64, false, Image::FORMAT_RGBAF); Color c = environment_custom_color; c.r *= environment_custom_energy; c.g *= environment_custom_energy; diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 90c7bc89ef..04f73a4cbd 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -96,6 +96,7 @@ private: mutable SelfList<Node> xform_change; + // This Data struct is to avoid namespace pollution in derived classes. struct Data { mutable Transform3D global_transform; mutable Transform3D local_transform; diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 3dba0221bb..7caf2f4874 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -77,9 +77,7 @@ Dictionary VoxelGIData::_get_data() const { d["octree_cells"] = get_octree_cells(); d["octree_data"] = get_data_cells(); if (otsize != Vector3i()) { - Ref<Image> img; - img.instantiate(); - img->create(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field()); + Ref<Image> img = Image::create_from_data(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field()); Vector<uint8_t> df_png = img->save_png_to_buffer(); ERR_FAIL_COND_V(df_png.size() == 0, Dictionary()); d["octree_df_png"] = df_png; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 4401d22f30..fe4ccbc7dc 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -36,49 +36,37 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// -void XRCamera3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - // need to find our XROrigin3D parent and let it know we're its camera! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin != nullptr) { - origin->set_tracked_camera(this); - } - } break; +void XRCamera3D::_bind_tracker() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); - case NOTIFICATION_EXIT_TREE: { - // need to find our XROrigin3D parent and let it know we're no longer its camera! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin != nullptr && origin->get_tracked_camera() == this) { - origin->set_tracked_camera(nullptr); - } - } break; + tracker = xr_server->get_tracker(tracker_name); + if (tracker.is_valid()) { + tracker->connect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed)); + + Ref<XRPose> pose = tracker->get_pose(pose_name); + if (pose.is_valid()) { + set_transform(pose->get_adjusted_transform()); + } } } +void XRCamera3D::_unbind_tracker() { + if (tracker.is_valid()) { + tracker->disconnect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed)); + } + tracker.unref(); +} + void XRCamera3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) { if (p_tracker_name == tracker_name) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - tracker = xr_server->get_tracker(p_tracker_name); - if (tracker.is_valid()) { - tracker->connect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed)); - - Ref<XRPose> pose = tracker->get_pose(pose_name); - if (pose.is_valid()) { - set_transform(pose->get_adjusted_transform()); - } - } + _bind_tracker(); } } void XRCamera3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) { if (p_tracker_name == tracker_name) { - if (tracker.is_valid()) { - tracker->disconnect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed)); - } - tracker.unref(); + _unbind_tracker(); } } @@ -213,6 +201,9 @@ XRCamera3D::XRCamera3D() { xr_server->connect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker)); xr_server->connect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker)); xr_server->connect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker)); + + // check if our tracker already exists and if so, bind it... + _bind_tracker(); } XRCamera3D::~XRCamera3D() { @@ -582,11 +573,22 @@ Plane XRAnchor3D::get_plane() const { //////////////////////////////////////////////////////////////////////////////////////////////////// +Vector<XROrigin3D *> XROrigin3D::origin_nodes; + PackedStringArray XROrigin3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { - if (tracked_camera == nullptr) { + bool has_camera = false; + for (int i = 0; !has_camera && i < get_child_count(); i++) { + XRCamera3D *camera = Object::cast_to<XRCamera3D>(get_child(i)); + if (camera) { + // found it! + has_camera = true; + } + } + + if (!has_camera) { warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node.")); } } @@ -603,14 +605,10 @@ void XROrigin3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &XROrigin3D::set_world_scale); ClassDB::bind_method(D_METHOD("get_world_scale"), &XROrigin3D::get_world_scale); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); -} -void XROrigin3D::set_tracked_camera(XRCamera3D *p_tracked_camera) { - tracked_camera = p_tracked_camera; -} - -XRCamera3D *XROrigin3D::get_tracked_camera() const { - return tracked_camera; + ClassDB::bind_method(D_METHOD("set_current", "enabled"), &XROrigin3D::set_current); + ClassDB::bind_method(D_METHOD("is_current"), &XROrigin3D::is_current); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); } real_t XROrigin3D::get_world_scale() const { @@ -629,6 +627,44 @@ void XROrigin3D::set_world_scale(real_t p_world_scale) { xr_server->set_world_scale(p_world_scale); } +void XROrigin3D::set_current(bool p_enabled) { + current = p_enabled; + + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + // Notify us of any transform changes + set_notify_local_transform(current); + set_notify_transform(current); + + if (current) { + for (int i = 0; i < origin_nodes.size(); i++) { + if (origin_nodes[i] != this) { + origin_nodes[i]->set_current(false); + } + } + } else { + bool found = false; + // We no longer have a current origin so find the first one we can make current + for (int i = 0; !found && i < origin_nodes.size(); i++) { + if (origin_nodes[i] != this) { + origin_nodes[i]->set_current(true); + found = true; + } + } + } +} + +bool XROrigin3D::is_current() const { + if (Engine::get_singleton()->is_editor_hint()) { + // return as is + return current; + } else { + return current && is_inside_tree(); + } +} + void XROrigin3D::_notification(int p_what) { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); @@ -636,34 +672,47 @@ void XROrigin3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - set_process_internal(true); + if (!Engine::get_singleton()->is_editor_hint()) { + if (origin_nodes.is_empty()) { + // first entry always becomes current + current = true; + } + + origin_nodes.push_back(this); + + if (current) { + // set this again so we do whatever setup is needed. + set_current(true); + } + } } break; case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - // set our world origin to our node transform - xr_server->set_world_origin(get_global_transform()); + if (!Engine::get_singleton()->is_editor_hint()) { + origin_nodes.erase(this); - // check if we have a primary interface - Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); - if (xr_interface.is_valid() && tracked_camera != nullptr) { - // get our positioning transform for our headset - Transform3D t = xr_interface->get_camera_transform(); + if (current) { + // We are no longer current + set_current(false); + } + } + } break; - // now apply this to our camera - tracked_camera->set_transform(t); + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: + case NOTIFICATION_TRANSFORM_CHANGED: { + if (current && !Engine::get_singleton()->is_editor_hint()) { + xr_server->set_world_origin(get_global_transform()); } } break; } - // send our notification to all active XE interfaces, they may need to react to it also - for (int i = 0; i < xr_server->get_interface_count(); i++) { - Ref<XRInterface> interface = xr_server->get_interface(i); - if (interface.is_valid() && interface->is_initialized()) { - interface->notification(p_what); + if (current) { + // send our notification to all active XE interfaces, they may need to react to it also + for (int i = 0; i < xr_server->get_interface_count(); i++) { + Ref<XRInterface> interface = xr_server->get_interface(i); + if (interface.is_valid() && interface->is_initialized()) { + interface->notification(p_what); + } } } } diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index ef846cc3a3..990fb61983 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -48,8 +48,8 @@ protected: StringName pose_name = "default"; Ref<XRPositionalTracker> tracker; - void _notification(int p_what); - + void _bind_tracker(); + void _unbind_tracker(); void _changed_tracker(const StringName p_tracker_name, int p_tracker_type); void _removed_tracker(const StringName p_tracker_name, int p_tracker_type); void _pose_changed(const Ref<XRPose> &p_pose); @@ -180,7 +180,8 @@ class XROrigin3D : public Node3D { GDCLASS(XROrigin3D, Node3D); private: - XRCamera3D *tracked_camera = nullptr; + bool current = false; + static Vector<XROrigin3D *> origin_nodes; // all origin nodes in tree protected: void _notification(int p_what); @@ -189,12 +190,12 @@ protected: public: PackedStringArray get_configuration_warnings() const override; - void set_tracked_camera(XRCamera3D *p_tracked_camera); - XRCamera3D *get_tracked_camera() const; - real_t get_world_scale() const; void set_world_scale(real_t p_world_scale); + void set_current(bool p_enabled); + bool is_current() const; + XROrigin3D() {} ~XROrigin3D() {} }; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index af6a99ca62..552345e4fe 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -60,7 +60,7 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseButton> mouse_button = p_event; - bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo(); + bool ui_accept = p_event->is_action("ui_accept", true) && !p_event->is_echo(); bool button_masked = mouse_button.is_valid() && (mouse_button_to_mask(mouse_button->get_button_index()) & button_mask) != MouseButton::NONE; if (button_masked || ui_accept) { @@ -349,10 +349,6 @@ Ref<Shortcut> BaseButton::get_shortcut() const { void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (!_is_focus_owner_in_shortcut_context()) { - return; - } - if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) { on_action_event(p_event); accept_event(); @@ -389,34 +385,6 @@ Ref<ButtonGroup> BaseButton::get_button_group() const { return button_group; } -void BaseButton::set_shortcut_context(Node *p_node) { - if (p_node != nullptr) { - shortcut_context = p_node->get_instance_id(); - } else { - shortcut_context = ObjectID(); - } -} - -Node *BaseButton::get_shortcut_context() const { - Object *ctx_obj = ObjectDB::get_instance(shortcut_context); - Node *ctx_node = Object::cast_to<Node>(ctx_obj); - - return ctx_node; -} - -bool BaseButton::_is_focus_owner_in_shortcut_context() const { - if (shortcut_context == ObjectID()) { - // No context, therefore global - always "in" context. - return true; - } - - Node *ctx_node = get_shortcut_context(); - Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr; - - // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. - return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); -} - bool BaseButton::_was_pressed_by_mouse() const { return was_mouse_pressed; } @@ -446,9 +414,6 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_group", "button_group"), &BaseButton::set_button_group); ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group); - ClassDB::bind_method(D_METHOD("set_shortcut_context", "node"), &BaseButton::set_shortcut_context); - ClassDB::bind_method(D_METHOD("get_shortcut_context"), &BaseButton::get_shortcut_context); - GDVIRTUAL_BIND(_pressed); GDVIRTUAL_BIND(_toggled, "button_pressed"); @@ -466,7 +431,6 @@ void BaseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_NODE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); BIND_ENUM_CONSTANT(DRAW_NORMAL); BIND_ENUM_CONSTANT(DRAW_PRESSED); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index c83b08aadf..7839239800 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -81,7 +81,6 @@ protected: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; void _notification(int p_what); - bool _is_focus_owner_in_shortcut_context() const; bool _was_pressed_by_mouse() const; GDVIRTUAL0(_pressed) @@ -132,9 +131,6 @@ public: void set_button_group(const Ref<ButtonGroup> &p_group); Ref<ButtonGroup> get_button_group() const; - void set_shortcut_context(Node *p_node); - Node *get_shortcut_context() const; - BaseButton(); ~BaseButton(); }; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 2dcae2553c..565e450d60 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1759,6 +1759,34 @@ void Control::warp_mouse(const Point2 &p_position) { get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position)); } +void Control::set_shortcut_context(const Node *p_node) { + if (p_node != nullptr) { + data.shortcut_context = p_node->get_instance_id(); + } else { + data.shortcut_context = ObjectID(); + } +} + +Node *Control::get_shortcut_context() const { + Object *ctx_obj = ObjectDB::get_instance(data.shortcut_context); + Node *ctx_node = Object::cast_to<Node>(ctx_obj); + + return ctx_node; +} + +bool Control::is_focus_owner_in_shortcut_context() const { + if (data.shortcut_context == ObjectID()) { + // No context, therefore global - always "in" context. + return true; + } + + const Node *ctx_node = get_shortcut_context(); + const Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr; + + // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. + return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); +} + // Drag and drop handling. void Control::set_drag_forwarding(Object *p_target) { @@ -2885,8 +2913,8 @@ void Control::_notification(int p_notification) { if (data.parent_canvas_item) { data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed)); data.parent_canvas_item = nullptr; - } else if (!is_set_as_top_level()) { - //disconnect viewport + } else { + // Disconnect viewport. Viewport *viewport = get_viewport(); ERR_FAIL_COND(!viewport); viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed)); @@ -3133,6 +3161,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Control::warp_mouse); + ClassDB::bind_method(D_METHOD("set_shortcut_context", "node"), &Control::set_shortcut_context); + ClassDB::bind_method(D_METHOD("get_shortcut_context"), &Control::get_shortcut_context); + ClassDB::bind_method(D_METHOD("update_minimum_size"), &Control::update_minimum_size); ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Control::set_layout_direction); @@ -3206,6 +3237,9 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mouse_force_pass_scroll_events"), "set_force_pass_scroll_events", "is_force_pass_scroll_events"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape"); + ADD_GROUP("Input", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_NODE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); + ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation"); diff --git a/scene/gui/control.h b/scene/gui/control.h index ee6443c81c..e526690cbe 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -159,6 +159,7 @@ private: } }; + // This Data struct is to avoid namespace pollution in derived classes. struct Data { // Global relations. @@ -218,6 +219,8 @@ private: NodePath focus_next; NodePath focus_prev; + ObjectID shortcut_context; + // Theming. ThemeOwner *theme_owner = nullptr; @@ -487,6 +490,10 @@ public: void warp_mouse(const Point2 &p_position); + bool is_focus_owner_in_shortcut_context() const; + void set_shortcut_context(const Node *p_node); + Node *get_shortcut_context() const; + // Drag and drop handling. virtual void set_drag_forwarding(Object *p_target); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 90552c32c2..caf8db4515 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1385,16 +1385,16 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { } if (p_ev->is_pressed()) { - if (p_ev->is_action("ui_graph_duplicate")) { + if (p_ev->is_action("ui_graph_duplicate", true)) { emit_signal(SNAME("duplicate_nodes_request")); accept_event(); - } else if (p_ev->is_action("ui_copy")) { + } else if (p_ev->is_action("ui_copy", true)) { emit_signal(SNAME("copy_nodes_request")); accept_event(); - } else if (p_ev->is_action("ui_paste")) { + } else if (p_ev->is_action("ui_paste", true)) { emit_signal(SNAME("paste_nodes_request")); accept_event(); - } else if (p_ev->is_action("ui_graph_delete")) { + } else if (p_ev->is_action("ui_graph_delete", true)) { TypedArray<StringName> nodes; for (int i = 0; i < get_child_count(); i++) { diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 0dc791f71d..223428906a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -728,7 +728,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } if (p_event->is_pressed() && items.size() > 0) { - if (p_event->is_action("ui_up")) { + if (p_event->is_action("ui_up", true)) { if (!search_string.is_empty()) { uint64_t now = OS::get_singleton()->get_ticks_msec(); uint64_t diff = now - search_time_msec; @@ -766,7 +766,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } accept_event(); } - } else if (p_event->is_action("ui_down")) { + } else if (p_event->is_action("ui_down", true)) { if (!search_string.is_empty()) { uint64_t now = OS::get_singleton()->get_ticks_msec(); uint64_t diff = now - search_time_msec; @@ -803,7 +803,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } accept_event(); } - } else if (p_event->is_action("ui_page_up")) { + } else if (p_event->is_action("ui_page_up", true)) { search_string = ""; //any mousepress cancels for (int i = 4; i > 0; i--) { @@ -817,7 +817,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { break; } } - } else if (p_event->is_action("ui_page_down")) { + } else if (p_event->is_action("ui_page_down", true)) { search_string = ""; //any mousepress cancels for (int i = 4; i > 0; i--) { @@ -832,7 +832,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { break; } } - } else if (p_event->is_action("ui_left")) { + } else if (p_event->is_action("ui_left", true)) { search_string = ""; //any mousepress cancels if (current % current_columns != 0) { @@ -852,7 +852,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } accept_event(); } - } else if (p_event->is_action("ui_right")) { + } else if (p_event->is_action("ui_right", true)) { search_string = ""; //any mousepress cancels if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) { @@ -872,9 +872,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } accept_event(); } - } else if (p_event->is_action("ui_cancel")) { + } else if (p_event->is_action("ui_cancel", true)) { search_string = ""; - } else if (p_event->is_action("ui_select") && select_mode == SELECT_MULTI) { + } else if (p_event->is_action("ui_select", true) && select_mode == SELECT_MULTI) { if (current >= 0 && current < items.size()) { if (items[current].selectable && !items[current].disabled && !items[current].selected) { select(current, false); @@ -884,7 +884,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { emit_signal(SNAME("multi_selected"), current, false); } } - } else if (p_event->is_action("ui_accept")) { + } else if (p_event->is_action("ui_accept", true)) { search_string = ""; //any mousepress cancels if (current >= 0 && current < items.size()) { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 36e85b5d02..65670b7786 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -2175,6 +2175,12 @@ void LineEdit::_emit_text_change() { } void LineEdit::_shape() { + const Ref<Font> &font = theme_cache.font; + int font_size = theme_cache.font_size; + if (font.is_null()) { + return; + } + Size2 old_size = TS->shaped_text_get_size(text_rid); TS->shaped_text_clear(text_rid); @@ -2197,9 +2203,6 @@ void LineEdit::_shape() { } TS->shaped_text_set_preserve_control(text_rid, draw_control_chars); - const Ref<Font> &font = theme_cache.font; - int font_size = theme_cache.font_size; - ERR_FAIL_COND(font.is_null()); TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language); for (int i = 0; i < TextServer::SPACING_MAX; i++) { TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 742042f65f..82ef53e317 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -41,7 +41,7 @@ void MenuBar::gui_input(const Ref<InputEvent> &p_event) { } MutexLock lock(mutex); - if (p_event->is_action("ui_left") && p_event->is_pressed()) { + if (p_event->is_action("ui_left", true) && p_event->is_pressed()) { int new_sel = selected_menu; int old_sel = (selected_menu < 0) ? 0 : selected_menu; do { @@ -63,7 +63,7 @@ void MenuBar::gui_input(const Ref<InputEvent> &p_event) { _open_popup(selected_menu, true); } return; - } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_right", true) && p_event->is_pressed()) { int new_sel = selected_menu; int old_sel = (selected_menu < 0) ? menu_cache.size() - 1 : selected_menu; do { @@ -149,10 +149,6 @@ void MenuBar::_open_popup(int p_index, bool p_focus_item) { void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (!_is_focus_owner_in_shortcut_context()) { - return; - } - if (disable_shortcuts) { return; } @@ -175,34 +171,6 @@ void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) { } } -void MenuBar::set_shortcut_context(Node *p_node) { - if (p_node != nullptr) { - shortcut_context = p_node->get_instance_id(); - } else { - shortcut_context = ObjectID(); - } -} - -Node *MenuBar::get_shortcut_context() const { - Object *ctx_obj = ObjectDB::get_instance(shortcut_context); - Node *ctx_node = Object::cast_to<Node>(ctx_obj); - - return ctx_node; -} - -bool MenuBar::_is_focus_owner_in_shortcut_context() const { - if (shortcut_context == ObjectID()) { - // No context, therefore global - always "in" context. - return true; - } - - Node *ctx_node = get_shortcut_context(); - Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr; - - // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. - return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); -} - void MenuBar::_popup_visibility_changed(bool p_visible) { if (!p_visible) { active_menu = -1; @@ -694,16 +662,12 @@ void MenuBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_menu_hidden", "menu", "hidden"), &MenuBar::set_menu_hidden); ClassDB::bind_method(D_METHOD("is_menu_hidden", "menu"), &MenuBar::is_menu_hidden); - ClassDB::bind_method(D_METHOD("set_shortcut_context", "node"), &MenuBar::set_shortcut_context); - ClassDB::bind_method(D_METHOD("get_shortcut_context"), &MenuBar::get_shortcut_context); - ClassDB::bind_method(D_METHOD("get_menu_popup", "menu"), &MenuBar::get_menu_popup); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "start_index"), "set_start_index", "get_start_index"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefer_global_menu"), "set_prefer_global_menu", "is_prefer_global_menu"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_NODE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index f7ef19e98b..c057a7c96f 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -118,8 +118,6 @@ class MenuBar : public Control { void _clear_menu(); void _update_menu(); - bool _is_focus_owner_in_shortcut_context() const; - protected: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; @@ -170,9 +168,6 @@ public: void set_menu_hidden(int p_menu, bool p_hidden); bool is_menu_hidden(int p_menu) const; - void set_shortcut_context(Node *p_node); - Node *get_shortcut_context() const; - PopupMenu *get_menu_popup(int p_menu) const; virtual void get_translatable_strings(List<String> *p_strings) const override; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 78aeab9457..786f23873e 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -36,10 +36,6 @@ void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (!_is_focus_owner_in_shortcut_context()) { - return; - } - if (disable_shortcuts) { return; } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 4cdde5902e..9a411ef7ed 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -273,7 +273,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!items.is_empty()) { - if (p_event->is_action("ui_down") && p_event->is_pressed()) { + if (p_event->is_action("ui_down", true) && p_event->is_pressed()) { int search_from = mouse_over + 1; if (search_from >= items.size()) { search_from = 0; @@ -305,7 +305,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { } } } - } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_up", true) && p_event->is_pressed()) { int search_from = mouse_over - 1; if (search_from < 0) { search_from = items.size() - 1; @@ -337,7 +337,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { } } } - } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_left", true) && p_event->is_pressed()) { Node *n = get_parent(); if (n) { if (Object::cast_to<PopupMenu>(n)) { @@ -349,7 +349,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { return; } } - } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_right", true) && p_event->is_pressed()) { if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { _activate_submenu(mouse_over, true); set_input_as_handled(); @@ -361,7 +361,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { return; } } - } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_accept", true) && p_event->is_pressed()) { if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { _activate_submenu(mouse_over, true); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 9217f31310..c96d3c763d 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2019,36 +2019,36 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (k->is_pressed()) { bool handled = false; - if (k->is_action("ui_page_up") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_page_up", true) && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_value() - vscroll->get_page()); handled = true; } - if (k->is_action("ui_page_down") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_page_down", true) && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_value() + vscroll->get_page()); handled = true; } - if (k->is_action("ui_up") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_up", true) && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_value() - theme_cache.normal_font->get_height(theme_cache.normal_font_size)); handled = true; } - if (k->is_action("ui_down") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_down", true) && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_value() + theme_cache.normal_font->get_height(theme_cache.normal_font_size)); handled = true; } - if (k->is_action("ui_home") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_home", true) && vscroll->is_visible_in_tree()) { vscroll->set_value(0); handled = true; } - if (k->is_action("ui_end") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_end", true) && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_max()); handled = true; } if (is_shortcut_keys_enabled()) { - if (k->is_action("ui_text_select_all")) { + if (k->is_action("ui_text_select_all", true)) { select_all(); handled = true; } - if (k->is_action("ui_copy")) { + if (k->is_action("ui_copy", true)) { selection_copy(); handled = true; } diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 6c05b171e3..193c6f1ce7 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -183,35 +183,35 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { } if (p_event->is_pressed()) { - if (p_event->is_action("ui_left")) { + if (p_event->is_action("ui_left", true)) { if (orientation != HORIZONTAL) { return; } set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - } else if (p_event->is_action("ui_right")) { + } else if (p_event->is_action("ui_right", true)) { if (orientation != HORIZONTAL) { return; } set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - } else if (p_event->is_action("ui_up")) { + } else if (p_event->is_action("ui_up", true)) { if (orientation != VERTICAL) { return; } set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - } else if (p_event->is_action("ui_down")) { + } else if (p_event->is_action("ui_down", true)) { if (orientation != VERTICAL) { return; } set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - } else if (p_event->is_action("ui_home")) { + } else if (p_event->is_action("ui_home", true)) { set_value(get_min()); - } else if (p_event->is_action("ui_end")) { + } else if (p_event->is_action("ui_end", true)) { set_value(get_max()); } } diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index a7d44c0f3c..6cbacf0dd3 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -138,10 +138,10 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { } set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); accept_event(); - } else if (p_event->is_action("ui_home") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_home", true) && p_event->is_pressed()) { set_value(get_min()); accept_event(); - } else if (p_event->is_action("ui_end") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_end", true) && p_event->is_pressed()) { set_value(get_max()); accept_event(); } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 2e8fa1e606..6f9a9a5141 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3191,7 +3191,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; bool is_command = k.is_valid() && k->is_command_or_control_pressed(); - if (p_event->is_action("ui_right") && p_event->is_pressed()) { + if (p_event->is_action("ui_right", true) && p_event->is_pressed()) { if (!cursor_can_exit_tree) { accept_event(); } @@ -3209,7 +3209,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } else { _go_right(); } - } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_left", true) && p_event->is_pressed()) { if (!cursor_can_exit_tree) { accept_event(); } @@ -3229,21 +3229,21 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { _go_left(); } - } else if (p_event->is_action("ui_up") && p_event->is_pressed() && !is_command) { + } else if (p_event->is_action("ui_up", true) && p_event->is_pressed() && !is_command) { if (!cursor_can_exit_tree) { accept_event(); } _go_up(); - } else if (p_event->is_action("ui_down") && p_event->is_pressed() && !is_command) { + } else if (p_event->is_action("ui_down", true) && p_event->is_pressed() && !is_command) { if (!cursor_can_exit_tree) { accept_event(); } _go_down(); - } else if (p_event->is_action("ui_page_down") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_page_down", true) && p_event->is_pressed()) { if (!cursor_can_exit_tree) { accept_event(); } @@ -3281,7 +3281,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } ensure_cursor_is_visible(); - } else if (p_event->is_action("ui_page_up") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_page_up", true) && p_event->is_pressed()) { if (!cursor_can_exit_tree) { accept_event(); } @@ -3318,7 +3318,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { prev->select(selected_col); } ensure_cursor_is_visible(); - } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_accept", true) && p_event->is_pressed()) { if (selected_item) { //bring up editor if possible if (!edit_selected()) { @@ -3327,7 +3327,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } } accept_event(); - } else if (p_event->is_action("ui_select") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_select", true) && p_event->is_pressed()) { if (select_mode == SELECT_MULTI) { if (!selected_item) { return; diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index 3b7f499a07..e8e3e3e556 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -38,7 +38,9 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { Vector2 scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP)); - if (scroll_vec != Vector2()) { + // Moving the scroll wheel sends two events: one with pressed as true, + // and one with pressed as false. Make sure we only process one of them. + if (scroll_vec != Vector2() && mb->is_pressed()) { if (control_scheme == SCROLL_PANS) { if (mb->is_ctrl_pressed()) { scroll_vec.y *= mb->get_factor(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6ab27853f1..2ea45df309 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -322,62 +322,62 @@ void Node::_propagate_exit_tree() { data.depth = -1; } -void Node::move_child(Node *p_child, int p_pos) { +void Node::move_child(Node *p_child, int p_index) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); // We need to check whether node is internal and move it only in the relevant node range. if (p_child->_is_internal_front()) { - if (p_pos < 0) { - p_pos += data.internal_children_front; + if (p_index < 0) { + p_index += data.internal_children_front; } - ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos)); - _move_child(p_child, p_pos); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, p_index); } else if (p_child->_is_internal_back()) { - if (p_pos < 0) { - p_pos += data.internal_children_back; + if (p_index < 0) { + p_index += data.internal_children_back; } - ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos)); - _move_child(p_child, data.children.size() - data.internal_children_back + p_pos); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, data.children.size() - data.internal_children_back + p_index); } else { - if (p_pos < 0) { - p_pos += get_child_count(false); + if (p_index < 0) { + p_index += get_child_count(false); } - ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos)); - _move_child(p_child, p_pos + data.internal_children_front); + ERR_FAIL_INDEX_MSG(p_index, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child index: %d.", p_index)); + _move_child(p_child, p_index + data.internal_children_front); } } -void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { +void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); // Specifying one place beyond the end - // means the same as moving to the last position + // means the same as moving to the last index if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly. if (p_child->_is_internal_front()) { - if (p_pos == data.internal_children_front) { - p_pos--; + if (p_index == data.internal_children_front) { + p_index--; } } else if (p_child->_is_internal_back()) { - if (p_pos == data.children.size()) { - p_pos--; + if (p_index == data.children.size()) { + p_index--; } } else { - if (p_pos == data.children.size() - data.internal_children_back) { - p_pos--; + if (p_index == data.children.size() - data.internal_children_back) { + p_index--; } } } - if (p_child->data.pos == p_pos) { + if (p_child->data.index == p_index) { return; //do nothing } - int motion_from = MIN(p_pos, p_child->data.pos); - int motion_to = MAX(p_pos, p_child->data.pos); + int motion_from = MIN(p_index, p_child->data.index); + int motion_to = MAX(p_index, p_child->data.index); - data.children.remove_at(p_child->data.pos); - data.children.insert(p_pos, p_child); + data.children.remove_at(p_child->data.index); + data.children.insert(p_index, p_child); if (data.tree) { data.tree->tree_changed(); @@ -386,7 +386,7 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { data.blocked++; //new pos first for (int i = motion_from; i <= motion_to; i++) { - data.children[i]->data.pos = i; + data.children[i]->data.index = i; } // notification second move_child_notify(p_child); @@ -1104,7 +1104,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { //add a child node quickly, without name validation p_child->data.name = p_name; - p_child->data.pos = data.children.size(); + p_child->data.index = data.children.size(); data.children.push_back(p_child); p_child->data.parent = this; @@ -1171,9 +1171,9 @@ void Node::remove_child(Node *p_child) { Node **children = data.children.ptrw(); int idx = -1; - if (p_child->data.pos >= 0 && p_child->data.pos < child_count) { - if (children[p_child->data.pos] == p_child) { - idx = p_child->data.pos; + if (p_child->data.index >= 0 && p_child->data.index < child_count) { + if (children[p_child->data.index] == p_child) { + idx = p_child->data.index; } } @@ -1209,12 +1209,12 @@ void Node::remove_child(Node *p_child) { children = data.children.ptrw(); for (int i = idx; i < child_count; i++) { - children[i]->data.pos = i; + children[i]->data.index = i; children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } p_child->data.parent = nullptr; - p_child->data.pos = -1; + p_child->data.index = -1; if (data.inside_tree) { p_child->_propagate_after_exit_tree(); @@ -1473,7 +1473,7 @@ bool Node::is_greater_than(const Node *p_node) const { int idx = data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, data.depth, false); - this_stack[idx--] = n->data.pos; + this_stack[idx--] = n->data.index; n = n->data.parent; } ERR_FAIL_COND_V(idx != -1, false); @@ -1481,7 +1481,7 @@ bool Node::is_greater_than(const Node *p_node) const { idx = p_node->data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, p_node->data.depth, false); - that_stack[idx--] = n->data.pos; + that_stack[idx--] = n->data.index; n = n->data.parent; } @@ -1892,9 +1892,9 @@ int Node::get_index(bool p_include_internal) const { ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false."); if (data.parent && !p_include_internal) { - return data.pos - data.parent->data.internal_children_front; + return data.index - data.parent->data.internal_children_front; } - return data.pos; + return data.index; } Ref<Tween> Node::create_tween() { @@ -2389,12 +2389,12 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) { } Node *parent = data.parent; - int pos_in_parent = data.pos; + int index_in_parent = data.index; if (data.parent) { parent->remove_child(this); parent->add_child(p_node); - parent->move_child(p_node, pos_in_parent); + parent->move_child(p_node, index_in_parent); } while (get_child_count()) { @@ -2757,7 +2757,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("add_to_group", "group", "persistent"), &Node::add_to_group, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_from_group", "group"), &Node::remove_from_group); ClassDB::bind_method(D_METHOD("is_in_group", "group"), &Node::is_in_group); - ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_position"), &Node::move_child); + ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_index"), &Node::move_child); ClassDB::bind_method(D_METHOD("get_groups"), &Node::_get_groups); ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); diff --git a/scene/main/node.h b/scene/main/node.h index 4e6530cccd..c8c8c395ce 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -91,6 +91,7 @@ private: SceneTree::Group *group = nullptr; }; + // This Data struct is to avoid namespace pollution in derived classes. struct Data { String scene_file_path; Ref<SceneState> instance_state; @@ -104,7 +105,7 @@ private: int internal_children_front = 0; int internal_children_back = 0; - int pos = -1; + int index = -1; int depth = -1; int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. StringName name; @@ -186,8 +187,8 @@ private: Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; } - _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; } + _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.index < data.parent->data.internal_children_front; } + _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.index >= data.parent->data.children.size() - data.parent->data.internal_children_back; } friend class SceneTree; @@ -346,8 +347,8 @@ public: void get_groups(List<GroupInfo> *p_groups) const; int get_persistent_group_count() const; - void move_child(Node *p_child, int p_pos); - void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false); + void move_child(Node *p_child, int p_index); + void _move_child(Node *p_child, int p_index, bool p_ignore_end = false); void set_owner(Node *p_owner); Node *get_owner() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 64eb3b879b..270e5b7025 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -44,6 +44,7 @@ #include "node.h" #include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" +#include "scene/gui/control.h" #include "scene/main/multiplayer_api.h" #include "scene/main/viewport.h" #include "scene/resources/environment.h" @@ -895,6 +896,8 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal call_lock++; + Vector<Node *> no_context_nodes; + for (int i = gr_node_count - 1; i >= 0; i--) { if (p_viewport->is_input_handled()) { break; @@ -913,9 +916,22 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal case CALL_INPUT_TYPE_INPUT: n->_call_input(p_input); break; - case CALL_INPUT_TYPE_SHORTCUT_INPUT: + case CALL_INPUT_TYPE_SHORTCUT_INPUT: { + const Control *c = Object::cast_to<Control>(n); + if (c) { + // If calling shortcut input on a control, ensure it respects the shortcut context. + // Shortcut context (based on focus) only makes sense for controls (UI), so don't need to worry about it for nodes + if (c->get_shortcut_context() == nullptr) { + no_context_nodes.append(n); + continue; + } + if (!c->is_focus_owner_in_shortcut_context()) { + continue; + } + } n->_call_shortcut_input(p_input); break; + } case CALL_INPUT_TYPE_UNHANDLED_INPUT: n->_call_unhandled_input(p_input); break; @@ -925,6 +941,10 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal } } + for (Node *n : no_context_nodes) { + n->_call_shortcut_input(p_input); + } + call_lock--; if (call_lock == 0) { call_skip.clear(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f7a515dc1d..f66337a2f9 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1490,7 +1490,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.key_event_accepted = false; Point2 mpos = mb->get_position(); - gui.last_mouse_pos = mpos; if (mb->is_pressed()) { Size2 pos = mpos; if (gui.mouse_focus_mask != MouseButton::NONE) { @@ -1644,8 +1643,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.key_event_accepted = false; Point2 mpos = mm->get_position(); - gui.last_mouse_pos = mpos; - // Drag & drop. if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { gui.drag_accum += mm->get_relative(); @@ -2759,6 +2756,11 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { ev = p_event; } + Ref<InputEventMouse> me = ev; + if (me.is_valid()) { + gui.last_mouse_pos = me->get_position(); + } + if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) { set_input_as_handled(); return; diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 0505f6b559..1b06e09bb8 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -639,9 +639,7 @@ void BitMap::resize(const Size2i &p_new_size) { } Ref<Image> BitMap::convert_to_image() const { - Ref<Image> image; - image.instantiate(); - image->create(width, height, false, Image::FORMAT_L8); + Ref<Image> image = Image::create_empty(width, height, false, Image::FORMAT_L8); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp index 4eaae111a1..5bcefcd0e4 100644 --- a/scene/resources/convex_polygon_shape_3d.cpp +++ b/scene/resources/convex_polygon_shape_3d.cpp @@ -42,9 +42,9 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const { if (err == OK) { Vector<Vector3> lines; lines.resize(md.edges.size() * 2); - for (int i = 0; i < md.edges.size(); i++) { - lines.write[i * 2 + 0] = md.vertices[md.edges[i].a]; - lines.write[i * 2 + 1] = md.vertices[md.edges[i].b]; + for (uint32_t i = 0; i < md.edges.size(); i++) { + lines.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a]; + lines.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b]; } return lines; } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 1f71d583b1..1c99fa5554 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -1546,7 +1546,7 @@ Array SceneState::get_connection_binds(int p_idx) const { return binds; } -bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method) { +bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method, bool p_no_inheritance) { // this method cannot be const because of this Ref<SceneState> ss = this; @@ -1578,6 +1578,10 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p } } + if (p_no_inheritance) { + break; + } + ss = ss->get_base_scene_state(); } while (ss.is_valid()); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 8e1a1d29b6..c6f82ddd5e 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -176,7 +176,7 @@ public: int get_connection_unbinds(int p_idx) const; Array get_connection_binds(int p_idx) const; - bool has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method); + bool has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method, bool p_no_inheritance = false); Vector<NodePath> get_editable_instances() const; diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 692a40badc..b77430c154 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -115,6 +115,7 @@ void ParticleProcessMaterial::init_shaders() { shader_names->sub_emitter_frequency = "sub_emitter_frequency"; shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end"; + shader_names->sub_emitter_amount_at_collision = "sub_emitter_amount_at_collision"; shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity"; shader_names->collision_friction = "collision_friction"; @@ -235,6 +236,9 @@ void ParticleProcessMaterial::_update_shader() { if (sub_emitter_mode == SUB_EMITTER_AT_END) { code += "uniform int sub_emitter_amount_at_end;\n"; } + if (sub_emitter_mode == SUB_EMITTER_AT_COLLISION) { + code += "uniform int sub_emitter_amount_at_collision;\n"; + } code += "uniform bool sub_emitter_keep_velocity;\n"; } @@ -866,7 +870,7 @@ void ParticleProcessMaterial::_update_shader() { code += " if (DELTA >= interval_rem) emit_count = 1;\n"; } break; case SUB_EMITTER_AT_COLLISION: { - code += " if (COLLIDED) emit_count = 1;\n"; + code += " if (COLLIDED) emit_count = sub_emitter_amount_at_collision;\n"; } break; case SUB_EMITTER_AT_END: { code += " float unit_delta = DELTA/LIFETIME;\n"; @@ -1425,6 +1429,10 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const p_property.usage = PROPERTY_USAGE_NONE; } + if (p_property.name == "sub_emitter_amount_at_collision" && sub_emitter_mode != SUB_EMITTER_AT_COLLISION) { + p_property.usage = PROPERTY_USAGE_NONE; + } + if (p_property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -1480,6 +1488,15 @@ int ParticleProcessMaterial::get_sub_emitter_amount_at_end() const { return sub_emitter_amount_at_end; } +void ParticleProcessMaterial::set_sub_emitter_amount_at_collision(int p_amount) { + sub_emitter_amount_at_collision = p_amount; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_amount_at_collision, p_amount); +} + +int ParticleProcessMaterial::get_sub_emitter_amount_at_collision() const { + return sub_emitter_amount_at_collision; +} + void ParticleProcessMaterial::set_sub_emitter_keep_velocity(bool p_enable) { sub_emitter_keep_velocity = p_enable; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_keep_velocity, p_enable); @@ -1632,6 +1649,9 @@ void ParticleProcessMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_end"), &ParticleProcessMaterial::get_sub_emitter_amount_at_end); ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_end", "amount"), &ParticleProcessMaterial::set_sub_emitter_amount_at_end); + ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_collision"), &ParticleProcessMaterial::get_sub_emitter_amount_at_collision); + ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_collision", "amount"), &ParticleProcessMaterial::set_sub_emitter_amount_at_collision); + ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticleProcessMaterial::get_sub_emitter_keep_velocity); ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticleProcessMaterial::set_sub_emitter_keep_velocity); @@ -1744,6 +1764,7 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,At End,At Collision"), "set_sub_emitter_mode", "get_sub_emitter_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01,suffix:Hz"), "set_sub_emitter_frequency", "get_sub_emitter_frequency"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_collision", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_collision", "get_sub_emitter_amount_at_collision"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity"); ADD_GROUP("Attractor Interaction", "attractor_interaction_"); @@ -1851,6 +1872,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() : set_sub_emitter_mode(SUB_EMITTER_DISABLED); set_sub_emitter_frequency(4); set_sub_emitter_amount_at_end(1); + set_sub_emitter_amount_at_collision(1); set_sub_emitter_keep_velocity(false); set_attractor_interaction_enabled(true); diff --git a/scene/resources/particle_process_material.h b/scene/resources/particle_process_material.h index 8fe9223a47..64d828b3e7 100644 --- a/scene/resources/particle_process_material.h +++ b/scene/resources/particle_process_material.h @@ -246,6 +246,7 @@ private: StringName sub_emitter_frequency; StringName sub_emitter_amount_at_end; + StringName sub_emitter_amount_at_collision; StringName sub_emitter_keep_velocity; StringName collision_friction; @@ -304,6 +305,7 @@ private: SubEmitterMode sub_emitter_mode; double sub_emitter_frequency = 0.0; int sub_emitter_amount_at_end = 0; + int sub_emitter_amount_at_collision = 0; bool sub_emitter_keep_velocity = false; //do not save emission points here @@ -418,6 +420,9 @@ public: void set_sub_emitter_amount_at_end(int p_amount); int get_sub_emitter_amount_at_end() const; + void set_sub_emitter_amount_at_collision(int p_amount); + int get_sub_emitter_amount_at_collision() const; + void set_sub_emitter_keep_velocity(bool p_enable); bool get_sub_emitter_keep_velocity() const; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 7d0336cff3..eb83a37c7b 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -771,6 +771,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto thisrow = 0; prevrow = 0; + const real_t side_normal_y = (bottom_radius - top_radius) / height; for (j = 0; j <= (rings + 1); j++) { v = j; v /= (rings + 1); @@ -789,7 +790,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto Vector3 p = Vector3(x * radius, y, z * radius); points.push_back(p); - normals.push_back(Vector3(x, 0.0, z)); + normals.push_back(Vector3(x, side_normal_y, z).normalized()); ADD_TANGENT(z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * 0.5)); point++; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 4085aa8ec2..3a2b0ed9cb 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -740,7 +740,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si } } - image->create(w, h, true, mipmap_images[0]->get_format(), img_data); + image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data); return image; } @@ -766,10 +766,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si f->get_buffer(wr, data.size()); } - Ref<Image> image; - image.instantiate(); - - image->create(tw, th, mipmaps - i ? true : false, format, data); + Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data); return image; } @@ -2336,11 +2333,11 @@ void GradientTexture2D::_update() { image.instantiate(); if (gradient->get_points_count() <= 1) { // No need to interpolate. - image->create(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8); + image->initialize_data(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8); image->fill((gradient->get_points_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1)); } else { if (use_hdr) { - image->create(width, height, false, Image::FORMAT_RGBAF); + image->initialize_data(width, height, false, Image::FORMAT_RGBAF); Gradient &g = **gradient; // `create()` isn't available for non-uint8_t data, so fill in the data manually. for (int y = 0; y < height; y++) { @@ -2367,7 +2364,7 @@ void GradientTexture2D::_update() { } } } - image->create(width, height, false, Image::FORMAT_RGBA8, data); + image->set_data(width, height, false, Image::FORMAT_RGBA8, data); } } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index d7d7b5fe31..3caf6484d9 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -1801,11 +1801,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { // Get the best tile. Ref<Texture2D> texture = counts[terrain_set][terrain].texture; Rect2i region = counts[terrain_set][terrain].region; - image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); + image->initialize_data(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); image->blit_rect(texture->get_image(), region, Point2i()); image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); } else { - image->create(1, 1, false, Image::FORMAT_RGBA8); + image->initialize_data(1, 1, false, Image::FORMAT_RGBA8); image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); } Ref<ImageTexture> icon = ImageTexture::create_from_image(image); @@ -4602,9 +4602,7 @@ void TileSetAtlasSource::_update_padded_texture() { return; } - Ref<Image> image; - image.instantiate(); - image->create(size.x, size.y, false, src->get_format()); + Ref<Image> image = Image::create_empty(size.x, size.y, false, src->get_format()); for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) { for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) { diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index ab7354f6e7..f125b05a26 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -460,9 +460,9 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Vector2> image.instantiate(); if (p_array.size() == 0) { - image->create(1, 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(1, 1, false, Image::Format::FORMAT_RGBF); } else { - image->create(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); } for (int i = 0; i < p_array.size(); i++) { @@ -481,9 +481,9 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Vector3> image.instantiate(); if (p_array.size() == 0) { - image->create(1, 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(1, 1, false, Image::Format::FORMAT_RGBF); } else { - image->create(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); } for (int i = 0; i < p_array.size(); i++) { @@ -502,9 +502,9 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Color> &p image.instantiate(); if (p_array.size() == 0) { - image->create(1, 1, false, Image::Format::FORMAT_RGBA8); + image->initialize_data(1, 1, false, Image::Format::FORMAT_RGBA8); } else { - image->create(p_array.size(), 1, false, Image::Format::FORMAT_RGBA8); + image->initialize_data(p_array.size(), 1, false, Image::Format::FORMAT_RGBA8); } for (int i = 0; i < p_array.size(); i++) { diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.cpp b/servers/physics_3d/godot_collision_solver_3d_sat.cpp index 933a5e28df..96253cb452 100644 --- a/servers/physics_3d/godot_collision_solver_3d_sat.cpp +++ b/servers/physics_3d/godot_collision_solver_3d_sat.cpp @@ -964,8 +964,8 @@ static void _collision_sphere_convex_polygon(const GodotShape3D *p_a, const Tran // edges of B for (int i = 0; i < edge_count; i++) { - Vector3 v1 = p_transform_b.xform(vertices[edges[i].a]); - Vector3 v2 = p_transform_b.xform(vertices[edges[i].b]); + Vector3 v1 = p_transform_b.xform(vertices[edges[i].vertex_a]); + Vector3 v2 = p_transform_b.xform(vertices[edges[i].vertex_b]); Vector3 v3 = p_transform_a.origin; Vector3 n1 = v2 - v1; @@ -1404,7 +1404,7 @@ static void _collision_box_convex_polygon(const GodotShape3D *p_a, const Transfo Vector3 e1 = p_transform_a.basis.get_column(i); for (int j = 0; j < edge_count; j++) { - Vector3 e2 = p_transform_b.basis.xform(vertices[edges[j].a]) - p_transform_b.basis.xform(vertices[edges[j].b]); + Vector3 e2 = p_transform_b.basis.xform(vertices[edges[j].vertex_a]) - p_transform_b.basis.xform(vertices[edges[j].vertex_b]); Vector3 axis = e1.cross(e2).normalized(); @@ -1460,8 +1460,8 @@ static void _collision_box_convex_polygon(const GodotShape3D *p_a, const Transfo } for (int e = 0; e < edge_count; e++) { - Vector3 p1 = p_transform_b.xform(vertices[edges[e].a]); - Vector3 p2 = p_transform_b.xform(vertices[edges[e].b]); + Vector3 p1 = p_transform_b.xform(vertices[edges[e].vertex_a]); + Vector3 p2 = p_transform_b.xform(vertices[edges[e].vertex_b]); Vector3 n = (p2 - p1); if (!separator.test_axis((point - p2).cross(n).cross(n).normalized())) { @@ -1771,7 +1771,7 @@ static void _collision_capsule_convex_polygon(const GodotShape3D *p_a, const Tra for (int i = 0; i < edge_count; i++) { // cylinder - Vector3 edge_axis = p_transform_b.basis.xform(vertices[edges[i].a]) - p_transform_b.basis.xform(vertices[edges[i].b]); + Vector3 edge_axis = p_transform_b.basis.xform(vertices[edges[i].vertex_a]) - p_transform_b.basis.xform(vertices[edges[i].vertex_b]); Vector3 axis = edge_axis.cross(p_transform_a.basis.get_column(1)).normalized(); if (!separator.test_axis(axis)) { @@ -1789,8 +1789,8 @@ static void _collision_capsule_convex_polygon(const GodotShape3D *p_a, const Tra Vector3 sphere_pos = p_transform_a.origin + ((i == 0) ? capsule_axis : -capsule_axis); for (int j = 0; j < edge_count; j++) { - Vector3 n1 = sphere_pos - p_transform_b.xform(vertices[edges[j].a]); - Vector3 n2 = p_transform_b.basis.xform(vertices[edges[j].a]) - p_transform_b.basis.xform(vertices[edges[j].b]); + Vector3 n1 = sphere_pos - p_transform_b.xform(vertices[edges[j].vertex_a]); + Vector3 n2 = p_transform_b.basis.xform(vertices[edges[j].vertex_a]) - p_transform_b.basis.xform(vertices[edges[j].vertex_b]); Vector3 axis = n1.cross(n2).cross(n2).normalized(); @@ -2075,6 +2075,16 @@ static void _collision_cylinder_face(const GodotShape3D *p_a, const Transform3D separator.generate_contacts(); } +static _FORCE_INLINE_ bool is_minkowski_face(const Vector3 &A, const Vector3 &B, const Vector3 &B_x_A, const Vector3 &C, const Vector3 &D, const Vector3 &D_x_C) { + // Test if arcs AB and CD intersect on the unit sphere + real_t CBA = C.dot(B_x_A); + real_t DBA = D.dot(B_x_A); + real_t ADC = A.dot(D_x_C); + real_t BDC = B.dot(D_x_C); + + return (CBA * DBA < 0.0f) && (ADC * BDC < 0.0f) && (CBA * BDC > 0.0f); +} + template <bool withMargin> static void _collision_convex_polygon_convex_polygon(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { const GodotConvexPolygonShape3D *convex_polygon_A = static_cast<const GodotConvexPolygonShape3D *>(p_a); @@ -2129,16 +2139,27 @@ static void _collision_convex_polygon_convex_polygon(const GodotShape3D *p_a, co } // A<->B edges + for (int i = 0; i < edge_count_A; i++) { - Vector3 e1 = p_transform_a.basis.xform(vertices_A[edges_A[i].a]) - p_transform_a.basis.xform(vertices_A[edges_A[i].b]); + Vector3 p1 = p_transform_a.xform(vertices_A[edges_A[i].vertex_a]); + Vector3 q1 = p_transform_a.xform(vertices_A[edges_A[i].vertex_b]); + Vector3 e1 = q1 - p1; + Vector3 u1 = p_transform_a.basis.xform(faces_A[edges_A[i].face_a].plane.normal).normalized(); + Vector3 v1 = p_transform_a.basis.xform(faces_A[edges_A[i].face_b].plane.normal).normalized(); for (int j = 0; j < edge_count_B; j++) { - Vector3 e2 = p_transform_b.basis.xform(vertices_B[edges_B[j].a]) - p_transform_b.basis.xform(vertices_B[edges_B[j].b]); + Vector3 p2 = p_transform_b.xform(vertices_B[edges_B[j].vertex_a]); + Vector3 q2 = p_transform_b.xform(vertices_B[edges_B[j].vertex_b]); + Vector3 e2 = q2 - p2; + Vector3 u2 = p_transform_b.basis.xform(faces_B[edges_B[j].face_a].plane.normal).normalized(); + Vector3 v2 = p_transform_b.basis.xform(faces_B[edges_B[j].face_b].plane.normal).normalized(); - Vector3 axis = e1.cross(e2).normalized(); + if (is_minkowski_face(u1, v1, -e1, -u2, -v2, -e2)) { + Vector3 axis = e1.cross(e2).normalized(); - if (!separator.test_axis(axis)) { - return; + if (!separator.test_axis(axis)) { + return; + } } } } @@ -2157,8 +2178,8 @@ static void _collision_convex_polygon_convex_polygon(const GodotShape3D *p_a, co //edge-vertex (shell) for (int i = 0; i < edge_count_A; i++) { - Vector3 e1 = p_transform_a.basis.xform(vertices_A[edges_A[i].a]); - Vector3 e2 = p_transform_a.basis.xform(vertices_A[edges_A[i].b]); + Vector3 e1 = p_transform_a.basis.xform(vertices_A[edges_A[i].vertex_a]); + Vector3 e2 = p_transform_a.basis.xform(vertices_A[edges_A[i].vertex_b]); Vector3 n = (e2 - e1); for (int j = 0; j < vertex_count_B; j++) { @@ -2171,8 +2192,8 @@ static void _collision_convex_polygon_convex_polygon(const GodotShape3D *p_a, co } for (int i = 0; i < edge_count_B; i++) { - Vector3 e1 = p_transform_b.basis.xform(vertices_B[edges_B[i].a]); - Vector3 e2 = p_transform_b.basis.xform(vertices_B[edges_B[i].b]); + Vector3 e1 = p_transform_b.basis.xform(vertices_B[edges_B[i].vertex_a]); + Vector3 e2 = p_transform_b.basis.xform(vertices_B[edges_B[i].vertex_b]); Vector3 n = (e2 - e1); for (int j = 0; j < vertex_count_A; j++) { @@ -2231,7 +2252,7 @@ static void _collision_convex_polygon_face(const GodotShape3D *p_a, const Transf // A<->B edges for (int i = 0; i < edge_count; i++) { - Vector3 e1 = p_transform_a.xform(vertices[edges[i].a]) - p_transform_a.xform(vertices[edges[i].b]); + Vector3 e1 = p_transform_a.xform(vertices[edges[i].vertex_a]) - p_transform_a.xform(vertices[edges[i].vertex_b]); for (int j = 0; j < 3; j++) { Vector3 e2 = vertex[j] - vertex[(j + 1) % 3]; @@ -2266,8 +2287,8 @@ static void _collision_convex_polygon_face(const GodotShape3D *p_a, const Transf //edge-vertex (shell) for (int i = 0; i < edge_count; i++) { - Vector3 e1 = p_transform_a.basis.xform(vertices[edges[i].a]); - Vector3 e2 = p_transform_a.basis.xform(vertices[edges[i].b]); + Vector3 e1 = p_transform_a.basis.xform(vertices[edges[i].vertex_a]); + Vector3 e2 = p_transform_a.basis.xform(vertices[edges[i].vertex_b]); Vector3 n = (e2 - e1); for (int j = 0; j < 3; j++) { diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp index e051c688fa..cd61ceab62 100644 --- a/servers/physics_3d/godot_shape_3d.cpp +++ b/servers/physics_3d/godot_shape_3d.cpp @@ -915,13 +915,13 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max, } for (int i = 0; i < ec; i++) { - real_t dot = (vertices[edges[i].a] - vertices[edges[i].b]).normalized().dot(p_normal); + real_t dot = (vertices[edges[i].vertex_a] - vertices[edges[i].vertex_b]).normalized().dot(p_normal); dot = ABS(dot); - if (dot < edge_support_threshold && (edges[i].a == vtx || edges[i].b == vtx)) { + if (dot < edge_support_threshold && (edges[i].vertex_a == vtx || edges[i].vertex_b == vtx)) { r_amount = 2; r_type = FEATURE_EDGE; - r_supports[0] = vertices[edges[i].a]; - r_supports[1] = vertices[edges[i].b]; + r_supports[0] = vertices[edges[i].vertex_a]; + r_supports[1] = vertices[edges[i].vertex_b]; return; } } @@ -1025,8 +1025,8 @@ Vector3 GodotConvexPolygonShape3D::get_closest_point_to(const Vector3 &p_point) int ec = mesh.edges.size(); for (int i = 0; i < ec; i++) { Vector3 s[2] = { - vertices[edges[i].a], - vertices[edges[i].b] + vertices[edges[i].vertex_a], + vertices[edges[i].vertex_b] }; Vector3 closest = Geometry3D::get_closest_point_to_segment(p_point, s); @@ -1058,7 +1058,7 @@ void GodotConvexPolygonShape3D::_setup(const Vector<Vector3> &p_vertices) { AABB _aabb; - for (int i = 0; i < mesh.vertices.size(); i++) { + for (uint32_t i = 0; i < mesh.vertices.size(); i++) { if (i == 0) { _aabb.position = mesh.vertices[i]; } else { @@ -1074,7 +1074,12 @@ void GodotConvexPolygonShape3D::set_data(const Variant &p_data) { } Variant GodotConvexPolygonShape3D::get_data() const { - return mesh.vertices; + Vector<Vector3> vertices; + vertices.resize(mesh.vertices.size()); + for (uint32_t i = 0; i < mesh.vertices.size(); i++) { + vertices.write[i] = mesh.vertices[i]; + } + return vertices; } GodotConvexPolygonShape3D::GodotConvexPolygonShape3D() { diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 9b6be0b422..f4a27144d9 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -2335,7 +2335,7 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_ img.instantiate(); for (uint32_t i = 0; i < cascade_size; i++) { Vector<uint8_t> subarr = data.slice(128 * 128 * i, 128 * 128 * (i + 1)); - img->create(cascade_size, cascade_size, false, Image::FORMAT_L8, subarr); + img->set_data(cascade_size, cascade_size, false, Image::FORMAT_L8, subarr); img->save_png("res://cascade_sdf_" + itos(cascade) + "_" + itos(i) + ".png"); } diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 4ff2a66975..6940276040 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -772,9 +772,7 @@ Ref<Image> SkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, con Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0); RD::get_singleton()->free(rad_tex); - Ref<Image> img; - img.instantiate(); - img->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data); + Ref<Image> img = Image::create_from_data(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data); for (int i = 0; i < p_size.width; i++) { for (int j = 0; j < p_size.height; j++) { Color c = img->get_pixel(i, j); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 039a2d2bf4..59a8cdf30a 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -940,12 +940,13 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con // LOD if (p_render_data->scene_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - //lod - Vector3 lod_support_min = inst->transformed_aabb.get_support(-p_render_data->scene_data->lod_camera_plane.normal); - Vector3 lod_support_max = inst->transformed_aabb.get_support(p_render_data->scene_data->lod_camera_plane.normal); + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - float distance_min = p_render_data->scene_data->lod_camera_plane.distance_to(lod_support_min); - float distance_max = p_render_data->scene_data->lod_camera_plane.distance_to(lod_support_max); + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max); float distance = 0.0; @@ -1830,7 +1831,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID()); bool finish_depth = using_ssao || using_sdfgi || using_voxelgi; - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_camera_plane, p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); _render_list_with_threads(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear); RD::get_singleton()->draw_command_end_label(); @@ -1900,7 +1901,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } } - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_camera_plane, p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); _render_list_with_threads(&render_list_params, color_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); if (will_continue_color && using_separate_specular) { // close the specular framebuffer, as it's no longer used @@ -2021,7 +2022,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co { uint32_t transparent_color_pass_flags = (color_pass_flags | COLOR_PASS_FLAG_TRANSPARENT) & ~(COLOR_PASS_FLAG_SEPARATE_SPECULAR); RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer; - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_camera_plane, p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); _render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); } @@ -2307,7 +2308,6 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page scene_data.view_projection[0] = p_projection; scene_data.z_far = p_zfar; scene_data.z_near = 0.0; - scene_data.lod_camera_plane = p_camera_plane; scene_data.lod_distance_multiplier = p_lod_distance_multiplier; scene_data.dual_paraboloid_side = p_use_dp_flip ? -1 : 1; scene_data.opaque_prepass_threshold = 0.1f; @@ -2378,7 +2378,7 @@ void RenderForwardClustered::_render_shadow_end(uint32_t p_barrier) { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); + RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); _render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 55f1ef01af..670eb93e20 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -206,7 +206,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { RID render_pass_uniform_set; bool force_wireframe = false; Vector2 uv_offset; - Plane lod_plane; float lod_distance_multiplier = 0.0; float screen_mesh_lod_threshold = 0.0; RD::FramebufferFormatID framebuffer_format = 0; @@ -214,7 +213,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { uint32_t barrier = RD::BARRIER_MASK_ALL; bool use_directional_soft_shadow = false; - RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { + RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { elements = p_elements; element_info = p_element_info; element_count = p_element_count; @@ -226,7 +225,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { render_pass_uniform_set = p_render_pass_uniform_set; force_wireframe = p_force_wireframe; uv_offset = p_uv_offset; - lod_plane = p_lod_plane; lod_distance_multiplier = p_lod_distance_multiplier; screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; element_offset = p_element_offset; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 2b2090f8ed..2dfdb302dc 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -916,7 +916,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_camera_plane, p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); render_list_params.framebuffer_format = fb_format; if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { // secondary command buffers need more testing at this time @@ -983,7 +983,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (using_subpass_transparent) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR_TRANSPARENT, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_camera_plane, p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR_TRANSPARENT, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); render_list_params.framebuffer_format = fb_format; if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { // secondary command buffers need more testing at this time @@ -1022,7 +1022,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_camera_plane, p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); render_list_params.framebuffer_format = fb_format; if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { // secondary command buffers need more testing at this time @@ -1266,7 +1266,6 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr scene_data.view_projection[0] = p_projection; scene_data.z_near = 0.0; scene_data.z_far = p_zfar; - scene_data.lod_camera_plane = p_camera_plane; scene_data.lod_distance_multiplier = p_lod_distance_multiplier; scene_data.dual_paraboloid_side = p_use_dp_flip ? -1 : 1; scene_data.opaque_prepass_threshold = 0.1; @@ -1335,7 +1334,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); + RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); _render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect); } @@ -1808,12 +1807,13 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->scene_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - //lod - Vector3 lod_support_min = inst->transformed_aabb.get_support(-p_render_data->scene_data->lod_camera_plane.normal); - Vector3 lod_support_max = inst->transformed_aabb.get_support(p_render_data->scene_data->lod_camera_plane.normal); + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - float distance_min = p_render_data->scene_data->lod_camera_plane.distance_to(lod_support_min); - float distance_max = p_render_data->scene_data->lod_camera_plane.distance_to(lod_support_max); + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max); float distance = 0.0; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 415bd79ad6..ce64d805b7 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -163,7 +163,6 @@ private: RID render_pass_uniform_set; bool force_wireframe = false; Vector2 uv_offset; - Plane lod_plane; uint32_t spec_constant_base_flags = 0; float lod_distance_multiplier = 0.0; float screen_mesh_lod_threshold = 0.0; @@ -172,7 +171,7 @@ private: uint32_t barrier = RD::BARRIER_MASK_ALL; uint32_t subpass = 0; - RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { + RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { elements = p_elements; element_info = p_element_info; element_count = p_element_count; @@ -183,7 +182,6 @@ private: render_pass_uniform_set = p_render_pass_uniform_set; force_wireframe = p_force_wireframe; uv_offset = p_uv_offset; - lod_plane = p_lod_plane; lod_distance_multiplier = p_lod_distance_multiplier; screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; element_offset = p_element_offset; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 061263ef76..75fe84f46b 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -250,9 +250,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba panorama_color = ambient_color.lerp(panorama_color, ambient_color_sky_mix); } - Ref<Image> panorama; - panorama.instantiate(); - panorama->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF); + Ref<Image> panorama = Image::create_empty(p_size.width, p_size.height, false, Image::FORMAT_RGBAF); panorama->fill(panorama_color); return panorama; } @@ -1101,7 +1099,6 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render // this should be the same for all cameras.. scene_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); - scene_data.lod_camera_plane = Plane(-p_camera_data->main_transform.basis.get_column(Vector3::AXIS_Z), p_camera_data->main_transform.get_origin()); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { scene_data.screen_mesh_lod_threshold = 0.0; @@ -1306,36 +1303,28 @@ TypedArray<Image> RendererSceneRenderRD::bake_render_uv2(RID p_base, const Typed { PackedByteArray data = RD::get_singleton()->texture_get_data(albedo_alpha_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); RD::get_singleton()->free(albedo_alpha_tex); ret.push_back(img); } { PackedByteArray data = RD::get_singleton()->texture_get_data(normal_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); RD::get_singleton()->free(normal_tex); ret.push_back(img); } { PackedByteArray data = RD::get_singleton()->texture_get_data(orm_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); RD::get_singleton()->free(orm_tex); ret.push_back(img); } { PackedByteArray data = RD::get_singleton()->texture_get_data(emission_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBAH, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBAH, data); RD::get_singleton()->free(emission_tex); ret.push_back(img); } diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 45dc63aa17..4a18c7c052 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -598,13 +598,11 @@ void main() { normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz); } - vec3 base_color = color.rgb; + vec4 base_color = color; if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) { color = vec4(0.0); //invisible by default due to using light mask } - vec4 original_color = color; - #ifdef MODE_LIGHT_ONLY color = vec4(0.0); #elif !defined(MODE_UNSHADED) @@ -624,12 +622,14 @@ void main() { #ifdef LIGHT_CODE_USED vec4 shadow_modulate = vec4(1.0); - light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, true); + light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true); #else if (normal_used) { vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height)); - light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; } #endif @@ -646,8 +646,6 @@ void main() { ); } - light_color.rgb *= original_color.rgb; - light_blend_compute(light_base, light_color, color.rgb); } @@ -685,7 +683,7 @@ void main() { vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height); light_color.rgb *= light_base_color.rgb; - light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, false); + light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false); #else light_color.rgb *= light_base_color.rgb * light_base_color.a; @@ -695,7 +693,9 @@ void main() { vec3 pos = light_vertex; vec3 light_vec = normalize(light_pos - pos); - light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; } #endif if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { @@ -743,8 +743,6 @@ void main() { ); } - light_color.rgb *= original_color.rgb; - light_blend_compute(light_base, light_color, color.rgb); } #endif diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl index eed9038502..28507e6c12 100644 --- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl @@ -381,7 +381,7 @@ void main() { float cell_depth_size = abs(view_pos.z - get_depth_at_pos(fog_cell_size.z, pos.z + 1)); //compute directional lights - if (total_density > 0.001) { + if (total_density > 0.00005) { for (uint i = 0; i < params.directional_light_count; i++) { if (directional_lights.data[i].volumetric_fog_energy > 0.001) { vec3 shadow_attenuation = vec3(1.0); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h index c2dc7d5f4c..9c031acc1e 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h @@ -61,7 +61,6 @@ public: float z_far = 0.0; float lod_distance_multiplier = 0.0; - Plane lod_camera_plane; float screen_mesh_lod_threshold = 0.0; uint32_t directional_light_count = 0; diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index d5285c07f4..bc70c57b69 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1124,9 +1124,7 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); texture_2d_initialize(p_texture, image); @@ -1135,9 +1133,7 @@ void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -1156,9 +1152,7 @@ void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -1181,9 +1175,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { #endif Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (tex->format != tex->validated_format) { image->convert(tex->format); @@ -1204,9 +1196,7 @@ Ref<Image> TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, p_layer); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (tex->format != tex->validated_format) { image->convert(tex->format); @@ -1232,9 +1222,7 @@ Vector<Ref<Image>> TextureStorage::texture_3d_get(RID p_texture) const { ERR_FAIL_COND_V(bs.offset + bs.buffer_size > (uint32_t)all_data.size(), Vector<Ref<Image>>()); Vector<uint8_t> sub_region = all_data.slice(bs.offset, bs.offset + bs.buffer_size); - Ref<Image> img; - img.instantiate(); - img->create(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); + Ref<Image> img = Image::create_from_data(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); ERR_FAIL_COND_V(img->is_empty(), Vector<Ref<Image>>()); if (tex->format != tex->validated_format) { img->convert(tex->format); diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp index bda0950097..e1ca5a7103 100644 --- a/servers/rendering/renderer_scene_occlusion_cull.cpp +++ b/servers/rendering/renderer_scene_occlusion_cull.cpp @@ -180,7 +180,7 @@ RID RendererSceneOcclusionCull::HZBuffer::get_debug_texture() { ptrw[i] = MIN(mips[0][i] / debug_tex_range, 1.0) * 255; } - debug_image->create(sizes[0].x, sizes[0].y, false, Image::FORMAT_L8, debug_data); + debug_image->set_data(sizes[0].x, sizes[0].y, false, Image::FORMAT_L8, debug_data); if (debug_texture.is_null()) { debug_texture = RS::get_singleton()->texture_2d_create(debug_image); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index e12c4fc79a..cf13118451 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2791,10 +2791,10 @@ void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry Vector<Vector3> vertices; Vector<Vector3> normals; - for (int i = 0; i < p_mesh_data.faces.size(); i++) { + for (uint32_t i = 0; i < p_mesh_data.faces.size(); i++) { const Geometry3D::MeshData::Face &f = p_mesh_data.faces[i]; - for (int j = 2; j < f.indices.size(); j++) { + for (uint32_t j = 2; j < f.indices.size(); j++) { vertices.push_back(p_mesh_data.vertices[f.indices[0]]); normals.push_back(f.plane.normal); diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 430e2042d9..a5ee1d5726 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -222,12 +222,7 @@ RID XRInterface::get_vrs_texture() { data_ptr[d++] = density; } } - - Ref<Image> image; - image.instantiate(); - image->create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_R8, data); - - images.push_back(image); + images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_R8, data)); } if (images.size() == 1) { diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 969f5fc096..cf34caac28 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1424,20 +1424,22 @@ TEST_CASE("[String] dedent") { } TEST_CASE("[String] Path functions") { - static const char *path[7] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc", "C:\\test.", "res://test", "/.test" }; - static const char *base_dir[7] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot", "C:\\", "res://", "/" }; - static const char *base_name[7] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "/" }; - static const char *ext[7] = { "tscn", "xscn", "scn", "doc", "", "", "test" }; - static const char *file[7] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", ".test" }; - static const bool abs[7] = { true, true, false, false, true, true, true }; - - for (int i = 0; i < 7; i++) { + static const char *path[8] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc", "C:\\test.", "res://test", "user://test", "/.test" }; + static const char *base_dir[8] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot", "C:\\", "res://", "user://", "/" }; + static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" }; + static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" }; + static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" }; + static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" }; + static const bool abs[8] = { true, true, false, false, true, true, true, true }; + + for (int i = 0; i < 8; i++) { CHECK(String(path[i]).get_base_dir() == base_dir[i]); CHECK(String(path[i]).get_basename() == base_name[i]); CHECK(String(path[i]).get_extension() == ext[i]); CHECK(String(path[i]).get_file() == file[i]); CHECK(String(path[i]).is_absolute_path() == abs[i]); CHECK(String(path[i]).is_relative_path() != abs[i]); + CHECK(String(path[i]).simplify_path() == String(simplified[i])); CHECK(String(path[i]).simplify_path().get_base_dir().path_join(file[i]) == String(path[i]).simplify_path()); } diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h index 635449181e..a102f40725 100644 --- a/tests/scene/test_bit_map.h +++ b/tests/scene/test_bit_map.h @@ -76,15 +76,11 @@ TEST_CASE("[BitMap] Create bit map from image alpha") { bit_map.create_from_image_alpha(empty_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because bitmap creation from an empty image should fail."); - Ref<Image> wrong_format_img; - wrong_format_img.instantiate(); - wrong_format_img->create(3, 3, false, Image::Format::FORMAT_DXT1); + Ref<Image> wrong_format_img = Image::create_empty(3, 3, false, Image::Format::FORMAT_DXT1); bit_map.create_from_image_alpha(wrong_format_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because converting from a compressed image should fail."); - Ref<Image> img; - img.instantiate(); - img->create(3, 3, false, Image::Format::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(3, 3, false, Image::Format::FORMAT_RGBA8); img->set_pixel(0, 0, Color(0, 0, 0, 0)); img->set_pixel(0, 1, Color(0, 0, 0, 0.09f)); img->set_pixel(0, 2, Color(0, 0, 0, 0.25f)); diff --git a/thirdparty/README.md b/thirdparty/README.md index ac97d9c2a2..19c155a2d4 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -656,7 +656,7 @@ instead of `miniz.h` as an external dependency. ## thorvg - Upstream: https://github.com/Samsung/thorvg -- Version: 0.8.1 (c4ccb1078f4390ec749ab8e05ba7e9e35f81285f, 2022) +- Version: 0.8.2 (496796f1e5e85bd5fbba36dae987edb1b3945592, 2022) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 879b70442b..68935c583b 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -13,5 +13,5 @@ #define THORVG_JPG_LOADER_SUPPORT 1 -#define THORVG_VERSION_STRING "0.8.1" +#define THORVG_VERSION_STRING "0.8.2" #endif diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h index 47b0cb83f5..157fdb8f82 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp index bba6f26a0b..04014a9ec3 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp index ced66ae35c..1027bb1f79 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp index 810df8d435..bf1c10a0c3 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,8 +22,8 @@ #ifdef _WIN32 #include <malloc.h> -#elif defined(__FreeBSD__) - #include <stdlib.h> +#elif __FreeBSD__ + #include<stdlib.h> #else #include <alloca.h> #endif diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp index be4392740e..fa213cc5d3 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp deleted file mode 100644 index 522c3c71a6..0000000000 --- a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. - - * 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 <memory.h> -#include <turbojpeg.h> -#include "tvgLoader.h" -#include "tvgJpgLoader.h" - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - -void JpgLoader::clear() -{ - if (freeData) free(data); - data = nullptr; - size = 0; - freeData = false; -} - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -JpgLoader::JpgLoader() -{ - jpegDecompressor = tjInitDecompress(); -} - - -JpgLoader::~JpgLoader() -{ - if (freeData) free(data); - tjDestroy(jpegDecompressor); - - //This image is shared with raster engine. - tjFree(image); -} - - -bool JpgLoader::open(const string& path) -{ - clear(); - - auto jpegFile = fopen(path.c_str(), "rb"); - if (!jpegFile) return false; - - auto ret = false; - - //determine size - if (fseek(jpegFile, 0, SEEK_END) < 0) goto finalize; - if (((size = ftell(jpegFile)) < 1)) goto finalize; - if (fseek(jpegFile, 0, SEEK_SET)) goto finalize; - - data = (unsigned char *) malloc(size); - if (!data) goto finalize; - - freeData = true; - - if (fread(data, size, 1, jpegFile) < 1) goto failure; - - int width, height, subSample, colorSpace; - if (tjDecompressHeader3(jpegDecompressor, data, size, &width, &height, &subSample, &colorSpace) < 0) { - TVGERR("JPG LOADER", "%s", tjGetErrorStr()); - goto failure; - } - - w = static_cast<float>(width); - h = static_cast<float>(height); - ret = true; - - goto finalize; - -failure: - clear(); - -finalize: - fclose(jpegFile); - return ret; -} - - -bool JpgLoader::open(const char* data, uint32_t size, bool copy) -{ - clear(); - - int width, height, subSample, colorSpace; - if (tjDecompressHeader3(jpegDecompressor, (unsigned char *) data, size, &width, &height, &subSample, &colorSpace) < 0) return false; - - if (copy) { - this->data = (unsigned char *) malloc(size); - if (!this->data) return false; - memcpy((unsigned char *)this->data, data, size); - freeData = true; - } else { - this->data = (unsigned char *) data; - freeData = false; - } - - w = static_cast<float>(width); - h = static_cast<float>(height); - this->size = size; - - return true; -} - - -bool JpgLoader::read() -{ - if (image) tjFree(image); - image = (unsigned char *)tjAlloc(static_cast<int>(w) * static_cast<int>(h) * tjPixelSize[TJPF_BGRX]); - if (!image) return false; - - //decompress jpg image - if (tjDecompress2(jpegDecompressor, data, size, image, static_cast<int>(w), 0, static_cast<int>(h), TJPF_BGRX, 0) < 0) { - TVGERR("JPG LOADER", "%s", tjGetErrorStr()); - tjFree(image); - image = nullptr; - return false; - } - - return true; -} - - -bool JpgLoader::close() -{ - clear(); - return true; -} - - -unique_ptr<Surface> JpgLoader::bitmap() -{ - if (!image) return nullptr; - - auto surface = static_cast<Surface*>(malloc(sizeof(Surface))); - surface->buffer = (uint32_t*)(image); - surface->stride = w; - surface->w = w; - surface->h = h; - surface->cs = SwCanvas::ARGB8888; - - return unique_ptr<Surface>(surface); -} diff --git a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h deleted file mode 100644 index 3f82af8003..0000000000 --- a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. - - * 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 _TVG_JPG_LOADER_H_ -#define _TVG_JPG_LOADER_H_ - -using tjhandle = void*; - -//TODO: Use Task? -class JpgLoader : public LoadModule -{ -public: - JpgLoader(); - ~JpgLoader(); - - using LoadModule::open; - bool open(const string& path) override; - bool open(const char* data, uint32_t size, bool copy) override; - bool read() override; - bool close() override; - - unique_ptr<Surface> bitmap() override; - -private: - void clear(); - - tjhandle jpegDecompressor; - unsigned char* data = nullptr; - unsigned char *image = nullptr; - unsigned long size = 0; - bool freeData = false; -}; - -#endif //_TVG_JPG_LOADER_H_ diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp deleted file mode 100644 index eaed025c54..0000000000 --- a/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp +++ /dev/null @@ -1,2647 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. - - * 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. - */ - -/* - LodePNG version 20200306 - - Copyright (c) 2005-2020 Lode Vandevenne - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any sourcedistribution. -*/ - -#include <cstdlib> -#include "tvgLodePng.h" - - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - -#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ - #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ - #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ -#endif /*_MSC_VER */ - - -/* convince the compiler to inline a function, for use when this measurably improves performance */ -/* inline is not available in C90, but use it when supported by the compiler */ -#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) - #define LODEPNG_INLINE inline -#else - #define LODEPNG_INLINE /* not available */ -#endif - -/* restrict is not available in C90, but use it when supported by the compiler */ -#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ - (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ - (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) - #define LODEPNG_RESTRICT __restrict -#else - #define LODEPNG_RESTRICT /* not available */ -#endif - -#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x)) - - -/* Replacements for C library functions such as memcpy and strlen, to support platforms -where a full C library is not available. The compiler can recognize them and compile -to something as fast. */ - -static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size) -{ - size_t i; - for (i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; -} - - -static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num) -{ - size_t i; - for (i = 0; i < num; i++) ((char*)dst)[i] = (char)value; -} - - -/* does not check memory out of bounds, do not use on untrusted data */ -static size_t lodepng_strlen(const char* a) -{ - const char* orig = a; - /* avoid warning about unused function in case of disabled COMPILE... macros */ - (void)(&lodepng_strlen); - while (*a) a++; - return (size_t)(a - orig); -} - - -/* Safely check if adding two integers will overflow (no undefined -behavior, compiler removing the code, etc...) and output result. */ -static int lodepng_addofl(size_t a, size_t b, size_t* result) -{ - *result = a + b; /* Unsigned addition is well defined and safe in C90 */ - return *result < a; -} - - -/* Safely check if multiplying two integers will overflow (no undefined -behavior, compiler removing the code, etc...) and output result. */ -static int lodepng_mulofl(size_t a, size_t b, size_t* result) -{ - *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ - return (a != 0 && *result / a != b); -} - - -/* Safely check if a + b > c, even if overflow could happen. */ -static int lodepng_gtofl(size_t a, size_t b, size_t c) -{ - size_t d; - if (lodepng_addofl(a, b, &d)) return 1; - return d > c; -} - - -/* - Often in case of an error a value is assigned to a variable and then it breaks - out of a loop (to go to the cleanup phase of a function). This macro does that. - It makes the error handling code shorter and more readable. - - Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83); -*/ -#define CERROR_BREAK(errorvar, code){\ - errorvar = code;\ - break;\ -} - -/* version of CERROR_BREAK that assumes the common case where the error variable is named "error" */ -#define ERROR_BREAK(code) CERROR_BREAK(error, code) - -/* Set error var to the error code, and return it.*/ -#define CERROR_RETURN_ERROR(errorvar, code){\ - errorvar = code;\ - return code;\ -} - -/* Try the code, if it returns error, also return the error. */ -#define CERROR_TRY_RETURN(call){\ - unsigned error = call;\ - if(error) return error;\ -} - -/* Set error var to the error code, and return from the void function. */ -#define CERROR_RETURN(errorvar, code){\ - errorvar = code;\ - return;\ -} - - -/* dynamic vector of unsigned chars */ -struct ucvector -{ - unsigned char* data; - size_t size; /*used size*/ - size_t allocsize; /*allocated size*/ -}; - - -/* returns 1 if success, 0 if failure ==> nothing done */ -static unsigned ucvector_resize(ucvector* p, size_t size) -{ - if (size > p->allocsize) { - size_t newsize = size + (p->allocsize >> 1u); - void* data = realloc(p->data, newsize); - if(data) { - p->allocsize = newsize; - p->data = (unsigned char*)data; - } - else return 0; /*error: not enough memory*/ - } - p->size = size; - return 1; /*success*/ -} - - -static ucvector ucvector_init(unsigned char* buffer, size_t size) -{ - ucvector v; - v.data = buffer; - v.allocsize = v.size = size; - return v; -} - - -static unsigned lodepng_read32bitInt(const unsigned char* buffer) -{ - return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]); -} - - -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* // End of common code and tools. Begin of Zlib related code. // */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ - -struct LodePNGBitReader -{ - const unsigned char* data; - size_t size; /*size of data in bytes*/ - size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/ - size_t bp; - unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/ -}; - - -/* data size argument is in bytes. Returns error if size too large causing overflow */ -static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) -{ - size_t temp; - reader->data = data; - reader->size = size; - /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */ - if (lodepng_mulofl(size, 8u, &reader->bitsize)) return 105; - /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and - trying to ensure 32 more bits*/ - if (lodepng_addofl(reader->bitsize, 64u, &temp)) return 105; - reader->bp = 0; - reader->buffer = 0; - return 0; /*ok*/ - } - -/* - ensureBits functions: - Ensures the reader can at least read nbits bits in one or more readBits calls, - safely even if not enough bits are available. - Returns 1 if there are enough bits available, 0 if not. -*/ - -/*See ensureBits documentation above. This one ensures exactly 1 bit */ -/*static unsigned ensureBits1(LodePNGBitReader* reader) { - if(reader->bp >= reader->bitsize) return 0; - reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u); - return 1; -}*/ - -/*See ensureBits documentation above. This one ensures up to 9 bits */ -static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) -{ - size_t start = reader->bp >> 3u; - size_t size = reader->size; - if (start + 1u < size) { - reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u); - reader->buffer >>= (reader->bp & 7u); - return 1; - } else { - reader->buffer = 0; - if (start + 0u < size) reader->buffer |= reader->data[start + 0]; - reader->buffer >>= (reader->bp & 7u); - return reader->bp + nbits <= reader->bitsize; - } -} - - -/*See ensureBits documentation above. This one ensures up to 17 bits */ -static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) -{ - size_t start = reader->bp >> 3u; - size_t size = reader->size; - if (start + 2u < size) { - reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u); - reader->buffer >>= (reader->bp & 7u); - return 1; - } else { - reader->buffer = 0; - if (start + 0u < size) reader->buffer |= reader->data[start + 0]; - if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); - reader->buffer >>= (reader->bp & 7u); - return reader->bp + nbits <= reader->bitsize; - } -} - - -/*See ensureBits documentation above. This one ensures up to 25 bits */ -static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) -{ - size_t start = reader->bp >> 3u; - size_t size = reader->size; - if (start + 3u < size) { - reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); - reader->buffer >>= (reader->bp & 7u); - return 1; - } else { - reader->buffer = 0; - if (start + 0u < size) reader->buffer |= reader->data[start + 0]; - if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); - if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); - reader->buffer >>= (reader->bp & 7u); - return reader->bp + nbits <= reader->bitsize; - } -} - - -/*See ensureBits documentation above. This one ensures up to 32 bits */ -static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) -{ - size_t start = reader->bp >> 3u; - size_t size = reader->size; - if(start + 4u < size) { - reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); - reader->buffer >>= (reader->bp & 7u); - reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u))); - return 1; - } else { - reader->buffer = 0; - if (start + 0u < size) reader->buffer |= reader->data[start + 0]; - if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); - if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); - if (start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u); - reader->buffer >>= (reader->bp & 7u); - return reader->bp + nbits <= reader->bitsize; - } -} - - -/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */ -static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) -{ - /* The shift allows nbits to be only up to 31. */ - return reader->buffer & ((1u << nbits) - 1u); -} - - -/* Must have enough bits available with ensureBits */ -static void advanceBits(LodePNGBitReader* reader, size_t nbits) -{ - reader->buffer >>= nbits; - reader->bp += nbits; -} - - -/* Must have enough bits available with ensureBits */ -static unsigned readBits(LodePNGBitReader* reader, size_t nbits) -{ - unsigned result = peekBits(reader, nbits); - advanceBits(reader, nbits); - return result; -} - - -/* Public for testing only. steps and result must have numsteps values. */ -unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result) -{ - size_t i; - LodePNGBitReader reader; - unsigned error = LodePNGBitReader_init(&reader, data, size); - if (error) return 0; - for (i = 0; i < numsteps; i++) { - size_t step = steps[i]; - unsigned ok; - if (step > 25) ok = ensureBits32(&reader, step); - else if (step > 17) ok = ensureBits25(&reader, step); - else if (step > 9) ok = ensureBits17(&reader, step); - else ok = ensureBits9(&reader, step); - if (!ok) return 0; - result[i] = readBits(&reader, step); - } - return 1; -} - - -static unsigned reverseBits(unsigned bits, unsigned num) -{ - /*TODO: implement faster lookup table based version when needed*/ - unsigned i, result = 0; - for (i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i; - return result; -} - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Deflate - Huffman / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -#define FIRST_LENGTH_CODE_INDEX 257 -#define LAST_LENGTH_CODE_INDEX 285 -/*256 literals, the end code, some length codes, and 2 unused codes*/ -#define NUM_DEFLATE_CODE_SYMBOLS 288 -/*the distance codes have their own symbols, 30 used, 2 unused*/ -#define NUM_DISTANCE_SYMBOLS 32 -/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ -#define NUM_CODE_LENGTH_CODES 19 - -/*the base lengths represented by codes 257-285*/ -static const unsigned LENGTHBASE[29] - = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258}; - -/*the extra bits used by codes 257-285 (added to base length)*/ -static const unsigned LENGTHEXTRA[29] - = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, - 4, 4, 4, 4, 5, 5, 5, 5, 0}; - -/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ -static const unsigned DISTANCEBASE[30] - = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, - 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; - -/*the extra bits of backwards distances (added to base)*/ -static const unsigned DISTANCEEXTRA[30] - = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, - 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; - -/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman -tree of the dynamic huffman tree lengths is generated*/ -static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] - = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - -/* ////////////////////////////////////////////////////////////////////////// */ - -/* -Huffman tree struct, containing multiple representations of the tree -*/ -struct HuffmanTree -{ - unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/ - unsigned* lengths; /*the lengths of the huffman codes*/ - unsigned maxbitlen; /*maximum number of bits a single code can get*/ - unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ - /* for reading only */ - unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/ - unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/ -}; - - -static void HuffmanTree_init(HuffmanTree* tree) -{ - tree->codes = 0; - tree->lengths = 0; - tree->table_len = 0; - tree->table_value = 0; -} - - -static void HuffmanTree_cleanup(HuffmanTree* tree) -{ - free(tree->codes); - free(tree->lengths); - free(tree->table_len); - free(tree->table_value); -} - - -/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/ -/* values 8u and 9u work the fastest */ -#define FIRSTBITS 9u - -/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination, -which is possible in case of only 0 or 1 present symbols. */ -#define INVALIDSYMBOL 65535u - -/* make table for huffman decoding */ -static unsigned HuffmanTree_makeTable(HuffmanTree* tree) -{ - static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/ - static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u; - size_t i, numpresent, pointer, size; /*total table size*/ - unsigned* maxlens = (unsigned*)malloc(headsize * sizeof(unsigned)); - if (!maxlens) return 83; /*alloc fail*/ - - /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/ - lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens)); - for (i = 0; i < tree->numcodes; i++) { - unsigned symbol = tree->codes[i]; - unsigned l = tree->lengths[i]; - unsigned index; - if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/ - /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/ - index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS); - maxlens[index] = LODEPNG_MAX(maxlens[index], l); - } - /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */ - size = headsize; - for (i = 0; i < headsize; ++i) { - unsigned l = maxlens[i]; - if (l > FIRSTBITS) size += (1u << (l - FIRSTBITS)); - } - tree->table_len = (unsigned char*)malloc(size * sizeof(*tree->table_len)); - tree->table_value = (unsigned short*)malloc(size * sizeof(*tree->table_value)); - if (!tree->table_len || !tree->table_value) { - free(maxlens); - /* freeing tree->table values is done at a higher scope */ - return 83; /*alloc fail*/ - } - /*initialize with an invalid length to indicate unused entries*/ - for (i = 0; i < size; ++i) tree->table_len[i] = 16; - - /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/ - pointer = headsize; - for (i = 0; i < headsize; ++i) { - unsigned l = maxlens[i]; - if(l <= FIRSTBITS) continue; - tree->table_len[i] = l; - tree->table_value[i] = pointer; - pointer += (1u << (l - FIRSTBITS)); - } - free(maxlens); - - /*fill in the first table for short symbols, or secondary table for long symbols*/ - numpresent = 0; - for (i = 0; i < tree->numcodes; ++i) { - unsigned l = tree->lengths[i]; - unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/ - /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/ - unsigned reverse = reverseBits(symbol, l); - if (l == 0) continue; - numpresent++; - - if (l <= FIRSTBITS) { - /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/ - unsigned num = 1u << (FIRSTBITS - l); - unsigned j; - for (j = 0; j < num; ++j) { - /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/ - unsigned index = reverse | (j << l); - if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ - tree->table_len[index] = l; - tree->table_value[index] = i; - } - } else { - /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/ - /*the FIRSTBITS MSBs of the symbol are the first table index*/ - unsigned index = reverse & mask; - unsigned maxlen = tree->table_len[index]; - /*log2 of secondary table length, should be >= l - FIRSTBITS*/ - unsigned tablelen = maxlen - FIRSTBITS; - unsigned start = tree->table_value[index]; /*starting index in secondary table*/ - unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/ - unsigned j; - if (maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ - for (j = 0; j < num; ++j) { - unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */ - unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS))); - tree->table_len[index2] = l; - tree->table_value[index2] = i; - } - } - } - - if (numpresent < 2) { - /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits, - but deflate uses 1 bit instead. In case of 0 symbols, no symbols can - appear at all, but such huffman tree could still exist (e.g. if distance - codes are never used). In both cases, not all symbols of the table will be - filled in. Fill them in with an invalid symbol value so returning them from - huffmanDecodeSymbol will cause error. */ - for (i = 0; i < size; ++i) { - if (tree->table_len[i] == 16) { - /* As length, use a value smaller than FIRSTBITS for the head table, - and a value larger than FIRSTBITS for the secondary table, to ensure - valid behavior for advanceBits when reading this symbol. */ - tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1); - tree->table_value[i] = INVALIDSYMBOL; - } - } - } else { - /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. - If that is not the case (due to too long length codes), the table will not - have been fully used, and this is an error (not all bit combinations can be - decoded): an oversubscribed huffman tree, indicated by error 55. */ - for (i = 0; i < size; ++i) { - if (tree->table_len[i] == 16) return 55; - } - } - return 0; -} - - -/* - Second step for the ...makeFromLengths and ...makeFromFrequencies functions. - numcodes, lengths and maxbitlen must already be filled in correctly. return - value is error. -*/ -static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) -{ - unsigned* blcount; - unsigned* nextcode; - unsigned error = 0; - unsigned bits, n; - - tree->codes = (unsigned*)malloc(tree->numcodes * sizeof(unsigned)); - blcount = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned)); - nextcode = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned)); - if (!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/ - - if (!error) { - for (n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0; - /*step 1: count number of instances of each code length*/ - for (bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]]; - /*step 2: generate the nextcode values*/ - for(bits = 1; bits <= tree->maxbitlen; ++bits) { - nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u; - } - /*step 3: generate all the codes*/ - for (n = 0; n != tree->numcodes; ++n) { - if (tree->lengths[n] != 0) { - tree->codes[n] = nextcode[tree->lengths[n]]++; - /*remove superfluous bits from the code*/ - tree->codes[n] &= ((1u << tree->lengths[n]) - 1u); - } - } - } - - free(blcount); - free(nextcode); - - if (!error) error = HuffmanTree_makeTable(tree); - return error; -} - - -/* - given the code lengths (as stored in the PNG file), generate the tree as defined - by Deflate. maxbitlen is the maximum bits that a code in the tree can have. - return value is error. -*/ -static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen) -{ - unsigned i; - tree->lengths = (unsigned*)malloc(numcodes * sizeof(unsigned)); - if (!tree->lengths) return 83; /*alloc fail*/ - for (i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; - tree->numcodes = (unsigned)numcodes; /*number of symbols*/ - tree->maxbitlen = maxbitlen; - return HuffmanTree_makeFromLengths2(tree); -} - - -/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ -static unsigned generateFixedLitLenTree(HuffmanTree* tree) -{ - unsigned i, error = 0; - unsigned* bitlen = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); - if (!bitlen) return 83; /*alloc fail*/ - - /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ - for (i = 0; i <= 143; ++i) bitlen[i] = 8; - for (i = 144; i <= 255; ++i) bitlen[i] = 9; - for (i = 256; i <= 279; ++i) bitlen[i] = 7; - for (i = 280; i <= 287; ++i) bitlen[i] = 8; - - error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); - - free(bitlen); - return error; -} - - -/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ -static unsigned generateFixedDistanceTree(HuffmanTree* tree) -{ - unsigned i, error = 0; - unsigned* bitlen = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); - if (!bitlen) return 83; /*alloc fail*/ - - /*there are 32 distance codes, but 30-31 are unused*/ - for (i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; - error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); - - free(bitlen); - return error; -} - - -/* - returns the code. The bit reader must already have been ensured at least 15 bits -*/ -static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) -{ - unsigned short code = peekBits(reader, FIRSTBITS); - unsigned short l = codetree->table_len[code]; - unsigned short value = codetree->table_value[code]; - if (l <= FIRSTBITS) { - advanceBits(reader, l); - return value; - } else { - unsigned index2; - advanceBits(reader, FIRSTBITS); - index2 = value + peekBits(reader, l - FIRSTBITS); - advanceBits(reader, codetree->table_len[index2] - FIRSTBITS); - return codetree->table_value[index2]; - } -} - - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Inflator (Decompressor) / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/*get the tree of a deflated block with fixed tree, as specified in the deflate specification -Returns error code.*/ -static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) -{ - unsigned error = generateFixedLitLenTree(tree_ll); - if (error) return error; - return generateFixedDistanceTree(tree_d); -} - - -/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ -static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, LodePNGBitReader* reader) -{ - /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ - unsigned error = 0; - unsigned n, HLIT, HDIST, HCLEN, i; - - /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ - unsigned* bitlen_ll = 0; /*lit,len code lengths*/ - unsigned* bitlen_d = 0; /*dist code lengths*/ - /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ - unsigned* bitlen_cl = 0; - HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ - - if (!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/ - - /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ - HLIT = readBits(reader, 5) + 257; - /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ - HDIST = readBits(reader, 5) + 1; - /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ - HCLEN = readBits(reader, 4) + 4; - - bitlen_cl = (unsigned*)malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); - if(!bitlen_cl) return 83 /*alloc fail*/; - - HuffmanTree_init(&tree_cl); - - while (!error) { - /*read the code length codes out of 3 * (amount of code length codes) bits*/ - if (lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) { - ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/ - } - for (i = 0; i != HCLEN; ++i) { - ensureBits9(reader, 3); /*out of bounds already checked above */ - bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3); - } - for (i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) { - bitlen_cl[CLCL_ORDER[i]] = 0; - } - - error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); - if(error) break; - - /*now we can use this tree to read the lengths for the tree that this function will return*/ - bitlen_ll = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); - bitlen_d = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); - if (!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); - lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll)); - lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d)); - - /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ - i = 0; - while (i < HLIT + HDIST) { - unsigned code; - ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/ - code = huffmanDecodeSymbol(reader, &tree_cl); - if (code <= 15) /*a length code*/ { - if (i < HLIT) bitlen_ll[i] = code; - else bitlen_d[i - HLIT] = code; - ++i; - } else if (code == 16) /*repeat previous*/ { - unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ - unsigned value; /*set value to the previous code*/ - - if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ - - replength += readBits(reader, 2); - - if (i < HLIT + 1) value = bitlen_ll[i - 1]; - else value = bitlen_d[i - HLIT - 1]; - /*repeat this value in the next lengths*/ - for (n = 0; n < replength; ++n) { - if (i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ - if (i < HLIT) bitlen_ll[i] = value; - else bitlen_d[i - HLIT] = value; - ++i; - } - } else if(code == 17) /*repeat "0" 3-10 times*/ { - unsigned replength = 3; /*read in the bits that indicate repeat length*/ - replength += readBits(reader, 3); - - /*repeat this value in the next lengths*/ - for (n = 0; n < replength; ++n) { - if (i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ - - if (i < HLIT) bitlen_ll[i] = 0; - else bitlen_d[i - HLIT] = 0; - ++i; - } - } else if(code == 18) /*repeat "0" 11-138 times*/ { - unsigned replength = 11; /*read in the bits that indicate repeat length*/ - replength += readBits(reader, 7); - - /*repeat this value in the next lengths*/ - for (n = 0; n < replength; ++n) { - if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ - - if(i < HLIT) bitlen_ll[i] = 0; - else bitlen_d[i - HLIT] = 0; - ++i; - } - } else /*if(code == INVALIDSYMBOL)*/ { - ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ - } - /*check if any of the ensureBits above went out of bounds*/ - if (reader->bp > reader->bitsize) { - /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol - (10=no endcode, 11=wrong jump outside of tree)*/ - /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ - ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - } - } - if (error) break; - - if (bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ - - /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ - error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); - if (error) break; - error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); - - break; /*end of error-while*/ - } - - free(bitlen_cl); - free(bitlen_ll); - free(bitlen_d); - HuffmanTree_cleanup(&tree_cl); - - return error; -} - - -/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ -static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, unsigned btype) -{ - unsigned error = 0; - HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ - HuffmanTree tree_d; /*the huffman tree for distance codes*/ - - HuffmanTree_init(&tree_ll); - HuffmanTree_init(&tree_d); - - if (btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); - else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); - - while (!error) /*decode all symbols until end reached, breaks at end code*/ { - /*code_ll is literal, length or end code*/ - unsigned code_ll; - ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ - code_ll = huffmanDecodeSymbol(reader, &tree_ll); - if (code_ll <= 255) /*literal symbol*/ { - if (!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); - out->data[out->size - 1] = (unsigned char)code_ll; - } else if (code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { - unsigned code_d, distance; - unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ - size_t start, backward, length; - - /*part 1: get length base*/ - length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; - - /*part 2: get extra bits and add the value of that to length*/ - numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; - if (numextrabits_l != 0) { - /* bits already ensured above */ - length += readBits(reader, numextrabits_l); - } - - /*part 3: get distance code*/ - ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */ - code_d = huffmanDecodeSymbol(reader, &tree_d); - if (code_d > 29) { - if (code_d <= 31) { - ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/ - } else /* if(code_d == INVALIDSYMBOL) */{ - ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ - } - } - distance = DISTANCEBASE[code_d]; - - /*part 4: get extra bits from distance*/ - numextrabits_d = DISTANCEEXTRA[code_d]; - if (numextrabits_d != 0) { - /* bits already ensured above */ - distance += readBits(reader, numextrabits_d); - } - - /*part 5: fill in all the out[n] values based on the length and dist*/ - start = out->size; - if (distance > start) ERROR_BREAK(52); /*too long backward distance*/ - backward = start - distance; - - if (!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); - if (distance < length) { - size_t forward; - lodepng_memcpy(out->data + start, out->data + backward, distance); - start += distance; - for (forward = distance; forward < length; ++forward) { - out->data[start++] = out->data[backward++]; - } - } else { - lodepng_memcpy(out->data + start, out->data + backward, length); - } - } else if (code_ll == 256) { - break; /*end code, break the loop*/ - } else /*if(code_ll == INVALIDSYMBOL)*/ { - ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ - } - /*check if any of the ensureBits above went out of bounds*/ - if (reader->bp > reader->bitsize) { - /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol - (10=no endcode, 11=wrong jump outside of tree)*/ - /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ - ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ - } - } - - HuffmanTree_cleanup(&tree_ll); - HuffmanTree_cleanup(&tree_d); - - return error; -} - - -static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, const LodePNGDecompressSettings* settings) -{ - size_t bytepos; - size_t size = reader->size; - unsigned LEN, NLEN, error = 0; - - /*go to first boundary of byte*/ - bytepos = (reader->bp + 7u) >> 3u; - - /*read LEN (2 bytes) and NLEN (2 bytes)*/ - if (bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/ - LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; - NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; - - /*check if 16-bit NLEN is really the one's complement of LEN*/ - if (!settings->ignore_nlen && LEN + NLEN != 65535) { - return 21; /*error: NLEN is not one's complement of LEN*/ - } - - if (!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/ - - /*read the literal data: LEN bytes are now stored in the out buffer*/ - if (bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/ - - lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN); - bytepos += LEN; - - reader->bp = bytepos << 3u; - - return error; -} - - -static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) -{ - unsigned BFINAL = 0; - LodePNGBitReader reader; - unsigned error = LodePNGBitReader_init(&reader, in, insize); - - if (error) return error; - - while (!BFINAL) { - unsigned BTYPE; - if (!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/ - BFINAL = readBits(&reader, 1); - BTYPE = readBits(&reader, 2); - - if (BTYPE == 3) return 20; /*error: invalid BTYPE*/ - else if (BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ - else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/ - - if (error) return error; - } - - return error; -} - - -static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) -{ - if (settings->custom_inflate) { - unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); - out->allocsize = out->size; - return error; - } else { - return lodepng_inflatev(out, in, insize, settings); - } -} - - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Adler32 / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) -{ - unsigned s1 = adler & 0xffffu; - unsigned s2 = (adler >> 16u) & 0xffffu; - - while (len != 0u) { - unsigned i; - /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ - unsigned amount = len > 5552u ? 5552u : len; - len -= amount; - for (i = 0; i != amount; ++i) { - s1 += (*data++); - s2 += s1; - } - s1 %= 65521u; - s2 %= 65521u; - } - - return (s2 << 16u) | s1; -} - -/*Return the adler32 of the bytes data[0..len-1]*/ -static unsigned adler32(const unsigned char* data, unsigned len) -{ - return update_adler32(1u, data, len); -} - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Zlib / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -static unsigned lodepng_zlib_decompressv(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) -{ - unsigned error = 0; - unsigned CM, CINFO, FDICT; - - if (insize < 2) return 53; /*error, size of zlib data too small*/ - /*read information from zlib header*/ - if ((in[0] * 256 + in[1]) % 31 != 0) { - /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ - return 24; - } - - CM = in[0] & 15; - CINFO = (in[0] >> 4) & 15; - /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ - FDICT = (in[1] >> 5) & 1; - /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ - - if (CM != 8 || CINFO > 7) { - /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ - return 25; - } - if (FDICT != 0) { - /*error: the specification of PNG says about the zlib stream: - "The additional flags shall not specify a preset dictionary."*/ - return 26; - } - - error = inflatev(out, in + 2, insize - 2, settings); - if (error) return error; - - if (!settings->ignore_adler32) { - unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); - unsigned checksum = adler32(out->data, (unsigned)(out->size)); - if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ - } - - return 0; /*no error*/ -} - - -/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ -static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) -{ - if(settings->custom_zlib) { - return settings->custom_zlib(out, outsize, in, insize, settings); - } else { - unsigned error; - ucvector v = ucvector_init(*out, *outsize); - if (expected_size) { - /*reserve the memory to avoid intermediate reallocations*/ - ucvector_resize(&v, *outsize + expected_size); - v.size = *outsize; - } - error = lodepng_zlib_decompressv(&v, in, insize, settings); - *out = v.data; - *outsize = v.size; - return error; - } -} - - -static void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) -{ - settings->ignore_adler32 = 0; - settings->ignore_nlen = 0; - settings->custom_zlib = 0; - settings->custom_inflate = 0; - settings->custom_context = 0; -} - - -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* // End of Zlib related code. Begin of PNG related code. // */ -/* ////////////////////////////////////////////////////////////////////////// */ -/* ////////////////////////////////////////////////////////////////////////// */ - - -#if 0 //thorvg don't use crc -/* CRC polynomial: 0xedb88320 */ -static unsigned lodepng_crc32_table[256] = { - 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, - 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, - 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, - 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, - 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, - 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, - 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, - 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, - 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, - 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, - 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, - 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, - 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, - 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, - 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, - 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, - 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, - 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, - 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, - 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, - 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, - 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, - 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, - 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, - 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, - 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, - 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, - 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, - 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, - 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, - 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, - 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u -}; - - -/* Calculate CRC32 of buffer - Return the CRC of the bytes buf[0..len-1]. */ -static unsigned lodepng_crc32(const unsigned char* data, size_t length) -{ - unsigned r = 0xffffffffu; - size_t i; - for (i = 0; i < length; ++i) { - r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u); - } - return r ^ 0xffffffffu; -} -#endif - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Reading and writing PNG color channel bits / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first, -so LodePNGBitWriter and LodePNGBitReader can't be used for those. */ - -static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) -{ - unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); - ++(*bitpointer); - return result; -} - - -/* TODO: make this faster */ -static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) -{ - unsigned result = 0; - size_t i; - for (i = 0 ; i < nbits; ++i) { - result <<= 1u; - result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); - } - return result; -} - - -static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) -{ - /*the current bit in bitstream may be 0 or 1 for this to work*/ - if (bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u)))); - else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u))); - ++(*bitpointer); -} - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / PNG chunks / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/* - The lodepng_chunk functions are normally not needed, except to traverse the - unknown chunks stored in the LodePNGInfo struct, or add new ones to it. - It also allows traversing the chunks of an encoded PNG file yourself. - - The chunk pointer always points to the beginning of the chunk itself, that is - the first byte of the 4 length bytes. - - In the PNG file format, chunks have the following format: - -4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer) - -4 bytes chunk type (ASCII a-z,A-Z only, see below) - -length bytes of data (may be 0 bytes if length was 0) - -4 bytes of CRC, computed on chunk name + data - - The first chunk starts at the 8th byte of the PNG file, the entire rest of the file - exists out of concatenated chunks with the above format. - - PNG standard chunk ASCII naming conventions: - -First byte: uppercase = critical, lowercase = ancillary - -Second byte: uppercase = public, lowercase = private - -Third byte: must be uppercase - -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy -*/ - - -/* - Gets the length of the data of the chunk. Total chunk length has 12 bytes more. - There must be at least 4 bytes to read from. If the result value is too large, - it may be corrupt data. -*/ -static unsigned lodepng_chunk_length(const unsigned char* chunk) -{ - return lodepng_read32bitInt(&chunk[0]); -} - - -/* check if the type is the given type */ -static unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) -{ - if (lodepng_strlen(type) != 4) return 0; - return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); -} - - -/* 0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard) */ -static unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) -{ - return ((chunk[4] & 32) != 0); -} - - -static const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) -{ - return &chunk[8]; -} - -#if 0 //thorvg don't use crc -/* returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!) */ -static unsigned lodepng_chunk_check_crc(const unsigned char* chunk) -{ - unsigned length = lodepng_chunk_length(chunk); - unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); - /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ - unsigned checksum = lodepng_crc32(&chunk[4], length + 4); - if (CRC != checksum) return 1; - else return 0; -} -#endif - -static const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) -{ - if (chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ - if (chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 - && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { - /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ - return chunk + 8; - } else { - size_t total_chunk_length; - const unsigned char* result; - if (lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; - result = chunk + total_chunk_length; - if (result < chunk) return end; /*pointer overflow*/ - return result; - } -} - - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / Color types, channels, bits / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype. -Return value is a LodePNG error code.*/ -static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) -{ - switch(colortype) { - case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; - case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break; - case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; - case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break; - case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break; - case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */ - default: return 31; /* invalid color type */ - } - return 0; /*allowed color type / bits combination*/ -} - - -static unsigned getNumColorChannels(LodePNGColorType colortype) -{ - switch(colortype) { - case LCT_GREY: return 1; - case LCT_RGB: return 3; - case LCT_PALETTE: return 1; - case LCT_GREY_ALPHA: return 2; - case LCT_RGBA: return 4; - case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */ - default: return 0; /*invalid color type*/ - } -} - - -static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) -{ - /*bits per pixel is amount of channels * bits per channel*/ - return getNumColorChannels(colortype) * bitdepth; -} - - -static void lodepng_color_mode_init(LodePNGColorMode* info) -{ - info->key_defined = 0; - info->key_r = info->key_g = info->key_b = 0; - info->colortype = LCT_RGBA; - info->bitdepth = 8; - info->palette = 0; - info->palettesize = 0; -} - - -/*allocates palette memory if needed, and initializes all colors to black*/ -static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) -{ - size_t i; - /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/ - /*the palette must have room for up to 256 colors with 4 bytes each.*/ - if (!info->palette) info->palette = (unsigned char*)malloc(1024); - if (!info->palette) return; /*alloc fail*/ - for (i = 0; i != 256; ++i) { - /*Initialize all unused colors with black, the value used for invalid palette indices. - This is an error according to the PNG spec, but common PNG decoders make it black instead. - That makes color conversion slightly faster due to no error handling needed.*/ - info->palette[i * 4 + 0] = 0; - info->palette[i * 4 + 1] = 0; - info->palette[i * 4 + 2] = 0; - info->palette[i * 4 + 3] = 255; - } -} - -static void lodepng_palette_clear(LodePNGColorMode* info) -{ - if (info->palette) free(info->palette); - info->palette = 0; - info->palettesize = 0; -} - - -static void lodepng_color_mode_cleanup(LodePNGColorMode* info) -{ - lodepng_palette_clear(info); -} - - -/*return value is error code (0 means no error)*/ -static unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) -{ - lodepng_color_mode_cleanup(dest); - lodepng_memcpy(dest, source, sizeof(LodePNGColorMode)); - if (source->palette) { - dest->palette = (unsigned char*)malloc(1024); - if (!dest->palette && source->palettesize) return 83; /*alloc fail*/ - lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4); - } - return 0; -} - - -static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) -{ - size_t i; - if (a->colortype != b->colortype) return 0; - if (a->bitdepth != b->bitdepth) return 0; - if (a->key_defined != b->key_defined) return 0; - if (a->key_defined) { - if(a->key_r != b->key_r) return 0; - if(a->key_g != b->key_g) return 0; - if(a->key_b != b->key_b) return 0; - } - if (a->palettesize != b->palettesize) return 0; - for (i = 0; i != a->palettesize * 4; ++i) { - if (a->palette[i] != b->palette[i]) return 0; - } - return 1; -} - - -static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) -{ - size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); - size_t n = (size_t)w * (size_t)h; - return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u; -} - - -/* Returns the byte size of a raw image buffer with given width, height and color mode */ -static size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) -{ - return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); -} - - -/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, -and in addition has one extra byte per line: the filter byte. So this gives a larger -result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */ -static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) -{ - /* + 1 for the filter byte, and possibly plus padding bits per line. */ - /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */ - size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u; - return (size_t)h * line; -} - - -/* Safely checks whether size_t overflow can be caused due to amount of pixels. - This check is overcautious rather than precise. If this check indicates no overflow, - you can safely compute in a size_t (but not an unsigned): - -(size_t)w * (size_t)h * 8 - -amount of bytes in IDAT (including filter, padding and Adam7 bytes) - -amount of bytes in raw color model - Returns 1 if overflow possible, 0 if not. */ -static int lodepng_pixel_overflow(unsigned w, unsigned h, const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) -{ - size_t bpp = LODEPNG_MAX(lodepng_get_bpp_lct(pngcolor->colortype, pngcolor->bitdepth), lodepng_get_bpp_lct(rawcolor->colortype, rawcolor->bitdepth)); - size_t numpixels, total; - size_t line; /* bytes per line in worst case */ - - if (lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; - if (lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ - - /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */ - if (lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1; - if (lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1; - - if (lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ - if (lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ - - return 0; /* no overflow */ -} - - -static void lodepng_info_init(LodePNGInfo* info) -{ - lodepng_color_mode_init(&info->color); - info->interlace_method = 0; - info->compression_method = 0; - info->filter_method = 0; -} - - -static void lodepng_info_cleanup(LodePNGInfo* info) -{ - lodepng_color_mode_cleanup(&info->color); -} - - -/* index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to */ -static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) -{ - unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ - /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ - unsigned p = index & m; - in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ - in = in << (bits * (m - p)); - if(p == 0) out[index * bits / 8u] = in; - else out[index * bits / 8u] |= in; -} - -/* - One node of a color tree - This is the data structure used to count the number of unique colors and to get a palette - index for a color. It's like an octree, but because the alpha channel is used too, each - node has 16 instead of 8 children. -*/ -struct ColorTree -{ - ColorTree* children[16]; /* up to 16 pointers to ColorTree of next level */ - int index; /* the payload. Only has a meaningful value if this is in the last level */ -}; - -static void color_tree_init(ColorTree* tree) -{ - lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children)); - tree->index = -1; -} - -static void color_tree_cleanup(ColorTree* tree) -{ - int i; - for (i = 0; i != 16; ++i) { - if(tree->children[i]) { - color_tree_cleanup(tree->children[i]); - free(tree->children[i]); - } - } -} - - -/* returns -1 if color not present, its index otherwise */ -static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - int bit = 0; - for (bit = 0; bit < 8; ++bit) { - int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); - if (!tree->children[i]) return -1; - else tree = tree->children[i]; - } - return tree ? tree->index : -1; -} - - -/* color is not allowed to already exist. - Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist") - Returns error code, or 0 if ok */ -static unsigned color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) -{ - int bit; - for (bit = 0; bit < 8; ++bit) { - int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); - if (!tree->children[i]) { - tree->children[i] = (ColorTree*)malloc(sizeof(ColorTree)); - if (!tree->children[i]) return 83; /*alloc fail*/ - color_tree_init(tree->children[i]); - } - tree = tree->children[i]; - } - tree->index = (int)index; - return 0; -} - -/* put a pixel, given its RGBA color, into image of any color type */ -static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - if (mode->colortype == LCT_GREY) { - unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ - if (mode->bitdepth == 8) out[i] = gray; - else if (mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray; - else { - /*take the most significant bits of gray*/ - gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u); - addColorBits(out, i, mode->bitdepth, gray); - } - } else if (mode->colortype == LCT_RGB) { - if (mode->bitdepth == 8) { - out[i * 3 + 0] = r; - out[i * 3 + 1] = g; - out[i * 3 + 2] = b; - } else { - out[i * 6 + 0] = out[i * 6 + 1] = r; - out[i * 6 + 2] = out[i * 6 + 3] = g; - out[i * 6 + 4] = out[i * 6 + 5] = b; - } - } else if(mode->colortype == LCT_PALETTE) { - int index = color_tree_get(tree, r, g, b, a); - if (index < 0) return 82; /*color not in palette*/ - if (mode->bitdepth == 8) out[i] = index; - else addColorBits(out, i, mode->bitdepth, (unsigned)index); - } else if (mode->colortype == LCT_GREY_ALPHA) { - unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ - if (mode->bitdepth == 8) { - out[i * 2 + 0] = gray; - out[i * 2 + 1] = a; - } else if (mode->bitdepth == 16) { - out[i * 4 + 0] = out[i * 4 + 1] = gray; - out[i * 4 + 2] = out[i * 4 + 3] = a; - } - } else if (mode->colortype == LCT_RGBA) { - if (mode->bitdepth == 8) { - out[i * 4 + 0] = r; - out[i * 4 + 1] = g; - out[i * 4 + 2] = b; - out[i * 4 + 3] = a; - } else { - out[i * 8 + 0] = out[i * 8 + 1] = r; - out[i * 8 + 2] = out[i * 8 + 3] = g; - out[i * 8 + 4] = out[i * 8 + 5] = b; - out[i * 8 + 6] = out[i * 8 + 7] = a; - } - } - return 0; /*no error*/ -} - - -/* put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type */ -static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a) -{ - if (mode->colortype == LCT_GREY) { - unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ - out[i * 2 + 0] = (gray >> 8) & 255; - out[i * 2 + 1] = gray & 255; - } else if (mode->colortype == LCT_RGB) { - out[i * 6 + 0] = (r >> 8) & 255; - out[i * 6 + 1] = r & 255; - out[i * 6 + 2] = (g >> 8) & 255; - out[i * 6 + 3] = g & 255; - out[i * 6 + 4] = (b >> 8) & 255; - out[i * 6 + 5] = b & 255; - } else if (mode->colortype == LCT_GREY_ALPHA) { - unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ - out[i * 4 + 0] = (gray >> 8) & 255; - out[i * 4 + 1] = gray & 255; - out[i * 4 + 2] = (a >> 8) & 255; - out[i * 4 + 3] = a & 255; - } else if (mode->colortype == LCT_RGBA) { - out[i * 8 + 0] = (r >> 8) & 255; - out[i * 8 + 1] = r & 255; - out[i * 8 + 2] = (g >> 8) & 255; - out[i * 8 + 3] = g & 255; - out[i * 8 + 4] = (b >> 8) & 255; - out[i * 8 + 5] = b & 255; - out[i * 8 + 6] = (a >> 8) & 255; - out[i * 8 + 7] = a & 255; - } -} - - -/* Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type. */ -static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) -{ - if (mode->colortype == LCT_GREY) { - if (mode->bitdepth == 8) { - *r = *g = *b = in[i]; - if (mode->key_defined && *r == mode->key_r) *a = 0; - else *a = 255; - } else if (mode->bitdepth == 16) { - *r = *g = *b = in[i * 2 + 0]; - if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; - else *a = 255; - } else { - unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */ - size_t j = i * mode->bitdepth; - unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); - *r = *g = *b = (value * 255) / highest; - if (mode->key_defined && value == mode->key_r) *a = 0; - else *a = 255; - } - } else if (mode->colortype == LCT_RGB) { - if (mode->bitdepth == 8) { - *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; - if (mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; - else *a = 255; - } else { - *r = in[i * 6 + 0]; - *g = in[i * 6 + 2]; - *b = in[i * 6 + 4]; - if (mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; - else *a = 255; - } - } else if (mode->colortype == LCT_PALETTE) { - unsigned index; - if (mode->bitdepth == 8) index = in[i]; - else { - size_t j = i * mode->bitdepth; - index = readBitsFromReversedStream(&j, in, mode->bitdepth); - } - /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ - *r = mode->palette[index * 4 + 0]; - *g = mode->palette[index * 4 + 1]; - *b = mode->palette[index * 4 + 2]; - *a = mode->palette[index * 4 + 3]; - } else if (mode->colortype == LCT_GREY_ALPHA) { - if (mode->bitdepth == 8) { - *r = *g = *b = in[i * 2 + 0]; - *a = in[i * 2 + 1]; - } else { - *r = *g = *b = in[i * 4 + 0]; - *a = in[i * 4 + 2]; - } - } else if (mode->colortype == LCT_RGBA) { - if (mode->bitdepth == 8) { - *r = in[i * 4 + 0]; - *g = in[i * 4 + 1]; - *b = in[i * 4 + 2]; - *a = in[i * 4 + 3]; - } else { - *r = in[i * 8 + 0]; - *g = in[i * 8 + 2]; - *b = in[i * 8 + 4]; - *a = in[i * 8 + 6]; - } - } -} - - -/* Similar to getPixelColorRGBA8, but with all the for loops inside of the color - mode test cases, optimized to convert the colors much faster, when converting - to the common case of RGBA with 8 bit per channel. buffer must be RGBA with - enough memory.*/ -static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode) -{ - unsigned num_channels = 4; - size_t i; - if (mode->colortype == LCT_GREY) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i]; - buffer[3] = 255; - } - if (mode->key_defined) { - buffer -= numpixels * num_channels; - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - if(buffer[0] == mode->key_r) buffer[3] = 0; - } - } - } else if (mode->bitdepth == 16) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i * 2]; - buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; - } - } else { - unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */ - size_t j = 0; - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); - buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; - buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; - } - } - } else if (mode->colortype == LCT_RGB) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - //lodepng_memcpy(buffer, &in[i * 3], 3); - //Convert colortype to LCT_BGR? - buffer[0] = in[i * 3 + 2]; - buffer[1] = in[i * 3 + 1]; - buffer[2] = in[i * 3 + 0]; - buffer[3] = 255; - } - if (mode->key_defined) { - buffer -= numpixels * num_channels; - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - if (buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0; - } - } - } else { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = in[i * 6 + 0]; - buffer[1] = in[i * 6 + 2]; - buffer[2] = in[i * 6 + 4]; - buffer[3] = mode->key_defined - && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; - } - } - } else if (mode->colortype == LCT_PALETTE) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - unsigned index = in[i]; - /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ - lodepng_memcpy(buffer, &mode->palette[index * 4], 4); - } - } else { - size_t j = 0; - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); - /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ - lodepng_memcpy(buffer, &mode->palette[index * 4], 4); - } - } - } else if (mode->colortype == LCT_GREY_ALPHA) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; - buffer[3] = in[i * 2 + 1]; - } - } else { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; - buffer[3] = in[i * 4 + 2]; - } - } - } else if (mode->colortype == LCT_RGBA) { - if (mode->bitdepth == 8) { - lodepng_memcpy(buffer, in, numpixels * 4); - } else { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = in[i * 8 + 0]; - buffer[1] = in[i * 8 + 2]; - buffer[2] = in[i * 8 + 4]; - buffer[3] = in[i * 8 + 6]; - } - } - } -} - - -/* Similar to getPixelColorsRGBA8, but with 3-channel RGB output. */ -static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode) -{ - const unsigned num_channels = 3; - size_t i; - if (mode->colortype == LCT_GREY) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i]; - } - } else if (mode->bitdepth == 16) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i * 2]; - } - } else { - unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */ - size_t j = 0; - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); - buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; - } - } - } else if (mode->colortype == LCT_RGB) { - if (mode->bitdepth == 8) { - lodepng_memcpy(buffer, in, numpixels * 3); - } else { - for(i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = in[i * 6 + 0]; - buffer[1] = in[i * 6 + 2]; - buffer[2] = in[i * 6 + 4]; - } - } - } else if (mode->colortype == LCT_PALETTE) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - unsigned index = in[i]; - /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ - lodepng_memcpy(buffer, &mode->palette[index * 4], 3); - } - } else { - size_t j = 0; - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); - /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */ - lodepng_memcpy(buffer, &mode->palette[index * 4], 3); - } - } - } else if (mode->colortype == LCT_GREY_ALPHA) { - if (mode->bitdepth == 8) { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; - } - } else { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; - } - } - } else if (mode->colortype == LCT_RGBA) { - if (mode->bitdepth == 8) { - for(i = 0; i != numpixels; ++i, buffer += num_channels) { - lodepng_memcpy(buffer, &in[i * 4], 3); - } - } else { - for (i = 0; i != numpixels; ++i, buffer += num_channels) { - buffer[0] = in[i * 8 + 0]; - buffer[1] = in[i * 8 + 2]; - buffer[2] = in[i * 8 + 4]; - } - } - } -} - - -/* Get RGBA16 color of pixel with index i (y * width + x) from the raw image with - given color type, but the given color type must be 16-bit itself. */ -static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) -{ - if (mode->colortype == LCT_GREY) { - *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; - if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; - else *a = 65535; - } else if (mode->colortype == LCT_RGB) { - *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; - *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; - *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; - if (mode->key_defined - && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; - else *a = 65535; - } else if (mode->colortype == LCT_GREY_ALPHA) { - *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; - *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; - } else if (mode->colortype == LCT_RGBA) { - *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; - *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; - *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; - *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; - } -} - -/* - Converts raw buffer from one color type to another color type, based on - LodePNGColorMode structs to describe the input and output color type. - See the reference manual at the end of this header file to see which color conversions are supported. - return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) - The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel - of the output color type (lodepng_get_bpp). - For < 8 bpp images, there should not be padding bits at the end of scanlines. - For 16-bit per channel colors, uses big endian format like PNG does. - Return value is LodePNG error code -*/ -static unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) -{ - size_t i; - ColorTree tree; - size_t numpixels = (size_t)w * (size_t)h; - unsigned error = 0; - - if (mode_in->colortype == LCT_PALETTE && !mode_in->palette) { - return 107; /* error: must provide palette if input mode is palette */ - } - - if (lodepng_color_mode_equal(mode_out, mode_in)) { - size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - lodepng_memcpy(out, in, numbytes); - return 0; - } - - if (mode_out->colortype == LCT_PALETTE) { - size_t palettesize = mode_out->palettesize; - const unsigned char* palette = mode_out->palette; - size_t palsize = (size_t)1u << mode_out->bitdepth; - /* if the user specified output palette but did not give the values, assume - they want the values of the input color type (assuming that one is palette). - Note that we never create a new palette ourselves.*/ - if (palettesize == 0) { - palettesize = mode_in->palettesize; - palette = mode_in->palette; - /* if the input was also palette with same bitdepth, then the color types are also - equal, so copy literally. This to preserve the exact indices that were in the PNG - even in case there are duplicate colors in the palette.*/ - if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { - size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - lodepng_memcpy(out, in, numbytes); - return 0; - } - } - if (palettesize < palsize) palsize = palettesize; - color_tree_init(&tree); - for (i = 0; i != palsize; ++i) { - const unsigned char* p = &palette[i * 4]; - error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); - if (error) break; - } - } - - if (!error) { - if (mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { - for (i = 0; i != numpixels; ++i) { - unsigned short r = 0, g = 0, b = 0, a = 0; - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); - rgba16ToPixel(out, i, mode_out, r, g, b, a); - } - } else if (mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { - getPixelColorsRGBA8(out, numpixels, in, mode_in); - } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { - getPixelColorsRGB8(out, numpixels, in, mode_in); - } else { - unsigned char r = 0, g = 0, b = 0, a = 0; - for (i = 0; i != numpixels; ++i) { - getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); - error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); - if (error) break; - } - } - } - - if (mode_out->colortype == LCT_PALETTE) { - color_tree_cleanup(&tree); - } - - return error; -} - - -/* Paeth predictor, used by PNG filter type 4 - The parameters are of type short, but should come from unsigned chars, the shorts - are only needed to make the paeth calculation correct. -*/ -static unsigned char paethPredictor(short a, short b, short c) -{ - short pa = LODEPNG_ABS(b - c); - short pb = LODEPNG_ABS(a - c); - short pc = LODEPNG_ABS(a + b - c - c); - /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */ - if (pb < pa) { a = b; pa = pb; } - return (pc < pa) ? c : a; -} - - -/*shared values used by multiple Adam7 related functions*/ -static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ -static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ -static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ -static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ - -/* Outputs various dimensions and positions in the image related to the Adam7 reduced images. - passw: output containing the width of the 7 passes - passh: output containing the height of the 7 passes - filter_passstart: output containing the index of the start and end of each - reduced image with filter bytes - padded_passstart output containing the index of the start and end of each - reduced image when without filter bytes but with padded scanlines - passstart: output containing the index of the start and end of each reduced - image without padding between scanlines, but still padding between the images - w, h: width and height of non-interlaced image - bpp: bits per pixel - "padded" is only relevant if bpp is less than 8 and a scanline or image does not - end at a full byte */ -static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) -{ - /* the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass */ - unsigned i; - - /* calculate width and height in pixels of each pass */ - for (i = 0; i != 7; ++i) { - passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; - passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; - if(passw[i] == 0) passh[i] = 0; - if(passh[i] == 0) passw[i] = 0; - } - - filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; - for (i = 0; i != 7; ++i) { - /* if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte) */ - filter_passstart[i + 1] = filter_passstart[i] - + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0); - /* bits padded if needed to fill full byte at end of each scanline */ - padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u); - /* only padded at end of reduced image */ - passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u; - } -} - - -/* ////////////////////////////////////////////////////////////////////////// */ -/* / PNG Decoder / */ -/* ////////////////////////////////////////////////////////////////////////// */ - -static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) -{ - /* For PNG filter method 0 - unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, - the filter works byte per byte (bytewidth = 1) - precon is the previous unfiltered scanline, recon the result, scanline the current one - the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead - recon and scanline MAY be the same memory address! precon must be disjoint. */ - - size_t i; - switch (filterType) { - case 0: { - if (bytewidth == 4) { - for (i = 0; i < length; i += 4) { - //RGBA -> BGRA - recon[i + 0] = scanline[i + 2]; - recon[i + 1] = scanline[i + 1]; - recon[i + 2] = scanline[i + 0]; - recon[i + 3] = scanline[i + 3]; - } - } else { - for (i = 0; i != length; ++i) recon[i] = scanline[i]; - } - break; - } - case 1: { - for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; - for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; - break; - } - case 2: { - if (precon) { - for (i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; - } else { - for (i = 0; i != length; ++i) recon[i] = scanline[i]; - } - break; - } - case 3: { - if (precon) { - for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); - for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); - } else { - for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; - for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); - } - break; - } - case 4: { - if (precon) { - for (i = 0; i != bytewidth; ++i) { - recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ - } - - /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that - adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ - if (bytewidth >= 4) { - for (; i + 3 < length; i += 4) { - size_t j = i - bytewidth; - unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; - unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; - unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; - unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3]; - recon[i + 0] = s0 + paethPredictor(r0, p0, q0); - recon[i + 1] = s1 + paethPredictor(r1, p1, q1); - recon[i + 2] = s2 + paethPredictor(r2, p2, q2); - recon[i + 3] = s3 + paethPredictor(r3, p3, q3); - } - } else if (bytewidth >= 3) { - for (; i + 2 < length; i += 3) { - size_t j = i - bytewidth; - unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; - unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; - unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; - unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2]; - recon[i + 0] = s0 + paethPredictor(r0, p0, q0); - recon[i + 1] = s1 + paethPredictor(r1, p1, q1); - recon[i + 2] = s2 + paethPredictor(r2, p2, q2); - } - } else if (bytewidth >= 2) { - for (; i + 1 < length; i += 2) { - size_t j = i - bytewidth; - unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; - unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; - unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; - unsigned char q0 = precon[j + 0], q1 = precon[j + 1]; - recon[i + 0] = s0 + paethPredictor(r0, p0, q0); - recon[i + 1] = s1 + paethPredictor(r1, p1, q1); - } - } - - for (; i != length; ++i) { - recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); - } - } else { - for (i = 0; i != bytewidth; ++i) { - recon[i] = scanline[i]; - } - for (i = bytewidth; i < length; ++i) { - /* paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth] */ - recon[i] = (scanline[i] + recon[i - bytewidth]); - } - } - break; - } - default: return 36; /* error: invalid filter type given */ - } - return 0; -} - - -static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) -{ - /* For PNG filter method 0 - this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) - out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline - w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel - in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */ - - unsigned y; - unsigned char* prevline = 0; - - /* bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise */ - size_t bytewidth = (bpp + 7u) / 8u; - /* the width of a scanline in bytes, not including the filter type */ - size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; - - for (y = 0; y < h; ++y) { - size_t outindex = linebytes * y; - size_t inindex = (1 + linebytes) * y; /* the extra filterbyte added to each row */ - unsigned char filterType = in[inindex]; - CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); - prevline = &out[outindex]; - } - - return 0; -} - -/* in: Adam7 interlaced image, with no padding bits between scanlines, but between - reduced images so that each reduced image starts at a byte. - out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h - bpp: bits per pixel - out has the following size in bits: w * h * bpp. - in is possibly bigger due to padding bits between reduced images. - out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation - (because that's likely a little bit faster) - NOTE: comments about padding bits are only relevant if bpp < 8 */ -static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) -{ - unsigned passw[7], passh[7]; - size_t filter_passstart[8], padded_passstart[8], passstart[8]; - unsigned i; - - Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - - if (bpp >= 8) { - for(i = 0; i != 7; ++i) { - unsigned x, y, b; - size_t bytewidth = bpp / 8u; - for (y = 0; y < passh[i]; ++y) - for (x = 0; x < passw[i]; ++x) { - size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; - size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth; - for (b = 0; b < bytewidth; ++b) { - out[pixeloutstart + b] = in[pixelinstart + b]; - } - } - } - } else /* bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers */ { - for (i = 0; i != 7; ++i) { - unsigned x, y, b; - unsigned ilinebits = bpp * passw[i]; - unsigned olinebits = bpp * w; - size_t obp, ibp; /* bit pointers (for out and in buffer) */ - for (y = 0; y < passh[i]; ++y) - for (x = 0; x < passw[i]; ++x) { - ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); - obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp; - for (b = 0; b < bpp; ++b) { - unsigned char bit = readBitFromReversedStream(&ibp, in); - setBitOfReversedStream(&obp, out, bit); - } - } - } - } -} - - -static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) -{ - /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need - to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers - for the Adam7 code, the color convert code and the output to the user. - in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must - have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits - also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 - only useful if (ilinebits - olinebits) is a value in the range 1..7 */ - unsigned y; - size_t diff = ilinebits - olinebits; - size_t ibp = 0, obp = 0; /*input and output bit pointers*/ - for (y = 0; y < h; ++y) { - size_t x; - for (x = 0; x < olinebits; ++x) { - unsigned char bit = readBitFromReversedStream(&ibp, in); - setBitOfReversedStream(&obp, out, bit); - } - ibp += diff; - } -} - - -/* out must be buffer big enough to contain full image, and in must contain the full decompressed data from - the IDAT chunks (with filter index bytes and possible padding bits) - return value is error */ -static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png) -{ - /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. - Steps: - *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8) - *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace - NOTE: the in buffer will be overwritten with intermediate data! */ - unsigned bpp = lodepng_get_bpp_lct(info_png->color.colortype, info_png->color.bitdepth); - if (bpp == 0) return 31; /* error: invalid colortype */ - - if (info_png->interlace_method == 0) { - if (bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { - CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); - removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h); - } - /* we can immediately filter into the out buffer, no other steps needed */ - else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); - } else /* interlace_method is 1 (Adam7) */ { - unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; - unsigned i; - - Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - - for (i = 0; i != 7; ++i) { - CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); - /* TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, - move bytes instead of bits or move not at all */ - if (bpp < 8) { - /* remove padding bits in scanlines; after this there still may be padding - bits between the different reduced images: each reduced image still starts nicely at a byte */ - removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]); - } - } - Adam7_deinterlace(out, in, w, h, bpp); - } - return 0; -} - - -static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) -{ - unsigned pos = 0, i; - color->palettesize = chunkLength / 3u; - if (color->palettesize == 0 || color->palettesize > 256) return 38; /* error: palette too small or big */ - lodepng_color_mode_alloc_palette(color); - if (!color->palette && color->palettesize) { - color->palettesize = 0; - return 83; /* alloc fail */ - } - - for (i = 0; i != color->palettesize; ++i) { - color->palette[4 * i + 0] = data[pos++]; /*R*/ - color->palette[4 * i + 1] = data[pos++]; /*G*/ - color->palette[4 * i + 2] = data[pos++]; /*B*/ - color->palette[4 * i + 3] = 255; /*alpha*/ - } - - return 0; /* OK */ -} - - -static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) -{ - unsigned i; - if (color->colortype == LCT_PALETTE) { - /* error: more alpha values given than there are palette entries */ - if (chunkLength > color->palettesize) return 39; - - for (i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; - } else if (color->colortype == LCT_GREY) { - /* error: this chunk must be 2 bytes for grayscale image */ - if (chunkLength != 2) return 30; - - color->key_defined = 1; - color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; - } else if (color->colortype == LCT_RGB) { - /* error: this chunk must be 6 bytes for RGB image */ - if (chunkLength != 6) return 41; - - color->key_defined = 1; - color->key_r = 256u * data[0] + data[1]; - color->key_g = 256u * data[2] + data[3]; - color->key_b = 256u * data[4] + data[5]; - } - else return 42; /* error: tRNS chunk not allowed for other color models */ - - return 0; /* OK */ -} - - -/* read a PNG, the result will be in the same color type as the PNG (hence "generic") */ -static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) -{ - unsigned char IEND = 0; - const unsigned char* chunk; - unsigned char* idat; /*the data from idat chunks, zlib compressed*/ - size_t idatsize = 0; - unsigned char* scanlines = 0; - size_t scanlines_size = 0, expected_size = 0; - size_t outsize = 0; - - /* safe output values in case error happens */ - *out = 0; - *w = *h = 0; - - state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ - if (state->error) return; - - if (lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) { - CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ - } - - /*the input filesize is a safe upper bound for the sum of idat chunks size*/ - idat = (unsigned char*)malloc(insize); - if (!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/ - - chunk = &in[33]; /*first byte of the first chunk after the header*/ - - /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. - IDAT data is put at the start of the in buffer*/ - while (!IEND && !state->error) { - unsigned chunkLength; - const unsigned char* data; /*the data in the chunk*/ - - /*error: size of the in buffer too small to contain next chunk*/ - if ((size_t)((chunk - in) + 12) > insize || chunk < in) { - if (state->decoder.ignore_end) break; /*other errors may still happen though*/ - CERROR_BREAK(state->error, 30); - } - - /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ - chunkLength = lodepng_chunk_length(chunk); - /*error: chunk length larger than the max PNG chunk size*/ - if (chunkLength > 2147483647) { - if (state->decoder.ignore_end) break; /*other errors may still happen though*/ - CERROR_BREAK(state->error, 63); - } - - if ((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { - CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ - } - - data = lodepng_chunk_data_const(chunk); - - /*for unknown chunk order*/ - //unsigned unknown = 0; - - /*IDAT chunk, containing compressed image data*/ - if (lodepng_chunk_type_equals(chunk, "IDAT")) { - size_t newsize; - if (lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); - if (newsize > insize) CERROR_BREAK(state->error, 95); - lodepng_memcpy(idat + idatsize, data, chunkLength); - idatsize += chunkLength; - } else if (lodepng_chunk_type_equals(chunk, "IEND")) { - /*IEND chunk*/ - IEND = 1; - } else if (lodepng_chunk_type_equals(chunk, "PLTE")) { - /*palette chunk (PLTE)*/ - state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); - if (state->error) break; - } else if (lodepng_chunk_type_equals(chunk, "tRNS")) { - /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled - in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that - affects the alpha channel of pixels. */ - state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); - if (state->error) break; - } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { - /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ - if (!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) { - CERROR_BREAK(state->error, 69); - } - //unknown = 1; - } - -#if 0 //We don't use CRC - if (!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { - if (lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ - } -#endif - if (!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); - } - - if (state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { - state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ - } - - if (!state->error) { - /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. - If the decompressed size does not match the prediction, the image must be corrupt.*/ - if (state->info_png.interlace_method == 0) { - size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth); - expected_size = lodepng_get_raw_size_idat(*w, *h, bpp); - } else { - size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth); - /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ - expected_size = 0; - expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp); - if (*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp); - expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp); - if (*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp); - expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp); - if (*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp); - expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp); - } - state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings); - } - - if (!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ - free(idat); - - if (!state->error) { - outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); - *out = (unsigned char*)malloc(outsize); - if (!*out) state->error = 83; /*alloc fail*/ - } - if (!state->error) { - lodepng_memset(*out, 0, outsize); - state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png); - } - free(scanlines); -} - - -static void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) -{ - settings->color_convert = 1; - settings->ignore_crc = 0; - settings->ignore_critical = 0; - settings->ignore_end = 0; - lodepng_decompress_settings_init(&settings->zlibsettings); -} - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -/*read the information from the header and store it in the LodePNGInfo. return value is error*/ -unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) -{ - unsigned width, height; - LodePNGInfo* info = &state->info_png; - if (insize == 0 || in == 0) { - CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ - } - if (insize < 33) { - CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ - } - - /* when decoding a new PNG image, make sure all parameters created after previous decoding are reset */ - /* TODO: remove this. One should use a new LodePNGState for new sessions */ - lodepng_info_cleanup(info); - lodepng_info_init(info); - - if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { - CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ - } - if (lodepng_chunk_length(in + 8) != 13) { - CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ - } - if (!lodepng_chunk_type_equals(in + 8, "IHDR")) { - CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ - } - - /*read the values given in the header*/ - width = lodepng_read32bitInt(&in[16]); - height = lodepng_read32bitInt(&in[20]); - /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/ - if (w) *w = width; - if (h) *h = height; - info->color.bitdepth = in[24]; - info->color.colortype = (LodePNGColorType)in[25]; - info->compression_method = in[26]; - info->filter_method = in[27]; - info->interlace_method = in[28]; - - /*errors returned only after the parsing so other values are still output*/ - - /*error: invalid image size*/ - if (width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93); - /*error: invalid colortype or bitdepth combination*/ - state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); - if (state->error) return state->error; - /*error: only compression method 0 is allowed in the specification*/ - if (info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); - /*error: only filter method 0 is allowed in the specification*/ - if (info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); - /*error: only interlace methods 0 and 1 exist in the specification*/ - if (info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); - -#if 0 //thorvg don't use crc - if (!state->decoder.ignore_crc) { - unsigned CRC = lodepng_read32bitInt(&in[29]); - unsigned checksum = lodepng_crc32(&in[12], 17); - if (CRC != checksum) { - CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ - } - } -#endif - return state->error; -} - - -unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) -{ - *out = 0; - decodeGeneric(out, w, h, state, in, insize); - if (state->error) return state->error; - if (!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { - /*same color type, no copying or converting of data needed*/ - /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype - the raw image has to the end user*/ - if (!state->decoder.color_convert) { - state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); - if (state->error) return state->error; - } - } else { /*color conversion needed*/ - unsigned char* data = *out; - size_t outsize; - - /*TODO: check if this works according to the statement in the documentation: "The converter can convert - from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/ - if (!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) { - return 56; /*unsupported color mode conversion*/ - } - - outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); - *out = (unsigned char*)malloc(outsize); - if (!(*out)) { - state->error = 83; /*alloc fail*/ - } - else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h); - free(data); - } - return state->error; -} - - -void lodepng_state_init(LodePNGState* state) -{ - lodepng_decoder_settings_init(&state->decoder); - lodepng_color_mode_init(&state->info_raw); - lodepng_info_init(&state->info_png); - state->error = 1; -} - - -void lodepng_state_cleanup(LodePNGState* state) -{ - lodepng_color_mode_cleanup(&state->info_raw); - lodepng_info_cleanup(&state->info_png); -} diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.h b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h deleted file mode 100644 index 0cdac7cbea..0000000000 --- a/thirdparty/thorvg/src/loaders/png/tvgLodePng.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. - - * 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. - */ - -/* - LodePNG version 20200306 - - Copyright (c) 2005-2020 Lode Vandevenne - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ - -#ifndef _TVG_LODEPNG_H_ -#define _TVG_LODEPNG_H_ - -#include <stddef.h> - -/*The PNG color types (also used for raw image).*/ -enum LodePNGColorType -{ - LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/ - LCT_RGB = 2, /*RGB: 8,16 bit*/ - LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ - LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/ - LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/ - /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid - byte value from 0 to 255 that could be present in an invalid PNG file header. Do - not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use - the valid color type names above, or numeric values like 1 or 7 when checking for - particular disallowed color type byte values, or cast to integer to print it.*/ - LCT_MAX_OCTET_VALUE = 255 -}; - -/*Settings for zlib decompression*/ -struct LodePNGDecompressSettings -{ - /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */ - unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ - unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ - - /*use custom zlib decoder instead of built in one (default: null)*/ - unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); - /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/ - unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); - - const void* custom_context; /*optional custom settings for custom functions*/ -}; - -/* - Color mode of an image. Contains all information required to decode the pixel - bits to RGBA colors. This information is the same as used in the PNG file - format, and is used both for PNG and raw image data in LodePNG. -*/ -struct LodePNGColorMode -{ - /*header (IHDR)*/ - LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ - unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ - - /* - palette (PLTE and tRNS) - - Dynamically allocated with the colors of the palette, including alpha. - This field may not be allocated directly, use lodepng_color_mode_init first, - then lodepng_palette_add per color to correctly initialize it (to ensure size - of exactly 1024 bytes). - - The alpha channels must be set as well, set them to 255 for opaque images. - - When decoding, by default you can ignore this palette, since LodePNG already - fills the palette colors in the pixels of the raw RGBA output. - - The palette is only supported for color type 3. - */ - unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/ - size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/ - - /* - transparent color key (tRNS) - - This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. - For grayscale PNGs, r, g and b will all 3 be set to the same. - - When decoding, by default you can ignore this information, since LodePNG sets - pixels with this key to transparent already in the raw RGBA output. - - The color key is only supported for color types 0 and 2. - */ - unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ - unsigned key_r; /*red/grayscale component of color key*/ - unsigned key_g; /*green component of color key*/ - unsigned key_b; /*blue component of color key*/ -}; - -/*Information about the PNG image, except pixels, width and height.*/ -struct LodePNGInfo -{ - /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ - unsigned compression_method;/*compression method of the original file. Always 0.*/ - unsigned filter_method; /*filter method of the original file*/ - unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ - LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ -}; - -/* - Settings for the decoder. This contains settings for the PNG and the Zlib - decoder, but not the Info settings from the Info structs. -*/ -struct LodePNGDecoderSettings -{ - LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ - - /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */ - unsigned ignore_crc; /*ignore CRC checksums*/ - unsigned ignore_critical; /*ignore unknown critical chunks*/ - unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ - /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable - errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some - strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters - in string keys, etc... */ - - unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ -}; - -/*The settings, state and information for extended encoding and decoding.*/ -struct LodePNGState -{ - LodePNGDecoderSettings decoder; /*the decoding settings*/ - LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ - LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ - unsigned error; -}; - -void lodepng_state_init(LodePNGState* state); -void lodepng_state_cleanup(LodePNGState* state); -unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); -unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); - -#endif //_TVG_LODEPNG_H_ diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp deleted file mode 100644 index 3e293176b7..0000000000 --- a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. - - * 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 <memory.h> -#include "tvgLoader.h" -#include "tvgPngLoader.h" - - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - - -static inline uint32_t PREMULTIPLY(uint32_t c) -{ - auto a = (c >> 24); - return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); -} - - -static void _premultiply(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = PREMULTIPLY(*src); - } - } -} - - -void PngLoader::clear() -{ - lodepng_state_cleanup(&state); - - if (freeData) free(data); - data = nullptr; - size = 0; - freeData = false; -} - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -PngLoader::PngLoader() -{ - lodepng_state_init(&state); -} - - -PngLoader::~PngLoader() -{ - if (freeData) free(data); - free(image); -} - - -bool PngLoader::open(const string& path) -{ - clear(); - - auto pngFile = fopen(path.c_str(), "rb"); - if (!pngFile) return false; - - auto ret = false; - - //determine size - if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize; - if (((size = ftell(pngFile)) < 1)) goto finalize; - if (fseek(pngFile, 0, SEEK_SET)) goto finalize; - - data = (unsigned char *) malloc(size); - if (!data) goto finalize; - - freeData = true; - - if (fread(data, size, 1, pngFile) < 1) goto failure; - - lodepng_state_init(&state); - - unsigned int width, height; - if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto failure; - - w = static_cast<float>(width); - h = static_cast<float>(height); - ret = true; - - goto finalize; - -failure: - clear(); - -finalize: - fclose(pngFile); - return ret; -} - - -bool PngLoader::open(const char* data, uint32_t size, bool copy) -{ - clear(); - - lodepng_state_init(&state); - - unsigned int width, height; - if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false; - - if (copy) { - this->data = (unsigned char *) malloc(size); - if (!this->data) return false; - memcpy((unsigned char *)this->data, data, size); - freeData = true; - } else { - this->data = (unsigned char *) data; - freeData = false; - } - - w = static_cast<float>(width); - h = static_cast<float>(height); - this->size = size; - - return true; -} - - -bool PngLoader::read() -{ - if (!data || w <= 0 || h <= 0) return false; - - TaskScheduler::request(this); - - return true; -} - - -bool PngLoader::close() -{ - this->done(); - clear(); - return true; -} - - -unique_ptr<Surface> PngLoader::bitmap() -{ - this->done(); - - if (!image) return nullptr; - - auto surface = static_cast<Surface*>(malloc(sizeof(Surface))); - surface->buffer = (uint32_t*)(image); - surface->stride = w; - surface->w = w; - surface->h = h; - surface->cs = SwCanvas::ARGB8888; - - return unique_ptr<Surface>(surface); -} - - -void PngLoader::run(unsigned tid) -{ - if (image) { - free(image); - image = nullptr; - } - auto width = static_cast<unsigned>(w); - auto height = static_cast<unsigned>(h); - - lodepng_decode(&image, &width, &height, &state, data, size); - - _premultiply((uint32_t*)(image), width, height); -} diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h deleted file mode 100644 index 8f07f6418f..0000000000 --- a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved. - - * 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 _TVG_PNG_LOADER_H_ -#define _TVG_PNG_LOADER_H_ - -#include "tvgLodePng.h" -#include "tvgTaskScheduler.h" - - -class PngLoader : public LoadModule, public Task -{ -private: - LodePNGState state; - unsigned char* data = nullptr; - unsigned char *image = nullptr; - unsigned long size = 0; - bool freeData = false; - - void clear(); - -public: - PngLoader(); - ~PngLoader(); - - using LoadModule::open; - bool open(const string& path) override; - bool open(const char* data, uint32_t size, bool copy) override; - bool read() override; - bool close() override; - - unique_ptr<Surface> bitmap() override; - void run(unsigned tid) override; -}; - -#endif //_TVG_PNG_LOADER_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp index d7c51bdc30..c373da2dd5 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp @@ -26,8 +26,8 @@ #ifdef _WIN32 #include <malloc.h> -#elif defined(__FreeBSD__) - #include <stdlib.h> +#elif __FreeBSD__ + #include<stdlib.h> #else #include <alloca.h> #endif @@ -390,7 +390,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb if (p) { //Invalid case: '<' nested - if (*p == '<') return false; + if (*p == '<' && type != SimpleXMLType::Doctype) return false; const char *start, *end; start = itr + 1 + toff; diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp index 66de860aaa..62a75ecd3d 100644 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp +++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp @@ -23,8 +23,8 @@ #ifdef _WIN32 #include <malloc.h> -#elif defined(__FreeBSD__) - #include <stdlib.h> +#elif __FreeBSD__ + #include<stdlib.h> #else #include <alloca.h> #endif diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp index fca313b430..adf85836c3 100644 --- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp +++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp @@ -28,8 +28,8 @@ #ifdef _WIN32 #include <malloc.h> -#elif defined(__FreeBSD__) - #include <stdlib.h> +#elif __FreeBSD__ + #include<stdlib.h> #else #include <alloca.h> #endif diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index a3b73a7135..f2fd2a80e4 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,12 +1,12 @@ -VERSION=0.8.1 +VERSION=0.8.2 rm -rf AUTHORS inc LICENSE src *.zip -curl -L -O https://github.com/Samsung/thorvg/archive/$VERSION.zip +curl -L -O https://github.com/Samsung/thorvg/archive/v$VERSION.zip bsdtar --strip-components=1 -xvf *.zip rm *.zip -rm -rf .github docs pc res test tools .git* *.md *.txt wasm_build.sh +rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh find . -type f -name 'meson.build' -delete rm -rf src/bin src/bindings src/examples src/wasm -rm -rf src/lib/gl_engine tvgcompat +rm -rf src/lib/gl_engine src/loaders/external_jpg src/loaders/png cat << EOF > inc/config.h #ifndef THORVG_CONFIG_H #define THORVG_CONFIG_H |