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
LONG
ReadPngFile(
_In_ LPCTSTR lpszFileName,
_Outptr_result_maybenull_ LPVOID* ppvDIBData,
_Out_ LPDWORD pdwDIBSize,
_Out_opt_ PNG_IMAGE_HEADER* pOutImageHeader = NULL);
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);
free(pvDIBData);
}
Encoder Section
There is one main function to use: WritePngFile()
. This function is overloaded to write PNG to file or memory.
Syntax
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
);
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)