Gif display...

Hello all!
Just to share a small piece of code I have done to display gif on a TFT display (IC controller - ILI9340). I share it but actually it would be very nice if you could give me some advice or possible improvements.

It is composed of 2 parts: the first one on processing to convert the gif file into a text file (store on the SD card) and the 2nd one on the arduino itself.

Processing:

PImage img;
PrintWriter output;
String imageName = "arduino.gif";
String saveDir = "./textFiles/";

void setup() {
  img = loadImage(imageName);
  size(img.width, img.height);
  output = createWriter(saveDir + imageName.substring(0, (imageName.length()-4)) + ".txt");
  background(255, 255, 0);
  
  int[][] imgPixels = new int[img.width][img.height];
  int[] findedColors = new int[1];
  int[][] b_pix = new int[img.width][img.height];
  
  output.print(img.width + ",");
  output.println(img.height);
  
  for (int i = 0; i < img.height; i++) { // création du tableau représentant l'image avec la valeur de la couleur codée en 16bit et = à -1 si transparant (int imgPixels[x][y])
    for (int j = 0; j < img.width; j++) {
      
      color tmp = img.get(j, i);
      int red = tmp >> 16 & 0xFF;
      int green = tmp >> 8 & 0xFF;
      int blue = tmp & 0xFF;
      float alpha = alpha(tmp);
      
      if(alpha == 255) {
        imgPixels[j][i] = Color565(red, green, blue);
      } else {
        imgPixels[j][i] = -1;
      }
    }
  }
  
  findedColors[0] = imgPixels[0][0];
  for (int i = 0; i < img.height; i++) { // création du tableau contenant les couleurs présentes dans l'image (int findedColors[num_couleur])
    for (int j = 0; j < img.width; j++) {
      boolean flag = false;
      for(int k = 0; k < findedColors.length; k++) {
        if(findedColors[k] == imgPixels[j][i]) {
          flag = true;
        }
      }
      if(!flag) {
        findedColors = append(findedColors, imgPixels[j][i]);
      }
    }
  }
  
  for(int i = 0; i < findedColors.length-1; i++) {
    output.print(findedColors[i] + ",");
  }
  output.println(findedColors[findedColors.length-1]);
  
  for(int k = 0; k < findedColors.length; k++) { // création du tableau d'entier représantant les coupeurs de pixels coresspondant (int b_pix[x][y])
    for (int i = 0; i < img.height; i++) {
      for (int j = 0; j < img.width; j++) {
        if(findedColors[k] == imgPixels[j][i]) {
          b_pix[j][i] = k;
        }
      }
    }
  }
  
  for(int i = 0; i < img.height; i++) { // sauvegarde du tableau représentant l'image
    for(int j = 0; j < img.width-1; j++) {
      output.print(b_pix[j][i] + ",");
    }
    output.print(b_pix[img.width-1][i]);
    if(i != img.height - 1)
      output.print(',');
  }
  
  output.flush();
  output.close();
}

void draw() {
  image(img, 0, 0);
}

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

the second on the uControler:
gif.h

class gif {
 public:
  File f_gif;
  unsigned short n_width;
  unsigned short n_height;
  unsigned short n_colorN;
  unsigned short n_color[];
  unsigned long posColors;
  unsigned long posPicture;

  gif(File file);
  unsigned short getColorsN();
  void getColors();
  void beginPicture();
  unsigned short getPixelcolor();
  int getVal();
  unsigned short getWidth();
  unsigned short getHeight();
  void info();
};

gif.cpp

#include <SD.h>
#include "gif.h"

gif::gif(File file) {
  this->f_gif = file;
  this->n_width = getVal();
  this->n_height = getVal();
  this->posColors = this->f_gif.position();
  this->n_colorN = getColorsN();
  this->posPicture = this->f_gif.position();
  getColors();
}

unsigned short gif::getColorsN(){
  this->f_gif.seek(posColors);
  unsigned short color = 0;
  char tmp1 = this->f_gif.read();
  while(this->f_gif.available() && tmp1 != '\r') {
    if(tmp1 == ',') {
      color++;
    }
    tmp1 = this->f_gif.read();
  }
  return color = color + 1;
}

void gif::getColors(){
  this->f_gif.seek(this->posColors);
  this->n_color[this->n_colorN];
  
  for(int i = 0; i < this->n_colorN; i++) {
    this->n_color[i] = getVal();
  }
}

int gif::getVal() {
  unsigned long initPos = this->f_gif.position();
  unsigned short car = 0;
  char tmp1 = this->f_gif.read();
  while(tmp1 == '\n' || tmp1 == '\r') {
    tmp1 = this->f_gif.read();
  }
  while(this->f_gif.available() && tmp1 != ',' && tmp1 != '\r') {
    car++;
    tmp1 = this->f_gif.read();
  }
  
  this->f_gif.seek(initPos);
  char val[car+1];
  int i = 0;
  tmp1 = this->f_gif.read();
  while(tmp1 == '\n' || tmp1 == '\r') {
    tmp1 = this->f_gif.read();
  }
  while(i < car) {
    val[i] = tmp1;
    i++;
    tmp1 = this->f_gif.read();
  }
  val[i] = NULL;
  return atoi(val);
}

void gif::beginPicture() {
  this->f_gif.seek(this->posPicture);
}

unsigned short gif::getPixelcolor() {
  return this->n_color[getVal()];
}

unsigned short gif::getWidth() {
  return this->n_width;
}

unsigned short gif::getHeight() {
  return this->n_height;
}

