6.s096 Final Project

Final Project Starter Kit

Starting a C or C++ project from scratch can be a daunting task. If you're having trouble picking a software library, installing it on your system, and testing that it works, maybe you should try this Starter Kit!

This Starter Kit uses the libpng library (which is already installed on Athena!) to render a series of random lines as a PNG image. The images look like this:

The example code was written by skimming the section of libpng's PDF manual on how to write PNG files. That manual also has a section on reading PNG files that you might find useful.

Use this kit to bootstrap your project and your imagination! The deadline looms, but there's still plenty of time to have fun making silly little PNG files!

Source Code

lines.c

#include <stdio.h>
#include <stdlib.h>

#define PNG_SETJMP_NOT_SUPPORTED
#include <png.h>

#define WIDTH 256
#define HEIGHT 32
#define COLOR_DEPTH 8

struct Pixel {
	png_byte r, g, b, a;
};

int main(int argc, char *argv[]) {
	srand(time(NULL));

	/* open PNG file for writing */
	FILE *f = fopen("out.png", "wb");
	if (!f) {
		fprintf(stderr, "could not open out.png\n");
		return 1;
	}

	/* initialize png data structures */
	png_structp png_ptr;
	png_infop info_ptr;

	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!png_ptr) {
		fprintf(stderr, "could not initialize png struct\n");
		return 1;
	}

	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr) {
		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
		fclose(f);
		return 1;
	}

	/* begin writing PNG File */
	png_init_io(png_ptr, f);
	png_set_IHDR(png_ptr, info_ptr, WIDTH, HEIGHT, COLOR_DEPTH,
	             PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
	             PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
	png_write_info(png_ptr, info_ptr);

	/* allocate image data */
	struct Pixel *row_pointers[HEIGHT];
	for (int row = 0; row < HEIGHT; row++) {
		row_pointers[row] = calloc(WIDTH*2, sizeof(struct Pixel));
	}

	/* draw a bunch of vertical lines */
	for (int col = 0; col < WIDTH; col++) {
		int bar_height = rand() % HEIGHT;
		for (int row = 0; row < HEIGHT; row++) {
			if (HEIGHT - row <= bar_height) {
				row_pointers[row][col].r = 255; // red
				row_pointers[row][col].g = 0; // green
				row_pointers[row][col].b = 0; // blue
				row_pointers[row][col].a = 255; // alpha (opacity)
			} else {
				row_pointers[row][col].r = 0; // red
				row_pointers[row][col].g = 0; // green
				row_pointers[row][col].b = 0; // blue
				row_pointers[row][col].a = 0; // alpha (opacity)
			}
		}
	}

	/* write image data to disk */
	png_write_image(png_ptr, (png_byte **)row_pointers);

	/* finish writing PNG file */
	png_write_end(png_ptr, NULL);

	/* clean up PNG-related data structures */
	png_destroy_write_struct(&png_ptr, &info_ptr);

	/* close the file */
	fclose(f);
	f = NULL;

	return 0;
}

Compiling

$ gcc -Wall -std=c99 lines.c -lpng -o lines

The -lpng flag is short for -l png, which links the png library with your project to create the program.

Running

$ ./lines

./lines creates a file called out.png in the current directory with some cRaZy graphics inside.

To view your image, you can use the scp command to copy it to your computer, or you can put it in your web directory so that you can download it at http://web.mit.edu/YOURNETID/www/out.png:

$ cp out.png ~/www/

Understand It

In the spirit of lab exercises, here are a few tasks to direct your focus to the parts of the program that matter:

  1. Make the program take the filename as a command-line argument instead of hard-coding "out.png".
  2. Tweak the values that are assigned to r, g, b, and a in the loop labeled "draw a bunch of vertical lines". The values range from 0 to 255. You should do this if you don't understand how pixels work. Wikipedia has a nice section on pixel colors.
  3. Memory is allocated for storing the image data, but it's never freed. You should free it. You'll definitely need to do this if you use this code as a base for your project!
  4. Tweak the program to draw horizontal lines instead of vertical ones.

What Next?

Here are some concrete project ideas that you can implement and submit, roughly in order of increasing difficulty.

Whatever you do, start (very) small and add features as time allows. The secret to success here is to be unambitious at first.

Plot a Graph

Instead of drawing lines of random height, allow users to plot a bar graph by providing numbers as program arguments or using stdin.

Draw Shapes

Draw shapes of various colors. Start with squares, then move on to lines and circles if you have more time or people.

Optionally, convert the program to C++ and create a Shape parent class with sub-classes for each type of shape.

Draw Text

Read text from stdin and draw it on the image by changing pixel colors in the patterns of (very simple) letters. Here's an example of how you might render 'hi':

Instead of hard-coding how to draw each letter shape, I recommend storing them as strings or in a separate file, but this is not necessary.

Invert an Image

Read a PNG image (with libpng), flip it upside-down, and save it as a new image. Here's an example program that reads and writes PNG files.

Game of Life

Implement the Game of Life and render each time step as a separate PNG file.