Browse Source

Remove Direct2D1 (#20132)

pull/20170/head
Julien Lebosquain 3 weeks ago
committed by GitHub
parent
commit
246cbfc566
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 22
      Avalonia.sln
  2. 4
      CONTRIBUTING.md
  3. 2
      nukebuild/Build.cs
  4. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  5. 20
      src/Avalonia.Base/Properties/AssemblyInfo.cs
  6. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  7. 1
      src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
  8. 22
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  9. 306
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  10. 49
      src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs
  11. 75
      src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
  12. 53
      src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
  13. 10
      src/Windows/Avalonia.Direct2D1/IExternalDirect2DRenderTargetSurface.cs
  14. 9
      src/Windows/Avalonia.Direct2D1/ILayerFactory.cs
  15. 66
      src/Windows/Avalonia.Direct2D1/Media/AvaloniaTextRenderer.cs
  16. 17
      src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs
  17. 15
      src/Windows/Avalonia.Direct2D1/Media/BrushWrapper.cs
  18. 37
      src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs
  19. 67
      src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs
  20. 84
      src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs
  21. 93
      src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs
  22. 72
      src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
  23. 736
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  24. 24
      src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs
  25. 116
      src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
  26. 34
      src/Windows/Avalonia.Direct2D1/Media/GeometryGroupImpl.cs
  27. 130
      src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs
  28. 131
      src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs
  29. 216
      src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs
  30. 124
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
  31. 36
      src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
  32. 59
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
  33. 77
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  34. 278
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  35. 55
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
  36. 34
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
  37. 24
      src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs
  38. 48
      src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs
  39. 53
      src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs
  40. 23
      src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs
  41. 37
      src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs
  42. 95
      src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs
  43. 52
      src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs
  44. 204
      src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
  45. 26
      src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs
  46. 22
      src/Windows/Avalonia.Direct2D1/OptionalDispose.cs
  47. 226
      src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs
  48. 45
      src/Windows/Avalonia.Direct2D1/RenderTarget.cs
  49. 124
      src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
  50. 13
      src/Windows/Avalonia.Direct2D1/Utils/DebugUtils.cs
  51. 1
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  52. 28
      tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
  53. 20
      tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj
  54. 69
      tests/Avalonia.Direct2D1.UnitTests/Controls/Shapes/PathTests.cs
  55. 89
      tests/Avalonia.Direct2D1.UnitTests/Media/FontManagerImplTests.cs
  56. 37
      tests/Avalonia.Direct2D1.UnitTests/Media/GeometryTests.cs
  57. 5
      tests/Avalonia.Direct2D1.UnitTests/Properties/AssemblyInfo.cs
  58. 21
      tests/Avalonia.Direct2D1.UnitTests/RectComparer.cs
  59. 6
      tests/Avalonia.RenderTests/Controls/AdornerTests.cs
  60. 4
      tests/Avalonia.RenderTests/Controls/BorderTests.cs
  61. 4
      tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs
  62. 4
      tests/Avalonia.RenderTests/Controls/ImageBlendTests.cs
  63. 4
      tests/Avalonia.RenderTests/Controls/ImageCompositionTests.cs
  64. 4
      tests/Avalonia.RenderTests/Controls/ImageTests.cs
  65. 14
      tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
  66. 2
      tests/Avalonia.RenderTests/CrossTests/Brushes/CrossTileBrushTests.cs
  67. 2
      tests/Avalonia.RenderTests/CrossTests/Brushes/RadialGradientBrushTests.cs
  68. 2
      tests/Avalonia.RenderTests/CrossTests/CrossGeometryTests.cs
  69. 2
      tests/Avalonia.RenderTests/CrossTests/Media/DrawingContextTests.cs
  70. 2
      tests/Avalonia.RenderTests/CrossTests/Media/ImageScalingTests.cs
  71. 6
      tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs
  72. 4
      tests/Avalonia.RenderTests/GeometryClippingTests.cs
  73. 7
      tests/Avalonia.RenderTests/ManualRenderTimer.cs
  74. 4
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  75. 4
      tests/Avalonia.RenderTests/Media/CombinedGeometryTests.cs
  76. 4
      tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs
  77. 7
      tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs
  78. 4
      tests/Avalonia.RenderTests/Media/GeometryGroupTests.cs
  79. 4
      tests/Avalonia.RenderTests/Media/GlyphRunTests.cs
  80. 4
      tests/Avalonia.RenderTests/Media/ImageBrushTests.cs
  81. 4
      tests/Avalonia.RenderTests/Media/ImageDrawingTests.cs
  82. 4
      tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs
  83. 4
      tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs
  84. 4
      tests/Avalonia.RenderTests/Media/RelativePointTestPrimitivesHelper.cs
  85. 4
      tests/Avalonia.RenderTests/Media/StreamGeometryTests.cs
  86. 4
      tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs
  87. 5
      tests/Avalonia.RenderTests/Media/TileBrushTests.cs
  88. 4
      tests/Avalonia.RenderTests/Media/VisualBrushTests.cs
  89. 4
      tests/Avalonia.RenderTests/OpacityMaskTests.cs
  90. 6
      tests/Avalonia.RenderTests/SVGPathTests.cs
  91. 4
      tests/Avalonia.RenderTests/Shapes/EllipseTests.cs
  92. 4
      tests/Avalonia.RenderTests/Shapes/LineTests.cs
  93. 4
      tests/Avalonia.RenderTests/Shapes/PathTests.cs
  94. 4
      tests/Avalonia.RenderTests/Shapes/PolygonTests.cs
  95. 4
      tests/Avalonia.RenderTests/Shapes/PolylineTests.cs
  96. 4
      tests/Avalonia.RenderTests/Shapes/RectangleTests.cs
  97. 22
      tests/Avalonia.RenderTests/TestBase.cs
  98. 12
      tests/Avalonia.RenderTests/TestRenderHelper.cs
  99. 6
      tests/Avalonia.RenderTests/TestRenderRoot.cs
  100. 4
      tests/Avalonia.RenderTests/TestSkip.cs

22
Avalonia.sln

@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{B39A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Simple", "src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
@ -23,10 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.UnitTests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base.UnitTests", "tests\Avalonia.Base.UnitTests\Avalonia.Base.UnitTests.csproj", "{2905FF23-53FB-45E6-AA49-6AF47A172056}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Markup", "Markup", "{8B6A8209-894F-4BA1-B880-965FD453982C}"
@ -108,7 +102,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props
build\SharedVersion.props = build\SharedVersion.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props
build\SourceGenerators.props = build\SourceGenerators.props
build\SourceLink.props = build\SourceLink.props
@ -307,10 +300,6 @@ Global
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.Build.0 = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.Build.0 = Release|Any CPU
{D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -331,14 +320,6 @@ Global
{2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.Build.0 = Release|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.Build.0 = Release|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.Build.0 = Release|Any CPU
{99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99135EAB-653D-47E4-A378-C96E1278CA44}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -685,11 +666,8 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{2905FF23-53FB-45E6-AA49-6AF47A172056} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{6417E941-21BC-467B-A771-0DE389353CE6} = {8B6A8209-894F-4BA1-B880-965FD453982C}

4
CONTRIBUTING.md

@ -10,7 +10,7 @@ A bug fix will ideally be accompanied by tests. There are a few types of tests:
- Unit tests are for issues that aren't related to platform features. These tests are located in the `tests` directly, categorised by the assembly which they're testing.
- Integration tests are for issues that are related to platform features (for example fixing a bug with Window resizing). These tests are located in the `tests/Avalonia.IntegrationTests.Appium` directory. Integration tests should be run on Windows and macOS. See the readme in that directory for more information
- Render tests are for issues with rendering. These tests are located in `tests/Avalonia.RenderTests` with separate project files for Skia and Direct2D. The Direct2D backend is currently mostly unmaintained so render tests that just run on Skia are acceptable
- Render tests are for issues with rendering. These tests are located in `tests/Avalonia.RenderTests` and are imported in the `Avalonia.Skia.RenderTests` project.
It's not always feasible to accompany a bug fix with a test, but doing so will speed up the review process.
@ -88,4 +88,4 @@ Render tests should describe what the produced image is:
## Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
For more information see the [Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)
For more information see the [Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)

2
nukebuild/Build.cs

@ -308,8 +308,6 @@ partial class Build : NukeBuild
.Executes(() =>
{
RunCoreTest("Avalonia.Skia.RenderTests");
if (Parameters.IsRunningOnWindows)
RunCoreTest("Avalonia.Direct2D1.RenderTests");
});
Target RunToolsTests => _ => _

3
src/Avalonia.Base/Avalonia.Base.csproj

@ -29,7 +29,6 @@
<InternalsVisibleTo Include="Avalonia.Desktop, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Direct2D1, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml.Loader, PublicKey=$(AvaloniaPublicKey)" />
@ -45,12 +44,10 @@
<InternalsVisibleTo Include="Avalonia.Browser, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia3.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" />

20
src/Avalonia.Base/Properties/AssemblyInfo.cs

@ -1,6 +1,3 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Data;
using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")]
@ -18,20 +15,3 @@ using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")]
[assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Controls.ColorPicker, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Browser.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Browser, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

1
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -14,7 +14,6 @@
<InternalsVisibleTo Include="Avalonia.Controls.ItemsRepeater, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia3.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />

1
src/Skia/Avalonia.Skia/Avalonia.Skia.csproj

@ -22,7 +22,6 @@
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia3.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>

22
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@ -1,22 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\SharpDX.props" />
<Import Project="..\..\..\build\HarfBuzzSharp.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" />
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Direct2D1.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Win32.Interoperability, PublicKey=$(AvaloniaPublicKey)"/>
<InternalsVisibleTo Include="Avalonia.Win32.Interop, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
</Project>

306
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -1,306 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Direct2D1.Media;
using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Media.TextFormatting;
using Avalonia.Platform;
using GlyphRun = Avalonia.Media.GlyphRun;
using SharpDX.Mathematics.Interop;
namespace Avalonia
{
public static class Direct2DApplicationExtensions
{
public static AppBuilder UseDirect2D1(this AppBuilder builder)
{
builder.UseRenderingSubsystem(Direct2D1.Direct2D1Platform.Initialize, "Direct2D1");
return builder;
}
}
}
namespace Avalonia.Direct2D1
{
internal class Direct2D1Platform : IPlatformRenderInterface
{
private static readonly Direct2D1Platform s_instance = new Direct2D1Platform();
public static SharpDX.Direct3D11.Device Direct3D11Device { get; private set; }
public static SharpDX.Direct2D1.Factory1 Direct2D1Factory { get; private set; }
public static SharpDX.Direct2D1.Device Direct2D1Device { get; private set; }
public static SharpDX.DirectWrite.Factory1 DirectWriteFactory { get; private set; }
public static SharpDX.WIC.ImagingFactory ImagingFactory { get; private set; }
public static SharpDX.DXGI.Device1 DxgiDevice { get; private set; }
private static readonly object s_initLock = new object();
private static bool s_initialized = false;
internal static void InitializeDirect2D()
{
lock (s_initLock)
{
if (s_initialized)
{
return;
}
#if DEBUG
if (Debugger.IsAttached)
{
try
{
Direct2D1Factory = new SharpDX.Direct2D1.Factory1(
SharpDX.Direct2D1.FactoryType.MultiThreaded,
SharpDX.Direct2D1.DebugLevel.Error);
}
catch
{
// ignore, retry below without the debug layer
}
}
#endif
if (Direct2D1Factory == null)
{
Direct2D1Factory = new SharpDX.Direct2D1.Factory1(
SharpDX.Direct2D1.FactoryType.MultiThreaded,
SharpDX.Direct2D1.DebugLevel.None);
}
using (var factory = new SharpDX.DirectWrite.Factory())
{
DirectWriteFactory = factory.QueryInterface<SharpDX.DirectWrite.Factory1>();
}
ImagingFactory = new SharpDX.WIC.ImagingFactory();
var featureLevels = new[]
{
SharpDX.Direct3D.FeatureLevel.Level_11_1,
SharpDX.Direct3D.FeatureLevel.Level_11_0,
SharpDX.Direct3D.FeatureLevel.Level_10_1,
SharpDX.Direct3D.FeatureLevel.Level_10_0,
SharpDX.Direct3D.FeatureLevel.Level_9_3,
SharpDX.Direct3D.FeatureLevel.Level_9_2,
SharpDX.Direct3D.FeatureLevel.Level_9_1,
};
Direct3D11Device = new SharpDX.Direct3D11.Device(
SharpDX.Direct3D.DriverType.Hardware,
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport | SharpDX.Direct3D11.DeviceCreationFlags.VideoSupport,
featureLevels);
DxgiDevice = Direct3D11Device.QueryInterface<SharpDX.DXGI.Device1>();
Direct2D1Device = new SharpDX.Direct2D1.Device(Direct2D1Factory, DxgiDevice);
s_initialized = true;
}
}
public static void Initialize()
{
InitializeDirect2D();
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(s_instance)
.Bind<IFontManagerImpl>().ToConstant(new FontManagerImpl())
.Bind<ITextShaperImpl>().ToConstant(new TextShaperImpl());
SharpDX.Configuration.EnableReleaseOnFinalizer = true;
}
private IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
foreach (var s in surfaces)
{
if (s is IPlatformHandle nativeWindow)
{
if (nativeWindow.HandleDescriptor != "HWND")
{
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from " +
nativeWindow.HandleDescriptor);
}
return new HwndRenderTarget(nativeWindow);
}
if (s is IExternalDirect2DRenderTargetSurface external)
{
return new ExternalRenderTarget(external);
}
if (s is IFramebufferPlatformSurface fb)
{
return new FramebufferShimRenderTarget(fb);
}
}
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces");
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
{
return new WicRenderTargetBitmapImpl(size, dpi);
}
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new WriteableWicBitmapImpl(size, dpi, format, alphaFormat);
}
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect);
public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl();
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList<IGeometryImpl> children) => new GeometryGroupImpl(fillRule, children);
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) => new CombinedGeometryImpl(combineMode, g1, g2);
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
{
return new GlyphRunImpl(glyphTypeface, fontRenderingEmSize, glyphInfos, baselineOrigin);
}
class D2DApi : IPlatformRenderInterfaceContext
{
private readonly Direct2D1Platform _platform;
public D2DApi(Direct2D1Platform platform)
{
_platform = platform;
}
public object TryGetFeature(Type featureType) => null;
public void Dispose()
{
}
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => _platform.CreateRenderTarget(surfaces);
public IDrawingContextLayerImpl CreateOffscreenRenderTarget(PixelSize pixelSize, double scaling) =>
new WicRenderTargetBitmapImpl(pixelSize, new Vector(96 * scaling, 96 * scaling));
public bool IsLost => false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; } = new Dictionary<Type, object>();
}
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) =>
new D2DApi(this);
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface)
{
throw new InvalidOperationException("PlatformImpl can't be null.");
}
var pathGeometry = new SharpDX.Direct2D1.PathGeometry(Direct2D1Factory);
using (var sink = pathGeometry.Open())
{
var glyphInfos = glyphRun.GlyphInfos;
var glyphs = new short[glyphInfos.Count];
for (int i = 0; i < glyphInfos.Count; i++)
{
glyphs[i] = (short)glyphInfos[i].GlyphIndex;
}
glyphTypeface.FontFace.GetGlyphRunOutline((float)glyphRun.FontRenderingEmSize, glyphs, null, null, false, !glyphRun.IsLeftToRight, sink);
sink.Close();
}
var (baselineOriginX, baselineOriginY) = glyphRun.BaselineOrigin;
var transformedGeometry = new SharpDX.Direct2D1.TransformedGeometry(
Direct2D1Factory,
pathGeometry,
new RawMatrix3x2(1.0f, 0.0f, 0.0f, 1.0f, (float)baselineOriginX, (float)baselineOriginY));
return new TransformedGeometryWrapper(transformedGeometry);
}
private class TransformedGeometryWrapper : GeometryImpl
{
public TransformedGeometryWrapper(SharpDX.Direct2D1.TransformedGeometry geometry) : base(geometry)
{
}
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(string fileName)
{
return new WicBitmapImpl(fileName);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(Stream stream)
{
return new WicBitmapImpl(stream);
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WriteableWicBitmapImpl(stream, width, true, interpolationMode);
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WriteableWicBitmapImpl(stream, height, false, interpolationMode);
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
return new WriteableWicBitmapImpl(fileName);
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
return new WriteableWicBitmapImpl(stream);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WicBitmapImpl(stream, width, true, interpolationMode);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WicBitmapImpl(stream, height, false, interpolationMode);
}
/// <inheritdoc />
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
// https://github.com/sharpdx/SharpDX/issues/959 blocks implementation.
throw new NotImplementedException();
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new WicBitmapImpl(format, alphaFormat, data, size, dpi, stride);
}
public bool SupportsIndividualRoundRects => false;
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888;
public bool IsSupportedBitmapPixelFormat(PixelFormat format) =>
format == PixelFormats.Bgra8888
|| format == PixelFormats.Rgba8888;
public bool SupportsRegions => false;
public IPlatformRenderInterfaceRegion CreateRegion() => throw new NotSupportedException();
}
}

