diff --git a/binding/Binding/SkiaApi.generated.cs b/binding/Binding/SkiaApi.generated.cs index c785b5cd29a..184bbb120bd 100644 --- a/binding/Binding/SkiaApi.generated.cs +++ b/binding/Binding/SkiaApi.generated.cs @@ -1,95 +1,102 @@ using System; using System.Runtime.InteropServices; +#region Namespaces + + +#endregion + +#region Class declarations + +using gr_backendrendertarget_t = System.IntPtr; +using gr_backendtexture_t = System.IntPtr; +using gr_direct_context_t = System.IntPtr; +using gr_glinterface_t = System.IntPtr; +using gr_recording_context_t = System.IntPtr; +using gr_vk_extensions_t = System.IntPtr; +using gr_vk_memory_allocator_t = System.IntPtr; +using gr_vkinterface_t = System.IntPtr; +using sk_3dview_t = System.IntPtr; +using sk_bitmap_t = System.IntPtr; +using sk_canvas_t = System.IntPtr; +using sk_codec_t = System.IntPtr; +using sk_colorfilter_t = System.IntPtr; +using sk_colorspace_icc_profile_t = System.IntPtr; +using sk_colorspace_t = System.IntPtr; +using sk_colortable_t = System.IntPtr; +using sk_compatpaint_t = System.IntPtr; +using sk_data_t = System.IntPtr; +using sk_document_t = System.IntPtr; +using sk_drawable_t = System.IntPtr; +using sk_font_t = System.IntPtr; +using sk_fontmgr_t = System.IntPtr; +using sk_fontstyle_t = System.IntPtr; +using sk_fontstyleset_t = System.IntPtr; +using sk_image_t = System.IntPtr; +using sk_imagefilter_croprect_t = System.IntPtr; +using sk_imagefilter_t = System.IntPtr; +using sk_manageddrawable_t = System.IntPtr; +using sk_managedtracememorydump_t = System.IntPtr; +using sk_maskfilter_t = System.IntPtr; +using sk_matrix44_t = System.IntPtr; +using sk_nodraw_canvas_t = System.IntPtr; +using sk_nvrefcnt_t = System.IntPtr; +using sk_nway_canvas_t = System.IntPtr; +using sk_opbuilder_t = System.IntPtr; +using sk_overdraw_canvas_t = System.IntPtr; +using sk_paint_t = System.IntPtr; +using sk_path_effect_t = System.IntPtr; +using sk_path_iterator_t = System.IntPtr; +using sk_path_rawiterator_t = System.IntPtr; +using sk_path_t = System.IntPtr; +using sk_pathmeasure_t = System.IntPtr; +using sk_picture_recorder_t = System.IntPtr; +using sk_picture_t = System.IntPtr; +using sk_pixelref_factory_t = System.IntPtr; +using sk_pixmap_t = System.IntPtr; +using sk_refcnt_t = System.IntPtr; +using sk_region_cliperator_t = System.IntPtr; +using sk_region_iterator_t = System.IntPtr; +using sk_region_spanerator_t = System.IntPtr; +using sk_region_t = System.IntPtr; +using sk_rrect_t = System.IntPtr; +using sk_runtimeeffect_t = System.IntPtr; +using sk_runtimeeffect_uniform_t = System.IntPtr; +using sk_shader_t = System.IntPtr; +using sk_stream_asset_t = System.IntPtr; +using sk_stream_filestream_t = System.IntPtr; +using sk_stream_managedstream_t = System.IntPtr; +using sk_stream_memorystream_t = System.IntPtr; +using sk_stream_streamrewindable_t = System.IntPtr; +using sk_stream_t = System.IntPtr; +using sk_string_t = System.IntPtr; +using sk_surface_t = System.IntPtr; +using sk_surfaceprops_t = System.IntPtr; +using sk_svgcanvas_t = System.IntPtr; +using sk_textblob_builder_t = System.IntPtr; +using sk_textblob_t = System.IntPtr; +using sk_tracememorydump_t = System.IntPtr; +using sk_typeface_t = System.IntPtr; +using sk_vertices_t = System.IntPtr; +using sk_wstream_dynamicmemorystream_t = System.IntPtr; +using sk_wstream_filestream_t = System.IntPtr; +using sk_wstream_managedstream_t = System.IntPtr; +using sk_wstream_t = System.IntPtr; +using sk_xmlstreamwriter_t = System.IntPtr; +using sk_xmlwriter_t = System.IntPtr; +using vk_device_t = System.IntPtr; +using vk_instance_t = System.IntPtr; +using vk_physical_device_features_2_t = System.IntPtr; +using vk_physical_device_features_t = System.IntPtr; +using vk_physical_device_t = System.IntPtr; +using vk_queue_t = System.IntPtr; + +#endregion + +#region Functions + namespace SkiaSharp { - #region Class declarations - - using gr_backendrendertarget_t = IntPtr; - using gr_backendtexture_t = IntPtr; - using gr_direct_context_t = IntPtr; - using gr_glinterface_t = IntPtr; - using gr_recording_context_t = IntPtr; - using gr_vk_extensions_t = IntPtr; - using gr_vk_memory_allocator_t = IntPtr; - using gr_vkinterface_t = IntPtr; - using sk_3dview_t = IntPtr; - using sk_bitmap_t = IntPtr; - using sk_canvas_t = IntPtr; - using sk_codec_t = IntPtr; - using sk_colorfilter_t = IntPtr; - using sk_colorspace_icc_profile_t = IntPtr; - using sk_colorspace_t = IntPtr; - using sk_colortable_t = IntPtr; - using sk_compatpaint_t = IntPtr; - using sk_data_t = IntPtr; - using sk_document_t = IntPtr; - using sk_drawable_t = IntPtr; - using sk_font_t = IntPtr; - using sk_fontmgr_t = IntPtr; - using sk_fontstyle_t = IntPtr; - using sk_fontstyleset_t = IntPtr; - using sk_image_t = IntPtr; - using sk_imagefilter_croprect_t = IntPtr; - using sk_imagefilter_t = IntPtr; - using sk_manageddrawable_t = IntPtr; - using sk_managedtracememorydump_t = IntPtr; - using sk_maskfilter_t = IntPtr; - using sk_matrix44_t = IntPtr; - using sk_nodraw_canvas_t = IntPtr; - using sk_nvrefcnt_t = IntPtr; - using sk_nway_canvas_t = IntPtr; - using sk_opbuilder_t = IntPtr; - using sk_overdraw_canvas_t = IntPtr; - using sk_paint_t = IntPtr; - using sk_path_effect_t = IntPtr; - using sk_path_iterator_t = IntPtr; - using sk_path_rawiterator_t = IntPtr; - using sk_path_t = IntPtr; - using sk_pathmeasure_t = IntPtr; - using sk_picture_recorder_t = IntPtr; - using sk_picture_t = IntPtr; - using sk_pixelref_factory_t = IntPtr; - using sk_pixmap_t = IntPtr; - using sk_refcnt_t = IntPtr; - using sk_region_cliperator_t = IntPtr; - using sk_region_iterator_t = IntPtr; - using sk_region_spanerator_t = IntPtr; - using sk_region_t = IntPtr; - using sk_rrect_t = IntPtr; - using sk_runtimeeffect_t = IntPtr; - using sk_runtimeeffect_uniform_t = IntPtr; - using sk_shader_t = IntPtr; - using sk_stream_asset_t = IntPtr; - using sk_stream_filestream_t = IntPtr; - using sk_stream_managedstream_t = IntPtr; - using sk_stream_memorystream_t = IntPtr; - using sk_stream_streamrewindable_t = IntPtr; - using sk_stream_t = IntPtr; - using sk_string_t = IntPtr; - using sk_surface_t = IntPtr; - using sk_surfaceprops_t = IntPtr; - using sk_svgcanvas_t = IntPtr; - using sk_textblob_builder_t = IntPtr; - using sk_textblob_t = IntPtr; - using sk_tracememorydump_t = IntPtr; - using sk_typeface_t = IntPtr; - using sk_vertices_t = IntPtr; - using sk_wstream_dynamicmemorystream_t = IntPtr; - using sk_wstream_filestream_t = IntPtr; - using sk_wstream_managedstream_t = IntPtr; - using sk_wstream_t = IntPtr; - using sk_xmlstreamwriter_t = IntPtr; - using sk_xmlwriter_t = IntPtr; - using vk_device_t = IntPtr; - using vk_instance_t = IntPtr; - using vk_physical_device_features_2_t = IntPtr; - using vk_physical_device_features_t = IntPtr; - using vk_physical_device_t = IntPtr; - using vk_queue_t = IntPtr; - - #endregion - internal unsafe partial class SkiaApi { #region gr_context.h @@ -13335,9 +13342,13 @@ internal static void sk_managedtracememorydump_set_procs (SKManagedTraceMemoryDu #endregion } +} - #region Delegates +#endregion Functions +#region Delegates + +namespace SkiaSharp { // typedef void (*)()* gr_gl_func_ptr [UnmanagedFunctionPointer (CallingConvention.Cdecl)] internal unsafe delegate void GRGlFuncPtr(); @@ -13477,9 +13488,13 @@ internal static void sk_managedtracememorydump_set_procs (SKManagedTraceMemoryDu [UnmanagedFunctionPointer (CallingConvention.Cdecl)] internal unsafe delegate void SKSurfaceRasterReleaseProxyDelegate(void* addr, void* context); - #endregion +} + +#endregion - #region Structs +#region Structs + +namespace SkiaSharp { // gr_context_options_t [StructLayout (LayoutKind.Sequential)] @@ -15582,10 +15597,13 @@ public readonly override int GetHashCode () } } +} + +#endregion - #endregion +#region Enums - #region Enums +namespace SkiaSharp { // gr_backend_t internal enum GRBackendNative { @@ -16295,6 +16313,6 @@ public enum SKWebpEncoderCompression { // LOSSLESS_SK_WEBPENCODER_COMPTRESSION = 1 Lossless = 1, } - - #endregion } + +#endregion diff --git a/binding/HarfBuzzSharp.Shared/HarfBuzzApi.generated.cs b/binding/HarfBuzzSharp.Shared/HarfBuzzApi.generated.cs index b4970fc0a78..b03c62f603c 100644 --- a/binding/HarfBuzzSharp.Shared/HarfBuzzApi.generated.cs +++ b/binding/HarfBuzzSharp.Shared/HarfBuzzApi.generated.cs @@ -1,23 +1,30 @@ using System; using System.Runtime.InteropServices; -namespace HarfBuzzSharp -{ - #region Class declarations +#region Namespaces + + +#endregion + +#region Class declarations - using hb_blob_t = IntPtr; - using hb_buffer_t = IntPtr; - using hb_face_t = IntPtr; - using hb_font_funcs_t = IntPtr; - using hb_font_t = IntPtr; - using hb_language_impl_t = IntPtr; - using hb_map_t = IntPtr; - using hb_set_t = IntPtr; - using hb_shape_plan_t = IntPtr; - using hb_unicode_funcs_t = IntPtr; +using hb_blob_t = System.IntPtr; +using hb_buffer_t = System.IntPtr; +using hb_face_t = System.IntPtr; +using hb_font_funcs_t = System.IntPtr; +using hb_font_t = System.IntPtr; +using hb_language_impl_t = System.IntPtr; +using hb_map_t = System.IntPtr; +using hb_set_t = System.IntPtr; +using hb_shape_plan_t = System.IntPtr; +using hb_unicode_funcs_t = System.IntPtr; - #endregion +#endregion +#region Functions + +namespace HarfBuzzSharp +{ internal unsafe partial class HarfBuzzApi { #region hb-blob.h @@ -4689,9 +4696,13 @@ private partial class Delegates { #endregion } +} - #region Delegates +#endregion Functions +#region Delegates + +namespace HarfBuzzSharp { // typedef hb_bool_t (*)(hb_buffer_t* buffer, hb_font_t* font, const char* message, void* user_data)* hb_buffer_message_func_t [UnmanagedFunctionPointer (CallingConvention.Cdecl)] [return: MarshalAs (UnmanagedType.I1)] @@ -4761,8 +4772,8 @@ private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] [return: MarshalAs (UnmanagedType.I1)] internal unsafe delegate bool FontGetVariationGlyphProxyDelegate(hb_font_t font, void* font_data, UInt32 unicode, UInt32 variation_selector, UInt32* glyph, void* user_data); -// TODO: typedef const const hb_language_impl_t* hb_language_t +// TODO: typedef const hb_language_impl_t* hb_language_t // typedef hb_blob_t* (*)(hb_face_t* face, hb_tag_t tag, void* user_data)* hb_reference_table_func_t [UnmanagedFunctionPointer (CallingConvention.Cdecl)] internal unsafe delegate hb_blob_t ReferenceTableProxyDelegate(hb_face_t face, UInt32 tag, void* user_data); @@ -4801,9 +4812,13 @@ private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] internal unsafe delegate UInt32 UnicodeScriptProxyDelegate(hb_unicode_funcs_t ufuncs, UInt32 unicode, void* user_data); - #endregion +} + +#endregion + +#region Structs - #region Structs +namespace HarfBuzzSharp { // hb_feature_t [StructLayout (LayoutKind.Sequential)] @@ -5468,10 +5483,13 @@ public readonly override int GetHashCode () } } +} + +#endregion - #endregion +#region Enums - #region Enums +namespace HarfBuzzSharp { // hb_buffer_cluster_level_t public enum ClusterLevel { @@ -6029,6 +6047,6 @@ public enum UnicodeGeneralCategory { // HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR = 29 SpaceSeparator = 29, } - - #endregion } + +#endregion diff --git a/binding/libHarfBuzzSharp.json b/binding/libHarfBuzzSharp.json index f618bc4af2e..2f7b479beef 100644 --- a/binding/libHarfBuzzSharp.json +++ b/binding/libHarfBuzzSharp.json @@ -2,6 +2,14 @@ { "dllName": "HARFBUZZ", "namespace": "HarfBuzzSharp", + "namespaces": { + "hb_ot_": { + "prefix": "OpenType" + }, + "hb_": { + "prefix": "" + } + }, "className": "HarfBuzzApi", "includeDirs": [ "src" diff --git a/binding/libSkiaSharp.json b/binding/libSkiaSharp.json index 97d5bf0d805..d4f3d0f375d 100644 --- a/binding/libSkiaSharp.json +++ b/binding/libSkiaSharp.json @@ -2,6 +2,17 @@ { "dllName": "SKIA", "namespace": "SkiaSharp", + "namespaces": { + "sk_managed": { + "prefix": "SKManaged" + }, + "sk_": { + "prefix": "SK" + }, + "gr_": { + "prefix": "GR" + } + }, "className": "SkiaApi", "includeDirs": [ "." diff --git a/utils/SkiaSharpGenerator/BaseTool.cs b/utils/SkiaSharpGenerator/BaseTool.cs index cf69fe83e2d..474dbfc29de 100644 --- a/utils/SkiaSharpGenerator/BaseTool.cs +++ b/utils/SkiaSharpGenerator/BaseTool.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading.Tasks; using CppAst; @@ -9,6 +11,11 @@ namespace SkiaSharpGenerator { public abstract class BaseTool { + private static readonly string[] keywords = + { + "out", "in", "var", "ref" + }; + protected readonly Dictionary typeMappings = new Dictionary(); protected readonly Dictionary functionMappings = new Dictionary(); protected readonly Dictionary skiaTypes = new Dictionary(); @@ -175,11 +182,13 @@ protected async Task LoadConfigAsync(string configPath) using var configJson = File.OpenRead(configPath); - return await JsonSerializer.DeserializeAsync(configJson, new JsonSerializerOptions + var config = await JsonSerializer.DeserializeAsync(configJson, new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, }); + + return config ?? throw new InvalidOperationException("Unable to parse json config file."); } protected string GetType(CppType type) @@ -196,21 +205,21 @@ protected string GetType(CppType type) if (!isStruct) return noPointers + pointers.Substring(1); if (typeMappings.TryGetValue(noPointers, out var map)) - return (map.CsType ?? Utils.CleanName(noPointers)) + pointers; + return (map.CsType ?? CleanName(noPointers)) + pointers; } else { if (typeMappings.TryGetValue(typeName, out var map)) - return map.CsType ?? Utils.CleanName(typeName); + return map.CsType ?? CleanName(typeName); if (typeMappings.TryGetValue(noPointers, out map)) - return (map.CsType ?? Utils.CleanName(noPointers)) + pointers; + return (map.CsType ?? CleanName(noPointers)) + pointers; if (functionMappings.TryGetValue(typeName, out var funcMap)) - return funcMap.CsType ?? Utils.CleanName(typeName); + return funcMap.CsType ?? CleanName(typeName); if (functionMappings.TryGetValue(noPointers, out funcMap)) - return (funcMap.CsType ?? Utils.CleanName(noPointers)) + pointers; + return (funcMap.CsType ?? CleanName(noPointers)) + pointers; } - return Utils.CleanName(typeName); + return CleanName(typeName); } protected static string GetCppType(CppType type) @@ -225,10 +234,114 @@ protected static string GetCppType(CppType type) while ((start = typeName.IndexOf("[")) != -1) { var end = typeName.IndexOf("]"); - typeName = typeName.Substring(0, start) + "*" + typeName.Substring(end + 1); + typeName = typeName[..start] + "*" + typeName[(end + 1)..]; } return typeName; } + + protected string CleanName(string type) + { + var prefix = ""; + var suffix = ""; + + type = type.TrimStart('_'); + + if (type.EndsWith("_t")) + type = type[0..^2]; + + if (type.EndsWith("_proc") || type.EndsWith("_func")) + { + type = type[0..^5]; + suffix = "ProxyDelegate"; + } + + foreach (var ns in config.Namespaces) + { + var nsPrefix = ns.Key; + if (type.StartsWith(nsPrefix)) + { + var mapping = ns.Value; + if (mapping.Prefix != null) + { + prefix = mapping.Prefix; + type = type[nsPrefix.Length..]; + } + } + } + + string[] parts; + + if (type.Any(c => char.IsLower(c)) && type.Any(c => char.IsUpper(c))) + { + // there is a mix of cases, so split on the uppercase, then underscores + parts = Regex.Split(type, @"(? p.Split('_')).ToArray(); + + // remove the initial "f" prefix + if (parts[0] == "f" && char.IsUpper(parts[1][0])) + parts = parts[1..]; + } + else + { + // this is either an fully uppercase enum or lowercase type + parts = type.ToLowerInvariant().Split('_'); + } + + for (var i = 0; i < parts.Length; i++) + { + var part = parts[i]; + parts[i] = part[0].ToString().ToUpperInvariant() + part[1..]; + } + + return prefix + string.Concat(parts) + suffix; + } + + protected string CleanEnumFieldName(string fieldName, string cppEnumName) + { + if (cppEnumName.EndsWith("_t")) + cppEnumName = cppEnumName[..^2]; + + fieldName = RemovePrefixSuffix(fieldName, cppEnumName); + + // special case for "flags" name and "flag" member + if (cppEnumName.EndsWith("_flags")) + fieldName = RemovePrefixSuffix(fieldName, cppEnumName[..^1]); + + // special case for bad skia enum fields + var lower = fieldName.ToLowerInvariant(); + var indexOfSplitter = lower.IndexOf("_sk_"); + if (indexOfSplitter == -1) + indexOfSplitter = lower.IndexOf("_gr_"); + if (indexOfSplitter != -1) + fieldName = fieldName[0..indexOfSplitter]; + + return CleanName(fieldName); + + static string RemovePrefixSuffix(string member, string type) + { + if (member.ToLowerInvariant().EndsWith("_" + type.ToLowerInvariant())) + member = member[..^(type.Length + 1)]; + + if (member.ToLowerInvariant().StartsWith(type.ToLowerInvariant() + "_")) + member = member[(type.Length + 1)..]; + + return member; + } + } + + protected string GetNamespace(string name) + { + foreach (var ns in config.Namespaces) + { + if (name.StartsWith(ns.Key) && !string.IsNullOrWhiteSpace(ns.Value.CsName)) + return $"{config.Namespace}.{ns.Value.CsName}"; + } + + return config.Namespace; + } + + protected static string SafeName(string name) => + keywords.Contains(name) ? "@" + name : name; } } diff --git a/utils/SkiaSharpGenerator/ConfigJson/Config.cs b/utils/SkiaSharpGenerator/ConfigJson/Config.cs index ac34b8e5919..de45436b297 100644 --- a/utils/SkiaSharpGenerator/ConfigJson/Config.cs +++ b/utils/SkiaSharpGenerator/ConfigJson/Config.cs @@ -14,6 +14,9 @@ public class Config [JsonPropertyName("source")] public Dictionary Source { get; set; } = new Dictionary(); + [JsonPropertyName("namespaces")] + public Dictionary Namespaces { get; set; } = new Dictionary(); + [JsonPropertyName("exclude")] public Exclude Exclude { get; set; } = new Exclude(); @@ -21,12 +24,12 @@ public class Config public List IncludeDirs { get; set; } = new List(); [JsonPropertyName("dllName")] - public string? DllName { get; set; } = "DllName"; + public string DllName { get; set; } = "DllName"; [JsonPropertyName("namespace")] - public string? Namespace { get; set; } = "Namespace"; + public string Namespace { get; set; } = "Namespace"; [JsonPropertyName("className")] - public string? ClassName { get; set; } = "ClassName"; + public string ClassName { get; set; } = "ClassName"; } } diff --git a/utils/SkiaSharpGenerator/ConfigJson/NamespaceMapping.cs b/utils/SkiaSharpGenerator/ConfigJson/NamespaceMapping.cs new file mode 100644 index 00000000000..d6589af3c33 --- /dev/null +++ b/utils/SkiaSharpGenerator/ConfigJson/NamespaceMapping.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace SkiaSharpGenerator +{ + public class NamespaceMapping + { + [JsonPropertyName("cs")] + public string? CsName { get; set; } + + [JsonPropertyName("prefix")] + public string? Prefix { get; set; } + } +} diff --git a/utils/SkiaSharpGenerator/Generate/Generator.cs b/utils/SkiaSharpGenerator/Generate/Generator.cs index 5de6cc196a0..018bc1e03e2 100644 --- a/utils/SkiaSharpGenerator/Generate/Generator.cs +++ b/utils/SkiaSharpGenerator/Generate/Generator.cs @@ -41,301 +41,377 @@ private void WriteApi(TextWriter writer) writer.WriteLine("using System;"); writer.WriteLine("using System.Runtime.InteropServices;"); writer.WriteLine(); - writer.WriteLine($"namespace {config.Namespace}"); - writer.WriteLine($"{{"); + WriteNamespaces(writer); + writer.WriteLine(); WriteClasses(writer); writer.WriteLine(); + writer.WriteLine($"#region Functions"); + writer.WriteLine(); + writer.WriteLine($"namespace {config.Namespace}"); + writer.WriteLine($"{{"); writer.WriteLine($"\tinternal unsafe partial class {config.ClassName}"); writer.WriteLine($"\t{{"); WriteFunctions(writer); writer.WriteLine($"\t}}"); + writer.WriteLine($"}}"); + writer.WriteLine(); + writer.WriteLine($"#endregion Functions"); writer.WriteLine(); WriteDelegates(writer); writer.WriteLine(); WriteStructs(writer); writer.WriteLine(); WriteEnums(writer); - writer.WriteLine($"}}"); } private void WriteDelegates(TextWriter writer) { Log?.LogVerbose(" Writing delegates..."); - writer.WriteLine($"\t#region Delegates"); + writer.WriteLine($"#region Delegates"); var delegates = compilation.Typedefs .Where(t => t.ElementType.TypeKind == CppTypeKind.Pointer) - .OrderBy(t => t.GetDisplayName()); - foreach (var del in delegates) + .OrderBy(t => t.GetDisplayName()) + .GroupBy(t => GetNamespace(t.GetDisplayName())); + + foreach (var group in delegates) { - if (!(((CppPointerType)del.ElementType).ElementType is CppFunctionType function)) - { - Log?.LogWarning($"Unknown delegate type {del}"); + writer.WriteLine(); + writer.WriteLine($"namespace {group.Key} {{"); - writer.WriteLine($"// TODO: {del}"); - continue; + foreach (var del in group) + { + WriteDelegate(writer, del); } - Log?.LogVerbose($" {del.GetDisplayName()}"); + writer.WriteLine($"}}"); + } + + writer.WriteLine(); + writer.WriteLine($"#endregion"); + } + + private void WriteDelegate(TextWriter writer, CppTypedef del) + { + if (!(((CppPointerType)del.ElementType).ElementType is CppFunctionType function)) + { + Log?.LogWarning($"Unknown delegate type {del}"); - var name = del.GetDisplayName(); - functionMappings.TryGetValue(name, out var map); - name = map?.CsType ?? Utils.CleanName(name); + writer.WriteLine($"// TODO: {del}"); + return; + } - writer.WriteLine(); - writer.WriteLine($"\t// {del}"); - writer.WriteLine($"\t[UnmanagedFunctionPointer (CallingConvention.Cdecl)]"); + var name = del.GetDisplayName(); - var paramsList = new List(); - for (var i = 0; i < function.Parameters.Count; i++) - { - var p = function.Parameters[i]; - var n = string.IsNullOrEmpty(p.Name) ? $"param{i}" : p.Name; - var t = GetType(p.Type); - var cppT = GetCppType(p.Type); - if (t == "Boolean" || cppT == "bool") - t = $"[MarshalAs (UnmanagedType.I1)] bool"; - if (map != null && map.Parameters.TryGetValue(i.ToString(), out var newT)) - t = newT; - paramsList.Add($"{t} {n}"); - } + Log?.LogVerbose($" {name}"); - var returnType = GetType(function.ReturnType); - if (map != null && map.Parameters.TryGetValue("-1", out var newR)) - { - returnType = newR; - } - else if (returnType == "Boolean" || GetCppType(function.ReturnType) == "bool") - { - returnType = "bool"; - writer.WriteLine($"\t[return: MarshalAs (UnmanagedType.I1)]"); - } + functionMappings.TryGetValue(name, out var map); + name = map?.CsType ?? CleanName(name); + + writer.WriteLine($"\t// {del}"); + writer.WriteLine($"\t[UnmanagedFunctionPointer (CallingConvention.Cdecl)]"); - writer.WriteLine($"\tinternal unsafe delegate {returnType} {name}({string.Join(", ", paramsList)});"); + var paramsList = new List(); + for (var i = 0; i < function.Parameters.Count; i++) + { + var p = function.Parameters[i]; + var n = string.IsNullOrEmpty(p.Name) ? $"param{i}" : p.Name; + var t = GetType(p.Type); + var cppT = GetCppType(p.Type); + if (t == "Boolean" || cppT == "bool") + t = $"[MarshalAs (UnmanagedType.I1)] bool"; + if (map != null && map.Parameters.TryGetValue(i.ToString(), out var newT)) + t = newT; + paramsList.Add($"{t} {n}"); + } + + var returnType = GetType(function.ReturnType); + if (map != null && map.Parameters.TryGetValue("-1", out var newR)) + { + returnType = newR; + } + else if (returnType == "Boolean" || GetCppType(function.ReturnType) == "bool") + { + returnType = "bool"; + writer.WriteLine($"\t[return: MarshalAs (UnmanagedType.I1)]"); } + writer.WriteLine($"\tinternal unsafe delegate {returnType} {name}({string.Join(", ", paramsList)});"); writer.WriteLine(); - writer.WriteLine($"\t#endregion"); } private void WriteStructs(TextWriter writer) { Log?.LogVerbose(" Writing structs..."); - writer.WriteLine($"\t#region Structs"); + writer.WriteLine($"#region Structs"); var classes = compilation.Classes .Where(c => c.SizeOf != 0) .OrderBy(c => c.GetDisplayName()) - .ToList(); - foreach (var klass in classes) + .GroupBy(c => GetNamespace(c.GetDisplayName())); + + foreach (var group in classes) { - var cppClassName = klass.GetDisplayName(); + writer.WriteLine(); + writer.WriteLine($"namespace {group.Key} {{"); - if (excludedTypes.Contains(cppClassName) == true) + foreach (var klass in group) { - Log?.LogVerbose($" Skipping struct '{cppClassName}' because it was in the exclude list."); - continue; + WriteStruct(writer, klass); } - Log?.LogVerbose($" {cppClassName}"); + writer.WriteLine($"}}"); + } - typeMappings.TryGetValue(cppClassName, out var map); - var name = map?.CsType ?? Utils.CleanName(cppClassName); + writer.WriteLine(); + writer.WriteLine($"#endregion"); + } - writer.WriteLine(); - writer.WriteLine($"\t// {cppClassName}"); - writer.WriteLine($"\t[StructLayout (LayoutKind.Sequential)]"); - var visibility = map?.IsInternal == true ? "internal" : "public"; - var isReadonly = map?.IsReadOnly == true ? " readonly" : ""; - var equatable = map?.GenerateEquality == true ? $" : IEquatable<{name}>" : ""; - writer.WriteLine($"\t{visibility}{isReadonly} unsafe partial struct {name}{equatable} {{"); - var allFields = new List(); - foreach (var field in klass.Fields) - { - var type = GetType(field.Type); - var cppT = GetCppType(field.Type); + private void WriteStruct(TextWriter writer, CppClass klass) + { + var cppClassName = klass.GetDisplayName(); + + if (excludedTypes.Contains(cppClassName) == true) + { + Log?.LogVerbose($" Skipping struct '{cppClassName}' because it was in the exclude list."); + return; + } + + Log?.LogVerbose($" {cppClassName}"); - writer.WriteLine($"\t\t// {field}"); + typeMappings.TryGetValue(cppClassName, out var map); + var name = map?.CsType ?? CleanName(cppClassName); - var fieldName = field.Name; - var isPrivate = fieldName.StartsWith("_private_", StringComparison.OrdinalIgnoreCase); - if (isPrivate) - fieldName = fieldName[9..]; - isPrivate |= fieldName.StartsWith("reserved", StringComparison.OrdinalIgnoreCase); + writer.WriteLine(); + writer.WriteLine($"\t// {cppClassName}"); + writer.WriteLine($"\t[StructLayout (LayoutKind.Sequential)]"); + var visibility = map?.IsInternal == true ? "internal" : "public"; + var isReadonly = map?.IsReadOnly == true ? " readonly" : ""; + var equatable = map?.GenerateEquality == true ? $" : IEquatable<{name}>" : ""; + writer.WriteLine($"\t{visibility}{isReadonly} unsafe partial struct {name}{equatable} {{"); + + var allFields = new List(); + foreach (var field in klass.Fields) + { + var type = GetType(field.Type); + var cppT = GetCppType(field.Type); + + writer.WriteLine($"\t\t// {field}"); - allFields.Add(fieldName); + var fieldName = field.Name; + var isPrivate = fieldName.StartsWith("_private_", StringComparison.OrdinalIgnoreCase); + if (isPrivate) + fieldName = fieldName[9..]; + isPrivate |= fieldName.StartsWith("reserved", StringComparison.OrdinalIgnoreCase); - var vis = map?.IsInternal == true ? "public" : "private"; - var ro = map?.IsReadOnly == true ? " readonly" : ""; - writer.WriteLine($"\t\t{vis}{ro} {type} {fieldName};"); + allFields.Add(fieldName); - if (!isPrivate && (map == null || (map.GenerateProperties && !map.IsInternal))) + var vis = map?.IsInternal == true ? "public" : "private"; + var ro = map?.IsReadOnly == true ? " readonly" : ""; + writer.WriteLine($"\t\t{vis}{ro} {type} {fieldName};"); + + if (!isPrivate && (map == null || (map.GenerateProperties && !map.IsInternal))) + { + var propertyName = fieldName; + if (map != null && map.Members.TryGetValue(propertyName, out var fieldMap)) { - var propertyName = fieldName; - if (map != null && map.Members.TryGetValue(propertyName, out var fieldMap)) + if (string.IsNullOrEmpty(fieldMap)) + isPrivate = true; + propertyName = fieldMap; + } + else + { + propertyName = CleanName(propertyName); + } + + if (!isPrivate) + { + if (fieldName == "value") + fieldName = "this." + fieldName; + + if (cppT == "bool") { - if (string.IsNullOrEmpty(fieldMap)) - isPrivate = true; - propertyName = fieldMap; + if (map?.IsReadOnly == true) + { + writer.WriteLine($"\t\tpublic readonly bool {propertyName} => {fieldName} > 0;"); + } + else + { + writer.WriteLine($"\t\tpublic bool {propertyName} {{"); + writer.WriteLine($"\t\t\treadonly get => {fieldName} > 0;"); + writer.WriteLine($"\t\t\tset => {fieldName} = value ? (byte)1 : (byte)0;"); + writer.WriteLine($"\t\t}}"); + } } else { - propertyName = Utils.CleanName(propertyName); - } - - if (!isPrivate) - { - if (fieldName == "value") - fieldName = "this." + fieldName; - - if (cppT == "bool") + if (map?.IsReadOnly == true) { - if (map?.IsReadOnly == true) - { - writer.WriteLine($"\t\tpublic readonly bool {propertyName} => {fieldName} > 0;"); - } - else - { - writer.WriteLine($"\t\tpublic bool {propertyName} {{"); - writer.WriteLine($"\t\t\treadonly get => {fieldName} > 0;"); - writer.WriteLine($"\t\t\tset => {fieldName} = value ? (byte)1 : (byte)0;"); - writer.WriteLine($"\t\t}}"); - } + writer.WriteLine($"\t\tpublic readonly {type} {propertyName} => {fieldName};"); } else { - if (map?.IsReadOnly == true) - { - writer.WriteLine($"\t\tpublic readonly {type} {propertyName} => {fieldName};"); - } - else - { - writer.WriteLine($"\t\tpublic {type} {propertyName} {{"); - writer.WriteLine($"\t\t\treadonly get => {fieldName};"); - writer.WriteLine($"\t\t\tset => {fieldName} = value;"); - writer.WriteLine($"\t\t}}"); - } + writer.WriteLine($"\t\tpublic {type} {propertyName} {{"); + writer.WriteLine($"\t\t\treadonly get => {fieldName};"); + writer.WriteLine($"\t\t\tset => {fieldName} = value;"); + writer.WriteLine($"\t\t}}"); } } } - - writer.WriteLine(); } - if (map?.GenerateEquality == true) + writer.WriteLine(); + } + + if (map?.GenerateEquality == true) + { + // IEquatable + var equalityFields = new List(); + foreach (var f in allFields) { - // IEquatable - var equalityFields = new List(); - foreach (var f in allFields) - { - equalityFields.Add($"{f} == obj.{f}"); - } - writer.WriteLine($"\t\tpublic readonly bool Equals ({name} obj) =>"); - writer.WriteLine($"\t\t\t{string.Join(" && ", equalityFields)};"); - writer.WriteLine(); + equalityFields.Add($"{f} == obj.{f}"); + } + writer.WriteLine($"\t\tpublic readonly bool Equals ({name} obj) =>"); + writer.WriteLine($"\t\t\t{string.Join(" && ", equalityFields)};"); + writer.WriteLine(); - // Equals - writer.WriteLine($"\t\tpublic readonly override bool Equals (object obj) =>"); - writer.WriteLine($"\t\t\tobj is {name} f && Equals (f);"); - writer.WriteLine(); + // Equals + writer.WriteLine($"\t\tpublic readonly override bool Equals (object obj) =>"); + writer.WriteLine($"\t\t\tobj is {name} f && Equals (f);"); + writer.WriteLine(); - // equality operators - writer.WriteLine($"\t\tpublic static bool operator == ({name} left, {name} right) =>"); - writer.WriteLine($"\t\t\tleft.Equals (right);"); - writer.WriteLine(); - writer.WriteLine($"\t\tpublic static bool operator != ({name} left, {name} right) =>"); - writer.WriteLine($"\t\t\t!left.Equals (right);"); - writer.WriteLine(); + // equality operators + writer.WriteLine($"\t\tpublic static bool operator == ({name} left, {name} right) =>"); + writer.WriteLine($"\t\t\tleft.Equals (right);"); + writer.WriteLine(); + writer.WriteLine($"\t\tpublic static bool operator != ({name} left, {name} right) =>"); + writer.WriteLine($"\t\t\t!left.Equals (right);"); + writer.WriteLine(); - // GetHashCode - writer.WriteLine($"\t\tpublic readonly override int GetHashCode ()"); - writer.WriteLine($"\t\t{{"); - writer.WriteLine($"\t\t\tvar hash = new HashCode ();"); - foreach (var f in allFields) - { - writer.WriteLine($"\t\t\thash.Add ({f});"); - } - writer.WriteLine($"\t\t\treturn hash.ToHashCode ();"); - writer.WriteLine($"\t\t}}"); - writer.WriteLine(); + // GetHashCode + writer.WriteLine($"\t\tpublic readonly override int GetHashCode ()"); + writer.WriteLine($"\t\t{{"); + writer.WriteLine($"\t\t\tvar hash = new HashCode ();"); + foreach (var f in allFields) + { + writer.WriteLine($"\t\t\thash.Add ({f});"); } - writer.WriteLine($"\t}}"); + writer.WriteLine($"\t\t\treturn hash.ToHashCode ();"); + writer.WriteLine($"\t\t}}"); + writer.WriteLine(); } - writer.WriteLine(); - writer.WriteLine($"\t#endregion"); + writer.WriteLine($"\t}}"); } private void WriteEnums(TextWriter writer) { Log?.LogVerbose(" Writing enums..."); - writer.WriteLine($"\t#region Enums"); + writer.WriteLine($"#region Enums"); var enums = compilation.Enums - .OrderBy(c => c.GetDisplayName()) - .ToList(); - foreach (var enm in enums) + .OrderBy(e => e.GetDisplayName()) + .GroupBy(e => GetNamespace(e.GetDisplayName())); + + foreach (var group in enums) { - var cppEnumName = enm.GetDisplayName(); + writer.WriteLine(); + writer.WriteLine($"namespace {group.Key} {{"); - if (string.IsNullOrEmpty(cppEnumName)) + foreach (var enm in group) { - Log?.LogWarning($"Unknown enum type {enm}"); - continue; + WriteEnum(writer, enm); } - typeMappings.TryGetValue(cppEnumName, out var map); - if (map?.Generate == false) + writer.WriteLine($"}}"); + } + + writer.WriteLine(); + writer.WriteLine($"#endregion"); + } + + private void WriteEnum(TextWriter writer, CppEnum enm) + { + var cppEnumName = enm.GetDisplayName(); + + if (string.IsNullOrEmpty(cppEnumName)) + { + Log?.LogWarning($"Unknown enum type {enm}"); + return; + } + + typeMappings.TryGetValue(cppEnumName, out var map); + if (map?.Generate == false) + return; + + Log?.LogVerbose($" {cppEnumName}"); + + var name = map?.CsType ?? CleanName(cppEnumName); + + var visibility = "public"; + if (map?.IsInternal == true) + visibility = "internal"; + + writer.WriteLine(); + writer.WriteLine($"\t// {cppEnumName}"); + if (map?.IsObsolete == true) + writer.WriteLine($"\t[Obsolete]"); + if (map?.IsFlags == true) + writer.WriteLine($"\t[Flags]"); + writer.WriteLine($"\t{visibility} enum {name} {{"); + foreach (var field in enm.Items) + { + var fieldName = field.Name; + if (map != null && map.Members.TryGetValue(fieldName, out var fieldMap)) + fieldName = fieldMap; + else + fieldName = CleanEnumFieldName(fieldName, cppEnumName); + + if (string.IsNullOrEmpty(fieldName)) continue; - Log?.LogVerbose($" {cppEnumName}"); + var commentVal = field.ValueExpression?.ToString(); + if (string.IsNullOrEmpty(commentVal)) + commentVal = field.Value.ToString(); - var name = map?.CsType ?? Utils.CleanName(cppEnumName); + writer.WriteLine($"\t\t// {field.Name} = {commentVal}"); + writer.WriteLine($"\t\t{fieldName} = {field.Value},"); + } + writer.WriteLine($"\t}}"); + } - var visibility = "public"; - if (map?.IsInternal == true) - visibility = "internal"; + private void WriteNamespaces(TextWriter writer) + { + Log?.LogVerbose(" Writing namespaces..."); - writer.WriteLine(); - writer.WriteLine($"\t// {cppEnumName}"); - if (map?.IsObsolete == true) - writer.WriteLine($"\t[Obsolete]"); - if (map?.IsFlags == true) - writer.WriteLine($"\t[Flags]"); - writer.WriteLine($"\t{visibility} enum {name} {{"); - foreach (var field in enm.Items) - { - var fieldName = field.Name; - if (map != null && map.Members.TryGetValue(fieldName, out var fieldMap)) - fieldName = fieldMap; - else - fieldName = Utils.CleanEnumFieldName(fieldName, cppEnumName); + writer.WriteLine($"#region Namespaces"); + writer.WriteLine(); - if (string.IsNullOrEmpty(fieldName)) - continue; + var namspaces = config.Namespaces.Values; + foreach (var ns in namspaces) + { + if (string.IsNullOrEmpty(ns.CsName)) + continue; - var commentVal = field.ValueExpression?.ToString(); - if (string.IsNullOrEmpty(commentVal)) - commentVal = field.Value.ToString(); + var full = $"{config.Namespace}.{ns.CsName}"; - writer.WriteLine($"\t\t// {field.Name} = {commentVal}"); - writer.WriteLine($"\t\t{fieldName} = {field.Value},"); - } - writer.WriteLine($"\t}}"); + Log?.LogVerbose($" {full}"); + + writer.WriteLine($"using {full};"); } writer.WriteLine(); - writer.WriteLine($"\t#endregion"); + writer.WriteLine($"#endregion"); } private void WriteClasses(TextWriter writer) { Log?.LogVerbose(" Writing usings..."); - writer.WriteLine($"\t#region Class declarations"); + writer.WriteLine($"#region Class declarations"); writer.WriteLine(); var classes = compilation.Classes @@ -346,14 +422,14 @@ private void WriteClasses(TextWriter writer) var type = klass.GetDisplayName(); skiaTypes.Add(type, klass.SizeOf != 0); - if (klass.SizeOf == 0) - writer.WriteLine($"\tusing {klass.GetDisplayName()} = IntPtr;"); - Log?.LogVerbose($" {klass.GetDisplayName()}"); + + if (klass.SizeOf == 0) + writer.WriteLine($"using {klass.GetDisplayName()} = System.IntPtr;"); } writer.WriteLine(); - writer.WriteLine($"\t#endregion"); + writer.WriteLine($"#endregion"); } private void WriteFunctions(TextWriter writer) @@ -389,7 +465,7 @@ private void WriteFunctions(TextWriter writer) { var p = function.Parameters[i]; var n = string.IsNullOrEmpty(p.Name) ? $"param{i}" : p.Name; - n = Utils.SafeName(n); + n = SafeName(n); var t = GetType(p.Type); var cppT = GetCppType(p.Type); if (excludedTypes.Contains(cppT) == true) diff --git a/utils/SkiaSharpGenerator/Generate/Utils.cs b/utils/SkiaSharpGenerator/Generate/Utils.cs deleted file mode 100644 index bad707ca8f4..00000000000 --- a/utils/SkiaSharpGenerator/Generate/Utils.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; - -namespace SkiaSharpGenerator -{ - public class Utils - { - private static readonly string[] keywords = - { - "out", "in", "var", "ref" - }; - - public static string CleanName(string type) - { - var prefix = ""; - var suffix = ""; - - type = type.TrimStart('_'); - - if (type.EndsWith("_t")) - type = type[0..^2]; - - if (type.StartsWith("hb_ot_")) - type = "open_type_" + type[6..]; - else if (type.StartsWith("hb_")) - type = type[3..]; - else if (type.StartsWith("sk_") || type.StartsWith("gr_")) - { - prefix = type[0..2].ToUpperInvariant(); - type = type[3..]; - } - if (type.EndsWith("_proc") || type.EndsWith("_func")) - { - type = type[0..^5]; - suffix = "ProxyDelegate"; - } - - // special case for managed - if (type.StartsWith("managed")) - type = type.Insert(7, "_"); - - string[] parts; - - if (type.Any(c => char.IsLower(c)) && type.Any(c => char.IsUpper(c))) - { - // there is a mix of cases, so split on the uppercase, then underscores - parts = Regex.Split(type, @"(? p.Split('_')).ToArray(); - - // remove the initial "f" prefix - if (parts[0] == "f" && char.IsUpper(parts[1][0])) - parts = parts[1..]; - } - else - { - // this is either an fully uppercase enum or lowercase type - parts = type.ToLowerInvariant().Split('_'); - } - - for (var i = 0; i < parts.Length; i++) - { - var part = parts[i]; - parts[i] = part[0].ToString().ToUpperInvariant() + part[1..]; - } - - return prefix + string.Concat(parts) + suffix; - } - - public static string CleanEnumFieldName(string fieldName, string cppEnumName) - { - if (cppEnumName.EndsWith("_t")) - cppEnumName = cppEnumName[..^2]; - - fieldName = RemovePrefixSuffix(fieldName, cppEnumName); - - // special case for "flags" name and "flag" member - if (cppEnumName.EndsWith("_flags")) - fieldName = RemovePrefixSuffix(fieldName, cppEnumName[..^1]); - - // special case for bad skia enum fields - var lower = fieldName.ToLowerInvariant(); - var indexOfSplitter = lower.IndexOf("_sk_"); - if (indexOfSplitter == -1) - indexOfSplitter = lower.IndexOf("_gr_"); - if (indexOfSplitter != -1) - fieldName = fieldName[0..indexOfSplitter]; - - return CleanName(fieldName); - - static string RemovePrefixSuffix(string member, string type) - { - if (member.ToLowerInvariant().EndsWith("_" + type.ToLowerInvariant())) - member = member[..^(type.Length + 1)]; - - if (member.ToLowerInvariant().StartsWith(type.ToLowerInvariant() + "_")) - member = member[(type.Length + 1)..]; - - return member; - } - } - - public static string SafeName(string name) - { - if (keywords.Contains(name)) - return "@" + name; - return name; - } - } -} diff --git a/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj b/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj index 321b6d206b0..8b237275732 100644 --- a/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj +++ b/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj @@ -7,7 +7,7 @@ - +