Help Please! I need to convert bitmap to 565 format (UTFT Library)

Hi,

It is about 3rd day I am truing to convert that little custom design into .565 format to use it with UTFT library. BMP24toILI565 not worked in my windows environment, I have tried to find required DLLs, compile VBA script etc. no luck.

Please someone can convert my png design to required 24bit bitmap and then 565 raw format? I will very appreciate.

File is attached

Regards,
Turkel.

TFT-LCD-Display.png

converted to bmp, one step todo

TFT-LCD-Display.zip (7.36 KB)

Step 2 by means of - http://stackoverflow.com/questions/19131556/how-to-get-rgb888-24-bit-and-rgb565-16-bit-framebuffer-dump-from-a-jpg-ima

#!/usr/bin/python

import sys
from PIL import Image

if len(sys.argv) == 3:
    # print "\nReading: " + sys.argv[1]
    out = open(sys.argv[2], "wb")
elif len(sys.argv) == 2:
    out = sys.stdout
else:
    print "Usage: png2fb.py infile [outfile]"
    sys.exit(1)

im = Image.open(sys.argv[1])

if im.mode == "RGB":
    pixelSize = 3
elif im.mode == "RGBA":
    pixelSize = 4
else:
    sys.exit('not supported pixel mode: "%s"' % (im.mode))

pixels = im.tostring()
pixels2 = ""
for i in range(0, len(pixels) - 1, pixelSize):
    pixels2 += chr(ord(pixels[i + 2]) >> 3 | (ord(pixels[i + 1]) << 3 & 0xe0))
    pixels2 += chr(ord(pixels[i]) & 0xf8 | (ord(pixels[i + 1]) >> 5 & 0x07))
out.write(pixels2)
out.close()

Compared to the BMP above it is perfectly 2/3 of its size as expected.

If this script does not work you might try to tweak it.

TFT-LCD-Display (2).zip (4.28 KB)

Sorry if I missed something along the way, but what is wrong with using the converter the Author of UTFT supplied in the Tools folder? It is called ‘ImageConverter565.exe’.

Results attached. (Remove .txt extension, forum does not permit .raw extension attachments! ;))

Regards,

Graham

TFT-LCD-Display.raw.txt (150 KB)

Thank you guys for all your help :slight_smile: But I still couldn`t get luck, it seems like my code is not correct. UTFT example images works OK but the one I convert and the converted ones in above attachments are not works. Serial Error message is “BMP format not recognized.”

I haven`t changed anything in code and it is like below:

#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <ILI9341_due_config.h>
#include <ILI9341_due.h>

#define TFT_RST 8 // uncomment if you have ILI9340
#define TFT_DC 9 // Command/Data for LCD
#define TFT_CS 10 // Chip Select for LCD

#define SD_CS 7 // Chip Select for SD card

#define BUFFPIXELCOUNT 160 // size of the buffer in pixels
#define SD_SPI_SPEED SPI_HALF_SPEED // SD card SPI speed, try SPI_FULL_SPEED

SdFat sd; // set filesystem
SdFile bmpFile; // set filesystem
//ArduinoOutStream cout(Serial);

//ILI9341_due tft = ILI9341_due(TFT_CS, TFT_DC); //ILI9341
ILI9341_due tft = ILI9341_due(TFT_CS, TFT_DC, TFT_RST); //ILI9340


// store error strings in flash to save RAM
#define error(s) sd.errorHalt_P(PSTR(s))

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

 tft.begin();
 //tft.setSPIClockDivider(4);
 progmemPrint(PSTR("Initializing SD card..."));

 if (!sd.begin(SD_CS, SD_SPI_SPEED)){
 progmemPrintln(PSTR("failed!"));
 return;
 }
 progmemPrintln(PSTR("OK!"));
}

void loop()
{
 tft.setRotation(iliRotation270);
 bmpDraw("giraffe.565", 0, 0);
 delay(2000);

 bmpDraw("bg.raw", 0, 0);
 delay(2000);

}

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size takes more of the Arduino's RAM but
// makes loading a little faster.  