49
src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs

@ -1,49 +0,0 @@
using Avalonia.Direct2D1.Media;
using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering;
using SharpDX;
namespace Avalonia.Direct2D1
{
class ExternalRenderTarget : IRenderTarget, ILayerFactory
{
private readonly IExternalDirect2DRenderTargetSurface _externalRenderTargetProvider;
public ExternalRenderTarget(
IExternalDirect2DRenderTargetSurface externalRenderTargetProvider)
{
_externalRenderTargetProvider = externalRenderTargetProvider;
}
public void Dispose()
{
_externalRenderTargetProvider.DestroyRenderTarget();
}
public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
{
var target = _externalRenderTargetProvider.GetOrCreateRenderTarget();
_externalRenderTargetProvider.BeforeDrawing();
return new DrawingContextImpl( null, target, useScaledDrawing, null, () =>
{
try
{
_externalRenderTargetProvider.AfterDrawing();
}
catch (SharpDXException ex) when ((uint) ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET
{
_externalRenderTargetProvider.DestroyRenderTarget();
}
});
}
public bool IsCorrupted => false;
public IDrawingContextLayerImpl CreateLayer(Size size)
{
var renderTarget = _externalRenderTargetProvider.GetOrCreateRenderTarget();
return D2DRenderTargetBitmapImpl.CreateCompatible(renderTarget, size);
}
}
}

75
src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs

@ -1,75 +0,0 @@
#nullable enable
using System;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Direct2D1.Media;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using SharpDX.WIC;
using PixelFormat = Avalonia.Platform.PixelFormat;
namespace Avalonia.Direct2D1
{
class FramebufferShimRenderTarget : IRenderTarget
{
private IFramebufferRenderTarget? _target;
public FramebufferShimRenderTarget(IFramebufferPlatformSurface surface)
{
_target = surface.CreateFramebufferRenderTarget();
}
public void Dispose()
{
_target?.Dispose();
_target = null;
}
public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
{
if (_target == null)
throw new ObjectDisposedException(nameof(FramebufferShimRenderTarget));
var locked = _target.Lock();
if (locked.Format == PixelFormat.Rgb565)
{
locked.Dispose();
throw new ArgumentException("Unsupported pixel format: " + locked.Format);
}
return new FramebufferShim(locked)
.CreateDrawingContext(useScaledDrawing);
}
public bool IsCorrupted => false;
class FramebufferShim : WicRenderTargetBitmapImpl
{
private readonly ILockedFramebuffer _target;
public FramebufferShim(ILockedFramebuffer target) :
base(target.Size, target.Dpi, target.Format)
{
_target = target;
}
public override IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
{
return base.CreateDrawingContext(useScaledDrawing, () =>
{
using (var l = WicImpl.Lock(BitmapLockFlags.Read))
{
for (var y = 0; y < _target.Size.Height; y++)
{
UnmanagedMethods.CopyMemory(
(_target.Address + _target.RowBytes * y),
(l.Data.DataPointer + l.Stride * y),
(UIntPtr)Math.Min(l.Stride, _target.RowBytes));
}
}
Dispose();
_target.Dispose();
});
}
}
}
}

53
src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs

@ -1,53 +0,0 @@
using Avalonia.Platform;
using Avalonia.Win32;
using Avalonia.Win32.Interop;
using SharpDX;
using SharpDX.DXGI;
namespace Avalonia.Direct2D1
{
class HwndRenderTarget : SwapChainRenderTarget
{
private readonly IPlatformHandle _window;
public HwndRenderTarget(IPlatformHandle window)
{
_window = window;
}
protected override SwapChain1 CreateSwapChain(Factory2 dxgiFactory, SwapChainDescription1 swapChainDesc)
{
return new SwapChain1(dxgiFactory, Direct2D1Platform.DxgiDevice, _window.Handle, ref swapChainDesc);
}
protected override Size2F GetWindowDpi()
{
if (UnmanagedMethods.ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{
uint dpix, dpiy;
var monitor = UnmanagedMethods.MonitorFromWindow(
_window.Handle,
UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
if (UnmanagedMethods.GetDpiForMonitor(
monitor,
UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
out dpix,
out dpiy) == 0)
{
return new Size2F(dpix, dpiy);
}
}
return new Size2F(96, 96);
}
protected override Size2 GetWindowSize()
{
UnmanagedMethods.RECT rc;
UnmanagedMethods.GetClientRect(_window.Handle, out rc);
return new Size2(rc.right - rc.left, rc.bottom - rc.top);
}
}
}

10
src/Windows/Avalonia.Direct2D1/IExternalDirect2DRenderTargetSurface.cs

@ -1,10 +0,0 @@
namespace Avalonia.Direct2D1
{
public interface IExternalDirect2DRenderTargetSurface
{
SharpDX.Direct2D1.RenderTarget GetOrCreateRenderTarget();
void DestroyRenderTarget();
void BeforeDrawing();
void AfterDrawing();
}
}

9
src/Windows/Avalonia.Direct2D1/ILayerFactory.cs

@ -1,9 +0,0 @@
using Avalonia.Platform;
namespace Avalonia.Direct2D1
{
internal interface ILayerFactory
{
IDrawingContextLayerImpl CreateLayer(Size size);
}
}

66
src/Windows/Avalonia.Direct2D1/Media/AvaloniaTextRenderer.cs

@ -1,66 +0,0 @@
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using SharpDX.Mathematics.Interop;
namespace Avalonia.Direct2D1.Media
{
internal class AvaloniaTextRenderer : TextRendererBase
{
private readonly DrawingContextImpl _context;
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
private readonly Brush _foreground;
public AvaloniaTextRenderer(
DrawingContextImpl context,
SharpDX.Direct2D1.RenderTarget target,
Brush foreground)
{
_context = context;
_renderTarget = target;
_foreground = foreground;
}
public override Result DrawGlyphRun(
object clientDrawingContext,
float baselineOriginX,
float baselineOriginY,
MeasuringMode measuringMode,
GlyphRun glyphRun,
GlyphRunDescription glyphRunDescription,
ComObject clientDrawingEffect)
{
var wrapper = clientDrawingEffect as BrushWrapper;
// TODO: Work out how to get the size below rather than passing new Size().
var brush = (wrapper == null) ?
_foreground :
_context.CreateBrush(wrapper.Brush, default).PlatformBrush;
_renderTarget.DrawGlyphRun(
new RawVector2 { X = baselineOriginX, Y = baselineOriginY },
glyphRun,
brush,
measuringMode);
if (wrapper != null)
{
brush.Dispose();
}
return Result.Ok;
}
public override RawMatrix3x2 GetCurrentTransform(object clientDrawingContext)
{
return _renderTarget.Transform;
}
public override float GetPixelsPerDip(object clientDrawingContext)
{
return _renderTarget.DotsPerInch.Width / 96;
}
}
}

17
src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs

@ -1,17 +0,0 @@
using System;
namespace Avalonia.Direct2D1.Media
{
internal abstract class BrushImpl : IDisposable
{
public SharpDX.Direct2D1.Brush PlatformBrush { get; set; }
public virtual void Dispose()
{
if (PlatformBrush != null)
{
PlatformBrush.Dispose();
}
}
}
}

15
src/Windows/Avalonia.Direct2D1/Media/BrushWrapper.cs

@ -1,15 +0,0 @@
using Avalonia.Media;
using SharpDX;
namespace Avalonia.Direct2D1.Media
{
internal class BrushWrapper : ComObject
{
public BrushWrapper(IBrush brush)
{
Brush = brush;
}
public IBrush Brush { get; private set; }
}
}

37
src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs

@ -1,37 +0,0 @@
using Avalonia.Platform;
using SharpDX.Direct2D1;
using AM = Avalonia.Media;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D implementation of a <see cref="Avalonia.Media.CombinedGeometry"/>.
/// </summary>
internal class CombinedGeometryImpl : GeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
public CombinedGeometryImpl(
AM.GeometryCombineMode combineMode,
IGeometryImpl geometry1,
IGeometryImpl geometry2)
: base(CreateGeometry(combineMode, geometry1, geometry2))
{
}
private static Geometry CreateGeometry(
AM.GeometryCombineMode combineMode,
IGeometryImpl geometry1,
IGeometryImpl geometry2)
{
var g1 = ((GeometryImpl)geometry1).Geometry;
var g2 = ((GeometryImpl)geometry2).Geometry;
var dest = new PathGeometry(Direct2D1Platform.Direct2D1Factory);
using var sink = dest.Open();
g1.Combine(g2, (CombineMode)combineMode, sink);
sink.Close();
return dest;
}
}
}

67
src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs

@ -1,67 +0,0 @@
using SharpDX;
using SharpDX.DirectWrite;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// Resource FontFileEnumerator.
/// </summary>
internal class DWriteResourceFontFileEnumerator : CallbackBase, FontFileEnumerator
{
private readonly Factory _factory;
private readonly FontFileLoader _loader;
private readonly DataStream _keyStream;
private FontFile _currentFontFile;
/// <summary>
/// Initializes a new instance of the <see cref="DWriteResourceFontFileEnumerator"/> class.
/// </summary>
/// <param name="factory">The factory.</param>
/// <param name="loader">The loader.</param>
/// <param name="key">The key.</param>
public DWriteResourceFontFileEnumerator(Factory factory, FontFileLoader loader, DataPointer key)
{
_factory = factory;
_loader = loader;
_keyStream = new DataStream(key.Pointer, key.Size, true, false);
}
/// <summary>
/// Advances to the next font file in the collection. When it is first created, the enumerator is positioned before the first element of the collection and the first call to MoveNext advances to the first file.
/// </summary>
/// <returns>
/// the value TRUE if the enumerator advances to a file; otherwise, FALSE if the enumerator advances past the last file in the collection.
/// </returns>
/// <unmanaged>HRESULT IDWriteFontFileEnumerator::MoveNext([Out] BOOL* hasCurrentFile)</unmanaged>
bool FontFileEnumerator.MoveNext()
{
bool moveNext = _keyStream.RemainingLength != 0;
if (!moveNext) return false;
_currentFontFile?.Dispose();
_currentFontFile = new FontFile(_factory, _keyStream.PositionPointer, 4, _loader);
_keyStream.Position += 4;
return true;
}
/// <summary>
/// Gets a reference to the current font file.
/// </summary>
/// <value></value>
/// <returns>a reference to the newly created <see cref="SharpDX.DirectWrite.FontFile"/> object.</returns>
/// <unmanaged>HRESULT IDWriteFontFileEnumerator::GetCurrentFontFile([Out] IDWriteFontFile** fontFile)</unmanaged>
FontFile FontFileEnumerator.CurrentFontFile
{
get
{
((IUnknown)_currentFontFile).AddReference();
return _currentFontFile;
}
}
}
}

84
src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs

@ -1,84 +0,0 @@
using System;
using SharpDX;
using SharpDX.DirectWrite;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// This FontFileStream implementation is reading data from a <see cref="DataStream"/>.
/// </summary>
internal class DWriteResourceFontFileStream : CallbackBase, FontFileStream
{
private readonly DataStream _stream;
/// <summary>
/// Initializes a new instance of the <see cref="DWriteResourceFontFileStream"/> class.
/// </summary>
/// <param name="stream">The stream.</param>
public DWriteResourceFontFileStream(DataStream stream)
{
_stream = stream;
}
/// <summary>
/// Reads a fragment from a font file.
/// </summary>
/// <param name="fragmentStart">When this method returns, contains an address of a reference to the start of the font file fragment. This parameter is passed uninitialized.</param>
/// <param name="fileOffset">The offset of the fragment, in bytes, from the beginning of the font file.</param>
/// <param name="fragmentSize">The size of the file fragment, in bytes.</param>
/// <param name="fragmentContext">When this method returns, contains the address of</param>
/// <remarks>
/// Note that ReadFileFragment implementations must check whether the requested font file fragment is within the file bounds. Otherwise, an error should be returned from ReadFileFragment. {{DirectWrite}} may invoke <see cref="SharpDX.DirectWrite.FontFileStream"/> methods on the same object from multiple threads simultaneously. Therefore, ReadFileFragment implementations that rely on internal mutable state must serialize access to such state across multiple threads. For example, an implementation that uses separate Seek and Read operations to read a file fragment must place the code block containing Seek and Read calls under a lock or a critical section.
/// </remarks>
/// <unmanaged>HRESULT IDWriteFontFileStream::ReadFileFragment([Out, Buffer] const void** fragmentStart,[None] __int64 fileOffset,[None] __int64 fragmentSize,[Out] void** fragmentContext)</unmanaged>
void FontFileStream.ReadFileFragment(out IntPtr fragmentStart, long fileOffset, long fragmentSize, out IntPtr fragmentContext)
{
lock (this)
{
fragmentContext = IntPtr.Zero;
_stream.Position = fileOffset;
fragmentStart = _stream.PositionPointer;
}
}
/// <summary>
/// Releases a fragment from a file.
/// </summary>
/// <param name="fragmentContext">A reference to the client-defined context of a font fragment returned from {{ReadFileFragment}}.</param>
/// <unmanaged>void IDWriteFontFileStream::ReleaseFileFragment([None] void* fragmentContext)</unmanaged>
void FontFileStream.ReleaseFileFragment(IntPtr fragmentContext)
{
// Nothing to release. No context are used
}
/// <summary>
/// Obtains the total size of a file.
/// </summary>
/// <returns>the total size of the file.</returns>
/// <remarks>
/// Implementing GetFileSize() for asynchronously loaded font files may require downloading the complete file contents. Therefore, this method should be used only for operations that either require a complete font file to be loaded (for example, copying a font file) or that need to make decisions based on the value of the file size (for example, validation against a persisted file size).
/// </remarks>
/// <unmanaged>HRESULT IDWriteFontFileStream::GetFileSize([Out] __int64* fileSize)</unmanaged>
long FontFileStream.GetFileSize()
{
return _stream.Length;
}
/// <summary>
/// Obtains the last modified time of the file.
/// </summary>
/// <returns>
/// the last modified time of the file in the format that represents the number of 100-nanosecond intervals since January 1, 1601 (UTC).
/// </returns>
/// <remarks>
/// The "last modified time" is used by DirectWrite font selection algorithms to determine whether one font resource is more up to date than another one.
/// </remarks>
/// <unmanaged>HRESULT IDWriteFontFileStream::GetLastWriteTime([Out] __int64* lastWriteTime)</unmanaged>
long FontFileStream.GetLastWriteTime()
{
return 0;
}
}
}

93
src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs

@ -1,93 +0,0 @@
using System.Collections.Generic;
using SharpDX;
using SharpDX.DirectWrite;
namespace Avalonia.Direct2D1.Media
{
using System.IO;
internal class DWriteResourceFontLoader : CallbackBase, FontCollectionLoader, FontFileLoader
{
private readonly List<DWriteResourceFontFileStream> _fontStreams = new List<DWriteResourceFontFileStream>();
private readonly List<DWriteResourceFontFileEnumerator> _enumerators = new List<DWriteResourceFontFileEnumerator>();
private readonly DataStream _keyStream;
/// <summary>
/// Initializes a new instance of the <see cref="DWriteResourceFontLoader"/> class.
/// </summary>
/// <param name="factory">The factory.</param>
/// <param name="fontAssets"></param>
public DWriteResourceFontLoader(Factory factory, Stream[] fontAssets)
{
var factory1 = factory;
foreach (var asset in fontAssets)
{
var dataStream = new DataStream((int)asset.Length, true, true);
asset.CopyTo(dataStream);
dataStream.Position = 0;
_fontStreams.Add(new DWriteResourceFontFileStream(dataStream));
}
// Build a Key storage that stores the index of the font
_keyStream = new DataStream(sizeof(int) * _fontStreams.Count, true, true);
for (int i = 0; i < _fontStreams.Count; i++)
{
_keyStream.Write(i);
}
_keyStream.Position = 0;
// Register the
factory1.RegisterFontFileLoader(this);
factory1.RegisterFontCollectionLoader(this);
}
/// <summary>
/// Gets the key used to identify the FontCollection as well as storing index for fonts.
/// </summary>
/// <value>The key.</value>
public DataStream Key => _keyStream;
/// <summary>
/// Creates a font file enumerator object that encapsulates a collection of font files. The font system calls back to this interface to create a font collection.
/// </summary>
/// <param name="factory">Pointer to the <see cref="SharpDX.DirectWrite.Factory"/> object that was used to create the current font collection.</param>
/// <param name="collectionKey">A font collection key that uniquely identifies the collection of font files within the scope of the font collection loader being used. The buffer allocated for this key must be at least the size, in bytes, specified by collectionKeySize.</param>
/// <returns>
/// a reference to the newly created font file enumerator.
/// </returns>
/// <unmanaged>HRESULT IDWriteFontCollectionLoader::CreateEnumeratorFromKey([None] IDWriteFactory* factory,[In, Buffer] const void* collectionKey,[None] int collectionKeySize,[Out] IDWriteFontFileEnumerator** fontFileEnumerator)</unmanaged>
FontFileEnumerator FontCollectionLoader.CreateEnumeratorFromKey(Factory factory, DataPointer collectionKey)
{
var enumerator = new DWriteResourceFontFileEnumerator(factory, this, collectionKey);
_enumerators.Add(enumerator);
return enumerator;
}
/// <summary>
/// Creates a font file stream object that encapsulates an open file resource.
/// </summary>
/// <param name="fontFileReferenceKey">A reference to a font file reference key that uniquely identifies the font file resource within the scope of the font loader being used. The buffer allocated for this key must at least be the size, in bytes, specified by fontFileReferenceKeySize.</param>
/// <returns>
/// a reference to the newly created <see cref="SharpDX.DirectWrite.FontFileStream"/> object.
/// </returns>
/// <remarks>
/// The resource is closed when the last reference to fontFileStream is released.
/// </remarks>
/// <unmanaged>HRESULT IDWriteFontFileLoader::CreateStreamFromKey([In, Buffer] const void* fontFileReferenceKey,[None] int fontFileReferenceKeySize,[Out] IDWriteFontFileStream** fontFileStream)</unmanaged>
FontFileStream FontFileLoader.CreateStreamFromKey(DataPointer fontFileReferenceKey)
{
var index = SharpDX.Utilities.Read<int>(fontFileReferenceKey.Pointer);
return _fontStreams[index];
}
}
}

