Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating Simple PNG Decoder/Encoder

0.00/5 (No votes)
26 Aug 2016 3  
Creating Simple PNG Decoder/Encoder

Introduction

If your program plays with images, you should add supporting PNG format images. PNG format is a very useful and flexible image format that is used over the web and in applications. For decoding PNG images, you can use GDI+ as an easy way but the GDI+ always creates 32-bit bitmap from PNGs regardless of actual image format. If you want to work professionally on PNG images, you should create your own decoder/encoder or you can use libpng; But I created a simple PNG decoder & encoder in WIN32 platform using Visual Studio that you can review. It will help you to understand that PNG is much easier than implementing libpng.

This decoder/encoder only uses critical PNG chunks IHDR, PLTE, IDAT and IEND. The ancillary chunks are not used. Those are enough to decode or encode PNG file.

Decoder (Reading PNG file)

Decoder creates a DIB from PNG file. The following table shows input PNG formats and corresponding output DIB format:

PNG FILE (in)

DIB (out)

Color Type Bit-depth Interlace Bit-depth
grayscale(0) 1, 2, 4, 8, 16 8, 16 1, 4, 4, 8, 8
rgb triple(2) 8, 16 8, 16 24, 24
indexed(3) 1, 2, 4, 8 8 1, 4, 4, 8
grayscale+alpha(4) 8, 16 8, 16 32, 32
rgb+alpha(6) 8, 16 8, 16 32, 32

Now! Streamed (multi-IDATs) are supported in the current version.

Encoder (Writing PNG file)

Encoder creates the PNG file from DIB data. The following table shows input DIB formats and corresponding output PNG format:

DIB (in) PNG (out)
Bit-depth Grayscale (flag) Interlace (flag) Color Type Bit-depth (per sample)
1 yes no Indexed / Grayscale 1
4 yes no Indexed / Grayscale 4
8 yes yes Indexed / Grayscale 8
16 yes yes RGB / Grayscale 8
24 yes yes RGB / Grayscale 8
32 yes yes RGB+Alpha / Grayscale+Alpha 8

Notes

Input DIB can be bottom-top or top-bottom line order.
1-bit & 4-bit DIBs can not be saved in interlaced mode.

All used input and output DIB is in CF_DIB format; which means the LPBITMAPINFO followed by DIBits.

To using this decoder/encoder, you need zlib (included in the demo project).

Using the Code

To use this decoder/encoder, you must only copy the following files to your project, then use #include "png.h":

  • png.h
  • crc.h
  • crc.cpp
  • pngcmn.h
  • pngcmn.cpp
  • pngdecoder.h
  • pngdecoder.cpp
  • pngencoder.h
  • pngencoder.cpp

Decoder Section

There is one main function to use: ReadPngFile(). This function is overloaded to read PNG from file or memory.

Syntax

// Load PNG from file
LONG
ReadPngFile(
    _In_ LPCTSTR lpszFileName,
    _Outptr_result_maybenull_ LPVOID* ppvDIBData,
    _Out_ LPDWORD pdwDIBSize,
    _Out_opt_ PNG_IMAGE_HEADER* pOutImageHeader = NULL);

// Load PNG from memory
LONG
ReadPngFile(
    _In_ LPBYTE lpMem,
    _In_ DWORD dwMemSize,
    _Outptr_result_maybenull_ LPVOID* ppvDIBData,
    _Out_ LPDWORD pdwDIBSize,
    _Out_opt_ PNG_IMAGE_HEADER* pOutImageHeader = NULL);

Parameters

  • lpszFileName 
    [in] The full qualified path of the PNG file.
  • lpMem 
    [in] Pointer to memory contains the PNG file data.
  • dwMemSize 
    [in] Size of lpMem in bytes.
  • ppvDIBData 
    [out] Pointer to result DIB data. You must use free() function to release this memory after it is no longer needed.
  • pdwDIBSize 
    [out] Pointer to a DWORD that receives the entire DIB size.
  • pOutImageHeader 
    [out, optional] You can receive the IHDR data as a PNG_IMAGE_HEADER structure for details about the PNG file. This parameter can be NULL.

