Table of Contents

Images - libpng - Write a PNG to a file

#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
/* Pixels in this bitmap structure are stored as BGR. */
typedef struct _RGBPixel {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} RGBPixel;
 
/* Structure for containing decompressed bitmaps. */
typedef struct _RGBBitmap {
    RGBPixel *pixels;
    size_t width;
    size_t height;
    size_t bytewidth;
    uint8_t bytes_per_pixel;
} RGBBitmap;
 
/* Returns pixel of bitmap at given point. */
#define RGBPixelAtPoint(image, x, y) \
    *(((image)->pixels) + (((image)->bytewidth * (y)) \
                        + ((x) * (image)->bytes_per_pixel)))
 
/* Attempts to save PNG to file; returns 0 on success, non-zero on error. */
int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
    FILE *fp = fopen(path, "wb");
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    size_t x, y;
    png_uint_32 bytes_per_row;
    png_byte **row_pointers = NULL;
 
    if (fp == NULL) return -1;
 
    /* Initialize the write struct. */
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {
        fclose(fp);
        return -1;
    }
 
    /* Initialize the info struct. */
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        png_destroy_write_struct(&png_ptr, NULL);
        fclose(fp);
        return -1;
    }
 
    /* Set up error handling. */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(fp);
        return -1;
    }
 
    /* Set image attributes. */
    png_set_IHDR(png_ptr,
                 info_ptr,
                 bitmap->width,
                 bitmap->height,
                 8,
                 PNG_COLOR_TYPE_RGB,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);
 
    /* Initialize rows of PNG. */
    bytes_per_row = bitmap->width * bitmap->bytes_per_pixel;
    row_pointers = png_malloc(png_ptr, bitmap->height * sizeof(png_byte *));
    for (y = 0; y < bitmap->height; ++y) {
        uint8_t *row = png_malloc(png_ptr, sizeof(uint8_t) * bitmap->bytes_per_pixel);
        row_pointers[y] = (png_byte *)row;
        for (x = 0; x < bitmap->width; ++x) {
            RGBPixel color = RGBPixelAtPoint(bitmap, x, y);
            *row++ = color.red;
            *row++ = color.green;
            *row++ = color.blue;
        }
    }
 
    /* Actually write the image data. */
    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);
 
    /* Cleanup. */
    for (y = 0; y < bitmap->height; y++) {
        png_free(png_ptr, row_pointers[y]);
    }
    png_free(png_ptr, row_pointers);
 
    /* Finish writing. */
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return 0;
}

Another solution

#include <png.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "string.h"
 
/* 8 QBit RGB/24 to 16 QBit Grayscale hack
 * based on code found at
 * http://www.lemoda.net/c/write-png/  and png.h libpng version 1.6.23
 */
/*
 gcc -L/usr/local/static -I/usr/local/static/include -lpng16 /home/photog/bin/png.test.gray16.c  -lm -o  /home/photog/bin/png.tg16
 
 */
// =============================================================================
typedef struct {
    uint8_t red;  uint8_t green;  uint8_t blue;  // A colored pixel
} pixel_t;
 
typedef struct {
    uint16_t gray;  // A GRAY pixel
} pixel_gray_16_t;
 
typedef struct  {  // A picture
    pixel_gray_16_t *pixels;
    size_t width;
    size_t height;
} bitmap_t;
 
