Browse Source

Merge remote-tracking branch 'Perspex/master'

pull/532/head
Wiesław Šoltés 10 years ago
parent
commit
3cc4a91bd2
  1. 3
      .gitmodules
  2. 3
      Perspex.sln
  3. 19
      samples/ControlCatalog.Desktop/Program.cs
  4. 16
      samples/ControlCatalog/Pages/CanvasPage.xaml
  5. 21
      src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs
  6. 1
      src/Markup/Perspex.Markup.Xaml/Context/PerspexTypeFeatureProvider.cs
  7. 10
      src/Markup/Perspex.Markup.Xaml/Converters/BitmapTypeConverter.cs
  8. 32
      src/Markup/Perspex.Markup.Xaml/Converters/SizeTypeConverter.cs
  9. 2
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StaticExtension.cs
  10. 2
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TypeExtension.cs
  11. 2
      src/Markup/Perspex.Markup.Xaml/OmniXAML
  12. 29
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  13. 66
      src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs
  14. 14
      src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs
  15. 1
      src/Markup/Perspex.Markup.Xaml/glass
  16. 1
      src/Markup/Perspex.Markup.Xaml/packages.config
  17. 11
      src/Perspex.Base/Platform/IAssetLoader.cs
  18. 103
      src/Perspex.SceneGraph/Media/ArcSegment.cs
  19. 65
      src/Perspex.SceneGraph/Media/BezierSegment .cs
  20. 2
      src/Perspex.SceneGraph/Media/Geometry.cs
  21. 31
      src/Perspex.SceneGraph/Media/LineSegment.cs
  22. 102
      src/Perspex.SceneGraph/Media/PathFigure.cs
  23. 123
      src/Perspex.SceneGraph/Media/PathGeometry.cs
  24. 15
      src/Perspex.SceneGraph/Media/PathGeometryCollections.cs
  25. 10
      src/Perspex.SceneGraph/Media/PathSegment.cs
  26. 46
      src/Perspex.SceneGraph/Media/QuadraticBezierSegment .cs
  27. 57
      src/Perspex.SceneGraph/Media/TransformGroup.cs
  28. 9
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  29. 25
      src/Perspex.SceneGraph/Size.cs
  30. 187
      src/Shared/PlatformSupport/AssetLoader.cs
  31. 2
      src/Skia/Perspex.Skia.Android.TestApp/Perspex.Skia.Android.TestApp.csproj
  32. 4
      src/Skia/Perspex.Skia.Android/Perspex.Skia.Android.csproj
  33. 2
      src/Skia/Perspex.Skia.Android/packages.config
  34. 6
      src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj
  35. 2
      src/Skia/Perspex.Skia.Desktop/packages.config
  36. 2
      src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj
  37. 2
      src/Skia/Perspex.Skia.iOS/packages.config
  38. 10
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  39. 5
      src/Skia/Perspex.Skia/SkiaSharpExtensions.cs
  40. 40
      src/Skia/Perspex.Skia/StreamGeometryImpl.cs
  41. 7
      src/Skia/Perspex.Skia/readme.md
  42. 7
      src/Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs
  43. 1
      src/Windows/Perspex.Direct2D1/Media/StreamGeometryImpl.cs

3
.gitmodules

@ -8,3 +8,6 @@
[submodule "src/Markup/Perspex.Markup.Xaml/OmniXAML"]
path = src/Markup/Perspex.Markup.Xaml/OmniXAML
url = https://github.com/Perspex/OmniXAML.git
[submodule "src/Markup/Perspex.Markup.Xaml/glass"]
path = src/Markup/Perspex.Markup.Xaml/glass
url = https://github.com/SuperJMN/glass

3
Perspex.sln

@ -60,9 +60,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Markup.Xaml", "src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj", "{3E53A01A-B331-47F3-B828-4A5717E77A24}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9B9E3891-2366-4253-A952-D08BCEB71098}"
ProjectSection(SolutionItems) = preProject
samples\ControlCatalog\ControlCatalog.csproj = samples\ControlCatalog\ControlCatalog.csproj
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "samples\TestApplication\TestApplication.csproj", "{E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}"
ProjectSection(ProjectDependencies) = postProject

19
samples/ControlCatalog.Desktop/Program.cs

@ -6,24 +6,6 @@ using Perspex;
using System.Reflection;
using Perspex.Platform;
// Not sure where the best home for this is
namespace Perspex
{
public static class SharedApplicationExtensions
{
// For true Portable apps we need to select the PCL assembly NOT the host platform exe. Unfortunately
// Win32 subsystem registers one by default (the wrong one) and so this can override that.
//
public static AppT UseAssetAssembly<AppT>(this AppT app, Assembly assembly) where AppT : Application
{
// Asset loading searches our own assembly?
PerspexLocator.CurrentMutable.GetService<IAssetLoader>().SetDefaultAssembly(assembly);
return app;
}
}
}
namespace ControlCatalog
{
internal class Program
@ -34,7 +16,6 @@ namespace ControlCatalog
new App()
.ConfigureRenderSystem(args)
.UseAssetAssembly(typeof(App).Assembly)
.LoadFromXaml()
.RunWithMainWindow<MainWindow>();
}

16
samples/ControlCatalog/Pages/CanvasPage.xaml

@ -4,10 +4,22 @@
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>
<Canvas Background="Yellow" Width="300" Height="400">
<Rectangle Fill="Blue" Width="63" Height="41" Canvas.Left="40" Canvas.Top="31"/>
<Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="160" Canvas.Top="79"/>
<Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/>
<Path Fill="Orange" Data="M 0,0 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
<Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="0,0" IsClosed="True">
<QuadraticBezierSegment Point1="50,0" Point2="50,-50" />
<QuadraticBezierSegment Point1="100,-50" Point2="100,0" />
<LineSegment Point="50,0" />
<LineSegment Point="50,50" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Line StartPoint="120,185" EndPoint="30,115" Stroke="Red" StrokeThickness="2"/>
<Polygon Points="75,0 120,120 0,45 150,45 30,120" Stroke="DarkBlue" StrokeThickness="1" Fill="Violet" Canvas.Left="150" Canvas.Top="180"/>
<Polygon Points="75,0 120,120 0,45 150,45 30,120" Stroke="DarkBlue" StrokeThickness="1" Fill="Violet" Canvas.Left="150" Canvas.Top="31"/>
<Polyline Points="0,0 65,0 78,-26 91,39 104,-39 117,13 130,0 195,0" Stroke="Brown" Canvas.Left="30" Canvas.Top="350"/>
</Canvas>
</StackPanel>

