Faster way of drawing bitmaps

11 May 2012

We've been trying to paint a 24-bit bitmap image onto our Nokia LCD purchased from sparkfun; we used the LCD driver from here, and the bitmap library from here.

So far, it works...but prints the image very slowly. The following code is what we're using to call the library functions:

   BitmapFile MyBitmap("/local/wasp.bmp");
    
    if (!MyBitmap.Initialize())
    {
        lcd.background(0x000000);
        lcd.cls();
        lcd.printf("CRITICAL ERROR");
        return 0;
    }
    
    //The following variables are used to fix an issue with the orientation
    //and flip the image 90 degrees counter clockwise.
    int c_x = MyBitmap.getHeight();
    int c_y = 0;
    
    for (int y = 0; y < MyBitmap.getHeight(); y = y + 1)
    {
        for (int x = 0; x < MyBitmap.getWidth(); x = x + 1)
        {
            int myPixel = MyBitmap.getPixel(c_x,c_y,false);
            
            //Because our image contains quite a bit of white space,
            //we check to see if the pixel is white, and avoid calling
            //the pixel function to paint it hoping to improve speed.
            //Unfortunately, speed didn't improve much at all.
            if (myPixel != 0xFFFFFF) lcd.pixel(x,y, myPixel);
            c_y = c_y + 1;
        }
        c_y = 0;
        c_x = c_x-1;
    }
    
    MyBitmap.close();

We're not familiar with a lot of the internal mechanics within the bitmap library, and have no idea how we could optimize the painting process.

Any help on this is appreciated as we have no experience in dealing with bitmaps. Also, it is not critical that the image be printed with high quality; resolution can suffer if necessary, but we hope to keep the coloring offered by the 24bit format intact.

11 May 2012

I am guessing that the ammount of time you spend looking if white, will dramaticaly slow down the writing process

Ceri

11 May 2012

First you need to find out what is taking so much time. A simple way is changing your myPixel=... to something else, such as a constant. Then you know if opening the bitmap takes time, or writing to your lcd display.

This file may also help: http://mbed.org/users/EricWieser/programs/Bitmap_copy_copy/lefnig/docs/main_8cpp_source.html

What probably helps is doing what they do, read an entire row from the bitmap file. Same is also true for writing. Your nokia library looks to be based on the library they use for writing to an LCD display. While they didnt document the function to write an entire row, it does exist, and I would expect it works exactly the same as in the link I posted. So I would try doing it the way as in that link.

11 May 2012

Look at the source code for the NokiaLCD class (you can see it from the project page). You can see that no matter what operation you do, the pixels get transferred one by one over an SPI serial interface, so it doesn't look like there's anything you can do about the drawing speed to the display (except to only draw what you need to - which you are doing by skipping white pixels) (or, to increase the baud rate of the SPI connection).

You should separate the bitmap rotation and the drawing by creating a new bitmap to hold the rotated image, and then draw to the display from that. That way, you can time those operations separately.

