The goal of this warmup is to ensure you can make programs that read text files and produce PNG files in the language of your choice and have those run successfully on the submission server. In theory this is a mix of copying code we provide, copying examples from your language’s official documentation, and writing some basic file processing code such as you did in CS1 and CS2. In practice it will likely mean working around a few setup challenges.
You will submit at least three files:
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
.
Code, in whatever language you prefer, including any necessary
support libraries that make build
cannot obtain.
A file named implemented.txt
that contains the test
cases you expect to pass, one per line.
You do not need to submit the example input or output files:
just your code, the Makefile
, and
implemented.txt
.
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
Makefile
directly, we use thatMakefile
, we use thatMakefile
in that directory, we use
thatThe 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.
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
position 2 20 200 150 90 40 290
ignore this line since "ignore" is not a keyword you know
likewise ignore this line, which also starts with an unknown keyword
color 4 255 127 0 255 0 127 255 127 0 0 0 255
drawPixels 2
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.
Each homework will define its own set of keywords. For this warmup MP, these are:
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.
Provides a buffer of pixel locations, giving 2 coordinates (x and y) for each.
You may assume that every x has a y, that 0 \le x_i \lt \text{\it width}, and that 0 \le y_i \lt \text{\it height}.
Provides a buffer of colors, giving 4 coordinates (red, green, blue, alpha) for each.
You may assume that every red has a corresponding green, blue, and alpha and that all values are between 0 and 255, inclusive.
Draws n pixels from the most-recently-provided buffers.
You may assume that this only comes after a position
and
color
keyword and that n is a positive integer
that does not exceed the number of position and color coordinates
provided in them.
You should be able to pass both of the following. To get credit for
them, also submit a file names implemented.txt
with the
following in it:
simple
messy
All test input files, with their reference output files, can be downloaded as a zip
This file’s contents are
png 5 8 simple.png
position 2 0 1 1 2 2 3 3 4 4 5 3 3 2 4
color 4 255 255 255 255 127 255 255 255 170 170 255 255 255 255 255 255 200 120 3 255 0 0 0 255 0 0 0 255
drawPixels 7
and it should produce 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:
This file’s contents are
png 5 8 messy1.png
color 4 127 255 255 127 170 170 255 255 200 120 3 255 0 0 0 191 255 255 255 255
nothing here 23425 56
position 2 1 2 2 3 4 5 2 4 1 1 3 6
drawPixels 4
and it should produce this image file:
This file’s contents are
png 8 8 messy2.png
color 4 127 255 255 127 170 170 255 255 200 120 3 255 0 0 0 191 255 255 255 255
position 2 1 2 2 3 4 5 0 0 2 4
drawPixels 5
position 2 0 0 0 7 7 7 7 0 6 1
drawPixels 5
and it should produce this image file:
This warmup 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.
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.
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.
#include "lodepng.h"
int main(int argc, char *argv[]){
/* ... */
unsigned char *image = calloc(width * height * 4, sizeof(unsigned char));
/* ... */
[((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;
image/* ... */
(filename, image, width, height);
lodepng_encode32_file(image);
free}
#include "miniz.h"
int main(int argc, char *argv[]){
/* ... */
unsigned char *image = calloc(width * height * 4, sizeof(unsigned char));
/* ... */
[((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;
image/* ... */
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");
(data, 1, size, out_file);
fwrite(out_file);
fclose(data);
free(image);
free}
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.
#define cimg_use_png
#define cimg_display 0
#include "CImg.h"
int main(int argc, char *argv[]){
/* ... */
::CImg<unsigned char> image(width, height, 1, 4);
cimg_library/* ... */
(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());
image}
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
.
using System.Drawing;
class ClassName {
static void Main(string[] args) {
// ...
= new Bitmap(width, height, PixelFormat.Format32bppArgb);
Bitmap img // ...
.SetPixel(x,y, Color.FromARGB(red,green,blue,alpha));
img// ...
.save(filename);
img}
}
The relevant package is image/image.dart
.
import 'package:image/image.dart';
import 'dart:io';
void main(List<String> args) {
// ...
= Image(width, height);
image // ...
.setPixelRgba(x, y, red, green, blue);
image// ...
.writeBytesAsSync(encodePng(image));
File(filename) }
The relevant package is image
.
import (
"image/png"
"image/color"
"io"
)
func main() {
// ...
:= image.NewRGBA(image.Rect(0, 0, width, height))
img // ...
.Set(x, y, color.RGBA{red, green, blue, alpha})
img// ...
, err := os.Create(img.filename)
w.Encode(w, img)
png}
Students have reported success using Phll
import qualified Data.ByteString.Lazy as B
import Phll
= flip map [0..width] $
pixels -> flip map [0..height] $
\x -> (red, green, blue, alpha)
\y
= B.writeFile filename $ B.pack $ Phll.png_rgba pixels main
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
.
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();
// ...
.SetPixel(x,y, new Color(red,green,blue,alpha));
raster// ...
ImageIO.write(image, "png", new File(filename));
}
}
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.
from PIL import Image
# ...
= Image.new("RGBA", (width, height), (0,0,0,0))
image # ...
image.im.putpixel((x,y), (red, green, blue, alpha))# ...
image.save(filename)
The relevant library is image::RgbaImage
and
image:Rgba
.
use image::{Rgba,RgbaImage};
use std::io::{BufRead, BufReader};
fn main() {
// ...
let mut image = RgbaImage::from_pixel(width, height, Rgba([0, 0, 0, 0]));
// ...
.put_pixel(x, y, Rgba([red, green, blue, alpha]));
image// ...
.save(filename).unwrap();
image}
The relevant library is jimp
.
import Jimp from 'jimp';
const run = async () => {
const image = new Jimp(width, height);
// ...
// make rgba a 32-bit integer of the form 0xRRGGBBAA
.setPixelColor(rgba, x, y);
image// ...
await image.writeAsync(filename);
;
}run().catch(console.error);
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.
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.
.PHONEY: build, run
build: program
run: program
$(file)
./program
program: main.c lodepng.c
clang -O3 -I. main.c lodepng.c -o program
.PHONEY: build, run
build: program
run: program
$(file)
./program
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
.
.PHONEY: build, run
build: program.exe
run: program.exe
$(file)
mono program.exe
program.exe: program.cs
mcs -r:System.Drawing -pkg:gtk-sharp-2.0 program.cs
.PHONEY: build, run
build:
pub get
run:
$(file) dart program.dart
.PHONEY: build, run
build:
go build -o bin/main main.go
run:
$(file) ./bin/main
.PHONEY: build, run
build:
run:
$(file) runghc program.hs
.PHONEY: build, run
build: Program.class
run: Program.class
$(file)
java Program
Program.class: Program.java
javac Program.java
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)
$(SRC)
javac
run: $(CLS)
$(file) java some.name.ClassWithMain
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.
.PHONEY: build, run
build:
run:
$(file) python program.py
.PHONEY: build, run
build:
cargo build
run:
$(file) cargo run
.PHONEY: build, run
build:
npm install && npm run build
run:
$(file) npm start
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
student.png
ref.png
ae.png
rawdiff.png
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.