4/4/4 Bitmap converter for RGB matrix panel

EL Wire/Tape/Panels, LEDs, pixels and strips, LCDs and TFTs, etc products from Adafruit

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
kjetilei
 
Posts: 2
Joined: Sun Feb 16, 2014 9:48 am

4/4/4 Bitmap converter for RGB matrix panel

Post by kjetilei »

Hi,

I have created a bitmap converter in Processing 2.0 that takes a 32x32 PNG/GIF/JPG and converts it to a 32x32 matrix buffer compatible image.h file.

The script is based on the drawPixel function in the Adafruit RGB matrix panel library.

I have it up and running, but with some color problems, most likely due to some of the bits being manipulated incorrectly.

It would be great if better coders than me could have a look at the script, to see where I get it wrong. If needed I can also upload test files and photos for further diagnostics.
PImage image;
String imageName = "test.gif";
String saveDir = "./imageFiles/";
PrintWriter outputMatrix;
int [][] matrixbuff= new int [3][32];

void setup() {
selectInput("Select a file to process:", "imageName");
}

void imageName(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
}
else {
println("User selected " + selection.getAbsolutePath());
// Do note that the file used is from the hardcoded reference in the top of this file and not the file selection dialog - need more code to handle it correctly
image = loadImage(imageName);
size(image.width, image.height);
outputMatrix = createWriter(saveDir + "image.h");
// outputMatrix = createWriter(saveDir + imageName.substring(0, (imageName.length()-4)) + ".h");
background(0, 0, 0);
image(image, 0, 0);

outputMatrix.println("// Generated by Processing script");
outputMatrix.println("// Original file: " + imageName);
outputMatrix.println("// Size: " + image.width + "x" + image.height + "px");
outputMatrix.println("#include <avr/pgmspace.h>");
outputMatrix.println("static const uint8_t PROGMEM image[] = {");

//Convert all pixels from original color > 5/6/5 > 4/4/4 > matrix buffer format and save to file
//Can be done much more elegantly but I'm to rusty/lazy to do it properly at the moment
for (int y = 0; y < 16; y++) {
println("y:" + y);
for (int x = 0; x < 32; x++) {
print("x:" + x + " y:");

int limit;
int nPlanes = 4;

int c;
int r;
int g;
int b;
int c16;
int r16;
int g16;
int b16;

// Convert original color to 5/6/5 format
color tempColor = image.get(x, y);
int red = tempColor >> 16 & 0xFF;
int green = tempColor >> 8 & 0xFF;
int blue = tempColor & 0xFF;
float alpha = alpha(tempColor);

if (alpha == 255) {
c = Color565(red, green, blue);
}
else {
c = Color565(0, 0, 0);
}

tempColor = image.get(x, y+16);
red = tempColor >> 16 & 0xFF;
green = tempColor >> 8 & 0xFF;
blue = tempColor & 0xFF;
alpha = alpha(tempColor);

if (alpha == 255) {
c16 = Color565(red, green, blue);
}
else {
c16 = Color565(0, 0, 0);
}

// Matrix needs 4/4/4. Pluck out relevant bits from 5/6/5 while separating into R,G,B:
// Upper half

r = c >> 12; // RRRRrggggggbbbbb
g = (c >> 7) & 0xF; // rrrrrGGGGggbbbbb
b = (c >> 1) & 0xF; // rrrrrggggggBBBBb

//quick and dirty graytone gradient hack to figure out whats wrong with my adaption of the bit manipulation routines
//r = g = b = y;

println(y + " Color:" + c + ",B" + binary(r, 4) + binary(g, 4) + binary(b, 4) + ",0x" + hex(r, 1) + ",0x" + hex(g, 1) + ",0x" + hex(b, 1) + ",");

// Matrix needs 4/4/4. Pluck out relevant bits from 5/6/5 while separating into R,G,B:
// Lower half
r16 = c16 >> 12; // RRRRrggggggbbbbb
g16 = (c16 >> 7) & 0xF; // rrrrrGGGGggbbbbb
b16 = (c16 >> 1) & 0xF; // rrrrrggggggBBBBb
println(" y:" + (y + 16) + " Color:" + c16 + ",B" + binary(r16, 4) + binary(g16, 4) + binary(b16, 4) + ",0x" + hex(r16, 1) + ",0x" + hex(g16, 1) + ",0x" + hex(b16, 1) + ",");

// Data for the upper half of the display is stored in the lower bits of each byte.
// Plane 0 is a tricky case -- its data is spread about, stored in least two bits not used by the other planes.
matrixbuff[2][x] &= ~unbinary("00000011"); // Plane 0 R,G mask out in one op
if (boolean(r & 1)) matrixbuff[2][x] |= unbinary("00000001"); // Plane 0 R: 64 bytes ahead, bit 0
if (boolean(g & 1)) matrixbuff[2][x] |= unbinary("00000010"); // Plane 0 G: 64 bytes ahead, bit 1
if (boolean(b & 1)) {
matrixbuff[1][x] |= unbinary("00000001"); // Plane 0 B: 32 bytes ahead, bit 0
}
else {
matrixbuff[1][x] &= ~unbinary("00000001"); // Plane 0 B unset; mask out
}

// The remaining three image planes are more normal-ish.
// Data is stored in the high 6 bits so it can be quickly
// copied to the DATAPORT register w/6 output lines.

// Loop counter stuff
limit = 1 << nPlanes;
for (int bit = 2; bit < limit; bit <<= 1) {
matrixbuff[0][x] &= ~unbinary("00011100"); // Mask out R,G,B in one op
if (boolean(r & bit)) matrixbuff[0][x] |= unbinary("00000100"); // Plane N R: bit 2
if (boolean(g & bit)) matrixbuff[0][x] |= unbinary("00001000"); // Plane N G: bit 3
if (boolean(b & bit)) matrixbuff[0][x] |= unbinary("00010000"); // Plane N B: bit 4
}

// Data for the lower half of the display is stored in the upper
// bits, except for the plane 0 stuff, using 2 least bits.
matrixbuff[0][x] &= ~unbinary("00000011"); // Plane 0 G,B mask out in one op
if (boolean(r16 & 1)) {
matrixbuff[1][x] |= unbinary("00000010"); // Plane 0 R: 32 bytes ahead, bit 1
}
else {
matrixbuff[1][x] &= ~unbinary("00000010"); // Plane 0 R unset; mask out
}
if (boolean(g16 & 1)) matrixbuff[0][x] |= unbinary("00000001"); // Plane 0 G: bit 0
if (boolean(b16 & 1)) matrixbuff[0][x] |= unbinary("00000010"); // Plane 0 B: bit 0

// Loop counter stuff

for (int bit = 2; bit < limit; bit <<= 1) {
matrixbuff[0][x] &= ~unbinary("11100000"); // Mask out R,G,B in one op
if (boolean(r16 & bit)) matrixbuff[0][x] |= unbinary("00100000"); // Plane N R: bit 5
if (boolean(g16 & bit)) matrixbuff[0][x] |= unbinary("01000000"); // Plane N G: bit 6
if (boolean(b16 & bit)) matrixbuff[0][x] |= unbinary("10000000"); // Plane N B: bit 7
}
println(" Buffer0:0x" + hex(matrixbuff[0][x], 2) + " Buffer1:0x" + hex(matrixbuff[1][x], 2) + " Buffer2:0x" + hex(matrixbuff[2][x], 2));
}
for (int x = 0; x < image.width; x++) {
outputMatrix.print("0x" + hex(matrixbuff[0][x], 2) + ",");
}
outputMatrix.println("");
for (int x = 0; x < image.width; x++) {
outputMatrix.print("0x" + hex(matrixbuff[1][x], 2) + ",");
}
outputMatrix.println("");
for (int x = 0; x < image.width; x++) {
outputMatrix.print("0x" + hex(matrixbuff[2][x], 2) + ",");
}
outputMatrix.println("");
}
outputMatrix.println("};");
outputMatrix.flush();
outputMatrix.close();
}
}

