Added GraphicsLayer example.

This commit is contained in:
mrfaptastic 2020-12-07 22:42:37 +00:00
parent 7683b1d8f3
commit 76351960b8
6 changed files with 581 additions and 0 deletions

View file

@ -0,0 +1,109 @@
/*
* Portions of this code are adapted from Aurora: https://github.com/pixelmatix/aurora
* Copyright (c) 2014 Jason Coon
*
* Portions of this code are adapted from LedEffects Plasma by Robert Atkins: https://bitbucket.org/ratkins/ledeffects/src/26ed3c51912af6fac5f1304629c7b4ab7ac8ca4b/Plasma.cpp?at=default
* Copyright (c) 2013 Robert Atkins
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//#define USE_CUSTOM_PINS // uncomment to use custom pins, then provide below
#define A_PIN 26
#define B_PIN 4
#define C_PIN 27
#define D_PIN 2
#define E_PIN 21
#define R1_PIN 5
#define R2_PIN 19
#define G1_PIN 17
#define G2_PIN 16
#define B1_PIN 18
#define B2_PIN 25
#define CLK_PIN 14
#define LAT_PIN 15
#define OE_PIN 13
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
MatrixPanel_I2S_DMA dma_display;
#include <FastLED.h>
int time_counter = 0;
int cycles = 0;
CRGBPalette16 currentPalette;
CRGB currentColor;
CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlendType blendType = LINEARBLEND) {
return ColorFromPalette(currentPalette, index, brightness, blendType);
}
void setup() {
Serial.begin(115200);
Serial.println("*****************************************************");
Serial.println(" HELLO !");
Serial.println("*****************************************************");
#ifdef USE_CUSTOM_PINS
dma_display.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
#else
dma_display.begin();
#endif
// fill the screen with 'black'
dma_display.fillScreen(dma_display.color444(0, 0, 0));
// Set current FastLED palette
currentPalette = RainbowColors_p;
}
void loop() {
for (int x = 0; x < dma_display.width(); x++) {
for (int y = 0; y < dma_display.height(); y++) {
int16_t v = 0;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
v += cos16(y * (128 - wibble) + time_counter);
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8) + 127); //, brightness, currentBlendType);
dma_display.drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}
time_counter += 1;
cycles++;
if (cycles >= 2048) {
time_counter = 0;
cycles = 0;
}
} // end loop

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View file

@ -0,0 +1,344 @@
/**
* Experimental layer class to do play with pixel in an off-screen buffer before painting to the DMA
*
* Requires FastLED
*
* Faptastic 2020
**/
#include "Layer.h"
// For adafruit
void Layer::drawPixel(int16_t x, int16_t y, uint16_t color) {
// 565 color conversion
uint8_t r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6;
uint8_t g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6;
uint8_t b = (((color & 0x1F) * 527) + 23) >> 6;
drawPixel(x, y, CRGB(r,g,b));
}
void Layer::drawPixel(int16_t x, int16_t y, int r, int g, int b) {
drawPixel(x, y, CRGB(r,g,b));
}
void Layer::drawPixel(int16_t x, int16_t y, CRGB color) {
if( x >= LAYER_WIDTH || x < 0) return; // 0;
if( y >= LAYER_HEIGHT || y < 0) return; // 0;
pixels->data[y][x] = color;
}
/**
* Dim all the pixels in the display.
*/
void Layer::dim(byte value) {
// nscale8 max value is 255, or it'll flip back to 0
// (documentation is wrong when it says x/256), it's actually x/255
for (int y = 0; y < LAYER_HEIGHT; y++) {
for (int x = 0; x < LAYER_WIDTH; x++) {
pixels->data[y][x].nscale8(value);
}}
}
void Layer::clear() {
memset(pixels, BLACK_BACKGROUND_PIXEL_COLOUR, sizeof(layerPixels) );
}
/**
* Send the layer to the display device.
*/
void Layer::display() {
CRGB _pixel = 0 ;
for (int y = 0; y < LAYER_HEIGHT; y++) {
for (int x = 0; x < LAYER_WIDTH; x++)
{
//_pixel = pixel[XY(x, y)];
_pixel = pixels->data[y][x];
matrix->drawPixelRGB888( x, y, _pixel.r, _pixel.g, _pixel.b);
/*
if ( !transparency_enabled ){
matrix->drawPixelRGB888( x, y, _pixel.r, _pixel.g, _pixel.b);
} else {
if (_pixel != transparency_colour) {
matrix->drawPixelRGB888( x, y, _pixel.r, _pixel.g, _pixel.b);
}
}
*/
} // end loop to copy fast led to the dma matrix
}
} // display
void Layer::overridePixelColor(int r, int g, int b) {
CRGB _pixel = 0 ;
for (int y = 0; y < LAYER_HEIGHT; y++) {
for (int x = 0; x < LAYER_WIDTH; x++)
{
//_pixel = pixel[XY(x, y)];
_pixel = pixels->data[y][x];
if (_pixel != transparency_colour) {
matrix->drawPixelRGB888( x, y, _pixel.r, _pixel.g, _pixel.b);
}
} // end loop to copy fast led to the dma matrix
}
}
// default value is in definition
void Layer::drawCentreText(const char *buf, textPosition textPos, const GFXfont *f, CRGB color, int yadjust)
{
int16_t x1, y1;
uint16_t w, h;
setTextWrap(false);
if (f) { // Font struct pointer passed in?
setFont((GFXfont *)f);
} else { // NULL passed. Current font struct defined?
setFont(); // use default
}
// getTextBounds isn't correct for variable width fonts
getTextBounds(buf, 0, 0, &x1, &y1, &w, &h); //calc width of new string
//Serial.printf("The width of the text is %d pixels, the height is %d pixels.\n", w,h);
/*
From: https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
For example, whereas the cursor position when printing with the classic font identified
the top-left corner of the character cell, with new fonts the cursor position indicates the baseline
the bottom-most row of subsequent text. Characters may vary in size and width, and dont
necessarily begin at the exact cursor column (as in below, this character starts one pixel
left of the cursor, but others may be on or to the right of it).
*/
if (!f) {
if (textPos == TOP) {
setCursor((LAYER_WIDTH - w) / 2, 0); // top
} else if (textPos == BOTTOM) {
setCursor((LAYER_WIDTH - w) / 2, LAYER_HEIGHT - h);
} else { // middle
setCursor((LAYER_WIDTH - w) / 2, (LAYER_HEIGHT - h) / 2); // top
}
}
else // custom font
/* As we can't reliable know what is the actual FIRST and last 'lit' pixel, we need to check what was printed to the layer.*/
{
int wstart = 0;
if (w > 42) wstart = (LAYER_WIDTH - w) / 2;
else wstart = (LAYER_WIDTH - w) / 2;
if (textPos == TOP) {
setCursor(wstart, h+yadjust); // top
} else if (textPos == BOTTOM) {
setCursor(wstart+1, (LAYER_HEIGHT-1)+yadjust);
} else { // middle
setCursor( wstart, ((LAYER_HEIGHT/2) + (h/2)) + yadjust);
}
//Serial.printf("Layer: x1: %d, y1: %d, w: %d, h: %d.\n", x1, y1, w, h);
}
// setCursor(0,16);
setTextColor(this->color565(color.r, color.g, color.b)); // Need to confirm from FastLed CRGB to adafruit 565
print(buf);
} // end drawCentreText
// Move the contents of the screen left (-ve) or right (+ve)
void Layer::moveX(int offset)
{
if(offset > 0) { // move right
// Sprintln("Moving right");
for(int x = LAYER_WIDTH - 1; x >= 0; x--){ // 63 to 0
for(int y = 0; y < LAYER_HEIGHT; y++){ // 0 to 31
if (x - offset >= 0)
{
// Serial.printf("setting y %d x %d to y %d x %d\n", y, x, y, x-offset);
pixels->data[y][x] = pixels->data[y][x-offset];
}
else {
pixels->data[y][x] = BLACK_BACKGROUND_PIXEL_COLOUR;
}
}
}
} else { // move left
// Sprintln("Moving Left");
for(int x = 0; x <=LAYER_WIDTH - 1; x++){
for(int y = 0; y < LAYER_HEIGHT; y++){
if ( x > (LAYER_WIDTH-1)+offset )
{
pixels->data[y][x] = BLACK_BACKGROUND_PIXEL_COLOUR;
//Serial.println("eh?");
}
else
{
pixels->data[y][x] = pixels->data[y][x-offset];
// Serial.println("eh?");
}
}
}
}
}
/**
* Centre the contents of the layer based on the leftmost and rightmost pixels.
* Useful if you want to make sure text / graphics IS in the centre of the display.
*/
void Layer::autoCenterX()
{
int leftmost_x = 0, rightmost_x = 0, adjusted_leftmost_x = 0;
// Find leftmost
for(int x = 0; x < LAYER_WIDTH; x++) {
for(int y = 0; y < LAYER_HEIGHT; y++) {
if (pixels->data[y][x] != BLACK_BACKGROUND_PIXEL_COLOUR)
{
leftmost_x = x;
//Serial.printf("Left most x pixel is %d\n", leftmost_x);
goto rightmost;
}
}
}
rightmost:
for(int x = LAYER_WIDTH-1; x >= 0; x--) {
for(int y = 0; y < LAYER_HEIGHT; y++) {
if (pixels->data[y][x] != BLACK_BACKGROUND_PIXEL_COLOUR)
{
rightmost_x = x+1;
//Serial.printf("Right most x pixel is %d\n", rightmost_x);
goto centreit;
}
}
}
centreit:
adjusted_leftmost_x = ( LAYER_WIDTH - (rightmost_x - leftmost_x))/2;
//Serial.printf("Adjusted: %d, Moving x coords by %d pixels.\n", adjusted_leftmost_x, adjusted_leftmost_x-leftmost_x);
moveX(adjusted_leftmost_x-leftmost_x);
} // end autoCentreX
void Layer::moveY(int delta)
{
// Not implemented
}
Layer::~Layer(void)
{
free(pixels);
}
/* Merge FastLED layers into a super layer and display. Definition */
namespace LayerCompositor
{
/*
* Display the foreground pixels if they're not the background/transparent color.
* If not, then fill with whatever is in the background.
*
* writeToBg = write the result back to the _bgLayer, and not directly to the output device!
* -> no need to do a subsequent bgLayer.display() otherwise.
*/
void Stack(RGB64x32MatrixPanel_I2S_DMA &disp, const Layer &_bgLayer, const Layer &_fgLayer, bool writeBackToBg)
{
for (int y = 0; y < LAYER_HEIGHT; y++) {
for (int x = 0; x < LAYER_WIDTH; x++)
{
//https://www.educative.io/edpresso/how-to-resolve-the-expression-must-have-class-type-error-in-cpp
if (_fgLayer.pixels->data[y][x] == _fgLayer.transparency_colour) // foreground is transparent, show the _bgLayer colors
{
if (writeBackToBg) // write the foreground to the background layer... perhaps so we can do stuff later with the _fgLayer.
_bgLayer.pixels->data[y][x] = _bgLayer.pixels->data[y][x];
else
disp.drawPixelRGB888(x,y, _bgLayer.pixels->data[y][x].r, _bgLayer.pixels->data[y][x].g, _bgLayer.pixels->data[y][x].b );
} // if the foreground is NOT transparent, then print whatever is the bg
else
{
if (writeBackToBg) // write the foreground to the background layer... perhaps so we can do stuff later with the _fgLayer.
_bgLayer.pixels->data[y][x] = _fgLayer.pixels->data[y][x];
else
disp.drawPixelRGB888(x,y, _fgLayer.pixels->data[y][x].r, _fgLayer.pixels->data[y][x].g, _fgLayer.pixels->data[y][x].b );
}
} // end x loop
} // end y loop
} // end stack
/*
* Where the foreground pixels are not the background/transparent color, populate with
* whatever is in the background.
*/
void Siloette(RGB64x32MatrixPanel_I2S_DMA &disp, const Layer &_bgLayer, const Layer &_fgLayer)
{
//const Layer *bg = &_bgLayer;
//const Layer *fg = &_fgLayer;
for (int y = 0; y < LAYER_HEIGHT; y++) {
for (int x = 0; x < LAYER_WIDTH; x++)
{
//https://www.educative.io/edpresso/how-to-resolve-the-expression-must-have-class-type-error-in-cpp
if (_fgLayer.pixels->data[y][x] != _fgLayer.transparency_colour)
{
disp.drawPixelRGB888(x,y, _bgLayer.pixels->data[y][x].r, _bgLayer.pixels->data[y][x].g, _bgLayer.pixels->data[y][x].b );
} // if the foreground is transparent, then print whatever is the bg
else
{
disp.drawPixelRGB888(x,y, 0,0,0);
}
} // end x loop
} // end y loop
} // end stack
void Blend(RGB64x32MatrixPanel_I2S_DMA &disp, const Layer &_bgLayer, const Layer &_fgLayer, uint8_t ratio)
{
CRGB _pixel = 0 ;
for (int y = 0; y < LAYER_HEIGHT; y++)
{
for (int x = 0; x < LAYER_WIDTH; x++)
{
/*
// set the blend ratio for the video cross fade
// (set ratio to 127 for a constant 50% / 50% blend)
uint8_t ratio = beatsin8(5);
*/
_pixel = blend(_bgLayer.pixels->data[y][x], _fgLayer.pixels->data[y][x], ratio);
// https://gist.github.com/StefanPetrick/0c0d54d0f35ea9cca983
disp.drawPixelRGB888(x,y, _pixel.r, _pixel.g, _pixel.b );
} // end x loop
} // end y loop
} // end blend
}