21
src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs

@ -7,6 +7,8 @@ using OmniXaml.ObjectAssembler;
using OmniXaml.ObjectAssembler.Commands;
using OmniXaml.TypeConversion;
using Perspex.Markup.Xaml.Templates;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Perspex.Markup.Xaml.Context
{
@ -27,7 +29,8 @@ namespace Perspex.Markup.Xaml.Context
mapping.Map<TreeDataTemplate>(x => x.Content, new TemplateLoader());
mapping.Map<ItemsPanelTemplate>(x => x.Content, new TemplateLoader());
var valueContext = new ValueContext(typeSource, topDownValueContext);
var parsingDictionary = GetDictionary(settings);
var valueContext = new ValueContext(typeSource, topDownValueContext, parsingDictionary);
assembler = new ObjectAssembler(typeSource, valueContext, settings);
objectAssembler = new TemplateHostingObjectAssembler(assembler, mapping);
}
@ -55,5 +58,21 @@ namespace Perspex.Markup.Xaml.Context
{
objectAssembler.OverrideInstance(instance);
}
private static IReadOnlyDictionary<string, object> GetDictionary(Settings settings)
{
IReadOnlyDictionary<string, object> dict;
if (settings != null)
{
dict = settings.ParsingContext;
}
else
{
dict = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
}
return dict;
}
}
}

1
src/Markup/Perspex.Markup.Xaml/Context/PerspexTypeFeatureProvider.cs

@ -165,6 +165,7 @@ namespace Perspex.Markup.Xaml.Context
RegisterTypeConverter(typeof(RelativePoint), new RelativePointTypeConverter());
RegisterTypeConverter(typeof(RelativeRect), new RelativeRectTypeConverter());
RegisterTypeConverter(typeof(RowDefinitions), new RowDefinitionsTypeConverter());
RegisterTypeConverter(typeof(Size), new SizeTypeConverter());
RegisterTypeConverter(typeof(Selector), new SelectorTypeConverter());
RegisterTypeConverter(typeof(SolidColorBrush), new BrushTypeConverter());
RegisterTypeConverter(typeof(Thickness), new ThicknessTypeConverter());

10
src/Markup/Perspex.Markup.Xaml/Converters/BitmapTypeConverter.cs

@ -24,6 +24,7 @@ namespace Perspex.Markup.Xaml.Converters
public object ConvertFrom(IValueContext context, CultureInfo culture, object value)
{
var uri = new Uri((string)value, UriKind.RelativeOrAbsolute);
var baseUri = GetBaseUri(context);
var scheme = uri.IsAbsoluteUri ? uri.Scheme : "file";
switch (scheme)
@ -32,7 +33,7 @@ namespace Perspex.Markup.Xaml.Converters
return new Bitmap((string)value);
default:
var assets = PerspexLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(uri));
return new Bitmap(assets.Open(uri, baseUri));
}
}
@ -40,5 +41,12 @@ namespace Perspex.Markup.Xaml.Converters
{
throw new NotImplementedException();
}
private Uri GetBaseUri(IValueContext context)
{
object result;
context.ParsingDictionary.TryGetValue("Uri", out result);
return result as Uri;
}
}
}

32
src/Markup/Perspex.Markup.Xaml/Converters/SizeTypeConverter.cs

@ -0,0 +1,32 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using OmniXaml.TypeConversion;
namespace Perspex.Markup.Xaml.Converters
{
public class SizeTypeConverter : ITypeConverter
{
public bool CanConvertFrom(IValueContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IValueContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IValueContext context, CultureInfo culture, object value)
{
return Size.Parse((string)value, culture);
}
public object ConvertTo(IValueContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

2
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StaticExtension.cs

@ -24,7 +24,7 @@ namespace Perspex.Markup.Xaml.MarkupExtensions
public override object ProvideValue(MarkupExtensionContext markupExtensionContext)
{
var typeRepository = markupExtensionContext.TypeRepository;
var typeRepository = markupExtensionContext.ValueContext.TypeRepository;
var typeAndMember = GetTypeAndMember(Identifier);
var prefixAndType = GetPrefixAndType(typeAndMember.Item1);
var xamlType = typeRepository.GetByPrefix(prefixAndType.Item1, prefixAndType.Item2);

2
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TypeExtension.cs

@ -43,7 +43,7 @@ namespace Perspex.Markup.Xaml.MarkupExtensions
return Type;
}
return ResolveFromString(TypeName, markupExtensionContext.TypeRepository);
return ResolveFromString(TypeName, markupExtensionContext.ValueContext.TypeRepository);
}
}
}

2
src/Markup/Perspex.Markup.Xaml/OmniXAML

@ -1 +1 @@
Subproject commit 75e0dc32fe9a6d97f5b59d2b7d689db2475f444f
Subproject commit b122549406107170bbe6e67c0d6a1a4252beef77

