indexedLayer black text over colored BackgroundLayer?

Is it possible to use a indexed layer to “show text in black” over a background that shows colors?

I already have everything working with a Teensy 4.1 and a 48x32px (3 32x16 panels). I have it showing the texts in white.

Example:
const rgb24 superWhite = {0xff, 0xff, 0xff};
matrix.addLayer(&indexedLayer);
indexedLayer.setIndexedColor(1, superWhite);
indexedLayer.drawChar( 5, textPositionV, 1, value[0]);

Yes, just set the scrolling layer’s text color to black

Or I’m misunderstanding something or the things are not so easy.
Are you suggesting to change from indexedLayer to Scrolling Layer?

Because if I just do the following, it doesn’t work as expected.
indexedLayer.setIndexedColor(1, black); // black is defined as (0,0,0)

I just realize I’m talking to God! Thanks Louis for your time and all answers I’ve read on the forum.

Well, I noticed I didn’t share I’m using AdafruitGFX layers because I’m using custom fonts (large). I don’t know if this changes anything.

Is my first project using Smartmatrix and I’m searching a lot to be able to do what I need.

Now I’m on the test/error with the scrollingLayer, based on the wiki and the examples again.

1 Like

I just did a test modifying the MultipleTextLayersGfx sketch and it can display black text over the FastLED pattern:

See scrollingLayer00.setColor({0,0,0});

/*
  This example shows how multiple scrolling text layers can be used
  This example requires the Adafruit_GFX Library, which you can install through Arduino Library Manager
  The "GFX" Layers in SmartMatrix Library 4.0 depend on Adafruit_GFX and give access to using large fonts included
    with Adafruit_GFX, and the ability to add custom fonts
  Performance of the "GFX" layers has been optimized, and with the Teensy 4, you can have 10 scrolling text layers
    on a 128x64 panel, and still refresh the panel at 240Hz
  Optionally draw a pattern behind the text layers using FastLED (same code as the FastLED_Functions example) by
    removing the comments from `#define INCLUDE_FASTLED_BACKGROUND` below.  With the Teensy 4 you'll still see 
    128x64 @ 240Hz Refresh even with the pattern updating at 60 FPS!
*/

#define USE_ADAFRUIT_GFX_LAYERS

#define INCLUDE_FASTLED_BACKGROUND

// uncomment one line to select your MatrixHardware configuration - configuration header needs to be included before <SmartMatrix.h>
//#include <MatrixHardware_Teensy3_ShieldV4.h>        // SmartLED Shield for Teensy 3 (V4)
#include <MatrixHardware_Teensy4_ShieldV5.h>        // SmartLED Shield for Teensy 4 (V5)
//#include <MatrixHardware_Teensy3_ShieldV1toV3.h>    // SmartMatrix Shield for Teensy 3 V1-V3
//#include <MatrixHardware_Teensy4_ShieldV4Adapter.h> // Teensy 4 Adapter attached to SmartLED Shield for Teensy 3 (V4)
//#include <MatrixHardware_ESP32_V0.h>                // This file contains multiple ESP32 hardware configurations, edit the file to define GPIOPINOUT (or add #define GPIOPINOUT with a hardcoded number before this #include)
//#include "MatrixHardware_Custom.h"                  // Copy an existing MatrixHardware file to your Sketch directory, rename, customize, and you can include it like this
#include <SmartMatrix.h>

#include <Fonts/Picopixel.h>
#include <Fonts/TomThumb.h>
#include <Fonts/FreeMono12pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>
#include <Fonts/FreeMonoBoldOblique24pt7b.h>
#include <Fonts/FreeSerif24pt7b.h>
#include <Fonts/Org_01.h>
#include <Fonts/Tiny3x3a2pt7b.h>
#include <Fonts/FreeSerifItalic9pt7b.h>
#include <Fonts/FreeSansOblique24pt7b.h>

