Browse Source

More iOS platform related improvements. Code style reformatting for PR. ControlCatalog can now run on Skia! AssetLoader hack for iOS to get us running.

pull/506/head
Jason Jarvis 11 years ago
parent
commit
b566401602
  1. 4
      samples/ControlCatalog/ControlCatalog.csproj
  2. 11
      samples/ControlCatalog/Program.cs
  3. 48
      samples/TestApplicationShared/App.cs
  4. 15
      src/Shared/PlatformSupport/AssetLoader.cs
  5. 98
      src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs
  6. 238
      src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs
  7. 18
      src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj
  8. 32
      src/Skia/Perspex.Skia.iOS/SkiaView.cs
  9. 42
      src/Skia/Perspex.Skia/BitmapImpl.cs
  10. 381
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  11. 500
      src/Skia/Perspex.Skia/FormattedTextImpl.cs
  12. 258
      src/Skia/Perspex.Skia/RenderTarget.cs
  13. 12
      src/Skia/Perspex.Skia/SkiaPlatform.cs
  14. 110
      src/Skia/Perspex.Skia/SkiaSharpExtensions.cs
  15. 138
      src/Skia/Perspex.Skia/StreamGeometryImpl.cs
  16. 18
      src/Skia/Perspex.Skia/TypefaceCache.cs
  17. 13
      src/Skia/Perspex.Skia/readme.md
  18. 1
      src/iOS/Perspex.iOS/Perspex.iOS.csproj
  19. 2
      src/iOS/Perspex.iOS/PerspexAppDelegate.cs
  20. 12
      src/iOS/Perspex.iOS/PerspexView.cs
  21. 107
      src/iOS/Perspex.iOS/iOSPlatform.cs
  22. 95
      src/iOS/Perspex.iOSTestApplication/AppDelegate.cs
  23. 4
      src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj

4
samples/ControlCatalog/ControlCatalog.csproj

@ -211,6 +211,10 @@
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Perspex.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Skia\Perspex.Skia.Desktop\Perspex.Skia.Desktop.csproj">
<Project>{925dd807-b651-475f-9f7c-cbeb974ce43d}</Project>
<Name>Perspex.Skia.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Windows\Perspex.Direct2D1\Perspex.Direct2D1.csproj">
<Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project>
<Name>Perspex.Direct2D1</Name>

11
samples/ControlCatalog/Program.cs

@ -110,12 +110,11 @@ namespace ControlCatalog
{
app.UseWin32();
// not available until we do the SkiaSharp merge
//if (args.Contains("--skia") || DefaultRenderSystem() == RenderSystem.Skia)
//{
// app.UseSkia();
//}
//else
if (args.Contains("--skia") || DefaultRenderSystem() == RenderSystem.Skia)
{
app.UseSkia();
}
else
{
app.UseDirect2D();
}

48
samples/TestApplicationShared/App.cs

@ -18,35 +18,35 @@ namespace TestApplication
{
public App()
{
// TODO: I believe this has to happen before we select sub systems. Can we
// move this safely into Application itself? Is there anything in here
// that is platform specific??
//
// TODO: I believe this has to happen before we select sub systems. Can we
// move this safely into Application itself? Is there anything in here
// that is platform specific??
//
RegisterServices();
}
public void Run()
{
Styles.Add(new DefaultTheme());
public void Run()
{
Styles.Add(new DefaultTheme());
var loader = new PerspexXamlLoader();
var baseLight = (IStyle)loader.Load(
new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.xaml?assembly=Perspex.Themes.Default"));
Styles.Add(baseLight);
var loader = new PerspexXamlLoader();
var baseLight = (IStyle)loader.Load(
new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.xaml?assembly=Perspex.Themes.Default"));
Styles.Add(baseLight);
Styles.Add(new SampleTabStyle());
DataTemplates = new DataTemplates
{
new FuncTreeDataTemplate<Node>(
x => new TextBlock {Text = x.Name},
x => x.Children),
};
Styles.Add(new SampleTabStyle());
DataTemplates = new DataTemplates
{
new FuncTreeDataTemplate<Node>(
x => new TextBlock {Text = x.Name},
x => x.Children),
};
MainWindow.RootNamespace = "TestApplication";
var wnd = MainWindow.Create();
DevTools.Attach(wnd);
MainWindow.RootNamespace = "TestApplication";
var wnd = MainWindow.Create();
wnd.AttachDevTools();
Run(wnd);
}
}
Run(wnd);
}
}
}

15
src/Shared/PlatformSupport/AssetLoader.cs

@ -105,7 +105,20 @@ namespace Perspex.Shared.PlatformSupport
"No defaultAssembly, entry assembly or explicit assembly specified, don't know where to look up for the resource, try specifiyng assembly explicitly");
IAssetDescriptor rv;
asm.Resources.TryGetValue(uri.AbsolutePath, out 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));

98
src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs

@ -26,66 +26,60 @@ using ReactiveUI;
namespace Perspex.Skia.Desktop.TestApp
{
internal class Program
{
private static void Main(string[] args)
{
// The version of ReactiveUI currently included is for WPF and so expects a WPF
// dispatcher. This makes sure it's initialized.
System.Windows.Threading.Dispatcher foo = System.Windows.Threading.Dispatcher.CurrentDispatcher;
new SkiaApp();
internal class Program
{
private static void Main(string[] args)
{
// The version of ReactiveUI currently included is for WPF and so expects a WPF
// dispatcher. This makes sure it's initialized.
System.Windows.Threading.Dispatcher foo = System.Windows.Threading.Dispatcher.CurrentDispatcher;
//RendererMixin.DrawFpsCounter = true;
new App()
.UseWin32()
.UseSkia();
MainWindow.RootNamespace = "Perspex.Skia.Desktop.TestApp";
var wnd = MainWindow.Create();
//RendererMixin.DrawFpsCounter = true;
// let's start small :)
//var wnd = SimpleWindow.Create();
MainWindow.RootNamespace = "Perspex.Skia.Desktop.TestApp";
var wnd = MainWindow.Create();
DevTools.Attach(wnd);
Application.Current.Run(wnd);
}
}
// let's start small :)
//var wnd = SimpleWindow.Create();
internal class SkiaApp : App
{
protected override void PlatformInitialization()
{
SkiaPlatform.Initialize();
InitializeSubsystem("Perspex.Win32");
}
}
DevTools.Attach(wnd);
Application.Current.Run(wnd);
}
}
internal class SimpleWindow
{
public static Window Create()
{
Border container;
internal class SimpleWindow
{
public static Window Create()
{
Border container;
Window window = new Window
{
Title = "Perspex Test Application",
Content = (container = new Border
{
Background = Brushes.Green,
BorderBrush = Brushes.Yellow,
BorderThickness = 12,
Padding = new Thickness(5),
Child = new Rectangle
{
Fill = Brushes.Blue,
Width = 200,
Height = 200,
Margin = new Thickness(50)
}
})
Window window = new Window
{
Title = "Perspex Test Application",
Content = (container = new Border
{
Background = Brushes.Green,
BorderBrush = Brushes.Yellow,
BorderThickness = 12,
Padding = new Thickness(5),
Child = new Rectangle
{
Fill = Brushes.Blue,
Width = 200,
Height = 200,
Margin = new Thickness(50)
}
})
};
};
window.Show();
return window;
}
window.Show();
return window;
}
}
}
}

238
src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs

