[stegosuite] 01/02: New upstream version 0.8.0

Tobias Ilte menosmalo-guest at moszumanska.debian.org
Fri Oct 13 11:35:23 UTC 2017


This is an automated email from the git hooks/post-receive script.

menosmalo-guest pushed a commit to branch master
in repository stegosuite.

commit c05f4b42390b93da021ffa82521867099432d2c5
Author: Tobias Ilte <tobias at stegosuite.org>
Date:   Fri Oct 13 13:21:54 2017 +0200

    New upstream version 0.8.0
---
 .gitignore                                         |   4 +
 CHANGELOG                                          |   4 +
 README.md                                          |   2 +-
 misc/stegosuite.appdata.xml                        |  45 ++
 pom.xml                                            |  42 +-
 src/main/java/org/stegosuite/Stegosuite.java       |  10 +-
 .../EmbeddingProgressObserver.java                 |  12 +-
 .../application/StegosuitePresenter.java           | 170 +++++++
 .../org/stegosuite/application/StegosuiteUI.java   |  21 +
 .../block_processing/BlockContainer.java           |   5 +
 .../block_processing/BlockProcessor.java           |  69 +++
 .../block_processing/FileBlockContainer.java       |  32 ++
 .../block_processing/MessageBlockContainer.java    |  21 +
 .../gui => application}/embedding/Embedding.java   |  13 +-
 .../embedding/EmbeddingDoneListener.java}          |   6 +-
 .../embedding/EmbeddingFactory.java                |  21 +-
 .../embedding/ExtractingDoneListener.java}         |   6 +-
 .../embedding/MyBMPLsbMultiColorChannel.java       |  14 +-
 .../embedding/MyGIFShuffle.java                    |  14 +-
 .../embedding/MyGIFSortedColorTable.java           |  14 +-
 .../{ui/gui => application}/embedding/MyJPGF5.java |  14 +-
 .../embedding/MyPNGLsbMultiColorChannel.java}      |  38 +-
 .../org/stegosuite/image/embedding/Visualizer.java |  17 +-
 .../embedding/bmp/BMPLsbMultiColorChannel.java     |  19 +-
 .../bmp/filter/BMPPointFilterHomogeneous.java      |  20 +-
 .../embedding/bmp/filter/BMPPointFilterNone.java   |   8 +-
 .../stegosuite/image/embedding/gif/GIFShuffle.java |  27 +-
 .../image/embedding/gif/GIFSortedColorTable.java   |  56 +--
 .../gif/filter/GIFPointFilterHomogeneous.java      |  24 +-
 .../embedding/gif/filter/GIFPointFilterNone.java   |   8 +-
 .../org/stegosuite/image/embedding/jpg/JPGF5.java  |  30 +-
 .../embedding/jpg/filter/JPGPointFilterNone.java   |   8 +-
 .../PNGLsbMultiColorChannel.java}                  |  49 +-
 .../filter/PNGPointFilterHomogeneous.java}         |  58 ++-
 .../embedding/png/filter/PNGPointFilterNone.java   |  26 +
 .../image/embedding/point/PointFilter.java         |   6 +-
 .../image/embedding/point/PointGenerator.java      |  16 +-
 .../java/org/stegosuite/image/format/BMPImage.java |   8 +-
 .../java/org/stegosuite/image/format/GIFImage.java |  31 +-
 .../org/stegosuite/image/format/ImageFormat.java   |  53 +-
 .../java/org/stegosuite/image/format/JPGImage.java |   4 +-
 .../java/org/stegosuite/image/format/PNGImage.java |  29 ++
 .../org/stegosuite/image/jpgtemp/james/Jpeg.java   |   3 +-
 .../image/jpgtemp/james/JpegEncoder.java           |  73 +--
 .../stegosuite/image/jpgtemp/james/JpegInfo.java   |   3 +-
 .../org/stegosuite/image/jpgtemp/net/f5/Embed.java |   7 +-
 .../stegosuite/image/jpgtemp/net/f5/Extract.java   |  50 +-
 .../stegosuite/image/jpgtemp/net/f5/image/Bmp.java |   3 +-
 .../image/jpgtemp/net/f5/ortega/HuffmanDecode.java |   3 +-
 .../java/org/stegosuite/model/payload/Payload.java |  38 +-
 .../stegosuite/model/payload/PayloadEmbedder.java  |  23 +-
 .../stegosuite/model/payload/PayloadExtractor.java |  14 +-
 .../org/stegosuite/model/payload/block/Block.java  |   5 +-
 .../stegosuite/model/payload/block/FileBlock.java  |  43 +-
 .../model/payload/block/MessageBlock.java          |   9 +-
 src/main/java/org/stegosuite/ui/cli/Arguments.java |  71 ---
 src/main/java/org/stegosuite/ui/cli/Cli.java       | 229 ++++-----
 src/main/java/org/stegosuite/ui/cli/CliParser.java |  71 +++
 src/main/java/org/stegosuite/ui/gui/EmbedUi.java   | 534 +++++++++------------
 src/main/java/org/stegosuite/ui/gui/Gui.java       |  66 ++-
 .../java/org/stegosuite/ui/gui/GuiComponents.java  | 268 ++++++++---
 .../java/org/stegosuite/ui/gui/ImageContainer.java |  12 +-
 .../java/org/stegosuite/ui/gui/ImageUtils.java     |   2 +-
 .../org/stegosuite/{image => }/util/ByteUtils.java |   2 +-
 .../stegosuite/{image => }/util/ColorDistance.java |  10 +-
 .../stegosuite/{image => }/util/ColorUtils.java    |  24 +-
 .../{image => }/util/CompressionUtils.java         |   4 +-
 .../stegosuite/{image => }/util/CryptoUtils.java   |  15 +-
 .../org/stegosuite/{image => }/util/FileUtils.java |  13 +-
 .../{image => }/util/ImageSwtAwtConverter.java     |  12 +-
 .../{image => }/util/InterchangeablePair.java      |   2 +-
 .../stegosuite/{image => }/util/RgbChannel.java    |   4 +-
 src/main/resources/logback.xml                     |   2 +-
 src/test/java/org/stegosuite/CliTest.java          |  58 +++
 .../org/stegosuite/EmbeddingAndExtractingTest.java | 126 +++++
 src/test/java/org/stegosuite/PresenterTest.java    | 188 ++++++++
 src/test/java/org/stegosuite/Resources.java        |  20 +
 .../org/stegosuite/image/format/GIFImageTest.java  |  27 +-
 src/test/resources/file.txt                        |   1 +
 src/test/resources/landscape.jpg                   | Bin 0 -> 61547 bytes
 src/test/resources/landscape_embed_ok.jpg          | Bin 0 -> 33062 bytes
 src/test/resources/snow_embed_ok.bmp               | Bin 0 -> 810054 bytes
 src/test/resources/sunflower_embed_ok.gif          | Bin 0 -> 740843 bytes
 83 files changed, 1994 insertions(+), 1115 deletions(-)

diff --git a/.gitignore b/.gitignore
index defa0c2..f45c01f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,7 @@
 *.idx
 *.fls
 *.synctex.gz
+
+## Exclude files from IDEA
+.idea/
+stegosuite.iml
diff --git a/CHANGELOG b/CHANGELOG
index 01fbd3f..da952c2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+0.8.0
+	- added png support
+	- added command-line interface
+
 0.7.3
 	- updated .desktop file
 	- internal changes
diff --git a/README.md b/README.md
index bf33ad7..11e7a67 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Stegosuite is a free steganography tool written in Java.
 With Stegosuite you can hide information in image files.
 
 ## Features
-* BMP, GIF and JPG supported
+* BMP, GIF, JPG and PNG supported
 * AES encryption of embedded data
 * Automatic avoidance of homogenous areas (only embed data in noisy areas)
 * Embed text messages and multiple files of any type
diff --git a/misc/stegosuite.appdata.xml b/misc/stegosuite.appdata.xml
new file mode 100644
index 0000000..00a3640
--- /dev/null
+++ b/misc/stegosuite.appdata.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="desktop-application">
+​  <id>org.stegosuite</id>
+​  <metadata_license>CC-BY-SA-4.0</metadata_license>
+​  <project_license>GPL-3.0</project_license>
+​  <name>Stegosuite</name>
+​  <summary>Steganography tool to hide information in image files</summary>
+​
+​  <description>
+​    <p>
+​      Stegosuite is a graphical steganography tool to easily hide information in image files. It allows the embedding of text messages and multiple files of any type. In addition, the embedded data is encrypted using AES. Currently supported file types are BMP, GIF, JPG and PNG.
+​    </p>   
+​  </description>
+​
+​  <launchable type="desktop-id">stegosuite.desktop</launchable>
+​
+​  <screenshots>
+​    <screenshot type="default">
+​      <caption>Embedding of 2 files</caption>
+​      <image>https://screenshots.debian.net/screenshot/stegosuite</image>
+​    </screenshot>
+​  </screenshots>
+​
+​  <url type="homepage">https://dev.stegosuite.org/stegosuite/stegosuite</url>
+​  
+  <provides>
+​    <binary>stegosuite</binary>
+​  </provides>
+
+  <categories>
+​    <category>Graphics</category>
+​    <category>2DGraphics</category>
+    <category>RasterGraphics</category>
+​  </categories>
+​
+  <mimetypes>
+​    <mimetype>image/gif</mimetype>
+​    <mimetype>image/jpeg</mimetype>
+​    <mimetype>image/bmp</mimetype>
+    <mimetype>image/png</mimetype>
+​  </mimetypes>
+
+  <update_contact>tobias_AT_stegosuite.org</update_contact>
+
+​</component>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 4fe3037..1b073d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>org.stegosuite</groupId>
 	<artifactId>stegosuite</artifactId>
-	<version>0.7.3</version>
+	<version>0.8.0</version>
 	<name>stegosuite</name>
 	<description>A free steganography tool to hide information in image files.</description>
 	<url>https://dev.stegosuite.org/stegosuite/stegosuite</url>
@@ -32,7 +32,7 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>
-				<version>3.2</version>
+				<version>3.6.2</version>
 				<configuration>
 					<source>1.8</source>
 					<target>1.8</target>
@@ -41,14 +41,14 @@
 			<plugin>
 				<groupId>org.codehaus.mojo</groupId>
 				<artifactId>exec-maven-plugin</artifactId>
-				<version>1.4.0</version>
+				<version>1.6.0</version>
 				<configuration>
 					<mainClass>org.stegosuite.Stegosuite</mainClass>
 				</configuration>
 			</plugin>
 			<plugin>
 				<artifactId>maven-assembly-plugin</artifactId>
-				<version>2.6</version>
+				<version>3.1.0</version>
 				<configuration>
 					<descriptorRefs>
 						<descriptorRef>jar-with-dependencies</descriptorRef>
@@ -74,7 +74,7 @@
 			</plugin>
 			<plugin>
 				<artifactId>maven-jar-plugin</artifactId>
-				<version>2.6</version>
+				<version>3.0.2</version>
 				<executions>
 					<execution>
 						<id>default-jar</id>
@@ -91,37 +91,35 @@
 		<dependency>
 			<groupId>${swt.groupId}</groupId>
 			<artifactId>${swt.artifactId}</artifactId>
-			<version>4.5.2</version>
+			<version>4.6</version>
 		</dependency>
-
-		<!-- <dependency>
-			<groupId>com.beust</groupId>
-			<artifactId>jcommander</artifactId>
-			<version>1.48</version>
-		</dependency> -->
-
 		<dependency>
 			<groupId>org.slf4j</groupId>
 			<artifactId>slf4j-api</artifactId>
-			<version>1.7.20</version>
+			<version>1.7.25</version>
 		</dependency>
 		<dependency>
 			<groupId>ch.qos.logback</groupId>
 			<artifactId>logback-classic</artifactId>
-			<version>1.1.6</version>
+			<version>1.1.9</version>
 		</dependency>
 		<dependency>
 			<groupId>ch.qos.logback</groupId>
 			<artifactId>logback-core</artifactId>
-			<version>1.1.6</version>
+			<version>1.1.9</version>
 		</dependency>
-		<dependency>
-			<groupId>org.testng</groupId>
-			<artifactId>testng</artifactId>
-			<version>6.9.4</version>
-			<scope>test</scope>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+    		<groupId>commons-cli</groupId>
+    		<artifactId>commons-cli</artifactId>
+    		<version>1.4</version>
 		</dependency>
-	</dependencies>
+    </dependencies>
 
 	<profiles>
 		<profile>
diff --git a/src/main/java/org/stegosuite/Stegosuite.java b/src/main/java/org/stegosuite/Stegosuite.java
index 7ba62d9..4dbd055 100644
--- a/src/main/java/org/stegosuite/Stegosuite.java
+++ b/src/main/java/org/stegosuite/Stegosuite.java
@@ -1,10 +1,10 @@
 package org.stegosuite;
 
-import org.slf4j.LoggerFactory;
-import org.stegosuite.ui.gui.Gui;
-
 import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.ui.cli.CliParser;
