Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drawing (vector icons) #1117

Merged
merged 8 commits into from Aug 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions samples/RenderTest/MainWindow.xaml
Expand Up @@ -27,6 +27,7 @@
</TabControl.Transition>
<TabItem Header="Animations"><pages:AnimationsPage/></TabItem>
<TabItem Header="Clipping"><pages:ClippingPage/></TabItem>
<TabItem Header="Drawing"><pages:DrawingPage/></TabItem>
</TabControl>
</DockPanel>
</Window>
132 changes: 132 additions & 0 deletions samples/RenderTest/Pages/DrawingPage.xaml
@@ -0,0 +1,132 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Styles>
<Style>
<Style.Resources>
<DrawingGroup x:Key="Bulb">
<DrawingGroup.Transform>
<MatrixTransform Matrix="1,0,0,1,0,-1028.4" />
</DrawingGroup.Transform>
<DrawingGroup>
<DrawingGroup.Transform>
<MatrixTransform Matrix="1,0,0,1.25,-10,1031.4" />
</DrawingGroup.Transform>
<GeometryDrawing Brush="#FF7F8C8D"
Geometry="F1 M24,14 A2,2,0,1,1,20,14 A2,2,0,1,1,24,14 z" />
</DrawingGroup>
<GeometryDrawing Brush="#FFF39C12"
Geometry="F1 M12,1030.4 C8.134,1030.4 5,1033.6 5,1037.6 5,1040.7 8.125,1043.5 9,1045.4 9.875,1047.2 9,1050.4 9,1050.4 L12,1049.9 15,1050.4 C15,1050.4 14.125,1047.2 15,1045.4 15.875,1043.5 19,1040.7 19,1037.6 19,1033.6 15.866,1030.4 12,1030.4 z" />
<GeometryDrawing Brush="#FFF1C40F"
Geometry="F1 M12,1030.4 C15.866,1030.4 19,1033.6 19,1037.6 19,1040.7 15.875,1043.5 15,1045.4 14.125,1047.2 15,1050.4 15,1050.4 L12,1049.9 12,1030.4 z" />
<GeometryDrawing Brush="#FFE67E22"
Geometry="F1 M9,1036.4 L8,1037.4 12,1049.4 16,1037.4 15,1036.4 14,1037.4 13,1036.4 12,1037.4 11,1036.4 10,1037.4 9,1036.4 z M9,1037.4 L10,1038.4 10.5,1037.9 11,1037.4 11.5,1037.9 12,1038.4 12.5,1037.9 13,1037.4 13.5,1037.9 14,1038.4 15,1037.4 15.438,1037.8 12,1048.1 8.5625,1037.8 9,1037.4 z" />
<DrawingGroup>
<DrawingGroup.Transform>
<MatrixTransform Matrix="1,0,0,1,9,1045.4" />
</DrawingGroup.Transform>
<GeometryDrawing Brush="#FFBDC3C7">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,6,5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
<GeometryDrawing Brush="#FF95A5A6"
Geometry="F1 M9,1045.4 L9,1050.4 12,1050.4 12,1049.4 15,1049.4 15,1048.4 12,1048.4 12,1047.4 15,1047.4 15,1046.4 12,1046.4 12,1045.4 9,1045.4 z" />
<GeometryDrawing Brush="#FF7F8C8D"
Geometry="F1 M9,1046.4 L9,1047.4 12,1047.4 12,1046.4 9,1046.4 z M9,1048.4 L9,1049.4 12,1049.4 12,1048.4 9,1048.4 z" />
</DrawingGroup>
</Style.Resources>
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="Auto,Auto,Auto,Auto">
<TextBlock Text="None"
Margin="3" />
<Border Grid.Column="0"
Grid.Row="1"
VerticalAlignment="Top"
HorizontalAlignment="Left"
BorderThickness="1"
BorderBrush="Gray"
Margin="5">
<DrawingPresenter Drawing="{StyleResource Bulb}" />
</Border>
<TextBlock Text="Fill"
Margin="3"
Grid.Column="1" />
<Border Grid.Column="1"
Grid.Row="1"
VerticalAlignment="Top"
HorizontalAlignment="Left"
BorderThickness="1"
BorderBrush="Gray"
Margin="5">
<DrawingPresenter Drawing="{StyleResource Bulb}"
Width="100"
Height="50"
Stretch="Fill" />
</Border>
<TextBlock Text="Uniform"
Margin="3"
Grid.Column="2" />
<Border Grid.Column="2"
Grid.Row="1"
VerticalAlignment="Top"
HorizontalAlignment="Left"
BorderThickness="1"
BorderBrush="Gray"
Margin="5">
<DrawingPresenter Drawing="{StyleResource Bulb}"
Width="100"
Height="50"
Stretch="Uniform" />
</Border>
<TextBlock Text="UniformToFill"
Margin="3"
Grid.Column="3" />
<Border Grid.Column="3"
Grid.Row="1"
VerticalAlignment="Top"
HorizontalAlignment="Left"
BorderThickness="1"
BorderBrush="Gray"
Margin="5">
<DrawingPresenter Drawing="{StyleResource Bulb}"
Width="100"
Height="50"
Stretch="UniformToFill" />
</Border>

<!-- For comparison -->