72
src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs

@ -1,72 +0,0 @@
using System.Collections.Concurrent;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using SharpDX.DirectWrite;
using FontFamily = Avalonia.Media.FontFamily;
using FontStyle = SharpDX.DirectWrite.FontStyle;
using FontWeight = SharpDX.DirectWrite.FontWeight;
using FontStretch = SharpDX.DirectWrite.FontStretch;
using Avalonia.Platform;
using System.Linq;
using System;
namespace Avalonia.Direct2D1.Media
{
internal static class Direct2D1FontCollectionCache
{
private static readonly ConcurrentDictionary<FontFamilyKey, FontCollection> s_cachedCollections;
internal static readonly FontCollection InstalledFontCollection;
static Direct2D1FontCollectionCache()
{
s_cachedCollections = new ConcurrentDictionary<FontFamilyKey, FontCollection>();
InstalledFontCollection = Direct2D1Platform.DirectWriteFactory.GetSystemFontCollection(false);
}
public static Font GetFont(Typeface typeface)
{
var fontFamily = typeface.FontFamily;
var fontCollection = GetOrAddFontCollection(fontFamily);
int index;
foreach (var name in fontFamily.FamilyNames)
{
if (fontCollection.FindFamilyName(name, out index))
{
return fontCollection.GetFontFamily(index).GetFirstMatchingFont(
(FontWeight)typeface.Weight,
(FontStretch)typeface.Stretch,
(FontStyle)typeface.Style);
}
}
InstalledFontCollection.FindFamilyName("Segoe UI", out index);
return InstalledFontCollection.GetFontFamily(index).GetFirstMatchingFont(
(FontWeight)typeface.Weight,
(FontStretch)typeface.Stretch,
(FontStyle)typeface.Style);
}
private static FontCollection GetOrAddFontCollection(FontFamily fontFamily)
{
return fontFamily.Key == null ? InstalledFontCollection : s_cachedCollections.GetOrAdd(fontFamily.Key, CreateFontCollection);
}
private static FontCollection CreateFontCollection(FontFamilyKey key)
{
var source = key.BaseUri != null ? new Uri(key.BaseUri, key.Source) : key.Source;
var assets = FontFamilyLoader.LoadFontAssets(source);
var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
var fontAssets = assets.Select(x => assetLoader.Open(x)).ToArray();
var fontLoader = new DWriteResourceFontLoader(Direct2D1Platform.DirectWriteFactory, fontAssets);
return new FontCollection(Direct2D1Platform.DirectWriteFactory, fontLoader, fontLoader.Key);
}
}
}

736
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -1,736 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// Draws using Direct2D1.
/// </summary>
internal class DrawingContextImpl : IDrawingContextImpl
{
private readonly ILayerFactory _layerFactory;
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
private readonly DeviceContext _deviceContext;
private readonly bool _ownsDeviceContext;
private readonly SharpDX.DXGI.SwapChain1 _swapChain;
private readonly Action _finishedCallback;
private readonly Stack<RenderOptions> _renderOptionsStack = new();
/// <summary>
/// Initializes a new instance of the <see cref="DrawingContextImpl"/> class.
/// </summary>
/// <param name="renderTarget">The render target to draw to.</param>
/// <param name="layerFactory">
/// An object to use to create layers. May be null, in which case a
/// <see cref="WicRenderTargetBitmapImpl"/> will created when a new layer is requested.
/// </param>
/// <param name="useScaledDrawing">Whether to scale drawings according to the DPI of <paramref name="renderTarget"/>.</param>
/// <param name="swapChain">An optional swap chain associated with this drawing context.</param>
/// <param name="finishedCallback">An optional delegate to be called when context is disposed.</param>
public DrawingContextImpl(
ILayerFactory layerFactory,
SharpDX.Direct2D1.RenderTarget renderTarget,
bool useScaledDrawing,
SharpDX.DXGI.SwapChain1 swapChain = null,
Action finishedCallback = null)
{
_layerFactory = layerFactory;
_renderTarget = renderTarget;
_swapChain = swapChain;
_finishedCallback = finishedCallback;
if (_renderTarget is DeviceContext deviceContext)
{
_deviceContext = deviceContext;
_ownsDeviceContext = false;
}
else
{
_deviceContext = _renderTarget.QueryInterface<DeviceContext>();
_ownsDeviceContext = true;
}
if (!useScaledDrawing)
{
var scaling = _renderTarget.DotsPerInch.Width / 96;
if (!MathUtilities.AreClose(1, scaling))
_postTransform = Matrix.CreateScale(1 / scaling, 1 / scaling);
}
_deviceContext.BeginDraw();
}
/// <summary>
/// Gets the current transform of the drawing context.
/// </summary>
public Matrix Transform
{
get { return _transform; }
set
{
_transform = value;
_deviceContext.Transform =
(_postTransform.HasValue ? value * _postTransform.Value : value).ToDirect2D();
}
}
public Matrix4x4 Transform4x4
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public RenderOptions RenderOptions
{
get => _renderOptions;
set
{
_renderOptions = value;
ApplyRenderOptions(value);
}
}
/// <inheritdoc/>
public void Clear(Color color)
{
_deviceContext.Clear(color.ToDirect2D());
}
/// <summary>
/// Ends a draw operation.
/// </summary>
public void Dispose()
{
foreach (var layer in _layerPool)
{
layer.Dispose();
}
try
{
_deviceContext.EndDraw();
_swapChain?.Present(1, SharpDX.DXGI.PresentFlags.None);
_finishedCallback?.Invoke();
}
catch (SharpDXException ex) when ((uint)ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET
{
throw new RenderTargetCorruptedException(ex);
}
finally
{
if (_ownsDeviceContext)
{
_deviceContext.Dispose();
}
}
}
/// <summary>
/// Draws a bitmap image.
/// </summary>
/// <param name="source">The bitmap image.</param>
/// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param>
public void DrawBitmap(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
using (var d2d = ((BitmapImpl)source).GetDirect2DBitmap(_deviceContext))
{
var interpolationMode = GetInterpolationMode(RenderOptions.BitmapInterpolationMode);
// TODO: How to implement CompositeMode here?
_deviceContext.DrawBitmap(
d2d.Value,
destRect.ToSharpDX(),
(float)opacity,
interpolationMode,
sourceRect.ToSharpDX(),
null);
}
}
private static InterpolationMode GetInterpolationMode(BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case BitmapInterpolationMode.Unspecified:
case BitmapInterpolationMode.LowQuality:
return InterpolationMode.Linear;
case BitmapInterpolationMode.MediumQuality:
return InterpolationMode.MultiSampleLinear;
case BitmapInterpolationMode.HighQuality:
return InterpolationMode.HighQualityCubic;
case BitmapInterpolationMode.None:
return InterpolationMode.NearestNeighbor;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
}
}
public static CompositeMode GetCompositeMode(BitmapBlendingMode blendingMode)
{
switch (blendingMode)
{
case BitmapBlendingMode.SourceIn:
return CompositeMode.SourceIn;
case BitmapBlendingMode.SourceOut:
return CompositeMode.SourceOut;
case BitmapBlendingMode.Unspecified:
case BitmapBlendingMode.SourceOver:
return CompositeMode.SourceOver;
case BitmapBlendingMode.SourceAtop:
return CompositeMode.SourceAtop;
case BitmapBlendingMode.DestinationIn:
return CompositeMode.DestinationIn;
case BitmapBlendingMode.DestinationOut:
return CompositeMode.DestinationOut;
case BitmapBlendingMode.DestinationOver:
return CompositeMode.DestinationOver;
case BitmapBlendingMode.DestinationAtop:
return CompositeMode.DestinationAtop;
case BitmapBlendingMode.Xor:
return CompositeMode.Xor;
case BitmapBlendingMode.Plus:
return CompositeMode.Plus;
default:
throw new ArgumentOutOfRangeException(nameof(blendingMode), blendingMode, null);
}
}
/// <summary>
/// Draws a bitmap image.
/// </summary>
/// <param name="source">The bitmap image.</param>
/// <param name="opacityMask">The opacity mask to draw with.</param>
/// <param name="opacityMaskRect">The destination rect for the opacity mask.</param>
/// <param name="destRect">The rect in the output to draw to.</param>
public void DrawBitmap(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{
var interpolationMode = GetInterpolationMode(RenderOptions.BitmapInterpolationMode);
using (var d2dSource = ((BitmapImpl)source).GetDirect2DBitmap(_deviceContext))
using (var sourceBrush = new BitmapBrush1(_deviceContext, d2dSource.Value, new BitmapBrushProperties1 { InterpolationMode = interpolationMode }))
using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect))
using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(Direct2D1Platform.Direct2D1Factory, destRect.ToDirect2D()))
{
if (d2dOpacityMask.PlatformBrush != null)
{
d2dOpacityMask.PlatformBrush.Transform = Matrix.CreateTranslation(opacityMaskRect.Position).ToDirect2D();
}
_deviceContext.FillGeometry(
geometry,
sourceBrush,
d2dOpacityMask.PlatformBrush);
}
}
/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pen">The stroke pen.</param>
/// <param name="p1">The first point of the line.</param>
/// <param name="p2">The second point of the line.</param>
public void DrawLine(IPen pen, Point p1, Point p2)
{
if (pen != null)
{
var bounds = new Rect(p1, p2);
using (var d2dBrush = CreateBrush(pen.Brush, bounds))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(_deviceContext))
{
if (d2dBrush.PlatformBrush != null)
{
_deviceContext.DrawLine(
p1.ToSharpDX(),
p2.ToSharpDX(),
d2dBrush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
}
}
}
/// <summary>
/// Draws a geometry.
/// </summary>
/// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
{
if (brush != null)
{
using (var d2dBrush = CreateBrush(brush, geometry.Bounds))
{
if (d2dBrush.PlatformBrush != null)
{
var impl = (GeometryImpl)geometry;
_deviceContext.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush);
}
}
}
if (pen != null)
{
using (var d2dBrush = CreateBrush(pen.Brush, geometry.GetRenderBounds(pen)))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(_deviceContext))
{
if (d2dBrush.PlatformBrush != null)
{
var impl = (GeometryImpl)geometry;
_deviceContext.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke);
}
}
}
}
/// <inheritdoc />
public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rrect, BoxShadows boxShadow = default)
{
var rc = rrect.Rect.ToDirect2D();
var rect = rrect.Rect;
var radiusX = Math.Max(rrect.RadiiTopLeft.X,
Math.Max(rrect.RadiiTopRight.X, Math.Max(rrect.RadiiBottomRight.X, rrect.RadiiBottomLeft.X)));
var radiusY = Math.Max(rrect.RadiiTopLeft.Y,
Math.Max(rrect.RadiiTopRight.Y, Math.Max(rrect.RadiiBottomRight.Y, rrect.RadiiBottomLeft.Y)));
var isRounded = !MathUtilities.IsZero(radiusX) || !MathUtilities.IsZero(radiusY);
if (brush != null)
{
using (var b = CreateBrush(brush, rect))
{
if (b.PlatformBrush != null)
{
if (isRounded)
{
_deviceContext.FillRoundedRectangle(
new RoundedRectangle
{
Rect = new RawRectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Right,
(float)rect.Bottom),
RadiusX = (float)radiusX,
RadiusY = (float)radiusY
},
b.PlatformBrush);
}
else
{
_deviceContext.FillRectangle(rc, b.PlatformBrush);
}
}
}
}
if (pen?.Brush != null)
{
using (var wrapper = CreateBrush(pen.Brush, rect))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(_deviceContext))
{
if (wrapper.PlatformBrush != null)
{
if (isRounded)
{
_deviceContext.DrawRoundedRectangle(
new RoundedRectangle { Rect = rc, RadiusX = (float)radiusX, RadiusY = (float)radiusY },
wrapper.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
else
{
_deviceContext.DrawRectangle(
rc,
wrapper.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
}
}
}
}
public void DrawRegion(IBrush brush, IPen pen, IPlatformRenderInterfaceRegion region)
{
throw new NotSupportedException();
}
/// <inheritdoc />
public void DrawEllipse(IBrush brush, IPen pen, Rect rect)
{
var rc = rect.ToDirect2D();
if (brush != null)
{
using (var b = CreateBrush(brush, rect))
{
if (b.PlatformBrush != null)
{
_deviceContext.FillEllipse(new Ellipse
{
Point = rect.Center.ToSharpDX(),
RadiusX = (float)(rect.Width / 2),
RadiusY = (float)(rect.Height / 2)
}, b.PlatformBrush);
}
}
}
if (pen?.Brush != null)
{
using (var wrapper = CreateBrush(pen.Brush, rect))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(_deviceContext))
{
if (wrapper.PlatformBrush != null)
{
_deviceContext.DrawEllipse(new Ellipse
{
Point = rect.Center.ToSharpDX(),
RadiusX = (float)(rect.Width / 2),
RadiusY = (float)(rect.Height / 2)
}, wrapper.PlatformBrush, (float)pen.Thickness, d2dStroke);
}
}
}
}
/// <summary>
/// Draws a glyph run.
/// </summary>
/// <param name="foreground">The foreground.</param>
/// <param name="glyphRun">The glyph run.</param>
public void DrawGlyphRun(IBrush foreground, IGlyphRunImpl glyphRun)
{
using (var brush = CreateBrush(foreground, glyphRun.Bounds))
{
var immutableGlyphRun = (GlyphRunImpl)glyphRun;
var dxGlyphRun = immutableGlyphRun.GlyphRun;
_renderTarget.DrawGlyphRun(glyphRun.BaselineOrigin.ToSharpDX(), dxGlyphRun,
brush.PlatformBrush, MeasuringMode.Natural);
}
}
public IDrawingContextLayerImpl CreateLayer(PixelSize pixelSize)
{
var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height);
if (_layerFactory != null)
{
return _layerFactory.CreateLayer(pixelSize.ToSizeWithDpi(dpi));
}
else
{
var platform = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
return (IDrawingContextLayerImpl)platform.CreateRenderTargetBitmap(pixelSize, dpi);
}
}
/// <summary>
/// Pushes a clip rectange.
/// </summary>
/// <param name="clip">The clip rectangle.</param>
/// <returns>A disposable used to undo the clip rectangle.</returns>
public void PushClip(Rect clip)
{
_deviceContext.PushAxisAlignedClip(clip.ToSharpDX(), AntialiasMode.PerPrimitive);
}
public void PushClip(RoundedRect clip)
{
//TODO: radius
_deviceContext.PushAxisAlignedClip(clip.Rect.ToDirect2D(), AntialiasMode.PerPrimitive);
}
public void PushClip(IPlatformRenderInterfaceRegion region)
{
throw new NotSupportedException();
}
public void PopClip()
{
_deviceContext.PopAxisAlignedClip();
}
public void PushLayer(Rect bounds)
{
var parameters = new LayerParameters
{
ContentBounds = bounds.ToDirect2D(),
MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
Opacity = 1
};
var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_deviceContext);
_deviceContext.PushLayer(ref parameters, layer);
_layers.Push(layer);
}
void IDrawingContextImpl.PopLayer()
{
PopLayer();
}
readonly Stack<Layer> _layers = new Stack<Layer>();
private readonly Stack<Layer> _layerPool = new Stack<Layer>();
private RenderOptions _renderOptions;
private readonly Matrix? _postTransform;
private Matrix _transform = Matrix.Identity;
/// <summary>
/// Pushes an opacity value.
/// </summary>
/// <param name="opacity">The opacity.</param>
/// <param name="bounds">The bounds.</param>
/// <returns>A disposable used to undo the opacity.</returns>
public void PushOpacity(double opacity, Rect? bounds)
{
if (opacity < 1)
{
if(bounds == null || bounds == default(Rect))
{
bounds = new Rect(0, 0, _renderTarget.PixelSize.Width, _renderTarget.PixelSize.Height);
}
var parameters = new LayerParameters
{
MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
Opacity = (float)opacity
};
if(bounds.HasValue)
{
parameters.ContentBounds = bounds.Value.ToDirect2D();
}
var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_deviceContext);
_deviceContext.PushLayer(ref parameters, layer);
_layers.Push(layer);
}
else
_layers.Push(null);
}
public void PopOpacity()
{
PopLayer();
}
public void PushRenderOptions(RenderOptions renderOptions)
{
_renderOptionsStack.Push(RenderOptions);
RenderOptions = RenderOptions.MergeWith(renderOptions);
}
public void PopRenderOptions()
{
RenderOptions = _renderOptionsStack.Pop();
}
private void PopLayer()
{
var layer = _layers.Pop();
if (layer != null)
{
_deviceContext.PopLayer();
_layerPool.Push(layer);
}
}
/// <summary>
/// Creates a Direct2D brush wrapper for a Avalonia brush.
/// </summary>
/// <param name="brush">The avalonia brush.</param>
/// <param name="destinationRect">The size of the brush's target area.</param>
/// <returns>The Direct2D brush wrapper.</returns>
public BrushImpl CreateBrush(IBrush brush, Rect destinationRect)
{
var solidColorBrush = brush as ISolidColorBrush;
var linearGradientBrush = brush as ILinearGradientBrush;
var radialGradientBrush = brush as IRadialGradientBrush;
var conicGradientBrush = brush as IConicGradientBrush;
var imageBrush = brush as IImageBrush;
var sceneBrush = brush as ISceneBrush;
var sceneBrushContent = brush as ISceneBrushContent;
if (solidColorBrush != null)
{
return new SolidColorBrushImpl(solidColorBrush, _deviceContext);
}
else if (linearGradientBrush != null)
{
return new LinearGradientBrushImpl(linearGradientBrush, _deviceContext, destinationRect);
}
else if (radialGradientBrush != null)
{
return new RadialGradientBrushImpl(radialGradientBrush, _deviceContext, destinationRect);
}
else if (conicGradientBrush != null)
{
// there is no Direct2D implementation of Conic Gradients so use Radial as a stand-in
return new SolidColorBrushImpl(conicGradientBrush, _deviceContext);
}
else if (imageBrush?.Source?.Bitmap != null)
{
return new ImageBrushImpl(
imageBrush,
_deviceContext,
(BitmapImpl)imageBrush.Source.Bitmap.Item,
destinationRect);
}
else if (sceneBrush != null || sceneBrushContent != null)
{
sceneBrushContent ??= sceneBrush.CreateContent();
if (sceneBrushContent != null)
{
var rect = sceneBrushContent.Rect;
var intermediateSize = rect.Size;
if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1)
{
// We need to ensure the size we're requesting is an integer pixel size, otherwise
// D2D alters the DPI of the render target, which messes stuff up. PixelSize.FromSize
// will do the rounding for us.
var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height);
var pixelSize = PixelSize.FromSizeWithDpi(intermediateSize, dpi);
var transform = rect.TopLeft == default ?
Matrix.Identity :
Matrix.CreateTranslation(-rect.X, -rect.Y);
var brushTransform = Matrix.Identity;
if (sceneBrushContent.Transform != null)
{
var transformOrigin = sceneBrushContent.TransformOrigin.ToPixels(rect);
var offset = Matrix.CreateTranslation(transformOrigin);
brushTransform = -offset * sceneBrushContent.Transform.Value * offset;
}
using (var intermediate = new BitmapRenderTarget(
_deviceContext,
CompatibleRenderTargetOptions.None,
pixelSize.ToSizeWithDpi(dpi).ToSharpDX()))
{
using (var ctx = new RenderTarget(intermediate).CreateDrawingContext(true))
{
intermediate.Clear(null);
if (sceneBrush?.TileMode == TileMode.None)
{
transform = brushTransform * transform;
}
sceneBrushContent.Render(ctx, transform);
}
return new ImageBrushImpl(
sceneBrushContent.Brush,
_deviceContext,
new D2DBitmapImpl(intermediate.Bitmap.QueryInterface<Bitmap1>()),
destinationRect);
}
}
}
}
return new SolidColorBrushImpl(null, _deviceContext);
}
public void PushGeometryClip(IGeometryImpl clip)
{
var parameters = new LayerParameters
{
ContentBounds = PrimitiveExtensions.RectangleInfinite,
MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
Opacity = 1,
GeometricMask = ((GeometryImpl)clip).Geometry,
MaskAntialiasMode = AntialiasMode.PerPrimitive
};
var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_deviceContext);
_deviceContext.PushLayer(ref parameters, layer);
_layers.Push(layer);
}
public void PopGeometryClip()
{
PopLayer();
}
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
{
// TODO: Stubs for now
}
public void PopBitmapBlendMode()
{
// TODO: Stubs for now
}
public void PushOpacityMask(IBrush mask, Rect bounds)
{
var parameters = new LayerParameters
{
ContentBounds = PrimitiveExtensions.RectangleInfinite,
MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
Opacity = 1,
OpacityBrush = CreateBrush(mask, bounds).PlatformBrush
};
var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_deviceContext);
_deviceContext.PushLayer(ref parameters, layer);
_layers.Push(layer);
}
public void PopOpacityMask()
{
PopLayer();
}
public object GetFeature(Type t) => null;
private void ApplyRenderOptions(RenderOptions renderOptions)
{
_deviceContext.AntialiasMode = renderOptions.EdgeMode != EdgeMode.Aliased ? AntialiasMode.PerPrimitive : AntialiasMode.Aliased;
switch (renderOptions.TextRenderingMode)
{
case TextRenderingMode.Unspecified:
_deviceContext.TextAntialiasMode = renderOptions.EdgeMode != EdgeMode.Aliased ? TextAntialiasMode.Default : TextAntialiasMode.Aliased;
break;
case TextRenderingMode.Alias:
_deviceContext.TextAntialiasMode = TextAntialiasMode.Aliased;
break;
case TextRenderingMode.Antialias:
_deviceContext.TextAntialiasMode = TextAntialiasMode.Grayscale;
break;
case TextRenderingMode.SubpixelAntialias:
_deviceContext.TextAntialiasMode = TextAntialiasMode.Cleartype;
break;
}
}
}
}