+import org.stegosuite.ui.gui.Gui;
 
 public class Stegosuite {
 
@@ -13,8 +13,8 @@ public class Stegosuite {
 		root.setLevel(Level.INFO);
 		if (args.length == 0) {
 			new Gui(null);
-			// } else if (args[0].startsWith("-")) {
-			// new Cli(args);
+		} else if (args[0].startsWith("-")) {
+			new CliParser(args).parse();
 		} else {
 			new Gui(args[0]);
 		}
diff --git a/src/main/java/org/stegosuite/ui/gui/EmbeddingProgressObserver.java b/src/main/java/org/stegosuite/application/EmbeddingProgressObserver.java
similarity index 80%
rename from src/main/java/org/stegosuite/ui/gui/EmbeddingProgressObserver.java
rename to src/main/java/org/stegosuite/application/EmbeddingProgressObserver.java
index d77f58e..73e8d82 100644
--- a/src/main/java/org/stegosuite/ui/gui/EmbeddingProgressObserver.java
+++ b/src/main/java/org/stegosuite/application/EmbeddingProgressObserver.java
@@ -1,12 +1,12 @@
-package org.stegosuite.ui.gui;
-
-import java.util.Observable;
-import java.util.Observer;
+package org.stegosuite.application;
 
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.ProgressBar;
 import org.stegosuite.image.embedding.EmbeddingProgress;
 
+import java.util.Observable;
+import java.util.Observer;
+
 public class EmbeddingProgressObserver
 		implements Observer {
 
@@ -19,8 +19,6 @@ public class EmbeddingProgressObserver
 
 	@Override
 	public void update(Observable o, Object arg) {
-		Display.getDefault().asyncExec(() -> {
-			progressbar.setSelection((int) arg);
-		});
+		Display.getDefault().asyncExec(() -> progressbar.setSelection((int) arg));
 	}
 }
diff --git a/src/main/java/org/stegosuite/application/StegosuitePresenter.java b/src/main/java/org/stegosuite/application/StegosuitePresenter.java
new file mode 100644
index 0000000..4243636
--- /dev/null
+++ b/src/main/java/org/stegosuite/application/StegosuitePresenter.java
@@ -0,0 +1,170 @@
+package org.stegosuite.application;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.stegosuite.application.block_processing.BlockProcessor;
+import org.stegosuite.application.embedding.Embedding;
+import org.stegosuite.application.embedding.EmbeddingDoneListener;
+import org.stegosuite.application.embedding.EmbeddingFactory;
+import org.stegosuite.application.embedding.ExtractingDoneListener;
+import org.stegosuite.image.embedding.EmbeddingMethod;
+import org.stegosuite.image.embedding.EmbeddingProgress;
+import org.stegosuite.image.embedding.Visualizer;
+import org.stegosuite.image.format.ImageFormat;
+import org.stegosuite.model.exception.SteganoEmbedException;
+import org.stegosuite.model.exception.SteganoExtractException;
+import org.stegosuite.model.exception.SteganoImageException;
+import org.stegosuite.model.payload.Payload;
+import org.stegosuite.model.payload.block.FileBlock;
+import org.stegosuite.model.payload.block.MessageBlock;
+import org.stegosuite.util.FileUtils;
+
+import java.io.File;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class StegosuitePresenter implements EmbeddingDoneListener, ExtractingDoneListener {
+	private ImageFormat image;
+	private Payload payload;
+	private Embedding embedding;
+	private StegosuiteUI ui;
+	private EmbeddingProgress progressListener = new EmbeddingProgress();
+
+	public StegosuitePresenter(ImageFormat image, StegosuiteUI ui) {
+		this.image = image;
+		this.ui = ui;
+		this.payload = new Payload();
+		this.embedding = embeddingFor(image);
+	}
+
+	public void setPointFilter(int value) {
+		this.embedding.setPointFilter(value);
+	}
+	
+	private Embedding embeddingFor(ImageFormat image) {
+		Embedding embedding = EmbeddingFactory.getEmbedding(image);
+		embedding.setPointFilter(1);
+		return embedding;
+	}
+
+	public void embedNotifying(EmbeddingProgress progressListener, String password) {
+		this.progressListener = progressListener;
+
+		embed(password);
+	}
+
+	public void embed(String password) {
+		try {
+			setPassword(password);
+			embedData();
+		} catch (SteganoEmbedException e) {
+			ui.showEmbeddingError(e);
+		}
+	}
+
+	public void addMessageToPayload(String message) {
+		payload.addBlock(new MessageBlock(message));
+	}
+	
+	public void addFileToPayload(String filename) {
+		payload.addBlock(new FileBlock(filename));
+		notifyAddedFile(filename);
+	}
+	
+	private void setPassword(String password) {
+		payload.setPassword(password);
+	}
+	
+	private void embedData() throws SteganoEmbedException {
+		embedding.embed(payload, progressListener, this);
+	}
+
+	@Override
+	public void onEmbeddingDone(EmbeddingMethod<? extends ImageFormat> embeddingMethod, ImageFormat embeddedImage) {
+		save(embeddedImage);
+		notifyEmbeddingCompleted(embeddingMethod, embeddedImage);
+	}
+
+	private void save(ImageFormat embeddedImage) {
+		try {
+			String outputPath = getOutputPathFor(embeddedImage);
+			embeddedImage.save(new File(outputPath));
+		} catch (SteganoImageException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void notifyEmbeddingCompleted(EmbeddingMethod<? extends ImageFormat> embeddingMethod, ImageFormat embeddedImage) {
+		Visualizer visualizer = embeddingMethod.getVisualizer();
+		String outputPath = getOutputPathFor(embeddedImage);
+
+		ui.embeddingCompleted(embeddedImage, outputPath, visualizer);
+	}
+
+	private String getOutputPathFor(ImageFormat embeddedImage) {
+		return FileUtils.addFileNameSuffix(embeddedImage.getFilePath(), "_embed");
+	}
+
+	public void extractNotifying(EmbeddingProgress progressListener, String password) {
+		this.progressListener = progressListener;
+
+		extractUsing(password);
+	}
+
+	public void extractUsing(String password) {
+		payload.setPassword(password);
+		try {
+			embedding.extract(payload, progressListener, this);
+		} catch (SteganoExtractException e) {
+			ui.showExtractingError(e);
+		}
+	}
+
+	@Override
+	public void onExtractingDone(EmbeddingMethod<? extends ImageFormat> embeddingMethod) {
+		BlockProcessor blockProcessor = new BlockProcessor(payload, image.getFilePath()).processBlocks();
+		notifyAddedFiles(blockProcessor.getFilePaths());
+		notifyExtractingCompleted(embeddingMethod,
+				blockProcessor.getExtractedMessage(),
+				blockProcessor.getFilePaths()
+		);
+	}
+
+	private void notifyAddedFiles(List<String> addedFilePaths) {
+		addedFilePaths.forEach(path -> notifyAddedFile(path));
+	}
+
+	private void notifyExtractingCompleted(EmbeddingMethod<? extends ImageFormat> embeddingMethod, String extractedMessage, List<String> filePaths) {
+		Visualizer visualizer = embeddingMethod.getVisualizer();
+		ImageData imageData = image.getImageData();
+		ui.extractingCompleted(extractedMessage, filePaths, visualizer, imageData);
+	}
+
+	private void notifyAddedFile(String filePath) {
+		String filename = FileUtils.getFileName(filePath);
+		String extension = FileUtils.getFileExtension(filePath);
+		long fileSize = FileUtils.getFileSize(filePath);
+
+		ui.addPayloadFile(filename, extension, fileSize);
+	}
+
+	public List<FileBlock> payloadFileBlocksWithFilename(String filename) {
+		return payload.getBlocks().stream()
+                .filter(block -> block.hasIdentifier(FileBlock.IDENTIFIER))
+                .map(block -> (FileBlock) block)
+                .filter(fileBlock -> fileBlock.hasPath(filename))
+                .collect(Collectors.toList());
+	}
+
+	public void removeBlock(FileBlock fileBlock) {
+		payload.removeBlock(fileBlock);
+	}
+
+	public void clearPayload() {
+		this.payload = new Payload();
+	}
+
+	public int getEmbeddingCapacity() {
+		return embedding.getCapacity();
+	}
+
+}
diff --git a/src/main/java/org/stegosuite/application/StegosuiteUI.java b/src/main/java/org/stegosuite/application/StegosuiteUI.java
new file mode 100644
index 0000000..2c11515
--- /dev/null
+++ b/src/main/java/org/stegosuite/application/StegosuiteUI.java
@@ -0,0 +1,21 @@
+package org.stegosuite.application;
+
+import java.util.List;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.stegosuite.image.embedding.Visualizer;
+import org.stegosuite.image.format.ImageFormat;
+import org.stegosuite.model.exception.SteganoEmbedException;
+import org.stegosuite.model.exception.SteganoExtractException;
+
+public interface StegosuiteUI {
+	void showEmbeddingError(SteganoEmbedException e);
+
+	void showExtractingError(SteganoExtractException e);
+
+	void extractingCompleted(String extractedMessage, List<String> filePaths, Visualizer visualizer, ImageData imageData);
+
+	void embeddingCompleted(ImageFormat embeddedImage, String outputPath, Visualizer visualizer);
+
+	void addPayloadFile(String filename, String extension, long fileSize);
+}
diff --git a/src/main/java/org/stegosuite/application/block_processing/BlockContainer.java b/src/main/java/org/stegosuite/application/block_processing/BlockContainer.java
new file mode 100644
index 0000000..6dbc8dc
--- /dev/null
+++ b/src/main/java/org/stegosuite/application/block_processing/BlockContainer.java
@@ -0,0 +1,5 @@
+package org.stegosuite.application.block_processing;
+
+interface BlockContainer {
+	void processBlock();
+}
diff --git a/src/main/java/org/stegosuite/application/block_processing/BlockProcessor.java b/src/main/java/org/stegosuite/application/block_processing/BlockProcessor.java
new file mode 100644
index 0000000..024b8a7
--- /dev/null
+++ b/src/main/java/org/stegosuite/application/block_processing/BlockProcessor.java
@@ -0,0 +1,69 @@
+package org.stegosuite.application.block_processing;
+
+import org.stegosuite.model.payload.Payload;
+import org.stegosuite.model.payload.block.Block;
+import org.stegosuite.model.payload.block.FileBlock;
+import org.stegosuite.model.payload.block.MessageBlock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BlockProcessor {
+
+	private final Payload payload;
+	private final List<String> messages = new ArrayList<>();
+	private final List<String> filePaths = new ArrayList<>();
+	private final String baseFilePath;
+
+	public BlockProcessor(Payload payload, String baseFilePath) {
+		this.baseFilePath = baseFilePath;
+		this.payload = payload;
+	}
+
+	public BlockProcessor processBlocks() {
+		payload.getBlocks().stream().map(block -> toBlockContainer(block)).forEach(BlockContainer::processBlock);
+
+		return this;
+	}
+
+	private BlockContainer toBlockContainer(Block aBlock) {
+		switch (aBlock.getIdentifier()) {
+			case FileBlock.IDENTIFIER:
+				return new FileBlockContainer((FileBlock) aBlock, filePaths, baseFilePath);
+			case MessageBlock.IDENTIFIER:
+				return new MessageBlockContainer((MessageBlock) aBlock, messages);
+			default:
+				return null;
+		}
+	}
+
+	public List<String> getFilePaths() {
+		return filePaths;
+	}
+
+	public String getExtractedMessage() {
+		if (messages.isEmpty()) {
+			return null;
+		} else {
+			return messages.get(0);
+		}
+
+	}
+
+	public String getStatusText() {
+		String status = "Extracting completed.";
+		if (thereWereProcessedFiles()) {
+			status += " Extracted file saved to " + lastFilePath();
+		}
+		return status;
+	}
+
+	private boolean thereWereProcessedFiles() {
+		return !filePaths.isEmpty();
+	}
+
+	private String lastFilePath() {
+		return filePaths.get(filePaths.size() - 1);
+	}
+
+}
diff --git a/src/main/java/org/stegosuite/application/block_processing/FileBlockContainer.java b/src/main/java/org/stegosuite/application/block_processing/FileBlockContainer.java
new file mode 100644
index 0000000..712192d
--- /dev/null
+++ b/src/main/java/org/stegosuite/application/block_processing/FileBlockContainer.java
@@ -0,0 +1,32 @@
+package org.stegosuite.application.block_processing;
+
+import org.stegosuite.model.payload.block.FileBlock;
+import org.stegosuite.util.FileUtils;
+
+import java.util.List;
+
+class FileBlockContainer implements BlockContainer {
+	private final FileBlock block;
+	private final List<String> processedFiles;
+	private final String baseFilePath;
+
+	public FileBlockContainer(FileBlock block, List<String> processedFiles, String baseFilePath) {
+		this.block = block;
+		this.processedFiles = processedFiles;
+		this.baseFilePath = baseFilePath;
+	}
+
+	@Override
+	public void processBlock() {
+		String extractionPath = getExtractionPath();
+		block.saveFileTo(extractionPath);
+		processedFiles.add(extractionPath);
+	}
+
+	private String getExtractionPath() {
+		return FileUtils.changeFileName(
+				baseFilePath,
+				block.getFileName()
+		);
+	}
+}
diff --git a/src/main/java/org/stegosuite/application/block_processing/MessageBlockContainer.java b/src/main/java/org/stegosuite/application/block_processing/MessageBlockContainer.java
new file mode 100644
index 0000000..1acbaa7
--- /dev/null
+++ b/src/main/java/org/stegosuite/application/block_processing/MessageBlockContainer.java
@@ -0,0 +1,21 @@
+package org.stegosuite.application.block_processing;
+
+import org.stegosuite.model.payload.block.MessageBlock;
+
+import java.util.List;
+
+class MessageBlockContainer implements BlockContainer {
+	private MessageBlock block;
+	private List<String> processedMessages;
+
+	public MessageBlockContainer(MessageBlock block, List<String> messages) {
+		this.block = block;
+		this.processedMessages = messages;
+	}
+
+	@Override
+	public void processBlock() {
+		String message = block.getMessage();
+		processedMessages.add(message);
+	}
+}
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/Embedding.java b/src/main/java/org/stegosuite/application/embedding/Embedding.java
similarity index 69%
rename from src/main/java/org/stegosuite/ui/gui/embedding/Embedding.java
rename to src/main/java/org/stegosuite/application/embedding/Embedding.java
index bfaca21..743c63f 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/Embedding.java
+++ b/src/main/java/org/stegosuite/application/embedding/Embedding.java
@@ -1,4 +1,4 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingProgress;
 import org.stegosuite.model.exception.SteganoEmbedException;
@@ -10,25 +10,24 @@ import org.stegosuite.model.payload.Payload;
  */
 public abstract class Embedding {
 
-	public abstract void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneEvent event)
+	public abstract void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneListener listener)
 			throws SteganoEmbedException;
 
-	public void embed(Payload payload, EmbeddingDoneEvent event)
+	public void embed(Payload payload, EmbeddingDoneListener event)
 			throws SteganoEmbedException {
 		this.embed(payload, null, event);
 	}
 
-	public abstract void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneEvent event)
+	public abstract void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneListener listener)
 			throws SteganoExtractException;
 
-	public void extract(Payload payload, ExtractingDoneEvent event)
+	public void extract(Payload payload, ExtractingDoneListener listener)
 			throws SteganoExtractException {
-		this.extract(payload, null, event);
+		this.extract(payload, null, listener);
 	}
 
 	public abstract int getCapacity();
 
 	public abstract void setPointFilter(int a);
 
-	public abstract Embedding getEmbeddingMethod();
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/EmbeddingDoneEvent.java b/src/main/java/org/stegosuite/application/embedding/EmbeddingDoneListener.java
similarity index 57%
rename from src/main/java/org/stegosuite/ui/gui/embedding/EmbeddingDoneEvent.java
rename to src/main/java/org/stegosuite/application/embedding/EmbeddingDoneListener.java
index 9e5a2a6..013111e 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/EmbeddingDoneEvent.java
+++ b/src/main/java/org/stegosuite/application/embedding/EmbeddingDoneListener.java
@@ -1,9 +1,9 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.format.ImageFormat;
 
-public interface EmbeddingDoneEvent {
+public interface EmbeddingDoneListener {
 
 	/**
 	 * Is fired by the abstraction layer to let the Gui process the embedding result
@@ -11,6 +11,6 @@ public interface EmbeddingDoneEvent {
 	 * @param embeddingMethod
 	 * @param embeddedImage
 	 */
-	void handleEvent(EmbeddingMethod<? extends ImageFormat> embeddingMethod, ImageFormat embeddedImage);
+	void onEmbeddingDone(EmbeddingMethod<? extends ImageFormat> embeddingMethod, ImageFormat embeddedImage);
 
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/EmbeddingFactory.java b/src/main/java/org/stegosuite/application/embedding/EmbeddingFactory.java
similarity index 82%
rename from src/main/java/org/stegosuite/ui/gui/embedding/EmbeddingFactory.java
rename to src/main/java/org/stegosuite/application/embedding/EmbeddingFactory.java
index 2bc7776..d8f4a72 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/EmbeddingFactory.java
+++ b/src/main/java/org/stegosuite/application/embedding/EmbeddingFactory.java
@@ -1,17 +1,14 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.image.format.*;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.format.BMPImage;
-import org.stegosuite.image.format.GIFImage;
-import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.format.JPGImage;
-
 /**
  * Creates instances of implementations of Embedding.java
  */
@@ -20,7 +17,8 @@ public enum EmbeddingFactory {
 	GIF_SORTED(MyGIFSortedColorTable.class), //
 	GIFSHUFFLE(MyGIFShuffle.class), //
 	BMP(MyBMPLsbMultiColorChannel.class), //
-	JPG(MyJPGF5.class); //
+	JPG(MyJPGF5.class), //
+	PNG(MyPNGLsbMultiColorChannel.class); //
 
 	private static final Logger LOG = LoggerFactory.getLogger(EmbeddingFactory.class);
 
@@ -33,6 +31,7 @@ public enum EmbeddingFactory {
 		embeddingDefaults.put(GIFImage.class, EmbeddingFactory.GIF_SORTED);
 		embeddingDefaults.put(BMPImage.class, EmbeddingFactory.BMP);
 		embeddingDefaults.put(JPGImage.class, EmbeddingFactory.JPG);
+		embeddingDefaults.put(PNGImage.class, EmbeddingFactory.PNG);
 	}
 
 	private Class<? extends Embedding> embeddingClass = null;
@@ -53,6 +52,10 @@ public enum EmbeddingFactory {
 	}
 
 	public static Embedding getEmbedding(ImageFormat image) {
+		// TODO: disabled until GIFShuffle works
+		//if (image.getClass().equals(GIFImage.class)) {
+		//	return EmbeddingFactory.GIFSHUFFLE.newEmbedding(image);
+		//}
 		EmbeddingFactory factory = embeddingDefaults.get(image.getClass());
 		if (factory == null) {
 			LOG.error("No embedding method found for {}", image.getClass().getName());
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/ExtractingDoneEvent.java b/src/main/java/org/stegosuite/application/embedding/ExtractingDoneListener.java
similarity index 58%
rename from src/main/java/org/stegosuite/ui/gui/embedding/ExtractingDoneEvent.java
rename to src/main/java/org/stegosuite/application/embedding/ExtractingDoneListener.java
index b1df6d2..3f97375 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/ExtractingDoneEvent.java
+++ b/src/main/java/org/stegosuite/application/embedding/ExtractingDoneListener.java
@@ -1,15 +1,15 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.format.ImageFormat;
 
-public interface ExtractingDoneEvent {
+public interface ExtractingDoneListener {
 
 	/**
 	 * Is fired by the abstraction layer to let the Gui process the extraction result
 	 * 
 	 * @param embeddingMethod
 	 */
-	void handleEvent(EmbeddingMethod<? extends ImageFormat> embeddingMethod);
+	void onExtractingDone(EmbeddingMethod<? extends ImageFormat> embeddingMethod);
 
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/MyBMPLsbMultiColorChannel.java b/src/main/java/org/stegosuite/application/embedding/MyBMPLsbMultiColorChannel.java
similarity index 87%
rename from src/main/java/org/stegosuite/ui/gui/embedding/MyBMPLsbMultiColorChannel.java
rename to src/main/java/org/stegosuite/application/embedding/MyBMPLsbMultiColorChannel.java
index 216da5d..b709e23 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/MyBMPLsbMultiColorChannel.java
+++ b/src/main/java/org/stegosuite/application/embedding/MyBMPLsbMultiColorChannel.java
@@ -1,4 +1,4 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.embedding.EmbeddingProgress;
@@ -28,17 +28,17 @@ public class MyBMPLsbMultiColorChannel
 	}
 
 	@Override
-	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneEvent event)
+	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneListener listener)
 			throws SteganoEmbedException {
 		BMPImage embeddedImage = embeddable.embed(payload, progress);
-		event.handleEvent(embeddable, embeddedImage);
+		listener.onEmbeddingDone(embeddable, embeddedImage);
 	}
 
 	@Override
-	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneEvent event)
+	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneListener listener)
 			throws SteganoExtractException {
 		embeddable.extract(payload, progress);
-		event.handleEvent(embeddable);
+		listener.onExtractingDone(embeddable);
 	}
 
 	@Override
@@ -55,8 +55,4 @@ public class MyBMPLsbMultiColorChannel
 		}
 	}
 
-	@Override
-	public Embedding getEmbeddingMethod() {
-		return this;
-	}
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/MyGIFShuffle.java b/src/main/java/org/stegosuite/application/embedding/MyGIFShuffle.java
similarity index 83%
rename from src/main/java/org/stegosuite/ui/gui/embedding/MyGIFShuffle.java
rename to src/main/java/org/stegosuite/application/embedding/MyGIFShuffle.java
index a5f404d..8e17e83 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/MyGIFShuffle.java
+++ b/src/main/java/org/stegosuite/application/embedding/MyGIFShuffle.java
@@ -1,4 +1,4 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.embedding.EmbeddingProgress;
@@ -23,17 +23,17 @@ public class MyGIFShuffle
 	}
 
 	@Override
-	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneEvent event)
+	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneListener listener)
 			throws SteganoEmbedException {
 		GIFImage embeddedImage = embeddable.embed(payload, progress);
-		event.handleEvent(embeddable, embeddedImage);
+		listener.onEmbeddingDone(embeddable, embeddedImage);
 	}
 
 	@Override
-	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneEvent event)
+	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneListener listener)
 			throws SteganoExtractException {
 		embeddable.extract(payload, progress);
-		event.handleEvent(embeddable);
+		listener.onExtractingDone(embeddable);
 	}
 
 	@Override
@@ -46,8 +46,4 @@ public class MyGIFShuffle
 		// no point filter used
 	}
 
-	@Override
-	public Embedding getEmbeddingMethod() {
-		return this;
-	}
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/MyGIFSortedColorTable.java b/src/main/java/org/stegosuite/application/embedding/MyGIFSortedColorTable.java
similarity index 87%
copy from src/main/java/org/stegosuite/ui/gui/embedding/MyGIFSortedColorTable.java
copy to src/main/java/org/stegosuite/application/embedding/MyGIFSortedColorTable.java
index 846d3ed..6c66a1c 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/MyGIFSortedColorTable.java
+++ b/src/main/java/org/stegosuite/application/embedding/MyGIFSortedColorTable.java
@@ -1,4 +1,4 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.embedding.EmbeddingProgress;
@@ -28,17 +28,17 @@ public class MyGIFSortedColorTable
 	}
 
 	@Override
-	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneEvent event)
+	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneListener listener)
 			throws SteganoEmbedException {
 		GIFImage embeddedImage = embeddable.embed(payload, progress);
-		event.handleEvent(embeddable, embeddedImage);
+		listener.onEmbeddingDone(embeddable, embeddedImage);
 	}
 
 	@Override
-	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneEvent event)
+	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneListener listener)
 			throws SteganoExtractException {
 		embeddable.extract(payload, progress);
-		event.handleEvent(embeddable);
+		listener.onExtractingDone(embeddable);
 	}
 
 	@Override
@@ -55,8 +55,4 @@ public class MyGIFSortedColorTable
 		}
 	}
 
-	@Override
-	public Embedding getEmbeddingMethod() {
-		return this;
-	}
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/MyJPGF5.java b/src/main/java/org/stegosuite/application/embedding/MyJPGF5.java
similarity index 83%
rename from src/main/java/org/stegosuite/ui/gui/embedding/MyJPGF5.java
rename to src/main/java/org/stegosuite/application/embedding/MyJPGF5.java
index 0ef0a2f..e2a24b5 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/MyJPGF5.java
+++ b/src/main/java/org/stegosuite/application/embedding/MyJPGF5.java
@@ -1,4 +1,4 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.embedding.EmbeddingProgress;
@@ -23,17 +23,17 @@ public class MyJPGF5
 	}
 
 	@Override
-	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneEvent event)
+	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneListener listener)
 			throws SteganoEmbedException {
 		JPGImage embeddedImage = embeddable.embed(payload, progress);
-		event.handleEvent(embeddable, embeddedImage);
+		listener.onEmbeddingDone(embeddable, embeddedImage);
 	}
 
 	@Override
-	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneEvent event)
+	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneListener listener)
 			throws SteganoExtractException {
 		embeddable.extract(payload, progress);
-		event.handleEvent(embeddable);
+		listener.onExtractingDone(embeddable);
 	}
 
 	@Override
@@ -46,8 +46,4 @@ public class MyJPGF5
 		// no point filter used
 	}
 
-	@Override
-	public Embedding getEmbeddingMethod() {
-		return this;
-	}
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/embedding/MyGIFSortedColorTable.java b/src/main/java/org/stegosuite/application/embedding/MyPNGLsbMultiColorChannel.java
similarity index 50%
rename from src/main/java/org/stegosuite/ui/gui/embedding/MyGIFSortedColorTable.java
rename to src/main/java/org/stegosuite/application/embedding/MyPNGLsbMultiColorChannel.java
index 846d3ed..7976fb6 100644
--- a/src/main/java/org/stegosuite/ui/gui/embedding/MyGIFSortedColorTable.java
+++ b/src/main/java/org/stegosuite/application/embedding/MyPNGLsbMultiColorChannel.java
@@ -1,12 +1,12 @@
-package org.stegosuite.ui.gui.embedding;
+package org.stegosuite.application.embedding;
 
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.embedding.EmbeddingProgress;
-import org.stegosuite.image.embedding.gif.GIFSortedColorTable;
-import org.stegosuite.image.embedding.gif.filter.GIFPointFilterHomogeneous;
-import org.stegosuite.image.embedding.gif.filter.GIFPointFilterNone;
-import org.stegosuite.image.format.GIFImage;
+import org.stegosuite.image.embedding.png.PNGLsbMultiColorChannel;
+import org.stegosuite.image.embedding.png.filter.PNGPointFilterHomogeneous;
+import org.stegosuite.image.embedding.png.filter.PNGPointFilterNone;
 import org.stegosuite.image.format.ImageFormat;
+import org.stegosuite.image.format.PNGImage;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.payload.Payload;
@@ -14,31 +14,31 @@ import org.stegosuite.model.payload.Payload;
 /**
  * Abstraction layer between the GUI and the embed/extract functions.
  */
-public class MyGIFSortedColorTable
+public class MyPNGLsbMultiColorChannel
 		extends Embedding {
 
-	private EmbeddingMethod<GIFImage> embeddable;
-	private EmbeddingMethod<GIFImage> embeddablePoint;
-	private EmbeddingMethod<GIFImage> embeddableNoPoint;
+	private EmbeddingMethod<PNGImage> embeddable;
+	private EmbeddingMethod<PNGImage> embeddablePoint;
+	private EmbeddingMethod<PNGImage> embeddableNoPoint;
 
-	public MyGIFSortedColorTable(ImageFormat image) {
-		embeddableNoPoint = new GIFSortedColorTable((GIFImage) image, new GIFPointFilterNone());
-		embeddablePoint = new GIFSortedColorTable((GIFImage) image, new GIFPointFilterHomogeneous());
+	public MyPNGLsbMultiColorChannel(ImageFormat image) {
+		embeddableNoPoint = new PNGLsbMultiColorChannel((PNGImage) image, new PNGPointFilterNone());
+		embeddablePoint = new PNGLsbMultiColorChannel((PNGImage) image, new PNGPointFilterHomogeneous());
 		embeddable = embeddableNoPoint;
 	}
 
 	@Override
-	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneEvent event)
+	public void embed(Payload payload, EmbeddingProgress progress, EmbeddingDoneListener listener)
 			throws SteganoEmbedException {
-		GIFImage embeddedImage = embeddable.embed(payload, progress);
-		event.handleEvent(embeddable, embeddedImage);
+		PNGImage embeddedImage = embeddable.embed(payload, progress);
+		listener.onEmbeddingDone(embeddable, embeddedImage);
 	}
 
 	@Override
-	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneEvent event)
+	public void extract(Payload payload, EmbeddingProgress progress, ExtractingDoneListener listener)
 			throws SteganoExtractException {
 		embeddable.extract(payload, progress);
-		event.handleEvent(embeddable);
+		listener.onExtractingDone(embeddable);
 	}
 
 	@Override
@@ -55,8 +55,4 @@ public class MyGIFSortedColorTable
 		}
 	}
 
-	@Override
-	public Embedding getEmbeddingMethod() {
-		return this;
-	}
 }
diff --git a/src/main/java/org/stegosuite/image/embedding/Visualizer.java b/src/main/java/org/stegosuite/image/embedding/Visualizer.java
index 4f36cbe..e4f701b 100644
--- a/src/main/java/org/stegosuite/image/embedding/Visualizer.java
+++ b/src/main/java/org/stegosuite/image/embedding/Visualizer.java
@@ -1,25 +1,24 @@
 package org.stegosuite.image.embedding;
 
-import static java.util.stream.Collectors.toMap;
+import org.eclipse.swt.graphics.ImageData;
+import org.stegosuite.image.format.ImageFormat;
+import org.stegosuite.util.ColorUtils;
+import org.stegosuite.util.ImageSwtAwtConverter;
 
-import java.awt.Color;
-import java.awt.Point;
+import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.util.Arrays;
 import java.util.Map;
 
