// Copyright (c) 2018-2020 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Text; using System.Linq; using Drawing2D; namespace Crow.VkvgBackend { public class Context : IContext { internal IntPtr handle = IntPtr.Zero; internal Context (Surface surf) { handle = NativeMethods.vkvg_create(surf.handle); this.FillRule = FillRule.NonZero; } ~Context() { Dispose(false); } public double LineWidth { get => NativeMethods.vkvg_get_line_width(handle); set { NativeMethods.vkvg_set_line_width(handle, (float)value); } } public LineJoin LineJoin { get => NativeMethods.vkvg_get_line_join(handle); set { NativeMethods.vkvg_set_line_join(handle, value); } } public LineCap LineCap { get => NativeMethods.vkvg_get_line_cap(handle); set { NativeMethods.vkvg_set_line_cap(handle, value); } } public Operator Operator { set { NativeMethods.vkvg_set_operator(handle, value); } get { return NativeMethods.vkvg_get_operator(handle); } } public FillRule FillRule { set { NativeMethods.vkvg_set_fill_rule(handle, value); } get { return NativeMethods.vkvg_get_fill_rule(handle); } } public Drawing2D.FontExtents FontExtents { get { FontExtents e; NativeMethods.vkvg_font_extents(handle, out e); return new Drawing2D.FontExtents (e.Ascent, e.Descent, e.Height, e.MaxXAdvance, e.MaxYAdvance); } } public Antialias Antialias { set; get; } public void Clear() => NativeMethods.vkvg_clear(handle); public void Clip() => NativeMethods.vkvg_clip(handle); public void ClipPreserve() => NativeMethods.vkvg_clip_preserve(handle); public void ResetClip() => NativeMethods.vkvg_reset_clip(handle); public void Save() => NativeMethods.vkvg_save(handle); public void Restore() => NativeMethods.vkvg_restore(handle); public void Flush() => NativeMethods.vkvg_flush(handle); public void Paint() => NativeMethods.vkvg_paint(handle); public void PaintWithAlpha (double alpha) => Paint(); public void SetFontSize (double size) => NativeMethods.vkvg_set_font_size(handle, (uint)size); public void ClosePath() => NativeMethods.vkvg_close_path (handle); public void NewPath() => NativeMethods.vkvg_new_path(handle); public void NewSubPath() => NativeMethods.vkvg_new_sub_path(handle); public void Arc(double xc, double yc, double radius, double a1, double a2) => NativeMethods.vkvg_arc(handle, (float)xc, (float)yc, (float)radius, (float)a1, (float)a2); public void Arc (PointD center, double radius, double angle1, double angle2) => NativeMethods.vkvg_arc (handle, (float)center.X, (float)center.Y, (float)radius, (float)angle1, (float)angle2); public void ArcNegative (PointD center, double radius, double angle1, double angle2) => NativeMethods.vkvg_arc_negative (handle, (float)center.X, (float)center.Y, (float)radius, (float)angle1, (float)angle2); public void ArcNegative(double xc, double yc, double radius, double a1, double a2) => NativeMethods.vkvg_arc_negative(handle, (float)xc, (float)yc, (float)radius, (float)a1, (float)a2); /*public IntPtr Handle => handle; public void AddReference() { NativeMethods.vkvg_reference(handle); } public uint References() => NativeMethods.vkvg_get_reference_count(handle); public string FontFace { set { NativeMethods.vkvg_select_font_face(handle, value); } } public Matrix Matrix { get { Matrix m; NativeMethods.vkvg_get_matrix(handle, out m); return m; } set { NativeMethods.vkvg_set_matrix(handle, ref value); } } } public Rectangle StrokeExtents () => default; public float[] Dashes { set { if (value == null) NativeMethods.vkvg_set_dash(handle, null, 0, 0); else NativeMethods.vkvg_set_dash(handle, value, (uint)value.Length, 0); } } public void PushGroup () { } public void PopGroupToSource () { } public void SelectFontFace(string family, FontSlant slant, FontWeight weight) { throw new NotImplementedException(); } */ //public void MoveTo(float x, float y) => NativeMethods.vkvg_move_to(handle, x, y); public void MoveTo(double x, double y) => NativeMethods.vkvg_move_to(handle, (float)x, (float)y); public void MoveTo(Point p) => NativeMethods.vkvg_move_to(handle, p.X, p.Y); public void MoveTo(PointD p) => NativeMethods.vkvg_move_to(handle, (float)p.X, (float)p.Y); //public void RelMoveTo(float x, float y) => NativeMethods.vkvg_rel_move_to(handle, x, y); //public void LineTo(float x, float y) => NativeMethods.vkvg_line_to(handle, x, y); public void LineTo(Point p) => NativeMethods.vkvg_line_to(handle, p.X, p.Y); public void LineTo(PointD p) => NativeMethods.vkvg_line_to(handle, (float)p.X, (float)p.Y); public void LineTo(double x, double y) => NativeMethods.vkvg_line_to(handle, (float)x, (float)y); //public void RelLineTo(float x, float y) => NativeMethods.vkvg_rel_line_to(handle, x, y); /*public void CurveTo(float x1, float y1, float x2, float y2, float x3, float y3) => NativeMethods.vkvg_curve_to(handle, x1, y1, x2, y2, x3, y3);*/ public void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) => NativeMethods.vkvg_curve_to (handle, (float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3); public void RelMoveTo(double x, double y) => NativeMethods.vkvg_rel_move_to(handle, (float)x, (float)y); public void RelLineTo(double x, double y) => NativeMethods.vkvg_rel_line_to(handle, (float)x, (float)y); public void RelCurveTo(double x1, double y1, double x2, double y2, double x3, double y3) => NativeMethods.vkvg_rel_curve_to(handle, (float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3); //public void RelCurveTo(float x1, float y1, float x2, float y2, float x3, float y3) // => NativeMethods.vkvg_rel_curve_to(handle, x1, y1, x2, y2, x3, y3); public void Rectangle(double x, double y, double width, double height) => NativeMethods.vkvg_rectangle (handle, (float)x, (float)y, (float)width, (float)height); public void Rectangle(Rectangle r) => NativeMethods.vkvg_rectangle (handle, (float)r.X, (float)r.Y, (float)r.Width, (float)r.Height); public void SetSource(IPattern pat) { if (pat is Pattern p) NativeMethods.vkvg_set_source (handle, p.handle); } public void SetSource (Color color) { NativeMethods.vkvg_set_source_rgba (handle, (float)(color.R / 255.0), (float)(color.G / 255.0), (float)(color.B / 255.0), (float)(color.A / 255.0)); } public void SetSource(ISurface surf, double x = 0, double y = 0) => NativeMethods.vkvg_set_source_surface(handle, (surf as Surface).handle, (float)x, (float)y); public void SetSource(double r, double g, double b, double a = 1.0) => NativeMethods.vkvg_set_source_rgba(handle, (float)r, (float)g, (float)b, (float)a); public void RenderSvg(ISvgHandle nsvgImage, string subId = null) => nsvgImage.Render (this, subId); Matrix savedMat = Matrix.Identity; public void SaveTransformations() => NativeMethods.vkvg_get_matrix (handle, out savedMat); public void RestoreTransformations() => NativeMethods.vkvg_set_matrix (handle, ref savedMat); public void Scale(double sx, double sy) => NativeMethods.vkvg_scale(handle, (float)sx, (float)sy); public void Translate(double dx, double dy) => NativeMethods.vkvg_translate(handle, (float)dx, (float)dy); public void Translate(PointD p) => NativeMethods.vkvg_translate(handle, (float)p.X, (float)p.Y); public void Rotate(double alpha) => NativeMethods.vkvg_rotate(handle, (float)alpha); public void Fill() => NativeMethods.vkvg_fill(handle); public void FillPreserve() => NativeMethods.vkvg_fill_preserve(handle); public void Stroke() => NativeMethods.vkvg_stroke(handle); public void StrokePreserve() => NativeMethods.vkvg_stroke_preserve(handle); public Rectangle StrokeExtents() { throw new NotImplementedException(); } public void PopGroupToSource() { throw new NotImplementedException(); } public void PushGroup() { throw new NotImplementedException(); } public void SelectFontFace(string family, FontSlant slant, FontWeight weight) { NativeMethods.vkvg_select_font_face (handle, family); } internal static byte[] TerminateUtf8(string s) { // compute the byte count including the trailing \0 var byteCount = Encoding.UTF8.GetMaxByteCount(s.Length + 1); var bytes = new byte[byteCount]; Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0); return bytes; } public void SetDash (double [] dashes, double offset = 0) { if (dashes == null) NativeMethods.vkvg_set_dash(handle, null, 0, 0); else { float[] floats = dashes.Cast ().ToArray (); NativeMethods.vkvg_set_dash(handle, floats, (uint)dashes.Length, (float)offset); } } public Drawing2D.TextExtents TextExtents (ReadOnlySpan s, int tabSize = 4) { TextExtents (s, tabSize, out Drawing2D.TextExtents e); return e; } public void TextExtents (ReadOnlySpan s, int tabSize, out Drawing2D.TextExtents extents) { if (s.Length == 0) { extents = default; return; } int size = s.Length * 4 + 1; Span 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 bytes, out Drawing2D.TextExtents extents) { NativeMethods.vkvg_text_extents (handle, ref bytes.GetPinnableReference (), out TextExtents e); extents = new Drawing2D.TextExtents (e.XBearing, e.YBearing, e.Width, e.Height, e.XAdvance, e.YAdvance); } public void ShowText (string text) => ShowText (text.AsSpan()); public void ShowText (ReadOnlySpan s, int tabSize = 4) { int size = s.Length * 4 + 1; Span 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 bytes) { NativeMethods.vkvg_show_text (handle, ref bytes.GetPinnableReference()); } public void ShowText(TextRun textRun) => NativeMethods.vkvg_show_text_run(handle, textRun.Handle); #region IDisposable implementation public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposing || handle == IntPtr.Zero) return; NativeMethods.vkvg_destroy(handle); handle = IntPtr.Zero; } #endregion } }