@ -1,238 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
/* OBSOLETE
//Library loaders were taken from https://github.com/kekekeks/evhttp-sharp/tree/master/EvHttpSharp/Interop (MIT licensed)
namespace Perspex.Skia
{
class MethodTableImpl : MethodTable
{
public MethodTableImpl() : base(GetMethodTable())
{
}
class DetectedPlatformInfo
{
public DetectedPlatformInfo(IDynLoader loader, string name, string cpuArch, string dllExtension)
{
Loader = loader;
Name = name;
CpuArch = cpuArch;
DllExtension = dllExtension;
}
public IDynLoader Loader { get; }
public string Name { get; }
public string CpuArch { get;}
public string DllExtension { get; }
}
static DetectedPlatformInfo DetectPlatform()
{
var macInfo = new DetectedPlatformInfo(new OsxLoader(), "Darwin", IntPtr.Size == 4 ? "i686" : "x86_64", ".dylib");
switch (Environment.OSVersion.Platform)
{
case PlatformID.Unix:
// Well, there are chances MacOSX is reported as Unix instead of MacOSX.
// Instead of platform check, we'll do a feature checks (Mac specific root folders)
if (Directory.Exists("/Applications")
& Directory.Exists("/System")
& Directory.Exists("/Users")
& Directory.Exists("/Volumes"))
return macInfo;
else
{
string cpuArch;
using (var proc =
Process.Start(
(new ProcessStartInfo("uname", "-p")
{
UseShellExecute = false,
RedirectStandardOutput = true
})))
{
proc.WaitForExit();
cpuArch = proc.StandardOutput.ReadToEnd().Trim();
}
return new DetectedPlatformInfo(new LinuxLoader(), "Linux", cpuArch, ".so");
}
case PlatformID.MacOSX:
return macInfo;
default:
return new DetectedPlatformInfo(new Win32Loader(), "Windows", IntPtr.Size == 4 ? "i686" : "x86_64", ".dll");
}
}
static IEnumerable<string> GeneratePossiblePaths()
{
foreach (
var basePath in
new[] {Assembly.GetEntryAssembly(), typeof (MethodTable).Assembly}.Select(
a => Path.GetDirectoryName(a?.GetModules()?[0]?.FullyQualifiedName))
.Concat(new[] { Directory.GetCurrentDirectory()}))
{
if(basePath == null)
continue;
yield return Path.Combine(basePath);
yield return Path.Combine(basePath, "native");
yield return Path.Combine(basePath, "lib");
yield return Path.Combine(basePath, "lib", "native");
}
}
static IntPtr GetMethodTable()
{
var plat = DetectPlatform();
var name = "libperspesk" + plat.DllExtension;
var paths = GeneratePossiblePaths().Select(p => Path.Combine(p, plat.Name, plat.CpuArch, name)).ToList();
var dll = paths.FirstOrDefault(File.Exists);
var msg = "Unable to find native library in following paths: '" +
string.Join("', '", paths) + "'";
Console.Error.WriteLine(msg);
if (dll == null)
throw new FileNotFoundException(msg);
IntPtr hLib = plat.Loader.LoadLibrary(dll);
IntPtr pGetMethodTable = plat.Loader.GetProcAddress(hLib, "GetPerspexMethodTable");
var getTable = (GetPerspexMethodTableDelegate)
Marshal.GetDelegateForFunctionPointer(pGetMethodTable, typeof (GetPerspexMethodTableDelegate));
return getTable();
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate IntPtr GetPerspexMethodTableDelegate();
internal interface IDynLoader
{
IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc);
}
class LinuxLoader : IDynLoader
{
// ReSharper disable InconsistentNaming
[DllImport("libdl.so.2")]
private static extern IntPtr dlopen(string path, int flags);
[DllImport("libdl.so.2")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")]
private static extern IntPtr dlerror();
// ReSharper restore InconsistentNaming
static string DlError()
{
return Marshal.PtrToStringAuto(dlerror());
}
public IntPtr LoadLibrary(string dll)
{
var handle = dlopen(dll, 1);
if (handle == IntPtr.Zero)
throw new Win32Exception(DlError());
return handle;
}
public IntPtr GetProcAddress(IntPtr dll, string proc)
{
var ptr = dlsym(dll, proc);
if (ptr == IntPtr.Zero)
throw new Win32Exception(DlError());
return ptr;
}
}
internal class Win32Loader : IDynLoader
{
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string lpszLib);
[DllImport("kernel32", EntryPoint = "SetDllDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)]
extern static bool SetDllDirectory(string lpPathName);
[DllImport("kernel32", EntryPoint = "GetDllDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)]
extern static int GetDllDirectory(int nBufferLength, char[] lpBuffer);
IntPtr IDynLoader.LoadLibrary(string dll)
{
var buffer = new char[2048];
int oldLen = GetDllDirectory(buffer.Length, buffer);
var oldDir = new string(buffer, 0, oldLen);
SetDllDirectory(Path.GetDirectoryName(dll));
var handle = LoadLibrary(dll);
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
SetDllDirectory(oldDir);
return handle;
}
IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc)
{
var ptr = GetProcAddress(dll, proc);
if (ptr == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return ptr;
}
}
class OsxLoader : IDynLoader
{
// ReSharper disable InconsistentNaming
[DllImport("/usr/lib/libSystem.dylib")]
public static extern IntPtr dlopen(string path, int mode);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlerror();
// ReSharper restore InconsistentNaming
static string DlError()
{
return Marshal.PtrToStringAuto(dlerror());
}
public IntPtr LoadLibrary(string dll)
{
var handle = dlopen(dll, 1);
if (handle == IntPtr.Zero)
throw new Win32Exception(DlError());
return handle;
}
public IntPtr GetProcAddress(IntPtr dll, string proc)
{
var ptr = dlsym(dll, proc);
if (ptr == IntPtr.Zero)
throw new Win32Exception(DlError());
return ptr;
}
}
}
}
*/

18
src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj

@ -70,7 +70,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MethodTableImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\..\Windows\Perspex.Win32\Interop\UnmanagedMethods.cs">
<Link>UnmanagedMethods.cs</Link>
@ -111,23 +110,6 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="..\native\Windows\i686\libEGL.dll">
<Link>native\Windows\i686\libEGL.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="..\native\Windows\i686\libGLESv2.dll">
<Link>native\Windows\i686\libGLESv2.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="..\native\Windows\i686\libperspesk.dll">
<Link>native\Windows\i686\libperspesk.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\native\Linux\x86_64\libperspesk.so">
<Link>native\Linux\x86_64\libperspesk.so</Link>
</None>
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\Perspex.Skia\Perspex.Skia.projitems" Label="Shared" />

32
src/Skia/Perspex.Skia.iOS/SkiaView.cs

