Displaying BMPs - freetronics/FTOLED GitHub Wiki
This is a guide to displaying bitmap images (BMP files) on the OLED128 display using the FTOLED library.
The FTOLED library supports BMPs with colour depths of 1, 2, 4, 8, 16 and 24 bits per pixel.
The more bits per pixel (bpp) the more realistic an image can look (although more than 16 makes no difference to FTOLED.) However higher bpp also means the image size is larger and the image takes longer to load.
For high detail images (slower to load) we recommended using 16 bits per pixel images (65536 colours.) For images that should be fast to load, or animations, we recommend 8 or 4 bits per pixel (256 or 16 colours, respectively.)
The FTOLED library does not support compressed BMPs, or transparent BMPs.
Most programs can save BMP images in a format which can be loaded by FTOLED. Here are a few examples for commonly available image editor programs:
If you use Windows, the "Paint" program can save images as suitable BMP files. Any of the four .BMP choices shown here are suitable.
GIMP is a free image editor program available on Windows, OS X and Linux. GIMP can save suitable 8, 16, or 24bpp BMP images.
-
For 8bpp images, select Image -> Mode -> Indexed to convert the image to 8bpp, then File -> Export As and choose a BMP file extension.
-
For 16bpp images, choose File -> Export As and then choose any of the "Advanced Options" choices as shown under "16 bit" or "24 bit" headings (it's suggested you choose "R5 G6 B5" as shown, as this matches the FTOLED output settings.)
When exporting from the GIMP, ensure the "Run-Length Encoded" checkbox is not checked.
ImageMagick is a free command line based image toolkit. You can use ImageMagick's "convert" tool to produce 4, 8 or 24bpp BMPs suitable for loading into FTOLED.
Here are some example ImageMagick command lines for producing suitable images:
For a 4bpp (16 colour) BMP:
convert <input> -background black -alpha remove -alpha off -colors 16 -compress none BMP2:output.bmp
For an 8bpp (256 colour) BMP:
convert <input> -background black -alpha remove -alpha off -colors 256 -compress none BMP2:output.bmp
For a 24bpp BMP:
convert <input> -background black -alpha remove -alpha off output.bmp
You can also use other ImageMagick "convert" options like -scale to resize images on the command line for use with FTOLED.
Add any resizing arguments at the beginning of the command line, after the input filename. For instance, to scale down an image to no larger than 128x128:
convert <input> -scale 128x128 -background black -alpha remove -alpha off -colors 256 -compress none BMP2:output.bmp
After you have your BMP file in an appropriate format, the easiest way to display it is to load it on an SD card and insert that into the OLED128's SD card slot.
You will need to have the additional SD card wires connected to the Arduino, as shown in the quickstart guide.
For an example of this technique, take a look at the example sketches demo_logo
(loading a single BMP) or flames
(loading a sequence of animation frames as BMPs.)
First you use the Arduino SD library to initialise the SD card:
const byte pin_sd_cs = 4;
void setup() {
SD.begin(pin_sd_cs);
oled.begin();
}
After both are initialised, you can load the BMP file from the SD card, and display it on the OLED128:
File image = SD.open("Label.bmp");
oled.displayBMP(image, 0, 0);
The method used here is:
BMP_Status displayBMP(File, X, Y)
Where File is a file loaded from the SD card, and X and Y are the bottom-left corner of the image.
For good performance, call oled.begin()
after SD.begin()
. This is because the SD library sets the SPI bus to 4MHz, but FTOLED sets it to 8MHz (16MHz on Arduino Due.) SD cards work fine at these higher speeds.
If you want to use the OLED display before the SD card is initialised (for instance to display errors in SD card intialisation, you can call begin()
twice - the second call resets the OLED module.
oled.begin();
if(!SD.begin(PIN_SD_CS)) {
oled.drawString(0,0,"SD Card Error", RED, BLACK);
}
oled.begin(); // reset the OLED display for optimum SPI performance
displayBMP() returns an enum of type "BMP_Status" which can give some details if the BMP fails to load:
BMP_Status result = oled.displayBMP(image, 0, 0);
if(result != BMP_OK) {
Serial.print("Error loading BMP ");
Serial.println((int)result);
}
The enum values and their numeric equivalents can be found in FTOLED.h:
enum BMP_Status {
BMP_OK = 0,
BMP_INVALID_FORMAT = 1,
BMP_UNSUPPORTED_HEADER = 2,
BMP_TOO_MANY_COLOURS = 3,
BMP_COMPRESSION_NOT_SUPPORTED = 4,
BMP_UNSUPPORTED_COLOURS = 5,
BMP_ORIGIN_OUTSIDE_IMAGE = 6
};
- 0/
BMP_OK
: Image displayed. - 1/
BMP_INVALID_FORMAT
: This didn't look like a valid BMP image to us. - 2/
BMP_UNSUPPORTED_HEADER
: BMP used a rare header format (not v2 or v3.) - 3/
BMP_TOO_MANY_COLOURS
: BMP used an unsupported colour depth (probably 32bpp) - 4/
BMP_COMPRESSION_NOT_SUPPORTED
: BMP used RLE (Run-Length Encoding) compression or another unsupported compression format. - 5/
BMP_UNSUPPORTED_COLOURS
: BMP used an unsupported bit depth (not 1,4,8,16 or 24bpp, or an unsupported colour layout (a 16bpp image which isn't RGB565 or RGB555.) - 6/
BMP_ORIGIN_OUTSIDE_IMAGE
: The arguments to displayBMP() specified source coordinates that were outside the image area (see below.)
It is also possible to use displayBMP to crop out a subsection of a larger image:
BMP_Status displayBMP(File, From_X, From_Y, To_X, To_Y);
The image will be displayed so that the bottom left corner of the source image at (From_X, From_Y)
will be shown on the display at display coordinates (To_X, To_Y)
.
If (From_X, From_Y)
turns out to be outside the image area, displayBMP returns status value 6 - BMP_ORIGIN_OUTSIDE_IMAGE
.
Images are automatically cropped to fit inside the 128x128 display. Very large BMP files (ie multi-megapixel photos) may fail to display, however. We recommend using an image editor program to crop and resize the image to the desired display size.
SD storage is the simplest and easiest way to display BMP images using FTOLED. However it is also possible to embed BMP images internally into the sketch itself.
This is a technique for advanced users, and the only real benefit to using it instead of the SD card is keeping the SD card slot free.
The technique requires processing the BMP image file into a C++ header file (.h extension) containing the same content, which is then compiled into the Arduino sketch.
For an example of storing images internally, look at the example sketch sprite
where the 32x32kb sprite animation images are compiled into the sketch.
In the FTOLED directory there is a Python script "bin2header.py" that takes in any binary file and outputs a header file with the contents of that file, ready to be compiled into an Arduino sketch.
bin2header.py is a command line script and requires Python 3 to be installed on yur computer.
The command line:
bin2header.py Photo.bmp
Produces a header file called "Photo.h" based on the source file "Photo.bmp". The header defines a constant variable called "Photo" pointing to the file data.
If you copy the generated header into your Arduino sketch's directory, and then reload the sketch, the header file should open in a tab inside the Arduino IDE alongside the main sketch file.
You can then add an include statement for the header at the top of your sketch:
#include "Photo.h"
And refer to "Photo" directly when calling displayBMP:
displayBMP(Photo, 0, 0);
When selecting BMP files for internal use, keep aware of some limits:
-
Many Arduino compatible boards only have 32kb of Flash - including hte Freetronics Eleven, EtherTen, LeoStick and other Arduino Uno or Leonardo compatibles. A lot of the 32kb is already needed for the sketch code. So only quite small BMPs can fit, for instance the 32x32 256 color sprites used in the sprite example.
-
Freetronics EtherMega and other Arduino Mega compatibles have much more room (256kb), as does Goldilocks (128kb) and the Arduino Due (512kb.)
-
On AVR-based boards (EtherMega, Arduino Mega, Goldilocks) any individual file processed by bin2header.py must be less than 32kb (a warning is printed otherwise.) The ARM-based Arduino Due does not have this limitation.
In general, SD card storage is much more flexible.
Displaying BMP images can be quite taxing on the limited processing capabilities of Arduino-compatible boards. In general, smaller images (resolution and bit depth) are faster and use less resources.
Here are some rules of thumb:
-
A standard Arduino-compatible (Freetronics Eleven, LeoStick, EtherMega, etc.) can display a 4bpp (16 colour) 128x128 BMP at about 4 frames per second (see the "flames" example.) Refresh is visible. Higher colour images are somewhat slower.
-
The ARM-based Arduino Due is much faster, approximately 10 frames per second for 128x128 4bpp.
-
Smaller images are much faster, all Arduino models are able to smoothly animate a 32x32 pixel sprite, with lots of processing power in reserve.
Rendering BMP images uses some RAM temporarily while the image is displayed, to buffer each row of pixels and also store any palette data. Sketches that perform a lot of other RAM-hungry functions, as well as BMP display, may run into problems. Especially on boards with only 2kb of RAM total (Freetronics Eleven, LeoStick, Arduino Uno.)
If you're worried about RAM usage in an already complex sketch, consider using either 4bpp (16 color) or 16bpp (65536 colour) BMPs.
A 128x128 4bpp image uses approximately 160 bytes of RAM (32 bytes palette + 128 bytes row buffer), 8bpp uses approximately 640 bytes of RAM (512 bytes palette + 128 bytes row buffer.) A 16bpp 128x128 image uses only 256 bytes of RAM (row buffer only.) 24bpp uses 384 bytes (row buffer only.)
All this RAM usage is temporary while displayBMP() is actually executing, it does not impact at other times while the sketch is running.