Project 3 - RGBFileFormat

To better support development (read: debugging) of SeamCarver and other graphics algorithms, a more human-readable file format would be helpful.

Objectives

  • Read and write text files representing 2D arrays.
  • Throw exceptions with specific error messages.

Introduction

For this project you will implement saving and loading images in a format that supports debugging graphics algorithms by being more human-readable.

When testing applications like SeamCarver, creating small images can be helpful. The “RGB File Format” is a simple way to create your own images in plain text files.

See the .txt files below for examples of the RGB file format. Each line of the file is a tab-delimited sequence of pixels in the format (r, g, b) where r, g, and b are integers in the range 0 to 255. Space characters before/after the parentheses and commas are optional (all spaces should be ignored).

Provided Image Files

Provided Source Files

Part A: RGBFileFormat.java

Your first task is to implement the RGBFileFormat class in the UML diagram below. Each of its methods throws a FileNotFoundException when applicable. In addition, the load() method throws RGBException (with a specific error message and location) if the file format is incorrect.

classDiagram
class RGBFileFormat
RGBFileFormat: +load(path String)$ Picture
RGBFileFormat: +save(path String, picture Picture)$

load()

This method reads an RGB file and returns a corresponding Picture object. You will need to determine the width and height of the picture based on the file contents. Read the file line by line, construct a Color object for each pixel, and set the corresponding (x, y) of the picture.

Your load method should be robust enough to read a “sloppier” version of a picture file than your own very clean save method would produce. Specifically, (as mentioned in the introduction) your load method should ignore space characters . Therefore, you should be able to load files when the numbers have not been padded to a width of 3 characters (which is required of your own save method).

You may assume that the number of tab characters (\t) between pixels on the same line is exactly 1 and that the line will not have any other tab characters.

Your code must be able to handle the following error conditions:

  • If the file is empty, throw an RGBException with the message "empty file".
  • If any line is blank, throw an RGBException with the message "blank line".
  • If the file has an inconsistent number of columns, throw an RGBException with the message "ragged".
  • If a pixel does not begin with '(' or end with ')', throw an RGBException with the message "parens".
  • If a pixel does not have exactly two commas, throw an RGBException with the message "commas".
  • If a number does not parse as an integer, throw an RGBException with the message "number".
  • If a number is not in the range 0 to 255, throw an RGBException with the message "range".

When throwing an RGBException, the x and y values should correspond to the location of the corresponding pixel. In the case of empty file, blank line, and ragged, the x value should be 0 and the y value should be the relevant location. In the case of an empty file, y should be 0.

save()

This method writes a Picture object to an RGB file. Each line of the file represents a row of the picture. Pixels on the same line should be separated by tab characters and use the format (r, g, b) with exactly one space after each comma. As shown in 3x4.txt and 6.5.txt, each integer should be 3 characters wide (with leading spaces), so that all pixels are 15 characters wide. Each line of the file should end with a newline character.

Unit Testing

RGBFileFormatTest is provided as a starting point to test your load and save methods. You should create additional .bad files of your own to test the other error conditions. These files and other test code will not be submitted; feel free to make any changes you like.

What unit tests should you write? Consider:

  1. What is the simplest example you can think of where you expect a certain outcome for some part of the code (a method or a small number of methods)?
    • Write a unit test that asserts your expectation is the result of calling those methods.
  2. What is an example where the same code should perhaps behave differently?
    • Maybe different “branch” or condition has been satisfied and so the behavior should be different according to the specs?
    • Maybe it’s an erroneous situation and the expectation is that a certain exception is thrown?
  3. Might there be unexpected interactions between different methods?

Part B: Convert.java

Implement an application that uses your RGBFileFormat class to convert images to/from other formats. This application will be run from the command line as follows:

  • To convert a png/jpg file to RGB format:
java Convert filename.png filename.txt
  • To convert an RGB file to png/jpg format:
java Convert filename.txt filename.png

As shown above, the main method must have exactly two arguments: the source filename and the destination filename. If the application is not run with two arguments, main should print the following message to System.err and exit with status code 1:

Usage: java Convert SRC DST

Exactly one of the command-line arguments must end with ".txt"; this argument is the file in RGB format. If neither argument ends with ".txt", main should print the following message to System.err and exit with status code 1:

One of the images must end with .txt

The other argument must end with either ".jpg" or ".png"; this argument is the image to convert to/from. If any other extension is found, main should print the following message to System.err and exit with status code 1:

Unsupported file format: FILENAME

where FILENAME is the invalid argument.

After validating the command-line arguments, the main method should perform the requested conversion. For example, use RGBFileFormat to load the image and Picture to save the image, or vice versa.

Submission and Grading

For this project, you will submit each part separately on Gradescope:

  • For Part A, submit only RGBFileFormat.java. It will be autograded with a longer version of RGBFileFormatTest that is more comprehensive than the provided code.
  • For Part B, submit only Convert.java. It will be autograded with the solution for RGBFileFormat. That way, you can get full credit for Part B even if you don’t finish Part A.

You may submit up to 10 times without penalty. Additional submissions (if any) will receive a penalty of 3 points each. You are strongly encouraged to test your code offline before submitting.

As in previous assignments, your code must pass Checkstyle to receive any points. Submissions that don’t pass Checkstyle count toward the limit of 10 submissions. You are strongly encouraged to run Checkstyle offline before submitting.

Project Part Weight
RGBFileFormat (via autograder) 60%
Convert (via autograder) 20%
Code Review 20%
Last modified April 30, 2022: practice coding exam (a2ce8c8)