Piko3D

libPNG Tutorial: Loading PNG files from streams

Updates

14-01-2016Β 23:51
added call to png_read_update_info() (thanks Nicholas!)
29-10-2011 15:22
updated for LibPNG 1.5.5 and removed some dead links.
27-06-2010
Added information about “extra information at the end of a file” (thanks Brian Z!)
20-01-2010
Fixed a bug where the buffer would be to small when conversion filters were used (thanks Tom!)
20-01-2010 10:22
Clearified cleaning up on error, and explained reverse row order (thanks for comment!)
19-10-2009 21:03
Added a bit of explanation on byte (row) order, like used with OpenGL
25-07-2009 0:49 :
corrected the check in the validate function,and corrected some symbols (Thanks Ethan!)

Introduction:

Starting out with libPNG can be a bit difficult. Especially if your coding experience so far is rather limited. This is what motivated me to do this tutorial. Even though this tutorial covers some of the lower level sides of loading images with libPNG, it does so step by step. Hoping to explain a more advanced topic in a simple manner. This tutorial specifically deals with the case of loading PNG files from streams (any iostream or istream like fstream etc) which is interesting when using c++.

libPNG can be a bit tricky to use, even with the convenience functions, but it does offer some powerful, functions that allow you to get a lot of control over how you want your images to be loaded. Piko3D uses this functionality, and I hope I can clearify how to use this side of libPNG a bit for you. It also shows how to use libPNG for getting header and signature information etc.

This tutorial is meant for people who want to have a png loader tailored to their own requirements, data structures and/or memory management. It is written for LibPNG 1.2.9 and up. If you don’t care about this, and just want to get an image loaded, may I suggest other libraries, such as FreeImage or DevIL

Index

In this tutorial, we’ll discuss these things in order:

Setting up LibPNG for Visual studio

First of all, you need the libpng package in order to use libpng in your project. You can find the packages of the free reference library at http://www.libpng.org/pub/png/libpng.html . On this site, find the sourcecode if you wish to try and compile libpng yourelf, or simply scroll to the “current binaries” section and pick the one you need for your system (in our case, GNUWin32) You will need to download the lib-package. It contains a “includes”, a “libs” and a “Manifest” folder. Unpack the package somewhere where you can find it πŸ˜‰

Include settings for LibPNG

Include settings for LibPNG

Go into your visual studio and open your kick-ass project that you want to use libpng in. Select the project settings and open the property page. Make sure the configuration is set to “All Configurations” and not just Active (Release) or someting like that. Now go into the “Configuration properties” on the left side of the window, and go into the “Linker” part of the menu. Open the “General” section under linker, and add the dir where you extracted your libpng/libs to the “Additional Library Directories” field. Go into the “Input” section, right under “General” and add “libpng.lib” to the “Additional Dependencies” field. This tells visual studio to link your project against the libpng.lib where it will find all the functions you’re going to use. Don’t close this window just yet.

Next, select the “C/C++” section, and go the the “Additional Include Directories” field. Here, you will need to enter the path to your libpng/include directory. This way, visual studio will know where to look when you say “#include “png.h” πŸ˜‰ (See screenshot)

That’s it! Yer done!
There are more ways to configure your include and/or lib directories, but I’ll let you figure that out πŸ˜‰

The Code

In this next part, I will discuss the code in detail. Since the tutorial deals whith loading from any std::istream, I assume that you already have some experience with creating an using streams. If you don’t, you can find all the info you need about streams at www.cplusplus.com

After you have opened a file into a stream, or have created your stream from some other datasource, you will be able to use the functions I’m going to describe next, to load your png file!

Checking the PNG signature

Every valid png file should start with the PNG signature. The PNG signature is 8 bytes long. I don’t know what it looks like, but that’s not a problem. libPNG’s png_sig_cmp() can validate the signature for you. Please note that reading the PNG signature is optional! But reading it will give you a nice early out when somebody is trying to feed you a file that isn’t really a PNG.

The following code block shows a function that takes your istream as parameter, reads the sig, and returns true when the file signature is valid.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define PNGSIGSIZE 8

