diff options
87 files changed, 772 insertions, 3733 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..4513c66ab5 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; @@ -292,7 +292,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/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/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/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/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/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/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/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/editor_properties.cpp b/editor/editor_properties.cpp index 155802d16c..332e47dc52 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -760,8 +760,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/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index c59781390a..fe33a91a41 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5455,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/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/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index e1beb2f374..b3ca8ff00a 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,12 @@ 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); + } +#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/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/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/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/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/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/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/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 488ae762c5..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) { @@ -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.h b/scene/main/node.h index 4e6530cccd..8c82c41e46 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; 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/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/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/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_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/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/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 |