29
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -66,6 +66,7 @@
<Compile Include="Converters\PerspexListTypeConverter.cs" />
<Compile Include="Converters\PerspexPropertyTypeConverter.cs" />
<Compile Include="Converters\PointsListTypeConverter.cs" />
<Compile Include="Converters\SizeTypeConverter.cs" />
<Compile Include="Converters\PointTypeConverter.cs" />
<Compile Include="Converters\RelativePointTypeConverter.cs" />
<Compile Include="Converters\RelativeRectTypeConverter.cs" />
@ -81,22 +82,22 @@
<Compile Include="Data\SourceBindingEndpoint.cs" />
<Compile Include="Data\StyleResourceBinding.cs" />
<Compile Include="Data\TargetBindingEndpoint.cs" />
<Compile Include="glass\Glass.Core\AutoKeyDictionary.cs" />
<Compile Include="glass\Glass.Core\DependencySorter.cs" />
<Compile Include="glass\Glass.Core\EnumExtensions.cs" />
<Compile Include="glass\Glass.Core\Extensions.cs" />
<Compile Include="glass\Glass.Core\Guard.cs" />
<Compile Include="glass\Glass.Core\IAdd.cs" />
<Compile Include="glass\Glass.Core\IDependency.cs" />
<Compile Include="glass\Glass.Core\ReflectionExtensions.cs" />
<Compile Include="glass\Glass.Core\StackingLinkedList.cs" />
<Compile Include="glass\Glass.Core\StackingLinkedListMixin.cs" />
<Compile Include="MarkupExtensions\StyleResourceExtension.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="MarkupExtensions\StaticExtension.cs" />
<Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />
<Compile Include="MarkupExtensions\TypeExtension.cs" />
<Compile Include="OmniXAML\Source\Glass\AutoKeyDictionary.cs" />
<Compile Include="OmniXAML\Source\Glass\DependencySorter.cs" />
<Compile Include="OmniXAML\Source\Glass\EnumExtensions.cs" />
<Compile Include="OmniXAML\Source\Glass\Extensions.cs" />
<Compile Include="OmniXAML\Source\Glass\Guard.cs" />
<Compile Include="OmniXAML\Source\Glass\IAdd.cs" />
<Compile Include="OmniXAML\Source\Glass\IDependency.cs" />
<Compile Include="OmniXAML\Source\Glass\ReflectionExtensions.cs" />
<Compile Include="OmniXAML\Source\Glass\StackingLinkedList.cs" />
<Compile Include="OmniXAML\Source\Glass\StackingLinkedListMixin.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\ContentPropertyAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\DependsOnAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\XmlnsDefinitionAttribute.cs" />
@ -275,6 +276,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="glass\Glass.Core\Glass.Core.nuspec" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@ -316,6 +318,10 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="Glass, Version=1.6.0.113, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Glass.1.6.0.113\lib\portable45-net45+win8\Glass.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Sprache, Version=2.0.0.50, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Sprache.2.0.0.50\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid+Xamarin.iOS10+MonoTouch\Sprache.dll</HintPath>
<Private>True</Private>
@ -333,6 +339,9 @@
<HintPath>..\..\..\packages\Rx-PlatformServices.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.PlatformServices.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Content Include="glass\Glass.Core\Glass.Core.csproj" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

66
src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs

@ -15,7 +15,7 @@ namespace Perspex.Markup.Xaml
using Controls;
using Data;
using OmniXaml.ObjectAssembler;
using System.Linq;
/// <summary>
/// Loads XAML for a perspex application.
/// </summary>
@ -23,6 +23,7 @@ namespace Perspex.Markup.Xaml
{
private static PerspexParserFactory s_parserFactory;
private static IInstanceLifeCycleListener s_lifeCycleListener = new PerspexLifeCycleListener();
private static Stack<Uri> s_uriStack = new Stack<Uri>();
/// <summary>
/// Initializes a new instance of the <see cref="PerspexXamlLoader"/> class.
@ -41,6 +42,18 @@ namespace Perspex.Markup.Xaml
{
}
/// <summary>
/// Gets the URI of the XAML file currently being loaded.
/// </summary>
/// <remarks>
/// TODO: Making this internal for now as I'm not sure that this is the correct
/// thing to do, but its needd by <see cref="StyleInclude"/> to get the URL of
/// the currently loading XAML file, as we can't use the OmniXAML parsing context
/// there. Maybe we need a way to inject OmniXAML context into the objects its
/// constructing?
/// </remarks>
internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null;
/// <summary>
/// Loads the XAML into a Perspex component.
/// </summary>
@ -84,7 +97,7 @@ namespace Perspex.Markup.Xaml
{
var initialize = rootInstance as ISupportInitialize;
initialize?.BeginInit();
return Load(stream, rootInstance);
return Load(stream, rootInstance, uri);
}
}
}
@ -96,11 +109,14 @@ namespace Perspex.Markup.Xaml
/// Loads XAML from a URI.
/// </summary>
/// <param name="uri">The URI of the XAML file.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(Uri uri, object rootInstance = null)
public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(uri != null);
@ -112,9 +128,9 @@ namespace Perspex.Markup.Xaml
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
using (var stream = assetLocator.Open(uri))
using (var stream = assetLocator.Open(uri, baseUri))
{
return Load(stream, rootInstance);
return Load(stream, rootInstance, uri);
}
}
@ -143,23 +159,43 @@ namespace Perspex.Markup.Xaml
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <param name="uri">The URI of the XAML</param>
/// <returns>The loaded object.</returns>
public object Load(Stream stream, object rootInstance = null)
public object Load(Stream stream, object rootInstance = null, Uri uri = null)
{
var result = base.Load(stream, new Settings
try
{
RootInstance = rootInstance,
InstanceLifeCycleListener = s_lifeCycleListener,
});
if (uri != null)
{
s_uriStack.Push(uri);
}
var topLevel = result as TopLevel;
var result = base.Load(stream, new Settings
{
RootInstance = rootInstance,
InstanceLifeCycleListener = s_lifeCycleListener,
ParsingContext = new Dictionary<string, object>
{
{ "Uri", uri }
}
});
var topLevel = result as TopLevel;
if (topLevel != null)
{
DelayedBinding.ApplyBindings(topLevel);
}
if (topLevel != null)
return result;
}
finally
{
DelayedBinding.ApplyBindings(topLevel);
if (uri != null)
{
s_uriStack.Pop();
}
}
return result;
}
private static PerspexParserFactory GetParserFactory()

14
src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs

