John Hamlett IV John Hamlett IV - 21 days ago 6
C++ Question

Loading RGB PNG results in black image

I am using the libpng library to load png files to a PixelBuffer, which is simply a 2-D array or RGBA values.

The following code works for and image that contains an alpha channel, but not on an image that does not contain the alpha channel.

PixelBuffer *IOManager::load_png_to_pixel_buffer(const char *file_name) {
FILE *infile;

char header[8];

bool alphaFlag = false;


if ((infile = fopen(file_name, "rb")) == NULL) {
std::cout << "Could not open file: " << file_name << std::endl;
return nullptr;
}

fread(header, 1, 8, infile);


png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

if (!png_ptr){
std::cout << "Could not create PNG read struct" << std::endl;
return nullptr;
}

png_infop info_ptr = png_create_info_struct(png_ptr);

if (!info_ptr){
std::cout << "Could not create PNG info struct" << std::endl;
return nullptr;
}


png_init_io(png_ptr, infile);
png_set_sig_bytes(png_ptr, 8);

png_read_info(png_ptr, info_ptr);

int width = png_get_image_width(png_ptr, info_ptr);
int height = png_get_image_height(png_ptr, info_ptr);
png_byte color_type = png_get_color_type(png_ptr, info_ptr);
png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);

if (bit_depth == 16){
png_set_strip_16(png_ptr);
}

if (color_type == PNG_COLOR_TYPE_PALETTE){
png_set_palette_to_rgb(png_ptr);
}

if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8){
png_set_expand_gray_1_2_4_to_8(png_ptr);
}

if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
png_set_tRNS_to_alpha(png_ptr);
}

if(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE){
std::cout << "Setting filler" << std::endl;
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
}

if(color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
png_set_gray_to_rgb(png_ptr);
}

png_read_update_info(png_ptr, info_ptr);




PixelBuffer * buffer = new PixelBuffer(width, height,
ColorData(1, 1, static_cast<float>(0.95)));

//png_bytep * row_pointer[width * 4];

png_bytep * row_pointers = new png_bytep[height]();

for (int y=0; y<height; y++){
std::cout << png_get_rowbytes(png_ptr,info_ptr) << std::endl;

row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
}


png_read_image(png_ptr, row_pointers);

std::cout << row_pointers << std::endl;

for (int x=0; x<width;x++){
for (int y=0; y < height; y++){
int height_iterator = height - 1;
int r = row_pointers[y][x*4];
int g = row_pointers[y][x*4+1];
int b = row_pointers[y][x*4+2];
int a = row_pointers[y][x*4+3];
if (a == 255)
printf("R: %d G: %d B: %d A: %d\n", r, g, b, a);
buffer->set_pixel(x, height_iterator - y, ColorData(r/255,
g/255, b/255,
a/255));
}
}
return buffer;
}


I have tried flipping the filler byte to 0x0 and still result in a black image.

The printf statement inside of the loop seems like valid colors, and I cannot seem to figure out why it is not working.

How can I get the image to load something other than a black image?

Answer

I assume ColorData accepts floats in the range [0, 1]. Change the call to the ColorData constructor to ColorData(r/255.0f, g/255.0f, b/255.0f, a/255.0f) so you get floating point values instead of integer zeroes.