Browse Source

Merge branch 'master' into fixes/5879-icustomhittest

pull/5923/head
Dariusz Komosiński 5 years ago
committed by GitHub
parent
commit
d23041b37e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      samples/RenderDemo/Pages/AnimationsPage.xaml
  2. 65
      samples/RenderDemo/Pages/TransitionsPage.xaml
  3. 10
      src/Avalonia.Animation/Transitions/DoubleTransition.cs
  4. 7
      src/Avalonia.Animation/Transitions/FloatTransition.cs
  5. 7
      src/Avalonia.Animation/Transitions/IntegerTransition.cs
  6. 6
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  7. 3
      src/Avalonia.Controls/ApiCompatBaseline.txt
  8. 11
      src/Avalonia.Controls/Viewbox.cs
  9. 29
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
  10. 186
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
  11. 21
      src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs
  12. 22
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  13. 39
      src/Avalonia.Styling/Controls/Classes.cs
  14. 10
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
  15. 23
      src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs
  16. 21
      src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs
  17. 10
      src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs
  18. 10
      src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs
  19. 10
      src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs
  20. 10
      src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs
  21. 5
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  22. 45
      src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
  23. 32
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  24. 25
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  25. 65
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  26. 22
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  27. 17
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
  28. 22
      tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
  29. 22
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
  30. 22
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

7
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -1,7 +1,8 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.AnimationsPage">
x:Class="RenderDemo.Pages.AnimationsPage"
MaxWidth="600">
<UserControl.Styles>
<Styles>
<Styles.Resources>
@ -167,8 +168,8 @@
<StackPanel.Clock>
<Clock />
</StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Spacing="20">
<TextBlock VerticalAlignment="Center">Hover to activate Keyframe Animations.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel>
<WrapPanel ClipToBounds="False">

65
samples/RenderDemo/Pages/TransitionsPage.xaml

@ -1,7 +1,8 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.TransitionsPage">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.TransitionsPage"
MaxWidth="600">
<UserControl.Styles>
<Styles>
<Styles.Resources>
@ -90,6 +91,56 @@
<Setter Property="RenderTransform" Value="none" />
</Style>
<Style Selector="Border.Rect7">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Height" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Rect7:pointerover">
<Setter Property="Height" Value="50" />
</Style>
<Style Selector="Border.Rect8">
<Setter Property="Transitions">
<Transitions>
<CornerRadiusTransition Property="CornerRadius" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Rect8:pointerover">
<Setter Property="CornerRadius" Value="50" />
</Style>
<Style Selector="Border.Rect9">
<Setter Property="Transitions">
<Transitions>
<ThicknessTransition Property="Padding" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Rect9:pointerover">
<Setter Property="Padding" Value="10" />
</Style>
<Style Selector="Border.Shadow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BoxShadow" Value="inset 0 0 0 2 Red, -15 -15 Green"/>
<Setter Property="Transitions">
<Transitions>
<BoxShadowsTransition Property="BoxShadow" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Shadow:pointerover">
<Setter Property="BoxShadow" Value="inset 30 30 20 30 Green, 20 40 20 10 Red"/>
</Style>
</Styles>
</UserControl.Styles>
@ -98,8 +149,8 @@
<StackPanel.Clock>
<Clock />
</StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Spacing="20">
<TextBlock VerticalAlignment="Center">Hover to activate Transitions.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel>
<WrapPanel ClipToBounds="False">
@ -109,6 +160,12 @@
<Border Classes="Test Rect4" Background="Navy"/>
<Border Classes="Test Rect5" Background="SeaGreen"/>
<Border Classes="Test Rect6" Background="Orange"/>
<Border Classes="Test Rect7" Background="Gold"/>
<Border Classes="Test Rect8" Background="Gray" />
<Border Classes="Test Rect9" Background="Red" />
<Border Classes="Test Shadow" CornerRadius="10" Child="{x:Null}" />
<Border Classes="Test Shadow" CornerRadius="0 30 60 0" Child="{x:Null}" />
</WrapPanel>
</StackPanel>
</Grid>

