Procházet zdrojové kódy

Some rendering is working

flabbet před 11 měsíci
rodič
revize
7203ead117

+ 2 - 1
src/PixiEditor.Desktop/Program.cs

@@ -18,7 +18,8 @@ public class Program
             .UsePlatformDetect()
             .UsePlatformDetect()
             .With(new Win32PlatformOptions()
             .With(new Win32PlatformOptions()
             {
             {
-                RenderingMode = new Win32RenderingMode[] { Win32RenderingMode.Vulkan, Win32RenderingMode.AngleEgl }
+                RenderingMode = new Win32RenderingMode[] { Win32RenderingMode.Wgl, Win32RenderingMode.AngleEgl },
+                OverlayPopups = true
             })
             })
             .LogToTrace();
             .LogToTrace();
 }
 }

+ 2 - 1
src/PixiEditor.DrawingApi.Core/Bridge/Operations/ISurfaceImplementation.cs

@@ -1,5 +1,4 @@
 using System;
 using System;
-using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
@@ -17,4 +16,6 @@ public interface ISurfaceImplementation
     public DrawingSurface Create(ImageInfo imageInfo);
     public DrawingSurface Create(ImageInfo imageInfo);
     public void Dispose(DrawingSurface drawingSurface);
     public void Dispose(DrawingSurface drawingSurface);
     public object GetNativeSurface(IntPtr objectPointer);
     public object GetNativeSurface(IntPtr objectPointer);
+    public void Flush(DrawingSurface drawingSurface);
 }
 }
+

+ 5 - 0
src/PixiEditor.DrawingApi.Core/Surfaces/DrawingSurface.cs

@@ -75,5 +75,10 @@ namespace PixiEditor.DrawingApi.Core.Surfaces
         {
         {
             Changed?.Invoke(changedrect);
             Changed?.Invoke(changedrect);
         }
         }
+
+        public void Flush()
+        {
+            DrawingBackendApi.Current.SurfaceImplementation.Flush(this);
+        }
     }
     }
 }
 }

+ 2 - 0
src/PixiEditor.DrawingApi.Core/Surfaces/ImageData/ImageInfo.cs

@@ -137,6 +137,8 @@ public struct ImageInfo : System.IEquatable<ImageInfo>
     /// <value />
     /// <value />
     public readonly RectI Rect => RectI.Create(this.Width, this.Height);
     public readonly RectI Rect => RectI.Create(this.Width, this.Height);
 
 
+    public bool GpuBacked { get; set; } = false;
+
 
 
     public readonly ImageInfo WithSize(VecI size) => this.WithSize(size.X, size.Y);
     public readonly ImageInfo WithSize(VecI size) => this.WithSize(size.X, size.Y);
 
 

+ 95 - 0
src/PixiEditor.DrawingApi.Core/Texture.cs

@@ -0,0 +1,95 @@
+using System;
+using System.IO;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.DrawingApi.Core;
+
+public class Texture : IDisposable
+{
+    public VecI Size { get; }
+    public DrawingSurface GpuSurface { get; }
+
+    public Texture(VecI size)
+    {
+        Size = size;
+        GpuSurface =
+            DrawingSurface.Create(
+                new ImageInfo(Size.X, Size.Y, ColorType.RgbaF16, AlphaType.Premul, ColorSpace.CreateSrgb())
+                {
+                    GpuBacked = true
+                });
+    }
+
+    public void Dispose()
+    {
+        GpuSurface.Dispose();
+    }
+
+    public static Texture Load(string path)
+    {
+        if (!File.Exists(path))
+            throw new FileNotFoundException(null, path);
+        using var image = Image.FromEncodedData(path);
+        if (image is null)
+            throw new ArgumentException($"The image with path {path} couldn't be loaded");
+
+        Texture texture = new Texture(image.Size);
+        texture.GpuSurface.Canvas.DrawImage(image, 0, 0);
+
+        return texture;
+    }
+
+    public static Texture Load(byte[] data)
+    {
+        using Image image = Image.FromEncodedData(data);
+        Texture texture = new Texture(image.Size);
+        texture.GpuSurface.Canvas.DrawImage(image, 0, 0);
+
+        return texture;
+    }
+
+    public static Texture? Load(byte[] encoded, ColorType colorType, VecI imageSize)
+    {
+        using var image = Image.FromPixels(new ImageInfo(imageSize.X, imageSize.Y, colorType), encoded);
+        if (image is null)
+            return null;
+
+        var surface = new Texture(new VecI(image.Width, image.Height));
+        surface.GpuSurface.Canvas.DrawImage(image, 0, 0);
+
+        return surface;
+    }
+
+    public Texture CreateResized(VecI newSize, ResizeMethod method)
+    {
+        using Image image = GpuSurface.Snapshot();
+        Texture newTexture = new(newSize);
+        using Paint paint = new();
+
+        FilterQuality filterQuality = method switch
+        {
+            ResizeMethod.HighQuality => FilterQuality.High,
+            ResizeMethod.MediumQuality => FilterQuality.Medium,
+            ResizeMethod.LowQuality => FilterQuality.Low,
+            _ => FilterQuality.None
+        };
+
+        paint.FilterQuality = filterQuality;
+
+        newTexture.GpuSurface.Canvas.DrawImage(image, new RectD(0, 0, newSize.X, newSize.Y), paint);
+        return newTexture;
+    }
+
+    public Color? GetSRGBPixel(VecI vecI)
+    {
+        if (vecI.X < 0 || vecI.X >= Size.X || vecI.Y < 0 || vecI.Y >= Size.Y)
+            return null;
+        
+        //TODO:
+        return Color.Empty;
+    }
+}

