Assignment 5

COMP112 2020 Tri 1: Assignment 5

  • Due 21 May 10am

Goals

This assignment will give you experience in writing programs that read some data from a file and do something with the data. It will also give you more practice using conditionals and loops.

Preparation

Download the zip file for assignment 5 and extract it to the COMP112-2020T1-Assig5 folder in your home folder. It should contain templates for the Java program you are to complete, along with data files for the ImageDisplayer program. Read through the whole assignment to see what you need to do, and watch the demonstration video of the assignment.

Watch the video that explains the model answers to assignment 4, and make sure you understand all the components of the programs. Also, go over the code examples from the lectures that used files.

Summary

  • ImageDisplayer: (90%)
    right Write a program to display gradients and ppm image files (the simplest possible image format).
  • Understanding Java. (10%)
    right Answer some questions about programming in Java.

To Submit

  • Your version of ImageDisplayer.java
  • Your UnderstandingJava.txt file

Overview

The assignment involves a program (ImageDisplayer) that displays different kinds of images on the graphics pane. The images are colour gradients and pnm format files (the simplest possible image format).

Image Displayer

Digital images are ubiquitous, and we happily expect our computers to show images in all sorts of contexts. The core information in a digital image is the color of each individual pixel of the image. A colour can be represented by a triple of three numbers, one for each of the red, green, and blue components of the colour, so we can represent an image by having three numbers for each pixel. Typically, the three components are represented by integers from 0 to 255. (0,0,0) is black, (255,0,0) is strong red, and (255,255,255) is white.

A colour gradient is an image (or part of an image) in which the colour changes smoothly from one colour at one end to another colour at the other end.

You will write methods to display a simple linear gradient, changing smoothly from one colour along the top to the other colour along the bottom, and a 2D gradient which changes smoothly from one corner to the other, but changing the red component down the image and the blue component across the image.

When an image is stored in a file, the values of the pixels must be encoded into the file. Exactly how we do that depends on the format of the image file. A very simple format will just list all the numbers, so that a 100x100 pixel image (which is pretty small) would have 30,000 numbers: three numbers for each of the 10,000 pixels. However, this will end up being a rather large file, and most of the common image file formats (such as jpg, gif, and png) have more complex formats in which the information is encoded and compressed to save space and remove redundancy. Turning the contents of these compressed image files into a coloured image on the screen can be a very complicated process, but the files can be very much smaller than when using the simple format.

You will write a program to display image files in the plain ppm format ("portable pixel map") - one of the simplest possible (and least efficient) file formats.

For the Core, you will write a method that displays the images stored in simplified ppm files - all the images are exactly 200 rows by 200 columns, and have nothing but the color values for each pixel in turn, starting at the top left of the image, and working from left to right along each row and working row by row down the image.

For the Completion, you will write a method that displays proper plain ppm files, which start with a header containing four pieces of information describing the image, followed by the color values for each pixel in turn,

Core

Gradient
Complete the drawVertGradient method. Its parameters are the component values (red, green, blue) for two colours.

It must draw a square image made of coloured pixels (1x1 squares) that is a smooth gradient from the first colour along the top row to the second colour along the bottom row.

On each row in between the top and the bottom, the colour will need to make a small step towards the target colour.

The image should be GRAD_ROWS x GRAD_COLS in size, and each pixel should be 1x1.

Note: The steps in the red, green, and blue components will be different, and depend on the number of row and the difference between the top colour and the bottom colour. The step may be negative.

Hint: be careful with your division - use doubles!

Here are some examples of some linear gradients:
(0,0,0) to (255, 0, 0)
black to red
(0,255,0) to (0, 0, 255)
green to blue
(255,100,0) -> (0, 255, 255)
orange to teal
black-to-red.png green-to-blue.png orange-to-teal.png

200x200 Image files

Complete the render200x200Image method. It is passed the name of a file consisting of 120,000 integers - a red, green and blue component for each of the 200x200 pixels. It should open a Scanner on the file, and read all the integers, constructing the colors for each pixel, and drawing them as a 1x1 rectangle on the graphics pane.

Hints:
  • Use a nested for loop to do this: an outer for loop that goes through each row, and an inner for loop that goes along the columns of the current row.
  • For each pixel, read three integers and construct a new Color value.

Note: There are four 200x200 image files (a bee, a flower, a fly and a rose) that you can test your program on.

Completion

Gradient
Complete the draw2DGradient method.

As for the drawVertGradient method, the parameters are the component values (red, green, blue) for two colours and the image should be GRAD_ROWS x GRAD_COLS in size, and each pixel should be 1x1.

It must draw a square image made of coloured pixels (1x1 squares) that is a smooth 2-dimensional gradient from the first colour at the top-left corner to the second colour at the bottom-right corner. All three components need to change together for a 2D gradient:

  • The red component should change smoothly from top to bottom;
  • The blue component should change smoothly from left to right;
  • The green component should change smoothly diagonally from top-left to bottom-right.

The first three examples 2D gradients have only one component that changes; the last three have all three components changing at once:

(0,0,0) to (255, 0, 0)
black to red
(0,0,0) to (0, 0, 255)
black to blue
(0, 0, 0) to (0, 255, 0)
black to green
black-to-red.png black-to-blue.png black-to-green.png