@ -11,8 +11,20 @@ namespace Perspex.Markup.Xaml.Styling
/// </summary>
public class StyleInclude : IStyle
{
private Uri _baseUri;
private IStyle _loaded;
/// <summary>
/// Initializes a new instance of the <see cref="StyleInclude"/> class.
/// </summary>
public StyleInclude()
{
// StyleInclude will usually be loaded from XAML and its URI can be relative to the
// XAML file that its included in, so store the current XAML file's URI if any as
// a base URI.
_baseUri = PerspexXamlLoader.UriContext;
}
/// <summary>
/// Gets or sets the source URL.
/// </summary>
@ -28,7 +40,7 @@ namespace Perspex.Markup.Xaml.Styling
if (_loaded == null)
{
var loader = new PerspexXamlLoader();
_loaded = (IStyle)loader.Load(Source);
_loaded = (IStyle)loader.Load(Source, _baseUri);
}
return _loaded;

1
src/Markup/Perspex.Markup.Xaml/glass

@ -0,0 +1 @@
Subproject commit 9e17adbd3ac0b342e7922ee2d5a11e5d50b7e687

1
src/Markup/Perspex.Markup.Xaml/packages.config

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Glass" version="1.6.0.113" targetFramework="portable45-net45+win8" />
<package id="Rx-Core" version="2.2.5" targetFramework="portable46-net451+win81" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable46-net451+win81" />
<package id="Rx-Linq" version="2.2.5" targetFramework="portable46-net451+win81" />

11
src/Perspex.Base/Platform/IAssetLoader.cs

@ -21,22 +21,27 @@ namespace Perspex.Platform
/// <param name="asm"></param>
void SetDefaultAssembly(Assembly asm);
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns>
bool Exists(Uri uri);
bool Exists(Uri uri, Uri baseUri = null);
/// <summary>
/// Opens the resource with the requested URI.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>A stream containing the resource contents.</returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
Stream Open(Uri uri);
Stream Open(Uri uri, Uri baseUri = null);
}
}

103
src/Perspex.SceneGraph/Media/ArcSegment.cs

@ -0,0 +1,103 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Perspex.Media
{
public sealed class ArcSegment : PathSegment
{
/// <summary>
/// Defines the <see cref="IsLargeArc"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsLargeArcProperty
= PerspexProperty.Register<ArcSegment, bool>(nameof(IsLargeArc), false);
/// <summary>
/// Defines the <see cref="Point"/> property.
/// </summary>
public static readonly StyledProperty<Point> PointProperty
= PerspexProperty.Register<ArcSegment, Point>(nameof(Point));
/// <summary>
/// Defines the <see cref="RotationAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> RotationAngleProperty
= PerspexProperty.Register<ArcSegment, double>(nameof(RotationAngle), 0);
/// <summary>
/// Defines the <see cref="Size"/> property.
/// </summary>
public static readonly StyledProperty<Size> SizeProperty
= PerspexProperty.Register<ArcSegment, Size>(nameof(Size));
/// <summary>
/// Defines the <see cref="SweepDirection"/> property.
/// </summary>
public static readonly StyledProperty<SweepDirection> SweepDirectionProperty
= PerspexProperty.Register<ArcSegment, SweepDirection>(nameof(SweepDirection), SweepDirection.Clockwise);
/// <summary>
/// Gets or sets a value indicating whether this instance is large arc.
/// </summary>
/// <value>
/// <c>true</c> if this instance is large arc; otherwise, <c>false</c>.
/// </value>
public bool IsLargeArc
{
get { return GetValue(IsLargeArcProperty); }
set { SetValue(IsLargeArcProperty, value); }
}
/// <summary>
/// Gets or sets the point.
/// </summary>
/// <value>
/// The point.
/// </value>
public Point Point
{
get { return GetValue(PointProperty); }
set { SetValue(PointProperty, value); }
}
/// <summary>
/// Gets or sets the rotation angle.
/// </summary>
/// <value>
/// The rotation angle.
/// </value>
public double RotationAngle
{
get { return GetValue(RotationAngleProperty); }
set { SetValue(RotationAngleProperty, value); }
}
/// <summary>
/// Gets or sets the size.
/// </summary>
/// <value>
/// The size.
/// </value>
public Size Size
{
get { return GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
/// <summary>
/// Gets or sets the sweep direction.
/// </summary>
/// <value>
/// The sweep direction.
/// </value>
public SweepDirection SweepDirection
{
get { return GetValue(SweepDirectionProperty); }
set { SetValue(SweepDirectionProperty, value); }
}
protected internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.ArcTo(Point, Size, RotationAngle, IsLargeArc, SweepDirection);
}
}
}

65
src/Perspex.SceneGraph/Media/BezierSegment .cs

@ -0,0 +1,65 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Perspex.Media
{
public sealed class BezierSegment : PathSegment
{
/// <summary>
/// Defines the <see cref="Point1"/> property.
/// </summary>
public static readonly StyledProperty<Point> Point1Property
= PerspexProperty.Register<BezierSegment, Point>(nameof(Point1));
/// <summary>
/// Defines the <see cref="Point2"/> property.
/// </summary>
public static readonly StyledProperty<Point> Point2Property
= PerspexProperty.Register<BezierSegment, Point>(nameof(Point2));
/// <summary>
/// Defines the <see cref="Point3"/> property.
/// </summary>
public static readonly StyledProperty<Point> Point3Property
= PerspexProperty.Register<BezierSegment, Point>(nameof(Point3));
/// <summary>
/// Gets or sets the point1.
/// </summary>
/// <value>
/// The point1.
/// </value>
public Point Point1
{
get { return GetValue(Point1Property); }
set { SetValue(Point1Property, value); }
}
/// <summary>
/// Gets or sets the point2.
/// </summary>
/// <value>
/// The point2.
/// </value>
public Point Point2
{
get { return GetValue(Point2Property); }
set { SetValue(Point2Property, value); }
}
/// <summary>
/// Gets or sets the point3.
/// </summary>
/// <value>
/// The point3.
/// </value>
public Point Point3
{
get { return GetValue(Point3Property); }
set { SetValue(Point3Property, value); }
}
protected internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.CubicBezierTo(Point1, Point2, Point3);
}
}
}

2
src/Perspex.SceneGraph/Media/Geometry.cs

