Loading Images

Loading .BMP images from an SD card is an option for most of our color displays…though it’s not built into Adafruit_GFX and must be separately installed.

The Adafruit_ImageReader library handles this task. It can be installed through the Arduino Library Manager (Sketch→Include Library→Manage Libraries…). Enter “imageread” in the search field and the library is easy to spot:

graphic_lcds_install-imagereader-lib.png
That’s “imageread,” not “ermahgerd.”

The syntax for using this library (and the separate installation above) are admittedly a bit peculiar…it’s a side-effect of the way Arduino handles libraries. We purposefully did not roll this into Adafruit_GFX because any mere mention of the SD library will incur all of that library’s considerable memory requirements…even if one’s sketch doesn’t use an SD card at all! A majority of graphics projects are self-contained and don’t reference files from a card…not everybody needs this functionality.

Using the Library

There are several example sketches in the Adafruit_ImageReader/examples folder. You can dissect these for ideas how to use the library in your own projects.

They all start with several #includes…

Download: file
#include <SPI.h>
#include <SD.h>
#include <Adafruit_GFX.h>         // Core graphics library
#include <Adafruit_ImageReader.h> // Image-reading functions
#include <Adafruit_ILI9341.h>     // Hardware-specific library

One of these lines may vary from one example to the next, depending which display hardware it’s written to support. Above we see it being used with the Adafruit_ILI9341 display library required of certain shields, FeatherWings or breakout boards. Others examples reference Adafruit_HX8357, Adafruit_ST7735, or other color TFT or OLED display libraries…use the right one for the hardware you have.

We declare a display object (called “tft” in most of the examples) the usual way…for example, with the 2.8 inch TFT touch shield for Arduino, it’s:

Download: file
#define SD_CS   4 // SD card select pin
#define TFT_CS 10 // TFT select pin
#define TFT_DC  9 // TFT display/command pin

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

And then also declare a global Adafruit_ImageReader object…we’ll call it “reader.” This will be used to access the image-loading functions later:

Download: file
Adafruit_ImageReader reader; // Class w/image-reading functions

After the SD and TFT’s begin() functions have been called (see the example sketches, in the setup() function), you can then call reader.drawBMP() to load a BMP image from the card to the screen:

Download: file
ImageReturnCode stat;
stat = reader.drawBMP("/purple.bmp", tft, 0, 0);

This accepts four arguments:

  • A filename in “8.3” format (you shouldn’t need to provide an absolute path (the leading “/”), but there are some issues with the SD library on some cutting-edge boards like the ESP32, so go ahead and include this for good measure).
  • The display object where the image will be drawn (e.g. “tft”). This is the weird syntax previously mentioned…rather than tft.drawBMP(), it’s reader.drawBMP(tft), because reasons.
  • An X and Y coordinate where the top-left corner of the image is positioned (this doesn’t need to be within screen bounds…the library will clip the image as it’s loaded). 0, 0 will draw the image at the top-left corner…so if the image dimensions match the screen dimensions, it will fill the entire screen.

This function returns a value of type ImageReturnCode, which you can either ignore or use it to provide some diagnostic functionality. Possible values are:

  • IMAGE_SUCCESS — Image loaded successfully (or was clipped fully off screen, still considered “successful” in that there was no error).
  • IMAGE_ERR_FILE_NOT_FOUND — Could not open the requested file (check spelling, confirm file actually exists on the card, make sure it conforms to “8.3” file naming convention (e.g. “filename.bmp”).
  • IMAGE_ERR_FORMAT — Not a supported image format. Currently only uncompressed 24-bit color BMPs are supported (more will likely be added over time).
  • IMAGE_ERR_MALLOC — Could not allocate memory for operation (drawBMP() won’t generate this error, but other ImageReader functions might).

Rather than dealing with these values yourself, you can optionally call a function to display a basic diagnostic message to the Serial console:

Download: file
reader.printStatus(stat);

If you need to know the size of a BMP image without actually loading it, there’s the bmpDimensions() function:

Download: file
int32_t width, height;
stat = reader.bmpDimensions("/parrot.bmp", &width, &height);

This accepts three arguments:

  • A filename, same rules as the drawBMP() function.
  • Pointers to two 32-bit integers. On successful completion, their contents will be set to the image width and height in pixels. On any error these values should be ignored (they’re left uninitialized).

This function returns an ImageReturnCode as explained with the drawBMP() function above.

Loading and Using Images in RAM

Depending on image size and other factors, loading an image from SD card to screen may take several seconds. Small images…those that can fit entirely in RAM…can be loaded once and used repeatedly. This can be handy for frequently-used icons or sprites, as it’s usually much easier than converting and embedding an image as an array directly in one’s code…a horrible process.

This introduces another ImageReader function plus a new object type, Adafruit_Image:

Download: file
Adafruit_Image img;
stat = reader.loadBMP("/wales.bmp", img);

loadBMP() accepts two arguments:

  • A filename, same rules as the previous functions.
  • An Adafruit_Image object. This is a slightly more flexible type than the bitmaps used by a few drawing functions in the GFX library.

This returns an ImageReturnCode as previously described. If an image is too large to fit in available RAM, a value of IMAGE_ERR_MALLOC will be returned. Color images require two bytes per pixel…for example, a 100x25 pixel image would need 100*25*2 = 5,000 bytes RAM.

On success, the img object will contain the image in RAM.

The loadBMP() function is useful only on microcontrollers with considerable RAM, like the Adafruit “M0” and “M4” boards, or ESP32. Small devices like the Arduino Uno just can’t cut it. It might be marginally useful on the Arduino Mega with very small images.

After loading, use the img.draw() function to display an image on the screen:

Download: file
img.draw(tft, x, y);

This accepts three arguments:

  • A display object (e.g. “tft” in most of the examples), similar to how drawBMP() worked.
  • An X and Y coordinate for the upper-left corner of the image on the screen, again similar to drawBMP().

We use img.draw(tft,…) rather than tft.drawRGBBitmap(…) (or other bitmap-drawing functions in the Adafruit_GFX library) because in the future we plan to add more flexibility with regard to image file formats and types. The Adafruit_Image object “understands” a bit about the image that’s been loaded and will call the appropriate bitmap-rendering function automatically, you won’t have to handle each separate case on your own.

If the image failed to load for any reason, img.draw() can still be called, it just won’t do anything. But at least the sketch won’t crash.

This guide was first published on Jul 29, 2012. It was last updated on Jul 29, 2012. This page (Loading Images) was last updated on May 24, 2019.