Browse Source

Merge pull request #7203 from AvaloniaUI/fixes/avalonia-visuals-nullability

Added nullable annotations to Avalonia.Visuals.
pull/7210/head
Max Katz 5 years ago
committed by GitHub
parent
commit
9563eeea24
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
  2. 2
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  3. 7
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  4. 2
      src/Avalonia.Input/AccessKeyHandler.cs
  5. 2
      src/Avalonia.Input/InputElement.cs
  6. 2
      src/Avalonia.Input/Pointer.cs
  7. 2
      src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs
  8. 4
      src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs
  9. 2
      src/Avalonia.Visuals/Animation/CompositePageTransition.cs
  10. 6
      src/Avalonia.Visuals/Animation/CrossFade.cs
  11. 2
      src/Avalonia.Visuals/Animation/IPageTransition.cs
  12. 10
      src/Avalonia.Visuals/Animation/PageSlide.cs
  13. 4
      src/Avalonia.Visuals/Animation/RenderLoopClock.cs
  14. 4
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  15. 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  16. 2
      src/Avalonia.Visuals/CornerRadius.cs
  17. 2
      src/Avalonia.Visuals/Matrix.cs
  18. 11
      src/Avalonia.Visuals/Media/BoxShadow.cs
  19. 6
      src/Avalonia.Visuals/Media/BoxShadows.cs
  20. 22
      src/Avalonia.Visuals/Media/Brush.cs
  21. 8
      src/Avalonia.Visuals/Media/BrushConverter.cs
  22. 6
      src/Avalonia.Visuals/Media/BrushExtensions.cs
  23. 2
      src/Avalonia.Visuals/Media/CharacterHit.cs
  24. 2
      src/Avalonia.Visuals/Media/Color.cs
  25. 2
      src/Avalonia.Visuals/Media/DashStyle.cs
  26. 43
      src/Avalonia.Visuals/Media/DrawingContext.cs
  27. 2
      src/Avalonia.Visuals/Media/DrawingImage.cs
  28. 6
      src/Avalonia.Visuals/Media/EllipseGeometry.cs
  29. 6
      src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs
  30. 2
      src/Avalonia.Visuals/Media/FontFallback.cs
  31. 16
      src/Avalonia.Visuals/Media/FontFamily.cs
  32. 6
      src/Avalonia.Visuals/Media/FontManager.cs
  33. 4
      src/Avalonia.Visuals/Media/FontManagerOptions.cs
  34. 6
      src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
  35. 10
      src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
  36. 8
      src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
  37. 16
      src/Avalonia.Visuals/Media/FormattedText.cs
  38. 4
      src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs
  39. 24
      src/Avalonia.Visuals/Media/Geometry.cs
  40. 27
      src/Avalonia.Visuals/Media/GlyphRun.cs
  41. 4
      src/Avalonia.Visuals/Media/GlyphTypeface.cs
  42. 12
      src/Avalonia.Visuals/Media/GradientBrush.cs
  43. 4
      src/Avalonia.Visuals/Media/IPen.cs
  44. 29
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  45. 16
      src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs
  46. 5
      src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs
  47. 14
      src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
  48. 4
      src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs
  49. 14
      src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs
  50. 4
      src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs
  51. 2
      src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs
  52. 8
      src/Avalonia.Visuals/Media/KnownColors.cs
  53. 6
      src/Avalonia.Visuals/Media/LineGeometry.cs
  54. 2
      src/Avalonia.Visuals/Media/MaterialExtensions.cs
  55. 35
      src/Avalonia.Visuals/Media/PathGeometry.cs
  56. 4
      src/Avalonia.Visuals/Media/PathGeometryCollections.cs
  57. 34
      src/Avalonia.Visuals/Media/PathMarkupParser.cs
  58. 22
      src/Avalonia.Visuals/Media/Pen.cs
  59. 2
      src/Avalonia.Visuals/Media/PixelPoint.cs
  60. 2
      src/Avalonia.Visuals/Media/PixelRect.cs
  61. 2
      src/Avalonia.Visuals/Media/PixelSize.cs
  62. 2
      src/Avalonia.Visuals/Media/PixelVector.cs
  63. 11
      src/Avalonia.Visuals/Media/PolylineGeometry.cs
  64. 6
      src/Avalonia.Visuals/Media/RectangleGeometry.cs
  65. 12
      src/Avalonia.Visuals/Media/StreamGeometry.cs
  66. 14
      src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs
  67. 2
      src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs
  68. 4
      src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
  69. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs
  70. 26
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
  71. 22
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  72. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
  73. 4
      src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs
  74. 16
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  75. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
  76. 4
      src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs
  77. 12
      src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
  78. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs
  79. 4
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs
  80. 2
      src/Avalonia.Visuals/Media/Transform.cs
  81. 6
      src/Avalonia.Visuals/Media/TransformConverter.cs
  82. 2
      src/Avalonia.Visuals/Media/TransformGroup.cs
  83. 4
      src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs
  84. 4
      src/Avalonia.Visuals/Media/Typeface.cs
  85. 4
      src/Avalonia.Visuals/Media/UnicodeRange.cs
  86. 6
      src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs
  87. 6
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  88. 2
      src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
  89. 2
      src/Avalonia.Visuals/Platform/IGeometryImpl.cs
  90. 2
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  91. 2
      src/Avalonia.Visuals/Platform/IRenderTarget.cs
  92. 2
      src/Avalonia.Visuals/Platform/ITextShaperImpl.cs
  93. 35
      src/Avalonia.Visuals/Platform/PathGeometryContext.cs
  94. 2
      src/Avalonia.Visuals/Point.cs
  95. 2
      src/Avalonia.Visuals/Rect.cs
  96. 2
      src/Avalonia.Visuals/RelativePoint.cs
  97. 2
      src/Avalonia.Visuals/RelativeRect.cs
  98. 16
      src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs
  99. 72
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  100. 2
      src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs

2
src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs

@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication
{ {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource public partial class Resource
{ {

2
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -6,7 +6,7 @@
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder> <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants> <DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants>
<CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies>
<NoWarn>NU1605</NoWarn> <NoWarn>NU1605;CS8632</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

7
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -86,14 +86,15 @@ namespace Avalonia.Diagnostics.ViewModels
public void SelectControl(IControl control) public void SelectControl(IControl control)
{ {
var node = default(TreeNode); var node = default(TreeNode);
IControl? c = control;
while (node == null && control != null) while (node == null && c != null)
{ {
node = FindNode(control); node = FindNode(c);
if (node == null) if (node == null)
{ {
control = control.GetVisualParent<IControl>(); c = c.GetVisualParent<IControl>();
} }
} }

2
src/Avalonia.Input/AccessKeyHandler.cs

@ -188,7 +188,7 @@ namespace Avalonia.Input
// If the menu is open, only match controls in the menu's visual tree. // If the menu is open, only match controls in the menu's visual tree.
if (menuIsOpen) if (menuIsOpen)
{ {
matches = matches.Where(x => MainMenu.IsVisualAncestorOf(x)); matches = matches.Where(x => x is not null && MainMenu!.IsVisualAncestorOf(x));
} }
var match = matches.FirstOrDefault(); var match = matches.FirstOrDefault();

2
src/Avalonia.Input/InputElement.cs

@ -675,7 +675,7 @@ namespace Avalonia.Input
/// <see cref="IsEffectivelyEnabled"/>. /// <see cref="IsEffectivelyEnabled"/>.
/// </summary> /// </summary>
/// <param name="parent">The parent control.</param> /// <param name="parent">The parent control.</param>
private void UpdateIsEffectivelyEnabled(InputElement parent) private void UpdateIsEffectivelyEnabled(InputElement? parent)
{ {
IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true); IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true);

2
src/Avalonia.Input/Pointer.cs

@ -54,7 +54,7 @@ namespace Avalonia.Input
Captured.DetachedFromVisualTree += OnCaptureDetached; Captured.DetachedFromVisualTree += OnCaptureDetached;
} }
IInputElement GetNextCapture(IVisual parent) IInputElement? GetNextCapture(IVisual parent)
{ {
return parent as IInputElement ?? parent.FindAncestorOfType<IInputElement>(); return parent as IInputElement ?? parent.FindAncestorOfType<IInputElement>();
} }

2
src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs

@ -143,7 +143,7 @@ namespace Avalonia.Animation.Animators
if (!match(firstKeyType)) if (!match(firstKeyType))
continue; continue;
animator = (IAnimator)Activator.CreateInstance(animatorType); animator = (IAnimator?)Activator.CreateInstance(animatorType);
if (animator != null) if (animator != null)
{ {
animator.Property = Property; animator.Property = Property;

4
src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs

@ -11,10 +11,10 @@ namespace Avalonia.Animation.Animators
/// </summary> /// </summary>
public class TransformAnimator : Animator<double> public class TransformAnimator : Animator<double>
{ {
DoubleAnimator _doubleAnimator; DoubleAnimator? _doubleAnimator;
/// <inheritdoc/> /// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete) public override IDisposable? Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete)
{ {
var ctrl = (Visual)control; var ctrl = (Visual)control;

2
src/Avalonia.Visuals/Animation/CompositePageTransition.cs

@ -37,7 +37,7 @@ namespace Avalonia.Animation
public List<IPageTransition> PageTransitions { get; set; } = new List<IPageTransition>(); public List<IPageTransition> PageTransitions { get; set; } = new List<IPageTransition>();
/// <inheritdoc /> /// <inheritdoc />
public Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{ {
var transitionTasks = PageTransitions var transitionTasks = PageTransitions
.Select(transition => transition.Start(from, to, forward, cancellationToken)) .Select(transition => transition.Start(from, to, forward, cancellationToken))

6
src/Avalonia.Visuals/Animation/CrossFade.cs

@ -100,7 +100,7 @@ namespace Avalonia.Animation
} }
/// <inheritdoc cref="Start(Visual, Visual, CancellationToken)" /> /// <inheritdoc cref="Start(Visual, Visual, CancellationToken)" />
public async Task Start(Visual from, Visual to, CancellationToken cancellationToken) public async Task Start(Visual? from, Visual? to, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -112,7 +112,7 @@ namespace Avalonia.Animation
{ {
if (to != null) if (to != null)
{ {
disposables.Add(to.SetValue(Visual.OpacityProperty, 0, Data.BindingPriority.Animation)); disposables.Add(to.SetValue(Visual.OpacityProperty, 0, Data.BindingPriority.Animation)!);
} }
if (from != null) if (from != null)
@ -151,7 +151,7 @@ namespace Avalonia.Animation
/// <returns> /// <returns>
/// A <see cref="Task"/> that tracks the progress of the animation. /// A <see cref="Task"/> that tracks the progress of the animation.
/// </returns> /// </returns>
Task IPageTransition.Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) Task IPageTransition.Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{ {
return Start(from, to, cancellationToken); return Start(from, to, cancellationToken);
} }

2
src/Avalonia.Visuals/Animation/IPageTransition.cs

@ -26,6 +26,6 @@ namespace Avalonia.Animation
/// <returns> /// <returns>
/// A <see cref="Task"/> that tracks the progress of the animation. /// A <see cref="Task"/> that tracks the progress of the animation.
/// </returns> /// </returns>
Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken); Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken);
} }
} }

10
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -62,7 +62,7 @@ namespace Avalonia.Animation
public Easing SlideOutEasing { get; set; } = new LinearEasing(); public Easing SlideOutEasing { get; set; } = new LinearEasing();
/// <inheritdoc /> /// <inheritdoc />
public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -155,17 +155,17 @@ namespace Avalonia.Animation
/// <remarks> /// <remarks>
/// Any one of the parameters may be null, but not both. /// Any one of the parameters may be null, but not both.
/// </remarks> /// </remarks>
private static IVisual GetVisualParent(IVisual from, IVisual to) private static IVisual GetVisualParent(IVisual? from, IVisual? to)
{ {
var p1 = (from ?? to).VisualParent; var p1 = (from ?? to)!.VisualParent;
var p2 = (to ?? from).VisualParent; var p2 = (to ?? from)!.VisualParent;
if (p1 != null && p2 != null && p1 != p2) if (p1 != null && p2 != null && p1 != p2)
{ {
throw new ArgumentException("Controls for PageSlide must have same parent."); throw new ArgumentException("Controls for PageSlide must have same parent.");
} }
return p1; return p1 ?? throw new InvalidOperationException("Cannot determine visual parent.");
} }
} }
} }

4
src/Avalonia.Visuals/Animation/RenderLoopClock.cs

@ -9,7 +9,9 @@ namespace Avalonia.Animation
{ {
protected override void Stop() protected override void Stop()
{ {
AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this); var loop = AvaloniaLocator.Current.GetService<IRenderLoop>() ??
throw new InvalidOperationException("Unable to locate IRenderLoop.");
loop.Remove(this);
} }
bool IRenderLoopTask.NeedsUpdate => HasSubscriptions; bool IRenderLoopTask.NeedsUpdate => HasSubscriptions;

4
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -5,6 +5,8 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
@ -77,4 +79,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWr
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
Total Issues: 78 Total Issues: 79

1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -15,4 +15,5 @@
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\System.Memory.props" /> <Import Project="..\..\build\System.Memory.props" />
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project> </Project>

2
src/Avalonia.Visuals/CornerRadius.cs

@ -91,7 +91,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The Object to compare against.</param> /// <param name="obj">The Object to compare against.</param>
/// <returns>True if the Object is equal to this corner radius; False otherwise.</returns> /// <returns>True if the Object is equal to this corner radius; False otherwise.</returns>
public override bool Equals(object obj) => obj is CornerRadius other && Equals(other); public override bool Equals(object? obj) => obj is CornerRadius other && Equals(other);
public override int GetHashCode() public override int GetHashCode()
{ {

2
src/Avalonia.Visuals/Matrix.cs

@ -272,7 +272,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The Object to compare against.</param> /// <param name="obj">The Object to compare against.</param>
/// <returns>True if the Object is equal to this matrix; False otherwise.</returns> /// <returns>True if the Object is equal to this matrix; False otherwise.</returns>
public override bool Equals(object obj) => obj is Matrix other && Equals(other); public override bool Equals(object? obj) => obj is Matrix other && Equals(other);
/// <summary> /// <summary>
/// Returns the hash code for this instance. /// Returns the hash code for this instance.

11
src/Avalonia.Visuals/Media/BoxShadow.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
@ -26,7 +27,7 @@ namespace Avalonia.Media
return OffsetX.Equals(other.OffsetX) && OffsetY.Equals(other.OffsetY) && Blur.Equals(other.Blur) && Spread.Equals(other.Spread) && Color.Equals(other.Color); return OffsetX.Equals(other.OffsetX) && OffsetY.Equals(other.OffsetY) && Blur.Equals(other.Blur) && Spread.Equals(other.Spread) && Color.Equals(other.Color);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is BoxShadow other && Equals(other); return obj is BoxShadow other && Equals(other);
} }
@ -59,7 +60,7 @@ namespace Avalonia.Media
_index = 0; _index = 0;
} }
public bool TryReadString(out string s) public bool TryReadString([MaybeNullWhen(false)] out string s)
{ {
s = null; s = null;
if (_index >= _arr.Length) if (_index >= _arr.Length)
@ -152,11 +153,11 @@ namespace Avalonia.Media
tokenizer.TryReadString(out var token5); tokenizer.TryReadString(out var token5);
if (token4 != null) if (token4 != null)
blur = double.Parse(token3, CultureInfo.InvariantCulture); blur = double.Parse(token3!, CultureInfo.InvariantCulture);
if (token5 != null) if (token5 != null)
spread = double.Parse(token4, CultureInfo.InvariantCulture); spread = double.Parse(token4!, CultureInfo.InvariantCulture);
var color = Color.Parse(token5 ?? token4 ?? token3); var color = Color.Parse(token5 ?? token4 ?? token3!);
return new BoxShadow return new BoxShadow
{ {
IsInset = inset, IsInset = inset,

6
src/Avalonia.Visuals/Media/BoxShadows.cs

@ -8,7 +8,7 @@ namespace Avalonia.Media
public struct BoxShadows public struct BoxShadows
{ {
private readonly BoxShadow _first; private readonly BoxShadow _first;
private readonly BoxShadow[] _list; private readonly BoxShadow[]? _list;
public int Count { get; } public int Count { get; }
static BoxShadows() static BoxShadows()
@ -39,7 +39,7 @@ namespace Avalonia.Media
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
if (c == 0) if (c == 0)
return _first; return _first;
return _list[c - 1]; return _list![c - 1];
} }
} }
@ -134,7 +134,7 @@ namespace Avalonia.Media
return true; return true;
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is BoxShadows other && Equals(other); return obj is BoxShadows other && Equals(other);
} }

22
src/Avalonia.Visuals/Media/Brush.cs

@ -19,7 +19,7 @@ namespace Avalonia.Media
AvaloniaProperty.Register<Brush, double>(nameof(Opacity), 1.0); AvaloniaProperty.Register<Brush, double>(nameof(Opacity), 1.0);
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler Invalidated; public event EventHandler? Invalidated;
static Brush() static Brush()
{ {
@ -43,18 +43,20 @@ namespace Avalonia.Media
/// <returns>The <see cref="Color"/>.</returns> /// <returns>The <see cref="Color"/>.</returns>
public static IBrush Parse(string s) public static IBrush Parse(string s)
{ {
Contract.Requires<ArgumentNullException>(s != null); _ = s ?? throw new ArgumentNullException(nameof(s));
Contract.Requires<FormatException>(s.Length > 0);
if (s[0] == '#') if (s.Length > 0)
{ {
return new ImmutableSolidColorBrush(Color.Parse(s)); if (s[0] == '#')
} {
return new ImmutableSolidColorBrush(Color.Parse(s));
}
var brush = KnownColors.GetKnownBrush(s); var brush = KnownColors.GetKnownBrush(s);
if (brush != null) if (brush != null)
{ {
return brush; return brush;
}
} }
throw new FormatException($"Invalid brush string: '{s}'."); throw new FormatException($"Invalid brush string: '{s}'.");

8
src/Avalonia.Visuals/Media/BrushConverter.cs

@ -9,14 +9,14 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public class BrushConverter : TypeConverter public class BrushConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{ {
return sourceType == typeof(string); return sourceType == typeof(string);
} }
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
{ {
return Brush.Parse((string)value); return value is string s ? Brush.Parse(s) : null;
} }
} }
} }

6
src/Avalonia.Visuals/Media/BrushExtensions.cs

@ -18,7 +18,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public static IBrush ToImmutable(this IBrush brush) public static IBrush ToImmutable(this IBrush brush)
{ {
Contract.Requires<ArgumentNullException>(brush != null); _ = brush ?? throw new ArgumentNullException(nameof(brush));
return (brush as IMutableBrush)?.ToImmutable() ?? brush; return (brush as IMutableBrush)?.ToImmutable() ?? brush;
} }
@ -33,7 +33,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public static ImmutableDashStyle ToImmutable(this IDashStyle style) public static ImmutableDashStyle ToImmutable(this IDashStyle style)
{ {
Contract.Requires<ArgumentNullException>(style != null); _ = style ?? throw new ArgumentNullException(nameof(style));
return style as ImmutableDashStyle ?? ((DashStyle)style).ToImmutable(); return style as ImmutableDashStyle ?? ((DashStyle)style).ToImmutable();
} }
@ -48,7 +48,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public static ImmutablePen ToImmutable(this IPen pen) public static ImmutablePen ToImmutable(this IPen pen)
{ {
Contract.Requires<ArgumentNullException>(pen != null); _ = pen ?? throw new ArgumentNullException(nameof(pen));
return pen as ImmutablePen ?? ((Pen)pen).ToImmutable(); return pen as ImmutablePen ?? ((Pen)pen).ToImmutable();
} }

2
src/Avalonia.Visuals/Media/CharacterHit.cs

@ -41,7 +41,7 @@ namespace Avalonia.Media
return FirstCharacterIndex == other.FirstCharacterIndex && TrailingLength == other.TrailingLength; return FirstCharacterIndex == other.FirstCharacterIndex && TrailingLength == other.TrailingLength;
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is CharacterHit other && Equals(other); return obj is CharacterHit other && Equals(other);
} }