@ -36,7 +36,7 @@ namespace Perspex.Media
/// <summary>
/// Gets the platform-specific implementation of the geometry.
/// </summary>
public IGeometryImpl PlatformImpl
public virtual IGeometryImpl PlatformImpl
{
get;
protected set;

31
src/Perspex.SceneGraph/Media/LineSegment.cs

@ -0,0 +1,31 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Perspex.Media
{
public sealed class LineSegment : PathSegment
{
/// <summary>
/// Defines the <see cref="Point"/> property.
/// </summary>
public static readonly StyledProperty<Point> PointProperty
= PerspexProperty.Register<LineSegment, Point>(nameof(Point));
/// <summary>
/// Gets or sets the point.
/// </summary>
/// <value>
/// The point.
/// </value>
public Point Point
{
get { return GetValue(PointProperty); }
set { SetValue(PointProperty, value); }
}
protected internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.LineTo(Point);
}
}
}

102
src/Perspex.SceneGraph/Media/PathFigure.cs

@ -0,0 +1,102 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Metadata;
namespace Perspex.Media
{
public sealed class PathFigure : PerspexObject
{
/// <summary>
/// Defines the <see cref="IsClosed"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsClosedProperty
= PerspexProperty.Register<PathFigure, bool>(nameof(IsClosed), true);
/// <summary>
/// Defines the <see cref="IsFilled"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsFilledProperty
= PerspexProperty.Register<PathFigure, bool>(nameof(IsFilled), true);
/// <summary>
/// Defines the <see cref="Segments"/> property.
/// </summary>
public static readonly DirectProperty<PathFigure, PathSegments> SegmentsProperty
= PerspexProperty.RegisterDirect<PathFigure, PathSegments>(nameof(Segments), f => f.Segments, (f, s) => f.Segments = s);
/// <summary>
/// Defines the <see cref="StartPoint"/> property.
/// </summary>
public static readonly StyledProperty<Point> StartPointProperty
= PerspexProperty.Register<PathFigure, Point>(nameof(StartPoint));
/// <summary>
/// Initializes a new instance of the <see cref="PathFigure"/> class.
/// </summary>
public PathFigure()
{
Segments = new PathSegments();
}
/// <summary>
/// Gets or sets a value indicating whether this instance is closed.
/// </summary>
/// <value>
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
/// </value>
public bool IsClosed
{
get { return GetValue(IsClosedProperty); }
set { SetValue(IsClosedProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether this instance is filled.
/// </summary>
/// <value>
/// <c>true</c> if this instance is filled; otherwise, <c>false</c>.
/// </value>
public bool IsFilled
{
get { return GetValue(IsFilledProperty); }
set { SetValue(IsFilledProperty, value); }
}
/// <summary>
/// Gets or sets the segments.
/// </summary>
/// <value>
/// The segments.
/// </value>
[Content]
public PathSegments Segments
{
get { return _segments; }
set { SetAndRaise(SegmentsProperty, ref _segments, value); }
}
/// <summary>
/// Gets or sets the start point.
/// </summary>
/// <value>
/// The start point.
/// </value>
public Point StartPoint
{
get { return GetValue(StartPointProperty); }
set { SetValue(StartPointProperty, value); }
}
internal void ApplyTo(StreamGeometryContext ctx)
{
ctx.BeginFigure(StartPoint, IsFilled);
foreach (var segment in Segments)
{
segment.ApplyTo(ctx);
}
ctx.EndFigure(IsClosed);
}
private PathSegments _segments;
}
}

123
src/Perspex.SceneGraph/Media/PathGeometry.cs

@ -0,0 +1,123 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Collections;
using Perspex.Metadata;
using Perspex.Platform;
using System;
namespace Perspex.Media
{
public class PathGeometry : StreamGeometry
{
/// <summary>
/// Defines the <see cref="Figures"/> property.
/// </summary>
public static readonly DirectProperty<PathGeometry, PathFigures> FiguresProperty =
PerspexProperty.RegisterDirect<PathGeometry, PathFigures>(nameof(Figures), g => g.Figures, (g, f) => g.Figures = f);
/// <summary>
/// Defines the <see cref="FillRule"/> property.
/// </summary>
public static readonly StyledProperty<FillRule> FillRuleProperty =
PerspexProperty.Register<PathGeometry, FillRule>(nameof(FillRule));
static PathGeometry()
{
FiguresProperty.Changed.Subscribe(onNext: v =>
{
(v.Sender as PathGeometry)?.OnFiguresChanged(v.OldValue as PathFigures, v.NewValue as PathFigures);
});
}
/// <summary>
/// Initializes a new instance of the <see cref="PathGeometry"/> class.
/// </summary>
public PathGeometry()
{
Figures = new PathFigures();
}
/// <summary>
/// Gets or sets the figures.
/// </summary>
/// <value>
/// The figures.
/// </value>
[Content]
public PathFigures Figures
{
get { return _figures; }
set { SetAndRaise(FiguresProperty, ref _figures, value); }
}
/// <summary>
/// Gets or sets the fill rule.
/// </summary>
/// <value>
/// The fill rule.
/// </value>
public FillRule FillRule
{
get { return GetValue(FillRuleProperty); }
set { SetValue(FillRuleProperty, value); }
}
public override IGeometryImpl PlatformImpl
{
get
{
PrepareIfNeeded();
return base.PlatformImpl;
}
protected set
{
base.PlatformImpl = value;
}
}
public override Geometry Clone()
{
PrepareIfNeeded();
return base.Clone();
}
public void PrepareIfNeeded()
{
if (_isDirty)
{
_isDirty = false;
using (var ctx = Open())
{
ctx.SetFillRule(FillRule);
foreach (var f in Figures)
{
f.ApplyTo(ctx);
}
}
}
}
internal void NotifyChanged()
{
_isDirty = true;
}
private PathFigures _figures;
private IDisposable _figuresObserver = null;
private IDisposable _figuresPropertiesObserver = null;
private bool _isDirty = true;
private void OnFiguresChanged(PathFigures oldValue, PathFigures newValue)
{
_figuresObserver?.Dispose();
_figuresPropertiesObserver?.Dispose();
_figuresObserver = newValue?.ForEachItem(f => NotifyChanged(), f => NotifyChanged(), () => NotifyChanged());
_figuresPropertiesObserver = newValue?.TrackItemPropertyChanged(t => NotifyChanged());
}
}
}

15
src/Perspex.SceneGraph/Media/PathGeometryCollections.cs

@ -0,0 +1,15 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Collections;
namespace Perspex.Media
{
public sealed class PathFigures : PerspexList<PathFigure>
{
}
public sealed class PathSegments : PerspexList<PathSegment>
{
}
}

10
src/Perspex.SceneGraph/Media/PathSegment.cs

@ -0,0 +1,10 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Perspex.Media
{
public abstract class PathSegment : PerspexObject
{
protected internal abstract void ApplyTo(StreamGeometryContext ctx);
}
}

46
src/Perspex.SceneGraph/Media/QuadraticBezierSegment .cs

@ -0,0 +1,46 @@
namespace Perspex.Media
{
public sealed class QuadraticBezierSegment : PathSegment
{
/// <summary>
/// Defines the <see cref="Point1"/> property.
/// </summary>
public static readonly StyledProperty<Point> Point1Property
= PerspexProperty.Register<QuadraticBezierSegment, Point>(nameof(Point1));
/// <summary>
/// Defines the <see cref="Point2"/> property.
/// </summary>
public static readonly StyledProperty<Point> Point2Property
= PerspexProperty.Register<QuadraticBezierSegment, Point>(nameof(Point2));
/// <summary>
/// Gets or sets the point1.
/// </summary>
/// <value>
/// The point1.
/// </value>
public Point Point1
{
get { return GetValue(Point1Property); }
set { SetValue(Point1Property, value); }
}
/// <summary>
/// Gets or sets the point2.
/// </summary>
/// <value>
/// The point2.
/// </value>
public Point Point2
{
get { return GetValue(Point2Property); }
set { SetValue(Point2Property, value); }
}
protected internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.QuadraticBezierTo(Point1, Point2);
}
}
}

57
src/Perspex.SceneGraph/Media/TransformGroup.cs

@ -0,0 +1,57 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Collections;
using Perspex.Metadata;
namespace Perspex.Media
{
public class TransformGroup : Transform
{
/// <summary>
/// Defines the <see cref="Children"/> property.
/// </summary>
public static readonly PerspexProperty<Transforms> ChildrenProperty =
PerspexProperty.Register<TransformGroup, Transforms>(nameof(Children));
public TransformGroup()
{
Children = new Transforms();
}
/// <summary>
/// Gets or sets the children.
/// </summary>
/// <value>
/// The children.
/// </value>
[Content]
public Transforms Children
{
get { return GetValue(ChildrenProperty); }
set { SetValue(ChildrenProperty, value); }
}
/// <summary>
/// Gets the tranform's <see cref="Matrix" />.
/// </summary>
public override Matrix Value
{
get
{
Matrix result = Matrix.Identity;
foreach (var t in Children)
{
result *= t.Value;
}
return result;
}
}
}
public sealed class Transforms : PerspexList<Transform>
{
}
}

9
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -61,11 +61,14 @@
<Compile Include="Matrix.cs" />
<Compile Include="Media\AlignmentY.cs" />
<Compile Include="Media\AlignmentX.cs" />
<Compile Include="Media\ArcSegment.cs" />
<Compile Include="Media\BezierSegment .cs" />
<Compile Include="Media\Brush.cs" />
<Compile Include="Media\Brushes.cs" />
<Compile Include="Media\BrushMappingMode.cs" />
<Compile Include="Media\Color.cs" />
<Compile Include="Media\Colors.cs" />
<Compile Include="Media\TransformGroup.cs" />
<Compile Include="Media\DashStyle.cs" />
<Compile Include="Media\DrawingContext.cs" />
<Compile Include="Media\FillRule.cs" />
@ -75,9 +78,15 @@
<Compile Include="Media\IBrush.cs" />
<Compile Include="Media\ISolidColorBrush.cs" />
<Compile Include="Media\LineGeometry.cs" />
<Compile Include="Media\LineSegment.cs" />
<Compile Include="Media\Mutable\SolidColorBrush.cs" />
<Compile Include="Media\PathGeometryCollections.cs" />
<Compile Include="Media\PathFigure.cs" />
<Compile Include="Media\PathGeometry.cs" />
<Compile Include="Media\PathSegment.cs" />
<Compile Include="Media\PenLineJoin.cs" />
<Compile Include="Media\PolylineGeometry.cs" />
<Compile Include="Media\QuadraticBezierSegment .cs" />
<Compile Include="Media\RadialGradientBrush.cs" />
<Compile Include="Media\LinearGradientBrush.cs" />
<Compile Include="Media\MediaExtensions.cs" />

25
src/Perspex.SceneGraph/Size.cs

@ -3,6 +3,7 @@
using System;
using System.Globalization;
using System.Linq;
namespace Perspex
{
@ -128,6 +129,28 @@ namespace Perspex
return new Size(size._width - toSubstract._width, size._height - toSubstract._height);
}
/// <summary>
/// Parses a <see cref="Size"/> string.
/// </summary>
/// <param name="s">The string.</param>
/// <param name="culture">The current culture.</param>
/// <returns>The <see cref="Size"/>.</returns>
public static Size Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
if (parts.Count == 2)
{
return new Size(double.Parse(parts[0], culture), double.Parse(parts[1], culture));
}
else
{
throw new FormatException("Invalid Size.");
}
}
/// <summary>
/// Constrains the size.
/// </summary>
@ -227,4 +250,4 @@ namespace Perspex
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", _width, _height);
}
}
}
}

