Lab 1: Image Displayer 3000

Overview

In this assignment you will read and write several file formats representing black and white images.

Pre-Lab

As part of the pre-lab activity you will see how to use GitHub Classroom to start your lab assignment and submit it when you are finished. When watching the video below, consider the following.

  • How to start a new project from your GitHub repository
  • How to commit changes to your local repo
  • How to push changes from your local repo to GitHub

The project is cloned from the GitHub repo to your local machine at the three-minute mark in the video. You will likely need to authorize IntelliJ to access your GitHub account. Hopefully the process is simple enough, but you can see screenshots for what to do in Figures 13-16 of this page.

What Must Be Done by the Pre-lab Due Date?

Assignment

Your program must allow users to read and write multiple black and white image formats specified in this assignment. Whenever an image is read from a file, your program must display it to the console using Unicode characters. Your program will have a simple menu with three options:

  1. Load image — the user is asked for a filename and the program loads and displays the image on the console. The program should provide a user-friendly error message if the file was not found or if the file was invalid.
  2. Save image — the user is asked to select the desired format for the saved image and the filename. If the file already exists, the user should be asked whether they would like to overwrite the existing file. If the user responds affirmatively, the file should be overwritten. Otherwise, nothing should be written, and the menu should be displayed again. User-friend error messages should be presented when appropriate.
  3. Exit

File Formats

Your program must support two file formats:

  1. 0/1 Text Format
  2. Block Unicode Character Text Format

0/1 Text File Format

This is the first of two text file formats. Here the image is represented with 0s and 1s. A zero represents a white pixel and one represents a black pixel. A \( 4 \times 4 \) image with black dots in all the corners and a diagonal line going from the lower left to the upper right is represented by the following:

1001
0010
0100
1001

When files in this format are written, the file extension must be .txt.

Block Unicode Character Text File Format

The Unicode Consortium is a standards body for the internationalization of software and services. The Block Elements document specifies the block characters to be used in this assignment. You will use the block characters that divide the character into four quadrants:

\u2588\u0020\u2580\u2584\u258C\u2590\u259A\u259E
\u2596\u2597\u2598\u259D\u259F\u2599\u259C\u259B

A \( 4 \times 4 \) image with black dots in all the corners and a diagonal line going from the lower left to the upper right is represented by the following string written to the file:

\u2598\u259E\n\u259E\u2597

When files in this format are written, the file extension must be .txt.

When reading a .txt file, your program will need to determine which of the two text formats the file is using.

Recall that each time an image is read, you must display it to the console. This can be accomplished by sending the same string that would be written to the file to the console instead.

Software Design

You are responsible for the overall design of your software solution; however, it must incorporate a PixelCluster enum and Image class that conform to the following requirements.

PixelCluster Enum

You must create an enum to represent the 16 unique Unicode Block elements. The enum should have a single char attribute code that stores the Unicode value for each enumeration. In addition, it should provide the following public methods:

Image Class

You must implement the Image class with the following public methods:

You must also have as an instance variable a 2-dimensional int array called pixels to store the pixels in their appropriate configuration.

2D Arrays

Recall that a 2-dimensional array in Java is actually an array of arrays. This can be thought of as a grid, where the first index is the row (the y-axis) and the second index is the column (the x-axis). When instantiating a 2D array, you may either allocate the memory for an empty array or assign values directly to the new array. To create an empty 2x2 int array, for example, you would declare the variable and assign the lengths of both dimensions.

int[][] array = new int[2][2];

If you had specific values you wanted to populate an array with, you could declare them as an array literal where you separate each value with a comma:

int[] array = new int[] {1, 0};

For a 2D array, the same applies. Remember, it is an array of arrays, so you would need to declare each array literal, separated by commas. So if you wanted a 2D array that contains the following values:

10
01

you would initialize it like so:

int[][] array = new int[][] {
    {1, 0},
    {0, 1}
};

Switch Expressions

A switch expression in Java is the modern form of a switch statement that has numerous improvements. The basic switch statement in Java is a slightly cleaner way of writing if/else statements when there are more than a few cases. For example, if you had a different result depending on which letter was stored in a String word, you may have a switch statement that looks like this:

char c;
switch(word) {
    case "A": c = 'a';
              break;
    case "B": c = 'b';
              break;
    .
    .
    .
    default: throw new IlegalArgumentException("Not a letter!");
}
return  c;

A switch statement will start at the top and compare its parameter with each case. When it matches, it will execute that code. One of the downsides to a switch statement is that, unlike an if/else block, it will continue to compare with every possible case even if it has already matched with one. This results in having to end each block of code for each case with a break statement to get out of the switch block.

The switch expression, on the other hand, functions like an if/else block, so once a match is found, it executes that case's block of code and exits the switch block. Here is what the above code would look like as a switch expression:

char c;
switch(word) {
    case "A" -> c = 'a';
    case "B" -> c = 'b';
    .
    .
    .
    default -> throw new IlegalArgumentException("Not a letter!");
}
return  c;

One other useful feature of a switch expression is to return the contents of whatever case block matches the parameter, so the above code could be written like this:

return switch(word) {
    case "A" -> 'a';
    case "B" -> 'b';
    .
    .
    .
    default: throw new IlegalArgumentException("Not a letter!");
};

Exception Handling

If any problems are encountered with reading the input files or writing the output file, the program should display a useful error message to the console and terminate gracefully. The program should not crash or display any exceptions.

Just For Fun

Because IntelliJ's console has a default line spacing of 1.2, you will see blank space between the rows of the images displayed. You can adjust IntelliJ's settings to remove this space.
  1. Select File > Settings
  2. Select Editor > Color Scheme > Console Font
  3. Check "Use console font instead of the default"
  4. Change "Line height" to "0.9"

Ambitious students may wish to:

Acknowledgement

This laboratory assignment, developed by Dr. Chris Taylor and Prof. Sean Jones.

See your professor's instructions for details on submission guidelines and due dates.