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
andresize
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 orcrop
out certain unwanted parts in the image. Should the image be too large, users canresize
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 andCurrentEdit
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 theWaterMark
command,SetPreset
command andundo/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:
-
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
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
.
import C:\Users\XXX\Desktop\Pictures
commandrotate 90
commandCurrent 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()
- Returnstrue
iftempImage
inCurrentEdit
is null.tempImage
is null only ifopen
command is never called. -
CurrentEdit#getTempImage
- Retrieves the temporary imagetempImage
which stores the filepath of the temporary image, its history of edits [List<Command>
] and its metadata. -
CurrentEdit#updateTempImage
- Replaces the temporary image intemp
directory with the newly edited image and updates thetempImage
instance in the class. -
CurrentEdit#addCommand
- Adds this command to the edit historyList<Command>
intempImage
for theundo/redo
command. -
CurrentEdit#displayTempImage()
- Displays the temporary image stored in thetemp
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:
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()
- Returnstrue
iftempImage
inCurrentEdit
is null.tempImage
is null only ifopen
command is never called. -
CurrentEdit#getTempImage
- Retrieves the temporary imagetempImage
which stores the filepath of the temporary image in thetemp
directory, its history of edits [List<Command>
] and its metadata. -
CurrentEdit#updateTempImage
- Replaces the temporary image intemp
directory with the newly edited image and updates thetempImage
instance in the class. -
CurrentEdit#addCommand
- Adds this command to the edit historyList<Command>
intempImage
for theundo/redo
command. -
CurrentEdit#displayTempImage()
- Displays the temporary image stored in thetemp
directory. -
Image#hasWaterMark()
- Checks if theImage
already has a watermark. -
Image#setWaterMark(boolean)
- Sets theImage
object’shasWaterMark
field accordingly - if theImage
object has or does not have a watermark.
The diagram below illustrates how the WaterMark Command works:
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:
-
isNewCommand
: Checks if theWaterMark
command is from an input by the user, from anundo/redo
command or from aSetPreset
command. -
isPreset
: Checks if theWaterMark
command is from a preset. -
tempImage#hasWaterMark()
: Checks if thetempImage
already has a watermark.
Step 6. According to the above 3 conditions, the following combinations will result in different actions:
-
isNewCommand
=True
andtempImage#hasWaterMark()
=False
:-
Invokes
tempImage#setWaterMark(True)
to indicate that the image has a watermark. -
Invokes
currentEdit#updateTempImage()
to save the newly editedBufferedImage
and replace the previous image in the filepath of thetempImage
. -
Sets
isNewCommand
to false to signal that this command is not a new rotate command if it is executed again through theundo/redo
function. -
Invokes
currentEdit#addCommand(this)
to add this command to theList<Command>
intempImage
for theundo/redo
function andcurrentEdit#displayTempImage()
is used to display the edited image on the graphical user interface.
-
-
isNewCommand
=True
andtempImage#hasWaterMark()
=True
:-
Throws an exception as there is already a watermark on
tempImage
.
-
-
isNewCommand
=False
andisPreset
=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 editedBufferedImage
and replace the previous image in the filepath of thetempImage
.
-
-
isNewCommand
=False
andisPreset
=True
andtempImage#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 editedBufferedImage
and replace the previous image in the filepath of thetempImage
.
-
-
Anything else:
-
Throws an exception as there is already a watermark on
tempImage
.
-