I am trying to get a program that will read pixel data of a PNG image.
Here's the issue:
I installed libpng and then used the code from this tutorial. It compiles and runs, but when I cout the data from rowPtrs (which, from what I understand, should contain the data from the pixels in each row), it always displays "\377".
I copied the code exactly from the tutorial, and the only code that I changed was the initialization of the istream, from which I copied the cpp reference code. (If you can't tell, I just wanted a quick, dirty, and temporary program).
Here's the code:
I've been stuck on this for quite some time, and any help is appreciated! Thanks!
Here's the issue:
I installed libpng and then used the code from this tutorial. It compiles and runs, but when I cout the data from rowPtrs (which, from what I understand, should contain the data from the pixels in each row), it always displays "\377".
I copied the code exactly from the tutorial, and the only code that I changed was the initialization of the istream, from which I copied the cpp reference code. (If you can't tell, I just wanted a quick, dirty, and temporary program).
Here's the code:
Code:
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include "libpng14/png.h"
#define PNGSIGSIZE 8
using namespace std;
bool validate(std::istream& source);
void userReadData(png_structp pngPtr, png_bytep data, png_size_t length);
png_infop infoPtr;
png_structp pngPtr;
png_bytep* rowPtrs;
int main (int argc, char * const argv[]) {
filebuf fb;
fb.open ("image.png",ios::in);
istream source(&fb);
//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 0; //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) {
cerr << "ERROR: Couldn't initialize png read struct" << std::endl;
return 0; //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!
infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
cerr << "ERROR: Couldn't initialize png info struct" << std::endl;
png_destroy_read_struct(&pngPtr, (png_infopp)0, (png_infopp)0);
return 0; //Do your own error recovery/handling here
}
//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.
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;
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 0; // Do your own error handling here.
}
png_set_read_fn(pngPtr,(voidp)&source, userReadData);
//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);
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);
//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 easily)
//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.
int q = (imgHeight-i-1) * stride;
rowPtrs[i] = (png_bytep)(data + q);
}
//And here it is! The actual 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);
fb.close();
return 0;
}
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);
}
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);
}
I've been stuck on this for quite some time, and any help is appreciated! Thanks!