19

Can I convert a bitmap to PNG in memory (i.e. without writing to a file) using only the Platform SDK? (i.e. no libpng, etc.).

I also want to be able to define a transparent color (not alpha channel) for this image.

The GdiPlus solution seems to be limited to images of width divisible by 4. Anything else fails during the call to Save(). Does anyone know the reason for this limitation and how/whether I can work around it?

Update: Bounty

I'm starting a bounty (I really want this to work). I implemented the GDI+ solution, but as I said, it's limited to images with quad width. The bounty will go to anyone who can solve this width issue (without changing the image dimensions), or can offer an alternative non-GDI+ solution that works.

Assaf Lavie
  • 73,079
  • 34
  • 148
  • 203

8 Answers8

21

LodePNG (GitHub) is a lib-less PNG encoder/decoder.

Andrew Keeton
  • 22,195
  • 6
  • 45
  • 72
Johan Kotlinski
  • 25,185
  • 9
  • 78
  • 101
14

I read and write PNGs using libpng and it seems to deal with everthing I throw at it (I've used it in unit-tests with things like 257x255 images and they cause no trouble). I believe the API is flexible enough to not be tied to file I/O (or at least you can override its default behaviour e.g see png_set_write_fn in section on customization)

In practice I always use it via the much cleaner boost::gil PNG IO extension, but unfortunately that takes char* filenames and if you dig into it the png_writer and file_mgr classes in its implementation it seem pretty tied to FILE* (although if you were on Linux a version using fmemopen and in-memory buffers could probably be cooked up quite easily).

timday
  • 24,582
  • 12
  • 83
  • 135
  • +1 thanks. May I know how to use Boost::GIL with PNG IO? I have no clue about integration and making use of that. – Viet Feb 26 '10 at 14:12
  • 1
    Boost GIL includes an "IO extension" which currently includes support for basic reading and writing to PNG files but doesn't provide enough hooks to read/write in-memory streams as the original poster was asking about. There seems to be a new IO extension (google something like "boost GIL new IO extension" to find various postings) under development which looks like it will give a lot more flexibility in this area. – timday Mar 02 '10 at 20:29
  • 1
    libpng, while a good solution, seems to violate the question's constraint of "using only the Platform SDK". – Adrian McCarthy Oct 31 '11 at 21:35
  • how do I to install this lib please? Or do I need to simply get the Source code? – Lucie kulza Mar 28 '14 at 00:21
  • 1
    @Lucie: I also struggled with finding a windows version; see my other question http://stackoverflow.com/questions/8669163/whats-the-quickest-and-easiest-way-of-getting-libpng-available-for-development . Windows development drives me bananas with this sort of stuff, compared with Linux where you can just apt-get install libpng-dev and be good to go in seconds. – timday Mar 28 '14 at 12:55
12

On this site the code shows how convert a bitmap to PNG writing it to a file: http://dotnet-snippets.de/dns/gdi-speichern-eines-png-SID814.aspx. Instead of writing to a file, the Save method of Bitmap also supports writing to a IStream (http://msdn.microsoft.com/en-us/library/ms535406%28VS.85%29.aspx). You can create a Stream backed up by memory using the CreateStreamOnHGlobal API function. (http://msdn.microsoft.com/en-us/library/aa378980%28VS.85%29.aspx). The used library, GDI+, is included in Windows up from WindowsXP, and works in Windows up from Windows98. I've never done something with it, just googled around. Looks like you can use that, though.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
8

The CImage class (ATL/MFC) supports saving into PNG format. Like the GDI+ solution, it also supports saving to a stream. Here's some code I use to save it to a CByteArray:

CByteArray baPicture;
IStream *pStream = NULL;
if (CreateStreamOnHGlobal(NULL, TRUE, &pStream) == S_OK)
{
    if (image.Save(pStream, Gdiplus::ImageFormatPNG) == S_OK)
    {
    ULARGE_INTEGER ulnSize;
        LARGE_INTEGER lnOffset;
        lnOffset.QuadPart = 0;
        if (pStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize) == S_OK)
        {
            if (pStream->Seek(lnOffset, STREAM_SEEK_SET, NULL) == S_OK)
            {                       
                baPicture.SetSize(ulnSize.QuadPart);
                ULONG ulBytesRead;
                pStream->Read(baPicture.GetData(), ulnSize.QuadPart, &ulBytesRead);
            }
        }
    }
}
pStream->Release();

I don't know if you'd want to use ATL or MFC, though.

djeidot
  • 4,542
  • 4
  • 42
  • 45
6

I've used GDI+ for saving a bitmap as a PNG to a file. You should probably check out the MSDN info about GDI+ here and in particular this function GdipSaveImageToStream.

This tutorial here will probably provide some help as well.

Twotymz
  • 412
  • 4
  • 6
5

GDI's (old school, non-plus) has a GetDIBits method that can be asked to output bits using PNG compression (BITMAPINFOHEADER::biCompression == BI_PNG). I wonder if this could be used to create a PNG file? Using GetDIBits to write standard bitmap files is complicated enough - so i suspect this would be even more difficult.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
Aardvark
  • 8,474
  • 7
  • 46
  • 64
  • 3
    For anyone who might be thinking of going this rout, see the [answer to "GetDIBits() is failing with PNG compression"](https://stackoverflow.com/a/3101090/2910230), or "[JPEG and PNG Extensions for Specific Bitmap Functions and Structures](https://msdn.microsoft.com/en-us/library/dd145023(VS.85).aspx)" on MSDN, that it links to. – Tamir Evan Sep 11 '17 at 16:53
3

If you want to only use Windows APIs, WIC is the way to accomplish this, and it supports both Bitmaps and PNGs.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
Ana Betts
  • 73,868
  • 16
  • 141
  • 209
2

It would probably be better to use a library instead of reinventing the wheel yourself.

Look into freeImage

DShook
  • 14,833
  • 9
  • 45
  • 55