2
src/Avalonia.Visuals/Media/Color.cs

@ -280,7 +280,7 @@ namespace Avalonia.Media
return A == other.A && R == other.R && G == other.G && B == other.B; return A == other.A && R == other.R && G == other.G && B == other.B;
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is Color other && Equals(other); return obj is Color other && Equals(other);
} }

2
src/Avalonia.Visuals/Media/DashStyle.cs

@ -133,7 +133,7 @@ namespace Avalonia.Media
} }
} }
private void DashesChanged(object sender, NotifyCollectionChangedEventArgs e) private void DashesChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
Invalidated?.Invoke(this, e); Invalidated?.Invoke(this, e);
} }

43
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -20,9 +20,9 @@ namespace Avalonia.Media
private static ThreadSafeObjectPool<Stack<TransformContainer>> TransformStackPool { get; } = private static ThreadSafeObjectPool<Stack<TransformContainer>> TransformStackPool { get; } =
ThreadSafeObjectPool<Stack<TransformContainer>>.Default; ThreadSafeObjectPool<Stack<TransformContainer>>.Default;
private Stack<PushedState> _states = StateStackPool.Get(); private Stack<PushedState>? _states = StateStackPool.Get();
private Stack<TransformContainer> _transformContainers = TransformStackPool.Get(); private Stack<TransformContainer>? _transformContainers = TransformStackPool.Get();
readonly struct TransformContainer readonly struct TransformContainer
{ {
@ -80,7 +80,7 @@ namespace Avalonia.Media
/// <param name="rect">The rect in the output to draw to.</param> /// <param name="rect">The rect in the output to draw to.</param>
public void DrawImage(IImage source, Rect rect) public void DrawImage(IImage source, Rect rect)
{ {
Contract.Requires<ArgumentNullException>(source != null); _ = source ?? throw new ArgumentNullException(nameof(source));
DrawImage(source, new Rect(source.Size), rect); DrawImage(source, new Rect(source.Size), rect);
} }
@ -94,7 +94,7 @@ namespace Avalonia.Media
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param> /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public void DrawImage(IImage source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default) public void DrawImage(IImage source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
{ {
Contract.Requires<ArgumentNullException>(source != null); _ = source ?? throw new ArgumentNullException(nameof(source));
source.Draw(this, sourceRect, destRect, bitmapInterpolationMode); source.Draw(this, sourceRect, destRect, bitmapInterpolationMode);
} }
@ -121,7 +121,8 @@ namespace Avalonia.Media
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, IPen pen, Geometry geometry) public void DrawGeometry(IBrush brush, IPen pen, Geometry geometry)
{ {
DrawGeometry(brush, pen, geometry.PlatformImpl); if (geometry.PlatformImpl is not null)
DrawGeometry(brush, pen, geometry.PlatformImpl);
} }
/// <summary> /// <summary>
@ -132,7 +133,7 @@ namespace Avalonia.Media
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
{ {
Contract.Requires<ArgumentNullException>(geometry != null); _ = geometry ?? throw new ArgumentNullException(nameof(geometry));
if (brush != null || PenIsVisible(pen)) if (brush != null || PenIsVisible(pen))
{ {
@ -157,7 +158,7 @@ namespace Avalonia.Media
/// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks> /// </remarks>
public void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX = 0, double radiusY = 0, public void DrawRectangle(IBrush? brush, IPen? pen, Rect rect, double radiusX = 0, double radiusY = 0,
BoxShadows boxShadows = default) BoxShadows boxShadows = default)
{ {
if (brush == null && !PenIsVisible(pen)) if (brush == null && !PenIsVisible(pen))
@ -201,7 +202,7 @@ namespace Avalonia.Media
/// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks> /// </remarks>
public void DrawEllipse(IBrush brush, IPen pen, Point center, double radiusX, double radiusY) public void DrawEllipse(IBrush? brush, IPen? pen, Point center, double radiusX, double radiusY)
{ {
if (brush == null && !PenIsVisible(pen)) if (brush == null && !PenIsVisible(pen))
{ {
@ -230,7 +231,7 @@ namespace Avalonia.Media
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
public void DrawText(IBrush foreground, Point origin, FormattedText text) public void DrawText(IBrush foreground, Point origin, FormattedText text)
{ {
Contract.Requires<ArgumentNullException>(text != null); _ = text ?? throw new ArgumentNullException(nameof(text));
if (foreground != null) if (foreground != null)
{ {
@ -245,7 +246,7 @@ namespace Avalonia.Media
/// <param name="glyphRun">The glyph run.</param> /// <param name="glyphRun">The glyph run.</param>
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun) public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{ {
Contract.Requires<ArgumentNullException>(glyphRun != null); _ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
if (foreground != null) if (foreground != null)
{ {
@ -279,11 +280,14 @@ namespace Avalonia.Media
Clip, Clip,
MatrixContainer, MatrixContainer,
GeometryClip, GeometryClip,
OpacityMask OpacityMask,
} }
public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix)) public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix))
{ {
if (context._states is null)
throw new ObjectDisposedException(nameof(DrawingContext));
_context = context; _context = context;
_type = type; _type = type;
_matrix = matrix; _matrix = matrix;
@ -295,6 +299,8 @@ namespace Avalonia.Media
{ {
if (_type == PushedStateType.None) if (_type == PushedStateType.None)
return; return;
if (_context._states is null || _context._transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
if (_context._currentLevel != _level) if (_context._currentLevel != _level)
throw new InvalidOperationException("Wrong Push/Pop state order"); throw new InvalidOperationException("Wrong Push/Pop state order");
_context._currentLevel--; _context._currentLevel--;
@ -343,7 +349,14 @@ namespace Avalonia.Media
/// <returns>A disposable used to undo the clip geometry.</returns> /// <returns>A disposable used to undo the clip geometry.</returns>
public PushedState PushGeometryClip(Geometry clip) public PushedState PushGeometryClip(Geometry clip)
{ {
Contract.Requires<ArgumentNullException>(clip != null); _ = clip ?? throw new ArgumentNullException(nameof(clip));
// HACK: This check was added when nullable annotations pointed out that we're potentially
// pushing a null value for the clip here. Ideally we'd return an empty PushedState here but
// I don't want to make that change as part of adding nullable annotations.
if (clip.PlatformImpl is null)
throw new InvalidOperationException("Cannot push empty geometry clip.");
PlatformImpl.PushGeometryClip(clip.PlatformImpl); PlatformImpl.PushGeometryClip(clip.PlatformImpl);
return new PushedState(this, PushedState.PushedStateType.GeometryClip); return new PushedState(this, PushedState.PushedStateType.GeometryClip);
} }
@ -407,6 +420,8 @@ namespace Avalonia.Media
/// <returns>A disposable used to undo the transformation.</returns> /// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushTransformContainer() public PushedState PushTransformContainer()
{ {
if (_transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
_transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform)); _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
_currentContainerTransform = CurrentTransform * _currentContainerTransform; _currentContainerTransform = CurrentTransform * _currentContainerTransform;
_currentTransform = Matrix.Identity; _currentTransform = Matrix.Identity;
@ -418,6 +433,8 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
if (_states is null || _transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
while (_states.Count != 0) while (_states.Count != 0)
_states.Peek().Dispose(); _states.Peek().Dispose();
StateStackPool.Return(_states); StateStackPool.Return(_states);
@ -430,7 +447,7 @@ namespace Avalonia.Media
PlatformImpl.Dispose(); PlatformImpl.Dispose();
} }
private static bool PenIsVisible(IPen pen) private static bool PenIsVisible(IPen? pen)
{ {
return pen?.Brush != null && pen.Thickness > 0; return pen?.Brush != null && pen.Thickness > 0;
} }

2
src/Avalonia.Visuals/Media/DrawingImage.cs

@ -26,7 +26,7 @@ namespace Avalonia.Media
AvaloniaProperty.Register<DrawingImage, Drawing>(nameof(Drawing)); AvaloniaProperty.Register<DrawingImage, Drawing>(nameof(Drawing));
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler Invalidated; public event EventHandler? Invalidated;
/// <summary> /// <summary>
/// Gets or sets the drawing content. /// Gets or sets the drawing content.

6
src/Avalonia.Visuals/Media/EllipseGeometry.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Media namespace Avalonia.Media
@ -95,9 +96,10 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override IGeometryImpl CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
if (Rect != default) return factory.CreateEllipseGeometry(Rect); if (Rect != default) return factory.CreateEllipseGeometry(Rect);

6
src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs

@ -78,7 +78,7 @@ namespace Avalonia.Media
AvaloniaProperty.Register<ExperimentalAcrylicMaterial, Color>(nameof(FallbackColor)); AvaloniaProperty.Register<ExperimentalAcrylicMaterial, Color>(nameof(FallbackColor));
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler Invalidated; public event EventHandler? Invalidated;
/// <summary> /// <summary>
/// Gets or Sets the BackgroundSource <seealso cref="AcrylicBackgroundSource"/>. /// Gets or Sets the BackgroundSource <seealso cref="AcrylicBackgroundSource"/>.
@ -299,14 +299,14 @@ namespace Avalonia.Media
var lightness = (max + min) / 2.0; var lightness = (max + min) / 2.0;
lightness = 1 - ((1 - lightness) * luminosityOpacity.Value); lightness = 1 - ((1 - lightness) * (luminosityOpacity ?? 1));
lightness = 0.13 + (lightness * 0.74); lightness = 0.13 + (lightness * 0.74);
var luminosityColor = new Color(255, Trim(lightness), Trim(lightness), Trim(lightness)); var luminosityColor = new Color(255, Trim(lightness), Trim(lightness), Trim(lightness));
var compensationMultiplier = 1 - PlatformTransparencyCompensationLevel; var compensationMultiplier = 1 - PlatformTransparencyCompensationLevel;
return new Color((byte)(255 * Math.Max(Math.Min(PlatformTransparencyCompensationLevel + (luminosityOpacity.Value * compensationMultiplier), 1.0), 0.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B); return new Color((byte)(255 * Math.Max(Math.Min(PlatformTransparencyCompensationLevel + ((luminosityOpacity ?? 1) * compensationMultiplier), 1.0), 0.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B);
} }
/// <summary> /// <summary>

2
src/Avalonia.Visuals/Media/FontFallback.cs

@ -8,7 +8,7 @@
/// <summary> /// <summary>
/// Get or set the fallback <see cref="FontFamily"/> /// Get or set the fallback <see cref="FontFamily"/>
/// </summary> /// </summary>
public FontFamily FontFamily { get; set; } public FontFamily FontFamily { get; set; } = FontFamily.Default;
/// <summary> /// <summary>
/// Get or set the <see cref="UnicodeRange"/> that is covered by the fallback. /// Get or set the <see cref="UnicodeRange"/> that is covered by the fallback.

16
src/Avalonia.Visuals/Media/FontFamily.cs

@ -27,7 +27,7 @@ namespace Avalonia.Media
/// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param> /// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param> /// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
/// <exception cref="T:System.ArgumentException">Base uri must be an absolute uri.</exception> /// <exception cref="T:System.ArgumentException">Base uri must be an absolute uri.</exception>
public FontFamily(Uri baseUri, string name) public FontFamily(Uri? baseUri, string name)
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
{ {
@ -77,7 +77,7 @@ namespace Avalonia.Media
/// The family key. /// The family key.
/// </value> /// </value>
/// <remarks>Key is only used for custom fonts.</remarks> /// <remarks>Key is only used for custom fonts.</remarks>
public FontFamilyKey Key { get; } public FontFamilyKey? Key { get; }
/// <summary> /// <summary>
/// Returns <c>True</c> if this instance is the system's default. /// Returns <c>True</c> if this instance is the system's default.
@ -95,7 +95,7 @@ namespace Avalonia.Media
private struct FontFamilyIdentifier private struct FontFamilyIdentifier
{ {
public FontFamilyIdentifier(string name, Uri source) public FontFamilyIdentifier(string name, Uri? source)
{ {
Name = name; Name = name;
Source = source; Source = source;
@ -103,7 +103,7 @@ namespace Avalonia.Media
public string Name { get; } public string Name { get; }
public Uri Source { get; } public Uri? Source { get; }
} }
private static FontFamilyIdentifier GetFontFamilyIdentifier(string name) private static FontFamilyIdentifier GetFontFamilyIdentifier(string name)
@ -152,7 +152,7 @@ namespace Avalonia.Media
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// Specified family is not supported. /// Specified family is not supported.
/// </exception> /// </exception>
public static FontFamily Parse(string s, Uri baseUri) public static FontFamily Parse(string s, Uri? baseUri)
{ {
if (string.IsNullOrEmpty(s)) if (string.IsNullOrEmpty(s))
{ {
@ -192,12 +192,12 @@ namespace Avalonia.Media
} }
} }
public static bool operator !=(FontFamily a, FontFamily b) public static bool operator !=(FontFamily? a, FontFamily? b)
{ {
return !(a == b); return !(a == b);
} }
public static bool operator ==(FontFamily a, FontFamily b) public static bool operator ==(FontFamily? a, FontFamily? b)
{ {
if (ReferenceEquals(a, b)) if (ReferenceEquals(a, b))
{ {
@ -207,7 +207,7 @@ namespace Avalonia.Media
return !(a is null) && a.Equals(b); return !(a is null) && a.Equals(b);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (ReferenceEquals(this, obj)) if (ReferenceEquals(this, obj))
{ {

6
src/Avalonia.Visuals/Media/FontManager.cs

@ -16,7 +16,7 @@ namespace Avalonia.Media
private readonly ConcurrentDictionary<Typeface, GlyphTypeface> _glyphTypefaceCache = private readonly ConcurrentDictionary<Typeface, GlyphTypeface> _glyphTypefaceCache =
new ConcurrentDictionary<Typeface, GlyphTypeface>(); new ConcurrentDictionary<Typeface, GlyphTypeface>();
private readonly FontFamily _defaultFontFamily; private readonly FontFamily _defaultFontFamily;
private readonly IReadOnlyList<FontFallback> _fontFallbacks; private readonly IReadOnlyList<FontFallback>? _fontFallbacks;
public FontManager(IFontManagerImpl platformImpl) public FontManager(IFontManagerImpl platformImpl)
{ {
@ -105,7 +105,7 @@ namespace Avalonia.Media
if (typeface.FontFamily == _defaultFontFamily) if (typeface.FontFamily == _defaultFontFamily)
{ {
return null; throw new InvalidOperationException($"Could not create glyph typeface for: {typeface.FontFamily.Name}.");
} }
typeface = new Typeface(_defaultFontFamily, typeface.Style, typeface.Weight); typeface = new Typeface(_defaultFontFamily, typeface.Style, typeface.Weight);
@ -126,7 +126,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight, FontWeight fontWeight,
FontFamily fontFamily, CultureInfo culture, out Typeface typeface) FontFamily? fontFamily, CultureInfo? culture, out Typeface typeface)
{ {
if(_fontFallbacks != null) if(_fontFallbacks != null)
{ {

4
src/Avalonia.Visuals/Media/FontManagerOptions.cs

@ -4,8 +4,8 @@ namespace Avalonia.Media
{ {
public class FontManagerOptions public class FontManagerOptions
{ {
public string DefaultFamilyName { get; set; } public string? DefaultFamilyName { get; set; }
public IReadOnlyList<FontFallback> FontFallbacks { get; set; } public IReadOnlyList<FontFallback>? FontFallbacks { get; set; }
} }
} }

6
src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs

@ -122,12 +122,12 @@ namespace Avalonia.Media.Fonts
} }
} }
public static bool operator !=(FamilyNameCollection a, FamilyNameCollection b) public static bool operator !=(FamilyNameCollection? a, FamilyNameCollection? b)
{ {
return !(a == b); return !(a == b);
} }
public static bool operator ==(FamilyNameCollection a, FamilyNameCollection b) public static bool operator ==(FamilyNameCollection? a, FamilyNameCollection? b)
{ {
if (ReferenceEquals(a, b)) if (ReferenceEquals(a, b))
{ {
@ -144,7 +144,7 @@ namespace Avalonia.Media.Fonts
/// <returns> /// <returns>
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>. /// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns> /// </returns>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (!(obj is FamilyNameCollection other)) if (!(obj is FamilyNameCollection other))
{ {

10
src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media.Fonts
/// </summary> /// </summary>
/// <param name="source"></param> /// <param name="source"></param>
/// <param name="baseUri"></param> /// <param name="baseUri"></param>
public FontFamilyKey(Uri source, Uri baseUri = null) public FontFamilyKey(Uri source, Uri? baseUri = null)
{ {
Source = source ?? throw new ArgumentNullException(nameof(source)); Source = source ?? throw new ArgumentNullException(nameof(source));
@ -27,7 +27,7 @@ namespace Avalonia.Media.Fonts
/// <summary> /// <summary>
/// A base URI to use if <see cref="Source"/> is relative /// A base URI to use if <see cref="Source"/> is relative
/// </summary> /// </summary>
public Uri BaseUri { get; } public Uri? BaseUri { get; }
/// <summary> /// <summary>
/// Returns a hash code for this instance. /// Returns a hash code for this instance.
@ -55,12 +55,12 @@ namespace Avalonia.Media.Fonts
} }
} }
public static bool operator !=(FontFamilyKey a, FontFamilyKey b) public static bool operator !=(FontFamilyKey? a, FontFamilyKey? b)
{ {
return !(a == b); return !(a == b);
} }
public static bool operator ==(FontFamilyKey a, FontFamilyKey b) public static bool operator ==(FontFamilyKey? a, FontFamilyKey? b)
{ {
if (ReferenceEquals(a, b)) if (ReferenceEquals(a, b))
{ {
@ -77,7 +77,7 @@ namespace Avalonia.Media.Fonts
/// <returns> /// <returns>
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>. /// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns> /// </returns>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (!(obj is FontFamilyKey other)) if (!(obj is FontFamilyKey other))
{ {

8
src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs

@ -32,7 +32,8 @@ namespace Avalonia.Media.Fonts
/// <returns></returns> /// <returns></returns>
private static IEnumerable<Uri> GetFontAssetsBySource(FontFamilyKey fontFamilyKey) private static IEnumerable<Uri> GetFontAssetsBySource(FontFamilyKey fontFamilyKey)
{ {
var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>(); var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>() ??
throw new InvalidOperationException("Unable to locate IAssetLoader.");
var availableAssets = assetLoader.GetAssets(fontFamilyKey.Source, fontFamilyKey.BaseUri); var availableAssets = assetLoader.GetAssets(fontFamilyKey.Source, fontFamilyKey.BaseUri);
@ -50,7 +51,8 @@ namespace Avalonia.Media.Fonts
/// <returns></returns> /// <returns></returns>
private static IEnumerable<Uri> GetFontAssetsByExpression(FontFamilyKey fontFamilyKey) private static IEnumerable<Uri> GetFontAssetsByExpression(FontFamilyKey fontFamilyKey)
{ {
var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>(); var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>() ??
throw new InvalidOperationException("Unable to locate IAssetLoader.");
var fileName = GetFileName(fontFamilyKey, out var fileExtension, out var location); var fileName = GetFileName(fontFamilyKey, out var fileExtension, out var location);
@ -87,7 +89,7 @@ namespace Avalonia.Media.Fonts
{ {
fileExtension = "." + fontFamilyKey.Source.AbsolutePath.Split('.').LastOrDefault(); fileExtension = "." + fontFamilyKey.Source.AbsolutePath.Split('.').LastOrDefault();
var fileName = fontFamilyKey.Source.LocalPath.Replace(fileExtension, string.Empty).Split('.').LastOrDefault(); var fileName = fontFamilyKey.Source.LocalPath.Replace(fileExtension, string.Empty).Split('.').Last();
location = new Uri(fontFamilyKey.Source.AbsoluteUri.Replace("." + fileName + fileExtension, string.Empty), UriKind.RelativeOrAbsolute); location = new Uri(fontFamilyKey.Source.AbsoluteUri.Replace("." + fileName + fileExtension, string.Empty), UriKind.RelativeOrAbsolute);

16
src/Avalonia.Visuals/Media/FormattedText.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Platform; using Avalonia.Platform;
@ -10,11 +11,11 @@ namespace Avalonia.Media
{ {
private readonly IPlatformRenderInterface _platform; private readonly IPlatformRenderInterface _platform;
private Size _constraint = Size.Infinity; private Size _constraint = Size.Infinity;
private IFormattedTextImpl _platformImpl; private IFormattedTextImpl? _platformImpl;
private IReadOnlyList<FormattedTextStyleSpan> _spans; private IReadOnlyList<FormattedTextStyleSpan>? _spans;
private Typeface _typeface; private Typeface _typeface;
private double _fontSize; private double _fontSize;
private string _text; private string? _text;
private TextAlignment _textAlignment; private TextAlignment _textAlignment;
private TextWrapping _textWrapping; private TextWrapping _textWrapping;
@ -23,7 +24,8 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public FormattedText() public FormattedText()
{ {
_platform = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); _platform = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
} }
/// <summary> /// <summary>
@ -98,7 +100,7 @@ namespace Avalonia.Media
/// Gets or sets a collection of spans that describe the formatting of subsections of the /// Gets or sets a collection of spans that describe the formatting of subsections of the
/// text. /// text.
/// </summary> /// </summary>
public IReadOnlyList<FormattedTextStyleSpan> Spans public IReadOnlyList<FormattedTextStyleSpan>? Spans
{ {
get => _spans; get => _spans;
set => Set(ref _spans, value); set => Set(ref _spans, value);
@ -107,7 +109,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the text. /// Gets or sets the text.
/// </summary> /// </summary>
public string Text public string? Text
{ {
get => _text; get => _text;
set => Set(ref _text, value); set => Set(ref _text, value);
@ -141,7 +143,7 @@ namespace Avalonia.Media
if (_platformImpl == null) if (_platformImpl == null)
{ {
_platformImpl = _platform.CreateFormattedText( _platformImpl = _platform.CreateFormattedText(
_text, _text ?? string.Empty,
_typeface, _typeface,
_fontSize, _fontSize,
_textAlignment, _textAlignment,

4
src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs

@ -14,7 +14,7 @@
public FormattedTextStyleSpan( public FormattedTextStyleSpan(
int startIndex, int startIndex,
int length, int length,
IBrush foregroundBrush = null) IBrush? foregroundBrush = null)
{ {
StartIndex = startIndex; StartIndex = startIndex;
Length = length; Length = length;
@ -34,6 +34,6 @@
/// <summary> /// <summary>
/// Gets the span's foreground brush. /// Gets the span's foreground brush.
/// </summary> /// </summary>
public IBrush ForegroundBrush { get; } public IBrush? ForegroundBrush { get; }
} }
} }

24
src/Avalonia.Visuals/Media/Geometry.cs

@ -11,11 +11,11 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Transform"/> property. /// Defines the <see cref="Transform"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<Transform> TransformProperty = public static readonly StyledProperty<Transform?> TransformProperty =
AvaloniaProperty.Register<Geometry, Transform>(nameof(Transform)); AvaloniaProperty.Register<Geometry, Transform?>(nameof(Transform));
private bool _isDirty = true; private bool _isDirty = true;
private IGeometryImpl _platformImpl; private IGeometryImpl? _platformImpl;
static Geometry() static Geometry()
{ {
@ -25,7 +25,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Raised when the geometry changes. /// Raised when the geometry changes.
/// </summary> /// </summary>
public event EventHandler Changed; public event EventHandler? Changed;
/// <summary> /// <summary>
/// Gets the geometry's bounding rectangle. /// Gets the geometry's bounding rectangle.
@ -35,7 +35,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets the platform-specific implementation of the geometry. /// Gets the platform-specific implementation of the geometry.
/// </summary> /// </summary>
public IGeometryImpl PlatformImpl public IGeometryImpl? PlatformImpl
{ {
get get
{ {
@ -60,7 +60,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets a transform to apply to the geometry. /// Gets or sets a transform to apply to the geometry.
/// </summary> /// </summary>
public Transform Transform public Transform? Transform
{ {
get { return GetValue(TransformProperty); } get { return GetValue(TransformProperty); }
set { SetValue(TransformProperty, value); } set { SetValue(TransformProperty, value); }
@ -127,7 +127,7 @@ namespace Avalonia.Media
/// Creates the platform implementation of the geometry, without the transform applied. /// Creates the platform implementation of the geometry, without the transform applied.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
protected abstract IGeometryImpl CreateDefiningGeometry(); protected abstract IGeometryImpl? CreateDefiningGeometry();
/// <summary> /// <summary>
/// Invalidates the platform implementation of the geometry. /// Invalidates the platform implementation of the geometry.
@ -141,8 +141,8 @@ namespace Avalonia.Media
private void TransformChanged(AvaloniaPropertyChangedEventArgs e) private void TransformChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var oldValue = (Transform)e.OldValue; var oldValue = (Transform?)e.OldValue;
var newValue = (Transform)e.NewValue; var newValue = (Transform?)e.NewValue;
if (oldValue != null) if (oldValue != null)
{ {
@ -157,9 +157,9 @@ namespace Avalonia.Media
TransformChanged(newValue, EventArgs.Empty); TransformChanged(newValue, EventArgs.Empty);
} }
private void TransformChanged(object sender, EventArgs e) private void TransformChanged(object? sender, EventArgs e)
{ {
var transform = ((Transform)sender)?.Value; var transform = ((Transform?)sender)?.Value;
if (_platformImpl is ITransformedGeometryImpl t) if (_platformImpl is ITransformedGeometryImpl t)
{ {
@ -174,7 +174,7 @@ namespace Avalonia.Media
} }
else if (_platformImpl != null && transform != null && transform != Matrix.Identity) else if (_platformImpl != null && transform != null && transform != Matrix.Identity)
{ {
_platformImpl = PlatformImpl.WithTransform(transform.Value); _platformImpl = _platformImpl.WithTransform(transform.Value);
} }
Changed?.Invoke(this, EventArgs.Empty); Changed?.Invoke(this, EventArgs.Empty);

27
src/Avalonia.Visuals/Media/GlyphRun.cs

@ -14,7 +14,7 @@ namespace Avalonia.Media
private static readonly IComparer<ushort> s_ascendingComparer = Comparer<ushort>.Default; private static readonly IComparer<ushort> s_ascendingComparer = Comparer<ushort>.Default;
private static readonly IComparer<ushort> s_descendingComparer = new ReverseComparer<ushort>(); private static readonly IComparer<ushort> s_descendingComparer = new ReverseComparer<ushort>();
private IGlyphRunImpl _glyphRunImpl; private IGlyphRunImpl? _glyphRunImpl;
private GlyphTypeface _glyphTypeface; private GlyphTypeface _glyphTypeface;
private double _fontRenderingEmSize; private double _fontRenderingEmSize;
private int _biDiLevel; private int _biDiLevel;
@ -27,14 +27,6 @@ namespace Avalonia.Media
private ReadOnlySlice<ushort> _glyphClusters; private ReadOnlySlice<ushort> _glyphClusters;
private ReadOnlySlice<char> _characters; private ReadOnlySlice<char> _characters;
/// <summary>
/// Initializes a new instance of the <see cref="GlyphRun"/> class.
/// </summary>
public GlyphRun()
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlyphRun"/> class by specifying properties of the class. /// Initializes a new instance of the <see cref="GlyphRun"/> class by specifying properties of the class.
/// </summary> /// </summary>
@ -56,7 +48,7 @@ namespace Avalonia.Media
ReadOnlySlice<ushort> glyphClusters = default, ReadOnlySlice<ushort> glyphClusters = default,
int biDiLevel = 0) int biDiLevel = 0)
{ {
GlyphTypeface = glyphTypeface; _glyphTypeface = glyphTypeface;
FontRenderingEmSize = fontRenderingEmSize; FontRenderingEmSize = fontRenderingEmSize;
@ -74,13 +66,9 @@ namespace Avalonia.Media
} }
/// <summary> /// <summary>
/// Gets or sets the <see cref="Media.GlyphTypeface"/> for the <see cref="GlyphRun"/>. /// Gets the <see cref="Media.GlyphTypeface"/> for the <see cref="GlyphRun"/>.
/// </summary> /// </summary>
public GlyphTypeface GlyphTypeface public GlyphTypeface GlyphTypeface => _glyphTypeface;
{
get => _glyphTypeface;
set => Set(ref _glyphTypeface, value);
}
/// <summary> /// <summary>
/// Gets or sets the em size used for rendering the <see cref="GlyphRun"/>. /// Gets or sets the em size used for rendering the <see cref="GlyphRun"/>.
@ -199,7 +187,7 @@ namespace Avalonia.Media
Initialize(); Initialize();
} }
return _glyphRunImpl; return _glyphRunImpl!;
} }
} }
@ -639,7 +627,8 @@ namespace Avalonia.Media
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
var platformRenderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var platformRenderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface");
_glyphRunImpl = platformRenderInterface.CreateGlyphRun(this); _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this);
} }
@ -651,7 +640,7 @@ namespace Avalonia.Media
private class ReverseComparer<T> : IComparer<T> private class ReverseComparer<T> : IComparer<T>
{ {
public int Compare(T x, T y) public int Compare(T? x, T? y)
{ {
return Comparer<T>.Default.Compare(y, x); return Comparer<T>.Default.Compare(y, x);
} }

4
src/Avalonia.Visuals/Media/GlyphTypeface.cs

@ -8,8 +8,8 @@ namespace Avalonia.Media
public const int InvisibleGlyph = 3; public const int InvisibleGlyph = 3;
public GlyphTypeface(Typeface typeface) public GlyphTypeface(Typeface typeface)
: this(FontManager.Current?.PlatformImpl.CreateGlyphTypeface(typeface)) : this(FontManager.Current.PlatformImpl.CreateGlyphTypeface(typeface))
{ {
} }
public GlyphTypeface(IGlyphTypefaceImpl platformImpl) public GlyphTypeface(IGlyphTypefaceImpl platformImpl)

12
src/Avalonia.Visuals/Media/GradientBrush.cs

@ -26,7 +26,7 @@ namespace Avalonia.Media
public static readonly StyledProperty<GradientStops> GradientStopsProperty = public static readonly StyledProperty<GradientStops> GradientStopsProperty =
AvaloniaProperty.Register<GradientBrush, GradientStops>(nameof(GradientStops)); AvaloniaProperty.Register<GradientBrush, GradientStops>(nameof(GradientStops));
private IDisposable _gradientStopsSubscription; private IDisposable? _gradientStopsSubscription;
static GradientBrush() static GradientBrush()
{ {
@ -64,13 +64,13 @@ namespace Avalonia.Media
{ {
if (e.Sender is GradientBrush brush) if (e.Sender is GradientBrush brush)
{ {
var oldValue = (GradientStops)e.OldValue; var oldValue = (GradientStops?)e.OldValue;
var newValue = (GradientStops)e.NewValue; var newValue = (GradientStops?)e.NewValue;
if (oldValue != null) if (oldValue != null)
{ {
oldValue.CollectionChanged -= brush.GradientStopsChanged; oldValue.CollectionChanged -= brush.GradientStopsChanged;
brush._gradientStopsSubscription.Dispose(); brush._gradientStopsSubscription?.Dispose();
} }
if (newValue != null) if (newValue != null)
@ -83,12 +83,12 @@ namespace Avalonia.Media
} }
} }
private void GradientStopsChanged(object sender, NotifyCollectionChangedEventArgs e) private void GradientStopsChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
RaiseInvalidated(EventArgs.Empty); RaiseInvalidated(EventArgs.Empty);
} }
private void GradientStopChanged(Tuple<object, PropertyChangedEventArgs> e) private void GradientStopChanged(Tuple<object?, PropertyChangedEventArgs> e)
{ {
RaiseInvalidated(EventArgs.Empty); RaiseInvalidated(EventArgs.Empty);
} }

4
src/Avalonia.Visuals/Media/IPen.cs

@ -8,12 +8,12 @@
/// <summary> /// <summary>
/// Gets the brush used to draw the stroke. /// Gets the brush used to draw the stroke.
/// </summary> /// </summary>
IBrush Brush { get; } IBrush? Brush { get; }
/// <summary> /// <summary>
/// Gets the style of dashed lines drawn with a <see cref="Pen"/> object. /// Gets the style of dashed lines drawn with a <see cref="Pen"/> object.
/// </summary> /// </summary>
IDashStyle DashStyle { get; } IDashStyle? DashStyle { get; }
/// <summary> /// <summary>
/// Gets the type of shape to use on both ends of a line. /// Gets the type of shape to use on both ends of a line.

29
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@ -21,8 +21,7 @@ namespace Avalonia.Media.Imaging
/// <returns>An instance of the <see cref="Bitmap"/> class.</returns> /// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{ {
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); return new Bitmap(GetFactory().LoadBitmapToWidth(stream, width, interpolationMode));
return new Bitmap(factory.LoadBitmapToWidth(stream, width, interpolationMode));
} }
/// <summary> /// <summary>
@ -35,8 +34,7 @@ namespace Avalonia.Media.Imaging
/// <returns>An instance of the <see cref="Bitmap"/> class.</returns> /// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{ {
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); return new Bitmap(GetFactory().LoadBitmapToHeight(stream, height, interpolationMode));
return new Bitmap(factory.LoadBitmapToHeight(stream, height, interpolationMode));
} }
/// <summary> /// <summary>
@ -47,8 +45,7 @@ namespace Avalonia.Media.Imaging
/// <returns>An instance of the <see cref="Bitmap"/> class.</returns> /// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
public Bitmap CreateScaledBitmap(PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) public Bitmap CreateScaledBitmap(PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{ {
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); return new Bitmap(GetFactory().ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode));
return new Bitmap(factory.ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode));
} }
/// <summary> /// <summary>
@ -57,8 +54,7 @@ namespace Avalonia.Media.Imaging
/// <param name="fileName">The filename of the bitmap.</param> /// <param name="fileName">The filename of the bitmap.</param>
public Bitmap(string fileName) public Bitmap(string fileName)
{ {
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(fileName));
PlatformImpl = RefCountable.Create(factory.LoadBitmap(fileName));
} }
/// <summary> /// <summary>
@ -67,8 +63,7 @@ namespace Avalonia.Media.Imaging
/// <param name="stream">The stream to read the bitmap from.</param> /// <param name="stream">The stream to read the bitmap from.</param>
public Bitmap(Stream stream) public Bitmap(Stream stream)
{ {
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(stream));
PlatformImpl = RefCountable.Create(factory.LoadBitmap(stream));
} }
/// <summary> /// <summary>
@ -106,9 +101,8 @@ namespace Avalonia.Media.Imaging
[Obsolete("Use overload taking an AlphaFormat.")] [Obsolete("Use overload taking an AlphaFormat.")]
public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{ {
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var ri = GetFactory();
PlatformImpl = RefCountable.Create(ri
PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
.LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride)); .LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride));
} }
@ -123,8 +117,7 @@ namespace Avalonia.Media.Imaging
/// <param name="stride">The number of bytes per row.</param> /// <param name="stride">The number of bytes per row.</param>
public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{ {
PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(format, alphaFormat, data, size, dpi, stride));
.LoadBitmap(format, alphaFormat, data, size, dpi, stride));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -173,5 +166,11 @@ namespace Avalonia.Media.Imaging
destRect, destRect,
bitmapInterpolationMode); bitmapInterpolationMode);
} }
private static IPlatformRenderInterface GetFactory()
{
return AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
}
} }
} }

16
src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs

@ -11,8 +11,8 @@ namespace Avalonia.Media.Imaging
/// <summary> /// <summary>
/// Defines the <see cref="Source"/> property. /// Defines the <see cref="Source"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IImage> SourceProperty = public static readonly StyledProperty<IImage?> SourceProperty =
AvaloniaProperty.Register<CroppedBitmap, IImage>(nameof(Source)); AvaloniaProperty.Register<CroppedBitmap, IImage?>(nameof(Source));
/// <summary> /// <summary>
/// Defines the <see cref="SourceRect"/> property. /// Defines the <see cref="SourceRect"/> property.
@ -20,7 +20,7 @@ namespace Avalonia.Media.Imaging
public static readonly StyledProperty<PixelRect> SourceRectProperty = public static readonly StyledProperty<PixelRect> SourceRectProperty =
AvaloniaProperty.Register<CroppedBitmap, PixelRect>(nameof(SourceRect)); AvaloniaProperty.Register<CroppedBitmap, PixelRect>(nameof(SourceRect));
public event EventHandler Invalidated; public event EventHandler? Invalidated;
static CroppedBitmap() static CroppedBitmap()
{ {
@ -31,7 +31,7 @@ namespace Avalonia.Media.Imaging
/// <summary> /// <summary>
/// Gets or sets the source for the bitmap. /// Gets or sets the source for the bitmap.
/// </summary> /// </summary>
public IImage Source public IImage? Source
{ {
get => GetValue(SourceProperty); get => GetValue(SourceProperty);
set => SetValue(SourceProperty, value); set => SetValue(SourceProperty, value);
@ -77,19 +77,19 @@ namespace Avalonia.Media.Imaging
public Size Size { public Size Size {
get get
{ {
if (Source == null) if (Source is not IBitmap bmp)
return Size.Empty; return Size.Empty;
if (SourceRect.IsEmpty) if (SourceRect.IsEmpty)
return Source.Size; return Source.Size;
return SourceRect.Size.ToSizeWithDpi((Source as IBitmap).Dpi); return SourceRect.Size.ToSizeWithDpi(bmp.Dpi);
} }
} }
public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
{ {
if (Source == null) if (Source is not IBitmap bmp)
return; return;
var topLeft = SourceRect.TopLeft.ToPointWithDpi((Source as IBitmap).Dpi); var topLeft = SourceRect.TopLeft.ToPointWithDpi(bmp.Dpi);
Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode); Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode);
} }
} }

5
src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs

@ -54,11 +54,12 @@ namespace Avalonia.Media.Imaging
/// <returns>The platform-specific implementation.</returns> /// <returns>The platform-specific implementation.</returns>
private static IRenderTargetBitmapImpl CreateImpl(PixelSize size, Vector dpi) private static IRenderTargetBitmapImpl CreateImpl(PixelSize size, Vector dpi)
{ {
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
return factory.CreateRenderTargetBitmap(size, dpi); return factory.CreateRenderTargetBitmap(size, dpi);
} }
/// <inheritdoc/> /// <inheritdoc/>
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer vbr) => PlatformImpl.Item.CreateDrawingContext(vbr); public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? vbr) => PlatformImpl.Item.CreateDrawingContext(vbr);
} }
} }

14
src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs

@ -46,7 +46,7 @@ namespace Avalonia.Media.Imaging
public static WriteableBitmap Decode(Stream stream) public static WriteableBitmap Decode(Stream stream)
{ {
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var ri = GetFactory();
return new WriteableBitmap(ri.LoadWriteableBitmap(stream)); return new WriteableBitmap(ri.LoadWriteableBitmap(stream));
} }
@ -61,7 +61,7 @@ namespace Avalonia.Media.Imaging
/// <returns>An instance of the <see cref="WriteableBitmap"/> class.</returns> /// <returns>An instance of the <see cref="WriteableBitmap"/> class.</returns>
public new static WriteableBitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) public new static WriteableBitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{ {
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var ri = GetFactory();
return new WriteableBitmap(ri.LoadWriteableBitmapToWidth(stream, width, interpolationMode)); return new WriteableBitmap(ri.LoadWriteableBitmapToWidth(stream, width, interpolationMode));
} }
@ -76,19 +76,25 @@ namespace Avalonia.Media.Imaging
/// <returns>An instance of the <see cref="WriteableBitmap"/> class.</returns> /// <returns>An instance of the <see cref="WriteableBitmap"/> class.</returns>
public new static WriteableBitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) public new static WriteableBitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{ {
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var ri = GetFactory();
return new WriteableBitmap(ri.LoadWriteableBitmapToHeight(stream, height, interpolationMode)); return new WriteableBitmap(ri.LoadWriteableBitmapToHeight(stream, height, interpolationMode));
} }
private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat) private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat)
{ {
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var ri = GetFactory();
PixelFormat finalFormat = format ?? ri.DefaultPixelFormat; PixelFormat finalFormat = format ?? ri.DefaultPixelFormat;
AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat; AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat;
return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat); return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat);
} }
private static IPlatformRenderInterface GetFactory()
{
return AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
}
} }
} }

4
src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs

@ -30,10 +30,10 @@ namespace Avalonia.Media.Immutable
public double Offset { get; } public double Offset { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) => Equals(obj as IDashStyle); public override bool Equals(object? obj) => Equals(obj as IDashStyle);
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(IDashStyle other) public bool Equals(IDashStyle? other)
{ {
if (ReferenceEquals(this, other)) if (ReferenceEquals(this, other))
{ {

14
src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs

@ -21,7 +21,7 @@ namespace Avalonia.Media.Immutable
public ImmutablePen( public ImmutablePen(
uint color, uint color,
double thickness = 1.0, double thickness = 1.0,
ImmutableDashStyle dashStyle = null, ImmutableDashStyle? dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat, PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter, PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) : this(new ImmutableSolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit) double miterLimit = 10.0) : this(new ImmutableSolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
@ -38,9 +38,9 @@ namespace Avalonia.Media.Immutable
/// <param name="lineJoin">The line join.</param> /// <param name="lineJoin">The line join.</param>
/// <param name="miterLimit">The miter limit.</param> /// <param name="miterLimit">The miter limit.</param>
public ImmutablePen( public ImmutablePen(
IBrush brush, IBrush? brush,
double thickness = 1.0, double thickness = 1.0,
ImmutableDashStyle dashStyle = null, ImmutableDashStyle? dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat, PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter, PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) double miterLimit = 10.0)
@ -58,7 +58,7 @@ namespace Avalonia.Media.Immutable
/// <summary> /// <summary>
/// Gets the brush used to draw the stroke. /// Gets the brush used to draw the stroke.
/// </summary> /// </summary>
public IBrush Brush { get; } public IBrush? Brush { get; }
/// <summary> /// <summary>
/// Gets the stroke thickness. /// Gets the stroke thickness.
@ -68,7 +68,7 @@ namespace Avalonia.Media.Immutable
/// <summary> /// <summary>
/// Specifies the style of dashed lines drawn with a <see cref="Pen"/> object. /// Specifies the style of dashed lines drawn with a <see cref="Pen"/> object.
/// </summary> /// </summary>
public IDashStyle DashStyle { get; } public IDashStyle? DashStyle { get; }
/// <summary> /// <summary>
/// Specifies the type of graphic shape to use on both ends of a line. /// Specifies the type of graphic shape to use on both ends of a line.
@ -87,10 +87,10 @@ namespace Avalonia.Media.Immutable
public double MiterLimit { get; } public double MiterLimit { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) => Equals(obj as IPen); public override bool Equals(object? obj) => Equals(obj as IPen);
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(IPen other) public bool Equals(IPen? other)
{ {
if (ReferenceEquals(this, other)) if (ReferenceEquals(this, other))
{ {

4
src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs

@ -46,14 +46,14 @@ namespace Avalonia.Media.Immutable
/// </summary> /// </summary>
public double Opacity { get; } public double Opacity { get; }
public bool Equals(ImmutableSolidColorBrush other) public bool Equals(ImmutableSolidColorBrush? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;
return Color.Equals(other.Color) && Opacity.Equals(other.Opacity); return Color.Equals(other.Color) && Opacity.Equals(other.Opacity);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is ImmutableSolidColorBrush other && Equals(other); return obj is ImmutableSolidColorBrush other && Equals(other);
} }

2
src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs

@ -34,7 +34,7 @@ namespace Avalonia.Media
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is ImmutableExperimentalAcrylicMaterial other && Equals(other); return obj is ImmutableExperimentalAcrylicMaterial other && Equals(other);
} }

8
src/Avalonia.Visuals/Media/KnownColors.cs

@ -20,7 +20,7 @@ namespace Avalonia.Media
foreach (var field in typeof(KnownColor).GetRuntimeFields()) foreach (var field in typeof(KnownColor).GetRuntimeFields())
{ {
if (field.FieldType != typeof(KnownColor)) continue; if (field.FieldType != typeof(KnownColor)) continue;
var knownColor = (KnownColor)field.GetValue(null); var knownColor = (KnownColor)field.GetValue(null)!;
if (knownColor == KnownColor.None) continue; if (knownColor == KnownColor.None) continue;
knownColorNames.Add(field.Name, knownColor); knownColorNames.Add(field.Name, knownColor);
@ -41,7 +41,7 @@ namespace Avalonia.Media
} }
#if !BUILDTASK #if !BUILDTASK
public static ISolidColorBrush GetKnownBrush(string s) public static ISolidColorBrush? GetKnownBrush(string s)
{ {
var color = GetKnownColor(s); var color = GetKnownColor(s);
return color != KnownColor.None ? color.ToBrush() : null; return color != KnownColor.None ? color.ToBrush() : null;
@ -58,7 +58,7 @@ namespace Avalonia.Media
return KnownColor.None; return KnownColor.None;
} }
public static string GetKnownColorName(uint rgb) public static string? GetKnownColorName(uint rgb)
{ {
return _knownColors.TryGetValue(rgb, out var name) ? name : null; return _knownColors.TryGetValue(rgb, out var name) ? name : null;
} }
@ -230,4 +230,4 @@ namespace Avalonia.Media
Yellow = 0xffffff00, Yellow = 0xffffff00,
YellowGreen = 0xff9acd32 YellowGreen = 0xff9acd32
} }
} }

6
src/Avalonia.Visuals/Media/LineGeometry.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Media namespace Avalonia.Media
@ -67,9 +68,10 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override IGeometryImpl CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
return factory.CreateLineGeometry(StartPoint, EndPoint); return factory.CreateLineGeometry(StartPoint, EndPoint);
} }

2
src/Avalonia.Visuals/Media/MaterialExtensions.cs

@ -14,7 +14,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public static IExperimentalAcrylicMaterial ToImmutable(this IExperimentalAcrylicMaterial material) public static IExperimentalAcrylicMaterial ToImmutable(this IExperimentalAcrylicMaterial material)
{ {
Contract.Requires<ArgumentNullException>(material != null); _ = material ?? throw new ArgumentNullException(nameof(material));
return (material as IMutableExperimentalAcrylicMaterial)?.ToImmutable() ?? material; return (material as IMutableExperimentalAcrylicMaterial)?.ToImmutable() ?? material;
} }

35
src/Avalonia.Visuals/Media/PathGeometry.cs

@ -11,8 +11,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Figures"/> property. /// Defines the <see cref="Figures"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<PathGeometry, PathFigures> FiguresProperty = public static readonly DirectProperty<PathGeometry, PathFigures?> FiguresProperty =
AvaloniaProperty.RegisterDirect<PathGeometry, PathFigures>(nameof(Figures), g => g.Figures, (g, f) => g.Figures = f); AvaloniaProperty.RegisterDirect<PathGeometry, PathFigures?>(nameof(Figures), g => g.Figures, (g, f) => g.Figures = f);
/// <summary> /// <summary>
/// Defines the <see cref="FillRule"/> property. /// Defines the <see cref="FillRule"/> property.
@ -20,9 +20,9 @@ namespace Avalonia.Media
public static readonly StyledProperty<FillRule> FillRuleProperty = public static readonly StyledProperty<FillRule> FillRuleProperty =
AvaloniaProperty.Register<PathGeometry, FillRule>(nameof(FillRule)); AvaloniaProperty.Register<PathGeometry, FillRule>(nameof(FillRule));
private PathFigures _figures; private PathFigures? _figures;
private IDisposable _figuresObserver; private IDisposable? _figuresObserver;
private IDisposable _figuresPropertiesObserver; private IDisposable? _figuresPropertiesObserver;
static PathGeometry() static PathGeometry()
{ {
@ -35,7 +35,7 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public PathGeometry() public PathGeometry()
{ {
Figures = new PathFigures(); _figures = new PathFigures();
} }
/// <summary> /// <summary>
@ -63,7 +63,7 @@ namespace Avalonia.Media
/// The figures. /// The figures.
/// </value> /// </value>
[Content] [Content]
public PathFigures Figures public PathFigures? Figures
{ {
get { return _figures; } get { return _figures; }
set { SetAndRaise(FiguresProperty, ref _figures, value); } set { SetAndRaise(FiguresProperty, ref _figures, value); }
@ -81,15 +81,21 @@ namespace Avalonia.Media
set { SetValue(FillRuleProperty, value); } set { SetValue(FillRuleProperty, value); }
} }
protected override IGeometryImpl CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var figures = Figures;
if (figures is null)
return null;
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
var geometry = factory.CreateStreamGeometry(); var geometry = factory.CreateStreamGeometry();
using (var ctx = new StreamGeometryContext(geometry.Open())) using (var ctx = new StreamGeometryContext(geometry.Open()))
{ {
ctx.SetFillRule(FillRule); ctx.SetFillRule(FillRule);
foreach (var f in Figures) foreach (var f in figures)
{ {
f.ApplyTo(ctx); f.ApplyTo(ctx);
} }
@ -98,7 +104,7 @@ namespace Avalonia.Media
return geometry; return geometry;
} }
private void OnFiguresChanged(PathFigures figures) private void OnFiguresChanged(PathFigures? figures)
{ {
_figuresObserver?.Dispose(); _figuresObserver?.Dispose();
_figuresPropertiesObserver?.Dispose(); _figuresPropertiesObserver?.Dispose();
@ -120,12 +126,15 @@ namespace Avalonia.Media
} }
private void InvalidateGeometryFromSegments(object _, EventArgs __) private void InvalidateGeometryFromSegments(object? _, EventArgs __)
{ {
InvalidateGeometry(); InvalidateGeometry();
} }
public override string ToString() public override string ToString()
=> $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}"; {
var figuresString = _figures is not null ? string.Join(" ", _figures) : string.Empty;
return $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{figuresString}";
}
} }
} }

4
src/Avalonia.Visuals/Media/PathGeometryCollections.cs

@ -20,11 +20,11 @@ namespace Avalonia.Media
parser.Parse(pathData); parser.Parse(pathData);
} }
return pathGeometry.Figures; return pathGeometry.Figures!;
} }
} }
public sealed class PathSegments : AvaloniaList<PathSegment> public sealed class PathSegments : AvaloniaList<PathSegment>
{ {
} }
} }

34
src/Avalonia.Visuals/Media/PathMarkupParser.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using Avalonia.Platform; using Avalonia.Platform;
@ -27,7 +28,7 @@ namespace Avalonia.Media
{ 'Z', Command.Close }, { 'Z', Command.Close },
}; };
private IGeometryContext _geometryContext; private IGeometryContext? _geometryContext;
private Point _currentPoint; private Point _currentPoint;
private Point? _beginFigurePoint; private Point? _beginFigurePoint;
private Point? _previousControlPoint; private Point? _previousControlPoint;
@ -98,6 +99,8 @@ namespace Avalonia.Media
/// <param name="pathData">The path data.</param> /// <param name="pathData">The path data.</param>
public void Parse(string pathData) public void Parse(string pathData)
{ {
ThrowIfDisposed();
var span = pathData.AsSpan(); var span = pathData.AsSpan();
_currentPoint = new Point(); _currentPoint = new Point();
@ -171,6 +174,8 @@ namespace Avalonia.Media
private void CreateFigure() private void CreateFigure()
{ {
ThrowIfDisposed();
if (_isOpen) if (_isOpen)
{ {
_geometryContext.EndFigure(false); _geometryContext.EndFigure(false);
@ -185,6 +190,8 @@ namespace Avalonia.Media
private void SetFillRule(ref ReadOnlySpan<char> span) private void SetFillRule(ref ReadOnlySpan<char> span)
{ {
ThrowIfDisposed();
if (!ReadArgument(ref span, out var fillRule) || fillRule.Length != 1) if (!ReadArgument(ref span, out var fillRule) || fillRule.Length != 1)
{ {
throw new InvalidDataException("Invalid fill rule."); throw new InvalidDataException("Invalid fill rule.");
@ -209,6 +216,8 @@ namespace Avalonia.Media
private void CloseFigure() private void CloseFigure()
{ {
ThrowIfDisposed();
if (_isOpen) if (_isOpen)
{ {
_geometryContext.EndFigure(true); _geometryContext.EndFigure(true);
@ -244,6 +253,8 @@ namespace Avalonia.Media
private void AddLine(ref ReadOnlySpan<char> span, bool relative) private void AddLine(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
_currentPoint = relative _currentPoint = relative
? ReadRelativePoint(ref span, _currentPoint) ? ReadRelativePoint(ref span, _currentPoint)
: ReadPoint(ref span); : ReadPoint(ref span);
@ -258,6 +269,8 @@ namespace Avalonia.Media
private void AddHorizontalLine(ref ReadOnlySpan<char> span, bool relative) private void AddHorizontalLine(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
_currentPoint = relative _currentPoint = relative
? new Point(_currentPoint.X + ReadDouble(ref span), _currentPoint.Y) ? new Point(_currentPoint.X + ReadDouble(ref span), _currentPoint.Y)
: _currentPoint.WithX(ReadDouble(ref span)); : _currentPoint.WithX(ReadDouble(ref span));
@ -272,6 +285,8 @@ namespace Avalonia.Media
private void AddVerticalLine(ref ReadOnlySpan<char> span, bool relative) private void AddVerticalLine(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
_currentPoint = relative _currentPoint = relative
? new Point(_currentPoint.X, _currentPoint.Y + ReadDouble(ref span)) ? new Point(_currentPoint.X, _currentPoint.Y + ReadDouble(ref span))
: _currentPoint.WithY(ReadDouble(ref span)); : _currentPoint.WithY(ReadDouble(ref span));
@ -286,6 +301,8 @@ namespace Avalonia.Media
private void AddCubicBezierCurve(ref ReadOnlySpan<char> span, bool relative) private void AddCubicBezierCurve(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
var point1 = relative var point1 = relative
? ReadRelativePoint(ref span, _currentPoint) ? ReadRelativePoint(ref span, _currentPoint)
: ReadPoint(ref span); : ReadPoint(ref span);
@ -316,6 +333,8 @@ namespace Avalonia.Media
private void AddQuadraticBezierCurve(ref ReadOnlySpan<char> span, bool relative) private void AddQuadraticBezierCurve(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
var start = relative var start = relative
? ReadRelativePoint(ref span, _currentPoint) ? ReadRelativePoint(ref span, _currentPoint)
: ReadPoint(ref span); : ReadPoint(ref span);
@ -340,6 +359,8 @@ namespace Avalonia.Media
private void AddSmoothCubicBezierCurve(ref ReadOnlySpan<char> span, bool relative) private void AddSmoothCubicBezierCurve(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
var point2 = relative var point2 = relative
? ReadRelativePoint(ref span, _currentPoint) ? ReadRelativePoint(ref span, _currentPoint)
: ReadPoint(ref span); : ReadPoint(ref span);
@ -369,6 +390,8 @@ namespace Avalonia.Media
private void AddSmoothQuadraticBezierCurve(ref ReadOnlySpan<char> span, bool relative) private void AddSmoothQuadraticBezierCurve(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
var end = relative var end = relative
? ReadRelativePoint(ref span, _currentPoint) ? ReadRelativePoint(ref span, _currentPoint)
: ReadPoint(ref span); : ReadPoint(ref span);
@ -390,6 +413,8 @@ namespace Avalonia.Media
private void AddArc(ref ReadOnlySpan<char> span, bool relative) private void AddArc(ref ReadOnlySpan<char> span, bool relative)
{ {
ThrowIfDisposed();
var size = ReadSize(ref span); var size = ReadSize(ref span);
span = ReadSeparator(span); span = ReadSeparator(span);
@ -570,5 +595,12 @@ namespace Avalonia.Media
span = span.Slice(1); span = span.Slice(1);
return true; return true;
} }
[MemberNotNull(nameof(_geometryContext))]
private void ThrowIfDisposed()
{
if (_isDisposed || _geometryContext is null)
throw new ObjectDisposedException(nameof(PathMarkupParser));
}
} }
} }

22
src/Avalonia.Visuals/Media/Pen.cs

@ -12,8 +12,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Brush"/> property. /// Defines the <see cref="Brush"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IBrush> BrushProperty = public static readonly StyledProperty<IBrush?> BrushProperty =
AvaloniaProperty.Register<Pen, IBrush>(nameof(Brush)); AvaloniaProperty.Register<Pen, IBrush?>(nameof(Brush));
/// <summary> /// <summary>
/// Defines the <see cref="Thickness"/> property. /// Defines the <see cref="Thickness"/> property.
@ -24,8 +24,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="DashStyle"/> property. /// Defines the <see cref="DashStyle"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IDashStyle> DashStyleProperty = public static readonly StyledProperty<IDashStyle?> DashStyleProperty =
AvaloniaProperty.Register<Pen, IDashStyle>(nameof(DashStyle)); AvaloniaProperty.Register<Pen, IDashStyle?>(nameof(DashStyle));
/// <summary> /// <summary>
/// Defines the <see cref="LineCap"/> property. /// Defines the <see cref="LineCap"/> property.
@ -64,7 +64,7 @@ namespace Avalonia.Media
public Pen( public Pen(
uint color, uint color,
double thickness = 1.0, double thickness = 1.0,
IDashStyle dashStyle = null, IDashStyle? dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat, PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter, PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit) double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
@ -81,9 +81,9 @@ namespace Avalonia.Media
/// <param name="lineJoin">The line join.</param> /// <param name="lineJoin">The line join.</param>
/// <param name="miterLimit">The miter limit.</param> /// <param name="miterLimit">The miter limit.</param>
public Pen( public Pen(
IBrush brush, IBrush? brush,
double thickness = 1.0, double thickness = 1.0,
IDashStyle dashStyle = null, IDashStyle? dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat, PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter, PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) double miterLimit = 10.0)
@ -110,7 +110,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the brush used to draw the stroke. /// Gets or sets the brush used to draw the stroke.
/// </summary> /// </summary>
public IBrush Brush public IBrush? Brush
{ {
get => GetValue(BrushProperty); get => GetValue(BrushProperty);
set => SetValue(BrushProperty, value); set => SetValue(BrushProperty, value);
@ -128,7 +128,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the style of dashed lines drawn with a <see cref="Pen"/> object. /// Gets or sets the style of dashed lines drawn with a <see cref="Pen"/> object.
/// </summary> /// </summary>
public IDashStyle DashStyle public IDashStyle? DashStyle
{ {
get => GetValue(DashStyleProperty); get => GetValue(DashStyleProperty);
set => SetValue(DashStyleProperty, value); set => SetValue(DashStyleProperty, value);
@ -165,7 +165,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Raised when the pen changes. /// Raised when the pen changes.
/// </summary> /// </summary>
public event EventHandler Invalidated; public event EventHandler? Invalidated;
/// <summary> /// <summary>
/// Creates an immutable clone of the brush. /// Creates an immutable clone of the brush.
@ -244,6 +244,6 @@ namespace Avalonia.Media
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e); protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);
private void AffectsRenderInvalidated(object sender, EventArgs e) => RaiseInvalidated(EventArgs.Empty); private void AffectsRenderInvalidated(object? sender, EventArgs e) => RaiseInvalidated(EventArgs.Empty);
} }
} }

2
src/Avalonia.Visuals/Media/PixelPoint.cs

@ -144,7 +144,7 @@ namespace Avalonia
/// <returns> /// <returns>
/// True if <paramref name="obj"/> is a point that equals the current point. /// True if <paramref name="obj"/> is a point that equals the current point.
/// </returns> /// </returns>
public override bool Equals(object obj) => obj is PixelPoint other && Equals(other); public override bool Equals(object? obj) => obj is PixelPoint other && Equals(other);
/// <summary> /// <summary>
/// Returns a hash code for a <see cref="PixelPoint"/>. /// Returns a hash code for a <see cref="PixelPoint"/>.

2
src/Avalonia.Visuals/Media/PixelRect.cs

@ -208,7 +208,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The object to compare against.</param> /// <param name="obj">The object to compare against.</param>
/// <returns>True if the object is equal to this rectangle; false otherwise.</returns> /// <returns>True if the object is equal to this rectangle; false otherwise.</returns>
public override bool Equals(object obj) => obj is PixelRect other && Equals(other); public override bool Equals(object? obj) => obj is PixelRect other && Equals(other);
/// <summary> /// <summary>
/// Returns the hash code for this instance. /// Returns the hash code for this instance.

2
src/Avalonia.Visuals/Media/PixelSize.cs

@ -94,7 +94,7 @@ namespace Avalonia
/// <returns> /// <returns>
/// True if <paramref name="obj"/> is a size that equals the current size. /// True if <paramref name="obj"/> is a size that equals the current size.
/// </returns> /// </returns>
public override bool Equals(object obj) => obj is PixelSize other && Equals(other); public override bool Equals(object? obj) => obj is PixelSize other && Equals(other);
/// <summary> /// <summary>
/// Returns a hash code for a <see cref="PixelSize"/>. /// Returns a hash code for a <see cref="PixelSize"/>.

2
src/Avalonia.Visuals/Media/PixelVector.cs

@ -143,7 +143,7 @@ namespace Avalonia
return Math.Abs(_x - other._x) < tolerance && Math.Abs(_y - other._y) < tolerance; return Math.Abs(_x - other._x) < tolerance && Math.Abs(_y - other._y) < tolerance;
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(null, obj)) return false;

11
src/Avalonia.Visuals/Media/PolylineGeometry.cs

@ -24,7 +24,7 @@ namespace Avalonia.Media
AvaloniaProperty.Register<PolylineGeometry, bool>(nameof(IsFilled)); AvaloniaProperty.Register<PolylineGeometry, bool>(nameof(IsFilled));
private Points _points; private Points _points;
private IDisposable _pointsObserver; private IDisposable? _pointsObserver;
static PolylineGeometry() static PolylineGeometry()
{ {
@ -37,7 +37,7 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public PolylineGeometry() public PolylineGeometry()
{ {
Points = new Points(); _points = new Points();
} }
/// <summary> /// <summary>
@ -74,9 +74,10 @@ namespace Avalonia.Media
return new PolylineGeometry(Points, IsFilled); return new PolylineGeometry(Points, IsFilled);
} }
protected override IGeometryImpl CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
var geometry = factory.CreateStreamGeometry(); var geometry = factory.CreateStreamGeometry();
using (var context = geometry.Open()) using (var context = geometry.Open())
@ -97,7 +98,7 @@ namespace Avalonia.Media
return geometry; return geometry;
} }
private void OnPointsChanged(Points newValue) private void OnPointsChanged(Points? newValue)
{ {
_pointsObserver?.Dispose(); _pointsObserver?.Dispose();
_pointsObserver = newValue?.ForEachItem( _pointsObserver = newValue?.ForEachItem(

6
src/Avalonia.Visuals/Media/RectangleGeometry.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Media namespace Avalonia.Media
@ -46,9 +47,10 @@ namespace Avalonia.Media
/// <inheritdoc/> /// <inheritdoc/>
public override Geometry Clone() => new RectangleGeometry(Rect); public override Geometry Clone() => new RectangleGeometry(Rect);
protected override IGeometryImpl CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
return factory.CreateRectangleGeometry(Rect); return factory.CreateRectangleGeometry(Rect);
} }

12
src/Avalonia.Visuals/Media/StreamGeometry.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Media namespace Avalonia.Media
@ -7,7 +8,7 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public class StreamGeometry : Geometry public class StreamGeometry : Geometry
{ {
IStreamGeometryImpl _impl; IStreamGeometryImpl? _impl;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StreamGeometry"/> class. /// Initializes a new instance of the <see cref="StreamGeometry"/> class.
@ -46,7 +47,7 @@ namespace Avalonia.Media
/// <inheritdoc/> /// <inheritdoc/>
public override Geometry Clone() public override Geometry Clone()
{ {
return new StreamGeometry(((IStreamGeometryImpl)PlatformImpl).Clone()); return new StreamGeometry(((IStreamGeometryImpl)PlatformImpl!).Clone());
} }
/// <summary> /// <summary>
@ -57,15 +58,16 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public StreamGeometryContext Open() public StreamGeometryContext Open()
{ {
return new StreamGeometryContext(((IStreamGeometryImpl)PlatformImpl).Open()); return new StreamGeometryContext(((IStreamGeometryImpl)PlatformImpl!).Open());
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override IGeometryImpl CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
if (_impl == null) if (_impl == null)
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
_impl = factory.CreateStreamGeometry(); _impl = factory.CreateStreamGeometry();
} }

14
src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs

@ -10,9 +10,9 @@ namespace Avalonia.Media.TextFormatting
private const double DefaultFontRenderingEmSize = 12; private const double DefaultFontRenderingEmSize = 12;
public GenericTextRunProperties(Typeface typeface, double fontRenderingEmSize = DefaultFontRenderingEmSize, public GenericTextRunProperties(Typeface typeface, double fontRenderingEmSize = DefaultFontRenderingEmSize,
TextDecorationCollection textDecorations = null, IBrush foregroundBrush = null, TextDecorationCollection? textDecorations = null, IBrush? foregroundBrush = null,
IBrush backgroundBrush = null, BaselineAlignment baselineAlignment = BaselineAlignment.Baseline, IBrush? backgroundBrush = null, BaselineAlignment baselineAlignment = BaselineAlignment.Baseline,
CultureInfo cultureInfo = null) CultureInfo? cultureInfo = null)
{ {
Typeface = typeface; Typeface = typeface;
FontRenderingEmSize = fontRenderingEmSize; FontRenderingEmSize = fontRenderingEmSize;
@ -30,18 +30,18 @@ namespace Avalonia.Media.TextFormatting
public override double FontRenderingEmSize { get; } public override double FontRenderingEmSize { get; }
/// <inheritdoc /> /// <inheritdoc />
public override TextDecorationCollection TextDecorations { get; } public override TextDecorationCollection? TextDecorations { get; }
/// <inheritdoc /> /// <inheritdoc />
public override IBrush ForegroundBrush { get; } public override IBrush? ForegroundBrush { get; }
/// <inheritdoc /> /// <inheritdoc />
public override IBrush BackgroundBrush { get; } public override IBrush? BackgroundBrush { get; }
/// <inheritdoc /> /// <inheritdoc />
public override BaselineAlignment BaselineAlignment { get; } public override BaselineAlignment BaselineAlignment { get; }
/// <inheritdoc /> /// <inheritdoc />
public override CultureInfo CultureInfo { get; } public override CultureInfo? CultureInfo { get; }
} }
} }

2
src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs

@ -10,6 +10,6 @@
/// </summary> /// </summary>
/// <param name="textSourceIndex">The text source index.</param> /// <param name="textSourceIndex">The text source index.</param>
/// <returns>The text run.</returns> /// <returns>The text run.</returns>
TextRun GetTextRun(int textSourceIndex); TextRun? GetTextRun(int textSourceIndex);
} }
} }

4
src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs

@ -166,7 +166,7 @@ namespace Avalonia.Media.TextFormatting
public readonly struct SplitTextCharactersResult public readonly struct SplitTextCharactersResult
{ {
public SplitTextCharactersResult(ShapedTextCharacters first, ShapedTextCharacters second) public SplitTextCharactersResult(ShapedTextCharacters first, ShapedTextCharacters? second)
{ {
First = first; First = first;
@ -187,7 +187,7 @@ namespace Avalonia.Media.TextFormatting
/// <value> /// <value>
/// The second text run. /// The second text run.
/// </value> /// </value>
public ShapedTextCharacters Second { get; } public ShapedTextCharacters? Second { get; }
} }
} }
} }

2
src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs

@ -41,6 +41,6 @@ namespace Avalonia.Media.TextFormatting
/// in terms of where the previous line in the paragraph was broken by the text formatting process.</param> /// in terms of where the previous line in the paragraph was broken by the text formatting process.</param>
/// <returns>The formatted line.</returns> /// <returns>The formatted line.</returns>
public abstract TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, public abstract TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null); TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null);
} }
} }

26
src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs

@ -8,7 +8,7 @@ namespace Avalonia.Media.TextFormatting
{ {
/// <inheritdoc cref="TextFormatter.FormatLine"/> /// <inheritdoc cref="TextFormatter.FormatLine"/>
public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null) TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null)
{ {
var textWrapping = paragraphProperties.TextWrapping; var textWrapping = paragraphProperties.TextWrapping;
@ -241,7 +241,7 @@ namespace Avalonia.Media.TextFormatting
first.Add(split.First); first.Add(split.First);
second.Add(split.Second); second.Add(split.Second!);
if (secondCount > 0) if (secondCount > 0)
{ {
@ -269,7 +269,7 @@ namespace Avalonia.Media.TextFormatting
/// The formatted text runs. /// The formatted text runs.
/// </returns> /// </returns>
private static List<ShapedTextCharacters> FetchTextRuns(ITextSource textSource, private static List<ShapedTextCharacters> FetchTextRuns(ITextSource textSource,
int firstTextSourceIndex, TextLineBreak previousLineBreak, out TextLineBreak nextLineBreak) int firstTextSourceIndex, TextLineBreak? previousLineBreak, out TextLineBreak? nextLineBreak)
{ {
nextLineBreak = default; nextLineBreak = default;
@ -277,7 +277,7 @@ namespace Avalonia.Media.TextFormatting
var textRuns = new List<ShapedTextCharacters>(); var textRuns = new List<ShapedTextCharacters>();
if (previousLineBreak != null) if (previousLineBreak?.RemainingCharacters != null)
{ {
for (var index = 0; index < previousLineBreak.RemainingCharacters.Count; index++) for (var index = 0; index < previousLineBreak.RemainingCharacters.Count; index++)
{ {
@ -298,11 +298,11 @@ namespace Avalonia.Media.TextFormatting
{ {
for (; index < previousLineBreak.RemainingCharacters.Count; index++) for (; index < previousLineBreak.RemainingCharacters.Count; index++)
{ {
splitResult.Second.Add(previousLineBreak.RemainingCharacters[index]); splitResult.Second!.Add(previousLineBreak.RemainingCharacters[index]);
} }
} }
nextLineBreak = new TextLineBreak(splitResult.Second); nextLineBreak = new TextLineBreak(splitResult.Second!);
return splitResult.First; return splitResult.First;
} }
@ -317,7 +317,7 @@ namespace Avalonia.Media.TextFormatting
while (textRunEnumerator.MoveNext()) while (textRunEnumerator.MoveNext())
{ {
var textRun = textRunEnumerator.Current; var textRun = textRunEnumerator.Current!;
switch (textRun) switch (textRun)
{ {
@ -346,7 +346,7 @@ namespace Avalonia.Media.TextFormatting
{ {
var splitResult = SplitTextRuns(textRuns, currentLength + runLineBreak.PositionWrap); var splitResult = SplitTextRuns(textRuns, currentLength + runLineBreak.PositionWrap);
nextLineBreak = new TextLineBreak(splitResult.Second); nextLineBreak = new TextLineBreak(splitResult.Second!);
return splitResult.First; return splitResult.First;
} }
@ -398,7 +398,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param> /// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param>
/// <returns>The wrapped text line.</returns> /// <returns>The wrapped text line.</returns>
private static TextLine PerformTextWrapping(List<ShapedTextCharacters> textRuns, TextRange textRange, private static TextLine PerformTextWrapping(List<ShapedTextCharacters> textRuns, TextRange textRange,
double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak currentLineBreak) double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak? currentLineBreak)
{ {
var availableWidth = paragraphWidth; var availableWidth = paragraphWidth;
var currentWidth = 0.0; var currentWidth = 0.0;
@ -517,7 +517,7 @@ namespace Avalonia.Media.TextFormatting
var lineBreak = remainingCharacters?.Count > 0 ? new TextLineBreak(remainingCharacters) : null; var lineBreak = remainingCharacters?.Count > 0 ? new TextLineBreak(remainingCharacters) : null;
if (lineBreak is null && currentLineBreak.TextEndOfLine != null) if (lineBreak is null && currentLineBreak?.TextEndOfLine != null)
{ {
lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine); lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine);
} }
@ -553,7 +553,7 @@ namespace Avalonia.Media.TextFormatting
internal readonly struct SplitTextRunsResult internal readonly struct SplitTextRunsResult
{ {
public SplitTextRunsResult(List<ShapedTextCharacters> first, List<ShapedTextCharacters> second) public SplitTextRunsResult(List<ShapedTextCharacters> first, List<ShapedTextCharacters>? second)
{ {
First = first; First = first;
@ -574,7 +574,7 @@ namespace Avalonia.Media.TextFormatting
/// <value> /// <value>
/// The second text runs. /// The second text runs.
/// </value> /// </value>
public List<ShapedTextCharacters> Second { get; } public List<ShapedTextCharacters>? Second { get; }
} }
private struct TextRunEnumerator private struct TextRunEnumerator
@ -590,7 +590,7 @@ namespace Avalonia.Media.TextFormatting
} }
// ReSharper disable once MemberHidesStaticFromOuterClass // ReSharper disable once MemberHidesStaticFromOuterClass
public TextRun Current { get; private set; } public TextRun? Current { get; private set; }
public bool MoveNext() public bool MoveNext()
{ {

22
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -14,7 +15,7 @@ namespace Avalonia.Media.TextFormatting
private readonly ReadOnlySlice<char> _text; private readonly ReadOnlySlice<char> _text;
private readonly TextParagraphProperties _paragraphProperties; private readonly TextParagraphProperties _paragraphProperties;
private readonly IReadOnlyList<ValueSpan<TextRunProperties>> _textStyleOverrides; private readonly IReadOnlyList<ValueSpan<TextRunProperties>>? _textStyleOverrides;
private readonly TextTrimming _textTrimming; private readonly TextTrimming _textTrimming;
/// <summary> /// <summary>
@ -41,12 +42,12 @@ namespace Avalonia.Media.TextFormatting
TextAlignment textAlignment = TextAlignment.Left, TextAlignment textAlignment = TextAlignment.Left,
TextWrapping textWrapping = TextWrapping.NoWrap, TextWrapping textWrapping = TextWrapping.NoWrap,
TextTrimming textTrimming = TextTrimming.None, TextTrimming textTrimming = TextTrimming.None,
TextDecorationCollection textDecorations = null, TextDecorationCollection? textDecorations = null,
double maxWidth = double.PositiveInfinity, double maxWidth = double.PositiveInfinity,
double maxHeight = double.PositiveInfinity, double maxHeight = double.PositiveInfinity,
double lineHeight = double.NaN, double lineHeight = double.NaN,
int maxLines = 0, int maxLines = 0,
IReadOnlyList<ValueSpan<TextRunProperties>> textStyleOverrides = null) IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides = null)
{ {
_text = string.IsNullOrEmpty(text) ? _text = string.IsNullOrEmpty(text) ?
new ReadOnlySlice<char>() : new ReadOnlySlice<char>() :
@ -228,7 +229,7 @@ namespace Avalonia.Media.TextFormatting
var currentY = 0d; var currentY = 0d;
var lineIndex = 0; var lineIndex = 0;
TextLine currentLine = null; TextLine? currentLine = null;
CharacterHit characterHit; CharacterHit characterHit;
for (; lineIndex < TextLines.Count; lineIndex++) for (; lineIndex < TextLines.Count; lineIndex++)
@ -289,7 +290,7 @@ namespace Avalonia.Media.TextFormatting
/// <returns></returns> /// <returns></returns>
private static TextParagraphProperties CreateTextParagraphProperties(Typeface typeface, double fontSize, private static TextParagraphProperties CreateTextParagraphProperties(Typeface typeface, double fontSize,
IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping, IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping,
TextDecorationCollection textDecorations, double lineHeight) TextDecorationCollection? textDecorations, double lineHeight)
{ {
var textRunStyle = new GenericTextRunProperties(typeface, fontSize, textDecorations, foreground); var textRunStyle = new GenericTextRunProperties(typeface, fontSize, textDecorations, foreground);
@ -339,6 +340,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// Updates the layout and applies specified text style overrides. /// Updates the layout and applies specified text style overrides.
/// </summary> /// </summary>
[MemberNotNull(nameof(TextLines))]
private void UpdateLayout() private void UpdateLayout()
{ {
if (_text.IsEmpty || MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight)) if (_text.IsEmpty || MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight))
@ -360,7 +362,7 @@ namespace Avalonia.Media.TextFormatting
var textSource = new FormattedTextSource(_text, var textSource = new FormattedTextSource(_text,
_paragraphProperties.DefaultTextRunProperties, _textStyleOverrides); _paragraphProperties.DefaultTextRunProperties, _textStyleOverrides);
TextLine previousLine = null; TextLine? previousLine = null;
while (currentPosition < _text.Length) while (currentPosition < _text.Length)
{ {
@ -558,17 +560,17 @@ namespace Avalonia.Media.TextFormatting
{ {
private readonly ReadOnlySlice<char> _text; private readonly ReadOnlySlice<char> _text;
private readonly TextRunProperties _defaultProperties; private readonly TextRunProperties _defaultProperties;
private readonly IReadOnlyList<ValueSpan<TextRunProperties>> _textModifier; private readonly IReadOnlyList<ValueSpan<TextRunProperties>>? _textModifier;
public FormattedTextSource(ReadOnlySlice<char> text, TextRunProperties defaultProperties, public FormattedTextSource(ReadOnlySlice<char> text, TextRunProperties defaultProperties,
IReadOnlyList<ValueSpan<TextRunProperties>> textModifier) IReadOnlyList<ValueSpan<TextRunProperties>>? textModifier)
{ {
_text = text; _text = text;
_defaultProperties = defaultProperties; _defaultProperties = defaultProperties;
_textModifier = textModifier; _textModifier = textModifier;
} }
public TextRun GetTextRun(int textSourceIndex) public TextRun? GetTextRun(int textSourceIndex)
{ {
if (textSourceIndex > _text.Length) if (textSourceIndex > _text.Length)
{ {
@ -597,7 +599,7 @@ namespace Avalonia.Media.TextFormatting
/// The created text style run. /// The created text style run.
/// </returns> /// </returns>
private static ValueSpan<TextRunProperties> CreateTextStyleRun(ReadOnlySlice<char> text, private static ValueSpan<TextRunProperties> CreateTextStyleRun(ReadOnlySlice<char> text,
TextRunProperties defaultProperties, IReadOnlyList<ValueSpan<TextRunProperties>> textModifier) TextRunProperties defaultProperties, IReadOnlyList<ValueSpan<TextRunProperties>>? textModifier)
{ {
if (textModifier == null || textModifier.Count == 0) if (textModifier == null || textModifier.Count == 0)
{ {

2
src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs

@ -29,7 +29,7 @@ namespace Avalonia.Media.TextFormatting
/// <returns> /// <returns>
/// A <see cref="TextLineBreak"/> value that represents the line break. /// A <see cref="TextLineBreak"/> value that represents the line break.
/// </returns> /// </returns>
public abstract TextLineBreak TextLineBreak { get; } public abstract TextLineBreak? TextLineBreak { get; }
/// <summary> /// <summary>
/// Gets the distance from the top to the baseline of the current TextLine object. /// Gets the distance from the top to the baseline of the current TextLine object.

4
src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs

@ -17,11 +17,11 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// Get the /// Get the
/// </summary> /// </summary>
public TextEndOfLine TextEndOfLine { get; } public TextEndOfLine? TextEndOfLine { get; }
/// <summary> /// <summary>
/// Get the remaining shaped characters that were split up by the <see cref="TextFormatter"/> during the formatting process. /// Get the remaining shaped characters that were split up by the <see cref="TextFormatter"/> during the formatting process.
/// </summary> /// </summary>
public IReadOnlyList<ShapedTextCharacters> RemainingCharacters { get; } public IReadOnlyList<ShapedTextCharacters>? RemainingCharacters { get; }
} }
} }

16
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -13,7 +14,7 @@ namespace Avalonia.Media.TextFormatting
private readonly TextLineMetrics _textLineMetrics; private readonly TextLineMetrics _textLineMetrics;
public TextLineImpl(List<ShapedTextCharacters> textRuns, TextRange textRange, double paragraphWidth, public TextLineImpl(List<ShapedTextCharacters> textRuns, TextRange textRange, double paragraphWidth,
TextParagraphProperties paragraphProperties, TextLineBreak lineBreak = null, bool hasCollapsed = false) TextParagraphProperties paragraphProperties, TextLineBreak? lineBreak = null, bool hasCollapsed = false)
{ {
TextRange = textRange; TextRange = textRange;
TextLineBreak = lineBreak; TextLineBreak = lineBreak;
@ -33,7 +34,7 @@ namespace Avalonia.Media.TextFormatting
public override TextRange TextRange { get; } public override TextRange TextRange { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override TextLineBreak TextLineBreak { get; } public override TextLineBreak? TextLineBreak { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override bool HasCollapsed { get; } public override bool HasCollapsed { get; }
@ -479,12 +480,13 @@ namespace Avalonia.Media.TextFormatting
/// </returns> /// </returns>
internal static ShapedTextCharacters CreateShapedSymbol(TextRun textRun) internal static ShapedTextCharacters CreateShapedSymbol(TextRun textRun)
{ {
var formatterImpl = AvaloniaLocator.Current.GetService<ITextShaperImpl>(); var properties = textRun.Properties;
var glyphRun = formatterImpl.ShapeText(textRun.Text, textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize, _ = properties ?? throw new InvalidOperationException($"{nameof(TextRun.Properties)} should not be null.");
textRun.Properties.CultureInfo);
return new ShapedTextCharacters(glyphRun, textRun.Properties); var glyphRun = TextShaper.Current.ShapeText(textRun.Text, properties.Typeface, properties.FontRenderingEmSize, properties.CultureInfo);
return new ShapedTextCharacters(glyphRun, properties);
} }
} }
} }

2
src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs

@ -44,7 +44,7 @@
/// If not null, text decorations to apply to all runs in the line. This is in addition /// If not null, text decorations to apply to all runs in the line. This is in addition
/// to any text decorations specified by the TextRunProperties for individual text runs. /// to any text decorations specified by the TextRunProperties for individual text runs.
/// </summary> /// </summary>
public virtual TextDecorationCollection TextDecorations => null; public virtual TextDecorationCollection? TextDecorations => null;
/// <summary> /// <summary>
/// Gets the text wrapping. /// Gets the text wrapping.

4
src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs

@ -24,7 +24,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// A set of properties shared by every characters in the run /// A set of properties shared by every characters in the run
/// </summary> /// </summary>
public virtual TextRunProperties Properties => null; public virtual TextRunProperties? Properties => null;
private class TextRunDebuggerProxy private class TextRunDebuggerProxy
{ {
@ -49,7 +49,7 @@ namespace Avalonia.Media.TextFormatting
} }
} }
public TextRunProperties Properties => _textRun.Properties; public TextRunProperties? Properties => _textRun.Properties;
} }
} }
} }

12
src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs

@ -25,29 +25,29 @@ namespace Avalonia.Media.TextFormatting
///<summary> ///<summary>
/// Run TextDecorations. /// Run TextDecorations.
///</summary> ///</summary>
public abstract TextDecorationCollection TextDecorations { get; } public abstract TextDecorationCollection? TextDecorations { get; }
/// <summary> /// <summary>
/// Brush used to fill text. /// Brush used to fill text.
/// </summary> /// </summary>
public abstract IBrush ForegroundBrush { get; } public abstract IBrush? ForegroundBrush { get; }
/// <summary> /// <summary>
/// Brush used to paint background of run. /// Brush used to paint background of run.
/// </summary> /// </summary>
public abstract IBrush BackgroundBrush { get; } public abstract IBrush? BackgroundBrush { get; }
/// <summary> /// <summary>
/// Run text culture. /// Run text culture.
/// </summary> /// </summary>
public abstract CultureInfo CultureInfo { get; } public abstract CultureInfo? CultureInfo { get; }
/// <summary> /// <summary>
/// Run vertical box alignment /// Run vertical box alignment
/// </summary> /// </summary>
public abstract BaselineAlignment BaselineAlignment { get; } public abstract BaselineAlignment BaselineAlignment { get; }
public bool Equals(TextRunProperties other) public bool Equals(TextRunProperties? other)
{ {
if (ReferenceEquals(null, other)) if (ReferenceEquals(null, other))
return false; return false;
@ -62,7 +62,7 @@ namespace Avalonia.Media.TextFormatting
Equals(CultureInfo, other.CultureInfo); Equals(CultureInfo, other.CultureInfo);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return ReferenceEquals(this, obj) || obj is TextRunProperties other && Equals(other); return ReferenceEquals(this, obj) || obj is TextRunProperties other && Equals(other);
} }

2
src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs

@ -46,7 +46,7 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc cref="ITextShaperImpl.ShapeText"/> /// <inheritdoc cref="ITextShaperImpl.ShapeText"/>
public GlyphRun ShapeText(ReadOnlySlice<char> text, Typeface typeface, double fontRenderingEmSize, public GlyphRun ShapeText(ReadOnlySlice<char> text, Typeface typeface, double fontRenderingEmSize,
CultureInfo culture) CultureInfo? culture)
{ {
return _platformImpl.ShapeText(text, typeface, fontRenderingEmSize, culture); return _platformImpl.ShapeText(text, typeface, fontRenderingEmSize, culture);
} }

4
src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs

@ -24,8 +24,8 @@
static UnicodeData() static UnicodeData()
{ {
s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")); s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")!);
s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")); s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")!);
} }
/// <summary> /// <summary>

2
src/Avalonia.Visuals/Media/Transform.cs

@ -19,7 +19,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Raised when the transform changes. /// Raised when the transform changes.
/// </summary> /// </summary>
public event EventHandler Changed; public event EventHandler? Changed;
/// <summary> /// <summary>
/// Gets the transform's <see cref="Matrix"/>. /// Gets the transform's <see cref="Matrix"/>.

6
src/Avalonia.Visuals/Media/TransformConverter.cs

@ -10,14 +10,14 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public class TransformConverter : TypeConverter public class TransformConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{ {
return sourceType == typeof(string); return sourceType == typeof(string);
} }
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
{ {
return TransformOperations.Parse((string)value); return value is string s ? TransformOperations.Parse(s) : null;
} }
} }
} }

2
src/Avalonia.Visuals/Media/TransformGroup.cs

@ -24,7 +24,7 @@ namespace Avalonia.Media
}; };
} }
private void ChildTransform_Changed(object sender, System.EventArgs e) private void ChildTransform_Changed(object? sender, System.EventArgs e)
{ {
this.RaiseChanged(); this.RaiseChanged();
} }

4
src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs

@ -92,8 +92,8 @@ namespace Avalonia.Media.Transformation
} }
// ReSharper disable PossibleInvalidOperationException // ReSharper disable PossibleInvalidOperationException
TransformOperation fromValue = fromIdentity ? Identity : from.Value; TransformOperation fromValue = fromIdentity ? Identity : from!.Value;
TransformOperation toValue = toIdentity ? Identity : to.Value; TransformOperation toValue = toIdentity ? Identity : to!.Value;
// ReSharper restore PossibleInvalidOperationException // ReSharper restore PossibleInvalidOperationException
var interpolationType = toIdentity ? fromValue.Type : toValue.Type; var interpolationType = toIdentity ? fromValue.Type : toValue.Type;

4
src/Avalonia.Visuals/Media/Typeface.cs

@ -48,7 +48,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets the font family. /// Gets the font family.
/// </summary> /// </summary>
public FontFamily FontFamily { get; } public FontFamily? FontFamily { get; }
/// <summary> /// <summary>
/// Gets the font style. /// Gets the font style.
@ -78,7 +78,7 @@ namespace Avalonia.Media
return a.Equals(b); return a.Equals(b);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is Typeface typeface && Equals(typeface); return obj is Typeface typeface && Equals(typeface);
} }

4
src/Avalonia.Visuals/Media/UnicodeRange.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media
public static UnicodeRange Default = Parse("0-10FFFD"); public static UnicodeRange Default = Parse("0-10FFFD");
private readonly UnicodeRangeSegment _single; private readonly UnicodeRangeSegment _single;
private readonly IReadOnlyList<UnicodeRangeSegment> _segments = null; private readonly IReadOnlyList<UnicodeRangeSegment>? _segments = null;
public UnicodeRange(int start, int end) public UnicodeRange(int start, int end)
{ {
@ -37,7 +37,7 @@ namespace Avalonia.Media
internal UnicodeRangeSegment Single => _single; internal UnicodeRangeSegment Single => _single;
internal IReadOnlyList<UnicodeRangeSegment> Segments => _segments; internal IReadOnlyList<UnicodeRangeSegment>? Segments => _segments;
/// <summary> /// <summary>
/// Determines if given value is inside the range. /// Determines if given value is inside the range.

6
src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs

@ -6,7 +6,7 @@ namespace Avalonia.Platform
public class ExportRenderingSubsystemAttribute : Attribute public class ExportRenderingSubsystemAttribute : Attribute
{ {
public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod, public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod,
Type environmentChecker = null) Type? environmentChecker = null)
{ {
Name = name; Name = name;
InitializationType = initializationType; InitializationType = initializationType;
@ -17,11 +17,11 @@ namespace Avalonia.Platform
} }
public string InitializationMethod { get; private set; } public string InitializationMethod { get; private set; }
public Type EnvironmentChecker { get; } public Type? EnvironmentChecker { get; }
public Type InitializationType { get; private set; } public Type InitializationType { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }
public int Priority { get; private set; } public int Priority { get; private set; }
public OperatingSystemType RequiredOS { get; private set; } public OperatingSystemType RequiredOS { get; private set; }
public string RequiresWindowingSubsystem { get; set; } public string? RequiresWindowingSubsystem { get; set; }
} }
} }

6
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -55,7 +55,7 @@ namespace Avalonia.Platform
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry); void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry);
/// <summary> /// <summary>
/// Draws a rectangle with the specified Brush and Pen. /// Draws a rectangle with the specified Brush and Pen.
@ -68,7 +68,7 @@ namespace Avalonia.Platform
/// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks> /// </remarks>
void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect,
BoxShadows boxShadows = default); BoxShadows boxShadows = default);
/// <summary> /// <summary>
@ -81,7 +81,7 @@ namespace Avalonia.Platform
/// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks> /// </remarks>
void DrawEllipse(IBrush brush, IPen pen, Rect rect); void DrawEllipse(IBrush? brush, IPen? pen, Rect rect);
/// <summary> /// <summary>
/// Draws text. /// Draws text.