24
src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs

@ -1,24 +0,0 @@
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D implementation of a <see cref="Avalonia.Media.EllipseGeometry"/>.
/// </summary>
internal class EllipseGeometryImpl : GeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
public EllipseGeometryImpl(Rect rect)
: base(CreateGeometry(rect))
{
}
private static Geometry CreateGeometry(Rect rect)
{
var ellipse = new Ellipse(rect.Center.ToSharpDX(), (float)rect.Width / 2, (float)rect.Height / 2);
return new EllipseGeometry(Direct2D1Platform.Direct2D1Factory, ellipse);
}
}
}

116
src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs

@ -1,116 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using Avalonia.Media;
using Avalonia.Platform;
using FontFamily = Avalonia.Media.FontFamily;
using FontStretch = Avalonia.Media.FontStretch;
using FontStyle = Avalonia.Media.FontStyle;
using FontWeight = Avalonia.Media.FontWeight;
namespace Avalonia.Direct2D1.Media
{
internal class FontManagerImpl : IFontManagerImpl
{
public string GetDefaultFontFamilyName()
{
//ToDo: Implement a real lookup of the system's default font.
return "Segoe UI";
}
public string[] GetInstalledFontFamilyNames(bool checkForUpdates = false)
{
var familyCount = Direct2D1FontCollectionCache.InstalledFontCollection.FontFamilyCount;
var fontFamilies = new string[familyCount];
for (var i = 0; i < familyCount; i++)
{
fontFamilies[i] = Direct2D1FontCollectionCache.InstalledFontCollection.GetFontFamily(i).FamilyNames.GetString(0);
}
return fontFamilies;
}
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight, FontStretch fontStretch, CultureInfo culture, out Typeface typeface)
{
var familyCount = Direct2D1FontCollectionCache.InstalledFontCollection.FontFamilyCount;
for (var i = 0; i < familyCount; i++)
{
var font = Direct2D1FontCollectionCache.InstalledFontCollection.GetFontFamily(i)
.GetMatchingFonts((SharpDX.DirectWrite.FontWeight)fontWeight,
(SharpDX.DirectWrite.FontStretch)fontStretch,
(SharpDX.DirectWrite.FontStyle)fontStyle).GetFont(0);
if (!font.HasCharacter(codepoint))
{
continue;
}
var fontFamilyName = font.FontFamily.FamilyNames.GetString(0);
typeface = new Typeface(fontFamilyName, fontStyle, fontWeight, fontStretch);
return true;
}
typeface = default;
return false;
}
public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface glyphTypeface)
{
var systemFonts = Direct2D1FontCollectionCache.InstalledFontCollection;
if (familyName == FontFamily.DefaultFontFamilyName)
{
familyName = "Segoe UI";
}
if (systemFonts.FindFamilyName(familyName, out var index))
{
var font = systemFonts.GetFontFamily(index).GetFirstMatchingFont(
(SharpDX.DirectWrite.FontWeight)weight,
(SharpDX.DirectWrite.FontStretch)stretch,
(SharpDX.DirectWrite.FontStyle)style);
glyphTypeface = new GlyphTypefaceImpl(font);
return true;
}
glyphTypeface = null;
return false;
}
public bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, out IGlyphTypeface glyphTypeface)
{
var fontLoader = new DWriteResourceFontLoader(Direct2D1Platform.DirectWriteFactory, new[] { stream });
var fontCollection = new SharpDX.DirectWrite.FontCollection(Direct2D1Platform.DirectWriteFactory, fontLoader, fontLoader.Key);
if (fontCollection.FontFamilyCount > 0)
{
var fontFamily = fontCollection.GetFontFamily(0);
if (fontFamily.FontCount > 0)
{
var font = fontFamily.GetFont(0);
glyphTypeface = new GlyphTypefaceImpl(font);
return true;
}
}
glyphTypeface = null;
return false;
}
}
}

34
src/Windows/Avalonia.Direct2D1/Media/GeometryGroupImpl.cs

@ -1,34 +0,0 @@
using System.Collections.Generic;
using Avalonia.Platform;
using SharpDX.Direct2D1;
using AM = Avalonia.Media;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D implementation of a <see cref="Avalonia.Media.GeometryGroup"/>.
/// </summary>
internal class GeometryGroupImpl : GeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
public GeometryGroupImpl(AM.FillRule fillRule, IReadOnlyList<IGeometryImpl> geometry)
: base(CreateGeometry(fillRule, geometry))
{
}
private static Geometry CreateGeometry(AM.FillRule fillRule, IReadOnlyList<IGeometryImpl> children)
{
var count = children.Count;
var c = new Geometry[count];
for (var i = 0; i < count; ++i)
{
c[i] = ((GeometryImpl)children[i]).Geometry;
}
return new GeometryGroup(Direct2D1Platform.Direct2D1Factory, (FillMode)fillRule, c);
}
}
}

130
src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs

@ -1,130 +0,0 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Platform;
using SharpDX.Direct2D1;
using Geometry = SharpDX.Direct2D1.Geometry;
using PathGeometry = SharpDX.Direct2D1.PathGeometry;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// The platform-specific interface for <see cref="Avalonia.Media.Geometry"/>.
/// </summary>
internal abstract class GeometryImpl : IGeometryImpl
{
private const float ContourApproximation = 0.0001f;
public GeometryImpl(Geometry geometry)
{
Geometry = geometry;
}
/// <inheritdoc/>
public Rect Bounds => Geometry.GetWidenedBounds(0).ToAvalonia();
/// <inheritdoc />
public double ContourLength => Geometry.ComputeLength(null, ContourApproximation);
public Geometry Geometry { get; }
/// <inheritdoc/>
public Rect GetRenderBounds(Avalonia.Media.IPen pen)
{
if (pen == null || Math.Abs(pen.Thickness) < float.Epsilon)
return Geometry.GetBounds().ToAvalonia();
var originalBounds = Geometry.GetWidenedBounds((float)pen.Thickness).ToAvalonia();
switch (pen.LineCap)
{
case PenLineCap.Flat:
return originalBounds;
case PenLineCap.Round:
return originalBounds.Inflate(pen.Thickness / 2);
case PenLineCap.Square:
return originalBounds.Inflate(pen.Thickness);
default:
throw new ArgumentOutOfRangeException();
}
}
public IGeometryImpl GetWidenedGeometry(IPen pen)
{
var result = new PathGeometry(Direct2D1Platform.Direct2D1Factory);
using (var sink = result.Open())
{
Geometry.Widen(
(float)pen.Thickness,
pen.ToDirect2DStrokeStyle(Direct2D1Platform.Direct2D1Factory),
0.25f,
sink);
sink.Close();
}
return new StreamGeometryImpl(result);
}
/// <inheritdoc/>
public bool FillContains(Point point)
{
return Geometry.FillContainsPoint(point.ToSharpDX());
}
/// <inheritdoc/>
public IGeometryImpl Intersect(IGeometryImpl geometry)
{
var result = new PathGeometry(Direct2D1Platform.Direct2D1Factory);
using (var sink = result.Open())
{
Geometry.Combine(((GeometryImpl)geometry).Geometry, CombineMode.Intersect, sink);
sink.Close();
}
return new StreamGeometryImpl(result);
}
/// <inheritdoc/>
public bool StrokeContains(Avalonia.Media.IPen pen, Point point)
{
return Geometry.StrokeContainsPoint(point.ToSharpDX(), (float)(pen?.Thickness ?? 0));
}
public ITransformedGeometryImpl WithTransform(Matrix transform)
{
return new TransformedGeometryImpl(
new TransformedGeometry(
Direct2D1Platform.Direct2D1Factory,
GetSourceGeometry(),
transform.ToDirect2D()),
this);
}
/// <inheritdoc />
public bool TryGetPointAtDistance(double distance, out Point point)
{
Geometry.ComputePointAtLength((float)distance, ContourApproximation, out var tangentVector);
point = new Point(tangentVector.X, tangentVector.Y);
return true;
}
/// <inheritdoc />
public bool TryGetPointAndTangentAtDistance(double distance, out Point point, out Point tangent)
{
// Direct2D doesn't have this sadly.
Logger.TryGet(LogEventLevel.Warning, LogArea.Visual)?.Log(this, "TryGetPointAndTangentAtDistance is not available in Direct2D.");
point = new Point();
tangent = new Point();
return false;
}
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry)
{
// Direct2D doesn't have this too sadly.
Logger.TryGet(LogEventLevel.Warning, LogArea.Visual)?.Log(this, "TryGetSegment is not available in Direct2D.");
segmentGeometry = null;
return false;
}
protected virtual Geometry GetSourceGeometry() => Geometry;
}
}

131
src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs

@ -1,131 +0,0 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Platform;
using SharpDX.DirectWrite;
#nullable enable
namespace Avalonia.Direct2D1.Media
{
internal class GlyphRunImpl : IGlyphRunImpl
{
private readonly GlyphTypefaceImpl _glyphTypefaceImpl;
private readonly short[] _glyphIndices;
private readonly float[] _glyphAdvances;
private readonly GlyphOffset[] _glyphOffsets;
private SharpDX.DirectWrite.GlyphRun? _glyphRun;
public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
{
_glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
FontRenderingEmSize = fontRenderingEmSize;
BaselineOrigin = baselineOrigin;
var glyphCount = glyphInfos.Count;
_glyphIndices = new short[glyphCount];
for (var i = 0; i < glyphCount; i++)
{
_glyphIndices[i] = (short)glyphInfos[i].GlyphIndex;
}
_glyphAdvances = new float[glyphCount];
var width = 0.0;
for (var i = 0; i < glyphCount; i++)
{
var advance = glyphInfos[i].GlyphAdvance;
width += advance;
_glyphAdvances[i] = (float)advance;
}
_glyphOffsets = new GlyphOffset[glyphCount];
var runBounds = new Rect();
var currentX = 0.0;
var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight;
for (var i = 0; i < glyphCount; i++)
{
var (x, y) = glyphInfos[i].GlyphOffset;
_glyphOffsets[i] = new GlyphOffset
{
AdvanceOffset = (float)x,
AscenderOffset = (float)y
};
if (_glyphTypefaceImpl.TryGetGlyphMetrics(glyphInfos[i].GlyphIndex, out var metrics))
{
// Found metrics with negative height, prefer to adjust it to positive.
var ybearing = metrics.YBearing;
var height = metrics.Height;
if (height < 0)
{
height = -height;
}
// Not entirely sure about why we need to do this, but it seems to work
var xOffset = metrics.XBearing * scale;
var xWidth = xOffset > 0 ? xOffset : 0;
var xBearing = xOffset < 0 ? xOffset : 0;
//yBearing is the vertical distance from the baseline to the top of the glyph's bbox. It is usually positive for horizontal layouts, and negative for vertical ones.
runBounds = runBounds.Union(new Rect(currentX + xBearing, baselineOrigin.Y - ybearing * scale, xWidth + metrics.Width * scale, height * scale));
}
currentX += glyphInfos[i].GlyphAdvance;
}
Bounds = runBounds.Translate(new Vector(baselineOrigin.X, 0));
}
public SharpDX.DirectWrite.GlyphRun GlyphRun
{
get
{
if (_glyphRun != null)
{
return _glyphRun;
}
_glyphRun = new SharpDX.DirectWrite.GlyphRun
{
FontFace = _glyphTypefaceImpl.FontFace,
FontSize = (float)FontRenderingEmSize,
Advances = _glyphAdvances,
Indices = _glyphIndices,
Offsets = _glyphOffsets
};
return _glyphRun;
}
}
public IGlyphTypeface GlyphTypeface => _glyphTypefaceImpl;
public double FontRenderingEmSize { get; }
public Point BaselineOrigin { get; }
public Rect Bounds { get; }
public IReadOnlyList<float> GetIntersections(float lowerBound, float upperBound) => Array.Empty<float>();
public void Dispose()
{
//_glyphRun?.Dispose();
_glyphRun = null;
}
}
}