bool validate(std::istream& source) {

    //Allocate a buffer of 8 bytes, where we can put the file signature.
    png_byte pngsig[PNGSIGSIZE];
    int is_png = 0;

    //Read the 8 bytes from the stream into the sig buffer.
    source.read((char*)pngsig, PNGSIGSIZE);

    //Check if the read worked...
    if (!source.good()) return false;

    //Let LibPNG check the sig. If this function returns 0, everything is OK.
    is_png = png_sig_cmp(pngsig, 0, PNGSIGSIZE);
    return (is_png == 0);
}

You may note that here, we have already read 8 bytes from the stream. This means that if we continue reading the PNG file now, we will miss the first 8 bytes of the file. Fortunately, libPNG will let you specify if you have read the header or not, so you don’t have to reset the stream. We’ll get to that part later.

The other option ofcourse to reset the stream with a call to istream::seekg, but we will use libpng’s function in this case.

Initializing PNG read and info structures

libPNG uses a 2 structs to keep track of the file you are tring to load. A read struct, and an info struct. There’s a second info struct, for reading (and perhaps writing) “information at the end of the file”. Apparently, this can be used for storing extra information. It can be used by applications to store program specific information, or perhaps copy right messages, or digital camera info. it can be safely ignored by a simple image loader. (Thanks Brian Z!)

The read struct can be seen as a handle to your PNG file. The info struct will be used to store information about the actual image stored in the PNG file.

To create the PNG read struct, we use the function png_create_read_struct() function. for the PNG info struct, we use the png_create_info_struct() function. Both functions return a pointer to a struct that libPNG will use to keep track of our data. These functions return variables of type png_structp and png_infop respectively. The ‘p’ at the end is the indication that these functions return pointers. Notice that the objects pointed to are not automatically destroyed! In fact, this is often forgotten, with a memory leak as a result. So make sure you cleanup after yourself before these pointers go out of scope, using the png_destroy_read_struct() function. This function not only takes the readstruct as a parameter, but is also used to clean the info struct pointers at the same time.

Let’s look at some code:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//so First, we validate our stream with the validate function I just mentioned
if (!validate(source)) {
    std::cerr << "ERROR: Data is not valid PNG-data" << std::endl;
    return; //Do your own error recovery/handling here
}

//Here we create the png read struct. The 3 NULL's at the end can be used
//for your own custom error handling functions, but we'll just use the default.
//if the function fails, NULL is returned. Always check the return values!
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngPtr) {
    std::cerr << "ERROR: Couldn't initialize png read struct" << std::endl;
    return; //Do your own error recovery/handling here
}

//Here we create the png info struct.
//Note that this time, if this function fails, we have to clean up the read struct!
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
    std::cerr << "ERROR: Couldn't initialize png info struct" << std::endl;
    png_destroy_read_struct(&pngPtr, (png_infopp)0, (png_infopp)0);
    return; //Do your own error recovery/handling here
}
Error Handling

After this, we can start to read the PNG file, but first, let’s make sure we can properly respond to any parsing errors that might occur while libPNG reads our PNG file.

As I mentioned in the code comment, the function png_create_read_struct() has an option to set functions that can libPNG can call whenever an error occurs. You can use this functionality for throwing exceptions, but not mush else, since the handler is not alowed to return. In this tutorial however, I’ll be using libPNG’s jump function, which will cause the program to jump back to a set point for error handling. It’s a bit dirty if you ask me, and can be risky, but it keeps it simple for now, and is usually enough.

Here’s the code:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Here I've defined 2 pointers up front, so I can use them in error handling.
//I will explain these 2 later. Just making sure these get deleted on error.
png_bytep* rowPtrs = NULL;
char* data = NULL;

if (setjmp(png_jmpbuf(pngPtr))) {
    //An error occured, so clean up what we have allocated so far...
    png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0);
    if (rowPtrs != NULL) delete [] rowPtrs;
    if (data != NULL) delete [] data;

    std::cout << "ERROR: An error occured while reading the PNG file"

    //Make sure you return here. libPNG will jump to here if something
    //goes wrong, and if you continue with your normal code, you might
    //End up with an infinite loop.
    return; // Do your own error handling here.
}

When an error occurs during parsing, libPNG will jump to the if above!! so control will actually jump in here! so make sure you return here, so that you don’t get into an infinite loop, where it loads, an error occurs, it jumps to here, you continue… it loads… error… etc..