187
src/Shared/PlatformSupport/AssetLoader.cs

@ -15,26 +15,6 @@ namespace Perspex.Shared.PlatformSupport
/// </summary>
public class AssetLoader : IAssetLoader
{
class AssemblyDescriptor
{
public AssemblyDescriptor(Assembly assembly)
{
Assembly = assembly;
if (assembly != null)
{
Resources = assembly.GetManifestResourceNames()
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
Name = assembly.GetName().Name;
}
}
public Assembly Assembly { get; }
public Dictionary<string, IAssetDescriptor> Resources { get; }
public string Name { get; }
}
private static readonly Dictionary<string, AssemblyDescriptor> AssemblyNameCache
= new Dictionary<string, AssemblyDescriptor>();
@ -53,7 +33,94 @@ namespace Perspex.Shared.PlatformSupport
_defaultAssembly = new AssemblyDescriptor(assembly);
}
AssemblyDescriptor GetAssembly(string name)
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri, Uri baseUri = null)
{
return GetAsset(uri, baseUri) != null;
}
/// <summary>
/// Opens the resource with the requested URI.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>A stream containing the resource contents.</returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
public Stream Open(Uri uri, Uri baseUri = null)
{
var asset = GetAsset(uri, baseUri);
if (asset == null)
{
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
return asset.GetStream();
}
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
{
if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
{
var uriQueryParams = ParseQueryString(uri);
var baseUriQueryParams = uri != null ? ParseQueryString(uri) : null;
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
if (asm == null && _defaultAssembly == null)
{
throw new ArgumentException(
"No default assembly, entry assembly or explicit assembly specified; " +
"don't know where to look up for the resource, try specifiyng assembly explicitly.");
}
IAssetDescriptor rv;
var resourceKey = uri.AbsolutePath;
#if __IOS__
// TODO: HACK: to get iOS up and running. Using Shared projects for resources
// is flawed as this alters the reource key locations across platforms
// I think we need to use Portable libraries from now on to avoid that.
if(asm.Name.Contains("iOS"))
{
resourceKey = resourceKey.Replace("TestApplication", "Perspex.iOSTestApplication");
}
#endif
asm.Resources.TryGetValue(resourceKey, out rv);
return rv;
}
throw new ArgumentException($"Invalid uri, see https://github.com/Perspex/Perspex/issues/282#issuecomment-166982104", nameof(uri));
}
private AssemblyDescriptor GetAssembly(Uri uri)
{
if (uri != null)
{
var qs = ParseQueryString(uri);
string assemblyName;
if (qs.TryGetValue("assembly", out assemblyName))
{
return GetAssembly(assemblyName);
}
}
return null;
}
private AssemblyDescriptor GetAssembly(string name)
{
if (name == null)
{
@ -82,13 +149,20 @@ namespace Perspex.Shared.PlatformSupport
return rv;
}
interface IAssetDescriptor
private Dictionary<string, string> ParseQueryString(Uri uri)
{
Stream GetStream();
return uri.Query.TrimStart('?')
.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split('='))
.ToDictionary(p => p[0], p => p[1]);
}
private interface IAssetDescriptor
{
Stream GetStream();
}
class AssemblyResourceDescriptor : IAssetDescriptor
private class AssemblyResourceDescriptor : IAssetDescriptor
{
private readonly Assembly _asm;
private readonly string _name;
@ -104,69 +178,24 @@ namespace Perspex.Shared.PlatformSupport
return _asm.GetManifestResourceStream(_name);
}
}
IAssetDescriptor GetAsset(Uri uri)
private class AssemblyDescriptor
{
if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
public AssemblyDescriptor(Assembly assembly)
{
var qs = uri.Query.TrimStart('?')
.Split(new[] {'&'}, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split('='))
.ToDictionary(p => p[0], p => p[1]);
//TODO: Replace _defaultAssembly by current one (need support from OmniXAML)
var asm = _defaultAssembly;
if (qs.ContainsKey("assembly"))
asm = GetAssembly(qs["assembly"]);
if (asm == null && _defaultAssembly == null)
throw new ArgumentException(
"No defaultAssembly, entry assembly or explicit assembly specified, don't know where to look up for the resource, try specifiyng assembly explicitly");
IAssetDescriptor rv;
var resourceKey = uri.AbsolutePath;
Assembly = assembly;
#if __IOS__
// TODO: HACK: to get iOS up and running. Using Shared projects for resources
// is flawed as this alters the reource key locations across platforms
// I think we need to use Portable libraries from now on to avoid that.
if(asm.Name.Contains("iOS"))
if (assembly != null)
{
resourceKey = resourceKey.Replace("TestApplication", "Perspex.iOSTestApplication");
Resources = assembly.GetManifestResourceNames()
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
Name = assembly.GetName().Name;
}
#endif
asm.Resources.TryGetValue(resourceKey, out rv);
return rv;
}
throw new ArgumentException($"Invalid uri, see https://github.com/Perspex/Perspex/issues/282#issuecomment-166982104", nameof(uri));
}
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri)
{
return GetAsset(uri) != null;
}
/// <summary>
/// Opens the resource with the requested URI.
/// </summary>
/// <param name="uri">The URI.</param>
/// <returns>A stream containing the resource contents.</returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
public Stream Open(Uri uri)
{
var asset = GetAsset(uri);
if (asset == null)
throw new FileNotFoundException($"The resource {uri} could not be found.");
return asset.GetStream();
public Assembly Assembly { get; }
public Dictionary<string, IAssetDescriptor> Resources { get; }
public string Name { get; }
}
}
}