216
src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs

@ -1,216 +0,0 @@
using System;
using Avalonia.Media;
using HarfBuzzSharp;
using SharpDX.DirectWrite;
using FontMetrics = Avalonia.Media.FontMetrics;
using FontSimulations = Avalonia.Media.FontSimulations;
using GlyphMetrics = Avalonia.Media.GlyphMetrics;
namespace Avalonia.Direct2D1.Media
{
internal class GlyphTypefaceImpl : IGlyphTypeface
{
private bool _isDisposed;
public GlyphTypefaceImpl(SharpDX.DirectWrite.Font font)
{
DWFont = font;
FontFace = new FontFace(DWFont).QueryInterface<FontFace1>();
Face = new Face(GetTable);
Font = new HarfBuzzSharp.Font(Face);
Font.SetFunctionsOpenType();
Font.GetScale(out var xScale, out _);
if (!Font.TryGetHorizontalFontExtents(out var fontExtents))
{
Font.TryGetVerticalFontExtents(out fontExtents);
}
Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition);
Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness);
Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition);
Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness);
Metrics = new FontMetrics
{
DesignEmHeight = (short)xScale,
Ascent = -fontExtents.Ascender,
Descent = -fontExtents.Descender,
LineGap = fontExtents.LineGap,
UnderlinePosition = underlinePosition,
UnderlineThickness = underlineThickness,
StrikethroughPosition = strikethroughPosition,
StrikethroughThickness = strikethroughThickness,
IsFixedPitch = FontFace.IsMonospacedFont
};
FamilyName = DWFont.FontFamily.FamilyNames.GetString(0);
Weight = (Avalonia.Media.FontWeight)DWFont.Weight;
Style = (Avalonia.Media.FontStyle)DWFont.Style;
Stretch = (Avalonia.Media.FontStretch)DWFont.Stretch;
}
private Blob GetTable(Face face, Tag tag)
{
var dwTag = (int)SwapBytes(tag);
if (FontFace.TryGetFontTable(dwTag, out var tableData, out _))
{
return new Blob(tableData.Pointer, tableData.Size, MemoryMode.ReadOnly, () => { });
}
return null;
}
private static uint SwapBytes(uint x)
{
x = (x >> 16) | (x << 16);
return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8);
}
public SharpDX.DirectWrite.Font DWFont { get; }
public FontFace1 FontFace { get; }
public Face Face { get; }
public HarfBuzzSharp.Font Font { get; }
public FontMetrics Metrics { get; }
public int GlyphCount { get; set; }
public FontSimulations FontSimulations => FontSimulations.None;
public string FamilyName { get; }
public Avalonia.Media.FontWeight Weight { get; }
public Avalonia.Media.FontStyle Style { get; }
public Avalonia.Media.FontStretch Stretch { get; }
/// <inheritdoc cref="IGlyphTypeface"/>
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
{
return (ushort)glyph;
}
return 0;
}
public bool TryGetGlyph(uint codepoint, out ushort glyph)
{
glyph = GetGlyph(codepoint);
return glyph != 0;
}
/// <inheritdoc cref="IGlyphTypeface"/>
public ushort[] GetGlyphs(ReadOnlySpan<uint> codepoints)
{
var glyphs = new ushort[codepoints.Length];
for (var i = 0; i < codepoints.Length; i++)
{
if (Font.TryGetGlyph(codepoints[i], out var glyph))
{
glyphs[i] = (ushort)glyph;
}
}
return glyphs;
}
/// <inheritdoc cref="IGlyphTypeface"/>
public int GetGlyphAdvance(ushort glyph)
{
return Font.GetHorizontalGlyphAdvance(glyph);
}
/// <inheritdoc cref="IGlyphTypeface"/>
public int[] GetGlyphAdvances(ReadOnlySpan<ushort> glyphs)
{
var glyphIndices = new uint[glyphs.Length];
for (var i = 0; i < glyphs.Length; i++)
{
glyphIndices[i] = glyphs[i];
}
return Font.GetHorizontalGlyphAdvances(glyphIndices);
}
public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics)
{
metrics = default;
if (!Font.TryGetGlyphExtents(glyph, out var extents))
{
return false;
}
metrics = new GlyphMetrics
{
XBearing = extents.XBearing,
YBearing = extents.YBearing,
Width = extents.Width,
Height = extents.Height
};
return true;
}
private void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
if (!disposing)
{
return;
}
Font?.Dispose();
Face?.Dispose();
FontFace?.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public bool TryGetTable(uint tag, out byte[] table)
{
table = null;
var blob = Face.ReferenceTable(tag);
if (blob.Length > 0)
{
table = blob.AsSpan().ToArray();
return true;
}
return false;
}
}
}

124
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@ -1,124 +0,0 @@
using Avalonia.Media;
using Avalonia.Rendering.Utilities;
using Avalonia.Utilities;
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
internal sealed class ImageBrushImpl : BrushImpl
{
private readonly OptionalDispose<Bitmap1> _bitmap;
public ImageBrushImpl(
ITileBrush brush,
SharpDX.Direct2D1.RenderTarget target,
BitmapImpl bitmap,
Rect destinationRect)
{
var dpi = new Vector(target.DotsPerInch.Width, target.DotsPerInch.Height);
var calc = new TileBrushCalculator(brush, bitmap.PixelSize.ToSizeWithDpi(dpi), destinationRect.Size);
Vector brushOffset = default;
if (brush.DestinationRect.Unit == RelativeUnit.Relative)
brushOffset = new Vector(destinationRect.X, destinationRect.Y);
if (!calc.NeedsIntermediate)
{
_bitmap = bitmap.GetDirect2DBitmap(target);
PlatformBrush = new BitmapBrush(
target,
_bitmap.Value,
GetBitmapBrushProperties(brush),
GetBrushProperties(brush, calc.DestinationRect, brushOffset));
}
else
{
using (var intermediate = RenderIntermediate(target, bitmap, calc))
{
PlatformBrush = new BitmapBrush(
target,
intermediate.Bitmap,
GetBitmapBrushProperties(brush),
GetBrushProperties(brush, calc.DestinationRect, brushOffset));
}
}
}
public override void Dispose()
{
_bitmap.Dispose();
base.Dispose();
}
private static BitmapBrushProperties GetBitmapBrushProperties(ITileBrush brush)
{
var tileMode = brush.TileMode;
return new BitmapBrushProperties
{
ExtendModeX = GetExtendModeX(tileMode),
ExtendModeY = GetExtendModeY(tileMode),
};
}
private static BrushProperties GetBrushProperties(ITileBrush brush, Rect destinationRect, Vector offset)
{
var tileTransform =
brush.TileMode != TileMode.None ?
Matrix.CreateTranslation(destinationRect.X, destinationRect.Y) :
Matrix.Identity;
if (offset != default)
tileTransform = Matrix.CreateTranslation(offset);
if (brush.Transform != null && brush.TileMode != TileMode.None)
{
var transformOrigin = brush.TransformOrigin.ToPixels(destinationRect);
var originOffset = Matrix.CreateTranslation(transformOrigin);
tileTransform = -originOffset * brush.Transform.Value * originOffset * tileTransform;
}
return new BrushProperties
{
Opacity = (float)brush.Opacity,
Transform = tileTransform.ToDirect2D(),
};
}
private static ExtendMode GetExtendModeX(TileMode tileMode)
{
return (tileMode & TileMode.FlipX) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap;
}
private static ExtendMode GetExtendModeY(TileMode tileMode)
{
return (tileMode & TileMode.FlipY) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap;
}
private BitmapRenderTarget RenderIntermediate(
SharpDX.Direct2D1.RenderTarget target,
BitmapImpl bitmap,
TileBrushCalculator calc)
{
var result = new BitmapRenderTarget(
target,
CompatibleRenderTargetOptions.None,
calc.IntermediateSize.ToSharpDX());
using (var context = new RenderTarget(result).CreateDrawingContext(true))
{
var dpi = new Vector(target.DotsPerInch.Width, target.DotsPerInch.Height);
var rect = new Rect(bitmap.PixelSize.ToSizeWithDpi(dpi));
context.Clear(Colors.Transparent);
context.PushClip(calc.IntermediateClip);
context.Transform = calc.IntermediateTransform;
context.DrawBitmap(bitmap, 1, rect, rect);
context.PopClip();
}
return result;
}
}
}

36
src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs

@ -1,36 +0,0 @@
using System;
using System.IO;
using Avalonia.Platform;
using D2DBitmap = SharpDX.Direct2D1.Bitmap1;
namespace Avalonia.Direct2D1.Media
{
internal abstract class BitmapImpl : IBitmapImpl, IDisposable
{
public abstract Vector Dpi { get; }
public abstract PixelSize PixelSize { get; }
public int Version { get; protected set; } = 1;
public abstract OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target);
public void Save(string fileName, int? quality = null)
{
if (Path.GetExtension(fileName) != ".png")
{
// Yeah, we need to support other formats.
throw new NotSupportedException("Use PNG, stoopid.");
}
using (FileStream s = new FileStream(fileName, FileMode.Create))
{
Save(s, quality);
}
}
public abstract void Save(Stream stream, int? quality = null);
public virtual void Dispose()
{
}
}
}

59
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs

@ -1,59 +0,0 @@
using System;
using System.IO;
using Avalonia.Metadata;
using SharpDX.Direct2D1;
using SharpDX.WIC;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D Bitmap implementation that uses a GPU memory bitmap as its image.
/// </summary>
internal class D2DBitmapImpl : BitmapImpl
{
private readonly Bitmap1 _direct2DBitmap;
/// <summary>
/// Initialize a new instance of the <see cref="BitmapImpl"/> class
/// with a bitmap backed by GPU memory.
/// </summary>
/// <param name="d2DBitmap">The GPU bitmap.</param>
/// <remarks>
/// This bitmap must be either from the same render target,
/// or if the render target is a <see cref="SharpDX.Direct2D1.DeviceContext"/>,
/// the device associated with this context, to be renderable.
/// </remarks>
public D2DBitmapImpl(Bitmap1 d2DBitmap)
{
_direct2DBitmap = d2DBitmap ?? throw new ArgumentNullException(nameof(d2DBitmap));
}
public override Vector Dpi => new Vector(96, 96);
public override PixelSize PixelSize => _direct2DBitmap.PixelSize.ToAvalonia();
public override void Dispose()
{
base.Dispose();
_direct2DBitmap.Dispose();
}
public override OptionalDispose<Bitmap1> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target)
{
return new OptionalDispose<Bitmap1>(_direct2DBitmap, false);
}
public override void Save(Stream stream, int? quality = null)
{
using (var encoder = new PngBitmapEncoder(Direct2D1Platform.ImagingFactory, stream))
using (var frame = new BitmapFrameEncode(encoder))
using (var bitmapSource = _direct2DBitmap.QueryInterface<BitmapSource>())
{
frame.Initialize();
frame.WriteSource(bitmapSource);
frame.Commit();
encoder.Commit();
}
}
}
}
;

77
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@ -1,77 +0,0 @@
using System;
using System.IO;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Utilities;
using SharpDX;
using SharpDX.Direct2D1;
using D2DBitmap = SharpDX.Direct2D1.Bitmap1;
namespace Avalonia.Direct2D1.Media.Imaging
{
internal class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory
{
private readonly BitmapRenderTarget _renderTarget;
public D2DRenderTargetBitmapImpl(BitmapRenderTarget renderTarget)
: base(renderTarget.Bitmap.QueryInterface<Bitmap1>())
{
_renderTarget = renderTarget;
}
public static D2DRenderTargetBitmapImpl CreateCompatible(
SharpDX.Direct2D1.RenderTarget renderTarget,
Size size)
{
var bitmapRenderTarget = new BitmapRenderTarget(
renderTarget,
CompatibleRenderTargetOptions.None,
new Size2F((float)size.Width, (float)size.Height));
return new D2DRenderTargetBitmapImpl(bitmapRenderTarget);
}
public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
{
return new DrawingContextImpl( this, _renderTarget, useScaledDrawing,
null, () => Version++);
}
public bool IsCorrupted => false;
public void Blit(IDrawingContextImpl context) => throw new NotSupportedException();
public bool CanBlit => false;
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return CreateCompatible(_renderTarget, size);
}
public override void Dispose()
{
_renderTarget.Dispose();
}
public override OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target)
{
return new OptionalDispose<D2DBitmap>(_renderTarget.Bitmap.QueryInterface<Bitmap1>(), false);
}
public override void Save(Stream stream, int? quality = null)
{
using (var wic = new WicRenderTargetBitmapImpl(PixelSize, Dpi))
{
using (var dc = wic.CreateDrawingContext(true, null))
{
dc.DrawBitmap(
this,
1,
new Rect(PixelSize.ToSizeWithDpi(Dpi.X)),
new Rect(PixelSize.ToSizeWithDpi(Dpi.X)));
}
wic.Save(stream);
}
}
}
}