int Color565(int r, int g, int b) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
As a backup plan I intend to make a Arduino sketch that accepts the image in 5/6/5 format without any bit manipulation, sends it to the buffer/panel through drawPixel calls and then dumps the buffer through a serial connection. This will be enough for my immediate needs, but I want to be able to share the Processing script with you guys for easy conversion.

Disclaimer: My panel was not bought from Adafruit - I live in Europe and the customs handling fees here would have made an Adafruit bought panel several times more expensive than the one I ended up buying. As such I don't expect support, but since this script is meant for the community and can help all Adafruit RGB matrix panel owners I hope for some leeway :)

User avatar
kjetilei
 
Posts: 2
Joined: Sun Feb 16, 2014 9:48 am

Re: 4/4/4 Bitmap converter for RGB matrix panel

Post by kjetilei »

While waiting/hoping for feedback on the bit manipulation problems (fingers crossed :)) I converted the Processing script to a simple 5/6/5 exporter and whipped out an equally simple Arduino sketch. Works beautifully but with a few steps more involved than if the 4/4/4 version is fixed.

I added the code for both below. Feel free to use :)

Processing:

Code: Select all

PImage image;
String imageName = "image.gif";
String saveDir = "./imageFiles/";
PrintWriter outputMatrix;

void setup() {
  selectInput("Select a file to process:", "imageName");
}