2
src/Avalonia.Visuals/Platform/IFontManagerImpl.cs

@ -31,7 +31,7 @@ namespace Avalonia.Platform
/// </returns> /// </returns>
bool TryMatchCharacter(int codepoint, FontStyle fontStyle, bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight, FontWeight fontWeight,
FontFamily fontFamily, CultureInfo culture, out Typeface typeface); FontFamily? fontFamily, CultureInfo? culture, out Typeface typeface);
/// <summary> /// <summary>
/// Creates a glyph typeface. /// Creates a glyph typeface.

2
src/Avalonia.Visuals/Platform/IGeometryImpl.cs

@ -23,7 +23,7 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
/// <param name="pen">The pen to use. May be null.</param> /// <param name="pen">The pen to use. May be null.</param>
/// <returns>The bounding rectangle.</returns> /// <returns>The bounding rectangle.</returns>
Rect GetRenderBounds(IPen pen); Rect GetRenderBounds(IPen? pen);
/// <summary> /// <summary>
/// Indicates whether the geometry's fill contains the specified point. /// Indicates whether the geometry's fill contains the specified point.

2
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -29,7 +29,7 @@ namespace Avalonia.Platform
TextAlignment textAlignment, TextAlignment textAlignment,
TextWrapping wrapping, TextWrapping wrapping,
Size constraint, Size constraint,
IReadOnlyList<FormattedTextStyleSpan> spans); IReadOnlyList<FormattedTextStyleSpan>? spans);
/// <summary> /// <summary>
/// Creates an ellipse geometry implementation. /// Creates an ellipse geometry implementation.