+ 10 - 0
src/PixiEditor.DrawingApi.Skia/Exceptions/GrContextAlreadyInitializedException.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Skia.Exceptions;
+
+public class GrContextAlreadyInitializedException : Exception
+{
+    public GrContextAlreadyInitializedException() : base("GRContext is already initialized")
+    {
+    }
+}

+ 45 - 6
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaSurfaceImplementation.cs

@@ -4,6 +4,7 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.Numerics;
 using SkiaSharp;
 using SkiaSharp;
 
 
 namespace PixiEditor.DrawingApi.Skia.Implementations
 namespace PixiEditor.DrawingApi.Skia.Implementations
@@ -14,16 +15,24 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         private readonly SkiaCanvasImplementation _canvasImplementation;
         private readonly SkiaCanvasImplementation _canvasImplementation;
         private readonly SkiaPaintImplementation _paintImplementation;
         private readonly SkiaPaintImplementation _paintImplementation;
 
 
-        public SkiaSurfaceImplementation(SkiaPixmapImplementation pixmapImplementation, SkiaCanvasImplementation canvasImplementation, SkiaPaintImplementation paintImplementation)
+        public GRContext GrContext { get; set; }
+
+        public SkiaSurfaceImplementation(GRContext context, SkiaPixmapImplementation pixmapImplementation, SkiaCanvasImplementation canvasImplementation, SkiaPaintImplementation paintImplementation)
         {
         {
             _pixmapImplementation = pixmapImplementation;
             _pixmapImplementation = pixmapImplementation;
             _canvasImplementation = canvasImplementation;
             _canvasImplementation = canvasImplementation;
             _paintImplementation = paintImplementation;
             _paintImplementation = paintImplementation;
+            GrContext = context;
         }
         }
         
         
         public Pixmap PeekPixels(DrawingSurface drawingSurface)
         public Pixmap PeekPixels(DrawingSurface drawingSurface)
         {
         {
             SKPixmap pixmap = ManagedInstances[drawingSurface.ObjectPointer].PeekPixels();
             SKPixmap pixmap = ManagedInstances[drawingSurface.ObjectPointer].PeekPixels();
+            if (pixmap == null)
+            {
+                return drawingSurface.Snapshot().PeekPixels();
+            }
+            
             return _pixmapImplementation.CreateFrom(pixmap);
             return _pixmapImplementation.CreateFrom(pixmap);
         }
         }
 
 
@@ -38,34 +47,58 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         {
         {
             SKCanvas canvas = _canvasImplementation[surfaceToDraw.ObjectPointer];
             SKCanvas canvas = _canvasImplementation[surfaceToDraw.ObjectPointer];
             SKPaint paint = _paintImplementation[drawingPaint.ObjectPointer];
             SKPaint paint = _paintImplementation[drawingPaint.ObjectPointer];
-            ManagedInstances[drawingSurface.ObjectPointer].Draw(canvas, x, y, paint);
+            var instance = ManagedInstances[drawingSurface.ObjectPointer];
+            instance.Draw(canvas, x, y, paint);
         }
         }
         
         
         public DrawingSurface Create(ImageInfo imageInfo, IntPtr pixels, int rowBytes)
         public DrawingSurface Create(ImageInfo imageInfo, IntPtr pixels, int rowBytes)
         {
         {
-            SKSurface skSurface = SKSurface.Create(imageInfo.ToSkImageInfo(), pixels, rowBytes);
+            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size, imageInfo.GpuBacked);
+            
+            var canvas = skSurface.Canvas;
+            canvas.DrawImage(SKImage.FromPixelCopy(imageInfo.ToSkImageInfo(), pixels, rowBytes), new SKPoint(0, 0));
+            
             return CreateDrawingSurface(skSurface);
             return CreateDrawingSurface(skSurface);
         }
         }
 
 
         public DrawingSurface Create(ImageInfo imageInfo, IntPtr pixelBuffer)
         public DrawingSurface Create(ImageInfo imageInfo, IntPtr pixelBuffer)
         {
         {
-            SKSurface skSurface = SKSurface.Create(imageInfo.ToSkImageInfo(), pixelBuffer);
+            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size, imageInfo.GpuBacked);
+            
+            var canvas = skSurface.Canvas;
+            canvas.DrawImage(SKImage.FromPixelCopy(imageInfo.ToSkImageInfo(), pixelBuffer), new SKPoint(0, 0));
+            
             return CreateDrawingSurface(skSurface);
             return CreateDrawingSurface(skSurface);
         }
         }
 
 
         public DrawingSurface Create(Pixmap pixmap)
         public DrawingSurface Create(Pixmap pixmap)
         {
         {
             SKPixmap skPixmap = _pixmapImplementation[pixmap.ObjectPointer];
             SKPixmap skPixmap = _pixmapImplementation[pixmap.ObjectPointer];
-            SKSurface skSurface = SKSurface.Create(skPixmap);
+            SKImageInfo info = skPixmap.Info;
+            SKSurface skSurface = CreateSkiaSurface(new VecI(info.Width, info.Height), true);
+            
+            var canvas = skSurface.Canvas;
+            canvas.DrawImage(SKImage.FromPixels(skPixmap), new SKPoint(0, 0));
+            
             return CreateDrawingSurface(skSurface);
             return CreateDrawingSurface(skSurface);
         }
         }
 
 
         public DrawingSurface Create(ImageInfo imageInfo)
         public DrawingSurface Create(ImageInfo imageInfo)
         {
         {
-            SKSurface skSurface = SKSurface.Create(imageInfo.ToSkImageInfo());
+            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size, imageInfo.GpuBacked);
             return CreateDrawingSurface(skSurface);
             return CreateDrawingSurface(skSurface);
         }
         }
 
 