Return Values

Returns PNG_SUCCESS if operation is successful or other defined png status codes.

If return value is PNG_WIN32_ERROR, you can call GetLastError() for more details.

Remarks

You must release the out DIB memory after use or when it is no longer needed by free() function.

Sample Code

DWORD dwDIBSize = 0;
LPVOID pvDIBData = NULL;
PNG_IMAGE_HEADER imageHeader;
if (ReadPngFile(_T("c:\\sample.png"), &pvDIBData, &dwDIBSize, &imageHeader) == PNG_SUCCESS)
{
        TRACE2("Dim: %u, %u\n", imageHeader.ihWidth, imageHeader.ihHeight);
        // TODO: you can paint the DIB by SetDIBitsToDevice() API function
        free(pvDIBData);
}

Encoder Section

There is one main function to use: WritePngFile(). This function is overloaded to write PNG to file or memory.

Syntax

// Write PNG file to disk
LONG
WritePngFile(
    _In_ LPCTSTR lpszFileName,
    _In_ LPVOID pvDIBData,
    _In_ BOOL bGrayscale    = FALSE,
    _In_ BOOL bInterlace    = FALSE,
    _In_ int nFilter        = WPF_FilterBest,
    _In_ int nCompression    = WPF_DefaultCompression
    );

// Write PNG file to memory
LONG
WritePngFile(
    _Out_ LPVOID* ppvPNGData,
    _Out_ LPDWORD pdwPNGSize,
    _In_ LPVOID pvDIBData,
    _In_ BOOL bGrayscale    = FALSE,
    _In_ BOOL bInterlace    = FALSE,
    _In_ int nFilter        = WPF_FilterBest,
    _In_ int nCompression    = WPF_DefaultCompression
);

Parameters

  • lpszFileName 
    [in] The full qualified path of the PNG file to be written.
  • ppvPNGData 
    [out] Pointer to the result PNG file. You must usefree() function to release this memory after no longer needed it.
  • pdwPNGSize 
    [out] Pointer to a DWORD that receive the entire PNG file size.
  • pvDIBData 
    [in] Pointer to buffer that contains input DIB data in CF_DIB format.
  • bGrayscale 
    [in] If this flag is set TRUE, the result PNG will be in grayscale color type. Also the function converts color pixels to grayscale.
  • bInterlace 
    [in] If this flag is set TRUE, the result PNG will be interlaced.
  • nFilter 
    [in] Specifies the filtering method. It can be one of the PngFilterTypes enum element. See Remarks.
  • nCompression 
    [in] Specifies the compression level. It can be one of the PngCompressionLevels enum element. See Remarks.

Return values

Returns PNG_SUCCESS if operation is successful or other defined png status codes.

If the return value is PNG_WIN32_ERROR you can call GetLastError() for more information.

Remarks

You must release the out PNG memory (ppvPNGData) after use or no longer needed it by free() function.

If the bGrayscale is TRUE, the result PNG file will be in colortype grayscale(0) for DIBs less than 32-bit and color type grayscale+alpha(4) for 32-bit DIBs.
Used formula for converting color pixels to grayscale is simply:

gray = (r + g + b) / 3;

The nFilter parameter can be on of the following enum elements:

enum PngFilterTypes {
    WPF_FilterNone      = 0,
    WPF_FilterSub       = 1,
    WPF_FilterUp        = 2,
    WPF_FilterAverage   = 3,
    WPF_FilterPaeth     = 4,
    WPF_FilterBest      = 5,
};

If the specified filter type is WPF_FilterBest, the function selects best filter type for each scanline.

The nCompression parameter can be one of the following enum elements:

enum PngCompressionLevels {
    WPF_DefaultCompression   = 0,
    WPF_NoCompression        = 1,
    WPF_BestSpeed            = 2,
    WPF_BestCompression      = 3,
};

Points of Interest

This decoder/encoder  is very easy to learn and use PNG files. Please review the attached source code.

History

Version 2

  • Encoder added
  • Support multi-IDAT chunks
  • Bug fixed on interlaced images
  • Minor changes on internal functions

Version 1

  • First code (only png decoder)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here