278
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -1,278 +0,0 @@
using System;
using System.IO;
using Avalonia.Win32.Interop;
using SharpDX.WIC;
using APixelFormat = Avalonia.Platform.PixelFormat;
using AlphaFormat = Avalonia.Platform.AlphaFormat;
using D2DBitmap = SharpDX.Direct2D1.Bitmap1;
using Avalonia.Platform;
using PixelFormat = SharpDX.WIC.PixelFormat;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A WIC implementation of a <see cref="Avalonia.Media.Imaging.Bitmap"/>.
/// </summary>
internal class WicBitmapImpl : BitmapImpl, IReadableBitmapWithAlphaImpl
{
private readonly BitmapDecoder _decoder;
private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case Avalonia.Media.Imaging.BitmapInterpolationMode.Unspecified:
return BitmapInterpolationMode.Fant;
case Avalonia.Media.Imaging.BitmapInterpolationMode.LowQuality:
return BitmapInterpolationMode.NearestNeighbor;
case Avalonia.Media.Imaging.BitmapInterpolationMode.MediumQuality:
return BitmapInterpolationMode.Fant;
default:
case Avalonia.Media.Imaging.BitmapInterpolationMode.HighQuality:
return BitmapInterpolationMode.HighQualityCubic;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
/// </summary>
/// <param name="fileName">The filename of the bitmap to load.</param>
public WicBitmapImpl(string fileName)
{
using (var decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, fileName, DecodeOptions.CacheOnDemand))
using (var frame = decoder.GetFrame(0))
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, frame, BitmapCreateCacheOption.CacheOnDemand);
Dpi = new Vector(96, 96);
SetFormatFromWic(WicImpl.PixelFormat);
}
}
private WicBitmapImpl(Bitmap bmp)
{
WicImpl = bmp;
Dpi = new Vector(96, 96);
SetFormatFromWic(WicImpl.PixelFormat);
}
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
public WicBitmapImpl(Stream stream)
{
// https://stackoverflow.com/questions/48982749/decoding-image-from-stream-using-wic/48982889#48982889
_decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad);
using var frame = _decoder.GetFrame(0);
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, frame, BitmapCreateCacheOption.CacheOnLoad);
Dpi = new Vector(96, 96);
SetFormatFromWic(WicImpl.PixelFormat);
}
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
/// </summary>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="pixelFormat">Pixel format</param>
/// <param name="alphaFormat">Alpha format.</param>
public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null, AlphaFormat? alphaFormat = null)
{
if (!pixelFormat.HasValue)
{
pixelFormat = APixelFormat.Bgra8888;
}
if (!alphaFormat.HasValue)
{
alphaFormat = Platform.AlphaFormat.Premul;
}
PixelFormat = pixelFormat;
AlphaFormat = alphaFormat;
WicImpl = new Bitmap(
Direct2D1Platform.ImagingFactory,
size.Width,
size.Height,
pixelFormat.Value.ToWic(alphaFormat.Value),
BitmapCreateCacheOption.CacheOnLoad);
Dpi = dpi;
}
public WicBitmapImpl(APixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, size.Width, size.Height, format.ToWic(alphaFormat), BitmapCreateCacheOption.CacheOnDemand);
WicImpl.SetResolution(dpi.X, dpi.Y);
PixelFormat = format;
AlphaFormat = alphaFormat;
Dpi = dpi;
using (var l = WicImpl.Lock(BitmapLockFlags.Write))
{
for (var row = 0; row < size.Height; row++)
{
UnmanagedMethods.CopyMemory(
(l.Data.DataPointer + row * l.Stride),
(data + row * stride),
(UIntPtr)l.Data.Pitch);
}
}
}
public WicBitmapImpl(Stream stream, int decodeSize, bool horizontal, Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode)
{
_decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad);
using var frame = _decoder.GetFrame(0);
// now scale that to the size that we want
var realScale = horizontal ? ((double)frame.Size.Height / frame.Size.Width) : ((double)frame.Size.Width / frame.Size.Height);
PixelSize desired;
if (horizontal)
{
desired = new PixelSize(decodeSize, (int)(realScale * decodeSize));
}
else
{
desired = new PixelSize((int)(realScale * decodeSize), decodeSize);
}
if (frame.Size.Width != desired.Width || frame.Size.Height != desired.Height)
{
using (var scaler = new BitmapScaler(Direct2D1Platform.ImagingFactory))
{
scaler.Initialize(frame, desired.Width, desired.Height, ConvertInterpolationMode(interpolationMode));
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, scaler, BitmapCreateCacheOption.CacheOnLoad);
}
}
else
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, frame, BitmapCreateCacheOption.CacheOnLoad);
}
Dpi = new Vector(96, 96);
}
private void SetFormatFromWic(Guid pixelFormat)
{
if (pixelFormat == SharpDX.WIC.PixelFormat.Format16bppBGR565)
{
PixelFormat = APixelFormat.Rgb565;
AlphaFormat = Platform.AlphaFormat.Premul;
}
else if (pixelFormat == SharpDX.WIC.PixelFormat.Format32bppRGB)
{
PixelFormat = APixelFormat.Rgb32;
AlphaFormat = Platform.AlphaFormat.Premul;
}
else if (pixelFormat == PixelFormats.Rgba8888.ToWic(Platform.AlphaFormat.Premul))
{
PixelFormat = APixelFormat.Rgba8888;
AlphaFormat = Platform.AlphaFormat.Premul;
}
else if (pixelFormat == PixelFormats.Rgba8888.ToWic(Platform.AlphaFormat.Opaque))
{
PixelFormat = APixelFormat.Rgba8888;
AlphaFormat = Platform.AlphaFormat.Opaque;
}
else if (pixelFormat == PixelFormats.Bgra8888.ToWic(Platform.AlphaFormat.Premul))
{
PixelFormat = APixelFormat.Bgra8888;
AlphaFormat = Platform.AlphaFormat.Premul;
}
else if (pixelFormat == PixelFormats.Bgra8888.ToWic(Platform.AlphaFormat.Opaque))
{
PixelFormat = APixelFormat.Bgra8888;
AlphaFormat = Platform.AlphaFormat.Opaque;
}
}
public override Vector Dpi { get; }
public override PixelSize PixelSize => WicImpl.Size.ToAvalonia();
public APixelFormat? PixelFormat { get; private set; }
public AlphaFormat? AlphaFormat { get; private set; }
public override void Dispose()
{
WicImpl.Dispose();
_decoder?.Dispose();
}
/// <summary>
/// Gets the WIC implementation of the bitmap.
/// </summary>
public Bitmap WicImpl { get; }
/// <summary>
/// Gets a Direct2D bitmap to use on the specified render target.
/// </summary>
/// <param name="renderTarget">The render target.</param>
/// <returns>The Direct2D bitmap.</returns>
public override OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget renderTarget)
{
using var converter = new FormatConverter(Direct2D1Platform.ImagingFactory);
converter.Initialize(WicImpl, SharpDX.WIC.PixelFormat.Format32bppPBGRA);
var d2dBitmap = D2DBitmap.FromWicBitmap(renderTarget, converter).QueryInterface<D2DBitmap>();
return new OptionalDispose<D2DBitmap>(d2dBitmap, true);
}
public override void Save(Stream stream, int? quality = null)
{
using (var encoder = new PngBitmapEncoder(Direct2D1Platform.ImagingFactory, stream))
using (var frame = new BitmapFrameEncode(encoder))
{
frame.Initialize();
frame.WriteSource(WicImpl);
frame.Commit();
encoder.Commit();
}
}
class LockedBitmap : ILockedFramebuffer
{
private readonly WicBitmapImpl _parent;
private readonly BitmapLock _lock;
private readonly APixelFormat _format;
public LockedBitmap(WicBitmapImpl parent, BitmapLock l, APixelFormat format)
{
_parent = parent;
_lock = l;
_format = format;
}
public void Dispose()
{
_lock.Dispose();
_parent.Version++;
}
public IntPtr Address => _lock.Data.DataPointer;
public PixelSize Size => _lock.Size.ToAvalonia();
public int RowBytes => _lock.Stride;
public Vector Dpi => _parent.Dpi;
public APixelFormat Format => _format;
}
APixelFormat? IReadableBitmapImpl.Format => PixelFormat;
public ILockedFramebuffer Lock() =>
new LockedBitmap(this, WicImpl.Lock(BitmapLockFlags.Write), PixelFormat.Value);
}
}

55
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs

@ -1,55 +0,0 @@
using System;
using Avalonia.Platform;
using Avalonia.Rendering;
using SharpDX.Direct2D1;
using RenderTargetProperties = SharpDX.Direct2D1.RenderTargetProperties;
namespace Avalonia.Direct2D1.Media
{
internal class WicRenderTargetBitmapImpl : WicBitmapImpl, IDrawingContextLayerImpl
{
private readonly WicRenderTarget _renderTarget;
public WicRenderTargetBitmapImpl(
PixelSize size,
Vector dpi,
Platform.PixelFormat? pixelFormat = null)
: base(size, dpi, pixelFormat)
{
var props = new RenderTargetProperties
{
DpiX = (float)dpi.X,
DpiY = (float)dpi.Y,
};
_renderTarget = new WicRenderTarget(
Direct2D1Platform.Direct2D1Factory,
WicImpl,
props);
}
public override void Dispose()
{
_renderTarget.Dispose();
base.Dispose();
}
public virtual IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
=> CreateDrawingContext(useScaledDrawing, null);
public bool IsCorrupted => false;
public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing, Action finishedCallback)
{
return new DrawingContextImpl(null, _renderTarget, useScaledDrawing, finishedCallback: () =>
{
Version++;
finishedCallback?.Invoke();
});
}
public void Blit(IDrawingContextImpl context) => throw new NotSupportedException();
public bool CanBlit => false;
}
}

34
src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs

@ -1,34 +0,0 @@
using System;
using System.IO;
using Avalonia.Platform;
using SharpDX.WIC;
using PixelFormat = Avalonia.Platform.PixelFormat;
namespace Avalonia.Direct2D1.Media.Imaging
{
internal class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl
{
public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal,
Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode)
: base(stream, decodeSize, horizontal, interpolationMode)
{
}
public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat, AlphaFormat? alphaFormat)
: base(size, dpi, pixelFormat, alphaFormat)
{
}
public WriteableWicBitmapImpl(Stream stream)
: base(stream)
{
}
public WriteableWicBitmapImpl(string fileName)
: base(fileName)
{
}
public PixelFormat? Format => PixelFormat;
}
}

24
src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs

@ -1,24 +0,0 @@
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D implementation of a <see cref="Avalonia.Media.LineGeometry"/>.
/// </summary>
internal class LineGeometryImpl : StreamGeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
public LineGeometryImpl(Point p1, Point p2)
{
using (var sink = ((PathGeometry)Geometry).Open())
{
sink.BeginFigure(p1.ToSharpDX(), FigureBegin.Hollow);
sink.AddLine(p2.ToSharpDX());
sink.EndFigure(FigureEnd.Open);
sink.Close();
}
}
}
}

48
src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs

@ -1,48 +0,0 @@
using System.Linq;
using Avalonia.Media;
namespace Avalonia.Direct2D1.Media
{
internal class LinearGradientBrushImpl : BrushImpl
{
public LinearGradientBrushImpl(
ILinearGradientBrush brush,
SharpDX.Direct2D1.RenderTarget target,
Rect destinationRect)
{
if (brush.GradientStops.Count == 0)
{
return;
}
var gradientStops = brush.GradientStops.Select(s => new SharpDX.Direct2D1.GradientStop
{
Color = s.Color.ToDirect2D(),
Position = (float)s.Offset
}).ToArray();
var startPoint = brush.StartPoint.ToPixels(destinationRect);
var endPoint = brush.EndPoint.ToPixels(destinationRect);
using (var stops = new SharpDX.Direct2D1.GradientStopCollection(
target,
gradientStops,
brush.SpreadMethod.ToDirect2D()))
{
PlatformBrush = new SharpDX.Direct2D1.LinearGradientBrush(
target,
new SharpDX.Direct2D1.LinearGradientBrushProperties
{
StartPoint = startPoint.ToSharpDX(),
EndPoint = endPoint.ToSharpDX()
},
new SharpDX.Direct2D1.BrushProperties
{
Opacity = (float)brush.Opacity,
Transform = PrimitiveExtensions.Matrix3x2Identity,
},
stops);
}
}
}
}

53
src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs

@ -1,53 +0,0 @@
using System.Linq;
using Avalonia.Media;
namespace Avalonia.Direct2D1.Media
{
internal class RadialGradientBrushImpl : BrushImpl
{
public RadialGradientBrushImpl(
IRadialGradientBrush brush,
SharpDX.Direct2D1.RenderTarget target,
Rect destinationRect)
{
if (brush.GradientStops.Count == 0)
{
return;
}
var gradientStops = brush.GradientStops.Select(s => new SharpDX.Direct2D1.GradientStop
{
Color = s.Color.ToDirect2D(),
Position = (float)s.Offset
}).ToArray();
var centerPoint = brush.Center.ToPixels(destinationRect);
var gradientOrigin = brush.GradientOrigin.ToPixels(destinationRect) - centerPoint;
var radiusX = brush.RadiusX.ToValue(destinationRect.Width);
var radiusY = brush.RadiusY.ToValue(destinationRect.Height);
using (var stops = new SharpDX.Direct2D1.GradientStopCollection(
target,
gradientStops,
brush.SpreadMethod.ToDirect2D()))
{
PlatformBrush = new SharpDX.Direct2D1.RadialGradientBrush(
target,
new SharpDX.Direct2D1.RadialGradientBrushProperties
{
Center = centerPoint.ToSharpDX(),
GradientOriginOffset = gradientOrigin.ToSharpDX(),
RadiusX = (float)radiusX,
RadiusY = (float)radiusY
},
new SharpDX.Direct2D1.BrushProperties
{
Opacity = (float)brush.Opacity,
Transform = PrimitiveExtensions.Matrix3x2Identity,
},
stops);
}
}
}
}

23
src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs

@ -1,23 +0,0 @@
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D implementation of a <see cref="Avalonia.Media.RectangleGeometry"/>.
/// </summary>
internal class RectangleGeometryImpl : GeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
public RectangleGeometryImpl(Rect rect)
: base(CreateGeometry(rect))
{
}
private static Geometry CreateGeometry(Rect rect)
{
return new RectangleGeometry(Direct2D1Platform.Direct2D1Factory, rect.ToDirect2D());
}
}
}

37
src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs

@ -1,37 +0,0 @@
using Avalonia.Media;
namespace Avalonia.Direct2D1.Media
{
internal class SolidColorBrushImpl : BrushImpl
{
public SolidColorBrushImpl(ISolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target)
{
PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush(
target,
brush?.Color.ToDirect2D() ?? new SharpDX.Mathematics.Interop.RawColor4(),
new SharpDX.Direct2D1.BrushProperties
{
Opacity = brush != null ? (float)brush.Opacity : 1.0f,
Transform = target.Transform
}
);
}
/// <summary>
/// Direct2D has no ConicGradient implementation so fall back to a solid colour brush based on
/// the first gradient stop.
/// </summary>
public SolidColorBrushImpl(IConicGradientBrush brush, SharpDX.Direct2D1.DeviceContext target)
{
PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush(
target,
brush?.GradientStops[0].Color.ToDirect2D() ?? new SharpDX.Mathematics.Interop.RawColor4(),
new SharpDX.Direct2D1.BrushProperties
{
Opacity = brush != null ? (float)brush.Opacity : 1.0f,
Transform = target.Transform
}
);
}
}
}

95
src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs

@ -1,95 +0,0 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Platform;
using SharpDX.Direct2D1;
using D2D = SharpDX.Direct2D1;
using SweepDirection = SharpDX.Direct2D1.SweepDirection;
namespace Avalonia.Direct2D1.Media
{
internal class StreamGeometryContextImpl : IStreamGeometryContextImpl
{
private readonly GeometrySink _sink;
public StreamGeometryContextImpl(GeometrySink sink)
{
_sink = sink;
}
public void ArcTo(
Point point,
Size size,
double rotationAngle,
bool isLargeArc,
Avalonia.Media.SweepDirection sweepDirection)
{
_sink.AddArc(new D2D.ArcSegment
{
Point = point.ToSharpDX(),
Size = size.ToSharpDX(),
RotationAngle = (float)rotationAngle,
ArcSize = isLargeArc ? ArcSize.Large : ArcSize.Small,
SweepDirection = (SweepDirection)sweepDirection,
});
}
public void BeginFigure(Point startPoint, bool isFilled)
{
_sink.BeginFigure(startPoint.ToSharpDX(), isFilled ? FigureBegin.Filled : FigureBegin.Hollow);
}
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
_sink.AddBezier(new D2D.BezierSegment
{
Point1 = point1.ToSharpDX(),
Point2 = point2.ToSharpDX(),
Point3 = point3.ToSharpDX(),
});
}
public void QuadraticBezierTo(Point control, Point dest)
{
_sink.AddQuadraticBezier(new D2D.QuadraticBezierSegment
{
Point1 = control.ToSharpDX(),
Point2 = dest.ToSharpDX()
});
}
public void LineTo(Point point)
{
_sink.AddLine(point.ToSharpDX());
}
public void EndFigure(bool isClosed)
{
_sink.EndFigure(isClosed ? FigureEnd.Closed : FigureEnd.Open);
}
public void SetFillRule(FillRule fillRule)
{
_sink.SetFillMode(fillRule == FillRule.EvenOdd ? FillMode.Alternate : FillMode.Winding);
}
public void Dispose()
{
// Put a catch around sink.Close as it may throw if there were an error e.g. parsing a path.
try
{
_sink.Close();
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)?.Log(
this,
"GeometrySink.Close exception: {Exception}",
ex);
}
_sink.Dispose();
}
}
}

52
src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs

@ -1,52 +0,0 @@
using Avalonia.Platform;
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
/// <summary>
/// A Direct2D implementation of a <see cref="Avalonia.Media.StreamGeometry"/>.
/// </summary>
internal class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
public StreamGeometryImpl()
: base(CreateGeometry())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
/// <param name="geometry">An existing Direct2D <see cref="PathGeometry"/>.</param>
public StreamGeometryImpl(PathGeometry geometry)
: base(geometry)
{
}
/// <inheritdoc/>
public IStreamGeometryImpl Clone()
{
var result = new PathGeometry(Direct2D1Platform.Direct2D1Factory);
using (var sink = result.Open())
{
((PathGeometry)Geometry).Stream(sink);
sink.Close();
}
return new StreamGeometryImpl(result);
}
/// <inheritdoc/>
public IStreamGeometryContextImpl Open()
{
return new StreamGeometryContextImpl(((PathGeometry)Geometry).Open());
}
private static Geometry CreateGeometry()
{
return new PathGeometry(Direct2D1Platform.Direct2D1Factory);
}
}
}

204
src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs

