diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 36238efc7c..590230e7a2 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -211,6 +211,10 @@ {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} Perspex.Themes.Default + + {925dd807-b651-475f-9f7c-cbeb974ce43d} + Perspex.Skia.Desktop + {3e908f67-5543-4879-a1dc-08eace79b3cd} Perspex.Direct2D1 diff --git a/samples/ControlCatalog/Program.cs b/samples/ControlCatalog/Program.cs index d01a6aa11b..e1118d2c11 100644 --- a/samples/ControlCatalog/Program.cs +++ b/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(); } diff --git a/samples/TestApplicationShared/App.cs b/samples/TestApplicationShared/App.cs index 83987d26df..0812f535f3 100644 --- a/samples/TestApplicationShared/App.cs +++ b/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( - x => new TextBlock {Text = x.Name}, - x => x.Children), - }; + Styles.Add(new SampleTabStyle()); + DataTemplates = new DataTemplates + { + new FuncTreeDataTemplate( + 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); + } + } } diff --git a/src/Shared/PlatformSupport/AssetLoader.cs b/src/Shared/PlatformSupport/AssetLoader.cs index 5b747ae666..c70133efaf 100644 --- a/src/Shared/PlatformSupport/AssetLoader.cs +++ b/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)); diff --git a/src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs b/src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs index 022306bbe9..98e79c8a8d 100644 --- a/src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs +++ b/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; + } - } + } } diff --git a/src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs b/src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs deleted file mode 100644 index bdd8b21821..0000000000 --- a/src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs +++ /dev/null @@ -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 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; - } - } - } -} - - */ \ No newline at end of file diff --git a/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj b/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj index 3b520fd408..6f5baf1afe 100644 --- a/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj +++ b/src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj @@ -70,7 +70,6 @@ - UnmanagedMethods.cs @@ -111,23 +110,6 @@ - - native\Windows\i686\libEGL.dll - Always - - - native\Windows\i686\libGLESv2.dll - Always - - - native\Windows\i686\libperspesk.dll - Always - - - - - native\Linux\x86_64\libperspesk.so - diff --git a/src/Skia/Perspex.Skia.iOS/SkiaView.cs b/src/Skia/Perspex.Skia.iOS/SkiaView.cs index 7542a18afe..01283d6625 100644 --- a/src/Skia/Perspex.Skia.iOS/SkiaView.cs +++ b/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(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(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(); diff --git a/src/Skia/Perspex.Skia/BitmapImpl.cs b/src/Skia/Perspex.Skia/BitmapImpl.cs index 02642082bd..f9b9ffa68a 100644 --- a/src/Skia/Perspex.Skia/BitmapImpl.cs +++ b/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))); } } diff --git a/src/Skia/Perspex.Skia/DrawingContextImpl.cs b/src/Skia/Perspex.Skia/DrawingContextImpl.cs index 3ed5e384a2..730da6380a 100644 --- a/src/Skia/Perspex.Skia/DrawingContextImpl.cs +++ b/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 _opacityStack = new Stack(); 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(); + } + } } \ No newline at end of file diff --git a/src/Skia/Perspex.Skia/FormattedTextImpl.cs b/src/Skia/Perspex.Skia/FormattedTextImpl.cs index 08a688b7ea..493831dc26 100644 --- a/src/Skia/Perspex.Skia/FormattedTextImpl.cs +++ b/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 _lines = new List(); + readonly List _lines = new List(); readonly List _rects = new List(); - List _skiaLines; - SKRect[] _skiaRects; + List _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 GetLines() + struct PerspexFormattedTextLine + { + public float Top; + public int Start; + public int Length; + public float Height; + public float Width; + }; + + public IEnumerable 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(); + + 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(); - - 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() + { + } + } } diff --git a/src/Skia/Perspex.Skia/RenderTarget.cs b/src/Skia/Perspex.Skia/RenderTarget.cs index 941e46d08c..7304b4bf7f 100644 --- a/src/Skia/Perspex.Skia/RenderTarget.cs +++ b/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(); + } - } + } } diff --git a/src/Skia/Perspex.Skia/SkiaPlatform.cs b/src/Skia/Perspex.Skia/SkiaPlatform.cs index d9c5728a04..b05096b84b 100644 --- a/src/Skia/Perspex.Skia/SkiaPlatform.cs +++ b/src/Skia/Perspex.Skia/SkiaPlatform.cs @@ -24,17 +24,17 @@ namespace Perspex.Skia public static void Initialize() => PerspexLocator.CurrentMutable.Bind().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(); + } + } } } diff --git a/src/Skia/Perspex.Skia/SkiaSharpExtensions.cs b/src/Skia/Perspex.Skia/SkiaSharpExtensions.cs index 2f20eb3012..a46f37f6ab 100644 --- a/src/Skia/Perspex.Skia/SkiaSharpExtensions.cs +++ b/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; + } + } + } } diff --git a/src/Skia/Perspex.Skia/StreamGeometryImpl.cs b/src/Skia/Perspex.Skia/StreamGeometryImpl.cs index c6443f107d..ecf1e03779 100644 --- a/src/Skia/Perspex.Skia/StreamGeometryImpl.cs +++ b/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 _elements = new List(); 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) diff --git a/src/Skia/Perspex.Skia/TypefaceCache.cs b/src/Skia/Perspex.Skia/TypefaceCache.cs index 4d51980802..bd1edf1abe 100644 --- a/src/Skia/Perspex.Skia/TypefaceCache.cs +++ b/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); } diff --git a/src/Skia/Perspex.Skia/readme.md b/src/Skia/Perspex.Skia/readme.md index 7f8a459eb4..698739415d 100644 --- a/src/Skia/Perspex.Skia/readme.md +++ b/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 \ No newline at end of file diff --git a/src/iOS/Perspex.iOS/Perspex.iOS.csproj b/src/iOS/Perspex.iOS/Perspex.iOS.csproj index b0ce948a3c..097c3f9bbd 100644 --- a/src/iOS/Perspex.iOS/Perspex.iOS.csproj +++ b/src/iOS/Perspex.iOS/Perspex.iOS.csproj @@ -37,6 +37,7 @@ + diff --git a/src/iOS/Perspex.iOS/PerspexAppDelegate.cs b/src/iOS/Perspex.iOS/PerspexAppDelegate.cs index 3a8c2e00df..3dd82d59ae 100644 --- a/src/iOS/Perspex.iOS/PerspexAppDelegate.cs +++ b/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 } } } +*/ \ No newline at end of file diff --git a/src/iOS/Perspex.iOS/PerspexView.cs b/src/iOS/Perspex.iOS/PerspexView.cs index 38314e0f82..385e6d3293 100644 --- a/src/iOS/Perspex.iOS/PerspexView.cs +++ b/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; diff --git a/src/iOS/Perspex.iOS/iOSPlatform.cs b/src/iOS/Perspex.iOS/iOSPlatform.cs new file mode 100644 index 0000000000..8f608de13b --- /dev/null +++ b/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(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(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().ToConstant(new WindowingPlatform(controller.PerspexView)); + + SkiaPlatform.Initialize(); + + return app; + } + + public static AppT UseAssetAssembly(this AppT app, Assembly assembly) where AppT : Application + { + // Asset loading searches our own assembly? + var loader = new AssetLoader(assembly); + PerspexLocator.CurrentMutable.Bind().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().ToSingleton() + .Bind().ToTransient() + //.Bind().ToTransient() + .Bind().ToTransient() + .Bind().ToConstant(KeyboardDevice) + .Bind().ToConstant(MouseDevice) + .Bind().ToSingleton() + .Bind().ToConstant(PlatformThreadingInterface.Instance); + } + } +} diff --git a/src/iOS/Perspex.iOSTestApplication/AppDelegate.cs b/src/iOS/Perspex.iOSTestApplication/AppDelegate.cs index 0a1b353fb1..fcec8f01c5 100644 --- a/src/iOS/Perspex.iOSTestApplication/AppDelegate.cs +++ b/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; + } } diff --git a/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj b/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj index 412712bc28..03f4a78aa7 100644 --- a/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj +++ b/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj @@ -147,6 +147,10 @@ {d2221c82-4a25-4583-9b43-d791e3f6820c} Perspex.Controls + + {7062ae20-5dcc-4442-9645-8195bdece63e} + Perspex.Diagnostics + {5fb2b005-0a7f-4dad-add4-3ed01444e63d} Perspex.HtmlRenderer