UPDATE: Looking at the bitmap code, each call to getPixel reads from the file. You may get better results using getRow, but probably not since these just read single values in a loop. I think you are just stuck. My gut feeling is that writing to the LCD is the bottleneck, not reading the file - but if it turns out that reading the file is really slow, you could do something like use a run-length-encoded format to store the file, so reading it would go faster (but you'd be writing a lot of code for that).

12 May 2012

agilent mbed wrote:

Look at the source code for the NokiaLCD class (you can see it from the project page). You can see that no matter what operation you do, the pixels get transferred one by one over an SPI serial interface, so it doesn't look like there's anything you can do about the drawing speed to the display (except to only draw what you need to - which you are doing by skipping white pixels) (or, to increase the baud rate of the SPI connection).

You should separate the bitmap rotation and the drawing by creating a new bitmap to hold the rotated image, and then draw to the display from that. That way, you can time those operations separately.

UPDATE: Looking at the bitmap code, each call to getPixel reads from the file. You may get better results using getRow, but probably not since these just read single values in a loop. I think you are just stuck. My gut feeling is that writing to the LCD is the bottleneck, not reading the file - but if it turns out that reading the file is really slow, you could do something like use a run-length-encoded format to store the file, so reading it would go faster (but you'd be writing a lot of code for that).

First of all thanks to all the people who've replied; you've been of great help in shedding some light for us on this subject.

I think the bottleneck is more likely going to be reading from the file, since the lcd can print fairly fast, at least from this video that's posted on the library's discussion page:

I tried the suggestion of looping without actually calling lcd.pixel() to see which function takes more time. The results show that reading and drawing takes 23 seconds, while reading alone takes also 23 seconds; reading nothing and printing a fixed pixel takes less than a second. This result is fairly coherent with what the video shows, that the lcd can actually draw pretty fast provided you can feed the data fast enough. Any thoughts on optimizing the bitmap reading to solve the bottleneck issue? It doesn't have to be bmp, we just need to display a picture on the screen that's all.

UPDATE

Erik - wrote:

First you need to find out what is taking so much time. A simple way is changing your myPixel=... to something else, such as a constant. Then you know if opening the bitmap takes time, or writing to your lcd display.

This file may also help: http://mbed.org/users/EricWieser/programs/Bitmap_copy_copy/lefnig/docs/main_8cpp_source.html

What probably helps is doing what they do, read an entire row from the bitmap file. Same is also true for writing. Your nokia library looks to be based on the library they use for writing to an LCD display. While they didnt document the function to write an entire row, it does exist, and I would expect it works exactly the same as in the link I posted. So I would try doing it the way as in that link.

I copied the code that calls the getRow() function and pasted into my program and it worked almost out of the box; all I had to do was use the int array instead of the char array...it even prints right side up!

Thanks to all who provided their input in this thread, we really appreciate your help. :)

12 May 2012

Good to hear that, out of curiosity, did you adapt it to use the pixel operation, or did you let it use the other option, blit()? (I ask because I wonder why those functions arent documented and/or shown in the example, while they do look useful).

agilent mbed wrote:

Look at the source code for the NokiaLCD class (you can see it from the project page). You can see that no matter what operation you do, the pixels get transferred one by one over an SPI serial interface, so it doesn't look like there's anything you can do about the drawing speed to the display (except to only draw what you need to - which you are doing by skipping white pixels) (or, to increase the baud rate of the SPI connection).

There is quite a large difference in the source code, the pixel option sets before writing each pixel the location where it needs to write. While the blit() command only sets the location once, and then transfers all data. That should be less overhead if it also works.

(Why don't these forums use standard bbcode)

12 May 2012

Erik - wrote:

Good to hear that, out of curiosity, did you adapt it to use the pixel operation, or did you let it use the other option, blit()? (I ask because I wonder why those functions arent documented and/or shown in the example, while they do look useful).

agilent mbed wrote:

Look at the source code for the NokiaLCD class (you can see it from the project page). You can see that no matter what operation you do, the pixels get transferred one by one over an SPI serial interface, so it doesn't look like there's anything you can do about the drawing speed to the display (except to only draw what you need to - which you are doing by skipping white pixels) (or, to increase the baud rate of the SPI connection).

There is quite a large difference in the source code, the pixel option sets before writing each pixel the location where it needs to write. While the blit() command only sets the location once, and then transfers all data. That should be less overhead if it also works.

(Why don't these forums use standard bbcode)

I followed the original code so it uses blit(). But looking closer at the function I see this:

case PCF8833:
     for (int i=0; i<width*height; i++) {
          putp(colour[i]);
     }
     break;

Which suggests to me that all it's doing is writing pixel by pixel.

12 May 2012

True that they are writing pixel by pixel, in the end you have to transfer that data. But if you look at the pixel function:

void NokiaLCD::pixel(int x, int y, int colour) {
_cs = 0;
_window(x, y, 1, 1);
_putp(colour);
_cs = 1;
}

This also calls the _windows function for every pixel, this function sends another 7 bytes per pixel you write, and depending on the LCD the color writing itself consists of 1.5 or 2 bytes. So then that looks to be a significant overhead (although of course in this case the bitmap reading was the bottleneck).

12 May 2012

Nice work figuring out where the bottleneck is - now we can pretty much ignore the LCD class, since the file I/O is 25 times slower!

Something funny is definitely going on for reading a file to take 23 seconds. When you copy a file from your PC to the MBED flash drive, it takes about a second (or less), and this is the same storage that you are reading your bitmap file from.

Lets gather more data and see if we can understand the behavior a little more deeply - then we can know if file I/O in general is slow, or if the problem is how the bitmap class is implemented.

I would Just use standard file I/O to time:

  1. Reading a block of 10000 bytes all at once
  2. Reading one byte 10000 times in a loop
  3. Reading one byte 10000 times in a loop calling open() every time (like getPixel does)
  4. Reading one byte 10000 times in a loop calling open() and seek() every time (like getPixel does)

As an aside, if you were writing this from scratch, you would use a format with RLE (run length encoding) like JPG, so you get more pixels per block of data read, and read whole rows of data at a time - not single pixels or bytes.

01 Oct 2017

This thread is quite old, but recently I tried using the FlashFileSystem from Adam Green and it was very easy to use and incredibly fast. It isn't drag-n-drop, since it requires preparing the bmp into flash. If that could work for your needs, you won't be disappointed with the performance.