2
src/Skia/Perspex.Skia.Android.TestApp/Perspex.Skia.Android.TestApp.csproj

@ -15,7 +15,7 @@
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup>

4
src/Skia/Perspex.Skia.Android/Perspex.Skia.Android.csproj

@ -14,7 +14,7 @@
<FileAlignment>512</FileAlignment>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -40,7 +40,7 @@
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\MonoAndroid\SkiaSharp.dll</HintPath>
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.1-beta\lib\MonoAndroid\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />

2
src/Skia/Perspex.Skia.Android/packages.config

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="monoandroid60" />
<package id="SkiaSharp" version="1.49.2.1-beta" targetFramework="monoandroid50" />
</packages>

6
src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj

@ -57,7 +57,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\net45\SkiaSharp.dll</HintPath>
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.1-beta\lib\net45\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@ -116,12 +116,12 @@
<Import Project="..\Perspex.Skia\Perspex.Skia.projitems" Label="Shared" />
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets" Condition="Exists('..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets')" />
<Import Project="..\..\..\packages\SkiaSharp.1.49.2.1-beta\build\net45\SkiaSharp.targets" Condition="Exists('..\..\..\packages\SkiaSharp.1.49.2.1-beta\build\net45\SkiaSharp.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets'))" />
<Error Condition="!Exists('..\..\..\packages\SkiaSharp.1.49.2.1-beta\build\net45\SkiaSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SkiaSharp.1.49.2.1-beta\build\net45\SkiaSharp.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