// =============================================================================
// Write "bitmap" to a PNG file specified by "path"; returns 0 on
//   success, non-zero on error
static int save_png_to_file (bitmap_t *bitmap, const char *path)  {
    FILE * fp;
    png_structp png_ptr = NULL;
    png_infop  info_ptr = NULL;
    size_t x, y;
    int pidx=0;  // Pixel_Index
    png_byte **row_pointers = NULL;  // KLUDGE!!
 
    /* "status" contains the return value of this function. At first
       it is set to a value which means 'failure'. When the routine
       has finished its work, it is set to a value which means
       'success'. */
    int status = -1;
    /* The following number is set by trial and error only. I cannot
       see where it it is documented in the libpng manual  */
    int pixel_size = 2;  // 3 for RGB/24;
    int depth = 16;      // 8 for RGB/24;
 
    fp = fopen (path, "wb");  if (! fp) {  goto fopen_failed;  }
 
    png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {  goto png_create_write_struct_failed;  }
 
    info_ptr = png_create_info_struct (png_ptr);
    if (info_ptr == NULL) {  goto png_create_info_struct_failed;  }
 
    /* Set up error handling. */
    if (setjmp (png_jmpbuf (png_ptr))) {  goto png_failure;  }
 
    // Set image attributes;  # de fine PNG_COLOR_TYPE_GRAY 0
    png_set_IHDR (png_ptr, info_ptr, bitmap->width, bitmap->height, depth,
        PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
        PNG_FILTER_TYPE_DEFAULT);
 
    /* Initialize rows of PNG. */
    row_pointers=png_malloc(png_ptr, bitmap->height * sizeof(png_uint_16 *));
 
    // Copy system Callocated user data to PNG owned space  
    for (y=0, pidx=0; y < bitmap->height; ++y) {
        png_byte *row = 
            png_malloc(png_ptr, sizeof(uint8_t) * bitmap->width * pixel_size);
        row_pointers[y] = row;
        memcpy((void *)row, bitmap->pixels+pidx, bitmap->width * 2);
        pidx += bitmap->width;  // Move to next row
    }
    /* Write the image data to "fp". */
    png_init_io (png_ptr, fp);
    png_set_rows (png_ptr, info_ptr, row_pointers);
    png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN,NULL); 
    //png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
 
    // The routine has successfully written the file, so we set "status" to a 
    // value which indicates success
    status = 0;
    for(y=0; y < bitmap->height; y++)  png_free (png_ptr, row_pointers[y]);  
    png_free (png_ptr, row_pointers);
 
 png_failure:
 png_create_info_struct_failed:
    png_destroy_write_struct (&png_ptr, &info_ptr);
 png_create_write_struct_failed:
    fclose (fp);
 fopen_failed:
    return status;
}
 
// =============================================================================
// =============================================================================
 
 
int main ()  {
    bitmap_t fruit;
    const char ofn[]={ "fruit.g16.png" };  // Output FileName
    int x, y, pidx=0;
    uint16_t gray_u16;
    float graysf;  // Gray Scale factor. 0->0, last_pix -> QMax(16)
 
    fruit.width  = 400;  // Size the image
    fruit.height = 400;
    graysf=(65535.0f/fruit.width)/fruit.height;  // Last pix => 65535
    fruit.pixels=calloc(sizeof(pixel_gray_16_t), fruit.width * fruit.height);
 
    // Create linear black -> white gradient
    for(y=0; y < fruit.height; y++)  {
        for(x=0; x < fruit.width; x++)  {
            gray_u16=(uint16_t)lrintf((y*fruit.width+x)*graysf);
            fruit.pixels[pidx++].gray = gray_u16;
        }
    }
 
    // Write the image to a file
    save_png_to_file (&fruit, ofn);
    printf("Wrote gray/16 PNG file %s\n", ofn);
    return 0;
}

Write Mandlebrot

// LibPNG example
// A.Greensted
// http://www.labbookpages.co.uk
 
// Version 2.0
// With some minor corrections to Mandlebrot code (thanks to Jan-Oliver)
 
// Version 1.0 - Initial release
 
#include <stdio.h>
#include <math.h>
#include <malloc.h>
#include <png.h>
 
// Creates a test image for saving. Creates a Mandelbrot Set fractal of size width x height
float *createMandelbrotImage(int width, int height, float xS, float yS, float rad, int maxIteration);
 
// This takes the float value 'val', converts it to red, green & blue values, then 
// sets those values into the image memory buffer location pointed to by 'ptr'
inline void setRGB(png_byte *ptr, float val);
 
// This function actually writes out the PNG image file. The string 'title' is
// also written into the image file
int writeImage(char* filename, int width, int height, float *buffer, char* title);
 
 
int main(int argc, char *argv[])
{
	// Make sure that the output filename argument has been provided
	if (argc != 2) {
		fprintf(stderr, "Please specify output file\n");
		return 1;
	}
 
	// Specify an output image size
	int width = 500;
	int height = 300;
 
	// Create a test image - in this case a Mandelbrot Set fractal
	// The output is a 1D array of floats, length: width * height
	printf("Creating Image\n");
	float *buffer = createMandelbrotImage(width, height, -0.802, -0.177, 0.011, 110);
	if (buffer == NULL) {
		return 1;
	}
 
	// Save the image to a PNG file
	// The 'title' string is stored as part of the PNG file
	printf("Saving PNG\n");
	int result = writeImage(argv[1], width, height, buffer, "This is my test image");
 
	// Free up the memorty used to store the image
	free(buffer);
 
	return result;
}
 