<Ellipse Grid.Row="2"
Grid.Column="0"
Width="100"
Height="50"
Stretch="None"
Fill="Blue"
Margin="5"/>
<Ellipse Grid.Row="2"
Grid.Column="1"
Width="100"
Height="50"
Stretch="Fill"
Fill="Blue"
Margin="5" />
<Ellipse Grid.Row="2"
Grid.Column="2"
Width="100"
Height="50"
Stretch="Uniform"
Fill="Blue"
Margin="5" />
<Ellipse Grid.Row="2"
Grid.Column="3"
Width="100"
Height="50"
Stretch="UniformToFill"
Fill="Blue"
Margin="5" />

</Grid>
</UserControl>
18 changes: 18 additions & 0 deletions samples/RenderTest/Pages/DrawingPage.xaml.cs
@@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace RenderTest.Pages
{
public class DrawingPage : UserControl
{
public DrawingPage()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
8 changes: 8 additions & 0 deletions samples/RenderTest/RenderTest.csproj
Expand Up @@ -51,6 +51,9 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\DrawingPage.xaml.cs">
<DependentUpon>DrawingPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ClippingPage.xaml.cs">
<DependentUpon>ClippingPage.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -178,6 +181,11 @@
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\DrawingPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
Expand Down
59 changes: 59 additions & 0 deletions src/Avalonia.Controls/DrawingPresenter.cs
@@ -0,0 +1,59 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.Metadata;

namespace Avalonia.Controls
{
public class DrawingPresenter : Control
{
static DrawingPresenter()
{
AffectsMeasure(DrawingProperty);
AffectsRender(DrawingProperty);
}

public static readonly StyledProperty<Drawing> DrawingProperty =
AvaloniaProperty.Register<DrawingPresenter, Drawing>(nameof(Drawing));

public static readonly StyledProperty<Stretch> StretchProperty =
AvaloniaProperty.Register<DrawingPresenter, Stretch>(nameof(Stretch), Stretch.Uniform);

[Content]
public Drawing Drawing
{
get => GetValue(DrawingProperty);
set => SetValue(DrawingProperty, value);
}

public Stretch Stretch
{
get => GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
}

private Matrix _transform = Matrix.Identity;

protected override Size MeasureOverride(Size availableSize)
{
if (Drawing == null) return new Size();

var (size, transform) = Shape.CalculateSizeAndTransform(availableSize, Drawing.GetBounds(), Stretch);

_transform = transform;

return size;
}

public override void Render(DrawingContext context)
{
if (Drawing != null)
{
using (context.PushPreTransform(_transform))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think to address the issue of required clipping in UniformToFill you could just push a clip here?

using (context.PushClip(Bounds))
{
Drawing.Draw(context);
}
}
}
}
}
28 changes: 16 additions & 12 deletions src/Avalonia.Controls/Shapes/Shape.cs
Expand Up @@ -155,11 +155,21 @@ protected override Size MeasureOverride(Size availableSize)
{
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
Rect shapeBounds = DefiningGeometry.Bounds;
var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);

if (_transform != transform)
{
_transform = transform;
_renderedGeometry = null;
}

return size;
}

internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)
{
Size shapeSize = new Size(shapeBounds.Right, shapeBounds.Bottom);
Matrix translate = Matrix.Identity;
double width = Width;
double height = Height;
double desiredX = availableSize.Width;
double desiredY = availableSize.Height;
double sx = 0.0;
Expand Down Expand Up @@ -226,15 +236,9 @@ protected override Size MeasureOverride(Size availableSize)
break;
}

var t = translate * Matrix.CreateScale(sx, sy);

if (_transform != t)
{
_transform = t;
_renderedGeometry = null;
}

return new Size(shapeSize.Width * sx, shapeSize.Height * sy);
var transform = translate * Matrix.CreateScale(sx, sy);
var size = new Size(shapeSize.Width * sx, shapeSize.Height * sy);
return (size, transform);
}

private static void AffectsGeometryInvalidate(AvaloniaPropertyChangedEventArgs e)
Expand Down
29 changes: 29 additions & 0 deletions src/Avalonia.Visuals/Matrix.cs
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Globalization;
using System.Linq;

namespace Avalonia
{
Expand Down Expand Up @@ -295,5 +296,33 @@ public Matrix Invert()
((_m21 * _m32) - (_m22 * _m31)) / d,
((_m12 * _m31) - (_m11 * _m32)) / d);
}

/// <summary>
/// Parses a <see cref="Matrix"/> string.
/// </summary>
/// <param name="s">The string.</param>
/// <param name="culture">The current culture.</param>
/// <returns>The <see cref="Matrix"/>.</returns>
public static Matrix Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToArray();

if (parts.Length == 6)
{
return new Matrix(
double.Parse(parts[0], culture),
double.Parse(parts[1], culture),
double.Parse(parts[2], culture),
double.Parse(parts[3], culture),
double.Parse(parts[4], culture),
double.Parse(parts[5], culture));
}
else
{
throw new FormatException("Invalid Matrix.");
}
}
}
}
9 changes: 9 additions & 0 deletions src/Avalonia.Visuals/Media/Drawing.cs
@@ -0,0 +1,9 @@
namespace Avalonia.Media
{
public abstract class Drawing : AvaloniaObject
{
public abstract void Draw(DrawingContext context);

public abstract Rect GetBounds();
}
}