mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
taskify thumbnail loading
This commit is contained in:
parent
8dc7208876
commit
4b9ec7958f
@ -44,6 +44,7 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javafx.beans.Observable;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -57,6 +58,7 @@ import javax.imageio.stream.ImageInputStream;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
import org.opencv.core.Core;
|
import org.opencv.core.Core;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||||
@ -409,6 +411,7 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* Get a file object for where the cached icon should exist. The returned
|
* Get a file object for where the cached icon should exist. The returned
|
||||||
* file may not exist.
|
* file may not exist.
|
||||||
*
|
*
|
||||||
@ -615,17 +618,119 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<javafx.scene.image.Image> newReadImageTask(AbstractFile file) {
|
public static Task<javafx.scene.image.Image> newGetThumbnailTask(AbstractFile file, int iconSize) {
|
||||||
return new ReadImageTask(file);
|
return new GetOrGenerateThumbnailTask(file, iconSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static private class ReadImageTask extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
|
static private class GetOrGenerateThumbnailTask extends ReadImageTaskBase {
|
||||||
|
|
||||||
|
private final int iconSize;
|
||||||
|
|
||||||
|
private GetOrGenerateThumbnailTask(AbstractFile file, int iconSize) {
|
||||||
|
super(file);
|
||||||
|
this.iconSize = iconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected javafx.scene.image.Image call() throws Exception {
|
||||||
|
|
||||||
|
// If a thumbnail file is already saved locally, just read that.
|
||||||
|
File cacheFile = getCachedThumbnailLocation(file.getId());
|
||||||
|
if (cacheFile.exists()) {
|
||||||
|
try {
|
||||||
|
BufferedImage cachedThumbnail = ImageIO.read(cacheFile);
|
||||||
|
if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
|
||||||
|
return SwingFXUtils.toFXImage(cachedThumbnail, null);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "ImageIO had a problem reading thumbnail for image {0}: {1}", new Object[]{file.getName(), ex.getMessage()}); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//no cached thumbnail so read the image from the datasource using the ReadImageTask
|
||||||
|
final ReadImageTask readImageTask = new ReadImageTask(file);
|
||||||
|
readImageTask.progressProperty().addListener((Observable observable)
|
||||||
|
-> updateProgress(readImageTask.getWorkDone(), readImageTask.getTotalWork()));
|
||||||
|
readImageTask.messageProperty().addListener((Observable observable)
|
||||||
|
-> updateMessage(readImageTask.getMessage()));
|
||||||
|
readImageTask.run();
|
||||||
|
try {
|
||||||
|
BufferedImage bufferedImage = readImageTask.getBufferedImage();
|
||||||
|
if (isNull(bufferedImage)) {
|
||||||
|
LOGGER.log(Level.WARNING, "Failed to read image for thumbnail generation.");
|
||||||
|
throw new IIOException("Failed to read image for thumbnail generation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMessage("scaling image");
|
||||||
|
updateProgress(-1, 1);
|
||||||
|
|
||||||
|
BufferedImage thumbnail;
|
||||||
|
try {
|
||||||
|
thumbnail = ScalrWrapper.resizeFast(bufferedImage, iconSize);
|
||||||
|
} catch (IllegalArgumentException | OutOfMemoryError e) {
|
||||||
|
// if resizing does not work due to extreme aspect ratio, crop the image instead.
|
||||||
|
try {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not scale image {0}: {1}.\nAttemptying to crop {0} instead", new Object[]{file.getUniquePath(), e.getLocalizedMessage()}); //NON-NLS
|
||||||
|
} catch (TskCoreException tskCoreException) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not scale image {0}: {1}.\nAttemptying to crop {0} instead", new Object[]{file.getName(), e.getLocalizedMessage()}); //NON-NLS
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to get unique path for file: " + file.getName(), tskCoreException); //NOI18N
|
||||||
|
}
|
||||||
|
final int cropHeight = Math.min(iconSize, bufferedImage.getHeight());
|
||||||
|
final int cropWidth = Math.min(iconSize, bufferedImage.getWidth());
|
||||||
|
try {
|
||||||
|
thumbnail = ScalrWrapper.cropImage(bufferedImage, cropWidth, cropHeight);
|
||||||
|
} catch (Exception e2) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not scale image {0}: {1}", new Object[]{file.getName(), e2.getLocalizedMessage()}); //NON-NLS
|
||||||
|
throw e2;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not scale image {0}: {1}", new Object[]{file.getName(), e.getLocalizedMessage()}); //NON-NLS
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
updateProgress(-1, 1);
|
||||||
|
|
||||||
|
saveThumbnail(thumbnail, cacheFile);
|
||||||
|
return SwingFXUtils.toFXImage(thumbnail, null);
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
// try {
|
||||||
|
// LOGGER.log(Level.WARNING, "Could not scale image {0}: {1}.\nAttemptying to crop {0} instead", new Object[]{file.getUniquePath(), e.getLocalizedMessage()}); //NON-NLS
|
||||||
|
// } catch (TskCoreException tskCoreException) {
|
||||||
|
// LOGGER.log(Level.WARNING, "Could not scale image {0}: {1}.\nAttemptying to crop {0} instead", new Object[]{file.getName(), e.getLocalizedMessage()}); //NON-NLS
|
||||||
|
// LOGGER.log(Level.SEVERE, "Failed to get unique path for file: " + file.getName(), tskCoreException); //NOI18N
|
||||||
|
// }
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveThumbnail(BufferedImage thumbnail, File cacheFile) {
|
||||||
|
if (nonNull(thumbnail) && DEFAULT_THUMBNAIL != thumbnail) {
|
||||||
|
imageSaver.execute(() -> {
|
||||||
|
try {
|
||||||
|
Files.createParentDirs(cacheFile);
|
||||||
|
if (cacheFile.exists()) {
|
||||||
|
cacheFile.delete();
|
||||||
|
}
|
||||||
|
ImageIO.write(thumbnail, FORMAT, cacheFile);
|
||||||
|
} catch (IllegalArgumentException | IOException ex1) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not write cache thumbnail: " + file.getName(), ex1); //NON-NLS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<javafx.scene.image.Image> newReadImageTask(AbstractFile file) {
|
||||||
|
return new ReadImageTask(file);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static private class ReadImageTask extends ReadImageTaskBase {
|
||||||
|
|
||||||
private final AbstractFile file;
|
|
||||||
volatile private BufferedImage bufferedImage = null;
|
volatile private BufferedImage bufferedImage = null;
|
||||||
|
|
||||||
ReadImageTask(AbstractFile file) {
|
ReadImageTask(AbstractFile file) {
|
||||||
this.file = file;
|
super(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -635,7 +740,6 @@ public class ImageUtils {
|
|||||||
protected javafx.scene.image.Image call() throws Exception {
|
protected javafx.scene.image.Image call() throws Exception {
|
||||||
updateMessage(Bundle.LoadImageTask_mesageText(file.getName()));
|
updateMessage(Bundle.LoadImageTask_mesageText(file.getName()));
|
||||||
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file));) {
|
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file));) {
|
||||||
|
|
||||||
if (ImageUtils.isGIF(file)) {
|
if (ImageUtils.isGIF(file)) {
|
||||||
//directly read GIF to preserve potential animation,
|
//directly read GIF to preserve potential animation,
|
||||||
javafx.scene.image.Image image = new javafx.scene.image.Image(new BufferedInputStream(inputStream));
|
javafx.scene.image.Image image = new javafx.scene.image.Image(new BufferedInputStream(inputStream));
|
||||||
@ -657,8 +761,9 @@ public class ImageUtils {
|
|||||||
reader.setInput(input);
|
reader.setInput(input);
|
||||||
/*
|
/*
|
||||||
* This is the important part, get or create a
|
* This is the important part, get or create a
|
||||||
* ReadParam, create a destination image to hold the
|
* ImageReadParam, create a destination image to hold
|
||||||
* decoded result, then pass that image with the param.
|
* the decoded result, then pass that image with the
|
||||||
|
* param.
|
||||||
*/
|
*/
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
|
|
||||||
@ -679,20 +784,36 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logError(@Nullable Throwable e) {
|
public BufferedImage getBufferedImage() throws InterruptedException, ExecutionException {
|
||||||
String message = e == null ? "" : "It may be unsupported or corrupt: " + e.getLocalizedMessage(); //NOI18N
|
get();
|
||||||
try {
|
return bufferedImage;
|
||||||
LOGGER.log(Level.WARNING, "Could not read the image: {0}. {1}", new Object[]{file.getUniquePath(), message}); //NOI18N
|
|
||||||
} catch (TskCoreException tskCoreException) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not read the image: {0}. {1}", new Object[]{file.getName(), message}); //NOI18N
|
|
||||||
LOGGER.log(Level.SEVERE, "Failed to get unique path for file", tskCoreException); //NOI18N
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static private abstract class ReadImageTaskBase extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
|
||||||
|
|
||||||
|
protected final AbstractFile file;
|
||||||
|
|
||||||
|
public ReadImageTaskBase(AbstractFile file) {
|
||||||
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void failed() {
|
public void imageProgress(ImageReader source, float percentageDone) {
|
||||||
super.failed();
|
//update this task with the progress reported by ImageReader.read
|
||||||
logError(getException());
|
updateProgress(percentageDone, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logError(@Nullable Throwable e) {
|
||||||
|
Exceptions.printStackTrace(e);
|
||||||
|
String message = e == null ? "" : "It may be unsupported or corrupt: " + e.getLocalizedMessage(); //NOI18N
|
||||||
|
try {
|
||||||
|
LOGGER.log(Level.WARNING, "ImageIO could not read {0}. {1}", new Object[]{file.getUniquePath(), message}); //NOI18N
|
||||||
|
} catch (TskCoreException tskCoreException) {
|
||||||
|
LOGGER.log(Level.WARNING, "ImageIO could not read {0}. {1}", new Object[]{file.getName(), message}); //NOI18N
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to get unique path for file: " + file.getName(), tskCoreException); //NOI18N
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -714,13 +835,9 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void imageProgress(ImageReader source, float percentageDone) {
|
protected void failed() {
|
||||||
//update this task with the progress reported by ImageReader.read
|
super.failed();
|
||||||
updateProgress(percentageDone, 100);
|
logError(getException());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void imageStarted(ImageReader source, int imageIndex) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -728,6 +845,10 @@ public class ImageUtils {
|
|||||||
updateProgress(100, 100);
|
updateProgress(100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void imageStarted(ImageReader source, int imageIndex) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sequenceStarted(ImageReader source, int minIndex) {
|
public void sequenceStarted(ImageReader source, int minIndex) {
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,15 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.WritableImage;
|
import javafx.scene.image.WritableImage;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
@ -165,7 +168,7 @@ public enum ThumbnailCache {
|
|||||||
*
|
*
|
||||||
* @param id the obj id of the file to get a cache file for
|
* @param id the obj id of the file to get a cache file for
|
||||||
*
|
*
|
||||||
* @return a Optional containing a File to store the cahced icon in or an
|
* @return a Optional containing a File to store the cached icon in or an
|
||||||
* empty optional if there was a problem.
|
* empty optional if there was a problem.
|
||||||
*/
|
*/
|
||||||
private static Optional<File> getCacheFile(DrawableFile<?> file) {
|
private static Optional<File> getCacheFile(DrawableFile<?> file) {
|
||||||
@ -177,4 +180,28 @@ public enum ThumbnailCache {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<Image> getThumbnailTask(DrawableFile<?> file) {
|
||||||
|
final Optional<Image> option = cache.getIfPresent(file.getId());
|
||||||
|
if (option != null && option.isPresent()) {
|
||||||
|
return new Task<Image>() {
|
||||||
|
@Override
|
||||||
|
protected Image call() throws Exception {
|
||||||
|
return option.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
final Task<Image> newGetThumbnailTask = ImageUtils.newGetThumbnailTask(file.getAbstractFile(), MAX_THUMBNAIL_SIZE);
|
||||||
|
newGetThumbnailTask.stateProperty().addListener((Observable observable) -> {
|
||||||
|
switch (newGetThumbnailTask.getState()) {
|
||||||
|
case SUCCEEDED:
|
||||||
|
try {
|
||||||
|
cache.put(Long.MIN_VALUE, Optional.of(newGetThumbnailTask.get()));
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
Exceptions.printStackTrace(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newGetThumbnailTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -39,7 +38,6 @@ import org.apache.commons.lang3.text.WordUtils;
|
|||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
@ -79,6 +77,7 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
SoftReference<Image> imageRef;
|
SoftReference<Image> imageRef;
|
||||||
|
// SoftReference<Image> thumbref;
|
||||||
|
|
||||||
private String drawablePath;
|
private String drawablePath;
|
||||||
|
|
||||||
@ -258,12 +257,19 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Image getThumbnail() {
|
public Image getThumbnail() {
|
||||||
return ThumbnailCache.getDefault().get(this);
|
try {
|
||||||
|
return getThumbnailTask().get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
}
|
||||||
|
|
||||||
|
public abstract Task<Image> getThumbnailTask();
|
||||||
|
|
||||||
|
@Deprecated //use non-blocking getReadFullSizeImageTask instead for most cases
|
||||||
public Image getFullSizeImage() {
|
public Image getFullSizeImage() {
|
||||||
try {
|
try {
|
||||||
return getReadFullSizeImageTask().get();
|
return getReadFullSizeImageTask().get();
|
||||||
@ -304,11 +310,6 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisplayableAsImage() {
|
|
||||||
Image thumbnail = getThumbnail();
|
|
||||||
return Objects.nonNull(thumbnail) && thumbnail.errorProperty().get() == false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public Set<String> getHashSetNamesUnchecked() {
|
public Set<String> getHashSetNamesUnchecked() {
|
||||||
try {
|
try {
|
||||||
|
@ -28,6 +28,7 @@ import javafx.scene.image.Image;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,6 +49,34 @@ public class ImageFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task<Image> getThumbnailTask() {
|
||||||
|
return ThumbnailCache.getDefault().getThumbnailTask(this);
|
||||||
|
// newGetThumbTask.stateProperty().addListener((Observable observable) -> {
|
||||||
|
// switch (newGetThumbTask.getState()) {
|
||||||
|
// case CANCELLED:
|
||||||
|
// break;
|
||||||
|
// case FAILED:
|
||||||
|
// break;
|
||||||
|
// case SUCCEEDED:
|
||||||
|
// try {
|
||||||
|
// thumbref = new SoftReference<>(newGetThumbTask.get());
|
||||||
|
// } catch (InterruptedException | ExecutionException interruptedException) {
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// return newGetThumbTask;
|
||||||
|
// } else {
|
||||||
|
// return new Task<Image>() {
|
||||||
|
// @Override
|
||||||
|
// protected Image call() throws Exception {
|
||||||
|
// return thumbnail;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<Image> getReadFullSizeImageTask() {
|
public Task<Image> getReadFullSizeImageTask() {
|
||||||
Image image = (imageRef != null) ? imageRef.get() : null;
|
Image image = (imageRef != null) ? imageRef.get() : null;
|
||||||
|
@ -24,9 +24,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
@ -36,9 +34,9 @@ import javafx.scene.media.MediaException;
|
|||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.VideoUtils;
|
import org.sleuthkit.autopsy.coreutils.VideoUtils;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
|
||||||
public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
||||||
@ -53,6 +51,12 @@ public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
return VIDEO_ICON;
|
return VIDEO_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task<Image> getThumbnailTask() {
|
||||||
|
return ThumbnailCache.getDefault().getThumbnailTask(VideoFile.this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<Image> getReadFullSizeImageTask() {
|
public Task<Image> getReadFullSizeImageTask() {
|
||||||
Image image = (imageRef != null) ? imageRef.get() : null;
|
Image image = (imageRef != null) ? imageRef.get() : null;
|
||||||
@ -61,7 +65,7 @@ public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Image call() throws Exception {
|
protected Image call() throws Exception {
|
||||||
final BufferedImage bufferedImage = (BufferedImage) ImageUtils.getThumbnail(getAbstractFile(), 1024);
|
final BufferedImage bufferedImage = ImageUtils.getThumbnail(getAbstractFile(), 1024);
|
||||||
return (bufferedImage == ImageUtils.getDefaultThumbnail())
|
return (bufferedImage == ImageUtils.getDefaultThumbnail())
|
||||||
? null
|
? null
|
||||||
: SwingFXUtils.toFXImage(bufferedImage, null);
|
: SwingFXUtils.toFXImage(bufferedImage, null);
|
||||||
@ -119,18 +123,6 @@ public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisplayableAsMedia() {
|
|
||||||
try {
|
|
||||||
Media media = getMedia();
|
|
||||||
return Objects.nonNull(media) && Objects.isNull(media.getError());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.getLogger(VideoFile.class.getName()).log(Level.SEVERE, "failed to write video to cache for playback.", ex);
|
|
||||||
return false;
|
|
||||||
} catch (MediaException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Double getWidth() {
|
Double getWidth() {
|
||||||
try {
|
try {
|
||||||
|
@ -34,11 +34,6 @@
|
|||||||
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" fx:id="x1" />
|
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" fx:id="x1" />
|
||||||
</HBox.margin>
|
</HBox.margin>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
<ImageView fx:id="undisplayableImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
|
||||||
<image>
|
|
||||||
<Image url="@../../images/prohibition.png" />
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="2.0" right="2.0" top="2.0" />
|
<Insets bottom="2.0" right="2.0" top="2.0" />
|
||||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.CacheHint;
|
import javafx.scene.CacheHint;
|
||||||
import javafx.scene.control.Control;
|
import javafx.scene.control.Control;
|
||||||
@ -90,8 +91,8 @@ public class DrawableTile extends DrawableTileBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
CachedLoaderTask<Image, DrawableFile<?>> newReadImageTask(DrawableFile<?> file) {
|
Task<Image> newReadImageTask(DrawableFile<?> file) {
|
||||||
return new ThumbnailLoaderTask(file);
|
return file.getThumbnailTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -71,7 +71,6 @@ import org.sleuthkit.autopsy.imagegallery.actions.OpenExternalViewerAction;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
@ -112,8 +111,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
@FXML
|
@FXML
|
||||||
private ImageView hashHitImageView;
|
private ImageView hashHitImageView;
|
||||||
|
|
||||||
@FXML
|
|
||||||
protected ImageView undisplayableImageView;
|
|
||||||
/**
|
/**
|
||||||
* displays the icon representing follow up tag
|
* displays the icon representing follow up tag
|
||||||
*/
|
*/
|
||||||
@ -316,7 +314,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
getFile().ifPresent(file -> {
|
getFile().ifPresent(file -> {
|
||||||
final boolean isVideo = file.isVideo();
|
final boolean isVideo = file.isVideo();
|
||||||
final boolean hasHashSetHits = hasHashHit();
|
final boolean hasHashSetHits = hasHashHit();
|
||||||
final boolean isUndisplayable = (isVideo ? ((VideoFile<?>) file).isDisplayableAsMedia() : file.isDisplayableAsImage()) == false;
|
|
||||||
final String text = getTextForLabel();
|
final String text = getTextForLabel();
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@ -324,8 +322,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
fileTypeImageView.setVisible(isVideo);
|
fileTypeImageView.setVisible(isVideo);
|
||||||
hashHitImageView.setManaged(hasHashSetHits);
|
hashHitImageView.setManaged(hasHashSetHits);
|
||||||
hashHitImageView.setVisible(hasHashSetHits);
|
hashHitImageView.setVisible(hasHashSetHits);
|
||||||
undisplayableImageView.setManaged(isUndisplayable);
|
|
||||||
undisplayableImageView.setVisible(isUndisplayable);
|
|
||||||
nameLabel.setText(text);
|
nameLabel.setText(text);
|
||||||
nameLabel.setTooltip(new Tooltip(text));
|
nameLabel.setTooltip(new Tooltip(text));
|
||||||
});
|
});
|
||||||
|
@ -56,7 +56,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
@NbBundle.Messages({"MediaViewImagePanel.errorLabel.text=Could not load file."})
|
@NbBundle.Messages({"MediaViewImagePanel.errorLabel.text=Could not load file."})
|
||||||
abstract public class DrawableUIBase extends AnchorPane implements DrawableView {
|
abstract public class DrawableUIBase extends AnchorPane implements DrawableView {
|
||||||
|
|
||||||
static final Executor exec = Executors.newWorkStealingPool();
|
static final Executor exec = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(DrawableUIBase.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(DrawableUIBase.class.getName());
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
|||||||
|
|
||||||
synchronized protected void disposeContent() {
|
synchronized protected void disposeContent() {
|
||||||
if (imageTask != null) {
|
if (imageTask != null) {
|
||||||
imageTask.cancel(true);
|
imageTask.cancel();
|
||||||
}
|
}
|
||||||
imageTask = null;
|
imageTask = null;
|
||||||
Platform.runLater(() -> imageView.setImage(null));
|
Platform.runLater(() -> imageView.setImage(null));
|
||||||
@ -244,20 +244,20 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
|||||||
abstract void saveToCache(X result);
|
abstract void saveToCache(X result);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThumbnailLoaderTask extends CachedLoaderTask<Image, DrawableFile<?>> {
|
// class ThumbnailLoaderTask extends CachedLoaderTask<Image, DrawableFile<?>> {
|
||||||
|
//
|
||||||
ThumbnailLoaderTask(DrawableFile<?> file) {
|
// ThumbnailLoaderTask(DrawableFile<?> file) {
|
||||||
super(file);
|
// super(file);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
Image load() {
|
// Image load() {
|
||||||
return isCancelled() ? null : file.getThumbnail();
|
// return isCancelled() ? null : file.getThumbnail();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
void saveToCache(Image result) {
|
// void saveToCache(Image result) {
|
||||||
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
//// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -774,12 +774,10 @@ public class GroupPane extends BorderPane {
|
|||||||
@Override
|
@Override
|
||||||
protected void updateItem(Long item, boolean empty) {
|
protected void updateItem(Long item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
|
|
||||||
tile.setFile(item);
|
tile.setFile(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetItem() {
|
void resetItem() {
|
||||||
// updateItem(null, true);
|
|
||||||
tile.setFile(null);
|
tile.setFile(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import java.util.stream.Collectors;
|
|||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@ -184,8 +185,8 @@ public class MetaDataPane extends DrawableUIBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
CachedLoaderTask<Image, DrawableFile<?>> newReadImageTask(DrawableFile<?> file) {
|
Task<Image> newReadImageTask(DrawableFile<?> file) {
|
||||||
return new ThumbnailLoaderTask(file);
|
return file.getThumbnailTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAttributesTable() {
|
public void updateAttributesTable() {
|
||||||
|
@ -55,11 +55,6 @@
|
|||||||
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" />
|
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" />
|
||||||
</HBox.margin>
|
</HBox.margin>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
<ImageView fx:id="undisplayableImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
|
||||||
<image>
|
|
||||||
<Image url="@../../images/prohibition.png" />
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="2.0" right="2.0" top="2.0" />
|
<Insets bottom="2.0" right="2.0" top="2.0" />
|
||||||
|
@ -331,12 +331,8 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
Logger.getLogger(VideoFile.class.getName()).log(Level.WARNING, "Failed to initialize VideoPlayer for file {0} : {1}", new Object[]{file.getName(), ex.getLocalizedMessage()});
|
Logger.getLogger(VideoFile.class.getName()).log(Level.WARNING, "Failed to initialize VideoPlayer for file {0} : {1}", new Object[]{file.getName(), ex.getLocalizedMessage()});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.isDisplayableAsImage()) {
|
|
||||||
return doReadImageTask(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if we couldn't even show it as an image. rethrow exception.
|
return doReadImageTask(file);
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user