taskify thumbnail loading

This commit is contained in:
millmanorama 2015-12-21 09:13:47 -05:00 committed by jmillman
parent 8dc7208876
commit 4b9ec7958f
13 changed files with 251 additions and 98 deletions

View File

@ -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) {
} }

View File

@ -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;
}
} }

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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" />

View File

@ -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

View File

@ -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));
}); });

View File

@ -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.
} // }
} // }
} }

View File

@ -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);
} }
} }

View File

@ -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() {

View File

@ -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" />

View File

@ -331,13 +331,9 @@ 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); return doReadImageTask(file);
} }
//if we couldn't even show it as an image. rethrow exception.
throw ex;
}
} }
} }
} }