@ -15,8 +15,8 @@ using UIKit;
namespace Perspex.Skia.iOS
{
public abstract class SkiaView : UIView //GLKView
{
public abstract class SkiaView : UIView //GLKView
{
//[DllImport("__Internal")]
//static extern IntPtr GetPerspexEAGLContext();
@ -24,15 +24,15 @@ namespace Perspex.Skia.iOS
//CADisplayLink _link;
static EAGLContext GetContext()
{
/* No longer needed with SkiaSharp
//Ensure initialization
MethodTable.Instance.SetOption((MethodTable.Option)0x10009999, IntPtr.Zero);
var ctx = GetPerspexEAGLContext();
var rv = Runtime.GetNSObject<EAGLContext>(ctx);
rv.DangerousRetain();
return rv;
*/
return null;
/* No longer needed with SkiaSharp, but require a variant for HW accel
//Ensure initialization
MethodTable.Instance.SetOption((MethodTable.Option)0x10009999, IntPtr.Zero);
var ctx = GetPerspexEAGLContext();
var rv = Runtime.GetNSObject<EAGLContext>(ctx);
rv.DangerousRetain();
return rv;
*/
return null;
}
@ -52,10 +52,10 @@ namespace Perspex.Skia.iOS
{
_drawQueued = false;
// GLKView
//Display();
// GLKView
//Display();
this.SetNeedsDisplay();
this.SetNeedsDisplay();
}
}
@ -64,9 +64,9 @@ namespace Perspex.Skia.iOS
_drawQueued = true;
}
protected IPlatformHandle PerspexPlatformHandle { get; }
protected IPlatformHandle PerspexPlatformHandle { get; }
= new PlatformHandle(IntPtr.Zero, "Null (iOS-specific)");
protected abstract void Draw();

42
src/Skia/Perspex.Skia/BitmapImpl.cs

@ -11,28 +11,28 @@ namespace Perspex.Skia
{
class BitmapImpl : IRenderTargetBitmapImpl
{
public SKBitmap Bitmap { get; private set; }
public SKBitmap Bitmap { get; private set; }
public BitmapImpl(SKBitmap bm)
{
Bitmap = bm;
PixelHeight = bm.Width;
PixelWidth = bm.Height;
}
public BitmapImpl(SKBitmap bm)
{
Bitmap = bm;
PixelHeight = bm.Width;
PixelWidth = bm.Height;
}
public BitmapImpl(int width, int height)
{
throw new NotImplementedException();
}
public BitmapImpl(int width, int height)
{
throw new NotImplementedException();
}
public void Dispose()
{
}
public void Dispose()
{
}
public void Save(string fileName)
public void Save(string fileName)
{
// TODO: Implement this for SkiaSharp
throw new NotImplementedException();
// TODO: Implement this for SkiaSharp
throw new NotImplementedException();
//var ext = Path.GetExtension(fileName)?.ToLower();
//var type = MethodTable.SkiaImageType.Png;
@ -49,12 +49,12 @@ namespace Perspex.Skia
public int PixelWidth { get; private set; }
public int PixelHeight { get; private set; }
public DrawingContext CreateDrawingContext()
{
return
new DrawingContext(
new DrawingContextImpl(null)); // MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
return
new DrawingContext(
new DrawingContextImpl(null)); // MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
}
}

381
src/Skia/Perspex.Skia/DrawingContextImpl.cs

@ -10,250 +10,251 @@ namespace Perspex.Skia
{
unsafe class DrawingContextImpl : IDrawingContextImpl
{
public SKCanvas Canvas { get; private set; }
public SKCanvas Canvas { get; private set; }
public DrawingContextImpl(SKCanvas canvas)
{
Canvas = canvas;
Canvas = canvas;
}
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
{
var impl = (BitmapImpl)source.PlatformImpl;
var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect();
Canvas.DrawBitmap(impl.Bitmap, s, d);
var impl = (BitmapImpl)source.PlatformImpl;
var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect();
Canvas.DrawBitmap(impl.Bitmap, s, d);
}
public void DrawLine(Pen pen, Point p1, Point p2)
{
using (var paint = CreatePaint(pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))))
{
Canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, paint);
using (var paint = CreatePaint(pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))))
{
Canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, paint);
}
}
}
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
{
var impl = ((StreamGeometryImpl)geometry.PlatformImpl);
var impl = ((StreamGeometryImpl)geometry.PlatformImpl);
var size = geometry.Bounds.Size;
using (var fill = brush != null ? CreatePaint(brush, size) : null)
using (var stroke = pen?.Brush != null ? CreatePaint(pen, size) : null)
using (var fill = brush != null ? CreatePaint(brush, size) : null)
using (var stroke = pen?.Brush != null ? CreatePaint(pen, size) : null)
{
if (fill != null)
{
Canvas.DrawPath(impl.EffectivePath, fill);
}
if (stroke != null)
{
Canvas.DrawPath(impl.EffectivePath, stroke);
}
}
}
private SKPaint CreatePaint(IBrush brush, Size targetSize)
if (fill != null)
{
Canvas.DrawPath(impl.EffectivePath, fill);
}
if (stroke != null)
{
Canvas.DrawPath(impl.EffectivePath, stroke);
}
}
}
private SKPaint CreatePaint(IBrush brush, Size targetSize)
{
SKPaint paint = new SKPaint();
SKPaint paint = new SKPaint();
paint.IsStroke = false;
paint.IsStroke = false;
// TODO: SkiaSharp does not contain alpha yet!
//double opacity = brush.Opacity * _currentOpacity;
//paint.SetAlpha(paint.GetAlpha() * opacity);
paint.IsAntialias = true;
// TODO: SkiaSharp does not contain alpha yet!
//double opacity = brush.Opacity * _currentOpacity;
//paint.SetAlpha(paint.GetAlpha() * opacity);
paint.IsAntialias = true;
var solid = brush as SolidColorBrush;
if (solid != null)
{
paint.Color = solid.Color.ToSKColor();
return paint;
paint.Color = solid.Color.ToSKColor();
return paint;
}
var gradient = brush as GradientBrush;
if (gradient != null)
{
var tileMode = gradient.SpreadMethod.ToSKShaderTileMode();
var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray();
var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray();
var tileMode = gradient.SpreadMethod.ToSKShaderTileMode();
var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray();
var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray();
var linearGradient = brush as LinearGradientBrush;
if (linearGradient != null)
var linearGradient = brush as LinearGradientBrush;
if (linearGradient != null)
{
var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint();
var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint();
// would be nice to cache these shaders possibly?
var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode);
paint.Shader = shader;
}
else
{
var radialGradient = brush as RadialGradientBrush;
if (radialGradient != null)
{
var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint();
var radius = (float)radialGradient.Radius;
// TODO: There is no SetAlpha in SkiaSharp
//paint.setAlpha(128);
// would be nice to cache these shaders possibly?
var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode);
paint.Shader = shader;
var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint();
var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint();
// would be nice to cache these shaders possibly?
var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode);
paint.Shader = shader;
}
else
{
var radialGradient = brush as RadialGradientBrush;
if (radialGradient != null)
{
var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint();
var radius = (float)radialGradient.Radius;
// TODO: There is no SetAlpha in SkiaSharp
//paint.setAlpha(128);
// would be nice to cache these shaders possibly?
var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode);
paint.Shader = shader;
}
}
return paint;
}
}
return paint;
}
var tileBrush = brush as TileBrush;
if (tileBrush != null)
{
// TODO: Get Tile Brushes working!!!
//
throw new NotImplementedException();
// rv.Brush->Type = NativeBrushType.Image;
// var helper = new TileBrushImplHelper(tileBrush, targetSize);
// var bitmap = new BitmapImpl((int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height);
// rv.AddDisposable(bitmap);
// using (var ctx = bitmap.CreateDrawingContext())
// helper.DrawIntermediate(ctx);
// rv.Brush->Bitmap = bitmap.Handle;
// rv.Brush->BitmapTileMode = tileBrush.TileMode;
// rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y);
// SkMatrix matrix;
// matrix.setTranslate(brush->BitmapTranslation);
// SkShader::TileMode tileX = brush->BitmapTileMode == ptmNone ? SkShader::kClamp_TileMode
// : (brush->BitmapTileMode == ptmFlipX || brush->BitmapTileMode == ptmFlipXY) ? SkShader::kMirror_TileMode : SkShader::kRepeat_TileMode;
// SkShader::TileMode tileY = brush->BitmapTileMode == ptmNone ? SkShader::kClamp_TileMode
// : (brush->BitmapTileMode == ptmFlipY || brush->BitmapTileMode == ptmFlipXY) ? SkShader::kMirror_TileMode : SkShader::kRepeat_TileMode;
// paint.setShader(SkShader::CreateBitmapShader(brush->Bitmap->Bitmap, tileX, tileY, &matrix))->unref();
// TODO: Get Tile Brushes working!!!
//
//throw new NotImplementedException();
// rv.Brush->Type = NativeBrushType.Image;
// var helper = new TileBrushImplHelper(tileBrush, targetSize);
// var bitmap = new BitmapImpl((int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height);
// rv.AddDisposable(bitmap);
// using (var ctx = bitmap.CreateDrawingContext())
// helper.DrawIntermediate(ctx);
// rv.Brush->Bitmap = bitmap.Handle;
// rv.Brush->BitmapTileMode = tileBrush.TileMode;
// rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y);
// SkMatrix matrix;
// matrix.setTranslate(brush->BitmapTranslation);
// SkShader::TileMode tileX = brush->BitmapTileMode == ptmNone ? SkShader::kClamp_TileMode
// : (brush->BitmapTileMode == ptmFlipX || brush->BitmapTileMode == ptmFlipXY) ? SkShader::kMirror_TileMode : SkShader::kRepeat_TileMode;
// SkShader::TileMode tileY = brush->BitmapTileMode == ptmNone ? SkShader::kClamp_TileMode
// : (brush->BitmapTileMode == ptmFlipY || brush->BitmapTileMode == ptmFlipXY) ? SkShader::kMirror_TileMode : SkShader::kRepeat_TileMode;
// paint.setShader(SkShader::CreateBitmapShader(brush->Bitmap->Bitmap, tileX, tileY, &matrix))->unref();
}
return paint;
return paint;
}
private SKPaint CreatePaint(Pen pen, Size targetSize)
private SKPaint CreatePaint(Pen pen, Size targetSize)
{
var paint = CreatePaint(pen.Brush, targetSize);
paint.IsStroke = true;
paint.StrokeWidth = (float) pen.Thickness;
if (pen.StartLineCap == PenLineCap.Round)
paint.StrokeCap = SKStrokeCap.Round;
else if (pen.StartLineCap == PenLineCap.Square)
paint.StrokeCap = SKStrokeCap.Square;
else
paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter)
paint.StrokeJoin = SKStrokeJoin.Mitter;
else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round;
else
paint.StrokeJoin = SKStrokeJoin.Bevel;
paint.StrokeMiter = (float) pen.MiterLimit;
//if (pen.DashStyle?.Dashes != null)
//{
// var dashes = pen.DashStyle.Dashes;
// if (dashes.Count > NativeBrush.MaxDashCount)
// throw new NotSupportedException("Maximum supported dash count is " + NativeBrush.MaxDashCount);
// brush.Brush->StrokeDashCount = dashes.Count;
// for (int c = 0; c < dashes.Count; c++)
// brush.Brush->StrokeDashes[c] = (float)dashes[c];
// brush.Brush->StrokeDashOffset = (float)pen.DashStyle.Offset;
//}
//if (brush->StrokeDashCount != 0)
//{
// paint.setPathEffect(SkDashPathEffect::Create(brush->StrokeDashes, brush->StrokeDashCount, brush->StrokeDashOffset))->unref();
//}
return paint;
var paint = CreatePaint(pen.Brush, targetSize);
paint.IsStroke = true;
paint.StrokeWidth = (float)pen.Thickness;
if (pen.StartLineCap == PenLineCap.Round)
paint.StrokeCap = SKStrokeCap.Round;
else if (pen.StartLineCap == PenLineCap.Square)
paint.StrokeCap = SKStrokeCap.Square;
else
paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter)
paint.StrokeJoin = SKStrokeJoin.Mitter;
else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round;
else
paint.StrokeJoin = SKStrokeJoin.Bevel;
paint.StrokeMiter = (float)pen.MiterLimit;
//if (pen.DashStyle?.Dashes != null)
//{
// var dashes = pen.DashStyle.Dashes;
// if (dashes.Count > NativeBrush.MaxDashCount)
// throw new NotSupportedException("Maximum supported dash count is " + NativeBrush.MaxDashCount);
// brush.Brush->StrokeDashCount = dashes.Count;
// for (int c = 0; c < dashes.Count; c++)
// brush.Brush->StrokeDashes[c] = (float)dashes[c];
// brush.Brush->StrokeDashOffset = (float)pen.DashStyle.Offset;
//}
//if (brush->StrokeDashCount != 0)
//{
// paint.setPathEffect(SkDashPathEffect::Create(brush->StrokeDashes, brush->StrokeDashCount, brush->StrokeDashOffset))->unref();
//}
return paint;
}
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0)
{
using (var paint = CreatePaint(pen, rect.Size))
using (var paint = CreatePaint(pen, rect.Size))
{
var rc = rect.ToSKRect();
if (cornerRadius == 0)
{
Canvas.DrawRect(rc, paint);
var rc = rect.ToSKRect();
if (cornerRadius == 0)
{
Canvas.DrawRect(rc, paint);
}
else
{
// DrawRRect is not accesible in SkiaSharp yet. We should add that
// to SkiaSharp and initiate a PR....
Canvas.DrawRect(rc, paint);
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
}
}
else
{
// this does not appear to exist in SkiaSharp?
throw new NotImplementedException();
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
}
}
}
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
{
using (var paint = CreatePaint(brush, rect.Size))
using (var paint = CreatePaint(brush, rect.Size))
{
var rc = rect.ToSKRect();
if (cornerRadius == 0)
{
Canvas.DrawRect(rc, paint);
var rc = rect.ToSKRect();
if (cornerRadius == 0)
{
Canvas.DrawRect(rc, paint);
}
else
{
// this does not appear to exist in SkiaSharp?
//throw new NotImplementedException();
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
Canvas.DrawRect(rc, paint);
}
}
else
{
// this does not appear to exist in SkiaSharp?
//throw new NotImplementedException();
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
Canvas.DrawRect(rc, paint);
}
}
}
public void DrawText(IBrush foreground, Point origin, FormattedText text)
{
using (var paint = CreatePaint(foreground, text.Measure()))
{
var textImpl = text.PlatformImpl as FormattedTextImpl;
textImpl.Draw(this.Canvas, origin.ToSKPoint());
using (var paint = CreatePaint(foreground, text.Measure()))
{
var textImpl = text.PlatformImpl as FormattedTextImpl;
textImpl.Draw(this.Canvas, origin.ToSKPoint());
}
}
}
public void PushClip(Rect clip)
{
Canvas.Save();
Canvas.ClipRect(clip.ToSKRect());
Canvas.Save();
Canvas.ClipRect(clip.ToSKRect());
}
public void PopClip()
{
Canvas.Restore();
Canvas.Restore();
}
double _currentOpacity = 1.0f;
double _currentOpacity = 1.0f;
private readonly Stack<double> _opacityStack = new Stack<double>();
public void PushOpacity(double opacity)
{
_opacityStack.Push(_currentOpacity);
_currentOpacity *= opacity;
_opacityStack.Push(_currentOpacity);
_currentOpacity *= opacity;
}
public void PopOpacity()
{
_currentOpacity = _opacityStack.Pop();
}
public void PopOpacity()
{
_currentOpacity = _opacityStack.Pop();
}
public virtual void Dispose()
{
}
public virtual void Dispose()
{
}
private Matrix _currentTransform = Matrix.Identity;
@ -262,30 +263,30 @@ namespace Perspex.Skia
get { return _currentTransform; }
set
{
if(_currentTransform == value)
if (_currentTransform == value)
return;
_currentTransform = value;
Canvas.SetMatrix(value.ToSKMatrix());
Canvas.SetMatrix(value.ToSKMatrix());
}
}
}
}
// not sure we need this yet
internal class WindowDrawingContextImpl : DrawingContextImpl
{
WindowRenderTarget _target;
public WindowDrawingContextImpl(WindowRenderTarget target)
: base(target.Surface.Canvas)
{
_target = target;
}
public override void Dispose()
{
base.Dispose();
_target.Present();
}
}
// not sure we need this yet
internal class WindowDrawingContextImpl : DrawingContextImpl
{
WindowRenderTarget _target;
public WindowDrawingContextImpl(WindowRenderTarget target)
: base(target.Surface.Canvas)
{
_target = target;
}
public override void Dispose()
{
base.Dispose();
_target.Present();
}
}
}

