Skip to content

Commit

Permalink
Commit of WIP before further refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
KodeMunkie committed Oct 6, 2018
1 parent 4910a9f commit a517705
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 107 deletions.
24 changes: 12 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@
<source>1.8</source>
<target>1.8</target>
</configuration>
<!--<dependencies>-->
<!--<dependency>-->
<!--<groupId>org.codehaus.groovy</groupId>-->
<!--<artifactId>groovy-eclipse-compiler</artifactId>-->
<!--<version>2.9.1-01</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.codehaus.groovy</groupId>-->
<!--<artifactId>groovy-eclipse-batch</artifactId>-->
<!--<version>2.3.7-01</version>-->
<!--</dependency>-->
<!--</dependencies>-->
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.9.1-01</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-batch</artifactId>
<version>2.3.7-01</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/uk/co/silentsoftware/core/helpers/SaveHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import javax.imageio.ImageIO;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -52,7 +53,8 @@ public class SaveHelper {
*/
public static void saveImage(final BufferedImage output, final File destFolder, final String fileName, final String formatName) throws IOException {
log.debug("Dest folder {}, filename {}, formatName {}", destFolder, fileName, formatName);
File destFile = new File(destFolder.getAbsolutePath()+"/"+fileName.substring(0, fileName.lastIndexOf("."))+FILE_SUFFIX+formatName);
String baseFileName = getBaseName(fileName);
File destFile = new File(destFolder.getAbsolutePath()+"/"+baseFileName+FILE_SUFFIX+formatName);
deleteFileIfExists(destFile);
log.debug("Writing image to {} with format {}", destFile, formatName);
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(destFile))) {
Expand All @@ -62,6 +64,13 @@ public static void saveImage(final BufferedImage output, final File destFolder,
throw io;
}
}

private static String getBaseName(String fileName) {
if (fileName.contains(".")) {
return fileName.substring(0, fileName.lastIndexOf("."));
}
return fileName;
}

