From a624505759f75cc6b46ac9e9058a90dd0fc58167 Mon Sep 17 00:00:00 2001 From: Amer Koleci Date: Tue, 1 Jun 2021 09:07:50 +0200 Subject: [PATCH] [ENH] DirectWrite: GlyphRun improvements and example for AdvancedText rendering. --- CHANGELOG.md | 1 + src/Vortice.Direct2D1/DirectWrite/GlyphRun.cs | 131 +++++++++--------- .../DirectWrite/GlyphRunDescription.cs | 57 ++++++++ .../IDWriteColorGlyphRunEnumerator.cs | 23 +++ .../IDWriteColorGlyphRunEnumerator1.cs | 23 +++ .../DirectWrite/IDWriteFactory2.cs | 54 +++++++- .../DirectWrite/IDWriteFactory4.cs | 37 +++++ .../DirectWrite/TextRendererBase.cs | 2 +- src/Vortice.Direct2D1/Mappings.xml | 18 ++- src/Vortice.DirectX/UnsafeUtilities.cs | 9 ++ .../AdvancedTextRenderingApp/Program.cs | 9 +- 11 files changed, 280 insertions(+), 84 deletions(-) create mode 100644 src/Vortice.Direct2D1/DirectWrite/GlyphRunDescription.cs create mode 100644 src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator.cs create mode 100644 src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator1.cs create mode 100644 src/Vortice.Direct2D1/DirectWrite/IDWriteFactory4.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b865b8..5d82b6fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Release: 1.9.77 (May 2021) - [ENH] Dxc: Update to April 2021. - [ENH] General: Move to standard types for Point, PointF, Size, SizeF, Rectangle and RectangleF. - [ENH] Direct2D1: Improve mappings. +- [ENH] DirectWrite: GlyphRun improvements and example for AdvancedText rendering. ----------------------------------------------- Release: 1.9.45 (April 2021) diff --git a/src/Vortice.Direct2D1/DirectWrite/GlyphRun.cs b/src/Vortice.Direct2D1/DirectWrite/GlyphRun.cs index 9e2909ff..9bfa06f4 100644 --- a/src/Vortice.Direct2D1/DirectWrite/GlyphRun.cs +++ b/src/Vortice.Direct2D1/DirectWrite/GlyphRun.cs @@ -2,6 +2,7 @@ // Distributed under the MIT license. See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SharpGen.Runtime; @@ -11,14 +12,17 @@ namespace Vortice.DirectWrite public partial class GlyphRun : IDisposable { public IDWriteFontFace? FontFace { set; get; } - public ushort[]? GlyphIndices { get; set; } - public float[]? GlyphAdvances { get; set; } - public GlyphOffset[]? GlyphOffsets { get; set; } + public ushort[]? Indices { get; set; } + public float[]? Advances { get; set; } + public GlyphOffset[]? Offsets { get; set; } public void Dispose() { - FontFace?.Dispose(); - FontFace = null; + if (FontFace != null) + { + FontFace.Dispose(); + FontFace = null; + } } #region Marshal @@ -53,104 +57,97 @@ internal unsafe void __MarshalFree(ref __Native @ref) internal unsafe void __MarshalFrom(ref __Native @ref) { FontFace = (@ref.FontFace == IntPtr.Zero) ? null : new IDWriteFontFace(@ref.FontFace); - FontFace?.AddRef(); + if (FontFace != null) + ((IUnknown)FontFace).AddRef(); - FontEmSize = @ref.FontEmSize; + FontSize = @ref.FontEmSize; + GlyphCount = @ref.GlyphCount; GlyphCount = @ref.GlyphCount; - GlyphIndices = null; - GlyphAdvances = null; - GlyphOffsets = null; - IsSideways = @ref.IsSideways; - BidiLevel = @ref.BidiLevel; - if (@ref.GlyphIndices != IntPtr.Zero) { - GlyphIndices = new ushort[@ref.GlyphCount]; - if (@ref.GlyphCount > 0) - fixed (void* indicesPtr = &GlyphIndices[0]) - { - Unsafe.CopyBlock(indicesPtr, @ref.GlyphIndices.ToPointer(), - (uint)(sizeof(ushort) * @ref.GlyphCount)); - } + Indices = new ushort[GlyphCount]; + if (GlyphCount > 0) + UnsafeUtilities.Read(@ref.GlyphIndices, Indices, GlyphCount); } if (@ref.GlyphAdvances != IntPtr.Zero) { - GlyphAdvances = new float[@ref.GlyphCount]; - if (@ref.GlyphCount > 0) - fixed (void* advancesPtr = &GlyphAdvances[0]) - { - Unsafe.CopyBlock( - advancesPtr, - @ref.GlyphAdvances.ToPointer(), - (uint)(sizeof(float) * @ref.GlyphCount)); - } + Advances = new float[GlyphCount]; + if (GlyphCount > 0) + UnsafeUtilities.Read(@ref.GlyphAdvances, Advances, GlyphCount); } if (@ref.GlyphOffsets != IntPtr.Zero) { - GlyphOffsets = new GlyphOffset[@ref.GlyphCount]; - if (@ref.GlyphCount > 0) - fixed (void* offsetsPtr = &GlyphOffsets[0]) - { - Unsafe.CopyBlock(offsetsPtr, @ref.GlyphOffsets.ToPointer(), (uint)(sizeof(GlyphOffset) * @ref.GlyphCount)); - } + Offsets = new GlyphOffset[GlyphCount]; + if (GlyphCount > 0) + UnsafeUtilities.Read(@ref.GlyphOffsets, Offsets, GlyphCount); } + + IsSideways = @ref.IsSideways; + BidiLevel = @ref.BidiLevel; } internal unsafe void __MarshalTo(ref __Native @ref) { @ref.FontFace = FontFace == null ? IntPtr.Zero : FontFace.NativePointer; - @ref.FontEmSize = FontEmSize; - @ref.GlyphCount = GlyphCount; + @ref.FontEmSize = FontSize; + @ref.GlyphCount = -1; @ref.GlyphIndices = IntPtr.Zero; @ref.GlyphAdvances = IntPtr.Zero; @ref.GlyphOffsets = IntPtr.Zero; - @ref.IsSideways = IsSideways; - @ref.BidiLevel = BidiLevel; - if (GlyphIndices != null) + if (Indices != null) { - @ref.GlyphIndices = Marshal.AllocHGlobal(GlyphIndices.Length * sizeof(ushort)); - if (GlyphCount > 0) + @ref.GlyphCount = Indices.Length; + + @ref.GlyphIndices = Marshal.AllocHGlobal(Indices.Length * sizeof(ushort)); + if (Indices.Length > 0) { - fixed (void* glyphIndicesPtr = &GlyphIndices[0]) - { - Unsafe.CopyBlock(@ref.GlyphIndices.ToPointer(), - glyphIndicesPtr, - (uint)(sizeof(ushort) * GlyphCount)); - } + UnsafeUtilities.Write(@ref.GlyphIndices, Indices, 0, Indices.Length); } - } - if (GlyphAdvances != null) + if (Advances != null) { - @ref.GlyphAdvances = Marshal.AllocHGlobal(GlyphAdvances.Length * sizeof(float)); - if (GlyphCount > 0) + if (@ref.GlyphCount >= 0 && @ref.GlyphCount != Advances.Length) { - fixed (void* glyphAdvancesPtr = &GlyphAdvances[0]) - { - Unsafe.CopyBlock(@ref.GlyphAdvances.ToPointer(), - glyphAdvancesPtr, - (uint)(sizeof(float) * GlyphCount)); - } + throw new InvalidOperationException( + $"Invalid length for array Advances [{Advances.Length}] and Indices [{@ref.GlyphCount}]. Indices, Advances and Offsets array must have same size - or may be null" + ); + } + + @ref.GlyphCount = Advances.Length; + @ref.GlyphAdvances = Marshal.AllocHGlobal(Advances.Length * sizeof(float)); + if (Advances.Length > 0) + { + UnsafeUtilities.Write(@ref.GlyphAdvances, Advances, 0, Advances.Length); } } - if (GlyphOffsets != null) + if (Offsets != null) { - @ref.GlyphOffsets = Marshal.AllocHGlobal(GlyphOffsets.Length * sizeof(GlyphOffset)); - if (GlyphCount > 0) + if (@ref.GlyphCount >= 0 && @ref.GlyphCount != Offsets.Length) { - fixed (void* offsetsPtr = &GlyphOffsets[0]) - { - Unsafe.CopyBlock(@ref.GlyphOffsets.ToPointer(), - offsetsPtr, - (uint)(sizeof(GlyphOffset) * GlyphCount)); - } + throw new InvalidOperationException($"Invalid length for array Offsets [{Offsets.Length}]. Indices, Advances and Offsets array must have same size (Current is [{@ref.GlyphCount}]- or may be null"); + } + + @ref.GlyphCount = this.Offsets.Length; + @ref.GlyphOffsets = Marshal.AllocHGlobal(this.Offsets.Length * sizeof(GlyphOffset)); + if (this.Offsets.Length > 0) + { + UnsafeUtilities.Write(@ref.GlyphOffsets, Offsets, 0, this.Offsets.Length); } } + + if (@ref.GlyphCount < 0) + @ref.GlyphCount = 0; + + // Update GlyphCount only for debug purpose + GlyphCount = @ref.GlyphCount; + + @ref.IsSideways = this.IsSideways; + @ref.BidiLevel = this.BidiLevel; } #endregion Marshal } diff --git a/src/Vortice.Direct2D1/DirectWrite/GlyphRunDescription.cs b/src/Vortice.Direct2D1/DirectWrite/GlyphRunDescription.cs new file mode 100644 index 00000000..e98fab29 --- /dev/null +++ b/src/Vortice.Direct2D1/DirectWrite/GlyphRunDescription.cs @@ -0,0 +1,57 @@ +// Copyright (c) Amer Koleci and contributors. +// Distributed under the MIT license. See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SharpGen.Runtime; + +namespace Vortice.DirectWrite +{ + public partial class GlyphRunDescription + { + #region Marshal + [StructLayout(LayoutKind.Sequential, Pack = 0)] + internal partial struct __Native + { + public IntPtr LocaleName; + public IntPtr Text; + public int TextLength; + public IntPtr ClusterMap; + public int TextPosition; + + internal unsafe void __MarshalFree() + { + if (LocaleName != IntPtr.Zero) + Marshal.FreeHGlobal(LocaleName); + if (Text != IntPtr.Zero) + Marshal.FreeHGlobal(Text); + } + } + + internal unsafe void __MarshalFree(ref __Native @ref) + { + @ref.__MarshalFree(); + } + + internal unsafe void __MarshalFrom(ref __Native @ref) + { + LocaleName = (@ref.LocaleName == IntPtr.Zero) ? null : Marshal.PtrToStringUni(@ref.LocaleName); + Text = (@ref.Text == IntPtr.Zero) ? null : Marshal.PtrToStringUni(@ref.Text, @ref.TextLength); + TextLength = @ref.TextLength; + ClusterMap = @ref.ClusterMap; + TextPosition = @ref.TextPosition; + } + + internal unsafe void __MarshalTo(ref __Native @ref) + { + @ref.LocaleName = string.IsNullOrEmpty(LocaleName) ? IntPtr.Zero : Marshal.StringToHGlobalUni(LocaleName); + @ref.Text = string.IsNullOrEmpty(Text) ? IntPtr.Zero : Marshal.StringToHGlobalUni(Text); + @ref.TextLength = string.IsNullOrEmpty(Text) ? 0 : Text.Length; + @ref.ClusterMap = ClusterMap; + @ref.TextPosition = TextPosition; + } + #endregion Marshal + } +} diff --git a/src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator.cs b/src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator.cs new file mode 100644 index 00000000..e7f604b3 --- /dev/null +++ b/src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator.cs @@ -0,0 +1,23 @@ +// Copyright (c) Amer Koleci and contributors. +// Distributed under the MIT license. See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Vortice.DCommon; +using Vortice.Direct2D1; + +namespace Vortice.DirectWrite +{ + public partial class IDWriteColorGlyphRunEnumerator + { + public ColorGlyphRun CurrentRun => GetCurrentRun(); + + internal unsafe ColorGlyphRun GetCurrentRun() + { + ColorGlyphRun colorGlyphRun = default; + ColorGlyphRun.__Native* colorGlyphRun_ = (ColorGlyphRun.__Native*)GetCurrentRun_(); + colorGlyphRun.__MarshalFrom(ref *colorGlyphRun_); + return colorGlyphRun; + } + } +} diff --git a/src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator1.cs b/src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator1.cs new file mode 100644 index 00000000..37e7e0fc --- /dev/null +++ b/src/Vortice.Direct2D1/DirectWrite/IDWriteColorGlyphRunEnumerator1.cs @@ -0,0 +1,23 @@ +// Copyright (c) Amer Koleci and contributors. +// Distributed under the MIT license. See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Vortice.DCommon; +using Vortice.Direct2D1; + +namespace Vortice.DirectWrite +{ + public partial class IDWriteColorGlyphRunEnumerator1 + { + public new ColorGlyphRun1 CurrentRun => GetCurrentRun(); + + internal new unsafe ColorGlyphRun1 GetCurrentRun() + { + ColorGlyphRun1 colorGlyphRun = default; + ColorGlyphRun1.__Native* colorGlyphRun_ = (ColorGlyphRun1.__Native*)GetCurrentRun_(); + colorGlyphRun.__MarshalFrom(ref *colorGlyphRun_); + return colorGlyphRun; + } + } +} diff --git a/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory2.cs b/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory2.cs index 66dcc8e3..21b8b8b1 100644 --- a/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory2.cs +++ b/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory2.cs @@ -1,10 +1,9 @@ // Copyright (c) Amer Koleci and contributors. // Distributed under the MIT license. See the LICENSE file in the project root for more information. -using System; -using System.Numerics; +using System.Drawing; +using SharpGen.Runtime; using Vortice.DCommon; -using Vortice.Direct2D1; namespace Vortice.DirectWrite { @@ -14,6 +13,38 @@ public IDWriteColorGlyphRunEnumerator TranslateColorGlyphRun( float baselineOriginX, float baselineOriginY, GlyphRun glyphRun) + { + TranslateColorGlyphRun( + baselineOriginX, + baselineOriginY, + glyphRun, + null, + MeasuringMode.Natural, + null, + 0, + out IDWriteColorGlyphRunEnumerator colorLayers).CheckError(); + return colorLayers; + } + + public IDWriteColorGlyphRunEnumerator TranslateColorGlyphRun(in PointF baselineOrigin, GlyphRun glyphRun) + { + TranslateColorGlyphRun( + baselineOrigin.X, + baselineOrigin.Y, + glyphRun, + null, + MeasuringMode.Natural, + null, + 0, + out IDWriteColorGlyphRunEnumerator colorLayers).CheckError(); + return colorLayers; + } + + public Result TranslateColorGlyphRun( + float baselineOriginX, + float baselineOriginY, + GlyphRun glyphRun, + out IDWriteColorGlyphRunEnumerator colorLayers) { return TranslateColorGlyphRun( baselineOriginX, @@ -22,7 +53,22 @@ public IDWriteColorGlyphRunEnumerator TranslateColorGlyphRun( null, MeasuringMode.Natural, null, - 0); + 0, + out colorLayers); + } + + public Result TranslateColorGlyphRun(in PointF baselineOrigin, GlyphRun glyphRun, + out IDWriteColorGlyphRunEnumerator colorLayers) + { + return TranslateColorGlyphRun( + baselineOrigin.X, + baselineOrigin.Y, + glyphRun, + null, + MeasuringMode.Natural, + null, + 0, + out colorLayers); } } } diff --git a/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory4.cs b/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory4.cs new file mode 100644 index 00000000..3ff3cfae --- /dev/null +++ b/src/Vortice.Direct2D1/DirectWrite/IDWriteFactory4.cs @@ -0,0 +1,37 @@ +// Copyright (c) Amer Koleci and contributors. +// Distributed under the MIT license. See the LICENSE file in the project root for more information. + +using System; +using System.Drawing; +using System.Numerics; +using SharpGen.Runtime; +using Vortice.DCommon; +using Vortice.Direct2D1; + +namespace Vortice.DirectWrite +{ + public partial class IDWriteFactory4 + { + public Result TranslateColorGlyphRun( + in PointF baselineOrigin, + GlyphRun glyphRun, + GlyphImageFormats desiredGlyphImageFormats, + MeasuringMode measuringMode, + out IDWriteColorGlyphRunEnumerator1 colorLayers) + { + return TranslateColorGlyphRun(baselineOrigin, glyphRun, null, desiredGlyphImageFormats, measuringMode, null, 0, out colorLayers); + } + + public IDWriteColorGlyphRunEnumerator1 TranslateColorGlyphRun( + in PointF baselineOrigin, + GlyphRun glyphRun, + GlyphImageFormats desiredGlyphImageFormats, + MeasuringMode measuringMode = MeasuringMode.Natural) + { + TranslateColorGlyphRun(baselineOrigin, glyphRun, null, + desiredGlyphImageFormats, + measuringMode, null, 0, out IDWriteColorGlyphRunEnumerator1 colorLayers).CheckError(); + return colorLayers; + } + } +} diff --git a/src/Vortice.Direct2D1/DirectWrite/TextRendererBase.cs b/src/Vortice.Direct2D1/DirectWrite/TextRendererBase.cs index 6c085822..c0d77aac 100644 --- a/src/Vortice.Direct2D1/DirectWrite/TextRendererBase.cs +++ b/src/Vortice.Direct2D1/DirectWrite/TextRendererBase.cs @@ -14,7 +14,7 @@ public abstract class TextRendererBase : CallbackBase, IDWriteTextRenderer public virtual float GetPixelsPerDip(IntPtr clientDrawingContext) => 1.0f; public virtual RawBool IsPixelSnappingDisabled(IntPtr clientDrawingContext) => false; - public virtual void DrawGlyphRun(IntPtr clientDrawingContext, float baselineOriginX, float baselineOriginY, MeasuringMode measuringMode, GlyphRun glyphRun, ref GlyphRunDescription glyphRunDescription, IUnknown clientDrawingEffect) + public virtual void DrawGlyphRun(IntPtr clientDrawingContext, float baselineOriginX, float baselineOriginY, MeasuringMode measuringMode, GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, IUnknown clientDrawingEffect) { } diff --git a/src/Vortice.Direct2D1/Mappings.xml b/src/Vortice.Direct2D1/Mappings.xml index c9688330..96fe360e 100644 --- a/src/Vortice.Direct2D1/Mappings.xml +++ b/src/Vortice.Direct2D1/Mappings.xml @@ -338,10 +338,15 @@ - - - - + + + + + + + + + @@ -489,6 +494,8 @@ + + @@ -571,6 +578,9 @@ + + + diff --git a/src/Vortice.DirectX/UnsafeUtilities.cs b/src/Vortice.DirectX/UnsafeUtilities.cs index a1e3a0e9..99090d89 100644 --- a/src/Vortice.DirectX/UnsafeUtilities.cs +++ b/src/Vortice.DirectX/UnsafeUtilities.cs @@ -31,6 +31,15 @@ public static IntPtr Read(IntPtr source, T[] values) where T : unmanaged } } + public static IntPtr Read(IntPtr source, T[] values, int count) where T : unmanaged + { + fixed (void* dstPtr = values) + { + Unsafe.CopyBlockUnaligned(dstPtr, (void*)source, (uint)(count * sizeof(T))); + return source + sizeof(T) * count; + } + } + public static IntPtr Write(IntPtr destination, ref T value) where T : unmanaged { fixed (void* valuePtr = &value) diff --git a/src/samples/AdvancedTextRenderingApp/Program.cs b/src/samples/AdvancedTextRenderingApp/Program.cs index 058d20ce..99958b53 100644 --- a/src/samples/AdvancedTextRenderingApp/Program.cs +++ b/src/samples/AdvancedTextRenderingApp/Program.cs @@ -24,7 +24,7 @@ public CustomColorRenderer(ID2D1RenderTarget renderTarget, ID2D1SolidColorBrush _defaultBrush = defaultBrush; } - public override void DrawGlyphRun(IntPtr clientDrawingContext, float baselineOriginX, float baselineOriginY, MeasuringMode measuringMode, GlyphRun glyphRun, ref GlyphRunDescription glyphRunDescription, IUnknown clientDrawingEffect) + public override void DrawGlyphRun(IntPtr clientDrawingContext, float baselineOriginX, float baselineOriginY, MeasuringMode measuringMode, GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, IUnknown clientDrawingEffect) { ID2D1SolidColorBrush brush = _defaultBrush; if (clientDrawingEffect is ComObject comObject) @@ -171,15 +171,8 @@ protected override void OnDraw(int width, int height) public static void Main() { -#if DEBUG - Configuration.EnableObjectTracking = true; -#endif - using var app = new TestApplication(); app.Run(); -#if DEBUG - Console.WriteLine(ObjectTracker.ReportActiveObjects()); -#endif } } }