Custom Read function: Reading from stream

Now we get to the interesting part. libPNG usually reads directly from a FILE* file. However if you want to specify some other kind of source for your data, this is entirely possible. You can write your own read function, that can gather data from wherever you see fit. In this case, our std::istream. The function will have to look like this:


1
void userReadData(png_structp pngPtr, png_bytep data, png_size_t length);

The first parameter is a pointer to the PNG read struct we’re currently using to read the file. The second parameter is a pointer to the datablock where we have to put our data, and the third parameter is the amount of data we should put in the data block (and thus the size of the block). First, let’s tell libPNG to use our readfunction to get the data. We use the png_set_read_fn() function to pass our function pointer to libPNG. Note that you cannot pass a class member function here, unless you make that function static.

The first parameter is the png read struct for the file we want to read. The second is a void pointer to whatever we want to pass to our userReadData function. In this case, the adress of our std::istream source. Note the & before source. If you pass the reference, the code will work, but you will get a nasty heap corruption upon exit after reading the file.


1
png_set_read_fn(pngPtr,(png_voidp)&source, userReadData);

Now that we’ve set this, it’s easier to explain the implementation for our custom read function. Here it is:


1
2
3
4
5
6
7
8
void userReadData(png_structp pngPtr, png_bytep data, png_size_t length) {
    //Here we get our IO pointer back from the read struct.
    //This is the parameter we passed to the png_set_read_fn() function.
    //Our std::istream pointer.
    png_voidp a = png_get_io_ptr(pngPtr);
    //Cast the pointer to std::istream* and read 'length' bytes into 'data'
    ((std::istream*)a)->read((char*)data, length);
}

That’s it, we’re all set to start reading our PNG file.

Reading the PNG Header

Next, let’s see what kind of image we’re loading. LibPNG lets us read the header of the PNG file before completely loading the image. This can be helpfull for checking for perhaps size limits, or other characteristics you perhaps don’t want to support. Also, when al is fine and dandy, we can use this information to allocate a block of memory large enough to load the image in.

We can let libPNG parse the PNG file header by calling the png_read_info() function. Remember how we’ve read the signature before? Well before we read the actuall image header, we have to tell libPNG that we’ve allready read the signature, and that 8 bytes will be already read from the stream. Thisway, libPNG will skip the signature and go straight to the header info. For this, we can use the png_set_sig_bytes().

Here’s the code:


1
2
3
4
5
6
    //Set the amount signature bytes we've already read:
    //We've defined PNGSIGSIZE as 8;
    png_set_sig_bytes(pngPtr, PNGSIGSIZE);

    //Now call png_read_info with our pngPtr as image handle, and infoPtr to receive the file info.
    png_read_info(pngPtr, infoPtr);

After we’ve read the header info, you can get information about the file, using the various functions libPNG provides. In this case, we’ll get the image width and height in pixels, the bitdepth (note: nr of bits per CHANNEL), number of channels, and the “color type”. The color type defines the format of the image… be it gray scale, RGBA color, or perhaps an image with a palette.

Here are the functions for reading the image info:


1
2
3
4
5
6
7
8
9
    png_uint_32 imgWidth =  png_get_image_width(pngPtr, infoPtr);
    png_uint_32 imgHeight = png_get_image_height(pngPtr, infoPtr);

    //bits per CHANNEL! note: not per pixel!
    png_uint_32 bitdepth   = png_get_bit_depth(pngPtr, infoPtr);
    //Number of channels
    png_uint_32 channels   = png_get_channels(pngPtr, infoPtr);
    //Color type. (RGB, RGBA, Luminance, luminance alpha... palette... etc)
    png_uint_32 color_type = png_get_color_type(pngPtr, infoPtr);