#define COLOR_DEPTH 24                  // Choose the color depth used for storing pixels in the layers: 24 or 48 (24 is good for most sketches - If the sketch uses type `rgb24` directly, COLOR_DEPTH must be 24)
const uint16_t kMatrixWidth = 64;       // Set to the width of your display, must be a multiple of 8
const uint16_t kMatrixHeight = 64;      // Set to the height of your display
const uint8_t kRefreshDepth = 36;       // Tradeoff of color quality vs refresh rate, max brightness, and RAM usage.  36 is typically good, drop down to 24 if you need to.  On Teensy, multiples of 3, up to 48: 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48.  On ESP32: 24, 36, 48
const uint8_t kDmaBufferRows = 4;       // known working: 2-4, use 2 to save RAM, more to keep from dropping frames and automatically lowering refresh rate.  (This isn't used on ESP32, leave as default)
const uint8_t kPanelType = SM_PANELTYPE_HUB75_64ROW_MOD32SCAN;   // Choose the configuration that matches your panels.  See more details in MatrixCommonHub75.h and the docs: https://github.com/pixelmatix/SmartMatrix/wiki
const uint32_t kMatrixOptions = (SM_HUB75_OPTIONS_NONE);        // see docs for options: https://github.com/pixelmatix/SmartMatrix/wiki
const uint8_t kScrollingLayerOptions = (SM_SCROLLING_OPTIONS_NONE);

SMARTMATRIX_ALLOCATE_BUFFERS(matrix, kMatrixWidth, kMatrixHeight, kRefreshDepth, kDmaBufferRows, kPanelType, kMatrixOptions);

#ifdef INCLUDE_FASTLED_BACKGROUND
  const uint8_t kBackgroundLayerOptions = (SM_BACKGROUND_OPTIONS_NONE);
  SMARTMATRIX_ALLOCATE_BACKGROUND_LAYER(backgroundLayer, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kBackgroundLayerOptions);
#endif

// Using the backwards compatible ALLOCATION macro is fine for smaller fonts
SMARTMATRIX_ALLOCATE_SCROLLING_LAYER(scrollingLayer07, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kScrollingLayerOptions);
SMARTMATRIX_ALLOCATE_SCROLLING_LAYER(scrollingLayer08, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kScrollingLayerOptions);
SMARTMATRIX_ALLOCATE_SCROLLING_LAYER(scrollingLayer09, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kScrollingLayerOptions);

// For larger fonts, you need to allocate enough memory to hold the entire scrolling text bitmap (the bounds of all the characters in the string) in memory
//   and this may be larger than kMatrixWidth*kMatrixHeight.  Use the SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER macro, which allows for setting the character bitmap
//   to different size than the matrix bounds.  We allocate several times the default amount of memory here:
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer00, kMatrixWidth, kMatrixHeight, kMatrixWidth*3, kMatrixHeight, COLOR_DEPTH, kScrollingLayerOptions);

// Basing the bitmap allocation on the matrix size doesn't make much sense, as the size of the scrolling text is mostly independent of matrix size
//   Here we allocate an amount of memory that is known to work as a 6kb wide bitmap, with height of 1.  The layer will resize the bitmap to fit the text
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer01, kMatrixWidth, kMatrixHeight, 6*1024, 1, COLOR_DEPTH, kScrollingLayerOptions);

// The required sizes for some of the layers are printed out to serial, so these layers can be sized exactly to the needed bitmap dimensions
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer02, kMatrixWidth, kMatrixHeight, 168, 48, COLOR_DEPTH, kScrollingLayerOptions);
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer03, kMatrixWidth, kMatrixHeight, 224, 40, COLOR_DEPTH, kScrollingLayerOptions);

// The same thing but in total pixels not width/height:
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer04, kMatrixWidth, kMatrixHeight, 8832, 1, COLOR_DEPTH, kScrollingLayerOptions);

// For small strings/fonts, there's actually a memory savings compared to the previous method of sizing the layer bitmap to the matrix size:
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer05, kMatrixWidth, kMatrixHeight, 256, 1, COLOR_DEPTH, kScrollingLayerOptions);

// This layer is sized too small, and you'll only see part of the text on the screen, also look for "Layer 06 text doesn't fit" printed to Serial
SMARTMATRIX_ALLOCATE_GFX_MONO_LAYER(scrollingLayer06, kMatrixWidth, kMatrixHeight, 128, 1, COLOR_DEPTH, kScrollingLayerOptions);


const int randomSpeedMax = 120;
const int randomSpeedMin = 20;

