Embedding PNG Images

From WxWiki
Jump to: navigation, search

Possible strategies for handling PNG images

There are the following possibilities with the PNG files:

  1. Load them during run-time. This is simple and flexible (files can be replaced, could be looked up in different directories, ...) but least efficient and requires you to always distribute all the files with your application.
  2. Embed PNG images in the program source code itself. This is most complicated and least flexible but doesn't require to distribute any separate files and the same solution can be used under all platforms.
  3. Platform-specific strategy: use whatever is the standard way for the current platform meaning:

The last strategy is the best from wxWidgets philosophy of trying to do everything in the most native way possible. Since version 2.9.5 wxWidgets simplifies its implementation by providing wxBITMAP_PNG() macro that will load the bitmap from the corresponding location depending on the platform, i.e. from the resource section under Windows, a file in the resources directory under OS X or from embedded byte array elsewhere.

Embedding PNG images into executables

XPM graphics files are really simple to embed into C++ source files, because they are made of plain C code, and you don't need to convert them (just #include them). The bad thing about XPM inclusion is the inflating of executables size and the lacking of alpha transparency.

PNG files do support alpha transparency, but are more difficult to embed, because we need to convert them into a C vector.

bin2c

First of all, you will need a program to convert the PNG binary files into a C vector, like:

static unsigned char myimage_png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 
// ...
};

You can use a small program to do this converting for you:

png2wx

png2wx is an alternative which also embeds files as a C string, but does so in a more compact way (by only escaping what is necessary). Running optipng beforehand is recommended. It is available as a perl script; a python version is also available, but it is a lot slower than Perl. The utility creates a .cpp file containing the images (provided directly as wxBitmap *) and a .hpp for making the names available to other .cpp files. (The -M flag allows you to specify a custom reinclusion guard name.)

png2wx.pl -C images.cpp -H images.hpp -M IMAGES_HPP images/*

Although it is called png2wx, it just embeds any files you specify (images/* in this case) and the rest is up to the wxWidgets image handlers.

wxInclude

This tool will enable you to convert more images into one header.

Example:

 wxInclude.exe --const --input-file=mydata1.bin --input-type=.png --input-type=.bmp --output-file=myheader.h mydata2.bin myimage.png

Most usefull is --input-type=.png this will convert all png files in the directory.

You can get the source code and static linked windows executable here.

Including

Once you have converted your graphics files with bin2c, you need to include them into your source code, e.g.:

 #include "myimage_png.cpp"

Since wxWidgets 2.9.5 you can then use the simple wxBITMAP_PNG_FROM_DATA macro to create the bitmap from the embedded file:

 wxBitmap bmp = wxBITMAP_PNG_FROM_DATA(myimage);

Notice that the "_png" suffix is appended by the macro itself.

For the previous wxWidgets versions you need to do it manually using wxMemoryInputStream class:

 wxMemoryInputStream istream(myimage_png, sizeof myimage_png);
 wxImage myimage_img(istream, wxBITMAP_TYPE_PNG);

Of course, you can define your own macro similar to the one above:

 #define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name ## _png, sizeof(name ## _png))
 inline wxBitmap _wxGetBitmapFromMemory(const unsigned char *data, int length) {
   wxMemoryInputStream is(data, length);
   return wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1);
 }


That's all! :)

Notes

Remember to add the support for the PNG format in your wxApp::OnInit() function:

 wxImage::AddHandler(new wxPNGHandler);


This technique of embedding a PNG is also used by the Audacity sound editor (latest CVS only). It has the bin2c code built into it. It uses wxWidgets code to combine and split pngs, so that a theme consists of a single large png rather than many small ones.

Authors

Sandro Sigala <sandro AT sigala DOT it>

Nicola Leoni <nicola AT exilo DOT net>

Embedding PNG images into Windows rc file

Under Windows it can be desirable to embed the PNG into the application resources instead of application code. One advantage of doing this is that the bitmap can be changed without recompilation (but relinking is still necessary). Another one is that standard tools working with the resources can be used.

Include into RC file

A PNG file can be included as RCDATA type. To add a PNG image add the following line to your .rc file:

  <PNG_BITMAP_NAME>	RCDATA		"<PNG file name>"

Load the data from the resource

NOTE: Since wxWidgets 2.9.5 simply use wxBITMAP_PNG macro described above to load the bitmap. The following instructions are only useful for previous versions.

I use three methods to load the data:


  wxBitmap* CreateBitmapFromPngResource(const wxString& t_name)
  {
     wxBitmap*   r_bitmapPtr = 0;
  
     char*       a_data      = 0;
     DWORD       a_dataSize  = 0;
  
     if(LoadDataFromResource(a_data, a_dataSize, t_name))
     {
        r_bitmapPtr = GetBitmapFromMemory(a_data, a_dataSize);
     }
  
     return r_bitmapPtr;
  }


  bool LoadDataFromResource(char*& t_data, DWORD& t_dataSize, const wxString& t_name)
  {
     bool     r_result    = false;
     HGLOBAL  a_resHandle = 0;
     HRSRC    a_resource;
  
     a_resource = FindResource(0, t_name.wchar_str(), RT_RCDATA);
  
     if(0 != a_resource)
     {
        a_resHandle = LoadResource(NULL, a_resource);
        if (0 != a_resHandle)
        {
           t_data = (char*)LockResource(a_resHandle);
           t_dataSize = SizeofResource(NULL, a_resource);
           r_result = true;
        }
     }
  
     return r_result;
  }


  wxBitmap* GetBitmapFromMemory(const char* t_data, const DWORD t_size)
  {
     wxMemoryInputStream a_is(t_data, t_size);
     return new wxBitmap(wxImage(a_is, wxBITMAP_TYPE_PNG, -1), -1);
  }

The t_name parameter passed to CreateBitmapFromPngResource is the PNG_BITMAP_NAME defined in the RC file.

One thing to be aware of: When you add a PNG resource in Visual Studio 2010, it may define it as "PNG" type rather than "RCDATA", and this method will fail to load them. The easiest way to deal with that is to edit the resource file (.rc) and change the "PNG" entry for each file added to an "RCDATA" entry.

Putting PNG images in OS X resources

Under OS X image files should be put into the Resources subdirectory of the application bundle. This can be done either in the Xcode project or by simply manually copying them there in your makefile. Since wxWidgets 2.9.5 wxBITMAP_PNG macro can be used to load the images from this standard location. With the previous versions you'd need to use wxStandardPaths::GetResourcesDir() manually.