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;
}