Skip to content
Larry Bank edited this page Jun 1, 2021 · 9 revisions

Public API

The PNG class exposes a few simple methods detailed below. The more challenging part of using it is when you're using a new type of media/file and/or display and need to write your own callback functions. Those are documented below the API section.

This is the entire class definition for PNG:

class PNG {
  public:
    int openRAM(uint8_t *pData, int iDataSize, PNG_DRAW_CALLBACK *pfnDraw);
    int openFLASH(uint8_t *pData, int iDataSize, PNG_DRAW_CALLBACK *pfnDraw);
    int open(char *szFilename, PNG_OPEN_CALLBACK *pfnOpen, PNG_CLOSE_CALLBACK *pfnClose, PNG_READ_CALLBACK *pfnRead, PNG_SEEK_CALLBACK *pfnSeek, PNG_DRAW_CALLBACK *pfnDraw);
    void close();
    int decode(void *pUser, int iOptions);
    int getWidth();
    int getHeight();
    int getBpp();
    int hasAlpha();
    uint32_t getTransparentColor();
    int isInterlaced();
    uint8_t * getPalette();
    int getPixelType();
    int getLastError();
    int getBufferSize();
    uint8_t *getBuffer();
    void setBuffer(uint8_t *pBuffer);
    uint8_t getAlphaMask(PNGDRAW *pDraw, uint8_t *pMask, uint8_t ucThreshold);
    void getLineAsRGB565(PNGDRAW *pDraw, uint16_t *pPixels, int iEndianness, uint32_t u32Bkgd);

  private:
    PNGIMAGE _png;
};

The class is basically a C++ wrapper of C code which does all of the actual work. The PNGIMAGE structure is kept private, but is passed as a pointer to the 'worker' functions. This makes it easy to lift out the C code if you want to use it in a pure C project. Here are details of each method:

int open()
There are three versions of the open method - two for PNG images in memory (RAM & FLASH) and another for images coming from a file. All return 0 (PNG_SUCCESS) for success, non-0 for failure (see error codes). The success or failure depends not just on the file successfully opening, but also on the PNG image file header being parsed to gather the size and options. Once this function returns successfully, you can use getWidth(), getHeight(), etc. The PNG_DRAW_CALLBACK is optional; if you provide your own full sized image buffer with setBuffer() then the decoded image will be written to that buffer, if not, you need to provide a callback function to receive the pixels line by line.

int close()
You can call this any time to close the file handle associated with your PNG image. For PNG files coming from memory, this has no effect.

int decode(void *pPrivate, int iOptions)
This method decodes the image. Once the file is successfully opened, this can be called multiple times in a row without needing to re-open the file. The pPrivate parameter allows you to pass a pointer to a private structure to your PNGDRAW callback function. This can be useful to pass info like image centering offsets or background colors. The pixels decoded into the bitmap buffer and passed to the PNGDRAW callback are in the native format encoded by the file. In other words, if the file was written as truecolor+alpha then each pixel will be a 32-bit value consisting of RGBA bytes. A method is provided to convert the native format into RGB565 output for simplifying the output to LCD displays (see getLineAsRGB565 below).
The iOptions parameter currently supports two options:
PNG_CHECK_CRC
-------------
Enables the internal Adler32 CRC checking used during the zlib inflate() process. If you know your data is good, disabling that check can speed up the decoder by 10-20%.

PNG_FAST_PALETTE
----------------
Enables a faster RGB565 palette lookup. The fast palette lookup is only for indexed (palette) color images and will reserve 512 bytes of the internal pixel buffer to create a fast RGB565 lookup table used in the getLineAsRGB565() function. That means there are 512 fewer bytes for decoding the current and reference lines (5120 bytes are reserved for this by default).

int getWidth()
Returns the width in pixels. This can be called any time after a successful open().

int getHeight()
Returns the height in pixels. This can be called any time after a successful open().

int getBpp()
Returns the number of bits per color stimulus (not necessarily per pixel). For example, for a 'truecolor' image with 24-bits per RGB pixel, this will return 8.

int hasAlpha()
Returns true (1) if the image has an alpha channel or alpha information in the palette or a transparent color is defined.

uint32_t getTransparentColor()
If a transparent color has been defined, this will return either the palette index or the RGB value (for non-palette images).

int isInterlaced()
Returns true (1) if the image is encoded with Adam7 interlacing. This option is not currently supported for decoding because of the extra memory required, but this function can be useful to know why the file fails with a PNG_UNSUPPORTED_FEATURE error.

