PROJECT: FomoFoto

1. Overview of the Project

My team and I were tasked with enhancing a basic command line AddressBook for our Software Engineering project. We decided to morph the project into an image editor.

FomoFoto is a robust yet simple image-editing tool. Users interact with the application through worded commands from their keyboard, and receive visual response through the displayed image on the application. Unlike other heavy image editors, FomoFoto has a gentle learning curve as it removes clutter by providing the more essential features. The features and implementations are well documented in guides for users and developers respectively. In addition, FomoFoto is well-maintained with high reliability and code quality as it is covered by rigorous tests and checks.


2. Summary of Contributions

This section provides a summary of my coding, documentation and miscellaneous contributions to FomoFoto, our team project.

  • Major Enhancement: I added image manipulation commands.

    • What it does: This feature allows users to manipulate images on a physical level, such as rotating, cropping and resizing the target image. Users key in keywords like rotate, crop and resize to activate the respective commands.

    • Justification: This feature improves the product significantly because it is an essential feature of an image editor. Users might want to rotate their images because the orientation is wrong or crop out certain unwanted parts in the image. Should the image be too large, users can resize it as well.

    • Highlights: This enhancement works with all other commands, including the image filters that were implemented by my teammate. An in-depth analysis of the commands were done before we decided to make use of a library. The implementation was challenging because there was a need to coordinate between the image filter commands and the commands here, because the two libraries returned different objects. There was a need to ensure that the Image class and CurrentEdit class could accept changes from both libraries.

    • Credits: ImgScalr Library

  • Major Enhancement: I added a watermark command.

    • What it does: This feature allows users to add a watermark (any words or numbers) with the © sign to the target image. Users key in the keyword wm to activate this command.

    • Justification: This feature improves the product significantly because it helps users such as professional photographers protect their images after editing it on our image editor. The copyright watermark prevents unauthorised third parties from using the images for their own private purposes.

    • Highlights: It was especially challenging to integrate this command with all other commands acting on the same image, because a watermark should only be added once to an image. This implied that we had to ensure that the Image class contains a flag that indicates the presence of a watermark on the image, and this flag had to be constantly updated accurately in every command that can possibly add or remove a watermark from an image, which includes the WaterMark command, SetPreset command and undo/redo commands.

  • Minor Enhancement: I added a feature where the Image returns a file type (e.g. JPEG, PNG), because the methods to edit images differ according to the file type.

  • Code Contributed: List of commits, Project Code Dashboard

  • Other Contributions:

    • Project Management:

      • Ensured that we were on task with our project documentation by allocating roles and ensuring consistency with our words and diagrams. (User Guide and Developer Guide)

      • Integrated Codacy to our GitHub repository: #249

    • Enhancements to Existing Features:

      • Wrote tests for image manipulation and watermark commands to increase coverage: #188, #208, #222

    • Documentation:

      • Did the Developer Guide architecture and sequence diagrams for the overall application: #84, #118

      • Did the Developer Guide documentation for image manipulation and watermark commands: #59, #123, #229, #234

      • Did the User Guide documentation for image manipulation and watermark commands: #61, #120, #221

      • Did the the User Guide documentation for the overall walk through: #65

    • Community:

    • Tools:

      • Integrated a third party library (ImgScalr) to the project (#26)

3. Contributions to the User Guide

The sections below are my contributions to the User Guide. They showcase my ability to write documentation targeting end-users.

3.1 Crop an Image : crop


Crops an image based on the given top left-hand corner coordinates, width and height of final cropped image wanted.
Format: crop X_POINTCOORD Y_POINTCOORD WIDTH HEIGHT

The point coordinates must be separated by a space each.

Example:

  • crop 2 3 500 500

3.2 Rotate an Image : rotate


Rotates the image by a given degree provided by the user. Only 90, 180 or 270 degrees of rotation is allowed.
Format: rotate ANGLE

Example:

  • rotate 90

3.3 WaterMark : wm


Adds a watermark to the image with a © at the start of the input message. Messages can contain spaces. Should the message be too long to fit within the width of the image, the message will be cut off.
Format: wm MESSAGE

Each image can only have one watermark.

Example:

  • wm FomoFoto

  • wm Done By FomoFoto

3.4 Other contributions


  • A Walk Through of the Image Editor

  • Resize Command

4. Contributions to the Developer Guide

The sections below are my contributions to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

4.1 How the Architecture Components Interact with Each Other


The Sequence Diagrams below show how the components interact with each other for the scenarios where the user keys in the commands import C:\Users\XXX\Desktop\Pictures and rotate 90.

SDforImportcommand
Figure 1. Component interactions for import C:\Users\XXX\Desktop\Pictures command
SDforRotatecommand
Figure 2. Component interactions for rotate 90 command

Current Implementation

This segment involves manipulating the physical image itself, such as rotating, cropping and resizing. The implementations of these features are facilitated by ImgScalr Library, which is an external API Library that helps process the target image.

The manipulation feature is facilitated by CurrentEdit. It uses the following operations:

  • CurrentEdit#tempImageDoNotExist() - Returns true if tempImage in CurrentEdit is null. tempImage is null only if open command is never called.

  • CurrentEdit#getTempImage - Retrieves the temporary image tempImage which stores the filepath of the temporary image, its history of edits [List<Command>] and its metadata.

  • CurrentEdit#updateTempImage - Replaces the temporary image in temp directory with the newly edited image and updates the tempImage instance in the class.

  • CurrentEdit#addCommand - Adds this command to the edit history List<Command> in tempImage for the undo/redo command.

  • CurrentEdit#displayTempImage() - Displays the temporary image stored in the temp directory.

This manipulation feature mainly consists of:

  • RotateCommand: Allows users to rotate images by specifying a degree (90, 180 or 270 only).

  • CropCommand: Allows users to crop images by specifying the coordinates of the top left corner, the width and the height of the desired cropped image.

  • ResizeCommand: Allows users to resize images to the desired width and height.

The following describes the main operations and processes for each command stated above.

Rotate Command

This command allows the user to rotate the targeted image by specifying a degree (90, 180 or 270 only). Upon receiving an input degree from the user, the degree will be checked for its validity and will throw an error if the degree is not within the specified range. The command will then be added to the List<Commands> belonging to tempImage found in currentEdit which saves the editing history of the targeted image.

The diagram below illustrates how the Rotate Command works:

RotateCommandSequenceDiagram
Figure 3. Sequence Diagram for Rotate Command

Given below is an example usage scenario and how the command should behave at each step. (This applies to the other image manipulation commands as well.):

Step 1. When the user runs an open command to edit an image, it invokes a method which creates an instance of an Image that stores the file path of the image, its history of edits List<Command> and its metadata. This Image object is saved under the variable name tempImage in currentEdit for editing.

Step 2. When the user enters the command (e.g. rotate 90), the entered command is parsed and the command will be executed.

If an invalid command is provided, a reminder of how to use the command will be given to the user and no command will be executed.

Step 3. During execution, the execute method in the RotateCommand class first invokes currentEdit#tempImageDoNotExist to check if an image is opened. If no image is opened, it will throw an error message to inform the user to open an image for editing. Else, the execute method will invoke currentEdit#getTempImage() to get the tempImage from CurrentEdit.

Step 4. Upon retrieving the tempImage, the execute method in RotateCommand gets a BufferedImage instance from tempImage.The method then calls the external library ImgScalr's class rotate and passes the BufferedImage object in.

Step 5. A BufferedImage object is returned from the external library and currentEdit#updateTempImage() is invoked to save the newly edited BufferedImage and replace the previous image in the file path of the tempImage.

Step 6. The execute method then checks if the boolean isNewCommand is true. If it is true, it indicates that the command is a new Rotate command called directly from the user and not through an undo/redo command which triggers Step 7 in the line below. Otherwise, the command’s execution ends in this step.

Step 7. isNewCommand is set to false to signal that this command is not a new rotate command if it is executed again through the undo/redo command. currentEdit#addCommand(this) is invoked to add this command to the List<Command> in tempImage for the undo/redo function and currentEdit#displayTempImage() is used to display the edited image on the graphical user interface.

Design Considerations
  • Images that are still being edited have to be stored and edited in a temporary directory first due to our undo/redo implementation.

4.3 WaterMark Feature


This command allows the user to add a watermark to their image. Upon receiving an input message from the user, the message will be checked for its validity and will throw an error if the message is empty or contains only spaces. Messages can contain words and numbers separated by spaces. The command will then be added to the List<Commands> belonging to tempImage found in currentEdit which saves the editing history of the targeted image.

Each image can only have 1 watermark. If the user would like to edit the watermark, the user can remove the watermark by using the Undo function and then add a new watermark.

Current Implementation

The manipulation feature is facilitated by CurrentEdit and Image. It uses the following operations:

  • CurrentEdit#tempImageDoNotExist() - Returns true if tempImage in CurrentEdit is null. tempImage is null only if open command is never called.

  • CurrentEdit#getTempImage - Retrieves the temporary image tempImage which stores the filepath of the temporary image in the temp directory, its history of edits [List<Command>] and its metadata.

  • CurrentEdit#updateTempImage - Replaces the temporary image in temp directory with the newly edited image and updates the tempImage instance in the class.

  • CurrentEdit#addCommand - Adds this command to the edit history List<Command> in tempImage for the undo/redo command.

  • CurrentEdit#displayTempImage() - Displays the temporary image stored in the temp directory.

  • Image#hasWaterMark() - Checks if the Image already has a watermark.

  • Image#setWaterMark(boolean) - Sets the Image object’s hasWaterMark field accordingly - if the Image object has or does not have a watermark.

The diagram below illustrates how the WaterMark Command works:

WaterMarkCommandSequenceDiagram
Figure 4. Sequence Diagram for WaterMark Command

Given below is an example usage scenario and how the command should behave at each step:

Step 1. When the user runs an open command to edit an image, it invokes a method which creates an instance of an Image that stores the file path of the image, its history of edits List<Command> and its metadata. This Image object is saved under the variable name tempImage in currentEdit for editing.

Step 2. When the user enters the command (e.g. wm FomoFoto), the entered command is parsed and the command will be executed.

If an invalid command is provided, a reminder of how to use the command will be given to the user and no command will be executed.

Step 3. During execution, the execute method in the WaterMarkCommand class first invokes currentEdit#tempImageDoNotExist to check if an image is opened. If no image is opened, it will throw an error message to inform the user to open an image for editing. Else, the execute method will invoke currentEdit#getTempImage() to get the tempImage from CurrentEdit.

Step 4. Upon retrieving the tempImage, the execute method in the WaterMarkCommand gets the BufferedImage instance of the tempImage object and copies the BufferedImage object to a temporary BufferedImage object with the same width, height and image type. The execute method then initialises the necessary graphics properties using the Graphics2D Java class. The message is centralised and overlays the temporary BufferedImage object.

Step 5. The execute method then checks for a few conditions listed below:

  1. isNewCommand: Checks if the WaterMark command is from an input by the user, from an undo/redo command or from a SetPreset command.

  2. isPreset: Checks if the WaterMark command is from a preset.

  3. tempImage#hasWaterMark(): Checks if the tempImage already has a watermark.

Step 6. According to the above 3 conditions, the following combinations will result in different actions:

  1. isNewCommand = True and tempImage#hasWaterMark() = False:

    • Invokes tempImage#setWaterMark(True) to indicate that the image has a watermark.

    • Invokes currentEdit#updateTempImage() to save the newly edited BufferedImage and replace the previous image in the filepath of the tempImage.

    • Sets isNewCommand to false to signal that this command is not a new rotate command if it is executed again through the undo/redo function.

    • Invokes currentEdit#addCommand(this) to add this command to the List<Command> in tempImage for the undo/redo function and currentEdit#displayTempImage() is used to display the edited image on the graphical user interface.

  2. isNewCommand = True and tempImage#hasWaterMark() = True:

    • Throws an exception as there is already a watermark on tempImage.

  3. isNewCommand = False and isPreset = False:

    • Indicates that it is an undo/redo function.

    • Invokes tempImage#setWaterMark(true) to indicate that the image has a watermark.

    • Invokes currentEdit#updateTempImage() to save the newly edited BufferedImage and replace the previous image in the filepath of the tempImage.

  4. isNewCommand = False and isPreset = True and tempImage#hasWaterMark() = False:

    • Indicates that a preset is added to an image with no watermark.

    • Invokes tempImage#setWaterMark(true) to indicate that the image has a watermark.

    • Invokes currentEdit#updateTempImage() to save the newly edited BufferedImage and replace the previous image in the filepath of the tempImage.

  5. Anything else:

    • Throws an exception as there is already a watermark on tempImage.

4.4 Other Contributions


  • Crop Command

  • Resize Command

  • Architecture Diagrams