--- /dev/null
+using System;
+using System.IO;
+using Drawing2D;
+using Glfw;
+
+namespace Crow.CairoBackend
+{
+ public abstract class CairoBackendBase : IBackend {
+ protected ISurface surf;
+ /// <summary> Global font rendering settings for Cairo </summary>
+ FontOptions FontRenderingOptions;
+ /// <summary> Global font rendering settings for Cairo </summary>
+ Antialias Antialias = Antialias.Subpixel;
+ protected CairoBackendBase ()
+ {
+ FontRenderingOptions = new FontOptions ();
+ FontRenderingOptions.Antialias = Antialias.Subpixel;
+ FontRenderingOptions.HintMetrics = HintMetrics.On;
+ FontRenderingOptions.HintStyle = HintStyle.Full;
+ FontRenderingOptions.SubpixelOrder = SubpixelOrder.Default;
+ }
+ ~CairoBackendBase ()
+ {
+ Dispose (false);
+ }
+ public abstract ISurface CreateSurface(int width, int height);
+ public abstract ISurface CreateSurface(byte[] data, int width, int height);
+ 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;
+ protected 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 virtual IContext PrepareUIFrame(IContext existingContext, IRegion clipping)
+ {
+ this.clipping = clipping;
+ IContext ctx = existingContext;
+ if (ctx == null) {
+ disposeContextOnFlush = true;
+ ctx = new Context (MainSurface);
+ } else
+ disposeContextOnFlush = false;
+ return ctx;
+ }
+ public virtual void FlushUIFrame(IContext ctx)
+ {
+ if (disposeContextOnFlush)
+ ctx.Dispose ();
+ clipping = null;
+ }
+ public void ResizeMainSurface (int width, int height)
+ {
+ surf.Resize (width, height);
+ }
+
+ #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
+ }
+}
+
namespace Crow.Backends
{
- public class DefaultBackend : ImageBackend {
+ public class DefaultBackend : EglBackend {
/// <summary>
/// Create a new generic backend bound to the application surface
/// </summary>
/// <param name="height">backend surface height</param>
public DefaultBackend (IntPtr nativeWindoPointer, int width, int height)
: base (nativeWindoPointer, width, height) { }
+ public DefaultBackend (int width, int height) : base (width, height) {
+
+ }
}
}
--- /dev/null
+using System;
+using System.IO;
+using Drawing2D;
+using Glfw;
+
+namespace Crow.CairoBackend
+{
+ public class EglBackend : CairoBackendBase {
+ IntPtr hWin;
+ EGLDevice device;
+ /// <summary>
+ /// Create a new generic backend bound to the application surface
+ /// </summary>
+ /// <param name="width">backend surface width</param>
+ /// <param name="height">backend surface height</param>
+ public EglBackend (IntPtr nativeWindoPointer, int width, int height) : base () {
+ hWin = nativeWindoPointer;
+ Glfw3.MakeContextCurrent (hWin);
+ Glfw3.SwapInterval (0);
+
+ device = new EGLDevice (Glfw3.GetEGLDisplay (), Glfw3.GetEGLContext (hWin));
+ surf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height);
+ }
+ /// <summary>
+ /// Create a new offscreen backend, used in perfTests
+ /// </summary>
+ /// <param name="width">backend surface width</param>
+ /// <param name="height">backend surface height</param>
+ public EglBackend (int width, int height) : base () {
+ device = new EGLDevice (Glfw3.GetEGLDisplay (), IntPtr.Zero);
+ surf = new GLTextureSurface (device, width, height);
+ }
+ public override ISurface CreateSurface(int width, int height)
+ //=> new GLTextureSurface (device, width, height);
+ => new ImageSurface (Format.ARGB32, width, height);
+ public override ISurface CreateSurface(byte[] data, int width, int height)
+ => new ImageSurface (data, Format.ARGB32, width, height, 4 * width);
+ public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping)
+ {
+ Glfw3.MakeContextCurrent (hWin);
+
+ IContext ctx = base.PrepareUIFrame (existingContext, clipping);
+
+ clear (ctx);
+
+ return ctx;
+ }
+ public override void FlushUIFrame(IContext ctx)
+ {
+ base.FlushUIFrame (ctx);
+
+ (surf as GLSurface).SwapBuffers ();
+ }
+ }
+}
+
// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
using System;
using Drawing2D;
-using OpenGL;
-using static OpenGL.Gl;
namespace Crow.CairoBackend
{
public void SetThreadAware (bool value) {
NativeMethods.cairo_gl_device_set_thread_aware (handle, value ? 1 : 0);
}
- public virtual ISurface CreateSurface(int width, int height) {
- uint tex = GenTexture ();
- BindTexture (TextureTarget.Texture2d, tex);
- TexImage2D (TextureTarget.Texture2d, 0, InternalFormat.Rgb, width, height,
- 0, PixelFormat.Rgb, PixelType.UnsignedByte, 0);
- TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, NEAREST);
- TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, NEAREST);
- return new GLSurface (this, Content.ColorAlpha, tex, width, height);
- }
}
}
//
using System;
+using OpenGL;
+using static OpenGL.Gl;
namespace Crow.CairoBackend {
+ public class GLTextureSurface : Surface
+ {
+ uint texId;
+ internal GLTextureSurface (CairoDevice device, int width, int height)
+ : base ()
+ {
+ texId = GenTexture ();
+ BindTexture (TextureTarget.Texture2d, texId);
+ TexImage2D (TextureTarget.Texture2d, 0, InternalFormat.Rgb, width, height,
+ 0, PixelFormat.Rgb, PixelType.UnsignedByte, 0);
+ TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, NEAREST);
+ TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, NEAREST);
+
+ handle = NativeMethods.cairo_gl_surface_create_for_texture (device.Handle,
+ (uint)Content.ColorAlpha, texId, width, height);
+ }
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && handle != IntPtr.Zero)
+ OpenGL.Gl.DeleteTextures (texId);
+ base.Dispose (disposing);
+ }
+
+ }
public class GLSurface : Surface
{
public GLSurface (IntPtr ptr, bool own) : base (ptr, own)
}
public override int Width => NativeMethods.cairo_gl_surface_get_width (handle);
public override int Height => NativeMethods.cairo_gl_surface_get_height (handle);
+ public override void Resize(int width, int height)
+ => NativeMethods.cairo_gl_surface_set_size(handle, width, height);
public void SwapBuffers(){
NativeMethods.cairo_gl_surface_swapbuffers (this.Handle);
namespace Crow.CairoBackend
{
- public class ImageBackend : IBackend {
- ISurface surf;
- /// <summary> Global font rendering settings for Cairo </summary>
- FontOptions FontRenderingOptions;
- /// <summary> Global font rendering settings for Cairo </summary>
- 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;
- }
+ public class ImageBackend : CairoBackendBase {
/// <summary>
/// Create a new generic backend bound to the application surface
/// </summary>
/// <param name="width">backend surface width</param>
/// <param name="height">backend surface height</param>
- public ImageBackend (IntPtr nativeWindoPointer, int width, int height) : this () {
+ public ImageBackend (IntPtr nativeWindoPointer, int width, int height) : base () {
switch (Environment.OSVersion.Platform) {
case PlatformID.Unix:
/// </summary>
/// <param name="width">backend surface width</param>
/// <param name="height">backend surface height</param>
- public ImageBackend (int width, int height) : this () {
+ public ImageBackend (int width, int height) : base () {
surf = new ImageSurface (Format.ARGB32, width, height);
}
- ~ImageBackend ()
- {
- Dispose (false);
- }
- public virtual ISurface CreateSurface(int width, int height)
+ public override ISurface CreateSurface(int width, int height)
=> new ImageSurface (Format.ARGB32, width, height);
- public virtual ISurface CreateSurface(byte[] data, int width, int height)
+ public override 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)
+ public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping)
{
- this.clipping = clipping;
- IContext ctx = existingContext;
- if (ctx == null) {
- disposeContextOnFlush = true;
- ctx = new Context (MainSurface);
- } else
- disposeContextOnFlush = false;
+ IContext ctx = base.PrepareUIFrame (existingContext, clipping);
clear (ctx);
ctx.PushGroup ();
return ctx;
}
- public void FlushUIFrame(IContext ctx)
+ public override void FlushUIFrame(IContext ctx)
{
ctx.PopGroupToSource ();
ctx.Paint ();
- if (disposeContextOnFlush)
- ctx.Dispose ();
- clipping = null;
- }
- public void ResizeMainSurface (int width, int height)
- {
- surf.Resize (width, height);
- }
- #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;
+ base.FlushUIFrame (ctx);
}
- #endregion
}
}
{
protected IntPtr handle = IntPtr.Zero;
- [Obsolete]
protected Surface()
{
}
handle = IntPtr.Zero;
}
public virtual void Resize (int width, int height) {
+
}
public Status Finish ()
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.0</TargetFramework>
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="src\**\*.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.3" />
+ <PackageReference Include="vke" Version="0.2.4-beta" />
+ <!--<PackageReference Include="SkiaSharp" Version="2.80.3" />-->
+ <ProjectReference Include="..\..\Drawing2D\Drawing2D.csproj" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Text;
+using System.Linq;
+using Drawing2D;
+using SkiaSharp;
+
+namespace Crow.SkiaBackend
+{
+ public class Context : IContext
+ {
+ VkSurface surf;
+ SKCanvas canvas;
+ SKPaint paint;
+ SKPath path;
+ FillRule fillRule = FillRule.NonZero;
+ SKFont font;
+ static Operator[] operatorLut = new Operator[] {
+ Operator.Clear,
+ Operator.Source,
+ Operator.Dest,
+ Operator.Over,
+ Operator.DestOver,
+ Operator.In,
+ Operator.DestIn,
+ Operator.Out,
+ Operator.DestOut,
+ Operator.Atop,
+ Operator.DestAtop,
+ Operator.Xor,
+ Operator.Add,
+ 0, //SKBlendMode.Modulate,
+ Operator.Screen,
+ Operator.Overlay,
+ Operator.Darken,
+ Operator.Lighten,
+ 0, //SKBlendMode.ColorDodge,
+ 0, //SKBlendMode.ColorBurn,
+ 0, //SKBlendMode.HardLight,
+ 0, //SKBlendMode.SoftLight,
+ 0, //SKBlendMode.Difference,
+ 0, //SKBlendMode.Exclusion,
+ Operator.Multiply,
+ 0, //SKBlendMode.Hue,
+ Operator.Saturate,//mod
+ 0, //SKBlendMode.Color,
+ 0, //SKBlendMode.Luminosity
+ };
+ static SKBlendMode[] skBlendModeLut = new SKBlendMode[] {
+ SKBlendMode.Clear,
+ SKBlendMode.Src,
+ SKBlendMode.SrcOver,
+ SKBlendMode.SrcIn,
+ SKBlendMode.SrcOut,
+ SKBlendMode.SrcATop,
+ SKBlendMode.Dst,
+ SKBlendMode.DstOver,
+ SKBlendMode.DstIn,
+ SKBlendMode.DstOut,
+ SKBlendMode.DstATop,
+ SKBlendMode.Xor,
+ SKBlendMode.Plus,
+ SKBlendMode.Saturation,
+ SKBlendMode.Multiply,
+ SKBlendMode.Screen,
+ SKBlendMode.Overlay,
+ SKBlendMode.Darken,
+ SKBlendMode.Lighten
+ };
+ internal Context (VkSurface surf)
+ {
+ canvas = surf.Canvas;
+ paint = new SKPaint ();
+ }
+ ~Context()
+ {
+ Dispose(false);
+ }
+
+ public IntPtr Handle => throw new NotImplementedException();
+
+ public double LineWidth {
+ get => (float)paint.StrokeWidth;
+ set => paint.StrokeWidth = (float)value;
+ }
+ public LineJoin LineJoin {
+ get => (LineJoin)paint.StrokeJoin;
+ set => paint.StrokeJoin = (SKStrokeJoin)value;
+ }
+ public LineCap LineCap {
+ get => (LineCap)paint.StrokeCap;
+ set => paint.StrokeCap = (SKStrokeCap)value;
+ }
+ public Operator Operator {
+ get => operatorLut [(int)paint.BlendMode];
+ set => paint.BlendMode = skBlendModeLut[(int)value];
+ }
+ public FillRule FillRule {
+ get => fillRule;
+ set => fillRule = value;
+ }
+
+ public FontExtents FontExtents {
+ get {
+ if (font == null)
+ createDefaultFont ();
+ paint.GetFontMetrics (out SKFontMetrics m);
+ return new FontExtents (m.Ascent, m.Descent, m.CapHeight, m.XMax, m.XHeight);
+ }
+ }
+ public Antialias Antialias {
+ get => paint.IsAntialias ? Antialias.Grey : Antialias.None;
+ set => paint.IsAntialias = (value == Antialias.Grey);
+ }
+
+ void checkPath () {
+ if (path == null)
+ path = new SKPath ();
+ }
+ public void Arc(double xc, double yc, double radius, double a1, double a2)
+ {
+ checkPath ();
+
+ float r = (float)radius;
+ float x = (float)xc;
+ float y = (float)yc;
+
+ SKRect rect = new SKRect (x - r, y - r, x + r, y + r);
+ path.ArcTo (rect, (float)a1, (float)a2 - (float)a1, false);
+ }
+
+ public void Arc(PointD center, double radius, double angle1, double angle2)
+ => Arc (center.X, center.Y, radius, angle1, angle2);
+ public void ArcNegative(double xc, double yc, double radius, double angle1, double angle2)
+ => Arc (xc, yc, radius, angle2, angle1);
+
+ public void ArcNegative(PointD center, double radius, double angle1, double angle2)
+ => Arc (center.X, center.Y, radius, angle2, angle1);
+
+ public void Clear() => canvas.Clear ();
+ public void Clip()
+ {
+ ClipPreserve ();
+ path?.Dispose ();
+ path = null;
+ }
+
+ public void ClipPreserve()
+ {
+ if (path == null)
+ return;
+ canvas.ClipPath (path,
+ fillRule == FillRule.NonZero ? SKClipOperation.Intersect : SKClipOperation.Difference, paint.IsAntialias);
+ }
+
+ public void ClosePath() => path?.Close ();
+
+ public void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3)
+ {
+ checkPath ();
+ path.CubicTo ((float) x1, (float) y1, (float) x2, (float) y2, (float) x3, (float) y3);
+ }
+ public void Fill()
+ {
+ FillPreserve ();
+ path?.Dispose ();
+ path = null;
+ }
+
+ public void FillPreserve()
+ {
+ if (path == null)
+ return;
+ paint.IsStroke = false;
+ canvas.DrawPath (path, paint);
+ }
+
+ public void Flush() => canvas.Flush ();
+ public void LineTo(double x, double y)
+ {
+ checkPath ();
+ path.LineTo ((float)x, (float)y);
+ }
+ public void LineTo(Point p) => LineTo (p.X, p.Y);
+ public void LineTo(PointD p) => LineTo (p.X, p.Y);
+ public void MoveTo(double x, double y)
+ {
+ checkPath ();
+ path.MoveTo ((float)x, (float)y);
+ }
+ public void MoveTo(Point p) => MoveTo (p.X, p.Y);
+ public void MoveTo(PointD p) => MoveTo (p.X, p.Y);
+ public void NewPath()
+ {
+ path?.Dispose ();
+ path = new SKPath ();
+ }
+
+ public void NewSubPath()
+ {
+ path = new SKPath (path);
+ }
+
+ public void Paint() => canvas.DrawPaint (paint);
+ public void PaintWithAlpha(double alpha)
+ {
+// throw new NotImplementedException();
+ canvas.DrawPaint (paint);
+ }
+
+ public void PopGroupToSource()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void PushGroup()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Rectangle(double x, double y, double width, double height)
+ {
+ checkPath ();
+ SKRect r = new SKRect ((float) x, (float) y, (float) (x + width), (float) (y + height));
+ path.AddRect (r, SKPathDirection.Clockwise);
+ }
+
+ public void Rectangle(Rectangle r) => Rectangle (r.X, r.Y, r.Width, r.Height);
+
+ public void RelCurveTo(double x1, double y1, double x2, double y2, double x3, double y3)
+ {
+ if (path == null)
+ return;
+ double dx = path.LastPoint.X, dy = path.LastPoint.Y;
+ CurveTo (dx + x1, dx + y1, dx + x2, dx + y2, dx + x3, dx + y3);
+ }
+
+ public void RelLineTo(double x, double y)
+ {
+ if (path == null)
+ return;
+ double dx = path.LastPoint.X, dy = path.LastPoint.Y;
+ LineTo (dx + x, dx + y);
+ }
+
+ public void RelMoveTo(double x, double y)
+ {
+ if (path == null)
+ return;
+ double dx = path.LastPoint.X, dy = path.LastPoint.Y;
+ MoveTo (dx + x, dx + y);
+ }
+
+ public void RenderSvg(IntPtr svgNativeHandle, string subId = null)
+ {
+ //throw new NotImplementedException();
+ }
+
+ public void ResetClip()
+ {
+ if (!canvas.IsClipEmpty)
+ canvas.ClipRegion (new SKRegion (new SKRectI (0,0, surf.Width, surf.Height)));
+ }
+
+ public void Restore() => canvas.Restore ();
+ SKMatrix savedMat;
+ public void RestoreTransformations()
+ {
+ savedMat = canvas.TotalMatrix;
+ }
+
+ public void Rotate(double alpha) => canvas.RotateRadians ((float)alpha);
+ public void Save() => canvas.Save ();
+ public void SaveTransformations() => canvas.SetMatrix (savedMat);
+ public void Scale(double sx, double sy) => canvas.Scale ((float)sx, (float)sy);
+
+ void updatePaintFont () {
+ paint.Typeface = font.Typeface;
+ paint.TextSize = font.Size;
+ }
+ void createDefaultFont () {
+ font = new SKFont (SKTypeface.FromFamilyName ("mono"));
+ }
+ public void SelectFontFace(string family, FontSlant slant, FontWeight weight)
+ {
+ font?.Dispose ();
+ font = new SKFont (SKTypeface.FromFamilyName (family,
+ (weight == FontWeight.Normal) ? SKFontStyleWeight.Normal : SKFontStyleWeight.Bold,
+ SKFontStyleWidth.Normal, (SKFontStyleSlant)slant));
+ updatePaintFont ();
+ }
+
+ public void SetDash(double[] dashes, double offset = 0)
+ {
+ if (dashes == null || dashes.Length == 1) {
+ paint.PathEffect = null;
+ return;
+ }
+ paint.PathEffect = SKPathEffect.CreateDash (dashes.Cast<float>().ToArray(), (float)offset);
+ }
+
+ public void SetFontSize(double scale)
+ {
+ if (font == null)
+ createDefaultFont ();
+ font.Size = (float)scale;
+ updatePaintFont ();
+ }
+
+ public void SetSource(IPattern pat)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetSource(Color color)
+ {
+ paint.Color = (UInt32)color;
+ }
+
+ public void SetSource(double r, double g, double b, double a = 1)
+ {
+ paint.Color = (UInt32)new Color (r, g, b, a);
+ }
+
+ public void SetSource(ISurface surf, double x = 0, double y = 0)
+ {
+ if (!(surf is Surface s))
+ return;
+ canvas.DrawSurface (s.SkSurf, (float)x, (float)y);
+ }
+
+ public void ShowText(string text)
+ {
+ ShowText (text.AsSpan ());
+ }
+
+ public void ShowText(ReadOnlySpan<char> s, int tabSize = 4)
+ {
+ int size = s.Length * 4 + 1;
+ Span<byte> bytes = size > 512 ? new byte[size] : stackalloc byte[size];
+ int encodedBytes = s.ToUtf8 (bytes, tabSize);
+ bytes[encodedBytes] = 0;
+ ShowText (bytes.Slice (0, encodedBytes + 1));
+ }
+
+ public void ShowText(Span<byte> bytes)
+ {
+ SKPoint origin = (path == null) ? SKPoint.Empty : path.LastPoint;
+ if (font == null)
+ createDefaultFont ();
+ SKTextBlob tb = SKTextBlob.Create (bytes, SKTextEncoding.Utf8, font, origin);
+ canvas.DrawText (tb, origin.X, origin.Y, paint);
+ }
+
+ public void Stroke()
+ {
+ StrokePreserve ();
+ path?.Dispose ();
+ path = null;
+ }
+
+ public Rectangle StrokeExtents()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void StrokePreserve()
+ {
+ if (path == null)
+ return;
+ paint.IsStroke = true;
+ canvas.DrawPath (path, paint);
+ }
+
+ public TextExtents TextExtents(ReadOnlySpan<char> s, int tabSize = 4)
+ {
+ TextExtents (s, tabSize, out Drawing2D.TextExtents e);
+ return e;
+ }
+
+ public void TextExtents(ReadOnlySpan<char> s, int tabSize, out TextExtents extents)
+ {
+ if (s.Length == 0) {
+ extents = default;
+ return;
+ }
+ int size = s.Length * 4 + 1;
+ Span<byte> bytes = size > 512 ? new byte[size] : stackalloc byte[size];
+ int encodedBytes = s.ToUtf8 (bytes, tabSize);
+ bytes[encodedBytes] = 0;
+ TextExtents (bytes.Slice (0, encodedBytes + 1), out extents);
+ }
+
+ public void TextExtents(Span<byte> bytes, out TextExtents extents)
+ {
+ if (font == null)
+ createDefaultFont ();
+ paint.GetFontMetrics (out SKFontMetrics metrics);
+ SKRect bounds = default;
+ paint.MeasureText (bytes, ref bounds);
+ extents = new TextExtents (0,0,bounds.Width, bounds.Height, bounds.Width, bounds.Height);
+ }
+
+ public void Translate(double dx, double dy)
+ {
+ canvas.Translate ((float)dx, (float)dy);
+ }
+
+ public void Translate(PointD p) => Translate (p.X, p.Y);
+
+
+ #region IDisposable implementation
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing)
+ return;
+ paint?.Dispose ();
+ path?.Dispose ();
+ font?.Dispose ();
+ }
+ #endregion
+ }
+}
+
--- /dev/null
+// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow.SkiaBackend;
+
+namespace Crow.Backends
+{
+ public class DefaultBackend : VulkanBackend
+ {
+ public DefaultBackend (IntPtr nativeWindoPointer, int width, int height)
+ : base (nativeWindoPointer, width, height) {}
+ public DefaultBackend (int width, int height)
+ : base (width, height) {}
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using Drawing2D;
+using SkiaSharp;
+
+namespace Crow.SkiaBackend
+{
+ public class EglBackend : IBackend
+ {
+ [DllImport("libEGL")]
+ extern static IntPtr eglGetProcAddress(string procname);
+
+ int sampleCount = 1;
+ Surface surf;
+ public ISurface MainSurface => surf;
+ public EglBackend (IntPtr nativeWindoPointer, int width, int height) : base () {
+
+ }
+ /// <summary>
+ /// Create a new offscreen backend, used in perfTests
+ /// </summary>
+ /// <param name="width">backend surface width</param>
+ /// <param name="height">backend surface height</param>
+ public EglBackend (int width, int height) : base ()
+ {
+ /*GRGlGetProcedureAddressDelegate del = new GRGlGetProcedureAddressDelegate (eglGetProcAddress);
+ GRGlInterface iface = GRGlInterface.CreateGles (del);
+ GRContext gr = GRContext.CreateGl (iface, default);
+ SKImageInfo sKImgInfo = new SKImageInfo (width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ GRBackendRenderTarget rtd = GRBackendRenderTarget.
+ GRBackendRenderTarget target = new GRBackendRenderTarget (width, height, sampleCount, 0, sKImgInfo);
+ SKSurface.CreateAsRenderTarget (gr,)
+ surf = new Surface (width, height);*/
+ }
+ public IContext CreateContext(ISurface surf)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IGradient CreateGradient(GradientType gradientType, Rectangle bounds)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IRegion CreateRegion()
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISurface CreateSurface(int width, int height)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISurface CreateSurface(byte[] data, int width, int height)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void FlushUIFrame(IContext ctx)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] LoadBitmap(Stream stream, out Size dimensions)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISvgHandle LoadSvg(Stream stream)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISvgHandle LoadSvg(string svgFragment)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IContext PrepareUIFrame(IContext existingContext, IRegion clipping)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ResizeMainSurface(int width, int height)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System.Collections.Generic;
+using System;
+using Drawing2D;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Crow.SkiaBackend {
+ public class Region : IRegion
+ {
+ Rectangle _bounds;
+ bool boundsUpToDate = true;
+ public List<Rectangle> list = new List<Rectangle>();
+ public int count => list.Count;
+
+ public void AddRectangle(Rectangle r)
+ {
+ if (r == default)
+ return;
+ if (DoesNotContains (r)) {
+ list.Add (r);
+ boundsUpToDate = false;
+ }
+ }
+ public bool DoesNotContains(Rectangle r)
+ {
+ foreach (Rectangle rInList in list)
+ if (rInList.ContainsOrIsEqual(r))
+ return false;
+ return true;
+ }
+ public bool intersect(Rectangle r)
+ {
+ foreach (Rectangle rInList in list)
+ if (rInList.Intersect(r))
+ return true;
+ return false;
+ }
+ public void stroke(IContext ctx, Color c)
+ {
+ foreach (Rectangle r in list)
+ ctx.Rectangle(r);
+
+ ctx.SetSource(c);
+
+ ctx.LineWidth = 2;
+ ctx.Stroke ();
+ }
+ public void clearAndClip(IContext ctx)
+ {
+ if (list.Count == 0)
+ return;
+ foreach (Rectangle r in list)
+ ctx.Rectangle(r);
+
+ ctx.ClipPreserve();
+ ctx.Operator = Operator.Clear;
+ ctx.Fill();
+ ctx.Operator = Operator.Over;
+ }
+
+ public void clip(IContext ctx)
+ {
+ foreach (Rectangle r in list)
+ ctx.Rectangle(r);
+
+ ctx.Clip();
+ }
+ public Rectangle Bounds {
+ get {
+ if (!boundsUpToDate) {
+ if (list.Count > 0) {
+ _bounds = list [0];
+ for (int i = 1; i < list.Count; i++) {
+ _bounds += list [i];
+ }
+ } else
+ _bounds = default;
+ boundsUpToDate = true;
+ }
+ return _bounds;
+ }
+ }
+ public void clear(IContext ctx)
+ {
+ foreach (Rectangle r in list)
+ ctx.Rectangle(r);
+ ctx.Operator = Operator.Clear;
+ ctx.Fill();
+ ctx.Operator = Operator.Over;
+ }
+
+ #region IRegion implemenatation
+ public bool IsEmpty => list.Count == 0;
+ public int NumRectangles => list.Count;
+ public Rectangle GetRectangle(int i) => list[i];
+ public void UnionRectangle (Rectangle r) {
+ /*if (r == default)
+ System.Diagnostics.Debugger.Break ();*/
+ AddRectangle (r);
+ }
+ public bool OverlapOut (Rectangle r) {
+ foreach (Rectangle rInList in list)
+ if (rInList.Intersect(r))
+ return false;
+ return true;
+ }
+ public RegionOverlap Contains(Rectangle rectangle)
+ {
+ throw new NotImplementedException();
+ }
+ public void Reset()
+ {
+ list = new List<Rectangle>();
+ _bounds = default;
+ boundsUpToDate = true;
+ }
+
+ public bool Equals([AllowNull] IRegion other)
+ => other is Region r ? Bounds.Equals (r.Bounds) : false;
+ #endregion
+
+ public override string ToString ()
+ {
+ string tmp = "";
+ foreach (Rectangle r in list) {
+ tmp += r.ToString ();
+ }
+ return tmp;
+ }
+
+ public void Dispose()
+ {
+
+ }
+
+ }
+}
--- /dev/null
+// Copyright (c) 2018-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Drawing2D;
+using SkiaSharp;
+using vke;
+using Vulkan;
+
+namespace Crow.SkiaBackend
+{
+ public class VkSurface : ISurface {
+ GRBackendRenderTarget rt;
+ SKSurface skSurf;
+ GRVkImageInfo imgInfo;
+ vke.Image img;
+
+ internal VkSurface (Device dev, Queue queue, GRContext gr, int width, int height, VkSampleCountFlags samples)
+ {
+ img = new vke.Image (dev, VkFormat.R8g8b8a8Unorm,
+ VkImageUsageFlags.TransferSrc | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal,
+ (uint)width, (uint)height);
+
+ imgInfo = new GRVkImageInfo {
+ CurrentQueueFamily = queue.qFamIndex,
+ Format = (uint)img.Format,
+ Image = img.Handle.Handle,
+ ImageLayout = (uint)VkImageLayout.ColorAttachmentOptimal,
+ ImageTiling = (uint)VkImageTiling.Optimal,
+ LevelCount = img.CreateInfo.mipLevels,
+ Protected = false,
+ Alloc = new GRVkAlloc()
+ {
+ Memory = img.Memory.Handle,
+ Flags = 0,
+ Offset = 0,
+ Size = img.AllocatedDeviceMemorySize
+ }
+ };
+ rt = new GRBackendRenderTarget (width, height, (int)samples, imgInfo);
+ skSurf = SKSurface.Create (gr, rt, SKColorType.Rgba8888);
+ }
+ ~VkSurface ()
+ {
+ Dispose (false);
+ }
+
+ public IntPtr Handle => throw new NotImplementedException();
+ public int Width => (int)img.Width;
+ public int Height => (int)img.Height;
+ internal SKCanvas Canvas => skSurf.Canvas;
+ internal SKSurface SkSurf => skSurf;
+ internal vke.Image Img => img;
+
+
+ public void Flush() => skSurf.Flush ();
+
+ public void Resize(int width, int height)
+ {
+ //throw new NotImplementedException();
+ }
+ #region IDisposable implementation
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (!disposing || skSurf == null)
+ return;
+ skSurf.Dispose ();
+ rt.Dispose ();
+ img.Dispose ();
+ }
+ #endregion
+
+ }
+ public class Surface: ISurface
+ {
+ protected SKSurface skSurf;
+ SKImageInfo sKImgInfo;
+ public IntPtr Handle => throw new NotImplementedException();
+ public int Width => sKImgInfo.Width;
+ public int Height => sKImgInfo.Height;
+ internal SKCanvas Canvas => skSurf.Canvas;
+ internal SKSurface SkSurf => skSurf;
+
+ #region CTOR
+ public Surface (int width, int height)
+ {
+ sKImgInfo = new SKImageInfo (width, height);
+ skSurf = SKSurface.Create (sKImgInfo);
+ }
+ Surface (IntPtr devHandle, int width, int height)
+ {
+
+ }
+ #endregion
+ ~Surface ()
+ {
+ Dispose (false);
+ }
+
+ public void Resize(int width, int height)
+ {
+
+ }
+ public void Flush ()
+ {
+ skSurf.Canvas.Flush ();
+ }
+
+ /*public void WriteToPng (string path) {
+ }
+ public void WriteTo (IntPtr bitmap) {
+ }
+ public void Clear () {
+ }*/
+
+ #region IDisposable implementation
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (!disposing || skSurf == null)
+ return;
+ skSurf.Dispose ();
+ }
+ #endregion
+ }
+}
+
--- /dev/null
+// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Crow.SkiaBackend;
+using Drawing2D;
+using Glfw;
+using SkiaSharp;
+
+using vke;
+using Vulkan;
+using static Vulkan.Vk;
+
+namespace Crow.SkiaBackend
+{
+ public class VulkanBackend : IBackend
+ {
+ protected Instance instance;
+ protected PhysicalDevice phy;
+ protected vke.Device dev;
+ protected Queue graphicQueue; //for vkvg, we must have at least a graphic queue
+ protected IntPtr hWin; //GLFW window native pointer.
+ protected VkSurfaceKHR hSurf; //Vulkan Surface
+ protected SwapChain swapChain;
+ protected CommandPool cmdPool;
+ protected PrimaryCommandBuffer[] cmds;
+ protected VkSemaphore[] drawComplete;
+ protected Fence drawFence;
+ VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1;
+ bool vsync = false;
+
+ GRContext gr;
+ VkSurface surf;
+ public ISurface MainSurface => surf;
+
+ bool tryGetPhy (vke.PhysicalDeviceCollection physicalDevices, VkPhysicalDeviceType phyType, out PhysicalDevice phy, bool swapchainSupport) {
+ phy = physicalDevices.FirstOrDefault (p => p.Properties.deviceType == phyType && p.HasSwapChainSupport == swapchainSupport);
+ return phy != null;
+ }
+
+ IntPtr getVkProcAddress(string name, IntPtr instance, IntPtr device) {
+ using (FixedUtf8String n = new FixedUtf8String (name))
+ {
+ return device == IntPtr.Zero ?
+ Vk.vkGetInstanceProcAddr (instance, n) :
+ Vk.vkGetDeviceProcAddr (device, n);
+ }
+ }
+
+ /// <summary>
+ /// Create a new offscreen backend, used in perfTests
+ /// </summary>
+ /// <param name="width">backend surface width</param>
+ /// <param name="height">backend surface height</param>
+ public VulkanBackend (int width, int height) : base () {
+#if DEBUG
+ instance = new Instance (Ext.I.VK_EXT_debug_utils);
+#else
+ instance = new Instance ();
+#endif
+ vke.PhysicalDeviceCollection phys = instance.GetAvailablePhysicalDevice ();
+ if (phys.Count() == 0)
+ throw new Exception ("[Crow.VkvgBackend] No vulkan hardware found.");
+
+ if (!tryGetPhy (phys, VkPhysicalDeviceType.DiscreteGpu, out phy, false))
+ if (!tryGetPhy (phys, VkPhysicalDeviceType.IntegratedGpu, out phy, false))
+ phy = phys[0];
+
+ VkPhysicalDeviceFeatures enabledFeatures = default;
+ dev = new vke.Device (phy);
+ graphicQueue = new Queue (dev, VkQueueFlags.Graphics);
+ dev.Activate (enabledFeatures);
+
+ }
+ public VulkanBackend (IntPtr nativeWindoPointer, int width, int height) : base ()
+ {
+ hWin = nativeWindoPointer;
+
+ SwapChain.IMAGES_USAGE = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst;
+ SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Unorm;
+
+ List<string> instExts = new List<string> (Glfw3.GetRequiredInstanceExtensions ());
+#if DEBUG
+ instExts.Add (Ext.I.VK_EXT_debug_utils);
+#endif
+ instance = new Instance (instExts.ToArray());
+ hSurf = instance.CreateSurface (hWin);
+
+ vke.PhysicalDeviceCollection phys = instance.GetAvailablePhysicalDevice ();
+ if (phys.Count() == 0)
+ throw new Exception ("[Crow.VkvgBackend] No vulkan hardware found.");
+
+ if (!tryGetPhy (phys, VkPhysicalDeviceType.DiscreteGpu, out phy, true))
+ if (!tryGetPhy (phys, VkPhysicalDeviceType.IntegratedGpu, out phy, true))
+ phy = phys[0];
+
+ VkPhysicalDeviceFeatures enabledFeatures = default;
+ dev = new vke.Device (phy);
+ graphicQueue = new PresentQueue (dev, VkQueueFlags.Graphics, hSurf);
+
+ dev.Activate (enabledFeatures, Ext.D.VK_KHR_swapchain);
+
+ swapChain = new SwapChain (graphicQueue as PresentQueue, (uint)width, (uint)height, SwapChain.PREFERED_FORMAT,
+ vsync ? VkPresentModeKHR.FifoKHR : VkPresentModeKHR.ImmediateKHR);
+ swapChain.Activate ();
+
+ cmdPool = new CommandPool (dev, graphicQueue.qFamIndex, VkCommandPoolCreateFlags.ResetCommandBuffer);
+ cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount);
+
+ drawComplete = new VkSemaphore[swapChain.ImageCount];
+ drawFence = new Fence (dev, true, "draw fence");
+
+ for (int i = 0; i < swapChain.ImageCount; i++) {
+ drawComplete[i] = dev.CreateSemaphore ();
+ drawComplete[i].SetDebugMarkerName (dev, "Semaphore DrawComplete" + i);
+ }
+
+ cmdPool.SetName ("main CmdPool");
+ GRVkBackendContext grVkCtx = new GRVkBackendContext {
+ VkInstance = instance.Handle,
+ VkPhysicalDevice = phy.Handle,
+ VkQueue = graphicQueue.Handle.Handle,
+ VkDevice = dev.VkDev.Handle,
+ GraphicsQueueIndex = 0,
+ GetProcedureAddress = getVkProcAddress
+ };
+ gr = GRContext.CreateVulkan (grVkCtx);
+
+ createMainSurface ((uint)width, (uint)height);
+ }
+ ~VulkanBackend ()
+ {
+ Dispose (false);
+ }
+
+ public IContext CreateContext(ISurface surf) => new Context (surf as VkSurface);
+ public IGradient CreateGradient(GradientType gradientType, Rectangle bounds)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IRegion CreateRegion () => new Crow.SkiaBackend.Region ();
+ public ISurface CreateSurface(int width, int height)
+ {
+ return new VkSurface (dev, graphicQueue, gr, (int)width, (int)height, samples);
+ }
+
+ public ISurface CreateSurface(byte[] data, int width, int height)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] LoadBitmap(Stream stream, out Size dimensions)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISvgHandle LoadSvg(Stream stream)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISvgHandle LoadSvg(string svgFragment)
+ {
+ throw new NotImplementedException();
+ }
+
+ 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 (surf);
+ } else
+ disposeContextOnFlush = false;
+
+ clear (ctx);
+
+ return ctx;
+ }
+ public void FlushUIFrame(IContext ctx)
+ {
+ if (disposeContextOnFlush)
+ ctx.Dispose ();
+ clipping = null;
+
+ dev.WaitIdle();
+
+ int idx = swapChain.GetNextImage ();
+ if (idx < 0) {
+ createMainSurface (swapChain.Width, swapChain.Height);
+ return;
+ }
+
+ drawFence.Wait ();
+ drawFence.Reset ();
+
+ graphicQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx], drawFence);
+ (graphicQueue as PresentQueue).Present (swapChain, drawComplete[idx]);
+
+ dev.WaitIdle();
+ }
+ public void ResizeMainSurface (int width, int height) {
+ //resize is done on swapchain image aquisition failure
+ }
+ void createMainSurface (uint width, uint height) {
+ dev.WaitIdle();
+
+ surf?.Dispose ();
+ surf = new VkSurface (dev, graphicQueue, gr, (int)width, (int)height, samples);
+
+ cmdPool.Reset();
+
+ vke.Image blitSource = surf.Img;
+
+ for (int i = 0; i < swapChain.ImageCount; i++) {
+ vke.Image blitDest = swapChain.images[i];
+ vke.PrimaryCommandBuffer cmd = cmds[i];
+ cmd.Start();
+
+ blitDest.SetLayout (cmd, VkImageAspectFlags.Color,
+ VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal,
+ VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.Transfer);
+
+ blitSource.SetLayout (cmd, VkImageAspectFlags.Color,
+ VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal,
+ VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer);
+
+ blitSource.BlitTo (cmd, blitDest, VkFilter.Nearest);
+
+ blitDest.SetLayout (cmd, VkImageAspectFlags.Color,
+ VkImageLayout.TransferDstOptimal, VkImageLayout.PresentSrcKHR,
+ VkPipelineStageFlags.Transfer, VkPipelineStageFlags.BottomOfPipe);
+
+ blitSource.SetLayout (cmd, VkImageAspectFlags.Color,
+ VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal,
+ VkPipelineStageFlags.Transfer, VkPipelineStageFlags.ColorAttachmentOutput);
+
+ cmd.End ();
+ }
+ dev.WaitIdle ();
+ }
+ #region IDisposable implementation
+ bool isDisposed;
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (!isDisposed && disposing) {
+ dev.WaitIdle ();
+
+ surf.Dispose ();
+ gr.Dispose ();
+
+ for (int i = 0; i < swapChain.ImageCount; i++) {
+ dev.DestroySemaphore (drawComplete[i]);
+ cmds[i].Free ();
+ }
+ drawFence.Dispose ();
+ swapChain.Dispose ();
+ cmdPool.Dispose ();
+
+ vkDestroySurfaceKHR (instance.Handle, hSurf, IntPtr.Zero);
+
+ dev.Dispose ();
+ instance.Dispose ();
+ }
+ isDisposed = true;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkvgBackend", "Backends\VkvgBackend\VkvgBackend.csproj", "{34976828-80CF-4AC5-8C81-F66F635DC5FC}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaBackend", "Backends\SkiaBackend\SkiaBackend.csproj", "{ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
{34976828-80CF-4AC5-8C81-F66F635DC5FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34976828-80CF-4AC5-8C81-F66F635DC5FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34976828-80CF-4AC5-8C81-F66F635DC5FC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{18EBB41F-815E-4BF5-B80F-C9E2FAB2993A} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
{E06441A9-0CFD-45BB-9478-99D28CEB327F} = {451F5727-2A2E-4361-A41B-089429ADE8F9}
{34976828-80CF-4AC5-8C81-F66F635DC5FC} = {451F5727-2A2E-4361-A41B-089429ADE8F9}
+ {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175} = {451F5727-2A2E-4361-A41B-089429ADE8F9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {00D4E149-7131-49F4-BAAD-559AA961A78E}
<ItemGroup>
<ProjectReference Include="..\Drawing2D\Drawing2D.csproj" />
+ <ProjectReference Include="..\Backends\SkiaBackend\SkiaBackend.csproj" />
<!--<ProjectReference Include="..\Backends\CairoBackend\CairoBackend.csproj" />-->
- <ProjectReference Include="..\Backends\VkvgBackend\VkvgBackend.csproj" />
+ <!--<ProjectReference Include="..\Backends\VkvgBackend\VkvgBackend.csproj" />-->
</ItemGroup>
<PropertyGroup Condition=" '$(CrowStbSharp)' == 'true'">
{
Glfw3.Init ();
- //Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi);
+
Glfw3.WindowHint (WindowAttribute.ClientApi, 0);
- //Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 3);
- //Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 0);
- Glfw3.WindowHint (WindowAttribute.ContextCreationApi, Constants.EglContextApi);
+ /*Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi);
+ Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 3);
+ Glfw3.WindowHint (WindowAttribute.ContextVersionMinor, 2);
+ Glfw3.WindowHint (WindowAttribute.ContextCreationApi, Constants.EglContextApi);*/
Glfw3.WindowHint (WindowAttribute.Resizable, 1);
Glfw3.WindowHint (WindowAttribute.Decorated, 1);
#region Operators
/*public static implicit operator string(Color c) => c.ToString();
- public static implicit operator UInt32 (Color c) => c.value;*/
+ */
/*public static implicit operator Color(string s)
{
}*/
-
+ public static implicit operator UInt32 (Color c) => c.value;
public static implicit operator Color (Colors c) => new Color ((UInt32)c);
public static implicit operator Colors (Color c) => (Colors)c.value;
public static bool operator ==(Color a, Color b) => a.Equals (b);
void Flush ();
- void WriteToPng (string path);
+ /*void WriteToPng (string path);
void WriteTo (IntPtr bitmap);
void Clear ();
- ISurface CreateSimilar (int width, int height);
+ ISurface CreateSimilar (int width, int height);*/
void Resize (int width, int height);
}
}
Program() : base (800, 600, true, true) {}
static void Main (string[] args) {
using (Program app = new Program ()) {
- app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Window Caption='Hello World'/>");
+ app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Label Text='Hello World'/>");
app.Run ();
}
}