(0,0,0) to (255, 255, 255)
black to white
(0,110,255) to (255,170,0)
lightblue to orange
(240, 10, 250) to (80, 200, 40)
magenta to midgreen
black-to-white.png lightblue-to-orange.png magenta-to-midgreen.png

PPM Files.

The format of the image file used in the core (render200x200Image) was not a useful image file format, because it only handled a fixed size image. Plain ppm files are the simplest "real" image file formats. They start with a header section describing the image, and then are followed by all the pixel values, as in the image files above.

The first token in a ppm file is "magic number" - the token P3, which specifies that the file is a plain ppm file. The next two tokens are the number of columns and the number of rows in the image. The last token of the header is the "colour depth" (the maximum value of all the pixel values.

You are to complete the renderPPMImage method which is passed the name of a file. It should open a Scanner on the file, and then read the first four tokens from the Scanner (ie, the header).
  • If the first token is not P3, it should print a message and exit the method.
  • It should store the number of columns and rows in variables.
  • It can throw away the colour depth.

It should then use a nested for loop to read and draw all the pixel values (as in the core).

For example, here is a tiny plain ppm image file ( image-tiny.ppm, which is a small part of the back of the bee extracted from the compl-bee.ppm file):

P3
12 5
255
200 182 163 215 198 177 130 116 93 37 28 9 31 22 7 
81 67 38 83 71 42 6 5 6 0 0 0 57 68 60 97 112 104 
97 92 76 202 186 165 97 82 60 32 25 5 38 30 13 103 90 
63 158 140 97 58 49 25 43 42 17 107 104 74 127 140 
113 95 102 79 66 58 41 71 57 37 41 30 7 82 71 41 111 
95 64 174 157 120 115 101 63 49 43 12 67 65 30 126 
124 74 133 136 97 88 87 62 98 93 54 78 63 37 108 93 
62 121 104 69 135 120 88 190 172 139 36 30 15 1 0 0 
16 17 9 64 77 58 50 57 39 7 2 0 105 106 64 121 103 71 
117 100 67 159 144 113 212 197 171 161 146 114 0 0 0 
0 0 0 37 48 32 72 88 68 24 26 19 12 12 9 74 72 49 
The first token is the string P3 which identifies the format of this image file as a plain ppm file.

The second and third tokens are 12 and 5, specifying the width (number of columns of pixels) and the height (the number of rows of pixels) of the image.

The fourth token specifies the maximum possible value of the colour values for the pixels. In this case, the colour values for the red, green, and blue components each go between 0 and 255.

The rest of the file is the color values of the pixels, with three numbers for each pixel. Therefore there will be 36 numbers for the first row (up to 97 92 76 at the beginning of the 3rd line), 36 numbers for the second row, etc. The line breaks don't mean anything special, (although the format specifies that the numbers should be broken into lines with no more than 70 characters).

There are several ppm images of different sizes that you may test your method on:

Here are png forms of the same images so you can see what they look like.

bee: cats:
image-bee.png image-cats.png
 
crane: flower:
image-crane.png image-flower.png
 
fly: rose:
image-fly.png image-rose.png

Challenge

The real plain ppm format is actually a little more complex than described above:
  • the colour depth doesn't have to be 255: if it were only 7, then each colour value would be between 0 and 7 and there would only be 8x8x8 = 512 possible colors. (0,0,0) would still correspond to black, but (7, 7, 7) would correspond to white.
  • The header part of the file can contain comments that are ignored by the renderer. A comment starts with #, and lasts to the end of the line, and it can be placed anywhere between the first token (P3)and the colour depth, for example:

     P3 # a plain ppm file
     # this is a part of a bee
     12 # width of image
     5  # height of image
     # the image was extracted from a larger image
     # converted from a free gif file from the web
     255
     200 182 163 215 198 177 130 116 93 37 28 9 31 22 7 81 
     67 38 83 71 42 6 5 6 0 .....
[Actually, the comments are even more flexible than this, but this is good enough]

Furthermore, plain ppm is just one in a family of related formats (pnm files):
  • Plain pgm ("portable grey map") is for grey scale images.
    pgm files start with the token "P2", and each pixel is represented by a single number (between 0 and the colour depth) specifying the grey level of the pixel.
  • Plain pbm ("portable bit map") is for black and white images.
    pgm files start with the token "P1", and each pixel is represented by a 1 (black) or 0 (white) single number. The colour depth is 1.

It is easy to make animated ppm files (like animated gifs) by making the ppm file contain a sequence of images right after each other, representing an "animated" image, which should be rendered by drawing the first image, pausing briefly, then drawing the second image in its place, and repeating until all the images have been drawn. Each image will have the four "header" tokens, then the pixel color values.

Complete the renderPNM method to render ppm, pgm and pbm files with all these different features.

There are lots of pnm files to test your method on. They all begin with "x-". They are attached below:

Understanding Java

Answer the following questions in the UnderstandingJava.txt file, and make sure you submit it.

  1. When do we use this to call a method and when do we use UI to call a method?
  2. We have learned many different data types and they can be categorised into two groups: primitives and Objects. List the primitive data types and some examples of the Object data types we have learned so far.