After reading this information, you might come to the conclusion that the PNG image is not exactly in the format you need. LibPNG offers functions to set conversion options so that the image can be loaded in a different format, when we actually parse the image file. You can for instance, load a grayscale image as RGB. I suggest you read the libPNG manual and/or reference on which conversion functions can be used. But I’ll paste the code I use in Piko3D here so you’d have a place to start from. Functions used are png_set_palette_to_rgb(), png_set_expand_gray_1_2_4_to_8(), png_get_valid(), png_set_tRNS_to_alpha() and png_set_strip_16()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    switch (color_type) {
        case PNG_COLOR_TYPE_PALETTE:
            png_set_palette_to_rgb(pngPtr);
            //Don't forget to update the channel info (thanks Tom!)
            //It's used later to know how big a buffer we need for the image
            channels = 3;          
            break;
        case PNG_COLOR_TYPE_GRAY:
            if (bitdepth < 8)
            png_set_expand_gray_1_2_4_to_8(pngPtr);
            //And the bitdepth info
            bitdepth = 8;
            break;
    }

    /*if the image has a transperancy set.. convert it to a full Alpha channel..*/
    if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
        png_set_tRNS_to_alpha(pngPtr);
        channels+=1;
    }

    //We don't support 16 bit precision.. so if the image Has 16 bits per channel
        //precision... round it down to 8.
    if (bitdepth == 16)
        png_set_strip_16(pngPtr);

    //As Nicholas suggested, we should let png update the information structs with the transformations we requested:
    png_read_update_info(pngPtr, infoPtr);

These functions set states for your PNG file to be loaded. They don’t actually do anything until you load the image. So that’s what we’re going to do next: Load the image data.

Loading the Image data

In order to load the image, you’d need to allocate a memory block large enough to hold it. Typical sizes for an 24Bit RGB image for instance, would be imageWidth * imageHeight * (24 / 8). I’d recommend writing your own bitmap or pixel buffer class to keep track of image hight, width, bits per pixel, line stride/pitch, aspect ratio etc. and of course, to store the image data. In piko3D, I use my own PixelBuffer object. For the purpose of this tutorial, I’ll just use a memory block.

LibPNG reads the image line by line, and in order to work with the read function, libPNG requires an array of pointers, where each sequential pointer, points to the start of each new line or row of pixels in your buffer.

OpenGL etc.
You can arrange these pointers in the order required for your application. When working with OpenGL for example, it suspects the first byte of the buffer to be the bottom left pixel of the image. So the bottom row first. (as you can read in the OpenGL reference for glTexImage2D) This is also how a .BMP image is stored. (Apparently because very old displays that raster bottom up, and in OpenGL to keep UV coordinates consistent with world coordinates in most 3D editors, where either Z or Y is UP! Thanks Mario and mysterious commenter! :D)

This all might be a bit vague at first, but perhaps some code will help in pointing out how this works:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    //Here's one of the pointers we've defined in the error handler section:
    //Array of row pointers. One for every row.
    rowPtrs = new png_bytep[imgHeight];

    //Alocate a buffer with enough space.
    //(Don't use the stack, these blocks get big easilly)
    //This pointer was also defined in the error handling section, so we can clean it up on error.
    data = new char[imgWidth * imgHeight * bitdepth * channels / 8];
    //This is the length in bytes, of one row.
    const unsigned int stride = imgWidth * bitdepth * channels / 8;

    //A little for-loop here to set all the row pointers to the starting
    //Adresses for every row in the buffer

    for (size_t i = 0; i < imgHeight; i++) {
        //Set the pointer to the data pointer + i times the row stride.
        //Notice that the row order is reversed with q.
        //This is how at least OpenGL expects it,
        //and how many other image loaders present the data.
        png_uint_32 q = (imgHeight- i - 1) * stride;
        rowPtrs[i] = (png_bytep)data + q;
    }

    //And here it is! The actuall reading of the image!
    //Read the imagedata and write it to the adresses pointed to
    //by rowptrs (in other words: our image databuffer)
    png_read_image(pngPtr, rowPtrs);

There we go! That’s the image! …. But don’t forget to clean up!


1
2
3
4
    //Delete the row pointers array....
    delete[] (png_bytep)rowPtrs;
    //And don't forget to clean up the read and info structs !
    png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0);

And of course, Don’t forget to delete your image buffer when you’re done πŸ˜‰

Image upside down and mirrored?
The image must be loaded bottom up for use with OpenGL, as you can see in the code. If you are NOT using OpenGL or DirectX, then you might not need the image buffer to be bottom up. In this case, change the rowPtrs assignment loop to just assign the rowPointers top down. (If you ARE using OpenGL/DirectX, check if your texture coordinates are conform your API’s standards. DirectX and OpenGL are different here.)