2
src/Avalonia.Visuals/Platform/IRenderTarget.cs

@ -18,7 +18,7 @@ namespace Avalonia.Platform
/// A render to be used to render visual brushes. May be null if no visual brushes are /// A render to be used to render visual brushes. May be null if no visual brushes are
/// to be drawn. /// to be drawn.
/// </param> /// </param>
IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer); IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer);
} }
public interface IRenderTargetWithCorruptionInfo : IRenderTarget public interface IRenderTargetWithCorruptionInfo : IRenderTarget

2
src/Avalonia.Visuals/Platform/ITextShaperImpl.cs

@ -17,6 +17,6 @@ namespace Avalonia.Platform
/// <param name="fontRenderingEmSize">The font rendering em size.</param> /// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="culture">The culture.</param> /// <param name="culture">The culture.</param>
/// <returns>A shaped glyph run.</returns> /// <returns>A shaped glyph run.</returns>
GlyphRun ShapeText(ReadOnlySlice<char> text, Typeface typeface, double fontRenderingEmSize, CultureInfo culture); GlyphRun ShapeText(ReadOnlySlice<char> text, Typeface typeface, double fontRenderingEmSize, CultureInfo? culture);
} }
} }

35
src/Avalonia.Visuals/Platform/PathGeometryContext.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
@ -6,8 +7,8 @@ namespace Avalonia.Visuals.Platform
{ {
public class PathGeometryContext : IGeometryContext public class PathGeometryContext : IGeometryContext
{ {
private PathFigure _currentFigure; private PathFigure? _currentFigure;
private PathGeometry _pathGeometry; private PathGeometry? _pathGeometry;
public PathGeometryContext(PathGeometry pathGeometry) public PathGeometryContext(PathGeometry pathGeometry)
{ {
@ -30,13 +31,16 @@ namespace Avalonia.Visuals.Platform
Point = point Point = point
}; };
_currentFigure.Segments.Add(arcSegment); CurrentFigureSegments().Add(arcSegment);
} }
public void BeginFigure(Point startPoint, bool isFilled) public void BeginFigure(Point startPoint, bool isFilled)
{ {
ThrowIfDisposed();
_currentFigure = new PathFigure { StartPoint = startPoint, IsClosed = false, IsFilled = isFilled }; _currentFigure = new PathFigure { StartPoint = startPoint, IsClosed = false, IsFilled = isFilled };
_pathGeometry.Figures ??= new();
_pathGeometry.Figures.Add(_currentFigure); _pathGeometry.Figures.Add(_currentFigure);
} }
@ -44,14 +48,14 @@ namespace Avalonia.Visuals.Platform
{ {
var bezierSegment = new BezierSegment { Point1 = point1, Point2 = point2, Point3 = point3 }; var bezierSegment = new BezierSegment { Point1 = point1, Point2 = point2, Point3 = point3 };
_currentFigure.Segments.Add(bezierSegment); CurrentFigureSegments().Add(bezierSegment);
} }
public void QuadraticBezierTo(Point control, Point endPoint) public void QuadraticBezierTo(Point control, Point endPoint)
{ {
var quadraticBezierSegment = new QuadraticBezierSegment { Point1 = control, Point2 = endPoint }; var quadraticBezierSegment = new QuadraticBezierSegment { Point1 = control, Point2 = endPoint };
_currentFigure.Segments.Add(quadraticBezierSegment); CurrentFigureSegments().Add(quadraticBezierSegment);
} }
public void LineTo(Point point) public void LineTo(Point point)
@ -61,7 +65,7 @@ namespace Avalonia.Visuals.Platform
Point = point Point = point
}; };
_currentFigure.Segments.Add(lineSegment); CurrentFigureSegments().Add(lineSegment);
} }
public void EndFigure(bool isClosed) public void EndFigure(bool isClosed)
@ -76,7 +80,26 @@ namespace Avalonia.Visuals.Platform
public void SetFillRule(FillRule fillRule) public void SetFillRule(FillRule fillRule)
{ {
ThrowIfDisposed();
_pathGeometry.FillRule = fillRule; _pathGeometry.FillRule = fillRule;
} }
[MemberNotNull(nameof(_pathGeometry))]
private void ThrowIfDisposed()
{
if (_pathGeometry is null)
throw new ObjectDisposedException(nameof(PathGeometryContext));
}
private PathSegments CurrentFigureSegments()
{
ThrowIfDisposed();
if (_currentFigure is null)
throw new InvalidOperationException("No figure in progress.");
if (_currentFigure.Segments is null)
throw new InvalidOperationException("Current figure's segments cannot be null.");
return _currentFigure.Segments;
}
} }
} }

