1 Overview

This programming assignment’s goal is to making sure you are ready for the non-real-time MPs that follow. It is dramatically easier than other MPs.

We also have some tips if this is your first open-ended project

This assignment, like all other assignments, is governed by the common components of MPs.

1.1 Logistics

Unlike other programming assignments

  • all parts are required
  • no extra credit is possible
  • avoid plagarism, but feel free to grab code from any source as long as you cite it
  • you are encouraged to submit it in each language you think you might want to use

1.2 Deliverables

Submit

  1. A Makefile with (at least) two targets: build, which accepts no arguments, and run, which accepts a single input file argument.

    Running

    make build
    make run file=inputfilename.txt

    should execute your code on inputfilename.txt.

  2. Your code, in whatever language you prefer, including any necessary support libraries that make build cannot obtain

  3. A file named implemented.txt that contains the names of the reference input files you believe your code handles, one per line

If all of your files are in the same directory, you can submit them as-is (but must submit all of them in one go; piecemeal uploads will not work).

If you need a specific directory structure, upload a .zip or .tar that contains those files. The logic for handling submissions is

  1. If you submitted a Makefile directly, we use that
  2. Otherwise if you submitted a tarball or zip archive, we extract that and
    1. If extracting it provides a Makefile, we use that
    2. Otherwise if extracting it created exactly one directory, we enter that and
      1. If there’s a Makefile in that directory, we use that
      2. Otherwise, report an upload format error
    3. Otherwise, report an upload format error
  3. Otherwise, report an upload format error

The input files your code will read are ASCII text files; after processing an input file, your code will produce one or more RGBA PNG files.

2 What to code

2.1 Reading Input

Each run, your program will be given one command-line argument, which is the path to an input file.

Each input file will have a number of lines; each line will have a keyword first, followed by zero or more extra pieces of information separated by whitespace (some mix of ' ' and '\t'). Lines should be read in order, first to last, because some lines refer to the lines above them.

Ignore blank lines and lines starting with anything other than a keyword you know. Always strip leading and trailing space from a line before parsing it.

In this assignment input files might look like

png 200 300 outfilename.png
xy 10 20

xyrgb 50 50   255 127 0
ignore this line since "ignore" is not a keyword you know 
likewise ignore this line, which also starts with an unknown keyword
xyc     150   250 #ff00ee

You do not need to have error checking code. For example, if a png keyword is not followed by exactly two positive integers and one string ending .png, your code is welcome to break in any way you wish.

2.1.1 Keywords

Each homework will define its own set of keywords. For this warmup MP, these are:

png width height filename

Every file will begin with png

png will be followed by two positive integers, width and height, and a filename. You should write a RGBA png image of the specified width and height (see [Image file creation]). You should write the file in the default directory. The initial color of every pixel in the image should be transparent black (0, 0, 0, 0).

You may assume the filename contains only non-whitespace ASCII characters and already has the appropriate .png ending.

xyrgb x y r g b

Fill the pixel noted by the x and y coordinate to have the specified color (r, g, b, 255).

You may assume r, g, and b are integers between 0 and 255, inclusive. See the discussion of xy for comments on x and y.

xyc x y hexColorString

Fill the pixel noted by the x and y coordinate to have the specified color. The color is given in a web-standard 3-byte hex code: #rrggbb, where rr is a two-digit hexidecimal value for red, gg for green, and bb for blue. Set the alpha to 255 (0xff)

You may assume hexColorString is always a seven-character string of the appropriate format. See the discussion of xy for comments on x and y.

3 Get two files working

You should be able to pass both of the following. To get credit for them, make sure you put their names in your implemented.txt file, like

mp0ex1.txt
mp0ex2.txt
mp0ex1.txt

This file’s contents are

png 5 8 mp0ex1.png
xyrgb 0 1 255 255 255
xyrgb 1 2 127 255 255
xyc 2 3 #aaaaff
xyc 3 4 #ffffff
xyrgb 4 5 200 120 3
xyrgb 3 3 0 0 0
xyrgb 2 4 0 0 0

and it should produces this image file: .

That’s so small it’s almost impossible to see, so let’s zoom in and put a striped background behind the image so you can tell the difference between transparent and white:

mp0ex2.txt

This file’s contents are

png 5 8 mp0ex2.png

xyrgb 1     2   127 255 255      
xyc             2 3 #aaaaff
nothing here 23425 56
xyrgb 4 5 200 120 3
xyrgb 2 4 0 0 0

and it should produces this image file:

4 Submission and Feedback

This warmup MP is not graded by a human. Rather, each time you submit your code to submission site it will run automated tests and add information about their outcomes to that site. There will be a delay of up to an hours before that information is visible.

Feel free to re-submit until you get positive status results. Please refer questions to the campus forum or office hours.

5 Tips

5.1 Creating PNG files