uint8_t * getPalette()
Returns a pointer to the palette entries (if defined) for the current image. If alpha info is present, it will be single byte values (one per palette entry) starting at offset 768 from this pointer.

int getPixelType()
Returns the PNG pixel type (see the PNG file specification or the enumeration in PNGdec.h).

int getLastError()
Returns the last error or 0 (PNG_SUCCESS) if there was none. See PNGdec.h for a list of error codes

int getBufferSize()
Returns the size in bytes of the required buffer to hold the uncompressed image. It's up to the caller to allocate and free this memory. After getting the required size and allocating the memory, pass the pointer to the PNG class with the setBuffer() method.

uint8_t *getBuffer()
Returns the pointer previously set with setBuffer() or NULL if there is none.

void setBuffer(uint8_t *pImage)
Sets the buffer which will be used to receive the decoded image. Get the necessary size in bytes by first calling getBufferSize().

uint8_t getAlphaMask(PNGDRAW *pDraw, uint8_t *pMask, uint8_t ucThreshold)
For images with an alpha channel, this function allows you to create a 1-bit per pixel mask (1=opaque, 0=transparent). Opaque is for pixels with an alpha value >= ucThreshold. This function is meant to be called inside the PNGDraw callback to create a mask compatible with the Adafruit_GFX drawBitmap function. With the mask, it can skip drawing pixels on SPI LCD displays where the mask is 0. The return value indicates if any pixels on the line were opaque. A 0 indicates all transparent pixels and non-0 indicates 1 or more pixels are opaque.

void getLineAsRGB565(PNGDRAW *pDraw, uint16_t *pPixels, int iEndianness, uint32_t u32Bkgd)
This function is meant to be called from within the PNG_DRAW_CALLBACK function. It simplifies the task of converting the native pixel format into RGB565 pixels. You must supply it with a buffer large enough for sizeof(uint16_t) * image_width bytes. You may request little or big endian pixels. To provide some flexibility with alpha images, you may optionally provide a RBGA888 color to be mixed as a background color. This color should be set to -1 (0xffffffff) to disable color mixing.

The Callback Functions

There are 7 callback functions defined by PNGdec. If you're displaying a PNG from memory and providing a buffer which can hold the entire image, then you don't need to provide any callback functions. If you are decoding from memory and want the image sent a line at a time, then you only need to provide a single function - PNG_DRAW_CALLBACK because the memory 'file' functions are provided for you inside the PNGdec library code. The example sketches contain code which implements this sufficiently to display most PNGs properly on any LCD. Let's see what's involved in implementing it. Here's the function prototype:

typedef void (PNG_DRAW_CALLBACK)(PNGDRAW *pDraw);

The PNGDRAW structure:

typedef struct png_draw_tag
{
  int y; // current line being drawn (0 = top line of image)
  int iWidth; // width of the current line
  uint8_t *pPixels; // source pixels for this line (native format)
  uint8_t *pPalette; // palette entries (if defined)
} PNGDRAW;

Hopefully you find the comments for each member variable pretty clear. The PNGDRAW callback is tasked with drawing the current scanline on the display. It's provided with the native pixels and possibly a palette (for indexed color images).

The other 4 callback functions need to be implemented if you're working with files. They implement the standard functions of open, close, read, and seek:

typedef void * (PNG_OPEN_CALLBACK)(char *szFilename, int32_t *pFileSize);
typedef void (PNG_CLOSE_CALLBACK)(void *pHandle);
typedef int32_t (PNG_READ_CALLBACK)(PNGFILE *pFile, uint8_t *pBuf, int32_t iLen);
typedef int32_t (PNG_SEEK_CALLBACK)(PNGFILE *pFile, int32_t iPosition);

The challenge with the file system callbacks is that file access on Arduino is usually not associated with a simple file handle, but with a C++ class. In order to manage this in a generic way that will work for all possible systems, the PNGdec class holds onto a void * pointer which you would like use to hold a class pointer. Let's look at the PNG_CLOSE_CALLBACK function I wrote for the Arduino SD library to understand how this is done:

void PNGCloseFile(void *pHandle)
{
  File *f = static_cast<File *>(pHandle);
  if (f != NULL)
    f->close();
}

The trick to making it all work is just to use static_cast to convert the void * into whatever class you need to access files.

See any of the sdcard example sketches for how to implement the other callback functions.

Clone this wiki locally