+        private SKSurface CreateSkiaSurface(VecI size, bool gpu)
+        {
+            if (!gpu)
+            {
+                return SKSurface.Create(new SKImageInfo(size.X, size.Y));
+            }
+            
+            return SKSurface.Create(GrContext, false, new SKImageInfo(size.X, size.Y));
+        }
+
         public void Dispose(DrawingSurface drawingSurface)
         public void Dispose(DrawingSurface drawingSurface)
         {
         {
             ManagedInstances[drawingSurface.ObjectPointer].Dispose();
             ManagedInstances[drawingSurface.ObjectPointer].Dispose();
@@ -84,7 +117,13 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
 
 
             DrawingSurface surface = new DrawingSurface(skSurface.Handle, canvas);
             DrawingSurface surface = new DrawingSurface(skSurface.Handle, canvas);
             ManagedInstances[skSurface.Handle] = skSurface;
             ManagedInstances[skSurface.Handle] = skSurface;
+            
             return surface;
             return surface;
         }
         }
+        
+        public void Flush(DrawingSurface drawingSurface)
+        {
+            ManagedInstances[drawingSurface.ObjectPointer].Flush(true);
+        }
     }
     }
 }
 }

+ 26 - 9
src/PixiEditor.DrawingApi.Skia/SkiaDrawingBackend.cs

@@ -1,12 +1,30 @@
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Bridge.NativeObjectsImpl;
 using PixiEditor.DrawingApi.Core.Bridge.NativeObjectsImpl;
 using PixiEditor.DrawingApi.Core.Bridge.Operations;
 using PixiEditor.DrawingApi.Core.Bridge.Operations;
+using PixiEditor.DrawingApi.Skia.Exceptions;
 using PixiEditor.DrawingApi.Skia.Implementations;
 using PixiEditor.DrawingApi.Skia.Implementations;
