I suspect that there is a more straightforward solutionAfter getting words of encouragement from Andre and to this via a Python programbe brief, but since I am not well verseddid some further research in Pythonthe "libpng" functionality and its use of pixel data, I will end my submission and attempt here and look forwarddevised a second program to what others didread and process the "moth/butterfly" image, producing a monochrome image depicting the iconic scene of Benjamin Franklin flying a kite in a thunderstorm with a key attached to solve the last partkite string and including the closing portion of the tasksaying ". Perhaps the decoder program will give some ideas to folks also trying to pursue a "C" program solution.. if two of them are dead".
Good luck everyoneFollowing is the second program to complete task 2 sprinkled with comments in case anyone wants to reference the use of "libpng" in their future code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#define HEADER 34
/* Prototypes */
void read_png_file(char *filename, unsigned int *input_width, unsigned int *input_height);
unsigned int *process_png(unsigned int *output_width, unsigned int *output_height, unsigned int input_width, unsigned int input_height);
unsigned int writeImage(char *filename, unsigned int wt, unsigned int ht, unsigned int *buffer, char *title);
png_bytep *row_pointers;
int main(int argc, char *argv[])
{
unsigned int rx, ht, wt, hx, wx, * buffer = NULL;
read_png_file(argv[1], &wx, &hx);
buffer = process_png(&wt, &ht, wx, hx);
printf("Output height: %d Output width: %d\n", ht, wt);
rx = writeImage(argv[2], wt, ht, buffer, "Hidden Image");
if (rx != 0)
{
printf("Exit error: %d\n", rx);
return 1;
}
for (int y = 0; y < hx - 1; y++)
{
free(row_pointers[y]);
}
free(row_pointers);
free(buffer);
return EXIT_SUCCESS;
}
void read_png_file(char *filename, unsigned int *input_width, unsigned int *input_height)
{
FILE *fp = fopen(filename, "rb");
png_byte bit_depth, color_type;
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);
*input_width = png_get_image_width(png, info);
*input_height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
if (bit_depth == 16) /* Read any color_type into 8bit depth, RGBA format. */
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) /* PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16 bit depth. */
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) * *input_height);
for (int y = 0; y < *input_height; y++)
{
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info));
}
png_read_image(png, row_pointers);
printf("Input height.: %d Input width.: %d\n", *input_height, *input_width);
fclose(fp);
return;
}
unsigned int *process_png(unsigned int *output_width, unsigned int *output_height, unsigned int input_width, unsigned int input_height)
{
int ix = 0;
png_bytep row = row_pointers[0];
/* Construct width and height values using the LSB of bytes 2 - 33 to convert from binary to integer */
*output_width = (row[22] % 2) + (row[21] % 2) * 2 + (row[20] % 2) * 4 + (row[18] % 2) * 8 + (row[17] % 2) * 16
+ (row[16] % 2) * 32 + (row[14] % 2) * 64 + (row[13] % 2) * 128 + (row[12] % 2) * 256 + (row[10] % 2) * 512;
*output_height = (row[44] % 2) + (row[42] % 2) * 2 + (row[41] % 2) * 4 + (row[40] % 2) * 8 + (row[38] % 2) * 16
+ (row[37] % 2) * 32 + (row[36] % 2) * 64 + (row[34] % 2) * 128 + (row[33] % 2) * 256 + (row[32] % 2) * 512;
unsigned int *buffer = (unsigned int *) malloc((*output_width * *output_height + HEADER) * sizeof(unsigned int));
/* Store the LSB of each pixel as a character in a large character array */
/* Only go as far as the two-dimensional array size */
for (unsigned int y = 0; y < input_height; y++)
{
png_bytep row = row_pointers[y];
for (unsigned int x = 0; x < input_width; x++)
{
png_bytep px = &(row[x * 4]);
buffer[ix] = px[0] % 2 + '0';
ix++;
buffer[ix] = px[1] % 2 + '0';
ix++;
buffer[ix] = px[2] % 2 + '0';
ix++;
if (ix > (*output_width * *output_height + HEADER))
break;
}
if (ix > (*output_width * *output_height + HEADER))
break;
}
for (int i = 0; i < (*output_width * *output_height); i++)
buffer[i] = buffer[i + HEADER];
return buffer;
}
unsigned int writeImage(char *filename, unsigned int wt, unsigned int ht, unsigned int *buffer, char *title)
{
int code = 0;
FILE *fp = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_bytep row = NULL;
fp = fopen(filename, "wb"); /* Open file for writing - binary mode */
if (fp == NULL)
{
fprintf(stderr, "Could not open file %s for writing\n", filename);
code = 1;
goto finalise;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); /* Initialize write structure */
if (png_ptr == NULL)
{
fprintf(stderr, "Could not allocate write struct\n");
code = 1;
goto finalise;
}
info_ptr = png_create_info_struct(png_ptr); /* Initialize info structure */
if (info_ptr == NULL)
{
fprintf(stderr, "Could not allocate info struct\n");
code = 1;
goto finalise;
}
if (setjmp(png_jmpbuf(png_ptr))) /* Set up exception handling */
{
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, wt, ht, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (title != NULL) /* Set the title */
{
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);
row = (png_bytep) malloc(3 * wt * sizeof(png_byte)); /* Allocate memory for one row (3 bytes per pixel - RGB) */
for (int i = 0; i < ht; i++) /* Write the image data */
{
for (int j = 0; j < wt; j++)
{
if (buffer[i * wt + j] == '1')
{
row[j * 3 + 0] = 255; /* These RGB values could be changed to */
row[j * 3 + 1] = 255; /* produce a different background colour */
row[j * 3 + 2] = 255;
}
else
{
row[j * 3 + 0] = 0; /* These RGB values could be changed to */
row[j * 3 + 1] = 0; /* produce a different foreground colour */
row[j * 3 + 2] = 0;
}
}
png_write_row(png_ptr, row);
}
png_write_end(png_ptr, NULL); /* Denote end of writing */
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;
}
Executing the above code resulted in the diagnostic information in the terminal and the subsequent monochrome image.
craig@Vera:~/C_Programs/Console/StegImage/bin/Release$ ./StegImage Moth.png ben.png
Input height.: 480 Input width.: 640
Output height: 900 Output width: 900
Thanks again. This was a nice challenge.