2
src/Avalonia.Visuals/Point.cs

@ -211,7 +211,7 @@ namespace Avalonia
/// <returns> /// <returns>
/// True if <paramref name="obj"/> is a point that equals the current point. /// True if <paramref name="obj"/> is a point that equals the current point.
/// </returns> /// </returns>
public override bool Equals(object obj) => obj is Point other && Equals(other); public override bool Equals(object? obj) => obj is Point other && Equals(other);
/// <summary> /// <summary>
/// Returns a hash code for a <see cref="Point"/>. /// Returns a hash code for a <see cref="Point"/>.

2
src/Avalonia.Visuals/Rect.cs

@ -341,7 +341,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The object to compare against.</param> /// <param name="obj">The object to compare against.</param>
/// <returns>True if the object is equal to this rectangle; false otherwise.</returns> /// <returns>True if the object is equal to this rectangle; false otherwise.</returns>
public override bool Equals(object obj) => obj is Rect other && Equals(other); public override bool Equals(object? obj) => obj is Rect other && Equals(other);
/// <summary> /// <summary>
/// Returns the hash code for this instance. /// Returns the hash code for this instance.

2
src/Avalonia.Visuals/RelativePoint.cs

@ -111,7 +111,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The other object.</param> /// <param name="obj">The other object.</param>
/// <returns>True if the objects are equal, otherwise false.</returns> /// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object obj) => obj is RelativePoint other && Equals(other); public override bool Equals(object? obj) => obj is RelativePoint other && Equals(other);
/// <summary> /// <summary>
/// Checks if the <see cref="RelativePoint"/> equals another point. /// Checks if the <see cref="RelativePoint"/> equals another point.

