PNGDIB
A DIB-PNG conversion library for Win32
By Jason Summers <jason1@pobox.com>
Version 3.1.0, Jul. 2010
Web site: <http://entropymine.com/jason/pngdib/>
This software is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
Permission is hereby granted to use, copy, modify, and distribute this
source code for any purpose, without fee.
Introduction
PNGDIB is a library that makes it easy to convert between PNG images
and Windows Device Independent Bitmaps (DIBs). PNGDIB is designed to be
quick and easy to use, but it does not attempt to support every feature
that one might want. If you wish use the PNG format to its full
capabilities, you may need to use libpng directly, or write our own
extensions to PNGDIB.
When using PNGDIB to read PNG files,
it will create version 3.0 DIBs/BMPs. The DIB will be
uncompressed, with either 1, 4, 8, or 24 bits-per-pixel.
(Note: A BMP image file is basically just a DIB copied directly from
memory, with a 14-byte file header tacked onto the front. The terms DIB
and BMP may sometimes be used almost interchangeably.)
When writing PNG files, PNGDIB supports
approximately version 3.0 DIBs, and also
OS/2-style DIBs. It supports bit depths of 1, 4, 8, 16, 24, and 32
bits-per-pixel. It supports 4 bpp and 8 bpp compressed DIBs (it uses
Windows GDI functions to uncompress them).
The library itself currently consists of just two files: pngdib.c and
pngdib.h.
Two sample programs, png2bmp.c and bmp2png.c, are included which demonstrate
the use of library functions. They can be compiled as "console mode" Win32
applications.
A third sample, "smview",
is a bare-bones Windows program that uses pngdib
to read and display PNG files. This is intended to demonstrate that it is
easy to add PNG support to an application that uses DIBs.
Using the library
Overview
First call pngdib_d2p_init() or pngdib_p2d_init() to
create a PNGDIB
object. A few functions can accept either type of object, but most only work
with one of the two types. Call pngdib_done() when finished.
The code samples below show an overview of the process.
Sample code for writing a PNG:
void save_dib_to_png_file(char *png_filename,
LPBITMAPINFOHEADER dib, int dib_size)
{
PNGDIB *pngdib;
pngdib = pngdib_d2p_init();
pngdib_d2p_set_dib(pngdib,dib,dib_size,NULL,0);
pngdib_d2p_set_png_filename(pngdib,png_filename);
pngdib_d2p_run(pngdib);
pngdib_done(pngdib);
}
Sample code for reading a PNG:
void read_png_from_file(char *png_filename)
{
PNGDIB *pngdib;
LPBITMAPINFOHEADER dib;
pngdib = pngdib_p2d_init();
pngdib_p2d_set_png_filename(pngdib,png_filename);
pngdib_p2d_run(pngdib);
pngdib_p2d_get_dib(pngdib,&dib,NULL);
pngdib_done(pngdib);
/* ... do something with dib ... */
pngdib_p2d_free_dib(NULL,dib);
}
Unless otherwise specified, functions that return a value
return nonzero on success, and zero on failure.
Note: If you are not using Unicode, "TCHAR" means exactly
the same thing as "char".
Functions that don't require an object, or that accept either type
- int pngdib_done(PNGDIB *xx);
- Cleans up and frees any memory and other resources
allocated by the library.
The only exception is the DIB that may have been created, which
must be freed by the caller.
- TCHAR* pngdib_get_version_string(void);
- Returns a pointer to a static string indicating the version,
such as "6.7.8".
- int pngdib_get_version(void);
- Returns an integer representing the version. For example,
60708 would be version 6.7.8.
- void pngdib_setcallback_malloc(PNGDIB *xx,
pngdib_malloc_cb_type mallocfunc,
pngdib_free_cb_type freefunc,
pngdib_realloc_cb_type reallocfunc);
typedef void* (PNGDIB_DECL *pngdib_malloc_cb_type)(void *userdata, int memblksize);
typedef void (PNGDIB_DECL *pngdib_free_cb_type)(void *userdata, void *memblk);
typedef void* (PNGDIB_DECL *pngdib_realloc_cb_type)(void *userdata, void *memblk, int memblksize);
- Use your own memory allocation functions.
This is currently only used when converting PNG-to-DIB.
The pointer you set with pngdib_set_userdata will be
passed to your functions when they are called by PNGDIB.
reallocfunc is reserved for future use, and can be NULL.
Your malloc function should zero out the memory that it
allocates.
- void pngdib_setcallback_pngptrhook(PNGDIB *xx,
pngdib_pngptrhook_cb_type pngptrhookfn);
typedef void (PNGDIB_DECL *pngdib_pngptrhook_cb_type)(void *userdata, void *pngptr);
- Set a callback function to be called immediately after the main libpng object
has been created. Cast the "pngptr" param to a "png_structp" to use it.
Not recommended if you can avoid using it, but sometimes you need a way to
call png_set_user_limits() or similar functions.
- TCHAR* pngdib_get_error_msg(PNGDIB *xx);
- Returns a pointer to an error message reflecting the
most recent error that occurred in pngdib_d2p_run or pndib_p2d_run.
- void pngdib_set_userdata(PNGDIB *xx, void *userdata);
- The client application can associate a pointer with the
PNGDIB object, for its own use. This is useful in conjunction with
callback functions.
- void* pngdib_get_userdata(PNGDIB *xx);
- Returns the pointer set with pngdib_set_userdata.
- void pngdib_set_dibalpha32(PNGDIB *xx, int flag);
- This is an experimental function that you probably shouldn't
use. If flag is 1, it tells PNGDIB to use 32-bit
DIBs, with 8 or the 32 bits used for an alpha channel
(0=transparent, 255=opaque). This is not a standard
type of DIB.
DIB-to-PNG functions
- PNGDIB* pngdib_d2p_init(void)
- Returns a pointer to a new PNG-to-DIB object, which can be passed
to other functions. Returns NULL if memory allocation failed.
Note: This is actually implemented as a macro.
- int pngdib_d2p_set_dib(PNGDIB *d2p,
const BITMAPINFOHEADER* pdib, int dibsize,
const void* pbits, int bitssize);
- Tells PNGDIB where to find your DIB in memory.
pdib: direct pointer to the beginning of your DIB's header.
dibsize: the number of bytes starting with pdib that are safe to read.
Can be 0 if this is unknown.
pbits: direct pointer to the beginning of the "bits" section
of your DIB. This can be NULL if it directly follows the DIB's header
and palette information.
bitssize: the number of bytes starting with pbits that
are safe to read. Can be 0 if you don't know. This is ignored
if pbits is NULL.
- void pngdib_d2p_set_interlace(PNGDIB *d2p, int interlaced);
- interlaced: 0 (default) = write a noninterlaced PNG image;
1 = write an interlaced image.
- int pngdib_d2p_set_png_filename(PNGDIB *d2p, const TCHAR *fn);
- Pointer to null-terminated filename of the PNG file to write.
- void pngdib_d2p_set_png_write_fn(PNGDIB *d2p, pngdib_write_cb_type writefunc);
- Use this instead of pngdib_d2p_set_png_filename to use a custom
write function. The pngdib_write_cb_type type is defined as:
typedef int (PNGDIB_DECL *pngdib_write_cb_type)(void *userdata, const void *buf, int nbytes);
Your write function must consume all the bytes supplied to it, and
return the number of bytes written ('nbytes').
- int pngdib_d2p_set_software_id(PNGDIB *d2p, const TCHAR *s);
- Pointer to a brief NULL-terminated string
identifying your application. Set to NULL (or don't call this function)
if you don't want your app to be identified in the PNG file.
- void pngdib_d2p_set_gamma_label(PNGDIB *d2p, int flag, double file_gamma);
- flag: 0 = Do not write a gamma label to the PNG file;
1 (default): write a gamma label to the file;
file_gamma: gamma value to write, default = PNGDIB_DEFAULT_FILE_GAMMA =
1/2.20.
- int pngdib_d2p_run(PNGDIB *d2p);
- Write the PNG file.
PNG-to-DIB functions
- PNGDIB* pngdib_p2d_init(void)
- Returns a pointer to a new DIB-to-PNG object, which can be passed
to other functions. Returns NULL if memory allocation failed.
Note: This is actually implemented as a macro.
- int pngdib_p2d_set_png_filename(PNGDIB *p2d, const TCHAR *fn);
- Set the filename of the input PNG file.
- void pngdib_p2d_set_png_memblk(PNGDIB *p2d, const void *mem, int memsize);
- Use this instead of pngdib_p2d_set_png_filename to have the PNG
"file" read from a memory block instead of a disk file.
You can set memsize to 0 if the size isn't known, but this
could lead to buffer overruns if the image isn't a valid PNG file.
- void pngdib_p2d_set_png_read_fn(PNGDIB *p2d, pngdib_read_cb_type readfunc);
- Use this instead of pngdib_p2d_set_png_filename to use a custom
read function. The pngdib_read_cb_type type is defined as:
typedef int (PNGDIB_DECL *pngdib_read_cb_type)(void *userdata, void *buf, int nbytes);
Your read function must try to fill the buffer with the number of bytes requested,
and return the number of bytes written to it. (If not enough bytes are available,
this indicates that the PNG file is invalid.)
- void pngdib_p2d_set_use_file_bg(PNGDIB *p2d, int flag);
- Indicates whether the background color stored with the image
should be applied to transparent areas. Set flag to 1 to use
the image's background color if possible, 0 to ignore it.
- void pngdib_p2d_set_custom_bg(PNGDIB *p2d, unsigned char r,
unsigned char g, unsigned char b);
- Defines a background color to apply to tranparent parts of the
image. If both this and pngdib_p2d_set_use_file_bg are called,
the file background color will take precedence if it exists.
- void pngdib_p2d_set_gamma_correction(PNGDIB *p2d, int flag,
double screen_gamma);
- flag: 0 (default) = do not do gamma correction;
1 = gamma correct colors when reading the PNG image.
For the screen_gamma parameter, you can use the
PNGDIB_DEFAULT_SCREEN_GAMMA symbol, or set your own display gamma.
PNG files with no gamma label are assumed have a gamma of 1/2.20.
- int pngdib_p2d_run(PNGDIB *p2d);
- Read the PNG file and create the DIB. If this function is
successful, the caller is responsible for freeing the DIB.
See pngdib_p2d_free_dib for details.
- int pngdib_p2d_get_dib(PNGDIB *p2d, BITMAPINFOHEADER **ppdib, int *pdibsize);
- Retrieve a pointer to the DIB, and its size in bytes.
- int pngdib_p2d_get_dibbits(PNGDIB *p2d, void **ppbits, int *pbitsoffset, int *pbitssize);
- Retrieve information about the "bits" (pixels) section of the DIB.
- int pngdib_p2d_get_palette(PNGDIB *p2d, RGBQUAD **ppal, int *ppaloffset, int *ppalnumcolors);
- Returns information about the DIB palette.
ppal is a direct pointer to the palette,
ppaloffset is the number of bytes from the beginning of the DIB
to the beginning of the palette,
ppalnumcolors is the number of colors in the palette.
Any of the parameters may be NULL if the information is not needed.
- int pngdib_p2d_get_colortype(PNGDIB *p2d);
- Returns an integer representing the basic color type of the
original PNG image.
0 = grayscale
2 = RGB
3 = palette color
4 = grayscale+alpha
6 = RGB+alpha
- int pngdib_p2d_get_bitspersample(PNGDIB *p2d);
- Returns the number of bits-per-sample stored in the original PNG
image. For paletted images, returns the number of bits-per-pixel.
- int pngdib_p2d_get_bitsperpixel(PNGDIB *p2d);
- Returns the number of bits-per-pixel stored in the original PNG
image.
- int pngdib_p2d_get_samplesperpixel(PNGDIB *p2d);
- Returns the number of samples-per-pixel stored in the original PNG
image. For paletted images, returns 1.
- int pngdib_p2d_get_interlace(PNGDIB *p2d);
- Returns 0 if the PNG image was noniterlaced; nonzero if it was interlaced.
- int pngdib_p2d_get_density(PNGDIB *p2d, int *dens_x, int *dens_y, int *dens_units);
- Retrieves the x and y density (a.k.a. "resolution" or "physical pixel size")
label of the PNG image. If the label exists, returns nonzero and sets
the parameters. The units parameter will be 1 meaning the x and y values
are in pixels per meter, or 0 meaning pixels per unspecified unit.
- int pngdib_p2d_get_file_gamma(PNGDIB *p2d, double *pgamma);
- Retrieves the file gamma setting stored in the PNG file.
Returns nonzero if successful; 0 if the file did not contain
gamma information.
- int pngdib_p2d_get_bgcolor(PNGDIB *p2d, unsigned char *pr, unsigned char *pg, unsigned char *pb);
- Retrieves the background color used when processing the image,
or returns 0 if none was used. If gamma correction was performed by PNGDIB, the
background color will be gamma corrected if it came from the PNG file.
- void pngdib_p2d_free_dib(PNGDIB *p2d, BITMAPINFOHEADER *pdib);
- Free the DIB memory block.
If you have not yet called pngdib_done, set pdib to NULL.
If you have called pngdib_done, you can only call this function
if you did not set your own custom memory allocation
functions -- in that case, set p2d to NULL.
If you did set your own memory allocation functions, then
use your own "free" function instead of this one.
Obsolete functions
- int read_png_to_dib(PNGD_P2DINFO *p2dp)
- Provided only for backward compatibility.
- int write_dib_to_png(PNGD_D2PINFO *d2pp)
- Provided only for backward compatibility.
To Do
Some changes I might conceivably make in some future version...
- Use CreateFile() and related functions instead of fopen(), etc.
- The background-color (transparency) and gamma-correction code is too
complicated. This is partly libpng's fault, but with some effort, one
could probably make the PNGDIB code more readable.
History
CHANGES in PNGDIB v3.1.0
- New pngdib_d2p_set_png_write_fn() and pngdib_p2d_set_png_read_fn() functions
for custom I/O.
CHANGES in PNGDIB v3.0.2
- New pngdib_setcallback_pngptrhook() function.
- Made the sample utilites (png2bmp, bmp2png, smview) support Unicode.
- Other minor cleanup of the sample utilities.
CHANGES in PNGDIB v3.0.1 (vs. 3.0.0)
- Re-fixed a memory-freeing error that I fixed in
version 2.2.2 and somehow reintroduced in version 3.0.0.
CHANGES in PNGDIB v3.0.0 (vs. 2.x)
- Completely changed the programming interface. The old
Win32-API-style method of filling in the fields of a structure and
passing it to a single function that does everything was becoming too
difficult to maintain. It now uses an object-oriented approach similar
to most libraries, with many separate functions that can be
called to access the various features. A compatibility layer is
provided so that older programs can continue to work, but if you want
to use any new features, you'll need to use the new API.
- The default calling convention for the pngdib_get_version
functions has been changed to WINAPI.
This will likely break binary compatibility
if you used those functions.
- Support for reading PNG files from memory images.
- Ability to set monitor gamma.
- Can define your own functions to use for memory allocation.
- An attempt at Unicode support.
Load a PNG image by selecting File|Open from the menu, or by drag-and-drop
from Windows Explorer.
Gamma Correction - Toggles gamma correction. If you turn this off, some
images, or parts of images, will look too light or too dark.
Save As PNG - Saves the visible image to a file.
This saves the image as it is currently being displayed. The
saved image will not have any transparency, and the background color and
gamma correction will be applied to the image, not saved as meta-data.
This means that you can lose information by loading and then saving a PNG
image.
Background colors - You can define a background color that will be used in
certain situations. The following logic is used to select a background
color: If "Use Image's Background Color" is checked, and the current PNG
image file contains a suggested background color, that suggested
background color will be used. Otherwise, if "Use Custom Background Color"
is checked, your custom background color will be used (by default this is
a very light gray color). Otherwise, no background color will be used at
all, and transparency information in the image will be completely ignored.
Some images will not look very good in this situation.
(A default background color – usually white –
will be used for the window, but will not be applied to the
image.)
Any time you make a change to the gamma or background color settings, the
PNG file will be reloaded from disk. That's not the ideal thing to do, but
this is just a demo program...