// select random offset, from range of what's visible on screen (0 - matrixHeight-layerHeight)
void chooseRandomVisibleOffset(SMLayerGFXMono<rgb24, rgb1, 0> *layer) {
  // first get the total number of pixels on the screen in the text's vertical direction
  int16_t maxHeight = (layer->getLayerRotation() % 2) ? kMatrixWidth : kMatrixHeight;

  // subtract the height of the text
  maxHeight -= layer->getLocalHeight();

  // get random number within range 0..maxHeight
  if(maxHeight > 0)
    maxHeight = random(maxHeight);
  else
    maxHeight = 0;

  // set offset that keeps the text visible
  layer->setOffsetFromTop(maxHeight);
}

rgb24 chooseRandomBrightColor() {
  rgb24 color;

  switch(random(3)) {
    case 0:
      color = rgb24(random(0x80, 0x100), random(0x40), random(0x40));
      break;

    case 1:
      color = rgb24(random(0x40), random(0x80, 0x100), random(0x40));
      break;

    case 2:
      color = rgb24(random(0x40), random(0x40), random(0x80, 0x100));
    default:
      break;
  }
  return color;
}

void printDimensionsOfString(const char text[], const GFXfont *font) {
  // we use scrollingLayer00 for this as it's a temporary test, and we'll set the actual font and text layer
  uint16_t w, h;
  scrollingLayer00.setFont(font);
  scrollingLayer00.getDimensionsOfPrintedString(text, &w, &h);
  Serial.print(w);
  Serial.print("x");
  Serial.print(h);
  Serial.print(" (");
  Serial.print(w*h);
  Serial.print(" pixels, ");
  Serial.print(w*h/8);
  Serial.println(" bytes)");
}

#ifdef INCLUDE_FASTLED_BACKGROUND
#include <FastLED.h>

// The 32bit version of our coordinates
static uint16_t x;
static uint16_t y;
static uint16_t z;

// We're using the x/y dimensions to map to the x/y pixels on the matrix.  We'll
// use the z-axis for "time".  speed determines how fast time moves forward.  Try
// 1 for a very slow moving effect, or 60 for something that ends up looking like
// water.
// uint16_t speed = 1; // almost looks like a painting, moves very slowly
uint16_t speed = 10; // a nice starting speed, mixes well with a scale of 100
// uint16_t speed = 33;
// uint16_t speed = 100; // wicked fast!

// Scale determines how far apart the pixels in our noise matrix are.  Try
// changing these values around to see how it affects the motion of the display.  The
// higher the value of scale, the more "zoomed out" the noise iwll be.  A value
// of 1 will be so zoomed in, you'll mostly see solid colors.

// uint16_t scale = 1; // mostly just solid colors
// uint16_t scale = 4011; // very zoomed out and shimmery
uint16_t scale = 31;

// This is the array that we keep our computed noise values in
uint8_t noise[kMatrixWidth][kMatrixHeight];

// Fill the x/y array of 8-bit noise values using the inoise8 function.
void fillnoise8() {
  for(int i = 0; i < kMatrixWidth; i++) {
    int ioffset = scale * i;
    for(int j = 0; j < kMatrixHeight; j++) {
      int joffset = scale * j;
      noise[i][j] = inoise8(x + ioffset,y + joffset,z);
    }
  }
  z += speed;
}

#endif