500
src/Skia/Perspex.Skia/FormattedTextImpl.cs

@ -10,59 +10,59 @@ namespace Perspex.Skia
{
unsafe class FormattedTextImpl : IFormattedTextImpl
{
public SKPaint Paint { get; private set; }
public SKPaint Paint { get; private set; }
public FormattedTextImpl(string text)
{
_text = text;
Paint = new SKPaint();
public FormattedTextImpl(string text)
{
_text = text;
Paint = new SKPaint();
Paint.TextEncoding = SKTextEncoding.Utf16;
Paint.IsStroke = false;
Paint.IsAntialias = true;
LineOffset = 0;
Paint.TextEncoding = SKTextEncoding.Utf16;
Paint.IsStroke = false;
Paint.IsAntialias = true;
LineOffset = 0;
//Replace 0 characters with zero-width spaces (200B)
_text = _text.Replace((char)0, (char) 0x200B);
}
//Replace 0 characters with zero-width spaces (200B)
_text = _text.Replace((char)0, (char)0x200B);
}
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
TextAlignment textAlignment, FontWeight fontWeight)
{
var typeface = TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight);
FormattedTextImpl instance = new FormattedTextImpl(text);
instance.Paint.Typeface = typeface;
instance.Paint.TextSize = (float) fontSize;
instance.Paint.TextAlign = textAlignment.ToSKTextAlign();
instance.Rebuild();
return instance;
}
var typeface = TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight);
FormattedTextImpl instance = new FormattedTextImpl(text);
instance.Paint.Typeface = typeface;
instance.Paint.TextSize = (float)fontSize;
instance.Paint.TextAlign = textAlignment.ToSKTextAlign();
instance.Rebuild();
return instance;
}
private readonly string _text;
private readonly string _text;
readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
readonly List<Rect> _rects = new List<Rect>();
List<PerspexFormattedTextLine> _skiaLines;
SKRect[] _skiaRects;
List<PerspexFormattedTextLine> _skiaLines;
SKRect[] _skiaRects;
Size _size;
Size _size;
const float MAX_LINE_WIDTH = 10000;
float LineOffset;
float WidthConstraint = -1;
const float MAX_LINE_WIDTH = 10000;
float LineOffset;
float WidthConstraint = -1;
struct PerspexFormattedTextLine
{
public float Top;
public int Start;
public int Length;
public float Height;
public float Width;
};
public IEnumerable<FormattedTextLine> GetLines()
struct PerspexFormattedTextLine
{
public float Top;
public int Start;
public int Length;
public float Height;
public float Width;
};
public IEnumerable<FormattedTextLine> GetLines()
{
return _lines;
}
@ -79,13 +79,13 @@ namespace Perspex.Skia
{
IsInside = true,
TextPosition = c,
IsTrailing = (point.X - rc.X) > rc.Width/2
IsTrailing = (point.X - rc.X) > rc.Width / 2
};
}
}
bool end = point.X > _size.Width || point.Y > _size.Height;
return new TextHitTestResult() {IsTrailing = end, TextPosition = end ? _text.Length - 1 : 0};
return new TextHitTestResult() { IsTrailing = end, TextPosition = end ? _text.Length - 1 : 0 };
}
public Rect HitTestTextPosition(int index)
@ -108,224 +108,224 @@ namespace Perspex.Skia
public void SetForegroundBrush(IBrush brush, int startIndex, int length)
{
// Is this method even relevant now???
//
//var scb = brush as SolidColorBrush;
//if (scb != null)
//{
// Layout.Attributes.Insert(brushAttr);
//}
// Is this method even relevant now???
//
//var scb = brush as SolidColorBrush;
//if (scb != null)
//{
// Layout.Attributes.Insert(brushAttr);
//}
}
void Rebuild()
void Rebuild()
{
var length = _text.Length;
_lines.Clear();
_skiaRects = new SKRect[length];
_skiaLines = new List<PerspexFormattedTextLine>();
int curOff = 0;
float curY = 0;
var metrics = Paint.FontMetrics;
var mTop = metrics.Top; // The greatest distance above the baseline for any glyph (will be <= 0).
var mBottom = metrics.Bottom; // The greatest distance below the baseline for any glyph (will be >= 0).
var mLeading = metrics.Leading; // The recommended distance to add between lines of text (will be >= 0).
// This seems like the best measure of full vertical extent
float lineHeight = mBottom - mTop;
// Rendering is relative to baseline
LineOffset = -metrics.Top;
string subString;
byte[] bytes;
GCHandle pinnedArray;
IntPtr pointer;
for (int c = 0; curOff < length; c++)
{
float lineWidth = -1;
int measured;
int extraSkip = 0;
if (WidthConstraint <= 0)
{
measured = length;
}
else
{
float constraint = WidthConstraint;
if (constraint > MAX_LINE_WIDTH)
constraint = MAX_LINE_WIDTH;
subString = _text.Substring(curOff);
// TODO: This method is not linking into SkiaSharp so we must use the RAW buffer version
//measured = (int)Paint.BreakText(subString, constraint, out lineWidth) / 2;
bytes = Encoding.UTF8.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
// for some reason I have to pass nBytes * 2
measured = (int)Paint.BreakText(pointer, (IntPtr)(bytes.Length * 2), constraint, out lineWidth) / 2;
// some weird unicode byte issue again
//if(subString.Length % 2 == 1)
//{
// measured -= 1;
//}
pinnedArray.Free();
if (measured == 0)
{
measured = 1;
lineWidth = -1;
}
char nextChar = ' ';
if (curOff + measured < length)
nextChar = _text[curOff + measured];
if (nextChar != ' ')
{
//Perform scan for the last space and end the line there
for (int si = curOff + measured - 1; si > curOff; si--)
{
if (_text[si] == ' ')
{
measured = si - curOff;
extraSkip = 1;
//Rects[si] = SkRect();
break;
}
}
}
}
PerspexFormattedTextLine line = new PerspexFormattedTextLine();
line.Start = curOff;
line.Length = measured;
line.Width = lineWidth;
line.Height = lineHeight;
line.Top = curY;
if (line.Width < 0)
line.Width = _skiaRects[line.Start + line.Length - 1].Right;
//Build character rects
for (int i = line.Start; i < line.Start + line.Length; i++)
{
float prevRight = 0;
if (i != line.Start)
prevRight = _skiaRects[i - 1].Right;
subString = _text.Substring(line.Start, i - line.Start + 1);
float w = Paint.MeasureText(subString);
SKRect rc;
rc.Left = prevRight;
rc.Right = w;
rc.Top = line.Top;
rc.Bottom = line.Top + line.Height;
_skiaRects[i] = rc;
}
subString = _text.Substring(line.Start, line.Length);
line.Width = Paint.MeasureText(subString);
_skiaLines.Add(line);
curY += lineHeight; // + mLeading;
curOff += measured + extraSkip;
}
// Now convert to Perspex data formats
_lines.Clear();
_rects.Clear();
float maxX = 0;
for (var c = 0; c < _skiaLines.Count; c++)
{
var w = _skiaLines[c].Width;
if (maxX < w)
maxX = w;
_lines.Add(new FormattedTextLine(_skiaLines[c].Length, _skiaLines[c].Height));
}
for (var c = 0; c < _text.Length; c++)
_rects.Add(_skiaRects[c].ToPerspexRect());
if (_skiaLines.Count == 0)
_size = new Size();
else
{
var lastLine = _skiaLines[_skiaLines.Count - 1];
_size = new Size(maxX, lastLine.Top + lastLine.Height);
}
}
internal void Draw(SKCanvas canvas, SKPoint origin)
{
var length = _text.Length;
_lines.Clear();
_skiaRects = new SKRect[length];
_skiaLines = new List<PerspexFormattedTextLine>();
int curOff = 0;
float curY = 0;
var metrics = Paint.FontMetrics;
var mTop = metrics.Top; // The greatest distance above the baseline for any glyph (will be <= 0).
var mBottom = metrics.Bottom; // The greatest distance below the baseline for any glyph (will be >= 0).
var mLeading = metrics.Leading; // The recommended distance to add between lines of text (will be >= 0).
// This seems like the best measure of full vertical extent
float lineHeight = mBottom - mTop;
// Rendering is relative to baseline
LineOffset = -metrics.Top;
string subString;
byte[] bytes;
GCHandle pinnedArray;
IntPtr pointer;
for (int c = 0; curOff < length; c++)
{
float lineWidth = -1;
int measured;
int extraSkip = 0;
if (WidthConstraint <= 0)
{
measured = length;
}
else
{
float constraint = WidthConstraint;
if (constraint > MAX_LINE_WIDTH)
constraint = MAX_LINE_WIDTH;
subString = _text.Substring(curOff);
// TODO: This method is not linking into SkiaSharp so we must use the RAW buffer version
//measured = (int)Paint.BreakText(subString, constraint, out lineWidth) / 2;
bytes = Encoding.UTF8.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
// for some reason I have to pass nBytes * 2
measured = (int)Paint.BreakText(pointer, (IntPtr)(bytes.Length * 2), constraint, out lineWidth) / 2;
// some weird unicode byte issue again
//if(subString.Length % 2 == 1)
//{
// measured -= 1;
//}
pinnedArray.Free();
if (measured == 0)
{
measured = 1;
lineWidth = -1;
}
char nextChar = ' ';
if (curOff + measured < length)
nextChar = _text[curOff + measured];
if (nextChar != ' ')
{
//Perform scan for the last space and end the line there
for (int si = curOff + measured - 1; si > curOff; si--)
{
if (_text[si] == ' ')
{
measured = si - curOff;
extraSkip = 1;
//Rects[si] = SkRect();
break;
}
}
}
}
PerspexFormattedTextLine line = new PerspexFormattedTextLine();
line.Start = curOff;
line.Length = measured;
line.Width = lineWidth;
line.Height = lineHeight;
line.Top = curY;
if (line.Width < 0)
line.Width = _skiaRects[line.Start + line.Length - 1].Right;
//Build character rects
for (int i = line.Start; i < line.Start + line.Length; i++)
{
float prevRight = 0;
if (i != line.Start)
prevRight = _skiaRects[i - 1].Right;
subString = _text.Substring(line.Start, i - line.Start + 1);
float w = Paint.MeasureText(subString);
SKRect rc;
rc.Left = prevRight;
rc.Right = w;
rc.Top = line.Top;
rc.Bottom = line.Top + line.Height;
_skiaRects[i] = rc;
}
subString = _text.Substring(line.Start, line.Length);
line.Width = Paint.MeasureText(subString);
_skiaLines.Add(line);
curY += lineHeight; // + mLeading;
curOff += measured + extraSkip;
}
// Now convert to Perspex data formats
_lines.Clear();
_rects.Clear();
float maxX = 0;
for (var c = 0; c < _skiaLines.Count; c++)
{
var w = _skiaLines[c].Width;
if (maxX < w)
maxX = w;
_lines.Add(new FormattedTextLine(_skiaLines[c].Length, _skiaLines[c].Height));
}
for (var c = 0; c < _text.Length; c++)
_rects.Add(_skiaRects[c].ToPerspexRect());
if (_skiaLines.Count == 0)
_size = new Size();
else
{
var lastLine = _skiaLines[_skiaLines.Count - 1];
_size = new Size(maxX, lastLine.Top + lastLine.Height);
}
}
internal void Draw(SKCanvas canvas, SKPoint origin)
{
SKPaint paint = Paint;
/* This originated from Native code, it might be useful
//Debugging code for character positions
SkPaint dpaint;
ctx->Canvas->save();
ctx->Canvas->translate(origin.fX, origin.fY);
for (int c = 0; c < Lines.size(); c++)
{
dpaint.setARGB(255, 0, 0, 0);
SkRect rc;
rc.fLeft = 0;
rc.fTop = Lines[c].Top;
rc.fRight = Lines[c].Width;
rc.fBottom = rc.fTop + LineOffset;
ctx->Canvas->drawRect(rc, dpaint);
}
for (int c = 0; c < Length; c++)
{
dpaint.setARGB(255, c % 10 * 125 / 10 + 125, (c * 7) % 10 * 250 / 10, (c * 13) % 10 * 250 / 10);
dpaint.setStyle(SkPaint::kFill_Style);
ctx->Canvas->drawRect(Rects[c], dpaint);
}
ctx->Canvas->restore();
*/
// These seems to vertically align the text properly
var yOffset = (LineOffset); // + 1) / 2;
for (int c = 0; c < _skiaLines.Count; c++)
{
PerspexFormattedTextLine line = _skiaLines[c];
var subString = _text.Substring(line.Start, line.Length);
canvas.DrawText(subString, origin.X, origin.Y + line.Top + yOffset, paint);
}
}
Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
SKPaint paint = Paint;
/* This originated from Native code, it might be useful
//Debugging code for character positions
SkPaint dpaint;
ctx->Canvas->save();
ctx->Canvas->translate(origin.fX, origin.fY);
for (int c = 0; c < Lines.size(); c++)
{
dpaint.setARGB(255, 0, 0, 0);
SkRect rc;
rc.fLeft = 0;
rc.fTop = Lines[c].Top;
rc.fRight = Lines[c].Width;
rc.fBottom = rc.fTop + LineOffset;
ctx->Canvas->drawRect(rc, dpaint);
}
for (int c = 0; c < Length; c++)
{
dpaint.setARGB(255, c % 10 * 125 / 10 + 125, (c * 7) % 10 * 250 / 10, (c * 13) % 10 * 250 / 10);
dpaint.setStyle(SkPaint::kFill_Style);
ctx->Canvas->drawRect(Rects[c], dpaint);
}
ctx->Canvas->restore();
*/
// These seems to vertically align the text properly
var yOffset = (LineOffset); // + 1) / 2;
for (int c = 0; c < _skiaLines.Count; c++)
{
PerspexFormattedTextLine line = _skiaLines[c];
var subString = _text.Substring(line.Start, line.Length);
canvas.DrawText(subString, origin.X, origin.Y + line.Top + yOffset, paint);
}
}
Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
public Size Constraint
{
get { return _constraint; }
set
{
if(_constraint == value)
if (_constraint == value)
return;
_constraint = value;
WidthConstraint = (_constraint.Width != double.PositiveInfinity)
? (float)_constraint.Width
: -1;
WidthConstraint = (_constraint.Width != double.PositiveInfinity)
? (float)_constraint.Width
: -1;
Rebuild();
Rebuild();
}
}
@ -334,8 +334,8 @@ namespace Perspex.Skia
return _text;
}
public void Dispose()
{
}
}
public void Dispose()
{
}
}
}