View file

@ -0,0 +1,122 @@
/**
* Experimental layer class to do play with pixel in an off-screen buffer before painting to the DMA
*
* Requires FastLED
*
* Faptastic 2020
**/
#ifndef DISPLAY_MATRIX_LAYER
#define DISPLAY_MATRIX_LAYER
/* Use GFX_Root (https://github.com/mrfaptastic/GFX_Root) instead of
* Adafruit_GFX library. No real benefit unless you don't want Bus_IO & Wire.h library dependencies.
*/
#define USE_GFX_ROOT 1
#ifdef USE_GFX_ROOT
#include "GFX.h" // Adafruit GFX core class -> https://github.com/mrfaptastic/GFX_Root
#else
#include "Adafruit_GFX.h" // Adafruit class with all the other stuff
#endif
#include <FastLed.h>
#include <ESP32-RGB64x32MatrixPanel-I2S-DMA.h>
/*
* Set the width and height of the layer buffers. This should match exactly that of your output display, or virtual display.
*/
#define LAYER_WIDTH 64
#define LAYER_HEIGHT 32
#define HALF_WHITE_COLOUR 0x8410
#define BLACK_BACKGROUND_PIXEL_COLOUR CRGB(0,0,0)
enum textPosition { TOP, MIDDLE, BOTTOM };
/* To help with direct pixel referencing by width and height */
struct layerPixels {
CRGB data[LAYER_HEIGHT][LAYER_WIDTH];
};
#ifdef USE_GFX_ROOT
class Layer : public GFX {
#else
class Layer : public Adafruit_GFX {
#endif
// class Layer : public GFX // use GFX Root for now
{
public:
// Static allocation of memory for layer
//CRGB pixels[LAYER_WIDTH][LAYER_HEIGHT] = {{0}};
Layer(RGB64x32MatrixPanel_I2S_DMA &disp) : GFX (LAYER_WIDTH, LAYER_HEIGHT) {
matrix = &disp;
}
inline void init()
{
// https://stackoverflow.com/questions/5914422/proper-way-to-initialize-c-structs
pixels = new layerPixels();
//pixels = (layerPixels *) malloc(sizeof(layerPixels));
// pixel = (CRGB *) &pixels[0];
//Serial.printf("Allocated %d bytes of memory for standard CRGB (24bit) layer.\r\n", NUM_PIXELS*sizeof(CRGB));
Serial.printf("Allocated %d bytes of memory for layerPixels.\r\n", sizeof(layerPixels));
} // end Layer
void drawPixel(int16_t x, int16_t y, uint16_t color); // overwrite adafruit implementation
void drawPixel(int16_t x, int16_t y, int r, int g, int b); // Layer implementation
void drawPixel(int16_t x, int16_t y, CRGB color); // Layer implementation
// Font Stuff
//https://forum.arduino.cc/index.php?topic=642749.0
void drawCentreText(const char *buf, textPosition textPos = BOTTOM, const GFXfont *f = NULL, CRGB color = 0x8410, int yadjust = 0); // 128,128,128 RGB @ bottom row by default
void dim(byte value);
void clear();
void display(); // flush to display / LED matrix
// override the color of all pixels that aren't the transparent color
void overridePixelColor(int r, int g, int b);
inline uint16_t color565(uint8_t r, uint8_t g, uint8_t b) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
inline void setTransparency(bool t) { transparency_enabled = t; }
// Effects
void moveX(int delta);
void autoCenterX();
void moveY(int delta);
// For layer composition - accessed publically
CRGB transparency_colour = BLACK_BACKGROUND_PIXEL_COLOUR;
bool transparency_enabled = true;
layerPixels *pixels;
// Release Memory
~Layer(void);
private:
RGB64x32MatrixPanel_I2S_DMA *matrix = NULL;
};
/* Merge FastLED layers into a super layer and display. */
namespace LayerCompositor
{
void Stack(RGB64x32MatrixPanel_I2S_DMA &disp, const Layer &_bgLayer, const Layer &_fgLayer, bool writeToBgLayer = false);
void Siloette(RGB64x32MatrixPanel_I2S_DMA &disp, const Layer &_bgLayer, const Layer &_fgLayer);
void Blend(RGB64x32MatrixPanel_I2S_DMA &disp, const Layer &_bgLayer, const Layer &_fgLayer, uint8_t ratio);
}
#endif

View file

@ -0,0 +1,5 @@
# Layer Class
Example of using additional pixel buffers / layers based on the FastLed CRGB data type, doing stuff with the pixels, merging the layers prior to sending to the DMA display library for output.
![It's better in real life](GraphicsLayer.jpg)

View file

@ -1,5 +1,6 @@
RGB64x32MatrixPanel_I2S_DMA KEYWORD1
MatrixPanel_I2S_DMA KEYWORD1
Layer KEYWORD1
fillScreen KEYWORD2
clearScreen KEYWORD2
fillScreenRGB888 KEYWORD2