+using SkiaSharp;
 
 
 namespace PixiEditor.DrawingApi.Skia
 namespace PixiEditor.DrawingApi.Skia
 {
 {
     public class SkiaDrawingBackend : IDrawingBackend
     public class SkiaDrawingBackend : IDrawingBackend
     {
     {
+        public GRContext? GraphicsContext
+        {
+            get => _grContext;
+            set
+            {
+                if (_grContext != null)
+                {
+                    throw new GrContextAlreadyInitializedException();
+                }
+                
+                _grContext = value;
+            }
+        }
+        
+        public bool IsGpuAccelerated => GraphicsContext != null;
+        
         public IColorImplementation ColorImplementation { get; }
         public IColorImplementation ColorImplementation { get; }
         public IImageImplementation ImageImplementation { get; }
         public IImageImplementation ImageImplementation { get; }
         public IImgDataImplementation ImgDataImplementation { get; }
         public IImgDataImplementation ImgDataImplementation { get; }
@@ -15,13 +33,16 @@ namespace PixiEditor.DrawingApi.Skia
         public IVectorPathImplementation PathImplementation { get; }
         public IVectorPathImplementation PathImplementation { get; }
         public IMatrix3X3Implementation MatrixImplementation { get; }
         public IMatrix3X3Implementation MatrixImplementation { get; }
         public IPixmapImplementation PixmapImplementation { get; }
         public IPixmapImplementation PixmapImplementation { get; }
-        public ISurfaceImplementation SurfaceImplementation { get; }
+        public ISurfaceImplementation SurfaceImplementation => _surfaceImplementation;
         public IColorSpaceImplementation ColorSpaceImplementation { get; }
         public IColorSpaceImplementation ColorSpaceImplementation { get; }
         public IBitmapImplementation BitmapImplementation { get; }
         public IBitmapImplementation BitmapImplementation { get; }
         public IColorFilterImplementation ColorFilterImplementation { get; }
         public IColorFilterImplementation ColorFilterImplementation { get; }
         public IImageFilterImplementation ImageFilterImplementation { get; }
         public IImageFilterImplementation ImageFilterImplementation { get; }
         public IShaderImplementation ShaderImplementation { get; set; }
         public IShaderImplementation ShaderImplementation { get; set; }
 
 
+        private SkiaSurfaceImplementation _surfaceImplementation;
+        private GRContext _grContext;
+
         public SkiaDrawingBackend()
         public SkiaDrawingBackend()
         {
         {
             ColorImplementation = new SkiaColorImplementation();
             ColorImplementation = new SkiaColorImplementation();
@@ -54,26 +75,22 @@ namespace PixiEditor.DrawingApi.Skia
             
             
             SkiaImageImplementation imgImpl = new SkiaImageImplementation(dataImpl, pixmapImpl);
             SkiaImageImplementation imgImpl = new SkiaImageImplementation(dataImpl, pixmapImpl);
             ImageImplementation = imgImpl;
             ImageImplementation = imgImpl;
-
             SkiaBitmapImplementation bitmapImpl = new SkiaBitmapImplementation(imgImpl);
             SkiaBitmapImplementation bitmapImpl = new SkiaBitmapImplementation(imgImpl);
             BitmapImplementation = bitmapImpl;
             BitmapImplementation = bitmapImpl;
             
             
             SkiaCanvasImplementation canvasImpl = new SkiaCanvasImplementation(paintImpl, imgImpl, bitmapImpl, pathImpl);
             SkiaCanvasImplementation canvasImpl = new SkiaCanvasImplementation(paintImpl, imgImpl, bitmapImpl, pathImpl);
             
             
-            var surfaceImpl = new SkiaSurfaceImplementation(pixmapImpl, canvasImpl, paintImpl);
+            _surfaceImplementation = new SkiaSurfaceImplementation(GraphicsContext, pixmapImpl, canvasImpl, paintImpl);
 
 
-            canvasImpl.SetSurfaceImplementation(surfaceImpl);
-            imgImpl.SetSurfaceImplementation(surfaceImpl);
+            canvasImpl.SetSurfaceImplementation(_surfaceImplementation);
+            imgImpl.SetSurfaceImplementation(_surfaceImplementation);
 
 
             CanvasImplementation = canvasImpl;
             CanvasImplementation = canvasImpl;
-
-            SurfaceImplementation = surfaceImpl;
-
         }
         }
         
         
         public void Setup()
         public void Setup()
         {
         {
-            
+            _surfaceImplementation.GrContext = GraphicsContext;
         }
         }
     }
     }
 }
 }

+ 1 - 1
src/PixiEditor/Models/Handlers/IReferenceLayerHandler.cs