258
src/Skia/Perspex.Skia/RenderTarget.cs

@ -17,174 +17,174 @@ using Perspex.Win32.Interop;
namespace Perspex.Skia
{
internal class RenderTarget : IRenderTarget
internal class RenderTarget : IRenderTarget
{
public SKSurface Surface { get; protected set; }
public SKSurface Surface { get; protected set; }
//protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderTarget(handle);
//protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderTarget(handle);
public virtual DrawingContext CreateDrawingContext()
public virtual DrawingContext CreateDrawingContext()
{
return
new DrawingContext(
new DrawingContextImpl(Surface.Canvas)); // MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
return
new DrawingContext(
new DrawingContextImpl(Surface.Canvas)); // MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
}
public void Dispose()
{
//throw new NotImplementedException();
}
}
public void Dispose()
{
//throw new NotImplementedException();
}
}
internal class WindowRenderTarget : RenderTarget
{
private readonly IntPtr _hwnd;
SKBitmap _bitmap;
internal class WindowRenderTarget : RenderTarget
{
private readonly IntPtr _hwnd;
SKBitmap _bitmap;
int Width { get; set; }
int Height { get; set; }
int Width { get; set; }
int Height { get; set; }
public WindowRenderTarget(IntPtr hwnd)
{
_hwnd = hwnd;
FixSize();
}
public WindowRenderTarget(IntPtr hwnd)
{
_hwnd = hwnd;
FixSize();
}
#if __IOS__
private CGRect GetApplicationFrame()
{
// if we are excluding Status Bar then we use ApplicationFrame
// otherwise we use full screen bounds. Note that this must also match
// the Skia/PerspexView!!!
//
bool excludeStatusArea = false; // TODO: make this configurable later
if (excludeStatusArea)
{
return UIScreen.MainScreen.ApplicationFrame;
}
else
{
return UIScreen.MainScreen.Bounds;
}
}
private CGRect GetApplicationFrame()
{
// if we are excluding Status Bar then we use ApplicationFrame
// otherwise we use full screen bounds. Note that this must also match
// the Skia/PerspexView!!!
//
bool excludeStatusArea = false; // TODO: make this configurable later
if (excludeStatusArea)
{
return UIScreen.MainScreen.ApplicationFrame;
}
else
{
return UIScreen.MainScreen.Bounds;
}
}
#endif
void FixSize()
{
int width, height;
GetPlatformWindowSize(_hwnd, out width, out height);
if (Width == width && Height == height)
return;
void FixSize()
{
int width, height;
GetPlatformWindowSize(_hwnd, out width, out height);
if (Width == width && Height == height)
return;
Width = width;
Height = height;
Width = width;
Height = height;
if(Surface != null)
{
Surface.Dispose();
}
if (Surface != null)
{
Surface.Dispose();
}
if(_bitmap != null)
{
_bitmap.Dispose();
}
if (_bitmap != null)
{
_bitmap.Dispose();
}
_bitmap = new SKBitmap(width, height, SKColorType.N_32, SKAlphaType.Premul);
_bitmap = new SKBitmap(width, height, SKColorType.N_32, SKAlphaType.Premul);
//bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
//bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
// Wrap the bitmap in a Surface and keep it cached
Surface = SKSurface.Create(_bitmap.Info, pixels, _bitmap.RowBytes);
}
// Wrap the bitmap in a Surface and keep it cached
Surface = SKSurface.Create(_bitmap.Info, pixels, _bitmap.RowBytes);
}
void GetPlatformWindowSize(IntPtr hwnd, out int w, out int h)
{
void GetPlatformWindowSize(IntPtr hwnd, out int w, out int h)
{
#if __IOS__
var bounds = GetApplicationFrame();
w = (int) bounds.Width;
h = (int)bounds.Height;
var bounds = GetApplicationFrame();
w = (int)bounds.Width;
h = (int)bounds.Height;
#elif WIN32
UnmanagedMethods.RECT rc;
UnmanagedMethods.GetClientRect(_hwnd, out rc);
w = rc.right - rc.left;
h = rc.bottom - rc.top;
UnmanagedMethods.RECT rc;
UnmanagedMethods.GetClientRect(_hwnd, out rc);
w = rc.right - rc.left;
h = rc.bottom - rc.top;
#else
throw new NotImplementedException();
#endif
}
}
public override DrawingContext CreateDrawingContext()
{
FixSize();
public override DrawingContext CreateDrawingContext()
{
FixSize();
var canvas = Surface.Canvas;
canvas.RestoreToCount(0);
canvas.Save();
var canvas = Surface.Canvas;
canvas.RestoreToCount(0);
canvas.Save();
#if __IOS__
var screenScale = UIScreen.MainScreen.Scale;
canvas.Scale((float)screenScale, (float)screenScale);
var screenScale = UIScreen.MainScreen.Scale;
canvas.Scale((float)screenScale, (float)screenScale);
#endif
canvas.Clear(SKColors.Red);
canvas.ResetMatrix();
canvas.Clear(SKColors.Red);
canvas.ResetMatrix();
return
new DrawingContext(
new WindowDrawingContextImpl(this));
}
return
new DrawingContext(
new WindowDrawingContextImpl(this));
}
public void Present()
{
_bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
public void Present()
{
_bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
#if __IOS__
const int bitmapInfo = ((int)CGBitmapFlags.ByteOrder32Big) | ((int)CGImageAlphaInfo.PremultipliedLast);
var bounds = GetApplicationFrame();
var statusBarOffset = UIScreen.MainScreen.Bounds.Height - bounds.Height;
using (var colorSpace = CGColorSpace.CreateDeviceRGB())
using (var bContext = new CGBitmapContext(pixels, _bitmap.Width, _bitmap.Height, 8, _bitmap.Width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo))
using (var image = bContext.ToImage())
using (var context = UIGraphics.GetCurrentContext())
{
// flip the image for CGContext.DrawImage
context.TranslateCTM(0, bounds.Height + statusBarOffset);
context.ScaleCTM(1, -1);
context.DrawImage(bounds, image);
}
const int bitmapInfo = ((int)CGBitmapFlags.ByteOrder32Big) | ((int)CGImageAlphaInfo.PremultipliedLast);
var bounds = GetApplicationFrame();
var statusBarOffset = UIScreen.MainScreen.Bounds.Height - bounds.Height;
using (var colorSpace = CGColorSpace.CreateDeviceRGB())
using (var bContext = new CGBitmapContext(pixels, _bitmap.Width, _bitmap.Height, 8, _bitmap.Width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo))
using (var image = bContext.ToImage())
using (var context = UIGraphics.GetCurrentContext())
{
// flip the image for CGContext.DrawImage
context.TranslateCTM(0, bounds.Height + statusBarOffset);
context.ScaleCTM(1, -1);
context.DrawImage(bounds, image);
}
#elif WIN32
UnmanagedMethods.BITMAPINFO bmi = new UnmanagedMethods.BITMAPINFO();
bmi.biSize = UnmanagedMethods.SizeOf_BITMAPINFOHEADER;
bmi.biWidth = _bitmap.Width;
bmi.biHeight = -_bitmap.Height; // top-down image
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biCompression = (uint)UnmanagedMethods.BitmapCompressionMode.BI_RGB;
bmi.biSizeImage = 0;
IntPtr hdc = UnmanagedMethods.GetDC(_hwnd);
int ret = UnmanagedMethods.SetDIBitsToDevice(hdc,
0, 0,
(uint) _bitmap.Width, (uint) _bitmap.Height,
0, 0,
0, (uint) _bitmap.Height,
pixels,
ref bmi,
(uint)UnmanagedMethods.DIBColorTable.DIB_RGB_COLORS);
UnmanagedMethods.ReleaseDC(_hwnd, hdc);
UnmanagedMethods.BITMAPINFO bmi = new UnmanagedMethods.BITMAPINFO();
bmi.biSize = UnmanagedMethods.SizeOf_BITMAPINFOHEADER;
bmi.biWidth = _bitmap.Width;
bmi.biHeight = -_bitmap.Height; // top-down image
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biCompression = (uint)UnmanagedMethods.BitmapCompressionMode.BI_RGB;
bmi.biSizeImage = 0;
IntPtr hdc = UnmanagedMethods.GetDC(_hwnd);
int ret = UnmanagedMethods.SetDIBitsToDevice(hdc,
0, 0,
(uint)_bitmap.Width, (uint)_bitmap.Height,
0, 0,
0, (uint)_bitmap.Height,
pixels,
ref bmi,
(uint)UnmanagedMethods.DIBColorTable.DIB_RGB_COLORS);
UnmanagedMethods.ReleaseDC(_hwnd, hdc);
#endif
_bitmap.UnlockPixels();
}
_bitmap.UnlockPixels();
}
}
}
}