You will create an 8-bit RGBA PNG image with a specified width and height. The way to do this will vary by language. You must use a technique that supports setting individual pixels to specific colors, not one with higher-level shape drawing functions (we’ll write those functions ourselves).

Some optional parts of homeworks 2 and 3 will include reading image files too. If you want to do those then, it might make sense to learn how now.

The following is example code to write PNGs taken from past semester’s student submissions. I don’t claim they are optimal, but they did work.

5.1.1 C

Past students have used miniz.c and LodePNG. Miniz can only write images (not read them) so it won’t let you do the texture-based optional parts of assignments. Lodepng does not have this limitation.

An example using LodePNG:
#include "lodepng.h"

int main(int argc, char *argv[]){
    /* ... */
    unsigned char *image = calloc(width * height * 4, sizeof(unsigned char));
    /* ... */
    image[((y * width) + x)*4 + 0] = red;
    image[((y * width) + x)*4 + 1] = green;
    image[((y * width) + x)*4 + 2] = blue;
    image[((y * width) + x)*4 + 3] = alpha;
    /* ... */
    lodepng_encode32_file(filename, image, width, height);
    free(image);
} 
An example using miniz
#include "miniz.h"

int main(int argc, char *argv[]){
    /* ... */
    unsigned char *image = calloc(width * height * 4, sizeof(unsigned char));
    /* ... */
    image[((y * width) + x)*4 + 0] = red;
    image[((y * width) + x)*4 + 1] = green;
    image[((y * width) + x)*4 + 2] = blue;
    image[((y * width) + x)*4 + 3] = alpha;
    /* ... */
    size_t size;
    void* data = tdefl_write_image_to_png_file_in_memory((void*) image, width, height, 4, &size);
    FILE* out_file = fopen(filename, "wb");
    fwrite(data, 1, size, out_file);
    fclose(out_file);
    free(data);
    free(image);
} 

5.1.2 C++

All methods that work for C also work for C++, and most students used one of those. An OO interface is also available through CImg. CImg depends on libpng, which some students have difficulty installing; the two C libraries do not depend on libpng and have worked for every student that has tried it so far.

An example using CImg:
#define cimg_use_png
#define cimg_display 0
#include "CImg.h"

int main(int argc, char *argv[]){
    /* ... */
    cimg_library::CImg<unsigned char> image(width, height, 1, 4);
    /* ... */
    image(x,y,0,0) = red;
    image(x,y,0,1) = green;
    image(x,y,0,2) = blue;
    image(x,y,0,3) = alpha;
    /* ... */
    image.save_png(filename.c_str());
} 

5.1.3 C#

The testing server is running mono on Linux, not Microsoft’s C# implementation. If you chose to use C#, you are responsible for ensuring it runs on Linux with mono.

The relevant class is System.Drawing.Bitmap.

An example
using System.Drawing;
class ClassName {
    static void Main(string[] args) {
        // ...
        Bitmap img = new Bitmap(width, height, PixelFormat.Format32bppArgb);
        // ...
        img.SetPixel(x,y, Color.FromARGB(red,green,blue,alpha));
        // ...
        img.save(filename);
    }
}

5.1.4 Dart

The relevant package is image/image.dart.

An example
import 'package:image/image.dart';
import 'dart:io';

void main(List<String> args) {
    // ...
    image = Image(width, height);
    // ...
    image.setPixelRgba(x, y, red, green, blue);
    // ...
    File(filename).writeBytesAsSync(encodePng(image));
}

5.1.5 Go

The relevant package is image.

An example
import (
 "image/png"
 "image/color"
 "io"
)
func main() {
    // ...
    img := image.NewRGBA(image.Rect(0, 0, width, height))
    // ...
    img.Set(x, y, color.RGBA{red, green, blue, alpha})
    // ...
    w, err := os.Create(img.filename)
    png.Encode(w, img)
}

5.1.6 Haskell

Students have reported success using Phll

An example
import qualified Data.ByteString.Lazy as B
import Phll

pixels = flip map [0..width] $
         \x -> flip map [0..height] $
               \y -> (red, green, blue, alpha)

main = B.writeFile filename $ B.pack $ Phll.png_rgba pixels

5.1.7 Java

The relevant libraries are Color from java.awt, BufferedImage and WritableRaster from java.awt.image and ImageIO from javax.imageio package. Do not use java.awt.Graphics.

An example
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.WriteableRaster;
import javax.imageio.ImageIO;

class ClassName {
    public static void main(String[] args) {
        // ...
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
        WritableRaster raster = b.getRaster();
        // ...
        raster.SetPixel(x,y, new Color(red,green,blue,alpha));
        // ...
        ImageIO.write(image, "png", new File(filename));
    }
}

5.1.8 Python

The relevant library is pillow. To install, do pip install pillow or pip3 install pillow from the command line on your machine (not in your Makefile because we’ve pre-installed pillow, but not pip, on the testing server). Do not use the ImageDraw module.

