Skip to content

Commit

Permalink
Merge pull request #1513 from KirillAldashkin/master
Browse files Browse the repository at this point in the history
Full C# refactor: move to .NET 6, clean code, add NuGet packaging, more examples, efficient APIs
  • Loading branch information
hzeller committed May 29, 2023
2 parents 6e53ad1 + abd7d30 commit b03d212
Show file tree
Hide file tree
Showing 37 changed files with 990 additions and 712 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -22,6 +22,7 @@ clean:
$(MAKE) -C $(PYTHON_LIB_DIR) clean

build-csharp:
$(MAKE) -C $(CSHARP_LIB_DIR) nuget
$(MAKE) -C $(CSHARP_LIB_DIR) build

build-python: $(RGB_LIBRARY)
Expand Down
7 changes: 5 additions & 2 deletions bindings/c#/.gitignore
@@ -1,2 +1,5 @@
*.dll
*.exe
**/.vs
**/bin
**/obj
**/*.sln
**/*.csproj.user
85 changes: 85 additions & 0 deletions bindings/c#/Bindings.cs
@@ -0,0 +1,85 @@
global using static RPiRgbLEDMatrix.Bindings;

using System.Runtime.InteropServices;

namespace RPiRgbLEDMatrix;

/*
Some of the extern methods listed below are marked with [SuppressGCTransition].
This disables some GC checks that may take a long time. But such methods should
be fast and trivial, otherwise the managed code may become unstable (see docs).
Keep this in mind when changing the C/C++ side.
https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.suppressgctransitionattribute
*/
internal static class Bindings
{
private const string Lib = "librgbmatrix.so.1";

[DllImport(Lib)]
public static extern IntPtr led_matrix_create(int rows, int chained, int parallel);

[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern IntPtr led_matrix_create_from_options_const_argv(
ref InternalRGBLedMatrixOptions options,
int argc,
string[] argv);

[DllImport(Lib)]
public static extern void led_matrix_delete(IntPtr matrix);

[DllImport(Lib)]
public static extern IntPtr led_matrix_create_offscreen_canvas(IntPtr matrix);

[DllImport(Lib)]
public static extern IntPtr led_matrix_swap_on_vsync(IntPtr matrix, IntPtr canvas);

[DllImport(Lib)]
public static extern IntPtr led_matrix_get_canvas(IntPtr matrix);

[DllImport(Lib)]
[SuppressGCTransition]
public static extern byte led_matrix_get_brightness(IntPtr matrix);

[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_matrix_set_brightness(IntPtr matrix, byte brightness);

[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern IntPtr load_font(string bdf_font_file);

[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern int draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b,
string utf8_text, int extra_spacing);

[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern int vertical_draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b,
string utf8_text, int kerning_offset);

[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern void delete_font(IntPtr font);

[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_canvas_get_size(IntPtr canvas, out int width, out int height);

[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_canvas_set_pixel(IntPtr canvas, int x, int y, byte r, byte g, byte b);

[DllImport(Lib)]
public static extern void led_canvas_set_pixels(IntPtr canvas, int x, int y, int width, int height,
ref Color colors);

[DllImport(Lib)]
public static extern void led_canvas_clear(IntPtr canvas);

[DllImport(Lib)]
public static extern void led_canvas_fill(IntPtr canvas, byte r, byte g, byte b);

[DllImport(Lib)]
public static extern void draw_circle(IntPtr canvas, int xx, int y, int radius, byte r, byte g, byte b);

[DllImport(Lib)]
public static extern void draw_line(IntPtr canvas, int x0, int y0, int x1, int y1, byte r, byte g, byte b);
}
38 changes: 38 additions & 0 deletions bindings/c#/Color.cs
@@ -0,0 +1,38 @@
namespace RPiRgbLEDMatrix;

/// <summary>
/// Represents an RGB (red, green, blue) color
/// </summary>
public struct Color
{
/// <summary>
/// The red component value of this instance.
/// </summary>
public byte R;

/// <summary>
/// The green component value of this instance.
/// </summary>
public byte G;

/// <summary>
/// The blue component value of this instance.
/// </summary>
public byte B;

/// <summary>
/// Creates a new color from the specified color values (red, green, and blue).
/// </summary>
/// <param name="r">The red component value.</param>
/// <param name="g">The green component value.</param>
/// <param name="b">The blue component value.</param>
public Color(int r, int g, int b) : this((byte)r, (byte)g, (byte)b) { }

/// <summary>
/// Creates a new color from the specified color values (red, green, and blue).
/// </summary>
/// <param name="r">The red component value.</param>
/// <param name="g">The green component value.</param>
/// <param name="b">The blue component value.</param>
public Color(byte r, byte g, byte b) => (R, G, B) = (r, g, b);
}
50 changes: 50 additions & 0 deletions bindings/c#/InternalRGBLedMatrixOptions.cs
@@ -0,0 +1,50 @@
using System.Runtime.InteropServices;

namespace RPiRgbLEDMatrix;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct InternalRGBLedMatrixOptions
{
public IntPtr hardware_mapping;
public int rows;
public int cols;
public int chain_length;
public int parallel;
public int pwm_bits;
public int pwm_lsb_nanoseconds;
public int pwm_dither_bits;
public int brightness;
public int scan_mode;
public int row_address_type;
public int multiplexing;
public IntPtr led_rgb_sequence;
public IntPtr pixel_mapper_config;
public IntPtr panel_type;
public byte disable_hardware_pulsing;
public byte show_refresh_rate;
public byte inverse_colors;
public int limit_refresh_rate_hz;

public InternalRGBLedMatrixOptions(RGBLedMatrixOptions opt)
{
chain_length = opt.ChainLength;
rows = opt.Rows;
cols = opt.Cols;
hardware_mapping = Marshal.StringToHGlobalAnsi(opt.HardwareMapping);
inverse_colors = (byte)(opt.InverseColors ? 1 : 0);
led_rgb_sequence = Marshal.StringToHGlobalAnsi(opt.LedRgbSequence);
pixel_mapper_config = Marshal.StringToHGlobalAnsi(opt.PixelMapperConfig);
panel_type = Marshal.StringToHGlobalAnsi(opt.PanelType);
parallel = opt.Parallel;
multiplexing = (int)opt.Multiplexing;
pwm_bits = opt.PwmBits;
pwm_lsb_nanoseconds = opt.PwmLsbNanoseconds;
pwm_dither_bits = opt.PwmDitherBits;
scan_mode = (int)opt.ScanMode;
show_refresh_rate = (byte)(opt.ShowRefreshRate ? 1 : 0);
limit_refresh_rate_hz = opt.LimitRefreshRateHz;
brightness = opt.Brightness;
disable_hardware_pulsing = (byte)(opt.DisableHardwarePulsing ? 1 : 0);
row_address_type = opt.RowAddressType;
}
};
33 changes: 23 additions & 10 deletions bindings/c#/Makefile
@@ -1,21 +1,34 @@
CSHARP_LIB=RGBLedMatrix.dll
SOURCES=RGBLedCanvas.cs RGBLedMatrix.cs RGBLedFont.cs
CSHARP_COMPILER=mcs
# This Makefile is intended to be used only by the toplevel Makefile.
# For any other purposes, use .NET SDK build tools directly

# Don't forget to synchronize these variables with the 'RPiRgbLEDMatrix.csproj' file
RGB_LIBDIR=../../lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).so.1

EXAMPLES_DIR=examples
NUGET_VERSION = 1.0.0
NUGET_ID = HZeller.RPiRgbLEDMatrix
NUGET_CONFIG = Release

$(CSHARP_LIB) : $(SOURCES) $(RGB_LIBRARY)
$(CSHARP_COMPILER) -target:library -out:$@ $(SOURCES)
NUGET_PACKAGE = /bin/$(NUGET_CONFIG)/$(NUGET_ID).$(NUGET_VERSION).nupkg

$(NUGET_PACKAGE): $(RGB_LIBRARY)
dotnet pack -c $(NUGET_CONFIG) -p:SkipNative=false -p:PackageId=$(NUGET_ID) -p:Version=$(NUGET_VERSION)

# The examples also depend on the 'RPiRgbLEDMatrix.csproj', but this will be handled by 'dotnet'
build: $(RGB_LIBRARY)
dotnet build examples/FontExample/FontExample.csproj -p:SkipNative=false
dotnet build examples/MatrixRain/MatrixRain.csproj -p:SkipNative=false
dotnet build examples/MinimalExample/MinimalExample.csproj -p:SkipNative=false
dotnet build examples/PulsingBrightness/PulsingBrightness.csproj -p:SkipNative=false
dotnet build examples/Rotating3DCube/Rotating3DCube.csproj -p:SkipNative=false
dotnet build examples/PlayGIF/PlayGIF.csproj -p:SkipNative=false

$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)

build: $(CSHARP_LIB)
$(MAKE) -C $(EXAMPLES_DIR) all
# Used by toplevel Makefile
nuget: $(NUGET_PACKAGE)

clean:
rm -f $(CSHARP_LIB)
# Used in 'RPiRgbLEDMatrix.csproj'
library: $(RGB_LIBRARY)
11 changes: 11 additions & 0 deletions bindings/c#/Multiplexing.cs
@@ -0,0 +1,11 @@
namespace RPiRgbLEDMatrix;

/// <summary>
/// Type of multiplexing.
/// </summary>
public enum Multiplexing : int
{
Direct = 0,
Stripe = 1,
Checker = 2
}
29 changes: 7 additions & 22 deletions bindings/c#/README.md
@@ -1,31 +1,16 @@
C# bindings for RGB Matrix library
C# bindings for RGB Matrix library
======================================

Building
--------

To build the C# wrapper for the RGB Matrix C library you need to first have mono installed.
To build the C# wrapper for the RGB Matrix C library you need to first have __.NET SDK__ installed.

### Install Mono
### Install .NET SDK

```shell
$ sudo apt-get update
$ sudo apt-get install mono-complete
```
`sudo apt install dotnet6` should work in most cases.
For some old distributions, read [docs](https://learn.microsoft.com/dotnet/core/install/linux)

Then, in the root directory for the matrix library type
Then, in the `bindings/c#` directory type: `dotnet build`

```shell
make build-csharp
```

To run the example applications in the c#\examples folder

```shell
sudo mono minimal-example.exe
```

Notes
--------

C# applications look for libraries in the working directory of the application. To use this library for your own projects you will need to ensure you have RGBLedMatrix.dll and librgbmatrix.so in the same folder as your exe.
To run the example applications in the c#\examples\EXAMPLE folder: `sudo dotnet run`

0 comments on commit b03d212

Please sign in to comment.