diff options
Diffstat (limited to 'modules')
70 files changed, 980 insertions, 374 deletions
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 8813c3827a..ae03abca50 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -200,7 +200,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, return err; } -Error ImageLoaderBMP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderBMP::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { bmp_header_s bmp_header; Error err = ERR_INVALID_DATA; diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index 63dee0a969..cf8346ecad 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -83,7 +83,7 @@ protected: const bmp_header_s &p_header); public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderBMP(); }; diff --git a/modules/denoise/config.py b/modules/denoise/config.py index 521115dae5..350839651a 100644 --- a/modules/denoise/config.py +++ b/modules/denoise/config.py @@ -5,14 +5,7 @@ def can_build(env, platform): # as doing lightmap generation and denoising on Android or HTML5 # would be a bit far-fetched. desktop_platforms = ["linuxbsd", "macos", "windows"] - supported_arch = env["bits"] == "64" - if env["arch"] == "arm64": - supported_arch = False - if env["arch"].startswith("ppc"): - supported_arch = False - if env["arch"].startswith("rv"): - supported_arch = False - return env["tools"] and platform in desktop_platforms and supported_arch + return env["tools"] and platform in desktop_platforms and env["arch"] == "x86_64" def configure(env): diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml index 46008317a2..67760ba5e8 100644 --- a/modules/enet/doc_classes/ENetPacketPeer.xml +++ b/modules/enet/doc_classes/ENetPacketPeer.xml @@ -18,6 +18,18 @@ Returns the number of channels allocated for communication with peer. </description> </method> + <method name="get_remote_address" qualifiers="const"> + <return type="String" /> + <description> + Returns the IP address of this peer. + </description> + </method> + <method name="get_remote_port" qualifiers="const"> + <return type="int" /> + <description> + Returns the remote port of this peer. + </description> + </method> <method name="get_state" qualifiers="const"> <return type="int" enum="ENetPacketPeer.PeerState" /> <description> diff --git a/modules/enet/enet_packet_peer.cpp b/modules/enet/enet_packet_peer.cpp index 62c0550edb..5a96f75c4f 100644 --- a/modules/enet/enet_packet_peer.cpp +++ b/modules/enet/enet_packet_peer.cpp @@ -206,6 +206,8 @@ void ENetPacketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("send", "channel", "packet", "flags"), &ENetPacketPeer::_send); ClassDB::bind_method(D_METHOD("throttle_configure", "interval", "acceleration", "deceleration"), &ENetPacketPeer::throttle_configure); ClassDB::bind_method(D_METHOD("set_timeout", "timeout", "timeout_min", "timeout_max"), &ENetPacketPeer::set_timeout); + ClassDB::bind_method(D_METHOD("get_remote_address"), &ENetPacketPeer::get_remote_address); + ClassDB::bind_method(D_METHOD("get_remote_port"), &ENetPacketPeer::get_remote_port); ClassDB::bind_method(D_METHOD("get_statistic", "statistic"), &ENetPacketPeer::get_statistic); ClassDB::bind_method(D_METHOD("get_state"), &ENetPacketPeer::get_state); ClassDB::bind_method(D_METHOD("get_channels"), &ENetPacketPeer::get_channels); diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 681aa45c8f..b9560ea69b 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -646,13 +646,13 @@ void GDScriptSyntaxHighlighter::_update_cache() { node_path_color = Color(0.72, 0.77, 0.49); node_ref_color = Color(0.39, 0.76, 0.35); annotation_color = Color(1.0, 0.7, 0.45); - string_name_color = Color(1.0, 0.66, 0.72); + string_name_color = Color(1.0, 0.76, 0.65); } else { function_definition_color = Color(0, 0.6, 0.6); node_path_color = Color(0.18, 0.55, 0); node_ref_color = Color(0.0, 0.5, 0); annotation_color = Color(0.8, 0.37, 0); - string_name_color = Color(0.8, 0.46, 0.52); + string_name_color = Color(0.8, 0.56, 0.45); } EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color); diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index cc1ca9131d..dbbccc9bcc 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -73,8 +73,8 @@ static void _editor_init() { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (blender3_path.is_empty()) { WARN_PRINT("Blend file import is enabled in the project settings, but no Blender path is configured in the editor settings. Blend files will not be imported."); - } else if (!da->file_exists(blender3_path)) { - WARN_PRINT("Blend file import is enabled, but the Blender path doesn't point to an accessible file. Blend files will not be imported."); + } else if (!da->dir_exists(blender3_path)) { + WARN_PRINT("Blend file import is enabled, but the Blender path doesn't point to an accessible directory. Blend files will not be imported."); } else { Ref<EditorSceneFormatImporterBlend> importer; importer.instantiate(); diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index eca689e87a..e7c6fe592d 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -33,7 +33,7 @@ #include "core/os/os.h" #include "core/string/print_string.h" -Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { String header = f->get_token(); ERR_FAIL_COND_V_MSG(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED, "Unsupported header information in HDR: " + header + "."); @@ -131,7 +131,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_f ptr[1] * exp / 255.0, ptr[2] * exp / 255.0); - if (p_force_linear) { + if (p_flags & FLAG_FORCE_LINEAR) { c = c.srgb_to_linear(); } diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index 16c0816562..1bff05129b 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -35,7 +35,7 @@ class ImageLoaderHDR : public ImageFormatLoader { public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderHDR(); }; diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index 0e03fa65c8..3e138bf633 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -104,7 +104,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p return OK; } -Error ImageLoaderJPG::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderJPG::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { Vector<uint8_t> src_image; uint64_t src_image_len = f->get_length(); ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT); diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h index 6d631446e7..caa0461d05 100644 --- a/modules/jpg/image_loader_jpegd.h +++ b/modules/jpg/image_loader_jpegd.h @@ -35,7 +35,7 @@ class ImageLoaderJPG : public ImageFormatLoader { public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderJPG(); }; diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 556c8e1563..3277f9beeb 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -18,7 +18,7 @@ def configure(env, env_mono): # is_android = env["platform"] == "android" # is_javascript = env["platform"] == "javascript" # is_ios = env["platform"] == "ios" - # is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"] + # is_ios_sim = is_ios and env["arch"] in ["x86_32", "x86_64"] tools_enabled = env["tools"] @@ -82,7 +82,7 @@ def find_dotnet_app_host_dir(env): dotnet_root = env["dotnet_root"] if not dotnet_root: - dotnet_cmd = find_executable("dotnet") + dotnet_cmd = find_dotnet_executable(env["arch"]) if dotnet_cmd: sdk_path = find_dotnet_sdk(dotnet_cmd, dotnet_version) if sdk_path: @@ -128,26 +128,22 @@ def find_dotnet_app_host_dir(env): def determine_runtime_identifier(env): + # The keys are Godot's names, the values are the Microsoft's names. + # List: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog names_map = { "windows": "win", "macos": "osx", "linuxbsd": "linux", } - - # .NET RID architectures: x86, x64, arm, or arm64 - + arch_map = { + "x86_64": "x64", + "x86_32": "x86", + "arm64": "arm64", + "arm32": "arm", + } platform = env["platform"] - if is_desktop(platform): - if env["arch"] in ["arm", "arm32"]: - rid = "arm" - elif env["arch"] == "arm64": - rid = "arm64" - else: - bits = env["bits"] - bit_arch_map = {"64": "x64", "32": "x86"} - rid = bit_arch_map[bits] - return "%s-%s" % (names_map[platform], rid) + return "%s-%s" % (names_map[platform], arch_map[env["arch"]]) else: raise NotImplementedError() @@ -185,6 +181,35 @@ def find_app_host_version(dotnet_cmd, search_version_str): return "" +def find_dotnet_arch(dotnet_cmd): + import subprocess + + try: + env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US") + lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines() + + for line_bytes in lines: + line = line_bytes.decode("utf-8") + + parts = line.split(":", 1) + if len(parts) < 2: + continue + + arch_str = parts[0].strip() + if arch_str != "Architecture": + continue + + arch_value = parts[1].strip() + arch_map = {"x64": "x86_64", "x86": "x86_32", "arm64": "arm64", "arm32": "arm32"} + return arch_map[arch_value] + except (subprocess.CalledProcessError, OSError) as e: + import sys + + print(e, file=sys.stderr) + + return "" + + def find_dotnet_sdk(dotnet_cmd, search_version_str): import subprocess from distutils.version import LooseVersion @@ -247,24 +272,44 @@ def find_dotnet_cli_rid(dotnet_cmd): ENV_PATH_SEP = ";" if os.name == "nt" else ":" -def find_executable(name): +def find_dotnet_executable(arch): is_windows = os.name == "nt" windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None path_dirs = os.environ["PATH"].split(ENV_PATH_SEP) search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list + for dir in path_dirs: + search_dirs += [ + os.path.join(dir, "x64"), + os.path.join(dir, "x86"), + os.path.join(dir, "arm64"), + os.path.join(dir, "arm32"), + ] # search subfolders for cross compiling + + # `dotnet --info` may not specify architecture. In such cases, + # we fallback to the first one we find without architecture. + sdk_path_unknown_arch = "" + for dir in search_dirs: - path = os.path.join(dir, name) + path = os.path.join(dir, "dotnet") if is_windows: for extension in windows_exts: path_with_ext = path + extension if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK): - return path_with_ext + sdk_arch = find_dotnet_arch(path_with_ext) + if sdk_arch == arch or arch == "": + return path_with_ext + elif sdk_arch == "": + sdk_path_unknown_arch = path_with_ext else: if os.path.isfile(path) and os.access(path, os.X_OK): - return path + sdk_arch = find_dotnet_arch(path) + if sdk_arch == arch or arch == "": + return path + elif sdk_arch == "": + sdk_path_unknown_arch = path - return "" + return sdk_path_unknown_arch diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs index 0f8b128da8..3dfa8000ba 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs @@ -1,6 +1,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace Godot.SourceGenerators { @@ -19,7 +20,7 @@ namespace Godot.SourceGenerators "must be declared with the partial modifier."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0001", + new DiagnosticDescriptor(id: "GD0001", title: message, messageFormat: message, category: "Usage", @@ -51,7 +52,7 @@ namespace Godot.SourceGenerators "containing types must be declared with the partial modifier."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0002", + new DiagnosticDescriptor(id: "GD0002", title: message, messageFormat: message, category: "Usage", @@ -78,7 +79,7 @@ namespace Godot.SourceGenerators " Remove the 'static' modifier or the '[Export]' attribute."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0101", + new DiagnosticDescriptor(id: "GD0101", title: message, messageFormat: message, category: "Usage", @@ -104,7 +105,7 @@ namespace Godot.SourceGenerators string description = $"{message}. Use a supported type or remove the '[Export]' attribute."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0102", + new DiagnosticDescriptor(id: "GD0102", title: message, messageFormat: message, category: "Usage", @@ -132,7 +133,7 @@ namespace Godot.SourceGenerators $"{message}. Exported properties must be writable."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0103", + new DiagnosticDescriptor(id: "GD0103", title: message, messageFormat: message, category: "Usage", @@ -156,7 +157,7 @@ namespace Godot.SourceGenerators string description = $"{message}. Exported properties must be readable."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0104", + new DiagnosticDescriptor(id: "GD0104", title: message, messageFormat: message, category: "Usage", @@ -181,7 +182,7 @@ namespace Godot.SourceGenerators string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0201", + new DiagnosticDescriptor(id: "GD0201", title: message, messageFormat: message, category: "Usage", @@ -205,7 +206,7 @@ namespace Godot.SourceGenerators string description = $"{message}. Use supported types only or remove the '[Signal]' attribute."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0202", + new DiagnosticDescriptor(id: "GD0202", title: message, messageFormat: message, category: "Usage", @@ -229,7 +230,7 @@ namespace Godot.SourceGenerators string description = $"{message}. Return void or remove the '[Signal]' attribute."; context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor(id: "GODOT-G0203", + new DiagnosticDescriptor(id: "GD0203", title: message, messageFormat: message, category: "Usage", @@ -239,5 +240,97 @@ namespace Godot.SourceGenerators location, location?.SourceTree?.FilePath)); } + + public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule = + new DiagnosticDescriptor(id: "GD0301", + title: "The generic type argument must be a Variant compatible type", + messageFormat: "The generic type argument must be a Variant compatible type: {0}", + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument."); + + public static void ReportGenericTypeArgumentMustBeVariant( + SyntaxNodeAnalysisContext context, + SyntaxNode typeArgumentSyntax, + ISymbol typeArgumentSymbol) + { + string message = "The generic type argument " + + $"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'"; + + string description = $"{message}. Use a Variant compatible type as the generic type argument."; + + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor(id: "GD0301", + title: message, + messageFormat: message, + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description), + typeArgumentSyntax.GetLocation(), + typeArgumentSyntax.SyntaxTree.FilePath)); + } + + public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule = + new DiagnosticDescriptor(id: "GD0302", + title: "The generic type parameter must be annotated with the MustBeVariant attribute", + messageFormat: "The generic type argument must be a Variant type: {0}", + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + "The generic type argument must be a Variant type. Use a Variant type as the generic type argument."); + + public static void ReportGenericTypeParameterMustBeVariantAnnotated( + SyntaxNodeAnalysisContext context, + SyntaxNode typeArgumentSyntax, + ISymbol typeArgumentSymbol) + { + string message = "The generic type parameter must be annotated with the MustBeVariant attribute"; + + string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter."; + + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor(id: "GD0302", + title: message, + messageFormat: message, + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description), + typeArgumentSyntax.GetLocation(), + typeArgumentSyntax.SyntaxTree.FilePath)); + } + + public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule = + new DiagnosticDescriptor(id: "GD0303", + title: "The generic type parameter must be annotated with the MustBeVariant attribute", + messageFormat: "The generic type argument must be a Variant type: {0}", + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + "The generic type argument must be a Variant type. Use a Variant type as the generic type argument."); + + public static void ReportTypeArgumentParentSymbolUnhandled( + SyntaxNodeAnalysisContext context, + SyntaxNode typeArgumentSyntax, + ISymbol parentSymbol) + { + string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " + + "that must be Variant compatible was not handled."; + + string description = $"{message}. Handle type arguments that are children of the unhandled symbol type."; + + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor(id: "GD0303", + title: message, + messageFormat: message, + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description), + typeArgumentSyntax.GetLocation(), + typeArgumentSyntax.SyntaxTree.FilePath)); + } } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs index bac4708165..de3b6c862a 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs @@ -177,6 +177,9 @@ namespace Godot.SourceGenerators public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol) => symbol.ToString() == GodotClasses.SignalAttr; + public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol) + => symbol.ToString() == GodotClasses.MustBeVariantAttr; + public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol) => symbol.ToString() == GodotClasses.GodotClassNameAttr; @@ -272,5 +275,13 @@ namespace Godot.SourceGenerators yield return new GodotFieldData(field, marshalType.Value); } } + + public static string Path(this Location location) + => location.SourceTree?.GetLineSpan(location.SourceSpan).Path + ?? location.GetLineSpan().Path; + + public static int StartLine(this Location location) + => location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line + ?? location.GetLineSpan().StartLinePosition.Line; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs index e899440e10..1d8ddbabf2 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs @@ -9,6 +9,7 @@ namespace Godot.SourceGenerators public const string ExportGroupAttr = "Godot.ExportGroupAttribute"; public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute"; public const string SignalAttr = "Godot.SignalAttribute"; + public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute"; public const string GodotClassNameAttr = "Godot.GodotClassName"; public const string SystemFlagsAttr = "System.FlagsAttribute"; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs index a3ad8cbabd..db395e21cb 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs @@ -59,4 +59,26 @@ namespace Godot.SourceGenerators public IFieldSymbol FieldSymbol { get; } public MarshalType Type { get; } } + + public struct GodotPropertyOrFieldData + { + public GodotPropertyOrFieldData(ISymbol symbol, MarshalType type) + { + Symbol = symbol; + Type = type; + } + + public GodotPropertyOrFieldData(GodotPropertyData propertyData) + : this(propertyData.PropertySymbol, propertyData.Type) + { + } + + public GodotPropertyOrFieldData(GodotFieldData fieldData) + : this(fieldData.FieldSymbol, fieldData.Type) + { + } + + public ISymbol Symbol { get; } + public MarshalType Type { get; } + } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs index ca84518c0c..831ac3bdeb 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs @@ -11,11 +11,11 @@ namespace Godot.SourceGenerators { public INamedTypeSymbol GodotObjectType { get; } - public TypeCache(GeneratorExecutionContext context) + public TypeCache(Compilation compilation) { INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName) { - return context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ?? + return compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ?? throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs new file mode 100644 index 0000000000..7aaadb27be --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs @@ -0,0 +1,100 @@ +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Godot.SourceGenerators +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class MustBeVariantAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics + => ImmutableArray.Create( + Common.GenericTypeArgumentMustBeVariantRule, + Common.GenericTypeParameterMustBeVariantAnnotatedRule, + Common.TypeArgumentParentSymbolUnhandledRule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.TypeArgumentList); + } + + private void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var typeArgListSyntax = (TypeArgumentListSyntax)context.Node; + + // Method invocation or variable declaration that contained the type arguments + var parentSyntax = context.Node.Parent; + Debug.Assert(parentSyntax != null); + + var sm = context.SemanticModel; + + var typeCache = new MarshalUtils.TypeCache(context.Compilation); + + for (int i = 0; i < typeArgListSyntax.Arguments.Count; i++) + { + var typeSyntax = typeArgListSyntax.Arguments[i]; + var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol; + Debug.Assert(typeSymbol != null); + + var parentSymbol = sm.GetSymbolInfo(parentSyntax).Symbol; + + if (!ShouldCheckTypeArgument(context, parentSyntax, parentSymbol, typeSyntax, typeSymbol, i)) + { + return; + } + + if (typeSymbol is ITypeParameterSymbol typeParamSymbol) + { + if (!typeParamSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false)) + { + Common.ReportGenericTypeParameterMustBeVariantAnnotated(context, typeSyntax, typeSymbol); + } + continue; + } + + var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(typeSymbol, typeCache); + + if (marshalType == null) + { + Common.ReportGenericTypeArgumentMustBeVariant(context, typeSyntax, typeSymbol); + continue; + } + } + } + + /// <summary> + /// Check if the given type argument is being used in a type parameter that contains + /// the <c>MustBeVariantAttribute</c>; otherwise, we ignore the attribute. + /// </summary> + /// <param name="context">Context for a syntax node action.</param> + /// <param name="parentSyntax">The parent node syntax that contains the type node syntax.</param> + /// <param name="parentSymbol">The symbol retrieved for the parent node syntax.</param> + /// <param name="typeArgumentSyntax">The type node syntax of the argument type to check.</param> + /// <param name="typeArgumentSymbol">The symbol retrieved for the type node syntax.</param> + /// <returns><see langword="true"/> if the type must be variant and must be analyzed.</returns> + private bool ShouldCheckTypeArgument(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntax, ISymbol parentSymbol, TypeSyntax typeArgumentSyntax, ITypeSymbol typeArgumentSymbol, int typeArgumentIndex) + { + var typeParamSymbol = parentSymbol switch + { + IMethodSymbol methodSymbol => methodSymbol.TypeParameters[typeArgumentIndex], + INamedTypeSymbol typeSymbol => typeSymbol.TypeParameters[typeArgumentIndex], + _ => null, + }; + + if (typeParamSymbol == null) + { + Common.ReportTypeArgumentParentSymbolUnhandled(context, typeArgumentSyntax, parentSymbol); + return false; + } + + return typeParamSymbol.GetAttributes() + .Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false); + } + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 1fdc04a262..5ac4f4a47e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -49,7 +49,7 @@ namespace Godot.SourceGenerators if (godotClasses.Length > 0) { - var typeCache = new MarshalUtils.TypeCache(context); + var typeCache = new MarshalUtils.TypeCache(context.Compilation); foreach (var godotClass in godotClasses) { diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index b4db3fda62..7629595b3a 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -49,7 +49,7 @@ namespace Godot.SourceGenerators if (godotClasses.Length > 0) { - var typeCache = new MarshalUtils.TypeCache(context); + var typeCache = new MarshalUtils.TypeCache(context.Compilation); foreach (var godotClass in godotClasses) { @@ -225,28 +225,21 @@ namespace Godot.SourceGenerators .Append(dictionaryType) .Append("();\n"); - foreach (var property in godotClassProperties) - { - foreach (var groupingInfo in DetermineGroupingPropertyInfo(property.PropertySymbol)) - AppendGroupingPropertyInfo(source, groupingInfo); + // To retain the definition order (and display categories correctly), we want to + // iterate over fields and properties at the same time, sorted by line number. + var godotClassPropertiesAndFields = Enumerable.Empty<GodotPropertyOrFieldData>() + .Concat(godotClassProperties.Select(propertyData => new GodotPropertyOrFieldData(propertyData))) + .Concat(godotClassFields.Select(fieldData => new GodotPropertyOrFieldData(fieldData))) + .OrderBy(data => data.Symbol.Locations[0].Path()) + .ThenBy(data => data.Symbol.Locations[0].StartLine()); - var propertyInfo = DeterminePropertyInfo(context, typeCache, - property.PropertySymbol, property.Type); - - if (propertyInfo == null) - continue; - - AppendPropertyInfo(source, propertyInfo.Value); - } - - foreach (var field in godotClassFields) + foreach (var member in godotClassPropertiesAndFields) { - - foreach (var groupingInfo in DetermineGroupingPropertyInfo(field.FieldSymbol)) + foreach (var groupingInfo in DetermineGroupingPropertyInfo(member.Symbol)) AppendGroupingPropertyInfo(source, groupingInfo); var propertyInfo = DeterminePropertyInfo(context, typeCache, - field.FieldSymbol, field.Type); + member.Symbol, member.Type); if (propertyInfo == null) continue; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 3b8ba21107..85fadaa52e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -49,7 +49,7 @@ namespace Godot.SourceGenerators if (godotClasses.Length > 0) { - var typeCache = new MarshalUtils.TypeCache(context); + var typeCache = new MarshalUtils.TypeCache(context.Compilation); foreach (var godotClass in godotClasses) { diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index 1b87c6e760..5a84122c4c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -49,7 +49,7 @@ namespace Godot.SourceGenerators if (godotClasses.Length > 0) { - var typeCache = new MarshalUtils.TypeCache(context); + var typeCache = new MarshalUtils.TypeCache(context.Compilation); foreach (var godotClass in godotClasses) { diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index f1c7706f9c..6b06f10db1 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -56,7 +56,7 @@ namespace Godot.SourceGenerators if (godotClasses.Length > 0) { - var typeCache = new MarshalUtils.TypeCache(context); + var typeCache = new MarshalUtils.TypeCache(context.Compilation); foreach (var godotClass in godotClasses) { @@ -117,10 +117,6 @@ namespace Godot.SourceGenerators source.Append(symbol.NameWithTypeParameters()); source.Append("\n{\n"); - // TODO: - // The delegate name already needs to end with 'Signal' to avoid collision with the event name. - // Requiring SignalAttribute is redundant. Should we remove it to make declaration shorter? - var members = symbol.GetMembers(); var signalDelegateSymbols = members diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs index 686202e81e..2448a2953b 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs @@ -25,10 +25,7 @@ namespace GodotTools.IdeMessaging public override bool Equals(object obj) { - if (obj is GodotIdeMetadata metadata) - return metadata == this; - - return false; + return obj is GodotIdeMetadata metadata && metadata == this; } public bool Equals(GodotIdeMetadata other) diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs index 3c5b897719..edbf53a389 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs @@ -27,15 +27,13 @@ namespace GodotTools.Build public override bool Equals(object? obj) { - if (obj is BuildInfo other) - return other.Solution == Solution && - other.Configuration == Configuration && other.RuntimeIdentifier == RuntimeIdentifier && - other.PublishOutputDir == PublishOutputDir && other.Restore == Restore && - other.Rebuild == Rebuild && other.OnlyClean == OnlyClean && - other.CustomProperties == CustomProperties && - other.LogsDirPath == LogsDirPath; - - return false; + return obj is BuildInfo other && + other.Solution == Solution && + other.Configuration == Configuration && other.RuntimeIdentifier == RuntimeIdentifier && + other.PublishOutputDir == PublishOutputDir && other.Restore == Restore && + other.Rebuild == Rebuild && other.OnlyClean == OnlyClean && + other.CustomProperties == CustomProperties && + other.LogsDirPath == LogsDirPath; } public override int GetHashCode() diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index 8aec1bd656..fc325fc25b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -75,8 +75,17 @@ namespace GodotTools.Export } else { - string bits = features.Contains("64") ? "64" : features.Contains("32") ? "32" : null; - CompileAssembliesForDesktop(exporter, platform, isDebug, bits, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir); + string arch = ""; + if (features.Contains("x86_64")) { + arch = "x86_64"; + } else if (features.Contains("x86_32")) { + arch = "x86_32"; + } else if (features.Contains("arm64")) { + arch = "arm64"; + } else if (features.Contains("arm32")) { + arch = "arm32"; + } + CompileAssembliesForDesktop(exporter, platform, isDebug, arch, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir); } } @@ -112,7 +121,7 @@ namespace GodotTools.Export } } - public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string bits, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir) + public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string arch, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir) { foreach (var assembly in assemblies) { @@ -126,9 +135,9 @@ namespace GodotTools.Export string outputFileName = assemblyName + ".dll" + outputFileExtension; string tempOutputFilePath = Path.Combine(aotTempDir, outputFileName); - var compilerArgs = GetAotCompilerArgs(platform, isDebug, bits, aotOpts, assemblyPath, tempOutputFilePath); + var compilerArgs = GetAotCompilerArgs(platform, isDebug, arch, aotOpts, assemblyPath, tempOutputFilePath); - string compilerDirPath = GetMonoCrossDesktopDirName(platform, bits); + string compilerDirPath = GetMonoCrossDesktopDirName(platform, arch); ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir); @@ -432,9 +441,9 @@ MONO_AOT_MODE_LAST = 1000, var androidToolPrefixes = new Dictionary<string, string> { - ["armeabi-v7a"] = "arm-linux-androideabi-", - ["arm64-v8a"] = "aarch64-linux-android-", - ["x86"] = "i686-linux-android-", + ["arm32"] = "arm-linux-androideabi-", + ["arm64"] = "aarch64-linux-android-", + ["x86_32"] = "i686-linux-android-", ["x86_64"] = "x86_64-linux-android-" }; @@ -547,9 +556,9 @@ MONO_AOT_MODE_LAST = 1000, { var androidAbis = new[] { - "armeabi-v7a", - "arm64-v8a", - "x86", + "arm32", + "arm64", + "x86_32", "x86_64" }; @@ -560,9 +569,9 @@ MONO_AOT_MODE_LAST = 1000, { var abiArchs = new Dictionary<string, string> { - ["armeabi-v7a"] = "armv7", - ["arm64-v8a"] = "aarch64-v8a", - ["x86"] = "i686", + ["arm32"] = "armv7", + ["arm64"] = "aarch64-v8a", + ["x86_32"] = "i686", ["x86_64"] = "x86_64" }; @@ -571,30 +580,25 @@ MONO_AOT_MODE_LAST = 1000, return $"{arch}-linux-android"; } - private static string GetMonoCrossDesktopDirName(string platform, string bits) + private static string GetMonoCrossDesktopDirName(string platform, string arch) { switch (platform) { case OS.Platforms.Windows: case OS.Platforms.UWP: { - string arch = bits == "64" ? "x86_64" : "i686"; return $"windows-{arch}"; } case OS.Platforms.MacOS: { - Debug.Assert(bits == null || bits == "64"); - string arch = "x86_64"; return $"{platform}-{arch}"; } case OS.Platforms.LinuxBSD: { - string arch = bits == "64" ? "x86_64" : "i686"; return $"linux-{arch}"; } case OS.Platforms.Haiku: { - string arch = bits == "64" ? "x86_64" : "i686"; return $"{platform}-{arch}"; } default: diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index 76f0f0b784..651922d019 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -15,6 +15,9 @@ namespace GodotTools.Utils [SuppressMessage("ReSharper", "InconsistentNaming")] public static class OS { + /// <summary> + /// Display names for the OS platforms. + /// </summary> private static class Names { public const string Windows = "Windows"; @@ -30,6 +33,9 @@ namespace GodotTools.Utils public const string HTML5 = "HTML5"; } + /// <summary> + /// Godot platform identifiers. + /// </summary> public static class Platforms { public const string Windows = "windows"; @@ -42,6 +48,10 @@ namespace GodotTools.Utils public const string HTML5 = "javascript"; } + /// <summary> + /// OS name part of the .NET runtime identifier (RID). + /// See https://docs.microsoft.com/en-us/dotnet/core/rid-catalog. + /// </summary> public static class DotNetOS { public const string Win = "win"; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 73d8f23081..d70a1e6c88 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -87,9 +87,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor" #define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind" -#define CS_STATIC_FIELD_METHOD_NAME_PREFIX "MethodName_" #define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_" -#define CS_STATIC_FIELD_SIGNAL_NAME_PREFIX "SignalName_" #define ICALL_PREFIX "godot_icall_" #define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method" @@ -1606,12 +1604,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n" << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n" << INDENT1 "private static readonly StringName " - << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name - << " = \"" << imethod.name << "\";\n"; - - output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n" - << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n" - << INDENT1 "private static readonly StringName " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name << " = \"" << imethod.proxy_name << "\";\n"; } @@ -1637,7 +1629,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // We check both native names (snake_case) and proxy names (PascalCase) output << INDENT2 "if ((method == " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name - << " || method == " << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name + << " || method == MethodName." << imethod.proxy_name << ") && argCount == " << itos(imethod.arguments.size()) << " && " << CS_METHOD_HAS_GODOT_CLASS_METHOD << "((godot_string_name)" << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name << ".NativeValue))\n" @@ -1713,7 +1705,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // again, but this time with the respective proxy name (PascalCase). It's the job of // user derived classes to override the method and check for those. Our C# source // generators take care of generating those override methods. - output << INDENT2 "if (method == " << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name + output << INDENT2 "if (method == MethodName." << imethod.proxy_name << ")\n" INDENT2 "{\n" << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_METHOD "(" << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name @@ -1731,6 +1723,45 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output << INDENT1 "}\n"; } + //Generate StringName for all class members + bool is_inherit = !itype.is_singleton && obj_types.has(itype.base_name); + //PropertyName + if (is_inherit) { + output << MEMBER_BEGIN "public new class PropertyName : " << obj_types[itype.base_name].proxy_name << ".PropertyName"; + } else { + output << MEMBER_BEGIN "public class PropertyName"; + } + output << "\n" + << INDENT1 "{\n"; + for (const PropertyInterface &iprop : itype.properties) { + output << INDENT2 "public static readonly StringName " << iprop.proxy_name << " = \"" << iprop.cname << "\";\n"; + } + output << INDENT1 "}\n"; + //MethodName + if (is_inherit) { + output << MEMBER_BEGIN "public new class MethodName : " << obj_types[itype.base_name].proxy_name << ".MethodName"; + } else { + output << MEMBER_BEGIN "public class MethodName"; + } + output << "\n" + << INDENT1 "{\n"; + for (const MethodInterface &imethod : itype.methods) { + output << INDENT2 "public static readonly StringName " << imethod.proxy_name << " = \"" << imethod.cname << "\";\n"; + } + output << INDENT1 "}\n"; + //SignalName + if (is_inherit) { + output << MEMBER_BEGIN "public new class SignalName : " << obj_types[itype.base_name].proxy_name << ".SignalName"; + } else { + output << MEMBER_BEGIN "public class SignalName"; + } + output << "\n" + << INDENT1 "{\n"; + for (const SignalInterface &isignal : itype.signals_) { + output << INDENT2 "public static readonly StringName " << isignal.proxy_name << " = \"" << isignal.cname << "\";\n"; + } + output << INDENT1 "}\n"; + output.append(CLOSE_BLOCK /* class */); output.append("\n" @@ -2052,9 +2083,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf p_output << "Object."; } - p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", \"" - << p_imethod.name - << "\");\n"; + p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", MethodName." + << p_imethod.proxy_name + << ");\n"; } if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { @@ -2226,7 +2257,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf } String delegate_name = p_isignal.proxy_name; - delegate_name += "Handler"; // Delegate name is [SignalName]Handler + delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler // Generate delegate p_output.append(MEMBER_BEGIN "public delegate void "); @@ -2239,17 +2270,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded? // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here. - // Cached signal name (StringName) - p_output.append(MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"); - p_output.append(INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN - "private static readonly StringName " CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); - p_output.append(p_isignal.name); - p_output.append(" = \""); - p_output.append(p_isignal.name); - p_output.append("\";\n"); - // Generate event - p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public "); + p_output.append(MEMBER_BEGIN "public "); if (p_itype.is_singleton) { p_output.append("static "); @@ -2262,21 +2284,21 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf p_output.append("\n" OPEN_BLOCK_L1 INDENT2); if (p_itype.is_singleton) { - p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); + p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(SignalName."); } else { - p_output.append("add => Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); + p_output.append("add => Connect(SignalName."); } - p_output.append(p_isignal.name); + p_output.append(p_isignal.proxy_name); p_output.append(", new Callable(value));\n"); if (p_itype.is_singleton) { - p_output.append(INDENT2 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); + p_output.append(INDENT2 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(SignalName."); } else { - p_output.append(INDENT2 "remove => Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); + p_output.append(INDENT2 "remove => Disconnect(SignalName."); } - p_output.append(p_isignal.name); + p_output.append(p_isignal.proxy_name); p_output.append(", new Callable(value));\n"); p_output.append(CLOSE_BLOCK_L1); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index f87f37bc43..b3a36e8ac8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -697,12 +697,7 @@ namespace Godot /// <returns>Whether or not the AABB and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is AABB) - { - return Equals((AABB)obj); - } - - return false; + return obj is AABB other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 81991c6626..f2984bd1fb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -483,7 +483,7 @@ namespace Godot.Collections /// <typeparam name="T">The type of the array.</typeparam> [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")] - public sealed class Array<T> : + public sealed class Array<[MustBeVariant] T> : IList<T>, IReadOnlyList<T>, ICollection<T>, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs new file mode 100644 index 0000000000..23088378d1 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Godot +{ + /// <summary> + /// Attribute that restricts generic type parameters to be only types + /// that can be marshaled from/to a <see cref="Variant"/>. + /// </summary> + [AttributeUsage(AttributeTargets.GenericParameter)] + public class MustBeVariantAttribute : Attribute { } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs index 07a214f543..38e68a89d5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs @@ -2,6 +2,6 @@ using System; namespace Godot { - [AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)] + [AttributeUsage(AttributeTargets.Delegate)] public class SignalAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 4cb9bf5758..87adf9efe5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -892,12 +892,7 @@ namespace Godot /// <returns>Whether or not the basis matrix and the object are exactly equal.</returns> public override bool Equals(object obj) { - if (obj is Basis) - { - return Equals((Basis)obj); - } - - return false; + return obj is Basis other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index ed0e1efd35..0cbaef3dad 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -1151,12 +1151,7 @@ namespace Godot /// <returns>Whether or not the color and the other object are equal.</returns> public override bool Equals(object obj) { - if (obj is Color) - { - return Equals((Color)obj); - } - - return false; + return obj is Color other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index 6fbc04702a..c4161d2ded 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -121,8 +121,8 @@ namespace Godot var sb = new StringBuilder(); - if (methodBase is MethodInfo) - sb.AppendTypeName(((MethodInfo)methodBase).ReturnType); + if (methodBase is MethodInfo methodInfo) + sb.AppendTypeName(methodInfo.ReturnType); sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>"); sb.Append('.'); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index fa8c94ed18..95ad097192 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -352,7 +352,7 @@ namespace Godot.Collections /// </summary> /// <typeparam name="TKey">The type of the dictionary's keys.</typeparam> /// <typeparam name="TValue">The type of the dictionary's values.</typeparam> - public class Dictionary<TKey, TValue> : + public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs index 1dc21b6303..df0e839866 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs @@ -93,8 +93,12 @@ namespace Godot /// Negative indices access the children from the last one. /// To access a child node via its name, use <see cref="GetNode"/>. /// </summary> - /// <seealso cref="GetChildOrNull{T}(int)"/> + /// <seealso cref="GetChildOrNull{T}(int, bool)"/> /// <param name="idx">Child index.</param> + /// <param name="includeInternal"> + /// If <see langword="false"/>, internal children are skipped (see <c>internal</c> + /// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>). + /// </param> /// <exception cref="InvalidCastException"> /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>. /// </exception> @@ -102,9 +106,9 @@ namespace Godot /// <returns> /// The child <see cref="Node"/> at the given index <paramref name="idx"/>. /// </returns> - public T GetChild<T>(int idx) where T : class + public T GetChild<T>(int idx, bool includeInternal = false) where T : class { - return (T)(object)GetChild(idx); + return (T)(object)GetChild(idx, includeInternal); } /// <summary> @@ -113,15 +117,20 @@ namespace Godot /// Negative indices access the children from the last one. /// To access a child node via its name, use <see cref="GetNode"/>. /// </summary> - /// <seealso cref="GetChild{T}(int)"/> + /// <seealso cref="GetChild{T}(int, bool)"/> /// <param name="idx">Child index.</param> + /// <param name="includeInternal"> + /// If <see langword="false"/>, internal children are skipped (see <c>internal</c> + /// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>). + /// </param> /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> /// <returns> /// The child <see cref="Node"/> at the given index <paramref name="idx"/>, or <see langword="null"/> if not found. /// </returns> - public T GetChildOrNull<T>(int idx) where T : class + public T GetChildOrNull<T>(int idx, bool includeInternal = false) where T : class { - return GetChild(idx) as T; + int count = GetChildCount(includeInternal); + return idx >= -count && idx < count ? GetChild(idx, includeInternal) as T : null; } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 9348cc1d00..9e7da7757a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -189,8 +189,6 @@ namespace Godot /// Pushes an error message to Godot's built-in debugger and to the OS terminal. /// /// Note: Errors printed this way will not pause project execution. - /// To print an error message and pause project execution in debug builds, - /// use [code]assert(false, "test error")[/code] instead. /// </summary> /// <example> /// <code> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 08beff8104..41a0dd871c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -754,9 +754,10 @@ namespace Godot } /// <summary> - /// Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. - /// If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). - /// If [code]length[/code] is less than zero, it becomes positive. + /// Returns the <paramref name="value"/> wrapped between <c>0</c> and the <paramref name="length"/>. + /// If the limit is reached, the next value the function returned is decreased to the <c>0</c> side + /// or increased to the <paramref name="length"/> side (like a triangle wave). + /// If <paramref name="length"/> is less than zero, it becomes positive. /// </summary> /// <param name="value">The value to pingpong.</param> /// <param name="length">The maximum value of the function.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 50832d7679..13070c8033 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -353,12 +353,7 @@ namespace Godot /// <returns>Whether or not the plane and the other object are exactly equal.</returns> public override bool Equals(object obj) { - if (obj is Plane) - { - return Equals((Plane)obj); - } - - return false; + return obj is Plane other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index b85a105a0b..5da1f3b560 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -39,22 +39,22 @@ namespace Godot } /// <summary> - /// The projections's X column. Also accessible by using the index position <c>[0]</c>. + /// The projection's X column. Also accessible by using the index position <c>[0]</c>. /// </summary> public Vector4 x; /// <summary> - /// The projections's Y column. Also accessible by using the index position <c>[1]</c>. + /// The projection's Y column. Also accessible by using the index position <c>[1]</c>. /// </summary> public Vector4 y; /// <summary> - /// The projections's Z column. Also accessible by using the index position <c>[2]</c>. + /// The projection's Z column. Also accessible by using the index position <c>[2]</c>. /// </summary> public Vector4 z; /// <summary> - /// The projections's W column. Also accessible by using the index position <c>[3]</c>. + /// The projection's W column. Also accessible by using the index position <c>[3]</c>. /// </summary> public Vector4 w; @@ -800,11 +800,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Projection) - { - return Equals((Projection)obj); - } - return false; + return obj is Projection other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 658a14ca1d..999500ca13 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -580,12 +580,7 @@ namespace Godot /// <returns>Whether or not the quaternion and the other object are exactly equal.</returns> public override bool Equals(object obj) { - if (obj is Quaternion) - { - return Equals((Quaternion)obj); - } - - return false; + return obj is Quaternion other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index d2c9b0ca8b..0b475fec19 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -426,12 +426,7 @@ namespace Godot /// <returns>Whether or not the rect and the other object are exactly equal.</returns> public override bool Equals(object obj) { - if (obj is Rect2) - { - return Equals((Rect2)obj); - } - - return false; + return obj is Rect2 other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index 5d53b8330e..8a2a98d6ee 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -426,12 +426,7 @@ namespace Godot /// <returns>Whether or not the rect and the other object are equal.</returns> public override bool Equals(object obj) { - if (obj is Rect2i) - { - return Equals((Rect2i)obj); - } - - return false; + return obj is Rect2i other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index 10739c02a7..b9ee0bc278 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -59,9 +59,9 @@ namespace Godot } /// <summary> - /// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string. + /// Constructs a <see cref="StringName"/> from the given <paramref name="name"/> string. /// </summary> - /// <param name="path">String to construct the <see cref="StringName"/> from.</param> + /// <param name="name">String to construct the <see cref="StringName"/> from.</param> public StringName(string name) { if (!string.IsNullOrEmpty(name)) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 70cf8bbe22..33b4f11f62 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -602,7 +602,7 @@ namespace Godot /// <returns>Whether or not the transform and the object are exactly equal.</returns> public override bool Equals(object obj) { - return obj is Transform2D transform2D && Equals(transform2D); + return obj is Transform2D other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 5481225e3f..4b739bb86b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -579,12 +579,7 @@ namespace Godot /// <returns>Whether or not the transform and the object are exactly equal.</returns> public override readonly bool Equals(object obj) { - if (obj is Transform3D) - { - return Equals((Transform3D)obj); - } - - return false; + return obj is Transform3D other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 04c2ea7eb9..03ee12884b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -925,11 +925,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Vector2) - { - return Equals((Vector2)obj); - } - return false; + return obj is Vector2 other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs index a8f42972d7..666616edec 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -667,12 +667,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Vector2i) - { - return Equals((Vector2i)obj); - } - - return false; + return obj is Vector2i other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index d5941d6b60..cdba06c089 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -993,12 +993,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Vector3) - { - return Equals((Vector3)obj); - } - - return false; + return obj is Vector3 other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs index eb46f36e7c..2947ef94a7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -676,12 +676,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Vector3i) - { - return Equals((Vector3i)obj); - } - - return false; + return obj is Vector3i other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs index 20a24616ce..705da04692 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs @@ -754,12 +754,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Vector4) - { - return Equals((Vector4)obj); - } - - return false; + return obj is Vector4 other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs index 11c2b7234b..73134b0baf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs @@ -629,12 +629,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override bool Equals(object obj) { - if (obj is Vector4i) - { - return Equals((Vector4i)obj); - } - - return false; + return obj is Vector4i other && Equals(other); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index f0d6748b73..aae7a5ebfa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -56,6 +56,7 @@ <Compile Include="Core\Attributes\ExportCategoryAttribute.cs" /> <Compile Include="Core\Attributes\ExportGroupAttribute.cs" /> <Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" /> + <Compile Include="Core\Attributes\MustBeVariantAttribute.cs" /> <Compile Include="Core\Attributes\RPCAttribute.cs" /> <Compile Include="Core\Attributes\ScriptPathAttribute.cs" /> <Compile Include="Core\Attributes\SignalAttribute.cs" /> diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 6bb522cc01..4191e46f62 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -453,11 +453,11 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) { } } -COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) { +COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance) { RvoAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); - agent->get_agent()->neighborDist_ = p_dist; + agent->get_agent()->neighborDist_ = p_distance; } COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) { diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index e5f4b421bc..05ba46ede1 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -135,7 +135,7 @@ public: virtual RID agent_create() const override; COMMAND_2(agent_set_map, RID, p_agent, RID, p_map); virtual RID agent_get_map(RID p_agent) const override; - COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist); + COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance); COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count); COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time); COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius); diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub index ef4c598194..074795759a 100644 --- a/modules/raycast/SCsub +++ b/modules/raycast/SCsub @@ -66,7 +66,7 @@ if env["builtin_embree"]: env_raycast.Append(CPPDEFINES=["EMBREE_TARGET_SSE2", "EMBREE_LOWEST_ISA", "TASKING_INTERNAL", "NDEBUG"]) if not env.msvc: - if env["arch"] in ["x86", "x86_64"]: + if env["arch"] == "x86_64": env_raycast.Append(CPPFLAGS=["-msse2", "-mxsave"]) if env["platform"] == "windows": @@ -83,7 +83,7 @@ if env["builtin_embree"]: env_thirdparty.disable_warnings() env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) - if not env["arch"] in ["x86", "x86_64"] or env.msvc: + if env["arch"] == "arm64" or env.msvc: # Embree needs those, it will automatically use SSE2NEON in ARM env_thirdparty.Append(CPPDEFINES=["__SSE2__", "__SSE__"]) diff --git a/modules/raycast/config.py b/modules/raycast/config.py index 7e8b3e9840..438779343e 100644 --- a/modules/raycast/config.py +++ b/modules/raycast/config.py @@ -1,18 +1,6 @@ def can_build(env, platform): - # Depends on Embree library, which only supports x86_64 and aarch64. - if env["arch"].startswith("rv") or env["arch"].startswith("ppc"): - return False - - if platform == "android": - return env["android_arch"] in ["arm64v8", "x86_64"] - - if platform == "javascript": - return False # No SIMD support yet - - if env["bits"] == "32": - return False - - return True + # Depends on Embree library, which only supports x86_64 and arm64. + return env["arch"] in ["x86_64", "arm64"] def configure(env): diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 4c9302eb0c..5f839bd979 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -136,11 +136,11 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const p_extensions->push_back("svg"); } -Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, bool p_force_linear, float p_scale) { +Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) { String svg = p_fileaccess->get_as_utf8_string(); create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>()); ERR_FAIL_COND_V(p_image->is_empty(), FAILED); - if (p_force_linear) { + if (p_flags & FLAG_FORCE_LINEAR) { p_image->srgb_to_linear(); } return OK; diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index f28df60c44..fc89b63edb 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -39,7 +39,7 @@ class ImageLoaderSVG : public ImageFormatLoader { public: void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map); - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, bool p_force_linear, float p_scale) override; + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) override; virtual void get_recognized_extensions(List<String> *p_extensions) const override; }; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e80eb43369..366e1ba604 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -29,12 +29,12 @@ /*************************************************************************/ #include "text_server_adv.h" -#include "core/object/worker_thread_pool.h" #ifdef GDEXTENSION // Headers for building as GDExtension plug-in. #include <godot_cpp/classes/file.hpp> +#include <godot_cpp/classes/project_settings.hpp> #include <godot_cpp/classes/rendering_server.hpp> #include <godot_cpp/classes/translation_server.hpp> #include <godot_cpp/core/error_macros.hpp> @@ -44,8 +44,10 @@ using namespace godot; #else // Headers for building as built-in module. +#include "core/config/project_settings.h" #include "core/core_bind.h" #include "core/error/error_macros.h" +#include "core/object/worker_thread_pool.h" #include "core/string/print_string.h" #include "core/string/translation.h" @@ -791,6 +793,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_ for (int i = 0; i < p_data->textures.size(); i++) { const FontTexture &ct = p_data->textures[i]; + if (p_image_format != ct.format) { + continue; + } + if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. continue; } @@ -1082,9 +1088,28 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( #endif #ifdef MODULE_FREETYPE_ENABLED -_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const { int w = bitmap.width; int h = bitmap.rows; + int color_size = 2; + + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY: { + color_size = 2; + } break; + case FT_PIXEL_MODE_BGRA: { + color_size = 4; + } break; + case FT_PIXEL_MODE_LCD: { + color_size = 4; + w /= 3; + } break; + case FT_PIXEL_MODE_LCD_V: { + color_size = 4; + h /= 3; + } break; + } int mw = w + p_rect_margin * 4; int mh = h + p_rect_margin * 4; @@ -1092,7 +1117,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma ERR_FAIL_COND_V(mw > 4096, FontGlyph()); ERR_FAIL_COND_V(mh > 4096, FontGlyph()); - int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false); @@ -1127,6 +1151,34 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; } break; + case FT_PIXEL_MODE_LCD: { + int ofs_color = i * bitmap.pitch + (j * 3); + if (p_bgra) { + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = 255; + } else { + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 3] = 255; + } + } break; + case FT_PIXEL_MODE_LCD_V: { + int ofs_color = i * bitmap.pitch * 3 + j; + if (p_bgra) { + wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 3] = 255; + } else { + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; + wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 3] = 255; + } + } break; default: ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + "."); break; @@ -1230,9 +1282,44 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, FT_Outline_Transform(&fd->face->glyph->outline, &mat); } + FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL; + bool bgra = false; + switch (p_font_data->antialiasing) { + case FONT_ANTIALIASING_NONE: { + aa_mode = FT_RENDER_MODE_MONO; + } break; + case FONT_ANTIALIASING_GRAY: { + aa_mode = FT_RENDER_MODE_NORMAL; + } break; + case FONT_ANTIALIASING_LCD: { + int aa_layout = (int)((p_glyph >> 24) & 7); + switch (aa_layout) { + case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: { + aa_mode = FT_RENDER_MODE_LCD; + bgra = false; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: { + aa_mode = FT_RENDER_MODE_LCD; + bgra = true; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: { + aa_mode = FT_RENDER_MODE_LCD_V; + bgra = false; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: { + aa_mode = FT_RENDER_MODE_LCD_V; + bgra = true; + } break; + default: { + aa_mode = FT_RENDER_MODE_NORMAL; + } break; + } + } break; + } + if (!outline) { if (!p_font_data->msdf) { - error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + error = FT_Render_Glyph(fd->face->glyph, aa_mode); } FT_GlyphSlot slot = fd->face->glyph; if (!error) { @@ -1244,7 +1331,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); #endif } else { - gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra); } } } else { @@ -1264,11 +1351,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { goto cleanup_glyph; } - if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) { goto cleanup_glyph; } glyph_bitmap = (FT_BitmapGlyph)glyph; - gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra); cleanup_glyph: FT_Done_Glyph(glyph); @@ -1925,23 +2012,23 @@ String TextServerAdvanced::font_get_name(const RID &p_font_rid) const { return fd->font_name; } -void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) { +void TextServerAdvanced::font_set_antialiasing(RID p_font_rid, TextServer::FontAntialiasing p_antialiasing) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); MutexLock lock(fd->mutex); - if (fd->antialiased != p_antialiased) { + if (fd->antialiasing != p_antialiasing) { _font_clear_cache(fd); - fd->antialiased = p_antialiased; + fd->antialiasing = p_antialiasing; } } -bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const { +TextServer::FontAntialiasing TextServerAdvanced::font_get_antialiasing(RID p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, false); + ERR_FAIL_COND_V(!fd, TextServer::FONT_ANTIALIASING_NONE); MutexLock lock(fd->mutex); - return fd->antialiased; + return fd->antialiasing; } void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) { @@ -2514,7 +2601,16 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_ Vector2i size = _get_size(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2557,7 +2653,16 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2593,7 +2698,16 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2629,7 +2743,16 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Rect2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2660,7 +2783,16 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return -1; // Invalid or non graphicl glyph, do not display errors. } @@ -2691,7 +2823,16 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return RID(); // Invalid or non graphicl glyph, do not display errors. } @@ -2730,7 +2871,16 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Size2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2986,16 +3136,18 @@ void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i if (fd->msdf) { _ensure_glyph(fd, size, (int32_t)idx); } else { - if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27)); - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - } else { - _ensure_glyph(fd, size, (int32_t)idx); + for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { + if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + } else { + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + } } } } @@ -3016,16 +3168,18 @@ void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i if (fd->msdf) { _ensure_glyph(fd, size, (int32_t)idx); } else { - if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27)); - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - } else { - _ensure_glyph(fd, size, (int32_t)idx); + for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { + if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + } else { + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + } } } } @@ -3041,9 +3195,19 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. + bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED if (!fd->msdf && fd->cache[size]->face) { + // LCD layout, bits 24, 25, 26 + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + lcd_aa = true; + index = index | (layout << 24); + } + } + // Subpixel X-shift, bits 27, 28 if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125)); index = index | (xshift << 27); @@ -3065,7 +3229,7 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can if (gl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) { + if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa) { modulate.r = modulate.g = modulate.b = 1.0; } #endif @@ -3103,7 +3267,11 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can } cpos += gl.rect.position; Size2 csize = gl.rect.size; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + if (lcd_aa) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + } else { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } } @@ -3119,9 +3287,19 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. + bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED if (!fd->msdf && fd->cache[size]->face) { + // LCD layout, bits 24, 25, 26 + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + lcd_aa = true; + index = index | (layout << 24); + } + } + // Subpixel X-shift, bits 27, 28 if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125)); index = index | (xshift << 27); @@ -3181,7 +3359,11 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI } cpos += gl.rect.position; Size2 csize = gl.rect.size; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + if (lcd_aa) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + } else { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } } @@ -4947,6 +5129,14 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count); + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + // Process glyphs. if (glyph_count > 0) { Glyph *w = (Glyph *)memalloc(glyph_count * sizeof(Glyph)); @@ -5000,7 +5190,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star gl.index = glyph_info[i].codepoint; if (gl.index != 0) { - _ensure_glyph(fd, fss, gl.index); + _ensure_glyph(fd, fss, gl.index | mod); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { if (subpos) { gl.advance = glyph_pos[i].x_advance / (64.0 / scale) + ea; diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 6c3e8dbdcb..02abc31c55 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -223,7 +223,7 @@ class TextServerAdvanced : public TextServerExtension { struct FontAdvanced { Mutex mutex; - bool antialiased = true; + TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; bool mipmaps = false; bool msdf = false; int msdf_range = 14; @@ -271,7 +271,7 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; #endif #ifdef MODULE_FREETYPE_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const; #endif _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const; @@ -498,8 +498,8 @@ public: virtual void font_set_name(const RID &p_font_rid, const String &p_name) override; virtual String font_get_name(const RID &p_font_rid) const override; - virtual void font_set_antialiased(const RID &p_font_rid, bool p_antialiased) override; - virtual bool font_is_antialiased(const RID &p_font_rid) const override; + virtual void font_set_antialiasing(RID p_font_rid, TextServer::FontAntialiasing p_antialiasing) override; + virtual TextServer::FontAntialiasing font_get_antialiasing(RID p_font_rid) const override; virtual void font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) override; virtual bool font_get_generate_mipmaps(const RID &p_font_rid) const override; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 016bcfc886..53b303cb20 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -34,6 +34,7 @@ // Headers for building as GDExtension plug-in. #include <godot_cpp/classes/file.hpp> +#include <godot_cpp/classes/project_settings.hpp> #include <godot_cpp/classes/rendering_server.hpp> #include <godot_cpp/classes/translation_server.hpp> #include <godot_cpp/core/error_macros.hpp> @@ -43,6 +44,7 @@ using namespace godot; #else // Headers for building as built-in module. +#include "core/config/project_settings.h" #include "core/error/error_macros.h" #include "core/string/print_string.h" #include "core/string/ucaps.h" @@ -209,6 +211,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_ for (int i = 0; i < p_data->textures.size(); i++) { const FontTexture &ct = p_data->textures[i]; + if (p_image_format != ct.format) { + continue; + } + if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. continue; } @@ -500,9 +506,28 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf( #endif #ifdef MODULE_FREETYPE_ENABLED -_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const { int w = bitmap.width; int h = bitmap.rows; + int color_size = 2; + + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY: { + color_size = 2; + } break; + case FT_PIXEL_MODE_BGRA: { + color_size = 4; + } break; + case FT_PIXEL_MODE_LCD: { + color_size = 4; + w /= 3; + } break; + case FT_PIXEL_MODE_LCD_V: { + color_size = 4; + h /= 3; + } break; + } int mw = w + p_rect_margin * 4; int mh = h + p_rect_margin * 4; @@ -510,7 +535,6 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma ERR_FAIL_COND_V(mw > 4096, FontGlyph()); ERR_FAIL_COND_V(mh > 4096, FontGlyph()); - int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false); @@ -545,6 +569,34 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; } break; + case FT_PIXEL_MODE_LCD: { + int ofs_color = i * bitmap.pitch + (j * 3); + if (p_bgra) { + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = 255; + } else { + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 3] = 255; + } + } break; + case FT_PIXEL_MODE_LCD_V: { + int ofs_color = i * bitmap.pitch * 3 + j; + if (p_bgra) { + wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 3] = 255; + } else { + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; + wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 3] = 255; + } + } break; default: ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + "."); break; @@ -650,9 +702,44 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, FT_Outline_Transform(&fd->face->glyph->outline, &mat); } + FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL; + bool bgra = false; + switch (p_font_data->antialiasing) { + case FONT_ANTIALIASING_NONE: { + aa_mode = FT_RENDER_MODE_MONO; + } break; + case FONT_ANTIALIASING_GRAY: { + aa_mode = FT_RENDER_MODE_NORMAL; + } break; + case FONT_ANTIALIASING_LCD: { + int aa_layout = (int)((p_glyph >> 24) & 7); + switch (aa_layout) { + case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: { + aa_mode = FT_RENDER_MODE_LCD; + bgra = false; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: { + aa_mode = FT_RENDER_MODE_LCD; + bgra = true; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: { + aa_mode = FT_RENDER_MODE_LCD_V; + bgra = false; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: { + aa_mode = FT_RENDER_MODE_LCD_V; + bgra = true; + } break; + default: { + aa_mode = FT_RENDER_MODE_NORMAL; + } break; + } + } break; + } + if (!outline) { if (!p_font_data->msdf) { - error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + error = FT_Render_Glyph(fd->face->glyph, aa_mode); } FT_GlyphSlot slot = fd->face->glyph; if (!error) { @@ -664,7 +751,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); #endif } else { - gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra); } } } else { @@ -684,11 +771,11 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { goto cleanup_glyph; } - if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) { goto cleanup_glyph; } glyph_bitmap = (FT_BitmapGlyph)glyph; - gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra); cleanup_glyph: FT_Done_Glyph(glyph); @@ -1014,23 +1101,23 @@ String TextServerFallback::font_get_name(const RID &p_font_rid) const { return fd->font_name; } -void TextServerFallback::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) { +void TextServerFallback::font_set_antialiasing(RID p_font_rid, TextServer::FontAntialiasing p_antialiasing) { FontFallback *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); MutexLock lock(fd->mutex); - if (fd->antialiased != p_antialiased) { + if (fd->antialiasing != p_antialiasing) { _font_clear_cache(fd); - fd->antialiased = p_antialiased; + fd->antialiasing = p_antialiasing; } } -bool TextServerFallback::font_is_antialiased(const RID &p_font_rid) const { +TextServer::FontAntialiasing TextServerFallback::font_get_antialiasing(RID p_font_rid) const { FontFallback *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, false); + ERR_FAIL_COND_V(!fd, TextServer::FONT_ANTIALIASING_NONE); MutexLock lock(fd->mutex); - return fd->antialiased; + return fd->antialiasing; } void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) { @@ -1589,7 +1676,16 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_ Vector2i size = _get_size(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -1632,7 +1728,16 @@ Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const V Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -1668,7 +1773,16 @@ Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vec Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -1704,7 +1818,16 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Rect2(); // Invalid or non graphicl glyph, do not display errors. } @@ -1735,7 +1858,16 @@ int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, co Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return -1; // Invalid or non graphicl glyph, do not display errors. } @@ -1766,7 +1898,16 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return RID(); // Invalid or non graphicl glyph, do not display errors. } @@ -1805,7 +1946,16 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Size2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2043,16 +2193,18 @@ void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i if (fd->msdf) { _ensure_glyph(fd, size, (int32_t)idx); } else { - if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27)); - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - } else { - _ensure_glyph(fd, size, (int32_t)idx); + for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { + if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + } else { + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + } } } } @@ -2073,16 +2225,18 @@ void TextServerFallback::font_render_glyph(const RID &p_font_rid, const Vector2i if (fd->msdf) { _ensure_glyph(fd, size, (int32_t)idx); } else { - if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27)); - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - } else { - _ensure_glyph(fd, size, (int32_t)idx); + for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { + if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + } else { + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + } } } } @@ -2098,9 +2252,19 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. + bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED if (!fd->msdf && fd->cache[size]->face) { + // LCD layout, bits 24, 25, 26 + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + lcd_aa = true; + index = index | (layout << 24); + } + } + // Subpixel X-shift, bits 27, 28 if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125)); index = index | (xshift << 27); @@ -2122,7 +2286,7 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can if (gl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) { + if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa) { modulate.r = modulate.g = modulate.b = 1.0; } #endif @@ -2160,7 +2324,11 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can } cpos += gl.rect.position; Size2 csize = gl.rect.size; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + if (lcd_aa) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + } else { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } } @@ -2176,9 +2344,19 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. + bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED if (!fd->msdf && fd->cache[size]->face) { + // LCD layout, bits 24, 25, 26 + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + lcd_aa = true; + index = index | (layout << 24); + } + } + // Subpixel X-shift, bits 27, 28 if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125)); index = index | (xshift << 27); @@ -2238,7 +2416,11 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI } cpos += gl.rect.position; Size2 csize = gl.rect.size; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + if (lcd_aa) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + } else { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } } diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 8e81433a2a..940c57a354 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -179,7 +179,7 @@ class TextServerFallback : public TextServerExtension { struct FontFallback { Mutex mutex; - bool antialiased = true; + TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; bool mipmaps = false; bool msdf = false; int msdf_range = 14; @@ -225,7 +225,7 @@ class TextServerFallback : public TextServerExtension { _FORCE_INLINE_ FontGlyph rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; #endif #ifdef MODULE_FREETYPE_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const; #endif _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const; @@ -378,8 +378,8 @@ public: virtual void font_set_name(const RID &p_font_rid, const String &p_name) override; virtual String font_get_name(const RID &p_font_rid) const override; - virtual void font_set_antialiased(const RID &p_font_rid, bool p_antialiased) override; - virtual bool font_is_antialiased(const RID &p_font_rid) const override; + virtual void font_set_antialiasing(RID p_font_rid, TextServer::FontAntialiasing p_antialiasing) override; + virtual TextServer::FontAntialiasing font_get_antialiasing(RID p_font_rid) const override; virtual void font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) override; virtual bool font_get_generate_mipmaps(const RID &p_font_rid) const override; diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index 08ad1ef9f8..16d9bf7b93 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -230,7 +230,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff return OK; } -Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { Vector<uint8_t> src_image; uint64_t src_image_len = f->get_length(); ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT); diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index 9b7cbbac77..d95c5ff30b 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -73,7 +73,7 @@ class ImageLoaderTGA : public ImageFormatLoader { static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size); public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderTGA(); }; diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 864df765ee..6f61251f9b 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -37,7 +37,7 @@ #include "thirdparty/tinyexr/tinyexr.h" -Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { Vector<uint8_t> src_image; uint64_t src_image_len = f->get_length(); ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT); @@ -229,7 +229,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool color.a = *a_channel++; } - if (p_force_linear) { + if (p_flags & FLAG_FORCE_LINEAR) { color = color.srgb_to_linear(); } @@ -260,7 +260,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool color.a = *a_channel++; } - if (p_force_linear) { + if (p_flags & FLAG_FORCE_LINEAR) { color = color.srgb_to_linear(); } diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h index 0d3de93956..8da2a0d4af 100644 --- a/modules/tinyexr/image_loader_tinyexr.h +++ b/modules/tinyexr/image_loader_tinyexr.h @@ -35,7 +35,7 @@ class ImageLoaderTinyEXR : public ImageFormatLoader { public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderTinyEXR(); }; diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp index 778d562278..705ab508ab 100644 --- a/modules/webp/image_loader_webp.cpp +++ b/modules/webp/image_loader_webp.cpp @@ -48,7 +48,7 @@ static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) { return img; } -Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { Vector<uint8_t> src_image; uint64_t src_image_len = f->get_length(); ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT); diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h index 9a5dc6cd7c..d868ae3f7f 100644 --- a/modules/webp/image_loader_webp.h +++ b/modules/webp/image_loader_webp.h @@ -35,7 +35,7 @@ class ImageLoaderWebP : public ImageFormatLoader { public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderWebP(); }; |