@@ -11,7 +11,7 @@ namespace PixiEditor.Models.Handlers;
 
 
 public interface IReferenceLayerHandler : IHandler
 public interface IReferenceLayerHandler : IHandler
 {
 {
-    public Surface? ReferenceBitmap { get; }
+    public Texture? ReferenceBitmap { get; }
     public ShapeCorners ReferenceShapeBindable { get; set; }
     public ShapeCorners ReferenceShapeBindable { get; set; }
     public bool IsTopMost { get; set; }
     public bool IsTopMost { get; set; }
     public bool IsTransforming { get; set; }
     public bool IsTransforming { get; set; }

+ 26 - 8
src/PixiEditor/Models/IO/Importer.cs

@@ -33,9 +33,9 @@ internal class Importer : ObservableObject
     /// <returns>WriteableBitmap of imported image.</returns>
     /// <returns>WriteableBitmap of imported image.</returns>
     public static Surface? ImportImage(string path, VecI size)
     public static Surface? ImportImage(string path, VecI size)
     {
     {
-        if (!Path.Exists(path)) 
+        if (!Path.Exists(path))
             throw new MissingFileException();
             throw new MissingFileException();
-        
+
         Surface original;
         Surface original;
         try
         try
         {
         {
@@ -45,7 +45,7 @@ internal class Importer : ObservableObject
         {
         {
             throw new CorruptedFileException(e);
             throw new CorruptedFileException(e);
         }
         }
-        
+
         if (original.Size == size || size == VecI.NegativeOne)
         if (original.Size == size || size == VecI.NegativeOne)
         {
         {
             return original;
             return original;
@@ -64,7 +64,8 @@ internal class Importer : ObservableObject
         }
         }
         catch (NotSupportedException e)
         catch (NotSupportedException e)
         {
         {
-            throw new InvalidFileTypeException(new LocalizedString("FILE_EXTENSION_NOT_SUPPORTED", Path.GetExtension(path)), e);
+            throw new InvalidFileTypeException(
+                new LocalizedString("FILE_EXTENSION_NOT_SUPPORTED", Path.GetExtension(path)), e);
         }
         }
         /*catch (FileFormatException e) TODO: Not found in Avalonia
         /*catch (FileFormatException e) TODO: Not found in Avalonia
         {
         {
@@ -126,7 +127,7 @@ internal class Importer : ObservableObject
                 // TODO: Handle
                 // TODO: Handle
                 throw new RecoverableException();
                 throw new RecoverableException();
             }
             }
-            
+
             var pixiDocument = parser.Deserialize(file);
             var pixiDocument = parser.Deserialize(file);
 
 
             var document = pixiDocument switch
             var document = pixiDocument switch
@@ -150,13 +151,30 @@ internal class Importer : ObservableObject
         }
         }
     }
     }
 
 
-    public static Surface GetPreviewBitmap(string path)
+    public static Texture GetPreviewTexture(string path)
     {
     {
         if (!IsSupportedFile(path))
         if (!IsSupportedFile(path))
         {
         {
-            throw new InvalidFileTypeException(new LocalizedString("FILE_EXTENSION_NOT_SUPPORTED", Path.GetExtension(path)));
+            throw new InvalidFileTypeException(new LocalizedString("FILE_EXTENSION_NOT_SUPPORTED",
+                Path.GetExtension(path)));
         }
         }
-        
+
+        if (Path.GetExtension(path) != ".pixi")
+            return Texture.Load(path);
+
+        using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
+
+        return Texture.Load(PixiParser.ReadPreview(fileStream));
+    }
+
+    public static Surface GetPreviewSurface(string path)
+    {
+        if (!IsSupportedFile(path))
+        {
+            throw new InvalidFileTypeException(new LocalizedString("FILE_EXTENSION_NOT_SUPPORTED",
+                Path.GetExtension(path)));
+        }
+
         if (Path.GetExtension(path) != ".pixi")
         if (Path.GetExtension(path) != ".pixi")
             return Surface.Load(path);
             return Surface.Load(path);
 
 

+ 8 - 8
src/PixiEditor/Models/UserData/RecentlyOpenedDocument.cs

@@ -20,7 +20,7 @@ internal class RecentlyOpenedDocument : ObservableObject
 
 
     private string filePath;
     private string filePath;
 
 
-    private Surface previewBitmap;
+    private Texture previewBitmap;
 
 
     public string FilePath
     public string FilePath
     {
     {
@@ -61,7 +61,7 @@ internal class RecentlyOpenedDocument : ObservableObject
         }
         }
     }
     }
 
 
-    public Surface PreviewBitmap
+    public Texture PreviewBitmap
     {
     {
         get
         get
         {
         {
@@ -80,7 +80,7 @@ internal class RecentlyOpenedDocument : ObservableObject
         FilePath = path;
         FilePath = path;
     }
     }
 
 
-    private Surface? LoadPreviewBitmap()
+    private Texture? LoadPreviewBitmap()
     {
     {
         if (!File.Exists(FilePath))
         if (!File.Exists(FilePath))
         {
         {
@@ -91,7 +91,7 @@ internal class RecentlyOpenedDocument : ObservableObject
         {
         {
             try
             try
             {
             {
-                return Importer.GetPreviewBitmap(FilePath);
+                return Importer.GetPreviewTexture(FilePath);
             }
             }
             catch
             catch
             {
             {
@@ -101,11 +101,11 @@ internal class RecentlyOpenedDocument : ObservableObject
 
 
         if (SupportedFilesHelper.IsExtensionSupported(FileExtension))
         if (SupportedFilesHelper.IsExtensionSupported(FileExtension))
         {
         {
-            Surface bitmap = null;
+            Texture bitmap = null;
 
 
             try
             try
             {
             {
-                bitmap = Surface.Load(FilePath);
+                bitmap = Texture.Load(FilePath);
             }
             }
             catch (RecoverableException)
             catch (RecoverableException)
             {
             {
@@ -122,12 +122,12 @@ internal class RecentlyOpenedDocument : ObservableObject
         return null;
         return null;
     }
     }
 
 
-    private Surface DownscaleToMaxSize(Surface bitmap)
+    private Texture DownscaleToMaxSize(Texture bitmap)
     {
     {
         if (bitmap.Size.X > Constants.MaxPreviewWidth || bitmap.Size.Y > Constants.MaxPreviewHeight)
         if (bitmap.Size.X > Constants.MaxPreviewWidth || bitmap.Size.Y > Constants.MaxPreviewHeight)
         {
         {
             double factor = Math.Min(Constants.MaxPreviewWidth / (double)bitmap.Size.X, Constants.MaxPreviewHeight / (double)bitmap.Size.Y);
             double factor = Math.Min(Constants.MaxPreviewWidth / (double)bitmap.Size.X, Constants.MaxPreviewHeight / (double)bitmap.Size.Y);
-            var scaledBitmap = bitmap.Resize(new VecI((int)(bitmap.Size.X * factor), (int)(bitmap.Size.Y * factor)),
+            var scaledBitmap = bitmap.CreateResized(new VecI((int)(bitmap.Size.X * factor), (int)(bitmap.Size.Y * factor)),
                 ResizeMethod.HighQuality);
                 ResizeMethod.HighQuality);
             return scaledBitmap;
             return scaledBitmap;
         }
         }

+ 1 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -626,7 +626,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
 
     public Color? PickColorFromReferenceLayer(VecD pos)
     public Color? PickColorFromReferenceLayer(VecD pos)
     {
     {
-        Surface? bitmap = ReferenceLayerViewModel.ReferenceBitmap;
+        Texture? bitmap = ReferenceLayerViewModel.ReferenceBitmap;
         if (bitmap is null)
         if (bitmap is null)
             return null;
             return null;
 
 

+ 2 - 2
src/PixiEditor/ViewModels/Document/ReferenceLayerViewModel.cs

@@ -26,7 +26,7 @@ internal class ReferenceLayerViewModel : ObservableObject, IReferenceLayerHandle
 
 
     public const double TopMostOpacity = 0.6;
     public const double TopMostOpacity = 0.6;
     
     
-    public Surface? ReferenceBitmap { get; private set; }
+    public Texture? ReferenceBitmap { get; private set; }
 
 
     private ShapeCorners referenceShape;
     private ShapeCorners referenceShape;
     public ShapeCorners ReferenceShapeBindable 
     public ShapeCorners ReferenceShapeBindable 
@@ -114,7 +114,7 @@ internal class ReferenceLayerViewModel : ObservableObject, IReferenceLayerHandle
     
     
     public void SetReferenceLayer(ImmutableArray<byte> imageBgra8888Bytes, VecI imageSize, ShapeCorners shape)
     public void SetReferenceLayer(ImmutableArray<byte> imageBgra8888Bytes, VecI imageSize, ShapeCorners shape)
     {
     {
-        ReferenceBitmap = Surface.Load(imageBgra8888Bytes.ToArray(), ColorType.Bgra8888, imageSize); //TODO: Was WriteableBitmapUtility.FromBgra8888Array(imageBgra8888Bytes.ToArray(), imageSize);
+        ReferenceBitmap = Texture.Load(imageBgra8888Bytes.ToArray(), ColorType.Bgra8888, imageSize); //TODO: Was WriteableBitmapUtility.FromBgra8888Array(imageBgra8888Bytes.ToArray(), imageSize);
         referenceShape = shape;
         referenceShape = shape;
         isVisible = true;
         isVisible = true;
         isTransforming = false;
         isTransforming = false;

+ 2 - 1
src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs

@@ -7,6 +7,7 @@ using Avalonia.Input;
 using Avalonia.Media;
 using Avalonia.Media;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Clipboard;
 using PixiEditor.Models.Clipboard;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Commands;
@@ -82,7 +83,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
 
 
         // TODO: Exception handling would probably be good
         // TODO: Exception handling would probably be good
-        var bitmap = Importer.GetPreviewBitmap(path);
+        var bitmap = Importer.GetPreviewSurface(path);
         byte[] pixels = bitmap.ToWriteableBitmap().ExtractPixels();
         byte[] pixels = bitmap.ToWriteableBitmap().ExtractPixels();
 
 
         doc.Operations.ImportReferenceLayer(
         doc.Operations.ImportReferenceLayer(

+ 40 - 4
src/PixiEditor/Views/MainWindow.axaml.cs

@@ -4,11 +4,16 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+using Avalonia.Rendering.Composition;
 using Avalonia.Threading;
 using Avalonia.Threading;
+using Avalonia.Vulkan;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Skia;
 using PixiEditor.DrawingApi.Skia;
+using PixiEditor.DrawingApi.Skia.Implementations;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
@@ -28,10 +33,15 @@ internal partial class MainWindow : Window
     private readonly IServiceProvider services;
     private readonly IServiceProvider services;
     private static ExtensionLoader extLoader;
     private static ExtensionLoader extLoader;
 
 
-    public new ViewModels_ViewModelMain DataContext { get => (ViewModels_ViewModelMain)base.DataContext; set => base.DataContext = value; }
-    
-    public static MainWindow? Current {
-        get 
+    public new ViewModels_ViewModelMain DataContext
+    {
+        get => (ViewModels_ViewModelMain)base.DataContext;
+        set => base.DataContext = value;
+    }
+
+    public static MainWindow? Current
+    {
+        get
         {
         {
             if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
             if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
                 return desktop.MainWindow as MainWindow;
                 return desktop.MainWindow as MainWindow;
@@ -55,6 +65,7 @@ internal partial class MainWindow : Window
         AsyncImageLoader.ImageLoader.AsyncImageLoader = new DiskCachedWebImageLoader();
         AsyncImageLoader.ImageLoader.AsyncImageLoader = new DiskCachedWebImageLoader();
 
 
         SkiaDrawingBackend skiaDrawingBackend = new SkiaDrawingBackend();
         SkiaDrawingBackend skiaDrawingBackend = new SkiaDrawingBackend();
+        skiaDrawingBackend.GraphicsContext = GetOpenGlGrContext();
         DrawingBackendApi.SetupBackend(skiaDrawingBackend);
         DrawingBackendApi.SetupBackend(skiaDrawingBackend);
 
 
         preferences = services.GetRequiredService<IPreferences>();
         preferences = services.GetRequiredService<IPreferences>();
@@ -65,6 +76,31 @@ internal partial class MainWindow : Window
         InitializeComponent();
         InitializeComponent();
     }
     }
 
 
+
+    public static GRContext GetOpenGlGrContext()
+    {
+        Compositor compositor = Compositor.TryGetDefaultCompositor();
+        var interop = compositor.TryGetCompositionGpuInterop();
+        var contextSharingFeature =
+            compositor.TryGetRenderInterfaceFeature(typeof(IOpenGlTextureSharingRenderInterfaceContextFeature)).Result
+                as IOpenGlTextureSharingRenderInterfaceContextFeature;
+
+        if (contextSharingFeature.CanCreateSharedContext)
+        {
+            IGlContext? glContext = contextSharingFeature.CreateSharedContext();
+            glContext.MakeCurrent();
+            return GRContext.CreateGl(GRGlInterface.Create(glContext.GlInterface.GetProcAddress));
+        }
+
+        return null;
+        /*var contextFactory = AvaloniaLocator.Current.GetRequiredService<IPlatformGraphicsOpenGlContextFactory>();
+        var ctx = contextFactory.CreateContext(null);
+        ctx.MakeCurrent();
+        var ctxInterface = GRGlInterface.Create(ctx.GlInterface.GetProcAddress);
+        var grContext = GRContext.CreateGl(ctxInterface);
+        return grContext;*/
+    }
+
     public static MainWindow CreateWithRecoveredDocuments(CrashReport report, out bool showMissingFilesDialog)
     public static MainWindow CreateWithRecoveredDocuments(CrashReport report, out bool showMissingFilesDialog)
     {
     {
         var window = GetMainWindow();
         var window = GetMainWindow();

+ 17 - 2
src/PixiEditor/Views/Overlays/ReferenceLayerOverlay.cs

@@ -9,11 +9,15 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Styling;
 using Avalonia.Styling;
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Helpers.Converters;
 using PixiEditor.Helpers.Converters;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.Views.Visuals;
 using PixiEditor.Views.Visuals;
+using Color = PixiEditor.DrawingApi.Core.ColorsImpl.Color;
 
 
 namespace PixiEditor.Views.Overlays;
 namespace PixiEditor.Views.Overlays;
 
 
@@ -79,8 +83,19 @@ internal class ReferenceLayerOverlay : Overlay
 
 
             RectD dirty = new RectD(0, 0, ReferenceLayer.ReferenceBitmap.Size.X, ReferenceLayer.ReferenceBitmap.Size.Y);
             RectD dirty = new RectD(0, 0, ReferenceLayer.ReferenceBitmap.Size.X, ReferenceLayer.ReferenceBitmap.Size.Y);
             Rect dirtyRect = new Rect(dirty.X, dirty.Y, dirty.Width, dirty.Height);
             Rect dirtyRect = new Rect(dirty.X, dirty.Y, dirty.Width, dirty.Height);
-            DrawSurfaceOperation drawOperation =
-                new DrawSurfaceOperation(dirtyRect, ReferenceLayer.ReferenceBitmap, Stretch.None, Opacity);
+            
+            double opacity = Opacity;
+            var referenceBitmap = ReferenceLayer.ReferenceBitmap;
+            DrawTextureOperation drawOperation =
+                new DrawTextureOperation(dirtyRect, Stretch.None, ReferenceLayer.ReferenceBitmap.Size, canvas =>
+                {
+                    using Paint opacityPaint = new Paint();
+                    opacityPaint.Color = new Color(255, 255, 255, (byte)(255 * opacity));
+                    opacityPaint.BlendMode = BlendMode.SrcOver;
+
+                    canvas.DrawSurface(referenceBitmap.GpuSurface.Native as SKSurface, 0, 0, opacityPaint.Native as SKPaint);
+                });
+            
             context.Custom(drawOperation);
             context.Custom(drawOperation);
 
 
             matrix.Dispose();
             matrix.Dispose();

+ 163 - 0
src/PixiEditor/Views/Visuals/TextureControl.cs

@@ -0,0 +1,163 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Rendering.SceneGraph;
+using Avalonia.Skia;
+using Avalonia.Threading;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Bridge;
+using PixiEditor.DrawingApi.Skia.Implementations;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Views.Visuals;
+
+public class TextureControl : Control
+{
+    public static readonly StyledProperty<Texture> TextureProperty = AvaloniaProperty.Register<TextureControl, Texture>(
+        nameof(Texture));
+
+    public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<TextureControl, Stretch>(
+        nameof(Stretch), Stretch.Uniform);
+
+    public Stretch Stretch
+    {
+        get => GetValue(StretchProperty);
+        set => SetValue(StretchProperty, value);
+    }
+
+    public Texture Texture
+    {
+        get => GetValue(TextureProperty);
+        set => SetValue(TextureProperty, value);
+    }
+
+    static TextureControl()
+    {
+        AffectsRender<TextureControl>(TextureProperty, StretchProperty);
+    }
+
+    public TextureControl()
+    {
+        ClipToBounds = true;
+    }
+
+    /// <summary>
+    /// Measures the control.
+    /// </summary>
+    /// <param name="availableSize">The available size.</param>
+    /// <returns>The desired size of the control.</returns>
+    protected override Size MeasureOverride(Size availableSize)
+    {
+        var source = Texture;
+        var result = new Size();
+
+        if (source != null)
+        {
+            result = Stretch.CalculateSize(availableSize, new Size(source.Size.X, source.Size.Y));
+        }
+        else if (Width > 0 && Height > 0)
+        {
+            result = Stretch.CalculateSize(availableSize, new Size(Width, Height));
+        }
+
+        return result;
+    }
+
+    /// <inheritdoc/>
+    protected override Size ArrangeOverride(Size finalSize)
+    {
+        var source = Texture;
+
+        if (source != null)
+        {
+            var sourceSize = source.Size;
+            var result = Stretch.CalculateSize(finalSize, new Size(sourceSize.X, sourceSize.Y));
+            return result;
+        }
+        else
+        {
+            return Stretch.CalculateSize(finalSize, new Size(Width, Height));
+        }
+
+        return new Size();
+    }
+
+    public override void Render(DrawingContext context)
+    {
+        Texture texture = Texture;
+        texture.GpuSurface.Flush();
+        ICustomDrawOperation drawOperation = new DrawTextureOperation(
+            new Rect(0, 0, Bounds.Width, Bounds.Height),
+            Stretch,
+            texture.Size,
+            canvas =>
+            {
+                canvas.DrawSurface(texture.GpuSurface.Native as SKSurface, 0, 0);
+            });
+
+        context.Custom(drawOperation);
+    }
+}
+
+internal class DrawTextureOperation : SkiaDrawOperation
+{
+    public event Action<SKCanvas> Draw;
+    public Stretch Stretch { get; }
+    public VecD TargetSize { get; }
+
+    public DrawTextureOperation(Rect dirtyBounds, Stretch stretch, VecD targetSize, Action<SKCanvas> draw) :
+        base(dirtyBounds)
+    {
+        Draw += draw;
+        Stretch = stretch;
+        TargetSize = targetSize;
+    }
+
+    public override void Render(ISkiaSharpApiLease lease)
+    {
+        SKCanvas canvas = lease.SkCanvas;
+        
+        (DrawingBackendApi.Current.SurfaceImplementation as SkiaSurfaceImplementation).GrContext = lease.GrContext;
+
+        canvas.Save();
+        ScaleCanvas(canvas);
+        Draw?.Invoke(lease.SkCanvas);
+        canvas.Restore();
+    }
+
+    private void ScaleCanvas(SKCanvas canvas)
+    {
+        float x = (float)TargetSize.X;
+        float y = (float)TargetSize.Y;
+
+        if (Stretch == Stretch.Fill)
+        {
+            canvas.Scale((float)Bounds.Width / x, (float)Bounds.Height / y);
+        }
+        else if (Stretch == Stretch.Uniform)
+        {
+            float scaleX = (float)Bounds.Width / x;
+            float scaleY = (float)Bounds.Height / y;
+            var scale = Math.Min(scaleX, scaleY);
+            float dX = (float)Bounds.Width / 2 / scale - x / 2;
+            float dY = (float)Bounds.Height / 2 / scale - y / 2;
+            canvas.Scale(scale, scale);
+            canvas.Translate(dX, dY);
+        }
+        else if (Stretch == Stretch.UniformToFill)
+        {
+            float scaleX = (float)Bounds.Width / x;
+            float scaleY = (float)Bounds.Height / y;
+            var scale = Math.Max(scaleX, scaleY);
+            float dX = (float)Bounds.Width / 2 / scale - x / 2;
+            float dY = (float)Bounds.Height / 2 / scale - y / 2;
+            canvas.Scale(scale, scale);
+            canvas.Translate(dX, dY);
+        }
+    }
+
+    public override bool Equals(ICustomDrawOperation? other)
+    {
+        return false;
+    }
+}

+ 3 - 3
src/PixiEditor/Views/Windows/HelloTherePopup.axaml

@@ -129,8 +129,8 @@
                                                     CommandParameter="{Binding FilePath}"
                                                     CommandParameter="{Binding FilePath}"
                                                     x:Name="fileButton">
                                                     x:Name="fileButton">
                                                 <Grid Width="100" Height="100">
                                                 <Grid Width="100" Height="100">
-                                                    <visuals:SurfaceControl
-                                                        Surface="{Binding PreviewBitmap}"
+                                                    <visuals:TextureControl
+                                                        Texture="{Binding PreviewBitmap}"
                                                         Margin="10"
                                                         Margin="10"
                                                         Stretch="Uniform"
                                                         Stretch="Uniform"
                                                         x:Name="image">
                                                         x:Name="image">
@@ -141,7 +141,7 @@
                                                                 <Binding ElementName="image" Path="Width" />
                                                                 <Binding ElementName="image" Path="Width" />
                                                             </MultiBinding>
                                                             </MultiBinding>
                                                         </ui:RenderOptionsBindable.BitmapInterpolationMode>
                                                         </ui:RenderOptionsBindable.BitmapInterpolationMode>
-                                                    </visuals:SurfaceControl>
+                                                    </visuals:TextureControl>
                                                     <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder"
                                                     <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder"
                                                             Margin="5"
                                                             Margin="5"
                                                             Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}"
                                                             Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}"