Layer Class
This commit is contained in:
parent
adaf7c8081
commit
f31f6e1db3
2 changed files with 442 additions and 0 deletions
338
Layer.cpp
Normal file
338
Layer.cpp
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
//pixel[XY(x,y)] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}}
|
||||||
|
// for (int i = 0; i < NUM_PIXELS; i++) pixel[i].nscale8(value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Layer::clear() {
|
||||||
|
|
||||||
|
memset(pixels, BLACK_BACKGROUND_PIXEL_COLOUR, sizeof(layerPixels) );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 don’t
|
||||||
|
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?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the leftmost and rightmost pixels across all rows. Centre the contents of the layer.
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
104
Layer.h
Normal file
104
Layer.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#include <GFX.h>
|
||||||
|
#include <FastLed.h>
|
||||||
|
#include <ESP32-RGB64x32MatrixPanel-I2S-DMA.h>
|
||||||
|
|
||||||
|
#define HALF_WHITE_COLOUR 0x8410
|
||||||
|
#define BLACK_BACKGROUND_PIXEL_COLOUR CRGB(0,0,0)
|
||||||
|
|
||||||
|
#define LAYER_WIDTH 64
|
||||||
|
#define LAYER_HEIGHT 32
|
||||||
|
|
||||||
|
enum textPosition { TOP, MIDDLE, BOTTOM };
|
||||||
|
|
||||||
|
|
||||||
|
/* To help with direct pixel referencing by width and height */
|
||||||
|
struct layerPixels {
|
||||||
|
CRGB data[LAYER_HEIGHT][LAYER_WIDTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
//CRGB *pixel;
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
Loading…
Reference in a new issue