void bmpDraw(char* filename, int x, int y) {

 SdFile   bmpFile;
 int      bmpWidth, bmpHeight;   // W+H in pixels
 uint8_t  bmpDepth;              // Bit depth (currently must be 24)
 uint8_t headerSize;
 uint32_t bmpImageoffset;        // Start of image data in file
 uint32_t rowSize;     // Not always = bmpWidth; may have padding
 uint32_t fileSize;
 boolean  goodBmp = false;       // Set to true on valid header parse
 boolean  flip = true;        // BMP is stored bottom-to-top
 uint16_t w, h, row, col;
 uint8_t  r, g, b;
 uint32_t pos = 0, startTime;

 if ((x >= tft.width()) || (y >= tft.height())) return;

 progmemPrint(PSTR("Loading image '"));
 Serial.print(filename);
 Serial.println('\'');
 startTime = millis();
 // Open requested file on SD card
 if (!bmpFile.open(filename, O_READ)) {
 Serial.println("File open failed.");
 return;
 }
 else {
 //Serial.println("File opened.");
 }

 // Parse BMP header
 if (read16(bmpFile) == 0x4D42) { // BMP signature
 fileSize = read32(bmpFile);
 //progmemPrint(PSTR("File size: ")); Serial.println(fileSize);
 (void)read32(bmpFile); // Read & ignore creator bytes
 bmpImageoffset = read32(bmpFile); // Start of image data
 //progmemPrint(PSTR("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
 // Read DIB header
 headerSize = read32(bmpFile);
 //progmemPrint(PSTR("Header size: ")); Serial.println(headerSize);
 bmpWidth = read32(bmpFile);
 bmpHeight = read32(bmpFile);
 if (read16(bmpFile) == 1) { // # planes -- must be '1'
 bmpDepth = read16(bmpFile); // bits per pixel
 //progmemPrint(PSTR("Bit Depth: ")); Serial.println(bmpDepth);
 if (read32(bmpFile) == 0) // 0 = uncompressed
 {
 //progmemPrint(PSTR("Image size: "));
 //Serial.print(bmpWidth);
 //Serial.print('x');
 //Serial.println(bmpHeight);

 // If bmpHeight is negative, image is in top-down order.
 // This is not canon but has been observed in the wild.
 if (bmpHeight < 0) {
 bmpHeight = -bmpHeight;
 flip = false;
 }

 // Crop area to be loaded
 w = bmpWidth;
 h = bmpHeight;
 if ((x + w - 1) >= tft.width())  w = tft.width() - x;
 if ((y + h - 1) >= tft.height()) h = tft.height() - y;
 
 // Set TFT address window to clipped image bounds
 tft.setAddrWindow(x, y, x + w - 1, y + h - 1);

 if (bmpDepth == 16) //565 format
 {
 goodBmp = true; // Supported BMP format -- proceed!

 uint16_t buffer[BUFFPIXELCOUNT]; // pixel buffer

 bmpFile.seekSet(54); //skip header
 uint32_t totalPixels = (uint32_t)bmpWidth*(uint32_t)bmpHeight;
 uint16_t numFullBufferRuns = totalPixels / BUFFPIXELCOUNT;
 for (uint32_t p = 0; p < numFullBufferRuns; p++) {
 // read pixels into the buffer
 bmpFile.read(buffer, 2 * BUFFPIXELCOUNT);
 // push them to the diplay
 tft.pushColors(buffer, 0, BUFFPIXELCOUNT);
 
 }

 // render any remaining pixels that did not fully fit the buffer
 uint32_t remainingPixels = totalPixels % BUFFPIXELCOUNT;
 if (remainingPixels > 0)
 {
 bmpFile.read(buffer, 2 * remainingPixels);
 tft.pushColors(buffer, 0, remainingPixels);
 }

 }
 //else if (bmpDepth == 24) // standard 24bit bmp
 //{
 // goodBmp = true; // Supported BMP format -- proceed!
 // uint16_t bufferSize = min(w, BUFFPIXELCOUNT);
 // uint8_t  sdbuffer[3 * bufferSize]; // pixel in buffer (R+G+B per pixel)
 // uint16_t lcdbuffer[bufferSize];  // pixel out buffer (16-bit per pixel)

 // // BMP rows are padded (if needed) to 4-byte boundary
 // rowSize = (bmpWidth * 3 + 3) & ~3;

 // for (row = 0; row < h; row++) { // For each scanline...
 // // Seek to start of scan line.  It might seem labor-
 // // intensive to be doing this on every line, but this
 // // method covers a lot of gritty details like cropping
 // // and scanline padding.  Also, the seek only takes
 // // place if the file position actually needs to change
 // // (avoids a lot of cluster math in SD library).

 // if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
 // pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
 // else     // Bitmap is stored top-to-bottom
 // pos = bmpImageoffset + row * rowSize;
 // if (bmpFile.curPosition() != pos) { // Need seek?
 // bmpFile.seekSet(pos);
 // }

 // for (col = 0; col < w; col += bufferSize)
 // {
 // // read pixels into the buffer
 // bmpFile.read(sdbuffer, 3 * bufferSize);

 // // convert color
 // for (int p = 0; p < bufferSize; p++)
 // {
 // b = sdbuffer[3 * p];
 // g = sdbuffer[3 * p + 1];
 // r = sdbuffer[3 * p + 2];
 // lcdbuffer[p] = tft.color565(r, g, b);
 // }
 // // push buffer to TFT
 // tft.pushColors(lcdbuffer, 0, bufferSize);
 // }

 // // render any remaining pixels that did not fully fit the buffer
 // uint16_t remainingPixels = w % bufferSize;
 // if (remainingPixels > 0)
 // {
 // bmpFile.read(sdbuffer, 3 * remainingPixels);

 // for (int p = 0; p < remainingPixels; p++)
 // {
 // b = sdbuffer[3 * p];
 // g = sdbuffer[3 * p + 1];
 // r = sdbuffer[3 * p + 2];
 // lcdbuffer[p] = tft.color565(r, g, b);
 // }

 // tft.pushColors(lcdbuffer, 0, remainingPixels);
 // }
 // }
 //}
 else
 {
 progmemPrint(PSTR("Unsupported Bit Depth."));
 }

 if (goodBmp)
 {
 progmemPrint(PSTR("Loaded in "));
 Serial.print(millis() - startTime);
 Serial.println(" ms");
 }
 }
 }
 }

 bmpFile.close();
 if (!goodBmp) progmemPrintln(PSTR("BMP format not recognized."));
}


// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(SdFile& f) {
 uint16_t result;
 ((uint8_t *)&result)[0] = f.read(); // LSB
 ((uint8_t *)&result)[1] = f.read(); // MSB
 return result;
}

uint32_t read32(SdFile& f) {
 uint32_t result;
 ((uint8_t *)&result)[0] = f.read(); // LSB
 ((uint8_t *)&result)[1] = f.read();
 ((uint8_t *)&result)[2] = f.read();
 ((uint8_t *)&result)[3] = f.read(); // MSB
 return result;
}

// Copy string from flash to serial port
// Source string MUST be inside a PSTR() declaration!
void progmemPrint(const char *str) {
 char c;
 while (c = pgm_read_byte(str++)) Serial.print(c);
}

// Same as above, with trailing newline
void progmemPrintln(const char *str) {
 progmemPrint(str);
 Serial.println();
}

//void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex
//{
// char tmp[length*5+1];
// byte first;
// byte second;
// for (int i=0; i<length; i++) {
// first = (data[i] >> 4) & 0x0f;
// second = data[i] & 0x0f;
// // base for converting single digit numbers to ASCII is 48
// // base for 10-16 to become upper-case characters A-F is 55
// // note: difference is 7
// tmp[i*5] = 48; // add leading 0
// tmp[i*5+1] = 120; // add leading x
// tmp[i*5+2] = first+48;
// tmp[i*5+3] = second+48;
// tmp[i*5+4] = 32; // add trailing space
// if (first > 9) tmp[i*5+2] += 7;
// if (second > 9) tmp[i*5+3] += 7;
// }
// tmp[length*5] = 0;
// Serial.print(tmp);
//}