void setup() {
  Serial.begin(115200);

  delay(2000);

  randomSeed(analogRead(0));
#ifdef INCLUDE_FASTLED_BACKGROUND
  matrix.addLayer(&backgroundLayer); 
  // Initialize our coordinates to some random values
  x = random16();
  y = random16();
  z = random16();
#endif

  matrix.addLayer(&scrollingLayer00); 
/*
  matrix.addLayer(&scrollingLayer01); 
  matrix.addLayer(&scrollingLayer02); 
  matrix.addLayer(&scrollingLayer03); 
  matrix.addLayer(&scrollingLayer04); 
  matrix.addLayer(&scrollingLayer05); 
  matrix.addLayer(&scrollingLayer06); 
  matrix.addLayer(&scrollingLayer07); 
  matrix.addLayer(&scrollingLayer08); 
  matrix.addLayer(&scrollingLayer09); 
  */

  matrix.begin();

  Serial.print("Bitmap size to hold `Layer 00` in font FreeMono12pt7b: ");
  printDimensionsOfString("Layer 00", &FreeMono12pt7b);
  Serial.print("Bitmap size to hold `Layer 01` in font FreeMonoBold18pt7b: ");
  printDimensionsOfString("Layer 01", &FreeMonoBold18pt7b);
  Serial.print("Bitmap size to hold `Layer 02` in font FreeSerif24pt7b: ");
  printDimensionsOfString("Layer 02", &FreeSerif24pt7b);
  Serial.print("Bitmap size to hold `Layer 03` in font FreeMonoBoldOblique24pt7b: ");
  printDimensionsOfString("Layer 03", &FreeMonoBoldOblique24pt7b);
  Serial.print("Bitmap size to hold `Layer 04` in font FreeSansOblique24pt7b: ");
  printDimensionsOfString("Layer 04", &FreeSansOblique24pt7b);
  Serial.print("Bitmap size to hold `Layer 05` in font Picopixel: ");
  printDimensionsOfString("Layer 05", &Picopixel);
  Serial.print("Bitmap size to hold `Layer 06` in font FreeSerifItalic9pt7b: ");
  printDimensionsOfString("Layer 06", &FreeSerifItalic9pt7b);

  scrollingLayer00.setMode((ScrollMode)random(3));
  scrollingLayer01.setMode((ScrollMode)random(3));
  scrollingLayer02.setMode((ScrollMode)random(3));
  scrollingLayer03.setMode((ScrollMode)random(3));
  scrollingLayer04.setMode((ScrollMode)random(3));
  scrollingLayer05.setMode((ScrollMode)random(3));
  scrollingLayer06.setMode((ScrollMode)random(3));
  scrollingLayer07.setMode((ScrollMode)random(3));
  scrollingLayer08.setMode((ScrollMode)random(3));
  scrollingLayer09.setMode((ScrollMode)random(3));

  scrollingLayer00.setColor({0,0,0});
  scrollingLayer01.setColor(chooseRandomBrightColor());
  scrollingLayer02.setColor(chooseRandomBrightColor());
  scrollingLayer03.setColor(chooseRandomBrightColor());
  scrollingLayer04.setColor(chooseRandomBrightColor());
  scrollingLayer05.setColor(chooseRandomBrightColor());
  scrollingLayer06.setColor(chooseRandomBrightColor());
  scrollingLayer07.setColor(chooseRandomBrightColor());
  scrollingLayer08.setColor(chooseRandomBrightColor());
  scrollingLayer09.setColor(chooseRandomBrightColor());

  scrollingLayer00.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer01.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer02.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer03.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer04.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer05.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer06.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer07.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer08.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);
  scrollingLayer09.setSpeed(random(randomSpeedMax-randomSpeedMin) + randomSpeedMin);

  scrollingLayer00.setFont(&FreeMono12pt7b);
  scrollingLayer01.setFont(&FreeMonoBold18pt7b);
  scrollingLayer02.setFont(&FreeSerif24pt7b);
  scrollingLayer03.setFont(&FreeMonoBoldOblique24pt7b);
  scrollingLayer04.setFont(&FreeSansOblique24pt7b);
  scrollingLayer05.setFont(&Picopixel);
  scrollingLayer06.setFont(&FreeSerifItalic9pt7b);
  scrollingLayer07.setFont(&TomThumb);
  scrollingLayer08.setFont(&Tiny3x3a2pt7b);
  scrollingLayer09.setFont(&Org_01);

  scrollingLayer00.setRotation(random(4));
  scrollingLayer01.setRotation(random(4));
  scrollingLayer02.setRotation(random(4));
  scrollingLayer03.setRotation(random(4));
  scrollingLayer04.setRotation(random(4));
  scrollingLayer05.setRotation(random(4));
  scrollingLayer06.setRotation(random(4));
  scrollingLayer07.setRotation(random(4));
  scrollingLayer08.setRotation(random(4));
  scrollingLayer09.setRotation(random(4));

  if(scrollingLayer00.start("Layer 00", -1) < 0)
    Serial.println("Layer 00 text doesn't fit");
  if(scrollingLayer01.start("Layer 01", -1) < 0)
    Serial.println("Layer 01 text doesn't fit");
  if(scrollingLayer02.start("Layer 02", -1) < 0)
    Serial.println("Layer 02 text doesn't fit");
  if(scrollingLayer03.start("Layer 03", -1) < 0)
    Serial.println("Layer 03 text doesn't fit");
  if(scrollingLayer04.start("Layer 04", -1) < 0)
    Serial.println("Layer 04 text doesn't fit");
  if(scrollingLayer05.start("Layer 05", -1) < 0)
    Serial.println("Layer 05 text doesn't fit");
  if(scrollingLayer06.start("Layer 06", -1) < 0)
    Serial.println("Layer 06 text doesn't fit");
  if(scrollingLayer07.start("Layer 07", -1) < 0)
    Serial.println("Layer 07 text doesn't fit");
  if(scrollingLayer08.start("Layer 08", -1) < 0)
    Serial.println("Layer 08 text doesn't fit");
  if(scrollingLayer09.start("Layer 09", -1) < 0)
    Serial.println("Layer 09 text doesn't fit");

  chooseRandomVisibleOffset(&scrollingLayer00);
  chooseRandomVisibleOffset(&scrollingLayer01);
  chooseRandomVisibleOffset(&scrollingLayer02);
  chooseRandomVisibleOffset(&scrollingLayer03);
  chooseRandomVisibleOffset(&scrollingLayer04);
  chooseRandomVisibleOffset(&scrollingLayer05);
  chooseRandomVisibleOffset(&scrollingLayer06);
  chooseRandomVisibleOffset(&scrollingLayer07);
  chooseRandomVisibleOffset(&scrollingLayer08);
  chooseRandomVisibleOffset(&scrollingLayer09);
}