10
src/Avalonia.Animation/Transitions/DoubleTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,15 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class DoubleTransition : Transition<double>
{
private static readonly DoubleAnimator s_animator = new DoubleAnimator();
/// <inheritdocs/>
public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

7
src/Avalonia.Animation/Transitions/FloatTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,12 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class FloatTransition : Transition<float>
{
private static readonly FloatAnimator s_animator = new FloatAnimator();
/// <inheritdocs/>
public override IObservable<float> DoTransition(IObservable<double> progress, float oldValue, float newValue)
{
var delta = newValue - oldValue;
return progress
.Select(p => (float)Easing.Ease(p) * delta + oldValue);
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

7
src/Avalonia.Animation/Transitions/IntegerTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,12 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class IntegerTransition : Transition<int>
{
private static readonly Int32Animator s_animator = new Int32Animator();
/// <inheritdocs/>
public override IObservable<int> DoTransition(IObservable<double> progress, int oldValue, int newValue)
{
var delta = newValue - oldValue;
return progress
.Select(p => (int)(Easing.Ease(p) * delta + oldValue));
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

6
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -535,7 +535,8 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<DataGrid, int>(
nameof(SelectedIndex),
o => o.SelectedIndex,
(o, v) => o.SelectedIndex = v);
(o, v) => o.SelectedIndex = v,
defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Gets or sets the index of the current selection.
@ -553,7 +554,8 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<DataGrid, object>(
nameof(SelectedItem),
o => o.SelectedItem,
(o, v) => o.SelectedItem = v);
(o, v) => o.SelectedItem = v,
defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Gets or sets the data item corresponding to the selected row.

3
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -4,10 +4,11 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
Total Issues: 11
Total Issues: 12

11
src/Avalonia.Controls/Viewbox.cs

@ -11,9 +11,8 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Stretch"/> property.
/// </summary>
public static readonly AvaloniaProperty<Stretch> StretchProperty =
AvaloniaProperty.RegisterDirect<Viewbox, Stretch>(nameof(Stretch),
v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform);
public static readonly StyledProperty<Stretch> StretchProperty =
AvaloniaProperty.Register<Image, Stretch>(nameof(Stretch), Stretch.Uniform);
/// <summary>
/// Defines the <see cref="StretchDirection"/> property.
@ -21,8 +20,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<StretchDirection> StretchDirectionProperty =
AvaloniaProperty.Register<Viewbox, StretchDirection>(nameof(StretchDirection), StretchDirection.Both);
private Stretch _stretch = Stretch.Uniform;
static Viewbox()
{
ClipToBoundsProperty.OverrideDefaultValue<Viewbox>(true);
@ -35,8 +32,8 @@ namespace Avalonia.Controls
/// </summary>
public Stretch Stretch
{
get => _stretch;
set => SetAndRaise(StretchProperty, ref _stretch, value);
get => GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
}
/// <summary>

29
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@ -1,4 +1,6 @@
using System;
using System.ComponentModel;
using System.Text;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.VisualTree;
@ -95,12 +97,27 @@ namespace Avalonia.Diagnostics.ViewModels
{
string CreateConstraintInfo(StyledProperty<double> minProperty, StyledProperty<double> maxProperty)
{
if (ao.IsSet(minProperty) || ao.IsSet(maxProperty))
bool hasMin = ao.IsSet(minProperty);
bool hasMax = ao.IsSet(maxProperty);
if (hasMin || hasMax)
{
var minValue = ao.GetValue(minProperty);
var maxValue = ao.GetValue(maxProperty);
var builder = new StringBuilder();
if (hasMin)
{
var minValue = ao.GetValue(minProperty);
builder.AppendFormat("Min: {0}", Math.Round(minValue, 2));
builder.AppendLine();
}
if (hasMax)
{
var maxValue = ao.GetValue(maxProperty);
builder.AppendFormat("Max: {0}", Math.Round(maxValue, 2));
}
return $"{minValue} < size < {maxValue}";
return builder.ToString();
}
return null;
@ -183,8 +200,8 @@ namespace Avalonia.Diagnostics.ViewModels
{
var size = _control.Bounds;
Width = size.Width;
Height = size.Height;
Width = Math.Round(size.Width, 2);
Height = Math.Round(size.Height, 2);
}
}
}

186
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

@ -8,11 +8,16 @@
<UserControl.Resources>
<SolidColorBrush x:Key="ThicknessBorderBrush" Color="#666666" />
<SolidColorBrush x:Key="HighlightBorderBrush" Color="CornflowerBlue" />
<SolidColorBrush x:Key="SizeGuidelineBrush" Color="#333333" />
<SolidColorBrush x:Key="MarginBackgroundBrush" Color="#D78965" />
<SolidColorBrush x:Key="MarginHighlightBrush" Color="#EA966F" />
<SolidColorBrush x:Key="BorderBackgroundBrush" Color="#E3C381" />
<SolidColorBrush x:Key="BorderHighlightBrush" Color="#EFCD88" />
<SolidColorBrush x:Key="PaddingBackgroundBrush" Color="#B8C47F" />
<SolidColorBrush x:Key="PaddingHighlightBrush" Color="#CEDA8E" />
<SolidColorBrush x:Key="SizeBackgroundBrush" Color="#88B2BD" />
<SolidColorBrush x:Key="SizeHighlightBrush" Color="#9ED0DC" />
<conv:BoolToOpacityConverter x:Key="BoolToOpacity" Opacity="0.6"/>
</UserControl.Resources>
@ -24,43 +29,60 @@
<Setter Property="BorderBrush" Value="{StaticResource ThicknessBorderBrush}" />
<Setter Property="Template">
<ControlTemplate>
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="Auto,*,Auto">
<Grid.Styles>
<Style Selector="TextBox.thickness-edit">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="2" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="(ScrollViewer.HorizontalScrollBarVisibility)" Value="Disabled" />
<Setter Property="(ScrollViewer.VerticalScrollBarVisibility)" Value="Disabled" />
<Setter Property="IsVisible" Value="{Binding $parent[local:ThicknessEditor].IsPresent}" />
</Style>
</Grid.Styles>
<TextBlock IsVisible="{TemplateBinding IsPresent}" Margin="4,0,0,0" Text="{TemplateBinding Header}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" />
<TextBox Grid.Row="1" Grid.Column="0" Text="{Binding Left, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<TextBox x:Name="Right" Grid.Row="0" Grid.Column="1" Text="{Binding Top, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Right, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Bottom, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<ContentPresenter Grid.Row="1" Grid.Column="1"
Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Grid>
</Border>
<Panel>
<Rectangle x:Name="PART_Background" Classes.no-content-pointerover="{Binding !#PART_ContentPresenter.IsPointerOver}" />
<Border
x:Name="PART_Border"
Classes.no-content-pointerover="{Binding !#PART_ContentPresenter.IsPointerOver}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="Auto,*,Auto">
<Grid.Styles>
<Style Selector="TextBox.thickness-edit">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="2" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="(ScrollViewer.HorizontalScrollBarVisibility)" Value="Disabled" />
<Setter Property="(ScrollViewer.VerticalScrollBarVisibility)" Value="Disabled" />
<Setter Property="IsVisible" Value="{Binding $parent[local:ThicknessEditor].IsPresent}" />
</Style>
</Grid.Styles>
<TextBlock IsVisible="{TemplateBinding IsPresent}" Margin="4,0,0,0" Text="{TemplateBinding Header}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" />
<TextBox Grid.Row="1" Grid.Column="0" Text="{Binding Left, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<TextBox x:Name="Right" Grid.Row="0" Grid.Column="1" Text="{Binding Top, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Right, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Bottom, RelativeSource={RelativeSource TemplatedParent}}" Classes="thickness-edit" />
<ContentPresenter Grid.Row="1" Grid.Column="1"
Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Grid>
</Border>
</Panel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="local|ThicknessEditor /template/ Rectangle#PART_Background">
<Setter Property="Fill" Value="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" />
</Style>
<Style Selector="local|ThicknessEditor:pointerover /template/ Rectangle#PART_Background.no-content-pointerover">
<Setter Property="Fill" Value="{Binding Highlight, RelativeSource={RelativeSource TemplatedParent}}" />
</Style>
<Style Selector="local|ThicknessEditor:pointerover /template/ Border#PART_Border.no-content-pointerover">
<Setter Property="BorderBrush" Value="{StaticResource HighlightBorderBrush}" />
</Style>
<Style Selector="local|ThicknessEditor[IsPresent=False]">
<Setter Property="BorderThickness" Value="0" />
</Style>
@ -110,47 +132,67 @@
<Grid Grid.Column="2" RowDefinitions="Auto,*, Auto,*,Auto" >
<TextBlock Grid.Row="0" Text="Layout Visualizer" Margin="4" />
<Grid Grid.Row="1" x:Name="LayoutRoot" Margin="8,0,8,8" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto">
<Border x:Name="VerticalSize" Grid.Row="0" Grid.Column="1" >
<TextBlock VerticalAlignment="Center" FontWeight="Bold"
TextDecorations="{Binding Layout.HeightConstraint, Converter={x:Static local:Converters.HasConstraintConverter}}"
Text="{Binding Layout.Height}"
ToolTip.Tip="{Binding Layout.HeightConstraint}" />
</Border>
<Border x:Name="HorizontalSize" Grid.Row="1" Grid.Column="0" >
<TextBlock HorizontalAlignment="Center" FontWeight="Bold"
TextDecorations="{Binding Layout.WidthConstraint, Converter={x:Static local:Converters.HasConstraintConverter}}"
Text="{Binding Layout.Width}"
ToolTip.Tip="{Binding Layout.WidthConstraint}" />
</Border>
<local:ThicknessEditor Grid.Row="0" Grid.Column="0" Header="margin" VerticalAlignment="Top" HorizontalAlignment="Center" Background="{StaticResource MarginBackgroundBrush}" Thickness="{Binding Layout.MarginThickness}">
<local:ThicknessEditor x:Name="BorderArea" Header="border" Background="{StaticResource BorderBackgroundBrush}" Thickness="{Binding Layout.BorderThickness}" IsPresent="{Binding Layout.HasBorder}">
<local:ThicknessEditor x:Name="PaddingArea" Header="padding" Background="{StaticResource PaddingBackgroundBrush}" Thickness="{Binding Layout.PaddingThickness}" IsPresent="{Binding Layout.HasPadding}">
<Border x:Name="ContentArea" BorderThickness="1" BorderBrush="{StaticResource ThicknessBorderBrush}" MinWidth="100" MinHeight="16" Background="{StaticResource SizeBackgroundBrush}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBlock Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Text="content" />
</Border>
<Viewbox Grid.Row="1" StretchDirection="DownOnly" >
<Grid Grid.Row="1" x:Name="LayoutRoot" Margin="8,0,8,8" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto">
<Grid.Styles>
<Style Selector="TextBlock.with-constraint">
<Setter Property="TextDecorations" Value="Underline" />
</Style>
</Grid.Styles>
<Border x:Name="VerticalSize" Grid.Row="0" Grid.Column="1" >
<TextBlock VerticalAlignment="Center" FontWeight="Bold"
Classes.with-constraint="{Binding Layout.HeightConstraint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text="{Binding Layout.Height}"
ToolTip.Tip="{Binding Layout.HeightConstraint}" />
</Border>
<Border x:Name="HorizontalSize" Grid.Row="1" Grid.Column="0" >
<TextBlock HorizontalAlignment="Center" FontWeight="Bold"
Classes.with-constraint="{Binding Layout.WidthConstraint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text="{Binding Layout.Width}"
ToolTip.Tip="{Binding Layout.WidthConstraint}" />
</Border>
<local:ThicknessEditor Grid.Row="0" Grid.Column="0" Header="margin" VerticalAlignment="Top" HorizontalAlignment="Center" Background="{StaticResource MarginBackgroundBrush}" Highlight="{StaticResource MarginHighlightBrush}" Thickness="{Binding Layout.MarginThickness}">
<local:ThicknessEditor x:Name="BorderArea" Header="border" Background="{StaticResource BorderBackgroundBrush}" Highlight="{StaticResource BorderHighlightBrush}" Thickness="{Binding Layout.BorderThickness}" IsPresent="{Binding Layout.HasBorder}">
<local:ThicknessEditor x:Name="PaddingArea" Header="padding" Background="{StaticResource PaddingBackgroundBrush}" Highlight="{StaticResource PaddingHighlightBrush}" Thickness="{Binding Layout.PaddingThickness}" IsPresent="{Binding Layout.HasPadding}">
<Border x:Name="ContentArea" BorderThickness="1" MinWidth="100" MinHeight="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border.Styles>
<Style Selector="Border">
<Setter Property="Background" Value="{StaticResource SizeBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource ThicknessBorderBrush}" />
</Style>
<Style Selector="Border:pointerover">
<Setter Property="Background" Value="{StaticResource SizeHighlightBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource HighlightBorderBrush}" />
</Style>
</Border.Styles>
<TextBlock Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Text="content" />
</Border>
</local:ThicknessEditor>
</local:ThicknessEditor>
</local:ThicknessEditor>
</local:ThicknessEditor>
<Canvas Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2">
<Canvas.Styles>
<Style Selector="Rectangle">
<Setter Property="StrokeDashArray" Value="1,3" />
<Setter Property="Stroke" Value="{StaticResource SizeGuidelineBrush}" />
<Setter Property="StrokeThickness" Value="1" />
</Style>
</Canvas.Styles>
<Rectangle x:Name="HorizontalSizeBegin" />
<Rectangle x:Name="HorizontalSizeEnd" />
<Rectangle x:Name="VerticalSizeBegin" />
<Rectangle x:Name="VerticalSizeEnd" />
</Canvas>
</Grid>
<Canvas Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2">
<Canvas.Styles>
<Style Selector="Rectangle">
<Setter Property="StrokeDashArray" Value="1,3" />
<Setter Property="Stroke" Value="{StaticResource SizeGuidelineBrush}" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="IsHitTestVisible" Value="False" />
</Style>
</Canvas.Styles>
<Rectangle x:Name="HorizontalSizeBegin" />
<Rectangle x:Name="HorizontalSizeEnd" />
<Rectangle x:Name="VerticalSizeBegin" />
<Rectangle x:Name="VerticalSizeEnd" />
</Canvas>
</Grid>
</Viewbox>
<Grid Grid.Row="2" Margin="4" RowDefinitions="Auto,Auto">

21
src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs

@ -1,21 +1,9 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace Avalonia.Diagnostics.Views
{
internal static class Converters
{
public static IValueConverter HasConstraintConverter =
new FuncValueConverter<object, TextDecorationCollection>(ConvertToDecoration);
private static TextDecorationCollection ConvertToDecoration(object arg)
{
return arg != null ? TextDecorations.Underline : null;
}
}
internal class ThicknessEditor : ContentControl
{
public static readonly DirectProperty<ThicknessEditor, Thickness> ThicknessProperty =
@ -44,6 +32,14 @@ namespace Avalonia.Diagnostics.Views
AvaloniaProperty.RegisterDirect<ThicknessEditor, double>(nameof(Bottom), o => o.Bottom,
(o, v) => o.Bottom = v);
public static readonly StyledProperty<IBrush> HighlightProperty =
AvaloniaProperty.Register<ThicknessEditor, IBrush>(nameof(Highlight));
public IBrush Highlight
{
get => GetValue(HighlightProperty);
set => SetValue(HighlightProperty, value);
}
private Thickness _thickness;
private string _header;
@ -52,7 +48,6 @@ namespace Avalonia.Diagnostics.Views
private double _top;
private double _right;
private double _bottom;
private bool _isUpdatingThickness;
public Thickness Thickness

22
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -70,6 +70,28 @@ namespace Avalonia.Headless
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));

39
src/Avalonia.Styling/Controls/Classes.cs

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using Avalonia.Collections;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -90,7 +92,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// Remvoes all non-pseudoclasses from the collection.
/// Removes all non-pseudoclasses from the collection.
/// </summary>
public override void Clear()
{
@ -135,7 +137,7 @@ namespace Avalonia.Controls
/// </remarks>
public override void InsertRange(int index, IEnumerable<string> names)
{
var c = new List<string>();
List<string>? toInsert = null;
foreach (var name in names)
{
@ -143,11 +145,16 @@ namespace Avalonia.Controls
if (!Contains(name))
{
c.Add(name);
toInsert ??= new List<string>();
toInsert.Add(name);
}
}
base.InsertRange(index, c);
if (toInsert != null)
{
base.InsertRange(index, toInsert);
}
}
/// <summary>
@ -176,19 +183,21 @@ namespace Avalonia.Controls
/// </remarks>
public override void RemoveAll(IEnumerable<string> names)
{
var c = new List<string>();
List<string>? toRemove = null;
foreach (var name in names)
{
ThrowIfPseudoclass(name, "removed");
if (Contains(name))
{
c.Add(name);
}
toRemove ??= new List<string>();
toRemove.Add(name);
}
base.RemoveAll(c);
if (toRemove != null)
{
base.RemoveAll(toRemove);
}
}
/// <summary>
@ -223,7 +232,7 @@ namespace Avalonia.Controls
/// <param name="source">The new contents of the collection.</param>
public void Replace(IList<string> source)
{
var toRemove = new List<string>();
List<string>? toRemove = null;
foreach (var name in source)
{
@ -234,11 +243,17 @@ namespace Avalonia.Controls
{
if (!name.StartsWith(":"))
{
toRemove ??= new List<string>();
toRemove.Add(name);
}
}
base.RemoveAll(toRemove);
if (toRemove != null)
{
base.RemoveAll(toRemove);
}
base.AddRange(source);
}

10
src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs

@ -12,6 +12,11 @@ namespace Avalonia.Animation.Animators
{
public override ISolidColorBrush Interpolate(double progress, ISolidColorBrush oldValue, ISolidColorBrush newValue)
{
if (oldValue is null || newValue is null)
{
return oldValue;
}
return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
}
@ -26,6 +31,11 @@ namespace Avalonia.Animation.Animators
{
public override SolidColorBrush Interpolate(double progress, SolidColorBrush oldValue, SolidColorBrush newValue)
{
if (oldValue is null || newValue is null)
{
return oldValue;
}
return new SolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
}
}

23
src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs

@ -0,0 +1,23 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
using Avalonia.Media;
namespace Avalonia.Animation
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="BoxShadows"/> type.
/// </summary>
public class BoxShadowsTransition : Transition<BoxShadows>
{
private static readonly BoxShadowsAnimator s_animator = new BoxShadowsAnimator();
/// <inheritdocs/>
public override IObservable<BoxShadows> DoTransition(IObservable<double> progress, BoxShadows oldValue, BoxShadows newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

21
src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,26 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class CornerRadiusTransition : Transition<CornerRadius>
{
private static readonly CornerRadiusAnimator s_animator = new CornerRadiusAnimator();
/// <inheritdocs/>
public override IObservable<CornerRadius> DoTransition(IObservable<double> progress, CornerRadius oldValue, CornerRadius newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
var deltaTL = newValue.TopLeft - oldValue.TopLeft;
var deltaTR = newValue.TopRight - oldValue.TopRight;
var deltaBR = newValue.BottomRight - oldValue.BottomRight;
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft;
var nTL = f * deltaTL + oldValue.TopLeft;
var nTR = f * deltaTR + oldValue.TopRight;
var nBR = f * deltaBR + oldValue.BottomRight;
var nBL = f * deltaBL + oldValue.BottomLeft;
return new CornerRadius(nTL, nTR, nBR, nBL);
});
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

10
src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,15 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class PointTransition : Transition<Point>
{
private static readonly PointAnimator s_animator = new PointAnimator();
/// <inheritdocs/>
public override IObservable<Point> DoTransition(IObservable<double> progress, Point oldValue, Point newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

10
src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,15 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class SizeTransition : Transition<Size>
{
private static readonly SizeAnimator s_animator = new SizeAnimator();
/// <inheritdocs/>
public override IObservable<Size> DoTransition(IObservable<double> progress, Size oldValue, Size newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

10
src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,15 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class ThicknessTransition : Transition<Thickness>
{
private static readonly ThicknessAnimator s_animator = new ThicknessAnimator();
/// <inheritdocs/>
public override IObservable<Thickness> DoTransition(IObservable<double> progress, Thickness oldValue, Thickness newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

10
src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs

@ -1,6 +1,8 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
@ -8,15 +10,13 @@ namespace Avalonia.Animation
/// </summary>
public class VectorTransition : Transition<Vector>
{
private static readonly VectorAnimator s_animator = new VectorAnimator();
/// <inheritdocs/>
public override IObservable<Vector> DoTransition(IObservable<double> progress, Vector oldValue, Vector newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

5
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -64,3 +64,8 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGl
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract.
Total Issues: 64
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.IO.Stream)' 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.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
Total Issues: 11

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

@ -1,5 +1,8 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media.Imaging
{
@ -34,8 +37,50 @@ namespace Avalonia.Media.Imaging
{
}
private WriteableBitmap(IWriteableBitmapImpl impl) : base(impl)
{
}
public ILockedFramebuffer Lock() => ((IWriteableBitmapImpl) PlatformImpl.Item).Lock();
public static WriteableBitmap Decode(Stream stream)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return new WriteableBitmap(ri.LoadWriteableBitmap(stream));
}
/// <summary>
/// Loads a WriteableBitmap from a stream and decodes at the desired width. Aspect ratio is maintained.
/// This is more efficient than loading and then resizing.
/// </summary>
/// <param name="stream">The stream to read the bitmap from. This can be any supported image format.</param>
/// <param name="width">The desired width of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
/// <returns>An instance of the <see cref="WriteableBitmap"/> class.</returns>
public new static WriteableBitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return new WriteableBitmap(ri.LoadWriteableBitmapToWidth(stream, width, interpolationMode));
}
/// <summary>
/// Loads a Bitmap from a stream and decodes at the desired height. Aspect ratio is maintained.
/// This is more efficient than loading and then resizing.
/// </summary>
/// <param name="stream">The stream to read the bitmap from. This can be any supported image format.</param>
/// <param name="height">The desired height of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
/// <returns>An instance of the <see cref="WriteableBitmap"/> class.</returns>
public new static WriteableBitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return new WriteableBitmap(ri.LoadWriteableBitmapToHeight(stream, height, interpolationMode));
}
private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();

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

@ -100,6 +100,38 @@ namespace Avalonia.Platform
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmap(Stream stream);
/// <summary>
/// Loads a WriteableBitmap implementation from a stream to a specified width maintaining aspect ratio.
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <param name="width">The desired width of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should resizing be required.</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality);
/// <summary>
/// Loads a WriteableBitmap implementation from a stream to a specified height maintaining aspect ratio.
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <param name="height">The desired height of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should resizing be required.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality);
/// <summary>
/// Loads a WriteableBitmap implementation from a file.
/// </summary>
/// <param name="fileName">The filename of the bitmap.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IWriteableBitmapImpl LoadWriteableBitmap(string fileName);
/// <summary>
/// Loads a WriteableBitmap implementation from a file.
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IWriteableBitmapImpl LoadWriteableBitmap(Stream stream);
/// <summary>
/// Loads a bitmap implementation from a stream to a specified width maintaining aspect ratio.
/// </summary>

25
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -77,6 +77,31 @@ namespace Avalonia.Skia
return new ImmutableBitmap(stream);
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WriteableBitmapImpl(stream, width, true, interpolationMode);
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WriteableBitmapImpl(stream, height, false, interpolationMode);
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
using (var stream = File.OpenRead(fileName))
{
return LoadWriteableBitmap(stream);
}
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
return new WriteableBitmapImpl(stream);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{

65
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -1,8 +1,10 @@
using System;
using System.IO;
using System.Threading;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Skia.Helpers;
using Avalonia.Visuals.Media.Imaging;
using SkiaSharp;
namespace Avalonia.Skia
@ -15,7 +17,70 @@ namespace Avalonia.Skia
private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc;
private readonly SKBitmap _bitmap;
private readonly object _lock = new object();
/// <summary>
/// Create a WriteableBitmap from given stream.
/// </summary>
/// <param name="stream">Stream containing encoded data.</param>
public WriteableBitmapImpl(Stream stream)
{
using (var skiaStream = new SKManagedStream(stream))
{
_bitmap = SKBitmap.Decode(skiaStream);
if (_bitmap == null)
{
throw new ArgumentException("Unable to load bitmap from provided data");
}
PixelSize = new PixelSize(_bitmap.Width, _bitmap.Height);
Dpi = SkiaPlatform.DefaultDpi;
}
}
public WriteableBitmapImpl(Stream stream, int decodeSize, bool horizontal, BitmapInterpolationMode interpolationMode)
{
using (var skStream = new SKManagedStream(stream))
using (var codec = SKCodec.Create(skStream))
{
var info = codec.Info;
// get the scale that is nearest to what we want (eg: jpg returned 512)
var supportedScale = codec.GetScaledDimensions(horizontal ? ((float)decodeSize / info.Width) : ((float)decodeSize / info.Height));
// decode the bitmap at the nearest size
var nearest = new SKImageInfo(supportedScale.Width, supportedScale.Height);
var bmp = SKBitmap.Decode(codec, nearest);
// now scale that to the size that we want
var realScale = horizontal ? ((double)info.Height / info.Width) : ((double)info.Width / info.Height);
SKImageInfo desired;
if (horizontal)
{
desired = new SKImageInfo(decodeSize, (int)(realScale * decodeSize));
}
else
{
desired = new SKImageInfo((int)(realScale * decodeSize), decodeSize);
}
if (bmp.Width != desired.Width || bmp.Height != desired.Height)
{
var scaledBmp = bmp.Resize(desired, interpolationMode.ToSKFilterQuality());
bmp.Dispose();
bmp = scaledBmp;
}
_bitmap = bmp;
PixelSize = new PixelSize(bmp.Width, bmp.Height);
Dpi = SkiaPlatform.DefaultDpi;
}
}
/// <summary>
/// Create new writeable bitmap.
/// </summary>

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

@ -188,6 +188,28 @@ namespace Avalonia.Direct2D1
return new WicBitmapImpl(stream);
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WriteableWicBitmapImpl(stream, width, true, interpolationMode);
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WriteableWicBitmapImpl(stream, height, false, interpolationMode);
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
return new WriteableWicBitmapImpl(fileName);
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
return new WriteableWicBitmapImpl(stream);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{

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

@ -1,4 +1,5 @@
using System;
using System.IO;
using Avalonia.Platform;
using SharpDX.WIC;
using PixelFormat = Avalonia.Platform.PixelFormat;
@ -7,11 +8,27 @@ namespace Avalonia.Direct2D1.Media.Imaging
{
class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl
{
public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal,
Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode)
: base(stream, decodeSize, horizontal, interpolationMode)
{
}
public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat, AlphaFormat? alphaFormat)
: base(size, dpi, pixelFormat, alphaFormat)
{
}
public WriteableWicBitmapImpl(Stream stream)
: base(stream)
{
}
public WriteableWicBitmapImpl(string fileName)
: base(fileName)
{
}
class LockedBitmap : ILockedFramebuffer
{
private readonly WriteableWicBitmapImpl _parent;

22
tests/Avalonia.Benchmarks/NullRenderingPlatform.cs

@ -61,6 +61,28 @@ namespace Avalonia.Benchmarks
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
throw new NotImplementedException();

22
tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

@ -66,6 +66,28 @@ namespace Avalonia.UnitTests
return Mock.Of<IBitmapImpl>();
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmap(string fileName)
{
return Mock.Of<IBitmapImpl>();

22
tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

@ -42,6 +42,28 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
{
throw new NotImplementedException();
}
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
{
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmap(string fileName)
{
throw new NotImplementedException();

Loading…
Cancel
Save