Actually I am able to use ImageConverter565.exe but as you see above message without luck.
I use Photoshop to convert it to 24bit bmp format.
The library I use is ILI9341_due_master: ILI9341_due
I was not able to make work BMP24toILI565 in my windows so I tried ImageConverter565.exe

Sample Image is attached by library.

images.zip (213 KB)

That is your own fault for supplying us with bad information then!! Why on earth put (UTFT Library) on the end of your subject post? ? ? ? ? Try replacing it with (ILI9341_due Library) !!!

Regards,

Graham

Edit: Attached is the file you wanted converting in ILI9341_due format.

TFT-LCD-Display.zip (3.9 KB)

Yes, I am sorry, I have lost between these libraries. I hve tried UTFT, Adafruite and DUE ILI9341 libraries all.

But the last converted image works perfectly :slight_smile: Thank you very much for your help!

Can you also tell me how you did it, so I don`t want to brother anyone in future about same case. I have tried BMP24toILI565 converter first, and it gave me error about missed msvcr120d.dll. After solving this case I am getting another error which is "The Application was unable to start correctly (0XC000007B)..."

I used windows paint to convert from .PNG to .BMP. I used BMP24toILI565.exe to convert .BMP to .565.

You say you fixed the missing DLL error......... How? if you downloaded that specific DLL from 'somewhere on the net', then you will continue to get errors. You should download the entire Visual Studio C++ runtime libraries package from Microsoft here :- Microsoft Corporation

Try that....... But one thing I also noticed, you said it was msvcr120d.dll which was missing, which is a slight 'bad' on Marek's part, it means he released the Debug version, he should have released the Release version.

Please download the package from Microsoft I mentioned above if that is not what you did......

Also, try the Release version of BMP24toILI565.exe I have attached here. (Remove .txt from the filename!!)

Let me know if this fixes the problem for you?

Regards,

Graham

BMP24toILI565.exe.txt (10.5 KB)

Huge Thanks for you, it works perfectly now. the one by library developer still returns error but yours one is great :slight_smile:

Hi,

It is again me :slight_smile:
I stack on transparent 8bit bitmap images which is not seems possible, so I want to know how can I use transparent images in my code ? may be to use C array but for conversation it also requires bitmap :S

Regards,
Turkel.

In order to have images with a transparent background, you would need to rewrite the bitmap function to ignore a certain color, like 0x0 (black). So where ever there is a 0x0 in the image array, the code will know to skip over that color and just show the others. Now if you do need black then you need to use 0x0001 (0x01) instead. We won't be able to see the difference but the code can.

Or if it is easier for you, you can leave black as is and just make the transparent color 0x01.

Hmm yes good idea, I use below function:

tft.drawImage(icon, 15, 15, iconWidth, iconHeight);

This is ILI9341 library function, how can I rewrite it to exclude specific array colors ?

I don't think you can (easily). But since your image is 15x15 only you may try to render it pixel by pixel with drawPixel(x,y,color) function. Just loop thru all the icon pixels skipping those with the specific color that you don't want to draw (keep incrementing x and y). It's slow but it might work good enough in your case.

I don't think we have the same GFX library.

Being that the images are just an array of 1's and 0's, this first function skips any pixel that is 0. This should automatically give you a transparent background.

void Adafruit_GFX::drawBitmap(int16_t x, int16_t y,
const uint8_t *bitmap, int16_t w, int16_t h,
uint16_t color) {

int16_t i, j, byteWidth = (w + 7) / 8;

for(j=0; j<h; j++) {
for(i=0; i<w; i++ ) {
if(pgm_read_byte(bitmap + j * byteWidth + i / 8 ) & (128 >> (i & 7))) {
drawPixel(x+i, y+j, color);
}
}
}
}

This function here allows the user to set a background color to whatever they want.

// Draw a 1-bit color bitmap at the specified x, y position from the
// provided bitmap buffer (must be PROGMEM memory) using color as the
// foreground color and bg as the background color.
void Adafruit_GFX::drawBitmap(int16_t x, int16_t y,
const uint8_t *bitmap, int16_t w, int16_t h,
uint16_t color, uint16_t bg) {

int16_t i, j, byteWidth = (w + 7) / 8;

for(j=0; j<h; j++) {
for(i=0; i<w; i++ ) {
if(pgm_read_byte(bitmap + j * byteWidth + i / 8 ) & (128 >> (i & 7))) {
drawPixel(x+i, y+j, color);
}
else {
drawPixel(x+i, y+j, bg);
}
}
}
}

I assume he is drawing 16bit color icons (since he uses the drawImage function) and not monochromatic icons that can be drawn with drawBitmap function which supports transparent background.

Perhaps I am the one who is behind as I do not have a drawImage() function in any of my Adafruit libraries.

Actually I will have several sized icons so can`t go pixel by pixel. Here is the code which drews picture, there is for loop but I am not sure how to exclude specific pixel code from this array:

 // Parse BMP header
  if (read16(bmpFile) == 0x4D42) { // BMP signature
    fileSize = read32(bmpFile);

    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data

    // Read DIB header
    headerSize = read32(bmpFile);

    bmpWidth = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel

      if (read32(bmpFile) == 0) // 0 = uncompressed
      {

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft.width())  w = tft.width() - x;
        if ((y + h - 1) >= tft.height()) h = tft.height() - y;
      
        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x + w - 1, y + h - 1);

        if (bmpDepth == 16) //565 format
        {
          goodBmp = true; // Supported BMP format -- proceed!

          uint16_t buffer[BUFFPIXELCOUNT]; // pixel buffer

          bmpFile.seekSet(54);  //skip header
          uint32_t totalPixels = (uint32_t)bmpWidth*(uint32_t)bmpHeight;
          uint16_t numFullBufferRuns = totalPixels / BUFFPIXELCOUNT;
          for (uint32_t p = 0; p < numFullBufferRuns; p++) {
            // read pixels into the buffer
            bmpFile.read(buffer, 2 * BUFFPIXELCOUNT);
            // push them to the diplay
            tft.pushColors(buffer, 0, BUFFPIXELCOUNT);
            
          }

          // render any remaining pixels that did not fully fit the buffer
          uint32_t remainingPixels = totalPixels % BUFFPIXELCOUNT;
          if (remainingPixels > 0)
          {
            bmpFile.read(buffer, 2 * remainingPixels);
            tft.pushColors(buffer, 0, remainingPixels);
          }

        }
      }
    }
  }

It would be in this for loop here:

for (uint32_t p = 0; p < numFullBufferRuns; p++) {
// read pixels into the buffer
bmpFile.read(buffer, 2 * BUFFPIXELCOUNT);
// push them to the diplay
tft.pushColors(buffer, 0, BUFFPIXELCOUNT);

}

Its reading the image and getting the colors, then passing them into the tft.pushColors() function. But you would actually need to go deeper than that and go into the pushColors() function itself and exclude the color you don't want.

What library is that drawImage() function from?

HazardsMind:
What library is that drawImage() function from?

Marek's ILI9341_due library.

I started to do something very similar to what trecords requires for my UTFT_CTE library, because while it is all well and good having lots of built-in icons in the font ic with black backgrounds, when you draw them on a red/blue/green/etc back ground, they look rubbish, but in the end it just slows the drawing routine down too much due to the way the drawing routine works. It is not possible to just 'skip' a pixel, because you set a draw area in the tft registers, then just write for example 32x32 pixels worth of data, and the x/y handling is done by the tft, so if you simply draw 'nothing' for 1 pixel, you have only actually drawn 32*31 +31 pixels if you see what I mean? So then it would come down to colour substitution which could get complicated. I am not sure what would be the best way of going about this.

Regards,

Graham