I have a code that produce PNG file (using libpng).
I can open these files with EOG (Eye Of Gnome) but with GIMP, imagemagic and others I have an error.
Exiftool tells me that png file is truncated, but I don't see where. On EOG everything is ok.
The code:
int savepng(const char *name, fits *fit, uint32_t bytes_per_sample,
gboolean is_colour) {
int32_t ret = -1;
png_structp png_ptr;
png_infop info_ptr;
const uint32_t width = fit->rx;
const uint32_t height = fit->ry;
char *filename = strdup(name);
if (!ends_with(filename, ".png")) {
filename = str_append(&filename, ".png");
}
FILE *p_png_file = g_fopen(name, "wb");
if (p_png_file == NULL) {
return ret;
}
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also check that
* the library version is compatible with the one used at compile time,
* in case we are using dynamically linked libraries. REQUIRED.
*/
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(p_png_file);
return ret;
}
/* Allocate/initialize the image information data. REQUIRED */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(p_png_file);
png_destroy_write_struct(&png_ptr, NULL);
return ret;
}
/* Set error handling. REQUIRED if you aren't supplying your own
* error handling functions in the png_create_write_struct() call.
*/
if (setjmp(png_jmpbuf(png_ptr))) {
/* If we get here, we had a problem writing the file */
fclose(p_png_file);
png_destroy_write_struct(&png_ptr, &info_ptr);
return ret;
}
/* Set up the output control if you are using standard C streams */
png_init_io(png_ptr, p_png_file);
/* Set the image information here. Width and height are up to 2^31,
* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
*/
if (is_colour) {
png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_DEFAULT);
uint32_t profile_len;
const unsigned char *profile = get_sRGB_profile_data(&profile_len);
if (profile_len > 0) {
png_set_iCCP(png_ptr, info_ptr, *name ? name : "icc", 0, (png_const_bytep) profile, profile_len);
}
} else {
png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
PNG_COLOR_TYPE_GRAY,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_DEFAULT);
}
/* Write the file header information. REQUIRED */
png_write_info(png_ptr, info_ptr);
png_bytep *row_pointers = malloc((size_t) height * sizeof(png_bytep));
int samples_per_pixel;
if (is_colour) {
samples_per_pixel = 3;
} else {
samples_per_pixel = 1;
}
if (bytes_per_sample == 2) {
/* swap bytes of 16 bit files to most significant bit first */
png_set_swap(png_ptr);
WORD *data = convert_data(fit);
for (unsigned i = 0, j = height - 1; i < height; i++)
row_pointers[j--] = (png_bytep) ((uint16_t*) data + (size_t) samples_per_pixel * i * width);
} else {
uint8_t *data = convert_data8(fit);
for (unsigned i = 0, j = height - 1; i < height; i++)
row_pointers[j--] = (uint8_t*) data + (size_t) samples_per_pixel * i * width;
}
png_write_image(png_ptr, row_pointers);
/* Clean up after the write, and free any memory allocated */
png_destroy_write_struct(&png_ptr, &info_ptr);
/* Close the file */
fclose(p_png_file);
free(row_pointers);
free(filename);
return 0;
}
Could someone point me out my error? Software that can't read the image just display an error dialog. That's all. So it is difficult for me to know where is the error.
As proposed by #MarkSetchell, one line was missing:
/* Clean up after the write, and free any memory allocated */
png_write_end(png_ptr, info_ptr); // this line was missing
png_destroy_write_struct(&png_ptr, &info_ptr);
That's all.
Thank you.
I think that png_write_image() only writes the image row data, so various headers or elements of meta-data are missing. I normally use png_write_png() to write the whole file.
I've attached some code that definitely works for me, in that the output can be read by Gimp, etc. I don't claim it's production-quality ;)
int bitmap_write_png (const bitmap_t *bitmap, const char *path)
{
int ret = -1;
size_t x, y;
int pixel_size = 3;
int depth = 8;
FILE *fp = fopen (path, "wb");
if (fp)
{
ret = 0;
png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
png_infop info_ptr = info_ptr = png_create_info_struct (png_ptr);
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_byte ** row_pointers = png_malloc (png_ptr,
bitmap->height * sizeof (png_byte *));
for (y = 0; y < bitmap->height; y++)
{
png_byte *row =
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; x++)
{
pixel_t *pixel = pixel_at_const (bitmap, x, y);
*row++ = pixel->red * 255;
*row++ = pixel->green * 255;
*row++ = pixel->blue * 255;
}
}
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
for (y = 0; y < bitmap->height; y++)
{
png_free (png_ptr, row_pointers[y]);
}
png_free (png_ptr, row_pointers);
close (fp);
}
else
{
ret = errno;
}
return ret;
}
Related
I want to convert bytes that is stored in memory like
// pix is array of bytes with format format R, G, B, A, R, G, B, A, ...
// further image is going to be 250x300, so size is 250*300*4
char pix[250*300*4] = {
0, 255, 0, 0, //1
0, 255, 0, 0, //2
...
0, 255, 0, 0 //250*300
} // green image
to png image with help of libpng.
But I have not found any suitable function to do this. So, I am seeking for function like
png_bitmap_to_png(void *bitmap, void* png_raw_bytes).
If you have other ideas how to convert byte array to png image I am glad to hear them but do not offer usage of other libraries or converters like ImageMagick if it is not necessary, please.
I figured out how to do the thing. Instead of pix array I used row_pointers to store my color numbers, you may create some function to convert pix array to row_pointers if you want. Here is my code, I hope it will help
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <png.h>
#define IMAGE_HEIGHT 250
#define IMAGE_WIDTH 300
#define IMAGE_RED_COLOR 0
#define IMAGE_GREEN_COLOR 255
#define IMAGE_BLUE_COLOR 0
#define IMAGE_ALPHA_CHANNEL 255
void write_png_image(char* filename)
{
png_byte** row_pointers; // pointer to image bytes
FILE* fp; // file for image
do // one time do-while to properly free memory and close file after error
{
row_pointers = (png_byte**)malloc(sizeof(png_byte*) * IMAGE_HEIGHT);
if (!row_pointers)
{
printf("Allocation failed\n");
break;
}
for (int i = 0; i < IMAGE_HEIGHT; i++)
{
row_pointers[i] = (png_byte*)malloc(4*IMAGE_WIDTH);
if (!row_pointers[i])
{
printf("Allocation failed\n");
break;
}
}
// fill image with color
for (int y = 0; y < IMAGE_HEIGHT; y++)
{
for (int x = 0; x < IMAGE_WIDTH*4; x+=4)
{
row_pointers[y][x] = IMAGE_RED_COLOR; //r
row_pointers[y][x + 1] = IMAGE_GREEN_COLOR; //g
row_pointers[y][x + 2] = IMAGE_BLUE_COLOR; //b
row_pointers[y][x + 3] = IMAGE_ALPHA_CHANNEL; //a
}
}
//printf("%d %d %d %d\n", row_pointers[0][0], row_pointers[0][1], row_pointers[0][2], row_pointers[0][3]);
fp = fopen(filename, "wb"); //create file for output
if (!fp)
{
printf("Open file failed\n");
break;
}
png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //create structure for write
if (!png)
{
printf("Create write struct failed\n");
break;
}
png_infop info = png_create_info_struct(png); // create info structure
if (!info)
{
printf("Create info struct failed\n");
break;
}
if (setjmp(png_jmpbuf(png))) // this is some routine for errors?
{
printf("setjmp failed\n");
}
png_init_io(png, fp); //initialize file output
png_set_IHDR( //set image properties
png, //pointer to png_struct
info, //pointer to info_struct
IMAGE_WIDTH, //image width
IMAGE_HEIGHT, //image height
8, //color depth
PNG_COLOR_TYPE_RGBA, //color type
PNG_INTERLACE_NONE, //interlace type
PNG_COMPRESSION_TYPE_DEFAULT, //compression type
PNG_FILTER_TYPE_DEFAULT //filter type
);
png_write_info(png, info); //write png image information to file
png_write_image(png, row_pointers); //the thing we gathered here for
png_write_end(png, NULL);
printf("Image was created successfully\nCheck %s file\n", filename);
} while(0);
//close file
if (fp)
{
fclose(fp);
}
//free allocated memory
for (int i = 0; i < IMAGE_HEIGHT; i++)
{
if (row_pointers[i])
{
free(row_pointers[i]);
}
}
if (row_pointers)
{
free(row_pointers);
}
}
int main(int argc, char* argv[])
{
if(argc == 2)
{
write_png_image(argv[1]);
}
else
{
printf("Usage: %s pngfile.png\n", argv[0]);
}
return 0;
}
I am Attempting to download a binary file(exe) from a php script. Been stuck for hours.
PHP Script:
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 1048);
echo $buffer;
}
C:
char update[1024] = {0};
FILE *fileo;
fileo = fopen("test.exe", "wb");
instruction = recv(s, update, 1024, 0);
int result =returnBody();//Removes headers
fwrite(body, sizeof(body),1,fileo);
memset(update, 0, 1024);
while ((instruction = recv(s, update, 1024, 0)) > 0)
{
fwrite(update, sizeof(update),1,fileo);
memset(update, 0, 1024);
}
fclose(fileo);
returnbody function:
int returnBody(){
for(int x = 0; x < strlen(message); x++)
{
if(message[x] == '\r')
{
if(message[x + 1] == '\n')
{
if(message[x + 2] == '\r')
{
if(message[x + 3] == '\n')
{
int y = x + 4;
body = (char *)realloc(body, ((strlen(message) - y) * sizeof(char)));
memset(body, 0, sizeof(body));
for(int b = 0; y < strlen(message); y++, b++){
body[b] = message[y];
}
return 1;
}
}
}
}
}
return 0;
}
I succeed in writing the file but it doesn't run. Gets an error, unsupported
16 bit application.
My Question is do I need to parse it as binary before I write it to the file?
As pointed in comments:
you can't use strlen when using binary data
you can't use sizeof when using pointers on array.
Lets see:
/* you should have some static variables to store data */
static void * body = NULL;
size_t body_size = 0;
/* extract body from message */
int returnBody(void *message, size_t message_size){
if (! message || message_size < 5)
{
return 0;
}
/* if your system doesn't have memmem function, see https://stackoverflow.com/a/52989329/1212012 */
void *data = memmem(message, message_size, "\r\n\r\n", 4);
if (data) {
/* header found */
/* data are four bytes after the \r\n\r\n sequence */
data += 4;
size_t header_size = data - message;
body_size = message_size - header_size;
void *p = realloc(body, body_size);
if (!p) {
perror("realloc");
return 0;
}
body = p;
memcpy(body, data, body_size);
return 1;
}
return 0;
}
And your reading writting function should be like:
char update[1024] = {0};
FILE *fileo;
fileo = fopen("test.exe", "wb");
/* you should test `fileo` here */
/* first read */
instruction = recv(s, update, sizeof update, 0);
/* set message and its size for */
int result =returnBody(udpate, instruction);//Removes headers
/* you should test `result` here */
fwrite(body, body_size, 1, fileo);
/* clearing update is not necessary */
/*memset(update, 0, sizeof update); */
while ((instruction = recv(s, update, sizeof update, 0)) > 0)
{
fwrite(update, instruction, 1, fileo);
//memset(update, 0, sizeof update);
}
fclose(fileo);
I'm trying to compute the average vector in RGB tristimulus space with libpng in C, and NumPy in Python, but I'm getting different results with each. I'm quite confident Python is giving the correct result with this image of [ 127.5 127.5 0. ]. However, with the following block of C I get the preposterous result of [ 38.406494 38.433670 38.459641 ]. I've been staring at my code for weeks without any give, so I thought I'd see if others may have an idea.
Also, I've tested this code with other images and it gives similar preposterous results. It's quite curious because all three numbers usually match for the first 4 or so digits. I'm not sure what may be causing this.
/* See if our average vector matches that of Python's */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <png.h>
// For getting the PNG data and header/information back
typedef struct
{
uint32_t width; // width of image
uint32_t height; // height of image
int bit_depth; // bits/pixel component (should be 8 in RGB)
png_bytep datap; // data
} rTuple;
#define PNG_BYTES_TO_CHECK 8
#define CHANNELS 3
int
check_PNG_signature(unsigned char *buffer)
{
unsigned i;
const unsigned char signature[8] = { 0x89, 0x50, 0x4e, 0x47,
0x0d, 0x0a, 0x1a, 0x0a };
for (i = 0; i < PNG_BYTES_TO_CHECK; ++i)
{
if (buffer[i] != signature[i])
{
fprintf(stderr, "** File sig does not match PNG, received ");
for (i = 0; i < PNG_BYTES_TO_CHECK; ++i)
fprintf(stderr, "%.2X ", buffer[i]);
fprintf(stderr, "\n");
abort();
}
}
return 1;
}
rTuple
read_png_file(char *file_name)
{
/* Get PNG data - I've pieced this together by reading `example.c` from
beginning to end */
printf("** Reading data from %s\n", file_name);
png_uint_32 width, height; // holds width and height of image
uint32_t row; // for iteration later
int bit_depth, color_type, interlace_type;
unsigned char *buff = malloc(PNG_BYTES_TO_CHECK * sizeof(char));
memset(buff, 0, PNG_BYTES_TO_CHECK * sizeof(char));
FILE *fp = fopen(file_name, "rb");
if (fp == NULL) abort();
if (fread(buff, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) {
fprintf(stderr, "** Could not read %d bytes\n", PNG_BYTES_TO_CHECK);
abort();
}
check_PNG_signature(buff);
rewind(fp);
// create and initialize the png_struct, which will be destroyed later
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING
, NULL /* Following 3 mean use stderr & longjump method */
, NULL
, NULL
);
if (!png_ptr) abort();
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) abort();
// following I/O initialization method is required
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 0); // libpng has this built in too
// call to png_read_info() gives us all of the information from the
// PNG file before the first IDAT (image data chunk)
png_read_info(png_ptr, info_ptr);
// Get header metadata now
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
// Scale 16-bit images to 8-bits as accurately as possible (shouldn't be an
// issue though, since we're working with RGB data)
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
png_set_scale_16(png_ptr);
#else
png_set_strip_16(png_ptr);
#endif
png_set_packing(png_ptr);
// PNGs we're working with should have a color_type RGB
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
// Required since we selected the RGB palette
png_read_update_info(png_ptr, info_ptr);
// Allocate memory to _hold_ the image data now (lines 547-)
png_bytep row_pointers[height];
for (row = 0; row < height; ++row)
row_pointers[row] = NULL;
for (row = 0; row < height; ++row)
row_pointers[row] = png_malloc(png_ptr,\
png_get_rowbytes(png_ptr, info_ptr)
);
png_read_image(png_ptr, row_pointers);
png_read_end(png_ptr, info_ptr);
// Now clean up - the image data is in memory
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
rTuple t = { width, height, bit_depth, *row_pointers };
return t;
}
int
main(int argc, char *argv[])
{
if (argc != 2) {
printf("** Provide filename\n");
abort();
}
char *fileName = argv[1];
// get data read
rTuple data = read_png_file(fileName);
/* let's try computing the absolute average vector */
uint32_t i, j, k;
double *avV = malloc(CHANNELS * sizeof(double));
memset(avV, 0, sizeof(double) * CHANNELS);
double new_px[CHANNELS];
png_bytep row, px;
for (i = 0; i < data.height; ++i)
{
row = &data.datap[i];
for (j = 0; j < data.width; ++j)
{
px = &(row[j * sizeof(int)]);
for (k = 0; k < CHANNELS; ++k) {
new_px[k] = (double)px[k];
avV[k] += new_px[k];
}
}
}
double size = (double)data.width * (double)data.height;
for (k = 0; k < CHANNELS; ++k) {
avV[k] /= size;
printf("channel %d: %lf\n", k + 1, avV[k]);
}
printf("\n");
return 0;
}
Now with Python I'm just opening an image with a simple context manager and computing np.mean(image_data, axis=(0, 1)), which yields my result above.
Basically, you had a couple bugs (libpng side and pointer arithmetic) that I try to find them by comparing your code with this Github gist. The followings are the list of changes that I have made to produce the same image mean as Python NumPy.
In rTuple struct, you need to change the png_bytep datap to a pointer of type png_byte using: png_bytep *datap;.
In read_png_file, use png_set_filler to add the filler byte after reading the image. See here for more information about it.
if(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
In read_png_file, update the changes before allocating the row_pointers using png_read_update_info(png_ptr, info_ptr);
Again, in read_png_file, change the way you are mallocing memory for image pixels using:
png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for(row = 0; row < height; row++)
{
row_pointers[row] = malloc(png_get_rowbytes(png_ptr,info_ptr));
}
In main, change row = &data.datap[i]; to row = data.datap[i]; as your accessing a pointer here.
I did not want to populate the answer with a code that is barely the same as the question, so if you want to just copy and paste the answer, this is the link to the complete code.
Does anyone know about some function that loads a grayscale image into a GSL matrix?
Something like:
gsl_matrix *M;
load_image("./image.jpg", M); // any image extension would also be fine
Something like this works on my PC: allows you to load a grayscale JPG image using libjpeg.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <gsl/gsl_matrix.h>
gsl_matrix *load_jpg_image(const char *pFileName)
{
FILE *pFile;
long jpegSize;
unsigned char *pJpegBytes, *pPixels;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
int status, w, h, numComponents, stride, x, y;
gsl_matrix *pMatrix;
pFile = fopen(pFileName, "rb");
if (!pFile)
{
fprintf(stderr, "Can't open file\n");
return NULL;
}
// Get the size of the file
fseek(pFile, 0, SEEK_END);
jpegSize = ftell(pFile);
rewind(pFile);
if (jpegSize == 0)
{
fclose(pFile);
fprintf(stderr, "Empty file\n");
return NULL;
}
// Read it into memory and close file
pJpegBytes = (unsigned char *)malloc(jpegSize);
fread(pJpegBytes, 1, jpegSize, pFile);
fclose(pFile);
// Jpeg decompression starts here
memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, pJpegBytes, jpegSize);
status = jpeg_read_header(&cinfo, TRUE);
if (status != 1)
{
free(pJpegBytes);
fprintf(stderr, "Invalid JPEG header\n");
return NULL;
}
jpeg_start_decompress(&cinfo);
w = cinfo.output_width;
h = cinfo.output_height;
numComponents = cinfo.output_components;
if (numComponents != 1)
{
free(pJpegBytes);
fprintf(stderr, "Can only handle 1 color component\n");
return NULL;
}
pPixels = (unsigned char *)malloc(w*h);
stride = w*numComponents;
// perhaps this can de done much faster by processing
// multiple lines at once
while (cinfo.output_scanline < cinfo.output_height)
{
unsigned char *buffer_array[1];
buffer_array[0] = pPixels + cinfo.output_scanline * stride;
jpeg_read_scanlines(&cinfo, buffer_array, 1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(pJpegBytes);
// Now, create and fill in the matrix
pMatrix = gsl_matrix_alloc(h, w);
for (y = 0 ; y < h ; y++)
for (x = 0 ; x < w ; x++)
gsl_matrix_set(pMatrix, y, x, pPixels[x+y*stride]);
return pMatrix;
}
int main(void)
{
gsl_matrix *pMatrix;
int rows, cols;
int i, j;
pMatrix = load_jpg_image("test.jpg");
if (pMatrix == NULL)
{
fprintf(stderr, "Can't load matrix\n");
return -1;
}
//
// Use the matrix
//
gsl_matrix_free(pMatrix);
return 0;
}
I'm looking for a way to decode some png file, I heard about libpng, but I don't understand how this one works. Does it convert the png file into an array of bytes in the ARGB8888 format or something else ?
Runnable example
This example reads and existing PNG, modifies it, and writes a modified version to disk.
The modification part is done on raw bytes.
Usage:
./a.out [<old-png> [<new-png>]]
old-png defaults to a.png
new-png defaults to b.png
Tested on Ubuntu 18.04, libpng 1.6.34, compile with:
gcc -std=c99 main.c -lpng
Adapted from: https://gist.github.com/niw/5963798
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
unsigned int width;
unsigned int height;
png_bytep *row_pointers;
static void read_png_file(char *filename) {
FILE *fp = fopen(filename, "rb");
png_byte bit_depth;
png_byte color_type;
unsigned int y;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) abort();
png_infop info = png_create_info_struct(png);
if (!info) abort();
if (setjmp(png_jmpbuf(png))) abort();
png_init_io(png, fp);
png_read_info(png, info);
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
/* Read any color_type into 8bit depth, RGBA format. */
/* See http://www.libpng.org/pub/png/libpng-manual.txt */
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
/* PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
/* These color_type don't have an alpha channel then fill it with 0xff. */
if (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for (y = 0; y < height; y++) {
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info));
}
png_read_image(png, row_pointers);
fclose(fp);
}
static void write_png_file(char *filename) {
unsigned int y;
FILE *fp = fopen(filename, "wb");
if (!fp) abort();
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) abort();
png_infop info = png_create_info_struct(png);
if (!info) abort();
if (setjmp(png_jmpbuf(png))) abort();
png_init_io(png, fp);
png_set_IHDR(
png,
info,
width,
height,
8,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
png_write_info(png, info);
/* To remove the alpha channel for PNG_COLOR_TYPE_RGB format, */
/* Use png_set_filler(). */
/*png_set_filler(png, 0, PNG_FILLER_AFTER);*/
png_write_image(png, row_pointers);
png_write_end(png, NULL);
for (y = 0; y < height; y++) {
free(row_pointers[y]);
}
free(row_pointers);
fclose(fp);
}
static void process_png(void) {
for (unsigned int y = 0; y < height; y++) {
png_bytep row = row_pointers[y];
for (unsigned int x = 0; x < width; x++) {
png_bytep px = &(row[x * 4]);
/*printf("%4d, %4d = RGBA(%3d, %3d, %3d, %3d)\n", x, y, px[0], px[1], px[2], px[3]);*/
png_byte old[4 * sizeof(png_byte)];
memcpy(old, px, sizeof(old));
px[0] = 255 - old[0];
px[1] = 255 - old[1];
px[2] = 255 - old[2];
}
}
}
int main(int argc, char *argv[]) {
char *in;
char *out;
if (argc > 1) {
in = argv[1];
} else {
in = "a.png";
}
if (argc > 2) {
out = argv[2];
} else {
out = "b.png";
}
read_png_file(in);
process_png();
write_png_file(out);
return EXIT_SUCCESS;
}
a.png
b.png
Image source: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Catherine_of_Aragon.png/430px-Catherine_of_Aragon.png
You might be better off looking at a dedicated image library that will decode the image for you and return it in a recognised structure. It'll also serve as a better platform when you want to actually do something with the image (saving, displaying, etc).