mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 19:14:55 +00:00
improve disposal of resources
This commit is contained in:
parent
8bb612ab2c
commit
8f32064c2d
@ -623,10 +623,13 @@ public class ImageUtils {
|
||||
ImageReader reader = readers.next();
|
||||
reader.setInput(input);
|
||||
try {
|
||||
|
||||
return propertyExtractor.extract(reader);
|
||||
} catch (IOException ex) {
|
||||
ImageUtils.logContentError(LOGGER, Level.WARNING, errorTemplate + ex.toString(), file);
|
||||
throw ex;
|
||||
} finally {
|
||||
reader.dispose();
|
||||
}
|
||||
} else {
|
||||
IIOException iioException = newImageReaderException(file);
|
||||
@ -672,7 +675,7 @@ public class ImageUtils {
|
||||
if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
|
||||
return SwingFXUtils.toFXImage(cachedThumbnail, null);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
} catch (IOException ex) {
|
||||
logContentError(logger, Level.WARNING, "ImageIO had a problem reading thumbnail for image {0}: " + ex.toString(), file);
|
||||
}
|
||||
}
|
||||
@ -747,6 +750,7 @@ public class ImageUtils {
|
||||
|
||||
ReadImageTask(AbstractFile file) {
|
||||
super(file);
|
||||
updateMessage(Bundle.LoadImageTask_mesageText(file.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -754,7 +758,7 @@ public class ImageUtils {
|
||||
"# {0} - file name",
|
||||
"LoadImageTask.mesageText=Reading image: {0}"})
|
||||
protected javafx.scene.image.Image call() throws Exception {
|
||||
updateMessage(Bundle.LoadImageTask_mesageText(file.getName()));
|
||||
|
||||
return readImage();
|
||||
}
|
||||
}
|
||||
@ -762,18 +766,12 @@ public class ImageUtils {
|
||||
static private abstract class ReadImageTaskBase extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
|
||||
|
||||
final AbstractFile file;
|
||||
private volatile BufferedImage bufferedImage = null;
|
||||
private ImageReader reader;
|
||||
|
||||
ReadImageTaskBase(AbstractFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public BufferedImage getBufferedImage() throws InterruptedException, ExecutionException {
|
||||
get();
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
protected javafx.scene.image.Image readImage() throws IOException {
|
||||
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file));) {
|
||||
if (ImageUtils.isGIF(file)) {
|
||||
@ -803,23 +801,23 @@ public class ImageUtils {
|
||||
*/
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
bufferedImage = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
|
||||
BufferedImage bufferedImage = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
|
||||
param.setDestination(bufferedImage);
|
||||
try {
|
||||
reader.read(0, param);
|
||||
bufferedImage = reader.read(0, param);
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException iOException) {
|
||||
// Ignore this exception or display a warning or similar, for exceptions happening during decoding
|
||||
logContentError(logger, Level.WARNING, "ImageIO could not read {0}. It may be unsupported or corrupt: " + iOException.toString(), file);
|
||||
} finally {
|
||||
reader.removeIIOReadProgressListener(this);
|
||||
reader.dispose();
|
||||
}
|
||||
reader.removeIIOReadProgressListener(this);
|
||||
reader.dispose();
|
||||
return SwingFXUtils.toFXImage(bufferedImage, null);
|
||||
} else {
|
||||
throw newImageReaderException(file);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public enum ThumbnailCache {
|
||||
* in memory cache. keeps at most 1000 items each for up to 10 minutes.
|
||||
* items may be garbage collected if there are no strong references to them.
|
||||
*/
|
||||
private final Cache<Long, Optional<Image>> cache = CacheBuilder.newBuilder()
|
||||
private final Cache<Long, Image> cache = CacheBuilder.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.softValues()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES).build();
|
||||
@ -99,7 +99,7 @@ public enum ThumbnailCache {
|
||||
@Nullable
|
||||
public Image get(DrawableFile<?> file) {
|
||||
try {
|
||||
return cache.get(file.getId(), () -> load(file)).orElse(null);
|
||||
return cache.get(file.getId(), () -> load(file));
|
||||
} catch (UncheckedExecutionException | CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load thumbnail for file: " + file.getName(), ex.getCause());
|
||||
return null;
|
||||
@ -124,12 +124,12 @@ public enum ThumbnailCache {
|
||||
*
|
||||
* @return an (possibly empty) optional containing a thumbnail
|
||||
*/
|
||||
private Optional<Image> load(DrawableFile<?> file) {
|
||||
private Image load(DrawableFile<?> file) {
|
||||
|
||||
if (FileTypeUtils.isGIF(file)) {
|
||||
//directly read gif to preserve potential animation,
|
||||
//NOTE: not saved to disk!
|
||||
return Optional.of(new Image(new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile())), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, true, true));
|
||||
return new Image(new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile())), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, true, true);
|
||||
}
|
||||
|
||||
BufferedImage thumbnail = getCacheFile(file).map(cachFile -> {
|
||||
@ -160,7 +160,7 @@ public enum ThumbnailCache {
|
||||
jfxthumbnail = SwingFXUtils.toFXImage(thumbnail, null);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(jfxthumbnail); //return icon, or null if generation failed
|
||||
return jfxthumbnail; //return icon, or null if generation failed
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,12 +182,12 @@ public enum ThumbnailCache {
|
||||
}
|
||||
|
||||
public Task<Image> getThumbnailTask(DrawableFile<?> file) {
|
||||
final Optional<Image> option = cache.getIfPresent(file.getId());
|
||||
if (option != null && option.isPresent()) {
|
||||
final Image thumbnail = cache.getIfPresent(file.getId());
|
||||
if (thumbnail != null) {
|
||||
return new Task<Image>() {
|
||||
@Override
|
||||
protected Image call() throws Exception {
|
||||
return option.get();
|
||||
return thumbnail;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -196,7 +196,7 @@ public enum ThumbnailCache {
|
||||
switch (newGetThumbnailTask.getState()) {
|
||||
case SUCCEEDED:
|
||||
try {
|
||||
cache.put(Long.MIN_VALUE, Optional.of(newGetThumbnailTask.get()));
|
||||
cache.put(Long.MIN_VALUE, newGetThumbnailTask.get());
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
}
|
||||
|
@ -60,9 +60,9 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
||||
private static final Logger LOGGER = Logger.getLogger(DrawableUIBase.class.getName());
|
||||
|
||||
@FXML
|
||||
BorderPane imageBorder;
|
||||
BorderPane imageBorder;
|
||||
@FXML
|
||||
ImageView imageView;
|
||||
ImageView imageView;
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
@ -118,33 +118,41 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
||||
synchronized public void setFile(Long newFileID) {
|
||||
if (getFileID().isPresent()) {
|
||||
if (Objects.equals(newFileID, getFileID().get()) == false) {
|
||||
// if (Objects.nonNull(newFileID)) {
|
||||
setFileHelper(newFileID);
|
||||
// }
|
||||
}
|
||||
} else {//if (Objects.nonNull(newFileID)) {
|
||||
} else {
|
||||
setFileHelper(newFileID);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized protected void updateContent() {
|
||||
if (getFile().isPresent() == false) {
|
||||
Platform.runLater(() -> imageBorder.setCenter(null));
|
||||
} else {
|
||||
if (getFile().isPresent()) {
|
||||
doReadImageTask(getFile().get());
|
||||
}
|
||||
}
|
||||
|
||||
synchronized Node doReadImageTask(DrawableFile<?> file) {
|
||||
disposeContent();
|
||||
Task<Image> myTask = newReadImageTask(file);
|
||||
imageTask = myTask;
|
||||
Node progressNode = newProgressIndicator(myTask);
|
||||
Platform.runLater(() -> imageBorder.setCenter(progressNode));
|
||||
|
||||
//called on fx thread
|
||||
imageTask.setOnSucceeded(succeeded -> showImage(file, myTask));
|
||||
imageTask.setOnFailed(failed -> showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file));
|
||||
myTask.setOnSucceeded(succeeded -> {
|
||||
showImage(file, myTask);
|
||||
synchronized (DrawableUIBase.this) {
|
||||
imageTask = null;
|
||||
}
|
||||
});
|
||||
myTask.setOnFailed(failed -> {
|
||||
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
|
||||
synchronized (DrawableUIBase.this) {
|
||||
imageTask = null;
|
||||
}
|
||||
});
|
||||
myTask.setOnCancelled(cancelled -> {
|
||||
disposeContent();
|
||||
});
|
||||
|
||||
exec.execute(myTask);
|
||||
return progressNode;
|
||||
@ -155,8 +163,10 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
||||
imageTask.cancel();
|
||||
}
|
||||
imageTask = null;
|
||||
Platform.runLater(() -> imageView.setImage(null));
|
||||
|
||||
Platform.runLater(() -> {
|
||||
imageView.setImage(null);
|
||||
imageBorder.setCenter(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,7 +181,7 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
void showImage(DrawableFile<?> file, Task<Image> imageTask) {
|
||||
private void showImage(DrawableFile<?> file, Task<Image> imageTask) {
|
||||
//Note that all error conditions are allready logged in readImageTask.succeeded()
|
||||
try {
|
||||
Image fxImage = imageTask.get();
|
||||
@ -201,57 +211,4 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
||||
}
|
||||
|
||||
abstract Task<Image> newReadImageTask(DrawableFile<?> file);
|
||||
|
||||
abstract class CachedLoaderTask<X, Y extends DrawableFile<?>> extends Task<X> {
|
||||
|
||||
protected final Y file;
|
||||
|
||||
public CachedLoaderTask(Y file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X call() throws Exception {
|
||||
return (isCancelled() == false) ? load() : null;
|
||||
}
|
||||
|
||||
abstract X load() throws Exception;
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
super.succeeded();
|
||||
if (isCancelled() == false) {
|
||||
try {
|
||||
saveToCache(get());
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to cache content for" + file.getName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed() {
|
||||
super.failed();
|
||||
LOGGER.log(Level.SEVERE, "Failed to cache content for" + file.getName(), getException());
|
||||
}
|
||||
|
||||
abstract void saveToCache(X result);
|
||||
}
|
||||
|
||||
// class ThumbnailLoaderTask extends CachedLoaderTask<Image, DrawableFile<?>> {
|
||||
//
|
||||
// ThumbnailLoaderTask(DrawableFile<?> file) {
|
||||
// super(file);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// Image load() {
|
||||
// return isCancelled() ? null : file.getThumbnail();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// void saveToCache(Image result) {
|
||||
//// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
@ -169,21 +169,23 @@ public class MetaDataPane extends DrawableUIBase {
|
||||
@Override
|
||||
synchronized protected void setFileHelper(Long newFileID) {
|
||||
setFileIDOpt(Optional.ofNullable(newFileID));
|
||||
if (newFileID == null) {
|
||||
Platform.runLater(() -> {
|
||||
imageView.setImage(null);
|
||||
imageBorder.setCenter(null);
|
||||
tableView.getItems().clear();
|
||||
getCategoryBorderRegion().setBorder(null);
|
||||
});
|
||||
} else {
|
||||
disposeContent();
|
||||
disposeContent();
|
||||
if (nonNull(newFileID)) {
|
||||
updateAttributesTable();
|
||||
updateCategory();
|
||||
updateContent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void disposeContent() {
|
||||
super.disposeContent();
|
||||
Platform.runLater(() -> {
|
||||
tableView.getItems().clear();
|
||||
getCategoryBorderRegion().setBorder(null);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
Task<Image> newReadImageTask(DrawableFile<?> file) {
|
||||
return file.getThumbnailTask();
|
||||
|
@ -19,8 +19,6 @@
|
||||
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.nonNull;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -76,6 +74,8 @@ public class SlideShowView extends DrawableTileBase {
|
||||
@FXML
|
||||
private BorderPane footer;
|
||||
|
||||
private volatile MediaLoadTask mediaTask;
|
||||
|
||||
SlideShowView(GroupPane gp, ImageGalleryController controller) {
|
||||
super(gp, controller);
|
||||
FXMLConstructor.construct(this, "SlideShowView.fxml");
|
||||
@ -160,49 +160,58 @@ public class SlideShowView extends DrawableTileBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeContent() {
|
||||
synchronized protected void disposeContent() {
|
||||
stopVideo();
|
||||
|
||||
if (mediaTask != null) {
|
||||
mediaTask.cancel(true);
|
||||
}
|
||||
mediaTask = null;
|
||||
super.disposeContent();
|
||||
// if (mediaTask != null) {
|
||||
// mediaTask.cancel(true);
|
||||
// }
|
||||
// mediaTask = null;
|
||||
mediaCache = null;
|
||||
}
|
||||
private SoftReference<Node> mediaCache;
|
||||
|
||||
@Override
|
||||
synchronized protected void updateContent() {
|
||||
if (getFile().isPresent() == false) {
|
||||
mediaCache = null;
|
||||
Platform.runLater(() -> imageBorder.setCenter(null));
|
||||
|
||||
} else {
|
||||
disposeContent();
|
||||
if (getFile().isPresent()) {
|
||||
DrawableFile<?> file = getFile().get();
|
||||
if (file.isVideo()) {
|
||||
//specially handling for videos
|
||||
Node mediaNode = (isNull(mediaCache)) ? null : mediaCache.get();
|
||||
if (nonNull(mediaNode)) {
|
||||
Platform.runLater(() -> imageBorder.setCenter(mediaNode));
|
||||
} else {
|
||||
|
||||
MediaLoadTask mediaTask = new MediaLoadTask(((VideoFile<?>) file));
|
||||
Node progressNode = newProgressIndicator(mediaTask);
|
||||
Platform.runLater(() -> imageBorder.setCenter(progressNode));
|
||||
|
||||
//called on fx thread
|
||||
mediaTask.setOnSucceeded(succedded -> showMedia(file, mediaTask));
|
||||
mediaTask.setOnFailed(failed -> showErrorNode(getMediaLoadErrorLabel(mediaTask), file));
|
||||
|
||||
exec.execute(mediaTask);
|
||||
}
|
||||
doMediaLoadTask((VideoFile<?>) file);
|
||||
} else {
|
||||
super.updateContent();
|
||||
doReadImageTask(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private Node doMediaLoadTask(VideoFile<?> file) {
|
||||
|
||||
//specially handling for videos
|
||||
MediaLoadTask myTask = new MediaLoadTask(file);
|
||||
mediaTask = myTask;
|
||||
Node progressNode = newProgressIndicator(myTask);
|
||||
Platform.runLater(() -> imageBorder.setCenter(progressNode));
|
||||
|
||||
//called on fx thread
|
||||
mediaTask.setOnSucceeded(succeedded -> {
|
||||
showMedia(file, myTask);
|
||||
synchronized (SlideShowView.this) {
|
||||
mediaTask = null;
|
||||
}
|
||||
});
|
||||
mediaTask.setOnFailed(failed -> {
|
||||
showErrorNode(getMediaLoadErrorLabel(myTask), file);
|
||||
synchronized (SlideShowView.this) {
|
||||
mediaTask = null;
|
||||
}
|
||||
});
|
||||
mediaTask.setOnCancelled(cancelled -> {
|
||||
disposeContent();
|
||||
});
|
||||
|
||||
exec.execute(myTask);
|
||||
return progressNode;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void showMedia(DrawableFile<?> file, Task<Node> mediaTask) {
|
||||
//Note that all error conditions are allready logged in readImageTask.succeeded()
|
||||
|
Loading…
x
Reference in New Issue
Block a user