diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index 2a5553f0ed..55f32beb7e 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -195,7 +195,7 @@ public class ImageUtils { BufferedImage bicon = ImageIO.read(iconFile); if (bicon == null) { icon = DEFAULT_ICON; - } else if (bicon.getWidth() != iconSize) { + } else if (bicon.getWidth() < iconSize) { icon = generateAndSaveIcon(content, iconSize, iconFile); } else { icon = bicon; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java index 2dbd2cde16..d7a42ec165 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java @@ -23,31 +23,22 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.util.concurrent.UncheckedExecutionException; import java.awt.image.BufferedImage; -import java.io.BufferedInputStream; import java.io.File; -import java.io.IOException; -import java.io.InputStream; import java.net.MalformedURLException; import java.util.Optional; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javafx.beans.property.SimpleIntegerProperty; import javafx.embed.swing.SwingFXUtils; import javafx.scene.image.Image; -import javafx.scene.image.WritableImage; import javax.annotation.Nullable; -import javax.imageio.ImageIO; -import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.corelibs.ScalrWrapper; +import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.gui.Toolbar; -import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; /** Singleton to manage creation and access of icons. Keeps a cache in memory of @@ -81,9 +72,7 @@ public enum ThumbnailCache { /** currently desired icon size. is bound in {@link Toolbar} */ public final SimpleIntegerProperty iconSize = new SimpleIntegerProperty(200); - /** thread that saves generated thumbnails to disk for use later */ - private final Executor imageSaver = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder().namingPattern("icon saver-%d").build()); - + /** * Clear out the cache between cases */ @@ -185,104 +174,9 @@ public enum ThumbnailCache { */ @Nullable private Image generateAndSaveThumbnail(final DrawableFile file) { - //create a buffered input stream for the underlying Abstractfile - try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile()))) { - final Image thumbnail = new Image(inputStream, MAX_ICON_SIZE, MAX_ICON_SIZE, true, true); - if (thumbnail.isError()) { //if there was an error loading the image via JFX, fall back on Swing - LOGGER.log(Level.WARNING, "problem loading thumbnail for image: " + file.getName() + " ."); - // Doing it this way puts the whole stack trace in the console output, which is probably not - // needed. There are a significant number of cases where this is expected to fail (bitmaps, - // empty files, etc.) - //LOGGER.log(Level.WARNING, "problem loading image: " + file.getName() + " .", thumbnail.getException()); - return fallbackToSwingImage(file); - } else { //if the load went successfully, save the thumbnail to disk on a background thread - imageSaver.execute(() -> { - saveIcon(file, thumbnail); - }); - return thumbnail; - } - } catch (IOException ex) { - //if the JX load throws an exception fall back to Swing - return fallbackToSwingImage(file); - } + + return SwingFXUtils.toFXImage((BufferedImage) ImageUtils.getIcon(file.getAbstractFile(), MAX_ICON_SIZE), null); } - /** - * use Swing to generate and save a thumbnail for the given file - * - * @param file - * - * @return a thumbnail generated for the given file, or {@code null} if a - * thumbnail could not be generated - */ - @Nullable - private Image fallbackToSwingImage(final DrawableFile file) { - final BufferedImage generateSwingIcon = generateSwingThumbnail(file); - if (generateSwingIcon == null) { //if swing failed, - return null; //propagate failure up cal stack. - } else {//Swing load succeeded, convert to JFX Image - final WritableImage toFXImage = SwingFXUtils.toFXImage(generateSwingIcon, null); - if (toFXImage != null) { //if conversion succeeded save to disk cache - imageSaver.execute(() -> { - saveIcon(file, toFXImage); - }); - } - return toFXImage; //could be null - } - } - /** - * use Swing/ImageIO to generate a thumbnail for the given file - * - * @param file - * - * @return a BufferedImage thumbail for the given file, or {@code null} if a - * thumbnail could not be generated - */ - private BufferedImage generateSwingThumbnail(DrawableFile file) { - //create a buffered input stream for the underlying Abstractfile - try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile()))) { - BufferedImage bi = ImageIO.read(inputStream); - if (bi != null) { - try { // resize (shrink) the buffered image if needed - if (Math.max(bi.getWidth(), bi.getHeight()) > MAX_ICON_SIZE) { - bi = ScalrWrapper.resizeFast(bi, iconSize.get()); - } - } catch (IllegalArgumentException e) { - //if scalr failed, just use unscaled image - LOGGER.log(Level.WARNING, "scalr could not scale image to 0: {0}", file.getName()); - } catch (OutOfMemoryError e) { - LOGGER.log(Level.WARNING, "scalr could not scale image (too large): {0}", file.getName()); - return null; - } - } else { //ImageIO failed to read the image - LOGGER.log(Level.WARNING, "No image reader for file: {0}", file.getName()); - return null; - } - return bi; - } catch (IOException ex) { - LOGGER.log(Level.WARNING, "Could not read image: " + file.getName()); - return null; - } - } - - /** - * save the generated thumbnail to disk in the cache folder with - * the obj_id as the name. - * - * @param file the file the given image is a thumbnail for - * @param bi the thumbnail to save for the given DrawableFile - */ - private void saveIcon(final DrawableFile file, final Image bi) { - if (bi != null) { - getCacheFile(file.getId()).ifPresent((File cacheFile) -> { - try { - //convert back to swing to save - ImageIO.write(SwingFXUtils.fromFXImage(bi, null), FORMAT, cacheFile); - } catch (IllegalArgumentException | IOException ex) { - LOGGER.log(Level.WARNING, "failed to save generated icon"); - } - }); - } - } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java index 86eed0a814..41355a8e48 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews; import java.util.Objects; import java.util.logging.Level; import javafx.application.Platform; +import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.scene.CacheHint; import javafx.scene.control.Control; @@ -113,9 +114,30 @@ public class DrawableTile extends DrawableTileBase { @Override protected Runnable getContentUpdateRunnable() { if (getFile().isPresent()) { - Image image = getFile().get().getThumbnail(); + return () -> { + new Task() { + + @Override + protected Image call() throws Exception { + Image image = getFile().get().getThumbnail(); + + } + + @Override + protected void failed() { + super.failed(); + LOGGER.log(null); + } + + @Override + protected void succeeded() { + super.succeeded(); //To change body of generated methods, choose Tools | Templates. + } + } + + imageView.setImage(image); }; } else {