12
src/Skia/Perspex.Skia/SkiaPlatform.cs

@ -24,17 +24,17 @@ namespace Perspex.Skia
public static void Initialize()
=> PerspexLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new PlatformRenderInterface());
public static bool ForceSoftwareRendering
public static bool ForceSoftwareRendering
{
get { return s_forceSoftwareRendering; }
set
{
s_forceSoftwareRendering = value;
// Do we still need this with SkiaSharp??
//MethodTable.Instance.SetOption(MethodTable.Option.ForceSoftware, new IntPtr(value ? 1 : 0));
throw new NotImplementedException();
}
}
// Do we still need this with SkiaSharp??
//MethodTable.Instance.SetOption(MethodTable.Option.ForceSoftware, new IntPtr(value ? 1 : 0));
throw new NotImplementedException();
}
}
}
}

110
src/Skia/Perspex.Skia/SkiaSharpExtensions.cs

@ -4,66 +4,66 @@ using SkiaSharp;
namespace Perspex.Skia
{
public static class SkiaSharpExtensions
{
public static SKPoint ToSKPoint(this Point p)
{
return new SKPoint((float)p.X, (float)p.Y);
}
public static class SkiaSharpExtensions
{
public static SKPoint ToSKPoint(this Point p)
{
return new SKPoint((float)p.X, (float)p.Y);
}
public static SKRect ToSKRect(this Rect r)
{
return new SKRect((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom);
}
public static SKRect ToSKRect(this Rect r)
{
return new SKRect((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom);
}
public static Rect ToPerspexRect(this SKRect r)
{
return new Rect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top);
}
public static Rect ToPerspexRect(this SKRect r)
{
return new Rect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top);
}
public static SKMatrix ToSKMatrix(this Matrix m)
{
var sm = new SKMatrix
{
ScaleX = (float)m.M11,
SkewX = (float)m.M21,
TransX = (float)m.M31,
SkewY = (float)m.M12,
ScaleY = (float)m.M22,
TransY = (float)m.M32,
Persp0 = 0,
Persp1 = 0,
Persp2 = 1
};
public static SKMatrix ToSKMatrix(this Matrix m)
{
var sm = new SKMatrix
{
ScaleX = (float)m.M11,
SkewX = (float)m.M21,
TransX = (float)m.M31,
SkewY = (float)m.M12,
ScaleY = (float)m.M22,
TransY = (float)m.M32,
Persp0 = 0,
Persp1 = 0,
Persp2 = 1
};
return sm;
}
return sm;
}
public static SKColor ToSKColor(this Media.Color c)
{
return new SKColor(c.R, c.G, c.B, c.A);
}
public static SKColor ToSKColor(this Media.Color c)
{
return new SKColor(c.R, c.G, c.B, c.A);
}
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m)
{
switch (m)
{
default:
case Media.GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp;
case Media.GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror;
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat;
}
}
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m)
{
switch (m)
{
default:
case Media.GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp;
case Media.GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror;
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat;
}
}
public static SKTextAlign ToSKTextAlign(this TextAlignment a)
{
switch (a)
{
default:
case TextAlignment.Left: return SKTextAlign.Left;
case TextAlignment.Center: return SKTextAlign.Center;
case TextAlignment.Right: return SKTextAlign.Right;
}
}
}
public static SKTextAlign ToSKTextAlign(this TextAlignment a)
{
switch (a)
{
default:
case TextAlignment.Left: return SKTextAlign.Left;
case TextAlignment.Center: return SKTextAlign.Center;
case TextAlignment.Right: return SKTextAlign.Right;
}
}
}
}