@ -1,204 +0,0 @@
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Globalization;
using System.Runtime.InteropServices;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using HarfBuzzSharp;
using Buffer = HarfBuzzSharp.Buffer;
using GlyphInfo = HarfBuzzSharp.GlyphInfo;
namespace Avalonia.Direct2D1.Media
{
internal class TextShaperImpl : ITextShaperImpl
{
private static readonly ConcurrentDictionary<int, Language> s_cachedLanguage = new();
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
{
var textSpan = text.Span;
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidiLevel;
var culture = options.Culture;
using (var buffer = new Buffer())
{
// HarfBuzz needs the surrounding characters to correctly shape the text
var containingText = GetContainingMemory(text, out var start, out var length).Span;
buffer.AddUtf16(containingText, start, length);
MergeBreakPair(buffer);
buffer.GuessSegmentProperties();
buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft;
var usedCulture = culture ?? CultureInfo.CurrentCulture;
buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, _ => new Language(usedCulture));
var font = ((GlyphTypefaceImpl)typeface).Font;
font.Shape(buffer, GetFeatures(options));
if (buffer.Direction == Direction.RightToLeft)
{
buffer.Reverse();
}
font.GetScale(out var scaleX, out _);
var textScale = fontRenderingEmSize / scaleX;
var bufferLength = buffer.Length;
var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel);
var glyphInfos = buffer.GetGlyphInfoSpan();
var glyphPositions = buffer.GetGlyphPositionSpan();
for (var i = 0; i < bufferLength; i++)
{
var sourceInfo = glyphInfos[i];
var glyphIndex = (ushort)sourceInfo.Codepoint;
var glyphCluster = (int)(sourceInfo.Cluster);
var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale) + options.LetterSpacing;
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
if (glyphCluster < containingText.Length && containingText[glyphCluster] == '\t')
{
glyphIndex = typeface.GetGlyph(' ');
glyphAdvance = options.IncrementalTabWidth > 0 ?
options.IncrementalTabWidth :
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
}
shapedBuffer[i] = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
}
return shapedBuffer;
}
}
private static void MergeBreakPair(Buffer buffer)
{
var length = buffer.Length;
var glyphInfos = buffer.GetGlyphInfoSpan();
var second = glyphInfos[length - 1];
if (!new Codepoint(second.Codepoint).IsBreakChar)
{
return;
}
if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n')
{
var first = glyphInfos[length - 2];
first.Codepoint = '\u200C';
second.Codepoint = '\u200C';
second.Cluster = first.Cluster;
unsafe
{
fixed (GlyphInfo* p = &glyphInfos[length - 2])
{
*p = first;
}
fixed (GlyphInfo* p = &glyphInfos[length - 1])
{
*p = second;
}
}
}
else
{
second.Codepoint = '\u200C';
unsafe
{
fixed (GlyphInfo* p = &glyphInfos[length - 1])
{
*p = second;
}
}
}
}
private static Vector GetGlyphOffset(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale)
{
var position = glyphPositions[index];
var offsetX = position.XOffset * textScale;
var offsetY = -position.YOffset * textScale;
return new Vector(offsetX, offsetY);
}
private static double GetGlyphAdvance(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale)
{
// Depends on direction of layout
// glyphPositions[index].YAdvance * textScale;
return glyphPositions[index].XAdvance * textScale;
}
private static ReadOnlyMemory<char> GetContainingMemory(ReadOnlyMemory<char> memory, out int start, out int length)
{
if (MemoryMarshal.TryGetString(memory, out var containingString, out start, out length))
{
return containingString.AsMemory();
}
if (MemoryMarshal.TryGetArray(memory, out var segment))
{
start = segment.Offset;
length = segment.Count;
return segment.Array.AsMemory();
}
if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<char> memoryManager, out start, out length))
{
return memoryManager.Memory;
}
// should never happen
throw new InvalidOperationException("Memory not backed by string, array or manager");
}
private static Feature[] GetFeatures(TextShaperOptions options)
{
if (options.FontFeatures is null || options.FontFeatures.Count == 0)
{
return Array.Empty<Feature>();
}
var features = new Feature[options.FontFeatures.Count];
for (var i = 0; i < options.FontFeatures.Count; i++)
{
var fontFeature = options.FontFeatures[i];
features[i] = new Feature(
Tag.Parse(fontFeature.Tag),
(uint)fontFeature.Value,
(uint)fontFeature.Start,
(uint)fontFeature.End);
}
return features;
}
}
}

26
src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs

@ -1,26 +0,0 @@
using Avalonia.Platform;
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
internal class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
/// </summary>
/// <param name="source">The source geometry.</param>
/// <param name="geometry">An existing Direct2D <see cref="TransformedGeometry"/>.</param>
public TransformedGeometryImpl(TransformedGeometry geometry, GeometryImpl source)
: base(geometry)
{
SourceGeometry = source;
}
public IGeometryImpl SourceGeometry { get; }
/// <inheritdoc/>
public Matrix Transform => ((TransformedGeometry)Geometry).Transform.ToAvalonia();
protected override Geometry GetSourceGeometry() => ((TransformedGeometry)Geometry).SourceGeometry;
}
}

22
src/Windows/Avalonia.Direct2D1/OptionalDispose.cs

@ -1,22 +0,0 @@
using System;
namespace Avalonia.Direct2D1
{
internal readonly record struct OptionalDispose<T> : IDisposable where T : IDisposable
{
private readonly bool _dispose;
public OptionalDispose(T value, bool dispose)
{
Value = value;
_dispose = dispose;
}
public T Value { get; }
public void Dispose()
{
if (_dispose) Value?.Dispose();
}
}
}

226
src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs

@ -1,226 +0,0 @@
using System;
using System.Linq;
using Avalonia.Platform;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using DWrite = SharpDX.DirectWrite;
namespace Avalonia.Direct2D1
{
internal static class PrimitiveExtensions
{
/// <summary>
/// The value for which all absolute numbers smaller than are considered equal to zero.
/// </summary>
public const float ZeroTolerance = 1e-6f; // Value a 8x higher than 1.19209290E-07F
public static readonly RawRectangleF RectangleInfinite;
/// <summary>
/// Gets the identity matrix.
/// </summary>
/// <value>The identity matrix.</value>
public readonly static RawMatrix3x2 Matrix3x2Identity = new RawMatrix3x2 { M11 = 1, M12 = 0, M21 = 0, M22 = 1, M31 = 0, M32 = 0 };
static PrimitiveExtensions()
{
RectangleInfinite = new RawRectangleF
{
Left = float.NegativeInfinity,
Top = float.NegativeInfinity,
Right = float.PositiveInfinity,
Bottom = float.PositiveInfinity
};
}
public static Rect ToAvalonia(this RawRectangleF r)
{
return new Rect(new Point(r.Left, r.Top), new Point(r.Right, r.Bottom));
}
public static PixelSize ToAvalonia(this Size2 p) => new PixelSize(p.Width, p.Height);
public static Vector ToAvaloniaVector(this Size2F p) => new Vector(p.Width, p.Height);
public static RawRectangleF ToSharpDX(this Rect r)
{
return new RawRectangleF((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom);
}
public static RawVector2 ToSharpDX(this Point p)
{
return new RawVector2 { X = (float)p.X, Y = (float)p.Y };
}
public static Size2F ToSharpDX(this Size p)
{
return new Size2F((float)p.Width, (float)p.Height);
}
public static ExtendMode ToDirect2D(this Avalonia.Media.GradientSpreadMethod spreadMethod)
{
if (spreadMethod == Avalonia.Media.GradientSpreadMethod.Pad)
return ExtendMode.Clamp;
else if (spreadMethod == Avalonia.Media.GradientSpreadMethod.Reflect)
return ExtendMode.Mirror;
else
return ExtendMode.Wrap;
}
public static SharpDX.Direct2D1.LineJoin ToDirect2D(this Avalonia.Media.PenLineJoin lineJoin)
{
if (lineJoin == Avalonia.Media.PenLineJoin.Round)
return LineJoin.Round;
else if (lineJoin == Avalonia.Media.PenLineJoin.Miter)
return LineJoin.Miter;
else
return LineJoin.Bevel;
}
public static SharpDX.Direct2D1.CapStyle ToDirect2D(this Avalonia.Media.PenLineCap lineCap)
{
if (lineCap == Avalonia.Media.PenLineCap.Flat)
return CapStyle.Flat;
else if (lineCap == Avalonia.Media.PenLineCap.Round)
return CapStyle.Round;
else if (lineCap == Avalonia.Media.PenLineCap.Square)
return CapStyle.Square;
else
return CapStyle.Triangle;
}
public static Guid ToWic(this Platform.PixelFormat format, Platform.AlphaFormat alphaFormat)
{
bool isPremul = alphaFormat == AlphaFormat.Premul;
if (format == Platform.PixelFormat.Rgb565)
return SharpDX.WIC.PixelFormat.Format16bppBGR565;
if (format == Platform.PixelFormat.Bgra8888)
return isPremul ? SharpDX.WIC.PixelFormat.Format32bppPBGRA : SharpDX.WIC.PixelFormat.Format32bppBGRA;
if (format == Platform.PixelFormat.Rgba8888)
return isPremul ? SharpDX.WIC.PixelFormat.Format32bppPRGBA : SharpDX.WIC.PixelFormat.Format32bppRGBA;
throw new ArgumentException("Unknown pixel format");
}
/// <summary>
/// Converts a pen to a Direct2D stroke style.
/// </summary>
/// <param name="pen">The pen to convert.</param>
/// <param name="renderTarget">The render target.</param>
/// <returns>The Direct2D brush.</returns>
public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.IPen pen, SharpDX.Direct2D1.RenderTarget renderTarget)
{
return pen.ToDirect2DStrokeStyle(Direct2D1Platform.Direct2D1Factory);
}
/// <summary>
/// Converts a pen to a Direct2D stroke style.
/// </summary>
/// <param name="pen">The pen to convert.</param>
/// <param name="factory">The factory associated with this resource.</param>
/// <returns>The Direct2D brush.</returns>
public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.IPen pen, Factory factory)
{
var d2dLineCap = pen.LineCap.ToDirect2D();
var properties = new StrokeStyleProperties
{
DashStyle = DashStyle.Solid,
MiterLimit = (float)pen.MiterLimit,
LineJoin = pen.LineJoin.ToDirect2D(),
StartCap = d2dLineCap,
EndCap = d2dLineCap,
DashCap = d2dLineCap
};
float[] dashes = null;
if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0)
{
properties.DashStyle = DashStyle.Custom;
properties.DashOffset = (float)pen.DashStyle.Offset;
dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray();
}
dashes = dashes ?? Array.Empty<float>();
return new StrokeStyle(factory, properties, dashes);
}
/// <summary>
/// Converts a Avalonia <see cref="Avalonia.Media.Color"/> to Direct2D.
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The Direct2D color.</returns>
public static RawColor4 ToDirect2D(this Avalonia.Media.Color color)
{
return new RawColor4(
(float)(color.R / 255.0),
(float)(color.G / 255.0),
(float)(color.B / 255.0),
(float)(color.A / 255.0));
}
/// <summary>
/// Converts a Avalonia <see cref="Avalonia.Matrix"/> to a Direct2D <see cref="RawMatrix3x2"/>
/// </summary>
/// <param name="matrix">The <see cref="Matrix"/>.</param>
/// <returns>The <see cref="RawMatrix3x2"/>.</returns>
public static RawMatrix3x2 ToDirect2D(this Matrix matrix)
{
return new RawMatrix3x2
{
M11 = (float)matrix.M11,
M12 = (float)matrix.M12,
M21 = (float)matrix.M21,
M22 = (float)matrix.M22,
M31 = (float)matrix.M31,
M32 = (float)matrix.M32
};
}
/// <summary>
/// Converts a Direct2D <see cref="RawMatrix3x2"/> to a Avalonia <see cref="Avalonia.Matrix"/>.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>a <see cref="Avalonia.Matrix"/>.</returns>
public static Matrix ToAvalonia(this RawMatrix3x2 matrix)
{
return new Matrix(
matrix.M11,
matrix.M12,
matrix.M21,
matrix.M22,
matrix.M31,
matrix.M32);
}
/// <summary>
/// Converts a Avalonia <see cref="Rect"/> to a Direct2D <see cref="RawRectangleF"/>
/// </summary>
/// <param name="rect">The <see cref="Rect"/>.</param>
/// <returns>The <see cref="RawRectangleF"/>.</returns>
public static RawRectangleF ToDirect2D(this Rect rect)
{
return new RawRectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Right,
(float)rect.Bottom);
}
public static DWrite.TextAlignment ToDirect2D(this Avalonia.Media.TextAlignment alignment)
{
switch (alignment)
{
case Avalonia.Media.TextAlignment.Left:
return DWrite.TextAlignment.Leading;
case Avalonia.Media.TextAlignment.Center:
return DWrite.TextAlignment.Center;
case Avalonia.Media.TextAlignment.Right:
return DWrite.TextAlignment.Trailing;
default:
throw new InvalidOperationException("Invalid TextAlignment");
}
}
}
}

45
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@ -1,45 +0,0 @@
using Avalonia.Direct2D1.Media;
using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.Direct2D1
{
internal class RenderTarget : IRenderTarget, ILayerFactory
{
/// <summary>
/// The render target.
/// </summary>
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
/// <summary>
/// Initializes a new instance of the <see cref="RenderTarget"/> class.
/// </summary>
/// <param name="renderTarget">The render target.</param>
public RenderTarget(SharpDX.Direct2D1.RenderTarget renderTarget)
{
_renderTarget = renderTarget;
}
/// <summary>
/// Creates a drawing context for a rendering session.
/// </summary>
/// <returns>An <see cref="Avalonia.Platform.IDrawingContextImpl"/>.</returns>
public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
{
return new DrawingContextImpl(this, _renderTarget, useScaledDrawing);
}
public bool IsCorrupted => false;
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return D2DRenderTargetBitmapImpl.CreateCompatible(_renderTarget, size);
}
public void Dispose()
{
_renderTarget.Dispose();
}
}
}

124
src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

@ -1,124 +0,0 @@
using Avalonia.Direct2D1.Media;
using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
namespace Avalonia.Direct2D1
{
internal abstract class SwapChainRenderTarget : IRenderTarget, ILayerFactory
{
private Size2 _savedSize;
private Size2F _savedDpi;
private DeviceContext _deviceContext;
private SwapChain1 _swapChain;
/// <summary>
/// Creates a drawing context for a rendering session.
/// </summary>
/// <returns>An <see cref="Avalonia.Platform.IDrawingContextImpl"/>.</returns>
public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing)
{
var size = GetWindowSize();
var dpi = GetWindowDpi();
if (size != _savedSize || dpi != _savedDpi)
{
_savedSize = size;
_savedDpi = dpi;
Resize();
}
return new DrawingContextImpl(this, _deviceContext, useScaledDrawing, _swapChain);
}
public bool IsCorrupted => false;
public IDrawingContextLayerImpl CreateLayer(Size size)
{
if (_deviceContext == null)
{
CreateDeviceContext();
}
return D2DRenderTargetBitmapImpl.CreateCompatible(_deviceContext, size);
}
public void Dispose()
{
_deviceContext?.Dispose();
_swapChain?.Dispose();
}
private void Resize()
{
_deviceContext?.Dispose();
_deviceContext = null;
_swapChain?.ResizeBuffers(0, 0, 0, Format.Unknown, SwapChainFlags.None);
CreateDeviceContext();
}
private void CreateSwapChain()
{
var swapChainDescription = new SwapChainDescription1
{
Width = _savedSize.Width,
Height = _savedSize.Height,
Format = Format.B8G8R8A8_UNorm,
SampleDescription = new SampleDescription
{
Count = 1,
Quality = 0,
},
Usage = Usage.RenderTargetOutput,
BufferCount = 1,
SwapEffect = SwapEffect.Discard,
};
using (var dxgiAdapter = Direct2D1Platform.DxgiDevice.Adapter)
using (var dxgiFactory = dxgiAdapter.GetParent<SharpDX.DXGI.Factory2>())
{
_swapChain = CreateSwapChain(dxgiFactory, swapChainDescription);
}
}
private void CreateDeviceContext()
{
_deviceContext = new DeviceContext(Direct2D1Platform.Direct2D1Device, DeviceContextOptions.None) { DotsPerInch = _savedDpi };
if (_swapChain == null)
{
CreateSwapChain();
}
using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0))
using (var d2dBackBuffer = new Bitmap1(
_deviceContext,
dxgiBackBuffer,
new BitmapProperties1(
new SharpDX.Direct2D1.PixelFormat
{
AlphaMode = SharpDX.Direct2D1.AlphaMode.Premultiplied,
Format = Format.B8G8R8A8_UNorm
},
_savedSize.Width,
_savedSize.Height,
BitmapOptions.Target | BitmapOptions.CannotDraw)))
{
_deviceContext.Target = d2dBackBuffer;
}
}
protected abstract SwapChain1 CreateSwapChain(SharpDX.DXGI.Factory2 dxgiFactory, SwapChainDescription1 swapChainDesc);
protected abstract Size2F GetWindowDpi();
protected abstract Size2 GetWindowSize();
}
}

13
src/Windows/Avalonia.Direct2D1/Utils/DebugUtils.cs

@ -1,13 +0,0 @@
using Avalonia.Direct2D1.Media.Imaging;
namespace Avalonia.Direct2D1.Utils
{
internal static class DebugUtils
{
public static void Save(SharpDX.Direct2D1.BitmapRenderTarget bitmap, string filename)
{
var rtb = new D2DRenderTargetBitmapImpl(bitmap);
rtb.Save(filename);
}
}
}

1
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -38,7 +38,6 @@
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="Avalonia.Win32.Interoperability, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Direct2D1, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
<ItemGroup>
<!-- By default, any projects supports Windows, Linux, MacOS platforms. -->

28
tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj

@ -1,28 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<DefineConstants>$(DefineConstants);AVALONIA_D2D</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Avalonia.RenderTests\**\*.cs" />
<Compile Remove="..\Avalonia.RenderTests\CrossTests\**\*.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\Avalonia.RenderTests\**\*.ttf" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
</ItemGroup>
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\XUnit.props" />
<Import Project="..\..\build\ImageSharp.props" />
<Import Project="..\..\build\SharedVersion.props" />
</Project>

20
tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
</PropertyGroup>
<Import Project="..\..\build\UnitTests.NetCore.targets" />
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\XUnit.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
<Import Project="..\..\build\SharedVersion.props" />
<ItemGroup>
<EmbeddedResource Include="..\Avalonia.RenderTests\**\*.ttf" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
</ItemGroup>
</Project>

69
tests/Avalonia.Direct2D1.UnitTests/Controls/Shapes/PathTests.cs