inline void setRGB(png_byte *ptr, float val)
{
	int v = (int)(val * 767);
	if (v < 0) v = 0;
	if (v > 767) v = 767;
	int offset = v % 256;
 
	if (v<256) {
		ptr[0] = 0; ptr[1] = 0; ptr[2] = offset;
	}
	else if (v<512) {
		ptr[0] = 0; ptr[1] = offset; ptr[2] = 255-offset;
	}
	else {
		ptr[0] = offset; ptr[1] = 255-offset; ptr[2] = 0;
	}
}
 
int writeImage(char* filename, int width, int height, float *buffer, char* title)
{
	int code = 0;
	FILE *fp = NULL;
	png_structp png_ptr = NULL;
	png_infop info_ptr = NULL;
	png_bytep row = NULL;
 
	// Open file for writing (binary mode)
	fp = fopen(filename, "wb");
	if (fp == NULL) {
		fprintf(stderr, "Could not open file %s for writing\n", filename);
		code = 1;
		goto finalise;
	}
 
	// Initialize write structure
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		fprintf(stderr, "Could not allocate write struct\n");
		code = 1;
		goto finalise;
	}
 
	// Initialize info structure
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		fprintf(stderr, "Could not allocate info struct\n");
		code = 1;
		goto finalise;
	}
 
	// Setup Exception handling
	if (setjmp(png_jmpbuf(png_ptr))) {
		fprintf(stderr, "Error during png creation\n");
		code = 1;
		goto finalise;
	}
 
	png_init_io(png_ptr, fp);
 
	// Write header (8 bit colour depth)
	png_set_IHDR(png_ptr, info_ptr, width, height,
			8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
			PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
 
	// Set title
	if (title != NULL) {
		png_text title_text;
		title_text.compression = PNG_TEXT_COMPRESSION_NONE;
		title_text.key = "Title";
		title_text.text = title;
		png_set_text(png_ptr, info_ptr, &title_text, 1);
	}
 
	png_write_info(png_ptr, info_ptr);
 
	// Allocate memory for one row (3 bytes per pixel - RGB)
	row = (png_bytep) malloc(3 * width * sizeof(png_byte));
 
	// Write image data
	int x, y;
	for (y=0 ; y<height ; y++) {
		for (x=0 ; x<width ; x++) {
			setRGB(&(row[x*3]), buffer[y*width + x]);
		}
		png_write_row(png_ptr, row);
	}
 
	// End write
	png_write_end(png_ptr, NULL);
 
	finalise:
	if (fp != NULL) fclose(fp);
	if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
	if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
	if (row != NULL) free(row);
 
	return code;
}
 
float *createMandelbrotImage(int width, int height, float xS, float yS, float rad, int maxIteration)
{
	float *buffer = (float *) malloc(width * height * sizeof(float));
	if (buffer == NULL) {
		fprintf(stderr, "Could not create image buffer\n");
		return NULL;
	}
 
	// Create Mandelbrot set image
 
	int xPos, yPos;
	float minMu = maxIteration;
	float maxMu = 0;
 
	for (yPos=0 ; yPos<height ; yPos++)
	{
		float yP = (yS-rad) + (2.0f*rad/height)*yPos;
 
		for (xPos=0 ; xPos<width ; xPos++)
		{
			float xP = (xS-rad) + (2.0f*rad/width)*xPos;
 
			int iteration = 0;
			float x = 0;
			float y = 0;
 
			while (x*x + y*y <= 4 && iteration < maxIteration)
			{
				float tmp = x*x - y*y + xP;
				y = 2*x*y + yP;
				x = tmp;
				iteration++;
			}
 
			if (iteration < maxIteration) {
				float modZ = sqrt(x*x + y*y);
				float mu = iteration - (log(log(modZ))) / log(2);
				if (mu > maxMu) maxMu = mu;
				if (mu < minMu) minMu = mu;
				buffer[yPos * width + xPos] = mu;
			}
			else {
				buffer[yPos * width + xPos] = 0;
			}
		}
	}
 
	// Scale buffer values between 0 and 1
	int count = width * height;
	while (count) {
		count --;
		buffer[count] = (buffer[count] - minMu) / (maxMu - minMu);
	}
 
	return buffer;
}