138
src/Skia/Perspex.Skia/StreamGeometryImpl.cs

@ -12,11 +12,11 @@ namespace Perspex.Skia
{
class StreamGeometryImpl : IStreamGeometryImpl
{
SKPath _path;
SKPath _transformedPath;
SKPath _path;
SKPath _transformedPath;
private Matrix _transform = Matrix.Identity;
private Matrix _transform = Matrix.Identity;
public SKPath EffectivePath => (_transformedPath ?? _path);
public Rect GetRenderBounds(double strokeThickness)
@ -32,7 +32,7 @@ namespace Perspex.Skia
get { return _transform; }
set
{
if(_transform == value)
if (_transform == value)
return;
_transform = value;
@ -42,80 +42,80 @@ namespace Perspex.Skia
void ApplyTransform()
{
if (_path == null)
return;
if (_transformedPath != null)
{
_transformedPath.Dispose();
_transformedPath = null;
}
// TODO: SkiaSharp does not expose Transform yet!!!
//if (!Transform.IsIdentity)
//{
// _transformedPath = new SKPath();
// _path.Transform(Transform.ToSKMatrix(), _transformedPath);
//}
}
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!!
}
if (_path == null)
return;
if (_transformedPath != null)
{
_transformedPath.Dispose();
_transformedPath = null;
}
// TODO: SkiaSharp does not expose Transform yet!!!
//if (!Transform.IsIdentity)
//{
// _transformedPath = new SKPath();
// _path.Transform(Transform.ToSKMatrix(), _transformedPath);
//}
}
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!!
}
public IStreamGeometryContextImpl Open()
{
_path = new SKPath();
_path = new SKPath();
return new StreamContext(this);
}
class StreamContext : IStreamGeometryContextImpl
{
private readonly StreamGeometryImpl _geometryImpl;
private SKPath _path;
private SKPath _path;
//readonly List<SkiaGeometryElement> _elements = new List<SkiaGeometryElement>();
Point _currentPoint;
public StreamContext(StreamGeometryImpl geometryImpl)
{
_geometryImpl = geometryImpl;
_path = _geometryImpl._path;
}
_path = _geometryImpl._path;
}
public void Dispose()
{
// Not sure what we need to do here....
public void Dispose()
{
// Not sure what we need to do here....
// //throw new NotImplementedException();
// //throw new NotImplementedException();
// var arr = _elements.ToArray();
// SkRect rc;
// _path?.Dispose();
// _path = new SKPath(new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc)));
// _geometryImpl.ApplyTransform();
// 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();
// _geometryImpl.Bounds = rc.ToRect();
SKRect rc;
_path.GetBounds(out rc);
_geometryImpl.ApplyTransform();
_geometryImpl.Bounds = rc.ToPerspexRect();
}
SKRect rc;
_path.GetBounds(out rc);
_geometryImpl.ApplyTransform();
_geometryImpl.Bounds = rc.ToPerspexRect();
}
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
{
throw new NotImplementedException();
throw new NotImplementedException();
//ArcToHelper.ArcTo(this, _currentPoint, point, size, rotationAngle, isLargeArc, sweepDirection);
//_currentPoint = point;
@ -123,34 +123,34 @@ namespace Perspex.Skia
public void BeginFigure(Point startPoint, bool isFilled)
{
_path.MoveTo((float)startPoint.X, (float)startPoint.Y);
_currentPoint = startPoint;
_path.MoveTo((float)startPoint.X, (float)startPoint.Y);
_currentPoint = startPoint;
}
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
_path.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
_currentPoint = point3;
_path.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
_currentPoint = point3;
}
public void QuadraticBezierTo(Point point1, Point point2)
{
_path.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
_currentPoint = point2;
_path.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
_currentPoint = point2;
}
public void LineTo(Point point)
{
_path.LineTo((float)point.X, (float)point.Y);
_currentPoint = point;
_path.LineTo((float)point.X, (float)point.Y);
_currentPoint = point;
}
public void EndFigure(bool isClosed)
{
if(isClosed)
{
_path.Close();
}
if (isClosed)
{
_path.Close();
}
}
public void SetFillRule(FillRule fillRule)

18
src/Skia/Perspex.Skia/TypefaceCache.cs

@ -22,13 +22,13 @@ namespace Perspex.Skia
SKTypeface typeface = null;
if (!entry.TryGetValue(style, out typeface))
{
typeface = SKTypeface.FromFamilyName(name, style);
if(typeface == null)
{
typeface = SKTypeface.FromFamilyName(null, style);
}
entry[style] = typeface;
}
typeface = SKTypeface.FromFamilyName(name, style);
if (typeface == null)
{
typeface = SKTypeface.FromFamilyName(null, style);
}
entry[style] = typeface;
}
return typeface;
}
@ -38,8 +38,8 @@ namespace Perspex.Skia
if (style != FontStyle.Normal)
sstyle |= SKTypefaceStyle.Italic;
if(weight>FontWeight.Normal)
sstyle |= SKTypefaceStyle.Bold;
if (weight > FontWeight.Normal)
sstyle |= SKTypefaceStyle.Bold;
return GetTypeface(name, sstyle);
}