@ -1,69 +0,0 @@
using Avalonia.Controls.Shapes;
using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
namespace Avalonia.Direct2D1.UnitTests.Controls.Shapes
{
public class PathTests
{
private static readonly RectComparer Compare = new RectComparer();
[Fact]
public void Should_Measure_Expander_Triangle_Correctly()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var target = new Path
{
Data = StreamGeometry.Parse("M 0 2 L 4 6 L 0 10 Z"),
StrokeThickness = 0,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
UseLayoutRounding = false,
};
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(new Rect(0, 0, 4, 10), target.Bounds, Compare);
}
}
[Fact]
public void Should_Measure_Expander_Triangle_With_Stroke_Correctly()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var target = new Path
{
Data = StreamGeometry.Parse("M 0 2 L 4 6 L 0 10 Z"),
Stroke = Brushes.Black,
StrokeThickness = 2,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
UseLayoutRounding = false,
};
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
// Measured geometry with stroke of 2px is:
//
// {-1, -0.414, 6.414, 12.828} (see GeometryTests)
//
// With origin at 0,0 the bounds should equal:
//
// Assert.Equal(new Rect(0, 0, 5.414, 12.414), target.Bounds, compare);
//
// However Path.Measure doesn't correctly handle strokes currently, so testing for
// the (incorrect) current output for now...
Assert.Equal(new Rect(0, 0, 4, 10), target.Bounds, Compare);
}
}
}
}

89
tests/Avalonia.Direct2D1.UnitTests/Media/FontManagerImplTests.cs

@ -1,89 +0,0 @@
using Avalonia.Direct2D1.Media;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Direct2D1.UnitTests.Media
{
public class FontManagerImplTests
{
private static string s_fontUri = "resm:Avalonia.Direct2D1.UnitTests.Assets?assembly=Avalonia.Direct2D1.UnitTests#Noto Mono";
[Fact]
public void Should_Create_Typeface_From_Fallback()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var typeface = new Typeface(new FontFamily("A, B, Arial"));
var glyphTypeface = typeface.GlyphTypeface;
Assert.Equal("Arial", glyphTypeface.FamilyName);
}
}
[Fact]
public void Should_Create_Typeface_From_Fallback_Bold()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var typeface = new Typeface(new FontFamily("A, B, Arial"), weight: FontWeight.Bold);
var glyphTypeface = typeface.GlyphTypeface;
Assert.Equal("Arial", glyphTypeface.FamilyName);
Assert.Equal(FontWeight.Bold, glyphTypeface.Weight);
Assert.Equal(FontStyle.Normal, glyphTypeface.Style);
}
}
[Fact]
public void Should_Create_Typeface_For_Unknown_Font()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var glyphTypeface = new Typeface(new FontFamily("Unknown")).GlyphTypeface;
var defaultName = FontManager.Current.DefaultFontFamily.Name;
Assert.Equal(defaultName, glyphTypeface.FamilyName);
}
}
[Fact]
public void Should_Load_Typeface_From_Resource()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Direct2D1Platform.Initialize();
var fontManager = new FontManagerImpl();
var glyphTypeface = new Typeface(s_fontUri).GlyphTypeface;
Assert.Equal("Noto Mono", glyphTypeface.FamilyName);
}
}
[Fact]
public void Should_Load_Nearest_Matching_Font()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Direct2D1Platform.Initialize();
var glyphTypeface = new Typeface(s_fontUri, FontStyle.Italic, FontWeight.Black).GlyphTypeface;
Assert.Equal("Noto Mono", glyphTypeface.FamilyName);
}
}
}
}

37
tests/Avalonia.Direct2D1.UnitTests/Media/GeometryTests.cs

@ -1,37 +0,0 @@
using Avalonia.Media;
using Xunit;
namespace Avalonia.Direct2D1.UnitTests.Media
{
public class GeometryTests
{
private static readonly RectComparer Compare = new RectComparer();
[Fact]
public void Should_Measure_Expander_Triangle_Correctly()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var target = StreamGeometry.Parse("M 0 2 L 4 6 L 0 10 Z");
Assert.Equal(new Rect(0, 2, 4, 8), target.Bounds, Compare);
}
}
[Fact]
public void Should_Measure_Expander_Triangle_With_Stroke_Correctly()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var target = StreamGeometry.Parse("M 0 2 L 4 6 L 0 10 Z");
var pen = new Pen(Brushes.Black, 2);
Assert.Equal(new Rect(-1, -0.414, 6.414, 12.828), target.GetRenderBounds(pen), Compare);
}
}
}
}

5
tests/Avalonia.Direct2D1.UnitTests/Properties/AssemblyInfo.cs

@ -1,5 +0,0 @@
using System.Reflection;
using Xunit;
// Don't run tests in parallel.
[assembly: CollectionBehavior(DisableTestParallelization = true)]

21
tests/Avalonia.Direct2D1.UnitTests/RectComparer.cs

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
namespace Avalonia.Direct2D1.UnitTests
{
public class RectComparer : IEqualityComparer<Rect>
{
public bool Equals(Rect a, Rect b)
{
return Math.Round(a.X, 3) == Math.Round(b.X, 3) &&
Math.Round(a.Y, 3) == Math.Round(b.Y, 3) &&
Math.Round(a.Width, 3) == Math.Round(b.Width, 3) &&
Math.Round(a.Height, 3) == Math.Round(b.Height, 3);
}
public int GetHashCode(Rect obj)
{
throw new NotImplementedException();
}
}
}

6
tests/Avalonia.RenderTests/Controls/AdornerTests.cs

@ -6,11 +6,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests;
#else
namespace Avalonia.Direct2D1.RenderTests.Controls;
#endif
public class AdornerTests : TestBase
{
@ -85,4 +81,4 @@ public class AdornerTests : TestBase
testName: "Focus_Adorner_Is_Properly_Clipped_Clip_" + clip);
}
}
}

4
tests/Avalonia.RenderTests/Controls/BorderTests.cs

@ -4,11 +4,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Controls
#endif
{
public class BorderTests : TestBase
{

4
tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs

@ -5,11 +5,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Controls
#endif
{
public class CustomRenderTests : TestBase
{

4
tests/Avalonia.RenderTests/Controls/ImageBlendTests.cs

@ -6,11 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Controls
#endif
{
public class ImageBlendTests : TestBase
{

4
tests/Avalonia.RenderTests/Controls/ImageCompositionTests.cs

@ -6,11 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Controls
#endif
{
public class ImageCompositionTests : TestBase
{

4
tests/Avalonia.RenderTests/Controls/ImageTests.cs

@ -5,11 +5,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Controls
#endif
{
public class ImageTests : TestBase
{

14
tests/Avalonia.RenderTests/Controls/TextBlockTests.cs

@ -8,11 +8,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Controls
#endif
{
public class TextBlockTests : TestBase
{
@ -299,7 +295,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
};
target.Children.Add(CreateText("aaaa"));
target.Children.Add(CreateText("a a "));
target.Children.Add(CreateText(" ")); // This one does not render correctly on Direct2D1
target.Children.Add(CreateText(" "));
target.Children.Add(CreateText("LLLL"));
var testName = $"Should_Keep_TrailingWhiteSpace";
@ -343,11 +339,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
await RenderToFile(target, testName);
CompareImages(testName);
#if AVALONIA_SKIA
const string symbolsFont = "resm:Avalonia.Skia.RenderTests.Assets?assembly=Avalonia.Skia.RenderTests#Source Serif 4 36pt";
#else
const string symbolsFont = "resm:Avalonia.Direct2D1.RenderTests.Assets?assembly=Avalonia.Direct2D1.RenderTests#Source Serif 4 36pt";
#endif
static TextBlock CreateText(string text) => new TextBlock
{
ClipToBounds = false,
@ -380,11 +372,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
await RenderToFile(target, testName);
CompareImages(testName);
#if AVALONIA_SKIA
const string symbolsFont = "resm:Avalonia.Skia.RenderTests.Assets?assembly=Avalonia.Skia.RenderTests#Source Serif 4 36pt";
#else
const string symbolsFont = "resm:Avalonia.Direct2D1.RenderTests.Assets?assembly=Avalonia.Direct2D1.RenderTests#Source Serif 4 36pt";
#endif
static TextBlock CreateText(string text) => new TextBlock
{
HorizontalAlignment = HorizontalAlignment.Center,

2
tests/Avalonia.RenderTests/CrossTests/Brushes/CrossTileBrushTests.cs

@ -5,8 +5,6 @@ using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests;
#elif AVALONIA_D2D
namespace Avalonia.Direct2D1.RenderTests;
#else
namespace Avalonia.RenderTests.WpfCompare;
#endif

2
tests/Avalonia.RenderTests/CrossTests/Brushes/RadialGradientBrushTests.cs

@ -4,8 +4,6 @@ using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests;
#elif AVALONIA_D2D
namespace Avalonia.Direct2D1.RenderTests;
#else
namespace Avalonia.RenderTests.WpfCompare;
#endif

2
tests/Avalonia.RenderTests/CrossTests/CrossGeometryTests.cs

@ -8,8 +8,6 @@ using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests;
#elif AVALONIA_D2D
namespace Avalonia.Direct2D1.RenderTests;
#else
namespace Avalonia.RenderTests.WpfCompare;
#endif

2
tests/Avalonia.RenderTests/CrossTests/Media/DrawingContextTests.cs

@ -3,8 +3,6 @@ using CrossUI;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests.CrossTests;
#elif AVALONIA_D2D
namespace Avalonia.Direct2D1.RenderTests.CrossTests;
#else
namespace Avalonia.RenderTests.WpfCompare.CrossTests;
#endif

2
tests/Avalonia.RenderTests/CrossTests/Media/ImageScalingTests.cs

@ -5,8 +5,6 @@ using CrossUI;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests.CrossTests;
#elif AVALONIA_D2D
namespace Avalonia.Direct2D1.RenderTests.CrossTests;
#else
namespace Avalonia.RenderTests.WpfCompare.CrossTests;
#endif

6
tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs

@ -84,14 +84,8 @@ namespace CrossUI
}
}
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests.CrossUI
{
#else
namespace Avalonia.Direct2D1.RenderTests.CrossUI
{
#endif
class AvaloniaCrossControl : Control
{
private readonly CrossControl _src;

4
tests/Avalonia.RenderTests/GeometryClippingTests.cs

@ -7,11 +7,7 @@ using System.Text;
using Xunit;
using System.Threading.Tasks;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class GeometryClippingTests : TestBase
{

7
tests/Avalonia.RenderTests/ManualRenderTimer.cs

@ -2,12 +2,7 @@ using Avalonia.Rendering;
using System.Threading.Tasks;
using System;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class ManualRenderTimer : IRenderTimer
{
@ -16,4 +11,4 @@ namespace Avalonia.Direct2D1.RenderTests
public void TriggerTick() => Tick?.Invoke(TimeSpan.Zero);
public Task TriggerBackgroundTick() => Task.Run(TriggerTick);
}
}
}

4
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -13,11 +13,7 @@ using Path = System.IO.Path;
#pragma warning disable CS0649
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class BitmapTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/CombinedGeometryTests.cs

@ -4,11 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class CombinedGeometryTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs

@ -4,11 +4,7 @@ using System;
using System.Threading.Tasks;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class ConicGradientBrushTests : TestBase
{

7
tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs

@ -1,14 +1,7 @@
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
using Avalonia.Direct2D1.RenderTests;
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class GeometryDrawingTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/GeometryGroupTests.cs

@ -6,11 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class GeometryGroupTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/GlyphRunTests.cs

@ -8,11 +8,7 @@ using Avalonia.Media.Imaging;
using Avalonia.Media.TextFormatting;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class GlyphRunTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/ImageBrushTests.cs

@ -6,11 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class ImageBrushTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/ImageDrawingTests.cs

@ -4,11 +4,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class ImageDrawingTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs

@ -7,11 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class LinearGradientBrushTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs

@ -7,11 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class RadialGradientBrushTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/RelativePointTestPrimitivesHelper.cs

@ -7,11 +7,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class RelativePointTestPrimitivesHelper : Control
{

4
tests/Avalonia.RenderTests/Media/StreamGeometryTests.cs

@ -6,11 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class StreamGeometryTests : TestBase
{

4
tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs

@ -9,11 +9,7 @@ using Avalonia.Media.TextFormatting;
using Avalonia.Utilities;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class TextLayoutTests : TestBase
{

5
tests/Avalonia.RenderTests/Media/TileBrushTests.cs

@ -5,11 +5,8 @@ using Avalonia.Media;
using Avalonia.Media.Immutable;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests;
#else
namespace Avalonia.Direct2D1.RenderTests.Media;
#endif
public class DrawingBrushTests: TestBase
{
public DrawingBrushTests()

4
tests/Avalonia.RenderTests/Media/VisualBrushTests.cs

@ -6,11 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class VisualBrushTests : TestBase
{

4
tests/Avalonia.RenderTests/OpacityMaskTests.cs

@ -7,11 +7,7 @@ using System.Text;
using Xunit;
using System.Threading.Tasks;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class OpacityMaskTests : TestBase
{

6
tests/Avalonia.RenderTests/SVGPathTests.cs

@ -7,13 +7,7 @@ using System.Text;
using Xunit;
using System.Threading.Tasks;
#if AVALONIA_CAIRO
namespace Avalonia.Cairo.RenderTests
#elif AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class SVGPathTests : TestBase
{

4
tests/Avalonia.RenderTests/Shapes/EllipseTests.cs

@ -4,11 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Shapes
#endif
{
public class EllipseTests : TestBase
{

4
tests/Avalonia.RenderTests/Shapes/LineTests.cs

@ -4,11 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Shapes
#endif
{
public class LineTests : TestBase
{

4
tests/Avalonia.RenderTests/Shapes/PathTests.cs

@ -4,11 +4,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Shapes
#endif
{
using System.Threading.Tasks;
using Avalonia.Collections;

4
tests/Avalonia.RenderTests/Shapes/PolygonTests.cs

@ -4,11 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Shapes
#endif
{
public class PolygonTests : TestBase
{

4
tests/Avalonia.RenderTests/Shapes/PolylineTests.cs

@ -4,11 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Shapes
#endif
{
public class PolylineTests : TestBase
{

4
tests/Avalonia.RenderTests/Shapes/RectangleTests.cs

@ -4,11 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Shapes
#endif
{
public class RectangleTests : TestBase
{

22
tests/Avalonia.RenderTests/TestBase.cs

@ -16,30 +16,19 @@ using System.Threading;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Skia;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Avalonia.Utilities;
using SixLabors.ImageSharp.PixelFormats;
using Image = SixLabors.ImageSharp.Image;
#if AVALONIA_SKIA
using Avalonia.Skia;
#else
using Avalonia.Direct2D1;
#endif
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class TestBase : IDisposable
{
#if AVALONIA_SKIA
private static string s_fontUri = "resm:Avalonia.Skia.RenderTests.Assets?assembly=Avalonia.Skia.RenderTests#Noto Mono";
#else
private static string s_fontUri = "resm:Avalonia.Direct2D1.RenderTests.Assets?assembly=Avalonia.Direct2D1.RenderTests#Noto Mono";
#endif
public static FontFamily TestFontFamily = new FontFamily(s_fontUri);
private const double AllowedError = 0.022;
@ -49,12 +38,7 @@ namespace Avalonia.Direct2D1.RenderTests
outputPath = outputPath.Replace('\\', Path.DirectorySeparatorChar);
var testPath = GetTestsDirectory();
var testFiles = Path.Combine(testPath, "TestFiles");
#if AVALONIA_SKIA
var platform = "Skia";
#else
var platform = "Direct2D1";
#endif
OutputPath = Path.Combine(testFiles, platform, outputPath);
OutputPath = Path.Combine(testFiles, "Skia", outputPath);
TestRenderHelper.BeginTest();
}

12
tests/Avalonia.RenderTests/TestRenderHelper.cs

@ -21,17 +21,9 @@ using Avalonia.UnitTests;
using Avalonia.Utilities;
using SixLabors.ImageSharp.PixelFormats;
using Image = SixLabors.ImageSharp.Image;
#if AVALONIA_SKIA
using Avalonia.Skia;
#else
using Avalonia.Direct2D1;
#endif
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests;
#else
namespace Avalonia.Direct2D1.RenderTests;
#endif
static class TestRenderHelper
{
@ -40,11 +32,7 @@ static class TestRenderHelper
static TestRenderHelper()
{
#if AVALONIA_SKIA
SkiaPlatform.Initialize();
#else
Direct2D1Platform.Initialize();
#endif
AvaloniaLocator.CurrentMutable
.Bind<IDispatcherImpl>()
.ToConstant(s_dispatcherImpl);

6
tests/Avalonia.RenderTests/TestRenderRoot.cs

@ -6,11 +6,7 @@ using Avalonia.Controls;
using Avalonia.Platform;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class TestRenderRoot : Decorator, IRenderRoot
{
@ -55,4 +51,4 @@ namespace Avalonia.Direct2D1.RenderTests
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
}
}
}

4
tests/Avalonia.RenderTests/TestSkip.cs

@ -3,11 +3,7 @@ using System.IO;
using System.Runtime.InteropServices;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests
#endif
{
public class Win32Fact : FactAttribute
{

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save