An example
from PIL import Image
# ...
image = Image.new("RGBA", (width, height), (0,0,0,0))
# ...
image.im.putpixel((x,y), (red, green, blue, alpha))
# ...
image.save(filename)

5.1.9 Rust

The relevant library is std::io::RgbaImage and std::io:Rgba.

An example
use std::io::{BufRead, BufReader};
fn main() {
    // ...
    let mut image = RgbaImage::from_pixel(width, height, Rgba([0, 0, 0, 0]));
    // ...
    image.put_pixel(x, y, Rgba([red, green, blue, alpha]));
    // ...
    image.save(filename).unwrap();
}

5.1.10 Typescript

The relevant library is jimp.

An example
import Jimp from 'jimp';
const run = async () => {
    const image = new Jimp(width, height);
    // ...
    // make rgba a 32-bit integer of the form 0xRRGGBBAA
    image.setPixelColor(rgba, x, y);
    // ...
    await image.writeAsync(filename);
};
run().catch(console.error);

5.1.11 Other

In earlier semesters we’ve also had students use D, Kotlin, and Scala. In principle I am open to any language I can get to work on the testing server; just let me know what you’d like.

5.2 Makefile

You will submit your code and a Makefile. We will run your Makefile on a Linux server. It is your responsibility to see that the Makefile and your code work in a Linux environment. Following are minimal Makefiles you might use as a baseline. We recommend using make’s more advanced operations (separate .o targets, pattern rules, variables, etc) if you understand them.

Note that Makefile indentation must be in tabs, not spaces and that the file name must be exactly Makefile with no filename extension.

C Makefile
.PHONEY: build, run

build: program

run: program
    ./program $(file)

program: main.c lodepng.c
    clang -O3 -I. main.c lodepng.c -o program
C++ Makefile
.PHONEY: build, run

build: program

run: program
    ./program $(file)

program: main.cpp
    clang++ -O3 -I. main.cpp -o program

Note: some libraries may require more flags. For example, CImg.h requires -lpng and -lm.

C# Makefile
.PHONEY: build, run

build: program.exe

run: program.exe
    mono program.exe $(file)

program.exe: program.cs
    mcs -r:System.Drawing -pkg:gtk-sharp-2.0 program.cs
Dart Makefile
.PHONEY: build, run

build:
    pub get

run:
    dart program.dart $(file)
Go Makefile
.PHONEY: build, run

build:
    go build -o bin/main main.go

run:
    ./bin/main $(file)
Haskell Makefile
.PHONEY: build, run

build:

run:
    runghc program.hs $(file)
Java Makefile
.PHONEY: build, run

build: Program.class

run: Program.class
    java Program $(file)

Program.class: Program.java
    javac Program.java
Java Makefile with packages

If your code is in a package (as many IDEs will make it), you’ll need a slightly more involved Makefile.

If the .java files contain package some.name; then they will be in some path path/prefix/some/name. Put the Makefile (and implemented.txt) in path/prefix/ as follows:

SRC = $(wildcard some/name/*.java)
CLS = $(SRC:.java=.class)

.PHONEY: build, run

build: $(CLS)
    javac $(SRC)

run: $(CLS)
    java some.name.ClassWithMain $(file)

the SRC uses the wildcard function to find all Java files in that one package; if you use several packages, you will need to add additional wildcards there.

Python Makefile
.PHONEY: build, run

build:

run:
    python program.py $(file)
Rust Makefile
.PHONEY: build, run

build:
    cargo build

run:
    cargo run $(file)
Typescript Makefile
.PHONEY: build, run

build:
    npm install && npm run build

run:
    npm start $(file)

5.3 Image Comparison

Think your output looks like the reference output? Maybe so, but like is a fuzzy idea and sometimes we’ll hold you to a higher standard of similarity than your eye is trained to see.

Enter ImageMagick (or its less popular but faster clone, GraphicsMagick). ImageMagick is a collection of versatile command-line tools for manipulating images, including many forms of image comparison.

During grading, we use ImageMagick to create comparison images containing

  • your image, student.png
  • the image we expect, ref.png
  • an image that highlights any differences between them in red, ae.png
  • an image that shows all color differences, rawdiff.png
  • an image that magnifies color differences, diff.png

We create those images and stick them together into one large image to look at during grading using the following commands:

compare -fuzz 2% student.png ref.png ae.png
composite student.png ref.png -alpha off -compose difference rawdiff.png
convert rawdiff.png -level 0%,8% diff.png
convert +append ref.png student.png ae.png rawdiff.png diff.png look_at_this.png

Note that some tasks are permissive of some differences while others will be more strict. For example, consider this image:

It is similar to its reference image, but the outline is not the same (a few missing pixels along the left edge, touching in the middle) and there’s visible horizontal banding in the color error. If this input was meant to test shading and overlap, those would result in lost points. It it were meant to measure positioning and perspective, they’d not be a concern.