2
src/Skia/Perspex.Skia.Desktop/packages.config

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="net45" />
<package id="SkiaSharp" version="1.49.2.1-beta" targetFramework="net45" />
</packages>

2
src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj

@ -41,7 +41,7 @@
</ItemGroup>
<ItemGroup>
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\XamariniOS\SkiaSharp.dll</HintPath>
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.1-beta\lib\XamariniOS\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />

2
src/Skia/Perspex.Skia.iOS/packages.config

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="xamarinios10" />
<package id="SkiaSharp" version="1.49.2.1-beta" targetFramework="xamarinios10" />
</packages>

10
src/Skia/Perspex.Skia/DrawingContextImpl.cs

@ -229,10 +229,7 @@ namespace Perspex.Skia
}
else
{
// TODO: DrawRRect (ore DrawRoundedRect) is not accesible in SkiaSharp yet. We should add that
// to SkiaSharp and initiate a PR....
Canvas.DrawRect(rc, paint.Paint);
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
Canvas.DrawRoundRect(rc, cornerRadius, cornerRadius, paint.Paint);
}
}
}
@ -248,10 +245,7 @@ namespace Perspex.Skia
}
else
{
// TODO: this does not exist in SkiaSharp yet
//throw new NotImplementedException();
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
Canvas.DrawRect(rc, paint.Paint);
Canvas.DrawRoundRect(rc, cornerRadius, cornerRadius, paint.Paint);
}
}
}

5
src/Skia/Perspex.Skia/SkiaSharpExtensions.cs

@ -65,5 +65,10 @@ namespace Perspex.Skia
case TextAlignment.Right: return SKTextAlign.Right;
}
}
public static SKPath Clone(this SKPath src)
{
return src != null ? new SKPath(src) : null;
}
}
}

40
src/Skia/Perspex.Skia/StreamGeometryImpl.cs

@ -50,28 +50,23 @@ namespace Perspex.Skia
_transformedPath.Dispose();
_transformedPath = null;
}
// TODO: SkiaSharp does not expose Transform yet!!!
//if (!Transform.IsIdentity)
//{
// _transformedPath = new SKPath();
// _path.Transform(Transform.ToSKMatrix(), _transformedPath);
//}
if (!Transform.IsIdentity)
{
_transformedPath = new SKPath(_path);
_transformedPath.Transform(Transform.ToSKMatrix());
}
}
public IStreamGeometryImpl Clone()
{
// TODO: there is no SKPath.Clone yet!!!!!!!!!!!!!
//
//return new StreamGeometryImpl
//{
// _path = _path?.Clone(),
// _transformedPath = _transformedPath?.Clone(),
// _transform = Transform,
// Bounds = Bounds
//};
return this; // this will probably end bad!!
return new StreamGeometryImpl
{
_path = _path?.Clone(),
_transformedPath = _transformedPath?.Clone(),
_transform = Transform,
Bounds = Bounds
};
}
public IStreamGeometryContextImpl Open()
@ -94,15 +89,6 @@ namespace Perspex.Skia
public void Dispose()
{
// TODO: Not sure what we need to do here. This code left here for reference.
//
// var arr = _elements.ToArray();
// SkRect rc;
// _path?.Dispose();
// _path = new SKPath(new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc)));
// _geometryImpl.ApplyTransform();
// _geometryImpl.Bounds = rc.ToRect();
SKRect rc;
_path.GetBounds(out rc);
_geometryImpl.ApplyTransform();

7
src/Skia/Perspex.Skia/readme.md

@ -5,7 +5,6 @@ BitmapImpl
- Save
DrawingContextImpl
- DrawRoundRect is not properly implemented due to lack of support in SkiaSharp
- Alpha support missing as SkiaSharp does not expose this
- Gradient Shader caching?
- TileBrushes
@ -18,12 +17,6 @@ RenderTarget
- Figure out a cleaner implementation across all platforms
- HW acceleration
StreamGeometry
- Paths within Paths may not work right
- Paths cannot be Cloned (lack of SkiaSupport)
- Paths cannot be transformed (lack of SkiaSupport)
- ArcTo
App Bootstrapping
- Cleanup the testapplications across all platforms
- Add a cleaner Fluent API for the subsystems

7
src/Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs

@ -5,6 +5,7 @@ using Perspex.Media;
using Perspex.Platform;
using SharpDX.Direct2D1;
using SweepDirection = SharpDX.Direct2D1.SweepDirection;
using D2D = SharpDX.Direct2D1;
namespace Perspex.Direct2D1.Media
{
@ -24,7 +25,7 @@ namespace Perspex.Direct2D1.Media
bool isLargeArc,
Perspex.Media.SweepDirection sweepDirection)
{
_sink.AddArc(new ArcSegment
_sink.AddArc(new D2D.ArcSegment
{
Point = point.ToSharpDX(),
Size = size.ToSharpDX(),
@ -41,7 +42,7 @@ namespace Perspex.Direct2D1.Media
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
_sink.AddBezier(new BezierSegment
_sink.AddBezier(new D2D.BezierSegment
{
Point1 = point1.ToSharpDX(),
Point2 = point2.ToSharpDX(),
@ -51,7 +52,7 @@ namespace Perspex.Direct2D1.Media
public void QuadraticBezierTo(Point control, Point dest)
{
_sink.AddQuadraticBezier(new QuadraticBezierSegment
_sink.AddQuadraticBezier(new D2D.QuadraticBezierSegment
{
Point1 = control.ToSharpDX(),
Point2 = dest.ToSharpDX()

1
src/Windows/Perspex.Direct2D1/Media/StreamGeometryImpl.cs

@ -1,7 +1,6 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Media;
using Perspex.Platform;
using SharpDX.Direct2D1;
using D2DGeometry = SharpDX.Direct2D1.Geometry;

Loading…
Cancel
Save