From: Jean-Philippe Bruyère Date: Wed, 24 Nov 2021 22:50:38 +0000 (+0100) Subject: IBackend instead of IDevice to bind to Interface X-Git-Tag: v0.9.9-beta~5 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=b25ad7a6e15a7284e40407377a1624747ffe42e5;p=jp%2Fcrow.git IBackend instead of IDevice to bind to Interface --- diff --git a/Backends/CairoBackend/src/DefaultBackend.cs b/Backends/CairoBackend/src/DefaultBackend.cs new file mode 100644 index 00000000..9ef04b33 --- /dev/null +++ b/Backends/CairoBackend/src/DefaultBackend.cs @@ -0,0 +1,16 @@ +using System; +using Crow.CairoBackend; + +namespace Crow.Backends +{ + public class DefaultBackend : ImageBackend { + /// + /// Create a new generic backend bound to the application surface + /// + /// backend surface width + /// backend surface height + public DefaultBackend (IntPtr nativeWindoPointer, int width, int height) + : base (nativeWindoPointer, width, height) { } + } +} + diff --git a/Backends/CairoBackend/src/Device.cs b/Backends/CairoBackend/src/Device.cs index c87b494b..2efe76ec 100644 --- a/Backends/CairoBackend/src/Device.cs +++ b/Backends/CairoBackend/src/Device.cs @@ -77,18 +77,9 @@ namespace Crow.CairoBackend } public class Device : IDevice { - /// Global font rendering settings for Cairo - FontOptions FontRenderingOptions; - /// Global font rendering settings for Cairo - Antialias Antialias = Antialias.Subpixel; public Device() { - FontRenderingOptions = new FontOptions (); - FontRenderingOptions.Antialias = Antialias.Subpixel; - FontRenderingOptions.HintMetrics = HintMetrics.On; - FontRenderingOptions.HintStyle = HintStyle.Full; - FontRenderingOptions.SubpixelOrder = SubpixelOrder.Default; } ~Device () @@ -106,87 +97,12 @@ namespace Crow.CairoBackend { throw new NotImplementedException(); } - public IRegion CreateRegion () => new Region (); - public virtual ISurface CreateSurface(int width, int height) - => new ImageSurface (Format.ARGB32, width, height); - public virtual ISurface CreateSurface(byte[] data, int width, int height) - => new ImageSurface (data, Format.ARGB32, width, height, 4 * width); - - public virtual ISurface CreateSurface (IntPtr nativeWindoPointer, int width, int height) { - switch (Environment.OSVersion.Platform) { - case PlatformID.Unix: - IntPtr disp = Glfw3.GetX11Display (); - IntPtr nativeWin = Glfw3.GetX11Window (nativeWindoPointer); - Int32 scr = Glfw3.GetX11DefaultScreen (disp); - IntPtr visual = Glfw3.GetX11DefaultVisual (disp, scr); - return new XlibSurface (disp, nativeWin, visual, width, height); - case PlatformID.Win32NT: - case PlatformID.Win32S: - case PlatformID.Win32Windows: - IntPtr hWin32 = Glfw3.GetWin32Window (nativeWindoPointer); - IntPtr hdc = Glfw3.GetWin32DC (hWin32); - return new Win32Surface (hdc); - } - throw new PlatformNotSupportedException ("Unable to create cairo surface."); - } - public virtual IContext CreateContext(ISurface surf) - { - Context gr = new Context (surf); - gr.FontOptions = FontRenderingOptions; - gr.Antialias = Antialias; - return gr; - } - public byte[] LoadBitmap (Stream stream, out Size dimensions) { - byte[] image; -#if STB_SHARP - StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); - image = new byte[stbi.Data.Length]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Data.Length; i += 4) { - image[i] = stbi.Data[i + 2]; - image[i + 1] = stbi.Data[i + 1]; - image[i + 2] = stbi.Data[i]; - image[i + 3] = stbi.Data[i + 3]; - } - dimensions = new Size (stbi.Width, stbi.Height); -#else - using (StbImage stbi = new StbImage (stream)) { - image = new byte [stbi.Size]; - for (int i = 0; i < stbi.Size; i+=4) { - //rgba to argb for cairo. ???? looks like bgra to me. - image [i] = Marshal.ReadByte (stbi.Handle, i + 2); - image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); - image [i + 2] = Marshal.ReadByte (stbi.Handle, i); - image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); - } - dimensions = new Size (stbi.Width, stbi.Height); - } -#endif - return image; - } - public ISvgHandle LoadSvg(Stream stream) - { - using (BinaryReader sr = new BinaryReader (stream)) - return new SvgHandle (sr.ReadBytes ((int)stream.Length)); - } - public ISvgHandle LoadSvg(string svgFragment) => - new SvgHandle (System.Text.Encoding.Unicode.GetBytes (svgFragment)); - - public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) { - switch (gradientType) { - case GradientType.Vertical: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); - case GradientType.Horizontal: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); - case GradientType.Oblic: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); - case GradientType.Radial: - throw new NotImplementedException (); - } - return null; - } + + + + #endregion #region IDispose implementation @@ -198,8 +114,10 @@ namespace Crow.CairoBackend protected virtual void Dispose (bool disposing) { - if (disposing) - FontRenderingOptions.Dispose (); + if (disposing){ + + } + } #endregion } diff --git a/Backends/CairoBackend/src/EGLDevice.cs b/Backends/CairoBackend/src/EGLDevice.cs index 83629374..d9296b74 100644 --- a/Backends/CairoBackend/src/EGLDevice.cs +++ b/Backends/CairoBackend/src/EGLDevice.cs @@ -34,15 +34,16 @@ namespace Crow.CairoBackend { public class EGLDevice : GLDevice { - public EGLDevice (IntPtr dpy, IntPtr gl_ctx, bool threadAwayre = false) : base (NativeMethods.cairo_egl_device_create (dpy, gl_ctx), true) + public EGLDevice (IntPtr dpy, IntPtr gl_ctx, bool threadAwayre = false) + : base (NativeMethods.cairo_egl_device_create (dpy, gl_ctx), true) { SetThreadAware(threadAwayre); } - public override ISurface CreateSurface(int width, int height) + /*public override ISurface CreateSurface(int width, int height) => new ImageSurface (Format.ARGB32, width, height); public override ISurface CreateSurface (IntPtr nativeWindoPointer, int width, int height) { return new GLSurface (this, Glfw.Glfw3.GetEGLSurface (nativeWindoPointer), width, height); - } + }*/ } } diff --git a/Backends/CairoBackend/src/GLSurface.cs b/Backends/CairoBackend/src/GLSurface.cs index c7959dc3..1f486c50 100644 --- a/Backends/CairoBackend/src/GLSurface.cs +++ b/Backends/CairoBackend/src/GLSurface.cs @@ -54,7 +54,7 @@ namespace Crow.CairoBackend { {} public override void Flush () { - base.Flush (); + //base.Flush (); SwapBuffers (); } public override int Width => NativeMethods.cairo_gl_surface_get_width (handle); diff --git a/Backends/CairoBackend/src/ImageBackend.cs b/Backends/CairoBackend/src/ImageBackend.cs new file mode 100644 index 00000000..e390ca98 --- /dev/null +++ b/Backends/CairoBackend/src/ImageBackend.cs @@ -0,0 +1,179 @@ +using System; +using System.IO; +using Drawing2D; +using Glfw; + +namespace Crow.CairoBackend +{ + public class ImageBackend : IBackend { + ISurface surf; + /// Global font rendering settings for Cairo + FontOptions FontRenderingOptions; + /// Global font rendering settings for Cairo + Antialias Antialias = Antialias.Subpixel; + protected ImageBackend () + { + FontRenderingOptions = new FontOptions (); + FontRenderingOptions.Antialias = Antialias.Subpixel; + FontRenderingOptions.HintMetrics = HintMetrics.On; + FontRenderingOptions.HintStyle = HintStyle.Full; + FontRenderingOptions.SubpixelOrder = SubpixelOrder.Default; + } + /// + /// Create a new generic backend bound to the application surface + /// + /// backend surface width + /// backend surface height + public ImageBackend (IntPtr nativeWindoPointer, int width, int height) : this () { + + switch (Environment.OSVersion.Platform) { + case PlatformID.Unix: + IntPtr disp = Glfw3.GetX11Display (); + IntPtr nativeWin = Glfw3.GetX11Window (nativeWindoPointer); + Int32 scr = Glfw3.GetX11DefaultScreen (disp); + IntPtr visual = Glfw3.GetX11DefaultVisual (disp, scr); + surf = new XlibSurface (disp, nativeWin, visual, width, height); + break; + case PlatformID.Win32NT: + case PlatformID.Win32S: + case PlatformID.Win32Windows: + IntPtr hWin32 = Glfw3.GetWin32Window (nativeWindoPointer); + IntPtr hdc = Glfw3.GetWin32DC (hWin32); + surf = new Win32Surface (hdc); + break; + default: + throw new PlatformNotSupportedException ("Unable to create cairo image backend."); + } + } + /// + /// Create a new offscreen backend, used in perfTests + /// + /// backend surface width + /// backend surface height + public ImageBackend (int width, int height) : this () { + surf = new ImageSurface (Format.ARGB32, width, height); + } + ~ImageBackend () + { + Dispose (false); + } + public virtual ISurface CreateSurface(int width, int height) + => new ImageSurface (Format.ARGB32, width, height); + public virtual ISurface CreateSurface(byte[] data, int width, int height) + => new ImageSurface (data, Format.ARGB32, width, height, 4 * width); + public ISurface MainSurface => surf; + public IRegion CreateRegion () => new Region (); + public IContext CreateContext (ISurface surf) + { + Context gr = new Context (surf); + gr.FontOptions = FontRenderingOptions; + gr.Antialias = Antialias; + return gr; + } + //IPattern CreatePattern (PatternType patternType); + public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) + { + switch (gradientType) { + case GradientType.Vertical: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); + case GradientType.Horizontal: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); + case GradientType.Oblic: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + case GradientType.Radial: + throw new NotImplementedException (); + } + return null; + } + public byte[] LoadBitmap (Stream stream, out Size dimensions) + { + byte[] image; +#if STB_SHARP + StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); + image = new byte[stbi.Data.Length]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Data.Length; i += 4) { + image[i] = stbi.Data[i + 2]; + image[i + 1] = stbi.Data[i + 1]; + image[i + 2] = stbi.Data[i]; + image[i + 3] = stbi.Data[i + 3]; + } + dimensions = new Size (stbi.Width, stbi.Height); +#else + using (StbImage stbi = new StbImage (stream)) { + image = new byte [stbi.Size]; + for (int i = 0; i < stbi.Size; i+=4) { + //rgba to argb for cairo. ???? looks like bgra to me. + image [i] = Marshal.ReadByte (stbi.Handle, i + 2); + image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); + image [i + 2] = Marshal.ReadByte (stbi.Handle, i); + image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); + } + dimensions = new Size (stbi.Width, stbi.Height); + } +#endif + return image; + } + public ISvgHandle LoadSvg(Stream stream) + { + using (BinaryReader sr = new BinaryReader (stream)) + return new SvgHandle (sr.ReadBytes ((int)stream.Length)); + } + public ISvgHandle LoadSvg(string svgFragment) => + new SvgHandle (System.Text.Encoding.Unicode.GetBytes (svgFragment)); + bool disposeContextOnFlush; + IRegion clipping; + protected void clear(IContext ctx) { + for (int i = 0; i < clipping.NumRectangles; i++) + ctx.Rectangle (clipping.GetRectangle (i)); + + ctx.ClipPreserve (); + ctx.Operator = Operator.Clear; + ctx.Fill (); + ctx.Operator = Operator.Over; + } + public IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + { + this.clipping = clipping; + IContext ctx = existingContext; + if (ctx == null) { + disposeContextOnFlush = true; + ctx = new Context (MainSurface); + } else + disposeContextOnFlush = false; + + clear (ctx); + ctx.PushGroup (); + + return ctx; + } + public void FlushUIFrame(IContext ctx) + { + ctx.PopGroupToSource (); + ctx.Paint (); + + if (disposeContextOnFlush) + ctx.Dispose (); + clipping = null; + } + + + #region IDispose implementation + bool isDisposed; + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + protected virtual void Dispose (bool disposing) + { + if (!isDisposed && disposing) { + surf.Dispose (); + FontRenderingOptions.Dispose (); + } + isDisposed = true; + } + #endregion + } +} + diff --git a/Crow/src/Fill/BmpPicture.cs b/Crow/src/Fill/BmpPicture.cs index 72cc36b8..813e8dec 100644 --- a/Crow/src/Fill/BmpPicture.cs +++ b/Crow/src/Fill/BmpPicture.cs @@ -40,7 +40,7 @@ namespace Crow return; } using (Stream stream = iFace.GetStreamFromPath (Path)) { - image = iFace.Device.LoadBitmap (stream, out Size dimensions); + image = iFace.Backend.LoadBitmap (stream, out Size dimensions); Dimensions = dimensions; iFace.sharedPictures[Path] = new sharedPicture (image, Dimensions); } @@ -89,13 +89,13 @@ namespace Crow widthRatio = heightRatio; } - using (ISurface tmp = iFace.Device.CreateSurface (bounds.Width, bounds.Height)) { - using (IContext gr = iFace.Device.CreateContext (tmp)) { + using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { + using (IContext gr = iFace.Backend.CreateContext (tmp)) { gr.Translate (bounds.Left, bounds.Top); gr.Scale (widthRatio, heightRatio); gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); - using (ISurface imgSurf = iFace.Device.CreateSurface (bounds.Width, bounds.Height)) { + using (ISurface imgSurf = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { gr.SetSource (imgSurf, 0,0); gr.Paint (); } @@ -138,7 +138,7 @@ namespace Crow gr.Scale (widthRatio, heightRatio); gr.Translate ((rect.Width/widthRatio - Dimensions.Width)/2, (rect.Height/heightRatio - Dimensions.Height)/2); - using (ISurface imgSurf = iFace.Device.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { + using (ISurface imgSurf = iFace.Backend.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { gr.SetSource (imgSurf, 0,0); gr.Paint (); } diff --git a/Crow/src/Fill/Gradient.cs b/Crow/src/Fill/Gradient.cs index d161eba8..abf841e3 100644 --- a/Crow/src/Fill/Gradient.cs +++ b/Crow/src/Fill/Gradient.cs @@ -54,7 +54,7 @@ namespace Crow public override void SetAsSource (Interface iFace, IContext ctx, Rectangle bounds = default(Rectangle)) { - IGradient grad = iFace.Device.CreateGradient (Type, bounds); + IGradient grad = iFace.Backend.CreateGradient (Type, bounds); foreach (ColorStop cs in Stops) { if (cs == null) continue; diff --git a/Crow/src/Fill/SvgPicture.cs b/Crow/src/Fill/SvgPicture.cs index d562b342..e6e54aff 100644 --- a/Crow/src/Fill/SvgPicture.cs +++ b/Crow/src/Fill/SvgPicture.cs @@ -38,13 +38,13 @@ namespace Crow return; } using (Stream stream = iFace.GetStreamFromPath (Path)) - hSVG = iFace.Device.LoadSvg (stream); + hSVG = iFace.Backend.LoadSvg (stream); Dimensions = hSVG.Dimensions; iFace.sharedPictures [Path] = new sharedPicture (hSVG, Dimensions); } public void LoadSvgFragment (Interface iface, string fragment) { - hSVG = iface.Device.LoadSvg (fragment); + hSVG = iface.Backend.LoadSvg (fragment); Dimensions = hSVG.Dimensions; } @@ -70,8 +70,8 @@ namespace Crow widthRatio = heightRatio; } - using (ISurface tmp = iFace.Device.CreateSurface (bounds.Width, bounds.Height)) { - using (IContext gr = iFace.Device.CreateContext (tmp)) { + using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { + using (IContext gr = iFace.Backend.CreateContext (tmp)) { gr.Translate (bounds.Left, bounds.Top); gr.Scale (widthRatio, heightRatio); gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index e0c00ede..19d12f3f 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -50,6 +50,54 @@ namespace Crow } #endregion + #region Static and constants + //initial capacity for layouting and clipping queues. + const int INIT_QUEUE_CAPACITY = 512; + /// Time interval in milisecond between Updates of the interface + public static int UPDATE_INTERVAL = 5; + /// + /// Time interval in milisecond between Glfw polling for devices. Wait is done in + /// the 'UpdateFrame' method in the 'Run' cycle and may be overriden. + /// + public static int POLLING_INTERVAL = 1; + /// Crow configuration root path + public static string CROW_CONFIG_ROOT; + /// If true, mouse focus is given when mouse is over control + public static bool FOCUS_ON_HOVER = false; + /// If true, newly focused window will be put on top + public static bool RAISE_WIN_ON_FOCUS = true; + /// Threshold to catch borders for sizing + public static int BorderThreshold = 3; + /// delay before tooltip appears + public static int TOOLTIP_DELAY = 500; + /// Double click threshold in milisecond + public static int DOUBLECLICK_TRESHOLD = 320;//max duration between two mouse_down evt for a dbl clk in milisec. + /// Time to wait in millisecond before starting repeat loop + public static int DEVICE_REPEAT_DELAY = 600; + /// Time interval in millisecond between device event repeat + public static int DEVICE_REPEAT_INTERVAL = 100; + public static float WheelIncrement = 1; + /// Tabulation size in Text controls + public static int TAB_SIZE = 4; + [Obsolete]public static string LineBreak = "\n"; + /// Allow rendering of interface in development environment + public static bool DesignerMode = false; + /// Disable caching for a widget if this threshold is reached + public const int MaxCacheSize = 2048; + /// Above this count, the layouting is discard from the current + /// update cycle and requeued for the next + public static int MaxLayoutingTries = 30; + /// Above this count, the layouting is discard for the widget and it + /// will not be rendered on screen + public static int MaxDiscardCount = 5; + + /// + /// Each control need a ref to the root interface containing it, if not set in Widget.currentInterface, + /// the ref of this one will be stored in Widget.currentInterface + /// + //protected static Interface CurrentInterface; + #endregion + internal static List crowAssemblies = new List (); /// /// Add Assembly that may contains CROW ui ressources like custom widget classes, IML, images, ... @@ -78,8 +126,6 @@ namespace Crow init_internal (); } } - static void nativeHelpMessage () { - } static IntPtr resolveUnmanaged(Assembly assembly, String libraryName) { try { @@ -171,33 +217,31 @@ namespace Crow /// the height of the window /// A valid GLFW window handle /// - public Interface (int width, int height, IntPtr glfwWindowHandle) : this (width, height, false, false) + public Interface (int width, int height, IntPtr glfwWindowHandle) : this (width, height, true, false) { hWin = glfwWindowHandle; PerformanceMeasure.InitMeasures (); } - IDevice dev; - public IDevice Device => dev; - /// /// Create a standard Crow interface. /// /// the width of the native window /// the height of the native window - /// If 'yes' start the ui update (InterfaceThread method) in a dedicated thread + /// If 'false' start the ui update (InterfaceThread method) in a dedicated thread /// If 'yes', create the main rendering surface on the native window - public Interface (int width = 800, int height = 600, bool startUIThread = true, bool createSurface = true) + public Interface (int width = 800, int height = 600, bool singleThreaded = false, bool createSurface = true) { CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; //CurrentInterface = this; clientRectangle = new Rectangle (0, 0, width, height); + SingleThreaded = singleThreaded; if (createSurface) initSurface (); PerformanceMeasure.InitMeasures (); - if (startUIThread) { + if (!SingleThreaded) { Thread t = new Thread (InterfaceThread) { IsBackground = true }; @@ -206,6 +250,57 @@ namespace Crow } #endregion + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + disposeContextMenus (); + Backend.Dispose (); + } + + currentCursor?.Dispose (); + + if (ownWindow) { + Glfw3.DestroyWindow (hWin); + Glfw3.Terminate (); + } + + disposedValue = true; + } + } + ~Interface() { + Dispose(false); + } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + + /// + /// Cache already searched extension methods to prevent searching again and again for + /// missing method or to speedup loading once a method is found. + /// + /// + /// This cache is reseted when a crow assembly is removed, or the theme is changed. + /// + protected Dictionary knownExtMethods; + /// + /// Cache already searched widget types. + /// + /// + /// This cache is reseted when a crow assembly is removed, or the theme is changed. + /// + protected Dictionary knownCrowWidgetTypes; + /// Client rectangle in the host context + protected Rectangle clientRectangle; #if MEASURE_TIME public PerformanceMeasure[] PerfMeasures => PerformanceMeasure.Measures; #endif @@ -214,12 +309,77 @@ namespace Crow static Dictionary windows = new Dictionary (); /** GLFW window native pointer and current native handle for mouse cursor */ IntPtr hWin; + protected IBackend backend;//backend device + /// Clipping rectangles on the root context + protected IRegion clipping; + static string backendDeviceTypeString = "Crow.CairoBackend.Device"; Cursor currentCursor; bool ownWindow; /// + /// If `true`, UI updates will be handle in the `Run()` method, so in the main thread of the application along with GLFW events polling. + /// If `false`, A dedicated thread will be started for the UI updates. + /// + public readonly bool SingleThreaded; + /// /// Native GLFW window handle bound to this interface. /// public IntPtr WindowHandle => hWin; + public IBackend Backend => backend; + /// Main backend surface + public ISurface MainSurface => backend.MainSurface; + public IntPtr SurfacePointer { + get { + lock(UpdateMutex) + return MainSurface.Handle; + } + } + + #region Public Fields + /// Graphic Tree of this interface + public List GraphicTree = new List(); + /// Interface's resulting bitmap + public byte[] bmp; + /// resulting bitmap limited to last redrawn part + public byte[] dirtyBmp; + /// True when host has to repaint Interface + public bool IsDirty; + /// Coordinate of the dirty bmp on the original bmp + public Rectangle DirtyRect; + /// Locked for each layouting operation + public object LayoutMutex = new object(); + /// Sync mutex between host and Crow for rendering operations (bmp, dirtyBmp,...) + public object RenderMutex = new object(); + /// Global lock of the update cycle + public object UpdateMutex = new object(); + /// Global lock of the clipping queue + public object ClippingMutex = new object(); + //TODO:share resource instances + /// + /// Store loaded resources instances shared among controls to reduce memory footprint + /// + public Dictionary Ressources = new Dictionary(); + /// The Main layouting queue. + public Queue LayoutingQueue = new Queue (INIT_QUEUE_CAPACITY); + /// Store discarded lqi between two updates + public Queue DiscardQueue; + /// Main drawing queue, holding layouted controls + public Queue ClippingQueue = new Queue(INIT_QUEUE_CAPACITY); + //TODO:use object instead for complex copy paste + public string Clipboard { + get => Glfw3.GetClipboardString (hWin); + set => Glfw3.SetClipboardString (hWin, value); + } + /// each IML and fragments (such as inline Templates) are compiled as a Dynamic Method stored here + /// on the first instance creation of a IML item. + /// + public Dictionary Instantiators; + /// + /// Item templates stored with their index + /// + public Dictionary ItemTemplates; + + public List CrowThreads = new List();//used to monitor thread finished + #endregion /// /// Register GLFW window callbacs (mouse, keyboard, sizing, refresh). @@ -240,19 +400,10 @@ namespace Crow #endif Glfw3.SetWindowRefreshCallback (hWin, HandleWindowRefreshDelegate); } -#if VKVG - protected VulkanContextBase vkCtx; - internal Device vkvgDevice => vkCtx.VkvgDevice; - /// - /// Create the VulkanContext with swapchain support. Override it to use another vulkan backend. - /// -#endif - static string backendDeviceTypeString = "Crow.CairoBackend.Device"; - protected void initBackend (bool offscreen = false) { - - dev = new Crow.CairoBackend.Device (); - clipping = dev.CreateRegion (); + protected virtual void initBackend () { + backend = new Crow.CairoBackend.ImageBackend (hWin, clientRectangle.Width, clientRectangle.Height); + clipping = Backend.CreateRegion (); } /// /// Create the main rendering surface. The default is a GLFW window with a cairo surface bound to it. @@ -261,88 +412,23 @@ namespace Crow { Glfw3.Init (); - Glfw3.WindowHint (WindowAttribute.ClientApi, 0); + Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi); + Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 3); + //Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 0); + Glfw3.WindowHint (WindowAttribute.ContextCreationApi, Constants.EglContextApi); + Glfw3.WindowHint (WindowAttribute.Resizable, 1); Glfw3.WindowHint (WindowAttribute.Decorated, 1); hWin = Glfw3.CreateWindow (clientRectangle.Width, clientRectangle.Height, "win name", MonitorHandle.Zero, IntPtr.Zero); if (hWin == IntPtr.Zero) - throw new Exception ("[GLFW3] Unable to create vulkan Window"); + throw new Exception ("[GLFW3] Unable to create Window"); ownWindow = true; registerGlfwCallbacks (); initBackend (); - - CreateMainSurface (ref clientRectangle); - } - /// - /// ?? - /// - /// - public void CreateMainSurface (ref Rectangle r) { - surf?.Dispose(); - surf = Device.CreateSurface (hWin, r.Width, r.Height); - } - public IntPtr SurfacePointer { - get { - lock(UpdateMutex) - return surf.Handle; - } - } - /// - /// Set the main GLFW window icon. - /// - /// - public void SetWindowIcon (string path) { - using (Stream stream = GetStreamFromPath (path)) { -#if STB_SHARP - StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); - byte[] image = new byte[stbi.Data.Length]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Data.Length; i += 4) { - image[i] = stbi.Data[i + 2]; - image[i + 1] = stbi.Data[i + 1]; - image[i + 2] = stbi.Data[i]; - image[i + 3] = stbi.Data[i + 3]; - } - Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); - Glfw3.SetWindowIcon (hWin, 1, ref icon); - icon.Dispose(); - -#else - using (StbImage stbi = new StbImage (stream)) { - byte[] image = new byte [stbi.Size]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Size; i+=4) { - image [i] = Marshal.ReadByte (stbi.Handle, i + 2); - image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); - image [i + 2] = Marshal.ReadByte (stbi.Handle, i); - image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); - } - Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); - Glfw3.SetWindowIcon (hWin, 1, ref icon); - icon.Dispose(); - } -#endif - } } - /// - /// Cache already searched extension methods to prevent searching again and again for - /// missing method or to speedup loading once a method is found. - /// - /// - /// This cache is reseted when a crow assembly is removed, or the theme is changed. - /// - protected Dictionary knownExtMethods; - /// - /// Cache already searched widget types. - /// - /// - /// This cache is reseted when a crow assembly is removed, or the theme is changed. - /// - protected Dictionary knownCrowWidgetTypes; - /// /// search for graphic object type in crow assembly, if not found, /// search for type independently of namespace in all the loaded assemblies @@ -403,17 +489,34 @@ namespace Crow return mi; } - #region events delegates + #region Events + //public event EventHandler MouseCursorChanged; + ////public event EventHandler Quit; + public event EventHandler Initialized; + public event EventHandler StartDragOperation; + public event EventHandler EndDragOperation; + public event EventHandler KeyDown; + public event EventHandler KeyUp; + + //public event EventHandler MouseWheelChanged; + //public event EventHandler MouseButtonUp; + //public event EventHandler MouseButtonDown; + //public event EventHandler MouseClick; + //public event EventHandler MouseMove; + //public event EventHandler KeyDown; + //public event EventHandler KeyPress; + /*public event EventHandler KeyboardKeyDown; + public event EventHandler KeyboardKeyUp;*/ + #region events delegates static CursorPosDelegate HandleCursorPosDelegate = (window, xPosition, yPosition) => { - windows [window].OnMouseMove ((int)(xPosition / windows [window].ZoomFactor), (int)(yPosition / windows [window].ZoomFactor)); + windows [window].OnMouseMove ((int)(xPosition), (int)(yPosition)); }; static MouseButtonDelegate HandleMouseButtonDelegate = (IntPtr window, MouseButton button, InputAction action, Modifier mods) => { if (action == InputAction.Release) windows [window].OnMouseButtonUp (button); else//press and repeat windows [window].OnMouseButtonDown (button); - }; static ScrollDelegate HandleScrollDelegate = (IntPtr window, double xOffset, double yOffset) => { windows [window].OnMouseWheelChanged ((int)yOffset); @@ -438,24 +541,53 @@ namespace Crow static WindowDelegate HandleWindowRefreshDelegate = (IntPtr window) => { windows [window].registerRefreshClientRectangle (); }; - + #endregion #endregion public string WindowTitle { set => Glfw3.SetWindowTitle (hWin, value); } + /// + /// Set the main GLFW window icon. + /// + /// + public void SetWindowIcon (string path) { + using (Stream stream = GetStreamFromPath (path)) { +#if STB_SHARP + StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); + byte[] image = new byte[stbi.Data.Length]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Data.Length; i += 4) { + image[i] = stbi.Data[i + 2]; + image[i + 1] = stbi.Data[i + 1]; + image[i + 2] = stbi.Data[i]; + image[i + 3] = stbi.Data[i + 3]; + } + Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); + Glfw3.SetWindowIcon (hWin, 1, ref icon); + icon.Dispose(); +#else + using (StbImage stbi = new StbImage (stream)) { + byte[] image = new byte [stbi.Size]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Size; i+=4) { + image [i] = Marshal.ReadByte (stbi.Handle, i + 2); + image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); + image [i + 2] = Marshal.ReadByte (stbi.Handle, i); + image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); + } + Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); + Glfw3.SetWindowIcon (hWin, 1, ref icon); + icon.Dispose(); + } +#endif + } + } public bool Running { get => !Glfw3.WindowShouldClose (hWin); set => Glfw3.SetWindowShouldClose (hWin, value == true ? 0 : 1); } - public virtual void InterfaceThread () - { - while (!Glfw3.WindowShouldClose (hWin)) { - Update (); - Thread.Sleep (UPDATE_INTERVAL); - } - } protected virtual void OnInitialized () { /*try { @@ -484,29 +616,38 @@ namespace Crow loadThemeFiles (); initContextMenus (); } + public virtual void InterfaceThread () + { + while (!Glfw3.WindowShouldClose (hWin)) { + Update (); + Thread.Sleep (UPDATE_INTERVAL); + } + } /// /// call Init() then enter the running loop performing ProcessEvents until running==false. /// public virtual void Run () { Init (); - while (!Glfw3.WindowShouldClose (hWin)) { - Glfw3.PollEvents (); - UpdateFrame (); + if (SingleThreaded) { + while (!Glfw3.WindowShouldClose (WindowHandle)) { + Glfw3.PollEvents (); + Update(); + UpdateFrame (); + Thread.Sleep (UPDATE_INTERVAL); + } + } else { + while (!Glfw3.WindowShouldClose (hWin)) { + Glfw3.PollEvents (); + UpdateFrame (); + Thread.Sleep (POLLING_INTERVAL); + } } Terminate (); } public virtual void Terminate () {} - public virtual void UpdateFrame () { -#if VKVG - Update (); - Thread.Sleep (UPDATE_INTERVAL); -#else - Thread.Sleep (POLLING_INTERVAL); -#endif - } - + public virtual void UpdateFrame () {} public virtual void Quit () => Glfw3.SetWindowShouldClose (hWin, 1); public bool Shift => Glfw3.GetKey(hWin, Key.LeftShift) == InputAction.Press || @@ -532,164 +673,18 @@ namespace Crow } #endif - #region IDisposable Support - private bool disposedValue = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - disposeContextMenus (); - dev.Dispose (); - } - - currentCursor?.Dispose (); - - if (ownWindow) { - Glfw3.DestroyWindow (hWin); - Glfw3.Terminate (); - } - - disposedValue = true; - } - } - ~Interface() { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - #endregion - - #region Static and constants - //initial capacity for layouting and clipping queues. - const int INIT_QUEUE_CAPACITY = 512; - /// Time interval in milisecond between Updates of the interface - public static int UPDATE_INTERVAL = 5; - /// - /// Time interval in milisecond between Glfw polling for devices. Wait is done in - /// the 'UpdateFrame' method in the 'Run' cycle and may be overriden. - /// - public static int POLLING_INTERVAL = 1; - /// Crow configuration root path - public static string CROW_CONFIG_ROOT; - /// If true, mouse focus is given when mouse is over control - public static bool FOCUS_ON_HOVER = false; - /// If true, newly focused window will be put on top - public static bool RAISE_WIN_ON_FOCUS = true; - /// Threshold to catch borders for sizing - public static int BorderThreshold = 3; - /// delay before tooltip appears - public static int TOOLTIP_DELAY = 500; - /// Double click threshold in milisecond - public static int DOUBLECLICK_TRESHOLD = 320;//max duration between two mouse_down evt for a dbl clk in milisec. - /// Time to wait in millisecond before starting repeat loop - public static int DEVICE_REPEAT_DELAY = 600; - /// Time interval in millisecond between device event repeat - public static int DEVICE_REPEAT_INTERVAL = 100; - public static float WheelIncrement = 1; - /// Tabulation size in Text controls - public static int TAB_SIZE = 4; - [Obsolete]public static string LineBreak = "\n"; - /// Allow rendering of interface in development environment - public static bool DesignerMode = false; - /// Disable caching for a widget if this threshold is reached - public const int MaxCacheSize = 2048; - /// Above this count, the layouting is discard from the current - /// update cycle and requeued for the next - public static int MaxLayoutingTries = 30; - /// Above this count, the layouting is discard for the widget and it - /// will not be rendered on screen - public static int MaxDiscardCount = 5; - - /// - /// Each control need a ref to the root interface containing it, if not set in Widget.currentInterface, - /// the ref of this one will be stored in Widget.currentInterface - /// - //protected static Interface CurrentInterface; - #endregion - #region Events - //public event EventHandler MouseCursorChanged; - ////public event EventHandler Quit; - public event EventHandler Initialized; - public event EventHandler StartDragOperation; - public event EventHandler EndDragOperation; - public event EventHandler KeyDown; - public event EventHandler KeyUp; - //public event EventHandler MouseWheelChanged; - //public event EventHandler MouseButtonUp; - //public event EventHandler MouseButtonDown; - //public event EventHandler MouseClick; - //public event EventHandler MouseMove; - //public event EventHandler KeyDown; - //public event EventHandler KeyPress; - /*public event EventHandler KeyboardKeyDown; - public event EventHandler KeyboardKeyUp;*/ - #endregion - /// Main backend surface - public ISurface surf; - #region Public Fields - /// Graphic Tree of this interface - public List GraphicTree = new List(); - /// Interface's resulting bitmap - public byte[] bmp; - /// resulting bitmap limited to last redrawn part - public byte[] dirtyBmp; - /// True when host has to repaint Interface - public bool IsDirty; - /// Coordinate of the dirty bmp on the original bmp - public Rectangle DirtyRect; - /// Locked for each layouting operation - public object LayoutMutex = new object(); - /// Sync mutex between host and Crow for rendering operations (bmp, dirtyBmp,...) - public object RenderMutex = new object(); - /// Global lock of the update cycle - public object UpdateMutex = new object(); - /// Global lock of the clipping queue - public object ClippingMutex = new object(); - //TODO:share resource instances - /// - /// Store loaded resources instances shared among controls to reduce memory footprint - /// - public Dictionary Ressources = new Dictionary(); - /// The Main layouting queue. - public Queue LayoutingQueue = new Queue (INIT_QUEUE_CAPACITY); - /// Store discarded lqi between two updates - public Queue DiscardQueue; - /// Main drawing queue, holding layouted controls - public Queue ClippingQueue = new Queue(INIT_QUEUE_CAPACITY); - //TODO:use object instead for complex copy paste - public string Clipboard { - get => Glfw3.GetClipboardString (hWin); - set => Glfw3.SetClipboardString (hWin, value); - } - /// each IML and fragments (such as inline Templates) are compiled as a Dynamic Method stored here - /// on the first instance creation of a IML item. - /// - public Dictionary Instantiators; - /// - /// Item templates stored with their index - /// - public Dictionary ItemTemplates; - public List CrowThreads = new List();//used to monitor thread finished + #region DragAndDrop public bool DragAndDropInProgress => DragAndDropOperation != null; public Widget DropTarget => DragAndDropOperation?.DropTarget; - public DragDropEventArgs DragAndDropOperation = null; internal Widget dragndropHover; - public ISurface DragImage = null; public Rectangle DragImageBounds, lastDragImageBounds; public bool DragImageFolowMouse;//prevent dragImg to be moved by mouse @@ -715,15 +710,6 @@ namespace Crow } #endregion - #region Private Fields - /// Client rectangle in the host context - protected Rectangle clientRectangle; - /// Clipping rectangles on the root context - IRegion clipping; - /// Main Cairo context - //Context ctx; - #endregion - #region Default values and Style loading /// Default values of properties from Widgets are retrieve from XML Attributes. /// The reflexion process used to retrieve those values being very slow, it is compiled in MSIL @@ -816,10 +802,10 @@ namespace Crow string resId = $"#{pic.Substring (path.Length + 1).Replace (Path.DirectorySeparatorChar, '.')}"; using (Stream s = new FileStream (pic, FileMode.Open, FileAccess.Read)) { if (resId.EndsWith (".svg", StringComparison.OrdinalIgnoreCase)) { - ISvgHandle hSVG = Device.LoadSvg (s); + ISvgHandle hSVG = Backend.LoadSvg (s); sharedPictures[resId] = new sharedPicture (hSVG, hSVG.Dimensions); } else { - byte[] image = Device.LoadBitmap (s, out Size dimensions); + byte[] image = Backend.LoadBitmap (s, out Size dimensions); sharedPictures[resId] = new sharedPicture (image, dimensions); } } @@ -1151,11 +1137,9 @@ namespace Crow } if (!clipping.IsEmpty || shouldDrawTextCursor) { - if (ctx == null) { - using (ctx = Device.CreateContext (surf)) - processDrawing (ctx); - }else - processDrawing (ctx); + ctx = Backend.PrepareUIFrame (ctx, clipping); + processDrawing (ctx); + Backend.FlushUIFrame (ctx); } } finally { @@ -1217,40 +1201,7 @@ namespace Crow PerformanceMeasure.End (PerformanceMeasure.Kind.Clipping); DbgLogger.EndEvent (DbgEvtType.ClippingRegistration, true); } - void clear(IContext ctx) { - for (int i = 0; i < clipping.NumRectangles; i++) - ctx.Rectangle (clipping.GetRectangle (i)); - ctx.ClipPreserve (); - ctx.Operator = Operator.Clear; - ctx.Fill (); - ctx.Operator = Operator.Over; - } - bool solidBackground = false; - public bool SolidBackground { - get => solidBackground; - set { - if (Environment.OSVersion.Platform == PlatformID.Unix) - solidBackground = value; - else - Debug.WriteLine ("SolidBackground property only available on unix."); - } - } - double zoomFactor = 1.0; - public double ZoomFactor { - get => zoomFactor; - set { - if (zoomFactor == value) - return; - zoomFactor = value; - NotifyValueChanged (zoomFactor); - lock (UpdateMutex) { - foreach (Widget g in GraphicTree) - g.RegisterForLayouting (LayoutingType.All); - registerRefreshClientRectangle (); - } - } - } /// Clipping Rectangles drive the drawing process. For compositing, each object under a clip rectangle should be /// repainted. If it contains also clip rectangles, its cache will be update, or if not cached a full redraw will take place @@ -1258,18 +1209,8 @@ namespace Crow DbgLogger.StartEvent (DbgEvtType.ProcessDrawing); PerformanceMeasure.Begin (PerformanceMeasure.Kind.Drawing); - if (!clipping.IsEmpty) { - ctx.Scale (zoomFactor,zoomFactor); - -#if VKVG - clear (ctx); -#else - ctx.PushGroup (); - - if (SolidBackground) - clear (ctx); -#endif + if (!clipping.IsEmpty) { for (int i = GraphicTree.Count -1; i >= 0 ; i--){ Widget p = GraphicTree[i]; if (!p.IsVisible) @@ -1282,7 +1223,6 @@ namespace Crow ctx.Restore (); } - if (lastDragImageBounds != DragImageBounds) { DirtyRect += lastDragImageBounds; ctx.Save (); @@ -1304,28 +1244,13 @@ namespace Crow #endif -#if VKVG - ctx.Flush(); -#else - ctx.PopGroupToSource (); - - if (!SolidBackground) - clear (ctx); - - ctx.Paint (); - - surf.Flush (); -#endif - clipping.Reset (); PerformanceMeasure.End (PerformanceMeasure.Kind.Drawing); IsDirty = true; } -#if !VKVG drawTextCursor (ctx); -#endif debugHighlightFocus (ctx); @@ -1354,7 +1279,7 @@ namespace Crow /*if (DragAndDropInProgress) { }*/ - surf.Flush (); + MainSurface.Flush (); } #region Blinking text cursor @@ -1372,7 +1297,7 @@ namespace Crow if (textCursor != null && c != textCursor.Value) RegisterClip (textCursor.Value); textCursor = c; - surf.Flush (); + MainSurface.Flush (); } else if (textCursor != null) RegisterClip (textCursor.Value); } @@ -1386,7 +1311,7 @@ namespace Crow if (blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY) { if (lab.DrawCursor (ctx, out Rectangle c)) { textCursor = c; - surf.Flush (); + MainSurface.Flush (); blinkingCursor.Restart (); } } @@ -1506,7 +1431,7 @@ namespace Crow public virtual void ProcessResize(Rectangle bounds){ lock (UpdateMutex) { clientRectangle = bounds; - surf.Resize (clientRectangle.Width, clientRectangle.Height); + MainSurface.Resize (clientRectangle.Width, clientRectangle.Height); foreach (Widget g in GraphicTree) g.RegisterForLayouting (LayoutingType.All); @@ -1514,7 +1439,7 @@ namespace Crow } } - internal void registerRefreshClientRectangle () => RegisterClip (clientRectangle); + protected void registerRefreshClientRectangle () => RegisterClip (clientRectangle); #region Mouse and Keyboard Handling MouseCursor cursor = MouseCursor.top_left_arrow; @@ -1646,7 +1571,7 @@ namespace Crow /// Ask OS to force the mouse position to the actual coordinate of Interface.MousePosition /// public virtual void ForceMousePosition () { - Glfw3.SetCursorPosition (hWin, MousePosition.X * ZoomFactor, MousePosition.Y * ZoomFactor); + Glfw3.SetCursorPosition (hWin, MousePosition.X, MousePosition.Y); } /// Processes mouse move events from the root container, this function @@ -2123,7 +2048,7 @@ namespace Crow set { throw new NotImplementedException (); } } - public Rectangle ClientRectangle => clientRectangle.Scaled (1.0/zoomFactor); + public Rectangle ClientRectangle => clientRectangle; public Rectangle GetClientRectangleForChild (ILayoutable child) => ClientRectangle; public Interface HostContainer => this; diff --git a/Crow/src/Widgets/DockWindow.cs b/Crow/src/Widgets/DockWindow.cs index 6c6c49f9..620d31f3 100644 --- a/Crow/src/Widgets/DockWindow.cs +++ b/Crow/src/Widgets/DockWindow.cs @@ -200,8 +200,8 @@ namespace Crow r.Inflate (r.Width / -3, r.Height / -3); break; } - ISurface dragImg = IFace.Device.CreateSurface (r.Width, r.Height); - using (IContext gr = IFace.Device.CreateContext (dragImg)) { + ISurface dragImg = IFace.Backend.CreateSurface (r.Width, r.Height); + using (IContext gr = IFace.Backend.CreateContext (dragImg)) { gr.LineWidth = 1; gr.Rectangle (0,0,r.Width,r.Height); gr.SetSource (0.2,0.3,0.9,0.5); diff --git a/Crow/src/Widgets/GroupBase.cs b/Crow/src/Widgets/GroupBase.cs index 1f62eb90..e99b176e 100644 --- a/Crow/src/Widgets/GroupBase.cs +++ b/Crow/src/Widgets/GroupBase.cs @@ -251,7 +251,7 @@ namespace Crow { DbgLogger.StartEvent(DbgEvtType.GOUpdateCache, this); if (!Clipping.IsEmpty) { - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { for (int i = 0; i < Clipping.NumRectangles; i++) gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index 5ef09523..330cbe55 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -514,7 +514,7 @@ namespace Crow int hoverLine = _multiline ? (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))), lines.Count - 1) : 0; hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X); - using (IContext gr = IFace.Device.CreateContext (IFace.surf)) { + using (IContext gr = IFace.Backend.CreateContext (IFace.MainSurface)) { gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); gr.SetFontSize (Font.Size); updateLocation (gr, ClientRectangle.Width, ref hoverLoc); @@ -650,7 +650,7 @@ namespace Crow getLines (); if (!textMeasureIsUpToDate) { - using (IContext gr = IFace.Device.CreateContext (IFace.surf)) { + using (IContext gr = IFace.Backend.CreateContext (IFace.MainSurface)) { gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); gr.SetFontSize (Font.Size); measureTextBounds (gr); diff --git a/Crow/src/Widgets/PrivateContainer.cs b/Crow/src/Widgets/PrivateContainer.cs index 3b705ea5..f5345523 100644 --- a/Crow/src/Widgets/PrivateContainer.cs +++ b/Crow/src/Widgets/PrivateContainer.cs @@ -215,7 +215,7 @@ namespace Crow Rectangle rb = Slot + Parent.ClientRectangle.Position; if (!Clipping.IsEmpty) { - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { for (int i = 0; i < Clipping.NumRectangles; i++) gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); diff --git a/Crow/src/Widgets/ScrollingStack.cs b/Crow/src/Widgets/ScrollingStack.cs index c67bf7d1..55535ded 100644 --- a/Crow/src/Widgets/ScrollingStack.cs +++ b/Crow/src/Widgets/ScrollingStack.cs @@ -136,7 +136,7 @@ namespace Crow { { DbgLogger.StartEvent(DbgEvtType.GOUpdateCache, this); if (!Clipping.IsEmpty) { - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { for (int i = 0; i < Clipping.NumRectangles; i++) gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); diff --git a/Crow/src/Widgets/Shape.cs b/Crow/src/Widgets/Shape.cs index efa8b45b..8322543e 100644 --- a/Crow/src/Widgets/Shape.cs +++ b/Crow/src/Widgets/Shape.cs @@ -242,7 +242,7 @@ namespace Crow if (size != default (Size)) contentSize = size; else { - using (IContext ctx = IFace.Device.CreateContext (IFace.surf)) { + using (IContext ctx = IFace.Backend.CreateContext (IFace.MainSurface)) { using (PathParser parser = new PathParser (path)) parser.Draw (ctx, true); Rectangle r = ctx.StrokeExtents (); diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 57b65aab..5d3e4c45 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -1168,7 +1168,7 @@ namespace Crow { DbgLogger.StartEvent (DbgEvtType.GOInitialization, this); - Clipping = IFace.Device.CreateRegion (); + Clipping = IFace.Backend.CreateRegion (); Type thisType = this.GetType (); if (!string.IsNullOrEmpty (style)) { @@ -1954,12 +1954,13 @@ namespace Crow else if (LastPaintedSlot.Width != Slot.Width || LastPaintedSlot.Height != Slot.Height) bmp.SetSize (Slot.Width, Slot.Height);*/ bmp?.Dispose (); - bmp = IFace.Device.CreateSurface (Slot.Width, Slot.Height); + //bmp = IFace.Device.CreateSurface (Slot.Width, Slot.Height); + bmp = IFace.Backend.CreateSurface (Slot.Width, Slot.Height); //bmp = new ImageSurface(Format.Argb32, Slot.Width, Slot.Height); DbgLogger.StartEvent (DbgEvtType.GOCreateContext, this); - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { DbgLogger.EndEvent (DbgEvtType.GOCreateContext); onDraw (gr); } diff --git a/Drawing2D/src/Enums.cs b/Drawing2D/src/Enums.cs index 12a6f035..bf20a432 100644 --- a/Drawing2D/src/Enums.cs +++ b/Drawing2D/src/Enums.cs @@ -6,6 +6,12 @@ using System; namespace Drawing2D { + public enum BackendType { + Native, + OpenGL, + EGL, + Vulkan + } public enum Status { Success = 0, diff --git a/Drawing2D/src/IBackend.cs b/Drawing2D/src/IBackend.cs new file mode 100644 index 00000000..811b95d5 --- /dev/null +++ b/Drawing2D/src/IBackend.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2018-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.IO; + +namespace Drawing2D +{ + public interface IBackend: IDisposable + { + ISurface CreateSurface (int width, int height); + ISurface CreateSurface (byte[] data, int width, int height); + //ISurface CreateMainSurface (IntPtr glfwWinHandle, int width, int height); + ISurface MainSurface { get; } + IRegion CreateRegion (); + IContext CreateContext (ISurface surf); + //IPattern CreatePattern (PatternType patternType); + IGradient CreateGradient (GradientType gradientType, Rectangle bounds); + byte[] LoadBitmap (Stream stream, out Size dimensions); + ISvgHandle LoadSvg (Stream stream); + ISvgHandle LoadSvg (string svgFragment); + IContext PrepareUIFrame (IContext existingContext, IRegion clipping); + void FlushUIFrame (IContext ctx); + /*IRegion CreateRegion (); + ISurface CreateSurface (int width, int height); + ISurface CreateSurface (byte[] data, int width, int height); + ISurface CreateSurface (IntPtr glfwWinHandle, int width, int height); + IContext CreateContext (ISurface surf); + //IPattern CreatePattern (PatternType patternType); + IGradient CreateGradient (GradientType gradientType, Rectangle bounds); + byte[] LoadBitmap (Stream stream, out Size dimensions); + ISvgHandle LoadSvg (Stream stream); + ISvgHandle LoadSvg (string svgFragment);*/ + } +} + diff --git a/Drawing2D/src/IDevice.cs b/Drawing2D/src/IDevice.cs index 891f8b2e..14846271 100644 --- a/Drawing2D/src/IDevice.cs +++ b/Drawing2D/src/IDevice.cs @@ -13,16 +13,6 @@ namespace Drawing2D void GetDpy (out int hdpy, out int vdpy); void SetDpy (int hdpy, int vdpy); - IRegion CreateRegion (); - ISurface CreateSurface (int width, int height); - ISurface CreateSurface (byte[] data, int width, int height); - ISurface CreateSurface (IntPtr glfwWinHandle, int width, int height); - IContext CreateContext (ISurface surf); - //IPattern CreatePattern (PatternType patternType); - IGradient CreateGradient (GradientType gradientType, Rectangle bounds); - byte[] LoadBitmap (Stream stream, out Size dimensions); - ISvgHandle LoadSvg (Stream stream); - ISvgHandle LoadSvg (string svgFragment); } } diff --git a/Samples/HelloWorld/main.cs b/Samples/HelloWorld/main.cs index e60b9782..e84607b9 100644 --- a/Samples/HelloWorld/main.cs +++ b/Samples/HelloWorld/main.cs @@ -1,13 +1,15 @@ using System; using Crow; +using Glfw; using Samples; namespace HelloWorld { - class Program { + class Program : Interface { + Program() : base (800, 600, true, true) {} static void Main (string[] args) { - using (Interface app = new Interface ()) { - app.Initialized += (sender, e) => app.LoadIMLFragment (@"