includes go-rpi-rgb-led-matrix as a dep
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
Various bindings to other programming languages.
|
||||
Typically these are wrapping the [C-binding](../include/led-matrix-c.h) that
|
||||
comes with rpi-rgb-led-matrix
|
||||
@@ -0,0 +1,2 @@
|
||||
*.dll
|
||||
*.exe
|
||||
@@ -0,0 +1,21 @@
|
||||
CSHARP_LIB=RGBLedMatrix.dll
|
||||
SOURCES=RGBLedCanvas.cs RGBLedMatrix.cs RGBLedFont.cs
|
||||
CSHARP_COMPILER=mcs
|
||||
|
||||
RGB_LIBDIR=../../lib
|
||||
RGB_LIBRARY_NAME=rgbmatrix
|
||||
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).so.1
|
||||
|
||||
EXAMPLES_DIR=examples
|
||||
|
||||
$(CSHARP_LIB) : $(SOURCES) $(RGB_LIBRARY)
|
||||
$(CSHARP_COMPILER) -target:library -out:$@ $(SOURCES)
|
||||
|
||||
$(RGB_LIBRARY):
|
||||
$(MAKE) -C $(RGB_LIBDIR)
|
||||
|
||||
build: $(CSHARP_LIB)
|
||||
$(MAKE) -C $(EXAMPLES_DIR) all
|
||||
|
||||
clean:
|
||||
rm -f $(CSHARP_LIB)
|
||||
@@ -0,0 +1,31 @@
|
||||
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.
|
||||
|
||||
### Install Mono
|
||||
|
||||
```shell
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install mono-complete
|
||||
```
|
||||
|
||||
Then, in the root directory for the matrix library type
|
||||
|
||||
```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.
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace rpi_rgb_led_matrix_sharp
|
||||
{
|
||||
public class RGBLedCanvas
|
||||
{
|
||||
#region DLLImports
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void led_canvas_get_size(IntPtr canvas, out int width, out int height);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void led_canvas_set_pixel(IntPtr canvas, int x, int y, byte r, byte g, byte b);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void led_canvas_clear(IntPtr canvas);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void led_canvas_fill(IntPtr canvas, byte r, byte g, byte b);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void draw_circle(IntPtr canvas, int xx, int y, int radius, byte r, byte g, byte b);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void draw_line(IntPtr canvas, int x0, int y0, int x1, int y1, byte r, byte g, byte b);
|
||||
#endregion
|
||||
|
||||
// This is a wrapper for canvas no need to implement IDisposable here
|
||||
// because RGBLedMatrix has ownership and takes care of disposing canvases
|
||||
internal IntPtr _canvas;
|
||||
|
||||
// this is not called directly by the consumer code,
|
||||
// consumer uses factory methods in RGBLedMatrix
|
||||
internal RGBLedCanvas(IntPtr canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
int width;
|
||||
int height;
|
||||
led_canvas_get_size(_canvas, out width, out height);
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public int Width {get; private set; }
|
||||
public int Height { get; private set; }
|
||||
|
||||
public void SetPixel(int x, int y, Color color)
|
||||
{
|
||||
led_canvas_set_pixel(_canvas, x, y, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public void Fill(Color color)
|
||||
{
|
||||
led_canvas_fill(_canvas, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
led_canvas_clear(_canvas);
|
||||
}
|
||||
|
||||
public void DrawCircle(int x0, int y0, int radius, Color color)
|
||||
{
|
||||
draw_circle(_canvas, x0, y0, radius, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public void DrawLine (int x0, int y0, int x1, int y1, Color color)
|
||||
{
|
||||
draw_line(_canvas, x0, y0, x1, y1, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public int DrawText(RGBLedFont font, int x, int y, Color color, string text, int spacing=0, bool vertical=false)
|
||||
{
|
||||
return font.DrawText(_canvas, x, y, color, text, spacing, vertical);
|
||||
}
|
||||
}
|
||||
|
||||
public struct Color
|
||||
{
|
||||
public Color (int r, int g, int b)
|
||||
{
|
||||
R = (byte)r;
|
||||
G = (byte)g;
|
||||
B = (byte)b;
|
||||
}
|
||||
public Color(byte r, byte g, byte b)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
public byte R;
|
||||
public byte G;
|
||||
public byte B;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace rpi_rgb_led_matrix_sharp
|
||||
{
|
||||
public class RGBLedFont : IDisposable
|
||||
{
|
||||
[DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
internal static extern IntPtr load_font(string bdf_font_file);
|
||||
|
||||
[DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
internal 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("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
internal 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("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
internal static extern void delete_font(IntPtr font);
|
||||
|
||||
public RGBLedFont(string bdf_font_file_path)
|
||||
{
|
||||
_font = load_font(bdf_font_file_path);
|
||||
}
|
||||
internal IntPtr _font;
|
||||
|
||||
internal int DrawText(IntPtr canvas, int x, int y, Color color, string text, int spacing=0, bool vertical=false)
|
||||
{
|
||||
if (!vertical)
|
||||
return draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing);
|
||||
else
|
||||
return vertical_draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
delete_font(_font);
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
~RGBLedFont()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace rpi_rgb_led_matrix_sharp
|
||||
{
|
||||
public class RGBLedMatrix : IDisposable
|
||||
{
|
||||
#region DLLImports
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern IntPtr led_matrix_create(int rows, int chained, int parallel);
|
||||
|
||||
[DllImport("librgbmatrix.so", CallingConvention= CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
internal static extern IntPtr led_matrix_create_from_options_const_argv(
|
||||
ref InternalRGBLedMatrixOptions options,
|
||||
int argc,
|
||||
string[] argv);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void led_matrix_delete(IntPtr matrix);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern IntPtr led_matrix_create_offscreen_canvas(IntPtr matrix);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern IntPtr led_matrix_swap_on_vsync(IntPtr matrix, IntPtr canvas);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern IntPtr led_matrix_get_canvas(IntPtr matrix);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern byte led_matrix_get_brightness(IntPtr matrix);
|
||||
|
||||
[DllImport("librgbmatrix.so")]
|
||||
internal static extern void led_matrix_set_brightness(IntPtr matrix, byte brightness);
|
||||
#endregion
|
||||
|
||||
public RGBLedMatrix(int rows, int chained, int parallel)
|
||||
{
|
||||
matrix= led_matrix_create(rows, chained, parallel);
|
||||
}
|
||||
|
||||
public RGBLedMatrix(RGBLedMatrixOptions options)
|
||||
{
|
||||
var opt = new InternalRGBLedMatrixOptions();
|
||||
|
||||
try {
|
||||
// pass in options to internal data structure
|
||||
opt.chain_length = options.ChainLength;
|
||||
opt.rows = options.Rows;
|
||||
opt.cols = options.Cols;
|
||||
opt.hardware_mapping = options.HardwareMapping != null ? Marshal.StringToHGlobalAnsi(options.HardwareMapping) : IntPtr.Zero;
|
||||
opt.inverse_colors = (byte)(options.InverseColors ? 1 : 0);
|
||||
opt.led_rgb_sequence = options.LedRgbSequence != null ? Marshal.StringToHGlobalAnsi(options.LedRgbSequence) : IntPtr.Zero;
|
||||
opt.pixel_mapper_config = options.PixelMapperConfig != null ? Marshal.StringToHGlobalAnsi(options.PixelMapperConfig) : IntPtr.Zero;
|
||||
opt.panel_type = options.PanelType != null ? Marshal.StringToHGlobalAnsi(options.PanelType) : IntPtr.Zero;
|
||||
opt.parallel = options.Parallel;
|
||||
opt.multiplexing = options.Multiplexing;
|
||||
opt.pwm_bits = options.PwmBits;
|
||||
opt.pwm_lsb_nanoseconds = options.PwmLsbNanoseconds;
|
||||
opt.pwm_dither_bits = options.PwmDitherBits;
|
||||
opt.scan_mode = options.ScanMode;
|
||||
opt.show_refresh_rate = (byte)(options.ShowRefreshRate ? 1 : 0);
|
||||
opt.limit_refresh_rate_hz = options.LimitRefreshRateHz;
|
||||
opt.brightness = options.Brightness;
|
||||
opt.disable_hardware_pulsing = (byte)(options.DisableHardwarePulsing ? 1 : 0);
|
||||
opt.row_address_type = options.RowAddressType;
|
||||
|
||||
string[] cmdline_args = Environment.GetCommandLineArgs();
|
||||
|
||||
// Because gpio-slowdown is not provided in the options struct,
|
||||
// we manually add it.
|
||||
// Let's add it first to the command-line we pass to the
|
||||
// matrix constructor, so that it can be overridden with the
|
||||
// users' commandline.
|
||||
// As always, as the _very_ first, we need to provide the
|
||||
// program name argv[0], so this is why our slowdown_arg
|
||||
// array will have these two elements.
|
||||
//
|
||||
// Given that we can't initialize the C# struct with a slowdown
|
||||
// that is not 0, we just override it here with 1 if we see 0
|
||||
// (zero only really is usable on super-slow vey old Rpi1,
|
||||
// but for everyone else, it would be a nuisance. So we use
|
||||
// 0 as our sentinel).
|
||||
string[] slowdown_arg = new string[] {cmdline_args[0], "--led-slowdown-gpio="+(options.GpioSlowdown == 0 ? 1 : options.GpioSlowdown) };
|
||||
|
||||
string[] argv = new string[ 2 + cmdline_args.Length-1];
|
||||
|
||||
// Progname + slowdown arg first
|
||||
slowdown_arg.CopyTo(argv, 0);
|
||||
|
||||
// Remaining args (excluding program name) then. This allows
|
||||
// the user to not only provide any of the other --led-*
|
||||
// options, but also override the --led-slowdown-gpio arg on
|
||||
// the commandline.
|
||||
Array.Copy(cmdline_args, 1, argv, 2, cmdline_args.Length-1);
|
||||
|
||||
int argc = argv.Length;
|
||||
|
||||
matrix = led_matrix_create_from_options_const_argv(ref opt, argc, argv);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (options.HardwareMapping != null) Marshal.FreeHGlobal(opt.hardware_mapping);
|
||||
if (options.LedRgbSequence != null) Marshal.FreeHGlobal(opt.led_rgb_sequence);
|
||||
if (options.PixelMapperConfig != null) Marshal.FreeHGlobal(opt.pixel_mapper_config);
|
||||
if (options.PanelType != null) Marshal.FreeHGlobal(opt.panel_type);
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr matrix;
|
||||
|
||||
public RGBLedCanvas CreateOffscreenCanvas()
|
||||
{
|
||||
var canvas=led_matrix_create_offscreen_canvas(matrix);
|
||||
return new RGBLedCanvas(canvas);
|
||||
}
|
||||
|
||||
public RGBLedCanvas GetCanvas()
|
||||
{
|
||||
var canvas = led_matrix_get_canvas(matrix);
|
||||
return new RGBLedCanvas(canvas);
|
||||
}
|
||||
|
||||
public RGBLedCanvas SwapOnVsync(RGBLedCanvas canvas)
|
||||
{
|
||||
canvas._canvas = led_matrix_swap_on_vsync(matrix, canvas._canvas);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
public byte Brightness
|
||||
{
|
||||
get { return led_matrix_get_brightness(matrix); }
|
||||
set { led_matrix_set_brightness(matrix, value); }
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
led_matrix_delete(matrix);
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
~RGBLedMatrix() {
|
||||
Dispose(false);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RGBLedMatrixOptions struct
|
||||
[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;
|
||||
};
|
||||
#endregion
|
||||
}
|
||||
|
||||
public struct RGBLedMatrixOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the hardware mapping used. If passed NULL here, the default is used.
|
||||
/// </summary>
|
||||
public string HardwareMapping;
|
||||
|
||||
/// <summary>
|
||||
/// The "rows" are the number of rows supported by the display, so 32 or 16.
|
||||
/// Default: 32.
|
||||
/// </summary>
|
||||
public int Rows;
|
||||
|
||||
/// <summary>
|
||||
/// The "cols" are the number of columns per panel. Typically something
|
||||
/// like 32, but also 64 is possible. Sometimes even 40.
|
||||
/// cols * chain_length is the total length of the display, so you can
|
||||
/// represent a 64 wide display as cols=32, chain=2 or cols=64, chain=1;
|
||||
/// same thing, but more convenient to think of.
|
||||
/// </summary>
|
||||
public int Cols;
|
||||
|
||||
/// <summary>
|
||||
/// The chain_length is the number of displays daisy-chained together
|
||||
/// (output of one connected to input of next). Default: 1
|
||||
/// </summary>
|
||||
public int ChainLength;
|
||||
|
||||
/// <summary>
|
||||
/// The number of parallel chains connected to the Pi; in old Pis with 26
|
||||
/// GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that can also
|
||||
/// be 2 or 3. The effective number of pixels in vertical direction is then
|
||||
/// thus rows * parallel. Default: 1
|
||||
/// </summary>
|
||||
public int Parallel;
|
||||
|
||||
/// <summary>
|
||||
/// Set PWM bits used for output. Default is 11, but if you only deal with limited
|
||||
/// comic-colors, 1 might be sufficient. Lower require less CPU and increases refresh-rate.
|
||||
/// </summary>
|
||||
public int PwmBits;
|
||||
|
||||
/// <summary>
|
||||
/// Change the base time-unit for the on-time in the lowest significant bit in
|
||||
/// nanoseconds. Higher numbers provide better quality (more accurate color, less
|
||||
/// ghosting), but have a negative impact on the frame rate.
|
||||
/// </summary>
|
||||
public int PwmLsbNanoseconds;
|
||||
|
||||
/// <summary>
|
||||
/// The lower bits can be time-dithered for higher refresh rate.
|
||||
/// </summary>
|
||||
public int PwmDitherBits;
|
||||
|
||||
/// <summary>
|
||||
/// The initial brightness of the panel in percent. Valid range is 1..100
|
||||
/// </summary>
|
||||
public int Brightness;
|
||||
|
||||
/// <summary>
|
||||
/// Scan mode: 0=progressive, 1=interlaced
|
||||
/// </summary>
|
||||
public int ScanMode;
|
||||
|
||||
/// <summary>
|
||||
/// Default row address type is 0, corresponding to direct setting of the
|
||||
/// row, while row address type 1 is used for panels that only have A/B,
|
||||
/// typically some 64x64 panels
|
||||
/// </summary>
|
||||
public int RowAddressType;
|
||||
|
||||
/// <summary>
|
||||
/// Type of multiplexing. 0 = direct, 1 = stripe, 2 = checker (typical 1:8)
|
||||
/// </summary>
|
||||
public int Multiplexing;
|
||||
|
||||
/// <summary>
|
||||
/// In case the internal sequence of mapping is not "RGB", this contains the real mapping. Some panels mix up these colors.
|
||||
/// </summary>
|
||||
public string LedRgbSequence;
|
||||
|
||||
/// <summary>
|
||||
/// A string describing a sequence of pixel mappers that should be applied
|
||||
/// to this matrix. A semicolon-separated list of pixel-mappers with optional
|
||||
/// parameter.
|
||||
public string PixelMapperConfig;
|
||||
|
||||
/// <summary>
|
||||
/// Panel type. Typically just empty, but certain panels (FM6126)
|
||||
/// requie an initialization sequence
|
||||
/// </summary>
|
||||
public string PanelType;
|
||||
|
||||
/// <summary>
|
||||
/// Allow to use the hardware subsystem to create pulses. This won't do anything if output enable is not connected to GPIO 18.
|
||||
/// </summary>
|
||||
public bool DisableHardwarePulsing;
|
||||
public bool ShowRefreshRate;
|
||||
public bool InverseColors;
|
||||
|
||||
/// <summary>
|
||||
/// Limit refresh rate of LED panel. This will help on a loaded system
|
||||
// to keep a constant refresh rate. <= 0 for no limit.
|
||||
/// </summary>
|
||||
public int LimitRefreshRateHz;
|
||||
|
||||
/// <summary>
|
||||
/// Slowdown GPIO. Needed for faster Pis/slower panels.
|
||||
/// </summary>
|
||||
public int GpioSlowdown;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
CSHARP_LIB=RGBLedMatrix.dll
|
||||
CSHARP_COMPILER=mcs
|
||||
CSHARP_LIBDIR=..
|
||||
CSHARP_LIBRARY=$(CSHARP_LIBDIR)/$(CSHARP_LIB)
|
||||
|
||||
RGB_LIBDIR=../../../lib
|
||||
RGB_LIBRARY_NAME=librgbmatrix
|
||||
RGB_LIBRARY=$(RGB_LIBDIR)/$(RGB_LIBRARY_NAME).so.1
|
||||
|
||||
all: $(CSHARP_LIB)
|
||||
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
|
||||
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:minimal-example.exe minimal-example.cs
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:matrix-rain.exe matrix-rain.cs
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:font-example.exe font-example.cs
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:pulsing-brightness.exe pulsing-brightness.cs
|
||||
|
||||
minimal-example.exe: $(CSHARP_LIB)
|
||||
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
|
||||
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:minimal-example.exe minimal-example.cs
|
||||
|
||||
matrix-rain.exe: $(CSHARP_LIB)
|
||||
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
|
||||
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:matrix-rain.exe matrix-rain.cs
|
||||
|
||||
font-example.exe: $(CSHARP_LIB)
|
||||
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
|
||||
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:font-example.exe font-example.cs
|
||||
|
||||
pulsing-brightness.exe: $(CSHARP_LIB)
|
||||
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
|
||||
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
|
||||
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:pulsing-brightness.exe pulsing-brightness.cs
|
||||
|
||||
$(CSHARP_LIB) :
|
||||
$(MAKE) -C $(CSHARP_LIBDIR)
|
||||
|
||||
.PHONY : all
|
||||
@@ -0,0 +1,36 @@
|
||||
using rpi_rgb_led_matrix_sharp;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace font_example
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine("font-example.exe [font_path] <text>");
|
||||
return -1;
|
||||
}
|
||||
string text = "Hello World!";
|
||||
if (args.Length > 1)
|
||||
text = args[1];
|
||||
|
||||
|
||||
var matrix = new RGBLedMatrix(32, 2, 1);
|
||||
var canvas = matrix.CreateOffscreenCanvas();
|
||||
var font = new RGBLedFont(args[0]);
|
||||
|
||||
canvas.DrawText(font, 1, 6, new Color(0, 255, 0), text);
|
||||
matrix.SwapOnVsync(canvas);
|
||||
|
||||
while (!Console.KeyAvailable)
|
||||
{
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using rpi_rgb_led_matrix_sharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace matrix_rain
|
||||
{
|
||||
class Program
|
||||
{
|
||||
const int MAX_HEIGHT = 16;
|
||||
const int COLOR_STEP = 15;
|
||||
const int FRAME_STEP = 1;
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
|
||||
var matrix = new RGBLedMatrix(new RGBLedMatrixOptions { ChainLength = 2 });
|
||||
var canvas = matrix.CreateOffscreenCanvas();
|
||||
var rnd = new Random();
|
||||
var points = new List<Point>();
|
||||
var recycled = new Stack<Point>();
|
||||
int frame = 0;
|
||||
var stopwatch = new Stopwatch();
|
||||
|
||||
while (!Console.KeyAvailable) {
|
||||
stopwatch.Restart();
|
||||
|
||||
frame++;
|
||||
|
||||
if (frame % FRAME_STEP == 0)
|
||||
{
|
||||
if (recycled.Count == 0)
|
||||
points.Add(new Point(rnd.Next(0, canvas.Width - 1), 0));
|
||||
else
|
||||
{
|
||||
var point = recycled.Pop();
|
||||
point.x = rnd.Next(0, canvas.Width - 1);
|
||||
point.y = 0;
|
||||
point.recycled = false;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.Clear();
|
||||
|
||||
foreach (var point in points)
|
||||
{
|
||||
if (!point.recycled)
|
||||
{
|
||||
point.y++;
|
||||
|
||||
if (point.y - MAX_HEIGHT > canvas.Height)
|
||||
{
|
||||
point.recycled = true;
|
||||
recycled.Push(point);
|
||||
}
|
||||
|
||||
for (var i=0; i< MAX_HEIGHT; i++)
|
||||
{
|
||||
canvas.SetPixel(point.x, point.y - i, new Color(0, 255 - i * COLOR_STEP, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvas = matrix.SwapOnVsync(canvas);
|
||||
|
||||
// force 30 FPS
|
||||
var elapsed= stopwatch.ElapsedMilliseconds;
|
||||
if (elapsed < 33)
|
||||
{
|
||||
Thread.Sleep(33 - (int)elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class Point
|
||||
{
|
||||
public Point(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
public int x;
|
||||
public int y;
|
||||
public bool recycled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using rpi_rgb_led_matrix_sharp;
|
||||
|
||||
namespace minimal_example
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
|
||||
var matrix= new RGBLedMatrix(32, 2, 1);
|
||||
var canvas = matrix.CreateOffscreenCanvas();
|
||||
|
||||
for (var i = 0; i < 1000; ++i)
|
||||
{
|
||||
for (var y = 0; y < canvas.Height; ++y)
|
||||
{
|
||||
for (var x = 0; x < canvas.Width; ++x)
|
||||
{
|
||||
canvas.SetPixel(x, y, new Color(i & 0xff, x, y));
|
||||
}
|
||||
}
|
||||
canvas.DrawCircle(canvas.Width / 2, canvas.Height / 2, 6, new Color(0, 0, 255));
|
||||
canvas.DrawLine(canvas.Width / 2 - 3, canvas.Height / 2 - 3, canvas.Width / 2 + 3, canvas.Height / 2 + 3, new Color(0, 0, 255));
|
||||
canvas.DrawLine(canvas.Width / 2 - 3, canvas.Height / 2 + 3, canvas.Width / 2 + 3, canvas.Height / 2 - 3, new Color(0, 0, 255));
|
||||
|
||||
canvas = matrix.SwapOnVsync(canvas);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
using rpi_rgb_led_matrix_sharp;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace pulsing_brightness
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
var matrix = new RGBLedMatrix(new RGBLedMatrixOptions {Rows = 32, Cols = 64});
|
||||
var canvas = matrix.CreateOffscreenCanvas();
|
||||
var maxBrightness = matrix.Brightness;
|
||||
var count = 0;
|
||||
const int c = 255;
|
||||
|
||||
while (!Console.KeyAvailable)
|
||||
{
|
||||
if (matrix.Brightness < 1)
|
||||
{
|
||||
matrix.Brightness = maxBrightness;
|
||||
count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
matrix.Brightness -= 1;
|
||||
}
|
||||
|
||||
switch (count % 4)
|
||||
{
|
||||
case 0:
|
||||
canvas.Fill(new Color(c, 0, 0));
|
||||
break;
|
||||
case 1:
|
||||
canvas.Fill(new Color(0, c, 0));
|
||||
break;
|
||||
case 2:
|
||||
canvas.Fill(new Color(0, 0, c));
|
||||
break;
|
||||
case 3:
|
||||
canvas.Fill(new Color(c, c, c));
|
||||
break;
|
||||
}
|
||||
|
||||
canvas = matrix.SwapOnVsync(canvas);
|
||||
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
build/*
|
||||
@@ -0,0 +1,92 @@
|
||||
# TODO: This contains a lot of {c|p}ython build boilerplate, this needs cleanup.
|
||||
PYTHON ?= python
|
||||
SETUP := setup.py
|
||||
BUILD_ARGS := build --build-lib .
|
||||
INST_ARGS := install
|
||||
ifdef DESTDIR
|
||||
INST_ARGS += --root=$(DESTDIR)
|
||||
endif
|
||||
CLEAN_ARGS := clean --all
|
||||
|
||||
MANPAGES := $(patsubst %.txt,%,$(wildcard *.txt))
|
||||
TXTTOMAN := a2x -f manpage
|
||||
|
||||
# Where our library resides. It is split between includes and the binary
|
||||
# library in lib
|
||||
RGB_LIBDIR=../../lib
|
||||
RGB_LIBRARY_NAME=rgbmatrix
|
||||
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
|
||||
|
||||
ifneq "$(wildcard debian/changelog)" ""
|
||||
PKGNAME := $(shell dpkg-parsechangelog | sed -n 's/^Source: //p')
|
||||
VERSION := $(shell dpkg-parsechangelog | sed -n 's/^Version: \([^-]*\).*/\1/p')
|
||||
UPSDIST := $(PKGNAME)-$(VERSION).tar.gz
|
||||
DEBDIST := $(PKGNAME)_$(VERSION).orig.tar.gz
|
||||
endif
|
||||
|
||||
all: build
|
||||
build: build-python
|
||||
install: install-python
|
||||
clean: clean-python
|
||||
find ./rgbmatrix -type f -name \*.so -delete
|
||||
find . -type f -name \*.pyc -delete
|
||||
$(RM) build-* install-* test-*
|
||||
|
||||
$(RGB_LIBRARY): FORCE
|
||||
$(MAKE) -C $(RGB_LIBDIR)
|
||||
|
||||
test: test-python
|
||||
test-python:
|
||||
ifneq "$(wildcard tests/*.py)" ""
|
||||
nosetests -v -w tests
|
||||
else
|
||||
$(info Test suite is not implemented...)
|
||||
endif
|
||||
|
||||
ifneq "$(wildcard debian/control)" ""
|
||||
PYVERS := $(shell pyversions -r -v debian/control)
|
||||
PYEXEC := $(shell pyversions -d)
|
||||
BUILD_ARGS += --executable=/usr/bin/$(PYEXEC)
|
||||
INST_ARGS += --no-compile -O0
|
||||
|
||||
build-python: $(PYVERS:%=build-python-%)
|
||||
build-python-%: $(RGB_LIBRARY)
|
||||
$(info * Doing build for $(PYTHON)$* ...)
|
||||
$(PYTHON)$* $(SETUP) $(BUILD_ARGS)
|
||||
|
||||
install-python: $(PYVERS:%=install-python-%)
|
||||
install-python-%:
|
||||
$(info * Doing install for $(PYTHON)$* ...)
|
||||
$(PYTHON)$* $(SETUP) $(INST_ARGS)
|
||||
|
||||
clean-python: $(PYVERS:%=clean-python-%)
|
||||
clean-python-%:
|
||||
$(PYTHON)$* $(SETUP) $(CLEAN_ARGS)
|
||||
else
|
||||
build-python: $(RGB_LIBRARY)
|
||||
$(PYTHON) $(SETUP) $(BUILD_ARGS)
|
||||
|
||||
install-python:
|
||||
$(PYTHON) $(SETUP) $(INST_ARGS)
|
||||
|
||||
clean-python:
|
||||
$(PYTHON) $(SETUP) $(CLEAN_ARGS)
|
||||
endif
|
||||
|
||||
distclean: clean
|
||||
dist: distclean
|
||||
$(info * Creating ../$(UPSDIST) and ../$(DEBDIST))
|
||||
@tar --exclude='.svn' \
|
||||
--exclude='*.swp' \
|
||||
--exclude='debian' \
|
||||
-czvf ../$(UPSDIST) \
|
||||
-C ../ $(notdir $(CURDIR))
|
||||
@cp ../$(UPSDIST) ../$(DEBDIST)
|
||||
@if test -d ../tarballs; then \
|
||||
mv -v ../$(DEBDIST) ../tarballs; \
|
||||
fi
|
||||
|
||||
FORCE:
|
||||
.PHONY: FORCE
|
||||
.PHONY: build install test clean dist distclean
|
||||
.PHONY: build-python install-python clean-python
|
||||
@@ -0,0 +1,147 @@
|
||||
Python bindings for RGB Matrix library
|
||||
======================================
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
If you have a different than the standard wiring (for instance if you have an
|
||||
Adafruit HAT), you can edit the [../../lib/Makefile](../../lib/Makefile#L26) first to choose
|
||||
the hardware in question (see below for setting it via command line argument).
|
||||
|
||||
Then, in the root directory for the matrix library simply type:
|
||||
|
||||
### Python 2
|
||||
|
||||
```shell
|
||||
sudo apt-get update && sudo apt-get install python2.7-dev python-pillow -y
|
||||
make build-python
|
||||
sudo make install-python
|
||||
```
|
||||
|
||||
### Python 3
|
||||
You can also build for Python 3:
|
||||
|
||||
```shell
|
||||
sudo apt-get update && sudo apt-get install python3-dev python3-pillow -y
|
||||
make build-python PYTHON=$(which python3)
|
||||
sudo make install-python PYTHON=$(which python3)
|
||||
```
|
||||
|
||||
### PyPy
|
||||
The cython binding to PyPy seems to be somewhat working but extremely slow (20x
|
||||
slower even than the regular Python binding, 160x slower than C++), so this is
|
||||
not recommended.
|
||||
|
||||
So Cython is not good together with PyPy which works best with a
|
||||
[CFFI](https://cffi.readthedocs.io/) binding. @Duality4Y did an experiment here
|
||||
https://github.com/Duality4Y/rgb-matrix-cffi which works well with PyPy and is
|
||||
about twice as fast as running Python3+cython (but Python3+cffi is slower than
|
||||
Python3+cython, so we can't just replace everything with cffi).
|
||||
|
||||
Of course, it would be nice to have the fastest possible binding to all kinds
|
||||
of Python interpreters. If anyone wants to work on that, this would certainly
|
||||
be a welcome pull request.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
The simplicity of scripting comes at a price: Python is slower than C++ of
|
||||
course.
|
||||
If you have to do a lot of pixel updates in your demo, this can be too slow
|
||||
depending on what you do. Here are some rough numbers for calling `SetPixel()`
|
||||
in a tight loop:
|
||||
|
||||
* On a Pi-2 and Pi-3, a Python script will be about 1/8 of the speed compared
|
||||
to the corresponding C++ program (pushing ~0.43 Megapixels/s Python
|
||||
vs. ~3.5 Megapixels/s C++ on a Pi-3 for instance)
|
||||
* On a Pi-1, the difference is even worse: 1/24 of the speed to the
|
||||
corresponding C++ program. Given that the Pi-1 is already about 1/10 the
|
||||
speed of a Pi-3, this almost makes Python unusable on a Pi-1
|
||||
(~0.015 Megapixels/s Python vs. ~0.36 Megapixels/s C++)
|
||||
* Also interesting: Python3 is a little bit slower than Python2.7.
|
||||
So if you can, stick with Python2.7 for now.
|
||||
* The good news is, that this is due to overhead per function call. If you
|
||||
can do more per function call, then this is less problematic. For instance
|
||||
if you have an image to be displayed with `SetImage()`, that will much faster
|
||||
per pixel (internally this then copies the pixels natively).
|
||||
|
||||
The ~0.015 Megapixels/s on a Pi-1 means that you can update a 32x32 matrix
|
||||
at most with ~15fps. If you have chained 5, then you barely reach 3fps.
|
||||
In a Pi-3, you get about 400fps update rate (85fps for 5-chain) with a Python
|
||||
program (while with C++, you can do the same thing with a comfortable 3500fps
|
||||
(700fps for 5)). Keep in mind that this is if all you do is just calling
|
||||
`SetPixel()`, it does not include any time of what you actually want to do in
|
||||
your demo - so anything in addition to that will drop your update rate.
|
||||
|
||||
If you can prepare the animation you want to show, then you can either prepare
|
||||
images and then use the much faster call to `SetImage()`, or can fill
|
||||
entire offscreen-frames (create with `CreateFrameCanvas()`) and then
|
||||
swap with `SwapOnVSync()` (this is the fastest method).
|
||||
|
||||
Using the library
|
||||
-----------------
|
||||
|
||||
Be aware of the fact that using the full performance of the RGBMatrix requires root privileges.
|
||||
Therefore you should run all you python scripts as using `sudo`.
|
||||
|
||||
You may find examples in the [samples/](./samples) subdirectory.
|
||||
The examples all use the [samplebase.py](./samples/samplebase.py) that provides
|
||||
some basic capabilities to all example programs, such as command-line parsing: all
|
||||
sample-programs accept `--led-rows`, `--led-chain` and `--led-parallel` as
|
||||
command line options to adapt to your configuration
|
||||
|
||||
```bash
|
||||
cd samples
|
||||
sudo ./runtext.py --led-chain=4
|
||||
```
|
||||
|
||||
To use different wiring without recompiling the library to change the default,
|
||||
you can use `--led-gpio-mapping` (or `-m`). For example, to use Adafruit HAT:
|
||||
```bash
|
||||
sudo ./runtext.py --led-gpio-mapping=adafruit-hat
|
||||
```
|
||||
|
||||
Here is a complete example showing how to write an image viewer:
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
import time
|
||||
import sys
|
||||
|
||||
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
||||
from PIL import Image
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("Require an image argument")
|
||||
else:
|
||||
image_file = sys.argv[1]
|
||||
|
||||
image = Image.open(image_file)
|
||||
|
||||
# Configuration for the matrix
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = 32
|
||||
options.chain_length = 1
|
||||
options.parallel = 1
|
||||
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
|
||||
|
||||
matrix = RGBMatrix(options = options)
|
||||
|
||||
# Make image fit our screen.
|
||||
image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
|
||||
|
||||
matrix.SetImage(image.convert('RGB'))
|
||||
|
||||
try:
|
||||
print("Press CTRL-C to stop.")
|
||||
while True:
|
||||
time.sleep(100)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
The source of truth for what is available in the Python bindings may be found [here](rgbmatrix/core.pyx) (RGBMatrix, FrameCanvas, RGBMatrixOptions) and [here](rgbmatrix/graphics.pyx) (graphics). The underlying implementation's ground truth documentation may be found [here](../../include), specifically for [RGBMatrix, RGBMatrixOptions, and FrameCanvas](../../include/led-matrix.h), [Canvas](../../include/canvas.h) (base class of RGBMatrix), and [graphics methods and Font](../../include/graphics.h).
|
||||
|
||||
### User
|
||||
|
||||
As noted in the Performance section above, Python programs not run as `root` will not be as high-performance as those run as `root`. When running as `root`, be aware of a potentially-unexpected behavior: to reduce the security attack surface, initializing an RGBMatrix as `root` changes the user from `root` to `daemon` (see [#1170](https://github.com/hzeller/rpi-rgb-led-matrix/issues/1170) for more information) by default. This means, for instance, that some file operations possible before initializing the RGBMatrix will not be possible after initialization. To disable this behavior, set `drop_privileges=False` in RGBMatrixOptions, but be aware that doing so will reduce security.
|
||||
@@ -0,0 +1,15 @@
|
||||
# The *.cpp files are included in the distribution; this is only needed when
|
||||
# working on the pyx files.
|
||||
#
|
||||
# Please check in modified *.cpp files with distribution to not require cython
|
||||
# to be installed on the users' machine.
|
||||
# for python3: make PYTHON=$(which python3) CYTHON=$(which cython3)
|
||||
CYTHON ?= cython
|
||||
|
||||
all : core.cpp graphics.cpp
|
||||
|
||||
%.cpp : %.pyx
|
||||
$(CYTHON) --cplus -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -rf core.cpp graphics.cpp
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
__version__ = "0.0.1"
|
||||
__author__ = "Christoph Friedrich <christoph.friedrich@vonaffenfels.de>"
|
||||
|
||||
from .core import RGBMatrix, FrameCanvas, RGBMatrixOptions
|
||||
File diff suppressed because it is too large
Load Diff
+24
@@ -0,0 +1,24 @@
|
||||
cimport cppinc
|
||||
|
||||
cdef class Canvas:
|
||||
cdef cppinc.Canvas *__getCanvas(self) except +
|
||||
|
||||
cdef class FrameCanvas(Canvas):
|
||||
cdef cppinc.FrameCanvas *__canvas
|
||||
|
||||
cdef class RGBMatrix(Canvas):
|
||||
cdef cppinc.RGBMatrix *__matrix
|
||||
|
||||
cdef class RGBMatrixOptions:
|
||||
cdef cppinc.Options __options
|
||||
cdef cppinc.RuntimeOptions __runtime_options
|
||||
# Must keep a reference to the encoded bytes for the strings,
|
||||
# otherwise, when the Options struct is used, it will be garbage collected
|
||||
cdef bytes __py_encoded_hardware_mapping
|
||||
cdef bytes __py_encoded_led_rgb_sequence
|
||||
cdef bytes __py_encoded_pixel_mapper_config
|
||||
cdef bytes __py_encoded_panel_type
|
||||
|
||||
# Local Variables:
|
||||
# mode: python
|
||||
# End:
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
# distutils: language = c++
|
||||
|
||||
from libcpp cimport bool
|
||||
from libc.stdint cimport uint8_t, uint32_t, uintptr_t
|
||||
from PIL import Image
|
||||
import cython
|
||||
|
||||
cdef class Canvas:
|
||||
cdef cppinc.Canvas* __getCanvas(self) except +:
|
||||
raise Exception("Not implemented")
|
||||
|
||||
def SetImage(self, image, int offset_x = 0, int offset_y = 0, unsafe=True):
|
||||
if (image.mode != "RGB"):
|
||||
raise Exception("Currently, only RGB mode is supported for SetImage(). Please create images with mode 'RGB' or convert first with image = image.convert('RGB'). Pull requests to support more modes natively are also welcome :)")
|
||||
|
||||
if unsafe:
|
||||
#In unsafe mode we directly access the underlying PIL image array
|
||||
#in cython, which is considered unsafe pointer accecss,
|
||||
#however it's super fast and seems to work fine
|
||||
#https://groups.google.com/forum/#!topic/cython-users/Dc1ft5W6KM4
|
||||
img_width, img_height = image.size
|
||||
self.SetPixelsPillow(offset_x, offset_y, img_width, img_height, image)
|
||||
else:
|
||||
# First implementation of a SetImage(). OPTIMIZE_ME: A more native
|
||||
# implementation that directly reads the buffer and calls the underlying
|
||||
# C functions can certainly be faster.
|
||||
img_width, img_height = image.size
|
||||
pixels = image.load()
|
||||
for x in range(max(0, -offset_x), min(img_width, self.width - offset_x)):
|
||||
for y in range(max(0, -offset_y), min(img_height, self.height - offset_y)):
|
||||
(r, g, b) = pixels[x, y]
|
||||
self.SetPixel(x + offset_x, y + offset_y, r, g, b)
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
def SetPixelsPillow(self, int xstart, int ystart, int width, int height, image):
|
||||
cdef cppinc.FrameCanvas* my_canvas = <cppinc.FrameCanvas*>self.__getCanvas()
|
||||
cdef int frame_width = my_canvas.width()
|
||||
cdef int frame_height = my_canvas.height()
|
||||
cdef int row, col
|
||||
cdef uint8_t r, g, b
|
||||
cdef uint32_t **image_ptr
|
||||
cdef uint32_t pixel
|
||||
image.load()
|
||||
ptr_tmp = dict(image.im.unsafe_ptrs)['image32']
|
||||
image_ptr = (<uint32_t **>(<uintptr_t>ptr_tmp))
|
||||
|
||||
for col in range(max(0, -xstart), min(width, frame_width - xstart)):
|
||||
for row in range(max(0, -ystart), min(height, frame_height - ystart)):
|
||||
pixel = image_ptr[row][col]
|
||||
r = (pixel ) & 0xFF
|
||||
g = (pixel >> 8) & 0xFF
|
||||
b = (pixel >> 16) & 0xFF
|
||||
my_canvas.SetPixel(xstart+col, ystart+row, r, g, b)
|
||||
|
||||
cdef class FrameCanvas(Canvas):
|
||||
def __dealloc__(self):
|
||||
if <void*>self.__canvas != NULL:
|
||||
self.__canvas = NULL
|
||||
|
||||
cdef cppinc.Canvas* __getCanvas(self) except *:
|
||||
if <void*>self.__canvas != NULL:
|
||||
return self.__canvas
|
||||
raise Exception("Canvas was destroyed or not initialized, you cannot use this object anymore")
|
||||
|
||||
def Fill(self, uint8_t red, uint8_t green, uint8_t blue):
|
||||
(<cppinc.FrameCanvas*>self.__getCanvas()).Fill(red, green, blue)
|
||||
|
||||
def Clear(self):
|
||||
(<cppinc.FrameCanvas*>self.__getCanvas()).Clear()
|
||||
|
||||
def SetPixel(self, int x, int y, uint8_t red, uint8_t green, uint8_t blue):
|
||||
(<cppinc.FrameCanvas*>self.__getCanvas()).SetPixel(x, y, red, green, blue)
|
||||
|
||||
|
||||
property width:
|
||||
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).width()
|
||||
|
||||
property height:
|
||||
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).height()
|
||||
|
||||
property pwmBits:
|
||||
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).pwmbits()
|
||||
def __set__(self, pwmBits): (<cppinc.FrameCanvas*>self.__getCanvas()).SetPWMBits(pwmBits)
|
||||
|
||||
property brightness:
|
||||
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).brightness()
|
||||
def __set__(self, val): (<cppinc.FrameCanvas*>self.__getCanvas()).SetBrightness(val)
|
||||
|
||||
|
||||
cdef class RGBMatrixOptions:
|
||||
def __cinit__(self):
|
||||
self.__options = cppinc.Options()
|
||||
self.__runtime_options = cppinc.RuntimeOptions()
|
||||
|
||||
# RGBMatrix::Options properties
|
||||
property hardware_mapping:
|
||||
def __get__(self): return self.__options.hardware_mapping
|
||||
def __set__(self, value):
|
||||
self.__py_encoded_hardware_mapping = value.encode('utf-8')
|
||||
self.__options.hardware_mapping = self.__py_encoded_hardware_mapping
|
||||
|
||||
property rows:
|
||||
def __get__(self): return self.__options.rows
|
||||
def __set__(self, uint8_t value): self.__options.rows = value
|
||||
|
||||
property cols:
|
||||
def __get__(self): return self.__options.cols
|
||||
def __set__(self, uint8_t value): self.__options.cols = value
|
||||
|
||||
property chain_length:
|
||||
def __get__(self): return self.__options.chain_length
|
||||
def __set__(self, uint8_t value): self.__options.chain_length = value
|
||||
|
||||
property parallel:
|
||||
def __get__(self): return self.__options.parallel
|
||||
def __set__(self, uint8_t value): self.__options.parallel = value
|
||||
|
||||
property pwm_bits:
|
||||
def __get__(self): return self.__options.pwm_bits
|
||||
def __set__(self, uint8_t value): self.__options.pwm_bits = value
|
||||
|
||||
property pwm_lsb_nanoseconds:
|
||||
def __get__(self): return self.__options.pwm_lsb_nanoseconds
|
||||
def __set__(self, uint32_t value): self.__options.pwm_lsb_nanoseconds = value
|
||||
|
||||
property brightness:
|
||||
def __get__(self): return self.__options.brightness
|
||||
def __set__(self, uint8_t value): self.__options.brightness = value
|
||||
|
||||
property scan_mode:
|
||||
def __get__(self): return self.__options.scan_mode
|
||||
def __set__(self, uint8_t value): self.__options.scan_mode = value
|
||||
|
||||
property multiplexing:
|
||||
def __get__(self): return self.__options.multiplexing
|
||||
def __set__(self, uint8_t value): self.__options.multiplexing = value
|
||||
|
||||
property row_address_type:
|
||||
def __get__(self): return self.__options.row_address_type
|
||||
def __set__(self, uint8_t value): self.__options.row_address_type = value
|
||||
|
||||
property disable_hardware_pulsing:
|
||||
def __get__(self): return self.__options.disable_hardware_pulsing
|
||||
def __set__(self, value): self.__options.disable_hardware_pulsing = value
|
||||
|
||||
property show_refresh_rate:
|
||||
def __get__(self): return self.__options.show_refresh_rate
|
||||
def __set__(self, value): self.__options.show_refresh_rate = value
|
||||
|
||||
property inverse_colors:
|
||||
def __get__(self): return self.__options.inverse_colors
|
||||
def __set__(self, value): self.__options.inverse_colors = value
|
||||
|
||||
property led_rgb_sequence:
|
||||
def __get__(self): return self.__options.led_rgb_sequence
|
||||
def __set__(self, value):
|
||||
self.__py_encoded_led_rgb_sequence = value.encode('utf-8')
|
||||
self.__options.led_rgb_sequence = self.__py_encoded_led_rgb_sequence
|
||||
|
||||
property pixel_mapper_config:
|
||||
def __get__(self): return self.__options.pixel_mapper_config
|
||||
def __set__(self, value):
|
||||
self.__py_encoded_pixel_mapper_config = value.encode('utf-8')
|
||||
self.__options.pixel_mapper_config = self.__py_encoded_pixel_mapper_config
|
||||
|
||||
property panel_type:
|
||||
def __get__(self): return self.__options.panel_type
|
||||
def __set__(self, value):
|
||||
self.__py_encoded_panel_type = value.encode('utf-8')
|
||||
self.__options.panel_type = self.__py_encoded_panel_type
|
||||
|
||||
property pwm_dither_bits:
|
||||
def __get__(self): return self.__options.pwm_dither_bits
|
||||
def __set__(self, uint8_t value): self.__options.pwm_dither_bits = value
|
||||
|
||||
property limit_refresh_rate_hz:
|
||||
def __get__(self): return self.__options.limit_refresh_rate_hz
|
||||
def __set__(self, value): self.__options.limit_refresh_rate_hz = value
|
||||
|
||||
|
||||
# RuntimeOptions properties
|
||||
|
||||
property gpio_slowdown:
|
||||
def __get__(self): return self.__runtime_options.gpio_slowdown
|
||||
def __set__(self, uint8_t value): self.__runtime_options.gpio_slowdown = value
|
||||
|
||||
property daemon:
|
||||
def __get__(self): return self.__runtime_options.daemon
|
||||
def __set__(self, uint8_t value): self.__runtime_options.daemon = value
|
||||
|
||||
property drop_privileges:
|
||||
def __get__(self): return self.__runtime_options.drop_privileges
|
||||
def __set__(self, uint8_t value): self.__runtime_options.drop_privileges = value
|
||||
|
||||
cdef class RGBMatrix(Canvas):
|
||||
def __cinit__(self, int rows = 0, int chains = 0, int parallel = 0,
|
||||
RGBMatrixOptions options = None):
|
||||
|
||||
# If RGBMatrixOptions not provided, create defaults and set any optional
|
||||
# parameters supplied
|
||||
if options == None:
|
||||
options = RGBMatrixOptions()
|
||||
|
||||
if rows > 0:
|
||||
options.rows = rows
|
||||
if chains > 0:
|
||||
options.chain_length = chains
|
||||
if parallel > 0:
|
||||
options.parallel = parallel
|
||||
|
||||
self.__matrix = cppinc.CreateMatrixFromOptions(options.__options,
|
||||
options.__runtime_options)
|
||||
|
||||
def __dealloc__(self):
|
||||
self.__matrix.Clear()
|
||||
del self.__matrix
|
||||
|
||||
cdef cppinc.Canvas* __getCanvas(self) except *:
|
||||
if <void*>self.__matrix != NULL:
|
||||
return self.__matrix
|
||||
raise Exception("Canvas was destroyed or not initialized, you cannot use this object anymore")
|
||||
|
||||
def Fill(self, uint8_t red, uint8_t green, uint8_t blue):
|
||||
self.__matrix.Fill(red, green, blue)
|
||||
|
||||
def SetPixel(self, int x, int y, uint8_t red, uint8_t green, uint8_t blue):
|
||||
self.__matrix.SetPixel(x, y, red, green, blue)
|
||||
|
||||
def Clear(self):
|
||||
self.__matrix.Clear()
|
||||
|
||||
def CreateFrameCanvas(self):
|
||||
return __createFrameCanvas(self.__matrix.CreateFrameCanvas())
|
||||
|
||||
def SwapOnVSync(self, FrameCanvas newFrame):
|
||||
return __createFrameCanvas(self.__matrix.SwapOnVSync(newFrame.__canvas))
|
||||
|
||||
property luminanceCorrect:
|
||||
def __get__(self): return self.__matrix.luminance_correct()
|
||||
def __set__(self, luminanceCorrect): self.__matrix.set_luminance_correct(luminanceCorrect)
|
||||
|
||||
property pwmBits:
|
||||
def __get__(self): return self.__matrix.pwmbits()
|
||||
def __set__(self, pwmBits): self.__matrix.SetPWMBits(pwmBits)
|
||||
|
||||
property brightness:
|
||||
def __get__(self): return self.__matrix.brightness()
|
||||
def __set__(self, brightness): self.__matrix.SetBrightness(brightness)
|
||||
|
||||
property height:
|
||||
def __get__(self): return self.__matrix.height()
|
||||
|
||||
property width:
|
||||
def __get__(self): return self.__matrix.width()
|
||||
|
||||
cdef __createFrameCanvas(cppinc.FrameCanvas* newCanvas):
|
||||
canvas = FrameCanvas()
|
||||
canvas.__canvas = newCanvas
|
||||
return canvas
|
||||
|
||||
# Local Variables:
|
||||
# mode: python
|
||||
# End:
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
from libcpp cimport bool
|
||||
from libc.stdint cimport uint8_t, uint32_t
|
||||
|
||||
########################
|
||||
### External classes ###
|
||||
########################
|
||||
|
||||
cdef extern from "canvas.h" namespace "rgb_matrix":
|
||||
cdef cppclass Canvas:
|
||||
int width()
|
||||
int height()
|
||||
void SetPixel(int, int, uint8_t, uint8_t, uint8_t) nogil
|
||||
void Clear() nogil
|
||||
void Fill(uint8_t, uint8_t, uint8_t) nogil
|
||||
|
||||
cdef extern from "led-matrix.h" namespace "rgb_matrix":
|
||||
cdef cppclass RGBMatrix(Canvas):
|
||||
bool SetPWMBits(uint8_t)
|
||||
uint8_t pwmbits()
|
||||
void set_luminance_correct(bool)
|
||||
bool luminance_correct()
|
||||
void SetBrightness(uint8_t)
|
||||
uint8_t brightness()
|
||||
FrameCanvas *CreateFrameCanvas()
|
||||
FrameCanvas *SwapOnVSync(FrameCanvas*)
|
||||
|
||||
cdef cppclass FrameCanvas(Canvas):
|
||||
bool SetPWMBits(uint8_t)
|
||||
uint8_t pwmbits()
|
||||
void SetBrightness(uint8_t)
|
||||
uint8_t brightness()
|
||||
|
||||
struct RuntimeOptions:
|
||||
RuntimeOptions() except +
|
||||
int gpio_slowdown
|
||||
int daemon
|
||||
int drop_privileges
|
||||
|
||||
|
||||
RGBMatrix *CreateMatrixFromOptions(Options &options, RuntimeOptions runtime_options)
|
||||
|
||||
|
||||
|
||||
cdef extern from "led-matrix.h" namespace "rgb_matrix::RGBMatrix":
|
||||
cdef struct Options:
|
||||
Options() except +
|
||||
|
||||
const char *hardware_mapping
|
||||
|
||||
int rows
|
||||
int cols
|
||||
int chain_length
|
||||
int parallel
|
||||
int pwm_bits
|
||||
int pwm_lsb_nanoseconds
|
||||
int brightness
|
||||
int scan_mode
|
||||
int row_address_type
|
||||
int multiplexing
|
||||
int pwm_dither_bits
|
||||
int limit_refresh_rate_hz
|
||||
|
||||
bool disable_hardware_pulsing
|
||||
bool show_refresh_rate
|
||||
bool inverse_colors
|
||||
|
||||
const char *led_rgb_sequence
|
||||
const char *pixel_mapper_config
|
||||
const char *panel_type
|
||||
|
||||
cdef extern from "graphics.h" namespace "rgb_matrix":
|
||||
cdef struct Color:
|
||||
Color(uint8_t, uint8_t, uint8_t) except +
|
||||
uint8_t r
|
||||
uint8_t g
|
||||
uint8_t b
|
||||
|
||||
cdef cppclass Font:
|
||||
Font() except +
|
||||
bool LoadFont(const char*)
|
||||
int height()
|
||||
int baseline()
|
||||
int CharacterWidth(uint32_t)
|
||||
int DrawGlyph(Canvas*, int, int, const Color, uint32_t);
|
||||
|
||||
cdef int DrawText(Canvas*, const Font, int, int, const Color, const char*)
|
||||
cdef void DrawCircle(Canvas*, int, int, int, const Color)
|
||||
cdef void DrawLine(Canvas*, int, int, int, int, const Color)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
cimport cppinc
|
||||
|
||||
cdef class Color:
|
||||
cdef cppinc.Color __color
|
||||
|
||||
cdef class Font:
|
||||
cdef cppinc.Font __font
|
||||
|
||||
# Local Variables:
|
||||
# mode: python
|
||||
# End:
|
||||
@@ -0,0 +1,54 @@
|
||||
# distutils: language = c++
|
||||
|
||||
from libcpp cimport bool
|
||||
from libc.stdint cimport uint8_t, uint32_t
|
||||
|
||||
cimport core
|
||||
|
||||
cdef class Color:
|
||||
def __init__(self, uint8_t red = 0, uint8_t green = 0, uint8_t blue = 0):
|
||||
self.__color.r = red
|
||||
self.__color.g = green
|
||||
self.__color.b = blue
|
||||
|
||||
property red:
|
||||
def __get__(self): return self.__color.r
|
||||
def __set__(self, uint8_t value): self.__color.r = value
|
||||
|
||||
property green:
|
||||
def __get__(self): return self.__color.g
|
||||
def __set__(self, uint8_t value): self.__color.g = value
|
||||
|
||||
property blue:
|
||||
def __get__(self): return self.__color.b
|
||||
def __set__(self, uint8_t value): self.__color.b = value
|
||||
|
||||
cdef class Font:
|
||||
def CharacterWidth(self, uint32_t char):
|
||||
return self.__font.CharacterWidth(char)
|
||||
|
||||
def LoadFont(self, file):
|
||||
if (not self.__font.LoadFont(file.encode('utf-8'))):
|
||||
raise Exception("Couldn't load font " + file)
|
||||
|
||||
def DrawGlyph(self, core.Canvas c, int x, int y, Color color, uint32_t char):
|
||||
return self.__font.DrawGlyph(c.__getCanvas(), x, y, color.__color, char)
|
||||
|
||||
property height:
|
||||
def __get__(self): return self.__font.height()
|
||||
|
||||
property baseline:
|
||||
def __get__(self): return self.__font.baseline()
|
||||
|
||||
def DrawText(core.Canvas c, Font f, int x, int y, Color color, text):
|
||||
return cppinc.DrawText(c.__getCanvas(), f.__font, x, y, color.__color, text.encode('utf-8'))
|
||||
|
||||
def DrawCircle(core.Canvas c, int x, int y, int r, Color color):
|
||||
cppinc.DrawCircle(c.__getCanvas(), x, y, r, color.__color)
|
||||
|
||||
def DrawLine(core.Canvas c, int x1, int y1, int x2, int y2, Color color):
|
||||
cppinc.DrawLine(c.__getCanvas(), x1, y1, x2, y2, color.__color)
|
||||
|
||||
# Local Variables:
|
||||
# mode: python
|
||||
# End:
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
from samplebase import SampleBase
|
||||
from rgbmatrix import graphics
|
||||
import time
|
||||
|
||||
|
||||
class GraphicsTest(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GraphicsTest, self).__init__(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
canvas = self.matrix
|
||||
font = graphics.Font()
|
||||
font.LoadFont("../../../fonts/7x13.bdf")
|
||||
|
||||
red = graphics.Color(255, 0, 0)
|
||||
graphics.DrawLine(canvas, 5, 5, 22, 13, red)
|
||||
|
||||
green = graphics.Color(0, 255, 0)
|
||||
graphics.DrawCircle(canvas, 15, 15, 10, green)
|
||||
|
||||
blue = graphics.Color(0, 0, 255)
|
||||
graphics.DrawText(canvas, font, 2, 10, blue, "Text")
|
||||
|
||||
time.sleep(10) # show display for 10 seconds before exit
|
||||
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
graphics_test = GraphicsTest()
|
||||
if (not graphics_test.process()):
|
||||
graphics_test.print_help()
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
from samplebase import SampleBase
|
||||
import time
|
||||
|
||||
|
||||
class GrayscaleBlock(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GrayscaleBlock, self).__init__(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
sub_blocks = 16
|
||||
width = self.matrix.width
|
||||
height = self.matrix.height
|
||||
x_step = max(1, width / sub_blocks)
|
||||
y_step = max(1, height / sub_blocks)
|
||||
count = 0
|
||||
|
||||
while True:
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
c = sub_blocks * int(y / y_step) + int(x / x_step)
|
||||
if count % 4 == 0:
|
||||
self.matrix.SetPixel(x, y, c, c, c)
|
||||
elif count % 4 == 1:
|
||||
self.matrix.SetPixel(x, y, c, 0, 0)
|
||||
elif count % 4 == 2:
|
||||
self.matrix.SetPixel(x, y, 0, c, 0)
|
||||
elif count % 4 == 3:
|
||||
self.matrix.SetPixel(x, y, 0, 0, c)
|
||||
|
||||
count += 1
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
grayscale_block = GrayscaleBlock()
|
||||
if (not grayscale_block.process()):
|
||||
grayscale_block.print_help()
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# (This is an example similar to an example from the Adafruit fork
|
||||
# to show the similarities. Most important difference currently is, that
|
||||
# this library wants RGB mode.)
|
||||
#
|
||||
# A more complex RGBMatrix example works with the Python Imaging Library,
|
||||
# demonstrating a few graphics primitives and image loading.
|
||||
# Note that PIL graphics do not have an immediate effect on the display --
|
||||
# image is drawn into a separate buffer, which is then copied to the matrix
|
||||
# using the SetImage() function (see examples below).
|
||||
# Requires rgbmatrix.so present in the same directory.
|
||||
|
||||
# PIL Image module (create or load images) is explained here:
|
||||
# http://effbot.org/imagingbook/image.htm
|
||||
# PIL ImageDraw module (draw shapes to images) explained here:
|
||||
# http://effbot.org/imagingbook/imagedraw.htm
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
import time
|
||||
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
||||
|
||||
# Configuration for the matrix
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = 32
|
||||
options.chain_length = 1
|
||||
options.parallel = 1
|
||||
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
|
||||
|
||||
matrix = RGBMatrix(options = options)
|
||||
|
||||
# RGB example w/graphics prims.
|
||||
# Note, only "RGB" mode is supported currently.
|
||||
image = Image.new("RGB", (32, 32)) # Can be larger than matrix if wanted!!
|
||||
draw = ImageDraw.Draw(image) # Declare Draw instance before prims
|
||||
# Draw some shapes into image (no immediate effect on matrix)...
|
||||
draw.rectangle((0, 0, 31, 31), fill=(0, 0, 0), outline=(0, 0, 255))
|
||||
draw.line((0, 0, 31, 31), fill=(255, 0, 0))
|
||||
draw.line((0, 31, 31, 0), fill=(0, 255, 0))
|
||||
|
||||
# Then scroll image across matrix...
|
||||
for n in range(-32, 33): # Start off top-left, move off bottom-right
|
||||
matrix.Clear()
|
||||
matrix.SetImage(image, n, n)
|
||||
time.sleep(0.05)
|
||||
|
||||
matrix.Clear()
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
import time
|
||||
from samplebase import SampleBase
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class ImageScroller(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ImageScroller, self).__init__(*args, **kwargs)
|
||||
self.parser.add_argument("-i", "--image", help="The image to display", default="../../../examples-api-use/runtext.ppm")
|
||||
|
||||
def run(self):
|
||||
if not 'image' in self.__dict__:
|
||||
self.image = Image.open(self.args.image).convert('RGB')
|
||||
self.image.resize((self.matrix.width, self.matrix.height), Image.ANTIALIAS)
|
||||
|
||||
double_buffer = self.matrix.CreateFrameCanvas()
|
||||
img_width, img_height = self.image.size
|
||||
|
||||
# let's scroll
|
||||
xpos = 0
|
||||
while True:
|
||||
xpos += 1
|
||||
if (xpos > img_width):
|
||||
xpos = 0
|
||||
|
||||
double_buffer.SetImage(self.image, -xpos)
|
||||
double_buffer.SetImage(self.image, -xpos + img_width)
|
||||
|
||||
double_buffer = self.matrix.SwapOnVSync(double_buffer)
|
||||
time.sleep(0.01)
|
||||
|
||||
# Main function
|
||||
# e.g. call with
|
||||
# sudo ./image-scroller.py --chain=4
|
||||
# if you have a chain of four
|
||||
if __name__ == "__main__":
|
||||
image_scroller = ImageScroller()
|
||||
if (not image_scroller.process()):
|
||||
image_scroller.print_help()
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
import time
|
||||
import sys
|
||||
|
||||
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
||||
from PIL import Image
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("Require an image argument")
|
||||
else:
|
||||
image_file = sys.argv[1]
|
||||
|
||||
image = Image.open(image_file)
|
||||
|
||||
# Configuration for the matrix
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = 32
|
||||
options.chain_length = 1
|
||||
options.parallel = 1
|
||||
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
|
||||
|
||||
matrix = RGBMatrix(options = options)
|
||||
|
||||
# Make image fit our screen.
|
||||
image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
|
||||
|
||||
matrix.SetImage(image.convert('RGB'))
|
||||
|
||||
try:
|
||||
print("Press CTRL-C to stop.")
|
||||
while True:
|
||||
time.sleep(100)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
from samplebase import SampleBase
|
||||
|
||||
|
||||
class GrayscaleBlock(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GrayscaleBlock, self).__init__(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
max_brightness = self.matrix.brightness
|
||||
count = 0
|
||||
c = 255
|
||||
|
||||
while (True):
|
||||
if self.matrix.brightness < 1:
|
||||
self.matrix.brightness = max_brightness
|
||||
count += 1
|
||||
else:
|
||||
self.matrix.brightness -= 1
|
||||
|
||||
if count % 4 == 0:
|
||||
self.matrix.Fill(c, 0, 0)
|
||||
elif count % 4 == 1:
|
||||
self.matrix.Fill(0, c, 0)
|
||||
elif count % 4 == 2:
|
||||
self.matrix.Fill(0, 0, c)
|
||||
elif count % 4 == 3:
|
||||
self.matrix.Fill(c, c, c)
|
||||
|
||||
self.usleep(20 * 1000)
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
grayscale_block = GrayscaleBlock()
|
||||
if (not grayscale_block.process()):
|
||||
grayscale_block.print_help()
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
from samplebase import SampleBase
|
||||
|
||||
|
||||
class PulsingColors(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PulsingColors, self).__init__(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
self.offscreen_canvas = self.matrix.CreateFrameCanvas()
|
||||
continuum = 0
|
||||
|
||||
while True:
|
||||
self.usleep(5 * 1000)
|
||||
continuum += 1
|
||||
continuum %= 3 * 255
|
||||
|
||||
red = 0
|
||||
green = 0
|
||||
blue = 0
|
||||
|
||||
if continuum <= 255:
|
||||
c = continuum
|
||||
blue = 255 - c
|
||||
red = c
|
||||
elif continuum > 255 and continuum <= 511:
|
||||
c = continuum - 256
|
||||
red = 255 - c
|
||||
green = c
|
||||
else:
|
||||
c = continuum - 512
|
||||
green = 255 - c
|
||||
blue = c
|
||||
|
||||
self.offscreen_canvas.Fill(red, green, blue)
|
||||
self.offscreen_canvas = self.matrix.SwapOnVSync(self.offscreen_canvas)
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
pulsing_colors = PulsingColors()
|
||||
if (not pulsing_colors.process()):
|
||||
pulsing_colors.print_help()
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
from samplebase import SampleBase
|
||||
import math
|
||||
|
||||
|
||||
def scale_col(val, lo, hi):
|
||||
if val < lo:
|
||||
return 0
|
||||
if val > hi:
|
||||
return 255
|
||||
return 255 * (val - lo) / (hi - lo)
|
||||
|
||||
|
||||
def rotate(x, y, sin, cos):
|
||||
return x * cos - y * sin, x * sin + y * cos
|
||||
|
||||
|
||||
class RotatingBlockGenerator(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RotatingBlockGenerator, self).__init__(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
cent_x = self.matrix.width / 2
|
||||
cent_y = self.matrix.height / 2
|
||||
|
||||
rotate_square = min(self.matrix.width, self.matrix.height) * 1.41
|
||||
min_rotate = cent_x - rotate_square / 2
|
||||
max_rotate = cent_x + rotate_square / 2
|
||||
|
||||
display_square = min(self.matrix.width, self.matrix.height) * 0.7
|
||||
min_display = cent_x - display_square / 2
|
||||
max_display = cent_x + display_square / 2
|
||||
|
||||
deg_to_rad = 2 * 3.14159265 / 360
|
||||
rotation = 0
|
||||
|
||||
# Pre calculate colors
|
||||
col_table = []
|
||||
for x in range(int(min_rotate), int(max_rotate)):
|
||||
col_table.insert(x, scale_col(x, min_display, max_display))
|
||||
|
||||
offset_canvas = self.matrix.CreateFrameCanvas()
|
||||
|
||||
while True:
|
||||
rotation += 1
|
||||
rotation %= 360
|
||||
|
||||
# calculate sin and cos once for each frame
|
||||
angle = rotation * deg_to_rad
|
||||
sin = math.sin(angle)
|
||||
cos = math.cos(angle)
|
||||
|
||||
for x in range(int(min_rotate), int(max_rotate)):
|
||||
for y in range(int(min_rotate), int(max_rotate)):
|
||||
# Our rotate center is always offset by cent_x
|
||||
rot_x, rot_y = rotate(x - cent_x, y - cent_x, sin, cos)
|
||||
|
||||
if x >= min_display and x < max_display and y >= min_display and y < max_display:
|
||||
x_col = col_table[x]
|
||||
y_col = col_table[y]
|
||||
offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, x_col, 255 - y_col, y_col)
|
||||
else:
|
||||
offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, 0, 0, 0)
|
||||
|
||||
offset_canvas = self.matrix.SwapOnVSync(offset_canvas)
|
||||
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
rotating_block_generator = RotatingBlockGenerator()
|
||||
if (not rotating_block_generator.process()):
|
||||
rotating_block_generator.print_help()
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
# Display a runtext with double-buffering.
|
||||
from samplebase import SampleBase
|
||||
from rgbmatrix import graphics
|
||||
import time
|
||||
|
||||
|
||||
class RunText(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RunText, self).__init__(*args, **kwargs)
|
||||
self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!")
|
||||
|
||||
def run(self):
|
||||
offscreen_canvas = self.matrix.CreateFrameCanvas()
|
||||
font = graphics.Font()
|
||||
font.LoadFont("../../../fonts/7x13.bdf")
|
||||
textColor = graphics.Color(255, 255, 0)
|
||||
pos = offscreen_canvas.width
|
||||
my_text = self.args.text
|
||||
|
||||
while True:
|
||||
offscreen_canvas.Clear()
|
||||
len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text)
|
||||
pos -= 1
|
||||
if (pos + len < 0):
|
||||
pos = offscreen_canvas.width
|
||||
|
||||
time.sleep(0.05)
|
||||
offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas)
|
||||
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
run_text = RunText()
|
||||
if (not run_text.process()):
|
||||
run_text.print_help()
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import argparse
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..'))
|
||||
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
||||
|
||||
|
||||
class SampleBase(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.parser = argparse.ArgumentParser()
|
||||
|
||||
self.parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int)
|
||||
self.parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 32)", default=32, type=int)
|
||||
self.parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int)
|
||||
self.parser.add_argument("-P", "--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int)
|
||||
self.parser.add_argument("-p", "--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int)
|
||||
self.parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int)
|
||||
self.parser.add_argument("-m", "--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm" , choices=['regular', 'regular-pi1', 'adafruit-hat', 'adafruit-hat-pwm'], type=str)
|
||||
self.parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int)
|
||||
self.parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int)
|
||||
self.parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel")
|
||||
self.parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 0..4. Default: 1", default=1, type=int)
|
||||
self.parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation")
|
||||
self.parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str)
|
||||
self.parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str)
|
||||
self.parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels; 2=row direct; 3=ABC-addressed panels; 4 = ABC Shift + DE direct", default=0, type=int, choices=[0,1,2,3,4])
|
||||
self.parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven... (Default: 0)", default=0, type=int)
|
||||
self.parser.add_argument("--led-panel-type", action="store", help="Needed to initialize special panels. Supported: 'FM6126A'", default="", type=str)
|
||||
self.parser.add_argument("--led-no-drop-privs", dest="drop_privileges", help="Don't drop privileges from 'root' after initializing the hardware.", action='store_false')
|
||||
self.parser.set_defaults(drop_privileges=True)
|
||||
|
||||
def usleep(self, value):
|
||||
time.sleep(value / 1000000.0)
|
||||
|
||||
def run(self):
|
||||
print("Running")
|
||||
|
||||
def process(self):
|
||||
self.args = self.parser.parse_args()
|
||||
|
||||
options = RGBMatrixOptions()
|
||||
|
||||
if self.args.led_gpio_mapping != None:
|
||||
options.hardware_mapping = self.args.led_gpio_mapping
|
||||
options.rows = self.args.led_rows
|
||||
options.cols = self.args.led_cols
|
||||
options.chain_length = self.args.led_chain
|
||||
options.parallel = self.args.led_parallel
|
||||
options.row_address_type = self.args.led_row_addr_type
|
||||
options.multiplexing = self.args.led_multiplexing
|
||||
options.pwm_bits = self.args.led_pwm_bits
|
||||
options.brightness = self.args.led_brightness
|
||||
options.pwm_lsb_nanoseconds = self.args.led_pwm_lsb_nanoseconds
|
||||
options.led_rgb_sequence = self.args.led_rgb_sequence
|
||||
options.pixel_mapper_config = self.args.led_pixel_mapper
|
||||
options.panel_type = self.args.led_panel_type
|
||||
|
||||
|
||||
if self.args.led_show_refresh:
|
||||
options.show_refresh_rate = 1
|
||||
|
||||
if self.args.led_slowdown_gpio != None:
|
||||
options.gpio_slowdown = self.args.led_slowdown_gpio
|
||||
if self.args.led_no_hardware_pulse:
|
||||
options.disable_hardware_pulsing = True
|
||||
if not self.args.drop_privileges:
|
||||
options.drop_privileges=False
|
||||
|
||||
self.matrix = RGBMatrix(options = options)
|
||||
|
||||
try:
|
||||
# Start loop
|
||||
print("Press CTRL-C to stop sample")
|
||||
self.run()
|
||||
except KeyboardInterrupt:
|
||||
print("Exiting\n")
|
||||
sys.exit(0)
|
||||
|
||||
return True
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python
|
||||
from samplebase import SampleBase
|
||||
|
||||
|
||||
class SimpleSquare(SampleBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SimpleSquare, self).__init__(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
offset_canvas = self.matrix.CreateFrameCanvas()
|
||||
while True:
|
||||
for x in range(0, self.matrix.width):
|
||||
offset_canvas.SetPixel(x, x, 255, 255, 255)
|
||||
offset_canvas.SetPixel(offset_canvas.height - 1 - x, x, 255, 0, 255)
|
||||
|
||||
for x in range(0, offset_canvas.width):
|
||||
offset_canvas.SetPixel(x, 0, 255, 0, 0)
|
||||
offset_canvas.SetPixel(x, offset_canvas.height - 1, 255, 255, 0)
|
||||
|
||||
for y in range(0, offset_canvas.height):
|
||||
offset_canvas.SetPixel(0, y, 0, 0, 255)
|
||||
offset_canvas.SetPixel(offset_canvas.width - 1, y, 0, 255, 0)
|
||||
offset_canvas = self.matrix.SwapOnVSync(offset_canvas)
|
||||
|
||||
|
||||
# Main function
|
||||
if __name__ == "__main__":
|
||||
simple_square = SimpleSquare()
|
||||
if (not simple_square.process()):
|
||||
simple_square.print_help()
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/python
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
core_ext = Extension(
|
||||
name = 'core',
|
||||
sources = ['rgbmatrix/core.cpp'],
|
||||
include_dirs = ['../../include'],
|
||||
library_dirs = ['../../lib'],
|
||||
libraries = ['rgbmatrix'],
|
||||
extra_compile_args = ["-O3", "-Wall"],
|
||||
language = 'c++'
|
||||
)
|
||||
|
||||
graphics_ext = Extension(
|
||||
name = 'graphics',
|
||||
sources = ['rgbmatrix/graphics.cpp'],
|
||||
include_dirs = ['../../include'],
|
||||
library_dirs = ['../../lib'],
|
||||
libraries = ['rgbmatrix'],
|
||||
extra_compile_args = ["-O3", "-Wall"],
|
||||
language = 'c++'
|
||||
)
|
||||
|
||||
setup(
|
||||
name = 'rgbmatrix',
|
||||
version = '0.0.1',
|
||||
author = 'Christoph Friedrich',
|
||||
author_email = 'christoph.friedrich@vonaffenfels.de',
|
||||
classifiers = ['Development Status :: 3 - Alpha'],
|
||||
ext_package = 'rgbmatrix',
|
||||
ext_modules = [core_ext, graphics_ext],
|
||||
packages = ['rgbmatrix']
|
||||
)
|
||||
Reference in New Issue
Block a user