Conclusion

That’s it for my libPNG tutorial! LibPNG can be a bit of a hassle, but it does give you a lot of control about how you want to handle your image, and your resources. Albeit not the most flattering library interface around ;-). I hope this tutorial was any help! It’s the first tutorial I’ve written, so if you have any questions, comments, suggestions, or just thought this tutorial was useful, leave a comment!, or write me an email (see contact page) I’d love to hear about it! Let me know if you find any problems, or have difficulty with any part of this tutorial and I’ll see if I can change it for the better.

I’ll probably be working in this page a bit more to correct mistakes and tweak colors/comments…

Best of Luck!

Cheers,
Wracky.

41 Comments »

Comment by Ethan — July 21, 2009 @ 5:26 pm

Great tutorial by the way! I’m still working on writing a png, to test if it actually worked, but so far it runs and doesn’t give any errors.

I had a slight issue with the following lines:

is_png = !png_sig_cmp(pngsig, 0, PNGSIGSIZE);

return (is_png == 0);

png_sig_cmp returns a 0 if there is no problem with the png header. You ! the return so is_png should be true on success. As it is now, the return returns false on success.

Another suggestion, you blog is butchering the & and -> > < symbols. But great work, I'll let you know when I successfully write a png.

Comment by Tom — July 27, 2009 @ 5:16 am

A fine tutorial there mate, helped me out a lot. Thanks for posting!

Comment by Guest — January 19, 2010 @ 11:42 pm

Thank you for this tutorial, very helpful.

>Funnily enough, this is also how a .BMP image is stored. (I don’t know why! if anyone knows, leave a comment! )

That’s because in 3D applications one of the axises (Y or Z) is usually directed straight up, not down. This would create additional inconsistency, if (U, V) image coordinates and (X, Y)/(X, Z) world coordinates will not be conformed.

Comment by nevelis — February 1, 2010 @ 10:21 pm

Shouldn’t:

for (size_t i = 0; i < bufferHeight; i++)

be:

for (size_t i = 0; i < imgHeight; i++)
?

Great tut πŸ™‚

Cheers!

Comment by Wracky — February 2, 2010 @ 4:06 pm

Changed it! Thanks for the comment πŸ˜€

Comment by DPurple — February 20, 2010 @ 6:43 am

Great tutorial, helped me a lot!
thanks!

Comment by xlq — March 11, 2010 @ 10:46 pm

Watch out for setjmp/longjmp and C++. If you’re using function-scope non-pod objects and you’re not careful, libpng could longjmp over some destructor calls and leak memory/cause undefined behaviour. Also, if something throws an exception in there, such as new, you’ll leak all your buffers and PNG structures.

Comment by Wracky — March 11, 2010 @ 11:39 pm

yeah you are right…I know its not the best option. I think i’ll spend some more time on this later and change the tutorial to use the callbacks for error handling. Thanks for the reply!

Comment by Tom — March 15, 2010 @ 11:34 pm

I’m not sure, but you use all those filters to change bit depths and stuff, and you still use the old depths and everything for you arrays. Shouldn’t you update these values as well?

Comment by Wracky — March 17, 2010 @ 9:41 pm

Thanks for noticing! You’re the first to notice it out of the 2.400+ people that have viewed the tutorial so far πŸ˜€ I’ve made a test case and it would indeed crash. I’ve updated the tutorial. Thanks again!!

Comment by Ra — April 3, 2010 @ 12:42 am

Thanks and congratulations for this great tutorial! Salud!

Comment by Michael — June 27, 2010 @ 3:14 pm

Thanks for this tutorial, it’s been of great value!

Comment by Ferneu — October 11, 2010 @ 10:06 pm

by the way, the PNG docs are not clear so I’ll ask here: is it possible/safe to reuse the pngPtr and the infoPtr? A certain png API that was used to “initialize” the infoPtr is deprecated (and I don’t really know if it was safe using it on the first place). And I have not found how to “reset” the requested transformations eighter. Perhaps they are not supposed to be reused, that is why there is no API for reseting them…

Oh well…

Comment by Wracky — October 13, 2010 @ 8:53 am

Hi Ferneu!