13
src/Skia/Perspex.Skia/readme.md

@ -5,18 +5,18 @@ BitmapImpl
- Save
DrawingContextImpl
- DrawRoundRect is not properly implemented due to lack of support in Skia
- 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
- Pen Dash styles
Formatted Text Rendering
- still needs a lot of work once SkiaSharp implementation is better
- minor polish
RenderTarget
- Figure out a cleaner implementation across all platforms
- HW accelerated working?
- HW acceleration
StreamGeometry
- Paths within Paths may not work right
@ -30,13 +30,12 @@ App Bootstrapping
- ie. app.UseDirect2D() (via platform specific extension methods)
Android
- Testing & fixes
- Not tested at all yet
iOS
- Get GLView working again?
- Get GLView working again. See HW above
General
- Cleanup/eliminate obsolete files
- Cleanup the many Test Applications
- Finish cleanup of the many Test Applications
- Get Skia Unit Tests passing

1
src/iOS/Perspex.iOS/Perspex.iOS.csproj

@ -37,6 +37,7 @@
<Compile Include="Clipboard.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="iOSPlatform.cs" />
<Compile Include="PerspexView.cs" />
<Compile Include="PerspexAppDelegate.cs" />
<Compile Include="PlatformSettings.cs" />

2
src/iOS/Perspex.iOS/PerspexAppDelegate.cs

@ -10,6 +10,7 @@ using Perspex.Shared.PlatformSupport;
using Perspex.Skia;
using UIKit;
/* No Longer needed
namespace Perspex.iOS
{
public class PerspexAppDelegate : UIApplicationDelegate
@ -73,3 +74,4 @@ namespace Perspex.iOS
}
}
}
*/

12
src/iOS/Perspex.iOS/PerspexView.cs

@ -168,7 +168,7 @@ namespace Perspex.iOS
var location = touch.LocationInView(this).ToPerspex();
Input?.Invoke(new RawMouseEventArgs(
PerspexAppDelegate.MouseDevice,
iOSPlatform.MouseDevice,
(uint) touch.Timestamp,
_inputRoot,
RawMouseEventType.LeftButtonUp,
@ -185,10 +185,10 @@ namespace Perspex.iOS
{
var location = touch.LocationInView(this).ToPerspex();
_touchLastPoint = location;
Input?.Invoke(new RawMouseEventArgs(PerspexAppDelegate.MouseDevice, (uint) touch.Timestamp, _inputRoot,
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint) touch.Timestamp, _inputRoot,
RawMouseEventType.Move, location, InputModifiers.None));
Input?.Invoke(new RawMouseEventArgs(PerspexAppDelegate.MouseDevice, (uint) touch.Timestamp, _inputRoot,
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint) touch.Timestamp, _inputRoot,
RawMouseEventType.LeftButtonDown, location, InputModifiers.None));
}
}
@ -199,15 +199,15 @@ namespace Perspex.iOS
if (touch != null)
{
var location = touch.LocationInView(this).ToPerspex();
if (PerspexAppDelegate.MouseDevice.Captured != null)
Input?.Invoke(new RawMouseEventArgs(PerspexAppDelegate.MouseDevice, (uint) touch.Timestamp, _inputRoot,
if (iOSPlatform.MouseDevice.Captured != null)
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint) touch.Timestamp, _inputRoot,
RawMouseEventType.Move, location, InputModifiers.LeftMouseButton));
else
{
//magic number based on test - correction of 0.02 is working perfect
double correction = 0.02;
Input?.Invoke(new RawMouseWheelEventArgs(PerspexAppDelegate.MouseDevice, (uint)touch.Timestamp,
Input?.Invoke(new RawMouseWheelEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp,
_inputRoot, location, (location - _touchLastPoint)* correction, InputModifiers.LeftMouseButton));
}
_touchLastPoint = location;

107
src/iOS/Perspex.iOS/iOSPlatform.cs

@ -0,0 +1,107 @@
using System;
using System.Reflection;
using Perspex.Input;
using Perspex.Input.Platform;
using Perspex.iOS;
using Perspex.Platform;
using Perspex.Shared.PlatformSupport;
using Perspex.Skia;
using UIKit;
namespace Perspex
{
public static class iOSApplicationExtensions
{
public static AppT UseiOS<AppT>(this AppT app) where AppT : Application
{
Perspex.iOS.iOSPlatform.Initialize();
return app;
}
// I wish I could merge this with the SkiaPlatform itself. Might be possible
// once we switch to SkiaSharp
public static AppT UseSkiaViewHost<AppT>(this AppT app) where AppT : Application
{
var window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new PerspexViewController(window);
window.RootViewController = controller;
window.MakeKeyAndVisible();
PerspexLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatform(controller.PerspexView));
SkiaPlatform.Initialize();
return app;
}
public static AppT UseAssetAssembly<AppT>(this AppT app, Assembly assembly) where AppT : Application
{
// Asset loading searches our own assembly?
var loader = new AssetLoader(assembly);
PerspexLocator.CurrentMutable.Bind<IAssetLoader>().ToConstant(loader);
return app;
}
// This is somewhat generic, could probably put this elsewhere. But I don't think
// it should part of the iOS App Delegate
//
class WindowingPlatform : IWindowingPlatform
{
private readonly IWindowImpl _window;
public WindowingPlatform(IWindowImpl window)
{
_window = window;
}
public IWindowImpl CreateWindow()
{
return _window;
}
public IWindowImpl CreateEmbeddableWindow()
{
throw new NotImplementedException();
}
public IPopupImpl CreatePopup()
{
throw new NotImplementedException();
}
}
}
}
namespace Perspex.iOS
{
// TODO: Perhaps we should make this class handle all these interfaces directly, like we
// do for Win32 and Gtk
//
public class iOSPlatform //: IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform
{
internal static MouseDevice MouseDevice;
internal static KeyboardDevice KeyboardDevice;
public static void Initialize()
{
MouseDevice = new MouseDevice();
KeyboardDevice = new KeyboardDevice();
// refactored
//SharedPlatform.Register(appType.Assembly);
PerspexLocator.CurrentMutable
.Bind<IPclPlatformWrapper>().ToSingleton<PclPlatformWrapper>()
.Bind<IClipboard>().ToTransient<Clipboard>()
//.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance);
}
}
}

95
src/iOS/Perspex.iOSTestApplication/AppDelegate.cs

@ -16,7 +16,7 @@ namespace Perspex.iOSTestApplication
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : PerspexAppDelegate
public partial class AppDelegate : UIApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
@ -27,58 +27,65 @@ namespace Perspex.iOSTestApplication
//
public override bool FinishedLaunching(UIApplication uiapp, NSDictionary options)
{
InitPerspex (typeof (App));
var app = new App();
MainWindow.RootNamespace = "Perspex.iOSTestApplication";
var window = MainWindow.Create();
//var window = Create();
window.Show();
app.Run(window);
app.UseiOS();
app.UseSkiaViewHost();
app.UseSkia();
// looking for this URI fails: "TestApplication.github_icon.png"
var asm = typeof(App).Assembly;
app.UseAssetAssembly(asm);
//MainWindow.RootNamespace = "Perspex.iOSTestApplication";
//var window = MainWindow.Create();
////var window = Create();
//window.Show();
//app.Run(window);
app.Run();
return true;
}
// This provides a simple UI tree for testing input handling
public static Window Create()
{
Window window = new Window
{
Title = "Perspex Test Application",
//Width = 900,
//Height = 480,
Background = Brushes.Red,
Content = new StackPanel
{
Margin = new Thickness(30),
Background = Brushes.Yellow,
Children = new Controls.Controls
{
new TextBlock
{
Text = "TEXT BLOCK",
Width = 300,
Height = 40,
Background = Brushes.White,
Foreground = Brushes.Black
},
// This provides a simple UI tree for testing input handling
public static Window Create()
{
Window window = new Window
{
Title = "Perspex Test Application",
//Width = 900,
//Height = 480,
Background = Brushes.Red,
Content = new StackPanel
{
Margin = new Thickness(30),
Background = Brushes.Yellow,
Children = new Controls.Controls
{
new TextBlock
{
Text = "TEXT BLOCK",
Width = 300,
Height = 40,
Background = Brushes.White,
Foreground = Brushes.Black
},
new Button
{
Content = "BUTTON",
Width = 150,
Height = 40,
Background = Brushes.LightGreen,
Foreground = Brushes.Black
}
new Button
{
Content = "BUTTON",
Width = 150,
Height = 40,
Background = Brushes.LightGreen,
Foreground = Brushes.Black
}
}
}
};
}
}
};
return window;
}
return window;
}
}

4
src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj

@ -147,6 +147,10 @@
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Perspex.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Diagnostics\Perspex.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Perspex.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.HtmlRenderer\Perspex.HtmlRenderer.csproj">
<Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
<Name>Perspex.HtmlRenderer</Name>

Loading…
Cancel
Save