void imageName(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } 
  else {
    println("User selected " + selection.getAbsolutePath());
    // Do note that the file used is from the hardcoded reference in the top of this code and not the file selection dialog - need more code to handle it correctly 
    image = loadImage(imageName);
    size(image.width, image.height);
    outputMatrix = createWriter(saveDir + "image.h");
//    outputMatrix = createWriter(saveDir + imageName.substring(0, (imageName.length()-4)) + ".h");
    background(0, 0, 0);
    image(image, 0, 0);

    outputMatrix.println("// Generated by Processing script");
    outputMatrix.println("// Original file: " + imageName);
    outputMatrix.println("// Size: " + image.width + "x" + image.height + "px");
    outputMatrix.println("#include <avr/pgmspace.h>");
    outputMatrix.println("static const uint16_t PROGMEM image[] = {");

    //Convert all pixels from original color to 5/6/5 and save to file

    for (int y = 0; y < 32; y++) {
      println("y:" + y);
      for (int x = 0; x < 32; x++) {

        // Convert original color to 5/6/5 format
        color tempColor = image.get(x, y);
        int red = tempColor >> 16 & 0xFF;
        int green = tempColor >> 8 & 0xFF;
        int blue = tempColor & 0xFF;
        float alpha = alpha(tempColor);
        int c;

        if (alpha == 255) c = Color565(red, green, blue);
        else c = Color565(0, 0, 0);

        print("0x" + hex(c, 4) + ",");
        outputMatrix.print("0x" + hex(c, 4) + ",");
        if (x == 31) {
          println("");
          outputMatrix.println("");
        }
      }
    }
    outputMatrix.println("};");  
    outputMatrix.flush();
    outputMatrix.close();
  }
}

int Color565(int r, int g, int b) {
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
Arduino:

Code: Select all

#include <Adafruit_GFX.h>
#include <RGBmatrixPanel.h>
#include "image.h"

// If your 32x32 matrix has the SINGLE HEADER input,
// use this pinout:
#define CLK 11  // MUST be on PORTB! (Use pin 11 on Mega)
#define OE  9
#define LAT 10
#define A   A0
#define B   A1
#define C   A2
#define D   A3
// If your matrix has the DOUBLE HEADER input, use:
//#define CLK 8  // MUST be on PORTB! (Use pin 11 on Mega)
//#define LAT 9
//#define OE  10
//#define A   A3
//#define B   A2
//#define C   A1
//#define D   A0
RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, false);

void setup() {
  matrix.begin();
  ///*
  //Enable this section to dump matrix buffer to serial. Copy data to an image.h file and look into the colorwheel_progmem_32x32 for an example of how to display the image in the image.h file
  Serial.begin(115200);
  int counter;
  for (int y = 0; y < 32; y++) {
    for (int x = 0; x < 32; x++) {
      counter = (y*32)+x;
      // read back a 2-byte int
      // img variable used below must equal the name of the uint8_t progmem array in the image.h file
      matrix.drawPixel(x,y,pgm_read_word_near(img + counter));
    }
  }
  // Dump image to a serial connection - remember to open the serial window in the Arduino editor ;)
  matrix.dumpMatrix();
  //*/
}

void loop() {
  // Nothing
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Glowy things (LCD, LED, TFT, EL) purchased at Adafruit”