Thanks for Commenting. As you already concluded, there are no reset functions.
I haven’t looked into the libPNG code itself though… but the functions provided indeed suggest that the infoPtr etc are only to be used once.

You could look into the LibPNG source to find out what’s exactly in the infoPtr.
See if it is safe to just reset it and find out what values would be appropriate.

That’s all I know really πŸ˜‰

Comment by Mario — January 5, 2011 @ 12:08 am

Old image file formats (tga, bmp…) by default stores the data bottom up because of very old displays of the time, that raster bottom up.
As Guest saids, OpenGL uses bottom up to be consistent with 3d coordinates (small UV coordinate, small axis position)

Comment by Wracky — January 5, 2011 @ 6:36 pm

Cool, thanks πŸ˜€ I’ve added your explanation to the tutorial.

Comment by Dave — January 25, 2011 @ 12:33 pm

Awesome tutorial… thanks for the effort. Much appreciated.

Comment by Paul — February 9, 2011 @ 5:09 pm

Very nice, thank you very much. Saved me at least a week of tinkering!

Comment by Ash McConnell — April 25, 2011 @ 8:52 pm

Thanks for the tutorial, it helped a lot. I had a problem with mirrored and flipped images.

Comment by Chris Arena — September 3, 2011 @ 5:26 pm

Thanks for the tutorial!!

BTW, you don’t have to check for non-NULL before doing:

delete [] rowPtrs;
or
delete ptr;

The delete operator accepts NULL gracefully.

Cheers!!

Comment by Benson — December 18, 2011 @ 2:33 am

Out of curiosity, should

pUInt q = (imgHeight- i – 1) * stride;

actually be:

png_uint_32 q = (imgHeight- i – 1) * stride;

?

Comment by Wracky — December 19, 2011 @ 1:36 pm

Hi Benson, and thanks for posting!

The pUInt is actually one of our own typedefs. It guarantees a 32 bit int, same as the PNG one.
I thought I’d removed all our own types for the sake of this tutorial, but I guess this one slipped by, so thanks!

Regards,
Wracky.

Comment by Wilds — February 23, 2012 @ 2:47 pm

I am getting a access write violation at png_read_image(pngPtr, rowPtrs);
Can you help me?

Comment by Wracky — February 23, 2012 @ 8:19 pm

Hi Wilds, and thanks for posting.

The one thing I can think of that could cause png_read_image to fail with a write violation, is some problem with your row pointers. Maybe your row-pointers are not pointing to the right blocks of data, or the buffer they are pointing to is not large enough. Make sure the buffer you allocate is conform the image format.

Did you look at the example code in the tutorial?

Regards,
Wracky

Comment by diya — May 18, 2012 @ 11:38 am

Amazing tutorial thanks a bunch for this..
Very good start to useing the libong library

Comment by telandor — June 3, 2012 @ 8:24 pm

Great tutorial!
But unfortunately I guess I did something wrong. The program does not crash but the texture I get seems to be rather random.
I start reading the image with this code:

ifstream source;
source.open(fileName, ios::binary | ios::in);
if (!source) {
fprintf(stderr, “Could not load imagen”);
return 0;
}

And at the end of my function I have this:

source.close();
return (reinterpret_cast(rowPtrs));

But already here rowPtrs does not contain the correct data.

Can anybody give me a hint what I do wrong?

Comment by Ratzzo — November 21, 2012 @ 6:24 am