2
src/Avalonia.Visuals/RelativeRect.cs

@ -113,7 +113,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The other object.</param> /// <param name="obj">The other object.</param>
/// <returns>True if the objects are equal, otherwise false.</returns> /// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object obj) => obj is RelativeRect other && Equals(other); public override bool Equals(object? obj) => obj is RelativeRect other && Equals(other);
/// <summary> /// <summary>
/// Checks if the <see cref="RelativeRect"/> equals another rectangle. /// Checks if the <see cref="RelativeRect"/> equals another rectangle.

16
src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs

@ -14,10 +14,10 @@ namespace Avalonia.Rendering
/// </remarks> /// </remarks>
public class DefaultRenderTimer : IRenderTimer public class DefaultRenderTimer : IRenderTimer
{ {
private IRuntimePlatform _runtime; private IRuntimePlatform? _runtime;
private int _subscriberCount; private int _subscriberCount;
private Action<TimeSpan> _tick; private Action<TimeSpan>? _tick;
private IDisposable _subscription; private IDisposable? _subscription;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DefaultRenderTimer"/> class. /// Initializes a new instance of the <see cref="DefaultRenderTimer"/> class.
@ -77,10 +77,8 @@ namespace Avalonia.Rendering
/// </remarks> /// </remarks>
protected virtual IDisposable StartCore(Action<TimeSpan> tick) protected virtual IDisposable StartCore(Action<TimeSpan> tick)
{ {
if (_runtime == null) _runtime ??= AvaloniaLocator.Current.GetService<IRuntimePlatform>() ??
{ throw new InvalidOperationException("Unable to locate IRuntimePlatform.");
_runtime = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
}
return _runtime.StartSystemTimer( return _runtime.StartSystemTimer(
TimeSpan.FromSeconds(1.0 / FramesPerSecond), TimeSpan.FromSeconds(1.0 / FramesPerSecond),
@ -92,13 +90,13 @@ namespace Avalonia.Rendering
/// </summary> /// </summary>
protected void Stop() protected void Stop()
{ {
_subscription.Dispose(); _subscription?.Dispose();
_subscription = null; _subscription = null;
} }
private void InternalTick(TimeSpan tickCount) private void InternalTick(TimeSpan tickCount)
{ {
_tick(tickCount); _tick?.Invoke(tickCount);
} }
} }
} }

72
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Avalonia.Logging; using Avalonia.Logging;
@ -19,20 +20,20 @@ namespace Avalonia.Rendering
/// </summary> /// </summary>
public class DeferredRenderer : RendererBase, IRenderer, IRenderLoopTask, IVisualBrushRenderer public class DeferredRenderer : RendererBase, IRenderer, IRenderLoopTask, IVisualBrushRenderer
{ {
private readonly IDispatcher _dispatcher; private readonly IDispatcher? _dispatcher;
private readonly IRenderLoop _renderLoop; private readonly IRenderLoop? _renderLoop;
private readonly IVisual _root; private readonly IVisual _root;
private readonly ISceneBuilder _sceneBuilder; private readonly ISceneBuilder _sceneBuilder;
private bool _running; private bool _running;
private bool _disposed; private bool _disposed;
private volatile IRef<Scene> _scene; private volatile IRef<Scene>? _scene;
private DirtyVisuals _dirty; private DirtyVisuals? _dirty;
private HashSet<IVisual> _recalculateChildren; private HashSet<IVisual>? _recalculateChildren;
private IRef<IRenderTargetBitmapImpl> _overlay; private IRef<IRenderTargetBitmapImpl>? _overlay;
private int _lastSceneId = -1; private int _lastSceneId = -1;
private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects(); private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
private IRef<IDrawOperation> _currentDraw; private IRef<IDrawOperation>? _currentDraw;
private readonly IDeferredRendererLock _lock; private readonly IDeferredRendererLock _lock;
private readonly object _sceneLock = new object(); private readonly object _sceneLock = new object();
private readonly object _startStopLock = new object(); private readonly object _startStopLock = new object();
@ -50,14 +51,12 @@ namespace Avalonia.Rendering
public DeferredRenderer( public DeferredRenderer(
IRenderRoot root, IRenderRoot root,
IRenderLoop renderLoop, IRenderLoop renderLoop,
ISceneBuilder sceneBuilder = null, ISceneBuilder? sceneBuilder = null,
IDispatcher dispatcher = null, IDispatcher? dispatcher = null,
IDeferredRendererLock rendererLock = null) : base(true) IDeferredRendererLock? rendererLock = null) : base(true)
{ {
Contract.Requires<ArgumentNullException>(root != null);
_dispatcher = dispatcher ?? Dispatcher.UIThread; _dispatcher = dispatcher ?? Dispatcher.UIThread;
_root = root; _root = root ?? throw new ArgumentNullException(nameof(root));
_sceneBuilder = sceneBuilder ?? new SceneBuilder(); _sceneBuilder = sceneBuilder ?? new SceneBuilder();
Layers = new RenderLayers(); Layers = new RenderLayers();
_renderLoop = renderLoop; _renderLoop = renderLoop;
@ -77,13 +76,10 @@ namespace Avalonia.Rendering
public DeferredRenderer( public DeferredRenderer(
IVisual root, IVisual root,
IRenderTarget renderTarget, IRenderTarget renderTarget,
ISceneBuilder sceneBuilder = null) : base(true) ISceneBuilder? sceneBuilder = null) : base(true)
{ {
Contract.Requires<ArgumentNullException>(root != null); _root = root ?? throw new ArgumentNullException(nameof(root));
Contract.Requires<ArgumentNullException>(renderTarget != null); RenderTarget = renderTarget ?? throw new ArgumentNullException(nameof(renderTarget));
_root = root;
RenderTarget = renderTarget;
_sceneBuilder = sceneBuilder ?? new SceneBuilder(); _sceneBuilder = sceneBuilder ?? new SceneBuilder();
Layers = new RenderLayers(); Layers = new RenderLayers();
_lock = new ManagedDeferredRendererLock(); _lock = new ManagedDeferredRendererLock();
@ -99,7 +95,7 @@ namespace Avalonia.Rendering
/// <summary> /// <summary>
/// Gets or sets a path to which rendered frame should be rendered for debugging. /// Gets or sets a path to which rendered frame should be rendered for debugging.
/// </summary> /// </summary>
public string DebugFramesPath { get; set; } public string? DebugFramesPath { get; set; }
/// <summary> /// <summary>
/// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered /// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered
@ -107,7 +103,7 @@ namespace Avalonia.Rendering
public bool RenderOnlyOnRenderThread { get; set; } public bool RenderOnlyOnRenderThread { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated; public event EventHandler<SceneInvalidatedEventArgs>? SceneInvalidated;
/// <summary> /// <summary>
/// Gets the render layers. /// Gets the render layers.
@ -117,7 +113,7 @@ namespace Avalonia.Rendering
/// <summary> /// <summary>
/// Gets the current render target. /// Gets the current render target.
/// </summary> /// </summary>
internal IRenderTarget RenderTarget { get; private set; } internal IRenderTarget? RenderTarget { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
public void AddDirty(IVisual visual) public void AddDirty(IVisual visual)
@ -167,7 +163,7 @@ namespace Avalonia.Rendering
} }
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter) public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool>? filter)
{ {
EnsureCanHitTest(); EnsureCanHitTest();
@ -177,7 +173,7 @@ namespace Avalonia.Rendering
} }
/// <inheritdoc/> /// <inheritdoc/>
public IVisual HitTestFirst(Point p, IVisual root, Func<IVisual, bool> filter) public IVisual? HitTestFirst(Point p, IVisual root, Func<IVisual, bool>? filter)
{ {
EnsureCanHitTest(); EnsureCanHitTest();
@ -199,7 +195,7 @@ namespace Avalonia.Rendering
while (true) while (true)
{ {
Scene scene; Scene? scene;
bool? updated; bool? updated;
lock (_sceneLock) lock (_sceneLock)
{ {
@ -297,7 +293,7 @@ namespace Avalonia.Rendering
internal void UnitTestRender() => Render(false); internal void UnitTestRender() => Render(false);
internal Scene UnitTestScene() => _scene.Item; internal Scene? UnitTestScene() => _scene?.Item;
private void EnsureCanHitTest() private void EnsureCanHitTest()
{ {
@ -315,7 +311,7 @@ namespace Avalonia.Rendering
if (l == null) if (l == null)
return; return;
IDrawingContextImpl context = null; IDrawingContextImpl? context = null;
try try
{ {
try try
@ -358,10 +354,10 @@ namespace Avalonia.Rendering
} }
} }
private (IRef<Scene> scene, bool updated) UpdateRenderLayersAndConsumeSceneIfNeeded(ref IDrawingContextImpl context, private (IRef<Scene>? scene, bool updated) UpdateRenderLayersAndConsumeSceneIfNeeded(ref IDrawingContextImpl? context,
bool recursiveCall = false) bool recursiveCall = false)
{ {
IRef<Scene> sceneRef; IRef<Scene>? sceneRef;
lock (_sceneLock) lock (_sceneLock)
sceneRef = _scene?.Clone(); sceneRef = _scene?.Clone();
if (sceneRef == null) if (sceneRef == null)
@ -416,7 +412,7 @@ namespace Avalonia.Rendering
} }
private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds) private void Render(IDrawingContextImpl context, VisualNode node, IVisual? layer, Rect clipBounds)
{ {
if (layer == null || node.LayerRoot == layer) if (layer == null || node.LayerRoot == layer)
{ {
@ -459,7 +455,7 @@ namespace Avalonia.Rendering
if (layer.Dirty.IsEmpty && !renderLayer.IsEmpty) if (layer.Dirty.IsEmpty && !renderLayer.IsEmpty)
continue; continue;
var renderTarget = renderLayer.Bitmap; var renderTarget = renderLayer.Bitmap;
var node = (VisualNode)scene.FindNode(layer.LayerRoot); var node = (VisualNode?)scene.FindNode(layer.LayerRoot);
if (node != null) if (node != null)
{ {
@ -515,7 +511,7 @@ namespace Avalonia.Rendering
Math.Ceiling(rect.Bottom * scale) / scale)); Math.Ceiling(rect.Bottom * scale) / scale));
} }
private void RenderOverlay(Scene scene, ref IDrawingContextImpl parentContent) private void RenderOverlay(Scene scene, ref IDrawingContextImpl? parentContent)
{ {
EnsureDrawingContext(ref parentContent); EnsureDrawingContext(ref parentContent);
@ -545,7 +541,7 @@ namespace Avalonia.Rendering
} }
} }
private void RenderComposite(Scene scene, ref IDrawingContextImpl context) private void RenderComposite(Scene scene, ref IDrawingContextImpl? context)
{ {
EnsureDrawingContext(ref context); EnsureDrawingContext(ref context);
@ -596,7 +592,7 @@ namespace Avalonia.Rendering
} }
} }
private void EnsureDrawingContext(ref IDrawingContextImpl context) private void EnsureDrawingContext([NotNull] ref IDrawingContextImpl? context)
{ {
if (context != null) if (context != null)
{ {
@ -605,7 +601,7 @@ namespace Avalonia.Rendering
if ((RenderTarget as IRenderTargetWithCorruptionInfo)?.IsCorrupted == true) if ((RenderTarget as IRenderTargetWithCorruptionInfo)?.IsCorrupted == true)
{ {
RenderTarget.Dispose(); RenderTarget!.Dispose();
RenderTarget = null; RenderTarget = null;
} }
@ -647,10 +643,10 @@ namespace Avalonia.Rendering
} }
else else
{ {
foreach (var visual in _recalculateChildren) foreach (var visual in _recalculateChildren!)
{ {
var node = scene.FindNode(visual); var node = scene.FindNode(visual);
((VisualNode)node)?.SortChildren(scene); ((VisualNode?)node)?.SortChildren(scene);
} }
_recalculateChildren.Clear(); _recalculateChildren.Clear();
@ -724,7 +720,7 @@ namespace Avalonia.Rendering
foreach (var layer in Layers) foreach (var layer in Layers)
{ {
var fileName = Path.Combine(DebugFramesPath, $"frame-{id}-layer-{index++}.png"); var fileName = Path.Combine(DebugFramesPath ?? string.Empty, $"frame-{id}-layer-{index++}.png");
layer.Bitmap.Item.Save(fileName); layer.Bitmap.Item.Save(fileName);
} }
} }

2
src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs

@ -4,6 +4,6 @@ namespace Avalonia.Rendering
{ {
public interface IDeferredRendererLock public interface IDeferredRendererLock
{ {
IDisposable TryLock(); IDisposable? TryLock();
} }
} }

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

Loading…
Cancel
Save