void gif::info() {
  Serial.print("n_width = ");
  Serial.println(this->n_width);
  Serial.print("n_height = ");
  Serial.println(this->n_height);
  Serial.print("n_colorN = ");
  Serial.println(this->n_colorN);
  Serial.print("posColors = ");
  Serial.println(this->posColors);
  Serial.print("posPicture = ");
  Serial.println(posPicture);
  for(int i = 0; i < this->n_colorN; i++) {
    Serial.print("n_color[");
    Serial.print(i);
    Serial.print("] = ");
    Serial.println(this->n_color[i]);
  }
}

and the main

#include <Adafruit_GFX.h>
#include "Adafruit_ILI9340.h"
#include <SPI.h>
#include <SD.h>
#include "gif.h"

#define TFT_RST 9
#define TFT_DC 8
#define TFT_CS 10
#define SD_CS 4

Adafruit_ILI9340 tft = Adafruit_ILI9340(TFT_CS, TFT_DC, TFT_RST);

unsigned short num = 1;
unsigned short BGcolor;

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

  Serial.print("Initializing SD card... ");
  if (!SD.begin(SD_CS)) {
    Serial.println("failed!");
  } else {
    Serial.println("OK!");
  }

  tft.begin();
  
  BGcolor = tft.Color565(1, 150, 156);
}

void loop() {
  
  tft.fillScreen(BGcolor);
  
  char fileName[] = "img/arduino.txt";
  File imgFile = SD.open(fileName);
  if (imgFile) {
    gif logo = gif(imgFile);
    logo.beginPicture();
    
    int posX = 34;
    int posY = 78;
    //test.voir();
    unsigned long debut = millis();
    tft.setAddrWindow(posX, posY, posX+logo.getWidth()-1, posY+logo.getHeight()-1);
    for(int i = 0; i < logo.getWidth()*logo.getHeight() - 1; i++) {
      unsigned short pix = logo.getPixelcolor();
      tft.pushColor(pix);
    }
    imgFile.close();
    Serial.print(num);
    Serial.print(" : The picture ");
    Serial.print(fileName);
    Serial.print(" was drawn in ");
    Serial.print(millis()-debut);
    Serial.println(" ms.");
    
    num++;
  }  else {
    Serial.print("error opening ");
    Serial.println(fileName);
  }
  delay(10000);
  
  tft.fillScreen(BGcolor);
  char fileName1[] = "img/arduino1.txt";
  File imgFile1 = SD.open(fileName1);
  if (imgFile1) {
    gif logo1 = gif(imgFile1);
    logo1.beginPicture();
    
    int posX = 34;
    int posY = 78;
    //test.voir();
    unsigned long debut = millis();
    tft.setAddrWindow(posX, posY, posX+logo1.getWidth()-1, posY+logo1.getHeight()-1);
    for(int i = 0; i < logo1.getWidth()*logo1.getHeight() - 1; i++) {
      unsigned short pix = logo1.getPixelcolor();
      tft.pushColor(pix);
    }
    imgFile1.close();
    Serial.print(num);
    Serial.print(" : The picture ");
    Serial.print(fileName1);
    Serial.print(" was drawn in ");
    Serial.print(millis()-debut);
    Serial.println(" ms.");
    
    num++;
  }  else {
    Serial.print("error opening ");
    Serial.println(fileName1);
  }
  delay(10000);
}

It takes 2.8 s to draw the arduino.txt file (172x85 pixels) and 4.1 s for arduino1.txt file (171x118 pixels).

arduino.gif

arduino1.gif

arduino.txt (28.6 KB)

arduino1.txt (39.5 KB)

  unsigned short n_color[];

What is this supposed to do? It defines n_color to be an array, but it doesn't provide a size, so no memory is allocated. You can't then populate this array with however much data you like.

Yep your right. When I create the object I cannot know how many colors the file will contain so I initialize it after I have count them with getColorsN(). That say it is true that my code is note very clear I should it in the constructor:

gif::gif(File file) {
  this->f_gif = file;
  this->n_width = getVal();
  this->n_height = getVal();
  this->posColors = this->f_gif.position();
  this->n_colorN = getColorsN();
  this->n_color[this->n_colorN];
  this->posPicture = this->f_gif.position();
  getColors();
}

void gif::getColors(){
  this->f_gif.seek(this->posColors);
  
  for(int i = 0; i < this->n_colorN; i++) {
    this->n_color[i] = getVal();
  }
}

Is there a better way to do it?

  this->n_color[this->n_colorN];

This does not set the size of an array, or allocate memory, or do anything useful.

This does exactly the same as

17;

(That is, nothing).

You need to use dynamic memory allocation (new or malloc) (and return the memory when you are done (delete or free)).

Great! :smiley:
so:

class gif {
 public:
  File f_gif;
  unsigned short n_width;
  unsigned short n_height;
  unsigned short n_colorN;
  unsigned short* n_color;
  unsigned long posColors;
  unsigned long posPicture;

  gif(File file);
  unsigned short getColorsN();
  void getColors();
  void beginPicture();
  unsigned short getPixelcolor();
  int getVal();
  unsigned short getWidth();
  unsigned short getHeight();
  void info();
};
gif::gif(File file) {
  this->f_gif = file;
  this->n_width = getVal();
  this->n_height = getVal();
  this->posColors = this->f_gif.position();
  this->n_colorN = getColorsN();
  this->n_color = (unsigned short*) malloc(this->n_colorN);
  this->posPicture = this->f_gif.position();
  getColors();
}

For the free() I suppose it is done automatically in the destructor?