/**
* Saves raw byte data to the given file deleting any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ String getImageId() {
return imageId;
}

void setImageId(String imageId) {
this.imageId = imageId;
}

ResultImage[] getResultImage() {
return resultImage;
}
Expand Down
125 changes: 48 additions & 77 deletions src/main/java/uk/co/silentsoftware/dispatcher/WorkManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -177,9 +174,8 @@ private Image getRandomFrameFromVideo(File f) throws Exception {
*
* @param image the image to apply the dithers to
* @param dithers the dither strategies to apply
* @throws InterruptedException if the processing is interrupted
*/
private <T extends DitherStrategy> void generatePopupPreviewDithers(Image image, @SuppressWarnings("unchecked") T... dithers) throws InterruptedException {
private <T extends DitherStrategy> void generatePopupPreviewDithers(Image image, @SuppressWarnings("unchecked") T... dithers) {
Stream<T> stream = Arrays.stream(dithers);
Runnable runnable = () -> {
stream.forEach(dither -> {
Expand Down Expand Up @@ -240,22 +236,13 @@ public void processFiles(final UiCallback uiCallback, File[] inFiles, File outFo
* Polls video and submits frames to the work engine for processing
*
* @param uiCallback the callback to control the ui
* @param f the video file to process
* @param inputFile the video file to process
* @param outFolder the output folder
* @throws InterruptedException if the processing is interrupted
*/
private void processVideo(UiCallback uiCallback, File f, File outFolder) throws InterruptedException {
final VideoLoadedLock videoLoadedLock = new VideoLoadedLock();
private void processVideo(UiCallback uiCallback, File inputFile, File outFolder) throws InterruptedException {
final BlockingQueue<Image> sharedQueue = new DisruptorBlockingQueue<>(MAX_QUEUE_SIZE);
exec.execute(() -> {
try {
OptionsObject.getInstance().getVideoImportEngine().convertVideoToImages(f, false, sharedQueue, videoLoadedLock);
} catch (Throwable t) {
log.error("Failed to convert video", t);
enableInput(uiCallback, t.getMessage());
}
});
videoLoadedLock.waitFor();
waitForVideoToSpoolUp(sharedQueue, uiCallback, inputFile);
Image buf;
Map<Integer, WorkContainer> results = new ConcurrentHashMap<>();

Expand All @@ -264,6 +251,7 @@ private void processVideo(UiCallback uiCallback, File f, File outFolder) throws

// The last unique frame number that was processed and outputed
int outputSequenceNumber = 0;

WorkOutputter workOutputter = null;
try {
workOutputter = new WorkOutputter(this, uiCallback, outFolder);
Expand All @@ -273,7 +261,8 @@ private void processVideo(UiCallback uiCallback, File f, File outFolder) throws
return;
}
int oldOutputSequenceNumber = outputSequenceNumber;
outputSequenceNumber = processFrame(f.getName(), sequenceNumber, outputSequenceNumber, workOutputter, results, buf, uiCallback);
processFrame(sequenceNumber+"_"+inputFile.getName(), sequenceNumber, results, buf, uiCallback);
outputSequenceNumber = outputNextImage(results, outputSequenceNumber, workOutputter);
sequenceNumber++;
// Only increase the fps count if it's actually removed an image from
// the queue (as opposed to starting the job and waiting for it to become
Expand All @@ -283,11 +272,7 @@ private void processVideo(UiCallback uiCallback, File f, File outFolder) throws
}
}
log.debug("Image relay finished awaiting remaining results");
if (!cancel) {
while (outputSequenceNumber < sequenceNumber) {
outputSequenceNumber = outputNextImage(f.getName(), results, outputSequenceNumber, workOutputter);
}
}
outputRemainingFrames(outputSequenceNumber, sequenceNumber, results, workOutputter);
} finally {
try {
if (workOutputter != null) {
Expand All @@ -301,6 +286,19 @@ private void processVideo(UiCallback uiCallback, File f, File outFolder) throws
log.debug("Finished polling result queue");
}

private void waitForVideoToSpoolUp(BlockingQueue<Image> sharedQueue, UiCallback uiCallback, File inputFile) {
final VideoLoadedLock videoLoadedLock = new VideoLoadedLock();
exec.execute(() -> {
try {
OptionsObject.getInstance().getVideoImportEngine().convertVideoToImages(inputFile, false, sharedQueue, videoLoadedLock);
} catch (Throwable t) {
log.error("Failed to convert video", t);
enableInput(uiCallback, t.getMessage());
}
});
videoLoadedLock.waitFor();
}

/**
* Inner core method for the process files method that specifically deals
* with a single files. The files are loaded as images and these are put
Expand All @@ -311,126 +309,99 @@ private void processVideo(UiCallback uiCallback, File f, File outFolder) throws
* @param uiCallback the callback to control the ui
* @param inFiles the single image files to process
* @param outFolder the output folder
* @throws InterruptedException if the processing is interrupted
*/
private void processSingleFiles(UiCallback uiCallback, File[] inFiles, File outFolder) throws InterruptedException {
private void processSingleFiles(UiCallback uiCallback, File[] inFiles, File outFolder) {
if (ArrayUtils.isEmpty(inFiles)) {
return;
}
Map<Integer, WorkContainer> results = new ConcurrentHashMap<>();
int sequenceNumber = 0;
int outputSequenceNumber = 0;
List<File> files = Arrays.asList(inFiles);
WorkOutputter workOutputter = null;
try {
int sequenceNumber = 0;
int outputSequenceNumber = 0;
Map<Integer, WorkContainer> results = new ConcurrentHashMap<>();
List<File> files = Arrays.asList(inFiles);
workOutputter = new WorkOutputter(this, uiCallback, outFolder);
for (File f : files) {
if (cancel) {
return;
}
int oldOutputSequenceNumber = outputSequenceNumber;
outputSequenceNumber = processFrame(f.getName(), sequenceNumber, outputSequenceNumber, workOutputter, results, readImage(f), uiCallback);
processFrame(f.getName(), sequenceNumber, results, readImage(f), uiCallback);
outputSequenceNumber = outputNextImage(results, outputSequenceNumber, workOutputter);
sequenceNumber++;
// Only increase the fps count if it's actually removed an image
// from the queue
// (as opposed to starting the job and waiting for it to become
// from the queue (as opposed to starting the job and waiting for it to become
// available)
if (outputSequenceNumber > oldOutputSequenceNumber) {
fpsFrameCount++;
}
}
if (!cancel) {
while (outputSequenceNumber < sequenceNumber) {
outputSequenceNumber = outputNextImage(inFiles[outputSequenceNumber].getName(), results, outputSequenceNumber, workOutputter);
}
}
outputRemainingFrames(outputSequenceNumber, sequenceNumber, results, workOutputter);
} finally {
try {
workOutputter.processEndStep();
} catch (Exception e) {
log.error("Unable to process end step", e);
}
;
enableInput(uiCallback, LanguageSupport.getCaption("main_operation_finished"));
}
}

private void outputRemainingFrames(int outputSequenceNumber, int sequenceNumber, Map<Integer, WorkContainer> results, WorkOutputter workOutputter) {
if (!cancel) {
while (outputSequenceNumber < sequenceNumber) {
outputSequenceNumber = outputNextImage(results, outputSequenceNumber, workOutputter);
}
}
}

/**
* Processes a single frame in a new thread
*
* @param name the output name
* @param sequenceNumber the sequence number for this frame
* @param outputSequenceNumber the last outputed frame sequence number
* @param workOutputter the work outputter instance
* @param results the results map with frame number to image
* @param image the image to convert
* @param uiCallback the uicallback for error messages
* @return the last outputed sequence number
* @throws InterruptedException if processing fails
* @return the last outputted sequence number
*/
private int processFrame(String name, int sequenceNumber, int outputSequenceNumber, WorkOutputter workOutputter, Map<Integer, WorkContainer> results,
Image image, UiCallback uiCallback) throws InterruptedException {
private void processFrame(String name, int sequenceNumber, Map<Integer, WorkContainer> results,
Image image, UiCallback uiCallback) {
try {
final int sequenceFinal = sequenceNumber;
exec.execute(() -> results.put(sequenceFinal, workDispatcher.submitFrame(image, StringUtils.EMPTY + sequenceNumber)));
exec.execute(() -> results.put(sequenceNumber, workDispatcher.submitFrame(image, StringUtils.EMPTY + name)));
} catch (OutOfMemoryError oome) {
uiCallback.setStatusMessage(oome.getMessage());
log.error("Out of memory on frame", oome);
outputSequenceNumber++;
return outputSequenceNumber;
} catch (Throwable t) {
// Ignore it and try to continue
log.error("Unhandled throwable", t);
}
return outputNextImage(name, results, outputSequenceNumber, workOutputter);
}

/**
* Sets the work container image id based on whether it is an image or video
*
* @param name the base name of the input file
* @param workContainer the WorkContainer to modify
* @param outputSequenceNumber the sequenceNumber for this image
*/
private void setImageId(String name, WorkContainer workContainer, int outputSequenceNumber) {
if (isVideo(name)) {
workContainer.setImageId(outputSequenceNumber + "_" + name);
} else {
workContainer.setImageId(name);
}
}

/**
* Outputs the given output sequence number frame if it has been processed
* otherwise returns
*
* @param name the name of input file
* @param results the map of workcontainer results
* @param outputSequenceNumber the frame number to output
* @param workOutputter the output object instance
* @return the updated outputted sequence number if a frame was output,
* otherwise the original outputSequenceNumber
*/
private int outputNextImage(String name, Map<Integer, WorkContainer> results, int outputSequenceNumber, WorkOutputter workOutputter) {
private int outputNextImage(Map<Integer, WorkContainer> results, int outputSequenceNumber, WorkOutputter workOutputter) {
// Yield gives the system a chance to breath - the work has just been added for processing
// but may not yet be available. Removing this call results in 10-20% better performance but
// stuttering video preview
if (!OptionsObject.getInstance().getTurboMode()) {
Thread.yield();
}

if (!cancel && results.size() > 0) {
WorkContainer workContainer = results.get(outputSequenceNumber);
if (workContainer != null) {
setImageId(name, workContainer, outputSequenceNumber);
workOutputter.outputFrame(workContainer);
final int previewSeqNumber = outputSequenceNumber;
uiFeederThread.execute(() -> {
try {
workOutputter.previewFrame(workContainer);
} finally {
results.remove(previewSeqNumber);
}
});
uiFeederThread.execute(() -> workOutputter.previewFrame(workContainer));
results.remove(outputSequenceNumber);
outputSequenceNumber++;
}
}
Expand Down Expand Up @@ -560,7 +531,7 @@ public void cancel() {
/**
* Reset the number of frames processed
*/
void resetFrameCount() {
private void resetFrameCount() {
fpsFrameCount = 0;
}
}
19 changes: 8 additions & 11 deletions src/main/java/uk/co/silentsoftware/dispatcher/WorkOutputter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,9 @@
*/
package uk.co.silentsoftware.dispatcher;

import static uk.co.silentsoftware.config.LanguageSupport.getCaption;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

import io.humble.ferry.Buffer;
import org.magicwerk.brownies.collections.BigList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.co.silentsoftware.config.OptionsObject;
import uk.co.silentsoftware.core.converters.image.ResultImage;
import uk.co.silentsoftware.core.converters.spectrum.ScrConverter;
Expand All @@ -40,6 +29,14 @@
import uk.co.silentsoftware.core.helpers.SaveHelper;
import uk.co.silentsoftware.ui.ImageToZxSpec.UiCallback;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

import static uk.co.silentsoftware.config.LanguageSupport.getCaption;

/**
* Manages the work output, i.e. using the work container results it
* decides what needs to be output based on the user's chosen options
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/uk/co/silentsoftware/ui/CoffeeDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void showPopupIfNecessary() {
JTextPane aboutField = new JTextPane();
aboutField.setContentType("text/html");
aboutField.setText("<h2>You seem to be enjoying Image to ZX Spec!</h2>" +
"Did you know Image to ZX Spec has been in development for the last 7 years?<br>"+
"Did you know Image to ZX Spec has been in development for the last 8 years?<br>"+
"That's a lot of coffee, so please consider a contribution of any amount to the<br>"+
"developer's coffee buying budget!<br>"+
"<h3><a href='"+COFFEE_LINK+"'>Buy the developer a coffee</a></h3>"+
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/uk/co/silentsoftware/ui/ImageToZxSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class ImageToZxSpec {
private static final Logger log = LoggerFactory.getLogger(ImageToZxSpec.class);

// TODO: Use build system properties
private static final String NAME_COPYRIGHT = "Image to ZX Spec 2.0.1 © Silent Software 2017";
private static final String NAME_COPYRIGHT = "Image to ZX Spec 2.0.2 © Silent Software 2018";

public static final ImageIcon IMAGE_ICON = new ImageIcon(ImageToZxSpec.class.getResource("/icons/logo.png"));
private static final ImageIcon OPEN_FILE_ICON = new ImageIcon(ImageToZxSpec.class.getResource("/icons/New Document.png"));
Expand Down

0 comments on commit a517705

Please sign in to comment.