-import org.eclipse.swt.graphics.ImageData;
-import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.util.ColorUtils;
-import org.stegosuite.image.util.ImageSwtAwtConverter;
+import static java.util.stream.Collectors.toMap;
 
 public class Visualizer {
 
 	/**
 	 * The different modes of visualization
 	 */
-	public static enum VisualizationMode {
-		ALTERED, UNALTERED;
+	public enum VisualizationMode {
+		ALTERED, UNALTERED
 	}
 
 	/**
diff --git a/src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java b/src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java
index a66a45a..38aeaeb 100644
--- a/src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java
+++ b/src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java
@@ -1,10 +1,5 @@
 package org.stegosuite.image.embedding.bmp;
 
-import java.awt.Color;
-import java.awt.Point;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.stegosuite.image.embedding.EmbeddingMethod;
@@ -15,14 +10,18 @@ import org.stegosuite.image.embedding.Visualizer.Visualize;
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.embedding.point.PointGenerator;
 import org.stegosuite.image.format.BMPImage;
-import org.stegosuite.image.util.ByteUtils;
-import org.stegosuite.image.util.RgbChannel;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoKeyException;
 import org.stegosuite.model.payload.Payload;
 import org.stegosuite.model.payload.PayloadEmbedder;
 import org.stegosuite.model.payload.PayloadExtractor;
+import org.stegosuite.util.ByteUtils;
+import org.stegosuite.util.RgbChannel;
+
+import java.awt.*;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
 
 /**
  * Embed/Extract on BMP-images using data-spreading method. This method will utilise all {@code 3}
@@ -41,9 +40,9 @@ public class BMPLsbMultiColorChannel
 	/**
 	 * Constructor
 	 *
-	 * @param image the {@link BmpImage} to be embeded/extracted
+	 * @param image the {@link BMPImage} to be embeded/extracted
 	 * @param pointFilter the {@link PointFilter} to filter out undesired areas of the given
-	 *        {@link BmpImage}
+	 *        {@link BMPImage}
 	 */
 	public BMPLsbMultiColorChannel(BMPImage image, PointFilter<BMPImage> pointFilter) {
 		super(image, pointFilter);
@@ -75,7 +74,7 @@ public class BMPLsbMultiColorChannel
 
 		LOG.debug("Performing BMP LSB embedding");
 
-		PayloadEmbedder payloadEmbedder = new PayloadEmbedder(this, image, payload);
+		PayloadEmbedder payloadEmbedder = new PayloadEmbedder(payload, this.capacity());
 		int numPayloadBytes = payloadEmbedder.getPayloadBytes().length;
 
 		// Initialize the data spreader
diff --git a/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java b/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java
index 3655861..ff2d6a5 100644
--- a/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java
+++ b/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java
@@ -1,22 +1,18 @@
 package org.stegosuite.image.embedding.bmp.filter;
 
-import java.awt.Color;
-import java.awt.Point;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.format.BMPImage;
 import org.stegosuite.model.exception.SteganoImageException;
 
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.List;
+
 /**
  * Removes all points that are part of homogeneous areas of a BMP image
  */
@@ -42,7 +38,7 @@ public class BMPPointFilterHomogeneous
 	protected Collection<Point> filter(BMPImage image) {
 		int[][] normalizedRgbValues = getNormalizedRgbValues(image.getBufferedImage());
 		// long startTime = System.nanoTime();
-		Collection<Point> filteredPoints = new HashSet<Point>();
+		Collection<Point> filteredPoints = new HashSet<>();
 		for (int x = 1; x < normalizedRgbValues.length - 1; x++) {
 			for (int y = 1; y < normalizedRgbValues[x].length - 1; y++) {
 				Collection<Point> homogenousPoints = getHomogeneousPoints(normalizedRgbValues, x, y);
diff --git a/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterNone.java b/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterNone.java
index 898696a..9ff06e7 100644
--- a/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterNone.java
+++ b/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterNone.java
@@ -1,12 +1,12 @@
 package org.stegosuite.image.embedding.bmp.filter;
 
-import java.awt.Point;
-import java.util.ArrayList;
-import java.util.Collection;
-
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.format.BMPImage;
 
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Allows embedding into all points of a BMP image
  */
diff --git a/src/main/java/org/stegosuite/image/embedding/gif/GIFShuffle.java b/src/main/java/org/stegosuite/image/embedding/gif/GIFShuffle.java
index 256cdb4..f689c4b 100644
--- a/src/main/java/org/stegosuite/image/embedding/gif/GIFShuffle.java
+++ b/src/main/java/org/stegosuite/image/embedding/gif/GIFShuffle.java
@@ -1,28 +1,25 @@
 package org.stegosuite.image.embedding.gif;
 
-import java.awt.Color;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
 import org.stegosuite.image.embedding.EmbeddingMethod;
 import org.stegosuite.image.embedding.EmbeddingProgress;
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.format.GIFImage;
-import org.stegosuite.image.util.ByteUtils;
-import org.stegosuite.image.util.ColorDistance;
-import org.stegosuite.image.util.CryptoUtils;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoKeyException;
 import org.stegosuite.model.payload.Payload;
 import org.stegosuite.model.payload.PayloadEmbedder;
 import org.stegosuite.model.payload.PayloadExtractor;
+import org.stegosuite.util.ByteUtils;
+import org.stegosuite.util.ColorDistance;
+import org.stegosuite.util.CryptoUtils;
+
+import java.awt.*;
+import java.math.BigInteger;
+import java.util.*;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * GIFShuffle embedding and extracting procedures. Source: http://www.darkside.com.au/gifshuffle/
@@ -52,12 +49,12 @@ public class GIFShuffle
 			throws SteganoEmbedException {
 
 		List<Color> originalTable = image.getColorTable();
-		List<Color> newTable = new ArrayList<Color>(originalTable);
+		List<Color> newTable = new ArrayList<>(originalTable);
 		List<Color> randomTable = image.getSortedColorTable(DISTANCE);
 		Collections.shuffle(randomTable, CryptoUtils.seededRandom(payload.getSteganoPassword()));
 
 		// Prepend 1 to the payload so that leading 0 bytes are not cut off
-		PayloadEmbedder embedder = new PayloadEmbedder(this, image, payload);
+		PayloadEmbedder embedder = new PayloadEmbedder(payload, this.capacity());
 		BigInteger numPayload = new BigInteger(ByteUtils.concat(new byte[] { 1 }, embedder.getPayloadBytes()));
 
 		for (int i = 0; i < originalTable.size(); i++) {
diff --git a/src/main/java/org/stegosuite/image/embedding/gif/GIFSortedColorTable.java b/src/main/java/org/stegosuite/image/embedding/gif/GIFSortedColorTable.java
index f7562d4..90cbccd 100644
--- a/src/main/java/org/stegosuite/image/embedding/gif/GIFSortedColorTable.java
+++ b/src/main/java/org/stegosuite/image/embedding/gif/GIFSortedColorTable.java
@@ -1,25 +1,5 @@
 package org.stegosuite.image.embedding.gif;
 
-import static java.util.stream.Collectors.toList;
-
-import java.awt.Color;
-import java.awt.Point;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.StandardOpenOption;
-import java.util.AbstractMap.SimpleEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.stegosuite.image.embedding.EmbeddingMethod;
@@ -31,8 +11,6 @@ import org.stegosuite.image.embedding.gif.filter.GIFPointFilterHomogeneous;
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.embedding.point.PointGenerator;
 import org.stegosuite.image.format.GIFImage;
-import org.stegosuite.image.util.ColorDistance;
-import org.stegosuite.image.util.ColorUtils;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoImageException;
@@ -42,6 +20,20 @@ import org.stegosuite.model.payload.PayloadEmbedder;
 import org.stegosuite.model.payload.PayloadExtractor;
 import org.stegosuite.model.payload.block.FileBlock;
 import org.stegosuite.model.payload.block.MessageBlock;
+import org.stegosuite.util.ColorDistance;
+import org.stegosuite.util.ColorUtils;
+
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.*;
+import java.util.List;
+import java.util.Map.Entry;
+
+import static java.util.stream.Collectors.toList;
 
 /**
  * "Normal" embedding and extracting procedures for GIF image, i.e. using a sorted color table.
@@ -91,7 +83,7 @@ public class GIFSortedColorTable
 
 		PointGenerator<GIFImage> pointGenerator = new PointGenerator<>(image, payload.getSteganoPassword(),
 				pointFilter);
-		PayloadEmbedder embedder = new PayloadEmbedder(this, image, payload);
+		PayloadEmbedder embedder = new PayloadEmbedder(payload, this.capacity());
 		int payloadNumBytes = embedder.getPayloadBytes().length;
 
 		int[] pixels = image.getPixels().clone();
@@ -243,14 +235,8 @@ public class GIFSortedColorTable
 		return skipColors.getValue();
 	}
 
-	/**
-	 * 
-	 * @param args
-	 * @throws SteganoImageException
-	 * @throws SteganoEmbedException
-	 * @throws SteganoExtractException
-	 * @throws IOException
-	 */
+
+	// TODO: Move this into tests
 	public static void main(String[] args)
 			throws SteganoImageException, SteganoEmbedException, SteganoExtractException, IOException {
 
@@ -275,10 +261,10 @@ public class GIFSortedColorTable
 
 		// Add file to payload
 		FileBlock fileBlock = new FileBlock(secretFileIn.getAbsolutePath());
-		payloadToEmbed.getBlocks().add(fileBlock);
+		payloadToEmbed.addBlock(fileBlock);
 
 		MessageBlock messageBlock = new MessageBlock("Some hidden message");
-		payloadToEmbed.getBlocks().add(messageBlock);
+		payloadToEmbed.addBlock(messageBlock);
 
 		// Embed
 		embeddingMethod.embed(payloadToEmbed, null);
@@ -296,11 +282,11 @@ public class GIFSortedColorTable
 		embeddingMethod.extract(payloadExtracted, null);
 
 		// Save extracted contents to file
-		fileBlock = (FileBlock) payloadExtracted.getBlocks().get(0);
+		fileBlock = (FileBlock) payloadExtracted.getBlock(0);
 		LOG.info("Original file name: " + fileBlock.getFileName());
 		Files.write(secretFileOut.toPath(), fileBlock.getFileContent(), StandardOpenOption.CREATE);
 
-		messageBlock = (MessageBlock) payloadExtracted.getBlocks().get(1);
+		messageBlock = (MessageBlock) payloadExtracted.getBlock(1);
 		LOG.info("Message: {}", messageBlock.getMessage());
 	}
 
diff --git a/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterHomogeneous.java b/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterHomogeneous.java
index bd936ea..7a9462e 100644
--- a/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterHomogeneous.java
+++ b/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterHomogeneous.java
@@ -1,25 +1,21 @@
 package org.stegosuite.image.embedding.gif.filter;
 
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.image.embedding.point.PointFilter;
+import org.stegosuite.image.format.GIFImage;
+import org.stegosuite.model.exception.SteganoImageException;
+import org.stegosuite.util.ColorDistance;
 
-import java.awt.Color;
-import java.awt.Point;
+import java.awt.*;
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.IntStream;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.embedding.point.PointFilter;
-import org.stegosuite.image.format.GIFImage;
-import org.stegosuite.image.util.ColorDistance;
-import org.stegosuite.model.exception.SteganoImageException;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
 
 /**
  * Removes all points that are part of homogeneous areas of a GIF image
diff --git a/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterNone.java b/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterNone.java
index 9e50a93..37119cc 100644
--- a/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterNone.java
+++ b/src/main/java/org/stegosuite/image/embedding/gif/filter/GIFPointFilterNone.java
@@ -1,12 +1,12 @@
 package org.stegosuite.image.embedding.gif.filter;
 
-import java.awt.Point;
-import java.util.ArrayList;
-import java.util.Collection;
-
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.format.GIFImage;
 
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Allows embedding into all points of a GIF image
  */
diff --git a/src/main/java/org/stegosuite/image/embedding/jpg/JPGF5.java b/src/main/java/org/stegosuite/image/embedding/jpg/JPGF5.java
index 1d18f82..f1290d8 100644
--- a/src/main/java/org/stegosuite/image/embedding/jpg/JPGF5.java
+++ b/src/main/java/org/stegosuite/image/embedding/jpg/JPGF5.java
@@ -1,15 +1,5 @@
 package org.stegosuite.image.embedding.jpg;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.StandardOpenOption;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.stegosuite.image.embedding.EmbeddingMethod;
@@ -18,7 +8,6 @@ import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.format.JPGImage;
 import org.stegosuite.image.jpgtemp.james.JpegEncoder;
 import org.stegosuite.image.jpgtemp.net.f5.Extract;
-import org.stegosuite.image.util.FileUtils;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoImageException;
@@ -27,6 +16,11 @@ import org.stegosuite.model.payload.PayloadEmbedder;
 import org.stegosuite.model.payload.PayloadExtractor;
 import org.stegosuite.model.payload.block.FileBlock;
 import org.stegosuite.model.payload.block.MessageBlock;
+import org.stegosuite.util.FileUtils;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
 
 public class JPGF5
 		extends EmbeddingMethod<JPGImage> {
@@ -48,7 +42,7 @@ public class JPGF5
 	protected void doEmbed(JPGImage image, Payload payload, EmbeddingProgress progress)
 			throws SteganoEmbedException {
 
-		PayloadEmbedder embedder = new PayloadEmbedder(this, image, payload);
+		PayloadEmbedder embedder = new PayloadEmbedder(payload, this.capacity());
 
 		final String comment = "JPEG Encoder Copyright 1998, James R. Weeks and BioElectroMech.  ";
 		final int quality = 80; // 0 is worst, 100 is best
@@ -61,7 +55,9 @@ public class JPGF5
 
 		FileOutputStream outputStream = null;
 
+		//TODO: Move this saving functionality to ImageFormat.save()
 		try {
+			LOG.info("Saving jpg image to {}", outputPath);
 			outputStream = new FileOutputStream(outputPath);
 		} catch (FileNotFoundException e1) {
 			e1.printStackTrace();
@@ -111,7 +107,7 @@ public class JPGF5
 		}
 	}
 
-	// for tests only
+	// TODO: Move this into tests
 	public static void main(String[] args)
 			throws SteganoImageException, SteganoEmbedException, SteganoExtractException, IOException {
 
@@ -136,10 +132,10 @@ public class JPGF5
 
 		// Add file to payload
 		FileBlock fileBlock = new FileBlock(secretFileIn.getAbsolutePath());
-		payloadToEmbed.getBlocks().add(fileBlock);
+		payloadToEmbed.addBlock(fileBlock);
 
 		MessageBlock messageBlock = new MessageBlock("Some hidden message");
-		payloadToEmbed.getBlocks().add(messageBlock);
+		payloadToEmbed.addBlock(messageBlock);
 
 		// Embed
 		embeddingMethod.embed(payloadToEmbed, null);
@@ -157,11 +153,11 @@ public class JPGF5
 
 		embeddingMethod.extract(payloadExtracted, null);
 		// Save extracted contents to file
-		fileBlock = (FileBlock) payloadExtracted.getBlocks().get(0);
+		fileBlock = (FileBlock) payloadExtracted.getBlock(0);
 		LOG.info("Original file name: " + fileBlock.getFileName());
 		Files.write(secretFileOut.toPath(), fileBlock.getFileContent(), StandardOpenOption.CREATE);
 
-		messageBlock = (MessageBlock) payloadExtracted.getBlocks().get(1);
+		messageBlock = (MessageBlock) payloadExtracted.getBlock(1);
 		LOG.info("Message: {}", messageBlock.getMessage());
 
 	}
diff --git a/src/main/java/org/stegosuite/image/embedding/jpg/filter/JPGPointFilterNone.java b/src/main/java/org/stegosuite/image/embedding/jpg/filter/JPGPointFilterNone.java
index d6b4cfa..de65ade 100644
--- a/src/main/java/org/stegosuite/image/embedding/jpg/filter/JPGPointFilterNone.java
+++ b/src/main/java/org/stegosuite/image/embedding/jpg/filter/JPGPointFilterNone.java
@@ -1,12 +1,12 @@
 package org.stegosuite.image.embedding.jpg.filter;
 
-import java.awt.Point;
-import java.util.ArrayList;
-import java.util.Collection;
-
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.format.JPGImage;
 
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * Allows embedding into all points of a GIF image
  */
diff --git a/src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java b/src/main/java/org/stegosuite/image/embedding/png/PNGLsbMultiColorChannel.java
similarity index 81%
copy from src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java
copy to src/main/java/org/stegosuite/image/embedding/png/PNGLsbMultiColorChannel.java
index a66a45a..02360a9 100644
--- a/src/main/java/org/stegosuite/image/embedding/bmp/BMPLsbMultiColorChannel.java
+++ b/src/main/java/org/stegosuite/image/embedding/png/PNGLsbMultiColorChannel.java
@@ -1,9 +1,4 @@
-package org.stegosuite.image.embedding.bmp;
-
-import java.awt.Color;
-import java.awt.Point;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
+package org.stegosuite.image.embedding.png;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -14,18 +9,22 @@ import org.stegosuite.image.embedding.Visualizer.VisualizationMode;
 import org.stegosuite.image.embedding.Visualizer.Visualize;
 import org.stegosuite.image.embedding.point.PointFilter;
 import org.stegosuite.image.embedding.point.PointGenerator;
-import org.stegosuite.image.format.BMPImage;
-import org.stegosuite.image.util.ByteUtils;
-import org.stegosuite.image.util.RgbChannel;
+import org.stegosuite.image.format.PNGImage;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoKeyException;
 import org.stegosuite.model.payload.Payload;
 import org.stegosuite.model.payload.PayloadEmbedder;
 import org.stegosuite.model.payload.PayloadExtractor;
+import org.stegosuite.util.ByteUtils;
+import org.stegosuite.util.RgbChannel;
+
+import java.awt.*;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
 
 /**
- * Embed/Extract on BMP-images using data-spreading method. This method will utilise all {@code 3}
+ * Embed/Extract on PNG-images using data-spreading method. This method will utilise all {@code 3}
  * color channels, sequentially, meaning the payload will first be embedded into the
  * <b>{@code red}</b>-byte, then the <b>{@code green} </b>-byte, and finally the <b>{@code blue}</b>
  * -byte.
@@ -33,30 +32,30 @@ import org.stegosuite.model.payload.PayloadExtractor;
  * @author alwin
  *
  */
-public class BMPLsbMultiColorChannel
-		extends EmbeddingMethod<BMPImage> {
+public class PNGLsbMultiColorChannel
+		extends EmbeddingMethod<PNGImage> {
 
-	private static final Logger LOG = LoggerFactory.getLogger(BMPLsbMultiColorChannel.class);
+	private static final Logger LOG = LoggerFactory.getLogger(PNGLsbMultiColorChannel.class);
 
 	/**
 	 * Constructor
 	 *
-	 * @param image the {@link BmpImage} to be embeded/extracted
+	 * @param image the {@link PNGImage} to be embeded/extracted
 	 * @param pointFilter the {@link PointFilter} to filter out undesired areas of the given
-	 *        {@link BmpImage}
+	 *        {@link PNGImage}
 	 */
-	public BMPLsbMultiColorChannel(BMPImage image, PointFilter<BMPImage> pointFilter) {
+	public PNGLsbMultiColorChannel(PNGImage image, PointFilter<PNGImage> pointFilter) {
 		super(image, pointFilter);
 	}
 
 	@Override
-	public Visualizer createVisualizer(BMPImage image) {
+	public Visualizer createVisualizer(PNGImage image) {
 		return new Visualizer(image, new Visualize(VisualizationMode.ALTERED, Color.RED),
 				new Visualize(VisualizationMode.UNALTERED, Color.GREEN));
 	}
 
 	@Override
-	public int doCapacity(BMPImage image) {
+	public int doCapacity(PNGImage image) {
 		LOG.debug("Embedding into {} LSBs", pointFilter.maxLsbCount());
 
 		// Embedding will be done on all 3 color channels
@@ -70,16 +69,16 @@ public class BMPLsbMultiColorChannel
 	}
 
 	@Override
-	protected void doEmbed(BMPImage image, Payload payload, EmbeddingProgress progress)
+	protected void doEmbed(PNGImage image, Payload payload, EmbeddingProgress progress)
 			throws SteganoEmbedException {
 
-		LOG.debug("Performing BMP LSB embedding");
+		LOG.debug("Performing PNG LSB embedding");
 
-		PayloadEmbedder payloadEmbedder = new PayloadEmbedder(this, image, payload);
+		PayloadEmbedder payloadEmbedder = new PayloadEmbedder(payload, this.capacity());
 		int numPayloadBytes = payloadEmbedder.getPayloadBytes().length;
 
 		// Initialize the data spreader
-		PointGenerator<BMPImage> pointGenerator = new PointGenerator<>(image, payload.getSteganoPassword(),
+		PointGenerator<PNGImage> pointGenerator = new PointGenerator<>(image, payload.getSteganoPassword(),
 				pointFilter);
 
 		int processedBits = 0;
@@ -123,13 +122,13 @@ public class BMPLsbMultiColorChannel
 	}
 
 	@Override
-	protected void doExtract(BMPImage image, Payload payload, EmbeddingProgress progress)
+	protected void doExtract(PNGImage image, Payload payload, EmbeddingProgress progress)
 			throws SteganoExtractException {
 
-		LOG.debug("Performing BMP LSB extraction");
+		LOG.debug("Performing PNG LSB extraction");
 
 		PayloadExtractor payloadExtractor = new PayloadExtractor(payload);
-		PointGenerator<BMPImage> pointGenerator = new PointGenerator<>(image, payload.getSteganoPassword(),
+		PointGenerator<PNGImage> pointGenerator = new PointGenerator<>(image, payload.getSteganoPassword(),
 				pointFilter);
 
 		try {
diff --git a/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java b/src/main/java/org/stegosuite/image/embedding/png/filter/PNGPointFilterHomogeneous.java
similarity index 71%
copy from src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java
copy to src/main/java/org/stegosuite/image/embedding/png/filter/PNGPointFilterHomogeneous.java
index 3655861..20746b5 100644
--- a/src/main/java/org/stegosuite/image/embedding/bmp/filter/BMPPointFilterHomogeneous.java
+++ b/src/main/java/org/stegosuite/image/embedding/png/filter/PNGPointFilterHomogeneous.java
@@ -1,34 +1,30 @@
-package org.stegosuite.image.embedding.bmp.filter;
-
-import java.awt.Color;
-import java.awt.Point;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
+package org.stegosuite.image.embedding.png.filter;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.stegosuite.image.embedding.point.PointFilter;
-import org.stegosuite.image.format.BMPImage;
+import org.stegosuite.image.format.PNGImage;
 import org.stegosuite.model.exception.SteganoImageException;
 
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.List;
+
 /**
- * Removes all points that are part of homogeneous areas of a BMP image
+ * Removes all points that are part of homogeneous areas of a PNG image
  */
-public class BMPPointFilterHomogeneous
-		extends PointFilter<BMPImage> {
+public class PNGPointFilterHomogeneous
+		extends PointFilter<PNGImage> {
 
 	/**
 	 * Sets the LSB of all 3 color channels of a 32bit RGB value to 0
 	 */
 	private static final int MASK_LSBS_TO_ZERO = ~0b10000000100000001;
 
-	private static final Logger LOG = LoggerFactory.getLogger(BMPPointFilterHomogeneous.class);
+	private static final Logger LOG = LoggerFactory.getLogger(PNGPointFilterHomogeneous.class);
 
 	@Override
 	public int maxLsbCount() {
@@ -39,10 +35,10 @@ public class BMPPointFilterHomogeneous
 	 * Returns a list of all homogeneous areas
 	 */
 	@Override
-	protected Collection<Point> filter(BMPImage image) {
+	protected Collection<Point> filter(PNGImage image) {
 		int[][] normalizedRgbValues = getNormalizedRgbValues(image.getBufferedImage());
 		// long startTime = System.nanoTime();
-		Collection<Point> filteredPoints = new HashSet<Point>();
+		Collection<Point> filteredPoints = new HashSet<>();
 		for (int x = 1; x < normalizedRgbValues.length - 1; x++) {
 			for (int y = 1; y < normalizedRgbValues[x].length - 1; y++) {
 				Collection<Point> homogenousPoints = getHomogeneousPoints(normalizedRgbValues, x, y);
@@ -113,30 +109,30 @@ public class BMPPointFilterHomogeneous
 	public static void main(String[] args)
 			throws SteganoImageException {
 
-		BMPImage bmpImage = new BMPImage();
-		bmpImage.load(new File("resources/Snow.bmp"));
+		PNGImage pngImage = new PNGImage();
+		pngImage.load(new File("resources/Snow.png"));
 
-		LOG.debug("Width: {}", bmpImage.getWidth());
-		LOG.debug("Height: {}", bmpImage.getHeight());
-		LOG.debug("Total pixel: {}", (bmpImage.getWidth() * bmpImage.getHeight()));
+		LOG.debug("Width: {}", pngImage.getWidth());
+		LOG.debug("Height: {}", pngImage.getHeight());
+		LOG.debug("Total pixel: {}", (pngImage.getWidth() * pngImage.getHeight()));
 
 		List<Point> allPoints = new ArrayList<>();
-		for (int x = 0; x < bmpImage.getWidth(); x++) {
-			for (int y = 0; y < bmpImage.getHeight(); y++) {
+		for (int x = 0; x < pngImage.getWidth(); x++) {
+			for (int y = 0; y < pngImage.getHeight(); y++) {
 				allPoints.add(new Point(x, y));
 			}
 		}
 
-		PointFilter<BMPImage> filter = new BMPPointFilterHomogeneous();
-		Collection<Point> filteredPoints = filter.getFilteredPoints(bmpImage);
+		PointFilter<PNGImage> filter = new PNGPointFilterHomogeneous();
+		Collection<Point> filteredPoints = filter.getFilteredPoints(pngImage);
 		LOG.debug("Count of non-noise pixels: {}", filteredPoints.size());
 
-		BufferedImage bufferedImage = bmpImage.getBufferedImage();
+		BufferedImage bufferedImage = pngImage.getBufferedImage();
 		for (Point p : filteredPoints) {
 			bufferedImage.setRGB(p.x, p.y, Color.RED.getRGB());
 		}
-		bmpImage.setBufferedImage(bufferedImage);
+		pngImage.setBufferedImage(bufferedImage);
 
-		bmpImage.save(new File("resources/sunflower_Noise2.bmp"));
+		pngImage.save(new File("resources/sunflower_Noise2.png"));
 	}
 }
diff --git a/src/main/java/org/stegosuite/image/embedding/png/filter/PNGPointFilterNone.java b/src/main/java/org/stegosuite/image/embedding/png/filter/PNGPointFilterNone.java
new file mode 100644
index 0000000..b60d264
--- /dev/null
+++ b/src/main/java/org/stegosuite/image/embedding/png/filter/PNGPointFilterNone.java
@@ -0,0 +1,26 @@
+package org.stegosuite.image.embedding.png.filter;
+
+import org.stegosuite.image.embedding.point.PointFilter;
+import org.stegosuite.image.format.PNGImage;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Allows embedding into all points of a PNG image
+ */
+public class PNGPointFilterNone
+		extends PointFilter<PNGImage> {
+
+	@Override
+	public int maxLsbCount() {
+		return 8;
+	}
+
+	@Override
+	protected Collection<Point> filter(PNGImage image) {
+		return new ArrayList<>();
+	}
+
+}
diff --git a/src/main/java/org/stegosuite/image/embedding/point/PointFilter.java b/src/main/java/org/stegosuite/image/embedding/point/PointFilter.java
index 0e0e25e..f782601 100644
--- a/src/main/java/org/stegosuite/image/embedding/point/PointFilter.java
+++ b/src/main/java/org/stegosuite/image/embedding/point/PointFilter.java
@@ -1,10 +1,10 @@
 package org.stegosuite.image.embedding.point;
 
-import java.awt.Point;
-import java.util.Collection;
-
 import org.stegosuite.image.format.ImageFormat;
 
+import java.awt.*;
+import java.util.Collection;
+
 /**
  * A filter can be applied to prevent certain points from being generated by the PointGenerator
  */
diff --git a/src/main/java/org/stegosuite/image/embedding/point/PointGenerator.java b/src/main/java/org/stegosuite/image/embedding/point/PointGenerator.java
index 36c0a5f..6f81deb 100644
--- a/src/main/java/org/stegosuite/image/embedding/point/PointGenerator.java
+++ b/src/main/java/org/stegosuite/image/embedding/point/PointGenerator.java
@@ -1,15 +1,11 @@
 package org.stegosuite.image.embedding.point;
 
-import java.awt.Point;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Random;
-
 import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.util.CryptoUtils;
+import org.stegosuite.util.CryptoUtils;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
 
 /**
  * This class is used for data spreading. It generates random 2D points using a key as a seed. Use
@@ -37,7 +33,7 @@ public class PointGenerator<T extends ImageFormat> {
 	/**
 	 * Contains all points that haven't been visited
 	 */
-	private List<Point> points = new LinkedList<Point>();
+	private List<Point> points = new LinkedList<>();
 
 	/**
 	 * The maximum number of times the points list should be seeded
diff --git a/src/main/java/org/stegosuite/image/format/BMPImage.java b/src/main/java/org/stegosuite/image/format/BMPImage.java
index 44612b5..56bef83 100644
--- a/src/main/java/org/stegosuite/image/format/BMPImage.java
+++ b/src/main/java/org/stegosuite/image/format/BMPImage.java
@@ -1,13 +1,13 @@
 package org.stegosuite.image.format;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.util.ColorUtils;
+
 import java.awt.image.BufferedImage;
 import java.util.Arrays;
 import java.util.List;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.util.ColorUtils;
-
 /**
  * Represent BMP-images and provides methods to manipulate BMP pixels
  *
diff --git a/src/main/java/org/stegosuite/image/format/GIFImage.java b/src/main/java/org/stegosuite/image/format/GIFImage.java
index 72f7c87..2f05e0b 100644
--- a/src/main/java/org/stegosuite/image/format/GIFImage.java
+++ b/src/main/java/org/stegosuite/image/format/GIFImage.java
@@ -1,32 +1,23 @@
 package org.stegosuite.image.format;
 
-import static java.util.stream.Collectors.toSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.model.exception.SteganoImageException;
+import org.stegosuite.util.ColorDistance;
+import org.stegosuite.util.ColorUtils;
 
-import java.awt.Color;
+import javax.imageio.*;
+import javax.imageio.stream.ImageOutputStream;
+import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.awt.image.IndexColorModel;
 import java.awt.image.WritableRaster;
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.imageio.IIOImage;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.ImageWriteParam;
-import javax.imageio.ImageWriter;
-import javax.imageio.stream.ImageOutputStream;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.util.ColorDistance;
-import org.stegosuite.image.util.ColorUtils;
-import org.stegosuite.model.exception.SteganoImageException;
+import static java.util.stream.Collectors.toSet;
 
 public class GIFImage
 		extends ImageFormat {
@@ -163,7 +154,7 @@ public class GIFImage
 			sortedColorTables.put(colorDistance, ColorUtils.sortColors(getColorTable(), colorDistance));
 			LOG.debug("Sorted color table in {} ms", (System.nanoTime() - startTime) / 1000000);
 		}
-		return new ArrayList<Color>(sortedColorTables.get(colorDistance));
+		return new ArrayList<>(sortedColorTables.get(colorDistance));
 	}
 
 	/**
diff --git a/src/main/java/org/stegosuite/image/format/ImageFormat.java b/src/main/java/org/stegosuite/image/format/ImageFormat.java
index 7fd23a3..93cfd23 100644
--- a/src/main/java/org/stegosuite/image/format/ImageFormat.java
+++ b/src/main/java/org/stegosuite/image/format/ImageFormat.java
@@ -1,20 +1,21 @@
 package org.stegosuite.image.format;
 
+import org.eclipse.swt.graphics.ImageData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.model.exception.SteganoImageException;
+import org.stegosuite.util.ColorUtils;
+import org.stegosuite.util.FileUtils;
+import org.stegosuite.util.ImageSwtAwtConverter;
+
+import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
 
-import javax.imageio.ImageIO;
-
-import org.eclipse.swt.graphics.ImageData;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.util.ColorUtils;
-import org.stegosuite.image.util.ImageSwtAwtConverter;
-import org.stegosuite.model.exception.SteganoImageException;
-
 /**
  * All supported image formats need to extended this class and implement its abstract methods
  */
@@ -29,6 +30,7 @@ public abstract class ImageFormat {
 		registeredImageExtensions.put(BMPImage.FILE_EXTENSION, BMPImage.class);
 		registeredImageExtensions.put(GIFImage.FILE_EXTENSION, GIFImage.class);
 		registeredImageExtensions.put(JPGImage.FILE_EXTENSION, JPGImage.class);
+		registeredImageExtensions.put(PNGImage.FILE_EXTENSION, PNGImage.class);
 	}
 
 	/**
@@ -54,7 +56,7 @@ public abstract class ImageFormat {
 	 */
 	public void load(File file)
 			throws SteganoImageException {
-		LOG.debug("Loading {} image from {}", getFileExtension(), file.getAbsolutePath());
+		LOG.info("Loading {} image from {}", getFileExtension(), file.getAbsolutePath());
 		this.file = file;
 		try {
 			setBufferedImage(ImageIO.read(file));
@@ -117,7 +119,7 @@ public abstract class ImageFormat {
 	/**
 	 * Sets an image.
 	 *
-	 * @param img AWT's BufferedImage format
+	 * @param image AWT's BufferedImage format
 	 */
 	public void setBufferedImage(BufferedImage image) {
 		this.image = image;
@@ -190,7 +192,11 @@ public abstract class ImageFormat {
 		return true;
 	}
 
-	public static Map<String, Class<? extends ImageFormat>> getRegisteredImageExtensions() {
+	public static Set<String> getSupportedFormats() {
+		return getRegisteredImageExtensions().keySet();
+	}
+
+	private static Map<String, Class<? extends ImageFormat>> getRegisteredImageExtensions() {
 		return registeredImageExtensions;
 	}
 
@@ -220,4 +226,27 @@ public abstract class ImageFormat {
 		}
 		return null;
 	}
+
+	/**
+     * Returns the ImageFormat object for a given image file path
+	 *
+	 * @param path absolute file-path of the image
+	 * @return the image format for the image
+	 * @throws SteganoImageException
+	 */
+	public static ImageFormat getImageFormat(String path) throws SteganoImageException {
+		ImageFormat image = null;
+		if (path != null) {
+			String extension = FileUtils.getFileExtension(path);
+			if (getRegisteredImageExtensions().containsKey(extension)) {
+				image = newInstance(extension);
+				image.load(new File(path));
+			}
+		}
+		return image;
+	}
+
+	public String getFilePath() {
+		return file.getAbsolutePath();
+	}
 }
diff --git a/src/main/java/org/stegosuite/image/format/JPGImage.java b/src/main/java/org/stegosuite/image/format/JPGImage.java
index 58f87dd..813e818 100644
--- a/src/main/java/org/stegosuite/image/format/JPGImage.java
+++ b/src/main/java/org/stegosuite/image/format/JPGImage.java
@@ -1,9 +1,9 @@
 package org.stegosuite.image.format;
 
-import java.io.File;
-
 import org.stegosuite.model.exception.SteganoImageException;
 
+import java.io.File;
+
 public class JPGImage
 		extends ImageFormat {
 
diff --git a/src/main/java/org/stegosuite/image/format/PNGImage.java b/src/main/java/org/stegosuite/image/format/PNGImage.java
new file mode 100644
index 0000000..8de07b2
--- /dev/null
+++ b/src/main/java/org/stegosuite/image/format/PNGImage.java
@@ -0,0 +1,29 @@
+package org.stegosuite.image.format;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Represent PNG-images and provides methods to manipulate PNG pixels
+ *
+ */
+public class PNGImage
+		extends ImageFormat {
+
+	private static final Logger LOG = LoggerFactory.getLogger(PNGImage.class);
+
+	public static final String FILE_EXTENSION = "png";
+
+	@Override
+	public String getFileExtension() {
+		return FILE_EXTENSION;
+	}
+
+	@Override
+	public void setBufferedImage(BufferedImage image) {
+		super.setBufferedImage(image);
+		LOG.debug("Width, height, type: {}*{}*{}", getWidth(), getHeight(), this.image.getType());
+	}
+}
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/james/Jpeg.java b/src/main/java/org/stegosuite/image/jpgtemp/james/Jpeg.java
index 4e89909..581d305 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/james/Jpeg.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/james/Jpeg.java
@@ -7,8 +7,7 @@
 
 package org.stegosuite.image.jpgtemp.james; // westfeld
 
-import java.awt.Image;
-import java.awt.Toolkit;
+import java.awt.*;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/james/JpegEncoder.java b/src/main/java/org/stegosuite/image/jpgtemp/james/JpegEncoder.java
index 59307f8..9df16c0 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/james/JpegEncoder.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/james/JpegEncoder.java
@@ -19,17 +19,18 @@
 // password switch
 package org.stegosuite.image.jpgtemp.james;
 
-import java.awt.Frame;
-import java.awt.Image;
-import java.awt.MediaTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.image.jpgtemp.net.f5.crypt.F5Random;
+import org.stegosuite.image.jpgtemp.net.f5.crypt.Permutation;
+import org.stegosuite.ui.cli.CliParser;
+
+import java.awt.*;
 import java.io.BufferedOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-import org.stegosuite.image.jpgtemp.net.f5.crypt.F5Random;
-import org.stegosuite.image.jpgtemp.net.f5.crypt.Permutation;
-
 /**
  * JpegEncoder - The JPEG main program which performs a jpeg compression of an image.
  */
@@ -37,6 +38,8 @@ import org.stegosuite.image.jpgtemp.net.f5.crypt.Permutation;
 public class JpegEncoder
 		extends Frame {
 
+	private static final Logger LOG = LoggerFactory.getLogger(JpegEncoder.class);
+
 	Thread runner;
 
 	BufferedOutputStream outStream;
@@ -176,8 +179,8 @@ public class JpegEncoder
 		}
 		final int coeff[] = new int[coeffCount];
 
-		System.out.println("DCT/quantisation starts");
-		System.out.println(this.imageWidth + " x " + this.imageHeight);
+		//LOG.debug("DCT/quantisation starts");
+		LOG.debug(this.imageWidth + " x " + this.imageHeight);
 		for (r = 0; r < MinBlockHeight; r++) {
 			for (c = 0; c < MinBlockWidth; c++) {
 				xpos = c * 8;
@@ -245,7 +248,7 @@ public class JpegEncoder
 				}
 			}
 		}
-		System.out.println("got " + coeffCount + " DCT AC/DC coefficients");
+//		LOG.debug("got " + coeffCount + " DCT AC/DC coefficients");
 		int _changed = 0;
 		int _embedded = 0;
 		int _examined = 0;
@@ -272,11 +275,11 @@ public class JpegEncoder
 		_expected = _large + (int) (0.49 * _one);
 		//
 		// System.out.println("zero="+_zero);
-		System.out.println("one=" + _one);
-		System.out.println("large=" + _large);
+//		LOG.debug("one=" + _one);
+//		LOG.debug("large=" + _large);
 		//
-		System.out.println("expected capacity: " + _expected + " bits");
-		System.out.println("expected capacity with");
+		LOG.debug("expected capacity: " + _expected + " bits");
+//		System.out.println("expected capacity with");
 		for (i = 1; i < 8; i++) {
 			int usable, changed, n;
 			n = (1 << i) - 1;
@@ -292,18 +295,18 @@ public class JpegEncoder
 				break;
 			}
 			if (i == 1) {
-				System.out.print("default");
+//				LOG.debug("default");
 			} else {
-				System.out.print("(1, " + n + ", " + i + ")");
+//				LOG.debug("(1, " + n + ", " + i + ")");
 			}
-			System.out.println(" code: " + usable + " bytes (efficiency: " + usable * 8 / changed + "."
-					+ usable * 80 / changed % 10 + " bits per change)");
+//			LOG.debug(" code: " + usable + " bytes (efficiency: " + usable * 8 / changed + "."
+//					+ usable * 80 / changed % 10 + " bits per change)");
 		}
 
 		// westfeld
 		if (this.embeddedData != null) {
 			// Now we embed the secret data in the permutated sequence.
-			System.out.println("Permutation starts");
+//			LOG.debug("Permutation starts");
 			final F5Random random = new F5Random(this.password.getBytes());
 			final Permutation permutation = new Permutation(coeffCount, random);
 			int nextBitToEmbed = 0;
@@ -317,7 +320,7 @@ public class JpegEncoder
 			} catch (final Exception e) {
 				e.printStackTrace();
 			}
-			System.out.print("Embedding of " + (byteToEmbed * 8 + 32) + " bits (" + byteToEmbed + "+4 bytes) ");
+//			LOG.debug("Embedding of " + (byteToEmbed * 8 + 32) + " bits (" + byteToEmbed + "+4 bytes) ");
 			// We use the most significant byte for the 1 of n
 			// code, and reserve one extra bit for future use.
 			if (byteToEmbed > 0x007fffff) {
@@ -345,10 +348,10 @@ public class JpegEncoder
 					this.n++;
 					break;
 				case 1:
-					System.out.println("using default code");
+//					LOG.debug("using default code");
 					break;
 				default:
-					System.out.println("using (1, " + this.n + ", " + k + ") code");
+//					LOG.debug("using (1, " + this.n + ", " + k + ") code");
 			}
 			byteToEmbed |= k << 24; // store k in the status word
 			// Since shuffling cannot hide the distribution, the
@@ -443,7 +446,7 @@ public class JpegEncoder
 							if (j >= coeffCount) {
 								// in rare cases the estimated capacity is too
 								// small
-								System.out.println("Capacity exhausted.");
+								LOG.info("Capacity exhausted.");
 								break embeddingLoop;
 							}
 							shuffledIndex = permutation.getShuffled(j);
@@ -536,14 +539,14 @@ public class JpegEncoder
 				}
 			}
 			if (_examined > 0) {
-				System.out.println(_examined + " coefficients examined");
+//				LOG.debug(_examined + " coefficients examined");
 			}
-			System.out.println(_changed + " coefficients changed (efficiency: " + _embedded / _changed + "."
-					+ _embedded * 10 / _changed % 10 + " bits per change)");
-			System.out.println(_thrown + " coefficients thrown (zeroed)");
-			System.out.println(_embedded + " bits (" + _embedded / 8 + " bytes) embedded");
+//			LOG.debug(_changed + " coefficients changed (efficiency: " + _embedded / _changed + "."
+//					+ _embedded * 10 / _changed % 10 + " bits per change)");
+//			LOG.debug(_thrown + " coefficients thrown (zeroed)");
+//			LOG.debug(_embedded + " bits (" + _embedded / 8 + " bytes) embedded");
 		}
-		System.out.println("Starting Huffman Encoding.");
+//		LOG.debug("Starting Huffman Encoding.");
 		// Do the Huffman Encoding now.
 		shuffledIndex = 0;
 		for (r = 0; r < MinBlockHeight; r++) {
@@ -713,7 +716,7 @@ public class JpegEncoder
 		try {
 			out.write(data, 0, 2);
 		} catch (final IOException e) {
-			System.out.println("IO Error: " + e.getMessage());
+			LOG.info("IO Error: " + e.getMessage());
 		}
 	}
 
@@ -768,8 +771,8 @@ public class JpegEncoder
 		}
 		final int coeff[] = new int[coeffCount];
 
-		System.out.println("DCT/quantisation starts");
-		System.out.println(this.imageWidth + " x " + this.imageHeight);
+//		LOG.debug("DCT/quantisation starts");
+//		LOG.debug(this.imageWidth + " x " + this.imageHeight);
 		for (r = 0; r < MinBlockHeight; r++) {
 			for (c = 0; c < MinBlockWidth; c++) {
 				xpos = c * 8;
@@ -837,7 +840,7 @@ public class JpegEncoder
 				}
 			}
 		}
-		System.out.println("got " + coeffCount + " DCT AC/DC coefficients");
+//		LOG.debug("got " + coeffCount + " DCT AC/DC coefficients");
 		int _changed = 0;
 		int _embedded = 0;
 		int _examined = 0;
@@ -864,10 +867,10 @@ public class JpegEncoder
 		_expected = _large + (int) (0.49 * _one);
 		//
 		// System.out.println("zero="+_zero);
-		System.out.println("one=" + _one);
-		System.out.println("large=" + _large);
+//		LOG.debug("one=" + _one);
+//		LOG.debug("large=" + _large);
 		//
-		System.out.println("expected capacity: " + _expected + " bits");
+//		LOG.debug("expected capacity: " + _expected + " bits");
 		return (_expected / 8);
 	}
 }
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/james/JpegInfo.java b/src/main/java/org/stegosuite/image/jpgtemp/james/JpegInfo.java
index 2cf72d1..bc63903 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/james/JpegInfo.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/james/JpegInfo.java
@@ -14,8 +14,7 @@
 
 package org.stegosuite.image.jpgtemp.james;
 
-import java.awt.AWTException;
-import java.awt.Image;
+import java.awt.*;
 import java.awt.image.PixelGrabber;
 
 /**
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Embed.java b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Embed.java
index 632a851..8da56cf 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Embed.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Embed.java
@@ -1,14 +1,13 @@
 package org.stegosuite.image.jpgtemp.net.f5;
 
-import java.awt.Image;
-import java.awt.Toolkit;
+import org.stegosuite.image.jpgtemp.james.JpegEncoder;
+
+import java.awt.*;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import org.stegosuite.image.jpgtemp.james.JpegEncoder;
-
 public class Embed {
 
 	public static void main(final String args[]) {
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Extract.java b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Extract.java
index 96e440e..7021287 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Extract.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/Extract.java
@@ -1,18 +1,19 @@
 package org.stegosuite.image.jpgtemp.net.f5;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.image.embedding.jpg.JPGF5;
 import org.stegosuite.image.jpgtemp.net.f5.crypt.F5Random;
 import org.stegosuite.image.jpgtemp.net.f5.crypt.Permutation;
 import org.stegosuite.image.jpgtemp.net.f5.ortega.HuffmanDecode;
+import org.stegosuite.model.exception.SteganoKeyException;
+
+import java.io.*;
 
 public class Extract {
 
+	private static final Logger LOG = LoggerFactory.getLogger(Extract.class);
+	
 	private static File f; // carrier file
 
 	private static byte[] carrier; // carrier data
@@ -30,16 +31,16 @@ public class Extract {
 			34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 };
 
 	public static void extract(final InputStream fis, final int flength, final OutputStream fos, final String password)
-			throws IOException {
+			throws IOException, SteganoKeyException {
 		carrier = new byte[flength];
 		fis.read(carrier);
 		final HuffmanDecode hd = new HuffmanDecode(carrier);
-		System.out.println("Huffman decoding starts");
+//		LOG.debug("Huffman decoding starts");
 		coeff = hd.decode();
-		System.out.println("Permutation starts");
+//		LOG.debug("Permutation starts");
 		final F5Random random = new F5Random(password.getBytes());
 		final Permutation permutation = new Permutation(coeff.length, random);
-		System.out.println(coeff.length + " indices shuffled");
+//		LOG.debug(coeff.length + " indices shuffled");
 		int extractedByte = 0;
 		int availableExtractedBits = 0;
 		int extractedFileLength = 0;
@@ -47,7 +48,7 @@ public class Extract {
 		int shuffledIndex = 0;
 		int extractedBit;
 		int i;
-		System.out.println("Extraction starts");
+//		LOG.debug("Extraction starts");
 		// extract length information
 		for (i = 0; availableExtractedBits < 32; i++) {
 			shuffledIndex = permutation.getShuffled(i);
@@ -74,12 +75,12 @@ public class Extract {
 		k %= 32;
 		final int n = (1 << k) - 1;
 		extractedFileLength &= 0x007fffff;
-		System.out.println("Length of embedded file: " + extractedFileLength + " bytes");
+//		LOG.debug("Length of embedded file: " + extractedFileLength + " bytes");
 		availableExtractedBits = 0;
 		if (n > 0) {
 			int startOfN = i;
 			int hash;
-			System.out.println("(1, " + n + ", " + k + ") code used");
+//			LOG.debug("(1, " + n + ", " + k + ") code used");
 			extractingLoop: do {
 				// 1. read n places, and calculate k bits
 				hash = 0;
@@ -126,7 +127,7 @@ public class Extract {
 				}
 			} while (true);
 		} else {
-			System.out.println("Default code used");
+//			LOG.debug("Default code used");
 			for (; i < coeff.length; i++) {
 				shuffledIndex = permutation.getShuffled(i);
 				if (shuffledIndex % 64 == 0) {
@@ -156,8 +157,9 @@ public class Extract {
 			}
 		}
 		if (nBytesExtracted < extractedFileLength) {
-			System.out.println(
-					"Incomplete file: only " + nBytesExtracted + " of " + extractedFileLength + " bytes extracted");
+//			LOG.debug(
+//					"Incomplete file: only " + nBytesExtracted + " of " + extractedFileLength + " bytes extracted");
+			throw new SteganoKeyException();
 		}
 	}
 
@@ -179,7 +181,7 @@ public class Extract {
 					continue;
 				}
 				if (args.length < i + 1) {
-					System.out.println("Missing parameter for switch " + args[i]);
+					LOG.debug("Missing parameter for switch " + args[i]);
 					usage();
 					return;
 				}
@@ -188,7 +190,7 @@ public class Extract {
 				} else if (args[i].equals("-p")) {
 					password = args[i + 1];
 				} else {
-					System.out.println("Unknown switch " + args[i] + " ignored.");
+					LOG.debug("Unknown switch " + args[i] + " ignored.");
 				}
 				i++;
 			}
@@ -203,10 +205,10 @@ public class Extract {
 	}
 
 	static void usage() {
-		System.out.println("java Extract [Options] \"image.jpg\"");
-		System.out.println("Options:");
-		System.out.println("\t-p password (default: abc123)");
-		System.out.println("\t-e extractedFileName (default: output.txt)");
-		System.out.println("\nAuthor: Andreas Westfeld, westfeld at inf.tu-dresden.de");
+		LOG.debug("java Extract [Options] \"image.jpg\"");
+		LOG.debug("Options:");
+		LOG.debug("\t-p password (default: abc123)");
+		LOG.debug("\t-e extractedFileName (default: output.txt)");
+		LOG.debug("\nAuthor: Andreas Westfeld, westfeld at inf.tu-dresden.de");
 	}
 }
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/image/Bmp.java b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/image/Bmp.java
index 5a74557..cecd8b4 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/image/Bmp.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/image/Bmp.java
@@ -1,7 +1,6 @@
 package org.stegosuite.image.jpgtemp.net.f5.image;
 
-import java.awt.Image;
-import java.awt.Toolkit;
+import java.awt.*;
 import java.awt.image.MemoryImageSource;
 import java.io.BufferedInputStream;
 import java.io.FileInputStream;
diff --git a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/ortega/HuffmanDecode.java b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/ortega/HuffmanDecode.java
index ed869a1..3c7fedd 100644
--- a/src/main/java/org/stegosuite/image/jpgtemp/net/f5/ortega/HuffmanDecode.java
+++ b/src/main/java/org/stegosuite/image/jpgtemp/net/f5/ortega/HuffmanDecode.java
@@ -33,7 +33,8 @@ package org.stegosuite.image.jpgtemp.net.f5.ortega;
 // constructor changed to byte array parameter
 // added method rawDecode
 //
-import java.awt.TextArea;
+
+import java.awt.*;
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
diff --git a/src/main/java/org/stegosuite/model/payload/Payload.java b/src/main/java/org/stegosuite/model/payload/Payload.java
index 0c4680c..d605f77 100644
--- a/src/main/java/org/stegosuite/model/payload/Payload.java
+++ b/src/main/java/org/stegosuite/model/payload/Payload.java
@@ -1,16 +1,16 @@
 package org.stegosuite.model.payload;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.model.payload.block.Block;
+import org.stegosuite.util.ByteUtils;
+
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.util.ByteUtils;
-import org.stegosuite.model.payload.block.Block;
-
 /**
  * This class is the outer-most wrapper of the data that the user wants to embed or extract.
  */
@@ -39,7 +39,7 @@ public class Payload {
 	/**
 	 * List of all blocks that are embedded/extracted
 	 */
-	private List<Block> blocks = new ArrayList<Block>();
+	private List<Block> blocks = new ArrayList<>();
 
 	/**
 	 * User-provided password for encryption and decryption. If it's null at embedding time, the
@@ -67,6 +67,7 @@ public class Payload {
 
 	/**
 	 * Parses the byte stream and creates the original block instances (as in unserialize)
+	 * Requires the block classes to have an empty constructor
 	 *
 	 * @param blocksData
 	 */
@@ -102,10 +103,6 @@ public class Payload {
 		return blocks;
 	}
 
-	public void setBlocks(List<Block> blocks) {
-		this.blocks = blocks;
-	}
-
 	public String getEncryptionPassword() {
 		return encryptionPassword;
 	}
@@ -113,4 +110,25 @@ public class Payload {
 	public void setEncryptionPassword(String encryptionPassword) {
 		this.encryptionPassword = encryptionPassword;
 	}
+
+	public void removeBlock(Block aBlock) {
+		blocks.remove(aBlock);
+	}
+
+	public void setPassword(String password) {
+		setSteganoPassword(password);
+		setEncryptionPassword(password);
+	}
+
+	public void addBlock(Block block) {
+		getBlocks().add(block);
+	}
+
+	public Block getBlock(int blockIndex) {
+		return getBlocks().get(blockIndex);
+	}
+
+	public boolean hasNoBlocks() {
+		return getBlocks().isEmpty();
+	}
 }
diff --git a/src/main/java/org/stegosuite/model/payload/PayloadEmbedder.java b/src/main/java/org/stegosuite/model/payload/PayloadEmbedder.java
index e3e426c..a192c8e 100644
--- a/src/main/java/org/stegosuite/model/payload/PayloadEmbedder.java
+++ b/src/main/java/org/stegosuite/model/payload/PayloadEmbedder.java
@@ -1,19 +1,17 @@
 package org.stegosuite.model.payload;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stegosuite.model.exception.SteganoEmbedException;
+import org.stegosuite.util.ByteUtils;
+import org.stegosuite.util.CompressionUtils;
+import org.stegosuite.util.CryptoUtils;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.text.DecimalFormat;
 import java.util.Arrays;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.stegosuite.image.embedding.EmbeddingMethod;
-import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.util.ByteUtils;
-import org.stegosuite.image.util.CompressionUtils;
-import org.stegosuite.image.util.CryptoUtils;
-import org.stegosuite.model.exception.SteganoEmbedException;
-
 public class PayloadEmbedder {
 
 	private static final Logger LOG = LoggerFactory.getLogger(PayloadEmbedder.class);
@@ -27,14 +25,14 @@ public class PayloadEmbedder {
 
 	/**
 	 * Constructor
-	 *
 	 * @param payload The payload instance that hold the data to be embedded
+	 * @param capacity capacity according to the embeddingMethod
 	 * @throws SteganoEmbedException
 	 */
-	public <T extends ImageFormat> PayloadEmbedder(EmbeddingMethod<T> embeddingMethod, T image, Payload payload)
+	public PayloadEmbedder(Payload payload, int capacity)
 			throws SteganoEmbedException {
 
-		if (payload.getBlocks().isEmpty()) {
+		if (payload.hasNoBlocks()) {
 			throw new SteganoEmbedException("No data to embed");
 		}
 
@@ -68,7 +66,6 @@ public class PayloadEmbedder {
 					maxPayloadLength, data.length));
 		}
 
-		int capacity = embeddingMethod.capacity();
 		if (data.length > capacity) {
 			throw new SteganoEmbedException(String.format(
 					"Payload is too large. Maximum capacity for this carrier file is %d bytes but payload is %d bytes.",
diff --git a/src/main/java/org/stegosuite/model/payload/PayloadExtractor.java b/src/main/java/org/stegosuite/model/payload/PayloadExtractor.java
index 5213356..794bd0e 100644
--- a/src/main/java/org/stegosuite/model/payload/PayloadExtractor.java
+++ b/src/main/java/org/stegosuite/model/payload/PayloadExtractor.java
@@ -1,17 +1,17 @@
 package org.stegosuite.model.payload;
 
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.stegosuite.image.util.ByteUtils;
-import org.stegosuite.image.util.CompressionUtils;
-import org.stegosuite.image.util.CryptoUtils;
 import org.stegosuite.model.exception.SteganoEncryptionException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoKeyException;
+import org.stegosuite.util.ByteUtils;
+import org.stegosuite.util.CompressionUtils;
+import org.stegosuite.util.CryptoUtils;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
 
 public class PayloadExtractor {
 
diff --git a/src/main/java/org/stegosuite/model/payload/block/Block.java b/src/main/java/org/stegosuite/model/payload/block/Block.java
index c456d40..2540d8d 100644
--- a/src/main/java/org/stegosuite/model/payload/block/Block.java
+++ b/src/main/java/org/stegosuite/model/payload/block/Block.java
@@ -12,7 +12,7 @@ public abstract class Block {
 	/**
 	 * This map keeps track of all available block types
 	 */
-	private static final Map<Byte, Class<? extends Block>> identifiers = new HashMap<Byte, Class<? extends Block>>();
+	private static final Map<Byte, Class<? extends Block>> identifiers = new HashMap<>();
 
 	static {
 		identifiers.put(FileBlock.IDENTIFIER, FileBlock.class);
@@ -44,4 +44,7 @@ public abstract class Block {
 	 */
 	public abstract void unpack(byte[] data);
 
+	public boolean hasIdentifier(byte identifier) {
+		return getIdentifier() == identifier;
+	}
 }
diff --git a/src/main/java/org/stegosuite/model/payload/block/FileBlock.java b/src/main/java/org/stegosuite/model/payload/block/FileBlock.java
index f4599ef..23952e5 100644
--- a/src/main/java/org/stegosuite/model/payload/block/FileBlock.java
+++ b/src/main/java/org/stegosuite/model/payload/block/FileBlock.java
@@ -1,13 +1,18 @@
 package org.stegosuite.model.payload.block;
 
+import org.stegosuite.util.ByteUtils;
+import org.stegosuite.util.FileUtils;
+
 import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 
-import org.stegosuite.image.util.ByteUtils;
+import static java.nio.file.StandardOpenOption.CREATE;
 
 /**
  * This block contains the contents of a file as well as its file name
@@ -16,6 +21,13 @@ public final class FileBlock
 		extends Block {
 
 	/**
+	 * Mandatory empty constructor
+	 * @see org.stegosuite.model.payload.Payload#unpack(byte[])
+	 */
+	@SuppressWarnings("unused")
+	public FileBlock() {}
+
+	/**
 	 * Unique number among all Block implementations
 	 */
 	public static final byte IDENTIFIER = 1;
@@ -30,8 +42,6 @@ public final class FileBlock
 	 */
 	private byte[] fileContent = null;
 
-	public FileBlock() {}
-
 	public FileBlock(String fileName) {
 		this.fileName = fileName;
 		try {
@@ -64,16 +74,31 @@ public final class FileBlock
 		return fileName;
 	}
 
-	public void setFileName(String fileName) {
-		this.fileName = fileName;
-	}
-
 	public byte[] getFileContent() {
 		return fileContent;
 	}
 
-	public void setFileContent(byte[] fileContent) {
-		this.fileContent = fileContent;
+	public long getSize() {
+		return FileUtils.getFileSize(fileName);
 	}
 
+	public boolean hasPath(String filename) {
+		String blockFilename = Paths.get(fileName).getFileName().toString();
+		return filename.equals(blockFilename);
+	}
+
+	public void saveFileTo(String extractionPath) {
+		try {
+			writeContentToFileIn(extractionPath);
+			// Update the filename, so that we can compute the file size later
+			fileName = extractionPath;
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void writeContentToFileIn(String extractionPath) throws IOException {
+		Path path = Paths.get(extractionPath);
+		Files.write(path, fileContent, CREATE);
+	}
 }
diff --git a/src/main/java/org/stegosuite/model/payload/block/MessageBlock.java b/src/main/java/org/stegosuite/model/payload/block/MessageBlock.java
index 49f5005..2dd723a 100644
--- a/src/main/java/org/stegosuite/model/payload/block/MessageBlock.java
+++ b/src/main/java/org/stegosuite/model/payload/block/MessageBlock.java
@@ -15,6 +15,11 @@ public final class MessageBlock
 
 	private String message = null;
 
+	/**
+	 * Mandatory empty constructor
+	 * @see org.stegosuite.model.payload.Payload#unpack(byte[])
+	 */
+	@SuppressWarnings("unused")
 	public MessageBlock() {}
 
 	public MessageBlock(String message) {
@@ -40,8 +45,4 @@ public final class MessageBlock
 		return message;
 	}
 
-	public void setMessage(String message) {
-		this.message = message;
-	}
-
 }
diff --git a/src/main/java/org/stegosuite/ui/cli/Arguments.java b/src/main/java/org/stegosuite/ui/cli/Arguments.java
deleted file mode 100644
index 1752353..0000000
--- a/src/main/java/org/stegosuite/ui/cli/Arguments.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*package org.stegosuite.ui.cli;
-
-import com.beust.jcommander.Parameter;
-
-public class Arguments {
-
-	@Parameter(names = { "-h", "--help" }, help = true, description = "Displays this message.")
-	private boolean help = false;
-
-	@Parameter(names = { "-V", "--verbose" }, description = "Show more information.")
-	private boolean verbose = false;
-
-	@Parameter(names = {
-			"--disable-noise-detection" }, description = "Disables the automatic exclusion of homogeneous areas in the image.")
-	private boolean disableNoiseDetection = false;
-
-	@Parameter(names = "-e", description = "Embed data into image")
-	private boolean embed = false;
-
-	@Parameter(names = "-x", description = "Extract data from image")
-	private boolean extract = false;
-
-	@Parameter(names = "-i", description = "Image for embedding/extracting")
-	private String steganogramPath;
-
-	@Parameter(names = "-k", description = "Secret key used for encrytion and embedding/extracting")
-	private String secretkey;
-
-	@Parameter(names = { "-m", "--embedMessage" }, description = "Message to embed")
-	private String message = "This is the default secret message";
-
-	@Parameter(names = { "-f", "--embedFile" }, description = "File to embed")
-	private String filePath;
-
-	boolean isDisableNoiseDetection() {
-		return disableNoiseDetection;
-	}
-
-	boolean isHelp() {
-		return help;
-	}
-
-	boolean isVerbose() {
-		return verbose;
-	}
-
-	boolean isEmbed() {
-		return embed;
-	}
-
-	boolean isExtract() {
-		return extract;
-	}
-
-	String getSteganogramPath() {
-		return steganogramPath;
-	}
-
-	String getFilePath() {
-		return filePath;
-	}
-
-	String getSecretKey() {
-		return secretkey;
-	}
-
-	String getMessage() {
-		return message;
-	}
-
-}*/
diff --git a/src/main/java/org/stegosuite/ui/cli/Cli.java b/src/main/java/org/stegosuite/ui/cli/Cli.java
index 91831e5..308c03d 100644
--- a/src/main/java/org/stegosuite/ui/cli/Cli.java
+++ b/src/main/java/org/stegosuite/ui/cli/Cli.java
@@ -1,146 +1,157 @@
-/*package org.stegosuite.ui.cli;
+package org.stegosuite.ui.cli;
 
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
+import java.util.List;
 
+import org.apache.commons.cli.CommandLine;
+import org.eclipse.swt.graphics.ImageData;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.stegosuite.application.StegosuitePresenter;
+import org.stegosuite.application.StegosuiteUI;
+import org.stegosuite.image.embedding.Visualizer;
 import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.util.FileUtils;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
 import org.stegosuite.model.exception.SteganoImageException;
-import org.stegosuite.model.payload.Payload;
-import org.stegosuite.model.payload.block.Block;
-import org.stegosuite.model.payload.block.FileBlock;
-import org.stegosuite.model.payload.block.MessageBlock;
-import org.stegosuite.ui.gui.embedding.Embedding;
-import org.stegosuite.ui.gui.embedding.EmbeddingFactory;
+import org.stegosuite.ui.gui.ImageUtils;
 
-import com.beust.jcommander.JCommander;
+public class Cli
+		implements
+		StegosuiteUI {
 
-import ch.qos.logback.classic.Level;
-
-public class Cli {
-
-	private String[] args;
-	private Arguments arguments;
 	private static final Logger LOG = LoggerFactory.getLogger(Cli.class);
-
-	public Cli(String[] s) {
-		args = s;
-		parseCli();
+	private ImageFormat image;
+	private StegosuitePresenter presenter;
+
+	private String getSteganogramPath(CommandLine cmd) {
+		if (cmd.getArgs().length > 0) {
+			return cmd.getArgs()[0];
+		} else {
+			return null;
+		}
 	}
 
-	private void parseCli() {
-		arguments = new Arguments();
-		JCommander jCommander = new JCommander(arguments, args);
-		jCommander.setProgramName("stegosuite");
-		if (arguments.isVerbose()) {
-			ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory
-					.getLogger(Logger.ROOT_LOGGER_NAME);
-			rootLogger.setLevel(Level.DEBUG);
-		}
-		if (arguments.isEmbed()) {
-			embed();
-		} else if (arguments.isExtract()) {
-			extract();
-		} else if (arguments.isHelp()) {
-			jCommander.usage();
+	public void embed(CommandLine cmd) {
+		String steganogramPath = getSteganogramPath(cmd);
+		if (steganogramPath == null)
+			return;
+		if (!validImageFormat(steganogramPath))
 			return;
+
+		pointFilter(cmd);
+
+		String message = cmd.getOptionValue("m");
+		if (message != null) {
+			presenter.addMessageToPayload(message);
+		}
+
+		String[] files = cmd.getOptionValues("f");
+		if (files != null) {
+			for (String string : files) {
+				presenter.addFileToPayload(string);
+			}
 		}
+
+		String key = cmd.getOptionValue("k");
+
+		embed(key);
 	}
 
-	private void embed() {
-		String steganogramPath = arguments.getSteganogramPath();
-		String filePath = arguments.getFilePath();
+	private void embed(String key) {
+		LOG.info("Embedding data...");
+		presenter.embed(key);
+	}
 
-		Payload payload = new Payload();
-		payload.getBlocks().add(new MessageBlock(arguments.getMessage()));
-		if (filePath != null) {
-			payload.getBlocks().add(new FileBlock(filePath));
-		}
+	public void extract(CommandLine cmd) {
+		String steganogramPath = getSteganogramPath(cmd);
+		if (steganogramPath == null)
+			return;
+		if (!validImageFormat(steganogramPath))
+			return;
 
-		payload.setSteganoPassword(arguments.getSecretKey());
-		payload.setEncryptionPassword(arguments.getSecretKey());
+		pointFilter(cmd);
+		String key = cmd.getOptionValue("k");
+		extract(key);
+	}
 
-		File steganogramFile = new File(steganogramPath);
-		String extension = FileUtils.getFileExtension(steganogramPath);
-		ImageFormat image = ImageFormat.newInstance(extension);
-		if (image == null) {
-			String supportedFormats = String.join(", ", ImageFormat.getRegisteredImageExtensions().keySet());
-			LOG.error("Error: Currently only these file types are supported: {}", supportedFormats);
+	private void extract(String key) {
+		LOG.info("Extracting data...");
+		presenter.extractUsing(key);
+	}
+
+	public void capacity(CommandLine cmd) {
+		String steganogramPath = getSteganogramPath(cmd);
+		if (steganogramPath == null)
+			return;
+		if (!validImageFormat(steganogramPath))
 			return;
+		pointFilter(cmd);
+		int capacity = presenter.getEmbeddingCapacity();
+		LOG.info("Capacity: {}", ImageUtils.formatSize(capacity));
+	}
+
+	private void pointFilter(CommandLine cmd) {
+		if (cmd.hasOption("disable-noise-detection")) {
+			presenter.setPointFilter(0);
+		} else {
+			presenter.setPointFilter(1);
 		}
+	}
 
-		Embedding embedding = EmbeddingFactory.getEmbedding(image);
-		embedding.setPointFilter(1);
-		if (arguments.isDisableNoiseDetection()) {
-			embedding.setPointFilter(0);
+	private boolean validImageFormat(String steganogramPath) {
+		image = getImageFormat(steganogramPath);
+		if (image == null) {
+			showFormatNotSupportedError();
+			return false;
 		}
+		presenter = new StegosuitePresenter(image, this);
+		return true;
+	}
 
+	private ImageFormat getImageFormat(String steganogramPath) {
 		try {
-			image.load(steganogramFile);
-			embedding.embed(payload, (embeddingMethod, embeddedImage) -> {
-				try {
-					String outputPath = FileUtils.addFileNameSuffix(steganogramFile.getAbsolutePath(), "_embed");
-					embeddedImage.save(new File(outputPath));
-				} catch (SteganoImageException e) {
-					e.printStackTrace();
-				}
-			});
-		} catch (SteganoEmbedException e) {
-			e.printStackTrace();
+			return ImageFormat.getImageFormat(steganogramPath);
 		} catch (SteganoImageException e) {
 			e.printStackTrace();
+			return null;
 		}
 	}
 
-	private void extract() {
-		String filePath = arguments.getSteganogramPath();
+	private void showFormatNotSupportedError() {
+		LOG.error("Error: Currently only these file types are supported: {}", supportedFormats());
+	}
 
-		Payload payload = new Payload();
-		payload.setSteganoPassword(arguments.getSecretKey());
-		payload.setEncryptionPassword(arguments.getSecretKey());
+	private String supportedFormats() {
+		return String.join(", ", ImageFormat.getSupportedFormats());
+	}
 
-		String extension = FileUtils.getFileExtension(filePath);
-		ImageFormat image = ImageFormat.newInstance(extension);
-		if (image == null) {
-			LOG.error("Error: Currently only gif and bmp file types are supported.");
-			return;
-		}
+	@Override
+	public void showEmbeddingError(SteganoEmbedException e) {
+		LOG.info(e.getMessage());
+	}
 
-		File file = new File(filePath);
-		try {
-			image.load(file);
-			Embedding embedding = EmbeddingFactory.getEmbedding(image);
-			embedding.setPointFilter(1);
-			if (arguments.isDisableNoiseDetection()) {
-				embedding.setPointFilter(0);
+	@Override
+	public void showExtractingError(SteganoExtractException e) {
+		e.printStackTrace();
+	}
+
+	@Override
+	public void extractingCompleted(String extractedMessage, List<String> filePaths, Visualizer visualizer,
+			ImageData imageData) {
+		LOG.info("Extracting completed");
+		if (extractedMessage != null) {
+			LOG.info("Extracted message: {}", extractedMessage);
+		}
+		if (!filePaths.isEmpty()) {
+			for (String string : filePaths) {
+				LOG.info("Extracted file saved to {}", string);
 			}
-			embedding.extract(payload, (embeddingMethod) -> {
-				for (Block block : payload.getBlocks()) {
-					if (block.getIdentifier() == FileBlock.IDENTIFIER) {
-						String outPath = FileUtils.changeFileName(filePath, ((FileBlock) block).getFileName());
-						LOG.debug("Outputpath: {}", outPath);
-						try {
-							Files.write(Paths.get(outPath), ((FileBlock) block).getFileContent(),
-									StandardOpenOption.CREATE);
-						} catch (IOException e) {
-							e.printStackTrace();
-						}
-					} else if (block.getIdentifier() == MessageBlock.IDENTIFIER) {
-						LOG.info("Extracted message:{}", ((MessageBlock) block).getMessage());
-					}
-				}
-			});
-		} catch (SteganoExtractException e) {
-			e.printStackTrace();
-		} catch (SteganoImageException e) {
-			e.printStackTrace();
 		}
 	}
-}*/
+
+	@Override
+	public void embeddingCompleted(ImageFormat embeddedImage, String outputPath, Visualizer visualizer) {}
+
+	@Override
+	public void addPayloadFile(String filename, String extension, long fileSize) {}
+}
diff --git a/src/main/java/org/stegosuite/ui/cli/CliParser.java b/src/main/java/org/stegosuite/ui/cli/CliParser.java
new file mode 100644
index 0000000..5e3c174
--- /dev/null
+++ b/src/main/java/org/stegosuite/ui/cli/CliParser.java
@@ -0,0 +1,71 @@
+package org.stegosuite.ui.cli;
+
+import ch.qos.logback.classic.Level;
+import org.apache.commons.cli.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CliParser {
+
+	private String[] args = null;
+	private static final Logger LOG = LoggerFactory.getLogger(CliParser.class);
+
+	public CliParser(String[] s) {
+		args = s;
+	}
+
+	public void parse() {
+		CommandLineParser parser = new DefaultParser();
+
+		Options options = new Options();
+		options.addOption("d", "debug", false, "show debug information");
+		options.addOption("e", "embed", false, "embed data into image");
+		options.addOption("x", "extract", false, "extract data from image");
+		options.addOption("m", "message", true, "message to embed");
+		options.addOption("k", "key", true, "secret key used for encrytion and hiding");
+		options.addOption("c", "capacity", false, "shows the maximum amount of data which can be embededded");
+		options.addOption("h", "help", false, "displays this help message");
+
+		Option files = Option.builder("f").hasArgs().longOpt("files").desc("files to embed").build();
+
+		options.addOption(files);
+
+//		Option stegokey = Option.builder().hasArg().longOpt("stegokey")
+//				.desc("the secret stego key used for hiding the content").build();
+//		Option cryptokey = Option.builder().hasArg().longOpt("encryptionkey")
+//				.desc("the secret key used for encryption of the content").build();
+		Option noNoise = Option.builder().longOpt("disable-noise-detection")
+				.desc("disables the automatic avoidance of homogeneous areas").build();
+//		Option extractPath = Option.builder().longOpt("extraction-path").desc("the folder to store extracted files")
+//				.build();
+
+//		options.addOption(stegokey); // TODO
+//		options.addOption(cryptokey); // TODO
+		options.addOption(noNoise);
+//		options.addOption(extractPath); // TODO
+
+		try {
+			CommandLine line = parser.parse(options, args);
+			Cli cli = new Cli();
+
+			if (line.hasOption("d")) { 
+				ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory
+						.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
+				root.setLevel(Level.DEBUG);
+			}
+
+			if (line.hasOption("e")) {
+				cli.embed(line);
+			} else if (line.hasOption("x")) {
+				cli.extract(line);
+			} else if (line.hasOption("c")) {
+				cli.capacity(line);
+			} else if (line.hasOption("h")) {
+				HelpFormatter formatter = new HelpFormatter();
+				formatter.printHelp("stegosuite", options);
+			}
+		} catch (ParseException exp) {
+			LOG.error("Unexpected exception:" + exp.getMessage());
+		}
+	}
+}
diff --git a/src/main/java/org/stegosuite/ui/gui/EmbedUi.java b/src/main/java/org/stegosuite/ui/gui/EmbedUi.java
index dcd421e..f3df3b8 100644
--- a/src/main/java/org/stegosuite/ui/gui/EmbedUi.java
+++ b/src/main/java/org/stegosuite/ui/gui/EmbedUi.java
@@ -1,12 +1,7 @@
 package org.stegosuite.ui.gui;
 
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-import java.util.Iterator;
-import java.util.ListIterator;
+import java.util.List;
+import java.util.Optional;
 import java.util.ResourceBundle;
 
 import org.eclipse.swt.SWT;
@@ -41,27 +36,21 @@ import org.eclipse.swt.widgets.TableItem;
 import org.eclipse.swt.widgets.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.stegosuite.image.embedding.EmbeddingMethod;
+import org.stegosuite.application.EmbeddingProgressObserver;
+import org.stegosuite.application.StegosuitePresenter;
+import org.stegosuite.application.StegosuiteUI;
 import org.stegosuite.image.embedding.EmbeddingProgress;
-import org.stegosuite.image.format.GIFImage;
+import org.stegosuite.image.embedding.Visualizer;
 import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.util.FileUtils;
 import org.stegosuite.model.exception.SteganoEmbedException;
 import org.stegosuite.model.exception.SteganoExtractException;
-import org.stegosuite.model.exception.SteganoImageException;
-import org.stegosuite.model.payload.Payload;
-import org.stegosuite.model.payload.block.Block;
 import org.stegosuite.model.payload.block.FileBlock;
-import org.stegosuite.model.payload.block.MessageBlock;
 import org.stegosuite.ui.gui.ImageContainer.ImageState;
-import org.stegosuite.ui.gui.embedding.Embedding;
-import org.stegosuite.ui.gui.embedding.EmbeddingFactory;
-import org.stegosuite.ui.gui.embedding.MyGIFShuffle;
 
 /**
  * Contains the GUI for embedding/extracting data.
  */
-public class EmbedUi {
+public class EmbedUi implements StegosuiteUI {
 
 	private Composite compositeImage;
 	private Text passwordField;
@@ -69,42 +58,42 @@ public class EmbedUi {
 	private ImageContainer imageContainer;
 	private Label imageLabel, payloadFileCounter, payloadFileSize;
 	private int fileSizeSum = 0;
-	private Payload payload;
 	private Button embedButton, extractButton;
-	private ImageFormat image;
 	private Cursor cursor;
-	private Embedding embedding;
 	private ProgressBar progressBar;
-	// private Text pathText;
 	private Table fileTable;
 	private StyledText messageField;
+	private Composite composite;
 	private static final Logger LOG = LoggerFactory.getLogger(EmbedUi.class);
 	private final ResourceBundle L = ResourceBundle.getBundle("Messages");
+	private StegosuitePresenter presenter;
 
-	public EmbedUi(Composite c1, GuiComponents components) {
+	public EmbedUi(Composite composite, GuiComponents components) {
+		this.composite = composite;
 
-		GuiComponents myComponents = components;
-		c1.setLayout(new FillLayout());
+		initializeGui(components);
+	}
+
+	private void initializeGui(GuiComponents components) {
+		composite.setLayout(new FillLayout());
 
-		Composite compositeControls = myComponents.createControlsComposite(c1);
-		Composite fileEmbedding = myComponents.createFileEmbedding(compositeControls);
+		Composite compositeControls = components.createControlsComposite(composite);
+		Composite fileEmbedding = components.createFileEmbedding(compositeControls);
 
 		messageField = (StyledText) compositeControls.getChildren()[0];
 		fileTable = (Table) fileEmbedding.getChildren()[2];
 		payloadFileCounter = (Label) fileEmbedding.getChildren()[0];
 		payloadFileSize = (Label) fileEmbedding.getChildren()[1];
-		passwordField = myComponents.createPasswordField(compositeControls);
+		passwordField = components.createPasswordField(compositeControls);
 
-		embedButton = myComponents.createMainButton(compositeControls, L.getString("embed_button"));
-		extractButton = myComponents.createMainButton(compositeControls, L.getString("extract_button"));
+		embedButton = components.createMainButton(compositeControls, L.getString("embed_button"));
+		extractButton = components.createMainButton(compositeControls, L.getString("extract_button"));
 		((GridData) extractButton.getLayoutData()).horizontalAlignment = SWT.END;
 
-		compositeImage = myComponents.createImageComposite(c1);
+		compositeImage = components.createImageComposite(composite);
 		imageLabel = new Label(compositeImage, SWT.NONE);
 		imageLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));
 
-		payload = new Payload();
-
 		compositeImage.addListener(SWT.Resize, event -> {
 			if (imageLabel.getImage() != null) {
 				imageLabel.setImage(imageContainer.scaleImage());
@@ -115,34 +104,30 @@ public class EmbedUi {
 		final DropTarget dropTarget = new DropTarget(fileTable, DND.DROP_MOVE);
 		dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
 		dropTarget.addDropListener(new DropTargetAdapter() {
-
 			@Override
 			public void drop(final DropTargetEvent event) {
 				final String[] filenames = (String[]) event.data;
-				if (filenames[0] != null) {
-					payload.getBlocks().add(new FileBlock(filenames[0]));
-					addPayloadFile(filenames[0]);
+				String filename = filenames[0];
+				if (filename != null) {
+					presenter.addFileToPayload(filename);
 				}
 			}
 		});
 
-		Menu menu = new Menu(c1.getShell(), SWT.POP_UP);
+		Menu menu = new Menu(composite.getShell(), SWT.POP_UP);
 		fileTable.setMenu(menu);
 
-		MenuItem item1 = new MenuItem(menu, SWT.PUSH);
-		item1.setText(L.getString("files_menu_delete"));
-		item1.addListener(SWT.Selection, event -> {
-			removeSelectedPayloadFile();
-		});
+		MenuItem deleteFileMenu = new MenuItem(menu, SWT.PUSH);
+		deleteFileMenu.setText(L.getString("files_menu_delete"));
+		deleteFileMenu.addListener(SWT.Selection, event -> removeSelectedPayloadFile());
 
-		MenuItem item2 = new MenuItem(menu, SWT.PUSH);
-		item2.setText(L.getString("files_menu_add"));
-		item2.addListener(SWT.Selection, event -> {
-			final FileDialog dlg = new FileDialog(c1.getShell(), SWT.OPEN);
+		MenuItem addFileMenu = new MenuItem(menu, SWT.PUSH);
+		addFileMenu.setText(L.getString("files_menu_add"));
+		addFileMenu.addListener(SWT.Selection, event -> {
+			final FileDialog dlg = new FileDialog(composite.getShell(), SWT.OPEN);
 			final String filePath = dlg.open();
 			if (filePath != null) {
-				payload.getBlocks().add(new FileBlock(filePath));
-				addPayloadFile(filePath);
+				presenter.addFileToPayload(filePath);
 			}
 		});
 
@@ -157,228 +142,151 @@ public class EmbedUi {
 		});
 
 		embedButton.addListener(SWT.Selection, event -> {
-			payload.getBlocks().add(new MessageBlock(messageField.getText()));
-
-			if (!passwordField.getText().isEmpty()) {
-				payload.setSteganoPassword(passwordField.getText());
-				payload.setEncryptionPassword(passwordField.getText());
-			} else {
-				payload.setSteganoPassword(null);
-				payload.setEncryptionPassword(null);
-			}
-
-			progressBar = new ProgressBar(compositeControls, SWT.SMOOTH);
-			progressBar.setSelection(0);
+			startEmbedding(compositeControls);
+		});
 
-			GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
-			data.horizontalSpan = 2;
-			progressBar.setLayoutData(data);
-			compositeControls.layout(true, true);
+		extractButton.addListener(SWT.Selection, event -> {
+			startExtraction(compositeControls);
+		});
+	}
 
-			adjustWindowSize();
+	private void startEmbedding(Composite compositeControls) {
+		progressBar = new ProgressBar(compositeControls, SWT.SMOOTH);
+		progressBar.setSelection(0);
 
-			EmbeddingProgress progress = new EmbeddingProgress();
-			new EmbeddingProgressObserver(progressBar, progress);
+		GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
+		data.horizontalSpan = 2;
+		progressBar.setLayoutData(data);
+		compositeControls.layout(true, true);
 
-			Gui.setStatusBarMsg("Embedding data...");
-			embedButton.setEnabled(false);
+		adjustWindowSize();
 
-			cursor = new Cursor(Display.getDefault(), SWT.CURSOR_WAIT);
-			c1.getShell().setCursor(cursor);
-			if (image.getClass().equals(GIFImage.class)) {
-				LOG.debug("Trying to embed with GIFShuffle.");
-			}
-			new Thread(() -> {
-				embed(progress, c1);
-			}).start();
-		});
+		Gui.setStatusBarMsg("Embedding data...");
+		embedButton.setEnabled(false);
 
-		extractButton.addListener(SWT.Selection, event -> {
+		cursor = new Cursor(Display.getDefault(), SWT.CURSOR_WAIT);
+		composite.getShell().setCursor(cursor);
 
-			if (!passwordField.getText().isEmpty()) {
-				payload.setSteganoPassword(passwordField.getText());
-				payload.setEncryptionPassword(passwordField.getText());
-			} else {
-				payload.setSteganoPassword(null);
-				payload.setEncryptionPassword(null);
-			}
+		EmbeddingProgress progress = new EmbeddingProgress();
+		new EmbeddingProgressObserver(progressBar, progress);
 
-			progressBar = new ProgressBar(compositeControls, SWT.SMOOTH);
-			progressBar.setSelection(0);
+		String message = messageField.getText();
+		String password = getEnteredPassword();
+		
+		presenter.addMessageToPayload(message);
 
-			GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
-			data.horizontalSpan = 2;
-			progressBar.setLayoutData(data);
-			compositeControls.layout(true, true);
+		runInNewThread(() -> presenter.embedNotifying(progress, password));
+	}
 
-			adjustWindowSize();
+	private void startExtraction(Composite compositeControls) {
+		progressBar = new ProgressBar(compositeControls, SWT.SMOOTH);
+		progressBar.setSelection(0);
 
-			EmbeddingProgress progress = new EmbeddingProgress();
-			new EmbeddingProgressObserver(progressBar, progress);
+		GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
+		data.horizontalSpan = 2;
+		progressBar.setLayoutData(data);
+		compositeControls.layout(true, true);
 
-			Gui.setStatusBarMsg("Extracting data...");
-			extractButton.setEnabled(false);
+		adjustWindowSize();
 
-			cursor = new Cursor(Display.getDefault(), SWT.CURSOR_WAIT);
-			c1.getShell().setCursor(cursor);
-			new Thread(() -> {
-				extract(progress, c1);
-			}).start();
-		});
-	}
+		EmbeddingProgress progress = new EmbeddingProgress();
+		new EmbeddingProgressObserver(progressBar, progress);
 
-	private void extract(EmbeddingProgress progress, Composite c1) {
-		try {
-			embedding.extract(payload, progress, (embeddingMethod) -> {
-				Display.getDefault().asyncExec(() -> extractingComplete(embeddingMethod));
-			});
-		} catch (SteganoExtractException e) {
+		Gui.setStatusBarMsg("Extracting data...");
+		extractButton.setEnabled(false);
 
-			if (embedding.getEmbeddingMethod() instanceof MyGIFShuffle) {
-				LOG.debug("Extracting using GIFShuffle failed. Switching to GIFSortedColorTable.");
-				embedding = EmbeddingFactory.GIF_SORTED.newEmbedding(image);
-				extract(progress, c1);
+		cursor = new Cursor(Display.getDefault(), SWT.CURSOR_WAIT);
+		composite.getShell().setCursor(cursor);
 
-			} else {
-				Display.getDefault().syncExec(() -> {
-					MessageBox dialog = new MessageBox(c1.getShell(), SWT.ICON_ERROR | SWT.OK);
-					dialog.setText("Error");
-					dialog.setMessage(e.getMessage());
-					dialog.open();
-					extractButton.setEnabled(true);
-					cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
-					compositeImage.getShell().setCursor(cursor);
-					progressBar.dispose();
-					Gui.setStatusBarMsg("Extracting aborted.");
+		String password = getEnteredPassword();
+		runInNewThread(() -> presenter.extractNotifying(progress, password));
+	}
 
-				});
-			}
+	private String getEnteredPassword() {
+		if (passwordField.getText().isEmpty()) {
+			return null;
+		} else {
+			return passwordField.getText();
 		}
 	}
 
-	private void embed(EmbeddingProgress progress, Composite c1) {
-		try {
-			embedding.embed(payload, progress, (embeddingMethod, embeddedImage) -> {
-				Display.getDefault().asyncExec(() -> embeddingComplete(embeddingMethod, embeddedImage));
-			});
-		} catch (SteganoEmbedException e) {
+	private void runInNewThread(Runnable runnable) {
+		new Thread(runnable).start();
+	}
 
-			if (embedding.getEmbeddingMethod() instanceof MyGIFShuffle) {
-				LOG.debug("Embedding with GIFShuffle failed. Switching to GIFSortedColorTable.");
-				embedding = EmbeddingFactory.GIF_SORTED.newEmbedding(image);
-				embed(progress, c1);
+	@Override
+	public void showEmbeddingError(SteganoEmbedException e) {
+		displayError("Embedding aborted.", embedButton, e.getMessage());
+	}
 
-			} else {
-				Display.getDefault().syncExec(() -> {
-					MessageBox dialog = new MessageBox(c1.getShell(), SWT.ICON_ERROR | SWT.OK);
-					dialog.setText("Error");
-					dialog.setMessage(e.getMessage());
-					dialog.open();
-					embedButton.setEnabled(true);
-					cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
-					compositeImage.getShell().setCursor(cursor);
-					progressBar.dispose();
-					Gui.setStatusBarMsg("Embedding aborted.");
+	@Override
+	public void showExtractingError(SteganoExtractException e) {
+		displayError("Extracting aborted.", extractButton, e.getMessage());
+	}
 
-				});
+	@Override
+	public void extractingCompleted(String extractedMessage, List<String> filePaths, Visualizer visualizer, ImageData imageData) {
+		runInGuiThread(() -> {
+			messageField.setText(extractedMessage);
+			
+			String status = "Extracting completed.";
+			if (!filePaths.isEmpty()) {
+				status += " Extracted file saved to " + filePaths.get(filePaths.size() - 1);
 			}
-		}
+			
+			Gui.setStatusBarMsg(status);
+			imageContainer.setImageData(ImageState.STEG, imageData);
+			if (visualizer != null) {
+				imageContainer.setImageData(ImageState.STEG_VISUALIZED, visualizer.getImageData());
+			}
+			extractButton.setEnabled(true);
+			updateVisualizationCheckbox();
+			cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
+			compositeImage.getShell().setCursor(cursor);
+			progressBar.dispose();
+		});
 	}
 
-	/**
-	 * Gets called after the embedding-process is finished. Displays the image, disposes the
-	 * progress bar etc.
-	 *
-	 * @param embeddingMethod
-	 * @param embeddedImage
-	 */
-	public void embeddingComplete(EmbeddingMethod<?> embeddingMethod, ImageFormat embeddedImage) {
-		String outputPath = FileUtils.addFileNameSuffix(embeddedImage.getFile().getAbsolutePath(), "_embed");
-		try {
-			embeddedImage.save(new File(outputPath));
-		} catch (SteganoImageException e) {
-			e.printStackTrace();
-		}
-		imageContainer.setImageData(ImageState.STEG, embeddedImage.getImageData());
-		if (embeddingMethod.getVisualizer() != null) {
-			imageContainer.setImageData(ImageState.STEG_VISUALIZED, embeddingMethod.getVisualizer().getImageData());
-		}
-		Gui.setStatusBarMsg("Embedding completed. File saved to " + outputPath);
-		embedButton.setEnabled(true);
-		imageLabel.setImage(imageContainer.scaleImage(ImageState.STEG));
-		visualizationCheckbox();
-		compositeImage.layout(true, true);
-		payload = new Payload();
-		fileTable.clearAll();
-		fileTable.setItemCount(0);
-		fileSizeSum = 0;
-		payloadFileSize.setText("");
-		payloadFileCounter.setText(fileTable.getItemCount() + " " + L.getString("files_text"));
-		cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
-		compositeImage.getShell().setCursor(cursor);
-		progressBar.dispose();
+	private void runInGuiThread(Runnable runnable) {
+		Display.getDefault().asyncExec(runnable);
 	}
 
-	/**
-	 * Gets called after the extracting-process is finished. Displays the message, saves the
-	 * extracted files, disposes the progress bar etc.
-	 *
-	 * @param embeddingMethod
-	 */
-	public void extractingComplete(EmbeddingMethod<?> embeddingMethod) {
-		String outPath = null;
-		ListIterator<Block> iterator = payload.getBlocks().listIterator();
-		while (iterator.hasNext()) {
-			Block block = iterator.next();
-			if (block.getIdentifier() == FileBlock.IDENTIFIER) {
-				outPath = FileUtils.changeFileName(image.getFile().getAbsolutePath(),
-						((FileBlock) block).getFileName());
-				try {
-					Files.write(Paths.get(outPath), ((FileBlock) block).getFileContent(), StandardOpenOption.CREATE);
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-				iterator.remove();
-				iterator.add(new FileBlock(outPath));
-				addPayloadFile(outPath);
-			} else if (block.getIdentifier() == MessageBlock.IDENTIFIER) {
-				String message = ((MessageBlock) block).getMessage();
-				messageField.setText(message);
+	@Override
+	public void embeddingCompleted(ImageFormat embeddedImage, String outputPath, Visualizer visualizer) {
+		runInGuiThread(() -> {
+			imageContainer.setImageData(ImageState.STEG, embeddedImage.getImageData());
+			if (visualizer != null) {
+				imageContainer.setImageData(ImageState.STEG_VISUALIZED, visualizer.getImageData());
 			}
-		}
+			Gui.setStatusBarMsg("Embedding completed. File saved to " + outputPath);
+			embedButton.setEnabled(true);
+			imageLabel.setImage(imageContainer.scaleImage(ImageState.STEG));
+			updateVisualizationCheckbox();
+			compositeImage.layout(true, true);
+			presenter.clearPayload();
+			fileTable.clearAll();
+			fileTable.setItemCount(0);
+			fileSizeSum = 0;
+			payloadFileSize.setText("");
+			updateFilesCount();
+			cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
+			compositeImage.getShell().setCursor(cursor);
+			progressBar.dispose();
+		});
+	}
 
-		// for (Block block : payload.getBlocks()) {
-		// if (block.getIdentifier() == FileBlock.IDENTIFIER) {
-		//
-		// outPath = FileUtils.changeFileName(image.getFile().getAbsolutePath(),
-		// ((FileBlock) block).getFileName());
-		// try {
-		// Files.write(Paths.get(outPath), ((FileBlock) block).getFileContent(),
-		// StandardOpenOption.CREATE);
-		// } catch (IOException e) {
-		// e.printStackTrace();
-		// }
-		// } else if (block.getIdentifier() == MessageBlock.IDENTIFIER) {
-		// String message = ((MessageBlock) block).getMessage();
-		// messageField.setText(message);
-		// }
-		// }
-
-		String statusMessage = "Extracting completed.";
-		if (outPath != null) {
-			statusMessage += " Extracted file saved to " + outPath;
-		}
-		Gui.setStatusBarMsg(statusMessage);
-		imageContainer.setImageData(ImageState.STEG, image.getImageData());
-		if (embeddingMethod.getVisualizer() != null) {
-			imageContainer.setImageData(ImageState.STEG_VISUALIZED, embeddingMethod.getVisualizer().getImageData());
-		}
-		extractButton.setEnabled(true);
-		visualizationCheckbox();
-		// payload = new Payload();
-		cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
-		compositeImage.getShell().setCursor(cursor);
-		progressBar.dispose();
+	private void displayError(String statusBarMessage, Button actionButton, String message) {
+		runInGuiThread(() -> {
+			MessageBox dialog = new MessageBox(composite.getShell(), SWT.ICON_ERROR | SWT.OK);
+			dialog.setText("Error");
+			dialog.setMessage(message);
+			dialog.open();
+			actionButton.setEnabled(true);
+			cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
+			compositeImage.getShell().setCursor(cursor);
+			progressBar.dispose();
+			Gui.setStatusBarMsg(statusBarMessage);
+		});
 	}
 
 	/**
@@ -386,54 +294,49 @@ public class EmbedUi {
 	 *
 	 * @param image The image to load
 	 */
-	void loadImage(ImageFormat image)
-			throws SteganoImageException {
-		this.image = image;
+	void loadImage(ImageFormat image) {
+		presenter = new StegosuitePresenter(image, this);
+
 		embedButton.setEnabled(false);
 		extractButton.setEnabled(false);
 		imageContainer = new ImageContainer(compositeImage);
 
 		Image img = imageContainer.loadImage(image.getImageData());
 		imageLabel.setImage(img);
-		imageLabel.setToolTipText(image.getFile().getAbsolutePath());
-
-		if (image.getClass().equals(GIFImage.class)) {
-			embedding = EmbeddingFactory.GIFSHUFFLE.newEmbedding(image);
-		}
+		imageLabel.setToolTipText(image.getFilePath());
 
 		messageField.setEnabled(true);
+		messageField.setText("");
+		passwordField.setText("");
 
 		fileTable.clearAll();
 		fileTable.setItemCount(0);
 		payloadFileSize.setText("");
 		fileSizeSum = 0;
-		payloadFileCounter.setText(fileTable.getItemCount() + " " + L.getString("files_text"));
-		payload = new Payload();
-		visualizationCheckbox();
+		updateFilesCount();
+		presenter.clearPayload();
+		updateVisualizationCheckbox();
 		compositeImage.layout(true, true);
 
 		Gui.setStatusBarMsg("Searching for homogeneous areas in the image...");
 		cursor = new Cursor(Display.getDefault(), SWT.CURSOR_WAIT);
 		passwordField.getParent().setCursor(cursor);
 		passwordField.getParent().layout(true, true);
-		embedding = EmbeddingFactory.getEmbedding(image);
 
-		new Thread(() -> {
-			embedding.setPointFilter(1);
-			int capacity = embedding.getCapacity();
+		runInNewThread(() -> {
+			int capacity = presenter.getEmbeddingCapacity();
 
-			Display.getDefault().asyncExec(() -> {
+			runInGuiThread(() -> {
 				Gui.setStatusBarMsg(L.getString("statusbar_capacity") + ": " + ImageUtils.formatSize(capacity));
 				embedButton.setEnabled(true);
 				extractButton.setEnabled(true);
 				cursor = new Cursor(Display.getDefault(), SWT.CURSOR_ARROW);
 				passwordField.getParent().setCursor(cursor);
-
 			});
-		}).start();
+		});
 	}
 
-	private void visualizationCheckbox() {
+	private void updateVisualizationCheckbox() {
 		if (imageContainer.getImageData(ImageState.STEG_VISUALIZED) != null) {
 			if (checkBoxVisualize == null || checkBoxVisualize.isDisposed()) {
 				checkBoxVisualize = new Button(compositeImage, SWT.CHECK);
@@ -460,66 +363,75 @@ public class EmbedUi {
 		}
 	}
 
-	private void addPayloadFile(String filePath) {
-		String filename = Paths.get(filePath).getFileName().toString();
-		TableItem item = new TableItem(fileTable, SWT.NONE);
-		item.setText(filename);
-
-		String extension = "";
-		int i = filename.lastIndexOf('.');
-		if (i > 0) {
-			extension = filename.substring(i);
-		}
-
-		Program p = Program.findProgram(extension);
-		if (p != null) {
-			ImageData data = p.getImageData();
-			if (data != null) {
-				Image image = new Image(Display.getDefault(), data);
-				item.setImage(image);
-			}
-		}
+	@Override
+	public void addPayloadFile(String filename, String extension, long fileSize) {
+		runInGuiThread(() -> {
+			fileSizeSum += fileSize;
+			addTableItemForFile(filename, fileSize, systemIconFor(extension));
+			updateFilesCount();
+			updateTotalFilesSize(fileSizeSum);
+			redrawFilesTable();
+		});
+	}
 
+	private void updateFilesCount() {
 		payloadFileCounter.setText(fileTable.getItemCount() + " " + L.getString("files_text"));
+	}
 
-		File f = new File(filePath);
-		fileSizeSum += f.length();
-		payloadFileSize.setText(ImageUtils.formatSize(fileSizeSum));
-		item.setText(1, ImageUtils.formatSize(f.length()));
-		item.setForeground(1, Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
-		final TableColumn[] columns = fileTable.getColumns();
-		for (TableColumn column : columns) {
+	private void redrawFilesTable() {
+		for (TableColumn column : fileTable.getColumns()) {
 			column.pack();
 		}
 		payloadFileSize.getParent().layout(true, true);
 	}
 
+	private void updateTotalFilesSize(int fileSizeSum) {
+		payloadFileSize.setText(ImageUtils.formatSize(fileSizeSum));
+	}
+
+	private static Optional<Image> systemIconFor(String extension) {
+		return Optional.ofNullable(Program.findProgram(extension))
+				.map(program -> program.getImageData())
+				.map(data -> new Image(Display.getDefault(), data));
+	}
+
+	private void addTableItemForFile(String filename, long fileSize, Optional<Image> icon) {
+		TableItem item = new TableItem(fileTable, SWT.NONE);
+		item.setText(filename);
+		item.setText(1, ImageUtils.formatSize(fileSize));
+		item.setForeground(1, Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
+		icon.ifPresent(image -> item.setImage(image));
+	}
+
 	private void removeSelectedPayloadFile() {
-		if (fileTable.getItemCount() > 0) {
-			if (fileTable.getSelectionIndex() >= 0) {
-				final TableItem it = fileTable.getItem(fileTable.getSelectionIndex());
-				String filename = it.getText();
-
-				fileTable.remove(fileTable.getSelectionIndex());
-				Iterator<Block> iterator = payload.getBlocks().iterator();
-				while (iterator.hasNext()) {
-					Block block = iterator.next();
-					if (block.getIdentifier() == FileBlock.IDENTIFIER) {
-						FileBlock fBlock = (FileBlock) block;
-						String filePath = fBlock.getFileName();
-						String filename2 = Paths.get(filePath).getFileName().toString();
-						if (filename.equals(filename2)) {
-							File f = new File(filePath);
-							fileSizeSum -= f.length();
-							payloadFileSize.setText(ImageUtils.formatSize(fileSizeSum));
-							iterator.remove();
-							LOG.debug("Fileblock removed.");
-						}
-					}
-				}
-				payloadFileCounter.setText(fileTable.getItemCount() + " " + L.getString("files_text"));
-				payloadFileSize.getParent().layout(true, true);
-			}
+		if (aFileIsSelected()) {
+			String selectedFilename = getSelectedFilename();
+
+            fileTable.remove(fileTable.getSelectionIndex());
+            // FIXME: If two files with the same name are added (being both the same file or not),
+			// all occurrences are deleted internally, but only one is deleted on the ui table
+            presenter.payloadFileBlocksWithFilename(selectedFilename)
+                    .forEach(this::removeFileBlock);
+
+            updateFilesCount();
+            payloadFileSize.getParent().layout(true, true);
 		}
 	}
+
+	private String getSelectedFilename() {
+		TableItem selectedItem = fileTable.getItem(fileTable.getSelectionIndex());
+		return selectedItem.getText();
+	}
+
+	private boolean aFileIsSelected() {
+		return fileTable.getItemCount() > 0 && fileTable.getSelectionIndex() >= 0;
+	}
+
+	private void removeFileBlock(FileBlock fileBlock) {
+		fileSizeSum -= fileBlock.getSize();
+		updateTotalFilesSize(fileSizeSum);
+		presenter.removeBlock(fileBlock);
+		LOG.debug("Fileblock removed.");
+	}
+
 }
diff --git a/src/main/java/org/stegosuite/ui/gui/Gui.java b/src/main/java/org/stegosuite/ui/gui/Gui.java
index 9bd5ff2..8497daf 100644
--- a/src/main/java/org/stegosuite/ui/gui/Gui.java
+++ b/src/main/java/org/stegosuite/ui/gui/Gui.java
@@ -1,31 +1,19 @@
 package org.stegosuite.ui.gui;
 
-import java.io.File;
-import java.util.ResourceBundle;
-
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTarget;
-import org.eclipse.swt.dnd.DropTargetAdapter;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.FileTransfer;
-import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.*;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.FontData;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.*;
 import org.stegosuite.image.format.ImageFormat;
-import org.stegosuite.image.util.FileUtils;
 import org.stegosuite.model.exception.SteganoImageException;
 
+import java.util.ResourceBundle;
+
 /**
  * Base class for the GUI. Contains global GUI-elements and global listeners.
  */
@@ -55,9 +43,9 @@ public class Gui {
 			showStartScreen();
 		}
 
-		final String[] FILTER_NAMES = { "All supported files (*.bmp/*.gif/*.jpg)", "BMP-Files (*.bmp)",
-				"GIF-Files (*.gif)", "JPG-Files (*.jpg)" };
-		final String[] FILTER_EXTS = { "*.bmp;*.gif;*.jpg", "*.bmp", "*.gif", "*.jpg" };
+		final String[] FILTER_NAMES = { "All supported files (*.bmp/*.gif/*.jpg/*.png)", "BMP-Files (*.bmp)",
+				"GIF-Files (*.gif)", "JPG-Files (*.jpg)", "PNG-Files (*.png)" };
+		final String[] FILTER_EXTS = { "*.bmp;*.gif;*.jpg;*.png", "*.bmp", "*.gif", "*.jpg", "*.png" };
 
 		// Drag and drop files into the window to load them
 		final DropTarget dropTarget = new DropTarget(shell, DND.DROP_MOVE);
@@ -106,31 +94,35 @@ public class Gui {
 	}
 
 	/**
-	 * Loads a gif- or bmp-image and displays it on all 3 tabs.
+	 * Loads a gif- or bmp-image and displays it.
 	 *
 	 * @param path absolute file-path of the image
 	 */
-	private void loadImages(final String path) {
-		if (path != null) {
-			String extension = FileUtils.getFileExtension(path);
-			if (ImageFormat.getRegisteredImageExtensions().containsKey(extension)) {
-				if (composite == null) {
-					if (shell.getChildren().length > 1) {
-						shell.getChildren()[1].dispose();
-					}
-					startLayout();
-				}
-				try {
-					ImageFormat image = ImageFormat.newInstance(extension);
-					image.load(new File(path));
-					guiComponents.embedUi.loadImage(image);
-				} catch (SteganoImageException e) {
-					e.printStackTrace();
-				}
+	private void loadImages(String path) {
+		try {
+			ImageFormat image = ImageFormat.getImageFormat(path);
+            if (image != null) {
+				initializeEmbedUi();
+				guiComponents.embedUi.loadImage(image);
 			}
+		} catch (SteganoImageException e) {
+			e.printStackTrace();
 		}
 	}
 
+	private void initializeEmbedUi() {
+		if (composite == null) {
+            removeStartScreen();
+            startLayout();
+        }
+	}
+
+	private void removeStartScreen() {
+		if (shell.getChildren().length > 1) {
+            shell.getChildren()[1].dispose();
+        }
+	}
+
 	/**
 	 * Sets the message of the global status bar.
 	 *
diff --git a/src/main/java/org/stegosuite/ui/gui/GuiComponents.java b/src/main/java/org/stegosuite/ui/gui/GuiComponents.java
index f45adef..8c439d2 100644
--- a/src/main/java/org/stegosuite/ui/gui/GuiComponents.java
+++ b/src/main/java/org/stegosuite/ui/gui/GuiComponents.java
@@ -1,27 +1,14 @@
 package org.stegosuite.ui.gui;
 
-import java.util.ResourceBundle;
-
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.layout.FormAttachment;
-import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.layout.FormLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.MenuItem;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
-import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.ResourceBundle;
 
 /**
  * Contains methods for creating the GUI-elements and setting their default parameters.
@@ -31,10 +18,13 @@ public class GuiComponents {
 	final static byte LOAD_MENU_ITEM = 0;
 	EmbedUi embedUi;
 	private final ResourceBundle L = ResourceBundle.getBundle("Messages");
+	public static final int EMPTY = 1;
+	public static final int NOT_EMPTY = 0;
+	public static final int PROCESSING = 2;
 
 	Shell createShell(Display display) {
 		Shell shell = new Shell(display);
-		shell.setText("StegoSuite");
+		shell.setText("Stegosuite");
 		Image icon = new Image(display, this.getClass().getClassLoader().getResourceAsStream("icon.png"));
 		shell.setImage(icon);
 		// shell.setImage(new Image(display, "resources/images/man-hat.png"));
@@ -65,6 +55,30 @@ public class GuiComponents {
 		MenuItem loadItem = new MenuItem(fileMenu, SWT.PUSH, LOAD_MENU_ITEM);
 		loadItem.setText(L.getString("load_image_menu"));
 
+		//
+		cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE);
+		cascadeFileMenu.setText("Help");
+
+		fileMenu = new Menu(shell, SWT.DROP_DOWN);
+		cascadeFileMenu.setMenu(fileMenu);
+
+		loadItem = new MenuItem(fileMenu, SWT.PUSH, LOAD_MENU_ITEM);
+		loadItem.setText("About");
+
+		loadItem.addListener(SWT.Selection, event -> {
+			int style = SWT.ICON_INFORMATION | SWT.OK;
+
+			MyDialog d = new MyDialog(shell, SWT.OK);
+			d.setText("About Stegosuite");
+			d.open();
+
+			// MessageBox dia = new MessageBox(shell, style);
+			// dia.setText("Information");
+			// dia.setMessage("Download completed.");
+			// dia.open();
+
+		});
+
 		shell.setMenuBar(menuBar);
 		return menuBar;
 	}
@@ -93,25 +107,9 @@ public class GuiComponents {
 
 		StyledText text = new StyledText(compositeControls, SWT.MULTI | SWT.V_SCROLL | SWT.WRAP | SWT.BORDER);
 		text.setAlwaysShowScrollBars(false);
-		text.setText(L.getString("message_text"));
 		text.setToolTipText(L.getString("message_text_tooltip"));
-		text.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
-
-		text.addListener(SWT.MouseDown, event -> {
-			if (text.getData() == null) {
-				text.setData(1);
-				text.setForeground(null);
-				text.setText("");
-			}
-		});
 
-		text.addListener(SWT.FocusOut, event -> {
-			if (text.getText().isEmpty()) {
-				text.setText(L.getString("message_text"));
-				text.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
-				text.setData(null);
-			}
-		});
+		setPlaceholder(text, L.getString("message_text"));
 
 		/*
 		 * text.addListener(SWT.MouseDown, event -> { text.setText(""); });
@@ -124,6 +122,59 @@ public class GuiComponents {
 		return compositeControls;
 	}
 
+	private void setPlaceholder(Scrollable text, String placeholder) {
+		text.setData(EMPTY);
+
+		Listener entryListener = event -> {
+			if (Objects.equals(text.getData(), EMPTY)) {
+				text.setData(PROCESSING);
+				setText(text, "");
+				text.setForeground(null);
+				text.setData(NOT_EMPTY);
+			}
+		};
+		Listener exitListener = event -> {
+			if (Objects.equals(text.getData(), NOT_EMPTY) && getText(text).isEmpty()) {
+				text.setData(PROCESSING);
+				setText(text, placeholder);
+				text.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
+				text.setData(EMPTY);
+			}
+		};
+
+		text.addListener(SWT.FocusIn, entryListener);
+		text.addListener(SWT.Verify, entryListener);
+
+		text.addListener(SWT.Modify, exitListener);
+		text.addListener(SWT.FocusOut, exitListener);
+
+		entryListener.handleEvent(null);
+		exitListener.handleEvent(null);
+	}
+
+	private void setText(Scrollable textField, String value) {
+		// Reflection was needed to call the method on instances of Text and StyledText
+		// (whose common ancestor class is Scrollable)
+		try {
+			Method setText = textField.getClass().getMethod("setText", String.class);
+			setText.invoke(textField, value);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	private String getText(Scrollable textField) {
+		// Reflection was needed to call the method on instances of Text and StyledText
+		// (whose common ancestor class is Scrollable)
+		try {
+			Method getText = textField.getClass().getMethod("getText");
+			return getText.invoke(textField).toString();
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
 	Composite createFileEmbedding(Composite parent) {
 		Composite composite = new Composite(parent, SWT.NONE);
 		final GridData gridData2Columns = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
@@ -206,26 +257,11 @@ public class GuiComponents {
 		// labelKey.setText("Secret key:");
 		// labelKey.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, true, false));
 
-		Text t2 = new Text(parent, SWT.SINGLE | SWT.BORDER);
-		t2.setText(L.getString("key_text"));
-		t2.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
-		t2.setToolTipText(L.getString("key_text_tooltip"));
-		t2.setLayoutData(data);
-		t2.addListener(SWT.MouseDown, event -> {
-			if (t2.getData() == null) {
-				t2.setData(1);
-				t2.setForeground(null);
-				t2.setText("");
-			}
-		});
+		Text txtPassword = new Text(parent, SWT.SINGLE | SWT.BORDER);
+		txtPassword.setToolTipText(L.getString("key_text_tooltip"));
+		txtPassword.setLayoutData(data);
 
-		t2.addListener(SWT.FocusOut, event -> {
-			if (t2.getText().isEmpty()) {
-				t2.setText(L.getString("key_text"));
-				t2.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
-				t2.setData(null);
-			}
-		});
+		setPlaceholder(txtPassword, L.getString("key_text"));
 
 		// GridLayout mGridLayoutEncryption = new GridLayout(1, true);
 		// mGridLayoutEncryption.verticalSpacing = 6;
@@ -255,7 +291,7 @@ public class GuiComponents {
 		// }
 		// });
 
-		return t2;
+		return txtPassword;
 	}
 
 	Button createMainButton(Composite parent, String text) {
@@ -280,3 +316,119 @@ public class GuiComponents {
 		return composite;
 	}
 }
+
+class MyDialog
+		extends Dialog {
+
+	Object result;
+
+	public MyDialog(Shell parent, int style) {
+		super(parent, style);
+	}
+
+	public MyDialog(Shell parent) {
+		this(parent, 0);
+	}
+
+	private Image resize(Image image, int width, int height) {
+		Image scaled = new Image(Display.getDefault(), width, height);
+		GC gc = new GC(scaled);
+		gc.setAntialias(SWT.ON);
+		gc.setInterpolation(SWT.HIGH);
+		gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0, 0, width, height);
+		gc.dispose();
+		image.dispose();
+		return scaled;
+	}
+
+	public Object open() {
+		Shell parent = getParent();
+		Shell shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+		shell.setText(getText());
+
+		Image icon = new Image(shell.getDisplay(), this.getClass().getClassLoader().getResourceAsStream("icon.png"));
+
+		icon = resize(icon, 96, 96);
+		ImageData imageData = icon.getImageData();
+		imageData.transparentPixel = imageData.getPixel(0, 0);
+		icon.dispose();
+
+		final Image icon2 = new Image(null, imageData);
+
+		shell.setSize(400, 350);
+		shell.setLayout(new FormLayout());
+
+		Label lblIcon = null;
+		if (icon2 != null) {
+			lblIcon = new Label(shell, SWT.TRANSPARENT);
+			lblIcon.setImage(icon2);
+			lblIcon.setSize(new Point(96, 96));
+
+			FormData fd_lblIcon = new FormData();
+			int offsetX = -lblIcon.computeSize(SWT.DEFAULT, SWT.DEFAULT).x / 2;
+
+			fd_lblIcon.top = new FormAttachment(0, 20);
+			fd_lblIcon.left = new FormAttachment(50, offsetX);
+			lblIcon.setLayoutData(fd_lblIcon);
+
+		}
+
+		Label lblMessage = new Label(shell, SWT.WRAP);
+		FormData fd_lblMessage = new FormData();
+		lblMessage.setText("Stegosuite");
+
+		final FontData[] fontData = lblMessage.getFont().getFontData();
+		for (FontData element : fontData) {
+			element.setHeight(13);
+		}
+		Font font = new Font(shell.getDisplay(),
+				new FontData(fontData[0].getName(), fontData[0].getHeight(), SWT.BOLD));
+		lblMessage.setFont(font);
+
+		int offsetX = -lblMessage.computeSize(SWT.DEFAULT, SWT.DEFAULT).x / 2;
+
+		fd_lblMessage.top = new FormAttachment(lblIcon, 20);
+		fd_lblMessage.left = new FormAttachment(50, offsetX);
+
+		lblMessage.setLayoutData(fd_lblMessage);
+
+		Label lblMessage2 = new Label(shell, SWT.WRAP);
+		FormData fd_lblMessage2 = new FormData();
+		lblMessage2.setText("0.8");
+		offsetX = -lblMessage2.computeSize(SWT.DEFAULT, SWT.DEFAULT).x / 2;
+		fd_lblMessage2.top = new FormAttachment(lblMessage, 15);
+		fd_lblMessage2.left = new FormAttachment(50, offsetX);
+		lblMessage2.setLayoutData(fd_lblMessage2);
+
+		Label lblMessage3 = new Label(shell, SWT.WRAP | SWT.CENTER);
+		FormData fd_lblMessage3 = new FormData();
+		lblMessage3.setText("Stegosuite is a free steganography tool for hiding\n information in image files. "
+				+ "Written in Java using SWT");
+		offsetX = -lblMessage3.computeSize(SWT.DEFAULT, SWT.DEFAULT).x / 2;
+		fd_lblMessage3.top = new FormAttachment(lblMessage2, 15);
+		fd_lblMessage3.left = new FormAttachment(0, 15);
+		fd_lblMessage3.right = new FormAttachment(100, -15);
+		lblMessage3.setLayoutData(fd_lblMessage3);
+
+		Button btnOk = new Button(shell, SWT.NONE);
+
+		btnOk.addListener(SWT.Selection, event -> {
+			shell.dispose();
+		});
+
+		FormData fd_btnOk = new FormData();
+		fd_btnOk.bottom = new FormAttachment(100, -15);
+		fd_btnOk.left = new FormAttachment(100, -95);
+		fd_btnOk.right = new FormAttachment(100, -15);
+		btnOk.setLayoutData(fd_btnOk);
+		btnOk.setText("Close");
+
+		shell.open();
+		Display display = parent.getDisplay();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+		return result;
+	}
+}
diff --git a/src/main/java/org/stegosuite/ui/gui/ImageContainer.java b/src/main/java/org/stegosuite/ui/gui/ImageContainer.java
index e72bbf4..fa4bd69 100644
--- a/src/main/java/org/stegosuite/ui/gui/ImageContainer.java
+++ b/src/main/java/org/stegosuite/ui/gui/ImageContainer.java
@@ -1,16 +1,16 @@
 package org.stegosuite.ui.gui;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.ImageData;
 import org.eclipse.swt.widgets.Composite;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public class ImageContainer {
 
 	public enum ImageState {
-		CARRIER, CARRIER_SCALED, STEG, STEG_VISUALIZED, STEG_LSB, STEG_LSB2;
+		CARRIER, CARRIER_SCALED, STEG, STEG_VISUALIZED, STEG_LSB, STEG_LSB2
 	}
 
 	private Image image = null;
@@ -74,10 +74,6 @@ public class ImageContainer {
 		}
 	}
 
-	public ImageState getState() {
-		return state;
-	}
-
 	public void setImageData(ImageState state, ImageData imageData) {
 		images.put(state, imageData);
 	}
diff --git a/src/main/java/org/stegosuite/ui/gui/ImageUtils.java b/src/main/java/org/stegosuite/ui/gui/ImageUtils.java
index 2097139..f560c85 100644
--- a/src/main/java/org/stegosuite/ui/gui/ImageUtils.java
+++ b/src/main/java/org/stegosuite/ui/gui/ImageUtils.java
@@ -3,7 +3,7 @@ package org.stegosuite.ui.gui;
 public class ImageUtils {
 
 	// taken from http://stackoverflow.com/a/24805871
-	static String formatSize(long v) {
+	public static String formatSize(long v) {
 		if (v < 1024) {
 			return v + " B";
 		}
diff --git a/src/main/java/org/stegosuite/image/util/ByteUtils.java b/src/main/java/org/stegosuite/util/ByteUtils.java
similarity index 99%
rename from src/main/java/org/stegosuite/image/util/ByteUtils.java
rename to src/main/java/org/stegosuite/util/ByteUtils.java
index cae24bb..7efe02d 100644
--- a/src/main/java/org/stegosuite/image/util/ByteUtils.java
+++ b/src/main/java/org/stegosuite/util/ByteUtils.java
@@ -1,4 +1,4 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/main/java/org/stegosuite/image/util/ColorDistance.java b/src/main/java/org/stegosuite/util/ColorDistance.java
similarity index 96%
rename from src/main/java/org/stegosuite/image/util/ColorDistance.java
rename to src/main/java/org/stegosuite/util/ColorDistance.java
index 6796b92..2162884 100644
--- a/src/main/java/org/stegosuite/image/util/ColorDistance.java
+++ b/src/main/java/org/stegosuite/util/ColorDistance.java
@@ -1,6 +1,6 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
-import java.awt.Color;
+import java.awt.*;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -63,11 +63,7 @@ public enum ColorDistance {
 		}
 
 		InterchangeablePair<Color, Color> pair = new InterchangeablePair<>(rgb1, rgb2);
-		Double distance = cache.get(this).get(pair);
-		if (distance == null) {
-			distance = getDistance(rgb1, rgb2);
-			cache.get(this).put(pair, distance);
-		}
+		Double distance = cache.get(this).computeIfAbsent(pair, k -> getDistance(rgb1, rgb2));
 
 		return distance;
 	}
diff --git a/src/main/java/org/stegosuite/image/util/ColorUtils.java b/src/main/java/org/stegosuite/util/ColorUtils.java
similarity index 89%
rename from src/main/java/org/stegosuite/image/util/ColorUtils.java
rename to src/main/java/org/stegosuite/util/ColorUtils.java
index 25eb573..1ff8640 100644
--- a/src/main/java/org/stegosuite/image/util/ColorUtils.java
+++ b/src/main/java/org/stegosuite/util/ColorUtils.java
@@ -1,26 +1,16 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
-import static java.util.stream.Collectors.toSet;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
+import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.awt.image.WritableRaster;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.*;
+
 /**
  * Utility class that provides various color-related methods such as color transformations, color
  * comparisons, color sorting, etc.
@@ -72,7 +62,7 @@ public class ColorUtils {
 	 * @return
 	 */
 	public static Map<Color, Integer> getHistogram(List<Color> colors, int[] pixels) {
-		Map<Color, Integer> histogram = new HashSet<Color>(colors).stream().collect(toMap(identity(), c -> 0));
+		Map<Color, Integer> histogram = new HashSet<>(colors).stream().collect(toMap(identity(), c -> 0));
 		Arrays.stream(pixels).mapToObj(colors::get).forEach(color -> histogram.put(color, histogram.get(color) + 1));
 		return histogram;
 	}
diff --git a/src/main/java/org/stegosuite/image/util/CompressionUtils.java b/src/main/java/org/stegosuite/util/CompressionUtils.java
similarity index 96%
rename from src/main/java/org/stegosuite/image/util/CompressionUtils.java
rename to src/main/java/org/stegosuite/util/CompressionUtils.java
index 6f486bc..e3c7702 100644
--- a/src/main/java/org/stegosuite/image/util/CompressionUtils.java
+++ b/src/main/java/org/stegosuite/util/CompressionUtils.java
@@ -1,4 +1,4 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -25,7 +25,7 @@ public class CompressionUtils {
 		ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
 
 		try (ByteArrayInputStream in = new ByteArrayInputStream(data);
-				DeflaterOutputStream outDeflate = new DeflaterOutputStream(outBytes);) {
+				DeflaterOutputStream outDeflate = new DeflaterOutputStream(outBytes)) {
 			ByteUtils.copy(in, outDeflate);
 			outBytes.close();
 		}
diff --git a/src/main/java/org/stegosuite/image/util/CryptoUtils.java b/src/main/java/org/stegosuite/util/CryptoUtils.java
similarity index 98%
rename from src/main/java/org/stegosuite/image/util/CryptoUtils.java
rename to src/main/java/org/stegosuite/util/CryptoUtils.java
index 245bba3..d29cfec 100644
--- a/src/main/java/org/stegosuite/image/util/CryptoUtils.java
+++ b/src/main/java/org/stegosuite/util/CryptoUtils.java
@@ -1,5 +1,11 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -9,13 +15,6 @@ import java.security.spec.KeySpec;
 import java.util.Arrays;
 import java.util.Random;
 
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-
 /**
  *
  * Some code in this class taken from: http://stackoverflow.com/a/992413/4862922
diff --git a/src/main/java/org/stegosuite/image/util/FileUtils.java b/src/main/java/org/stegosuite/util/FileUtils.java
similarity index 80%
rename from src/main/java/org/stegosuite/image/util/FileUtils.java
rename to src/main/java/org/stegosuite/util/FileUtils.java
index 89d9447..e95199b 100644
--- a/src/main/java/org/stegosuite/image/util/FileUtils.java
+++ b/src/main/java/org/stegosuite/util/FileUtils.java
@@ -1,4 +1,4 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
 import java.io.File;
 import java.nio.file.Path;
@@ -45,4 +45,15 @@ public class FileUtils {
 		return path.getParent() + File.separator + newFileName;
 	}
 
+	/**
+	 * @param filePath D:\folder\foo.bar
+	 * @return foo.bar
+	 */
+	public static String getFileName(String filePath) {
+		return Paths.get(filePath).getFileName().toString();
+	}
+
+	public static long getFileSize(String filePath) {
+		return new File(filePath).length();
+	}
 }
diff --git a/src/main/java/org/stegosuite/image/util/ImageSwtAwtConverter.java b/src/main/java/org/stegosuite/util/ImageSwtAwtConverter.java
similarity index 93%
rename from src/main/java/org/stegosuite/image/util/ImageSwtAwtConverter.java
rename to src/main/java/org/stegosuite/util/ImageSwtAwtConverter.java
index 132628e..99e74e1 100644
--- a/src/main/java/org/stegosuite/image/util/ImageSwtAwtConverter.java
+++ b/src/main/java/org/stegosuite/util/ImageSwtAwtConverter.java
@@ -1,16 +1,12 @@
-package org.stegosuite.image.util;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.ComponentColorModel;
-import java.awt.image.DirectColorModel;
-import java.awt.image.IndexColorModel;
-import java.awt.image.WritableRaster;
-import java.util.stream.IntStream;
+package org.stegosuite.util;
 
 import org.eclipse.swt.graphics.ImageData;
 import org.eclipse.swt.graphics.PaletteData;
 import org.eclipse.swt.graphics.RGB;
 
+import java.awt.image.*;
+import java.util.stream.IntStream;
+
 public class ImageSwtAwtConverter {
 
 	/** Private constructor to hide the implicit public one */
diff --git a/src/main/java/org/stegosuite/image/util/InterchangeablePair.java b/src/main/java/org/stegosuite/util/InterchangeablePair.java
similarity index 94%
rename from src/main/java/org/stegosuite/image/util/InterchangeablePair.java
rename to src/main/java/org/stegosuite/util/InterchangeablePair.java
index e1c305e..59e540c 100644
--- a/src/main/java/org/stegosuite/image/util/InterchangeablePair.java
+++ b/src/main/java/org/stegosuite/util/InterchangeablePair.java
@@ -1,4 +1,4 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
 public class InterchangeablePair<F, S> {
 
diff --git a/src/main/java/org/stegosuite/image/util/RgbChannel.java b/src/main/java/org/stegosuite/util/RgbChannel.java
similarity index 96%
rename from src/main/java/org/stegosuite/image/util/RgbChannel.java
rename to src/main/java/org/stegosuite/util/RgbChannel.java
index 71166d0..1fe763e 100644
--- a/src/main/java/org/stegosuite/image/util/RgbChannel.java
+++ b/src/main/java/org/stegosuite/util/RgbChannel.java
@@ -1,6 +1,6 @@
-package org.stegosuite.image.util;
+package org.stegosuite.util;
 
-import java.awt.Color;
+import java.awt.*;
 import java.util.Arrays;
 import java.util.List;
 
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 93f7f13..4f9ba77 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -6,7 +6,7 @@
 			<onMismatch>DENY</onMismatch>
 		</filter>
 		<encoder>
-			<pattern>%date{HH:mm:ss.SSS} %-17(%level @ %thread) \(%replace(%class{0}.java:%line){'\$\w+\.', '\.'}\) %message%n
+			<pattern>%date{HH:mm:ss.SSS} @ %thread (%replace(%class{0}.java:%line){'\$\w+\.', '\.'}\) %message%n
 			</pattern>
 		</encoder>
 	</appender>
diff --git a/src/test/java/org/stegosuite/CliTest.java b/src/test/java/org/stegosuite/CliTest.java
new file mode 100644
index 0000000..8489ffe
--- /dev/null
+++ b/src/test/java/org/stegosuite/CliTest.java
@@ -0,0 +1,58 @@
+package org.stegosuite;
+
+import org.junit.After;
+import org.junit.Test;
+import org.stegosuite.ui.cli.CliParser;
+
+import java.io.File;
+
+import static org.junit.Assert.assertTrue;
+import static org.stegosuite.Resources.pathOf;
+
+public class CliTest {
+
+	@After
+	public void tearDown() throws Exception {
+		Resources.delete("snow_embed.bmp");
+	}
+
+	@Test
+	public void testEmbedAndExtract() throws Exception {
+		// TODO: Remove temporal coupling and separate in two tests
+		testEmbed();
+		testExtract();
+	}
+
+	private void testEmbed() {
+		String imagePath = pathOf("snow.bmp");
+		String key = "password";
+		String message = "message";
+		String[] args = getEmbedCommand(imagePath, key, message);
+
+		new CliParser(args).parse();
+
+		assertTrue(new File(pathOf("snow_embed.bmp")).exists());
+	}
+
+	private void testExtract() {
+		String imagePath = pathOf("snow_embed.bmp");
+		String key = "password";
+		String[] args = getExtractCommand(imagePath, key);
+
+		new CliParser(args).parse();
+
+		// TODO: Assert something (Until now, the only side-effect is the logging)
+	}
+
+	private String[] getEmbedCommand(String imagePath, String key, String message) {
+		String command = String.format("-e %s -k %s -m %s",
+				imagePath, key, message);
+		return command.split(" ");
+	}
+
+	private String[] getExtractCommand(String imagePath, String key) {
+		String command = String.format("-x %s -k %s",
+				imagePath, key);
+		return command.split(" ");
+	}
+}
diff --git a/src/test/java/org/stegosuite/EmbeddingAndExtractingTest.java b/src/test/java/org/stegosuite/EmbeddingAndExtractingTest.java
new file mode 100644
index 0000000..9c31d4a
--- /dev/null
+++ b/src/test/java/org/stegosuite/EmbeddingAndExtractingTest.java
@@ -0,0 +1,126 @@
+package org.stegosuite;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.junit.After;
+import org.junit.Test;
+import org.stegosuite.application.StegosuitePresenter;
+import org.stegosuite.application.StegosuiteUI;
+import org.stegosuite.image.embedding.EmbeddingProgress;
+import org.stegosuite.image.embedding.Visualizer;
+import org.stegosuite.image.format.ImageFormat;
+import org.stegosuite.model.exception.SteganoEmbedException;
+import org.stegosuite.model.exception.SteganoExtractException;
+import org.stegosuite.model.exception.SteganoImageException;
+import org.stegosuite.model.exception.SteganoKeyException;
+
+import java.io.File;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.stegosuite.Resources.pathOf;
+
+public class EmbeddingAndExtractingTest {
+
+    private static final String THE_MESSAGE = "a message";
+    private static final String THE_PASSWORD = "a password";
+    private static final String INCORRECT_PASSWORD = "an incorrect password";
+
+    private final StegosuiteUI ui = new UIStub();
+    private String outputPath;
+    private String extractedMessage;
+
+    @After
+    public void tearDown() throws Exception {
+    	Resources.delete(outputPath);
+    }
+
+    @Test
+    public void testEmbeddingAndExtractingFromGifFile() throws Exception {
+        testEmbeddingAndThenExtractingOk("sunflower.gif");
+    }
+
+    @Test
+    public void testEmbeddingAndExtractingFromBmpFile() throws Exception {
+        testEmbeddingAndThenExtractingOk("snow.bmp");
+    }
+
+    @Test
+    public void testEmbeddingAndExtractingFromJpgFile() throws Exception {
+        testEmbeddingAndThenExtractingOk("landscape.jpg");
+    }
+
+    @Test(expected = SteganoKeyException.class)
+    public void testIncorrectPasswordFromGifFile() throws Throwable {
+        testExtractingWhenPasswordIsIncorrect("sunflower_embed_ok.gif");
+    }
+
+    @Test(expected = SteganoKeyException.class)
+    public void testIncorrectPasswordFromBmpFile() throws Throwable {
+        testExtractingWhenPasswordIsIncorrect("snow_embed_ok.bmp");
+    }
+
+    @Test(expected = SteganoKeyException.class)
+    public void testIncorrectPasswordFromJpgFile() throws Throwable {
+        testExtractingWhenPasswordIsIncorrect("landscape_embed_ok.jpg");
+    }
+
+    private void testEmbeddingAndThenExtractingOk(String imageName) throws SteganoImageException, SteganoEmbedException, InterruptedException, java.util.concurrent.ExecutionException, SteganoExtractException {
+        embedPayload(imageName, THE_PASSWORD, THE_MESSAGE);
+        assertTrue(new File(outputPath).exists());
+
+        extractPayload(outputPath, THE_PASSWORD);
+        assertEquals(THE_MESSAGE, extractedMessage);
+    }
+
+    private void testExtractingWhenPasswordIsIncorrect(String imageName) throws Throwable {
+        try {
+            extractPayload(pathOf(imageName), INCORRECT_PASSWORD);
+        } catch (Exception e) {
+            throw e.getCause();
+        }
+    }
+
+    private void embedPayload(String imageName, String password, String message) throws SteganoImageException, SteganoEmbedException {
+        String imagePath = pathOf(imageName);
+        StegosuitePresenter presenter = getPresenterFor(imagePath);
+        presenter.addMessageToPayload(message);
+        presenter.embedNotifying(new EmbeddingProgress(), password);
+    }
+
+    private void extractPayload(String imagePath, String password) throws SteganoImageException, SteganoExtractException {
+        StegosuitePresenter presenter = getPresenterFor(imagePath);
+        presenter.extractNotifying(new EmbeddingProgress(), password);
+    }
+
+    private StegosuitePresenter getPresenterFor(String imagePath) throws SteganoImageException {
+        ImageFormat image = ImageFormat.getImageFormat(imagePath);
+        return new StegosuitePresenter(image, ui);
+    }
+
+    private class UIStub implements StegosuiteUI {
+        @Override
+		public void showEmbeddingError(SteganoEmbedException e) {
+			throw new RuntimeException(e);
+		}
+
+        @Override
+		public void showExtractingError(SteganoExtractException e) {
+			throw new RuntimeException(e);
+		}
+
+        @Override
+		public void extractingCompleted(String extractedMessage, List<String> filePaths, Visualizer visualizer, ImageData imageData) {
+            EmbeddingAndExtractingTest.this.extractedMessage = extractedMessage;
+		}
+
+        @Override
+		public void embeddingCompleted(ImageFormat embeddedImage, String outputPath, Visualizer visualizer) {
+			EmbeddingAndExtractingTest.this.outputPath = outputPath;
+		}
+
+        @Override
+		public void addPayloadFile(String filename, String extension, long fileSize) {
+		}
+    }
+}
diff --git a/src/test/java/org/stegosuite/PresenterTest.java b/src/test/java/org/stegosuite/PresenterTest.java
new file mode 100644
index 0000000..6357d8f
--- /dev/null
+++ b/src/test/java/org/stegosuite/PresenterTest.java
@@ -0,0 +1,188 @@
+package org.stegosuite;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.stegosuite.application.StegosuitePresenter;
+import org.stegosuite.application.StegosuiteUI;
+import org.stegosuite.image.embedding.EmbeddingProgress;
+import org.stegosuite.image.embedding.Visualizer;
+import org.stegosuite.image.format.ImageFormat;
+import org.stegosuite.model.exception.SteganoEmbedException;
+import org.stegosuite.model.exception.SteganoExtractException;
+import org.stegosuite.model.exception.SteganoImageException;
+import org.stegosuite.model.payload.block.FileBlock;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Observable;
+import java.util.Observer;
+
+import static org.junit.Assert.*;
+import static org.stegosuite.Resources.pathOf;
+
+public class PresenterTest implements StegosuiteUI, Observer {
+	private static final String IMAGE_NAME = "snow.bmp";
+	private static final String EMBEDDED_IMAGE = "snow_embed.bmp";
+	private static final String FILE_CONTENT = "The content of the file";
+	private static final String FILE_EXTENSION = "txt";
+	private static final String FILE_NAME = "file.txt";
+	private final String message = "a message";
+	private final String password = "a password";
+
+	private int currentProgress;
+	private EmbeddingProgress progressListener;
+	private StegosuitePresenter presenter;
+
+	private String outputPath;
+	private ImageFormat embeddedImage;
+	private Visualizer outputVisualizer;
+
+	private String extractedMessage;
+	private String statusMessage;
+	private ImageData imageData;
+
+	private String addedFileName;
+	private String addedFileExtension;
+	private long addedFileSize;
+
+	@Before
+	public void setUp() throws Exception {
+		progressListener = new EmbeddingProgress();
+		progressListener.addObserver(this);
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		Resources.delete(EMBEDDED_IMAGE);
+	}
+
+	@Test
+	public void embeddingTest() throws Exception {
+		presenter = presenterWithImage(IMAGE_NAME);
+		presenter.addMessageToPayload(message);
+		presenter.embedNotifying(progressListener, password);
+
+		assertEquals(100, currentProgress);
+		assertNotNull(outputVisualizer);
+		assertEquals(pathOf(IMAGE_NAME), embeddedImage.getFilePath());
+		assertEquals(pathOf(EMBEDDED_IMAGE), outputPath);
+		assertTrue(new File(outputPath).exists());
+	}
+
+	@Test
+	public void extractTest() throws Exception {
+		presenter = presenterWithImage(IMAGE_NAME);
+		presenter.addMessageToPayload(message);
+		presenter.embedNotifying(progressListener, password);
+		resetCurrentProgress();
+
+		presenter = presenterWithImage(EMBEDDED_IMAGE);
+		presenter.extractNotifying(progressListener, password);
+
+		assertEquals(message, extractedMessage);
+		//assertEquals("Extracting completed.", statusMessage); TODO: use filePaths instead
+		assertNotNull(outputVisualizer);
+		assertArrayEquals(imageDataOf(EMBEDDED_IMAGE).data, imageData.data);
+	}
+
+	@Test
+	public void extractFileTest() throws Exception {
+		presenter = presenterWithImage(IMAGE_NAME);
+		presenter.addFileToPayload(pathOf(FILE_NAME));
+		presenter.addMessageToPayload(message);
+		presenter.embedNotifying(progressListener, password);
+		resetCurrentProgress();
+		presenter = presenterWithImage(EMBEDDED_IMAGE);
+
+		presenter.extractNotifying(progressListener, password);
+
+		assertEquals(message, extractedMessage);
+		//assertEquals("Extracting completed. Extracted file saved to "
+		//		+ pathOf(FILE_NAME), statusMessage); TODO: use filePaths instead
+		assertNotNull(outputVisualizer);
+		assertArrayEquals(imageDataOf(EMBEDDED_IMAGE).data, imageData.data);
+		assertEquals(FILE_NAME, addedFileName);
+		assertEquals(FILE_EXTENSION, addedFileExtension);
+		assertEquals(FILE_CONTENT.length(), fileBlockSize());
+		assertEquals(FILE_CONTENT.length(), addedFileSize);
+		assertEquals(FILE_CONTENT, readContentFromFile(FILE_NAME));
+	}
+
+	private long fileBlockSize() {
+		return presenter.payloadFileBlocksWithFilename(FILE_NAME).stream()
+				.findFirst().get().getSize();
+	}
+
+	private FileBlock getFileBlockFromPresenter() {
+		return presenter.payloadFileBlocksWithFilename(FILE_NAME)
+				.stream().findAny().get();
+	}
+
+	private StegosuitePresenter presenterWithImage(String imageName) throws SteganoImageException {
+		ImageFormat image = getImage(imageName);
+		return new StegosuitePresenter(image, this);
+	}
+
+	private ImageFormat getImage(String imageName) throws SteganoImageException {
+		return ImageFormat.getImageFormat(pathOf(imageName));
+	}
+
+	private String readContentFromFile(String fileName) throws IOException {
+		return Files.readAllLines(Paths.get(pathOf(fileName)))
+					.stream()
+					.reduce(String::concat)
+					.get();
+	}
+
+	private void resetCurrentProgress() {
+		currentProgress = 0;
+	}
+
+	private ImageData imageDataOf(String image) throws SteganoImageException {
+		return getImage(image).getImageData();
+	}
+
+	@Override
+	public void showEmbeddingError(SteganoEmbedException e) {
+		throw new RuntimeException(e);
+	}
+
+	@Override
+	public void showExtractingError(SteganoExtractException e) {
+		throw new RuntimeException(e);
+	}
+
+	@Override
+	public void extractingCompleted(String extractedMessage, List<String> filePaths, Visualizer visualizer, ImageData imageData) {
+		this.extractedMessage = extractedMessage;
+		//this.statusMessage = statusMessage; //TODO: Use filePaths instead of statusMessage
+		this.outputVisualizer = visualizer;
+		this.imageData = imageData;
+	}
+
+	@Override
+	public void embeddingCompleted(ImageFormat embeddedImage, String outputPath, Visualizer visualizer) {
+		this.outputPath = outputPath;
+		this.embeddedImage = embeddedImage;
+		this.outputVisualizer = visualizer;
+	}
+
+	@Override
+	public void addPayloadFile(String filename, String extension, long fileSize) {
+		addedFileName = filename;
+		addedFileExtension = extension;
+		addedFileSize = fileSize;
+	}
+
+	@Override
+	public void update(Observable o, Object arg) {
+		int notifiedProgress = (int) arg;
+		assertTrue(notifiedProgress >= currentProgress);
+		currentProgress = notifiedProgress;
+	}
+}
diff --git a/src/test/java/org/stegosuite/Resources.java b/src/test/java/org/stegosuite/Resources.java
new file mode 100644
index 0000000..6f1ce54
--- /dev/null
+++ b/src/test/java/org/stegosuite/Resources.java
@@ -0,0 +1,20 @@
+package org.stegosuite;
+
+import java.io.File;
+import java.net.URL;
+
+public class Resources {
+	public static String pathOf(String fileName) {
+		URL resourceURL = Resources.class.getClassLoader().getResource(fileName);
+		if (resourceURL == null)
+			throw new RuntimeException("The resource " + fileName + " does not exist");
+
+		return resourceURL.getPath();
+	}
+
+	static void delete(String filename) {
+		try {
+			new File(pathOf(filename)).delete();
+		} catch (Exception ignored) {}
+	}
+}
diff --git a/src/test/java/org/stegosuite/image/format/GIFImageTest.java b/src/test/java/org/stegosuite/image/format/GIFImageTest.java
index 0dca7c0..44a282f 100644
--- a/src/test/java/org/stegosuite/image/format/GIFImageTest.java
+++ b/src/test/java/org/stegosuite/image/format/GIFImageTest.java
@@ -1,13 +1,15 @@
 package org.stegosuite.image.format;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.stegosuite.model.exception.SteganoImageException;
+
 import java.io.File;
 import java.util.Arrays;
 import java.util.stream.IntStream;
 
-import org.stegosuite.model.exception.SteganoImageException;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
 
 public class GIFImageTest {
 
@@ -18,7 +20,7 @@ public class GIFImageTest {
 	 * 
 	 * @throws SteganoImageException
 	 */
-	@BeforeMethod
+	@Before
 	public void beforeMethod()
 			throws SteganoImageException {
 		image = new GIFImage();
@@ -27,27 +29,26 @@ public class GIFImageTest {
 
 	@Test
 	public void testWidth() {
-		Assert.assertEquals(image.getWidth(), 1160);
+		assertEquals(image.getWidth(), 1160);
 	}
 
 	@Test
 	public void testHeight() {
-		Assert.assertEquals(image.getHeight(), 1376);
+		assertEquals(image.getHeight(), 1376);
 	}
 
-	@Test(dependsOnMethods = { "testWidth", "testHeight" })
+	@Test
 	public void testGetPixels() {
 		Integer[] pixels = IntStream.of(image.getPixels()).boxed().toArray(Integer[]::new);
-		Assert.assertEquals(pixels.length, image.getWidth() * image.getHeight());
-		Assert.assertEquals(Arrays.deepHashCode(pixels), -2099205017);
+		assertEquals(pixels.length, image.getWidth() * image.getHeight());
+		assertEquals(Arrays.deepHashCode(pixels), -2099205017);
 	}
 
-	@Test(dependsOnMethods = { "testGetPixels" })
+	@Test
 	public void testSetPixels() {
 		int[] pixels = new int[image.getWidth() * image.getHeight()];
 		Arrays.setAll(pixels, i -> 0);
 		image.setPixels(pixels);
-		Assert.assertEquals(image.getPixels(), pixels);
+		assertArrayEquals(image.getPixels(), pixels);
 	}
-
 }
diff --git a/src/test/resources/file.txt b/src/test/resources/file.txt
new file mode 100644
index 0000000..b3072bd
--- /dev/null
+++ b/src/test/resources/file.txt
@@ -0,0 +1 @@
+The content of the file
\ No newline at end of file
diff --git a/src/test/resources/landscape.jpg b/src/test/resources/landscape.jpg
new file mode 100644
index 0000000..4616d94
Binary files /dev/null and b/src/test/resources/landscape.jpg differ
diff --git a/src/test/resources/landscape_embed_ok.jpg b/src/test/resources/landscape_embed_ok.jpg
new file mode 100644
index 0000000..45ff66a
Binary files /dev/null and b/src/test/resources/landscape_embed_ok.jpg differ
diff --git a/src/test/resources/snow_embed_ok.bmp b/src/test/resources/snow_embed_ok.bmp
new file mode 100644
index 0000000..f1bbf94
Binary files /dev/null and b/src/test/resources/snow_embed_ok.bmp differ
diff --git a/src/test/resources/sunflower_embed_ok.gif b/src/test/resources/sunflower_embed_ok.gif
new file mode 100644
index 0000000..8f1e148
Binary files /dev/null and b/src/test/resources/sunflower_embed_ok.gif differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/stegosuite.git



More information about the pkg-java-commits mailing list