thank you very much, I was having some trouble, this libpng is not made the way I though, it’s a bit complex but it feels very simple after I read this tutorial, it was very well done and helpful, greetings (:

Comment by Brian Jason — December 2, 2012 @ 6:57 am

Please update the link address about pitch/stride from
http://www.toymaker.info/Games/html/pitch___stride.html
to
http://www.toymaker.info/Games/html/pitch__stride.html

Comment by Wracky — January 3, 2013 @ 3:05 pm

Thanks for the heads up!

Comment by bobsadinops — January 3, 2013 @ 3:41 pm

unfortunately libpng 1.2.7 has no inculde folder

Comment by carlixyz — September 26, 2013 @ 8:27 am

Nope, 1.2.7, 1.2.8, 1.2.5, 1.2.37 with and without zlib… in my Windows 7 IT DON’T WORKS I Had always the same issue with a interrumption issue between two calls:

before: png_init_io(png_ptr, fp);
and after: png_read_info(png_ptr, info_ptr);

I tryied a LOT of things and still not working,
man I HAD NIGHTMARES WITH (LIBPNG + ZLIB == HELL )!

Comment by Wracky — September 26, 2013 @ 1:29 pm

Sorry to hear that carlixyz,

LibPNG is kind of a low level API, and very error prone, if not set up correctly.
Usually when these types of error occur, it means some of the pointers are incorrect. Please make sure your row-pointers and the memory block associated with them is set up correctly, as shown in the tutorial.

In Piko3D, we have since switched to the FreeImage library. It is of course heavier than just LibPNG, but is easy to use, and supports a wide variety of image formats and platforms, as well as convenience functions for managing the image data. Perhaps this can help you out. You can find it here: http://freeimage.sourceforge.net/download.html

Comment by carlixyz — September 27, 2013 @ 1:21 am

Thanks for the answer!
well so it seems, maybe it’s something of my fault, who knows
the other problem it’s I can’t found a proper windows version bigger than 1.3++,
compiled with bins, includes and all…

In my case I’m playing with tinyPTC and trying to load some spritesheet into buffers and the like but nevermind… I think there are better solutions

Comment by carlixyz — September 27, 2013 @ 8:02 am

Ok, After some advances finally I had a new story to tell:

I remembered using a long time ago an old library called Corona (nothing related with the games SDK) that has a little .DLL and was kind of simple, It added in the proyect and well… Β‘BAM! IT WORKS! πŸ˜€

in fact it’s the easiest thing to load a PNG and work with it, supports a lots of formats, is openSource and even it doesn’t need libPng neither Zlib linkings (Altought maybe it should be using some of that in the deeps but it’s nicely wrapped ala C++ style so I will never look at the horror again)

http://corona.sourceforge.net/

Comment by tguen03 — September 28, 2013 @ 10:39 pm

Nice tutorial. Thanks for that!

Comment by Nicholas — November 25, 2014 @ 6:40 am

Great tutorial! πŸ™‚

One question: Do you need to call png_read_update_info()? It appears that calling that will update the info struct (png_infop) to reflect the transformation information.

Also, if this function should be called, should we also re-obtain the # of channels, bit-depth, etc… so that the information reflects what is actually inside of the info struct?

Here are a couple sources that seem to indicate that:
https://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Desktop-generic/LSB-Desktop-generic/libpng12.png.read.update.info.1.html
http://stackoverflow.com/questions/9364344/libpng-png-set-add-alpha-png-set-filler-error-sequential-row-overflow
http://www.libpng.org/pub/png/book/chapter13.html#png.ch13.div.7

Comment by Wracky — January 14, 2016 @ 10:56 pm

Sorry for the late response!
I think you’re right! We currently don’t use LibPNG in Piko3D anymore, so I cannot test it out right now, but it seems pretty safe to at least update the tutorial. Thank you for your feedback!

Comment by Jiboo — December 13, 2015 @ 4:01 pm

Hi,

Thanks for the article.
I didn’t test it, but after png_set_strip_16, in the branch where you test if bitdepth == 16 shouldn’t you set bitdepth to 8?

Bye,
Jiboo.

Comment by Wracky — January 14, 2016 @ 10:59 pm

Sorry for the late response.
With a call to png_read_update_info() as Nicholas suggested, I think this should be sorted. I have updated the tutorial. If it doesn’t help, please use the contact page to let me know, and I will make a more elaborate test. Thanks!

Comment by shreyas — January 25, 2018 @ 9:32 am

Hi,

I am trying to store PNG image bytes in a structure
i am using png_set_write_fn ()
for writing the png byte to not file but to mememory buffer
how can i save any leads will be helpfull
and i have go through this link too
https://stackoverflow.com/questions/1821806/how-to-encode-png-to-buffer-using-libpng/1823604

Comment by Carlos — September 27, 2021 @ 2:03 am

Thanks you for this really helpful tutorial, very straightforward and easy to understand!

RSS feed for comments on this post. TrackBack URL

Leave a comment

 

Creative Commons License
This work by piko3d.net is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Netherlands License
Powered by WordPress