void loop() {
#ifdef INCLUDE_FASTLED_BACKGROUND
  
  static uint8_t circlex = 0;
  static uint8_t circley = 0;

  // if sketch uses swapBuffers(false), wait to get a new backBuffer() pointer after the swap is done:
  while(backgroundLayer.isSwapPending());

  rgb24 *buffer = backgroundLayer.backBuffer();

  static uint8_t ihue=0;
  fillnoise8();
  for(int i = 0; i < kMatrixWidth; i++) {
    for(int j = 0; j < kMatrixHeight; j++) {
      // We use the value at the (i,j) coordinate in the noise
      // array for our brightness, and the flipped value from (j,i)
      // for our pixel's hue.
      buffer[kMatrixWidth*j + i] = CRGB(CHSV(noise[j][i],255,noise[i][j]));

      // You can also explore other ways to constrain the hue used, like below
      // buffer[kMatrixHeight*j + i] = CRGB(CHSV(ihue + (noise[j][i]>>2),255,noise[i][j]));
    }
  }
  ihue+=1;

  backgroundLayer.fillCircle(circlex % kMatrixWidth, circley % kMatrixHeight, 6, (rgb24)CRGB(CHSV(ihue+128,255,255)));
  circlex += random16(2);
  circley += random16(2);

  // buffer is filled completely each time, use swapBuffers without buffer copy to save CPU cycles
  backgroundLayer.swapBuffers(false);
  matrix.countFPS();      // print the loop() frames per second to Serial
  EVERY_N_SECONDS( 1 ) {   
    Serial.print("Refresh Rate: ");
    Serial.println(matrix.getRefreshRate()); 
  }

#else  

  Serial.print("Refresh Rate: ");
  Serial.println(matrix.getRefreshRate());
  delay(1000);

#endif
}

Hi Louis, Thanks for your efforts.

I used your example as a start because I saw it was working, But I don’t want to use as a scroll. One of the advantages I saw on the GFX is to use coordinates to exhibit the texts at specific places, mount “the image” and just swapBuffers to show them. And with this in mind, the serColor({0,0,0}) didn’t work

In the end, I’m using the GFX_MONO_LAYER as you suggested and I just created a fakeBlack color as {0x1,0x1,0x11) and this is “accepted” as black. Duct tape but it’s working…

You are amazing, man. I’ve read the huge amount of answers you give to this community and all you do to try to help people. There are no words to thank you enough.

Hi
what is “scrolling layer” and where I could read about it?

Hi b707,

You can read about the layers on the wiki.

1 Like

Very interesting
Great idea
Thanks